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;
|
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 {
|
extension AccountExtension on Account {
|
||||||
String get url => "$scheme://$address";
|
String get url => "$scheme://$address";
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,15 @@ import 'package:event_bus/event_bus.dart';
|
||||||
import 'package:kiwi/kiwi.dart';
|
import 'package:kiwi/kiwi.dart';
|
||||||
import 'package:nc_photos/account.dart';
|
import 'package:nc_photos/account.dart';
|
||||||
import 'package:nc_photos/event/event.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';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
class Pref {
|
class Pref {
|
||||||
static Future<void> init() async {
|
static Future<void> init() async {
|
||||||
|
if (await CompatV32.isPrefNeedMigration()) {
|
||||||
|
await CompatV32.migratePref();
|
||||||
|
}
|
||||||
return SharedPreferences.getInstance().then((pref) {
|
return SharedPreferences.getInstance().then((pref) {
|
||||||
_inst._pref = pref;
|
_inst._pref = pref;
|
||||||
});
|
});
|
||||||
|
@ -15,15 +20,16 @@ class Pref {
|
||||||
|
|
||||||
factory Pref.inst() => _inst;
|
factory Pref.inst() => _inst;
|
||||||
|
|
||||||
List<Account>? getAccounts() {
|
List<PrefAccount>? getAccounts2() {
|
||||||
final jsonObjs = _pref.getStringList(_toKey(PrefKey.accounts));
|
final jsonObjs = _pref.getStringList(_toKey(PrefKey.accounts2));
|
||||||
return jsonObjs?.map((e) => Account.fromJson(jsonDecode(e))).toList();
|
return jsonObjs?.map((e) => PrefAccount.fromJson(jsonDecode(e))).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Account> getAccountsOr(List<Account> def) => getAccounts() ?? def;
|
List<PrefAccount> getAccounts2Or(List<PrefAccount> def) =>
|
||||||
Future<bool> setAccounts(List<Account> value) {
|
getAccounts2() ?? def;
|
||||||
|
Future<bool> setAccounts2(List<PrefAccount> value) {
|
||||||
final jsons = value.map((e) => jsonEncode(e.toJson())).toList();
|
final jsons = value.map((e) => jsonEncode(e.toJson())).toList();
|
||||||
return _setStringList(PrefKey.accounts, jsons);
|
return _setStringList(PrefKey.accounts2, jsons);
|
||||||
}
|
}
|
||||||
|
|
||||||
int? getCurrentAccountIndex() =>
|
int? getCurrentAccountIndex() =>
|
||||||
|
@ -158,8 +164,8 @@ class Pref {
|
||||||
|
|
||||||
String _toKey(PrefKey key) {
|
String _toKey(PrefKey key) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case PrefKey.accounts:
|
case PrefKey.accounts2:
|
||||||
return "accounts";
|
return "accounts2";
|
||||||
case PrefKey.currentAccountIndex:
|
case PrefKey.currentAccountIndex:
|
||||||
return "currentAccountIndex";
|
return "currentAccountIndex";
|
||||||
case PrefKey.homePhotosZoomLevel:
|
case PrefKey.homePhotosZoomLevel:
|
||||||
|
@ -205,8 +211,48 @@ class Pref {
|
||||||
late SharedPreferences _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 {
|
enum PrefKey {
|
||||||
accounts,
|
accounts2,
|
||||||
currentAccountIndex,
|
currentAccountIndex,
|
||||||
homePhotosZoomLevel,
|
homePhotosZoomLevel,
|
||||||
albumBrowserZoomLevel,
|
albumBrowserZoomLevel,
|
||||||
|
@ -231,7 +277,9 @@ enum PrefKey {
|
||||||
extension PrefExtension on Pref {
|
extension PrefExtension on Pref {
|
||||||
Account? getCurrentAccount() {
|
Account? getCurrentAccount() {
|
||||||
try {
|
try {
|
||||||
return Pref.inst().getAccounts()![Pref.inst().getCurrentAccountIndex()!];
|
return Pref.inst()
|
||||||
|
.getAccounts2()![Pref.inst().getCurrentAccountIndex()!]
|
||||||
|
.account;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return null;
|
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
|
@override
|
||||||
initState() {
|
initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_accounts = Pref.inst().getAccountsOr([]);
|
_accounts = Pref.inst().getAccounts2Or([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
build(BuildContext context) {
|
build(BuildContext context) {
|
||||||
final otherAccountOptions = _accounts
|
final otherAccountOptions = _accounts
|
||||||
.where((a) => a != widget.account)
|
.where((a) => a.account != widget.account)
|
||||||
.map((a) => SimpleDialogOption(
|
.map((a) => SimpleDialogOption(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
onPressed: () => _onItemPressed(a),
|
onPressed: () => _onItemPressed(a),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
dense: true,
|
dense: true,
|
||||||
title: Text(a.url),
|
title: Text(a.account.url),
|
||||||
subtitle: Text(a.username),
|
subtitle: Text(a.account.username),
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.close,
|
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));
|
Pref.inst().setCurrentAccountIndex(_accounts.indexOf(account));
|
||||||
Navigator.of(context).pushNamedAndRemoveUntil(Home.routeName, (_) => false,
|
Navigator.of(context).pushNamedAndRemoveUntil(Home.routeName, (_) => false,
|
||||||
arguments: HomeArguments(account));
|
arguments: HomeArguments(account.account));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onRemoveItemPressed(Account account) {
|
void _onRemoveItemPressed(PrefAccount account) {
|
||||||
try {
|
try {
|
||||||
_removeAccount(account);
|
_removeAccount(account);
|
||||||
setState(() {
|
setState(() {
|
||||||
_accounts = Pref.inst().getAccounts()!;
|
_accounts = Pref.inst().getAccounts2()!;
|
||||||
});
|
});
|
||||||
SnackBarManager().showSnackBar(SnackBar(
|
SnackBarManager().showSnackBar(SnackBar(
|
||||||
content:
|
content: Text(
|
||||||
Text(L10n.global().removeServerSuccessNotification(account.url)),
|
L10n.global().removeServerSuccessNotification(account.account.url)),
|
||||||
duration: k.snackBarDurationNormal,
|
duration: k.snackBarDurationNormal,
|
||||||
));
|
));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -166,22 +166,24 @@ class _AccountPickerDialogState extends State<AccountPickerDialog> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _removeAccount(Account account) {
|
void _removeAccount(PrefAccount account) {
|
||||||
final currentAccounts = Pref.inst().getAccounts()!;
|
_log.info("[_removeAccount] Remove account: ${account.account}");
|
||||||
|
final currentAccounts = Pref.inst().getAccounts2()!;
|
||||||
final currentAccount =
|
final currentAccount =
|
||||||
currentAccounts[Pref.inst().getCurrentAccountIndex()!];
|
currentAccounts[Pref.inst().getCurrentAccountIndex()!];
|
||||||
final newAccounts =
|
final newAccounts = currentAccounts
|
||||||
currentAccounts.where((element) => element != account).toList();
|
.where((element) => element.account != account.account)
|
||||||
|
.toList();
|
||||||
final newAccountIndex = newAccounts.indexOf(currentAccount);
|
final newAccountIndex = newAccounts.indexOf(currentAccount);
|
||||||
if (newAccountIndex == -1) {
|
if (newAccountIndex == -1) {
|
||||||
throw StateError("Active account not found in resulting account list");
|
throw StateError("Active account not found in resulting account list");
|
||||||
}
|
}
|
||||||
Pref.inst()
|
Pref.inst()
|
||||||
..setAccounts(newAccounts)
|
..setAccounts2(newAccounts)
|
||||||
..setCurrentAccountIndex(newAccountIndex);
|
..setCurrentAccountIndex(newAccountIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
late List<Account> _accounts;
|
late List<PrefAccount> _accounts;
|
||||||
|
|
||||||
static final _log =
|
static final _log =
|
||||||
Logger("widget.account_picker_dialog._AccountPickerDialogState");
|
Logger("widget.account_picker_dialog._AccountPickerDialogState");
|
||||||
|
|
|
@ -226,15 +226,19 @@ class _SignInState extends State<SignIn> {
|
||||||
}).then((result) {
|
}).then((result) {
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
// we've got a good account
|
// we've got a good account
|
||||||
|
final pa = PrefAccount(result);
|
||||||
// only signing in with app password would trigger distinct
|
// only signing in with app password would trigger distinct
|
||||||
final accounts =
|
final accounts = (Pref.inst().getAccounts2Or([])..add(pa)).distinctIf(
|
||||||
(Pref.inst().getAccountsOr([])..add(result)).distinct();
|
(a, b) => a.account == b.account,
|
||||||
|
(a) => a.account.hashCode,
|
||||||
|
);
|
||||||
Pref.inst()
|
Pref.inst()
|
||||||
..setAccounts(accounts)
|
..setAccounts2(accounts)
|
||||||
..setCurrentAccountIndex(accounts.indexOf(result));
|
..setCurrentAccountIndex(
|
||||||
|
accounts.indexWhere((element) => element.account == result));
|
||||||
Navigator.pushNamedAndRemoveUntil(
|
Navigator.pushNamedAndRemoveUntil(
|
||||||
context, Home.routeName, (route) => false,
|
context, Home.routeName, (route) => false,
|
||||||
arguments: HomeArguments(result));
|
arguments: HomeArguments(pa.account));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue