mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
committed by
GitHub
parent
7c3e3cb1f8
commit
d6587cf3b6
@@ -13,6 +13,7 @@ import 'package:PiliPlus/models/github/latest.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import '../../utils/cache_manage.dart';
|
||||
import '../mine/controller.dart';
|
||||
|
||||
class AboutPage extends StatefulWidget {
|
||||
const AboutPage({super.key, this.showAppBar});
|
||||
@@ -302,6 +303,9 @@ Commit Hash: ${BuildConfig.commitHash}''',
|
||||
.putAll(res)
|
||||
.then((_) => Accounts.refresh())
|
||||
.then((_) {
|
||||
MineController.anonymity.value =
|
||||
!Accounts.get(AccountType.heartbeat)
|
||||
.isLogin;
|
||||
if (Accounts.main.isLogin) {
|
||||
return LoginUtils.onLoginMain();
|
||||
}
|
||||
|
||||
@@ -646,7 +646,9 @@ class LoginPageController extends GetxController
|
||||
LoginAccount(BiliCookieJar.fromList(cookieInfo),
|
||||
tokenInfo['access_token'], tokenInfo['refresh_token'])
|
||||
.onChange(),
|
||||
AnonymousAccount().logout().then((i) => Request.buvidActive(i))
|
||||
AnonymousAccount()
|
||||
.delete()
|
||||
.then((_) => Request.buvidActive(AnonymousAccount()))
|
||||
]);
|
||||
if (Accounts.main.isLogin) {
|
||||
SmartDialog.showToast('登录成功');
|
||||
|
||||
@@ -35,7 +35,7 @@ class MainController extends GetxController {
|
||||
|
||||
late int homeIndex = -1;
|
||||
late DynamicBadgeMode msgBadgeMode = GStorage.msgBadgeMode;
|
||||
late List<MsgUnReadType> msgUnReadTypes = GStorage.msgUnReadTypeV2;
|
||||
late Set<MsgUnReadType> msgUnReadTypes = GStorage.msgUnReadTypeV2.toSet();
|
||||
late final RxString msgUnReadCount = ''.obs;
|
||||
late int lastCheckUnreadAt = 0;
|
||||
|
||||
@@ -83,7 +83,7 @@ class MainController extends GetxController {
|
||||
try {
|
||||
bool shouldCheckPM = msgUnReadTypes.contains(MsgUnReadType.pm);
|
||||
bool shouldCheckFeed =
|
||||
([...msgUnReadTypes]..remove(MsgUnReadType.pm)).isNotEmpty;
|
||||
shouldCheckPM ? msgUnReadTypes.length > 1 : msgUnReadTypes.isNotEmpty;
|
||||
List res = await Future.wait([
|
||||
if (shouldCheckPM) _queryPMUnread(),
|
||||
if (shouldCheckFeed) _queryMsgFeedUnread(),
|
||||
|
||||
@@ -7,13 +7,15 @@ import 'package:PiliPlus/pages/setting/privacy_setting.dart';
|
||||
import 'package:PiliPlus/pages/setting/recommend_setting.dart';
|
||||
import 'package:PiliPlus/pages/setting/style_setting.dart';
|
||||
import 'package:PiliPlus/pages/setting/video_setting.dart';
|
||||
import 'package:PiliPlus/utils/accounts/account.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/login.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import 'widgets/multi_select_dialog.dart';
|
||||
|
||||
class _SettingsModel {
|
||||
final String name;
|
||||
final String title;
|
||||
@@ -37,7 +39,7 @@ class SettingPage extends StatefulWidget {
|
||||
|
||||
class _SettingPageState extends State<SettingPage> {
|
||||
late String _type = 'privacySetting';
|
||||
final RxBool _isLogin = Accounts.main.isLogin.obs;
|
||||
final RxBool _noAccount = Accounts.accountMode.isEmpty.obs;
|
||||
TextStyle get _titleStyle => Theme.of(context).textTheme.titleMedium!;
|
||||
TextStyle get _subTitleStyle => Theme.of(context)
|
||||
.textTheme
|
||||
@@ -173,8 +175,15 @@ class _SettingPageState extends State<SettingPage> {
|
||||
leading: const Icon(Icons.switch_account_outlined),
|
||||
title: const Text('设置账号模式'),
|
||||
),
|
||||
// TODO: 多账号登出
|
||||
_buildLoginItem,
|
||||
Obx(
|
||||
() => _noAccount.value
|
||||
? const SizedBox.shrink()
|
||||
: ListTile(
|
||||
leading: const Icon(Icons.logout_outlined),
|
||||
onTap: () => _logoutDialog(context),
|
||||
title: Text('退出登录', style: _titleStyle),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
tileColor: _getTileColor(_items.last.name),
|
||||
onTap: () => _toPage(_items.last.name),
|
||||
@@ -186,18 +195,30 @@ class _SettingPageState extends State<SettingPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget get _buildLoginItem => Obx(
|
||||
() => _isLogin.value.not
|
||||
? const SizedBox.shrink()
|
||||
: ListTile(
|
||||
leading: const Icon(Icons.logout_outlined),
|
||||
onTap: () {
|
||||
showDialog(
|
||||
Future<void> _logoutDialog(BuildContext context) async {
|
||||
final result = await showDialog<Set<LoginAccount>>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return MultiSelectDialog<LoginAccount>(
|
||||
title: '选择要登出的账号uid',
|
||||
initValues: Iterable.empty(),
|
||||
values: {for (var i in Accounts.account.values) i: i.mid.toString()},
|
||||
);
|
||||
},
|
||||
);
|
||||
if (!context.mounted || result.isNullOrEmpty) return;
|
||||
Future<void> logout() {
|
||||
_noAccount.value = result!.length == Accounts.account.length;
|
||||
return Accounts.deleteAll(result);
|
||||
}
|
||||
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: const Text('提示'),
|
||||
content: const Text('确认要退出登录吗'),
|
||||
content: Text(
|
||||
"确认要退出以下账号登录吗\n\n${result!.map((i) => i.mid.toString()).join('\n')}"),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: Get.back,
|
||||
@@ -211,18 +232,11 @@ class _SettingPageState extends State<SettingPage> {
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
_isLogin.value = false;
|
||||
LoginUtils.onLogoutMain();
|
||||
final account = Accounts.main;
|
||||
Accounts.accountMode
|
||||
.removeWhere((_, a) => a == account);
|
||||
account.logout().then((_) => Accounts.refresh());
|
||||
logout();
|
||||
},
|
||||
child: Text(
|
||||
'仅登出',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.error),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
@@ -230,10 +244,8 @@ class _SettingPageState extends State<SettingPage> {
|
||||
SmartDialog.showLoading();
|
||||
final res = await LoginHttp.logout(Accounts.main);
|
||||
if (res['status']) {
|
||||
await Accounts.main.logout();
|
||||
await LoginUtils.onLogoutMain();
|
||||
_isLogin.value = false;
|
||||
SmartDialog.dismiss();
|
||||
logout();
|
||||
Get.back();
|
||||
} else {
|
||||
SmartDialog.dismiss();
|
||||
@@ -244,12 +256,8 @@ class _SettingPageState extends State<SettingPage> {
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
title: Text('退出登录', style: _titleStyle),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget get _buildSearchItem => Padding(
|
||||
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 5),
|
||||
|
||||
@@ -344,15 +344,13 @@ List<SettingsModel> get styleSettings => [
|
||||
SettingsModel(
|
||||
settingsType: SettingsType.normal,
|
||||
onTap: (setState) async {
|
||||
List<MsgUnReadType>? result = await showDialog(
|
||||
final result = await showDialog<Set<MsgUnReadType>>(
|
||||
context: Get.context!,
|
||||
builder: (context) {
|
||||
return MultiSelectDialog<MsgUnReadType>(
|
||||
title: '消息未读类型',
|
||||
initValues: GStorage.msgUnReadTypeV2,
|
||||
values: MsgUnReadType.values.map((e) {
|
||||
return {'title': e.title, 'value': e};
|
||||
}).toList(),
|
||||
values: {for (var i in MsgUnReadType.values) i: i.title},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -2,16 +2,15 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MultiSelectDialog<T> extends StatefulWidget {
|
||||
final List<T> initValues;
|
||||
final Iterable<T> initValues;
|
||||
final String title;
|
||||
final List<dynamic> values;
|
||||
final Map<T, String> values;
|
||||
|
||||
const MultiSelectDialog({
|
||||
super.key,
|
||||
const MultiSelectDialog(
|
||||
{super.key,
|
||||
required this.initValues,
|
||||
required this.values,
|
||||
required this.title,
|
||||
});
|
||||
required this.title});
|
||||
|
||||
@override
|
||||
State<MultiSelectDialog<T>> createState() => _MultiSelectDialogState<T>();
|
||||
@@ -36,30 +35,24 @@ class _MultiSelectDialogState<T> extends State<MultiSelectDialog<T>> {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: List.generate(
|
||||
widget.values.length,
|
||||
(index) {
|
||||
bool isChecked =
|
||||
_tempValues.contains(widget.values[index]['value']);
|
||||
children: widget.values.entries.map((i) {
|
||||
bool isChecked = _tempValues.contains(i.key);
|
||||
return CheckboxListTile(
|
||||
dense: true,
|
||||
value: isChecked,
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
title: Text(
|
||||
widget.values[index]['title'],
|
||||
i.value,
|
||||
style: Theme.of(context).textTheme.titleMedium!,
|
||||
),
|
||||
onChanged: (value) {
|
||||
if (isChecked) {
|
||||
_tempValues.remove(widget.values[index]['value']);
|
||||
} else {
|
||||
_tempValues.add(widget.values[index]['value']);
|
||||
}
|
||||
isChecked
|
||||
? _tempValues.remove(i.key)
|
||||
: _tempValues.add(i.key);
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
}),
|
||||
@@ -75,7 +68,7 @@ class _MultiSelectDialogState<T> extends State<MultiSelectDialog<T>> {
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Get.back(result: _tempValues.toList()),
|
||||
onPressed: () => Get.back(result: _tempValues),
|
||||
child: const Text('确定'),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -16,7 +16,7 @@ abstract class Account {
|
||||
|
||||
bool activited = false;
|
||||
|
||||
Future<AnonymousAccount> logout();
|
||||
Future<void> delete();
|
||||
Future<void> onChange();
|
||||
|
||||
Map<String, dynamic>? toJson();
|
||||
@@ -55,10 +55,7 @@ class LoginAccount implements Account {
|
||||
bool activited = false;
|
||||
|
||||
@override
|
||||
Future<AnonymousAccount> logout() async {
|
||||
await Future.wait([cookieJar.deleteAll(), _box.delete(_midStr)]);
|
||||
return AnonymousAccount();
|
||||
}
|
||||
Future<void> delete() => _box.delete(_midStr);
|
||||
|
||||
@override
|
||||
Future<void> onChange() => _box.put(_midStr, this);
|
||||
@@ -121,10 +118,9 @@ class AnonymousAccount implements Account {
|
||||
bool activited = false;
|
||||
|
||||
@override
|
||||
Future<AnonymousAccount> logout() async {
|
||||
Future<void> delete() async {
|
||||
await cookieJar.deleteAll();
|
||||
activited = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -19,23 +19,22 @@ final _setCookieReg = RegExp('(?<=)(,)(?=[^;]+?=)');
|
||||
class AccountManager extends Interceptor {
|
||||
static final Map<AccountType, Set<String>> apiTypeSet = {
|
||||
AccountType.heartbeat: {
|
||||
Api.videoUrl,
|
||||
Api.videoIntro,
|
||||
Api.relatedList,
|
||||
Api.replyList,
|
||||
Api.replyReplyList,
|
||||
Api.searchSuggest,
|
||||
Api.searchByType,
|
||||
Api.heartBeat,
|
||||
Api.ab2c,
|
||||
Api.bangumiInfo,
|
||||
Api.liveRoomInfo,
|
||||
Api.liveRoomInfoH5,
|
||||
Api.onlineTotal,
|
||||
Api.dynamicDetail,
|
||||
Api.aiConclusion,
|
||||
Api.getSeasonDetailApi,
|
||||
Api.liveRoomDmToken,
|
||||
Api.liveRoomDmPrefetch,
|
||||
Api.searchByType,
|
||||
Api.memberDynamicSearch
|
||||
},
|
||||
AccountType.recommend: {
|
||||
Api.recommendListWeb,
|
||||
@@ -43,10 +42,11 @@ class AccountManager extends Interceptor {
|
||||
Api.feedDislike,
|
||||
Api.feedDislikeCancel,
|
||||
Api.hotList,
|
||||
Api.relatedList,
|
||||
Api.hotSearchList, // 不同账号搜索结果可能不一样
|
||||
Api.searchDefault,
|
||||
Api.searchSuggest,
|
||||
Api.searchByType
|
||||
Api.liveList,
|
||||
},
|
||||
AccountType.video: {Api.videoUrl, Api.bangumiVideoUrl}
|
||||
};
|
||||
|
||||
@@ -21,9 +21,11 @@ extension ScrollControllerExt on ScrollController {
|
||||
}
|
||||
}
|
||||
|
||||
extension ListExt<T> on List<T>? {
|
||||
extension IterableExt<T> on Iterable<T>? {
|
||||
bool get isNullOrEmpty => this == null || this!.isEmpty;
|
||||
}
|
||||
|
||||
extension ListExt<T> on List<T>? {
|
||||
T? getOrNull(int index) {
|
||||
if (isNullOrEmpty) {
|
||||
return null;
|
||||
|
||||
@@ -105,7 +105,7 @@ class LoginUtils {
|
||||
} catch (_) {}
|
||||
} else {
|
||||
// 获取用户信息失败
|
||||
await Accounts.set(AccountType.main, await account.logout());
|
||||
await Accounts.deleteAll({account});
|
||||
SmartDialog.showNotify(
|
||||
msg: '登录失败,请检查cookie是否正确,${result['message']}',
|
||||
notifyType: NotifyType.warning);
|
||||
|
||||
@@ -862,16 +862,25 @@ class Accounts {
|
||||
for (var i in AccountType.values) {
|
||||
accountMode[i] = AnonymousAccount();
|
||||
}
|
||||
if (!AnonymousAccount().activited) {
|
||||
await AnonymousAccount().delete();
|
||||
Request.buvidActive(AnonymousAccount());
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> close() async {
|
||||
account.compact();
|
||||
account.close();
|
||||
}
|
||||
|
||||
static Future<void> deleteAll(Set<Account> accounts) async {
|
||||
var isloginMain = Accounts.main.isLogin;
|
||||
Accounts.accountMode
|
||||
.updateAll((_, a) => accounts.contains(a) ? AnonymousAccount() : a);
|
||||
await Future.wait(accounts.map((i) => i.delete()));
|
||||
if (isloginMain && !Accounts.main.isLogin) {
|
||||
await LoginUtils.onLogoutMain();
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> set(AccountType key, Account account) async {
|
||||
await (accountMode[key]?..type.remove(key))?.onChange();
|
||||
accountMode[key] = account..type.add(key);
|
||||
@@ -879,11 +888,9 @@ class Accounts {
|
||||
if (!account.activited) await Request.buvidActive(account);
|
||||
switch (key) {
|
||||
case AccountType.main:
|
||||
if (account.isLogin) {
|
||||
await LoginUtils.onLoginMain();
|
||||
} else {
|
||||
await LoginUtils.onLogoutMain();
|
||||
}
|
||||
await (account.isLogin
|
||||
? LoginUtils.onLoginMain()
|
||||
: LoginUtils.onLogoutMain());
|
||||
break;
|
||||
case AccountType.heartbeat:
|
||||
MineController.anonymity.value = !account.isLogin;
|
||||
|
||||
Reference in New Issue
Block a user