This commit is contained in:
otsmr 2025-06-18 23:22:40 +02:00
parent 0b1f51713c
commit 2ecebb2429
10 changed files with 618 additions and 104 deletions

View file

@ -156,6 +156,8 @@
"close": "Schließen",
"cancel": "Abbrechen",
"ok": "Ok",
"disable": "Deaktiviern",
"enable": "Aktivieren",
"switchFrontAndBackCamera": "Zwischen Front- und Rückkamera wechseln.",
"addTextItem": "Text",
"protectAsARealTwonly": "Als echtes twonly senden!",
@ -263,5 +265,35 @@
"backupNoticeTitle": "Kein Backup konfiguriert",
"backupNoticeDesc": "Wenn du dein Gerät wechselst oder verlierst, kann ohne Backup niemand dein Account wiederherstellen. Sichere deshalb deine Daten.",
"backupNoticeLater": "Später erinnern",
"backupNoticeOpenBackup": "Backup erstellen"
"backupNoticeOpenBackup": "Backup erstellen",
"backupPending": "Ausstehend",
"backupFailed": "Fehlgeschlagen",
"backupSuccess": "Erfolgreich",
"backupTwonlySafeDesc": "Sichere deine twonly-Identität, da dies die einzige Möglichkeit ist, dein Konto wiederherzustellen, wenn du die App deinstallierst oder dein Handy verlierst.",
"backupServer": "Server",
"backupMaxBackupSize": "max. Backup-Größe",
"backupStorageRetention": "Speicheraufbewahrung",
"backupLastBackupDate": "Letztes Backup",
"backupLastBackupSize": "Backup-Größe",
"backupLastBackupResult": "Ergebnis",
"deleteBackupTitle": "Bist du sicher?",
"deleteBackupBody": "Ohne ein Backup kannst du dein Benutzerkonto nicht wiederherstellen.",
"backupData": "Daten-Backup",
"backupDataDesc": "Das Daten-Backup enthält neben deiner twonly-Identität auch alle deine Mediendateien. Dieses Backup ist ebenfalls verschlüsselt, wird jedoch lokal gespeichert. Du musst es dann manuell auf deinen Laptop oder ein Gerät deiner Wahl kopieren.",
"backupInsecurePassword": "Unsicheres Passwort",
"backupInsecurePasswordDesc": "Das gewählte Passwort ist sehr unsicher und kann daher leicht von Angreifern erraten werden. Bitte wähle ein sicheres Passwort.",
"backupInsecurePasswordOk": "Trotzdem fortfahren",
"backupInsecurePasswordCancel": "Erneut versuchen",
"backupTwonlySafeLongDesc": "twonly hat keine zentralen Benutzerkonten. Ein Schlüsselpaar wird während der Installation erstellt, das aus einem öffentlichen und einem privaten Schlüssel besteht. Der private Schlüssel wird nur auf deinem Gerät gespeichert, um ihn vor unbefugtem Zugriff zu schützen. Der öffentliche Schlüssel wird auf den Server hochgeladen und mit deinem gewählten Benutzernamen verknüpft, damit andere dich finden können.\n\ntwonly Safe erstellt regelmäßig ein verschlüsseltes, anonymes Backup deines privaten Schlüssels zusammen mit deinen Kontakten und Einstellungen. Dein Benutzername und das gewählte Passwort reichen aus, um diese Daten auf einem anderen Gerät wiederherzustellen.",
"backupSelectStrongPassword": "Wähle ein sicheres Passwort. Dies ist erforderlich, wenn du dein twonly Safe-Backup wiederherstellen möchtest.",
"password": "Passwort",
"passwordRepeated": "Passwort wiederholen",
"passwordRepeatedNotEqual": "Passwörter stimmen nicht überein.",
"backupPasswordRequirement": "Das Passwort muss mindestens 8 Zeichen lang sein.",
"backupExpertSettings": "Experteneinstellungen",
"backupEnableBackup": "Automatische Sicherung aktivieren",
"backupOwnServerDesc": "Speichere dein twonly Safe-Backups auf einem Server deiner Wahl.",
"backupUseOwnServer": "Server verwenden",
"backupResetServer": "Standardserver verwenden",
"backupTwonlySaveNow": "Jetzt speichern"
}

