Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Device Calendar Sync #2072

Merged
merged 8 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changes/2072-calendar-sync.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- [New Labs] You can now ask Acter to sync the events you haven't declined yet to your system calendar (on certain devices) including a reminder. As of now, this is behind a labs-feature flag you need to activate manually while we are rolling this out.
3 changes: 3 additions & 0 deletions app/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

<uses-permission android:name="android.permission.READ_CALENDAR" android:minSdkVersion="14" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" android:minSdkVersion="14" />

<!-- File Picker Permissions issue on Android 10 https://github.com/miguelpruivo/flutter_file_picker/issues/1461 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
Expand Down
4 changes: 2 additions & 2 deletions app/integration_test/tests/bug_reporter.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:acter/features/bug_report/pages/bug_report_page.dart';
import 'package:acter/features/home/data/keys.dart';
import 'package:acter/features/home/pages/home_shell.dart';
import 'package:acter/config/app_shell.dart';
import 'package:acter/features/search/model/keys.dart';
import 'package:acter/router/router.dart';
import 'package:convenient_test_dev/convenient_test_dev.dart';
Expand Down Expand Up @@ -220,7 +220,7 @@ void bugReporterTests() {
final prevReports = await latestReported();
// totally clean
await t.freshAccount();
final HomeShellState home = t.tester.state(find.byKey(homeShellKey));
final AppShellState home = t.tester.state(find.byKey(appShellKey));
// as if we shaked
openBugReport(home.context);

Expand Down
29 changes: 26 additions & 3 deletions app/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ PODS:
- connectivity_plus (0.0.1):
- Flutter
- FlutterMacOS
- device_calendar (0.0.1):
- Flutter
- device_info_plus (0.0.1):
- Flutter
- DKImagePickerController/Core (4.3.4):
Expand Down Expand Up @@ -86,6 +88,8 @@ PODS:
- Flutter
- media_kit_video (0.0.1):
- Flutter
- open_filex (0.0.2):
- Flutter
- package_info (0.0.1):
- Flutter
- package_info_plus (0.4.5):
Expand All @@ -105,6 +109,11 @@ PODS:
- SDWebImage/Core (5.13.0)
- sensors_plus (0.0.1):
- Flutter
- Sentry/HybridSDK (8.30.1)
- sentry_flutter (8.4.0):
- Flutter
- FlutterMacOS
- Sentry/HybridSDK (= 8.30.1)
- share_plus (0.0.1):
- Flutter
- shared_preferences_foundation (0.0.1):
Expand All @@ -128,6 +137,7 @@ DEPENDENCIES:
- acter_flutter_sdk (from `.symlinks/plugins/acter_flutter_sdk/ios`)
- app_settings (from `.symlinks/plugins/app_settings/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`)
- device_calendar (from `.symlinks/plugins/device_calendar/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- emoji_picker_flutter (from `.symlinks/plugins/emoji_picker_flutter/ios`)
- fc_native_video_thumbnail (from `.symlinks/plugins/fc_native_video_thumbnail/darwin`)
Expand All @@ -143,13 +153,15 @@ DEPENDENCIES:
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
- media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`)
- media_kit_video (from `.symlinks/plugins/media_kit_video/ios`)
- open_filex (from `.symlinks/plugins/open_filex/ios`)
- package_info (from `.symlinks/plugins/package_info/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- pointer_interceptor_ios (from `.symlinks/plugins/pointer_interceptor_ios/ios`)
- push_ios (from `.symlinks/plugins/push_ios/ios`)
- screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`)
- sensors_plus (from `.symlinks/plugins/sensors_plus/ios`)
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
Expand All @@ -168,6 +180,7 @@ SPEC REPOS:
- GoogleUtilities
- PromisesObjC
- SDWebImage
- Sentry
- SwiftyGif

EXTERNAL SOURCES:
Expand All @@ -177,6 +190,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/app_settings/ios"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/darwin"
device_calendar:
:path: ".symlinks/plugins/device_calendar/ios"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
emoji_picker_flutter:
Expand Down Expand Up @@ -207,6 +222,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/media_kit_native_event_loop/ios"
media_kit_video:
:path: ".symlinks/plugins/media_kit_video/ios"
open_filex:
:path: ".symlinks/plugins/open_filex/ios"
package_info:
:path: ".symlinks/plugins/package_info/ios"
package_info_plus:
Expand All @@ -221,6 +238,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/screen_brightness_ios/ios"
sensors_plus:
:path: ".symlinks/plugins/sensors_plus/ios"
sentry_flutter:
:path: ".symlinks/plugins/sentry_flutter/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation:
Expand All @@ -240,6 +259,7 @@ SPEC CHECKSUMS:
acter_flutter_sdk: e60481171e46975418babb7d0ec8809eaacaaa03
app_settings: 017320c6a680cdc94c799949d95b84cb69389ebc
connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db
device_calendar: 9cb33f88a02e19652ec7b8b122ca778f751b1f7b
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
Expand All @@ -256,11 +276,12 @@ SPEC CHECKSUMS:
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
keyboard_height_plugin: 43fa8bba20fd5c4fdeed5076466b8b9d43cc6b86
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e
open_filex: 6e26e659846ec990262224a12ef1c528bb4edbe4
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
Expand All @@ -269,7 +290,9 @@ SPEC CHECKSUMS:
push_ios: 2bd1b4d3f782209da1f571db1250af236957e807
screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625
SDWebImage: 0327043dbb9533e75f2eff8445b3df0f2ceca6ac
sensors_plus: 5717760720f7e6acd96fdbd75b7428f5ad755ec2
sensors_plus: 4b7c4bd9c9be2efbd575649368fe77ef09b429ad
Sentry: 514a3ea653886e9a48c6287d8b7bf05ec24bf3be
sentry_flutter: edc037f7af0dc1512d6c33a5c2c7c838bd0d6806
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
Expand All @@ -281,4 +304,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: c4c93c5f6502fe2754f48404d3594bf779584011

COCOAPODS: 1.14.3
COCOAPODS: 1.15.2
2 changes: 1 addition & 1 deletion app/ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import UIKit
import Flutter

@UIApplicationMain
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
Expand Down
6 changes: 6 additions & 0 deletions app/ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@
<string>Record messages with Acter to share via Chat or in Spaces with others.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Allows you to select photos from your photo library to be uploaded into Acter and shared via Chat or in Spaces with others.</string>
<key>NSCalendarsUsageDescription</key>
<string>Access most functions for calendar viewing and editing.</string>
<key>NSContactsUsageDescription</key>
<string>Access contacts for event attendee editing.</string>
<key>NSCalendarsFullAccessUsageDescription</key>
<string>Access most functions for calendar viewing and editing.</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UIBackgroundModes</key>
Expand Down
16 changes: 0 additions & 16 deletions app/lib/common/utils/feature_flagger.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:riverpod/riverpod.dart';

final _log = Logger('a3::common::feature_flag');

Expand Down Expand Up @@ -81,18 +80,3 @@ class Features<T extends Enum> {
return Features(flags: newFlags, defaultOn: defaultOn);
}
}

