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": {
|
"@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"
|
"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",
|
"settingsViewerTitle": "Viewer",
|
||||||
"settingsViewerDescription": "Customize the image/video viewer",
|
"settingsViewerDescription": "Customize the image/video viewer",
|
||||||
"settingsViewerPageTitle": "Viewer settings",
|
"settingsViewerPageTitle": "Viewer settings",
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
{
|
{
|
||||||
"cs": [
|
"cs": [
|
||||||
|
"settingsAccountTitle",
|
||||||
|
"settingsAccountPageTitle",
|
||||||
|
"settingsIncludedFoldersTitle",
|
||||||
"settingsAlbumTitle",
|
"settingsAlbumTitle",
|
||||||
"settingsAlbumDescription",
|
"settingsAlbumDescription",
|
||||||
"settingsAlbumPageTitle",
|
"settingsAlbumPageTitle",
|
||||||
|
@ -39,6 +42,9 @@
|
||||||
],
|
],
|
||||||
|
|
||||||
"de": [
|
"de": [
|
||||||
|
"settingsAccountTitle",
|
||||||
|
"settingsAccountPageTitle",
|
||||||
|
"settingsIncludedFoldersTitle",
|
||||||
"settingsAlbumTitle",
|
"settingsAlbumTitle",
|
||||||
"settingsAlbumDescription",
|
"settingsAlbumDescription",
|
||||||
"settingsAlbumPageTitle",
|
"settingsAlbumPageTitle",
|
||||||
|
@ -93,6 +99,9 @@
|
||||||
|
|
||||||
"el": [
|
"el": [
|
||||||
"collectionsTooltip",
|
"collectionsTooltip",
|
||||||
|
"settingsAccountTitle",
|
||||||
|
"settingsAccountPageTitle",
|
||||||
|
"settingsIncludedFoldersTitle",
|
||||||
"settingsViewerTitle",
|
"settingsViewerTitle",
|
||||||
"settingsViewerDescription",
|
"settingsViewerDescription",
|
||||||
"settingsViewerPageTitle",
|
"settingsViewerPageTitle",
|
||||||
|
@ -200,6 +209,9 @@
|
||||||
],
|
],
|
||||||
|
|
||||||
"es": [
|
"es": [
|
||||||
|
"settingsAccountTitle",
|
||||||
|
"settingsAccountPageTitle",
|
||||||
|
"settingsIncludedFoldersTitle",
|
||||||
"settingsAlbumTitle",
|
"settingsAlbumTitle",
|
||||||
"settingsAlbumDescription",
|
"settingsAlbumDescription",
|
||||||
"settingsAlbumPageTitle",
|
"settingsAlbumPageTitle",
|
||||||
|
@ -240,6 +252,9 @@
|
||||||
|
|
||||||
"fr": [
|
"fr": [
|
||||||
"collectionsTooltip",
|
"collectionsTooltip",
|
||||||
|
"settingsAccountTitle",
|
||||||
|
"settingsAccountPageTitle",
|
||||||
|
"settingsIncludedFoldersTitle",
|
||||||
"settingsViewerTitle",
|
"settingsViewerTitle",
|
||||||
"settingsViewerDescription",
|
"settingsViewerDescription",
|
||||||
"settingsViewerPageTitle",
|
"settingsViewerPageTitle",
|
||||||
|
@ -327,6 +342,9 @@
|
||||||
],
|
],
|
||||||
|
|
||||||
"ru": [
|
"ru": [
|
||||||
|
"settingsAccountTitle",
|
||||||
|
"settingsAccountPageTitle",
|
||||||
|
"settingsIncludedFoldersTitle",
|
||||||
"settingsAlbumTitle",
|
"settingsAlbumTitle",
|
||||||
"settingsAlbumDescription",
|
"settingsAlbumDescription",
|
||||||
"settingsAlbumPageTitle",
|
"settingsAlbumPageTitle",
|
||||||
|
|
|
@ -9,7 +9,7 @@ import 'package:nc_photos/pref.dart';
|
||||||
import 'package:nc_photos/snack_bar_manager.dart';
|
import 'package:nc_photos/snack_bar_manager.dart';
|
||||||
import 'package:nc_photos/theme.dart';
|
import 'package:nc_photos/theme.dart';
|
||||||
import 'package:nc_photos/widget/home.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';
|
import 'package:nc_photos/widget/sign_in.dart';
|
||||||
|
|
||||||
/// A dialog that allows the user to switch between accounts
|
/// A dialog that allows the user to switch between accounts
|
||||||
|
@ -87,11 +87,11 @@ class _AccountPickerDialogState extends State<AccountPickerDialog> {
|
||||||
),
|
),
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.edit,
|
Icons.settings_outlined,
|
||||||
color: AppTheme.getSecondaryTextColor(context),
|
color: AppTheme.getSecondaryTextColor(context),
|
||||||
),
|
),
|
||||||
tooltip: L10n.global().editTooltip,
|
tooltip: L10n.global().settingsAccountPageTitle,
|
||||||
onPressed: () => _onEditPressed(),
|
onPressed: _onEditPressed,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
titlePadding: const EdgeInsetsDirectional.fromSTEB(8, 16, 8, 0),
|
titlePadding: const EdgeInsetsDirectional.fromSTEB(8, 16, 8, 0),
|
||||||
|
@ -126,44 +126,11 @@ class _AccountPickerDialogState extends State<AccountPickerDialog> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onEditPressed() async {
|
void _onEditPressed() {
|
||||||
try {
|
Navigator.of(context)
|
||||||
final result = await Navigator.of(context).pushNamed<Account>(
|
..pop()
|
||||||
RootPicker.routeName,
|
..pushNamed(AccountSettingsWidget.routeName,
|
||||||
arguments: RootPickerArguments(widget.account));
|
arguments: AccountSettingsWidgetArguments(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 _removeAccount(PrefAccount account) {
|
void _removeAccount(PrefAccount account) {
|
||||||
|
|
|
@ -149,6 +149,7 @@ class _MyAppState extends State<MyApp> implements SnackBarHandler {
|
||||||
route ??= _handleSharingBrowserRoute(settings);
|
route ??= _handleSharingBrowserRoute(settings);
|
||||||
route ??= _handleSharedFileViewerRoute(settings);
|
route ??= _handleSharedFileViewerRoute(settings);
|
||||||
route ??= _handleAlbumShareOutlierBrowserRoute(settings);
|
route ??= _handleAlbumShareOutlierBrowserRoute(settings);
|
||||||
|
route ??= _handleAccountSettingsRoute(settings);
|
||||||
return route;
|
return route;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,6 +411,20 @@ class _MyAppState extends State<MyApp> implements SnackBarHandler {
|
||||||
return null;
|
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>();
|
final _scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
|
||||||
|
|
||||||
late AppEventListener<ThemeChangedEvent> _themeChangedListener;
|
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/app_localizations.dart';
|
||||||
import 'package:nc_photos/debug_util.dart';
|
import 'package:nc_photos/debug_util.dart';
|
||||||
import 'package:nc_photos/event/event.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/k.dart' as k;
|
||||||
import 'package:nc_photos/language_util.dart' as language_util;
|
import 'package:nc_photos/language_util.dart' as language_util;
|
||||||
import 'package:nc_photos/mobile/android/android_info.dart';
|
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/snack_bar_manager.dart';
|
||||||
import 'package:nc_photos/theme.dart';
|
import 'package:nc_photos/theme.dart';
|
||||||
import 'package:nc_photos/widget/fancy_option_picker.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/lab_settings.dart';
|
||||||
|
import 'package:nc_photos/widget/root_picker.dart';
|
||||||
import 'package:nc_photos/widget/stateful_slider.dart';
|
import 'package:nc_photos/widget/stateful_slider.dart';
|
||||||
import 'package:screen_brightness/screen_brightness.dart';
|
import 'package:screen_brightness/screen_brightness.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
@ -92,6 +95,11 @@ class _SettingsState extends State<Settings> {
|
||||||
value: _isEnableExif,
|
value: _isEnableExif,
|
||||||
onChanged: (value) => _onExifSupportChanged(context, value),
|
onChanged: (value) => _onExifSupportChanged(context, value),
|
||||||
),
|
),
|
||||||
|
_buildSubSettings(
|
||||||
|
context,
|
||||||
|
label: L10n.global().settingsAccountTitle,
|
||||||
|
builder: () => AccountSettingsWidget(account: widget.account),
|
||||||
|
),
|
||||||
if (platform_k.isMobile)
|
if (platform_k.isMobile)
|
||||||
_buildSubSettings(
|
_buildSubSettings(
|
||||||
context,
|
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) {
|
void _onLanguageTap(BuildContext context) {
|
||||||
final selected =
|
final selected =
|
||||||
Pref.inst().getLanguageOr(language_util.supportedLanguages[0]!.langId);
|
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";
|
"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 {
|
class _ViewerSettings extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
createState() => _ViewerSettingsState();
|
createState() => _ViewerSettingsState();
|
||||||
|
@ -689,3 +845,15 @@ class _ThemeSettingsState extends State<_ThemeSettings> {
|
||||||
|
|
||||||
static final _log = Logger("widget.settings._ThemeSettingsState");
|
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