Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CouldNotFindUser #13

Open
Nileshsri2022 opened this issue Jul 5, 2024 · 0 comments
Open

CouldNotFindUser #13

Nileshsri2022 opened this issue Jul 5, 2024 · 0 comments

Comments

@Nileshsri2022
Copy link

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_control_structures
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_control_structures
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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant