-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
388 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
class DatabaseAlreadyOpenException implements Exception {} | ||
|
||
class UnableToGetDocumentsDirectory implements Exception {} | ||
|
||
class DatabaseIsNotOpen implements Exception {} | ||
|
||
class CouldNotDeleteUser implements Exception {} | ||
|
||
class UserAlreadyExists implements Exception {} | ||
|
||
class CouldNotFindUser implements Exception {} | ||
|
||
class CouldNotDeleteNote implements Exception {} | ||
|
||
class CouldNotFindNotes implements Exception {} | ||
|
||
class CouldNotUpdateNote implements Exception {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,258 @@ | ||
import 'package:cleanifi/services/crud/crud_exceptions.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:sqflite/sqflite.dart'; | ||
import 'package:path_provider/path_provider.dart'; | ||
import 'package:path/path.dart' show join; | ||
|
||
class NotesService { | ||
Database? _db; | ||
|
||
Future<DatabaseNote> updateNote({ | ||
required DatabaseNote note, | ||
required String text, | ||
}) async { | ||
final db = _getDatabaseOrThrow(); | ||
|
||
await getNote(id: note.id); | ||
|
||
final updatesCount = await db.update(noteTable, { | ||
textColumn: text, | ||
isSyncedWithCloudColumn: 0, | ||
}); | ||
|
||
if (updatesCount == 0) { | ||
throw CouldNotUpdateNote; | ||
} else { | ||
return await getNote(id: note.id); | ||
} | ||
} | ||
|
||
Future<Iterable<DatabaseNote>> getAllNotes() async { | ||
final db = _getDatabaseOrThrow(); | ||
final notes = await db.query(noteTable); | ||
|
||
return notes.map((noteRow) => DatabaseNote.fromRow(noteRow)); | ||
} | ||
|
||
Future<DatabaseNote> getNote({required int id}) async { | ||
final db = _getDatabaseOrThrow(); | ||
final notes = await db.query( | ||
noteTable, | ||
limit: 1, | ||
where: "id = ?", | ||
whereArgs: [id], | ||
); | ||
|
||
if (notes.isEmpty) { | ||
throw CouldNotFindNotes(); | ||
} else { | ||
return DatabaseNote.fromRow(notes.first); | ||
} | ||
} | ||
|
||
Future<int> deleteAllNotes() async { | ||
final db = _getDatabaseOrThrow(); | ||
return await db.delete(noteTable); | ||
} | ||
|
||
Future<void> deleteNote({required int id}) async { | ||
final db = _getDatabaseOrThrow(); | ||
final deletedCount = await db.delete( | ||
noteTable, | ||
where: 'id = ?', | ||
whereArgs: [id], | ||
); | ||
if (deletedCount == 0) { | ||
throw CouldNotDeleteNote(); | ||
} | ||
} | ||
|
||
Future<DatabaseNote> createNote({required DatabaseUser owner}) async { | ||
final db = _getDatabaseOrThrow(); | ||
|
||
// make sure owner exists in the database with the correct id | ||
final dbUser = await getUser(email: owner.email); | ||
if (dbUser != owner) { | ||
throw CouldNotFindUser(); | ||
} | ||
|
||
const text = ""; | ||
// create the note | ||
final noteId = await db.insert(noteTable, { | ||
userIdColumn: owner.id, | ||
textColumn: text, | ||
isSyncedWithCloudColumn: 1, | ||
}); | ||
|
||
final note = DatabaseNote( | ||
id: noteId, userId: owner.id, text: text, isSyncedWithCloud: true); | ||
return note; | ||
} | ||
|
||
Future<DatabaseUser> getUser({required String email}) async { | ||
final db = _getDatabaseOrThrow(); | ||
|
||
final results = await db.query( | ||
userTable, | ||
limit: 1, | ||
where: "email = ?", | ||
whereArgs: [email.toLowerCase()], | ||
); | ||
if (results.isEmpty) { | ||
throw CouldNotFindUser; | ||
} else { | ||
return DatabaseUser.fromRow(results.first); | ||
} | ||
} | ||
|
||
Future<DatabaseUser> createUser({required String email}) async { | ||
final db = _getDatabaseOrThrow(); | ||
final results = await db.query( | ||
userTable, | ||
limit: 1, | ||
where: "email = ?", | ||
whereArgs: [email.toLowerCase()], | ||
); | ||
if (results.isNotEmpty) { | ||
throw UserAlreadyExists(); | ||
} | ||
|
||
final userID = await db.insert(userTable, { | ||
emailColumn: email.toLowerCase(), | ||
}); | ||
|
||
return DatabaseUser( | ||
id: userID, | ||
email: email, | ||
); | ||
} | ||
|
||
Future<void> deleteUser({required String email}) async { | ||
final db = _getDatabaseOrThrow(); | ||
final deletedCount = await db.delete( | ||
userTable, | ||
where: "email = ?", | ||
whereArgs: [email.toLowerCase()], | ||
); | ||
if (deletedCount != 1) { | ||
throw CouldNotDeleteUser(); | ||
} | ||
} | ||
|
||
Database _getDatabaseOrThrow() { | ||
final db = _db; | ||
if (db == null) { | ||
throw DatabaseIsNotOpen(); | ||
} else { | ||
return db; | ||
} | ||
} | ||
|
||
Future<void> close() async { | ||
final db = _db; | ||
if (db == null) { | ||
throw DatabaseIsNotOpen(); | ||
} else { | ||
await db.close(); | ||
_db = null; | ||
} | ||
} | ||
|
||
Future<void> open() async { | ||
if (_db != null) { | ||
throw DatabaseAlreadyOpenException(); | ||
} | ||
try { | ||
final docsPath = await getApplicationDocumentsDirectory(); | ||
final dbPath = join(docsPath.path, dbName); | ||
final db = await openDatabase(dbPath); | ||
_db = db; | ||
// create user table | ||
await db.execute(createUserTable); | ||
// create note table | ||
await db.execute(createNoteTable); | ||
} 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"; | ||
|
||
@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) == 1 ? true : false; | ||
|
||
@override | ||
String toString() => | ||
"Note, ID = $id, userId = $userId, isSyncedWithCloud = $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" INTEGER, | ||
"is_synced_with_cloud" INTEGER NOT NULL DEFAULT 0, | ||
FOREIGN KEY("user_id") REFERENCES "user"("id"), | ||
PRIMARY KEY("id" AUTOINCREMENT) | ||
); | ||
'''; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.