View file

@ -274,17 +274,13 @@
"contactRemoveTitle": "Remove {username}",
"contactRemoveBody": "Remove the user and permanently delete the chat and all associated media files. This will also delete YOUR ACCOUNT FROM YOUR CONTACT'S PHONE.",
"undo": "Undo",
"@undo": {},
"redo": "Redo",
"@redo": {},
"next": "Next",
"@next": {},
"close": "Close",
"@close": {},
"disable": "Disable",
"enable": "Enable",
"cancel": "Cancel",
"@cancel": {},
"ok": "Ok",
"@ok": {},
"switchFrontAndBackCamera": "Switch between front and back camera.",
"@switchFrontAndBackCamera": {},
"addTextItem": "Text",
@ -423,5 +419,35 @@
"backupNoticeTitle": "No backup configured",
"backupNoticeDesc": "If you change or lose your device, no one can restore your account without a backup. Therefore, back up your data.",
"backupNoticeLater": "Remind later",
"backupNoticeOpenBackup": "Create backup"
"backupNoticeOpenBackup": "Create backup",
"backupPending": "Pending",
"backupFailed": "Failed",
"backupSuccess": "Success",
"backupTwonlySafeDesc": "Back up your twonly identity, as this is the only way to restore your account if you uninstall the app or lose your phone.",
"backupServer": "Server",
"backupMaxBackupSize": "max. backup size",
"backupStorageRetention": "Storage retention",
"backupLastBackupDate": "Last backup",
"backupLastBackupSize": "Backup size",
"backupLastBackupResult": "Result",
"deleteBackupTitle": "Are you sure?",
"deleteBackupBody": "Without an backup, you can not restore your user account.",
"backupData": "Data-Backup",
"backupDataDesc": "This backup contains besides of your twonly-Identity also all of your media files. This backup will is also encrypted but stored locally. You then have to ensure to manually copy it onto your laptop or device of your choice.",
"backupInsecurePassword": "Insecure password",
"backupInsecurePasswordDesc": "The chosen password is very insecure and can therefore easily be guessed by attackers. Please choose a secure password.",
"backupInsecurePasswordOk": "Continue anyway",
"backupInsecurePasswordCancel": "Try again",
"backupTwonlySafeLongDesc": "twonly does not have any central user accounts. A key pair is created during installation, which consists of a public and a private key. The private key is only stored on your device to protect it from unauthorized access. The public key is uploaded to the server and linked to your chosen username so that others can find you.\n\ntwonly Safe regularly creates an encrypted, anonymous backup of your private key together with your contacts and settings. Your username and chosen password are enough to restore this data on another device.",
"backupSelectStrongPassword": "Choose a secure password. This is required if you want to restore your twonly Safe backup.",
"password": "Password",
"passwordRepeated": "Repeat password",
"passwordRepeatedNotEqual": "Passwords do not match.",
"backupPasswordRequirement": "Password must be at least 8 characters long.",
"backupExpertSettings": "Expert settings",
"backupEnableBackup": "Activate automatic backup",
"backupOwnServerDesc": "Save your twonly safe backups at twonly or on any server of your choice.",
"backupUseOwnServer": "Use server",
"backupResetServer": "Use standard server",
"backupTwonlySaveNow": "Save now"
}

View file

