diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 0ba42a0d0..efac6e106 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -564,6 +564,7 @@ class MessageLookup extends MessageLookupByLibrary { "discord": MessageLookupByLibrary.simpleMessage("Discord"), "dismiss": MessageLookupByLibrary.simpleMessage("Dismiss"), "distanceInKMUnit": MessageLookupByLibrary.simpleMessage("km"), + "doNotSignOut": MessageLookupByLibrary.simpleMessage("Do not sign out"), "doThisLater": MessageLookupByLibrary.simpleMessage("Do this later"), "doYouWantToDiscardTheEditsYouHaveMade": MessageLookupByLibrary.simpleMessage( @@ -1208,6 +1209,12 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Shared with you"), "sharing": MessageLookupByLibrary.simpleMessage("Sharing..."), "showMemories": MessageLookupByLibrary.simpleMessage("Show memories"), + "signOutFromOtherDevices": + MessageLookupByLibrary.simpleMessage("Sign out from other devices"), + "signOutOtherBody": MessageLookupByLibrary.simpleMessage( + "If you think someone might know your password, you can force all other devices using your account to sign out."), + "signOutOtherDevices": + MessageLookupByLibrary.simpleMessage("Sign out other devices"), "signUpTerms": MessageLookupByLibrary.simpleMessage( "I agree to the terms of service and privacy policy"), "singleFileDeleteFromDevice": m49, diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 9dbddb101..e0af479e3 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -8097,6 +8097,46 @@ class S { args: [], ); } + + /// `Sign out from other devices` + String get signOutFromOtherDevices { + return Intl.message( + 'Sign out from other devices', + name: 'signOutFromOtherDevices', + desc: '', + args: [], + ); + } + + /// `If you think someone might know your password, you can force all other devices using your account to sign out.` + String get signOutOtherBody { + return Intl.message( + 'If you think someone might know your password, you can force all other devices using your account to sign out.', + name: 'signOutOtherBody', + desc: '', + args: [], + ); + } + + /// `Sign out other devices` + String get signOutOtherDevices { + return Intl.message( + 'Sign out other devices', + name: 'signOutOtherDevices', + desc: '', + args: [], + ); + } + + /// `Do not sign out` + String get doNotSignOut { + return Intl.message( + 'Do not sign out', + name: 'doNotSignOut', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 39ff3f697..18943691e 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1152,5 +1152,10 @@ }, "contacts": "Contacts", "noInternetConnection": "No internet connection", - "pleaseCheckYourInternetConnectionAndTryAgain": "Please check your internet connection and try again." + "pleaseCheckYourInternetConnectionAndTryAgain": "Please check your internet connection and try again.", + + "signOutFromOtherDevices": "Sign out from other devices", + "signOutOtherBody": "If you think someone might know your password, you can force all other devices using your account to sign out.", + "signOutOtherDevices": "Sign out other devices", + "doNotSignOut": "Do not sign out" } \ No newline at end of file diff --git a/lib/services/user_service.dart b/lib/services/user_service.dart index e52a69984..6fb418aae 100644 --- a/lib/services/user_service.dart +++ b/lib/services/user_service.dart @@ -502,6 +502,7 @@ class UserService { Future registerOrUpdateSrp( Uint8List loginKey, { SetKeysRequest? setKeysRequest, + bool logOutOtherDevices = false, }) async { try { final String username = const Uuid().v4().toString(); @@ -558,6 +559,7 @@ class UserService { 'setupID': setupSRPResponse.setupID, 'srpM1': base64Encode(SRP6Util.encodeBigInt(clientM!)), 'updatedKeyAttr': setKeysRequest.toMap(), + 'logOutOtherDevices': logOutOtherDevices, }, ); } @@ -676,8 +678,9 @@ class UserService { Future updateKeyAttributes( KeyAttributes keyAttributes, - Uint8List loginKey, - ) async { + Uint8List loginKey, { + required bool logoutOtherDevices, + }) async { try { final setKeyRequest = SetKeysRequest( kekSalt: keyAttributes.kekSalt, @@ -686,7 +689,11 @@ class UserService { memLimit: keyAttributes.memLimit!, opsLimit: keyAttributes.opsLimit!, ); - await registerOrUpdateSrp(loginKey, setKeysRequest: setKeyRequest); + await registerOrUpdateSrp( + loginKey, + setKeysRequest: setKeyRequest, + logOutOtherDevices: logoutOtherDevices, + ); await _config.setKeyAttributes(keyAttributes); } catch (e) { _logger.severe(e); diff --git a/lib/ui/account/password_entry_page.dart b/lib/ui/account/password_entry_page.dart index 11889c76e..f148ef21e 100644 --- a/lib/ui/account/password_entry_page.dart +++ b/lib/ui/account/password_entry_page.dart @@ -7,11 +7,13 @@ import 'package:photos/core/event_bus.dart'; import 'package:photos/events/account_configured_event.dart'; import 'package:photos/events/subscription_purchased_event.dart'; import "package:photos/generated/l10n.dart"; +import "package:photos/l10n/l10n.dart"; import "package:photos/models/key_gen_result.dart"; import 'package:photos/services/user_service.dart'; import 'package:photos/ui/account/recovery_key_page.dart'; import 'package:photos/ui/common/dynamic_fab.dart'; import 'package:photos/ui/common/web_page.dart'; +import "package:photos/ui/components/models/button_type.dart"; import 'package:photos/ui/payment/subscription.dart'; import 'package:photos/utils/dialog_util.dart'; import 'package:photos/utils/navigation_util.dart'; @@ -27,9 +29,10 @@ enum PasswordEntryMode { class PasswordEntryPage extends StatefulWidget { final PasswordEntryMode mode; - const PasswordEntryPage({required this.mode, Key? - key,}) - : super(key: key); + const PasswordEntryPage({ + required this.mode, + Key? key, + }) : super(key: key); @override State createState() => _PasswordEntryPageState(); @@ -379,13 +382,18 @@ class _PasswordEntryPageState extends State { } void _updatePassword() async { + final logOutFromOthers = await logOutFromOtherDevices(context); final dialog = createProgressDialog(context, S.of(context).generatingEncryptionKeys); await dialog.show(); try { final result = await Configuration.instance .getAttributesForNewPassword(_passwordController1.text); - await UserService.instance.updateKeyAttributes(result.item1, result.item2); + await UserService.instance.updateKeyAttributes( + result.item1, + result.item2, + logoutOtherDevices: logOutFromOthers, + ); await dialog.hide(); showShortToast(context, S.of(context).passwordChangedSuccessfully); Navigator.of(context).pop(); @@ -400,12 +408,33 @@ class _PasswordEntryPageState extends State { } } + Future logOutFromOtherDevices(BuildContext context) async { + bool logOutFromOther = true; + await showChoiceDialog( + context, + title: context.l10n.signOutFromOtherDevices, + body: context.l10n.signOutOtherBody, + isDismissible: false, + firstButtonLabel: context.l10n.signOutOtherDevices, + firstButtonType: ButtonType.critical, + firstButtonOnTap: () async { + logOutFromOther = true; + }, + secondButtonLabel: context.l10n.doNotSignOut, + secondButtonOnTap: () async { + logOutFromOther = false; + }, + ); + return logOutFromOther; + } + Future _showRecoveryCodeDialog(String password) async { final dialog = createProgressDialog(context, S.of(context).generatingEncryptionKeys); await dialog.show(); try { - final KeyGenResult result = await Configuration.instance.generateKey(password); + final KeyGenResult result = + await Configuration.instance.generateKey(password); Configuration.instance.setVolatilePassword(null); await dialog.hide(); onDone() async { diff --git a/pubspec.yaml b/pubspec.yaml index 8012c5d68..991043101 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,7 +12,7 @@ description: ente photos application # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.8.2+522 +version: 0.8.3+523 environment: sdk: ">=3.0.0 <4.0.0"