diff --git a/app/lib/l10n/app_en.arb b/app/lib/l10n/app_en.arb index 514d324e..da87f272 100644 --- a/app/lib/l10n/app_en.arb +++ b/app/lib/l10n/app_en.arb @@ -330,6 +330,11 @@ "@settingsAccountPageTitle": { "description": "Dedicated page for account settings" }, + "settingsAccountLabelTitle": "Label", + "@settingsAccountLabelTitle": { + "description": "An account label is used to replace the server URL in the app bar, could be useful for privacy reason" + }, + "settingsAccountLabelDescription": "Set a label to be shown in place of the server URL", "settingsIncludedFoldersTitle": "Included folders", "@settingsIncludedFoldersTitle": { "description": "Change the included folders of an account" diff --git a/app/lib/l10n/untranslated-messages.txt b/app/lib/l10n/untranslated-messages.txt index 5fd78a79..c7841c83 100644 --- a/app/lib/l10n/untranslated-messages.txt +++ b/app/lib/l10n/untranslated-messages.txt @@ -6,6 +6,8 @@ "settingsMemoriesSubtitle", "settingsAccountTitle", "settingsAccountPageTitle", + "settingsAccountLabelTitle", + "settingsAccountLabelDescription", "settingsIncludedFoldersTitle", "settingsShareFolderTitle", "settingsShareFolderDialogTitle", @@ -118,6 +120,8 @@ "settingsMemoriesSubtitle", "settingsAccountTitle", "settingsAccountPageTitle", + "settingsAccountLabelTitle", + "settingsAccountLabelDescription", "settingsIncludedFoldersTitle", "settingsShareFolderTitle", "settingsShareFolderDialogTitle", @@ -240,17 +244,28 @@ "el": [ "settingsExifWifiOnlyTitle", "settingsExifWifiOnlyFalseSubtitle", + "settingsAccountLabelTitle", + "settingsAccountLabelDescription", "enhanceStyleTransferStyleDialogTitle" ], "es": [ + "settingsAccountLabelTitle", + "settingsAccountLabelDescription", "rootPickerSkipConfirmationDialogContent2" ], + "fi": [ + "settingsAccountLabelTitle", + "settingsAccountLabelDescription" + ], + "fr": [ "collectionsTooltip", "settingsExifWifiOnlyTitle", "settingsExifWifiOnlyFalseSubtitle", + "settingsAccountLabelTitle", + "settingsAccountLabelDescription", "settingsPhotoEnhancementTitle", "settingsPhotoEnhancementPageTitle", "settingsEnhanceMaxResolutionTitle", @@ -281,6 +296,8 @@ "pl": [ "settingsExifWifiOnlyTitle", "settingsExifWifiOnlyFalseSubtitle", + "settingsAccountLabelTitle", + "settingsAccountLabelDescription", "settingsPhotoEnhancementTitle", "settingsPhotoEnhancementPageTitle", "settingsEnhanceMaxResolutionTitle", @@ -329,6 +346,8 @@ "pt": [ "settingsExifWifiOnlyTitle", "settingsExifWifiOnlyFalseSubtitle", + "settingsAccountLabelTitle", + "settingsAccountLabelDescription", "settingsPhotoEnhancementTitle", "settingsPhotoEnhancementPageTitle", "settingsEnhanceMaxResolutionTitle", @@ -356,6 +375,8 @@ "ru": [ "settingsExifWifiOnlyTitle", "settingsExifWifiOnlyFalseSubtitle", + "settingsAccountLabelTitle", + "settingsAccountLabelDescription", "settingsPhotoEnhancementTitle", "settingsPhotoEnhancementPageTitle", "settingsEnhanceMaxResolutionTitle", @@ -383,6 +404,8 @@ "zh": [ "settingsExifWifiOnlyTitle", "settingsExifWifiOnlyFalseSubtitle", + "settingsAccountLabelTitle", + "settingsAccountLabelDescription", "settingsPhotoEnhancementTitle", "settingsPhotoEnhancementPageTitle", "settingsEnhanceMaxResolutionTitle", @@ -410,6 +433,8 @@ "zh_Hant": [ "settingsExifWifiOnlyTitle", "settingsExifWifiOnlyFalseSubtitle", + "settingsAccountLabelTitle", + "settingsAccountLabelDescription", "settingsPhotoEnhancementTitle", "settingsPhotoEnhancementPageTitle", "settingsEnhanceMaxResolutionTitle", diff --git a/app/lib/pref.dart b/app/lib/pref.dart index 2cf54cee..fb0f4068 100644 --- a/app/lib/pref.dart +++ b/app/lib/pref.dart @@ -289,6 +289,17 @@ class AccountPref { (key, value) => provider.setString(key, value)); Future removeTouchRootEtag() => _remove(PrefKey.touchRootEtag); + String? getAccountLabel() => provider.getString(PrefKey.accountLabel); + String getAccountLabelOr([String def = ""]) => getAccountLabel() ?? def; + Future setAccountLabel(String? value) { + if (value == null) { + return _remove(PrefKey.accountLabel); + } else { + return _set(PrefKey.accountLabel, value, + (key, value) => provider.setString(key, value)); + } + } + Future _set(PrefKey key, T value, Future Function(PrefKey key, T value) setFn) async { if (await setFn(key, value)) { @@ -519,6 +530,7 @@ enum PrefKey { hasNewSharedAlbum, isEnableMemoryAlbum, touchRootEtag, + accountLabel, } extension on PrefKey { @@ -590,6 +602,8 @@ extension on PrefKey { return "isEnableMemoryAlbum"; case PrefKey.touchRootEtag: return "touchRootEtag"; + case PrefKey.accountLabel: + return "accountLabel"; } } } diff --git a/app/lib/widget/account_picker_dialog.dart b/app/lib/widget/account_picker_dialog.dart index 88e500ec..c7b36a12 100644 --- a/app/lib/widget/account_picker_dialog.dart +++ b/app/lib/widget/account_picker_dialog.dart @@ -33,26 +33,27 @@ class _AccountPickerDialogState extends State { @override build(BuildContext context) { - final otherAccountOptions = _accounts - .where((a) => a != widget.account) - .map((a) => SimpleDialogOption( - padding: const EdgeInsets.symmetric(horizontal: 8), - onPressed: () => _onItemPressed(a), - child: ListTile( - dense: true, - title: Text(a.url), - subtitle: Text(a.username.toString()), - trailing: IconButton( - icon: Icon( - Icons.close, - color: AppTheme.getUnfocusedIconColor(context), - ), - tooltip: L10n.global().deleteTooltip, - onPressed: () => _onRemoveItemPressed(a), - ), - ), - )) - .toList(); + final otherAccountOptions = + _accounts.where((a) => a != widget.account).map((a) { + final label = AccountPref.of(a).getAccountLabel(); + return SimpleDialogOption( + padding: const EdgeInsets.symmetric(horizontal: 8), + onPressed: () => _onItemPressed(a), + child: ListTile( + dense: true, + title: Text(label ?? a.url), + subtitle: label == null ? Text(a.username.toString()) : null, + trailing: IconButton( + icon: Icon( + Icons.close, + color: AppTheme.getUnfocusedIconColor(context), + ), + tooltip: L10n.global().deleteTooltip, + onPressed: () => _onRemoveItemPressed(a), + ), + ), + ); + }).toList(); final addAccountOptions = [ SimpleDialogOption( padding: const EdgeInsets.all(8), @@ -72,18 +73,21 @@ class _AccountPickerDialogState extends State { ), ), ]; + final accountLabel = AccountPref.of(widget.account).getAccountLabel(); return AppTheme( child: SimpleDialog( title: ListTile( dense: true, title: Text( - widget.account.url, - style: const TextStyle(fontWeight: FontWeight.bold), - ), - subtitle: Text( - widget.account.username.toString(), + accountLabel ?? widget.account.url, style: const TextStyle(fontWeight: FontWeight.bold), ), + subtitle: accountLabel == null + ? Text( + widget.account.username.toString(), + style: const TextStyle(fontWeight: FontWeight.bold), + ) + : null, trailing: IconButton( icon: Icon( Icons.settings_outlined, diff --git a/app/lib/widget/home_app_bar.dart b/app/lib/widget/home_app_bar.dart index eb5f41b1..824b7aa2 100644 --- a/app/lib/widget/home_app_bar.dart +++ b/app/lib/widget/home_app_bar.dart @@ -23,6 +23,7 @@ class HomeSliverAppBar extends StatelessWidget { @override build(BuildContext context) { + final accountLabel = AccountPref.of(account).getAccountLabel(); return SliverAppBar( title: InkWell( onTap: () { @@ -57,16 +58,17 @@ class HomeSliverAppBar extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - account.url.substring(account.scheme.length + 3), + accountLabel ?? account.address, style: const TextStyle(fontSize: 16), ), - Text( - account.username.toString(), - style: TextStyle( - fontSize: 14, - color: AppTheme.getSecondaryTextColor(context), + if (accountLabel == null) + Text( + account.username.toString(), + style: TextStyle( + fontSize: 14, + color: AppTheme.getSecondaryTextColor(context), + ), ), - ), ], ), ), diff --git a/app/lib/widget/settings.dart b/app/lib/widget/settings.dart index c62abc67..b93abf17 100644 --- a/app/lib/widget/settings.dart +++ b/app/lib/widget/settings.dart @@ -26,6 +26,7 @@ import 'package:nc_photos/widget/home.dart'; import 'package:nc_photos/widget/list_tile_center_leading.dart'; import 'package:nc_photos/widget/root_picker.dart'; import 'package:nc_photos/widget/share_folder_picker.dart'; +import 'package:nc_photos/widget/simple_input_dialog.dart'; import 'package:nc_photos/widget/stateful_slider.dart'; import 'package:screen_brightness/screen_brightness.dart'; import 'package:tuple/tuple.dart'; @@ -495,6 +496,7 @@ class _AccountSettingsState extends State { final settings = AccountPref.of(_account); _isEnableFaceRecognitionApp = settings.isEnableFaceRecognitionAppOr(); _shareFolder = settings.getShareFolderOr(); + _label = settings.getAccountLabel(); } @override @@ -527,6 +529,12 @@ class _AccountSettingsState extends State { SliverList( delegate: SliverChildListDelegate( [ + ListTile( + title: Text(L10n.global().settingsAccountLabelTitle), + subtitle: Text( + _label ?? L10n.global().settingsAccountLabelDescription), + onTap: () => _onLabelPressed(context), + ), ListTile( title: Text(L10n.global().settingsIncludedFoldersTitle), subtitle: Text(_account.roots.map((e) => "/$e").join("; ")), @@ -560,6 +568,24 @@ class _AccountSettingsState extends State { ); } + Future _onLabelPressed(BuildContext context) async { + final result = await showDialog( + context: context, + builder: (context) => SimpleInputDialog( + titleText: L10n.global().settingsAccountLabelTitle, + buttonText: MaterialLocalizations.of(context).okButtonLabel, + initialText: _label ?? "", + )); + if (result == null) { + return; + } + if (result.isEmpty) { + _setLabel(null); + } else { + _setLabel(result); + } + } + Future _onIncludedFoldersPressed() async { try { final result = await Navigator.of(context).pushNamed( @@ -650,6 +676,24 @@ class _AccountSettingsState extends State { } } + Future _setLabel(String? value) async { + _log.info("[_setLabel] New value: $value"); + final oldValue = _label; + setState(() { + _label = value; + }); + if (!await AccountPref.of(_account).setAccountLabel(value)) { + _log.severe("[_setLabel] Failed writing pref"); + SnackBarManager().showSnackBar(SnackBar( + content: Text(L10n.global().writePreferenceFailureNotification), + duration: k.snackBarDurationNormal, + )); + setState(() { + _label = oldValue; + }); + } + } + Future _setShareFolder(String value) async { _log.info("[_setShareFolder] New value: $value"); final oldValue = _shareFolder; @@ -672,6 +716,7 @@ class _AccountSettingsState extends State { late Account _account; late bool _isEnableFaceRecognitionApp; late String _shareFolder; + late String? _label; static final _log = Logger("widget.settings._AccountSettingsState"); }