@ -926,6 +926,18 @@ abstract class AppLocalizations {
/// **'Close'**
String get close;
/// No description provided for @disable.
///
/// In en, this message translates to:
/// **'Disable'**
String get disable;
/// No description provided for @enable.
///
/// In en, this message translates to:
/// **'Enable'**
String get enable;
/// No description provided for @cancel.
///
/// In en, this message translates to:
@ -1603,6 +1615,186 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Create backup'**
String get backupNoticeOpenBackup;
/// No description provided for @backupPending.
///
/// In en, this message translates to:
/// **'Pending'**
String get backupPending;
/// No description provided for @backupFailed.
///
/// In en, this message translates to:
/// **'Failed'**
String get backupFailed;
/// No description provided for @backupSuccess.
///
/// In en, this message translates to:
/// **'Success'**
String get backupSuccess;
/// No description provided for @backupTwonlySafeDesc.
///
/// In en, this message translates to:
/// **'Back up your twonly identity, as this is the only way to restore your account if you uninstall the app or lose your phone.'**
String get backupTwonlySafeDesc;
/// No description provided for @backupServer.
///
/// In en, this message translates to:
/// **'Server'**
String get backupServer;
/// No description provided for @backupMaxBackupSize.
///
/// In en, this message translates to:
/// **'max. backup size'**
String get backupMaxBackupSize;
/// No description provided for @backupStorageRetention.
///
/// In en, this message translates to:
/// **'Storage retention'**
String get backupStorageRetention;
/// No description provided for @backupLastBackupDate.
///
/// In en, this message translates to:
/// **'Last backup'**
String get backupLastBackupDate;
/// No description provided for @backupLastBackupSize.
///
/// In en, this message translates to:
/// **'Backup size'**
String get backupLastBackupSize;
/// No description provided for @backupLastBackupResult.
///
/// In en, this message translates to:
/// **'Result'**
String get backupLastBackupResult;
/// No description provided for @deleteBackupTitle.
///
/// In en, this message translates to:
/// **'Are you sure?'**
String get deleteBackupTitle;
/// No description provided for @deleteBackupBody.
///
/// In en, this message translates to:
/// **'Without an backup, you can not restore your user account.'**
String get deleteBackupBody;
/// No description provided for @backupData.
///
/// In en, this message translates to:
/// **'Data-Backup'**
String get backupData;
/// No description provided for @backupDataDesc.
///
/// In en, this message translates to:
/// **'This backup contains besides of your twonly-Identity also all of your media files. This backup will is also encrypted but stored locally. You then have to ensure to manually copy it onto your laptop or device of your choice.'**
String get backupDataDesc;
/// No description provided for @backupInsecurePassword.
///
/// In en, this message translates to:
/// **'Insecure password'**
String get backupInsecurePassword;
/// No description provided for @backupInsecurePasswordDesc.
///
/// In en, this message translates to:
/// **'The chosen password is very insecure and can therefore easily be guessed by attackers. Please choose a secure password.'**
String get backupInsecurePasswordDesc;
/// No description provided for @backupInsecurePasswordOk.
///
/// In en, this message translates to:
/// **'Continue anyway'**
String get backupInsecurePasswordOk;
/// No description provided for @backupInsecurePasswordCancel.
///
/// In en, this message translates to:
/// **'Try again'**
String get backupInsecurePasswordCancel;
/// No description provided for @backupTwonlySafeLongDesc.
///
/// In en, this message translates to:
/// **'twonly does not have any central user accounts. A key pair is created during installation, which consists of a public and a private key. The private key is only stored on your device to protect it from unauthorized access. The public key is uploaded to the server and linked to your chosen username so that others can find you.\n\ntwonly Safe regularly creates an encrypted, anonymous backup of your private key together with your contacts and settings. Your username and chosen password are enough to restore this data on another device.'**
String get backupTwonlySafeLongDesc;
/// No description provided for @backupSelectStrongPassword.
///
/// In en, this message translates to:
/// **'Choose a secure password. This is required if you want to restore your twonly Safe backup.'**
String get backupSelectStrongPassword;
/// No description provided for @password.
///
/// In en, this message translates to:
/// **'Password'**
String get password;
/// No description provided for @passwordRepeated.
///
/// In en, this message translates to:
/// **'Repeat password'**
String get passwordRepeated;
/// No description provided for @passwordRepeatedNotEqual.
///
/// In en, this message translates to:
/// **'Passwords do not match.'**
String get passwordRepeatedNotEqual;
/// No description provided for @backupPasswordRequirement.
///
/// In en, this message translates to:
/// **'Password must be at least 8 characters long.'**
String get backupPasswordRequirement;
/// No description provided for @backupExpertSettings.
///
/// In en, this message translates to:
/// **'Expert settings'**
String get backupExpertSettings;
/// No description provided for @backupEnableBackup.
///
/// In en, this message translates to:
/// **'Activate automatic backup'**
String get backupEnableBackup;
/// No description provided for @backupOwnServerDesc.
///
/// In en, this message translates to:
/// **'Save your twonly safe backups at twonly or on any server of your choice.'**
String get backupOwnServerDesc;
/// No description provided for @backupUseOwnServer.
///
/// In en, this message translates to:
/// **'Use server'**
String get backupUseOwnServer;
/// No description provided for @backupResetServer.
///
/// In en, this message translates to:
/// **'Use standard server'**
String get backupResetServer;
/// No description provided for @backupTwonlySaveNow.
///
/// In en, this message translates to:
/// **'Save now'**
String get backupTwonlySaveNow;
}
class _AppLocalizationsDelegate

