diff --git a/lib/features/did/did_provider.dart b/lib/features/did/did_provider.dart index 25635a4a..a9b34f3c 100644 --- a/lib/features/did/did_provider.dart +++ b/lib/features/did/did_provider.dart @@ -1,4 +1,5 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:web5/web5.dart'; -final didProvider = Provider((ref) => throw UnimplementedError()); +final didProvider = + StateProvider((ref) => throw UnimplementedError()); diff --git a/lib/features/did/did_qr_code_page.dart b/lib/features/did/did_qr_code_page.dart index d46fafb8..2e1a2971 100644 --- a/lib/features/did/did_qr_code_page.dart +++ b/lib/features/did/did_qr_code_page.dart @@ -2,6 +2,10 @@ import 'dart:math'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:didpay/features/did/did_provider.dart'; +import 'package:didpay/features/did/did_storage_service.dart'; +import 'package:didpay/features/vcs/vcs_notifier.dart'; +import 'package:didpay/l10n/app_localizations.dart'; +import 'package:didpay/shared/confirm_dialog.dart'; import 'package:didpay/shared/theme/grid.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -14,6 +18,7 @@ class DidQrCodePage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final did = ref.watch(didProvider); + final didStorageService = ref.watch(didServiceProvider); const maxSize = 400.0; final screenSize = MediaQuery.of(context).size; @@ -51,16 +56,50 @@ class DidQrCodePage extends HookConsumerWidget { left: (screenSize.width - qrSize) / 2, top: offsetY + qrSize + Grid.sm, width: qrSize, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: Grid.sm), - child: AutoSizeText( - 'Scan to pay $dap', - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.bold, - ), - maxLines: 2, - textAlign: TextAlign.center, - ), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: Grid.sm), + child: AutoSizeText( + did.uri, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + maxLines: 2, + textAlign: TextAlign.center, + ), + ), + const SizedBox(height: Grid.sm), + FilledButton( + onPressed: () { + showDialog( + context: context, + builder: (context) => ConfirmDialog( + title: Loc.of(context).areYouSure, + description: Loc.of(context) + .allOfYourCredentialsWillAlsoBeDeleted, + confirmText: Loc.of(context).regenerate, + cancelText: Loc.of(context).goBack, + onConfirm: () async { + final newDid = + await didStorageService.regenerateDid(); + ref.read(didProvider.notifier).state = newDid; + + await ref.read(vcsProvider.notifier).reset(); + + if (context.mounted) { + Navigator.of(context).pop(); + } + }, + onCancel: () async { + Navigator.of(context).pop(); + }, + ), + ); + }, + child: const Text('Regenerate DID'), + ), + ], ), ), ], diff --git a/lib/features/did/did_qr_tabs.dart b/lib/features/did/did_qr_tabs.dart index 3838f471..8e3a5192 100644 --- a/lib/features/did/did_qr_tabs.dart +++ b/lib/features/did/did_qr_tabs.dart @@ -54,7 +54,7 @@ class DidQrTabs extends HookWidget { Theme.of(context).colorScheme.onBackground, tabs: [ Tab(text: Loc.of(context).scan), - Tab(text: Loc.of(context).myDap), + Tab(text: Loc.of(context).myDid), ], ), ), diff --git a/lib/features/did/did_storage_service.dart b/lib/features/did/did_storage_service.dart index dd5cc616..cd658363 100644 --- a/lib/features/did/did_storage_service.dart +++ b/lib/features/did/did_storage_service.dart @@ -2,8 +2,12 @@ import 'dart:convert'; import 'package:didpay/shared/constants.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:web5/web5.dart'; +final didServiceProvider = + Provider((ref) => throw UnimplementedError()); + class DidStorageService { final FlutterSecureStorage storage; @@ -29,4 +33,18 @@ class DidStorageService { ); return did; } + + Future regenerateDid() async { + await storage.delete(key: Constants.portableDidKey); + + final newDid = await DidDht.create(publish: true); + final portableDid = await newDid.export(); + final portableDidJson = jsonEncode(portableDid.map); + + await storage.write( + key: Constants.portableDidKey, + value: portableDidJson, + ); + return newDid; + } } diff --git a/lib/features/vcs/vcs_notifier.dart b/lib/features/vcs/vcs_notifier.dart index 404283c8..150bd08f 100644 --- a/lib/features/vcs/vcs_notifier.dart +++ b/lib/features/vcs/vcs_notifier.dart @@ -31,6 +31,11 @@ class VcsNotifier extends StateNotifier> { await _save(); } + Future reset() async { + state = []; + await box.clear(); + } + Future _save() async { await box.put(storageKey, state); } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 41db4701..582422fd 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -63,6 +63,7 @@ "noDidQrCodeFound": "No DID QR Code found", "noDapQrCodeFound": "No DAP QR Code found", "myDap": "My DAP", + "myDid": "My DID", "myVc": "My VC", "vcNotFound": "VC not found", "copiedDid": "Copied DID!", @@ -166,5 +167,9 @@ "ifYouExitNow": "If you exit now, you'll lose all your progress", "enterADap": "Enter a Decentralized Agnostic Paytag (DAP)", "verifyingDap": "Verifying DAP...", - "placeholderDap": "@username/didpay.me" + "placeholderDap": "@username/didpay.me", + "areYouSure": "Are you sure?", + "allOfYourCredentialsWillAlsoBeDeleted": "All of your credentials will also be deleted", + "regenerate": "Regenerate", + "goBack": "Go back" } \ No newline at end of file diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 80e7f24b..c37ee0bc 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -373,6 +373,12 @@ abstract class Loc { /// **'My DAP'** String get myDap; + /// No description provided for @myDid. + /// + /// In en, this message translates to: + /// **'My DID'** + String get myDid; + /// No description provided for @myVc. /// /// In en, this message translates to: @@ -864,6 +870,30 @@ abstract class Loc { /// In en, this message translates to: /// **'@username/didpay.me'** String get placeholderDap; + + /// No description provided for @areYouSure. + /// + /// In en, this message translates to: + /// **'Are you sure?'** + String get areYouSure; + + /// No description provided for @allOfYourCredentialsWillAlsoBeDeleted. + /// + /// In en, this message translates to: + /// **'All of your credentials will also be deleted'** + String get allOfYourCredentialsWillAlsoBeDeleted; + + /// No description provided for @regenerate. + /// + /// In en, this message translates to: + /// **'Regenerate'** + String get regenerate; + + /// No description provided for @goBack. + /// + /// In en, this message translates to: + /// **'Go back'** + String get goBack; } class _LocDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 9f4c092a..09b0de42 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -149,6 +149,9 @@ class LocEn extends Loc { @override String get myDap => 'My DAP'; + @override + String get myDid => 'My DID'; + @override String get myVc => 'My VC'; @@ -400,4 +403,16 @@ class LocEn extends Loc { @override String get placeholderDap => '@username/didpay.me'; + + @override + String get areYouSure => 'Are you sure?'; + + @override + String get allOfYourCredentialsWillAlsoBeDeleted => 'All of your credentials will also be deleted'; + + @override + String get regenerate => 'Regenerate'; + + @override + String get goBack => 'Go back'; } diff --git a/lib/main.dart b/lib/main.dart index 05b5f28a..99578276 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -40,7 +40,8 @@ void main() async { overrides: [ sharedPreferencesProvider.overrideWithValue(sharedPreferences), secureStorageProvider.overrideWithValue(secureStorage), - didProvider.overrideWithValue(did), + didServiceProvider.overrideWithValue(didService), + didProvider.overrideWith((ref) => did), ...overrides, ], child: const App(), diff --git a/lib/shared/exit_dialog.dart b/lib/shared/confirm_dialog.dart similarity index 86% rename from lib/shared/exit_dialog.dart rename to lib/shared/confirm_dialog.dart index a9c97b21..7961ecb7 100644 --- a/lib/shared/exit_dialog.dart +++ b/lib/shared/confirm_dialog.dart @@ -1,19 +1,22 @@ -import 'package:didpay/l10n/app_localizations.dart'; import 'package:didpay/shared/theme/grid.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; -class ExitDialog extends HookWidget { +class ConfirmDialog extends HookWidget { final String title; final String description; - final Future Function() onExit; - final Future Function() onStay; + final String confirmText; + final String cancelText; + final Future Function() onConfirm; + final Future Function() onCancel; - const ExitDialog({ + const ConfirmDialog({ required this.title, required this.description, - required this.onExit, - required this.onStay, + required this.confirmText, + required this.cancelText, + required this.onConfirm, + required this.onCancel, super.key, }); @@ -52,7 +55,7 @@ class ExitDialog extends HookWidget { ), ), ), - onPressed: () async => onStay(), + onPressed: () async => onCancel(), child: Container( alignment: Alignment.center, padding: const EdgeInsets.symmetric(vertical: Grid.sm), @@ -67,7 +70,7 @@ class ExitDialog extends HookWidget { ), ), child: Text( - Loc.of(context).stay, + cancelText, style: TextStyle( color: Theme.of(context).colorScheme.outline, ), @@ -85,12 +88,12 @@ class ExitDialog extends HookWidget { ), ), ), - onPressed: () async => onExit(), + onPressed: () async => onConfirm(), child: Container( alignment: Alignment.center, padding: const EdgeInsets.symmetric(vertical: Grid.sm), child: Text( - Loc.of(context).exit, + confirmText, style: TextStyle( color: Theme.of(context).colorScheme.error, ), diff --git a/test/features/app/app_tabs_test.dart b/test/features/app/app_tabs_test.dart index 246ef24d..a80f3759 100644 --- a/test/features/app/app_tabs_test.dart +++ b/test/features/app/app_tabs_test.dart @@ -49,7 +49,7 @@ void main() async { Widget appTabsTestWidget() => WidgetHelpers.testableWidget( child: const AppTabs(), overrides: [ - didProvider.overrideWithValue(did), + didProvider.overrideWith((ref) => did), tbdexServiceProvider.overrideWith((ref) => mockTbdexService), pfisProvider.overrideWith((ref) => mockPfisNotifier), vcsProvider.overrideWith((ref) => mockVcsNotifier), diff --git a/test/features/app/app_test.dart b/test/features/app/app_test.dart index 5b3c63c6..aa1404d9 100644 --- a/test/features/app/app_test.dart +++ b/test/features/app/app_test.dart @@ -45,7 +45,7 @@ void main() async { WidgetHelpers.testableWidget( child: const AppTabs(), overrides: [ - didProvider.overrideWithValue(did), + didProvider.overrideWith((ref) => did), tbdexServiceProvider.overrideWith((ref) => mockTbdexService), pfisProvider.overrideWith((ref) => mockPfisNotifier), vcsProvider.overrideWith((ref) => mockVcsNotifier), diff --git a/test/features/home/home_page_test.dart b/test/features/home/home_page_test.dart index f0cdc09c..eb7cc25e 100644 --- a/test/features/home/home_page_test.dart +++ b/test/features/home/home_page_test.dart @@ -31,7 +31,7 @@ void main() async { Widget homePageTestWidget() => WidgetHelpers.testableWidget( child: const HomePage(), overrides: [ - didProvider.overrideWithValue(did), + didProvider.overrideWith((ref) => did), tbdexServiceProvider.overrideWith((ref) => mockTbdexService), transactionProvider.overrideWith(MockTransactionNotifier.new), accountBalanceProvider diff --git a/test/features/payment/payment_amount_page_test.dart b/test/features/payment/payment_amount_page_test.dart index 21b1baa2..25863c0e 100644 --- a/test/features/payment/payment_amount_page_test.dart +++ b/test/features/payment/payment_amount_page_test.dart @@ -50,7 +50,7 @@ void main() async { PaymentState(transactionType: TransactionType.deposit), ), overrides: [ - didProvider.overrideWithValue(did), + didProvider.overrideWith((ref) => did), tbdexServiceProvider.overrideWith((ref) => mockTbdexService), pfisProvider.overrideWith((ref) => mockPfisNotifier), ], diff --git a/test/features/payment/payment_details_page_test.dart b/test/features/payment/payment_details_page_test.dart index b61b8e24..6059307d 100644 --- a/test/features/payment/payment_details_page_test.dart +++ b/test/features/payment/payment_details_page_test.dart @@ -48,7 +48,7 @@ void main() async { ), ), overrides: [ - didProvider.overrideWithValue(did), + didProvider.overrideWith((ref) => did), pfisProvider.overrideWith((ref) => mockPfisNotifier), ], ); diff --git a/test/features/payment/payment_review_page_test.dart b/test/features/payment/payment_review_page_test.dart index 671b9eb6..b42eeb36 100644 --- a/test/features/payment/payment_review_page_test.dart +++ b/test/features/payment/payment_review_page_test.dart @@ -66,7 +66,7 @@ void main() async { ), ), overrides: [ - didProvider.overrideWithValue(did), + didProvider.overrideWith((ref) => did), quoteProvider.overrideWith(() => mockTbdexQuoteNotifier), tbdexServiceProvider.overrideWith((ref) => mockTbdexService), ], diff --git a/test/features/send/send_page_test.dart b/test/features/send/send_page_test.dart index efd6025b..5bb4ce6d 100644 --- a/test/features/send/send_page_test.dart +++ b/test/features/send/send_page_test.dart @@ -23,7 +23,7 @@ void main() async { Widget sendDetailsPageTestWidget() => WidgetHelpers.testableWidget( child: const SendPage(), overrides: [ - didProvider.overrideWithValue(did), + didProvider.overrideWith((ref) => did), featureFlagsProvider .overrideWith((ref) => mockFeatureFlagsNotifier), ],