mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-07 00:24:18 +00:00
chore: Merge pull request 'feat(backups): Implement modal for copying backups encryption key' (#242) from clipboard-key into master
Reviewed-on: https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/pulls/242 Reviewed-by: Inex Code <inex.code@selfprivacy.org>
This commit is contained in:
commit
15039777e0
|
@ -34,7 +34,8 @@
|
|||
"apply": "Apply",
|
||||
"done": "Done",
|
||||
"continue": "Continue",
|
||||
"alert": "Alert"
|
||||
"alert": "Alert",
|
||||
"copied_to_clipboard": "Copied to clipboard!"
|
||||
},
|
||||
"more_page": {
|
||||
"configuration_wizard": "Setup wizard",
|
||||
|
@ -196,6 +197,11 @@
|
|||
"autobackup_custom_hint": "Enter custom period in minutes",
|
||||
"autobackup_set_period": "Set period",
|
||||
"autobackup_period_set": "Period set",
|
||||
"backups_encryption_key": "Encryption key",
|
||||
"backups_encryption_key_subtitle": "Keep it in a safe place.",
|
||||
"backups_encryption_key_copy": "Copy the encryption key",
|
||||
"backups_encryption_key_show": "Show the encryption key",
|
||||
"backups_encryption_key_description": "This key is used to encrypt your backups. If you lose it, you will not be able to restore your backups. Keep it in a safe place, as it will be useful if you ever need to restore from backups manually.",
|
||||
"pending_jobs": "Currently running backup jobs",
|
||||
"snapshots_title": "Snapshot list"
|
||||
},
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
"done": "Готово",
|
||||
"continue": "Продолжить",
|
||||
"alert": "Уведомление",
|
||||
"copied_to_clipboard": "Скопировано в буфер обмена!",
|
||||
"app_name": "SelfPrivacy"
|
||||
},
|
||||
"more_page": {
|
||||
|
@ -197,6 +198,7 @@
|
|||
"autobackup_custom_hint": "Введите период в минутах",
|
||||
"autobackup_set_period": "Установить период",
|
||||
"autobackup_period_set": "Период установлен",
|
||||
"backups_encryption_key": "Ключ шифрования",
|
||||
"snapshots_title": "Список снимков"
|
||||
},
|
||||
"storage": {
|
||||
|
@ -536,4 +538,4 @@
|
|||
"ignore_tls_description": "Приложение не будет проверять сертификаты TLS при подключении к серверу.",
|
||||
"ignore_tls": "Не проверять сертификаты TLS"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -76,21 +76,44 @@ class ServerInstallationRepository {
|
|||
|
||||
if (box.get(BNames.hasFinalChecked, defaultValue: false)) {
|
||||
TlsOptions.verifyCertificate = true;
|
||||
return ServerInstallationFinished(
|
||||
installationDialoguePopUp: null,
|
||||
providerApiToken: providerApiToken!,
|
||||
serverTypeIdentificator: serverTypeIdentificator!,
|
||||
dnsApiToken: dnsApiToken!,
|
||||
serverDomain: serverDomain!,
|
||||
backblazeCredential: backblazeCredential!,
|
||||
serverDetails: serverDetails!,
|
||||
rootUser: box.get(BNames.rootUser),
|
||||
isServerStarted: box.get(BNames.isServerStarted, defaultValue: false),
|
||||
isServerResetedFirstTime:
|
||||
box.get(BNames.isServerResetedFirstTime, defaultValue: false),
|
||||
isServerResetedSecondTime:
|
||||
box.get(BNames.isServerResetedSecondTime, defaultValue: false),
|
||||
);
|
||||
if (serverTypeIdentificator == null && serverDetails != null) {
|
||||
final finalServerType = await ProvidersController.currentServerProvider!
|
||||
.getServerType(serverDetails.id);
|
||||
await saveServerType(finalServerType.data!);
|
||||
await ProvidersController.currentServerProvider!
|
||||
.trySetServerLocation(finalServerType.data!.location.identifier);
|
||||
return ServerInstallationFinished(
|
||||
installationDialoguePopUp: null,
|
||||
providerApiToken: providerApiToken!,
|
||||
serverTypeIdentificator: finalServerType.data!.identifier,
|
||||
dnsApiToken: dnsApiToken!,
|
||||
serverDomain: serverDomain!,
|
||||
backblazeCredential: backblazeCredential!,
|
||||
serverDetails: serverDetails,
|
||||
rootUser: box.get(BNames.rootUser),
|
||||
isServerStarted: box.get(BNames.isServerStarted, defaultValue: false),
|
||||
isServerResetedFirstTime:
|
||||
box.get(BNames.isServerResetedFirstTime, defaultValue: false),
|
||||
isServerResetedSecondTime:
|
||||
box.get(BNames.isServerResetedSecondTime, defaultValue: false),
|
||||
);
|
||||
} else {
|
||||
return ServerInstallationFinished(
|
||||
installationDialoguePopUp: null,
|
||||
providerApiToken: providerApiToken!,
|
||||
serverTypeIdentificator: serverTypeIdentificator!,
|
||||
dnsApiToken: dnsApiToken!,
|
||||
serverDomain: serverDomain!,
|
||||
backblazeCredential: backblazeCredential!,
|
||||
serverDetails: serverDetails!,
|
||||
rootUser: box.get(BNames.rootUser),
|
||||
isServerStarted: box.get(BNames.isServerStarted, defaultValue: false),
|
||||
isServerResetedFirstTime:
|
||||
box.get(BNames.isServerResetedFirstTime, defaultValue: false),
|
||||
isServerResetedSecondTime:
|
||||
box.get(BNames.isServerResetedSecondTime, defaultValue: false),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (box.get(BNames.isRecoveringServer, defaultValue: false) &&
|
||||
|
|
|
@ -16,6 +16,7 @@ import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
|||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||
import 'package:selfprivacy/ui/helpers/modals.dart';
|
||||
import 'package:selfprivacy/ui/pages/backups/change_period_modal.dart';
|
||||
import 'package:selfprivacy/ui/pages/backups/copy_encryption_key_modal.dart';
|
||||
import 'package:selfprivacy/ui/pages/backups/create_backups_modal.dart';
|
||||
import 'package:selfprivacy/ui/router/router.dart';
|
||||
import 'package:selfprivacy/utils/extensions/duration.dart';
|
||||
|
@ -144,6 +145,37 @@ class BackupDetailsPage extends StatelessWidget {
|
|||
: 'backup.autobackup_period_never'.tr(),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
onTap: preventActions
|
||||
? null
|
||||
: () {
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (final BuildContext context) =>
|
||||
DraggableScrollableSheet(
|
||||
expand: false,
|
||||
maxChildSize: 0.9,
|
||||
minChildSize: 0.5,
|
||||
initialChildSize: 0.7,
|
||||
builder: (final context, final scrollController) =>
|
||||
CopyEncryptionKeyModal(
|
||||
scrollController: scrollController,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
leading: const Icon(
|
||||
Icons.key_outlined,
|
||||
),
|
||||
title: Text(
|
||||
'backup.backups_encryption_key'.tr(),
|
||||
),
|
||||
subtitle: Text(
|
||||
'backup.backups_encryption_key_subtitle'.tr(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (backupJobs.isNotEmpty)
|
||||
Column(
|
||||
|
|
|
@ -20,9 +20,6 @@ class ChangeAutobackupsPeriodModal extends StatefulWidget {
|
|||
|
||||
class _ChangeAutobackupsPeriodModalState
|
||||
extends State<ChangeAutobackupsPeriodModal> {
|
||||
// This is a modal with radio buttons to select the autobackup period
|
||||
// Period might be none, selected from predefined list or custom
|
||||
// Store in state the selected period
|
||||
Duration? selectedPeriod;
|
||||
|
||||
static const List<Duration> autobackupPeriods = [
|
||||
|
|
142
lib/ui/pages/backups/copy_encryption_key_modal.dart
Normal file
142
lib/ui/pages/backups/copy_encryption_key_modal.dart
Normal file
|
@ -0,0 +1,142 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart';
|
||||
|
||||
class CopyEncryptionKeyModal extends StatefulWidget {
|
||||
const CopyEncryptionKeyModal({
|
||||
required this.scrollController,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final ScrollController scrollController;
|
||||
|
||||
@override
|
||||
State<CopyEncryptionKeyModal> createState() => _CopyEncryptionKeyModalState();
|
||||
}
|
||||
|
||||
class _CopyEncryptionKeyModalState extends State<CopyEncryptionKeyModal> {
|
||||
bool isKeyVisible = false;
|
||||
bool copiedToClipboard = false;
|
||||
Timer? copyToClipboardTimer;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
copyToClipboardTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
final String encryptionKey =
|
||||
context.watch<BackupsCubit>().state.backblazeBucket!.encryptionKey;
|
||||
return ListView(
|
||||
controller: widget.scrollController,
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'backup.backups_encryption_key'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'backup.backups_encryption_key_description'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Stack(
|
||||
children: [
|
||||
SelectableText(
|
||||
encryptionKey,
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
setState(
|
||||
() {
|
||||
isKeyVisible = !isKeyVisible;
|
||||
},
|
||||
);
|
||||
},
|
||||
child: AnimatedOpacity(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
opacity: isKeyVisible ? 0 : 1,
|
||||
child: Container(
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.visibility_outlined),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'backup.backups_encryption_key_show'.tr(),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
const SizedBox(height: 8),
|
||||
FilledButton.icon(
|
||||
onPressed: () {
|
||||
setState(
|
||||
() {
|
||||
copiedToClipboard = true;
|
||||
},
|
||||
);
|
||||
// Make a timer to reset the copyToClipboardTime
|
||||
setState(() {
|
||||
copyToClipboardTimer?.cancel();
|
||||
copyToClipboardTimer = Timer(
|
||||
const Duration(seconds: 5),
|
||||
() {
|
||||
setState(() {
|
||||
copiedToClipboard = false;
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
Clipboard.setData(
|
||||
ClipboardData(
|
||||
text: encryptionKey,
|
||||
),
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.copy_all_outlined),
|
||||
label: Text(
|
||||
copiedToClipboard
|
||||
? 'basis.copied_to_clipboard'.tr()
|
||||
: 'backup.backups_encryption_key_copy'.tr(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue