Skip to content

Commit

Permalink
persistent storage for circles #12
Browse files Browse the repository at this point in the history
  • Loading branch information
LGro committed May 15, 2024
1 parent 509b47f commit 60389bb
Show file tree
Hide file tree
Showing 11 changed files with 247 additions and 31 deletions.
11 changes: 11 additions & 0 deletions lib/data/providers/persistent_storage/base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import '../../models/coag_contact.dart';
import '../../models/contact_update.dart';
import '../../models/profile_sharing_settings.dart';

abstract class PersistentStorage {
Future<CoagContact> getContact(String coagContactId);
Expand All @@ -20,4 +21,14 @@ abstract class PersistentStorage {
Future<List<ContactUpdate>> getUpdates();

Future<void> addUpdate(ContactUpdate update);

Future<Map<String, String>> getCircles();
Future<void> updateCircles(Map<String, String> circles);

Future<Map<String, List<String>>> getCircleMemberships();
Future<void> updateCircleMemberships(
Map<String, List<String>> circleMemberships);

Future<ProfileSharingSettings> getProfileSharingSettings();
Future<void> updateProfileSharingSettings(ProfileSharingSettings settings);
}
55 changes: 47 additions & 8 deletions lib/data/providers/persistent_storage/hive.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart';
import '../../models/coag_contact.dart';
import '../../models/contact_update.dart';
import '../../models/profile_sharing_settings.dart';
import 'base.dart';

part 'hive.g.dart';
Expand Down Expand Up @@ -91,18 +92,56 @@ class HiveStorage extends PersistentStorage {
Future<void> removeContact(String coagContactId) async =>
(await _lazyGetSettingsBox()).delete(coagContactId);

Future<Box<String>> _lazyGetUpdatesBox() async =>
Hive.openBox('hive_coag_updates_box');
Future<Box<List<String>>> _lazyGetUpdatesBox() async =>
Hive.openBox<List<String>>('hive_coag_updates_box');

@override
Future<void> addUpdate(ContactUpdate update) async =>
throw UnimplementedError();
// (await _lazyGetUpdatesBox())
// .put(contact.coagContactId, json.encode(update.toJson()));
Future<void> addUpdate(ContactUpdate update) async {
throw UnimplementedError();
}

@override
Future<List<ContactUpdate>> getUpdates() async => _lazyGetUpdatesBox()
.then((box) async => box.get('updates'))
.then((updates) => (updates ?? [])
.map((u) =>
ContactUpdate.fromJson(json.decode(u) as Map<String, dynamic>))
.toList());

@override
Future<Map<String, List<String>>> getCircleMemberships() {
// TODO: implement getCircleMemberships
throw UnimplementedError();
}

@override
Future<Map<String, String>> getCircles() {
// TODO: implement getCircles
throw UnimplementedError();
}

@override
Future<ProfileSharingSettings> getProfileSharingSettings() {
// TODO: implement getProfileSharingSettings
throw UnimplementedError();
}

@override
Future<void> updateCircleMemberships(
Map<String, List<String>> circleMemberships) {
// TODO: implement updateCircleMemberships
throw UnimplementedError();
}

@override
Future<void> updateCircles(Map<String, String> circles) {
// TODO: implement updateCircles
throw UnimplementedError();
}

@override
Future<List<ContactUpdate>> getUpdates() {
// TODO: implement getUpdates
Future<void> updateProfileSharingSettings(ProfileSharingSettings settings) {
// TODO: implement updateProfileSharingSettings
throw UnimplementedError();
}
}
38 changes: 38 additions & 0 deletions lib/data/providers/persistent_storage/shared_preferences.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:uuid/uuid.dart';

import '../../models/coag_contact.dart';
import '../../models/contact_update.dart';
import '../../models/profile_sharing_settings.dart';
import 'base.dart';

const String mostRecentSchemaVersion = '1';
Expand Down Expand Up @@ -80,4 +81,41 @@ class SharedPreferencesStorage extends PersistentStorage {
await (await SharedPreferences.getInstance())
.setString('updates', json.encode((await getUpdates())..add(update)));
}

@override
Future<Map<String, List<String>>> getCircleMemberships() {
// TODO: implement getCircleMemberships
throw UnimplementedError();
}

@override
Future<Map<String, String>> getCircles() {
// TODO: implement getCircles
throw UnimplementedError();
}

@override
Future<ProfileSharingSettings> getProfileSharingSettings() {
// TODO: implement getProfileSharingSettings
throw UnimplementedError();
}

@override
Future<void> updateCircleMemberships(
Map<String, List<String>> circleMemberships) {
// TODO: implement updateCircleMemberships
throw UnimplementedError();
}

@override
Future<void> updateCircles(Map<String, String> circles) {
// TODO: implement updateCircles
throw UnimplementedError();
}

@override
Future<void> updateProfileSharingSettings(ProfileSharingSettings settings) {
// TODO: implement updateProfileSharingSettings
throw UnimplementedError();
}
}
101 changes: 91 additions & 10 deletions lib/data/providers/persistent_storage/sqlite.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,28 @@
import 'dart:async';
import 'dart:convert';

import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:path/path.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sqflite/sqflite.dart';

import '../../models/coag_contact.dart';
import '../../models/contact_update.dart';
import '../../models/profile_sharing_settings.dart';
import 'base.dart';

Future<Database> getDatabase() async => openDatabase(
join(await getDatabasesPath(), 'contacts.db'),
onCreate: (db, version) => db.execute(
'CREATE TABLE contacts(id TEXT PRIMARY KEY, contactJson TEXT)'),
onCreate: (db, version) {
db
..execute(
'CREATE TABLE contacts(id TEXT PRIMARY KEY, contactJson TEXT)')
// TODO: Consider using specific columns for update attributes instead of one json string
..execute(
'CREATE TABLE updates(id INTEGER PRIMARY KEY, updateJson TEXT)')
..execute(
'CREATE TABLE settings(id TEXT PRIMARY KEY, settingsJson TEXT)');
},
version: 1,
);

Expand Down Expand Up @@ -77,14 +87,85 @@ class SqliteStorage extends PersistentStorage {
(await SharedPreferences.getInstance()).remove(coagContactId);

@override
Future<void> addUpdate(ContactUpdate update) {
// TODO: implement addUpdate
throw UnimplementedError();
}
Future<void> addUpdate(ContactUpdate update) async =>
getDatabase().then((db) async =>
db.insert('updates', {'updateJson': json.encode(update.toJson())}));

@override
Future<List<ContactUpdate>> getUpdates() {
// TODO: implement getUpdates
throw UnimplementedError();
}
Future<List<ContactUpdate>> getUpdates() async => getDatabase()
.then((db) async => db.query('updates', columns: ['updateJson']))
.then((results) => results
.map((r) => ContactUpdate.fromJson(
json.decode(r['updateJson']! as String) as Map<String, dynamic>))
.asList());

@override
Future<Map<String, List<String>>> getCircleMemberships() async => getDatabase()
.then((db) async => db.query('settings',
columns: ['settingsJson'],
where: 'id = ?',
whereArgs: ['circleMemberships'],
limit: 1))
.then((results) => (results.isEmpty)
? {}
: (json.decode(results.first['settingsJson']! as String)
as Map<String, dynamic>)
.map((key, value) =>
MapEntry(key, (value is List) ? List<String>.from(value) : <String>[])));

@override
Future<Map<String, String>> getCircles() async => getDatabase()
.then((db) async => db.query('settings',
columns: ['settingsJson'],
where: 'id = ?',
whereArgs: ['circles'],
limit: 1))
.then((results) => (results.isEmpty)
? {}
: ((json.decode(results.first['settingsJson']! as String)
as Map<String, dynamic>)
.map((key, value) =>
MapEntry(key, (value is String) ? value : '???'))));

@override
Future<ProfileSharingSettings> getProfileSharingSettings() async =>
getDatabase()
.then((db) async => db.query('settings',
columns: ['settingsJson'],
where: 'id = ?',
whereArgs: ['profileSharingSettings'],
limit: 1))
.then((results) => (results.isEmpty)
? const ProfileSharingSettings()
: ProfileSharingSettings.fromJson(
json.decode(results.first['settingsJson']! as String)
as Map<String, dynamic>));

@override
Future<void> updateCircleMemberships(
Map<String, List<String>> circleMemberships) async =>
getDatabase().then((db) async => db.insert(
'settings',
{
'id': 'circleMemberships',
'settingsJson': json.encode(circleMemberships)
},
conflictAlgorithm: ConflictAlgorithm.replace));

@override
Future<void> updateCircles(Map<String, String> circles) async =>
getDatabase().then((db) async => db.insert(
'settings', {'id': 'circles', 'settingsJson': json.encode(circles)},
conflictAlgorithm: ConflictAlgorithm.replace));

@override
Future<void> updateProfileSharingSettings(
ProfileSharingSettings settings) async =>
getDatabase().then((db) async => db.insert(
'settings',
{
'id': 'profileSharingSettings',
'settingsJson': json.encode(settings.toJson())
},
conflictAlgorithm: ConflictAlgorithm.replace));
}
18 changes: 14 additions & 4 deletions lib/data/repositories/contacts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,11 @@ class ContactsRepository {
// Load profile contact ID from persistent storage
profileContactId = await persistentStorage.getProfileContactId();

// TODO: Initialize circles, circle memberships and sharing settings from persistent storage
// Initialize circles, circle memberships and sharing settings from persistent storage
_profileSharingSettings =
await persistentStorage.getProfileSharingSettings();
_circleMemberships = await persistentStorage.getCircleMemberships();
_circles = await persistentStorage.getCircles();

// Load updates from persistent storage
updates = await persistentStorage.getUpdates();
Expand Down Expand Up @@ -232,18 +236,24 @@ class ContactsRepository {
// }
}

void updateCircleMemberships(Map<String, List<String>> memberships) {
Future<void> updateCircleMemberships(
Map<String, List<String>> memberships) async {
_circleMemberships = memberships;
_circlesStreamController.add(null);
await persistentStorage.updateCircleMemberships(memberships);
}

void updateCircles(Map<String, String> circles) {
Future<void> updateCircles(Map<String, String> circles) async {
_circles = circles;
_circlesStreamController.add(null);
await persistentStorage.updateCircles(circles);
}

void setProfileSharingSettings(ProfileSharingSettings settings) {
Future<void> setProfileSharingSettings(
ProfileSharingSettings settings) async {
_profileSharingSettings = settings;
await persistentStorage.updateProfileSharingSettings(settings);
// TODO: Trigger update of all shareprofile
}

ProfileSharingSettings getProfileSharingSettings() => _profileSharingSettings;
Expand Down
4 changes: 2 additions & 2 deletions lib/ui/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';

import '../data/providers/distributed_storage/dht.dart';
import '../data/providers/persistent_storage/shared_preferences.dart';
import '../data/providers/persistent_storage/sqlite.dart';
import '../data/providers/system_contacts/system_contacts.dart';
import '../data/repositories/contacts.dart';
import '../tick.dart';
Expand Down Expand Up @@ -112,7 +112,7 @@ class CoagulateApp extends StatelessWidget {
return BackgroundTicker(
child: RepositoryProvider.value(
value: ContactsRepository(
SharedPreferencesStorage(), VeilidDhtStorage(), SystemContacts()),
SqliteStorage(), VeilidDhtStorage(), SystemContacts()),
child: MaterialApp.router(
title: 'Coagulate',
theme: ThemeData(
Expand Down
2 changes: 1 addition & 1 deletion lib/ui/locations/page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ class _LocationFormState extends State<LocationForm> {
.contactsRepository
.profileContactId!),
// TODO: add callback
callback: (circles) => ()),
callback: (circles) async => {throw Error()}),
const SizedBox(height: 24),
if (_state.status.isInProgress)
const CircularProgressIndicator()
Expand Down
2 changes: 1 addition & 1 deletion lib/ui/profile/page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Future<void> showPickCirclesBottomSheet(
]),
allowCreateNew: false,
circles: circles,
callback: (circles) => callback(circles
callback: (circles) async => callback(circles
.where((c) => c.$3)
.map((c) => c.$1)
.asList()))))
Expand Down
6 changes: 3 additions & 3 deletions lib/ui/widgets/circles/cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,22 @@ class CirclesCubit extends Cubit<CirclesState> {
final String coagContactId;
late final StreamSubscription<void> _circlesSubscription;

void update(List<(String, String, bool)> circles) {
Future<void> update(List<(String, String, bool)> circles) async {
// Check if there is a new circle, add it
var storedCircles = contactsRepository.getCircles();
for (final (id, label, _) in circles) {
if (!storedCircles.containsKey(id)) {
storedCircles[id] = label;
}
}
contactsRepository.updateCircles(storedCircles);
await contactsRepository.updateCircles(storedCircles);

// Update circle membership
final memberships = Map<String, List<String>>.from(
contactsRepository.getCircleMemberships());
memberships[coagContactId] =
circles.where((c) => c.$3).map((c) => c.$1).asList();
contactsRepository.updateCircleMemberships(memberships);
await contactsRepository.updateCircleMemberships(memberships);
}

@override
Expand Down
4 changes: 2 additions & 2 deletions lib/ui/widgets/circles/widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class CirclesForm extends StatefulWidget {
this.customHeader,
super.key});

final void Function(List<(String, String, bool)>) callback;
final Future<void> Function(List<(String, String, bool)>) callback;
final List<(String, String, bool)> circles;
final bool allowCreateNew;
final Widget? customHeader;
Expand Down Expand Up @@ -69,7 +69,7 @@ class _CirclesFormState extends State<CirclesForm> {
});

try {
widget.callback(_state.circles);
await widget.callback(_state.circles);
_state = _state.copyWith(status: FormzSubmissionStatus.success);
Navigator.pop(context);
} catch (e) {
Expand Down
Loading

0 comments on commit 60389bb

Please sign in to comment.