View file

@ -468,6 +468,12 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get close => 'Schließen';
@override
String get disable => 'Deaktiviern';
@override
String get enable => 'Aktivieren';
@override
String get cancel => 'Abbrechen';
@ -849,4 +855,102 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get backupNoticeOpenBackup => 'Backup erstellen';
@override
String get backupPending => 'Ausstehend';
@override
String get backupFailed => 'Fehlgeschlagen';
@override
String get backupSuccess => 'Erfolgreich';
@override
String get backupTwonlySafeDesc =>
'Sichere deine twonly-Identität, da dies die einzige Möglichkeit ist, dein Konto wiederherzustellen, wenn du die App deinstallierst oder dein Handy verlierst.';
@override
String get backupServer => 'Server';
@override
String get backupMaxBackupSize => 'max. Backup-Größe';
@override
String get backupStorageRetention => 'Speicheraufbewahrung';
@override
String get backupLastBackupDate => 'Letztes Backup';
@override
String get backupLastBackupSize => 'Backup-Größe';
@override
String get backupLastBackupResult => 'Ergebnis';
@override
String get deleteBackupTitle => 'Bist du sicher?';
@override
String get deleteBackupBody =>
'Ohne ein Backup kannst du dein Benutzerkonto nicht wiederherstellen.';
@override
String get backupData => 'Daten-Backup';
@override
String get backupDataDesc =>
'Das Daten-Backup enthält neben deiner twonly-Identität auch alle deine Mediendateien. Dieses Backup ist ebenfalls verschlüsselt, wird jedoch lokal gespeichert. Du musst es dann manuell auf deinen Laptop oder ein Gerät deiner Wahl kopieren.';
@override
String get backupInsecurePassword => 'Unsicheres Passwort';
@override
String get backupInsecurePasswordDesc =>
'Das gewählte Passwort ist sehr unsicher und kann daher leicht von Angreifern erraten werden. Bitte wähle ein sicheres Passwort.';
@override
String get backupInsecurePasswordOk => 'Trotzdem fortfahren';
@override
String get backupInsecurePasswordCancel => 'Erneut versuchen';
@override
String get backupTwonlySafeLongDesc =>
'twonly hat keine zentralen Benutzerkonten. Ein Schlüsselpaar wird während der Installation erstellt, das aus einem öffentlichen und einem privaten Schlüssel besteht. Der private Schlüssel wird nur auf deinem Gerät gespeichert, um ihn vor unbefugtem Zugriff zu schützen. Der öffentliche Schlüssel wird auf den Server hochgeladen und mit deinem gewählten Benutzernamen verknüpft, damit andere dich finden können.\n\ntwonly Safe erstellt regelmäßig ein verschlüsseltes, anonymes Backup deines privaten Schlüssels zusammen mit deinen Kontakten und Einstellungen. Dein Benutzername und das gewählte Passwort reichen aus, um diese Daten auf einem anderen Gerät wiederherzustellen.';
@override
String get backupSelectStrongPassword =>
'Wähle ein sicheres Passwort. Dies ist erforderlich, wenn du dein twonly Safe-Backup wiederherstellen möchtest.';
@override
String get password => 'Passwort';
@override
String get passwordRepeated => 'Passwort wiederholen';
@override
String get passwordRepeatedNotEqual => 'Passwörter stimmen nicht überein.';
@override
String get backupPasswordRequirement =>
'Das Passwort muss mindestens 8 Zeichen lang sein.';
@override
String get backupExpertSettings => 'Experteneinstellungen';
@override
String get backupEnableBackup => 'Automatische Sicherung aktivieren';
@override
String get backupOwnServerDesc =>
'Speichere dein twonly Safe-Backups auf einem Server deiner Wahl.';
@override
String get backupUseOwnServer => 'Server verwenden';
@override
String get backupResetServer => 'Standardserver verwenden';
@override
String get backupTwonlySaveNow => 'Jetzt speichern';
}

