mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-02-25 02:48:54 +01:00
Support account specific settings
This commit is contained in:
parent
001ed6b514
commit
5fe70c3964
5 changed files with 146 additions and 31 deletions
|
@ -72,6 +72,29 @@ class Account with EquatableMixin {
|
|||
final List<String> _roots;
|
||||
}
|
||||
|
||||
class AccountSettings with EquatableMixin {
|
||||
const AccountSettings();
|
||||
|
||||
factory AccountSettings.fromJson(JsonObj json) {
|
||||
return AccountSettings();
|
||||
}
|
||||
|
||||
JsonObj toJson() => {};
|
||||
|
||||
@override
|
||||
toString() {
|
||||
return "$runtimeType {"
|
||||
"}";
|
||||
}
|
||||
|
||||
AccountSettings copyWith() {
|
||||
return AccountSettings();
|
||||
}
|
||||
|
||||
@override
|
||||
get props => [];
|
||||
}
|
||||
|
||||
extension AccountExtension on Account {
|
||||
String get url => "$scheme://$address";
|
||||
}
|
||||
|
|
|
@ -4,10 +4,15 @@ import 'package:event_bus/event_bus.dart';
|
|||
import 'package:kiwi/kiwi.dart';
|
||||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/event/event.dart';
|
||||
import 'package:nc_photos/type.dart';
|
||||
import 'package:nc_photos/use_case/compat/v32.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class Pref {
|
||||
static Future<void> init() async {
|
||||
if (await CompatV32.isPrefNeedMigration()) {
|
||||
await CompatV32.migratePref();
|
||||
}
|
||||
return SharedPreferences.getInstance().then((pref) {
|
||||
_inst._pref = pref;
|
||||
});
|
||||
|
@ -15,15 +20,16 @@ class Pref {
|
|||
|
||||
factory Pref.inst() => _inst;
|
||||
|
||||
List<Account>? getAccounts() {
|
||||
final jsonObjs = _pref.getStringList(_toKey(PrefKey.accounts));
|
||||
return jsonObjs?.map((e) => Account.fromJson(jsonDecode(e))).toList();
|
||||
List<PrefAccount>? getAccounts2() {
|
||||
final jsonObjs = _pref.getStringList(_toKey(PrefKey.accounts2));
|
||||
return jsonObjs?.map((e) => PrefAccount.fromJson(jsonDecode(e))).toList();
|
||||
}
|
||||
|
||||
List<Account> getAccountsOr(List<Account> def) => getAccounts() ?? def;
|
||||
Future<bool> setAccounts(List<Account> value) {
|
||||
List<PrefAccount> getAccounts2Or(List<PrefAccount> def) =>
|
||||
getAccounts2() ?? def;
|
||||
Future<bool> setAccounts2(List<PrefAccount> value) {
|
||||
final jsons = value.map((e) => jsonEncode(e.toJson())).toList();
|
||||
return _setStringList(PrefKey.accounts, jsons);
|
||||
return _setStringList(PrefKey.accounts2, jsons);
|
||||
}
|
||||
|
||||
int? getCurrentAccountIndex() =>
|
||||
|
@ -158,8 +164,8 @@ class Pref {
|
|||
|
||||
String _toKey(PrefKey key) {
|
||||
switch (key) {
|
||||
case PrefKey.accounts:
|
||||
return "accounts";
|
||||
case PrefKey.accounts2:
|
||||
return "accounts2";
|
||||
case PrefKey.currentAccountIndex:
|
||||
return "currentAccountIndex";
|
||||
case PrefKey.homePhotosZoomLevel:
|
||||
|
@ -205,8 +211,48 @@ class Pref {
|
|||
late SharedPreferences _pref;
|
||||
}
|
||||
|
||||
class PrefAccount {
|
||||
const PrefAccount(
|
||||
this.account, [
|
||||
this.settings = const AccountSettings(),
|
||||
]);
|
||||
|
||||
factory PrefAccount.fromJson(JsonObj json) {
|
||||
return PrefAccount(
|
||||
Account.fromJson(json["account"].cast<String, dynamic>()),
|
||||
AccountSettings.fromJson(json["settings"].cast<String, dynamic>()),
|
||||
);
|
||||
}
|
||||
|
||||
JsonObj toJson() => {
|
||||
"account": account.toJson(),
|
||||
"settings": settings.toJson(),
|
||||
};
|
||||
|
||||
PrefAccount copyWith({
|
||||
Account? account,
|
||||
AccountSettings? settings,
|
||||
}) {
|
||||
return PrefAccount(
|
||||
account ?? this.account,
|
||||
settings ?? this.settings,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
toString() {
|
||||
return "$runtimeType {"
|
||||
"account: $account, "
|
||||
"settings: $settings, "
|
||||
"}";
|
||||
}
|
||||
|
||||
final Account account;
|
||||
final AccountSettings settings;
|
||||
}
|
||||
|
||||
enum PrefKey {
|
||||
accounts,
|
||||
accounts2,
|
||||
currentAccountIndex,
|
||||
homePhotosZoomLevel,
|
||||
albumBrowserZoomLevel,
|
||||
|
@ -231,7 +277,9 @@ enum PrefKey {
|
|||
extension PrefExtension on Pref {
|
||||
Account? getCurrentAccount() {
|
||||
try {
|
||||
return Pref.inst().getAccounts()![Pref.inst().getCurrentAccountIndex()!];
|
||||
return Pref.inst()
|
||||
.getAccounts2()![Pref.inst().getCurrentAccountIndex()!]
|
||||
.account;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
|
|
38
lib/use_case/compat/v32.dart
Normal file
38
lib/use_case/compat/v32.dart
Normal file
|
@ -0,0 +1,38 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:nc_photos/type.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// Compatibility helper for v32
|
||||
class CompatV32 {
|
||||
static Future<bool> isPrefNeedMigration() async {
|
||||
final pref = await SharedPreferences.getInstance();
|
||||
return pref.containsKey("accounts");
|
||||
}
|
||||
|
||||
static Future<void> migratePref() async {
|
||||
final pref = await SharedPreferences.getInstance();
|
||||
final jsons = pref.getStringList("accounts");
|
||||
if (jsons == null) {
|
||||
return;
|
||||
}
|
||||
_log.info("[call] Migrate Pref.accounts");
|
||||
final newJsons = <JsonObj>[];
|
||||
for (final j in jsons) {
|
||||
newJsons.add(<String, dynamic>{
|
||||
"account": jsonDecode(j),
|
||||
"settings": <String, dynamic>{},
|
||||
});
|
||||
}
|
||||
if (await pref.setStringList(
|
||||
"accounts2", newJsons.map((e) => jsonEncode(e)).toList())) {
|
||||
_log.info("[call] Migrated ${newJsons.length} accounts");
|
||||
await pref.remove("accounts");
|
||||
} else {
|
||||
_log.severe("[call] Failed while writing pref");
|
||||
}
|
||||
}
|
||||
|
||||
static final _log = Logger("use_case.compat.v32.CompatV32");
|
||||
}
|
|
@ -29,20 +29,20 @@ class _AccountPickerDialogState extends State<AccountPickerDialog> {
|
|||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
_accounts = Pref.inst().getAccountsOr([]);
|
||||
_accounts = Pref.inst().getAccounts2Or([]);
|
||||
}
|
||||
|
||||
@override
|
||||
build(BuildContext context) {
|
||||
final otherAccountOptions = _accounts
|
||||
.where((a) => a != widget.account)
|
||||
.where((a) => a.account != 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),
|
||||
title: Text(a.account.url),
|
||||
subtitle: Text(a.account.username),
|
||||
trailing: IconButton(
|
||||
icon: Icon(
|
||||
Icons.close,
|
||||
|
@ -101,21 +101,21 @@ class _AccountPickerDialogState extends State<AccountPickerDialog> {
|
|||
);
|
||||
}
|
||||
|
||||
void _onItemPressed(Account account) {
|
||||
void _onItemPressed(PrefAccount account) {
|
||||
Pref.inst().setCurrentAccountIndex(_accounts.indexOf(account));
|
||||
Navigator.of(context).pushNamedAndRemoveUntil(Home.routeName, (_) => false,
|
||||
arguments: HomeArguments(account));
|
||||
arguments: HomeArguments(account.account));
|
||||
}
|
||||
|
||||
void _onRemoveItemPressed(Account account) {
|
||||
void _onRemoveItemPressed(PrefAccount account) {
|
||||
try {
|
||||
_removeAccount(account);
|
||||
setState(() {
|
||||
_accounts = Pref.inst().getAccounts()!;
|
||||
_accounts = Pref.inst().getAccounts2()!;
|
||||
});
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content:
|
||||
Text(L10n.global().removeServerSuccessNotification(account.url)),
|
||||
content: Text(
|
||||
L10n.global().removeServerSuccessNotification(account.account.url)),
|
||||
duration: k.snackBarDurationNormal,
|
||||
));
|
||||
} catch (e) {
|
||||
|
@ -166,22 +166,24 @@ class _AccountPickerDialogState extends State<AccountPickerDialog> {
|
|||
}
|
||||
}
|
||||
|
||||
void _removeAccount(Account account) {
|
||||
final currentAccounts = Pref.inst().getAccounts()!;
|
||||
void _removeAccount(PrefAccount account) {
|
||||
_log.info("[_removeAccount] Remove account: ${account.account}");
|
||||
final currentAccounts = Pref.inst().getAccounts2()!;
|
||||
final currentAccount =
|
||||
currentAccounts[Pref.inst().getCurrentAccountIndex()!];
|
||||
final newAccounts =
|
||||
currentAccounts.where((element) => element != account).toList();
|
||||
final newAccounts = currentAccounts
|
||||
.where((element) => element.account != account.account)
|
||||
.toList();
|
||||
final newAccountIndex = newAccounts.indexOf(currentAccount);
|
||||
if (newAccountIndex == -1) {
|
||||
throw StateError("Active account not found in resulting account list");
|
||||
}
|
||||
Pref.inst()
|
||||
..setAccounts(newAccounts)
|
||||
..setAccounts2(newAccounts)
|
||||
..setCurrentAccountIndex(newAccountIndex);
|
||||
}
|
||||
|
||||
late List<Account> _accounts;
|
||||
late List<PrefAccount> _accounts;
|
||||
|
||||
static final _log =
|
||||
Logger("widget.account_picker_dialog._AccountPickerDialogState");
|
||||
|
|
|
@ -226,15 +226,19 @@ class _SignInState extends State<SignIn> {
|
|||
}).then((result) {
|
||||
if (result != null) {
|
||||
// we've got a good account
|
||||
final pa = PrefAccount(result);
|
||||
// only signing in with app password would trigger distinct
|
||||
final accounts =
|
||||
(Pref.inst().getAccountsOr([])..add(result)).distinct();
|
||||
final accounts = (Pref.inst().getAccounts2Or([])..add(pa)).distinctIf(
|
||||
(a, b) => a.account == b.account,
|
||||
(a) => a.account.hashCode,
|
||||
);
|
||||
Pref.inst()
|
||||
..setAccounts(accounts)
|
||||
..setCurrentAccountIndex(accounts.indexOf(result));
|
||||
..setAccounts2(accounts)
|
||||
..setCurrentAccountIndex(
|
||||
accounts.indexWhere((element) => element.account == result));
|
||||
Navigator.pushNamedAndRemoveUntil(
|
||||
context, Home.routeName, (route) => false,
|
||||
arguments: HomeArguments(result));
|
||||
arguments: HomeArguments(pa.account));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue