Compare commits

...

2 Commits

Author SHA1 Message Date
abu
dc2685a273 merge 2025-09-03 11:27:29 +08:00
abu
839d6ef5ad merge 2025-09-03 11:25:31 +08:00
33 changed files with 2661 additions and 885 deletions

View File

@ -13,12 +13,123 @@ class ChatController extends GetxController {
final chatList = <ConversationViewModel>[].obs; final chatList = <ConversationViewModel>[].obs;
void initChatData() { void initChatData() {
// chatList.value = <ConversationViewModel>[];
chatList.clear(); chatList.clear();
nextSeq.value = '0'; nextSeq.value = '0';
isFinished.value = false; isFinished.value = false;
} }
///
Future<bool> handleGroup({required String conversationID}) async {
final isReal = await ImService.instance.getConversation(conversationID: conversationID);
final V2TimConversation conv = isReal.data;
if (conv.lastMessage != null) {
//
return true;
} else {
//
return false;
}
}
///
Future<void> removeNoFriend({required String conversationID}) async {
try {
final isContinue = await handleGroup(conversationID: conversationID);
if (isContinue) {
await ImService.instance.deleteConversationsFromGroup(
groupName: myConversationType.ConversationTypeStatic.noFriend,
conversationIDList: [conversationID],
);
}
} catch (e) {
logger.e('移除陌生人会话分组异常:$e');
}
}
///
Future<void> mergeNoFriend({required conversationID}) async {
try {
final isContinue = await handleGroup(conversationID: conversationID);
if (isContinue) {
final typeEnum = myConversationType.ConversationTypeStatic.noFriend;
final hasGroupRes = await ImService.instance.getConversationGroupList();
if (hasGroupRes.success) {
final exists = hasGroupRes.data?.any((item) => item == typeEnum) ?? false;
if (!exists) {
// group中
await ImService.instance.createConversationGroup(
groupName: typeEnum,
conversationIDList: [conversationID],
);
logger.i('取关触发添加会话分组$typeEnum成功');
} else {
//
await ImService.instance.addConversationsToGroup(
groupName: typeEnum,
conversationIDList: [conversationID],
);
}
}
final res = chatList.firstWhereOrNull((item) => item.conversation.conversationID == conversationID);
if (res != null) {
chatList.remove(res);
}
}
} catch (e) {
logger.w('移除会话列表时发生错误: $e');
}
}
// nofd菜单入口
Future<void> updateNoFriendMenu() async {
final res = await ImService.instance.getConversationListByFilter(
filter: V2TimConversationFilter(conversationGroup: myConversationType.ConversationType.noFriend.name),
nextSeq: 0,
count: 1,
);
if (res.success && res.data != null) {
final convList = res.data!.conversationList ?? [];
if (convList.isNotEmpty) {
//
final unread = await ImService.instance.getUnreadMessageCountByFilter(
filter: V2TimConversationFilter(
conversationGroup: myConversationType.ConversationType.noFriend.name,
hasUnreadCount: true,
),
);
if (unread.success) {
var viewModelList = await ConversationViewModel.createConversationViewModel(convList: [convList.first]);
final conviewModel = viewModelList.first;
conviewModel.faceUrl = 'assets/images/notify/msr.png';
conviewModel.isCustomAdmin = myConversationType.ConversationTypeStatic.noFriend;
conviewModel.conversation.showName = '陌生人消息';
conviewModel.conversation.unreadCount = unread.data;
conviewModel.conversation.conversationID = myConversationType.ConversationTypeStatic.noFriend;
// final newList = List<ConversationViewModel>.from(chatList);
final index = chatList.indexWhere(
(item) => item.isCustomAdmin == myConversationType.ConversationTypeStatic.noFriend,
);
if (index != -1) {
chatList[index] = conviewModel;
//
chatList.sort((a, b) {
final atime = a.conversation.lastMessage?.timestamp ?? 0;
final btime = b.conversation.lastMessage?.timestamp ?? 0;
return btime.compareTo(atime); //
});
//
chatList.refresh();
}
}
}
} else {
logger.e('刷新入口失败-----${res.desc}');
}
}
// //
Future<void> getConversationList() async { Future<void> getConversationList() async {
logger.e('开始获取会话列表数据'); logger.e('开始获取会话列表数据');
@ -57,15 +168,29 @@ class ChatController extends GetxController {
} }
} }
/// ///
void getNoFriendData({V2TimConversation? csion}) async { Future<void> getNoFriendData({V2TimConversation? csion}) async {
// //
final hasNoFriend = chatList.any((item) => item.conversation.conversationGroupList?.contains(myConversationType.ConversationType.noFriend.name) ?? false); // final hasNoFriend = chatList.any((item) => item.conversation.conversationGroupList?.contains(myConversationType.ConversationType.noFriend.name) ?? false);
final hasNoFriend = chatList.any((item) => item.isCustomAdmin!.contains(myConversationType.ConversationType.noFriend.name));
logger.w('检测是否存在nofriend入口$hasNoFriend'); logger.w('检测是否存在nofriend入口$hasNoFriend');
if (hasNoFriend) { if (hasNoFriend) {
// //
// nofriend分组是否还有数据
final noFriendData = await ImService.instance.getConversationListByFilter(
filter: V2TimConversationFilter(conversationGroup: myConversationType.ConversationType.noFriend.name),
nextSeq: 0,
count: 1,
);
if (noFriendData.success && (noFriendData.data?.conversationList?.isNotEmpty == true)) {
//
final ConversationViewModel matchItem = chatList.firstWhere( final ConversationViewModel matchItem = chatList.firstWhere(
(item) => item.conversation.conversationGroupList?.contains(myConversationType.ConversationType.noFriend.name) ?? false, (item) => item.conversation.conversationGroupList?.contains(myConversationType.ConversationType.noFriend.name) ?? false,
orElse: () {
//viewmodel会话上的分组被移除
logger.w("没有找到符合条件的元素,执行移除逻辑");
return ConversationViewModel(conversation: V2TimConversation(conversationID: ''));
},
); );
// //
final unreadTotal = await ImService.instance.getUnreadMessageCountByFilter( final unreadTotal = await ImService.instance.getUnreadMessageCountByFilter(
@ -74,10 +199,28 @@ class ChatController extends GetxController {
hasUnreadCount: true, hasUnreadCount: true,
), ),
); );
// nofriend分组
if (matchItem.conversation.conversationID.isEmpty) {
// viewmodel
await ConversationViewModel.createConversationViewModel(convList: noFriendData.data!.conversationList!);
} else {
matchItem.conversation.lastMessage = csion!.lastMessage; matchItem.conversation.lastMessage = csion!.lastMessage;
matchItem.conversation.unreadCount = unreadTotal.data; matchItem.conversation.unreadCount = unreadTotal.data;
}
} else {
//
chatList.removeWhere((chatItem) => chatItem.isCustomAdmin == myConversationType.ConversationType.noFriend.name);
}
chatList.refresh(); chatList.refresh();
} else { } else {
logger.w('构建陌生人消息入口--开始');
await createNoFriendMenu();
}
}
//
Future<void> createNoFriendMenu() async {
// //
final res = await ImService.instance.getConversationListByFilter( final res = await ImService.instance.getConversationListByFilter(
filter: V2TimConversationFilter(conversationGroup: myConversationType.ConversationType.noFriend.name), filter: V2TimConversationFilter(conversationGroup: myConversationType.ConversationType.noFriend.name),
@ -95,12 +238,15 @@ class ChatController extends GetxController {
hasUnreadCount: true, hasUnreadCount: true,
), ),
); );
if (unread.success) { if (unread.success) {
var viewModelList = await ConversationViewModel.createConversationViewModel(convList: [convList.first]); var viewModelList = await ConversationViewModel.createConversationViewModel(convList: [convList.first]);
final conviewModel = viewModelList.first; final conviewModel = viewModelList.first;
conviewModel.faceUrl = 'assets/images/notify/msr.png'; conviewModel.faceUrl = 'assets/images/notify/msr.png';
conviewModel.isCustomAdmin = myConversationType.ConversationTypeStatic.noFriend;
conviewModel.conversation.showName = '陌生人消息'; conviewModel.conversation.showName = '陌生人消息';
conviewModel.conversation.unreadCount = unread.data; conviewModel.conversation.unreadCount = unread.data;
conviewModel.conversation.conversationID = myConversationType.ConversationTypeStatic.noFriend;
// final createItem = ConversationViewModel( // final createItem = ConversationViewModel(
// conversation: conv, // conversation: conv,
// faceUrl: faceUrl, // faceUrl: faceUrl,
@ -115,7 +261,8 @@ class ChatController extends GetxController {
chatList.value = newList; chatList.value = newList;
} }
} }
} } else {
logger.e('构建失败-----${res.desc}\n${res.data!.toJson()}');
} }
} }

View File

