mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-02-25 02:48:54 +01:00
Move account settings to Settings
This commit is contained in:
parent
5fe70c3964
commit
b2f6e1a73a
5 changed files with 231 additions and 54 deletions
|
@ -313,6 +313,15 @@
|
|||
"@settingsExifSupportTrueSubtitle": {
|
||||
"description": "Subtitle of the EXIF support setting when the value is true. The goal is to warn user about the possible side effects of enabling this setting"
|
||||
},
|
||||
"settingsAccountTitle": "Account",
|
||||
"settingsAccountPageTitle": "Account settings",
|
||||
"@settingsAccountPageTitle": {
|
||||
"description": "Dedicated page for account settings"
|
||||
},
|
||||
"settingsIncludedFoldersTitle": "Included folders",
|
||||
"@settingsIncludedFoldersTitle": {
|
||||
"description": "Change the included folders of an account"
|
||||
},
|
||||
"settingsViewerTitle": "Viewer",
|
||||
"settingsViewerDescription": "Customize the image/video viewer",
|
||||
"settingsViewerPageTitle": "Viewer settings",
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
{
|
||||
"cs": [
|
||||
"settingsAccountTitle",
|
||||
"settingsAccountPageTitle",
|
||||
"settingsIncludedFoldersTitle",
|
||||
"settingsAlbumTitle",
|
||||
"settingsAlbumDescription",
|
||||
"settingsAlbumPageTitle",
|
||||
|
@ -39,6 +42,9 @@
|
|||
],
|
||||
|
||||
"de": [
|
||||
"settingsAccountTitle",
|
||||
"settingsAccountPageTitle",
|
||||
"settingsIncludedFoldersTitle",
|
||||
"settingsAlbumTitle",
|
||||
"settingsAlbumDescription",
|
||||
"settingsAlbumPageTitle",
|
||||
|
@ -93,6 +99,9 @@
|
|||
|
||||
"el": [
|
||||
"collectionsTooltip",
|
||||
"settingsAccountTitle",
|
||||
"settingsAccountPageTitle",
|
||||
"settingsIncludedFoldersTitle",
|
||||
"settingsViewerTitle",
|
||||
"settingsViewerDescription",
|
||||
"settingsViewerPageTitle",
|
||||
|
@ -200,6 +209,9 @@
|
|||
],
|
||||
|
||||
"es": [
|
||||
"settingsAccountTitle",
|
||||
"settingsAccountPageTitle",
|
||||
"settingsIncludedFoldersTitle",
|
||||
"settingsAlbumTitle",
|
||||
"settingsAlbumDescription",
|
||||
"settingsAlbumPageTitle",
|
||||
|
@ -240,6 +252,9 @@
|
|||
|
||||
"fr": [
|
||||
"collectionsTooltip",
|
||||
"settingsAccountTitle",
|
||||
"settingsAccountPageTitle",
|
||||
"settingsIncludedFoldersTitle",
|
||||
"settingsViewerTitle",
|
||||
"settingsViewerDescription",
|
||||
"settingsViewerPageTitle",
|
||||
|
@ -327,6 +342,9 @@
|
|||
],
|
||||
|
||||
"ru": [
|
||||
"settingsAccountTitle",
|
||||
"settingsAccountPageTitle",
|
||||
"settingsIncludedFoldersTitle",
|
||||
"settingsAlbumTitle",
|
||||
"settingsAlbumDescription",
|
||||
"settingsAlbumPageTitle",
|
||||
|
|
|
@ -9,7 +9,7 @@ import 'package:nc_photos/pref.dart';
|
|||
import 'package:nc_photos/snack_bar_manager.dart';
|
||||
import 'package:nc_photos/theme.dart';
|
||||
import 'package:nc_photos/widget/home.dart';
|
||||
import 'package:nc_photos/widget/root_picker.dart';
|
||||
import 'package:nc_photos/widget/settings.dart';
|
||||
import 'package:nc_photos/widget/sign_in.dart';
|
||||
|
||||
/// A dialog that allows the user to switch between accounts
|
||||
|
@ -87,11 +87,11 @@ class _AccountPickerDialogState extends State<AccountPickerDialog> {
|
|||
),
|
||||
trailing: IconButton(
|
||||
icon: Icon(
|
||||
Icons.edit,
|
||||
Icons.settings_outlined,
|
||||
color: AppTheme.getSecondaryTextColor(context),
|
||||
),
|
||||
tooltip: L10n.global().editTooltip,
|
||||
onPressed: () => _onEditPressed(),
|
||||
tooltip: L10n.global().settingsAccountPageTitle,
|
||||
onPressed: _onEditPressed,
|
||||
),
|
||||
),
|
||||
titlePadding: const EdgeInsetsDirectional.fromSTEB(8, 16, 8, 0),
|
||||
|
@ -126,44 +126,11 @@ class _AccountPickerDialogState extends State<AccountPickerDialog> {
|
|||
}
|
||||
}
|
||||
|
||||
void _onEditPressed() async {
|
||||
try {
|
||||
final result = await Navigator.of(context).pushNamed<Account>(
|
||||
RootPicker.routeName,
|
||||
arguments: RootPickerArguments(widget.account));
|
||||
if (result != null) {
|
||||
// we've got a good account
|
||||
if (result == widget.account) {
|
||||
// no changes, do nothing
|
||||
_log.fine("[_onEditPressed] No changes");
|
||||
Navigator.of(context).pop();
|
||||
return;
|
||||
}
|
||||
final accounts = Pref.inst().getAccounts()!;
|
||||
if (accounts.contains(result)) {
|
||||
// conflict with another account. This normally won't happen because
|
||||
// the app passwords are unique to each entry, but just in case
|
||||
Navigator.of(context).pop();
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global().editAccountConflictFailureNotification),
|
||||
duration: k.snackBarDurationNormal,
|
||||
));
|
||||
return;
|
||||
}
|
||||
accounts[Pref.inst().getCurrentAccountIndex()!] = result;
|
||||
Pref.inst().setAccounts(accounts);
|
||||
Navigator.pushNamedAndRemoveUntil(
|
||||
context, Home.routeName, (route) => false,
|
||||
arguments: HomeArguments(result));
|
||||
}
|
||||
} catch (e, stacktrace) {
|
||||
_log.shout("[_onEditPressed] Exception", e, stacktrace);
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(exception_util.toUserString(e)),
|
||||
duration: k.snackBarDurationNormal,
|
||||
));
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
void _onEditPressed() {
|
||||
Navigator.of(context)
|
||||
..pop()
|
||||
..pushNamed(AccountSettingsWidget.routeName,
|
||||
arguments: AccountSettingsWidgetArguments(widget.account));
|
||||
}
|
||||
|
||||
void _removeAccount(PrefAccount account) {
|
||||
|
|
|
@ -149,6 +149,7 @@ class _MyAppState extends State<MyApp> implements SnackBarHandler {
|
|||
route ??= _handleSharingBrowserRoute(settings);
|
||||
route ??= _handleSharedFileViewerRoute(settings);
|
||||
route ??= _handleAlbumShareOutlierBrowserRoute(settings);
|
||||
route ??= _handleAccountSettingsRoute(settings);
|
||||
return route;
|
||||
}
|
||||
|
||||
|
@ -410,6 +411,20 @@ class _MyAppState extends State<MyApp> implements SnackBarHandler {
|
|||
return null;
|
||||
}
|
||||
|
||||
Route<dynamic>? _handleAccountSettingsRoute(RouteSettings settings) {
|
||||
try {
|
||||
if (settings.name == AccountSettingsWidget.routeName &&
|
||||
settings.arguments != null) {
|
||||
final args = settings.arguments as AccountSettingsWidgetArguments;
|
||||
return AccountSettingsWidget.buildRoute(args);
|
||||
}
|
||||
} catch (e) {
|
||||
_log.severe(
|
||||
"[_handleAccountSettingsRoute] Failed while handling route", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
final _scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
|
||||
|
||||
late AppEventListener<ThemeChangedEvent> _themeChangedListener;
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:nc_photos/account.dart';
|
|||
import 'package:nc_photos/app_localizations.dart';
|
||||
import 'package:nc_photos/debug_util.dart';
|
||||
import 'package:nc_photos/event/event.dart';
|
||||
import 'package:nc_photos/exception_util.dart' as exception_util;
|
||||
import 'package:nc_photos/k.dart' as k;
|
||||
import 'package:nc_photos/language_util.dart' as language_util;
|
||||
import 'package:nc_photos/mobile/android/android_info.dart';
|
||||
|
@ -15,7 +16,9 @@ import 'package:nc_photos/pref.dart';
|
|||
import 'package:nc_photos/snack_bar_manager.dart';
|
||||
import 'package:nc_photos/theme.dart';
|
||||
import 'package:nc_photos/widget/fancy_option_picker.dart';
|
||||
import 'package:nc_photos/widget/home.dart';
|
||||
import 'package:nc_photos/widget/lab_settings.dart';
|
||||
import 'package:nc_photos/widget/root_picker.dart';
|
||||
import 'package:nc_photos/widget/stateful_slider.dart';
|
||||
import 'package:screen_brightness/screen_brightness.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
@ -92,6 +95,11 @@ class _SettingsState extends State<Settings> {
|
|||
value: _isEnableExif,
|
||||
onChanged: (value) => _onExifSupportChanged(context, value),
|
||||
),
|
||||
_buildSubSettings(
|
||||
context,
|
||||
label: L10n.global().settingsAccountTitle,
|
||||
builder: () => AccountSettingsWidget(account: widget.account),
|
||||
),
|
||||
if (platform_k.isMobile)
|
||||
_buildSubSettings(
|
||||
context,
|
||||
|
@ -182,18 +190,6 @@ class _SettingsState extends State<Settings> {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _buildCaption(BuildContext context, String label) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onLanguageTap(BuildContext context) {
|
||||
final selected =
|
||||
Pref.inst().getLanguageOr(language_util.supportedLanguages[0]!.langId);
|
||||
|
@ -352,6 +348,166 @@ class _SettingsState extends State<Settings> {
|
|||
"https://gitlab.com/nkming2/nc-photos/-/tree/master/lib/l10n";
|
||||
}
|
||||
|
||||
class AccountSettingsWidgetArguments {
|
||||
const AccountSettingsWidgetArguments(this.account);
|
||||
|
||||
final Account account;
|
||||
}
|
||||
|
||||
class AccountSettingsWidget extends StatefulWidget {
|
||||
static const routeName = "/account-settings";
|
||||
|
||||
static Route buildRoute(AccountSettingsWidgetArguments args) =>
|
||||
MaterialPageRoute(
|
||||
builder: (context) => AccountSettingsWidget.fromArgs(args),
|
||||
);
|
||||
|
||||
const AccountSettingsWidget({
|
||||
Key? key,
|
||||
required this.account,
|
||||
}) : super(key: key);
|
||||
|
||||
AccountSettingsWidget.fromArgs(AccountSettingsWidgetArguments args,
|
||||
{Key? key})
|
||||
: this(
|
||||
key: key,
|
||||
account: args.account,
|
||||
);
|
||||
|
||||
@override
|
||||
createState() => _AccountSettingsState();
|
||||
|
||||
final Account account;
|
||||
}
|
||||
|
||||
class _AccountSettingsState extends State<AccountSettingsWidget> {
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
_account = widget.account;
|
||||
}
|
||||
|
||||
@override
|
||||
build(BuildContext context) {
|
||||
return AppTheme(
|
||||
child: Scaffold(
|
||||
body: Builder(
|
||||
builder: (context) => _buildContent(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContent(BuildContext context) {
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
SliverAppBar(
|
||||
pinned: true,
|
||||
title: Text(L10n.global().settingsAccountPageTitle),
|
||||
leading: _hasModified
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.check),
|
||||
tooltip: L10n.global().doneButtonTooltip,
|
||||
onPressed: () => _onDonePressed(context),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
SliverList(
|
||||
delegate: SliverChildListDelegate(
|
||||
[
|
||||
ListTile(
|
||||
title: Text(L10n.global().settingsIncludedFoldersTitle),
|
||||
subtitle: Text(_account.roots.map((e) => "/$e").join("; ")),
|
||||
onTap: _onIncludedFoldersPressed,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _onDonePressed(BuildContext context) {
|
||||
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||
Home.routeName,
|
||||
(route) => false,
|
||||
arguments: HomeArguments(_account),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onIncludedFoldersPressed() async {
|
||||
try {
|
||||
final result = await Navigator.of(context).pushNamed<Account>(
|
||||
RootPicker.routeName,
|
||||
arguments: RootPickerArguments(_account));
|
||||
if (result == null) {
|
||||
// user canceled
|
||||
return;
|
||||
}
|
||||
// we've got a good account
|
||||
if (result == _account) {
|
||||
// no changes, do nothing
|
||||
_log.fine("[_onIncludedFoldersPressed] No changes");
|
||||
return;
|
||||
}
|
||||
final accounts = Pref.inst().getAccounts2()!;
|
||||
if (accounts.any((element) => element.account == result)) {
|
||||
// conflict with another account. This normally won't happen because
|
||||
// the app passwords are unique to each entry, but just in case
|
||||
Navigator.of(context).pop();
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global().editAccountConflictFailureNotification),
|
||||
duration: k.snackBarDurationNormal,
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
final index = _findAccount(_account, accounts);
|
||||
if (index < 0) {
|
||||
_log.shout("[_onIncludedFoldersPressed] Account not found: $_account");
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global().writePreferenceFailureNotification),
|
||||
duration: k.snackBarDurationNormal,
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
final newAccount = accounts[index].copyWith(
|
||||
account: result,
|
||||
);
|
||||
accounts[index] = newAccount;
|
||||
if (!await Pref.inst().setAccounts2(accounts)) {
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global().writePreferenceFailureNotification),
|
||||
duration: k.snackBarDurationNormal,
|
||||
));
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_account = result;
|
||||
_hasModified = true;
|
||||
});
|
||||
} catch (e, stackTrace) {
|
||||
_log.shout("[_onIncludedFoldersPressed] Exception", e, stackTrace);
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(exception_util.toUserString(e)),
|
||||
duration: k.snackBarDurationNormal,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the index of [account] in [Pref.getAccounts2]
|
||||
static int _findAccount(Account account, [List<PrefAccount>? accounts]) {
|
||||
final from = accounts ?? Pref.inst().getAccounts2Or([]);
|
||||
return from.indexWhere((element) => element.account == account);
|
||||
}
|
||||
|
||||
bool _hasModified = false;
|
||||
late Account _account;
|
||||
|
||||
static final _log = Logger("widget.settings._AccountSettingsState");
|
||||
}
|
||||
|
||||
class _ViewerSettings extends StatefulWidget {
|
||||
@override
|
||||
createState() => _ViewerSettingsState();
|
||||
|
@ -689,3 +845,15 @@ class _ThemeSettingsState extends State<_ThemeSettings> {
|
|||
|
||||
static final _log = Logger("widget.settings._ThemeSettingsState");
|
||||
}
|
||||
|
||||
Widget _buildCaption(BuildContext context, String label) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue