vandadnp / mynotes-course

This is the GitHub repository of the MyNotes application for the Free Flutter Course (https://www.youtube.com/playlist?list=PL6yRaaP0WPkVtoeNIGqILtRAgd3h2CNpT)
552 stars 198 forks source link

CouldNotFindUser #13

Open Nileshsri2022 opened 4 months ago

Nileshsri2022 commented 4 months ago

notes_view.dart=>

import 'dart:async';

import 'package:flutter/foundation.dart'; import 'package:flutter_application_1/constants/routes.dart'; import 'package:flutter_application_1/services/crud/crud_exceptions.dart'; import 'package:sqflite/sqflite.dart'; import 'package:path_provider/path_provider.dart' show getApplicationDocumentsDirectory, MissingPlatformDirectoryException; import 'package:path/path.dart' show join;

class NotesService { Database? _db; // to create singleton class only one instace of this is created in whole project static final NotesService _shared = NotesService._sharedInstance(); NotesService._sharedInstance() { _notesStreamController = StreamController<List>.broadcast( onListen: () { _notesStreamController.sink.add(_notes); }, ); } factory NotesService() => _shared;

// _notes is cached data where CRUD operation done List _notes = []; // manipulation of data in pipe is through _notesStreamController // if you listen to changes in pipe and do hot reload then error occurs thats why broadcast here which closes the current listening channel // before listening them again you must close the previos one late final StreamController<List> _notesStreamController; Stream<List> get allNotes => _notesStreamController.stream;

Future getOrCreateUser({ required String email, }) async { try { print('inside getorcreate user'); final user = await getUser(email: email); print('User=>$user'); return user; } on CouldNotFindUser { final createdUser = await createUser(email: email); print('Created User=>$createdUser'); return createdUser; } catch (e) { rethrow; } }

// _cachedNotes() show warning because it is private and not been used and other are public Future _cachedNotes() async { final allNotes = await getAllNotes(); _notes = allNotes.toList(); _notesStreamController.add(_notes); }

Future updateNote({ required DatabaseNote note, required String text, }) async { await _ensureDbIsOpen(); final db = _getDatabaseOrThrow(); // make sure note exists await getNote(id: note.id); // update db final updatesCount = await db.update(noteTable, { textColumn: text, isSyncedWithCloudColumn: 0, }); if (updatesCount == 0) { throw CouldNotUpdateNote(); } else { final updatedNote = await getNote(id: note.id); _notes.removeWhere((note) => note.id == updatedNote.id); _notes.add(updatedNote); _notesStreamController.add(_notes); return updatedNote; } }

Future<Iterable> getAllNotes() async { await _ensureDbIsOpen(); final db = _getDatabaseOrThrow(); final notes = await db.query(noteTable); return notes.map((noteRow) => DatabaseNote.fromRow(noteRow)); }

Future getNote({required int id}) async { await _ensureDbIsOpen(); final db = _getDatabaseOrThrow(); final notes = await db.query( noteTable, limit: 1, where: 'id=?', whereArgs: [id], ); if (notes.isEmpty) { throw CouldNotFindNote(); } else { final note = DatabaseNote.fromRow(notes.first); _notes.removeWhere((note) => note.id == id); _notes.add(note); _notesStreamController.add(_notes); return note; } }

Future deleteAllNote() async { await _ensureDbIsOpen(); final db = _getDatabaseOrThrow(); final numberOfDeletions = await db.delete(noteTable); _notes = []; _notesStreamController.add(_notes); return numberOfDeletions; }

Future deleteNote({required int id}) async { await _ensureDbIsOpen(); final db = _getDatabaseOrThrow(); final deletedCount = await db.delete( noteTable, where: 'id=?', whereArgs: [id], ); if (deletedCount == 0) { throw CouldNotDeleteNote(); } else { // safeguard added please check final countBefore = _notes.length; _notes.removeWhere((note) => note.id == id); if (_notes.length != countBefore) { _notesStreamController.add(_notes); } } }

Future createNote({required DatabaseUser owner}) async { await _ensureDbIsOpen(); final db = _getDatabaseOrThrow();

// make sure owner exists in the database with correct id
await getUser(email: owner.email);

const text = '';

// Check if the user_id is correct
print('Creating note for user_id: ${owner.id}');

// create the note
final noteId = await db.insert(noteTable, {
  userIdColumn: owner.id,
  textColumn: text,
  isSyncedWithCloudColumn: 0, // Assuming default value for new note
});

final note = DatabaseNote(
  id: noteId,
  userId: owner.id,
  text: text,
  isSyncedWithCloud: true,
);

_notes.add(note);
_notesStreamController.add(_notes);

return note;

}

Future getUser({required String email}) async { await _ensureDbIsOpen(); final db = _getDatabaseOrThrow(); print('Database=>$db'); print('Fetching user for email: $email'); final results = await db.query( userTable, limit: 1, where: 'email=?', whereArgs: [email.toLowerCase()], ); print('Results for user query: $results'); if (results.isEmpty) { print('Could not find user with email: $email'); throw CouldNotFindUser(); } else { final row = DatabaseUser.fromRow(results.first); print('Found user: $row'); return row; } }

Future createUser({required String email}) async { await _ensureDbIsOpen(); final db = _getDatabaseOrThrow(); final results = await db.query( userTable, limit: 1, where: 'email=?', whereArgs: [email.toLowerCase()], ); print('Results in createUser =>$results'); if (results.isNotEmpty) { throw UserAlreadyExists(); } final userId = await db.insert(userTable, { emailColumn: email.toLowerCase(), }); print('userId=>$userId'); return DatabaseUser( id: userId, email: email, ); }

Future deleteUser({required String email}) async { await _ensureDbIsOpen(); final db = _getDatabaseOrThrow(); final deletedCount = await db.delete( userTable, where: 'email=?', whereArgs: [email.toLowerCase()], ); if (deletedCount != 1) { throw CouldNotDeleteUser(); } }

Database _getDatabaseOrThrow() { final db = _db; // print('Database\n'); print(db?.database); // print('Database:$db'); if (db == null) { throw DatabaseIsNotOpen(); } else { return db; } }

Future close() async { final db = _db; if (db == null) { throw DatabaseIsNotOpen(); } else { await db.close(); _db = null; } }

Future _ensureDbIsOpen() async { if (_db == null) { await open(); } } // Add this function to handle database migrations // Future _migrate(Database db, int oldVersion, int newVersion) async { // if (oldVersion < 2) { // // Assuming version 2 introduces the is_synced_with_cloud column // await db.execute('ALTER TABLE note ADD COLUMN is_synced_with_cloud INTEGER NOT NULL DEFAULT 0'); // } // }

Future _ensureColumnExists( Database db, String table, String column) async { final result = await db.rawQuery('PRAGMA table_info($table)'); print('result=>$result'); final columnExists = result.any((element) => element['name'] == column); print('column exist=>$columnExists'); if (!columnExists) { await db.execute( 'ALTER TABLE $table ADD COLUMN $column INTEGER NOT NULL DEFAULT 0'); } }

Future open() async { if (_db != null) { throw DatabaseAlreadyOpenException(); } try { final docsPath = await getApplicationDocumentsDirectory(); final dbPath = join(docsPath.path, dbName); final db = await openDatabase(dbPath, version: 1);

  _db = db;

  // Ensure tables exist
 try{ 
  await db.execute(createUserTable);
  await db.execute(createNoteTable);
  }
  catch(err){

print('SOmething wrong happend $err'); }

  // Check and add column if not exists
  await _ensureColumnExists(db, noteTable, isSyncedWithCloudColumn);

  await _cachedNotes();

} on MissingPlatformDirectoryException {
  throw UnableToGetDocumentsDirectory();
}

} }

@immutable class DatabaseUser { final int id; final String email;

const DatabaseUser({required this.id, required this.email}); DatabaseUser.fromRow(Map<String, Object?> map) : id = map[idColumn] as int, email = map[emailColumn] as String;

@override String toString() => 'Person,ID =$id, email=$email';

// allow you to change the behavior of input parameter so that they do not confirm the signature of parameter in super class @override bool operator ==(covariant DatabaseUser other) => id == other.id;

@override int get hashCode => id.hashCode; }

class DatabaseNote { final int id; final int userId; final String text; final bool isSyncedWithCloud;

DatabaseNote( {required this.id, required this.userId, required this.text, required this.isSyncedWithCloud}); DatabaseNote.fromRow(Map<String, Object?> map) : id = map[idColumn] as int, userId = map[userIdColumn] as int, text = map[textColumn] as String, isSyncedWithCloud = (map[isSyncedWithCloudColumn] as int? ?? 0) == 1; @override String toString() => 'Note,ID=$id, userId= $userId, isSyncedWithCloudColumn=$isSyncedWithCloud,text= $text'; @override bool operator ==(covariant DatabaseNote other) => id == other.id;

@override int get hashCode => id.hashCode; }

const dbName = 'notes.db'; const noteTable = 'note'; const userTable = 'user'; const idColumn = 'id'; const emailColumn = 'email'; const userIdColumn = 'user_id'; const textColumn = 'text'; const isSyncedWithCloudColumn = 'is_synced_with_cloud'; const createUserTable = '''CREATE TABLE IF NOT EXISTS "user" ( "id" INTEGER NOT NULL, "email" TEXT NOT NULL UNIQUE, PRIMARY KEY("id" AUTOINCREMENT) );'''; const createNoteTable = '''CREATE TABLE IF NOT EXISTS "note" ( "id" INTEGER NOT NULL, "user_id" INTEGER NOT NULL, "text" TEXT, "is_synced_with_cloud" INTEGER NOT NULL DEFAULT 0, FOREIGN KEY("user_id") REFERENCES "user"("id"), PRIMARY KEY("id" AUTOINCREMENT) );''';

firebase_authProvider.dart

import 'package:firebase_auth/firebase_auth.dart' show FirebaseAuth, FirebaseAuthException; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter_application_1/firebase_options.dart'; import 'package:flutter_application_1/services/auth/auth_provider.dart';

import 'package:flutter_application_1/services/auth/auth_user.dart'; import 'package:flutter_application_1/services/auth/auth_exceptions.dart';

class FirebaseAuthProvider implements AuthProvider { // check2 @override Future initialize() async { await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); }

@override Future createUser({ required String email, required String password, }) async { try { await FirebaseAuth.instance.createUserWithEmailAndPassword( email: email, password: password, ); final user = currentUser; if (user != null) { print('User created => $user'); return user; } // ignore: curly_braces_in_flow_controlstructures else { throw UserNotFoundAuthException(); } } on FirebaseAuthException catch (e) { if (e.code == 'weak-password') { throw WeakPasswordAuthException(); } else if (e.code == 'email-already-in-use') { throw EmailAlreadyInUseAuthException(); } else if (e.code == 'invalid-email') { throw InvalidEmailAuthException(); } else { throw GenericAuthException(); } } catch () { throw GenericAuthException(); } }

@override AuthUser? get currentUser { final user = FirebaseAuth.instance.currentUser; print('current user =>$user'); if (user != null) { print('convert fibase user to AuthUser=>${AuthUser.fromFirebase(user)}'); return AuthUser.fromFirebase(user); } else { return null; } }

@override Future logIn({ required String email, required String password, }) async { try { await FirebaseAuth.instance .signInWithEmailAndPassword(email: email, password: password); final user = currentUser; print('login user=> $user'); if (user != null) { return user; } // ignore: curly_braces_in_flow_controlstructures else { throw UserNotFoundAuthException(); } } on FirebaseAuthException catch (e) { if (e.code == 'user-not-found') { throw UserNotFoundAuthException(); } else if (e.code == 'wrong-password') { throw WrongPasswordAuthException(); } else { throw GenericAuthException(); } } catch () { throw GenericAuthException(); } }

@override Future logOut() async { final user = FirebaseAuth.instance.currentUser; print('logout user=>$user'); if (user != null) { await FirebaseAuth.instance.signOut(); } else { throw UserNotLoggedInAuthException(); } }

@override Future sendEmailVerification() async { final user = FirebaseAuth.instance.currentUser; print('Send email verification=>$user'); if (user != null) { await user.sendEmailVerification(); } else { throw UserNotLoggedInAuthException(); } } }

notes_view.dart

import 'package:flutter/material.dart'; import 'package:flutter_application_1/services/auth/auth_service.dart'; import 'package:flutter_application_1/services/crud/notes_service.dart';

class NewNoteView extends StatefulWidget { const NewNoteView({super.key});

@override State createState() => _NewNoteViewState(); }

class _NewNoteViewState extends State { @override initState() { _notesService = NotesService(); _textController = TextEditingController(); super.initState(); }

DatabaseNote? _note; late final NotesService _notesService; late final TextEditingController _textController;

void _textControllerListener() async { final note = _note; print('note=>$note'); if (note == null) { return; } final text = _textController.text; print('Text=>$text'); await _notesService.updateNote( note: note, text: text, ); }

void _setupTextControllerListener() { _textController.removeListener(_textControllerListener); _textController.addListener(_textControllerListener); } @override void dispose() { _deleteNoteIfTextIsEmpty(); _saveNoteIfTextNotEmpty(); _textController.dispose(); super.dispose(); } // step1 Future createNewNote() async { print('inside createNewNote()'); final existingNote = _note; print('existing note=>$existingNote'); if (existingNote != null) { return existingNote; } // if currentUser is null then we want app to crash so ! it is safe to use final currentUser = AuthService.firebase().currentUser!; final email = currentUser.email!; print('Email=>$email'); final owner = await _notesService.getUser(email: email); print('Owner=>$owner'); final note = await _notesService.createNote(owner: owner); print('note here=>$note'); return note; }

void _deleteNoteIfTextIsEmpty() async { final note = _note; if (_textController.text.isEmpty && note != null) { _notesService.deleteNote(id: note.id); } }

void _saveNoteIfTextNotEmpty() async { final note = _note; final text = _textController.text; if (note != null && text.isNotEmpty) { await _notesService.updateNote( note: note, text: text, ); } }

@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('New Note'), ), body: FutureBuilder( future: createNewNote(), builder: (context, snapshot) { switch (snapshot.connectionState) { case ConnectionState.done: // print('Data:$snapshot.data'); if (snapshot.hasData) { _note = snapshot.data as DatabaseNote; } print('new_note_view=>$_note'); _setupTextControllerListener(); return TextField( controller: _textController, keyboardType: TextInputType.multiline, maxLines: null, decoration: const InputDecoration( hintText: 'Start typing your text...', ), ); default: return const CircularProgressIndicator(); } }, ), ); } }

please find out the error it show Results=>[] and stuck in debug mode where CouldNotFindUser