mirror of
https://github.com/ente-io/ente.git
synced 2025-08-14 10:16:10 +00:00
[auth][mob] Add recovery support for passkey
This commit is contained in:
78
auth/lib/generated/l10n.dart
Normal file
78
auth/lib/generated/l10n.dart
Normal file
@@ -0,0 +1,78 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'intl/messages_all.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// Generator: Flutter Intl IDE plugin
|
||||
// Made by Localizely
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars
|
||||
// ignore_for_file: join_return_with_assignment, prefer_final_in_for_each
|
||||
// ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes
|
||||
|
||||
class S {
|
||||
S();
|
||||
|
||||
static S? _current;
|
||||
|
||||
static S get current {
|
||||
assert(_current != null,
|
||||
'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.');
|
||||
return _current!;
|
||||
}
|
||||
|
||||
static const AppLocalizationDelegate delegate = AppLocalizationDelegate();
|
||||
|
||||
static Future<S> load(Locale locale) {
|
||||
final name = (locale.countryCode?.isEmpty ?? false)
|
||||
? locale.languageCode
|
||||
: locale.toString();
|
||||
final localeName = Intl.canonicalizedLocale(name);
|
||||
return initializeMessages(localeName).then((_) {
|
||||
Intl.defaultLocale = localeName;
|
||||
final instance = S();
|
||||
S._current = instance;
|
||||
|
||||
return instance;
|
||||
});
|
||||
}
|
||||
|
||||
static S of(BuildContext context) {
|
||||
final instance = S.maybeOf(context);
|
||||
assert(instance != null,
|
||||
'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?');
|
||||
return instance!;
|
||||
}
|
||||
|
||||
static S? maybeOf(BuildContext context) {
|
||||
return Localizations.of<S>(context, S);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
||||
const AppLocalizationDelegate();
|
||||
|
||||
List<Locale> get supportedLocales {
|
||||
return const <Locale>[
|
||||
Locale.fromSubtags(languageCode: 'en'),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
bool isSupported(Locale locale) => _isSupported(locale);
|
||||
@override
|
||||
Future<S> load(Locale locale) => S.load(locale);
|
||||
@override
|
||||
bool shouldReload(AppLocalizationDelegate old) => false;
|
||||
|
||||
bool _isSupported(Locale locale) {
|
||||
for (var supportedLocale in supportedLocales) {
|
||||
if (supportedLocale.languageCode == locale.languageCode) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -144,7 +144,8 @@
|
||||
"enterCodeHint": "Enter the 6-digit code from\nyour authenticator app",
|
||||
"lostDeviceTitle": "Lost device?",
|
||||
"twoFactorAuthTitle": "Two-factor authentication",
|
||||
"passkeyAuthTitle": "Passkey authentication",
|
||||
"passkeyAuthTitle": "Passkey verification",
|
||||
"verifyPasskey": "Verify passkey",
|
||||
"recoverAccount": "Recover account",
|
||||
"enterRecoveryKeyHint": "Enter your recovery key",
|
||||
"recover": "Recover",
|
||||
@@ -407,7 +408,7 @@
|
||||
"hearUsWhereTitle": "How did you hear about Ente? (optional)",
|
||||
"hearUsExplanation": "We don't track app installs. It'd help if you told us where you found us!",
|
||||
"waitingForBrowserRequest": "Waiting for browser request...",
|
||||
"launchPasskeyUrlAgain": "Launch passkey URL again",
|
||||
"waitingForVerification": "Waiting for verification...",
|
||||
"passkey": "Passkey",
|
||||
"developerSettingsWarning":"Are you sure that you want to modify Developer settings?",
|
||||
"developerSettings": "Developer settings",
|
||||
|
13
auth/lib/models/account/two_factor.dart
Normal file
13
auth/lib/models/account/two_factor.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
enum TwoFactorType { totp, passkey }
|
||||
|
||||
// ToString for TwoFactorType
|
||||
String twoFactorTypeToString(TwoFactorType type) {
|
||||
switch (type) {
|
||||
case TwoFactorType.totp:
|
||||
return "totp";
|
||||
case TwoFactorType.passkey:
|
||||
return "passkey";
|
||||
default:
|
||||
return type.name;
|
||||
}
|
||||
}
|
@@ -17,6 +17,28 @@ class PasskeyService {
|
||||
return response.data!["accountsToken"] as String;
|
||||
}
|
||||
|
||||
Future<bool> isPasskeyRecoveryEnabled() async {
|
||||
final response = await _enteDio.get(
|
||||
"/users/two-factor/recovery-status",
|
||||
);
|
||||
return response.data!["isPasskeyRecoveryEnabled"] as bool;
|
||||
}
|
||||
|
||||
Future<void> configurePasskeyRecovery(
|
||||
String secret,
|
||||
String userEncryptedSecret,
|
||||
String userSecretNonce,
|
||||
) async {
|
||||
await _enteDio.post(
|
||||
"/users/two-factor/passkeys/configure-recovery",
|
||||
data: {
|
||||
"secret": secret,
|
||||
"userSecretCipher": userEncryptedSecret,
|
||||
"userSecretNonce": userSecretNonce,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> openPasskeyPage(BuildContext context) async {
|
||||
try {
|
||||
final jwtToken = await getJwtToken();
|
||||
|
@@ -11,6 +11,7 @@ import 'package:ente_auth/core/event_bus.dart';
|
||||
import 'package:ente_auth/core/network.dart';
|
||||
import 'package:ente_auth/events/user_details_changed_event.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/models/account/two_factor.dart';
|
||||
import 'package:ente_auth/models/api/user/srp.dart';
|
||||
import 'package:ente_auth/models/delete_account.dart';
|
||||
import 'package:ente_auth/models/key_attributes.dart';
|
||||
@@ -762,7 +763,11 @@ class UserService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> recoverTwoFactor(BuildContext context, String sessionID) async {
|
||||
Future<void> recoverTwoFactor(
|
||||
BuildContext context,
|
||||
String sessionID,
|
||||
TwoFactorType type,
|
||||
) async {
|
||||
final dialog = createProgressDialog(context, context.l10n.pleaseWait);
|
||||
await dialog.show();
|
||||
try {
|
||||
@@ -770,6 +775,7 @@ class UserService {
|
||||
_config.getHttpEndpoint() + "/users/two-factor/recover",
|
||||
queryParameters: {
|
||||
"sessionID": sessionID,
|
||||
"twoFactorType": twoFactorTypeToString(type),
|
||||
},
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
@@ -778,6 +784,7 @@ class UserService {
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return TwoFactorRecoveryPage(
|
||||
type,
|
||||
sessionID,
|
||||
response.data["encryptedSecret"],
|
||||
response.data["secretDecryptionNonce"],
|
||||
@@ -788,6 +795,7 @@ class UserService {
|
||||
);
|
||||
}
|
||||
} on DioError catch (e) {
|
||||
await dialog.hide();
|
||||
_logger.severe(e);
|
||||
if (e.response != null && e.response!.statusCode == 404) {
|
||||
showToast(context, context.l10n.sessionExpired);
|
||||
@@ -809,6 +817,7 @@ class UserService {
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
await dialog.hide();
|
||||
_logger.severe(e);
|
||||
// ignore: unawaited_futures
|
||||
showErrorDialog(
|
||||
@@ -823,6 +832,7 @@ class UserService {
|
||||
|
||||
Future<void> removeTwoFactor(
|
||||
BuildContext context,
|
||||
TwoFactorType type,
|
||||
String sessionID,
|
||||
String recoveryKey,
|
||||
String encryptedSecret,
|
||||
@@ -857,11 +867,15 @@ class UserService {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final secretValue = type == TwoFactorType.passkey
|
||||
? utf8.decode(base64.decode(secret))
|
||||
: secret;
|
||||
final response = await _dio.post(
|
||||
_config.getHttpEndpoint() + "/users/two-factor/remove",
|
||||
data: {
|
||||
"sessionID": sessionID,
|
||||
"secret": secret,
|
||||
"secret": secretValue,
|
||||
"twoFactorType": twoFactorTypeToString(type),
|
||||
},
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
@@ -881,6 +895,7 @@ class UserService {
|
||||
);
|
||||
}
|
||||
} on DioError catch (e) {
|
||||
await dialog.hide();
|
||||
_logger.severe(e);
|
||||
if (e.response != null && e.response!.statusCode == 404) {
|
||||
showToast(context, "Session expired");
|
||||
@@ -902,6 +917,7 @@ class UserService {
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
await dialog.hide();
|
||||
_logger.severe(e);
|
||||
// ignore: unawaited_futures
|
||||
showErrorDialog(
|
||||
|
@@ -1,9 +1,11 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
import 'package:ente_auth/ente_theme_data.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/models/account/two_factor.dart';
|
||||
import 'package:ente_auth/services/user_service.dart';
|
||||
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
|
||||
import 'package:ente_auth/ui/components/models/button_type.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
@@ -99,30 +101,50 @@ class _PasskeyPageState extends State<PasskeyPage> {
|
||||
}
|
||||
|
||||
Widget _getBody() {
|
||||
final l10n = context.l10n;
|
||||
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
l10n.waitingForBrowserRequest,
|
||||
style: const TextStyle(
|
||||
height: 1.4,
|
||||
fontSize: 16,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
context.l10n.waitingForVerification,
|
||||
style: const TextStyle(
|
||||
height: 1.4,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||
child: ElevatedButton(
|
||||
style: Theme.of(context).colorScheme.optionalActionButtonStyle,
|
||||
onPressed: launchPasskey,
|
||||
child: Text(l10n.launchPasskeyUrlAgain),
|
||||
const SizedBox(height: 16),
|
||||
ButtonWidget(
|
||||
buttonType: ButtonType.primary,
|
||||
labelText: context.l10n.verifyPasskey,
|
||||
onTap: () => launchPasskey(),
|
||||
),
|
||||
),
|
||||
],
|
||||
const Padding(padding: EdgeInsets.all(30)),
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
UserService.instance.recoverTwoFactor(
|
||||
context,
|
||||
widget.sessionID,
|
||||
TwoFactorType.passkey,
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Center(
|
||||
child: Text(
|
||||
context.l10n.recoverAccount,
|
||||
style: const TextStyle(
|
||||
decoration: TextDecoration.underline,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
@@ -21,6 +22,8 @@ import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:ente_auth/utils/navigation_util.dart';
|
||||
import 'package:ente_auth/utils/toast_util.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class SecuritySectionWidget extends StatefulWidget {
|
||||
const SecuritySectionWidget({Key? key}) : super(key: key);
|
||||
@@ -32,6 +35,7 @@ class SecuritySectionWidget extends StatefulWidget {
|
||||
class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
||||
final _config = Configuration.instance;
|
||||
late bool _hasLoggedIn;
|
||||
final Logger _logger = Logger('SecuritySectionWidget');
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -75,7 +79,7 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
trailingIcon: Icons.chevron_right_outlined,
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () => PasskeyService.instance.openPasskeyPage(context),
|
||||
onTap: () async => await onPasskeyClick(context),
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
@@ -159,6 +163,32 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> onPasskeyClick(BuildContext buildContext) async {
|
||||
try {
|
||||
final isPassKeyResetEnabled =
|
||||
await PasskeyService.instance.isPasskeyRecoveryEnabled();
|
||||
if (!isPassKeyResetEnabled) {
|
||||
final Uint8List recoveryKey = Configuration.instance.getRecoveryKey();
|
||||
final resetSecret = const Uuid().v4().toString();
|
||||
final bytes = utf8.encode(resetSecret);
|
||||
final base64Str = base64.encode(bytes);
|
||||
final encryptionResult = CryptoUtil.encryptSync(
|
||||
CryptoUtil.base642bin(base64Str),
|
||||
recoveryKey,
|
||||
);
|
||||
await PasskeyService.instance.configurePasskeyRecovery(
|
||||
resetSecret,
|
||||
CryptoUtil.bin2base64(encryptionResult.encryptedData!),
|
||||
CryptoUtil.bin2base64(encryptionResult.nonce!),
|
||||
);
|
||||
}
|
||||
PasskeyService.instance.openPasskeyPage(buildContext).ignore();
|
||||
} catch (e, s) {
|
||||
_logger.severe("failed to open passkey page", e, s);
|
||||
await showGenericErrorDialog(context: context);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateEmailMFA(bool enableEmailMFA) async {
|
||||
try {
|
||||
final UserDetails details =
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/models/account/two_factor.dart';
|
||||
import 'package:ente_auth/services/user_service.dart';
|
||||
import 'package:ente_auth/ui/lifecycle_event_handler.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -129,7 +130,11 @@ class _TwoFactorAuthenticationPageState
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
UserService.instance.recoverTwoFactor(context, widget.sessionID);
|
||||
UserService.instance.recoverTwoFactor(
|
||||
context,
|
||||
widget.sessionID,
|
||||
TwoFactorType.totp,
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/models/account/two_factor.dart';
|
||||
import 'package:ente_auth/services/user_service.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -9,8 +10,10 @@ class TwoFactorRecoveryPage extends StatefulWidget {
|
||||
final String sessionID;
|
||||
final String encryptedSecret;
|
||||
final String secretDecryptionNonce;
|
||||
final TwoFactorType type;
|
||||
|
||||
const TwoFactorRecoveryPage(
|
||||
this.type,
|
||||
this.sessionID,
|
||||
this.encryptedSecret,
|
||||
this.secretDecryptionNonce, {
|
||||
@@ -72,6 +75,7 @@ class _TwoFactorRecoveryPageState extends State<TwoFactorRecoveryPage> {
|
||||
? () async {
|
||||
await UserService.instance.removeTwoFactor(
|
||||
context,
|
||||
widget.type,
|
||||
widget.sessionID,
|
||||
_recoveryKey.text,
|
||||
widget.encryptedSecret,
|
||||
|
2
mobile/lib/generated/intl/messages_cs.dart
generated
2
mobile/lib/generated/intl/messages_cs.dart
generated
@@ -29,12 +29,14 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"contacts": MessageLookupByLibrary.simpleMessage("Contacts"),
|
||||
"deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage(
|
||||
"This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted."),
|
||||
"descriptions": MessageLookupByLibrary.simpleMessage("Descriptions"),
|
||||
"editLocation": MessageLookupByLibrary.simpleMessage("Edit location"),
|
||||
"editsToLocationWillOnlyBeSeenWithinEnte":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"Edits to location will only be seen within Ente"),
|
||||
"fileTypes": MessageLookupByLibrary.simpleMessage("File types"),
|
||||
"joinDiscord": MessageLookupByLibrary.simpleMessage("Join Discord"),
|
||||
"locations": MessageLookupByLibrary.simpleMessage("Locations"),
|
||||
"modifyYourQueryOrTrySearchingFor":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"Modify your query, or try searching for"),
|
||||
|
2
mobile/lib/generated/intl/messages_de.dart
generated
2
mobile/lib/generated/intl/messages_de.dart
generated
@@ -562,6 +562,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("Geteiltes Album löschen?"),
|
||||
"deleteSharedAlbumDialogBody": MessageLookupByLibrary.simpleMessage(
|
||||
"Dieses Album wird für alle gelöscht\n\nDu wirst den Zugriff auf geteilte Fotos in diesem Album, die anderen gehören, verlieren"),
|
||||
"descriptions": MessageLookupByLibrary.simpleMessage("Descriptions"),
|
||||
"deselectAll": MessageLookupByLibrary.simpleMessage("Alle abwählen"),
|
||||
"designedToOutlive":
|
||||
MessageLookupByLibrary.simpleMessage("Entwickelt um zu bewahren"),
|
||||
@@ -872,6 +873,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"locationName": MessageLookupByLibrary.simpleMessage("Standortname"),
|
||||
"locationTagFeatureDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"Ein Standort-Tag gruppiert alle Fotos, die in einem Radius eines Fotos aufgenommen wurden"),
|
||||
"locations": MessageLookupByLibrary.simpleMessage("Locations"),
|
||||
"lockButtonLabel": MessageLookupByLibrary.simpleMessage("Sperren"),
|
||||
"lockScreenEnablePreSteps": MessageLookupByLibrary.simpleMessage(
|
||||
"Um den Sperrbildschirm zu aktivieren, legen Sie bitte den Geräte-Passcode oder die Bildschirmsperre in den Systemeinstellungen fest."),
|
||||
|
4
mobile/lib/generated/intl/messages_en.dart
generated
4
mobile/lib/generated/intl/messages_en.dart
generated
@@ -548,6 +548,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("Delete shared album?"),
|
||||
"deleteSharedAlbumDialogBody": MessageLookupByLibrary.simpleMessage(
|
||||
"The album will be deleted for everyone\n\nYou will lose access to shared photos in this album that are owned by others"),
|
||||
"descriptions": MessageLookupByLibrary.simpleMessage("Descriptions"),
|
||||
"deselectAll": MessageLookupByLibrary.simpleMessage("Deselect all"),
|
||||
"designedToOutlive":
|
||||
MessageLookupByLibrary.simpleMessage("Designed to outlive"),
|
||||
@@ -797,8 +798,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Kindly help us with this information"),
|
||||
"language": MessageLookupByLibrary.simpleMessage("Language"),
|
||||
"lastUpdated": MessageLookupByLibrary.simpleMessage("Last updated"),
|
||||
"launchPasskeyUrlAgain":
|
||||
MessageLookupByLibrary.simpleMessage("Launch passkey URL again"),
|
||||
"leave": MessageLookupByLibrary.simpleMessage("Leave"),
|
||||
"leaveAlbum": MessageLookupByLibrary.simpleMessage("Leave album"),
|
||||
"leaveFamily": MessageLookupByLibrary.simpleMessage("Leave family"),
|
||||
@@ -848,6 +847,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"locationName": MessageLookupByLibrary.simpleMessage("Location name"),
|
||||
"locationTagFeatureDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"A location tag groups all photos that were taken within some radius of a photo"),
|
||||
"locations": MessageLookupByLibrary.simpleMessage("Locations"),
|
||||
"lockButtonLabel": MessageLookupByLibrary.simpleMessage("Lock"),
|
||||
"lockScreenEnablePreSteps": MessageLookupByLibrary.simpleMessage(
|
||||
"To enable lockscreen, please setup device passcode or screen lock in your system settings."),
|
||||
|
2
mobile/lib/generated/intl/messages_es.dart
generated
2
mobile/lib/generated/intl/messages_es.dart
generated
@@ -487,6 +487,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("¿Borrar álbum compartido?"),
|
||||
"deleteSharedAlbumDialogBody": MessageLookupByLibrary.simpleMessage(
|
||||
"El álbum se eliminará para todos\n\nPerderás el acceso a las fotos compartidas en este álbum que son propiedad de otros"),
|
||||
"descriptions": MessageLookupByLibrary.simpleMessage("Descriptions"),
|
||||
"deselectAll":
|
||||
MessageLookupByLibrary.simpleMessage("Deseleccionar todo"),
|
||||
"designedToOutlive":
|
||||
@@ -761,6 +762,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("Nombre de la ubicación"),
|
||||
"locationTagFeatureDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"Una etiqueta de ubicación agrupa todas las fotos que fueron tomadas dentro de un radio de una foto"),
|
||||
"locations": MessageLookupByLibrary.simpleMessage("Locations"),
|
||||
"lockButtonLabel": MessageLookupByLibrary.simpleMessage("Bloquear"),
|
||||
"lockScreenEnablePreSteps": MessageLookupByLibrary.simpleMessage(
|
||||
"Para activar la pantalla de bloqueo, por favor configure el código de acceso del dispositivo o el bloqueo de pantalla en los ajustes de su sistema."),
|
||||
|
2
mobile/lib/generated/intl/messages_fr.dart
generated
2
mobile/lib/generated/intl/messages_fr.dart
generated
@@ -562,6 +562,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Supprimer l\'album partagé ?"),
|
||||
"deleteSharedAlbumDialogBody": MessageLookupByLibrary.simpleMessage(
|
||||
"L\'album sera supprimé pour tout le monde\n\nVous perdrez l\'accès aux photos partagées dans cet album qui sont détenues par d\'autres personnes"),
|
||||
"descriptions": MessageLookupByLibrary.simpleMessage("Descriptions"),
|
||||
"deselectAll":
|
||||
MessageLookupByLibrary.simpleMessage("Tout déselectionner"),
|
||||
"designedToOutlive":
|
||||
@@ -870,6 +871,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"locationName": MessageLookupByLibrary.simpleMessage("Nom du lieu"),
|
||||
"locationTagFeatureDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"Un tag d\'emplacement regroupe toutes les photos qui ont été prises dans un certain rayon d\'une photo"),
|
||||
"locations": MessageLookupByLibrary.simpleMessage("Locations"),
|
||||
"lockButtonLabel": MessageLookupByLibrary.simpleMessage("Verrouiller"),
|
||||
"lockScreenEnablePreSteps": MessageLookupByLibrary.simpleMessage(
|
||||
"Pour activer l\'écran de verrouillage, veuillez configurer le code d\'accès de l\'appareil ou le verrouillage de l\'écran dans les paramètres de votre système."),
|
||||
|
2
mobile/lib/generated/intl/messages_it.dart
generated
2
mobile/lib/generated/intl/messages_it.dart
generated
@@ -542,6 +542,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Eliminare l\'album condiviso?"),
|
||||
"deleteSharedAlbumDialogBody": MessageLookupByLibrary.simpleMessage(
|
||||
"L\'album verrà eliminato per tutti\n\nPerderai l\'accesso alle foto condivise in questo album che sono di proprietà di altri"),
|
||||
"descriptions": MessageLookupByLibrary.simpleMessage("Descriptions"),
|
||||
"deselectAll":
|
||||
MessageLookupByLibrary.simpleMessage("Deseleziona tutti"),
|
||||
"designedToOutlive":
|
||||
@@ -838,6 +839,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("Nome della località"),
|
||||
"locationTagFeatureDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"Un tag di localizzazione raggruppa tutte le foto scattate entro il raggio di una foto"),
|
||||
"locations": MessageLookupByLibrary.simpleMessage("Locations"),
|
||||
"lockButtonLabel": MessageLookupByLibrary.simpleMessage("Blocca"),
|
||||
"lockScreenEnablePreSteps": MessageLookupByLibrary.simpleMessage(
|
||||
"Per abilitare la schermata di blocco, configura il codice di accesso del dispositivo o il blocco schermo nelle impostazioni di sistema."),
|
||||
|
2
mobile/lib/generated/intl/messages_ko.dart
generated
2
mobile/lib/generated/intl/messages_ko.dart
generated
@@ -29,12 +29,14 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"contacts": MessageLookupByLibrary.simpleMessage("Contacts"),
|
||||
"deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage(
|
||||
"This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted."),
|
||||
"descriptions": MessageLookupByLibrary.simpleMessage("Descriptions"),
|
||||
"editLocation": MessageLookupByLibrary.simpleMessage("Edit location"),
|
||||
"editsToLocationWillOnlyBeSeenWithinEnte":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"Edits to location will only be seen within Ente"),
|
||||
"fileTypes": MessageLookupByLibrary.simpleMessage("File types"),
|
||||
"joinDiscord": MessageLookupByLibrary.simpleMessage("Join Discord"),
|
||||
"locations": MessageLookupByLibrary.simpleMessage("Locations"),
|
||||
"modifyYourQueryOrTrySearchingFor":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"Modify your query, or try searching for"),
|
||||
|
2
mobile/lib/generated/intl/messages_nl.dart
generated
2
mobile/lib/generated/intl/messages_nl.dart
generated
@@ -564,6 +564,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("Gedeeld album verwijderen?"),
|
||||
"deleteSharedAlbumDialogBody": MessageLookupByLibrary.simpleMessage(
|
||||
"Het album wordt verwijderd voor iedereen\n\nJe verliest de toegang tot gedeelde foto\'s in dit album die eigendom zijn van anderen"),
|
||||
"descriptions": MessageLookupByLibrary.simpleMessage("Descriptions"),
|
||||
"deselectAll":
|
||||
MessageLookupByLibrary.simpleMessage("Alles deselecteren"),
|
||||
"designedToOutlive": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -880,6 +881,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"locationName": MessageLookupByLibrary.simpleMessage("Locatie naam"),
|
||||
"locationTagFeatureDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"Een locatie tag groept alle foto\'s die binnen een bepaalde straal van een foto zijn genomen"),
|
||||
"locations": MessageLookupByLibrary.simpleMessage("Locations"),
|
||||
"lockButtonLabel": MessageLookupByLibrary.simpleMessage("Vergrendel"),
|
||||
"lockScreenEnablePreSteps": MessageLookupByLibrary.simpleMessage(
|
||||
"Om vergrendelscherm in te schakelen, moet u een toegangscode of schermvergrendeling instellen in uw systeeminstellingen."),
|
||||
|
2
mobile/lib/generated/intl/messages_no.dart
generated
2
mobile/lib/generated/intl/messages_no.dart
generated
@@ -41,6 +41,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Vi er lei oss for at du forlater oss. Gi oss gjerne en tilbakemelding så vi kan forbedre oss."),
|
||||
"deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage(
|
||||
"This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted."),
|
||||
"descriptions": MessageLookupByLibrary.simpleMessage("Descriptions"),
|
||||
"editLocation": MessageLookupByLibrary.simpleMessage("Edit location"),
|
||||
"editsToLocationWillOnlyBeSeenWithinEnte":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
@@ -57,6 +58,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"joinDiscord": MessageLookupByLibrary.simpleMessage("Join Discord"),
|
||||
"kindlyHelpUsWithThisInformation": MessageLookupByLibrary.simpleMessage(
|
||||
"Vær vennlig og hjelp oss med denne informasjonen"),
|
||||
"locations": MessageLookupByLibrary.simpleMessage("Locations"),
|
||||
"modifyYourQueryOrTrySearchingFor":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"Modify your query, or try searching for"),
|
||||
|
2
mobile/lib/generated/intl/messages_pl.dart
generated
2
mobile/lib/generated/intl/messages_pl.dart
generated
@@ -78,6 +78,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Inna, niewymieniona wyżej przyczyna"),
|
||||
"deleteRequestSLAText": MessageLookupByLibrary.simpleMessage(
|
||||
"Twoje żądanie zostanie przetworzone w ciągu 72 godzin."),
|
||||
"descriptions": MessageLookupByLibrary.simpleMessage("Descriptions"),
|
||||
"doThisLater": MessageLookupByLibrary.simpleMessage("Spróbuj później"),
|
||||
"editLocation": MessageLookupByLibrary.simpleMessage("Edit location"),
|
||||
"editsToLocationWillOnlyBeSeenWithinEnte":
|
||||
@@ -116,6 +117,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"joinDiscord": MessageLookupByLibrary.simpleMessage("Join Discord"),
|
||||
"kindlyHelpUsWithThisInformation":
|
||||
MessageLookupByLibrary.simpleMessage("Pomóż nam z tą informacją"),
|
||||
"locations": MessageLookupByLibrary.simpleMessage("Locations"),
|
||||
"logInLabel": MessageLookupByLibrary.simpleMessage("Zaloguj się"),
|
||||
"moderateStrength": MessageLookupByLibrary.simpleMessage("Umiarkowana"),
|
||||
"modifyYourQueryOrTrySearchingFor":
|
||||
|
4
mobile/lib/generated/intl/messages_pt.dart
generated
4
mobile/lib/generated/intl/messages_pt.dart
generated
@@ -558,6 +558,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Excluir álbum compartilhado?"),
|
||||
"deleteSharedAlbumDialogBody": MessageLookupByLibrary.simpleMessage(
|
||||
"O álbum será apagado para todos\n\nVocê perderá o acesso a fotos compartilhadas neste álbum que pertencem aos outros"),
|
||||
"descriptions": MessageLookupByLibrary.simpleMessage("Descriptions"),
|
||||
"deselectAll": MessageLookupByLibrary.simpleMessage("Desmarcar todos"),
|
||||
"designedToOutlive":
|
||||
MessageLookupByLibrary.simpleMessage("Feito para ter logenvidade"),
|
||||
@@ -820,8 +821,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"language": MessageLookupByLibrary.simpleMessage("Idioma"),
|
||||
"lastUpdated":
|
||||
MessageLookupByLibrary.simpleMessage("Última atualização"),
|
||||
"launchPasskeyUrlAgain": MessageLookupByLibrary.simpleMessage(
|
||||
"Iniciar a URL de chave de acesso novamente"),
|
||||
"leave": MessageLookupByLibrary.simpleMessage("Sair"),
|
||||
"leaveAlbum": MessageLookupByLibrary.simpleMessage("Sair do álbum"),
|
||||
"leaveFamily": MessageLookupByLibrary.simpleMessage("Sair da família"),
|
||||
@@ -873,6 +872,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"locationName": MessageLookupByLibrary.simpleMessage("Nome do Local"),
|
||||
"locationTagFeatureDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"Uma tag em grupo de todas as fotos que foram tiradas dentro de algum raio de uma foto"),
|
||||
"locations": MessageLookupByLibrary.simpleMessage("Locations"),
|
||||
"lockButtonLabel": MessageLookupByLibrary.simpleMessage("Bloquear"),
|
||||
"lockScreenEnablePreSteps": MessageLookupByLibrary.simpleMessage(
|
||||
"Para ativar o bloqueio de tela, por favor ative um método de autenticação nas configurações do sistema do seu dispositivo."),
|
||||
|
4
mobile/lib/generated/intl/messages_zh.dart
generated
4
mobile/lib/generated/intl/messages_zh.dart
generated
@@ -465,6 +465,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"deleteSharedAlbum": MessageLookupByLibrary.simpleMessage("要删除共享相册吗?"),
|
||||
"deleteSharedAlbumDialogBody": MessageLookupByLibrary.simpleMessage(
|
||||
"将为所有人删除相册\n\n您将无法访问此相册中他人拥有的共享照片"),
|
||||
"descriptions": MessageLookupByLibrary.simpleMessage("Descriptions"),
|
||||
"deselectAll": MessageLookupByLibrary.simpleMessage("取消全选"),
|
||||
"designedToOutlive": MessageLookupByLibrary.simpleMessage("经久耐用"),
|
||||
"details": MessageLookupByLibrary.simpleMessage("详情"),
|
||||
@@ -673,8 +674,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("请帮助我们了解这个信息"),
|
||||
"language": MessageLookupByLibrary.simpleMessage("语言"),
|
||||
"lastUpdated": MessageLookupByLibrary.simpleMessage("最后更新"),
|
||||
"launchPasskeyUrlAgain":
|
||||
MessageLookupByLibrary.simpleMessage("再次启动 通行密钥 URL"),
|
||||
"leave": MessageLookupByLibrary.simpleMessage("离开"),
|
||||
"leaveAlbum": MessageLookupByLibrary.simpleMessage("离开相册"),
|
||||
"leaveFamily": MessageLookupByLibrary.simpleMessage("离开家庭计划"),
|
||||
@@ -717,6 +716,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"locationName": MessageLookupByLibrary.simpleMessage("地点名称"),
|
||||
"locationTagFeatureDescription":
|
||||
MessageLookupByLibrary.simpleMessage("位置标签将在照片的某个半径范围内拍摄的所有照片进行分组"),
|
||||
"locations": MessageLookupByLibrary.simpleMessage("Locations"),
|
||||
"lockButtonLabel": MessageLookupByLibrary.simpleMessage("锁定"),
|
||||
"lockScreenEnablePreSteps":
|
||||
MessageLookupByLibrary.simpleMessage("要启用锁屏,请在系统设置中设置设备密码或屏幕锁定。"),
|
||||
|
50
mobile/lib/generated/l10n.dart
generated
50
mobile/lib/generated/l10n.dart
generated
@@ -6919,16 +6919,6 @@ class S {
|
||||
);
|
||||
}
|
||||
|
||||
/// `Descriptions`
|
||||
String get descriptions {
|
||||
return Intl.message(
|
||||
'Descriptions',
|
||||
name: 'descriptions',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `File types and names`
|
||||
String get fileTypesAndNames {
|
||||
return Intl.message(
|
||||
@@ -6949,16 +6939,6 @@ class S {
|
||||
);
|
||||
}
|
||||
|
||||
/// `Locations`
|
||||
String get locations {
|
||||
return Intl.message(
|
||||
'Locations',
|
||||
name: 'locations',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Moments`
|
||||
String get moments {
|
||||
return Intl.message(
|
||||
@@ -8328,16 +8308,6 @@ class S {
|
||||
);
|
||||
}
|
||||
|
||||
/// `Launch passkey URL again`
|
||||
String get launchPasskeyUrlAgain {
|
||||
return Intl.message(
|
||||
'Launch passkey URL again',
|
||||
name: 'launchPasskeyUrlAgain',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Passkey`
|
||||
String get passkey {
|
||||
return Intl.message(
|
||||
@@ -8427,6 +8397,26 @@ class S {
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Locations`
|
||||
String get locations {
|
||||
return Intl.message(
|
||||
'Locations',
|
||||
name: 'locations',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Descriptions`
|
||||
String get descriptions {
|
||||
return Intl.message(
|
||||
'Descriptions',
|
||||
name: 'descriptions',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
||||
|
@@ -1188,7 +1188,6 @@
|
||||
"editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente",
|
||||
"cleanUncategorized": "Clean Uncategorized",
|
||||
"waitingForVerification": "Waiting for verification...",
|
||||
"launchPasskeyUrlAgain": "Launch passkey URL again",
|
||||
"passkey": "Passkey",
|
||||
"passkeyAuthTitle": "Passkey verification",
|
||||
"verifyPasskey": "Verify passkey",
|
||||
|
@@ -912,11 +912,14 @@ class UserService {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final secretValue = type == TwoFactorType.passkey
|
||||
? utf8.decode(base64.decode(secret))
|
||||
: secret;
|
||||
final response = await _dio.post(
|
||||
_config.getHttpEndpoint() + "/users/two-factor/remove",
|
||||
data: {
|
||||
"sessionID": sessionID,
|
||||
"secret": utf8.decode(base64.decode(secret)),
|
||||
"secret": secretValue,
|
||||
"twoFactorType": twoFactorTypeToString(type),
|
||||
},
|
||||
);
|
||||
|
Reference in New Issue
Block a user