@ -13,6 +13,7 @@ class ChatDetailController extends GetxController {
final RxList<V2TimMessage> chatList = <V2TimMessage>[].obs; final RxList<V2TimMessage> chatList = <V2TimMessage>[].obs;
final RxBool isFriend = true.obs; final RxBool isFriend = true.obs;
final RxInt followType = 0.obs;
void updateChatListWithTimeLabels(List<V2TimMessage> originMessages) async { void updateChatListWithTimeLabels(List<V2TimMessage> originMessages) async {
final idRes = await ImService.instance.selfUserId(); final idRes = await ImService.instance.selfUserId();
@ -55,7 +56,7 @@ class ChatDetailController extends GetxController {
// label后插入 // label后插入
displayMessages.add(current); displayMessages.add(current);
if (needInsertLabel) { if (needInsertLabel) {
final labelTime = Utils().formatChatTime(currentTimestamp); final labelTime = Utils.formatChatTime(currentTimestamp);
final timeLabel = await IMMessage().insertTimeLabel(labelTime, selfUserId); final timeLabel = await IMMessage().insertTimeLabel(labelTime, selfUserId);
displayMessages.add(timeLabel.data); displayMessages.add(timeLabel.data);
} }

View File

@ -131,6 +131,4 @@ class ImUserInfoController extends GetxController {
if (gender.value < 0) return; if (gender.value < 0) return;
await ImService.instance.setSelfInfo(userFullInfo: V2TimUserFullInfo(gender: gender.value)); await ImService.instance.setSelfInfo(userFullInfo: V2TimUserFullInfo(gender: gender.value));
} }
/// updateAvatarupdateSignature
} }

View File

@ -51,12 +51,15 @@ class GlobalBadge extends GetxController {
final chatItem = ctl.chatList[i]; final chatItem = ctl.chatList[i];
logger.w('需要更新的ID:${chatItem.conversation.conversationID}'); logger.w('需要更新的ID:${chatItem.conversation.conversationID}');
if (updatedIds.contains(chatItem.conversation.conversationID)) { if (updatedIds.contains(chatItem.conversation.conversationID)) {
logger.w('找到的chatList的ID:${chatItem.conversation.conversationID}'); logger.w('chatList中包含:${chatItem.conversation.conversationID}');
// onchange中找到原始数据
final updatedConv = conversationList.firstWhere( final updatedConv = conversationList.firstWhere(
(c) => c.conversationID == chatItem.conversation.conversationID, (c) => c.conversationID == chatItem.conversation.conversationID,
orElse: () => V2TimConversation(conversationID: ''), orElse: () => V2TimConversation(conversationID: ''),
); );
//
ctl.getNoFriendData(csion: chatItem.conversation);
if (updatedConv.conversationID != '' && (updatedConv.conversationGroupList?.contains(ConversationType.noFriend.name) ?? false)) { if (updatedConv.conversationID != '' && (updatedConv.conversationGroupList?.contains(ConversationType.noFriend.name) ?? false)) {
// //
@ -68,30 +71,45 @@ class GlobalBadge extends GetxController {
); );
chatItem.conversation.lastMessage = updatedConv.lastMessage; chatItem.conversation.lastMessage = updatedConv.lastMessage;
chatItem.conversation.unreadCount = unread.data; // chatItem.conversation.unreadCount = unread.data; //
} else { update();
} else if (updatedConv.conversationID != '') {
// //
logger.w('不需要分组的会话,正常更新'); logger.w('非陌生人消息的会话,正常更新');
chatItem.conversation = updatedConv; chatItem.conversation = updatedConv;
update(); update();
} }
} else { } else {
logger.e('会话列表中不包含的会话:$updatedIds'); logger.e('本地会话列表中不包含的会话:$updatedIds'); // 1nofriend分组会话2:
//
for (var cvID in updatedIds) { for (var cvID in updatedIds) {
// //
final isReal = await ImService.instance.getConversation(conversationID: cvID); final isReal = await ImService.instance.getConversation(conversationID: cvID);
final V2TimConversation realConv = isReal.data;
if (isReal.success) { if (isReal.success) {
if (realConv.conversationID.isNotEmpty) { final V2TimConversation realConv = isReal.data;
if (realConv.lastMessage != null) {
// //
if (Get.isRegistered<NotifyNoFriendController>()) { if (Get.isRegistered<NotifyNoFriendController>()) {
logger.w('在陌生人会话列表');
final notifyCtl = Get.find<NotifyNoFriendController>(); final notifyCtl = Get.find<NotifyNoFriendController>();
//
if (realConv.conversationGroupList?.contains(ConversationType.noFriend.name) ?? false) {
notifyCtl.updateLastMsg(conversation: realConv); notifyCtl.updateLastMsg(conversation: realConv);
} else { } else {
// notifyCtl.del(conversation: realConv);
if (realConv.conversationGroupList?.isEmpty ?? false) {
willInsert.add(realConv);
} }
} }
// nofriend会话菜单入口的未读数量
if (chatItem.isCustomAdmin?.contains(ConversationType.noFriend.name) ?? false) {
await ctl.updateNoFriendMenu();
}
if (realConv.conversationGroupList?.contains(ConversationType.noFriend.name) ?? false) {
//
await ctl.getNoFriendData(csion: realConv);
} else {
// 2
willInsert.add(realConv);
}
} }
} }
} }
@ -99,7 +117,17 @@ class GlobalBadge extends GetxController {
} }
// //
if (willInsert.isNotEmpty) { if (willInsert.isNotEmpty) {
var viewModelList = await ConversationViewModel.createConversationViewModel(convList: willInsert); logger.w('收集到要插入的数据为:${willInsert.first.toLogString()}');
// willInsert
final deduped = {for (var c in willInsert) c.conversationID.trim(): c}.values.toList();
// ID集合
final existingIds = ctl.chatList.map((e) => e.conversation.conversationID.trim()).where((id) => id.isNotEmpty).toSet();
//
final filtered = deduped.where((c) => !existingIds.contains(c.conversationID.trim())).toList();
logger.w('最终需要插入的会话数量: ${filtered.length}, ids: ${filtered.map((c) => '"${(c.conversationID).trim()}"').toList()}');
final viewModelList = await ConversationViewModel.createConversationViewModel(convList: filtered);
ctl.chatList.insertAll(0, viewModelList); ctl.chatList.insertAll(0, viewModelList);
} }
// //
@ -116,12 +144,23 @@ class GlobalBadge extends GetxController {
final btime = b.conversation.lastMessage?.timestamp ?? 0; final btime = b.conversation.lastMessage?.timestamp ?? 0;
return btime.compareTo(atime); // return btime.compareTo(atime); //
}); });
//
final seen = <String>{};
ctl.chatList.retainWhere((item) {
final id = item.conversation.conversationID.trim();
if (seen.contains(id)) {
return false;
} else {
seen.add(id);
return true;
}
});
ctl.chatList.refresh(); ctl.chatList.refresh();
}, }, //changeEnd;
); );
// final ctl = Get.find<ChatController>(); // final ctl = Get.find<ChatController>();
// ctl.getConversationList(); // ctl.getConversationList();
_initUnreadCount(); initUnreadCount();
_addListener(); _addListener();
} }
@ -171,7 +210,7 @@ class GlobalBadge extends GetxController {
} }
/// ///
void _initUnreadCount() async { void initUnreadCount() async {
final res = await TencentImSDKPlugin.v2TIMManager.getConversationManager().getTotalUnreadMessageCount(); final res = await TencentImSDKPlugin.v2TIMManager.getConversationManager().getTotalUnreadMessageCount();
if (res.code == 0) { if (res.code == 0) {
totalUnread.value = res.data ?? 0; totalUnread.value = res.data ?? 0;
@ -187,7 +226,7 @@ class GlobalBadge extends GetxController {
/// ///
handAndroid() { handAndroid() {
_initUnreadCount(); initUnreadCount();
final ctl = Get.find<ChatController>(); final ctl = Get.find<ChatController>();
ctl.initChatData(); ctl.initChatData();
ctl.getConversationList(); ctl.getConversationList();
@ -201,7 +240,7 @@ class GlobalBadge extends GetxController {
/// total /// total
Future<void> refreshUnreadCount() async { Future<void> refreshUnreadCount() async {
_initUnreadCount(); initUnreadCount();
} }
/// ///

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:logger/logger.dart'; import 'package:logger/logger.dart';
import 'package:loopin/IM/controller/im_user_info_controller.dart';
import 'package:loopin/IM/im_service.dart'; import 'package:loopin/IM/im_service.dart';
import 'package:loopin/controller/video_module_controller.dart'; import 'package:loopin/controller/video_module_controller.dart';
import 'package:loopin/utils/common.dart'; import 'package:loopin/utils/common.dart';
@ -51,6 +52,8 @@ class ImCore {
}, },
onUserSigExpired: () => logger.w("UserSig 过期"), onUserSigExpired: () => logger.w("UserSig 过期"),
onSelfInfoUpdated: (V2TimUserFullInfo info) { onSelfInfoUpdated: (V2TimUserFullInfo info) {
final ImUserInfoController imUserInfoController = Get.find<ImUserInfoController>();
imUserInfoController.refreshUserInfo();
logger.i("用户信息更新: ${info.toJson()}"); logger.i("用户信息更新: ${info.toJson()}");
}, },
), ),

View File

@ -1,5 +1,4 @@
import 'package:logger/logger.dart'; import 'package:logger/logger.dart';
import 'package:loopin/utils/notification_banner.dart';
import 'package:tencent_cloud_chat_sdk/enum/V2TimFriendshipListener.dart'; import 'package:tencent_cloud_chat_sdk/enum/V2TimFriendshipListener.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_application.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_application.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info.dart';
@ -62,7 +61,7 @@ class ImFriendListeners {
for (var item in userInfoList) { for (var item in userInfoList) {
logger.i('新增粉丝:${item.toJson()}'); logger.i('新增粉丝:${item.toJson()}');
} }
NotificationBanner.foucs(userInfoList.last); // NotificationBanner.foucs(userInfoList.last);
} else { } else {
// //
for (var item in userInfoList) { for (var item in userInfoList) {

View File

@ -3,6 +3,7 @@ import 'dart:async';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:logger/logger.dart'; import 'package:logger/logger.dart';
import 'package:loopin/IM/controller/chat_detail_controller.dart'; import 'package:loopin/IM/controller/chat_detail_controller.dart';
import 'package:loopin/IM/global_badge.dart';
import 'package:loopin/IM/im_message.dart'; import 'package:loopin/IM/im_message.dart';
import 'package:loopin/IM/im_service.dart'; import 'package:loopin/IM/im_service.dart';
import 'package:loopin/utils/index.dart'; import 'package:loopin/utils/index.dart';
@ -49,12 +50,12 @@ class ImMessageListenerService extends GetxService {
DateTime.now().millisecondsSinceEpoch, DateTime.now().millisecondsSinceEpoch,
)) { )) {
// 3 // 3
final showLabel = Utils().formatChatTime(DateTime.now().millisecondsSinceEpoch ~/ 1000); final showLabel = Utils.formatChatTime(DateTime.now().millisecondsSinceEpoch ~/ 1000);
final resMsg = await IMMessage().insertTimeLabel(showLabel, selfUserId); final resMsg = await IMMessage().insertTimeLabel(showLabel, selfUserId);
messagesToInsert.add(resMsg.data); messagesToInsert.add(resMsg.data);
} else { } else {
// //
final showLabel = Utils().formatChatTime(DateTime.now().millisecondsSinceEpoch ~/ 1000); final showLabel = Utils.formatChatTime(DateTime.now().millisecondsSinceEpoch ~/ 1000);
final resMsg = await IMMessage().insertTimeLabel(showLabel, selfUserId); final resMsg = await IMMessage().insertTimeLabel(showLabel, selfUserId);
messagesToInsert.add(resMsg.data); messagesToInsert.add(resMsg.data);
} }
@ -177,11 +178,15 @@ class ImMessageListenerService extends GetxService {
} }
}, },
onRecvC2CReadReceipt: (List<V2TimMessageReceipt> receiptList) { onRecvC2CReadReceipt: (List<V2TimMessageReceipt> receiptList) {
final gtl = Get.find<GlobalBadge>();
gtl.initUnreadCount();
for (var receipt in receiptList) { for (var receipt in receiptList) {
logger.i("C2C已读: msgID=${receipt.msgID}, userID=${receipt.userID}"); logger.i("C2C已读: msgID=${receipt.msgID}, userID=${receipt.userID}");
} }
}, },
onRecvMessageReadReceipts: (List<V2TimMessageReceipt> receiptList) { onRecvMessageReadReceipts: (List<V2TimMessageReceipt> receiptList) {
final gtl = Get.find<GlobalBadge>();
gtl.initUnreadCount();
for (var receipt in receiptList) { for (var receipt in receiptList) {
logger.i("群已读: groupID=${receipt.groupID}, msgID=${receipt.msgID}, read=${receipt.readCount}, unread=${receipt.unreadCount}"); logger.i("群已读: groupID=${receipt.groupID}, msgID=${receipt.msgID}, read=${receipt.readCount}, unread=${receipt.unreadCount}");
} }

View File

@ -710,4 +710,15 @@ class ImService {
); );
return ImResult.wrap(res); return ImResult.wrap(res);
} }
///
/// [nextCursor]
Future<ImResult<V2TimUserInfoResult>> getMyFollowingList({
required String nextCursor,
}) async {
final res = await TIMFriendshipManager.instance.getMyFollowingList(
nextCursor: nextCursor,
);
return ImResult.wrap(res);
}
} }

View File