View file

@ -463,6 +463,12 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get close => 'Close';
@override
String get disable => 'Disable';
@override
String get enable => 'Enable';
@override
String get cancel => 'Cancel';
@ -843,4 +849,102 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get backupNoticeOpenBackup => 'Create backup';
@override
String get backupPending => 'Pending';
@override
String get backupFailed => 'Failed';
@override
String get backupSuccess => 'Success';
@override
String get backupTwonlySafeDesc =>
'Back up your twonly identity, as this is the only way to restore your account if you uninstall the app or lose your phone.';
@override
String get backupServer => 'Server';
@override
String get backupMaxBackupSize => 'max. backup size';
@override
String get backupStorageRetention => 'Storage retention';
@override
String get backupLastBackupDate => 'Last backup';
@override
String get backupLastBackupSize => 'Backup size';
@override
String get backupLastBackupResult => 'Result';
@override
String get deleteBackupTitle => 'Are you sure?';
@override
String get deleteBackupBody =>
'Without an backup, you can not restore your user account.';
@override
String get backupData => 'Data-Backup';
@override
String get backupDataDesc =>
'This backup contains besides of your twonly-Identity also all of your media files. This backup will is also encrypted but stored locally. You then have to ensure to manually copy it onto your laptop or device of your choice.';
@override
String get backupInsecurePassword => 'Insecure password';
@override
String get backupInsecurePasswordDesc =>
'The chosen password is very insecure and can therefore easily be guessed by attackers. Please choose a secure password.';
@override
String get backupInsecurePasswordOk => 'Continue anyway';
@override
String get backupInsecurePasswordCancel => 'Try again';
@override
String get backupTwonlySafeLongDesc =>
'twonly does not have any central user accounts. A key pair is created during installation, which consists of a public and a private key. The private key is only stored on your device to protect it from unauthorized access. The public key is uploaded to the server and linked to your chosen username so that others can find you.\n\ntwonly Safe regularly creates an encrypted, anonymous backup of your private key together with your contacts and settings. Your username and chosen password are enough to restore this data on another device.';
@override
String get backupSelectStrongPassword =>
'Choose a secure password. This is required if you want to restore your twonly Safe backup.';
@override
String get password => 'Password';
@override
String get passwordRepeated => 'Repeat password';
@override
String get passwordRepeatedNotEqual => 'Passwords do not match.';
@override
String get backupPasswordRequirement =>
'Password must be at least 8 characters long.';
@override
String get backupExpertSettings => 'Expert settings';
@override
String get backupEnableBackup => 'Activate automatic backup';
@override
String get backupOwnServerDesc =>
'Save your twonly safe backups at twonly or on any server of your choice.';
@override
String get backupUseOwnServer => 'Use server';
@override
String get backupResetServer => 'Use standard server';
@override
String get backupTwonlySaveNow => 'Save now';
}

View file

