Skip to content

Commit

Permalink
feat: Improved error handling (for instance : if crypto store cannot …
Browse files Browse the repository at this point in the history
…be loaded).
  • Loading branch information
Skyost committed Feb 7, 2025
1 parent 6af1af0 commit db48590
Show file tree
Hide file tree
Showing 17 changed files with 572 additions and 299 deletions.
12 changes: 11 additions & 1 deletion lib/i18n/en/app_unlock.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,15 @@
"enable": "Authenticate to enable local authentication.",
"disable": "Authenticate to disable local authentication."
},
"masterPasswordDialogMessage": "Please enter your master password to unlock the app."
"masterPasswordDialogMessage": "Please enter your master password to unlock the app.",
"cannotUnlock": {
"localAuthentication": {
"deviceNotSupported": "Cannot unlock the app because your device is not supported by local authentication. Disable this locking method with the button below.",
"button": "Disable"
},
"masterPassword": {
"noPasswordVerificationMethodAvailable": "Cannot unlock the app with your password because the app data integrity has been altered. Please change your master password to continue.\nNote that your TOTPs will still need to be decrypted manually.",
"button": "Change master password"
}
}
}
4 changes: 4 additions & 0 deletions lib/i18n/en/home.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,9 @@
"title": "Add manually",
"subtitle": "Manually enter your TOTP details (eg. secret, label, issuer, ...)."
}
},
"noCryptoStore": {
"message": "An error occurred while loading your crypto store. This means that we cannot do any encryption / decryption operation. Please restart the app or enter a new master password using the button below.",
"resetButton": "Reset master password"
}
}
12 changes: 11 additions & 1 deletion lib/i18n/fr/app_unlock.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,15 @@
"enable": "Authentifiez-vous pour activer l'authentification locale.",
"disable": "Authentifiez-vous pour désactiver l'authentification locale."
},
"masterPasswordDialogMessage": "Veuillez entrer votre mot de passe maître pour déverrouiller l'application."
"masterPasswordDialogMessage": "Veuillez entrer votre mot de passe maître pour déverrouiller l'application.",
"cannotUnlock": {
"localAuthentication": {
"deviceNotSupported": "Impossible de déverouiller l'application car l'authentification locale n'est pas supportée. Désactivez cette méthode de verrouillage avec le bouton ci-dessous.",
"button": "Désactiver"
},
"masterPassword": {
"noPasswordVerificationMethodAvailable": "Impossible de déverouiller l'application parce que l'intégrité des données de l'application a été altérée.\nVeuillez modifier votre mot de passe maître pour continuer.\nNotez que vos TOTPs devront tout de même être déchiffrés manuellement.",
"button": "Changer le mot de passe maître"
}
}
}
4 changes: 4 additions & 0 deletions lib/i18n/fr/home.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,9 @@
"title": "Ajouter manuellement",
"subtitle": "Entrer manuellement les détails du TOTP (ex. secret, étiquette, émetteur, ...)."
}
},
"noCryptoStore": {
"message": "Une erreur est survenue durant la lecture du crypto store. Cela veut dire qu'aucune opération de chiffrement / déchiffrement n'est possible. Veuillez redémarrer l'application ou entrer un nouveau mot de passe maître en utilisant le bouton ci-dessous.",
"resetButton": "Réinitialiser le mot de passe maître"
}
}
220 changes: 127 additions & 93 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'package:open_authenticator/i18n/translations.g.dart';
import 'package:open_authenticator/model/app_links.dart';
import 'package:open_authenticator/model/authentication/providers/email_link.dart';
import 'package:open_authenticator/model/authentication/providers/provider.dart';
import 'package:open_authenticator/model/crypto.dart';
import 'package:open_authenticator/model/settings/show_intro.dart';
import 'package:open_authenticator/model/settings/theme.dart';
import 'package:open_authenticator/model/totp/repository.dart';
Expand Down Expand Up @@ -102,108 +103,141 @@ class OpenAuthenticatorApp extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
AsyncValue<bool> showIntro = ref.watch(showIntroSettingsEntryProvider);
AsyncValue<ThemeMode> theme = ref.watch(themeSettingsEntryProvider);
Locale locale = TranslationProvider.of(context).flutterLocale;
return switch (showIntro) {
AsyncData(:bool value) => _createMaterialApp(
showIntroState: 'data',
theme: theme,
locale: locale,
initialRoute: value ? IntroPage.name : HomePage.name,
),
AsyncError(:final error) => _createMaterialApp(
showIntroState: 'error',
theme: theme,
locale: locale,
home: Center(
child: Text('Error : $error.'),
),
),
_ => _createMaterialApp(
showIntroState: 'loading',
theme: theme,
locale: locale,
home: const CenteredCircularProgressIndicator(),
),
};
}