@ -8,6 +8,16 @@ enum ConversationType {
groupNotify, // groupNotify, //
} }
///
class ConversationTypeStatic {
static const String noFriend = 'noFriend';
static const String system = 'system';
static const String newFoucs = 'newFoucs';
static const String interaction = 'interaction';
static const String order = 'order';
static const String groupNotify = 'groupNotify';
}
extension ConversationTypeExtension on ConversationType { extension ConversationTypeExtension on ConversationType {
String get name { String get name {
switch (this) { switch (this) {

View File

@ -19,7 +19,6 @@ class ConversationViewModel {
// userID groupID // userID groupID
for (var conv in convList) { for (var conv in convList) {
logger.e('未过滤前到会话数据:${conv.toLogString()}');
if (conv.userID != null) { if (conv.userID != null) {
userIDList.add(conv.userID!); userIDList.add(conv.userID!);
} else if (conv.groupID != null) { } else if (conv.groupID != null) {

View File

@ -530,7 +530,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
final url = obj['url']; final url = obj['url'];
final title = obj['title']; final title = obj['title'];
final price = obj['price']; final price = obj['price'];
final sell = Utils().graceNumber(int.tryParse(obj['sell'])!); final sell = Utils.graceNumber(int.tryParse(obj['sell'])!);
msgtpl.add(RenderChatItem( msgtpl.add(RenderChatItem(
data: item, data: item,
child: GestureDetector( child: GestureDetector(
@ -1039,12 +1039,12 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
DateTime.now().millisecondsSinceEpoch, DateTime.now().millisecondsSinceEpoch,
)) { )) {
// 3 // 3
final showLabel = Utils().formatChatTime(DateTime.now().millisecondsSinceEpoch ~/ 1000); final showLabel = Utils.formatChatTime(DateTime.now().millisecondsSinceEpoch ~/ 1000);
final resMsg = await IMMessage().insertTimeLabel(showLabel, selfUserId); final resMsg = await IMMessage().insertTimeLabel(showLabel, selfUserId);
messagesToInsert.add(resMsg.data); messagesToInsert.add(resMsg.data);
} else { } else {
// //
final showLabel = Utils().formatChatTime(DateTime.now().millisecondsSinceEpoch ~/ 1000); final showLabel = Utils.formatChatTime(DateTime.now().millisecondsSinceEpoch ~/ 1000);
final resMsg = await IMMessage().insertTimeLabel(showLabel, selfUserId); final resMsg = await IMMessage().insertTimeLabel(showLabel, selfUserId);
messagesToInsert.add(resMsg.data); messagesToInsert.add(resMsg.data);
@ -2155,19 +2155,33 @@ class RenderChatItem extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
String? displayName = (data.friendRemark?.isNotEmpty ?? false)
? data.friendRemark
: (data.nameCard?.isNotEmpty ?? false)
? data.nameCard
: (data.nickName?.isNotEmpty ?? false)
? data.nickName
: '未知昵称';
return Container( return Container(
margin: const EdgeInsets.only(bottom: 10.0), margin: const EdgeInsets.only(bottom: 10.0),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
!(data.isSelf ?? false) !(data.isSelf ?? false)
? SizedBox( ? GestureDetector(
onTap: () {
//
logger.e("点击了头像");
Get.toNamed('/vloger', arguments: {'memberId': data.sender});
},
child: SizedBox(
height: 35.0, height: 35.0,
width: 35.0, width: 35.0,
child: ClipRRect( child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(20.0)), borderRadius: const BorderRadius.all(Radius.circular(20.0)),
child: NetworkOrAssetImage(imageUrl: data.faceUrl), child: NetworkOrAssetImage(imageUrl: data.faceUrl),
), ),
),
) )
: const SizedBox.shrink(), : const SizedBox.shrink(),
Expanded( Expanded(
@ -2176,13 +2190,13 @@ class RenderChatItem extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: !(data.isSelf ?? false) ? CrossAxisAlignment.start : CrossAxisAlignment.end, crossAxisAlignment: !(data.isSelf ?? false) ? CrossAxisAlignment.start : CrossAxisAlignment.end,
children: [ children: [
Text( // Text(
data.friendRemark ?? data.nameCard ?? data.nickName ?? '未知昵称', // displayName ?? '未知昵称',
style: const TextStyle(color: Colors.grey, fontSize: 12.0), // style: const TextStyle(color: Colors.grey, fontSize: 12.0),
), // ),
const SizedBox( // const SizedBox(
height: 3.0, // height: 3.0,
), // ),
Stack( Stack(
children: [ children: [
// //

View File

@ -792,12 +792,12 @@ class _ChatState extends State<ChatGroup> with SingleTickerProviderStateMixin {
DateTime.now().millisecondsSinceEpoch, DateTime.now().millisecondsSinceEpoch,
)) { )) {
// 3 // 3
final showLabel = Utils().formatChatTime(DateTime.now().millisecondsSinceEpoch ~/ 1000); final showLabel = Utils.formatChatTime(DateTime.now().millisecondsSinceEpoch ~/ 1000);
final resMsg = await IMMessage().insertTimeLabel(showLabel, selfUserId); final resMsg = await IMMessage().insertTimeLabel(showLabel, selfUserId);
messagesToInsert.add(resMsg.data); messagesToInsert.add(resMsg.data);
} else { } else {
// //
final showLabel = Utils().formatChatTime(DateTime.now().millisecondsSinceEpoch ~/ 1000); final showLabel = Utils.formatChatTime(DateTime.now().millisecondsSinceEpoch ~/ 1000);
final resMsg = await IMMessage().insertTimeLabel(showLabel, selfUserId); final resMsg = await IMMessage().insertTimeLabel(showLabel, selfUserId);
messagesToInsert.add(resMsg.data); messagesToInsert.add(resMsg.data);

View File

@ -3,16 +3,17 @@ library;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:loopin/IM/controller/chat_controller.dart';
import 'package:loopin/IM/controller/chat_detail_controller.dart'; import 'package:loopin/IM/controller/chat_detail_controller.dart';
import 'package:loopin/IM/im_message.dart'; import 'package:loopin/IM/im_message.dart';
import 'package:loopin/IM/im_result.dart'; import 'package:loopin/IM/im_result.dart';
import 'package:loopin/IM/im_service.dart'; import 'package:loopin/IM/im_service.dart';
import 'package:loopin/components/network_or_asset_image.dart';
import 'package:loopin/components/preview_video.dart'; import 'package:loopin/components/preview_video.dart';
import 'package:loopin/models/conversation_type.dart'; import 'package:loopin/models/conversation_type.dart';
import 'package:loopin/pages/chat/notify_controller/notify_no_friend_controller.dart'; import 'package:loopin/pages/chat/notify_controller/notify_no_friend_controller.dart';
import 'package:loopin/utils/snapshot.dart'; import 'package:loopin/utils/snapshot.dart';
import 'package:shirne_dialog/shirne_dialog.dart'; import 'package:shirne_dialog/shirne_dialog.dart';
import 'package:tencent_cloud_chat_sdk/enum/friend_type_enum.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart'; import 'package:wechat_assets_picker/wechat_assets_picker.dart';
@ -41,6 +42,8 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
// //
final bool isNeedScrollBottom = true; final bool isNeedScrollBottom = true;
final String _tips = '回复或关注对方之前,对方只能发送一条消息';
bool isLoading = false; // bool isLoading = false; //
bool hasMore = true; // bool hasMore = true; //
final RxBool _throttleFlag = false.obs; // final RxBool _throttleFlag = false.obs; //
@ -152,13 +155,27 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
void isMyFriend() async { void isMyFriend() async {
final isFriendId = arguments.value.userID; final isFriendId = arguments.value.userID;
final isfd = await ImService.instance.isMyFriend(isFriendId!, FriendTypeEnum.V2TIM_FRIEND_TYPE_BOTH); // final isfd = await ImService.instance.isMyFriend(isFriendId!, FriendTypeEnum.V2TIM_FRIEND_TYPE_BOTH);
// if (isfd.success) {
// controller.isFriend.value = isfd.data;
// print(isfd.data);
// } else {
// controller.isFriend.value = false;
// print(isfd.desc);
// }
/// 0
/// 1
/// 2
/// 3
final isfd = await ImService.instance.checkFollowType(userIDList: [isFriendId!]);
if (isfd.success) { if (isfd.success) {
controller.isFriend.value = isfd.data; controller.followType.value = isfd.data?.first.followType ?? 0;
print(isfd.data); if ([3].contains(controller.followType.value)) {
controller.isFriend.value = true;
} else { } else {
controller.isFriend.value = false; controller.isFriend.value = false;
print(isfd.desc); }
logger.i('当前聊天的关系为:${controller.followType.value}');
} }
} }
@ -294,9 +311,9 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
child: RichTextUtil.getRichText(item.textElem?.text ?? '', color: !(item.isSelf ?? false) ? Colors.black : Colors.white), // emoj// child: RichTextUtil.getRichText(item.textElem?.text ?? '', color: !(item.isSelf ?? false) ? Colors.black : Colors.white), // emoj//
), ),
onLongPress: () { // onLongPress: () {
contextMenuDialog(); // contextMenuDialog();
}, // },
), ),
), ),
), ),
@ -883,12 +900,12 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
DateTime.now().millisecondsSinceEpoch, DateTime.now().millisecondsSinceEpoch,
)) { )) {
// 3 // 3
final showLabel = Utils().formatChatTime(DateTime.now().millisecondsSinceEpoch ~/ 1000); final showLabel = Utils.formatChatTime(DateTime.now().millisecondsSinceEpoch ~/ 1000);
final resMsg = await IMMessage().insertTimeLabel(showLabel, selfUserId); final resMsg = await IMMessage().insertTimeLabel(showLabel, selfUserId);
messagesToInsert.add(resMsg.data); messagesToInsert.add(resMsg.data);
} else { } else {
// //
final showLabel = Utils().formatChatTime(DateTime.now().millisecondsSinceEpoch ~/ 1000); final showLabel = Utils.formatChatTime(DateTime.now().millisecondsSinceEpoch ~/ 1000);
final resMsg = await IMMessage().insertTimeLabel(showLabel, selfUserId); final resMsg = await IMMessage().insertTimeLabel(showLabel, selfUserId);
messagesToInsert.add(resMsg.data); messagesToInsert.add(resMsg.data);
@ -1203,6 +1220,7 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
// //
void contextMenuDialog() { void contextMenuDialog() {
return;
showDialog( showDialog(
context: context, context: context,
builder: (context) { builder: (context) {
@ -1295,115 +1313,66 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
)), )),
), ),
actions: [ actions: [
IconButton( // IconButton(
icon: const Icon( // icon: const Icon(
Icons.more_horiz, // Icons.more_horiz,
color: Colors.white, // color: Colors.white,
), // ),
onPressed: () async { // onPressed: () async {
final paddingTop = MediaQuery.of(Get.context!).padding.top; // final paddingTop = MediaQuery.of(Get.context!).padding.top;
final selected = await showMenu( // final selected = await showMenu(
context: Get.context!, // context: Get.context!,
position: RelativeRect.fromLTRB( // position: RelativeRect.fromLTRB(
double.infinity, // double.infinity,
kToolbarHeight + paddingTop - 12, // kToolbarHeight + paddingTop - 12,
8, // 8,
double.infinity, // double.infinity,
), // ),
color: FStyle.primaryColor, // color: FStyle.primaryColor,
elevation: 8, // elevation: 8,
items: [ // items: [
PopupMenuItem<String>( // PopupMenuItem<String>(
value: 'remark', // value: 'report',
child: Row( // child: Row(
children: [ // children: [
Icon(Icons.edit, color: Colors.white, size: 18), // Icon(Icons.report, color: Colors.white, size: 18),
SizedBox(width: 8), // SizedBox(width: 8),
Text( // Text(
'设置备注', // '举报',
style: TextStyle(color: Colors.white), // style: TextStyle(color: Colors.white),
), // ),
], // ],
), // ),
), // ),
PopupMenuItem<String>( // PopupMenuItem<String>(
value: 'not', // value: 'block',
child: Row( // child: Row(
children: [ // children: [
Icon(Icons.do_not_disturb_on, color: Colors.white, size: 18), // Icon(Icons.block, color: Colors.white, size: 18),
SizedBox(width: 8), // SizedBox(width: 8),
Text( // Text(
'设为免打扰', // '拉黑',
style: TextStyle(color: Colors.white), // style: TextStyle(color: Colors.white),
), // ),
], // ],
), // ),
), // ),
PopupMenuItem<String>( // ],
value: 'report', // );
child: Row(
children: [
Icon(Icons.report, color: Colors.white, size: 18),
SizedBox(width: 8),
Text(
'举报',
style: TextStyle(color: Colors.white),
),
],
),
),
PopupMenuItem<String>(
value: 'block',
child: Row(
children: [
Icon(Icons.block, color: Colors.white, size: 18),
SizedBox(width: 8),
Text(
'拉黑',
style: TextStyle(color: Colors.white),
),
],
),
),
PopupMenuItem<String>(
value: 'foucs',
child: Row(
children: [
Icon(Icons.person_remove_alt_1, color: Colors.white, size: 18),
SizedBox(width: 8),
Text(
'取消关注',
style: TextStyle(color: Colors.white),
),
],
),
),
],
);
if (selected != null) { // if (selected != null) {
switch (selected) { // switch (selected) {
case 'remark': // case 'report':
print('点击了备注'); // print('点击了举报');
setRemark(); // break;
break; // case 'block':
case 'not': // print('点击了拉黑');
print('点击了免打扰'); // break;
break; // }
case 'report': // }
print('点击了举报'); // },
break; // ),
case 'block':
print('点击了拉黑');
break;
case 'foucs':
print('点击了取关');
break;
}
}
},
),
], ],
), ),
body: Flex( body: Flex(
@ -1430,6 +1399,39 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
// ), // ),
// ), // ),
//
// Expanded(
// child: GestureDetector(
// behavior: HitTestBehavior.opaque,
// onTap: handleClickChatArea,
// child: LayoutBuilder(
// builder: (context, constraints) {
// return Obx(() {
// // final tipWidget = Builder(builder: (context) => Text(tips));
// final msgWidgets = renderChatList().reversed.toList();
// // if (controller.isFriend.value) {}
// return ListView(
// controller: chatController,
// reverse: true,
// padding: const EdgeInsets.all(10.0),
// children: [
// ConstrainedBox(
// constraints: BoxConstraints(
// minHeight: constraints.maxHeight - 20,
// ),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: msgWidgets,
// ),
// ),
// ],
// );
// });
// },
// ),
// ),
// ),
// //
Expanded( Expanded(
child: GestureDetector( child: GestureDetector(
@ -1438,16 +1440,26 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
child: LayoutBuilder( child: LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
return Obx(() { return Obx(() {
final showTipWidget = controller.followType.value;
const double tipHeight = 50; //
final msgWidgets = renderChatList().reversed.toList(); final msgWidgets = renderChatList().reversed.toList();
return ListView( return Stack(
children: [
ListView(
controller: chatController, controller: chatController,
reverse: true, reverse: true,
padding: const EdgeInsets.all(10.0), padding: EdgeInsets.only(
top: [1, 2].contains(showTipWidget) ? tipHeight + 10 : 0, //
left: 10,
right: 10,
bottom: 10,
),
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints( constraints: BoxConstraints(
minHeight: constraints.maxHeight - 20, minHeight: constraints.maxHeight - ([1, 2].contains(showTipWidget) ? tipHeight : 0),
), ),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -1455,6 +1467,66 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
), ),
), ),
], ],
),
if ([1, 2].contains(showTipWidget))
Positioned(
top: 0,
left: 0,
right: 0,
height: tipHeight, //
child: Container(
color: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(
Icons.error_outline,
color: FStyle.primaryColor,
size: 20,
),
SizedBox(
width: 10,
),
Expanded(
child: Text(
'对方未回复或互关前仅可发送一条消息',
style: const TextStyle(color: Colors.black, fontSize: 14),
overflow: TextOverflow.ellipsis,
),
),
Visibility(
visible: [2].contains(showTipWidget),
child: TextButton(
style: TextButton.styleFrom(
backgroundColor: FStyle.primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
onPressed: () async {
//
final res = await ImService.instance.followUser(userIDList: [arguments.value.userID!]);
if (res.success) {
controller.isFriend.value = true;
controller.followType.value = 3;
if (arguments.value.conversationGroupList?.isNotEmpty == true) {
//
final ctl = Get.find<ChatController>();
ctl.removeNoFriend(conversationID: arguments.value.conversationID);
ctl.updateNoFriendMenu();
}
}
},
child: const Text('回关', style: TextStyle(color: Colors.white)),
),
)
],
),
),
),
],
); );
}); });
}, },
@ -1481,19 +1553,28 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
children: [ children: [
InkWell( InkWell(
child: Icon( child: Icon(
voiceBtnEnable ? Icons.keyboard_outlined : Icons.contactless_outlined, // voiceBtnEnable ? Icons.keyboard_outlined : Icons.contactless_outlined,
Icons.keyboard_outlined,
color: const Color(0xFF3B3B3B), color: const Color(0xFF3B3B3B),
size: 30.0, size: 30.0,
), ),
onTap: () { onTap: () {
setState(() { setState(() {
toolbarEnable = false; toolbarEnable = false;
if (voiceBtnEnable) { if (editorFocusNode.hasFocus) {
voiceBtnEnable = false;
editorFocusNode.requestFocus();
} else {
voiceBtnEnable = true;
editorFocusNode.unfocus(); editorFocusNode.unfocus();
} else {
editorFocusNode.requestFocus();
}
if (toolbarEnable) {
// voiceBtnEnable = false;
// editorFocusNode.requestFocus();
// editorFocusNode.requestFocus();
} else {
// voiceBtnEnable = true;
// toolbarEnable = true;
// editorFocusNode.unfocus();
} }
}); });
}, },
@ -1597,29 +1678,31 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
const SizedBox( const SizedBox(
width: 10.0, width: 10.0,
), ),
InkWell(
child: const Icon( // InkWell(
Icons.add_reaction_rounded, // child: const Icon(
color: Color(0xFF3B3B3B), // Icons.add_reaction_rounded,
size: 30.0, // color: Color(0xFF3B3B3B),
), // size: 30.0,
onTap: () { // ),
handleEmojChooseState(0); // onTap: () {
}, // handleEmojChooseState(0);
), // },
const SizedBox( // ),
width: 8.0, // const SizedBox(
), // width: 8.0,
InkWell( // ),
child: const Icon(
Icons.add, // InkWell(
color: Color(0xFF3B3B3B), // child: const Icon(
size: 30.0, // Icons.add,
), // color: Color(0xFF3B3B3B),
onTap: () { // size: 30.0,
handleEmojChooseState(1); // ),
}, // onTap: () {
), // handleEmojChooseState(1);
// },
// ),
const SizedBox( const SizedBox(
width: 8.0, width: 8.0,
), ),
@ -1942,27 +2025,33 @@ class RenderChatItem extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
String? displayName = (data.friendRemark?.isNotEmpty ?? false)
? data.friendRemark
: (data.nameCard?.isNotEmpty ?? false)
? data.nameCard
: (data.nickName?.isNotEmpty ?? false)
? data.nickName
: '未知昵称';
return Container( return Container(
margin: const EdgeInsets.only(bottom: 10.0), margin: const EdgeInsets.only(bottom: 10.0),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
!(data.isSelf ?? false) !(data.isSelf ?? false)
? SizedBox( ? GestureDetector(
onTap: () {
//
logger.e("点击了头像");
Get.toNamed('/vloger', arguments: {'memberId': data.sender});
//
},
child: SizedBox(
height: 35.0, height: 35.0,
width: 35.0, width: 35.0,
child: ClipRRect( child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(20.0)), borderRadius: const BorderRadius.all(Radius.circular(20.0)),
child: Image.network( child: NetworkOrAssetImage(imageUrl: data.faceUrl),
data.faceUrl ?? 'https://wuzhongjie.com.cn/download/logo.png',
errorBuilder: (context, error, stackTrace) {
return Image.asset(
'assets/images/pic1.jpg',
height: 60.0,
width: 60.0,
fit: BoxFit.cover,
);
},
), ),
), ),
) )
@ -1973,13 +2062,13 @@ class RenderChatItem extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: !(data.isSelf ?? false) ? CrossAxisAlignment.start : CrossAxisAlignment.end, crossAxisAlignment: !(data.isSelf ?? false) ? CrossAxisAlignment.start : CrossAxisAlignment.end,
children: [ children: [
Text( // Text(
data.friendRemark ?? data.nameCard ?? data.nickName ?? '未知昵称', // displayName ?? '未知昵称',
style: const TextStyle(color: Colors.grey, fontSize: 12.0), // style: const TextStyle(color: Colors.grey, fontSize: 12.0),
), // ),
const SizedBox( // const SizedBox(
height: 3.0, // height: 3.0,
), // ),
Stack( Stack(
children: [ children: [
// //
@ -2011,17 +2100,7 @@ class RenderChatItem extends StatelessWidget {
width: 35.0, width: 35.0,
child: ClipRRect( child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(20.0)), borderRadius: const BorderRadius.all(Radius.circular(20.0)),
child: Image.network( child: NetworkOrAssetImage(imageUrl: data.faceUrl),
data.faceUrl ?? 'https://wuzhongjie.com.cn/download/logo.png',
errorBuilder: (context, error, stackTrace) {
return Image.asset(
'assets/images/pic1.jpg',
height: 60.0,
width: 60.0,
fit: BoxFit.cover,
);
},
),
), ),
) )
: const SizedBox.shrink(), : const SizedBox.shrink(),

View File

@ -232,17 +232,18 @@ class ChatPageState extends State<ChatPage> {
if (selected != null) { if (selected != null) {
switch (selected) { switch (selected) {
case 'group': case 'group':
print('点击了发起群聊'); logger.w('点击了发起群聊');
Get.toNamed('/group');
break; break;
case 'friend': case 'friend':
print('点击了添加朋友'); logger.w('点击了添加朋友');
break; break;
case 'scan': case 'scan':
print('点击了扫一扫'); logger.w('点击了扫一扫');
ScanUtil.openScanner(onResult: (code) { ScanUtil.openScanner(onResult: (code) {
print('扫码结果:$code'); logger.w('扫码结果:$code');
Get.snackbar('扫码成功', code); Get.snackbar('扫码成功', code);
// //
}); });
break; break;
} }
@ -279,7 +280,12 @@ class ChatPageState extends State<ChatPage> {
Text('群聊'), Text('群聊'),
], ],
), ),
Column( GestureDetector(
onTap: () {
//
Get.toNamed('/eachFlow');
},
child: Column(
children: [ children: [
SvgPicture.asset( SvgPicture.asset(
'assets/images/svg/kefu.svg', 'assets/images/svg/kefu.svg',
@ -289,7 +295,13 @@ class ChatPageState extends State<ChatPage> {
Text('互关'), Text('互关'),
], ],
), ),
Column( ),
GestureDetector(
onTap: () {
//
Get.toNamed('/fans');
},
child: Column(
children: [ children: [
SvgPicture.asset( SvgPicture.asset(
'assets/images/svg/comment.svg', 'assets/images/svg/comment.svg',
@ -299,7 +311,13 @@ class ChatPageState extends State<ChatPage> {
Text('粉丝'), Text('粉丝'),
], ],
), ),
Column( ),
GestureDetector(
onTap: () {
//
Get.toNamed('/flow');
},
child: Column(
children: [ children: [
SvgPicture.asset( SvgPicture.asset(
'assets/images/svg/comment.svg', 'assets/images/svg/comment.svg',
@ -309,16 +327,13 @@ class ChatPageState extends State<ChatPage> {
Text('关注'), Text('关注'),
], ],
), ),
),
], ],
), ),
), ),
Expanded( Expanded(
child: RefreshIndicator( child: Obx(
backgroundColor: Colors.white, () {
color: Color(0xFFFF5000),
displacement: 10.0,
onRefresh: handleRefresh,
child: Obx(() {
final chatList = controller.chatList; final chatList = controller.chatList;
return ListView.builder( return ListView.builder(
@ -326,8 +341,11 @@ class ChatPageState extends State<ChatPage> {
physics: BouncingScrollPhysics(), physics: BouncingScrollPhysics(),
itemCount: chatList.length, itemCount: chatList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
// logger.w(chatList[index].conversation.conversationGroupList);
// logger.w(chatList[index].isCustomAdmin);
final isNoFriend = chatList[index].conversation.conversationGroupList?.contains(ConversationType.noFriend.name) ?? false; final isNoFriend = chatList[index].conversation.conversationGroupList?.contains(ConversationType.noFriend.name) ?? false;
final isAdmin = chatList[index].isCustomAdmin != null && chatList[index].isCustomAdmin != '0'; final isAdmin =
chatList[index].isCustomAdmin != null && (chatList[index].isCustomAdmin?.isNotEmpty ?? false) && chatList[index].isCustomAdmin != '0';
// logger.e(chatList[index].isCustomAdmin); // logger.e(chatList[index].isCustomAdmin);
return Ink( return Ink(
@ -361,9 +379,7 @@ class ChatPageState extends State<ChatPage> {
), ),
const SizedBox(height: 2.0), const SizedBox(height: 2.0),
Text( Text(
chatList[index].conversation.lastMessage != null chatList[index].conversation.lastMessage != null ? parseMessageSummary(chatList[index].conversation.lastMessage!) : '',
? parseMessageSummary(chatList[index].conversation.lastMessage!)
: '',
style: const TextStyle(color: Colors.grey, fontSize: 13.0), style: const TextStyle(color: Colors.grey, fontSize: 13.0),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
@ -382,8 +398,7 @@ class ChatPageState extends State<ChatPage> {
// DateTime.fromMillisecondsSinceEpoch( // DateTime.fromMillisecondsSinceEpoch(
// (chatList[index].conversation.lastMessage!.timestamp ?? 0) * 1000, // (chatList[index].conversation.lastMessage!.timestamp ?? 0) * 1000,
// ).toLocal().toString().substring(0, 16), // // ).toLocal().toString().substring(0, 16), //
Utils.formatTime( Utils.formatTime(chatList[index].conversation.lastMessage!.timestamp ?? DateTime.now().millisecondsSinceEpoch ~/ 1000),
chatList[index].conversation.lastMessage!.timestamp ?? DateTime.now().millisecondsSinceEpoch ~/ 1000),
style: const TextStyle(color: Colors.grey, fontSize: 12.0), style: const TextStyle(color: Colors.grey, fontSize: 12.0),
), ),
), ),
@ -431,7 +446,8 @@ class ChatPageState extends State<ChatPage> {
); );
}, },
); );
})), },
),
), ),
], ],
), ),

View File

@ -0,0 +1,304 @@
///
library;
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:loopin/IM/controller/chat_controller.dart';
import 'package:loopin/IM/im_service.dart';
import 'package:loopin/behavior/custom_scroll_behavior.dart';
import 'package:loopin/components/network_or_asset_image.dart';
import 'package:loopin/styles/index.dart';
import 'package:loopin/utils/index.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_follow_type_check_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
class UserWithFollow {
final V2TimUserFullInfo userInfo;
int followType;
UserWithFollow({
required this.userInfo,
this.followType = 0,
});
}
class Newfoucs extends StatefulWidget {
const Newfoucs({super.key});
@override
State<Newfoucs> createState() => NewfoucsState();
}
class NewfoucsState extends State<Newfoucs> with SingleTickerProviderStateMixin {
bool isLoading = false; //
bool hasMore = true; //
String page = '';
List<UserWithFollow> dataList = <UserWithFollow>[];
///-------------------
@override
void initState() {
super.initState();
getData();
}
//
Future<void> getData() async {
/// 0
/// 1
/// 2
/// 3
final res = await ImService.instance.getMyFollowingList(
nextCursor: page,
);
if (res.success && res.data != null) {
logger.i('获取成功:${res.data!.nextCursor}');
final userInfoList = res.data!.userFullInfoList ?? [];
//
List<UserWithFollow> wrappedList = userInfoList.map((u) {
return UserWithFollow(userInfo: u);
}).toList();
// id
final userIDList = userInfoList.map((item) => item.userID).whereType<String>().toList();
if (userIDList.isNotEmpty) {
final shiRes = await ImService.instance.checkFollowType(userIDList: userIDList);
if (shiRes.success && shiRes.data != null) {
final shipResData = shiRes.data!;
for (final uwf in wrappedList) {
final userID = uwf.userInfo.userID;
if (userID != null) {
//
final match = shipResData.firstWhere(
(e) => e.userID == userID,
orElse: () => V2TimFollowTypeCheckResult(userID: ''),
);
if (match.userID?.isNotEmpty ?? false) {
uwf.followType = match.followType ?? 0;
}
}
}
}
}
final isFinished = res.data!.nextCursor == null || res.data!.nextCursor!.isEmpty;
if (isFinished) {
setState(() {
hasMore = false;
});
//
page = '';
} else {
page = res.data!.nextCursor ?? '';
}
logger.i('获取数据成功:$userInfoList');
setState(() {
dataList.addAll(wrappedList);
});
} else {
logger.e('获取数据失败:${res.desc}');
}
}
//
Future<void> handleRefresh() async {
dataList.clear();
page = '';
getData();
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[50],
appBar: AppBar(
centerTitle: true,
forceMaterialTransparency: true,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1.0),
child: Container(
color: Colors.grey[300],
height: 1.0,
),
),
title: const Text(
'关注',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
actions: [],
),
body: ScrollConfiguration(
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
child: Column(
children: [
Expanded(
child: EasyRefresh.builder(
callLoadOverOffset: 20, //
callRefreshOverOffset: 20, //
header: ClassicHeader(
dragText: '下拉刷新',
armedText: '释放刷新',
readyText: '加载中...',
processingText: '加载中...',
processedText: '加载完成',
failedText: '加载失败,请重试',
messageText: '最后更新于 %T',
),
footer: ClassicFooter(
dragText: '加载更多',
armedText: '释放加载',
readyText: '加载中...',
processingText: '加载中...',
processedText: hasMore ? '加载完成' : '没有更多了~',
failedText: '加载失败,请重试',
messageText: '最后更新于 %T',
),
onRefresh: () async {
await handleRefresh();
},
onLoad: () async {
if (hasMore) {
await getData();
}
},
childBuilder: (context, physics) {
return ListView.builder(
physics: physics,
itemCount: dataList.length,
itemBuilder: (context, index) {
final item = dataList[index];
return Ink(
key: ValueKey(item.userInfo.userID),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
// + +
Expanded(
child: InkWell(
onTap: () {
Get.toNamed(
'/vloger',
arguments: item.userInfo.userID,
);
},
child: Row(
children: [
ClipOval(
child: NetworkOrAssetImage(
imageUrl: item.userInfo.faceUrl,
width: 50,
height: 50,
),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
item.userInfo.nickName?.isNotEmpty == true ? item.userInfo.nickName! : '未知昵称',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
),
),
if (item.userInfo.selfSignature?.isNotEmpty ?? false) ...[
const SizedBox(height: 2.0),
Text(
item.userInfo.selfSignature!,
style: const TextStyle(
color: Colors.grey,
fontSize: 13.0,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
],
),
),
],
),
),
),
SizedBox(width: 10),
//
TextButton(
style: TextButton.styleFrom(
backgroundColor: item.followType == 3 ? Colors.grey : FStyle.primaryColor,
minimumSize: const Size(70, 32),
padding: const EdgeInsets.symmetric(horizontal: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
),
onPressed: () async {
final ctl = Get.find<ChatController>();
final checkRes = await ImService.instance.checkFollowType(userIDList: [item.userInfo.userID!]);
int realFollowType = 0;
if (checkRes.success && checkRes.data != null) {
realFollowType = checkRes.data!.first.followType ?? 0;
if ([1, 3].contains(realFollowType)) {
//
final unRes = await ImService.instance.unfollowUser(userIDList: [item.userInfo.userID!]);
if (unRes.success) {
setState(() {
item.followType = 2;
});
ctl.mergeNoFriend(conversationID: 'c2c_${item.userInfo.userID!}');
}
} else {
//
final res = await ImService.instance.followUser(userIDList: [item.userInfo.userID!]);
if (res.success) {
setState(() {
item.followType = realFollowType == 0
? 1
: realFollowType == 2
? 3
: 0;
});
final chatRes = await ImService.instance.followUser(userIDList: [item.userInfo.userID!]);
if (chatRes.success) {
final res = await ImService.instance.getConversation(conversationID: 'c2c_${item.userInfo.userID}');
if (res.success) {
V2TimConversation conversation = res.data;
if (conversation.conversationGroupList?.isNotEmpty ?? false) {
await ImService.instance.deleteConversationsFromGroup(
groupName: conversation.conversationGroupList!.first!,
conversationIDList: [conversation.conversationID],
);
ctl.updateNoFriendMenu();
}
}
}
}
}
}
},
child: Text(
Utils.getTipText(item.followType),
style: const TextStyle(color: Colors.white, fontSize: 14),
),
),
],
),
),
);
},
);
},
),
),
],
),
),
);
}
}

View File

@ -111,7 +111,7 @@ class NofriendState extends State<Nofriend> with SingleTickerProviderStateMixin
), ),
title: Text( title: Text(
'陌生人消息', '陌生人消息',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
), ),
actions: [], actions: [],
), ),
@ -137,6 +137,8 @@ class NofriendState extends State<Nofriend> with SingleTickerProviderStateMixin
final isNoFriend = conv.conversationGroupList?.contains(ConversationType.noFriend.name) ?? false; final isNoFriend = conv.conversationGroupList?.contains(ConversationType.noFriend.name) ?? false;
return Ink( return Ink(
key: ValueKey(conv.conversationID),
// color: conv['topMost'] == null ? Colors.white : Colors.grey[100], // // color: conv['topMost'] == null ? Colors.white : Colors.grey[100], //
child: InkWell( child: InkWell(
splashColor: Colors.grey[200], splashColor: Colors.grey[200],

View File

@ -20,11 +20,19 @@ class NotifyNoFriendController extends GetxController {
if (index != -1) { if (index != -1) {
convList[index] = conversation; convList[index] = conversation;
convList.refresh(); convList.refresh();
// convList.value = List.from(convList)..[index] = conversation;
} else { } else {
// id查询会话检测是否存在 logger.e('当前会话数据未拉取到,执行插入逻辑');
logger.e('会话不存在,更新会话失败'); // insert进去 convlist做去重处理
// insert进去 convlist做去重处理 convList.insert(0, conversation);
convList.refresh();
}
}
void del({required V2TimConversation conversation}) {
final index = convList.indexWhere((c) => c.conversationID == conversation.conversationID);
if (index != -1) {
convList.removeAt(index);
convList.refresh();
} }
} }

View File

@ -123,7 +123,7 @@ class _GoodsState extends State<Goods> {
"price": shopObj['price'], "price": shopObj['price'],
"title": shopObj['describe'], "title": shopObj['describe'],
"url": shopObj['pic'], "url": shopObj['pic'],
"sell": Utils().graceNumber(int.parse(shopObj['sales'] ?? '0')), "sell": Utils.graceNumber(int.parse(shopObj['sales'] ?? '0')),
}); });
final res = await IMMessage().createCustomMessage( final res = await IMMessage().createCustomMessage(
data: makeJson, data: makeJson,
@ -389,7 +389,7 @@ class _GoodsState extends State<Goods> {
), ),
Text( Text(
// '已售${Utils().graceNumber(shopObj['sales'] ?? 0)}', // '已售${Utils().graceNumber(shopObj['sales'] ?? 0)}',
'已售${Utils().graceNumber(int.tryParse(shopObj['sales']?.toString() ?? '0') ?? 0)}', '已售${Utils.graceNumber(int.tryParse(shopObj['sales']?.toString() ?? '0') ?? 0)}',
style: TextStyle(color: Colors.white, fontSize: 12.0), style: TextStyle(color: Colors.white, fontSize: 12.0),
), ),
], ],

View File

@ -0,0 +1,219 @@
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
class StartGroupChatPage extends StatefulWidget {
const StartGroupChatPage({super.key});
@override
State<StartGroupChatPage> createState() => _StartGroupChatPageState();
}
class _StartGroupChatPageState extends State<StartGroupChatPage> {
final TextEditingController _searchController = TextEditingController();
List<Map<String, dynamic>> dataList = [];
List<Map<String, dynamic>> filteredList = [];
Set<String> selectedIds = {}; // id
int page = 1;
bool hasMore = true;
@override
void initState() {
super.initState();
_loadData(reset: true);
}
@override
void dispose() {
_searchController.dispose();
super.dispose();
}
Future<void> _loadData({bool reset = false}) async {
if (reset) {
page = 1;
hasMore = true;
dataList.clear();
}
//
await Future.delayed(const Duration(seconds: 1));
List<Map<String, dynamic>> newItems = List.generate(
10,
(index) => {"nickName": "用户 ${(page - 1) * 10 + index + 1}", "id": "${page}_$index"},
);
if (newItems.isEmpty) {
hasMore = false;
} else {
dataList.addAll(newItems);
page++;
}
_applySearch(_searchController.text);
}
void _applySearch(String query) {
setState(() {
if (query.isEmpty) {
filteredList = List.from(dataList);
} else {
filteredList = dataList.where((item) => item["nickName"].toString().contains(query.trim())).toList();
}
});
}
void _toggleSelection(String id) {
setState(() {
if (selectedIds.contains(id)) {
selectedIds.remove(id);
} else {
selectedIds.add(id);
}
});
}
Widget _buildCard({
required IconData icon,
required String title,
required VoidCallback onTap,
}) {
return Card(
color: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: ListTile(
leading: Icon(icon, color: Colors.black),
title: Text(title, style: const TextStyle(fontSize: 16)),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
onTap: onTap,
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
// backgroundColor: Colors.white,
centerTitle: true,
forceMaterialTransparency: true,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1.0),
child: Container(
color: Colors.grey[300],
height: 1.0,
),
),
title: const Text(
"创建群聊",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
body: Column(
children: [
// -
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
_buildCard(
icon: Icons.public,
title: "创建公开群",
onTap: () {
print("跳转到 创建公开群");
},
),
_buildCard(
icon: Icons.group,
title: "创建好友群",
onTap: () {
print("跳转到 创建好友群");
},
),
],
),
),
const Divider(),
// - +
Expanded(
child: Column(
children: [
//
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _searchController,
decoration: InputDecoration(
hintText: "搜索用户昵称",
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
onChanged: _applySearch,
),
),
//
Expanded(
child: EasyRefresh(
onRefresh: () async => _loadData(reset: true),
onLoad: () async {
if (hasMore) await _loadData();
},
child: ListView.builder(
itemCount: filteredList.length,
itemBuilder: (context, index) {
final item = filteredList[index];
final id = item["id"] as String;
final isSelected = selectedIds.contains(id);
return ListTile(
leading: CircleAvatar(
child: Text(item["nickName"].substring(0, 1)),
),
title: Text(item["nickName"]),
trailing: Checkbox(
value: isSelected,
onChanged: (_) => _toggleSelection(id),
),
onTap: () => _toggleSelection(id),
);
},
),
),
),
],
),
),
],
),
//
bottomNavigationBar: SafeArea(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: ElevatedButton(
onPressed: selectedIds.isEmpty
? null
: () {
print("选择了用户:$selectedIds");
},
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(50),
backgroundColor: selectedIds.isEmpty ? Colors.grey : Colors.blue,
),
child: const Text(
"发起聊天",
style: TextStyle(fontSize: 16),
),
),
),
),
);
}
}

