Skip to content

Commit 0377725

Browse files
committed
CardDav Sync
1 parent a360d70 commit 0377725

File tree

13 files changed

+1592
-248
lines changed

13 files changed

+1592
-248
lines changed

.dart_tool/build/fcd1995bc647fb959e82ea360c6c2c9a/asset_graph.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

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

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import 'package:bluebubbles/services/rustpush/rustpush_service.dart';
2222
import 'package:dio/dio.dart';
2323
import 'package:flutter/material.dart';
2424
import 'package:flutter_rust_bridge/flutter_rust_bridge.dart';
25+
import 'package:google_sign_in_all_platforms/google_sign_in_all_platforms.dart';
2526
import 'package:in_app_purchase_android/billing_client_wrappers.dart';
2627
import 'package:get/get.dart';
2728
import 'package:bluebubbles/services/network/backend_service.dart';
@@ -52,7 +53,7 @@ class _ProfilePanelState extends OptimizedState<ProfilePanel> with WidgetsBindin
5253
RxList<api.PrivateDeviceInfo> forwardingTargets = RxList([]);
5354

5455
Rxn<api.QuotaInfo> quotaInfo = Rxn(null);
55-
// Rxn<api.CloudMessageSummary> cloudMessageSummary = Rxn(null);
56+
Rxn<GoogleSignInCredentials> googleCreds = Rxn(null);
5657

5758
Future<void> handleSubscriptionToken(String subscription) async {
5859
var activated = await http.dio.post("https://hw.openbubbles.app/ticket/${ticket!}/activate", data: {"purchase_token": subscription});
@@ -129,6 +130,9 @@ class _ProfilePanelState extends OptimizedState<ProfilePanel> with WidgetsBindin
129130
handlePurchases(details);
130131
});
131132
if (pushService.state!.icloudServices != null) api.getQuotaInfo(info: pushService.state!.icloudServices!.tokenProvider).then((quota) => quotaInfo.value = quota);
133+
pushService.googleSignIn.signInOffline().then((state) {
134+
googleCreds.value = state;
135+
});
132136
// api.countRecords(state: pushService.state).then((summary) => cloudMessageSummary.value = summary);
133137
}
134138

@@ -516,6 +520,66 @@ class _ProfilePanelState extends OptimizedState<ProfilePanel> with WidgetsBindin
516520
),
517521
]
518522
)),
523+
if (kIsDesktop)
524+
SettingsHeader(
525+
iosSubtitle: iosSubtitle,
526+
materialSubtitle: materialSubtitle,
527+
text: "Contacts Syncing"),
528+
if (kIsDesktop)
529+
Obx(() => SettingsSection(
530+
backgroundColor: tileColor,
531+
children: [
532+
SettingsOptions<String>(
533+
title: "Sync contacts with",
534+
initial: ss.settings.contactSyncProvider.value,
535+
clampWidth: false,
536+
options: ["iCloud", "Google", "CardDav"],
537+
secondaryColor: headerColor,
538+
useCupertino: false,
539+
textProcessing: (str) => str,
540+
capitalize: false,
541+
onChanged: (value) async {
542+
ss.settings.ctags.clear();
543+
ss.settings.tokens.clear();
544+
ss.settings.contactSyncProvider.value = value ?? "iCloud";
545+
ss.saveSettings();
546+
cs.refreshContacts();
547+
},
548+
),
549+
if (ss.settings.contactSyncProvider.value == "Google" && googleCreds.value == null)
550+
SettingsTile(
551+
title: "Sign In",
552+
onTap: () async {
553+
final credentials = await pushService.googleSignIn.signIn();
554+
if (credentials != null) {
555+
print('Signed in successfully: ${credentials.accessToken}');
556+
googleCreds.value = credentials;
557+
cs.refreshContacts();
558+
} else {
559+
print('Sign in failed');
560+
}
561+
},
562+
trailing: const NextButton(),
563+
),
564+
if (ss.settings.contactSyncProvider.value == "Google" && googleCreds.value != null)
565+
SettingsTile(
566+
title: "Sign Out",
567+
onTap: () async {
568+
await pushService.googleSignIn.signOut();
569+
googleCreds.value = null;
570+
},
571+
trailing: const NextButton(),
572+
),
573+
if (ss.settings.contactSyncProvider.value == "CardDav")
574+
SettingsTile(
575+
title: "Set CardDav Server Details",
576+
onTap: () async {
577+
pushService.updateCardDav();
578+
},
579+
trailing: const NextButton(),
580+
),
581+
]
582+
)),
519583
SettingsHeader(
520584
iosSubtitle: iosSubtitle,
521585
materialSubtitle: materialSubtitle,

lib/app/layouts/setup/pages/rustpush/finalize.dart

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@ import 'dart:async';
22
import 'dart:convert';
33

44
import 'package:bluebubbles/app/layouts/conversation_list/pages/conversation_list.dart';
5+
import 'package:bluebubbles/app/layouts/settings/widgets/content/next_button.dart';
56
import 'package:bluebubbles/app/layouts/settings/widgets/content/settings_dropdown.dart';
67
import 'package:bluebubbles/app/layouts/settings/widgets/content/settings_switch.dart';
8+
import 'package:bluebubbles/app/layouts/settings/widgets/content/settings_tile.dart';
9+
import 'package:bluebubbles/app/layouts/settings/widgets/layout/settings_header.dart';
10+
import 'package:bluebubbles/app/layouts/settings/widgets/layout/settings_section.dart';
711
import 'package:bluebubbles/app/layouts/setup/pages/page_template.dart';
812
import 'package:bluebubbles/app/layouts/setup/setup_view.dart';
913
import 'package:bluebubbles/app/wrappers/stateful_boilerplate.dart';
1014
import 'package:bluebubbles/services/backend/settings/settings_service.dart';
1115
import 'package:bluebubbles/services/network/backend_service.dart';
16+
import 'package:bluebubbles/services/ui/contact_service.dart';
1217
import 'package:bluebubbles/src/rust/api/api.dart' as api;
1318
import 'package:bluebubbles/helpers/helpers.dart';
1419
import 'package:bluebubbles/services/rustpush/rustpush_service.dart';
@@ -17,6 +22,7 @@ import 'package:flutter/material.dart';
1722
import 'package:flutter/services.dart';
1823
import 'package:flutter_rust_bridge/flutter_rust_bridge.dart';
1924
import 'package:get/get.dart' hide Response;
25+
import 'package:google_sign_in_all_platforms/google_sign_in_all_platforms.dart';
2026
import 'package:telephony_plus/telephony_plus.dart';
2127

2228
class FinalizePage extends StatefulWidget {
@@ -28,6 +34,7 @@ class _FinalizePageState extends OptimizedState<FinalizePage> {
2834
final controller = Get.find<SetupViewController>();
2935

3036
List<String> handles = [];
37+
Rxn<GoogleSignInCredentials> googleCreds = Rxn(null);
3138

3239
@override
3340
void initState() {
@@ -37,6 +44,9 @@ class _FinalizePageState extends OptimizedState<FinalizePage> {
3744
handles = result;
3845
});
3946
});
47+
pushService.googleSignIn.signInOffline().then((state) {
48+
googleCreds.value = state;
49+
});
4050
}
4151