/// Creates a [MaterialApp] widget.
Widget _createMaterialApp({
required String showIntroState,
required AsyncValue<ThemeMode> theme,
required Locale locale,
String? initialRoute,
Widget? home,
}) {
ColorScheme light = ColorScheme.fromSeed(
seedColor: Colors.green,
);
ColorScheme dark = ColorScheme.fromSeed(
seedColor: Colors.green,
brightness: Brightness.dark,
);
return switch (showIntro) {
AsyncData(:bool value) => MaterialApp(
title: App.appName,
locale: TranslationProvider.of(context).flutterLocale,
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: AppLocaleUtils.supportedLocales,
themeMode: theme.value,
darkTheme: ThemeData(
brightness: Brightness.dark,
appBarTheme: AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.light,
systemNavigationBarColor: dark.surface,
),
shape: const RoundedRectangleBorder(),
surfaceTintColor: Colors.green,
),
colorScheme: dark,
// iconButtonTheme: IconButtonThemeData(
// style: ButtonStyle(
// foregroundColor: MaterialStatePropertyAll(Colors.green.shade300),
// ),
// ),
buttonTheme: const ButtonThemeData(
alignedDropdown: true,
),
floatingActionButtonTheme: FloatingActionButtonThemeData(
shape: const CircleBorder(),
backgroundColor: Colors.green.shade700,
foregroundColor: Colors.green.shade50,
),
return MaterialApp(
key: ValueKey('materialApp.$showIntroState'),
title: App.appName,
locale: locale,
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: AppLocaleUtils.supportedLocales,
themeMode: theme.value,
darkTheme: ThemeData(
brightness: Brightness.dark,
appBarTheme: AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.light,
systemNavigationBarColor: dark.surface,
),
theme: ThemeData(
colorScheme: light,
appBarTheme: AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.dark,
systemNavigationBarColor: light.surface,
),
shape: const RoundedRectangleBorder(),
),
buttonTheme: const ButtonThemeData(
alignedDropdown: true,
),
floatingActionButtonTheme: FloatingActionButtonThemeData(
shape: const CircleBorder(),
backgroundColor: Colors.green.shade50,
foregroundColor: Colors.green.shade700,
),
inputDecorationTheme: InputDecorationTheme(
disabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey.shade400),
),
),
dividerTheme: const DividerThemeData(
color: Colors.black12,
),
shape: const RoundedRectangleBorder(),
surfaceTintColor: Colors.green,
),
colorScheme: dark,
// iconButtonTheme: IconButtonThemeData(
// style: ButtonStyle(
// foregroundColor: MaterialStatePropertyAll(Colors.green.shade300),
// ),
// ),
buttonTheme: const ButtonThemeData(
alignedDropdown: true,
),
floatingActionButtonTheme: FloatingActionButtonThemeData(
shape: const CircleBorder(),
backgroundColor: Colors.green.shade700,
foregroundColor: Colors.green.shade50,
),
),
theme: ThemeData(
colorScheme: light,
appBarTheme: AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.dark,
systemNavigationBarColor: light.surface,
),
routes: {
IntroPage.name: (_) => _RouteWidget(
child: const IntroPage(),
),
HomePage.name: (_) => _RouteWidget(
listen: currentPlatform.isMobile || kDebugMode,
rateMyApp: true,
child: const HomePage(),
),
ScanPage.name: (_) => const _RouteWidget(
child: ScanPage(),
),
SettingsPage.name: (_) => const _RouteWidget(
child: SettingsPage(),
),
TotpPage.name: (context) {
Map<String, dynamic>? arguments = ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>?;
return _RouteWidget(
child: TotpPage(
totp: arguments?[kRouteParameterTotp],
add: arguments?[kRouteParameterAddTotp],
),
);
},
ContributorPlanFallbackPaywallPage.name: (_) => const _RouteWidget(
child: ContributorPlanFallbackPaywallPage(),
),
},
initialRoute: value ? IntroPage.name : HomePage.name,
shape: const RoundedRectangleBorder(),
),
AsyncError(:final error) => Text('Error : $error.'),
_ => const CenteredCircularProgressIndicator(),
};
buttonTheme: const ButtonThemeData(
alignedDropdown: true,
),
floatingActionButtonTheme: FloatingActionButtonThemeData(
shape: const CircleBorder(),
backgroundColor: Colors.green.shade50,
foregroundColor: Colors.green.shade700,
),
inputDecorationTheme: InputDecorationTheme(
disabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey.shade400),
),
),
dividerTheme: const DividerThemeData(
color: Colors.black12,
),
),
routes: home == null
? {
IntroPage.name: (_) => _RouteWidget(
child: const IntroPage(),
),
HomePage.name: (_) => _RouteWidget(
listen: true,
rateMyApp: true,
child: const HomePage(),
),
ScanPage.name: (_) => const _RouteWidget(
child: ScanPage(),
),
SettingsPage.name: (_) => const _RouteWidget(
child: SettingsPage(),
),
TotpPage.name: (context) {
Map<String, dynamic>? arguments = ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>?;
return _RouteWidget(
child: TotpPage(
totp: arguments?[kRouteParameterTotp],
add: arguments?[kRouteParameterAddTotp],
),
);
},
ContributorPlanFallbackPaywallPage.name: (_) => const _RouteWidget(
child: ContributorPlanFallbackPaywallPage(),
),
}
: {},
initialRoute: home == null ? initialRoute : null,
home: home,
);
}
}

Expand All @@ -212,7 +246,7 @@ class _RouteWidget extends ConsumerStatefulWidget {
/// The route widget.
final Widget child;

/// Listen to [appLinksListenerProvider], [totpLimitProvider] and [appUnlockStateProvider].
/// Listen to [appLinksListenerProvider], [totpLimitProvider], [appUnlockStateProvider] and [cryptoStoreProvider].
final bool listen;

/// Whether to initialize and run RateMyApp.
Expand Down
Loading

0 comments on commit db48590

Please sign in to comment.