diff --git a/lib/http/api.dart b/lib/http/api.dart index 0ea3e5f4..885680c3 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -278,4 +278,7 @@ class Api { // 我的订阅 static const String bangumiFollow = '/x/space/bangumi/follow/list?type=1&follow_status=0&pn=1&ps=15&ts=1691544359969'; + + // 黑名单 + static const String blackLst = '/x/relation/blacks'; } diff --git a/lib/http/black.dart b/lib/http/black.dart new file mode 100644 index 00000000..599b088b --- /dev/null +++ b/lib/http/black.dart @@ -0,0 +1,26 @@ +import 'package:pilipala/http/index.dart'; +import 'package:pilipala/models/user/black.dart'; + +class BlackHttp { + static Future blackList({required int pn, int? ps}) async { + var res = await Request().get(Api.blackLst, data: { + 'pn': pn, + 'ps': ps ?? 50, + 're_version': 0, + 'jsonp': 'jsonp', + 'csrf': await Request.getCsrf(), + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': BlackListDataModel.fromJson(res.data['data']) + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } +} diff --git a/lib/http/video.dart b/lib/http/video.dart index 77712852..1191841e 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -66,8 +66,13 @@ class VideoHttp { ); if (res.data['code'] == 0) { List list = []; + List blackMidsList = + setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]); for (var i in res.data['data']['items']) { - if (i['card_goto'] != 'ad_av') { + // 屏蔽推广和拉黑用户 + if (i['card_goto'] != 'ad_av' && + (i['args'] != null && + !blackMidsList.contains(i['args']['up_mid']))) { list.add(RecVideoItemAppModel.fromJson(i)); } } @@ -89,8 +94,12 @@ class VideoHttp { ); if (res.data['code'] == 0) { List list = []; + List blackMidsList = + setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]); for (var i in res.data['data']['list']) { - list.add(HotVideoItemModel.fromJson(i)); + if (!blackMidsList.contains(i['owner']['mid'])) { + list.add(HotVideoItemModel.fromJson(i)); + } } return {'status': true, 'data': list}; } else { diff --git a/lib/models/user/black.dart b/lib/models/user/black.dart new file mode 100644 index 00000000..9833967b --- /dev/null +++ b/lib/models/user/black.dart @@ -0,0 +1,37 @@ +class BlackListDataModel { + BlackListDataModel({ + this.list, + this.total, + }); + + List? list; + int? total; + + BlackListDataModel.fromJson(Map json) { + list = json['list'] + .map((e) => BlackListItem.fromJson(e)) + .toList(); + total = json['total']; + } +} + +class BlackListItem { + BlackListItem({ + this.face, + this.mid, + this.mtime, + this.uname, + }); + + String? face; + int? mid; + int? mtime; + String? uname; + + BlackListItem.fromJson(Map json) { + face = json['face']; + mid = json['mid']; + mtime = json['mtime']; + uname = json['uname']; + } +} diff --git a/lib/pages/blacklist/index.dart b/lib/pages/blacklist/index.dart new file mode 100644 index 00000000..27aa770f --- /dev/null +++ b/lib/pages/blacklist/index.dart @@ -0,0 +1,156 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:hive/hive.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/http/black.dart'; +import 'package:pilipala/models/user/black.dart'; +import 'package:pilipala/utils/storage.dart'; +import 'package:pilipala/utils/utils.dart'; + +class BlackListPage extends StatefulWidget { + const BlackListPage({super.key}); + + @override + State createState() => _BlackListPageState(); +} + +class _BlackListPageState extends State { + final BlackListController _blackListController = + Get.put(BlackListController()); + final ScrollController scrollController = ScrollController(); + Future? _futureBuilderFuture; + bool _isLoadingMore = false; + Box setting = GStrorage.setting; + + @override + void initState() { + super.initState(); + _futureBuilderFuture = _blackListController.queryBlacklist(); + scrollController.addListener( + () async { + if (scrollController.position.pixels >= + scrollController.position.maxScrollExtent - 200) { + if (!_isLoadingMore) { + _isLoadingMore = true; + await _blackListController.queryBlacklist(type: 'onLoad'); + _isLoadingMore = false; + } + } + }, + ); + } + + @override + void dispose() { + List blackMidsList = + _blackListController.blackList.map((e) => e.mid!).toList(); + setting.put(SettingBoxKey.blackMidsList, blackMidsList); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + elevation: 0, + scrolledUnderElevation: 0, + titleSpacing: 0, + centerTitle: false, + title: Obx( + () => Text( + '黑名单管理 (${_blackListController.blackList.length} / 5000)', + style: Theme.of(context).textTheme.titleMedium, + ), + ), + ), + body: RefreshIndicator( + onRefresh: () async => await _blackListController.queryBlacklist(), + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + var data = snapshot.data; + if (data['status']) { + List list = _blackListController.blackList; + return Obx( + () => list.length == 1 + ? const SizedBox() + : ListView.builder( + controller: scrollController, + itemCount: list.length, + itemBuilder: (BuildContext context, int index) { + return ListTile( + onTap: () {}, + leading: NetworkImgLayer( + width: 45, + height: 45, + type: 'avatar', + src: list[index].face, + ), + title: Text( + list[index].uname!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle(fontSize: 14), + ), + subtitle: Text( + Utils.dateFormat(list[index].mtime), + maxLines: 1, + style: TextStyle( + color: + Theme.of(context).colorScheme.outline), + overflow: TextOverflow.ellipsis, + ), + dense: true, + // trailing: TextButton( + // onPressed: () {}, + // child: const Text('移除'), + // ), + ); + }, + ), + ); + } else { + return CustomScrollView( + slivers: [ + HttpError( + errMsg: data['msg'], + fn: () => _blackListController.queryBlacklist(), + ) + ], + ); + } + } else { + // 骨架屏 + return const SizedBox(); + } + }, + ), + ), + ); + } +} + +class BlackListController extends GetxController { + int currentPage = 1; + int pageSize = 50; + RxList blackList = [BlackListItem()].obs; + + Future queryBlacklist({type = 'init'}) async { + if (type == 'init') { + currentPage = 1; + } + var result = await BlackHttp.blackList(pn: currentPage, ps: pageSize); + if (result['status']) { + if (type == 'init') { + blackList.value = result['data'].list; + } else { + blackList.addAll(result['data'].list); + } + + currentPage += 1; + } + return result; + } +} diff --git a/lib/pages/setting/privacy_setting.dart b/lib/pages/setting/privacy_setting.dart new file mode 100644 index 00000000..5594fc1e --- /dev/null +++ b/lib/pages/setting/privacy_setting.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:hive/hive.dart'; +import 'package:pilipala/utils/storage.dart'; + +class PrivacySetting extends StatefulWidget { + const PrivacySetting({super.key}); + + @override + State createState() => _PrivacySettingState(); +} + +class _PrivacySettingState extends State { + bool userLogin = false; + Box user = GStrorage.user; + + @override + void initState() { + super.initState(); + userLogin = user.get(UserBoxKey.userLogin) ?? false; + } + + @override + Widget build(BuildContext context) { + TextStyle titleStyle = Theme.of(context).textTheme.titleMedium!; + TextStyle subTitleStyle = Theme.of(context) + .textTheme + .labelMedium! + .copyWith(color: Theme.of(context).colorScheme.outline); + return Scaffold( + appBar: AppBar( + centerTitle: false, + titleSpacing: 0, + title: Text( + '隐私设置', + style: Theme.of(context).textTheme.titleMedium, + ), + ), + body: Column( + children: [ + ListTile( + onTap: () { + if (!userLogin) { + SmartDialog.showToast('登录后查看'); + return; + } + Get.toNamed('/blackListPage'); + }, + dense: false, + title: Text('黑名单管理', style: titleStyle), + subtitle: Text('已拉黑用户', style: subTitleStyle), + ), + ], + ), + ); + } +} diff --git a/lib/pages/setting/view.dart b/lib/pages/setting/view.dart index 74c4af1a..8b443529 100644 --- a/lib/pages/setting/view.dart +++ b/lib/pages/setting/view.dart @@ -19,6 +19,11 @@ class SettingPage extends StatelessWidget { ), body: Column( children: [ + ListTile( + onTap: () => Get.toNamed('/privacySetting'), + dense: false, + title: const Text('隐私设置'), + ), ListTile( onTap: () => Get.toNamed('/playSetting'), dense: false, diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 14be2aae..62e79b35 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -1,4 +1,5 @@ import 'package:get/get.dart'; +import 'package:pilipala/pages/blacklist/index.dart'; import 'package:pilipala/pages/dynamics/deatil/index.dart'; import 'package:pilipala/pages/dynamics/index.dart'; import 'package:pilipala/pages/fan/index.dart'; @@ -15,6 +16,7 @@ import 'package:pilipala/pages/preview/index.dart'; import 'package:pilipala/pages/search/index.dart'; import 'package:pilipala/pages/searchResult/index.dart'; import 'package:pilipala/pages/setting/play_setting.dart'; +import 'package:pilipala/pages/setting/privacy_setting.dart'; import 'package:pilipala/pages/setting/style_setting.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/replyReply/index.dart'; @@ -73,7 +75,12 @@ class Routes { // 播放设置 GetPage(name: '/playSetting', page: () => const PlaySetting()), - + // 外观设置 GetPage(name: '/styleSetting', page: () => const StyleSetting()), + // 隐私设置 + GetPage(name: '/privacySetting', page: () => const PrivacySetting()), + + // + GetPage(name: '/blackListPage', page: () => const BlackListPage()), ]; } diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 8c09c6bb..e3743908 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -117,6 +117,8 @@ class SettingBoxKey { static const String danmakuEnable = 'danmakuEnable'; static const String fullScreenMode = 'fullScreenMode'; + + static const String blackMidsList = 'blackMidsList'; } class LocalCacheKey {