I'm trying to call data api when a firestore document is changed but I'm getting error: Error type 'null' is not a subtype of 'bool' with riverpod. What am I doing wrong? Thanks
final firebaseAuthProvider = Provider<FirebaseAuth>((ref) => FirebaseAuth.instance);
final authStateChangesProvider = StreamProvider<User?>((ref) => ref.watch(firebaseAuthProvider).authStateChanges());
final databaseProvider = Provider<DatabaseService?>((ref) {
final auth = ref.watch(authStateChangesProvider);
if (auth.asData?.value?.uid != null) {
return DatabaseService(uid: auth.asData!.value!.uid);
}
return null;
});
final firestoreDatabaseProvider = Provider<FirestoreDatabase?>((ref) {
final auth = ref.watch(authStateChangesProvider);
if (auth.asData?.value?.uid != null) {
return FirestoreDatabase(uid: auth.asData!.value!.uid);
}
return null;
});
final apiDataProvider2 = FutureProvider.autoDispose<ProfileUser?>((ref) {
final authUser = ref.watch(authStateChangesProvider);
final user = authUser.value;
if (user != null) {
final userValue = ref.watch(firestoreDatabaseProvider);
final dbProvider = ref.watch(databaseProvider);
var profileUser;
userValue?.getFirestoreUser().listen((event) {
profileUser = dbProvider!.getProfile();
});
if (profileUser) {
return profileUser;
} else {
return Future.value(null);
}
}
});
The problem is with profileUser; It is being returned before
var profileUser;
userValue?.getFirestoreUser().listen((event) {
profileUser = dbProvider!.getProfile();
});
if (profileUser) {
return profileUser;
} else {
return Future.value(null);
}
Try this instead:
var profileUser = (await userValue?.getFirestoreUser().first)!.getProfile();
if (profileUser) {
return profileUser;
} else {
return Future.value(null);
}
Related
In Firestore there is a collection "students" which has two fields "date" and "onBus". I want to update these two fields everyday automatically based on the day date,
like "everyday reset "on_bus" to "0" and set "date" to today date for all students".
How can I do this for flutter app?
i try the following code but it does not work
Future<bool> resetStudentOnBus() async {
var now = DateTime.now();
var formatter = DateFormat('yyyy-MM-dd');
String formattedDate = formatter.format(now);
for (var doc in FirebaseFirestore.instance.collection('students').docs) {
if (doc?.date != formattedDate && doc?.onBus == "1") {
try {
db.collection(FirebaseConstants.pathStudentUserCollection)
.doc()
.update({"on_bus": "0", "date": formattedDate}).then((value) {
print("success");
return true;
}).catchError((onError) {
print(onError);
return false;
});
return true;
} catch (e) {
return false;
}
}
else {
return false;
}
}
}
I just want to say if the document exists setState(the first one) else setState(the second one). I'm new in flutter so please don't hate :)
Thanks for helping!
Future<String> TerminBesetztOderFrei(String Friseur, String Tag, String Uhrzeit) async {
await Firestore.instance.collection("$Friseur/1/$Tag/1/$Uhrzeit")
.getDocuments()
.then((querySnapshot) {
querySnapshot.documents.forEach((result) {
print(result.exists);
setState(() {
terminText = "Termin nicht verfügbar!";
terminTextFarbe = Colors.red;
buttonVisible = false;
});
});
});
setState(() {
if(nameController.text != "" && telController.text != "") {
terminText = "Termin verfügbar!";
terminTextFarbe = Colors.green;
buttonVisible = true;
} else {
terminText = "Termin verfügbar! Bitte Name und Telefon eingeben!";
terminTextFarbe = Colors.green;
buttonVisible = false;
}
});
}
If you have the document Id:
final docSnapshot = await Firestore.instance
.collection("$Friseur/1/$Tag/1/$Uhrzeit")
.document(${doc_id_here})
.get();
if(docSnapshot.exists) {
setState(...)
}
else {
setState(...)
}
If you haven't
final querySnapshot = await Firestore.instance
.collection("$Friseur/1/$Tag/1/$Uhrzeit")
.getDocuments();
if(querySnapshot.documents.isNotEmpty) {
setState(...)
}
So i have a sessionStore:
class SessionStore: ObservableObject {
var handle: AuthStateDidChangeListenerHandle?
#Published var isLoggedIn = false
#Published var userInSession: User?
func listenAuthenticationState() {
handle = Auth.auth().addStateDidChangeListener({(auth, user) in
if let user = user {
let firestoreGetUser = Firestore.firestore().collection("users").document(user.uid)
firestoreGetUser.getDocument{(document, error) in
if let dict = document?.data() {
guard let decodedUser = try? User.init(fromDictionary: dict) else { return }
self.userInSession = decodedUser
print("decoded user = \(decodedUser)")
}
}
self.isLoggedIn = true
print("user logged in")
} else {
self.isLoggedIn = false
self.userInSession = nil
print("no one logged in")
}
})
}
func logout() {
do {
try Auth.auth().signOut()
} catch {
}
}
func unbind() {
if let handle = handle {
Auth.auth().removeStateDidChangeListener(handle)
}
}
deinit {
unbind()
}
}
Its working as expected, I am able to sign in etc.
I have the following to pull the current user data:
import Foundation
import Firebase
import FirebaseAuth
import FirebaseFirestore
class ProfileViewModel: ObservableObject {
var uid: String = ""
var email: String = ""
var username: String = ""
var profileURL: String = ""
var bio: String = ""
var occupation: String = ""
var city: String = ""
func LoadAUser(userId: String) {
Firestore.firestore().collection("users").document(userId).getDocument{(snapshot, error) in
guard let snap = snapshot else {
print("error fetching data")
return
}
let dict = snap.data()
guard let decodedUser = try? User.init(fromDictionary: dict!) else { return }
print("decoded user - load user - \(decodedUser)")
}
}
}
In my view im trying to call it like:
import SwiftUI
struct ProfileView: View {
#EnvironmentObject var session: SessionStore
#ObservedObject var profileViewModel = ProfileViewModel()
func loadUserData() {
profileViewModel.LoadAUser(userId: session.userInSession!.uid)
}
var body: some View {
VStack {
Text("Edit Profile")
.fontWeight(.semibold)
.font(.system(.title, design: .rounded))
.foregroundColor(Color("startColor"))
Spacer()
VStack(alignment: .leading) {
Text("view")
}.padding()
.onAppear(perform: loadUserData)
}
}
struct ProfileView_Previews: PreviewProvider {
static var previews: some View {
ProfileView()
}
}
Im using .onAppear(perform: loadUserData) which is causing an issue - Thread1: Fatal error: Unexpectedly found nil while unwrapping
I also tried:
init() {
profileViewModel.LoadAUser(userId: session.userInSession!.uid)
}
But this also causes the same error.
The thing is I should only be able to get to this view if I'm logged in as this already works:
struct InitialView: View {
#EnvironmentObject var session: SessionStore
func listen() {
session.listenAuthenticationState()
}
var body: some View {
Group {
if session.isLoggedIn {
MainView()
} else {
NavigationView {
SignUpView()
}
}
}.onAppear(perform: listen)
}
}
I have an initialView()
struct InitialView: View {
#EnvironmentObject var session: SessionStore
func listen() {
session.listenAuthenticationState()
}
var body: some View {
Group {
if session.isLoggedIn {
MainView()
} else {
NavigationView {
SignUpView()
}
}
}.onAppear(perform: listen)
}
}
which takes you to the MainView() which has tabs to control which screen you can navigate to, then from here i can go to ProfileView()
Anyway by the logic of provided code it is more correct to activate isLoggedIn in
let firestoreGetUser = Firestore.firestore().collection("users").document(user.uid)
firestoreGetUser.getDocument{(document, error) in
if let dict = document?.data() {
guard let decodedUser = try? User.init(fromDictionary: dict) else { return }
self.userInSession = decodedUser
print("decoded user = \(decodedUser)")
self.isLoggedIn = true // << here !!
print("user logged in")
}
}
So whats worked for me is passing in Auth instead of session data:
func loadUserData() {
profileViewModel.LoadAUser(userId: Auth.auth().currentUser!.uid)
}
I successfully uploaded the image to firebase storage but I'm having trouble to store image url in firestore.
I received an error on console when trying to view the image:
core.js:6462 WARNING: sanitizing unsafe URL value
C:\fakepath\coke.jpg (see http://g.co/ng/security#xss)
GET unsafe:C:\fakepath\coke.jpg net::ERR_UNKNOWN_URL_SCHEME
Below is the html code: (details.page.html)
<!-- PRODUCT PICTURE INPUT -->
<ion-item>
<ion-label position="stacked">Product Picture</ion-label>
<ion-input accept="image/*" type="file" name="productPics" formControlName="productPics" (change)="showPreview($event)"></ion-input>
</ion-item>
Below is the typescript code: (details.page.ts)
ngOnInit() {
this.productForm = this.fb.group({
productPics: new FormControl('', Validators.compose([
Validators.required,
])),
});
this.resetForm();
}
showPreview(event: any) {
if (event.target.files && event.target.files[0]) {
const reader = new FileReader();
reader.onload = (e: any) => this.imgSrc = e.target.result;
reader.readAsDataURL(event.target.files[0]);
this.selectedImage = event.target.files[0];
} else {
this.imgSrc = "./assets/default_image.jpg";
this.selectedImage = null;
}
}
async saveProduct(formValue) {
this.isSubmitted = true;
this.product.userId = this.authService.getAuth().currentUser.uid;
if (this.productId) {
try {
this.product.createdAt = new Date().getTime();
console.log('product add');
console.log(this.productForm.value);
var filePath = `${formValue.productCategory}/${this.selectedImage.name}${new Date().getTime()}`;
const fileRef = this.storage.ref(filePath);
this.storage.upload(filePath, this.selectedImage).snapshotChanges().pipe(
finalize(() => {
fileRef.getDownloadURL().subscribe((url) => {
formValue['productPics'] = url;
this.service.insertImageDetails(formValue);
this.resetForm();
})
})
).subscribe();
await this.productService.addProduct(this.product);
await this.loading.dismiss();
this.navCtrl.navigateBack('/vendor-tabs/home-vendor');
} catch (error) {
console.log('product dont add');
this.presentToast('Error trying to save');
}
}
}
Below is the service: (product.service.ts)
private productsCollection: AngularFirestoreCollection<Product>;
addProduct(product: Product) {
return this.productsCollection.add(product);
}
getProduct(id: string) {
return this.productsCollection.doc<Product>(id).valueChanges();
}
updateProduct(id: string, product: Product) {
return this.productsCollection.doc<Product>(id).update(product);
}
deleteProduct(id: string) {
return this.productsCollection.doc(id).delete();
}
insertImageDetails(image) {
this.productsCollection.add(image);
}
I want to write a function loggedIn() in file auth.service.ts to check the token from local storage, and then verify it with firebase/php-jwt in server side. But the code in Typescript gives an infinite loop. Here is my code:
auth.service.ts
loggedIn(){
const token: string = localStorage.getItem('id_token');
if (token == null) {
return false;
}
else {
const subs = this.http.post('http://localhost/url/to/myPHP.php', {"token":token})
.map(res=>res.json()).subscribe(data=>{
if(data.valid){
this.valid = true;
} else {
this.valid = false;
}
},
err=>console.log(err));
if (this.valid){
console.log("Valid");
return true;
} else {
console.log("Invalid");
return false;
}
}
}
Given token: valid token.
Result: give no error but infinite console.log of 'Valid' as well as return true, until the Apache down.
Given token: invalid token
Result: give no error but infinite console.log of 'Invalid' as well as return false, until the Apache down.
What I have tried:
loggedIn(){
const token: string = localStorage.getItem('id_token');
if (token == null) {
return false;
}
else {
const subs = this.http.post('http://localhost/url/to/myPHP.php', {"token":token})
.map(res=>res.json()).subscribe(data=>{
if(data.valid){
this.valid = true;
} else {
this.valid = false;
}
},
err=>console.log(err));
if (this.valid){
console.log("Valid");
console.log(this.valid);
return true;
} else {
console.log("Invalid");
console.log(this.valid);
return false;
}
subs.unsubscribe();
return true;
}
}
The line subs.unsubscribe(); did stop the loop, yet it will literally unsubscribe the Observable<Response> and the code inside .subscribe() will not run. Please help.
Edit: Usage of loggedIn()
*ngIf="authService.loggedIn()
for 4 times in navbar component.
Inside auth.guard.ts
canActivate(){
if (this.authService.validToken){
return true;
} else {
this.router.navigate(['/login']);
return false;
}
}
In app.module.ts
{path:'profile', component:ProfileComponent, canActivate:[AuthGuard]}
I finally solve the problem. The problem with my code: .subscribe() is an async call (which actually scheduled for last/later execution). This is the situation:ngOnInit() : which located in component < ts > file
ngOnInit(){
this.authService.loggedIn();
console.log("10");
}
loggedIn() : which located in auth.service.ts file
loggedIn(){
const token: string = localStorage.getItem('id_token');
if (token == null) {
return false;
}
else {
const subs = this.http.post('http://localhost/url/to/myPHP.php', {"token":token})
.map(res=>res.json()).subscribe(data=>{
if(data.valid){
console.log("1");
} else {
console.log("2");
}
console.log("3")
},
err=>console.log(err));
}
}
and then the result will be :
1013 which mean, anything you do inside the subscribe will change only after you need it (in many cases). So we need to do whatever we need to, inside the subscribe(), and fire it only when needed. In my case, I want to fire it if any changes apply to token inside local storage.
This is my solution
As I want it always check with the token. I used DoCheck()
Inside auth.service.ts
verifyToken(authToken) {
const body = {token:authToken};
return this.http.post('http://localhost/url/to/myPHP.php',body)
.map(res => res.json());
}
tokenExist(): boolean {
const token: string = localStorage.getItem('id_token');
if (token == null) {
return false;
} else {
return true;
}
}
Inside navbar.component.ts
ngOnInit() {
this.curToken = localStorage.getItem('id_token');
this.loggedIn(this.curToken);
}
ngDoCheck(){
if (this.curToken != localStorage.getItem('id_token')){
this.curToken = localStorage.getItem('id_token');
this.loggedIn(this.curToken);
}
}
loggedIn(token){
if(this.authService.tokenExist()){
this.authService.verifyToken(token).subscribe(data=>{
if(data.valid){
this.validLogin = true;
} else {
console.log("Invalid Token");
this.validLogin = false;
}
});
} else {
console.log("Token Missing");
this.validLogin = false;
}
}
Of course, don't forget to implement DoCheck in the line
export class NavbarComponent implements OnInit, DoCheck