Skip to content

Commit d9b8c49

Browse files
committed
Merge branch 'supabase-integration' into update-package-specs-docs
2 parents 700f42d + 8340f84 commit d9b8c49

File tree

21 files changed

+1903
-14
lines changed

21 files changed

+1903
-14
lines changed

packages/brick_offline_first_with_rest/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## Unreleased
22

3+
* Expose offline queue functionality in `offline_queue.dart`
4+
35
## 3.0.2
46

57
* Apply standardized lints

packages/brick_offline_first_with_rest/example/README.md

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export 'package:brick_offline_first_with_rest/src/offline_queue/rest_offline_queue_client.dart';
2+
export 'package:brick_offline_first_with_rest/src/offline_queue/rest_offline_request_queue.dart';
3+
export 'package:brick_offline_first_with_rest/src/offline_queue/rest_request_sqlite_cache.dart';
4+
export 'package:brick_offline_first_with_rest/src/offline_queue/rest_request_sqlite_cache_manager.dart';

packages/brick_offline_first_with_supabase/README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,62 @@
44

55
The `OfflineFirstWithSupabase` domain uses all the same configurations and annotations as `OfflineFirst`.
66

7+
## Repository
8+
9+
Adding offline support to Supabase is slightly more complicated than the average [../brick_offline_first_with_rest/README.md](repository process). Feedback is welcome on a smoother and more intuitive integration.
10+
11+
````dart
12+
class MyRepository extends OfflineFirstWithSupabaseRepository {
13+
static late MyRepository? _singleton;
14+
15+
MyRepository._({
16+
required super.supabaseProvider,
17+
required super.sqliteProvider,
18+
required super.migrations,
19+
required super.offlineRequestQueue,
20+
super.memoryCacheProvider,
21+
});
22+
23+
factory MyRepository() => _singleton!;
24+
25+
static void configure({
26+
required String supabaseUrl,
27+
required String apiKey,
28+
required Set<Migration> migrations,
29+
}) {
30+
// Convenience method `.clientQueue` makes creating the queue and client easy.
31+
final (client, queue) = OfflineFirstWithSupabaseRepository.clientQueue(
32+
databaseFactory: databaseFactory,
33+
);
34+
35+
final provider = SupabaseProvider(
36+
// It's important to pass the offline client to your Supabase#Client instantiation.
37+
// If you're using supabase_flutter, make sure you initialize with the clientQueue's client
38+
// before passing it here. For example:
39+
// ```dart
40+
// await Supabase.initialize(httpClient: client)
41+
// SupabaseProvider(Supabase.instance.client, modelDictionary: ...)
42+
// ```
43+
SupabaseClient(supabaseUrl, apiKey, httpClient: client),
44+
modelDictionary: supabaseModelDictionary,
45+
);
46+
47+
// Finally, initialize the repository as normal.
48+
_singleton = MyRepository._(
49+
supabaseProvider: provider,
50+
sqliteProvider: SqliteProvider(
51+
'my_repository.sqlite',
52+
databaseFactory: databaseFactory,
53+
modelDictionary: sqliteModelDictionary,
54+
),
55+
migrations: migrations,
56+
offlineRequestQueue: queue,
57+
memoryCacheProvider: MemoryCacheProvider(),
58+
);
59+
}
60+
}
61+
````
62+
763
## Models
864