@ -94,6 +94,11 @@ Future performTwonlySafeBackup({bool force = false}) async {
if (userBackup == null) return;
// FILTER settings which should not be in the backup
userBackup.twonlySafeBackup = null;
userBackup.lastImageSend = null;
userBackup.todaysImageCounter = null;
userBackup.lastPlanBallance = "";
userBackup.additionalUserInvites = "";
userBackup.signalLastSignedPreKeyUpdated = null;
secureStorageBackup[SecureStorageKeys.userData] = jsonEncode(userBackup);

View file

@ -4,11 +4,16 @@ import 'package:flutter/material.dart';
import 'package:twonly/src/utils/misc.dart';
Future<bool> showAlertDialog(
BuildContext context, String title, String content) async {
BuildContext context,
String title,
String content, {
String? customOk,
String? customCancel,
}) async {
Completer<bool> completer = Completer<bool>();
Widget okButton = TextButton(
child: Text(context.lang.ok),
child: Text(customOk ?? context.lang.ok),
onPressed: () {
completer.complete(true);
Navigator.pop(context);
@ -16,7 +21,7 @@ Future<bool> showAlertDialog(
);
Widget cancelButton = TextButton(
child: Text(context.lang.cancel),
child: Text(customCancel ?? context.lang.cancel),
onPressed: () {
completer.complete(false);
Navigator.pop(context);

View file

@ -25,6 +25,7 @@ BackupServer defaultBackupServer = BackupServer(
class _BackupViewState extends State<BackupView> {
TwonlySafeBackup? twonlySafeBackup;
BackupServer backupServer = defaultBackupServer;
bool isLoading = false;
int activePageIdx = 0;
@ -57,13 +58,13 @@ class _BackupViewState extends State<BackupView> {
String backupStatus(LastBackupUploadState status) {
switch (status) {
case LastBackupUploadState.none:
return '-';
return '';
case LastBackupUploadState.pending:
return 'Pending';
return context.lang.backupPending;
case LastBackupUploadState.failed:
return 'Failed';
return context.lang.backupFailed;
case LastBackupUploadState.success:
return 'Success';
return context.lang.backupSuccess;
}
}
@ -83,69 +84,96 @@ class _BackupViewState extends State<BackupView> {
children: [
BackupOption(
title: 'twonly Safe',
description:
'Back up your twonly identity, as this is the only way to restore your account if you uninstall or lose your phone.',
description: context.lang.backupTwonlySafeDesc,
autoBackupEnabled: twonlySafeBackup != null,
child: (twonlySafeBackup != null)
? Table(
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
child: (twonlySafeBackup == null)
? null
: Column(
children: [
...[
(
"Server",
(backupServer.serverUrl.contains("@"))
? backupServer.serverUrl.split("@")[1]
: backupServer.serverUrl
.replaceAll("https://", "")
),
(
"Max. Backup-Größe",
formatBytes(backupServer.maxBackupBytes)
),
("Speicherdauer", "${backupServer.retentionDays} Days"),
(
"Letztes Backup",
formatDateTime(
context, twonlySafeBackup!.lastBackupDone)
),
(
"Backup-Größe",
formatBytes(twonlySafeBackup!.lastBackupSize)
),
(
"Ergebnis",
backupStatus(twonlySafeBackup!.backupUploadState)
)
].map((pair) {
return TableRow(
children: [
TableCell(
// padding: EdgeInsets.all(4),
child: Text(pair.$1),
Table(
defaultVerticalAlignment:
TableCellVerticalAlignment.middle,
children: [
...[
(
context.lang.backupServer,
(backupServer.serverUrl.contains("@"))
? backupServer.serverUrl.split("@")[1]
: backupServer.serverUrl
.replaceAll("https://", "")
),
TableCell(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 4),
child: Text(
pair.$2,
textAlign: TextAlign.right,
(
context.lang.backupMaxBackupSize,
formatBytes(backupServer.maxBackupBytes)
),
(
context.lang.backupStorageRetention,
"${backupServer.retentionDays} Days"
),
(
context.lang.backupLastBackupDate,
formatDateTime(
context, twonlySafeBackup!.lastBackupDone)
),
(
context.lang.backupLastBackupSize,
formatBytes(twonlySafeBackup!.lastBackupSize)
),
(
context.lang.backupLastBackupResult,
backupStatus(twonlySafeBackup!.backupUploadState)
)
].map((pair) {
return TableRow(
children: [
TableCell(
// padding: EdgeInsets.all(4),
child: Text(pair.$1),
),
),
),
],
);
}),
TableCell(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 4),
child: Text(
pair.$2,
textAlign: TextAlign.right,
),
),
),
],
);
}),
],
),
SizedBox(height: 10),
FilledButton(
onPressed: (isLoading)
? null
: () async {
setState(() {
isLoading = true;
});
await performTwonlySafeBackup(force: true);
setState(() {
isLoading = false;
});
},
child: Text(context.lang.backupTwonlySaveNow),
)
],
)
: null,
),
onTap: () async {
if (twonlySafeBackup != null) {
bool disable = await showAlertDialog(context, "Are you sure?",
"Without an backup, you can not restore your user account.");
bool disable = await showAlertDialog(
context,
context.lang.deleteBackupTitle,
context.lang.deleteBackupBody);
if (disable) {
await disableTwonlySafe();
}
} else {
setState(() {
isLoading = true;
});
await Navigator.push(context,
MaterialPageRoute(builder: (context) {
return TwonlyIdentityBackupView();
@ -155,9 +183,8 @@ class _BackupViewState extends State<BackupView> {
},
),
BackupOption(
title: 'Daten-Backup (Coming Soon)',
description:
'This backup contains besides of your twonly-Identity also all of your media files. This backup will also be encrypted using a password chosen by the user but stored locally on the smartphone. You then have to ensure to manually copy it onto your laptop or device of your choice.',
title: "${context.lang.backupData} (Coming Soon)",
description: context.lang.backupDataDesc,
autoBackupEnabled: false,
onTap: null,
),
@ -177,7 +204,7 @@ class _BackupViewState extends State<BackupView> {
),
BottomNavigationBarItem(
icon: FaIcon(FontAwesomeIcons.boxArchive, size: 17),
label: "Daten-Backup",
label: context.lang.backupData,
),
],
onTap: (int index) {
@ -237,11 +264,11 @@ class BackupOption extends StatelessWidget {
child: (autoBackupEnabled)
? OutlinedButton(
onPressed: onTap,
child: Text("Disable"),
child: Text(context.lang.disable),
)
: FilledButton(
onPressed: onTap,
child: Text("Enable"),
child: Text(context.lang.enable),
),
)
],

View file

@ -1,3 +1,4 @@
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
@ -21,6 +22,21 @@ class _TwonlyIdentityBackupViewState extends State<TwonlyIdentityBackupView> {
final TextEditingController repeatedPasswordCtrl = TextEditingController();
Future onPressedEnableTwonlySafe() async {
if (!mounted) return;
if (!await isSecurePassword(passwordCtrl.text)) {
if (!mounted) return;
bool ignore = await showAlertDialog(
context,
context.lang.backupInsecurePassword,
context.lang.backupInsecurePasswordDesc,
customCancel: context.lang.backupInsecurePasswordOk,
customOk: context.lang.backupInsecurePasswordCancel,
);
if (ignore) {
return;
}
}
setState(() {
isLoading = true;
});
@ -45,7 +61,7 @@ class _TwonlyIdentityBackupViewState extends State<TwonlyIdentityBackupView> {
IconButton(
onPressed: () {
showAlertDialog(context, "twonly Safe",
"twonly does not have any central user accounts. A key pair is created during installation, which consists of a public and a private key. The private key is only stored on your device to protect it from unauthorized access. The public key is uploaded to the server and linked to your chosen user name so that others can find you.\n\ntwonly Safe regularly creates an encrypted, anonymous backup of your private key together with your contacts and settings. Your username and chosen password are enough to restore this data on another device. ");
context.lang.backupTwonlySafeLongDesc);
},
icon: FaIcon(FontAwesomeIcons.circleInfo),
iconSize: 18,
@ -57,7 +73,7 @@ class _TwonlyIdentityBackupViewState extends State<TwonlyIdentityBackupView> {
child: ListView(
children: [
Text(
"Wähle ein sicheres Passwort. Dieses wird benötigt, wenn du dein twonly Safe-Backup wiederherstellen möchtest.",
context.lang.backupSelectStrongPassword,
textAlign: TextAlign.center,
),
const SizedBox(height: 30),
@ -67,16 +83,12 @@ class _TwonlyIdentityBackupViewState extends State<TwonlyIdentityBackupView> {
controller: passwordCtrl,
onChanged: (value) {
setState(() {});
// usernameController.text = value.toLowerCase();
// usernameController.selection = TextSelection.fromPosition(
// TextPosition(offset: usernameController.text.length),
// );
},
style: TextStyle(fontSize: 17),
obscureText: obscureText,
decoration: getInputDecoration(
context,
"Password",
context.lang.password,
),
),
Positioned(
@ -102,9 +114,9 @@ class _TwonlyIdentityBackupViewState extends State<TwonlyIdentityBackupView> {
Padding(
padding: EdgeInsetsGeometry.all(5),
child: Text(
"Passwort muss mind. 10 Zeichen lang sein.",
context.lang.backupPasswordRequirement,
style: TextStyle(
color: ((passwordCtrl.text.length <= 10 &&
color: ((passwordCtrl.text.length < 8 &&
passwordCtrl.text.isNotEmpty))
? Colors.red
: Colors.transparent),
@ -115,22 +127,18 @@ class _TwonlyIdentityBackupViewState extends State<TwonlyIdentityBackupView> {
controller: repeatedPasswordCtrl,
onChanged: (value) {
setState(() {});
// usernameController.text = value.toLowerCase();
// usernameController.selection = TextSelection.fromPosition(
// TextPosition(offset: usernameController.text.length),
// );
},
style: TextStyle(fontSize: 17),
obscureText: true,
decoration: getInputDecoration(
context,
"Passwordwiederholung",
context.lang.passwordRepeated,
),
),
Padding(
padding: EdgeInsetsGeometry.all(5),
child: Text(
"Passwörter stimmen nicht überein.",
context.lang.passwordRepeatedNotEqual,
style: TextStyle(
color: (passwordCtrl.text != repeatedPasswordCtrl.text &&
repeatedPasswordCtrl.text.isNotEmpty)
@ -146,7 +154,7 @@ class _TwonlyIdentityBackupViewState extends State<TwonlyIdentityBackupView> {
return TwonlySafeServerView();
}));
},
child: Text("Experten Einstellungen"),
child: Text(context.lang.backupExpertSettings),
),
),
SizedBox(height: 10),
@ -165,7 +173,7 @@ class _TwonlyIdentityBackupViewState extends State<TwonlyIdentityBackupView> {
child: CircularProgressIndicator(strokeWidth: 1),
)
: Icon(Icons.lock_clock_rounded),
label: Text("Automatisches Backup aktivieren"),
label: Text(context.lang.backupEnableBackup),
))
],
),
@ -173,3 +181,16 @@ class _TwonlyIdentityBackupViewState extends State<TwonlyIdentityBackupView> {
);
}
}
Future<bool> isSecurePassword(String password) async {
String badPasswordsStr =
await rootBundle.loadString('assets/passwords/bad_passwords.txt');
List<String> badPasswords = badPasswordsStr.split("\n");
if (badPasswords.contains(password)) {
return false;
}
// Check if the password meets all criteria
return RegExp(r'[A-Z]').hasMatch(password) &&
RegExp(r'[a-z]').hasMatch(password) &&
RegExp(r'[0-9]').hasMatch(password);
}

View file

@ -1,10 +1,10 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:http/http.dart' as http;
import 'package:twonly/src/model/json/userdata.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/utils/storage.dart';
class TwonlySafeServerView extends StatefulWidget {
@ -58,17 +58,15 @@ class _TwonlySafeServerViewState extends State<TwonlySafeServerView> {
serverUrl = "https://$username@$password$serverUrl";
}
final uri = Uri.parse("${serverUrl}config");
final response = await http.get(
uri,
headers: {
'User-Agent': 'twonly',
'Accept': 'application/json',
},
);
try {
final uri = Uri.parse("${serverUrl}config");
final response = await http.get(
uri,
headers: {
'User-Agent': 'twonly',
'Accept': 'application/json',
},
);
if (response.statusCode == 200) {
// If the server returns a 200 OK response, parse the JSON.
final data = jsonDecode(response.body);
@ -111,7 +109,7 @@ class _TwonlySafeServerViewState extends State<TwonlySafeServerView> {
child: ListView(
children: [
Text(
"Speichere dein twonly Safe-Backup bei twonly oder auf einem beliebigen Server deiner Wahl.",
context.lang.backupOwnServerDesc,
textAlign: TextAlign.center,
),
const SizedBox(height: 30),
@ -156,7 +154,7 @@ class _TwonlySafeServerViewState extends State<TwonlySafeServerView> {
? checkAndUpdateBackupServer
: null,
icon: FaIcon(FontAwesomeIcons.server),
label: Text('Server verwenden'),
label: Text(context.lang.backupUseOwnServer),
),
),
SizedBox(height: 10.0),
@ -169,7 +167,7 @@ class _TwonlySafeServerViewState extends State<TwonlySafeServerView> {
});
if (context.mounted) Navigator.pop(context);
},
child: Text("Standardserver verwenden"),
child: Text(context.lang.backupResetServer),
),
)
],