diff --git a/README.md b/README.md
index bce62b2..3cb70b2 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,15 @@
A Flutter application for interfacing with the Friendica social network.
-For Linux development be sure that libsecret-1-dev and libjsoncpp-dev are installed on the machine. For running only make sure the non-dev versions are...
+For Linux development be sure that libsecret-1-dev and libjsoncpp-dev are installed on the machine. For running only
+make sure the non-dev versions are...
+## Development Notes
+
+Whenever a model is changed that is stored in ObjectBox it is necessary to execute the command:
+
+```bash
+flutter pub run build_runner build
+```
Licensed with the Mozilla Public License 2.0 copyleft license.
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
index c29ba58..5c9e0d1 100644
--- a/android/app/src/debug/AndroidManifest.xml
+++ b/android/app/src/debug/AndroidManifest.xml
@@ -5,4 +5,5 @@
to allow setting breakpoints, to provide hot reload, etc.
-->
+
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index dd26b82..831fd53 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -37,4 +37,6 @@
+
+
diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml
index c29ba58..5c9e0d1 100644
--- a/android/app/src/profile/AndroidManifest.xml
+++ b/android/app/src/profile/AndroidManifest.xml
@@ -5,4 +5,5 @@
to allow setting breakpoints, to provide hot reload, etc.
-->
+
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 7caad5f..f7df94d 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -43,6 +43,10 @@ PODS:
- FMDB/standard (2.7.5)
- image_picker_ios (0.0.1):
- Flutter
+ - ObjectBox (1.8.1-rc)
+ - objectbox_flutter_libs (0.0.1):
+ - Flutter
+ - ObjectBox (= 1.8.1-rc)
- path_provider_ios (0.0.1):
- Flutter
- SDWebImage (5.13.2):
@@ -66,6 +70,7 @@ DEPENDENCIES:
- flutter_file_dialog (from `.symlinks/plugins/flutter_file_dialog/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
+ - objectbox_flutter_libs (from `.symlinks/plugins/objectbox_flutter_libs/ios`)
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`)
@@ -77,6 +82,7 @@ SPEC REPOS:
- DKImagePickerController
- DKPhotoGallery
- FMDB
+ - ObjectBox
- SDWebImage
- SwiftyGif
@@ -91,6 +97,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
+ objectbox_flutter_libs:
+ :path: ".symlinks/plugins/objectbox_flutter_libs/ios"
path_provider_ios:
:path: ".symlinks/plugins/path_provider_ios/ios"
shared_preferences_foundation:
@@ -111,6 +119,8 @@ SPEC CHECKSUMS:
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb
+ ObjectBox: 7615cc462f2976cb45127d301a17cb473edf3f9e
+ objectbox_flutter_libs: 55835e03ff76bf9d5ce0a41a2ef3902d2e8c1f20
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
SDWebImage: 72f86271a6f3139cc7e4a89220946489d4b9a866
shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca
diff --git a/lib/controls/timeline/interactions_bar_control.dart b/lib/controls/timeline/interactions_bar_control.dart
index 9dc8653..247a73e 100644
--- a/lib/controls/timeline/interactions_bar_control.dart
+++ b/lib/controls/timeline/interactions_bar_control.dart
@@ -133,7 +133,7 @@ class _InteractionsBarControlState extends State {
);
}
} else {
- context.push('/post/view/${widget.entry.id}');
+ context.push('/post/view/${widget.entry.id}/${widget.entry.id}');
}
}
diff --git a/lib/data/interfaces/connections_repo_intf.dart b/lib/data/interfaces/connections_repo_intf.dart
new file mode 100644
index 0000000..36beb28
--- /dev/null
+++ b/lib/data/interfaces/connections_repo_intf.dart
@@ -0,0 +1,34 @@
+import 'package:result_monad/result_monad.dart';
+
+import '../../models/connection.dart';
+import '../../models/exec_error.dart';
+
+class IConnectionsRepo {
+ bool addConnection(Connection connection) {
+ throw UnimplementedError();
+ }
+
+ bool addAllConnections(Iterable newConnections) {
+ throw UnimplementedError();
+ }
+
+ bool updateConnection(Connection connection) {
+ throw UnimplementedError();
+ }
+
+ Result getById(String id) {
+ throw UnimplementedError();
+ }
+
+ Result getByName(String name) {
+ throw UnimplementedError();
+ }
+
+ List getMyContacts() {
+ throw UnimplementedError();
+ }
+
+ List getKnownUsersByName(String name) {
+ throw UnimplementedError();
+ }
+}
diff --git a/lib/data/interfaces/groups_repo.intf.dart b/lib/data/interfaces/groups_repo.intf.dart
new file mode 100644
index 0000000..1ab1437
--- /dev/null
+++ b/lib/data/interfaces/groups_repo.intf.dart
@@ -0,0 +1,26 @@
+import 'package:result_monad/result_monad.dart';
+
+import '../../models/exec_error.dart';
+import '../../models/group_data.dart';
+
+class IGroupsRepo {
+ void addAllGroups(List groups) {
+ throw UnimplementedError();
+ }
+
+ void clearGroups() {
+ throw UnimplementedError();
+ }
+
+ List getMyGroups() {
+ throw UnimplementedError();
+ }
+
+ Result, ExecError> getGroupsForUser(String id) {
+ throw UnimplementedError();
+ }
+
+ bool updateConnectionGroupData(String id, List currentGroups) {
+ throw UnimplementedError();
+ }
+}
diff --git a/lib/data/interfaces/hashtag_repo_intf.dart b/lib/data/interfaces/hashtag_repo_intf.dart
new file mode 100644
index 0000000..ae9f17d
--- /dev/null
+++ b/lib/data/interfaces/hashtag_repo_intf.dart
@@ -0,0 +1,11 @@
+import '../../models/hashtag.dart';
+
+class IHashtagRepo {
+ void add(Hashtag tag) {
+ throw UnimplementedError();
+ }
+
+ List getMatchingHashTags(String text) {
+ throw UnimplementedError();
+ }
+}
diff --git a/lib/data/memory/memory_connections_repo.dart b/lib/data/memory/memory_connections_repo.dart
new file mode 100644
index 0000000..bd43aa8
--- /dev/null
+++ b/lib/data/memory/memory_connections_repo.dart
@@ -0,0 +1,97 @@
+import 'package:result_monad/result_monad.dart';
+
+import '../../models/connection.dart';
+import '../../models/exec_error.dart';
+import '../interfaces/connections_repo_intf.dart';
+
+class MemoryConnectionsRepo implements IConnectionsRepo {
+ final _connectionsById = {};
+ final _connectionsByName = {};
+ final _myContacts = [];
+
+ @override
+ bool addAllConnections(Iterable newConnections) {
+ bool result = true;
+
+ for (final connection in newConnections) {
+ result &= addConnection(connection);
+ }
+
+ return result;
+ }
+
+ @override
+ bool addConnection(Connection connection) {
+ if (_connectionsById.containsKey(connection.id)) {
+ return false;
+ }
+ return updateConnection(connection);
+ }
+
+ @override
+ List getKnownUsersByName(String name) {
+ return _connectionsByName.values.where((it) {
+ final normalizedHandle = it.handle.toLowerCase();
+ final normalizedName = it.name.toLowerCase();
+ final normalizedQuery = name.toLowerCase();
+ return normalizedHandle.contains(normalizedQuery) ||
+ normalizedName.contains(normalizedQuery);
+ }).toList();
+ }
+
+ @override
+ bool updateConnection(Connection connection) {
+ _connectionsById[connection.id] = connection;
+ _connectionsByName[connection.name] = connection;
+ int index = _myContacts.indexWhere((c) => c.id == connection.id);
+ if (index >= 0) {
+ _myContacts.removeAt(index);
+ }
+ switch (connection.status) {
+ case ConnectionStatus.youFollowThem:
+ case ConnectionStatus.theyFollowYou:
+ case ConnectionStatus.mutual:
+ if (index > 0) {
+ _myContacts.insert(index, connection);
+ } else {
+ _myContacts.add(connection);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return true;
+ }
+
+ @override
+ List getMyContacts() {
+ return _myContacts;
+ }
+
+ @override
+ Result getById(String id) {
+ final result = _connectionsById[id];
+ if (result == null) {
+ return Result.error(ExecError(
+ type: ErrorType.notFound,
+ message: '$id not found',
+ ));
+ }
+
+ return Result.ok(result);
+ }
+
+ @override
+ Result getByName(String name) {
+ final result = _connectionsByName[name];
+ if (result == null) {
+ return Result.error(ExecError(
+ type: ErrorType.notFound,
+ message: '$name not found',
+ ));
+ }
+
+ return Result.ok(result);
+ }
+}
diff --git a/lib/data/memory/memory_groups_repo.dart b/lib/data/memory/memory_groups_repo.dart
new file mode 100644
index 0000000..a3ce883
--- /dev/null
+++ b/lib/data/memory/memory_groups_repo.dart
@@ -0,0 +1,43 @@
+import 'package:result_monad/result_monad.dart';
+
+import '../../models/exec_error.dart';
+import '../../models/group_data.dart';
+import '../interfaces/groups_repo.intf.dart';
+
+class MemoryGroupsRepo implements IGroupsRepo {
+ final _groupsForConnection = >{};
+ final _myGroups = {};
+
+ @override
+ Result, ExecError> getGroupsForUser(String id) {
+ if (!_groupsForConnection.containsKey(id)) {
+ return Result.error(ExecError(
+ type: ErrorType.notFound,
+ message: '$id not a known user ID',
+ ));
+ }
+
+ return Result.ok(_groupsForConnection[id]!);
+ }
+
+ @override
+ List getMyGroups() {
+ return _myGroups.toList();
+ }
+
+ @override
+ void clearGroups() {
+ _myGroups.clear();
+ }
+
+ @override
+ void addAllGroups(List groups) {
+ _myGroups.addAll(groups);
+ }
+
+ @override
+ bool updateConnectionGroupData(String id, List currentGroups) {
+ _groupsForConnection[id] = currentGroups;
+ return true;
+ }
+}
diff --git a/lib/data/objectbox/objectbox_cache.dart b/lib/data/objectbox/objectbox_cache.dart
new file mode 100644
index 0000000..29cc714
--- /dev/null
+++ b/lib/data/objectbox/objectbox_cache.dart
@@ -0,0 +1,23 @@
+import 'package:logging/logging.dart';
+import 'package:objectbox/objectbox.dart';
+import 'package:path/path.dart' as p;
+import 'package:path_provider/path_provider.dart';
+
+import '../../objectbox.g.dart';
+
+class ObjectBoxCache {
+ static final _logger = Logger('ObjectBoxCache');
+ late final Store store;
+
+ ObjectBoxCache._create(this.store);
+
+ static Future create() async {
+ final docsDir = await getApplicationSupportDirectory();
+
+ final path = p.join(docsDir.path, 'objectboxcache');
+ _logger.info('ObjectBoxCache path: $path');
+ final store = await openStore(
+ directory: path, macosApplicationGroup: 'friendica_portal');
+ return ObjectBoxCache._create(store);
+ }
+}
diff --git a/lib/data/objectbox/objectbox_connections_repo.dart b/lib/data/objectbox/objectbox_connections_repo.dart
new file mode 100644
index 0000000..734918c
--- /dev/null
+++ b/lib/data/objectbox/objectbox_connections_repo.dart
@@ -0,0 +1,104 @@
+import 'package:result_monad/result_monad.dart';
+
+import '../../globals.dart';
+import '../../models/connection.dart';
+import '../../models/exec_error.dart';
+import '../../objectbox.g.dart';
+import '../interfaces/connections_repo_intf.dart';
+import '../memory/memory_connections_repo.dart';
+import 'objectbox_cache.dart';
+
+class ObjectBoxConnectionsRepo implements IConnectionsRepo {
+ late final Box box;
+ final memCache = MemoryConnectionsRepo();
+
+ ObjectBoxConnectionsRepo() {
+ box = getIt().store.box();
+ }
+
+ @override
+ bool addAllConnections(Iterable newConnections) {
+ memCache.addAllConnections(newConnections);
+ final result = box.putMany(newConnections.toList());
+ return result.length == newConnections.length;
+ }
+
+ @override
+ bool addConnection(Connection connection) {
+ memCache.addConnection(connection);
+ box.putAsync(connection);
+ return true;
+ }
+
+ @override
+ Result getById(String id) {
+ final result = memCache.getById(id);
+ if (result.isSuccess) {
+ return result;
+ }
+
+ return _getConnection(
+ Connection_.id.equals(id),
+ ).mapError(
+ (error) => error.copy(
+ message: "Can't find Connection for ID: $id",
+ ),
+ );
+ }
+
+ @override
+ Result getByName(String name) {
+ final result = memCache.getByName(name);
+ if (result.isSuccess) {
+ return result;
+ }
+ return _getConnection(
+ Connection_.name.equals(name),
+ ).mapError(
+ (error) => error.copy(
+ message: "Can't find Connection for ID: $name",
+ ),
+ );
+ }
+
+ @override
+ List getKnownUsersByName(String name) {
+ return box
+ .query(
+ Connection_.name.contains(name, caseSensitive: false) |
+ Connection_.handle.contains(name, caseSensitive: false),
+ )
+ .build()
+ .find();
+ }
+
+ @override
+ List getMyContacts() {
+ final connectedStatuses = [
+ ConnectionStatus.youFollowThem.code,
+ ConnectionStatus.theyFollowYou.code,
+ ConnectionStatus.mutual.code,
+ ];
+ return box
+ .query(Connection_.dbStatus.oneOf(connectedStatuses))
+ .build()
+ .find();
+ }
+
+ @override
+ bool updateConnection(Connection connection) {
+ memCache.updateConnection(connection);
+ box.put(connection);
+ return true;
+ }
+
+ Result _getConnection(Condition query) {
+ final connection = box.query(query).build().findFirst();
+ if (connection == null) {
+ return buildErrorResult(type: ErrorType.notFound);
+ }
+
+ memCache.addConnection(connection);
+ return Result.ok(connection);
+ }
+}
diff --git a/lib/data/objectbox/objectbox_hashtag_repo.dart b/lib/data/objectbox/objectbox_hashtag_repo.dart
new file mode 100644
index 0000000..46c741b
--- /dev/null
+++ b/lib/data/objectbox/objectbox_hashtag_repo.dart
@@ -0,0 +1,34 @@
+import '../../globals.dart';
+import '../../models/hashtag.dart';
+import '../../objectbox.g.dart';
+import '../interfaces/hashtag_repo_intf.dart';
+import 'objectbox_cache.dart';
+
+class ObjectBoxHashtagRepo implements IHashtagRepo {
+ late final Box box;
+
+ ObjectBoxHashtagRepo() {
+ box = getIt().store.box();
+ }
+
+ @override
+ void add(Hashtag tag) {
+ box.putAsync(tag);
+ }
+
+ @override
+ List getMatchingHashTags(String text) {
+ return (box
+ .query(
+ text.length <= 2
+ ? Hashtag_.tag.startsWith(text, caseSensitive: false)
+ : Hashtag_.tag.contains(text, caseSensitive: false),
+ )
+ .order(Hashtag_.tag)
+ .build()
+ ..limit = 100)
+ .find()
+ .map((h) => h.tag)
+ .toList();
+ }
+}
diff --git a/lib/main.dart b/lib/main.dart
index 3bc46fd..f9e016f 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -5,6 +5,13 @@ import 'package:multi_trigger_autocomplete/multi_trigger_autocomplete.dart';
import 'package:provider/provider.dart';
import 'package:result_monad/result_monad.dart';
+import 'data/interfaces/connections_repo_intf.dart';
+import 'data/interfaces/groups_repo.intf.dart';
+import 'data/interfaces/hashtag_repo_intf.dart';
+import 'data/memory/memory_groups_repo.dart';
+import 'data/objectbox/objectbox_cache.dart';
+import 'data/objectbox/objectbox_connections_repo.dart';
+import 'data/objectbox/objectbox_hashtag_repo.dart';
import 'globals.dart';
import 'models/TimelineIdentifiers.dart';
import 'routes.dart';
@@ -39,11 +46,18 @@ void main() async {
final entryManagerService = EntryManagerService();
final timelineManager = TimelineManager();
final galleryService = GalleryService();
+
getIt.registerSingletonAsync(() async {
final service = SettingsService();
await service.initialize();
return service;
});
+
+ final objectBoxCache = await ObjectBoxCache.create();
+ getIt.registerSingleton(objectBoxCache);
+ getIt.registerSingleton(ObjectBoxConnectionsRepo());
+ getIt.registerSingleton(ObjectBoxHashtagRepo());
+ getIt.registerSingleton(MemoryGroupsRepo());
getIt.registerLazySingleton(() => ConnectionsManager());
getIt.registerLazySingleton(() => HashtagService());
getIt.registerSingleton(galleryService);
diff --git a/lib/models/connection.dart b/lib/models/connection.dart
index 43b13c2..00b9e6d 100644
--- a/lib/models/connection.dart
+++ b/lib/models/connection.dart
@@ -1,44 +1,67 @@
+import 'package:objectbox/objectbox.dart';
+
+@Entity()
class Connection {
- final ConnectionStatus status;
+ @Id()
+ int obId;
+
+ ConnectionStatus status;
+
+ int get dbStatus => status.code;
+
+ set dbStatus(int value) => status = ConnectionStatus.fromValue(value);
final String name;
final String handle;
+ @Unique(onConflict: ConflictStrategy.replace)
final String id;
- final Uri profileUrl;
+ final String profileUrl;
final String network;
- final Uri avatarUrl;
+ final String avatarUrl;
- Connection({this.status = ConnectionStatus.unknown,
- this.name = '',
- this.handle = '',
- this.id = '',
- Uri? profileUrl,
- this.network = '',
- Uri? avatarUrl})
- : profileUrl = profileUrl ?? Uri(),
- avatarUrl = avatarUrl ?? Uri();
+ @Property(type: PropertyType.date)
+ final DateTime lastUpdateTime;
+
+ Connection(
+ {this.obId = 0,
+ this.status = ConnectionStatus.unknown,
+ this.name = '',
+ this.handle = '',
+ this.id = '',
+ String? profileUrl,
+ this.network = '',
+ String? avatarUrl,
+ DateTime? lastUpdateTime})
+ : profileUrl = profileUrl ?? '',
+ avatarUrl = avatarUrl ?? '',
+ lastUpdateTime = lastUpdateTime ?? DateTime.now();
bool get isEmpty =>
name.isEmpty &&
- id.isEmpty &&
- network.isEmpty &&
- status == ConnectionStatus.unknown;
+ id.isEmpty &&
+ network.isEmpty &&
+ status == ConnectionStatus.unknown;
bool get isNotEmpty => !isEmpty;
- Connection copy({ConnectionStatus? status,
+ Connection copy({
+ int? obId,
+ ConnectionStatus? status,
String? name,
String? handle,
String? id,
- Uri? profileUrl,
+ String? profileUrl,
String? network,
- Uri? avatarUrl}) =>
+ String? avatarUrl,
+ DateTime? lastUpdateTime,
+ }) =>
Connection(
+ obId: obId ?? this.obId,
status: status ?? this.status,
name: name ?? this.name,
handle: handle ?? this.handle,
@@ -46,6 +69,7 @@ class Connection {
profileUrl: profileUrl ?? this.profileUrl,
network: network ?? this.network,
avatarUrl: avatarUrl ?? this.avatarUrl,
+ lastUpdateTime: lastUpdateTime ?? this.lastUpdateTime,
);
@override
@@ -56,20 +80,28 @@ class Connection {
@override
bool operator ==(Object other) =>
identical(this, other) ||
- other is Connection && runtimeType == other.runtimeType &&
- id == other.id;
+ other is Connection && runtimeType == other.runtimeType && id == other.id;
@override
int get hashCode => id.hashCode;
}
enum ConnectionStatus {
- youFollowThem,
- theyFollowYou,
- mutual,
- you,
- none,
- unknown,
+ youFollowThem(1),
+ theyFollowYou(2),
+ mutual(3),
+ you(4),
+ none(5),
+ unknown(6),
+ ;
+
+ final int code;
+
+ const ConnectionStatus(this.code);
+
+ factory ConnectionStatus.fromValue(int value) {
+ return ConnectionStatus.values.where((e) => e.code == value).first;
+ }
}
extension FriendStatusWriter on ConnectionStatus {
diff --git a/lib/models/exec_error.dart b/lib/models/exec_error.dart
index b0d0f68..961ef7c 100644
--- a/lib/models/exec_error.dart
+++ b/lib/models/exec_error.dart
@@ -1,11 +1,31 @@
import 'package:result_monad/result_monad.dart';
+Result buildErrorResult({
+ required ErrorType type,
+ String message = '',
+}) =>
+ Result.error(
+ ExecError(
+ type: type,
+ message: message,
+ ),
+ );
+
class ExecError {
final ErrorType type;
final String message;
ExecError({required this.type, this.message = ''});
+ ExecError copy({
+ ErrorType? type,
+ String? message,
+ }) =>
+ ExecError(
+ type: type ?? this.type,
+ message: message ?? this.message,
+ );
+
@override
String toString() {
return 'ExecError{type: $type, message: $message}';
diff --git a/lib/models/hashtag.dart b/lib/models/hashtag.dart
new file mode 100644
index 0000000..6d2afa5
--- /dev/null
+++ b/lib/models/hashtag.dart
@@ -0,0 +1,30 @@
+import 'package:objectbox/objectbox.dart';
+
+@Entity()
+class Hashtag {
+ @Id()
+ int id;
+
+ @Unique(onConflict: ConflictStrategy.replace)
+ String tag;
+
+ String url;
+
+ @Property(type: PropertyType.date)
+ DateTime lastUpdateTime;
+
+ Hashtag({
+ this.id = 0,
+ required this.tag,
+ required this.url,
+ DateTime? updateTime,
+ }) : lastUpdateTime = updateTime ?? DateTime.now();
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is Hashtag && runtimeType == other.runtimeType && tag == other.tag;
+
+ @override
+ int get hashCode => tag.hashCode;
+}
diff --git a/lib/objectbox-model.json b/lib/objectbox-model.json
new file mode 100644
index 0000000..2597277
--- /dev/null
+++ b/lib/objectbox-model.json
@@ -0,0 +1,105 @@
+{
+ "_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.",
+ "_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
+ "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
+ "entities": [
+ {
+ "id": "1:1213035855270739890",
+ "lastPropertyId": "9:7727190023732579468",
+ "name": "Connection",
+ "properties": [
+ {
+ "id": "1:4133343279264917280",
+ "name": "obId",
+ "type": 6,
+ "flags": 1
+ },
+ {
+ "id": "2:3393770296096844708",
+ "name": "name",
+ "type": 9
+ },
+ {
+ "id": "3:5864801995210079539",
+ "name": "handle",
+ "type": 9
+ },
+ {
+ "id": "4:2926904168461994523",
+ "name": "id",
+ "type": 9,
+ "flags": 34848,
+ "indexId": "1:8342366639839511243"
+ },
+ {
+ "id": "5:3621370552742492695",
+ "name": "network",
+ "type": 9
+ },
+ {
+ "id": "6:3054748457893853359",
+ "name": "profileUrl",
+ "type": 9
+ },
+ {
+ "id": "7:3716471511430220806",
+ "name": "avatarUrl",
+ "type": 9
+ },
+ {
+ "id": "8:3334077197732145885",
+ "name": "dbStatus",
+ "type": 6
+ },
+ {
+ "id": "9:7727190023732579468",
+ "name": "lastUpdateTime",
+ "type": 10
+ }
+ ],
+ "relations": []
+ },
+ {
+ "id": "2:8060242331335522964",
+ "lastPropertyId": "4:985152873657204249",
+ "name": "Hashtag",
+ "properties": [
+ {
+ "id": "1:3633001791521338712",
+ "name": "id",
+ "type": 6,
+ "flags": 1
+ },
+ {
+ "id": "2:3468373950035339457",
+ "name": "tag",
+ "type": 9,
+ "flags": 34848,
+ "indexId": "2:6156017341759176249"
+ },
+ {
+ "id": "3:5102584273729210526",
+ "name": "url",
+ "type": 9
+ },
+ {
+ "id": "4:985152873657204249",
+ "name": "lastUpdateTime",
+ "type": 10
+ }
+ ],
+ "relations": []
+ }
+ ],
+ "lastEntityId": "2:8060242331335522964",
+ "lastIndexId": "2:6156017341759176249",
+ "lastRelationId": "0:0",
+ "lastSequenceId": "0:0",
+ "modelVersion": 5,
+ "modelVersionParserMinimum": 5,
+ "retiredEntityUids": [],
+ "retiredIndexUids": [],
+ "retiredPropertyUids": [],
+ "retiredRelationUids": [],
+ "version": 1
+}
\ No newline at end of file
diff --git a/lib/objectbox.g.dart b/lib/objectbox.g.dart
new file mode 100644
index 0000000..82ec189
--- /dev/null
+++ b/lib/objectbox.g.dart
@@ -0,0 +1,287 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// This code was generated by ObjectBox. To update it run the generator again:
+// With a Flutter package, run `flutter pub run build_runner build`.
+// With a Dart package, run `dart run build_runner build`.
+// See also https://docs.objectbox.io/getting-started#generate-objectbox-code
+
+// ignore_for_file: camel_case_types
+// coverage:ignore-file
+
+import 'dart:typed_data';
+
+import 'package:flat_buffers/flat_buffers.dart' as fb;
+import 'package:objectbox/internal.dart'; // generated code can access "internal" functionality
+import 'package:objectbox/objectbox.dart';
+import 'package:objectbox_flutter_libs/objectbox_flutter_libs.dart';
+
+import 'models/connection.dart';
+import 'models/hashtag.dart';
+
+export 'package:objectbox/objectbox.dart'; // so that callers only have to import this file
+
+final _entities = [
+ ModelEntity(
+ id: const IdUid(1, 1213035855270739890),
+ name: 'Connection',
+ lastPropertyId: const IdUid(9, 7727190023732579468),
+ flags: 0,
+ properties: [
+ ModelProperty(
+ id: const IdUid(1, 4133343279264917280),
+ name: 'obId',
+ type: 6,
+ flags: 1),
+ ModelProperty(
+ id: const IdUid(2, 3393770296096844708),
+ name: 'name',
+ type: 9,
+ flags: 0),
+ ModelProperty(
+ id: const IdUid(3, 5864801995210079539),
+ name: 'handle',
+ type: 9,
+ flags: 0),
+ ModelProperty(
+ id: const IdUid(4, 2926904168461994523),
+ name: 'id',
+ type: 9,
+ flags: 34848,
+ indexId: const IdUid(1, 8342366639839511243)),
+ ModelProperty(
+ id: const IdUid(5, 3621370552742492695),
+ name: 'network',
+ type: 9,
+ flags: 0),
+ ModelProperty(
+ id: const IdUid(6, 3054748457893853359),
+ name: 'profileUrl',
+ type: 9,
+ flags: 0),
+ ModelProperty(
+ id: const IdUid(7, 3716471511430220806),
+ name: 'avatarUrl',
+ type: 9,
+ flags: 0),
+ ModelProperty(
+ id: const IdUid(8, 3334077197732145885),
+ name: 'dbStatus',
+ type: 6,
+ flags: 0),
+ ModelProperty(
+ id: const IdUid(9, 7727190023732579468),
+ name: 'lastUpdateTime',
+ type: 10,
+ flags: 0)
+ ],
+ relations: [],
+ backlinks: []),
+ ModelEntity(
+ id: const IdUid(2, 8060242331335522964),
+ name: 'Hashtag',
+ lastPropertyId: const IdUid(4, 985152873657204249),
+ flags: 0,
+ properties: [
+ ModelProperty(
+ id: const IdUid(1, 3633001791521338712),
+ name: 'id',
+ type: 6,
+ flags: 1),
+ ModelProperty(
+ id: const IdUid(2, 3468373950035339457),
+ name: 'tag',
+ type: 9,
+ flags: 34848,
+ indexId: const IdUid(2, 6156017341759176249)),
+ ModelProperty(
+ id: const IdUid(3, 5102584273729210526),
+ name: 'url',
+ type: 9,
+ flags: 0),
+ ModelProperty(
+ id: const IdUid(4, 985152873657204249),
+ name: 'lastUpdateTime',
+ type: 10,
+ flags: 0)
+ ],
+ relations: [],
+ backlinks: [])
+];
+
+/// Open an ObjectBox store with the model declared in this file.
+Future openStore(
+ {String? directory,
+ int? maxDBSizeInKB,
+ int? fileMode,
+ int? maxReaders,
+ bool queriesCaseSensitiveDefault = true,
+ String? macosApplicationGroup}) async =>
+ Store(getObjectBoxModel(),
+ directory: directory ?? (await defaultStoreDirectory()).path,
+ maxDBSizeInKB: maxDBSizeInKB,
+ fileMode: fileMode,
+ maxReaders: maxReaders,
+ queriesCaseSensitiveDefault: queriesCaseSensitiveDefault,
+ macosApplicationGroup: macosApplicationGroup);
+
+/// ObjectBox model definition, pass it to [Store] - Store(getObjectBoxModel())
+ModelDefinition getObjectBoxModel() {
+ final model = ModelInfo(
+ entities: _entities,
+ lastEntityId: const IdUid(2, 8060242331335522964),
+ lastIndexId: const IdUid(2, 6156017341759176249),
+ lastRelationId: const IdUid(0, 0),
+ lastSequenceId: const IdUid(0, 0),
+ retiredEntityUids: const [],
+ retiredIndexUids: const [],
+ retiredPropertyUids: const [],
+ retiredRelationUids: const [],
+ modelVersion: 5,
+ modelVersionParserMinimum: 5,
+ version: 1);
+
+ final bindings = {
+ Connection: EntityDefinition(
+ model: _entities[0],
+ toOneRelations: (Connection object) => [],
+ toManyRelations: (Connection object) => {},
+ getId: (Connection object) => object.obId,
+ setId: (Connection object, int id) {
+ object.obId = id;
+ },
+ objectToFB: (Connection object, fb.Builder fbb) {
+ final nameOffset = fbb.writeString(object.name);
+ final handleOffset = fbb.writeString(object.handle);
+ final idOffset = fbb.writeString(object.id);
+ final networkOffset = fbb.writeString(object.network);
+ final profileUrlOffset = fbb.writeString(object.profileUrl);
+ final avatarUrlOffset = fbb.writeString(object.avatarUrl);
+ fbb.startTable(10);
+ fbb.addInt64(0, object.obId);
+ fbb.addOffset(1, nameOffset);
+ fbb.addOffset(2, handleOffset);
+ fbb.addOffset(3, idOffset);
+ fbb.addOffset(4, networkOffset);
+ fbb.addOffset(5, profileUrlOffset);
+ fbb.addOffset(6, avatarUrlOffset);
+ fbb.addInt64(7, object.dbStatus);
+ fbb.addInt64(8, object.lastUpdateTime.millisecondsSinceEpoch);
+ fbb.finish(fbb.endTable());
+ return object.obId;
+ },
+ objectFromFB: (Store store, ByteData fbData) {
+ final buffer = fb.BufferContext(fbData);
+ final rootOffset = buffer.derefObject(0);
+
+ final object = Connection(
+ obId: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
+ name: const fb.StringReader(asciiOptimization: true)
+ .vTableGet(buffer, rootOffset, 6, ''),
+ handle: const fb.StringReader(asciiOptimization: true)
+ .vTableGet(buffer, rootOffset, 8, ''),
+ id: const fb.StringReader(asciiOptimization: true)
+ .vTableGet(buffer, rootOffset, 10, ''),
+ profileUrl: const fb.StringReader(asciiOptimization: true)
+ .vTableGet(buffer, rootOffset, 14, ''),
+ network: const fb.StringReader(asciiOptimization: true)
+ .vTableGet(buffer, rootOffset, 12, ''),
+ avatarUrl: const fb.StringReader(asciiOptimization: true)
+ .vTableGet(buffer, rootOffset, 16, ''),
+ lastUpdateTime: DateTime.fromMillisecondsSinceEpoch(
+ const fb.Int64Reader().vTableGet(buffer, rootOffset, 20, 0)))
+ ..dbStatus =
+ const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0);
+
+ return object;
+ }),
+ Hashtag: EntityDefinition(
+ model: _entities[1],
+ toOneRelations: (Hashtag object) => [],
+ toManyRelations: (Hashtag object) => {},
+ getId: (Hashtag object) => object.id,
+ setId: (Hashtag object, int id) {
+ object.id = id;
+ },
+ objectToFB: (Hashtag object, fb.Builder fbb) {
+ final tagOffset = fbb.writeString(object.tag);
+ final urlOffset = fbb.writeString(object.url);
+ fbb.startTable(5);
+ fbb.addInt64(0, object.id);
+ fbb.addOffset(1, tagOffset);
+ fbb.addOffset(2, urlOffset);
+ fbb.addInt64(3, object.lastUpdateTime.millisecondsSinceEpoch);
+ fbb.finish(fbb.endTable());
+ return object.id;
+ },
+ objectFromFB: (Store store, ByteData fbData) {
+ final buffer = fb.BufferContext(fbData);
+ final rootOffset = buffer.derefObject(0);
+
+ final object = Hashtag(
+ id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
+ tag: const fb.StringReader(asciiOptimization: true)
+ .vTableGet(buffer, rootOffset, 6, ''),
+ url: const fb.StringReader(asciiOptimization: true)
+ .vTableGet(buffer, rootOffset, 8, ''))
+ ..lastUpdateTime = DateTime.fromMillisecondsSinceEpoch(
+ const fb.Int64Reader().vTableGet(buffer, rootOffset, 10, 0));
+
+ return object;
+ })
+ };
+
+ return ModelDefinition(model, bindings);
+}
+
+/// [Connection] entity fields to define ObjectBox queries.
+class Connection_ {
+ /// see [Connection.obId]
+ static final obId =
+ QueryIntegerProperty(_entities[0].properties[0]);
+
+ /// see [Connection.name]
+ static final name =
+ QueryStringProperty(_entities[0].properties[1]);
+
+ /// see [Connection.handle]
+ static final handle =
+ QueryStringProperty(_entities[0].properties[2]);
+
+ /// see [Connection.id]
+ static final id = QueryStringProperty(_entities[0].properties[3]);
+
+ /// see [Connection.network]
+ static final network =
+ QueryStringProperty(_entities[0].properties[4]);
+
+ /// see [Connection.profileUrl]
+ static final profileUrl =
+ QueryStringProperty(_entities[0].properties[5]);
+
+ /// see [Connection.avatarUrl]
+ static final avatarUrl =
+ QueryStringProperty(_entities[0].properties[6]);
+
+ /// see [Connection.dbStatus]
+ static final dbStatus =
+ QueryIntegerProperty(_entities[0].properties[7]);
+
+ /// see [Connection.lastUpdateTime]
+ static final lastUpdateTime =
+ QueryIntegerProperty(_entities[0].properties[8]);
+}
+
+/// [Hashtag] entity fields to define ObjectBox queries.
+class Hashtag_ {
+ /// see [Hashtag.id]
+ static final id = QueryIntegerProperty(_entities[1].properties[0]);
+
+ /// see [Hashtag.tag]
+ static final tag = QueryStringProperty(_entities[1].properties[1]);
+
+ /// see [Hashtag.url]
+ static final url = QueryStringProperty(_entities[1].properties[2]);
+
+ /// see [Hashtag.lastUpdateTime]
+ static final lastUpdateTime =
+ QueryIntegerProperty(_entities[1].properties[3]);
+}
diff --git a/lib/serializers/friendica/connection_friendica_extensions.dart b/lib/serializers/friendica/connection_friendica_extensions.dart
index fa3f19a..8440808 100644
--- a/lib/serializers/friendica/connection_friendica_extensions.dart
+++ b/lib/serializers/friendica/connection_friendica_extensions.dart
@@ -14,7 +14,7 @@ extension ConnectionFriendicaExtensions on Connection {
status: status,
name: name,
id: id,
- profileUrl: profileUrl,
+ profileUrl: profileUrl.toString(),
network: network);
}
}
diff --git a/lib/serializers/mastodon/connection_mastodon_extensions.dart b/lib/serializers/mastodon/connection_mastodon_extensions.dart
index 95e537d..b135dee 100644
--- a/lib/serializers/mastodon/connection_mastodon_extensions.dart
+++ b/lib/serializers/mastodon/connection_mastodon_extensions.dart
@@ -23,9 +23,9 @@ extension ConnectionMastodonExtensions on Connection {
name: name,
id: id,
handle: handle,
- profileUrl: profileUrl,
+ profileUrl: profileUrl.toString(),
network: network,
- avatarUrl: avatar,
+ avatarUrl: avatar.toString(),
);
}
}
diff --git a/lib/serializers/mastodon/hashtag_mastodon_extensions.dart b/lib/serializers/mastodon/hashtag_mastodon_extensions.dart
new file mode 100644
index 0000000..b325584
--- /dev/null
+++ b/lib/serializers/mastodon/hashtag_mastodon_extensions.dart
@@ -0,0 +1,12 @@
+import '../../models/hashtag.dart';
+
+extension HashtagMastodonExtensions on Hashtag {
+ static Hashtag fromJson(Map json) {
+ final tag = json['name'];
+ final url = json['url'];
+ return Hashtag(
+ tag: tag,
+ url: url,
+ );
+ }
+}
diff --git a/lib/serializers/mastodon/timeline_entry_mastodon_extensions.dart b/lib/serializers/mastodon/timeline_entry_mastodon_extensions.dart
index 31e09f6..2522da9 100644
--- a/lib/serializers/mastodon/timeline_entry_mastodon_extensions.dart
+++ b/lib/serializers/mastodon/timeline_entry_mastodon_extensions.dart
@@ -10,6 +10,7 @@ import '../../services/connections_manager.dart';
import '../../services/hashtag_service.dart';
import '../../utils/dateutils.dart';
import 'connection_mastodon_extensions.dart';
+import 'hashtag_mastodon_extensions.dart';
final _logger = Logger('TimelineEntryMastodonExtensions');
@@ -75,9 +76,9 @@ extension TimelineEntryMastodonExtensions on TimelineEntry {
final List? tags = json['tags'];
if (tags?.isNotEmpty ?? false) {
final tagManager = getIt();
- for (final tag in tags!) {
- final tagName = tag['name'];
- tagManager.addHashtage(tagName);
+ for (final tagJson in tags!) {
+ final tag = HashtagMastodonExtensions.fromJson(tagJson);
+ tagManager.add(tag);
}
}
diff --git a/lib/services/connections_manager.dart b/lib/services/connections_manager.dart
index 9e7ca19..09e7472 100644
--- a/lib/services/connections_manager.dart
+++ b/lib/services/connections_manager.dart
@@ -1,9 +1,12 @@
+import 'dart:collection';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:result_monad/result_monad.dart';
+import '../data/interfaces/connections_repo_intf.dart';
+import '../data/interfaces/groups_repo.intf.dart';
import '../globals.dart';
import '../models/connection.dart';
import '../models/exec_error.dart';
@@ -12,75 +15,28 @@ import 'auth_service.dart';
class ConnectionsManager extends ChangeNotifier {
static final _logger = Logger('$ConnectionsManager');
- final _connectionsById = {};
- final _connectionsByName = {};
- final _connectionsByProfileUrl = {};
- final _groupsForConnection = >{};
- final _myGroups = {};
- final _myContacts = [];
- var _myContactsInitialized = false;
+ late final IConnectionsRepo conRepo;
+ late final IGroupsRepo groupsRepo;
- int get length => _connectionsById.length;
-
- void clearCaches() {
- _connectionsById.clear();
- _connectionsByName.clear();
- _connectionsByProfileUrl.clear();
- _groupsForConnection.clear();
- _myGroups.clear();
- _myContacts.clear();
+ ConnectionsManager() {
+ conRepo = getIt();
+ groupsRepo = getIt();
}
bool addConnection(Connection connection) {
- if (_connectionsById.containsKey(connection.id)) {
- return false;
- }
- return updateConnection(connection);
+ return conRepo.addConnection(connection);
}
List getKnownUsersByName(String name) {
- return _connectionsByName.values.where((it) {
- final normalizedHandle = it.handle.toLowerCase();
- final normalizedName = it.name.toLowerCase();
- final normalizedQuery = name.toLowerCase();
- return normalizedHandle.contains(normalizedQuery) ||
- normalizedName.contains(normalizedQuery);
- }).toList();
+ return conRepo.getKnownUsersByName(name);
}
bool updateConnection(Connection connection) {
- _connectionsById[connection.id] = connection;
- _connectionsByName[connection.name] = connection;
- _connectionsByProfileUrl[connection.profileUrl] = connection;
- int index = _myContacts.indexWhere((c) => c.id == connection.id);
- if (index >= 0) {
- _myContacts.removeAt(index);
- }
- switch (connection.status) {
- case ConnectionStatus.youFollowThem:
- case ConnectionStatus.theyFollowYou:
- case ConnectionStatus.mutual:
- if (index > 0) {
- _myContacts.insert(index, connection);
- } else {
- _myContacts.add(connection);
- }
- break;
- default:
- break;
- }
-
- return true;
+ return conRepo.updateConnection(connection);
}
bool addAllConnections(Iterable newConnections) {
- bool result = true;
-
- for (final connection in newConnections) {
- result &= addConnection(connection);
- }
-
- return result;
+ return conRepo.addAllConnections(newConnections);
}
Future acceptFollowRequest(Connection connection) async {
@@ -179,12 +135,7 @@ class ConnectionsManager extends ChangeNotifier {
}
List getMyContacts() {
- if (!_myContactsInitialized) {
- updateAllContacts();
- _myContactsInitialized = true;
- }
-
- return _myContacts.toList(growable: false);
+ return conRepo.getMyContacts();
}
Future updateAllContacts() async {
@@ -234,29 +185,34 @@ class ConnectionsManager extends ChangeNotifier {
});
}
- _myContacts.clear();
- _myContacts.addAll(results.values);
addAllConnections(results.values);
- _myContacts.sort((c1, c2) => c1.name.compareTo(c2.name));
- _logger.finest('# Contacts:${_myContacts.length}');
+ final myContacts = conRepo.getMyContacts().toList();
+ myContacts.sort((c1, c2) => c1.name.compareTo(c2.name));
+ _logger.finest('# Contacts:${myContacts.length}');
notifyListeners();
}
List getMyGroups() {
- if (_myGroups.isNotEmpty) {
- return _myGroups.toList(growable: false);
+ final myGroups = groupsRepo.getMyGroups();
+ if (myGroups.isEmpty) {
+ _updateMyGroups(true);
}
- _updateMyGroups(true);
- return [];
+
+ return myGroups;
}
Result, ExecError> getGroupsForUser(String id) {
- if (!_groupsForConnection.containsKey(id)) {
- _refreshGroupListData(id, true);
- return Result.ok([]);
+ final result = groupsRepo.getGroupsForUser(id);
+ if (result.isSuccess) {
+ return result;
}
- return Result.ok(_groupsForConnection[id]!);
+ if (result.isFailure && result.error.type != ErrorType.notFound) {
+ return result;
+ }
+
+ _refreshGroupListData(id, true);
+ return Result.ok(UnmodifiableListView([]));
}
FutureResult addUserToGroup(
@@ -291,37 +247,22 @@ class ConnectionsManager extends ChangeNotifier {
return result.execErrorCast();
}
- Result getById(String id) {
- final result = _connectionsById[id];
- if (result == null) {
- return Result.error('$id not found');
- }
- if (result.status == ConnectionStatus.unknown) {
- _refreshConnection(result, true);
- }
- return Result.ok(result);
+ Result getById(String id) {
+ return conRepo.getById(id).andThenSuccess((c) {
+ if (c.status == ConnectionStatus.unknown) {
+ _refreshConnection(c, true);
+ }
+ return c;
+ }).execErrorCast();
}
- Result getByName(String name) {
- final result = _connectionsByName[name];
- if (result == null) {
- Result.error('$name not found');
- }
- if (result!.status == ConnectionStatus.unknown) {
- _refreshConnection(result, true);
- }
- return Result.ok(result);
- }
-
- Result getByProfileUrl(Uri url) {
- final result = _connectionsByProfileUrl[url];
- if (result == null) {
- Result.error('$url not found');
- }
- if (result!.status == ConnectionStatus.unknown) {
- _refreshConnection(result, true);
- }
- return Result.ok(result);
+ Result getByName(String name) {
+ return conRepo.getByName(name).andThenSuccess((c) {
+ if (c.status == ConnectionStatus.unknown) {
+ _refreshConnection(c, true);
+ }
+ return c;
+ }).execErrorCast();
}
Future fullRefresh(Connection connection) async {
@@ -337,8 +278,8 @@ class ConnectionsManager extends ChangeNotifier {
.currentClient
.andThenAsync((client) => client.getMemberGroupsForConnection(id))
.match(
- onSuccess: (lists) {
- _groupsForConnection[id] = lists;
+ onSuccess: (groups) {
+ groupsRepo.updateConnectionGroupData(id, groups);
if (withNotification) {
notifyListeners();
}
@@ -376,8 +317,8 @@ class ConnectionsManager extends ChangeNotifier {
.match(
onSuccess: (groups) {
_logger.finest('Got updated groups:${groups.map((e) => e.name)}');
- _myGroups.clear();
- _myGroups.addAll(groups);
+ groupsRepo.clearGroups();
+ groupsRepo.addAllGroups(groups);
if (withNotification) {
notifyListeners();
}
diff --git a/lib/services/hashtag_service.dart b/lib/services/hashtag_service.dart
index 70fec9a..a9c6ea9 100644
--- a/lib/services/hashtag_service.dart
+++ b/lib/services/hashtag_service.dart
@@ -1,21 +1,22 @@
import 'package:flutter/foundation.dart';
-class HashtagService extends ChangeNotifier {
- final _hashTags = {};
+import '../data/interfaces/hashtag_repo_intf.dart';
+import '../globals.dart';
+import '../models/hashtag.dart';
- void clear() {
- _hashTags.clear();
- notifyListeners();
+class HashtagService extends ChangeNotifier {
+ late final IHashtagRepo repo;
+
+ HashtagService() {
+ repo = getIt();
}
- void addHashtage(String hashtag) {
- _hashTags.add(hashtag);
+ void add(Hashtag tag) {
+ repo.add(tag);
notifyListeners();
}
List getMatchingHashTags(String searchString) {
- return _hashTags
- .where((tag) => tag.toLowerCase().contains(searchString.toLowerCase()))
- .toList();
+ return repo.getMatchingHashTags(searchString);
}
}
diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc
index 241dc8b..c1e3da3 100644
--- a/linux/flutter/generated_plugin_registrant.cc
+++ b/linux/flutter/generated_plugin_registrant.cc
@@ -8,6 +8,7 @@
#include
#include
+#include
#include
void fl_register_plugins(FlPluginRegistry* registry) {
@@ -17,6 +18,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
+ g_autoptr(FlPluginRegistrar) objectbox_flutter_libs_registrar =
+ fl_plugin_registry_get_registrar_for_plugin(registry, "ObjectboxFlutterLibsPlugin");
+ objectbox_flutter_libs_plugin_register_with_registrar(objectbox_flutter_libs_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake
index fd2ab75..28dcfea 100644
--- a/linux/flutter/generated_plugins.cmake
+++ b/linux/flutter/generated_plugins.cmake
@@ -5,6 +5,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
desktop_window
flutter_secure_storage_linux
+ objectbox_flutter_libs
url_launcher_linux
)
diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift
index 808f3fb..a3accbe 100644
--- a/macos/Flutter/GeneratedPluginRegistrant.swift
+++ b/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -7,6 +7,7 @@ import Foundation
import desktop_window
import flutter_secure_storage_macos
+import objectbox_flutter_libs
import path_provider_macos
import shared_preferences_foundation
import sqflite
@@ -15,6 +16,7 @@ import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DesktopWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWindowPlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
+ ObjectboxFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "ObjectboxFlutterLibsPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
diff --git a/macos/Podfile.lock b/macos/Podfile.lock
index 4b4a807..fb1b443 100644
--- a/macos/Podfile.lock
+++ b/macos/Podfile.lock
@@ -7,6 +7,10 @@ PODS:
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5)
+ - ObjectBox (1.8.1-rc)
+ - objectbox_flutter_libs (0.0.1):
+ - FlutterMacOS
+ - ObjectBox (= 1.8.1-rc)
- path_provider_macos (0.0.1):
- FlutterMacOS
- shared_preferences_foundation (0.0.1):
@@ -22,6 +26,7 @@ DEPENDENCIES:
- desktop_window (from `Flutter/ephemeral/.symlinks/plugins/desktop_window/macos`)
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
+ - objectbox_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/objectbox_flutter_libs/macos`)
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos`)
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`)
@@ -30,6 +35,7 @@ DEPENDENCIES:
SPEC REPOS:
trunk:
- FMDB
+ - ObjectBox
EXTERNAL SOURCES:
desktop_window:
@@ -38,6 +44,8 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos
FlutterMacOS:
:path: Flutter/ephemeral
+ objectbox_flutter_libs:
+ :path: Flutter/ephemeral/.symlinks/plugins/objectbox_flutter_libs/macos
path_provider_macos:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
shared_preferences_foundation:
@@ -52,6 +60,8 @@ SPEC CHECKSUMS:
flutter_secure_storage_macos: 75c8cadfdba05ca007c0fa4ea0c16e5cf85e521b
FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
+ ObjectBox: 7615cc462f2976cb45127d301a17cb473edf3f9e
+ objectbox_flutter_libs: 6c6e858d1df4a7cd5cb664c2108e6fa0b412be38
path_provider_macos: 05fb0ef0cedf3e5bd179b9e41a638682b37133ea
shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca
sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea
diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements
index 225aa48..ef5d0fe 100644
--- a/macos/Runner/Release.entitlements
+++ b/macos/Runner/Release.entitlements
@@ -8,5 +8,9 @@
keychain-access-groups
+ com.apple.security.application-groups
+
+ friendica_portal
+
diff --git a/macos/Runner/RunnerDebug.entitlements b/macos/Runner/RunnerDebug.entitlements
index 1fbcb4e..2a0b6a2 100644
--- a/macos/Runner/RunnerDebug.entitlements
+++ b/macos/Runner/RunnerDebug.entitlements
@@ -12,5 +12,9 @@
keychain-access-groups
+ com.apple.security.application-groups
+
+ friendica_portal
+
diff --git a/pubspec.lock b/pubspec.lock
index 50f7094..b5c14b2 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -1,6 +1,20 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
+ _fe_analyzer_shared:
+ dependency: transitive
+ description:
+ name: _fe_analyzer_shared
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "50.0.0"
+ analyzer:
+ dependency: transitive
+ description:
+ name: analyzer
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "5.2.0"
archive:
dependency: transitive
description:
@@ -29,6 +43,62 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
+ build:
+ dependency: transitive
+ description:
+ name: build
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.3.1"
+ build_config:
+ dependency: transitive
+ description:
+ name: build_config
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.1"
+ build_daemon:
+ dependency: transitive
+ description:
+ name: build_daemon
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.1.0"
+ build_resolvers:
+ dependency: transitive
+ description:
+ name: build_resolvers
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
+ build_runner:
+ dependency: "direct dev"
+ description:
+ name: build_runner
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.3.3"
+ build_runner_core:
+ dependency: transitive
+ description:
+ name: build_runner_core
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "7.2.7"
+ built_collection:
+ dependency: transitive
+ description:
+ name: built_collection
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "5.1.1"
+ built_value:
+ dependency: transitive
+ description:
+ name: built_value
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "8.4.3"
cached_network_image:
dependency: "direct main"
description:
@@ -57,6 +127,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
+ checked_yaml:
+ dependency: transitive
+ description:
+ name: checked_yaml
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.2"
clock:
dependency: transitive
description:
@@ -64,6 +141,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
+ code_builder:
+ dependency: transitive
+ description:
+ name: code_builder
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.4.0"
collection:
dependency: transitive
description:
@@ -92,6 +176,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2"
+ cryptography:
+ dependency: transitive
+ description:
+ name: cryptography
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.5"
csslib:
dependency: transitive
description:
@@ -106,6 +197,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
+ dart_style:
+ dependency: transitive
+ description:
+ name: dart_style
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.2.4"
desktop_window:
dependency: "direct main"
description:
@@ -148,6 +246,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "5.2.5"
+ fixnum:
+ dependency: transitive
+ description:
+ name: fixnum
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.1"
+ flat_buffers:
+ dependency: transitive
+ description:
+ name: flat_buffers
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.5"
flutter:
dependency: "direct main"
description: flutter
@@ -261,6 +373,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.1"
+ frontend_server_client:
+ dependency: transitive
+ description:
+ name: frontend_server_client
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.2.0"
functional_listener:
dependency: transitive
description:
@@ -289,6 +408,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.4+2"
+ glob:
+ dependency: transitive
+ description:
+ name: glob
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.1"
go_router:
dependency: "direct main"
description:
@@ -296,6 +422,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "5.2.4"
+ graphs:
+ dependency: transitive
+ description:
+ name: graphs
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.2.0"
html:
dependency: transitive
description:
@@ -310,6 +443,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.13.5"
+ http_multi_server:
+ dependency: transitive
+ description:
+ name: http_multi_server
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.2.1"
http_parser:
dependency: transitive
description:
@@ -359,6 +499,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.6.2"
+ io:
+ dependency: transitive
+ description:
+ name: io
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.3"
js:
dependency: transitive
description:
@@ -366,6 +513,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.4"
+ json_annotation:
+ dependency: transitive
+ description:
+ name: json_annotation
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.7.0"
lints:
dependency: transitive
description:
@@ -415,6 +569,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.1"
+ mime:
+ dependency: transitive
+ description:
+ name: mime
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.4"
multi_trigger_autocomplete:
dependency: "direct main"
description:
@@ -436,6 +597,27 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.1"
+ objectbox:
+ dependency: "direct main"
+ description:
+ name: objectbox
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.7.1"
+ objectbox_flutter_libs:
+ dependency: "direct main"
+ description:
+ name: objectbox_flutter_libs
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.7.1"
+ objectbox_generator:
+ dependency: "direct dev"
+ description:
+ name: objectbox_generator
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.7.1"
octo_image:
dependency: transitive
description:
@@ -443,6 +625,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
+ package_config:
+ dependency: transitive
+ description:
+ name: package_config
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
path:
dependency: "direct main"
description:
@@ -451,7 +640,7 @@ packages:
source: hosted
version: "1.8.2"
path_provider:
- dependency: transitive
+ dependency: "direct main"
description:
name: path_provider
url: "https://pub.dartlang.org"
@@ -534,6 +723,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.6.2"
+ pool:
+ dependency: transitive
+ description:
+ name: pool
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.5.1"
process:
dependency: transitive
description:
@@ -548,6 +744,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.5"
+ pub_semver:
+ dependency: transitive
+ description:
+ name: pub_semver
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.3"
+ pubspec_parse:
+ dependency: transitive
+ description:
+ name: pubspec_parse
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.2.1"
result_monad:
dependency: "direct main"
description:
@@ -618,11 +828,32 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
+ shelf:
+ dependency: transitive
+ description:
+ name: shelf
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.4.0"
+ shelf_web_socket:
+ dependency: transitive
+ description:
+ name: shelf_web_socket
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.3"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
+ source_gen:
+ dependency: transitive
+ description:
+ name: source_gen
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.2.6"
source_span:
dependency: transitive
description:
@@ -644,6 +875,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.1"
+ sqlite3:
+ dependency: "direct main"
+ description:
+ name: sqlite3
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.9.1"
stack_trace:
dependency: transitive
description:
@@ -658,6 +896,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
+ stream_transform:
+ dependency: transitive
+ description:
+ name: stream_transform
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
string_scanner:
dependency: transitive
description:
@@ -700,6 +945,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.17"
+ timing:
+ dependency: transitive
+ description:
+ name: timing
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.0"
typed_data:
dependency: transitive
description:
@@ -812,6 +1064,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.13"
+ watcher:
+ dependency: transitive
+ description:
+ name: watcher
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.2"
+ web_socket_channel:
+ dependency: transitive
+ description:
+ name: web_socket_channel
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.3.0"
win32:
dependency: transitive
description:
@@ -833,6 +1099,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.0"
+ yaml:
+ dependency: transitive
+ description:
+ name: yaml
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.1.1"
sdks:
dart: ">=2.18.2 <3.0.0"
flutter: ">=3.3.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index 0ff792c..05b221d 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -34,15 +34,21 @@ dependencies:
result_monad: ^2.0.2
scrollable_positioned_list: ^0.3.5
shared_preferences: ^2.0.15
+ sqlite3: ^1.9.1
time_machine: ^0.9.17
url_launcher: ^6.1.6
uuid: ^3.0.6
video_player: ^2.4.10
+ objectbox: ^1.7.1
+ objectbox_flutter_libs: ^1.7.1
+ path_provider: ^2.0.11
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
+ build_runner: ^2.3.3
+ objectbox_generator: ^1.7.1
flutter:
uses-material-design: true
diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc
index f50c00b..4c170c8 100644
--- a/windows/flutter/generated_plugin_registrant.cc
+++ b/windows/flutter/generated_plugin_registrant.cc
@@ -8,6 +8,7 @@
#include
#include
+#include
#include
void RegisterPlugins(flutter::PluginRegistry* registry) {
@@ -15,6 +16,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("DesktopWindowPlugin"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
+ ObjectboxFlutterLibsPluginRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("ObjectboxFlutterLibsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
}
diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake
index ddb2474..5a12988 100644
--- a/windows/flutter/generated_plugins.cmake
+++ b/windows/flutter/generated_plugins.cmake
@@ -5,6 +5,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
desktop_window
flutter_secure_storage_windows
+ objectbox_flutter_libs
url_launcher_windows
)