View File

@ -88,7 +88,7 @@ class _IndexPageState extends State<IndexPage> with SingleTickerProviderStateMix
]), ]),
), ),
Text( Text(
'已售${Utils().graceNumber(int.parse(item['sales'] ?? '0'))}', '已售${Utils.graceNumber(int.parse(item['sales'] ?? '0'))}',
style: TextStyle(color: Colors.grey, fontSize: 10.0), style: TextStyle(color: Colors.grey, fontSize: 10.0),
), ),
], ],
@ -254,8 +254,9 @@ class _IndexPageState extends State<IndexPage> with SingleTickerProviderStateMix
header: ClassicHeader( header: ClassicHeader(
dragText: '下拉刷新', dragText: '下拉刷新',
armedText: '释放刷新', armedText: '释放刷新',
readyText: '刷新中...', readyText: '加载中...',
processingText: '刷新完成', processingText: '加载中...',
processedText: '加载完成',
messageText: '最后更新于 %T', messageText: '最后更新于 %T',
), ),
child: CustomScrollView( child: CustomScrollView(

304
lib/pages/my/fans.dart Normal file
View File

@ -0,0 +1,304 @@
///
library;
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:loopin/IM/controller/chat_controller.dart';
import 'package:loopin/IM/im_service.dart';
import 'package:loopin/behavior/custom_scroll_behavior.dart';
import 'package:loopin/components/network_or_asset_image.dart';
import 'package:loopin/styles/index.dart';
import 'package:loopin/utils/index.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_follow_type_check_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
class UserWithFollow {
final V2TimUserFullInfo userInfo;
int followType;
UserWithFollow({
required this.userInfo,
this.followType = 0,
});
}
class Fans extends StatefulWidget {
const Fans({super.key});
@override
State<Fans> createState() => FansState();
}
class FansState extends State<Fans> with SingleTickerProviderStateMixin {
bool isLoading = false; //
bool hasMore = true; //
String page = '';
List<UserWithFollow> dataList = <UserWithFollow>[];
///-------------------
@override
void initState() {
super.initState();
getData();
}
//
Future<void> getData() async {
/// 0
/// 1
/// 2
/// 3
final res = await ImService.instance.getMyFollowersList(
nextCursor: page,
);
if (res.success && res.data != null) {
logger.i('获取成功:${res.data!.nextCursor}');
final userInfoList = res.data!.userFullInfoList ?? [];
//
List<UserWithFollow> wrappedList = userInfoList.map((u) {
return UserWithFollow(userInfo: u);
}).toList();
// id
final userIDList = userInfoList.map((item) => item.userID).whereType<String>().toList();
if (userIDList.isNotEmpty) {
final shiRes = await ImService.instance.checkFollowType(userIDList: userIDList);
if (shiRes.success && shiRes.data != null) {
final shipResData = shiRes.data!;
for (final uwf in wrappedList) {
final userID = uwf.userInfo.userID;
if (userID != null) {
//
final match = shipResData.firstWhere(
(e) => e.userID == userID,
orElse: () => V2TimFollowTypeCheckResult(userID: ''),
);
if (match.userID?.isNotEmpty ?? false) {
uwf.followType = match.followType ?? 0;
}
}
}
}
}
final isFinished = res.data!.nextCursor == null || res.data!.nextCursor!.isEmpty;
if (isFinished) {
setState(() {
hasMore = false;
});
//
page = '';
} else {
page = res.data!.nextCursor ?? '';
}
logger.i('获取数据成功:$userInfoList');
setState(() {
dataList.addAll(wrappedList);
});
} else {
logger.e('获取数据失败:${res.desc}');
}
}
//
Future<void> handleRefresh() async {
dataList.clear();
page = '';
getData();
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[50],
appBar: AppBar(
centerTitle: true,
forceMaterialTransparency: true,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1.0),
child: Container(
color: Colors.grey[300],
height: 1.0,
),
),
title: const Text(
'粉丝',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
actions: [],
),
body: ScrollConfiguration(
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
child: Column(
children: [
Expanded(
child: EasyRefresh.builder(
callLoadOverOffset: 20, //
callRefreshOverOffset: 20, //
header: ClassicHeader(
dragText: '下拉刷新',
armedText: '释放刷新',
readyText: '加载中...',
processingText: '加载中...',
processedText: '加载完成',
failedText: '加载失败,请重试',
messageText: '最后更新于 %T',
),
footer: ClassicFooter(
dragText: '加载更多',
armedText: '释放加载',
readyText: '加载中...',
processingText: '加载中...',
processedText: hasMore ? '加载完成' : '没有更多了~',
failedText: '加载失败,请重试',
messageText: '最后更新于 %T',
),
onRefresh: () async {
await handleRefresh();
},
onLoad: () async {
if (hasMore) {
await getData();
}
},
childBuilder: (context, physics) {
return ListView.builder(
physics: physics,
itemCount: dataList.length,
itemBuilder: (context, index) {
final item = dataList[index];
return Ink(
key: ValueKey(item.userInfo.userID),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
// + +
Expanded(
child: InkWell(
onTap: () {
Get.toNamed(
'/vloger',
arguments: item.userInfo.userID,
);
},
child: Row(
children: [
ClipOval(
child: NetworkOrAssetImage(
imageUrl: item.userInfo.faceUrl,
width: 50,
height: 50,
),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
item.userInfo.nickName?.isNotEmpty == true ? item.userInfo.nickName! : '未知昵称',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
),
),
if (item.userInfo.selfSignature?.isNotEmpty ?? false) ...[
const SizedBox(height: 2.0),
Text(
item.userInfo.selfSignature!,
style: const TextStyle(
color: Colors.grey,
fontSize: 13.0,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
],
),
),
],
),
),
),
SizedBox(width: 10),
//
TextButton(
style: TextButton.styleFrom(
backgroundColor: item.followType != 3 ? FStyle.primaryColor : Colors.grey,
minimumSize: const Size(70, 32),
padding: const EdgeInsets.symmetric(horizontal: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
),
onPressed: () async {
final ctl = Get.find<ChatController>();
final checkRes = await ImService.instance.checkFollowType(userIDList: [item.userInfo.userID!]);
int realFollowType = 0;
if (checkRes.success && checkRes.data != null) {
realFollowType = checkRes.data!.first.followType ?? 0;
if ([1, 3].contains(realFollowType)) {
//
final unRes = await ImService.instance.unfollowUser(userIDList: [item.userInfo.userID!]);
if (unRes.success) {
setState(() {
item.followType = 2;
});
ctl.mergeNoFriend(conversationID: 'c2c_${item.userInfo.userID!}');
}
} else {
//
final res = await ImService.instance.followUser(userIDList: [item.userInfo.userID!]);
if (res.success) {
setState(() {
item.followType = realFollowType == 0
? 1
: realFollowType == 2
? 3
: 0;
});
final chatRes = await ImService.instance.followUser(userIDList: [item.userInfo.userID!]);
if (chatRes.success) {
final res = await ImService.instance.getConversation(conversationID: 'c2c_${item.userInfo.userID}');
if (res.success) {
V2TimConversation conversation = res.data;
if (conversation.conversationGroupList?.isNotEmpty ?? false) {
await ImService.instance.deleteConversationsFromGroup(
groupName: conversation.conversationGroupList!.first!,
conversationIDList: [conversation.conversationID],
);
ctl.updateNoFriendMenu();
}
}
}
}
}
}
},
child: Text(
Utils.getTipText(item.followType),
style: const TextStyle(color: Colors.white, fontSize: 14),
),
),
],
),
),
);
},
);
},
),
),
],
),
),
);
}
}