4252
@override
@@ -101,6 +111,56 @@ class _FinalizePageState extends OptimizedState<FinalizePage> {
101111
await backend.setDefaultHandle(value);
102112
},
103113
),
114+
if (kIsDesktop)
115+
SettingsOptions<String>(
116+
title: "Sync contacts with",
117+
initial: ss.settings.contactSyncProvider.value,
118+
clampWidth: false,
119+
options: ["iCloud", "Google", "CardDav"],
120+
secondaryColor: headerColor,
121+
useCupertino: false,
122+
textProcessing: (str) => str,
123+
capitalize: false,
124+
onChanged: (value) async {
125+
ss.settings.ctags.clear();
126+
ss.settings.tokens.clear();
127+
ss.settings.contactSyncProvider.value = value ?? "iCloud";
128+
ss.saveSettings();
129+
cs.refreshContacts();
130+
},
131+
),
132+
if (kIsDesktop && ss.settings.contactSyncProvider.value == "Google" && googleCreds.value == null)
133+
SettingsTile(
134+
title: "Sign In",
135+
onTap: () async {
136+
final credentials = await pushService.googleSignIn.signIn();
137+
if (credentials != null) {
138+
print('Signed in successfully: ${credentials.accessToken}');
139+
googleCreds.value = credentials;
140+
cs.refreshContacts();
141+
} else {
142+
print('Sign in failed');
143+
}
144+
},
145+
trailing: const NextButton(),
146+
),
147+
if (kIsDesktop && ss.settings.contactSyncProvider.value == "Google" && googleCreds.value != null)
148+
SettingsTile(
149+
title: "Sign Out",
150+
onTap: () async {
151+
await pushService.googleSignIn.signOut();
152+
googleCreds.value = null;
153+
},
154+
trailing: const NextButton(),
155+
),
156+
if (kIsDesktop && ss.settings.contactSyncProvider.value == "CardDav")
157+
SettingsTile(
158+
title: "Set CardDav Server Details",
159+
onTap: () async {
160+
pushService.updateCardDav();
161+
},
162+
trailing: const NextButton(),
163+
),
104164
],
105165
),
106166
),