class FeaturesNotifier<T extends Enum> extends StateNotifier<Features<T>> {
FeaturesNotifier(super.initState);

// Let's the UI update the state of a flag
void setActive(T f, bool active) {
state = state.updateFlag(f, active);
_log.info('updated ${state.toJson()}');
}

// Allow higher level to reset the features flagged
void resetFeatures(List<FeatureFlag<T>> features) {
state = Features(flags: features, defaultOn: state.defaultOn);
}
}
3 changes: 3 additions & 0 deletions app/lib/common/utils/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,9 @@ enum LabsFeature {
// specific features
chatUnread,

// system features
deviceCalendarSync,

// not a lab anymore but needs to stay for backwards compat
tasks,
events,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import 'package:acter/common/dialogs/logout_confirmation.dart';
import 'package:acter/config/notifications/init.dart';
import 'package:acter/common/providers/keyboard_visbility_provider.dart';
import 'package:acter/common/tutorial_dialogs/bottom_navigation_tutorials/bottom_navigation_tutorials.dart';
import 'package:acter/common/utils/constants.dart';
import 'package:acter/common/utils/device.dart';
import 'package:acter/common/utils/routes.dart';
import 'package:acter/common/utils/utils.dart';
import 'package:acter/features/auth/pages/logged_out_screen.dart';
import 'package:acter/features/calendar_sync/calendar_sync.dart';
import 'package:acter/features/cross_signing/widgets/cross_signing.dart';
import 'package:acter/features/home/providers/client_providers.dart';
import 'package:acter/features/home/providers/navigation.dart';
Expand All @@ -19,7 +20,6 @@ import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart';
import 'package:logging/logging.dart';
import 'package:screenshot/screenshot.dart';
Expand Down Expand Up @@ -54,29 +54,38 @@ Future<void> openBugReport(BuildContext context) async {
}
}

