Change root dirs of the current account

This commit is contained in:
Ming Ming 2021-07-03 17:11:04 +08:00
parent ea5706161d
commit c2b06cfeb6
4 changed files with 137 additions and 3 deletions

View file

@ -525,6 +525,15 @@
"description": "Edit the opened album"
},
"doneButtonTooltip": "Done",
"editTooltip": "Edit",
"editAccountConflictFailureNotification": "An account already exists with the same settings",
"@editAccountConflictFailureNotification": {
"description": "Error when user modified an account such that it's identical to another one"
},
"genericProcessingDialogContent": "Please wait",
"@genericProcessingDialogContent": {
"description": "Generic dialog shown when the app is temporarily blocking user input to work on something"
},
"changelogTitle": "Changelog",
"@changelogTitle": {

View file

@ -1,14 +1,18 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/account.dart';
import 'package:nc_photos/exception_util.dart' as exception_util;
import 'package:nc_photos/k.dart' as k;
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/sign_in.dart';
/// A dialog that allows the user to switch between accounts
class AccountPickerDialog extends StatefulWidget {
AccountPickerDialog({
Key key,
@ -76,6 +80,14 @@ class _AccountPickerDialogState extends State<AccountPickerDialog> {
widget.account.username,
style: const TextStyle(fontWeight: FontWeight.bold),
),
trailing: IconButton(
icon: Icon(
Icons.edit,
color: AppTheme.getSecondaryTextColor(context),
),
tooltip: AppLocalizations.of(context).editTooltip,
onPressed: () => _onEditPressed(),
),
),
children: otherAccountOptions + addAccountOptions,
);
@ -99,6 +111,46 @@ class _AccountPickerDialogState extends State<AccountPickerDialog> {
));
}
void _onEditPressed() async {
try {
final result = await Navigator.of(context).pushNamed(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(AppLocalizations.of(context)
.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, context)),
duration: k.snackBarDurationNormal,
));
Navigator.of(context).pop();
}
}
void _removeAccount(Account account) {
final currentAccounts = Pref.inst().getAccounts([]);
final currentAccount =
@ -115,4 +167,7 @@ class _AccountPickerDialogState extends State<AccountPickerDialog> {
}
List<Account> _accounts;
static final _log =
Logger("widget.account_picker_dialog._AccountPickerDialogState");
}

View file

@ -47,6 +47,11 @@ mixin DirPickerMixin<T extends StatefulWidget> on State<T> {
@protected
bool canPickDir(File file) => true;
@protected
void pickAll(List<File> dirs) {
_picks.addAll(dirs);
}
void _initBloc() {
_log.info("[_initBloc] Initialize bloc");
_bloc = LsDirBloc();

View file

@ -1,14 +1,20 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/account.dart';
import 'package:nc_photos/api/api_util.dart' as api_util;
import 'package:nc_photos/entity/file.dart';
import 'package:nc_photos/entity/file/data_source.dart';
import 'package:nc_photos/exception_util.dart' as exception_util;
import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/snack_bar_manager.dart';
import 'package:nc_photos/theme.dart';
import 'package:nc_photos/use_case/ls_single_file.dart';
import 'package:nc_photos/widget/dir_picker_mixin.dart';
import 'package:nc_photos/widget/processing_dialog.dart';
class RootPickerArguments {
RootPickerArguments(this.account);
@ -38,6 +44,38 @@ class RootPicker extends StatefulWidget {
class _RootPickerState extends State<RootPicker>
with DirPickerMixin<RootPicker> {
@override
initState() {
super.initState();
_initAccount();
}
void _initAccount() async {
try {
final fileSrc = FileWebdavDataSource();
final files = <File>[];
for (final r in widget.account.roots) {
if (r.isNotEmpty) {
_isIniting = true;
_ensureInitDialog();
files.add(await LsSingleFile(fileSrc).call(widget.account,
"${api_util.getWebdavRootUrlRelative(widget.account)}/$r"));
}
}
setState(() {
_isIniting = false;
pickAll(files);
});
} catch (e) {
SnackBarManager().showSnackBar(SnackBar(
content: Text(exception_util.toUserString(e, context)),
duration: k.snackBarDurationNormal,
));
} finally {
_dismissInitDialog();
}
}
@override
build(BuildContext context) {
return AppTheme(
@ -77,7 +115,10 @@ class _RootPickerState extends State<RootPicker>
),
),
Expanded(
child: buildDirPicker(context),
child: IgnorePointer(
ignoring: _isIniting,
child: buildDirPicker(context),
),
),
Padding(
padding: const EdgeInsets.all(16),
@ -126,8 +167,7 @@ class _RootPickerState extends State<RootPicker>
],
)).then((value) {
if (value == true) {
// default is to include all files, so we just return the same account
Navigator.of(context).pop(widget.account);
Navigator.of(context).pop(widget.account.copyWith(roots: [""]));
}
});
}
@ -147,5 +187,30 @@ class _RootPickerState extends State<RootPicker>
Navigator.of(context).pop(newAccount);
}
void _ensureInitDialog() {
if (_isInitDialogShown) {
return;
}
_isInitDialogShown = true;
SchedulerBinding.instance.addPostFrameCallback((_) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) => ProcessingDialog(
text: AppLocalizations.of(context).genericProcessingDialogContent),
);
});
}
void _dismissInitDialog() {
if (!_isInitDialogShown) {
return;
}
Navigator.of(context).pop();
}
bool _isIniting = false;
bool _isInitDialogShown = false;
static final _log = Logger("widget.root_picker._RootPickerState");
}