lib/database/global/settings.dart

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,14 @@ class Settings {
214214
final RxBool attachmentSyncEnabled = false.obs;
215215
final RxnString keychainDefaultPassword = RxnString();
216216

217+
final RxMap<String, String?> ctags = <String, String?>{}.obs;
218+
final RxMap<String, String?> tokens = <String, String?>{}.obs;
219+
final RxString contactSyncProvider = "iCloud".obs;
220+
221+
final RxString cardDavServer = "".obs;
222+
final RxString cardDavUser = "".obs;
223+
final RxString cardDavPass = "".obs;
224+
217225

218226
Future<DisplayMode> getDisplayMode() async {
219227
List<DisplayMode> modes = await FlutterDisplayMode.supported;
@@ -412,6 +420,9 @@ class Settings {
412420
'replaceEmoticonsWithEmoji': replaceEmoticonsWithEmoji.value,
413421
'lastReviewRequestTimestamp': lastReviewRequestTimestamp.value,
414422
'defaultHandle': defaultHandle.value,
423+
'cardDavServer': cardDavServer.value,
424+
'cardDavUser': cardDavUser.value,
425+
'cardDavPass': cardDavPass.value,
415426
'macIsMine': macIsMine.value,
416427
'deviceIsHosted': deviceIsHosted.value,
417428
'hostedToken': hostedToken.value,
@@ -428,6 +439,7 @@ class Settings {
428439
'isTester': isTester.value,
429440
'cloudSyncingEnabled': cloudSyncingEnabled.value,
430441
'attachmentSyncEnabled': attachmentSyncEnabled.value,
442+
'contactSyncProvider': contactSyncProvider.value,
431443
};
432444
if (includeAll) {
433445
map.addAll({
@@ -446,6 +458,8 @@ class Settings {
446458
'receiveSoundPath': receiveSoundPath.value,
447459
'cachedCodes': cachedCodes,
448460
'smsIncomingTargets': smsForwardingTargets,
461+
'ctags': ctags,
462+
'tokens': tokens,
449463
});
450464
}
451465
return map;
@@ -582,6 +596,10 @@ class Settings {
582596
ss.settings.hideNamesForReactions.value = map['hideNamesForReactions'] ?? false;
583597
ss.settings.replaceEmoticonsWithEmoji.value = map['replaceEmoticonsWithEmoji'] ?? false;
584598
ss.settings.defaultHandle.value = map['defaultHandle'] ?? "";
599+
ss.settings.cardDavServer.value = map['cardDavServer'] ?? "";
600+
ss.settings.cardDavUser.value = map['cardDavUser'] ?? "";
601+
ss.settings.cardDavPass.value = map['cardDavPass'] ?? "";
602+
ss.settings.contactSyncProvider.value = map['contactSyncProvider'] ?? "iCloud";
585603
// ss.settings.macIsMine.value = map['macIsMine'] ?? true;
586604
// ss.settings.deviceIsHosted.value = map['deviceIsHosted'] ?? false;
587605
ss.settings.hostedToken.value = map['hostedToken'];
@@ -600,6 +618,8 @@ class Settings {
600618
ss.settings.isTester.value = map['isTester'] ?? false;
601619
ss.settings.cloudSyncingEnabled.value = map['cloudSyncingEnabled'] ?? false;
602620
ss.settings.attachmentSyncEnabled.value = map['attachmentSyncEnabled'] ?? false;
621+
ss.settings.ctags.value = map['ctags'] ?? {};
622+
ss.settings.tokens.value = map['tokens'] ?? {};
603623
ss.settings.save();
604624

605625
eventDispatcher.emit("theme-update", null);
@@ -750,6 +770,10 @@ class Settings {
750770
s.replaceEmoticonsWithEmoji.value = map['replaceEmoticonsWithEmoji'] ?? false;
751771
s.lastReviewRequestTimestamp.value = map['lastReviewRequestTimestamp'] ?? 0;
752772
s.defaultHandle.value = map['defaultHandle'] ?? "";
773+
s.cardDavServer.value = map['cardDavServer'] ?? "";
774+
s.cardDavUser.value = map['cardDavUser'] ?? "";
775+
s.cardDavPass.value = map['cardDavPass'] ?? "";
776+
s.contactSyncProvider.value = map['contactSyncProvider'] ?? "iCloud";
753777
s.macIsMine.value = map['macIsMine'] ?? true;
754778
s.deviceIsHosted.value = map['deviceIsHosted'] ?? false;
755779
s.hostedToken.value = map['hostedToken'];
@@ -768,6 +792,9 @@ class Settings {
768792
s.isTester.value = map['isTester'] ?? false;
769793
s.cloudSyncingEnabled.value = map['cloudSyncingEnabled'] ?? false;
770794
s.attachmentSyncEnabled.value = map['attachmentSyncEnabled'] ?? false;
795+
796+
s.ctags.value = map['ctags'] is String ? jsonDecode(map['ctags']).cast<String, String?>() : <String, String?>{};
797+
s.tokens.value = map['tokens'] is String ? jsonDecode(map['tokens']).cast<String, String?>() : <String, String?>{};
771798
return s;
772799
}
773800

0 commit comments

Comments
 (0)