class HomeShell extends ConsumerStatefulWidget {
class AppShell extends ConsumerStatefulWidget {
final StatefulNavigationShell navigationShell;

const HomeShell({super.key, required this.navigationShell});
const AppShell({super.key, required this.navigationShell});

@override
ConsumerState<ConsumerStatefulWidget> createState() => HomeShellState();
ConsumerState<ConsumerStatefulWidget> createState() => AppShellState();
}

class HomeShellState extends ConsumerState<HomeShell> {
class AppShellState extends ConsumerState<AppShell> {
final GlobalKey<ScaffoldState> _key =
GlobalKey<ScaffoldState>(debugLabel: 'home shell scaffold');
late ShakeDetector detector;

@override
void initState() {
super.initState();
initShake();
initNotifications();
_init();
}

Future<void> _init() async {
// no wait goes there
Future.delayed(
const Duration(seconds: 1),
() => bottomNavigationTutorials(context: context),
);
initShake();

// these want to be sure to execute in order
await initNotifications();
// calendar sync
await initCalendarSync();
}

Future<void> initShake() async {
Expand Down Expand Up @@ -113,64 +122,6 @@ class HomeShellState extends ConsumerState<HomeShell> {
setupPushNotifications(client);
}

Widget buildLoggedOutScreen(BuildContext context, bool softLogout) {
// We have a special case
return Scaffold(
body: Container(
margin: const EdgeInsets.only(top: kToolbarHeight),
child: Center(
child: Column(
children: [
Container(
margin: const EdgeInsets.symmetric(vertical: 15),
height: 100,
width: 100,
child: SvgPicture.asset(
'assets/images/undraw_access_denied_re_awnf.svg',
),
),
Container(
margin: const EdgeInsets.symmetric(vertical: 15),
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
text: L10n.of(context).access,
style: Theme.of(context).textTheme.headlineLarge,
children: <TextSpan>[
TextSpan(
text: ' ${L10n.of(context).denied}',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.error,
fontSize: 32,
),
),
],
),
),
),
Container(
margin: const EdgeInsets.symmetric(vertical: 15),
child: Text(
L10n.of(context).yourSessionHasBeenTerminatedByServer,
),
),
softLogout
? OutlinedButton(
onPressed: onLoginAgain,
child: Text(L10n.of(context).loginAgain),
)
: OutlinedButton(
onPressed: onClearDB,
child: Text(L10n.of(context).clearDBAndReLogin),
),
],
),
),
),
);
}

@override
Widget build(BuildContext context) {
// get platform of context.
Expand All @@ -187,7 +138,7 @@ class HomeShellState extends ConsumerState<HomeShell> {
if (errorMsg != null) {
final softLogout = errorMsg == 'SoftLogout';
if (softLogout || errorMsg == 'Unauthorized') {
return buildLoggedOutScreen(context, softLogout);
return LoggedOutScreen(softLogout: softLogout);
}
}

Expand Down Expand Up @@ -219,15 +170,6 @@ class HomeShellState extends ConsumerState<HomeShell> {
);
}

void onLoginAgain() {
// FIXME: not yet properly supported
context.goNamed(Routes.intro.name);
}

void onClearDB() {
logoutConfirmationDialog(context, ref);
}

Widget topNavigationWidget(BuildContext context) {
final hasFirstSynced =
ref.watch(syncStateProvider.select((v) => !v.initialSync));
Expand Down
Loading
Loading