`const handleDateChange = (value: any, employeeId: string, i: number) =\> {
if (selectedDate?.filter((e: any) =\> e.employeeId === employeeId).length \> 0) {
setSelectedDate(selectedDate.map((e: any) =\> {
if (e.employeeId === employeeId) {
return { ...e, dateOfBirth: value }
}
else {
return { ...e }
}
}))
}
else {
setSelectedDate([...selectedDate, { employeeId: employeeId, dateOfBirth: value }])
}
setIsDisabled(false);
};`
If this datepicker contains functionality that allows you to change a button's state, develop a test for it.
Related
I know that this sounds a bit dumb, but how do you call a function only once. I have a tab bar at the bottom of my app, and every time that it is called, the name that I got from my firebase database, keeps on being added. For example, the name in firebase is Bob. The app for the first time will display Bob. Then you would click on the settings, and go back to the home view. Then the app will say BobBob, and over and over again. How do I make this stop.
Code:
import SwiftUI
import Firebase
struct HomeView: View {
#State var name = ""
var body: some View {
NavigationView {
ZStack {
VStack{
Text("Welcome \(name)")
.font(.title)
Text("Upcoming Lessions/Reservations:")
.bold()
.padding()
Divider()
}
}
}
.navigationTitle("Home")
.onAppear(perform: {
downloadNameServerData()
})
}
private func downloadNameServerData() {
let db = Firestore.firestore()
db.collection("users").addSnapshotListener {(snap, err) in
if err != nil{
print("\(String(describing: err))")
return
}
for i in snap!.documentChanges {
_ = i.document.documentID
if let Name = i.document.get("Name") as? String {
DispatchQueue.main.async {
name.append(Name)
print("\(name)")
}
}
}
}
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}
import SwiftUI
import Firebase
struct HomeView: View {
#State var name = ""
var body: some View {
NavigationView {
ZStack {
VStack{
Text("Welcome \(name)")
.font(.title)
Text("Upcoming Lessions/Reservations:")
.bold()
.padding()
Divider()
}
}
}
.navigationTitle("Home")
.onAppear(perform: {
downloadNameServerData()
})
}
private func downloadNameServerData() {
if !name.isEmpty { return }
let db = Firestore.firestore()
db.collection("users").addSnapshotListener {(snap, err) in
if err != nil{
print("\(String(describing: err))")
return
}
for i in snap!.documentChanges {
_ = i.document.documentID
if let Name = i.document.get("Name") as? String {
DispatchQueue.main.async {
name = Name
print("\(name)")
}
}
}
}
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}
Did you consider only loading the name if you don't have one yet?
.onAppear(perform: {
if (name == null) downloadNameServerData()
})
I need to call a function in my SignScreen's onCreate method but there is not any applicable place for this. I can't call currentUserCheck function from anywhere.
What i tried :
Calling it in init block in viewModel. But the problem was it throwing nullPointerException for NavController here.
Calling it in MainActivity and MyTheme but i faced many weird issues in these scopes.
ViewModel:
#HiltViewModel
class SignViewModel (#ApplicationContext context: Context) : ViewModel() {
val auth = FirebaseAuth.getInstance()
init {
currentUserCheck(NavController(context))
}
fun signIn(email: String?, password: String?, context: Context, navController: NavController) {
if (email != null && email.isNotBlank() && password != null && password.isNotBlank()) {
auth.signInWithEmailAndPassword(email, password).addOnSuccessListener {
navController.navigate(ScreenHolder.ProfileScreen.route) {
popUpTo(ScreenHolder.SigningScreen.route) {
inclusive = true
}
}
}.addOnFailureListener {
Toast.makeText(context, it.localizedMessage, Toast.LENGTH_LONG).show()
}
} else {
Toast.makeText(
context,
"Lütfen email ve şifre alanlarını boş bırakmayınız.",
Toast.LENGTH_LONG
).show()
}
}
fun signUp(email: String?, password: String?, context: Context, navController: NavController) {
if (email != null && email.isNotBlank() && password != null && password.isNotBlank()) {
auth.createUserWithEmailAndPassword(email, password)
.addOnSuccessListener {
navController.navigate(ScreenHolder.ProfileScreen.route) {
popUpTo(ScreenHolder.ProfileScreen.route) {
inclusive = true
}
}
}
} else {
Toast.makeText(
context,
"Lütfen email ve şifre alanlarını boş bırakmayınız.",
Toast.LENGTH_LONG
).show()
}
}
fun currentUserCheck(navController: NavController) {
if (auth.currentUser != null) {
navController.navigate(ScreenHolder.ProfileScreen.route) {
popUpTo(ScreenHolder.ProfileScreen.route) {
inclusive = true
}
}
}
}
}
Solution
As #Pztar said i declare the authentication controlling operation as a state in viewModel and then i observed it in my Composable SignScreen with LaunchEffect and it is solved.
#HiltViewModel
class SignViewModel (#ApplicationContext context: Context) : ViewModel() {
val auth = FirebaseAuth.getInstance()
var currentUser = mutableStateOf(false)
//New currenUserCheck method that declare if state is true or not
fun currentUserCheck() {
if (auth.currentUser != null) {
currentUser.value = true
}
}
}
#Composable
fun SignScreen(viewModel: SignViewModel= hiltViewModel(),navController: NavController,context: Context) {
//Observing the state from my composable and routing user if state is true.
LaunchedEffect(key1 = Unit ){
viewModel.currentUserCheck()
if (viewModel.currentUser.value){
navController.navigate(ScreenHolder.ProfileScreen.route) {
popUpTo(ScreenHolder.SigningScreen.route) {
inclusive = true
}
}
}
}
}
I have this view:
import SwiftUI
struct SectionView1: View {
let dateStr:String
#Binding var isSectionView:Bool
var body: some View {
HStack {
Button(action: {
self.isSectionView.toggle()
}) {
Image(systemName: isSectionView ? "chevron.down.circle" : "chevron.right.circle")
}
Text("Media del \(dateStr)")
}
}
}
which will be called from view:
import SwiftUI
import Photos
struct MediaView: View {
let geoFolder:GeoFolderCD
#State private var assetsForDate = [String :[PHAsset]]()
#State private var isSectionViewArray:[String:Bool] = [:]
var body: some View {
List {
ForEach(assetsForDate.keys.sorted(by: > ), id: \.self) { dateStr in
Section {
SectionView1(dateStr: dateStr,
isSectionView: self.$isSectionViewArray[dateStr, default: true])
}
}
}
.onAppear {
self.assetsForDate = FetchMediaUtility().fetchGeoFolderAssetsForDate(geoFolder: geoFolderStruct, numAssets: numMediaToFetch)
for dateStr in self.assetsForDate.keys.sorted() {
self.isSectionViewArray[dateStr] = true
}
}
}
}
but I have the error: Subscript index of type '() -> Bool' in a key path must be Hashable in isSectionView: self.$isSectionViewArray[dateStr, default: true]
Why isSectionViewArray:[String:Bool] = [:] is not Hasbable?
How can modify the code for work?
If I remove, in SectionView, #Binding var isSectionView:Bool, the code work fine, or if I set, from SectionView, #Binding var isSectionViewArray:[String:Bool] = [:], the code work fine.
You can write your own binding with the below code and it should work
var body: some View {
List {
ForEach(assetsForDate.keys.sorted(by: > ), id: \.self) { dateStr in
let value = Binding<Bool>(get: { () -> Bool in
return self.isSectionViewArray[dateStr, default: true]
}) { (value) in
}
Section {
SectionView1(dateStr: dateStr,
isSectionView: value)
}
}
}
.onAppear {
self.assetsForDate = FetchMediaUtility().fetchGeoFolderAssetsForDate(geoFolder: geoFolderStruct, numAssets: numMediaToFetch)
for dateStr in self.assetsForDate.keys.sorted() {
self.isSectionViewArray[dateStr] = true
}
}
}
I don't really understand any of this from https://github.com/Akryum/meteor-vuex-example/tree/master/imports/vuex/modules: from init(data) all the way to if data in getters at the bottom references to vue instance data or state of vuex.
subModule.addTrackers({
selectedThread() {
let sub;
return {
init(data) {
data.selectedThread = null;
data.posts = [];
},
watch(state) {
// Dynamic subscription
if(sub) {
sub.stop();
}
if(state.selectedThreadId) {
sub = Meteor.subscribe('posts', state.selectedThreadId);
console.log('subscribed posts to thread ', state.selectedThreadId);
}
return {
id: state.selectedThreadId
}
},
update(data, {id}) {
data.selectedThread = Object.freeze(Threads.findOne({
_id: id
}));
data.posts = Object.freeze(Posts.find({
thread_id: id
}, {
sort: {created: -1}
}).fetch());
console.log('posts', data.posts);
},
getters: {
getSelectedThread: data => data.selectedThread,
getPosts: data => data.posts
}
}
}
})
Meteor.publishComposite('jobs', {
find: function() {
var user = null;
if (this.userId) {
user = Meteor.users.findOne(this.userId);
if ( user && user.profile && user.profile.isAdmin ) {
return Jobs.find({}, { sort: { createdAt: -1 }});
} else if(user && user._id) {
return Jobs.find({'createdBy': user._id});
}
} else {
return this.ready();
}
},
children: [
{
find: function(job) {
// Find post author. Even though we only want to return
// one record here, we use "find" instead of "findOne"
// since this function should return a cursor.
return Meteor.users.find(
{
_id: job.createdBy
},
{
fields: {
'profile': 1,
'createdAt': 1
}
}
);
}
}
]
});
This is the code I'm using from meteor-publishComposite package. I do not get the profile on my subscriptions for some reason. I can get the user.services to show up, but not the user.profile.