304
lib/pages/my/flowing.dart Normal file
View File

@ -0,0 +1,304 @@
///
library;
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:loopin/IM/controller/chat_controller.dart';
import 'package:loopin/IM/im_service.dart';
import 'package:loopin/behavior/custom_scroll_behavior.dart';
import 'package:loopin/components/network_or_asset_image.dart';
import 'package:loopin/styles/index.dart';
import 'package:loopin/utils/index.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_follow_type_check_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
class UserWithFollow {
final V2TimUserFullInfo userInfo;
int followType;
UserWithFollow({
required this.userInfo,
this.followType = 0,
});
}
class Flowing extends StatefulWidget {
const Flowing({super.key});
@override
State<Flowing> createState() => FlowingState();
}
class FlowingState extends State<Flowing> with SingleTickerProviderStateMixin {
bool isLoading = false; //
bool hasMore = true; //
String page = '';
List<UserWithFollow> dataList = <UserWithFollow>[];
///-------------------
@override
void initState() {
super.initState();
getData();
}
//
Future<void> getData() async {
/// 0
/// 1
/// 2
/// 3
final res = await ImService.instance.getMyFollowingList(
nextCursor: page,
);
if (res.success && res.data != null) {
logger.i('获取成功:${res.data!.nextCursor}');
final userInfoList = res.data!.userFullInfoList ?? [];
//
List<UserWithFollow> wrappedList = userInfoList.map((u) {
return UserWithFollow(userInfo: u);
}).toList();
// id
final userIDList = userInfoList.map((item) => item.userID).whereType<String>().toList();
if (userIDList.isNotEmpty) {
final shiRes = await ImService.instance.checkFollowType(userIDList: userIDList);
if (shiRes.success && shiRes.data != null) {
final shipResData = shiRes.data!;
for (final uwf in wrappedList) {
final userID = uwf.userInfo.userID;
if (userID != null) {
//
final match = shipResData.firstWhere(
(e) => e.userID == userID,
orElse: () => V2TimFollowTypeCheckResult(userID: ''),
);
if (match.userID?.isNotEmpty ?? false) {
uwf.followType = match.followType ?? 0;
}
}
}
}
}
final isFinished = res.data!.nextCursor == null || res.data!.nextCursor!.isEmpty;
if (isFinished) {
setState(() {
hasMore = false;
});
//
page = '';
} else {
page = res.data!.nextCursor ?? '';
}
logger.i('获取数据成功:$userInfoList');
setState(() {
dataList.addAll(wrappedList);
});
} else {
logger.e('获取数据失败:${res.desc}');
}
}
//
Future<void> handleRefresh() async {
dataList.clear();
page = '';
getData();
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[50],
appBar: AppBar(
centerTitle: true,
forceMaterialTransparency: true,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1.0),
child: Container(
color: Colors.grey[300],
height: 1.0,
),
),
title: const Text(
'关注',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
actions: [],
),
body: ScrollConfiguration(
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
child: Column(
children: [
Expanded(
child: EasyRefresh.builder(
callLoadOverOffset: 20, //
callRefreshOverOffset: 20, //
header: ClassicHeader(
dragText: '下拉刷新',
armedText: '释放刷新',
readyText: '加载中...',
processingText: '加载中...',
processedText: '加载完成',
failedText: '加载失败,请重试',
messageText: '最后更新于 %T',
),
footer: ClassicFooter(
dragText: '加载更多',
armedText: '释放加载',
readyText: '加载中...',
processingText: '加载中...',
processedText: hasMore ? '加载完成' : '没有更多了~',
failedText: '加载失败,请重试',
messageText: '最后更新于 %T',
),
onRefresh: () async {
await handleRefresh();
},
onLoad: () async {
if (hasMore) {
await getData();
}
},
childBuilder: (context, physics) {
return ListView.builder(
physics: physics,
itemCount: dataList.length,
itemBuilder: (context, index) {
final item = dataList[index];
return Ink(
key: ValueKey(item.userInfo.userID),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
// + +
Expanded(
child: InkWell(
onTap: () {
Get.toNamed(
'/vloger',
arguments: item.userInfo.userID,
);
},
child: Row(
children: [
ClipOval(
child: NetworkOrAssetImage(
imageUrl: item.userInfo.faceUrl,
width: 50,
height: 50,
),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
item.userInfo.nickName?.isNotEmpty == true ? item.userInfo.nickName! : '未知昵称',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
),
),
if (item.userInfo.selfSignature?.isNotEmpty ?? false) ...[
const SizedBox(height: 2.0),
Text(
item.userInfo.selfSignature!,
style: const TextStyle(
color: Colors.grey,
fontSize: 13.0,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
],
),
),
],
),
),
),
SizedBox(width: 10),
//
TextButton(
style: TextButton.styleFrom(
backgroundColor: item.followType == 3 ? Colors.grey : FStyle.primaryColor,
minimumSize: const Size(70, 32),
padding: const EdgeInsets.symmetric(horizontal: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
),
onPressed: () async {
final ctl = Get.find<ChatController>();
final checkRes = await ImService.instance.checkFollowType(userIDList: [item.userInfo.userID!]);
int realFollowType = 0;
if (checkRes.success && checkRes.data != null) {
realFollowType = checkRes.data!.first.followType ?? 0;
if ([1, 3].contains(realFollowType)) {
//
final unRes = await ImService.instance.unfollowUser(userIDList: [item.userInfo.userID!]);
if (unRes.success) {
setState(() {
item.followType = 2;
});
ctl.mergeNoFriend(conversationID: 'c2c_${item.userInfo.userID!}');
}
} else {
//
final res = await ImService.instance.followUser(userIDList: [item.userInfo.userID!]);
if (res.success) {
setState(() {
item.followType = realFollowType == 0
? 1
: realFollowType == 2
? 3
: 0;
});
final chatRes = await ImService.instance.followUser(userIDList: [item.userInfo.userID!]);
if (chatRes.success) {
final res = await ImService.instance.getConversation(conversationID: 'c2c_${item.userInfo.userID}');
if (res.success) {
V2TimConversation conversation = res.data;
if (conversation.conversationGroupList?.isNotEmpty ?? false) {
await ImService.instance.deleteConversationsFromGroup(
groupName: conversation.conversationGroupList!.first!,
conversationIDList: [conversation.conversationID],
);
ctl.updateNoFriendMenu();
}
}
}
}
}
}
},
child: Text(
Utils.getTipText(item.followType),
style: const TextStyle(color: Colors.white, fontSize: 14),
),
),
],
),
),
);
},
);
},
),
),
],
),
),
);
}
}

