Skip to content

Commit d02b543

Browse files
committed
Message sync cutoff
1 parent 36a5fee commit d02b543

6 files changed

Lines changed: 102 additions & 6 deletions

File tree

android/app/src/main/kotlin/com/bluebubbles/messaging/services/system/NativeSyncIsolateHandler.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import kotlin.coroutines.suspendCoroutine
1818
class NativeSyncIsolateHandler : MethodCallHandlerImpl() {
1919
companion object {
2020
const val tag = "native-sync-isolate"
21+
22+
var engine: FlutterEngine? = null
2123
}
2224

2325
override fun handleMethodCall(
@@ -26,19 +28,35 @@ class NativeSyncIsolateHandler : MethodCallHandlerImpl() {
2628
mainContext: Context
2729
) {
2830
val context = mainContext.applicationContext
31+
32+
var param = call.argument<Boolean>("close") ?: false
33+
if (param) {
34+
engine?.destroy()
35+
engine = null
36+
mainresult.success(null)
37+
return
38+
}
39+
40+
if (engine != null) {
41+
mainresult.success(null)
42+
return
43+
}
44+
2945
FlutterMain.startInitialization(context)
3046
FlutterMain.ensureInitializationComplete(context, null)
3147

3248
Log.d(Constants.logTag, "Loading callback info")
3349
val info = ApplicationInfoLoader.load(context)
3450
val workerEngine = FlutterEngine(context)
51+
engine = workerEngine
3552
MethodChannel(workerEngine.dartExecutor.binaryMessenger, Constants.methodChannel).setMethodCallHandler {
3653
call, result -> run {
3754
if (call.method == "ready") {
3855
Log.d(Constants.logTag, "Dart engine is ready!")
3956
mainresult.success(null)
4057
} else if (call.method == "exit") {
4158
workerEngine.destroy()
59+
engine = null
4260
} else {
4361
MethodCallHandler().methodCallHandler(call, result, context)
4462
}

lib/app/layouts/settings/pages/profile/profile_panel.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ class ProfilePanel extends StatefulWidget {
4242
}
4343

4444
class _ProfilePanelState extends OptimizedState<ProfilePanel> with WidgetsBindingObserver {
45+
static const List<int> _syncHistoryOptions = [604800000, 2592000000, 15552000000, 31536000000, 0];
46+
static const Map<int, String> _syncHistoryLabels = {
47+
604800000: "7 days",
48+
2592000000: "1 month",
49+
15552000000: "6 months",
50+
31536000000: "1 year",
51+
0: "No limit",
52+
};
4553
final RxDouble opacity = 1.0.obs;
4654
final RxMap<String, dynamic> accountInfo = RxMap({});
4755
final RxMap<String, dynamic> accountContact = RxMap({});
@@ -474,6 +482,11 @@ class _ProfilePanelState extends OptimizedState<ProfilePanel> with WidgetsBindin
474482
Logger.info("Enabling messages in iCloud!");
475483
ss.settings.cloudSyncingEnabled.value = val;
476484
ss.saveSettings();
485+
if (!val) {
486+
await pushService.resetCloudKitSync();
487+
} else {
488+
pushService.doCloudKitSync();
489+
}
477490
},
478491
initialVal: ss.settings.cloudSyncingEnabled.value,
479492
title: "Messages in iCloud (BETA)",
@@ -489,6 +502,24 @@ class _ProfilePanelState extends OptimizedState<ProfilePanel> with WidgetsBindin
489502
subtitle: "Disable to reduce iCloud storage usage",
490503
backgroundColor: tileColor,
491504
),
505+
if(ss.settings.cloudSyncingEnabled.value)
506+
SettingsOptions<int>(
507+
title: "Sync history",
508+
initial: _syncHistoryOptions.contains(ss.settings.syncHistoryTime.value) ? ss.settings.syncHistoryTime.value : 0,
509+
clampWidth: false,
510+
options: _syncHistoryOptions,
511+
secondaryColor: headerColor,
512+
useCupertino: false,
513+
capitalize: false,
514+
textProcessing: (value) => _syncHistoryLabels[value] ?? "No limit",
515+
onChanged: (value) async {
516+
if (value == null) return;
517+
ss.settings.syncHistoryTime.value = value;
518+
ss.saveSettings();
519+
await pushService.resetCloudKitSync();
520+
pushService.doCloudKitSync();
521+
},
522+
),
492523
if (quotaInfo.value != null && ss.settings.cloudSyncingEnabled.value)
493524
Container(
494525
child: Padding(

lib/app/layouts/setup/setup_view.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -759,9 +759,6 @@ class SetupViewController extends StatefulController {
759759
pageController.jumpToPage(5);
760760
return;
761761
}
762-
if (newError.contains("6001")) {
763-
newError += " Make sure Contact Key Verification and Advanced Data Protection are off.";
764-
}
765762
error = newError;
766763
if (ss.settings.deviceIsHosted.value && errorIsProblem()) {
767764
pushService.mixpanel?.track("hosted-setup-error");

lib/database/global/settings.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ class Settings {
212212

213213
final RxBool cloudSyncingEnabled = false.obs;
214214
final RxBool attachmentSyncEnabled = false.obs;
215+
final RxInt syncHistoryTime = 0.obs;
215216
final RxnString keychainDefaultPassword = RxnString();
216217

217218
final RxMap<String, String?> ctags = <String, String?>{}.obs;
@@ -439,6 +440,7 @@ class Settings {
439440
'isTester': isTester.value,
440441
'cloudSyncingEnabled': cloudSyncingEnabled.value,
441442
'attachmentSyncEnabled': attachmentSyncEnabled.value,
443+
'syncHistoryTime': syncHistoryTime.value,
442444
'contactSyncProvider': contactSyncProvider.value,
443445
};
444446
if (includeAll) {
@@ -618,6 +620,7 @@ class Settings {
618620
ss.settings.isTester.value = map['isTester'] ?? false;
619621
ss.settings.cloudSyncingEnabled.value = map['cloudSyncingEnabled'] ?? false;
620622
ss.settings.attachmentSyncEnabled.value = map['attachmentSyncEnabled'] ?? false;
623+
ss.settings.syncHistoryTime.value = map['syncHistoryTime'] ?? 0;
621624
ss.settings.ctags.value = map['ctags'] ?? {};
622625
ss.settings.tokens.value = map['tokens'] ?? {};
623626
ss.settings.save();
@@ -792,6 +795,7 @@ class Settings {
792795
s.isTester.value = map['isTester'] ?? false;
793796
s.cloudSyncingEnabled.value = map['cloudSyncingEnabled'] ?? false;
794797
s.attachmentSyncEnabled.value = map['attachmentSyncEnabled'] ?? false;
798+
s.syncHistoryTime.value = map['syncHistoryTime'] ?? 0;
795799

796800
s.ctags.value = map['ctags'] is String ? jsonDecode(map['ctags']).cast<String, String?>() : <String, String?>{};
797801
s.tokens.value = map['tokens'] is String ? jsonDecode(map['tokens']).cast<String, String?>() : <String, String?>{};

lib/services/rustpush/rustpush_service.dart

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2148,6 +2148,22 @@ class RustPushService extends GetxService {
21482148
Database.attachments.putMany(attachments);
21492149
}
21502150

2151+
// forcibly stops a running sync operation.
2152+
Future<void> resetCloudKitSync() async {
2153+
if (isSyncing.value == null) return;
2154+
2155+
if (kIsDesktop) {
2156+
exit(0);
2157+
} else {
2158+
await mcs.invokeMethod("native-sync-isolate", {
2159+
"close": true
2160+
});
2161+
ui.IsolateNameServer.removePortNameMapping("bg_sync");
2162+
pushService.isSyncing.value = null;
2163+
chats.restoring = false;
2164+
}
2165+
}
2166+
21512167
Rxn<String> isSyncing = Rxn(null);
21522168
Future<void> doCloudKitSync() async {
21532169
if (kIsDesktop) {
@@ -2187,6 +2203,20 @@ class RustPushService extends GetxService {
21872203
return "${size.toStringAsFixed(decimals)} ${suffixes[i]}";
21882204
}
21892205

2206+
(int, DateTime) getCutoffTime() {
2207+
// yes, we call this a lot, it's a bit of a shame.
2208+
ss.prefs.reload();
2209+
var time = ss.prefs.getInt('syncHistoryTime') ?? 0;
2210+
2211+
var cutoffDateTime = DateTime.fromMillisecondsSinceEpoch(0);
2212+
var cutoffTime = 0;
2213+
if (time != 0) {
2214+
cutoffTime = RustPushBBUtils.nsSinceAppleEpoch(DateTime.now()) - (time * 1000000);
2215+
cutoffDateTime = DateTime.now().subtract(Duration(milliseconds: time));
2216+
}
2217+
return (cutoffTime, cutoffDateTime);
2218+
}
2219+
21902220
Future<void> uploadMessages(
21912221
List<Message> messages,
21922222
List<(String, String)> uploadAttachments,
@@ -2394,6 +2424,8 @@ class RustPushService extends GetxService {
23942424

23952425
isSyncing.value = "Downloading Attachments...";
23962426

2427+
// we must have one uniform cutoff time to ensure we don't upload duplicates
2428+
var (cutoffTime, cutoffDateTime) = getCutoffTime();
23972429
var attCount = 0;
23982430
currentState = 0;
23992431
while (currentState != 3) {
@@ -2413,6 +2445,12 @@ class RustPushService extends GetxService {
24132445
}
24142446
if (dupDeleteAttachments.contains(item.key)) continue;
24152447
var decoded = api.decodeAttachmentmeta(wrapped: item.value!.cm);
2448+
2449+
if (cutoffTime > decoded.createdDate && currentState != 3) {
2450+
Logger.info("Stopping attachment sync for cutoff!");
2451+
currentState = 3;
2452+
}
2453+
24162454
var existing = Attachment.findOne(convertAttachmentGuid(decoded.guid));
24172455
if (existing != null) {
24182456
if (existing.ckRecordId != null && existing.ckRecordId != item.key) {
@@ -2482,6 +2520,12 @@ class RustPushService extends GetxService {
24822520
continue;
24832521
}
24842522
if (dupDeleteMessages.contains(item.key)) continue;
2523+
2524+
if (cutoffTime > item.value!.time && currentState != 3) {
2525+
Logger.info("Stopping message sync for cutoff!");
2526+
currentState = 3;
2527+
}
2528+
24852529
var existing = Message.findOne(guid: item.value!.guid);
24862530
if (existing != null) {
24872531
if (existing.ckRecordId == item.key) {
@@ -2591,7 +2635,8 @@ class RustPushService extends GetxService {
25912635
bool noAttachments = !ss.settings.attachmentSyncEnabled.value;
25922636

25932637

2594-
var unsyncedMessages = Database.messages.query(Message_.ckRecordId.isNull().and(Message_.itemType.equals(0)).and(Message_.ckSyncState.equals(false).or(Message_.ckSyncState.isNull())))
2638+
var unsyncedMessages = Database.messages.query(Message_.ckRecordId.isNull().and(Message_.itemType.equals(0)).and(Message_.ckSyncState.equals(false).or(Message_.ckSyncState.isNull()))
2639+
.and(Message_.dateCreated.greaterThanDate(cutoffDateTime)))
25952640
.build()
25962641
..limit = 3000;
25972642
var messages = unsyncedMessages.find();
@@ -2601,7 +2646,8 @@ class RustPushService extends GetxService {
26012646
Logger.info("Syncing batch ${messages.length}");
26022647
await uploadMessages(messages, uploadAttachments, idToAttachment, noAttachments);
26032648

2604-
var unsyncedMessages = Database.messages.query(Message_.ckRecordId.isNull().and(Message_.itemType.equals(0)).and(Message_.ckSyncState.equals(false).or(Message_.ckSyncState.isNull())))
2649+
var unsyncedMessages = Database.messages.query(Message_.ckRecordId.isNull().and(Message_.itemType.equals(0)).and(Message_.ckSyncState.equals(false).or(Message_.ckSyncState.isNull()))
2650+
.and(Message_.dateCreated.greaterThanDate(cutoffDateTime)))
26052651
.build()
26062652
..limit = 3000;
26072653
messages = unsyncedMessages.find();

0 commit comments

Comments
 (0)