Move account settings to Settings

This commit is contained in:
Ming Ming 2021-10-20 04:11:35 +08:00
parent 5fe70c3964
commit b2f6e1a73a
5 changed files with 231 additions and 54 deletions

View file

@ -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",

View file

@ -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",

View file

@ -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) {

View file

@ -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;

View file

@ -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,
),
),
);
}