View File

@ -369,7 +369,10 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
child: Column( child: Column(
children: [ children: [
const SizedBox(height: 10),
Obx(() => _buildStatsCard()), Obx(() => _buildStatsCard()),
const SizedBox(height: 10),
Obx(() => _buildInfoDesc(context)),
const SizedBox(height: 10.0), const SizedBox(height: 10.0),
_buildOrderCard(context), _buildOrderCard(context),
const SizedBox(height: 10.0), const SizedBox(height: 10.0),
@ -463,6 +466,37 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
); );
} }
Widget _buildInfoDesc(BuildContext context) {
final tx = imUserInfoController?.signature;
if (tx == null || tx.isEmpty) {
return const SizedBox.shrink();
}
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15.0),
boxShadow: [BoxShadow(color: Colors.black.withAlpha(10), offset: const Offset(0.0, 1.0), blurRadius: 2.0, spreadRadius: 0.0)],
),
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(12),
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'${imUserInfoController!.signature}',
style: const TextStyle(fontSize: 16),
),
),
],
));
}
Widget _buildGridTab(int tabIndex) { Widget _buildGridTab(int tabIndex) {
final listToShow = tabIndex == 0 ? items : favoriteItems; final listToShow = tabIndex == 0 ? items : favoriteItems;
PageParams params = tabIndex == 0 ? itemsParams : favoriteParams; PageParams params = tabIndex == 0 ? itemsParams : favoriteParams;
@ -724,104 +758,6 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
); );
} }
// Widget _buildFlexibleSpace() {
// return LayoutBuilder(
// builder: (BuildContext context, BoxConstraints constraints) {
// final double maxHeight = 180.0;
// final double minHeight = 120.0;
// final double currentHeight = constraints.maxHeight;
// double ratio = (currentHeight - minHeight) / (maxHeight - minHeight);
// ratio = ratio.clamp(0.0, 1.0);
// final faceUrl = imUserInfoController?.faceUrl.value ?? '';
// return Obx(
// () => Stack(
// fit: StackFit.expand,
// children: [
// Positioned.fill(
// child: Opacity(
// opacity: 1.0,
// child: NetworkOrAssetImage(imageUrl: imUserInfoController?.customInfo['coverBg'] ?? '', placeholderAsset: 'assets/images/bk.jpg'),
// ),
// ),
// Positioned(
// left: 15.0,
// bottom: 0,
// right: 15.0,
// child: Container(
// padding: const EdgeInsets.symmetric(vertical: 10.0),
// child: Row(
// crossAxisAlignment: CrossAxisAlignment.end,
// children: <Widget>[
// ClipOval(
// child: ClipRRect(
// borderRadius: BorderRadius.circular(30),
// child: NetworkOrAssetImage(
// imageUrl: faceUrl,
// width: 80,
// height: 80,
// ),
// ),
// ),
// const SizedBox(width: 15.0),
// Expanded(
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Row(
// children: [
// Container(
// padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
// decoration: BoxDecoration(color: Colors.black.withAlpha((0.3 * 255).round()), borderRadius: BorderRadius.circular(20.0)),
// child: Obx(
// () => Text(
// imUserInfoController?.nickname.value.isNotEmpty ?? false == true
// ? imUserInfoController?.nickname.value ?? imUserInfoController!.nickname.value
// : '昵称',
// style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold, fontFamily: 'Arial', color: Colors.white),
// ),
// )),
// const SizedBox(width: 8.0),
// InkWell(
// onTap: () {
// qrcodeAlertDialog(context);
// },
// child: Container(
// padding: const EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0),
// decoration: BoxDecoration(color: Colors.black.withAlpha((0.3 * 255).round()), borderRadius: BorderRadius.circular(20.0)),
// child: const Icon(Icons.qr_code_outlined, size: 18.0, color: Colors.white),
// ),
// ),
// ],
// ),
// const SizedBox(height: 8.0),
// Container(
// padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
// decoration: BoxDecoration(color: Colors.black.withAlpha((0.3 * 255).round()), borderRadius: BorderRadius.circular(20.0)),
// child: InkWell(
// onTap: () {
// logger.i('点击id');
// Clipboard.setData(ClipboardData(text: imUserInfoController?.userID.value ?? ''));
// MyDialog.toast('ID已复制',
// icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.green.withAlpha(200)));
// },
// child: Text('ID:${imUserInfoController?.userID.value ?? ''}', style: TextStyle(fontSize: 12.0, color: Colors.white)),
// ),
// ),
// ],
// ),
// ),
// ],
// ),
// ),
// ),
// ],
// ),
// );
// },
// );
// }
Widget _buildStatsCard() { Widget _buildStatsCard() {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
@ -836,21 +772,46 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
Column(children: [Text('9999', style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)), SizedBox(height: 3.0), Text('获赞')]), Column(children: [Text('9999', style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)), SizedBox(height: 3.0), Text('获赞')]),
Column(children: [ GestureDetector(
onTap: () async {
//
await Get.toNamed('/eachFlow');
refreshData();
},
child: Column(children: [
Text('${followInfo.value?.mutualFollowersCount ?? 0}', style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)), Text('${followInfo.value?.mutualFollowersCount ?? 0}', style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)),
SizedBox(height: 3.0), SizedBox(height: 3.0),
Text('互关') Text('互关')
]), ]),
Column(children: [ ),
GestureDetector(
onTap: () async {
//
await Get.toNamed('/flow');
refreshData();
},
child: Column(children: [
Text('${followInfo.value?.followingCount ?? 0}', style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)), Text('${followInfo.value?.followingCount ?? 0}', style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)),
SizedBox(height: 3.0), SizedBox(height: 3.0),
Text('关注') Text('关注')
]), ]),
Column(children: [ ),
Text('${followInfo.value?.followersCount ?? 0}', style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)), GestureDetector(
SizedBox(height: 3.0), onTap: () async {
Text('粉丝') await Get.toNamed('/fans');
]), refreshData();
},
child: Column(
children: [
Text(
'${followInfo.value?.followersCount ?? 0}',
style: const TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),
),
const SizedBox(height: 3.0),
const Text('粉丝'),
],
),
)
], ],
)), )),
); );

View File

@ -0,0 +1,304 @@
///
library;
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:loopin/IM/controller/chat_controller.dart';
import 'package:loopin/IM/im_service.dart';
import 'package:loopin/behavior/custom_scroll_behavior.dart';
import 'package:loopin/components/network_or_asset_image.dart';
import 'package:loopin/styles/index.dart';
import 'package:loopin/utils/index.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_follow_type_check_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
class UserWithFollow {
final V2TimUserFullInfo userInfo;
int followType;
UserWithFollow({
required this.userInfo,
this.followType = 0,
});
}
class MutualFollowers extends StatefulWidget {
const MutualFollowers({super.key});
@override
State<MutualFollowers> createState() => MutualFollowersState();
}
class MutualFollowersState extends State<MutualFollowers> with SingleTickerProviderStateMixin {
bool isLoading = false; //
bool hasMore = true; //
String page = '';
List<UserWithFollow> dataList = <UserWithFollow>[];
///-------------------
@override
void initState() {
super.initState();
getData();
}
//
Future<void> getData() async {
/// 0
/// 1
/// 2
/// 3
final res = await ImService.instance.getMutualFollowersList(
nextCursor: page,
);
if (res.success && res.data != null) {
logger.i('获取成功:${res.data!.nextCursor}');
final userInfoList = res.data!.userFullInfoList ?? [];
//
List<UserWithFollow> wrappedList = userInfoList.map((u) {
return UserWithFollow(userInfo: u);
}).toList();
// id
final userIDList = userInfoList.map((item) => item.userID).whereType<String>().toList();
if (userIDList.isNotEmpty) {
final shiRes = await ImService.instance.checkFollowType(userIDList: userIDList);
if (shiRes.success && shiRes.data != null) {
final shipResData = shiRes.data!;
for (final uwf in wrappedList) {
final userID = uwf.userInfo.userID;
if (userID != null) {
//
final match = shipResData.firstWhere(
(e) => e.userID == userID,
orElse: () => V2TimFollowTypeCheckResult(userID: ''),
);
if (match.userID?.isNotEmpty ?? false) {
uwf.followType = match.followType ?? 0;
}
}
}
}
}
final isFinished = res.data!.nextCursor == null || res.data!.nextCursor!.isEmpty;
if (isFinished) {
setState(() {
hasMore = false;
});
//
page = '';
} else {
page = res.data!.nextCursor ?? '';
}
logger.i('获取数据成功:$userInfoList');
setState(() {
dataList.addAll(wrappedList);
});
} else {
logger.e('获取数据失败:${res.desc}');
}
}
//
Future<void> handleRefresh() async {
dataList.clear();
page = '';
getData();
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[50],
appBar: AppBar(
centerTitle: true,
forceMaterialTransparency: true,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1.0),
child: Container(
color: Colors.grey[300],
height: 1.0,
),
),
title: const Text(
'互关',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
actions: [],
),
body: ScrollConfiguration(
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
child: Column(
children: [
Expanded(
child: EasyRefresh.builder(
callLoadOverOffset: 20, //
callRefreshOverOffset: 20, //
header: ClassicHeader(
dragText: '下拉刷新',
armedText: '释放刷新',
readyText: '加载中...',
processingText: '加载中...',
processedText: '加载完成',
failedText: '加载失败,请重试',
messageText: '最后更新于 %T',
),
footer: ClassicFooter(
dragText: '加载更多',
armedText: '释放加载',
readyText: '加载中...',
processingText: '加载中...',
processedText: hasMore ? '加载完成' : '没有更多了~',
failedText: '加载失败,请重试',
messageText: '最后更新于 %T',
),
onRefresh: () async {
await handleRefresh();
},
onLoad: () async {
if (hasMore) {
await getData();
}
},
childBuilder: (context, physics) {
return ListView.builder(
physics: physics,
itemCount: dataList.length,
itemBuilder: (context, index) {
final item = dataList[index];
return Ink(
key: ValueKey(item.userInfo.userID),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
// + +
Expanded(
child: InkWell(
onTap: () {
Get.toNamed(
'/vloger',
arguments: item.userInfo.userID,
);
},
child: Row(
children: [
ClipOval(
child: NetworkOrAssetImage(
imageUrl: item.userInfo.faceUrl,
width: 50,
height: 50,
),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
item.userInfo.nickName?.isNotEmpty == true ? item.userInfo.nickName! : '未知昵称',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
),
),
if (item.userInfo.selfSignature?.isNotEmpty ?? false) ...[
const SizedBox(height: 2.0),
Text(
item.userInfo.selfSignature!,
style: const TextStyle(
color: Colors.grey,
fontSize: 13.0,
),
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
],
],
),
),
],
),
),
),
SizedBox(width: 10),
//
TextButton(
style: TextButton.styleFrom(
backgroundColor: item.followType == 3 ? Colors.grey : FStyle.primaryColor,
minimumSize: const Size(70, 32),
padding: const EdgeInsets.symmetric(horizontal: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
),
onPressed: () async {
final ctl = Get.find<ChatController>();
final checkRes = await ImService.instance.checkFollowType(userIDList: [item.userInfo.userID!]);
int realFollowType = 0;
if (checkRes.success && checkRes.data != null) {
realFollowType = checkRes.data!.first.followType ?? 0;
if ([1, 3].contains(realFollowType)) {
//
final unRes = await ImService.instance.unfollowUser(userIDList: [item.userInfo.userID!]);
if (unRes.success) {
setState(() {
item.followType = 2;
});
ctl.mergeNoFriend(conversationID: 'c2c_${item.userInfo.userID!}');
}
} else {
//
final res = await ImService.instance.followUser(userIDList: [item.userInfo.userID!]);
if (res.success) {
setState(() {
item.followType = realFollowType == 0
? 1
: realFollowType == 2
? 3
: 0;
});
final chatRes = await ImService.instance.followUser(userIDList: [item.userInfo.userID!]);
if (chatRes.success) {
final res = await ImService.instance.getConversation(conversationID: 'c2c_${item.userInfo.userID}');
if (res.success) {
V2TimConversation conversation = res.data;
if (conversation.conversationGroupList?.isNotEmpty ?? false) {
await ImService.instance.deleteConversationsFromGroup(
groupName: conversation.conversationGroupList!.first!,
conversationIDList: [conversation.conversationID],
);
ctl.updateNoFriendMenu();
}
}
}
}
}
}
},
child: Text(
Utils.getTipText(item.followType),
style: const TextStyle(color: Colors.white, fontSize: 14),
),
),
],
),
),
);
},
);
},
),
),
],
),
),
);
}
}

