diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 01d47a84..d436da8c 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -312,15 +312,14 @@ class MsgHttp { 'csrf_token': csrf, 'csrf': csrf }); - var res = await Request() - .post(HttpString.tUrl + Api.removeMsg, data: FormData.fromMap(data)); + var res = await Request().post( + HttpString.tUrl + Api.removeMsg, + data: FormData.fromMap(data), + ); if (res.data['code'] == 0) { return {'status': true}; } else { - return { - 'status': false, - 'msg': res.data['message'], - }; + return {'status': false, 'msg': res.data['message']}; } } @@ -381,10 +380,10 @@ class MsgHttp { } } - static Future setTop( - dynamic talkerId, - int opType, - ) async { + static Future setTop({ + required dynamic talkerId, + required int opType, + }) async { String csrf = await Request.getCsrf(); Map data = await WbiSign.makSign({ 'talker_id': talkerId, @@ -408,32 +407,28 @@ class MsgHttp { } // 会话列表 - static Future sessionList({int? endTs}) async { - Map params = { + static Future?>> sessionList( + {int? endTs}) async { + final params = await WbiSign.makSign({ 'session_type': 1, 'group_fold': 1, 'unfollow_fold': 0, 'sort_rule': 2, 'build': 0, 'mobi_app': 'web', - }; - if (endTs != null) { - params['end_ts'] = endTs; - } - - Map signParams = await WbiSign.makSign(params); - var res = await Request().get(Api.sessionList, queryParameters: signParams); + if (endTs != null) 'end_ts': endTs, + }); + var res = await Request().get(Api.sessionList, queryParameters: params); if (res.data['code'] == 0) { try { - return { - 'status': true, - 'data': SessionDataModel.fromJson(res.data['data']).sessionList, - }; + return LoadingState.success( + SessionDataModel.fromJson(res.data['data']).sessionList, + ); } catch (err) { - return {'status': false, 'msg': err.toString()}; + return LoadingState.error(err.toString()); } } else { - return {'status': false, 'msg': res.data['message']}; + return LoadingState.error(res.data['message']); } } diff --git a/lib/models/msg/msgfeed_unread.dart b/lib/models/msg/msgfeed_unread.dart index df3622ee..f044e065 100644 --- a/lib/models/msg/msgfeed_unread.dart +++ b/lib/models/msg/msgfeed_unread.dart @@ -4,7 +4,7 @@ class MsgFeedUnread { this.chat = 0, this.like = 0, this.reply = 0, - this.sys_msg = 0, + this.sysMsg = 0, this.up = 0, }); @@ -12,7 +12,7 @@ class MsgFeedUnread { int chat = 0; int like = 0; int reply = 0; - int sys_msg = 0; + int sysMsg = 0; int up = 0; MsgFeedUnread.fromJson(Map json) { @@ -20,7 +20,7 @@ class MsgFeedUnread { chat = json['chat'] ?? 0; like = json['like'] ?? 0; reply = json['reply'] ?? 0; - sys_msg = json['sys_msg'] ?? 0; + sysMsg = json['sys_msg'] ?? 0; up = json['up'] ?? 0; } } diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index bc920c0f..c80223b3 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -1,160 +1,159 @@ +import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/models/msg/account.dart'; +import 'package:PiliPlus/models/msg/session.dart'; +import 'package:PiliPlus/pages/common/common_list_controller.dart'; +import 'package:PiliPlus/utils/extension.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:PiliPlus/http/msg.dart'; -import 'package:PiliPlus/models/msg/account.dart'; -import 'package:PiliPlus/models/msg/session.dart'; import '../../models/msg/msgfeed_unread.dart'; import '../../utils/storage.dart'; -class WhisperController extends GetxController { - RxList sessionList = [].obs; - RxList accountList = [].obs; - bool isLoading = false; - Rx msgFeedUnread = MsgFeedUnread().obs; - RxList msgFeedTop = [ - { - "name": "回复我的", - "icon": Icons.message_outlined, - "route": "/replyMe", - "enabled": true, - "value": 0 - }, - { - "name": "@我", - "icon": Icons.alternate_email_outlined, - "route": "/atMe", - "enabled": true, - "value": 0 - }, - { - "name": "收到的赞", - "icon": Icons.favorite_border_outlined, - "route": "/likeMe", - "enabled": true, - "value": 0 - }, - { - "name": "系统通知", - "icon": Icons.notifications_none_outlined, - "route": "/sysMsg", - "enabled": true, - "value": 0 - }, - ].obs; +class WhisperController + extends CommonListController?, SessionList> { + late final bool disableLikeMsg; + late final List msgFeedTopItems; + late final RxList unreadCounts; + + int? endTs; + + @override + void onInit() { + super.onInit(); + disableLikeMsg = + GStorage.setting.get(SettingBoxKey.disableLikeMsg, defaultValue: false); + msgFeedTopItems = [ + { + "name": "回复我的", + "icon": Icons.message_outlined, + "route": "/replyMe", + "enabled": true, + }, + { + "name": "@我", + "icon": Icons.alternate_email_outlined, + "route": "/atMe", + "enabled": true, + }, + { + "name": "收到的赞", + "icon": Icons.favorite_border_outlined, + "route": "/likeMe", + "enabled": !disableLikeMsg, + }, + { + "name": "系统通知", + "icon": Icons.notifications_none_outlined, + "route": "/sysMsg", + "enabled": true, + }, + ]; + unreadCounts = + List.generate(msgFeedTopItems.length, (index) => 0).toList().obs; + queryMsgFeedUnread(); + queryData(); + } Future queryMsgFeedUnread() async { var res = await MsgHttp.msgFeedUnread(); if (res['status']) { - msgFeedUnread.value = MsgFeedUnread.fromJson(res['data']); - msgFeedTop[0]["value"] = msgFeedUnread.value.reply; - msgFeedTop[1]["value"] = msgFeedUnread.value.at; - msgFeedTop[2]["value"] = msgFeedUnread.value.like; - msgFeedTop[3]["value"] = msgFeedUnread.value.sys_msg; - if (GStorage.setting - .get(SettingBoxKey.disableLikeMsg, defaultValue: false)) { - msgFeedTop[2]["value"] = -1; - msgFeedTop[2]["enabled"] = false; - } - // 触发更新 - msgFeedTop.refresh(); + final data = MsgFeedUnread.fromJson(res['data']); + unreadCounts.value = [data.reply, data.at, data.like, data.sysMsg]; } else { SmartDialog.showToast(res['msg']); } } - Future onRemove(int index) async { - var res = await MsgHttp.removeMsg(sessionList[index].talkerId); + @override + Future?>> customGetData() => + MsgHttp.sessionList(endTs: endTs); + + @override + bool customHandleResponse( + bool isRefresh, Success?> response) { + endTs = response.response?.lastOrNull?.sessionTs; + List? dataList = response.response; + if (dataList.isNullOrEmpty) { + isEnd = true; + if (isRefresh) { + loadingState.value = LoadingState?>.success(dataList); + } + isLoading = false; + return true; + } + queryAccountList(dataList!).then((_) { + if (isRefresh) { + loadingState.value = LoadingState?>.success(dataList); + } else if (loadingState.value is Success) { + List list = (loadingState.value as Success).response; + list.addAll(dataList); + loadingState.refresh(); + } + }); + return true; + } + + @override + Future onRefresh() { + queryMsgFeedUnread(); + endTs = null; + return super.onRefresh(); + } + + Future onRemove(int index, int? talkerId) async { + var res = await MsgHttp.removeMsg(talkerId); if (res['status']) { - sessionList.removeAt(index); + List list = (loadingState.value as Success).response; + list.removeAt(index); + loadingState.refresh(); SmartDialog.showToast('删除成功'); } else { SmartDialog.showToast(res['msg']); } } - Future onSetTop(int index) async { - bool isTop = sessionList[index].topTs != 0; + Future onSetTop(int index, bool isTop, int? talkerId) async { var res = await MsgHttp.setTop( - sessionList[index].talkerId, - isTop ? 1 : 0, + talkerId: talkerId, + opType: isTop ? 1 : 0, ); if (res['status']) { - List list = sessionList.map((item) { - if (item.talkerId == sessionList[index].talkerId) { - return item..topTs = isTop ? 0 : 1; - } else { - return item; - } - }).toList(); + List list = (loadingState.value as Success).response; + list[index].topTs = isTop ? 0 : 1; if (!isTop) { SessionList item = list.removeAt(index); list.insert(0, item); } - sessionList.value = list; + loadingState.refresh(); SmartDialog.showToast('${isTop ? '移除' : ''}置顶成功'); } else { SmartDialog.showToast(res['msg']); } } - Future querySessionList(String? type) async { - if (isLoading) return; - var res = await MsgHttp.sessionList( - endTs: type == 'onLoad' ? sessionList.last.sessionTs : null, - ); - if (res['status']) { - List? sessionList = res['data']; - if (sessionList != null) { - if (sessionList.isNotEmpty) { - await queryAccountList(sessionList); - // 将 accountList 转换为 Map 结构 - Map accountMap = {}; - for (var j in accountList) { - accountMap[j.mid!] = j; - } - - // 遍历 sessionList,通过 mid 查找并赋值 accountInfo - for (var i in sessionList) { - var accountInfo = accountMap[i.talkerId]; - if (accountInfo != null) { - i.accountInfo = accountInfo; - } - if (i.talkerId == 844424930131966) { - i.accountInfo = AccountListModel( - name: 'UP主小助手', - face: - 'https://message.biliimg.com/bfs/im/489a63efadfb202366c2f88853d2217b5ddc7a13.png', - ); - } - } - } - - if (type == 'onLoad') { - this.sessionList.addAll(sessionList); - } else { - this.sessionList.value = sessionList; - } - } - } - isLoading = false; - return res; + void onTap(int index) { + List list = (loadingState.value as Success).response; + list[index].unreadCount = 0; + loadingState.refresh(); } Future queryAccountList(List sessionList) async { List midsList = sessionList.map((e) => e.talkerId).toList(); var res = await MsgHttp.accountList(midsList.join(',')); if (res['status']) { - accountList.value = res['data']; + List accountList = res['data']; + Map accountMap = {}; + for (AccountListModel item in accountList) { + accountMap[item.mid!] = item; + } + for (SessionList item in sessionList) { + AccountListModel? accountInfo = accountMap[item.talkerId]; + if (accountInfo != null) { + item.accountInfo = accountInfo; + } + } } } - - Future onLoad() async { - querySessionList('onLoad'); - } - - Future onRefresh() async { - querySessionList('onRefresh'); - } } diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index bfe73fcc..f71646b3 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -1,12 +1,11 @@ import 'package:PiliPlus/common/widgets/http_error.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; +import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/msg/session.dart'; -import 'package:easy_debounce/easy_throttle.dart'; +import 'package:PiliPlus/pages/whisper/widgets/item.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:PiliPlus/common/widgets/network_img_layer.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'controller.dart'; @@ -18,329 +17,126 @@ class WhisperPage extends StatefulWidget { } class _WhisperPageState extends State { - late final WhisperController _whisperController = - Get.put(WhisperController()); - late Future _futureBuilderFuture; - final ScrollController _scrollController = ScrollController(); - - @override - void initState() { - super.initState(); - _whisperController.queryMsgFeedUnread(); - _futureBuilderFuture = _whisperController.querySessionList('init'); - _scrollController.addListener(_scrollListener); - } - - @override - void dispose() { - _scrollController.removeListener(_scrollListener); - _scrollController.dispose(); - super.dispose(); - } - - Future _scrollListener() async { - if (_scrollController.position.pixels >= - _scrollController.position.maxScrollExtent - 200) { - EasyThrottle.throttle('my-throttler', const Duration(milliseconds: 800), - () async { - await _whisperController.onLoad(); - _whisperController.isLoading = true; - }); - } - } + final _whisperController = Get.put(WhisperController()); @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text('消息'), - // actions: [ - // IconButton( - // icon: Icon(Icons.open_in_browser_outlined, - // color: Theme.of(context).colorScheme.primary), - // tooltip: '用浏览器打开', - // onPressed: () { - // Get.toNamed('/webview', parameters: { - // 'url': 'https://message.bilibili.com', - // 'type': 'whisper', - // 'pageTitle': '消息中心', - // 'uaType': 'pc', - // }); - // }, - // ), - // const SizedBox(width: 12) - // ], - ), + appBar: AppBar(title: const Text('消息')), body: refreshIndicator( onRefresh: () async { - await Future.wait([ - _whisperController.queryMsgFeedUnread(), - _whisperController.onRefresh(), - ]); + await _whisperController.onRefresh(); }, - // TODO: refactor - child: ListView( - padding: EdgeInsets.only(bottom: 80), - controller: _scrollController, + child: CustomScrollView( physics: const AlwaysScrollableScrollPhysics(), - children: [ - LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - // 在这里根据父级容器的约束条件构建小部件树 - return Padding( - padding: const EdgeInsets.only(left: 20, right: 20), - child: SizedBox( - height: 90, - child: Obx( - () => Row( - children: Iterable.generate( - _whisperController.msgFeedTop.length) - .map((idx) { - return Expanded( - child: GestureDetector( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Badge( - isLabelVisible: _whisperController - .msgFeedTop[idx]['value'] > - 0, - label: Text( - " ${_whisperController.msgFeedTop[idx]['value']} "), - alignment: Alignment.topRight, - child: CircleAvatar( - radius: 22, - backgroundColor: Theme.of(context) - .colorScheme - .onInverseSurface, - child: Icon( - _whisperController.msgFeedTop[idx]['icon'], - size: 20, - color: - Theme.of(context).colorScheme.primary, - ), - ), - ), - const SizedBox(height: 6), - Text(_whisperController.msgFeedTop[idx]['name'], - style: const TextStyle(fontSize: 13)) - ], - ), - onTap: () { - if (!_whisperController.msgFeedTop[idx] - ['enabled']) { - SmartDialog.showToast('已禁用'); - return; - } - setState(() { - _whisperController.msgFeedTop[idx]['value'] = 0; - }); - Get.toNamed( - _whisperController.msgFeedTop[idx]['route']); - }, - )); - }).toList(), - ), - ), - ), - ); - }), - FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done && - snapshot.data != null) { - // TODO: refactor - if (snapshot.data is! Map) { - return HttpError( - isSliver: false, - callback: () => setState(() { - _futureBuilderFuture = - _whisperController.querySessionList('init'); - }), - ); - } - Map data = snapshot.data as Map; - if (data['status']) { - List sessionList = - _whisperController.sessionList; - return Obx( - () => sessionList.isEmpty - ? const SizedBox() - : ListView.separated( - itemCount: sessionList.length, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, int i) { - dynamic content = - sessionList[i].lastMsg?.content; - if (content == null || content == "") { - content = '不支持的消息类型'; - } else { - dynamic msg = content['text'] ?? - content['content'] ?? - content['title'] ?? - content['reply_content']; - if (msg == null) { - if (content['imageType'] != null) { - msg = '[图片消息]'; - } - } - content = msg ?? content.toString(); - } - return ListTile( - tileColor: sessionList[i].topTs == 0 - ? null - : Theme.of(context) - .colorScheme - .onInverseSurface, - onLongPress: () { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: - const EdgeInsets.symmetric( - vertical: 12), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - dense: true, - onTap: () { - Get.back(); - _whisperController - .onSetTop(i); - }, - title: Text( - sessionList[i].topTs == 0 - ? '置顶' - : '移除置顶', - style: const TextStyle( - fontSize: 14), - ), - ), - ListTile( - dense: true, - onTap: () { - Get.back(); - _whisperController - .onRemove(i); - }, - title: const Text( - '删除', - style: - TextStyle(fontSize: 14), - ), - ), - ], - ), - ); - }, - ); - }, - onTap: () { - setState(() { - sessionList[i].unreadCount = 0; - }); - Get.toNamed( - '/whisperDetail', - parameters: { - 'talkerId': - '${sessionList[i].talkerId}', - 'name': - sessionList[i].accountInfo?.name ?? - '', - 'face': - sessionList[i].accountInfo?.face ?? - '', - if (sessionList[i].accountInfo?.mid != - null) - 'mid': - '${sessionList[i].accountInfo?.mid}', - }, - ); - }, - leading: Builder(builder: (context) { - Widget buildAvatar() => NetworkImgLayer( - width: 45, - height: 45, - type: 'avatar', - src: sessionList[i] - .accountInfo - ?.face ?? - "", - ); - return sessionList[i].unreadCount != null - ? Badge( - isLabelVisible: - sessionList[i].unreadCount! > 0, - label: Text( - " ${sessionList[i].unreadCount} "), - alignment: Alignment.topRight, - child: buildAvatar(), - ) - : buildAvatar(); - }), - title: Text( - sessionList[i].accountInfo?.name ?? ""), - subtitle: Text( - '$content', - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: Theme.of(context) - .textTheme - .labelMedium! - .copyWith( - color: Theme.of(context) - .colorScheme - .outline), - ), - trailing: sessionList[i].lastMsg?.timestamp != - null - ? Text( - Utils.dateFormat( - sessionList[i].lastMsg!.timestamp, - formatType: "day"), - style: Theme.of(context) - .textTheme - .labelSmall! - .copyWith( - color: Theme.of(context) - .colorScheme - .outline), - ) - : null, - ); - }, - separatorBuilder: - (BuildContext context, int index) { - return Divider( - indent: 72, - endIndent: 20, - height: 6, - color: Colors.grey.withOpacity(0.1), - ); - }, - ), - ); - } else { - // 请求错误 - return Center( - child: Text(data['msg'] ?? '请求异常'), - ); - } - } else { - // 骨架屏 - return const SizedBox(); - } - }, - ) + slivers: [ + SliverSafeArea( + top: false, + bottom: false, + sliver: _buildTopItems, + ), + Obx(() => _buildBody(_whisperController.loadingState.value)), ], ), ), ); } + + Widget _buildBody(LoadingState?> loadingState) { + return switch (loadingState) { + Loading() => const SliverToBoxAdapter(), + Success() => loadingState.response?.isNotEmpty == true + ? SliverPadding( + padding: EdgeInsets.only( + bottom: MediaQuery.paddingOf(context).bottom + 100, + ), + sliver: SliverList.separated( + itemCount: loadingState.response!.length, + itemBuilder: (context, index) { + if (index == loadingState.response!.length - 1) { + _whisperController.onLoadMore(); + } + return WhisperSessionItem( + item: loadingState.response![index], + onSetTop: (isTop, talkerId) => + _whisperController.onSetTop(index, isTop, talkerId), + onRemove: (talkerId) => + _whisperController.onRemove(index, talkerId), + onTap: () => _whisperController.onTap(index), + ); + }, + separatorBuilder: (context, index) => Divider( + indent: 72, + endIndent: 20, + height: 1, + color: Colors.grey.withOpacity(0.1), + ), + ), + ) + : HttpError( + callback: _whisperController.onReload, + ), + Error() => HttpError( + errMsg: loadingState.errMsg, + callback: _whisperController.onReload, + ), + _ => throw UnimplementedError(), + }; + } + + Widget get _buildTopItems => SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.only(left: 20, right: 20, bottom: 10), + child: Row( + children: List.generate(_whisperController.msgFeedTopItems.length, + (index) { + return Expanded( + child: GestureDetector( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Obx( + () => Badge( + isLabelVisible: + _whisperController.unreadCounts[index] > 0, + label: Text( + " ${_whisperController.unreadCounts[index]} "), + alignment: Alignment.topRight, + child: CircleAvatar( + radius: 22, + backgroundColor: + Theme.of(context).colorScheme.onInverseSurface, + child: Icon( + _whisperController.msgFeedTopItems[index]['icon'], + size: 20, + color: Theme.of(context).colorScheme.primary, + ), + ), + ), + ), + const SizedBox(height: 6), + Text( + _whisperController.msgFeedTopItems[index]['name'], + style: const TextStyle(fontSize: 13), + ), + ], + ), + onTap: () { + if (!_whisperController.msgFeedTopItems[index]['enabled']) { + SmartDialog.showToast('已禁用'); + return; + } + _whisperController.unreadCounts[index] = 0; + Get.toNamed( + _whisperController.msgFeedTopItems[index]['route'], + ); + }, + ), + ); + }).toList(), + ), + ), + ); } diff --git a/lib/pages/whisper/widgets/item.dart b/lib/pages/whisper/widgets/item.dart new file mode 100644 index 00000000..a9e1bb3e --- /dev/null +++ b/lib/pages/whisper/widgets/item.dart @@ -0,0 +1,132 @@ +import 'package:PiliPlus/common/widgets/network_img_layer.dart'; +import 'package:PiliPlus/models/msg/session.dart'; +import 'package:PiliPlus/utils/utils.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class WhisperSessionItem extends StatelessWidget { + const WhisperSessionItem({ + super.key, + required this.item, + required this.onSetTop, + required this.onRemove, + required this.onTap, + }); + + final SessionList item; + final Function(bool isTop, int? talkerId) onSetTop; + final ValueChanged onRemove; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + dynamic content = item.lastMsg?.content; + if (content == null || content == "") { + content = '不支持的消息类型'; + } else { + dynamic msg = content['text'] ?? + content['content'] ?? + content['title'] ?? + content['reply_content']; + if (msg == null) { + if (content['imageType'] != null) { + msg = '[图片消息]'; + } + } + content = msg ?? content.toString(); + } + + return ListTile( + tileColor: item.topTs == 0 + ? null + : Theme.of(context).colorScheme.onInverseSurface, + onLongPress: () { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const EdgeInsets.symmetric(vertical: 12), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + dense: true, + onTap: () { + Get.back(); + onSetTop(item.topTs != 0, item.talkerId); + }, + title: Text( + item.topTs == 0 ? '置顶' : '移除置顶', + style: const TextStyle(fontSize: 14), + ), + ), + ListTile( + dense: true, + onTap: () { + Get.back(); + onRemove(item.talkerId); + }, + title: const Text( + '删除', + style: TextStyle(fontSize: 14), + ), + ), + ], + ), + ); + }, + ); + }, + onTap: () { + onTap(); + Get.toNamed( + '/whisperDetail', + parameters: { + 'talkerId': '${item.talkerId}', + 'name': item.accountInfo?.name ?? '', + 'face': item.accountInfo?.face ?? '', + if (item.accountInfo?.mid != null) + 'mid': '${item.accountInfo?.mid}', + }, + ); + }, + leading: Builder( + builder: (context) { + Widget buildAvatar() => NetworkImgLayer( + width: 45, + height: 45, + type: 'avatar', + src: item.accountInfo?.face ?? "", + ); + return item.unreadCount != null && item.unreadCount! > 0 + ? Badge( + label: Text(" ${item.unreadCount} "), + alignment: Alignment.topRight, + child: buildAvatar(), + ) + : buildAvatar(); + }, + ), + title: Text(item.accountInfo?.name ?? ""), + subtitle: Text( + '$content', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context) + .textTheme + .labelMedium! + .copyWith(color: Theme.of(context).colorScheme.outline), + ), + trailing: item.lastMsg?.timestamp != null + ? Text( + Utils.dateFormat(item.lastMsg!.timestamp, formatType: "day"), + style: TextStyle( + fontSize: 12, + color: Theme.of(context).colorScheme.outline, + ), + ) + : null, + ); + } +}