965
### ConnectOfflineFirstWithSupabase
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// GENERATED CODE DO NOT EDIT
2+
part of '../brick.g.dart';
3+
4+
Future<Horse> _$HorseFromSupabase(Map<String, dynamic> data,
5+
{required SupabaseProvider provider, OfflineFirstWithSupabaseRepository? repository}) async {
6+
return Horse(
7+
name: data['name'] as String?,
8+
mounties: await Future.wait<Mounty>(data['mounties']
9+
?.map((d) =>
10+
MountyAdapter().fromSupabase(d, provider: provider, repository: repository))
11+
.toList()
12+
.cast<Future<Mounty>>() ??
13+
[]));
14+
}
15+
16+
Future<Map<String, dynamic>> _$HorseToSupabase(Horse instance,
17+
{required SupabaseProvider provider, OfflineFirstWithSupabaseRepository? repository}) async {
18+
return {
19+
'name': instance.name,
20+
'mounties': await Future.wait<Map<String, dynamic>>(instance.mounties
21+
?.map((s) => MountyAdapter().toSupabase(s, provider: provider, repository: repository))
22+
.toList() ??
23+
[])
24+
};
25+
}
26+
27+
Future<Horse> _$HorseFromSqlite(Map<String, dynamic> data,
28+
{required SqliteProvider provider, OfflineFirstWithSupabaseRepository? repository}) async {
29+
return Horse(
30+
name: data['name'] == null ? null : data['name'] as String?,
31+
mounties: (await provider.rawQuery(
32+
'SELECT DISTINCT `f_Mounty_brick_id` FROM `_brick_Horse_mounties` WHERE l_Horse_brick_id = ?',
33+
[data['_brick_id'] as int]).then((results) {
34+
final ids = results.map((r) => r['f_Mounty_brick_id']);
35+
return Future.wait<Mounty>(ids.map((primaryKey) => repository!
36+
.getAssociation<Mounty>(
37+
Query.where('primaryKey', primaryKey, limit1: true),
38+
)
39+
.then((r) => r!.first)));
40+
}))
41+
.toList()
42+
.cast<Mounty>())
43+
..primaryKey = data['_brick_id'] as int;
44+
}
45+
46+
Future<Map<String, dynamic>> _$HorseToSqlite(Horse instance,
47+
{required SqliteProvider provider, OfflineFirstWithSupabaseRepository? repository}) async {
48+
return {'name': instance.name};
49+
}
50+
51+
/// Construct a [Horse]
52+
class HorseAdapter extends OfflineFirstWithSupabaseAdapter<Horse> {
53+
HorseAdapter();
54+
55+
@override
56+
final defaultToNull = true;
57+
@override
58+
final Map<String, RuntimeSqliteColumnDefinition> fieldsToSqliteColumns = {
59+
'name': const RuntimeSupabaseColumnDefinition(
60+
association: false,
61+
associationForeignKey: 'null',
62+
associationType: String,
63+
columnName: 'name',
64+
),
65+
'mounties': const RuntimeSupabaseColumnDefinition(
66+
association: true,
67+
associationForeignKey: 'null',
68+
associationType: Mounty,
69+
columnName: 'mounties',
70+
)
71+
};
72+
@override
73+
final ignoreDuplicates = false;
74+
@override
75+
final uniqueFields = {};
76+
@override
77+
final Map<String, RuntimeSqliteColumnDefinition> fieldsToSqliteColumns = {
78+
'primaryKey': const RuntimeSqliteColumnDefinition(
79+
association: false,
80+
columnName: '_brick_id',
81+
iterable: false,
82+
type: int,
83+
),
84+
'name': const RuntimeSqliteColumnDefinition(
85+
association: false,
86+
columnName: 'name',
87+
iterable: false,
88+
type: String,
89+
),
90+
'mounties': const RuntimeSqliteColumnDefinition(
91+
association: true,
92+
columnName: 'mounties',
93+
iterable: true,
94+
type: Mounty,
95+
)
96+
};
97+
@override
98+
Future<int?> primaryKeyByUniqueColumns(Horse instance, DatabaseExecutor executor) async =>
99+
instance.primaryKey;
100+
@override
101+
final String tableName = 'Horse';
102+
@override
103+
Future<void> afterSave(instance, {required provider, repository}) async {
104+
if (instance.primaryKey != null) {
105+
final mountiesOldColumns = await provider.rawQuery(
106+
'SELECT `f_Mounty_brick_id` FROM `_brick_Horse_mounties` WHERE `l_Horse_brick_id` = ?',
107+
[instance.primaryKey]);
108+
final mountiesOldIds = mountiesOldColumns.map((a) => a['f_Mounty_brick_id']);
109+
final mountiesNewIds = instance.mounties?.map((s) => s.primaryKey).whereType<int>() ?? [];
110+
final mountiesIdsToDelete = mountiesOldIds.where((id) => !mountiesNewIds.contains(id));
111+
112+
await Future.wait<void>(mountiesIdsToDelete.map((id) async {
113+
return await provider.rawExecute(
114+
'DELETE FROM `_brick_Horse_mounties` WHERE `l_Horse_brick_id` = ? AND `f_Mounty_brick_id` = ?',
115+
[instance.primaryKey, id]).catchError((e) => null);
116+
}));
117+
118+
await Future.wait<int?>(instance.mounties?.map((s) async {
119+
final id = s.primaryKey ?? await provider.upsert<Mounty>(s, repository: repository);
120+
return await provider.rawInsert(
121+
'INSERT OR IGNORE INTO `_brick_Horse_mounties` (`l_Horse_brick_id`, `f_Mounty_brick_id`) VALUES (?, ?)',
122+
[instance.primaryKey, id]);
123+
}) ??
124+
[]);
125+
}
126+
}
127+
128+
@override
129+
Future<Horse> fromSupabase(Map<String, dynamic> input,
130+
{required provider, covariant OfflineFirstWithSupabaseRepository? repository}) async =>
131+
await _$HorseFromSupabase(input, provider: provider, repository: repository);
132+
@override
133+
Future<Map<String, dynamic>> toSupabase(Horse input,
134+
{required provider, covariant OfflineFirstWithSupabaseRepository? repository}) async =>
135+
await _$HorseToSupabase(input, provider: provider, repository: repository);
136+
@override
137+
Future<Horse> fromSqlite(Map<String, dynamic> input,
138+
{required provider, covariant OfflineFirstWithSupabaseRepository? repository}) async =>
139+
await _$HorseFromSqlite(input, provider: provider, repository: repository);
140+
@override
141+
Future<Map<String, dynamic>> toSqlite(Horse input,
142+
{required provider, covariant OfflineFirstWithSupabaseRepository? repository}) async =>
143+
await _$HorseToSqlite(input, provider: provider, repository: repository);
144+
}

0 commit comments

Comments
 (0)