Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/alexandrim0/keyper
Browse files Browse the repository at this point in the history
  • Loading branch information
dmitrsosnin committed Dec 26, 2023
2 parents 98e2c65 + aebf795 commit e9fbd0d
Show file tree
Hide file tree
Showing 15 changed files with 275 additions and 124 deletions.
68 changes: 36 additions & 32 deletions lib/app/app.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import 'package:get_it/get_it.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:sentry_flutter/sentry_flutter.dart';

import 'package:guardian_keyper/ui/theme/theme.dart';
import 'package:guardian_keyper/ui/widgets/splash.dart';
import 'package:guardian_keyper/ui/utils/theme_mode_mapper.dart';
import 'package:guardian_keyper/ui/utils/current_route_observer.dart';

import 'package:guardian_keyper/feature/home/ui/home_screen.dart';
import 'package:guardian_keyper/feature/settings/domain/use_case/settings_theme_case.dart';

import 'di.dart';
import 'routes.dart';
import 'lifecycler.dart';

class App extends StatelessWidget with ThemeModeMapper {
const App({
Expand All @@ -23,37 +25,39 @@ class App extends StatelessWidget with ThemeModeMapper {
@override
Widget build(BuildContext context) => FutureBuilder(
future: di.init(),
builder: (context, state) {
// TBD: light color scheme
if (di.isNotInited) return const Splash(brightness: Brightness.dark);
final themeModeHandler = SettingsThemeCase();
final sentryNavigatorObserver = SentryNavigatorObserver();
return StreamBuilder<ThemeMode>(
builder: (context, state) => di.isNotInited
// TBD: light color scheme
// initialData: mapBoolToThemeMode(themeModeHandler.isDarkMode),
initialData: ThemeMode.dark,
stream: themeModeHandler.events.map<ThemeMode>(mapBoolToThemeMode),
builder: (context, snapshot) {
SystemChrome.setSystemUIOverlayStyle(switch (snapshot.data) {
ThemeMode.dark => systemStyleDark,
ThemeMode.light => systemStyleLight,
_ =>
MediaQuery.of(context).platformBrightness == Brightness.dark
? systemStyleDark
: systemStyleLight,
});
return MaterialApp(
title: 'Guardian Keyper',
theme: themeLight,
darkTheme: themeDark,
themeMode: snapshot.data,
debugShowCheckedModeBanner: false,
navigatorObservers: [sentryNavigatorObserver],
routes: routes,
home: const HomeScreen(),
);
},
);
},
? const Splash(brightness: Brightness.dark)
: StreamBuilder<ThemeMode>(
// TBD: light color scheme
// initialData: mapBoolToThemeMode(themeModeHandler.isDarkMode),
initialData: ThemeMode.dark,
stream: SettingsThemeCase()
.events
.map<ThemeMode>(mapBoolToThemeMode),
builder: (context, snapshot) {
SystemChrome.setSystemUIOverlayStyle(switch (snapshot.data) {
ThemeMode.dark => systemStyleDark,
ThemeMode.light => systemStyleLight,
_ => MediaQuery.of(context).platformBrightness ==
Brightness.dark
? systemStyleDark
: systemStyleLight,
});
return MaterialApp(
title: 'Guardian Keyper',
routes: routes,
theme: themeLight,
darkTheme: themeDark,
themeMode: snapshot.data,
debugShowCheckedModeBanner: false,
navigatorObservers: [
GetIt.I<CurrentRouteObserver>(),
GetIt.I<SentryNavigatorObserver>(),
],
home: const Lifecycler(key: Key('AppLifecycler')),
);
},
),
);
}
12 changes: 11 additions & 1 deletion lib/app/di.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import 'dart:typed_data';
import 'package:hive/hive.dart';
import 'package:sentry_flutter/sentry_flutter.dart';

import 'package:guardian_keyper/data/services/platform_service.dart';
import 'package:guardian_keyper/data/services/analytics_service.dart';
import 'package:guardian_keyper/data/services/preferences_service.dart';
import 'package:guardian_keyper/data/repositories/settings_repository.dart';
import 'package:guardian_keyper/ui/utils/current_route_observer.dart';

import 'package:guardian_keyper/feature/auth/data/auth_manager.dart';
import 'package:guardian_keyper/feature/network/data/network_manager.dart';
Expand All @@ -30,7 +32,15 @@ class DI {
final preferences = await PreferencesService().init();
GetIt.I.registerSingleton<PreferencesService>(preferences);
GetIt.I.registerSingleton<PlatformService>(PlatformService());
GetIt.I.registerSingleton<AnalyticsService>(await AnalyticsService.init());
GetIt.I.registerSingleton<SentryNavigatorObserver>(
SentryNavigatorObserver(),
);
GetIt.I.registerSingleton<CurrentRouteObserver>(
CurrentRouteObserver(),
);
GetIt.I.registerSingleton<AnalyticsService>(
await AnalyticsService.init(),
);

// Managers
GetIt.I.registerSingleton<AuthManager>(
Expand Down
122 changes: 122 additions & 0 deletions lib/app/lifecycler.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import 'dart:ui';
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';

import 'package:guardian_keyper/app/routes.dart';
import 'package:guardian_keyper/ui/utils/current_route_observer.dart';

import 'package:guardian_keyper/feature/auth/data/auth_manager.dart';
import 'package:guardian_keyper/feature/network/data/network_manager.dart';
import 'package:guardian_keyper/feature/message/data/message_repository.dart';
import 'package:guardian_keyper/feature/message/domain/use_case/message_interactor.dart';

import 'package:guardian_keyper/feature/home/ui/home_screen.dart';
import 'package:guardian_keyper/feature/auth/ui/dialogs/on_demand_auth_dialog.dart';
import 'package:guardian_keyper/feature/message/ui/dialogs/on_message_active_dialog.dart';

class Lifecycler extends StatefulWidget {
const Lifecycler({super.key});

@override
State<Lifecycler> createState() => _LifecyclerState();
}

class _LifecyclerState extends State<Lifecycler>
with WidgetsBindingObserver, RouteAware {
final _authManager = GetIt.I<AuthManager>();
final _networkManager = GetIt.I<NetworkManager>();
final _routeObserver = GetIt.I<CurrentRouteObserver>();
final _messageInteractor = GetIt.I<MessageInteractor>();

late final StreamSubscription<MessageRepositoryEvent> _requestsStream;

bool _canShowNotification = true;

@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
// Listen network requests
_requestsStream = _messageInteractor.watch().listen(
(e) {
if (e.isDeleted) return;
if (_canShowNotification && e.message!.isReceived) {
_canShowNotification = false;
OnMessageActiveDialog.show(
context,
message: e.message!,
).then((_) => _canShowNotification = true);
}
},
);
// Do stuff on start
Future.microtask(() async {
if (_authManager.passCode.isEmpty) {
await Navigator.of(context).pushNamed(routeIntro);
} else {
unawaited(_messageInteractor.pruneMessages());
await OnDemandAuthDialog.show(context);
}
await _networkManager.start();
});
}

@override
void didChangeDependencies() {
super.didChangeDependencies();
_routeObserver.subscribe(this, ModalRoute.of(context)!);
}

@override
void didPushNext() {
_canShowNotification = false;
if (kDebugMode) print('Can Show Message: $_canShowNotification');
}

@override
void didPopNext() {
_canShowNotification = true;
if (kDebugMode) print('Can Show Message: $_canShowNotification');
}

// @override
// void didChangeAppLifecycleState(AppLifecycleState state) {
// switch (state) {
// case AppLifecycleState.resumed:
// case AppLifecycleState.paused:
// case _:
// }
// super.didChangeAppLifecycleState(state);
// }

@override
void didHaveMemoryPressure() {
if (kDebugMode) print('didHaveMemoryPressure');
super.didHaveMemoryPressure();
}

@override
Future<bool> didPopRoute() {
if (kDebugMode) print('didPopRoute');
return super.didPopRoute();
}

@override
Future<AppExitResponse> didRequestAppExit() {
if (kDebugMode) print('didRequestAppExit');
return super.didRequestAppExit();
}

@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_routeObserver.unsubscribe(this);
_requestsStream.cancel();
super.dispose();
}

@override
Widget build(BuildContext context) => const HomeScreen();
}
3 changes: 3 additions & 0 deletions lib/app/routes.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:guardian_keyper/feature/intro/ui/intro_screen.dart';
import 'package:guardian_keyper/feature/dev_panel/dev_panel_screen.dart';
import 'package:guardian_keyper/feature/settings/ui/settings_screen.dart';
import 'package:guardian_keyper/feature/vault/ui/_vault_show/vault_show_screen.dart';
import 'package:guardian_keyper/feature/vault/ui/_shard_show/shard_show_screen.dart';
Expand All @@ -17,9 +18,11 @@ const routeShardShow = '/vault/shard/show';
const routeVaultSecretAdd = '/vault/secret/add';
const routeVaultGuardianAdd = '/vault/guardian/add';
const routeVaultSecretRecovery = '/vault/secret/recovery';
const routeDevPanel = DevPanelScreen.route;

final routes = {
routeIntro: (_) => const IntroScreen(),
routeDevPanel: (_) => const DevPanelScreen(),
routeSettings: (_) => const SettingsScreen(),
routeShardShow: (_) => const ShardShowScreen(),
routeVaultShow: (_) => const VaultShowScreen(),
Expand Down
2 changes: 2 additions & 0 deletions lib/feature/dev_panel/dev_panel_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import 'package:guardian_keyper/feature/message/domain/entity/message_model.dart
import 'package:guardian_keyper/feature/message/ui/dialogs/on_message_active_dialog.dart';

class DevPanelScreen extends StatelessWidget {
static const route = '/dev_panel';

const DevPanelScreen({super.key});

@override
Expand Down
52 changes: 1 addition & 51 deletions lib/feature/home/ui/home_screen.dart
Original file line number Diff line number Diff line change
@@ -1,21 +1,12 @@
import 'dart:async';

import 'package:guardian_keyper/consts.dart';
import 'package:guardian_keyper/app/routes.dart';
import 'package:guardian_keyper/ui/widgets/common.dart';
import 'package:guardian_keyper/ui/utils/screen_size.dart';
import 'package:guardian_keyper/ui/dialogs/qr_code_show_dialog.dart';

import 'package:guardian_keyper/feature/auth/data/auth_manager.dart';
import 'package:guardian_keyper/feature/network/data/network_manager.dart';
import 'package:guardian_keyper/feature/message/data/message_repository.dart';
import 'package:guardian_keyper/feature/message/domain/use_case/message_interactor.dart';

import 'package:guardian_keyper/feature/vault/ui/widgets/shards_list.dart';
import 'package:guardian_keyper/feature/vault/ui/widgets/vaults_list.dart';
import 'package:guardian_keyper/feature/message/ui/widgets/requests_list.dart';
import 'package:guardian_keyper/feature/auth/ui/dialogs/on_demand_auth_dialog.dart';
import 'package:guardian_keyper/feature/message/ui/dialogs/on_message_active_dialog.dart';

import 'widgets/dashboard_list.dart';
import 'utils/build_navbar_items.dart';
Expand Down Expand Up @@ -43,33 +34,15 @@ class HomeScreenState extends State<HomeScreen> {
];

final _tabsBucket = PageStorageBucket();
final _authManager = GetIt.I<AuthManager>();
final _networkManager = GetIt.I<NetworkManager>();
final _messageInteractor = GetIt.I<MessageInteractor>();

late final _navBarItems = buildNavbarItems(context);

late final _isTitleVisible =
MediaQuery.of(context).size.height >= ScreenMedium.height;

late final StreamSubscription<MessageRepositoryEvent> _requestsStream;

int _currentTab = 0;
bool _canShowMessage = true;
DateTime _lastExitTryAt = DateTime.timestamp();

@override
void initState() {
super.initState();
_requestsStream = _messageInteractor.watch().listen(_onRequest);
Future.microtask(_onFirstStart);
}

@override
Future<void> dispose() async {
await _requestsStream.cancel();
super.dispose();
}
int _currentTab = 0;

@override
Widget build(BuildContext context) => ScaffoldSafe(
Expand All @@ -89,29 +62,6 @@ class HomeScreenState extends State<HomeScreen> {
),
);

void _onRequest(MessageRepositoryEvent event) {
if (event.isDeleted) return;
if (!_canShowMessage) return;
if (event.message!.isNotReceived) return;
final routeName = ModalRoute.of(context)?.settings.name;
if (routeName == '/' || routeName == QRCodeShowDialog.route) {
_canShowMessage = false;
Navigator.of(context).popUntil((r) => r.isFirst);
OnMessageActiveDialog.show(context, message: event.message!)
.then((_) => _canShowMessage = true);
}
}

Future<void> _onFirstStart() async {
if (_authManager.passCode.isEmpty) {
await Navigator.of(context).pushNamed(routeIntro);
} else {
unawaited(_messageInteractor.pruneMessages());
await OnDemandAuthDialog.show(context);
}
await _networkManager.start();
}

Future<bool> _onWillPop() async {
final now = DateTime.timestamp();
if (_lastExitTryAt.isAfter(now.subtract(snackBarDuration))) {
Expand Down
Loading

0 comments on commit e9fbd0d

Please sign in to comment.