diff --git a/lib/app/layouts/conversation_list/widgets/header/header_widgets.dart b/lib/app/layouts/conversation_list/widgets/header/header_widgets.dart index 11832f706b..83917c0083 100644 --- a/lib/app/layouts/conversation_list/widgets/header/header_widgets.dart +++ b/lib/app/layouts/conversation_list/widgets/header/header_widgets.dart @@ -173,7 +173,7 @@ class MaterialOverflowMenu extends StatelessWidget { style: context.textTheme.bodyLarge!.apply(color: context.theme.colorScheme.properOnSurface), ), ), - if (pushService.state?.icloudServices?.sharedstreams != null) + if (backend.supportsSharedStreams()) PopupMenuItem( value: 9, child: Text( @@ -188,7 +188,7 @@ class MaterialOverflowMenu extends StatelessWidget { style: context.textTheme.bodyLarge!.apply(color: context.theme.colorScheme.properOnSurface), ), ), - if (pushService.state?.icloudServices?.keychain != null) + if (backend.supportsKeychain()) PopupMenuItem( value: 11, child: Text( @@ -323,7 +323,7 @@ class CupertinoOverflowMenu extends StatelessWidget { icon: CupertinoIcons.location, onTap: () => goToFindMy(context), ), - if (pushService.state?.icloudServices?.sharedstreams != null) + if (backend.supportsSharedStreams()) PullDownMenuItem( title: 'Shared Albums', icon: CupertinoIcons.photo, @@ -334,7 +334,7 @@ class CupertinoOverflowMenu extends StatelessWidget { icon: CupertinoIcons.video_camera, onTap: () => goToFaceTime(context), ), - if (pushService.state?.icloudServices?.keychain != null) + if (backend.supportsKeychain()) PullDownMenuItem( title: 'Passwords', icon: Icons.key, diff --git a/lib/services/network/backend_service.dart b/lib/services/network/backend_service.dart index 3429736805..7f06179c80 100644 --- a/lib/services/network/backend_service.dart +++ b/lib/services/network/backend_service.dart @@ -45,6 +45,8 @@ abstract class BackendService { {void Function(int, int)? onReceiveProgress, CancelToken? cancelToken}); bool canSchedule(); bool supportsFindMy(); + bool supportsSharedStreams(); + bool supportsKeychain(); bool canCreateGroupChats(); bool supportsSmsForwarding(); void startedTyping(Chat c, [iMessageAppData? appdata]); diff --git a/lib/services/network/http_service.dart b/lib/services/network/http_service.dart index 5912e460dc..724c039851 100644 --- a/lib/services/network/http_service.dart +++ b/lib/services/network/http_service.dart @@ -276,6 +276,16 @@ class HttpBackend implements BackendService { return ss.isMinCatalinaSync; } + @override + bool supportsSharedStreams() { + return false; + } + + @override + bool supportsKeychain() { + return false; + } + @override bool canCreateGroupChats() { return ss.canCreateGroupChatSync(); diff --git a/lib/services/rustpush/rustpush_service.dart b/lib/services/rustpush/rustpush_service.dart index 6deb3c1e42..5c238fcc26 100644 --- a/lib/services/rustpush/rustpush_service.dart +++ b/lib/services/rustpush/rustpush_service.dart @@ -1265,7 +1265,17 @@ class RustPushBackend implements BackendService { @override bool supportsFindMy() { - return pushService.state?.icloudServices?.fmfd != null; + return pushService.hasFindMy; + } + + @override + bool supportsSharedStreams() { + return pushService.hasSharedStreams; + } + + @override + bool supportsKeychain() { + return pushService.hasKeychain; } @override @@ -1307,7 +1317,45 @@ class RustPushBackend implements BackendService { } class RustPushService extends GetxService { - api.SharedPushState? state; + api.SharedPushState? _state; + api.SharedPushState? get state => _state; + set state(api.SharedPushState? value) { + _state = value; + _cacheCapabilities(value); + } + + // Which iCloud services the account has is only known once the push state has + // loaded, which happens a few seconds after launch. Remember the last known + // answer so UI built before that (the overflow menu, for example) doesn't + // briefly hide Find My / Shared Streams / Passwords. + static const _capFindMy = "capFindMy"; + static const _capSharedStreams = "capSharedStreams"; + static const _capKeychain = "capKeychain"; + + void _cacheCapabilities(api.SharedPushState? value) { + if (value == null) { + ss.prefs.remove(_capFindMy); + ss.prefs.remove(_capSharedStreams); + ss.prefs.remove(_capKeychain); + return; + } + final services = value.icloudServices; + ss.prefs.setBool(_capFindMy, services?.fmfd != null); + ss.prefs.setBool(_capSharedStreams, services?.sharedstreams != null); + ss.prefs.setBool(_capKeychain, services?.keychain != null); + } + + bool get hasFindMy => _state != null + ? _state!.icloudServices?.fmfd != null + : ss.prefs.getBool(_capFindMy) ?? false; + + bool get hasSharedStreams => _state != null + ? _state!.icloudServices?.sharedstreams != null + : ss.prefs.getBool(_capSharedStreams) ?? false; + + bool get hasKeychain => _state != null + ? _state!.icloudServices?.keychain != null + : ss.prefs.getBool(_capKeychain) ?? false; Mixpanel? mixpanel;