View File

@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:loopin/IM/controller/im_user_info_controller.dart'; import 'package:loopin/IM/controller/im_user_info_controller.dart';
import 'package:loopin/api/common_api.dart'; import 'package:loopin/api/common_api.dart';
import 'package:loopin/components/network_or_asset_image.dart';
import 'package:loopin/service/http.dart'; import 'package:loopin/service/http.dart';
import 'package:loopin/styles/index.dart'; import 'package:loopin/styles/index.dart';
import 'package:loopin/utils/index.dart'; import 'package:loopin/utils/index.dart';
@ -321,9 +322,13 @@ class _UserInfoState extends State<UserInfo> {
final imageUrl = userInfoController.customInfo['coverBg']; final imageUrl = userInfoController.customInfo['coverBg'];
return GestureDetector( return GestureDetector(
onTap: () => pickCover(context), onTap: () => pickCover(context),
child: Image( // child: Image(
image: (imageUrl != null && imageUrl.isNotEmpty) ? NetworkImage(imageUrl) : const AssetImage('assets/images/pic2.jpg') as ImageProvider, // image: (imageUrl != null && imageUrl.isNotEmpty) ? NetworkImage(imageUrl) : const AssetImage('assets/images/pic2.jpg') as ImageProvider,
fit: BoxFit.cover, // fit: BoxFit.cover,
// ),
child: NetworkOrAssetImage(
imageUrl: imageUrl,
placeholderAsset: 'assets/images/bk.jpg',
), ),
); );
}), }),
@ -423,7 +428,7 @@ class _UserInfoState extends State<UserInfo> {
maxLines: 1, maxLines: 1,
); );
} else { } else {
String wxText = val['unionId'] == null || val['unionId'] == '' ? '未授权' : '已授权'; String wxText = val['openId'] == null || val['openId'] == '' ? '未授权' : '已授权';
return Row( return Row(
children: [ children: [
Spacer(), Spacer(),
@ -473,9 +478,13 @@ class _UserInfoState extends State<UserInfo> {
child: CircleAvatar( child: CircleAvatar(
radius: 60, radius: 60,
backgroundColor: Colors.white, backgroundColor: Colors.white,
child: CircleAvatar( child: ClipOval(
radius: 57, child: NetworkOrAssetImage(
backgroundImage: avatar.isNotEmpty ? NetworkImage(avatar) : const AssetImage('assets/images/avatar/img11.jpg') as ImageProvider, imageUrl: avatar,
width: double.infinity,
height: double.infinity,
fit: BoxFit.cover,
),
), ),
), ),
), ),

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:get/get_rx/src/rx_typedefs/rx_typedefs.dart'; import 'package:get/get_rx/src/rx_typedefs/rx_typedefs.dart';
import 'package:loopin/IM/controller/chat_controller.dart';
import 'package:loopin/IM/im_service.dart'; import 'package:loopin/IM/im_service.dart';
import 'package:loopin/components/custom_sticky_header.dart'; import 'package:loopin/components/custom_sticky_header.dart';
import 'package:loopin/components/network_or_asset_image.dart'; import 'package:loopin/components/network_or_asset_image.dart';
@ -202,7 +203,7 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
print('User navigated back'); print('User navigated back');
} }
}, },
child:Scaffold( child: Scaffold(
backgroundColor: const Color(0xFFFAF6F9), backgroundColor: const Color(0xFFFAF6F9),
body: Obx(() { body: Obx(() {
return NestedScrollViewPlus( return NestedScrollViewPlus(
@ -224,7 +225,7 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
Get.back(result: { Get.back(result: {
'returnTo': '/', 'returnTo': '/',
'vlogerId': args['memberId'], 'vlogerId': args['memberId'],
'followStatus': (followed.value == 1 || followed.value == 3)?true:false, 'followStatus': (followed.value == 1 || followed.value == 3) ? true : false,
}); });
}, },
), ),
@ -387,7 +388,6 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
final double currentHeight = constraints.maxHeight; final double currentHeight = constraints.maxHeight;
double ratio = (currentHeight - minHeight) / (maxHeight - minHeight); double ratio = (currentHeight - minHeight) / (maxHeight - minHeight);
ratio = ratio.clamp(0.0, 1.0); ratio = ratio.clamp(0.0, 1.0);
String coverBg = userInfo.value.customInfo?['coverBg'] ?? '';
return Stack( return Stack(
fit: StackFit.expand, fit: StackFit.expand,
children: [ children: [
@ -395,8 +395,9 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
child: Opacity( child: Opacity(
opacity: 1.0, opacity: 1.0,
child: NetworkOrAssetImage( child: NetworkOrAssetImage(
imageUrl: coverBg, imageUrl: userInfo.value.customInfo?['coverBg'],
width: double.infinity, width: double.infinity,
placeholderAsset: 'assets/images/bk.jpg',
), ),
), ),
), ),
@ -410,7 +411,11 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[ children: <Widget>[
ClipOval( ClipOval(
child: NetworkOrAssetImage(imageUrl: userInfo.value.faceUrl), child: NetworkOrAssetImage(
imageUrl: userInfo.value.faceUrl,
width: 80,
height: 80,
),
), ),
const SizedBox(width: 15.0), const SizedBox(width: 15.0),
Expanded( Expanded(
@ -540,19 +545,42 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
child: child, child: child,
); );
}, },
child: [1, 3].contains(followed.value) child: [1, 2, 3].contains(followed.value)
? Row( ? Row(
key: const ValueKey('followed'), key: const ValueKey('followed'),
children: [ children: [
Expanded( Expanded(
child: ElevatedButton( child: ElevatedButton(
onPressed: () async { onPressed: () async {
print('点击已关注'); logger.w('点击已关注/已回关:${followed.value}');
final ctl = Get.find<ChatController>();
if (followed.value == 2) {
//
final res = await ImService.instance.followUser(userIDList: [vlogerId]);
if (res.success) {
followed.value = 3;
// noFriend会话组
final res = await ImService.instance.getConversation(conversationID: 'c2c_$vlogerId');
if (res.success) {
V2TimConversation conversation = res.data;
if (conversation.conversationGroupList?.isNotEmpty ?? false) {
//
await ImService.instance.deleteConversationsFromGroup(
groupName: conversation.conversationGroupList!.first!, //
conversationIDList: [conversation.conversationID],
);
ctl.updateNoFriendMenu();
}
}
}
} else {
final res = await ImService.instance.unfollowUser(userIDList: [vlogerId]); final res = await ImService.instance.unfollowUser(userIDList: [vlogerId]);
if (res.success) { if (res.success) {
// 1032 // 1032
followed.value = followed.value == 1 ? 0 : 2; followed.value = followed.value == 1 ? 0 : 2;
// group ctl.mergeNoFriend(conversationID: 'c2c_${userInfo.value.userID}');
}
} }
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
@ -565,8 +593,10 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
), ),
child: Text(followed.value == 1 child: Text(followed.value == 1
? '已关注' ? '已关注'
: followed.value == 2
? '回关'
: followed.value == 3 : followed.value == 3
? '互关' ? '互关'
: '未知状态'), : '未知状态'),
), ),
), ),
@ -574,7 +604,7 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
Expanded( Expanded(
child: ElevatedButton( child: ElevatedButton(
onPressed: () async { onPressed: () async {
print('私信'); logger.w('私信');
// //
final res = await ImService.instance.getConversation(conversationID: 'c2c_$vlogerId'); final res = await ImService.instance.getConversation(conversationID: 'c2c_$vlogerId');
V2TimConversation conversation = res.data; V2TimConversation conversation = res.data;
@ -614,12 +644,12 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
child: ElevatedButton.icon( child: ElevatedButton.icon(
onPressed: () async { onPressed: () async {
// 02 // 02
print('点击关注'); logger.w('点击关注');
final res = await ImService.instance.followUser(userIDList: [vlogerId]); final res = await ImService.instance.followUser(userIDList: [vlogerId]);
if (res.success) { if (res.success) {
followed.value = followed.value == 0 ? 1 : 3; followed.value = followed.value == 0 ? 1 : 3;
if (followed.value == 3) { if (followed.value == 3) {
// 3noFriend会话组 // 3noFriend会话组
final res = await ImService.instance.getConversation(conversationID: 'c2c_$vlogerId'); final res = await ImService.instance.getConversation(conversationID: 'c2c_$vlogerId');
if (res.success) { if (res.success) {
V2TimConversation conversation = res.data; V2TimConversation conversation = res.data;
@ -629,14 +659,13 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
groupName: conversation.conversationGroupList!.first!, groupName: conversation.conversationGroupList!.first!,
conversationIDList: [conversation.conversationID], conversationIDList: [conversation.conversationID],
); );
//
} }
} }
} }
} }
}, },
icon: const Icon(Icons.add), icon: const Icon(Icons.add),
label: const Text('关注'), label: Text('关注'),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: FStyle.primaryColor, backgroundColor: FStyle.primaryColor,
foregroundColor: Colors.white, foregroundColor: Colors.white,

View File

@ -1,13 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:loopin/IM/controller/chat_controller.dart';
import 'package:loopin/IM/im_core.dart';
import 'package:loopin/IM/im_message.dart';
import 'package:loopin/IM/im_service.dart' hide logger; import 'package:loopin/IM/im_service.dart' hide logger;
import 'package:loopin/service/http.dart';
import 'package:loopin/api/common_api.dart'; import 'package:loopin/api/common_api.dart';
import 'package:loopin/utils/index.dart';
import 'package:loopin/components/my_toast.dart'; import 'package:loopin/components/my_toast.dart';
import 'package:loopin/service/http.dart';
import 'package:loopin/utils/index.dart';
import '../../behavior/custom_scroll_behavior.dart'; import '../../behavior/custom_scroll_behavior.dart';
class SearchResultPage extends StatefulWidget { class SearchResultPage extends StatefulWidget {
@ -198,7 +196,7 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
final data = { final data = {
'title': _searchQuery, 'title': _searchQuery,
'size': _pageSize, 'size': _pageSize,
'type': tabIndex+1, // 1-2-3- 'type': tabIndex + 1, // 1-2-3-
'current': currentPage, 'current': currentPage,
}; };
final res = await Http.post(CommonApi.aggregationSearchApi, data: data); final res = await Http.post(CommonApi.aggregationSearchApi, data: data);
@ -270,28 +268,30 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
} }
// //
onFocusBtnClick (user,index) async { onFocusBtnClick(user, index) async {
final vlogerId = user['id']; final vlogerId = user['id'];
final doIFollowVloger = user['doIFollowVloger']; final doIFollowVloger = user['doIFollowVloger'];
print('是否关注此用户------------->${doIFollowVloger}'); print('是否关注此用户------------->$doIFollowVloger');
if (doIFollowVloger == null || doIFollowVloger == false) { print('此用户UserId------------->$vlogerId');
if (doIFollowVloger == false) {
final res = await ImService.instance.followUser(userIDList: [vlogerId]); final res = await ImService.instance.followUser(userIDList: [vlogerId]);
print('关注结果------------->${res.success}'); print('关注结果------------->${res.success}');
if (res.success) { if (res.success) {
setState(() { setState(() {
_userResults[index]['doIFollowVloger'] = true; _userResults[index]['doIFollowVloger'] = !_userResults[index]['doIFollowVloger'];
}); });
} }
}else{ } else {
final res = await ImService.instance.unfollowUser(userIDList: [vlogerId]); final res = await ImService.instance.followUser(userIDList: [vlogerId]);
print('取消关注结果------------->${res.success}'); print('取消关注结果------------->${res.success}');
if (res.success) { if (res.success) {
setState(() { setState(() {
_userResults[index]['doIFollowVloger'] = false; _userResults[index]['doIFollowVloger'] = !_userResults[index]['doIFollowVloger'];
}); });
} }
} }
} }
// //
Widget _buildLoadMoreWidget(int tabIndex) { Widget _buildLoadMoreWidget(int tabIndex) {
if (_isLoadingMore) { if (_isLoadingMore) {
@ -338,7 +338,7 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
} }
return Container(); return Container();
} }
// Tab的数据 // Tab的数据
List<dynamic> _getCurrentResults(int tabIndex) { List<dynamic> _getCurrentResults(int tabIndex) {
@ -476,9 +476,7 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
return NotificationListener<ScrollNotification>( return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) { onNotification: (ScrollNotification scrollInfo) {
if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && !_isLoadingMore && _videoHasMore) {
!_isLoadingMore &&
_videoHasMore) {
_loadMoreData(0); _loadMoreData(0);
} }
return false; return false;
@ -605,9 +603,7 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
) )
: null, : null,
), ),
child: video['avatar'] == null || video['avatar'].toString().isEmpty child: video['avatar'] == null || video['avatar'].toString().isEmpty ? const Icon(Icons.person, size: 10, color: Colors.grey) : null,
? const Icon(Icons.person, size: 10, color: Colors.grey)
: null,
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
Expanded( Expanded(
@ -627,7 +623,7 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
const Icon(Icons.favorite_border, size: 10, color: Colors.grey), const Icon(Icons.favorite_border, size: 10, color: Colors.grey),
const SizedBox(width: 2), const SizedBox(width: 2),
Text( Text(
Utils().formatLikeCount(video['likeCounts'] ?? 0), Utils.graceNumber(video['likeCounts'] ?? 0),
style: const TextStyle( style: const TextStyle(
fontSize: 9, fontSize: 9,
color: Colors.grey, color: Colors.grey,
@ -688,9 +684,7 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
return NotificationListener<ScrollNotification>( return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) { onNotification: (ScrollNotification scrollInfo) {
if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && !_isLoadingMore && _productHasMore) {
!_isLoadingMore &&
_productHasMore) {
_loadMoreData(1); _loadMoreData(1);
} }
return false; return false;
@ -791,7 +785,7 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
product['pic'].toString(), product['pic'].toString(),
fit: BoxFit.cover, fit: BoxFit.cover,
) )
: Container( : SizedBox(
height: 110, height: 110,
child: const Center( child: const Center(
child: Icon(Icons.shopping_bag, color: Colors.grey, size: 32), child: Icon(Icons.shopping_bag, color: Colors.grey, size: 32),
@ -845,7 +839,7 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
), ),
), ),
); );
} }
// Tab // Tab
Widget _buildUserTab() { Widget _buildUserTab() {
@ -855,9 +849,7 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
return NotificationListener<ScrollNotification>( return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) { onNotification: (ScrollNotification scrollInfo) {
if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && !_isLoadingMore && _userHasMore) {
!_isLoadingMore &&
_userHasMore) {
_loadMoreData(2); _loadMoreData(2);
} }
return false; return false;
@ -872,7 +864,7 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
return _buildLoadMoreWidget(2); return _buildLoadMoreWidget(2);
} }
final user = _userResults[index] as Map<String, dynamic>; final user = _userResults[index] as Map<String, dynamic>;
return _buildUserItem(user,index); return _buildUserItem(user, index);
}, },
), ),
), ),
@ -882,7 +874,7 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
Widget _buildUserItem(Map<String, dynamic> user, int index) { Widget _buildUserItem(Map<String, dynamic> user, int index) {
// //
bool isFollowing = user['doIFollowVloger'] ?? false; bool isFollowing = user['doIFollowVloger'] ?? false;
print('111111111111111111111111${isFollowing}'); print('111111111111111111111111$isFollowing');
return Container( return Container(
margin: const EdgeInsets.only(bottom: 8), margin: const EdgeInsets.only(bottom: 8),
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
@ -912,9 +904,7 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
) )
: null, : null,
), ),
child: user['avatar'] == null child: user['avatar'] == null ? const Icon(Icons.person, color: Colors.grey, size: 20) : null,
? const Icon(Icons.person, color: Colors.grey, size: 20)
: null,
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
Expanded( Expanded(
@ -951,9 +941,7 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
), ),
// //
side: isFollowing side: isFollowing ? BorderSide(color: Colors.grey[400]!, width: 0.5) : BorderSide.none,
? BorderSide(color: Colors.grey[400]!, width: 0.5)
: BorderSide.none,
), ),
child: Text( child: Text(
isFollowing ? '已关注' : '关注', isFollowing ? '已关注' : '关注',

View File

@ -675,7 +675,7 @@ class _RecommendModuleState extends State<RecommendModule> {
'size': pageSize, 'size': pageSize,
}); });
final data = res['data']; final data = res['data'];
logger.d('关注用户的视频列表:$data'); // logger.d('关注用户的视频列表:$data');
if (data == null || (data is List && data.isEmpty)) { if (data == null || (data is List && data.isEmpty)) {
return; return;
} }
@ -698,7 +698,7 @@ class _RecommendModuleState extends State<RecommendModule> {
if (videos.isNotEmpty) { if (videos.isNotEmpty) {
page++; page++;
} }
logger.i('视频数据列表------------------->$videoList'); // logger.i('视频数据列表------------------->$videoList');
// //
player.open( player.open(
@ -1247,16 +1247,13 @@ class _RecommendModuleState extends State<RecommendModule> {
), ),
], ],
), ),
onTap: ()async { onTap: () async {
player.pause(); player.pause();
// //
final result = await Get.toNamed( final result = await Get.toNamed('/report', arguments: videoList[videoModuleController.videoPlayIndex.value]);
'/report',
arguments: videoList[videoModuleController
.videoPlayIndex.value]);
if (result != null) { if (result != null) {
player.play(); player.play();
}; }
}, },
), ),
], ],

View File

@ -10,15 +10,19 @@ import 'package:loopin/pages/chat/chat_no_friend.dart';
import 'package:loopin/pages/chat/notify/interaction.dart'; import 'package:loopin/pages/chat/notify/interaction.dart';
import 'package:loopin/pages/chat/notify/noFriend.dart'; import 'package:loopin/pages/chat/notify/noFriend.dart';
import 'package:loopin/pages/chat/notify/system.dart'; import 'package:loopin/pages/chat/notify/system.dart';
import 'package:loopin/pages/groupChat/index.dart';
import 'package:loopin/pages/my/des.dart'; import 'package:loopin/pages/my/des.dart';
import 'package:loopin/pages/my/fans.dart';
import 'package:loopin/pages/my/flowing.dart';
import 'package:loopin/pages/my/mutual_followers.dart';
import 'package:loopin/pages/my/nick_name.dart'; import 'package:loopin/pages/my/nick_name.dart';
import 'package:loopin/pages/my/setting.dart'; import 'package:loopin/pages/my/setting.dart';
import 'package:loopin/pages/my/user_info.dart'; import 'package:loopin/pages/my/user_info.dart';
import 'package:loopin/pages/my/vloger.dart'; import 'package:loopin/pages/my/vloger.dart';
import 'package:loopin/pages/video/report.dart';
import 'package:loopin/pages/search/index.dart'; import 'package:loopin/pages/search/index.dart';
import 'package:loopin/pages/search/search-result.dart'; import 'package:loopin/pages/search/search-result.dart';
import 'package:loopin/pages/video/commonVideo.dart'; import 'package:loopin/pages/video/commonVideo.dart';
import 'package:loopin/pages/video/report.dart';
import '../layouts/index.dart'; import '../layouts/index.dart';
/* 引入路由页面 */ /* 引入路由页面 */
@ -44,6 +48,9 @@ final Map<String, Widget> routes = {
'/videoDetail': const VideoDetailPage(), '/videoDetail': const VideoDetailPage(),
'/search': const SearchPage(), '/search': const SearchPage(),
'/search-result': const SearchResultPage(), '/search-result': const SearchResultPage(),
'/videoDetail': const VideoDetailPage(),
'/search': const SearchPage(),
'/search-result': const SearchResultPage(),
//settins //settins
'/setting': const Setting(), '/setting': const Setting(),
'/userInfo': const UserInfo(), '/userInfo': const UserInfo(),
@ -56,6 +63,18 @@ final Map<String, Widget> routes = {
'/noFriend': const Nofriend(), '/noFriend': const Nofriend(),
'/system': const System(), '/system': const System(),
'/interaction': const Interaction(), '/interaction': const Interaction(),
//
'/fans': const Fans(),
'/flow': const Flowing(),
'/eachFlow': const MutualFollowers(),
//
'/group': const StartGroupChatPage(),
//
'/fans': const Fans(),
'/flow': const Flowing(),
'/eachFlow': const MutualFollowers(),
//
'/group': const StartGroupChatPage(),
}; };
final List<GetPage> routeList = routes.entries final List<GetPage> routeList = routes.entries

View File

@ -10,6 +10,7 @@ class HttpConfig {
// baseUrl: 'http://cjh.wuzhongjie.com.cn', // baseUrl: 'http://cjh.wuzhongjie.com.cn',
// baseUrl: 'http://82.156.121.2:8880', // baseUrl: 'http://82.156.121.2:8880',
baseUrl: 'http://192.168.1.65:8880', baseUrl: 'http://192.168.1.65:8880',
// baseUrl: 'http://192.168.1.22:8080',
// connectTimeout: Duration(seconds: 30), // connectTimeout: Duration(seconds: 30),
// receiveTimeout: Duration(seconds: 30), // receiveTimeout: Duration(seconds: 30),

View File

@ -5,26 +5,34 @@ import 'package:flutter/material.dart';
class FStyle { class FStyle {
// Badge // Badge
static badge(int count, { static badge(int count, {Color color = Colors.red, bool isdot = false, double height = 16.0, double width = 16.0}) {
Color color = Colors.red,
bool isdot = false,
double height = 16.0,
double width = 16.0
}) {
final num = count > 99 ? '99+' : count; final num = count > 99 ? '99+' : count;
return Container( return Container(
alignment: Alignment.center, alignment: Alignment.center,
height: isdot ? height / 2 : height, height: isdot ? height / 2 : height,
width: isdot ? width / 2 : width, width: isdot ? width / 2 : width,
decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(100.00)), decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(100.00)),
child: isdot ? null : Text('$num', style: const TextStyle(color: Colors.white, fontSize: 10.0,)), child: isdot
? null
: Text('$num',
style: const TextStyle(
color: Colors.white,
fontSize: 10.0,
)),
); );
} }
// //
static const border = Divider(color: Color(0xFFBBBBBB), height: 1.0, thickness: .5,); // static const border = Divider(color: Color(0xFFBBBBBB), height: 1.0, thickness: .5,);
static const border = Divider(
color: Colors.white,
height: 1.0,
thickness: .5,
);
// //
static const backgroundColor = Color(0xFFEEEEEE); // static const backgroundColor = Color(0xFFEEEEEE);
static const backgroundColor = Colors.white;
static const primaryColor = Color(0xFFFF5000); static const primaryColor = Color(0xFFFF5000);
static const white = Colors.white; static const white = Colors.white;
static const c999 = Color(0xFF999999); static const c999 = Color(0xFF999999);

View File

@ -113,7 +113,7 @@ class Utils {
} }
// //
String graceNumber(int number) { static String graceNumber(int number) {
if (number == 0) return "0"; if (number == 0) return "0";
if (number < 1000) { if (number < 1000) {
@ -130,12 +130,12 @@ class Utils {
} }
} }
String _trimTrailingZero(double number) { static String _trimTrailingZero(double number) {
return number.toStringAsFixed(1).replaceAll(RegExp(r'\.0$'), ''); return number.toStringAsFixed(1).replaceAll(RegExp(r'\.0$'), '');
} }
// //
String formatChatTime(int timestamp) { static String formatChatTime(int timestamp) {
// timestamp DateTime // timestamp DateTime
DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
@ -189,31 +189,29 @@ class Utils {
'${messageTime.minute.toString().padLeft(2, '0')}'; '${messageTime.minute.toString().padLeft(2, '0')}';
} }
String _formatHourMinute(DateTime dt) { static String _formatHourMinute(DateTime dt) {
final hour = dt.hour.toString().padLeft(2, '0'); final hour = dt.hour.toString().padLeft(2, '0');
final minute = dt.minute.toString().padLeft(2, '0'); final minute = dt.minute.toString().padLeft(2, '0');
return "$hour:$minute"; return "$hour:$minute";
} }
String _weekdayLabel(int weekday) { static String _weekdayLabel(int weekday) {
const weekdays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]; const weekdays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
return weekdays[(weekday - 1) % 7]; return weekdays[(weekday - 1) % 7];
} }
//
String formatLikeCount(int count) {
if (count >= 10000) {
return '${(count / 10000).toStringAsFixed(1)}w';
} else if (count >= 1000) {
return '${(count / 1000).toStringAsFixed(1)}k';
}
return count.toString();
}
//
static String fenToYuanSimple(dynamic fen) {
if (fen == null) {
return '0.00';
}
return (double.parse(fen) / 100).toStringAsFixed(2); static String getTipText(int realFollowType) {
switch (realFollowType) {
case 0:
return '关注';
case 1:
return '已关注';
case 2:
return '回关';
case 3:
return '已互关';
default:
return '未知状态';
}
} }
} }

View File

@ -1,6 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'package:loopin/IM/push_service.dart';
import 'package:loopin/models/notify_message.type.dart'; import 'package:loopin/models/notify_message.type.dart';
import 'package:loopin/models/summary_type.dart'; import 'package:loopin/models/summary_type.dart';
import 'package:tencent_cloud_chat_sdk/enum/message_elem_type.dart'; import 'package:tencent_cloud_chat_sdk/enum/message_elem_type.dart';
@ -53,8 +52,8 @@ String _parseCustomMessage(V2TimMessage? msg) {
if (msg == null) return '[null]'; if (msg == null) return '[null]';
final sum = msg.cloudCustomData; final sum = msg.cloudCustomData;
final elment = msg.customElem; // final elment = msg.customElem; //
logger.w('解析自定义消息:${msg.toJson()}'); // logger.w('解析自定义消息:${msg.toJson()}');
logger.w('解析element${elment?.desc ?? 'summary_error'}'); // logger.w('解析element${elment?.desc ?? 'summary_error'}');
try { try {
switch (sum) { switch (sum) {
case SummaryType.hongbao: case SummaryType.hongbao: