Compare commits

..

No commits in common. "dc2685a27377ecfae9ff2f90dbef5edfc8d1b050" and "f4dcf8a52d0af1c0e4219cf9a5feed844fc238fb" have entirely different histories.

33 changed files with 890 additions and 2666 deletions

View File

@ -13,123 +13,12 @@ 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('开始获取会话列表数据');
@ -168,101 +57,65 @@ class ChatController extends GetxController {
} }
} }
/// ///
Future<void> getNoFriendData({V2TimConversation? csion}) async { 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 ConversationViewModel matchItem = chatList.firstWhere(
final noFriendData = await ImService.instance.getConversationListByFilter( (item) => item.conversation.conversationGroupList?.contains(myConversationType.ConversationType.noFriend.name) ?? false,
);
//
final unreadTotal = await ImService.instance.getUnreadMessageCountByFilter(
filter: V2TimConversationFilter(
conversationGroup: myConversationType.ConversationType.noFriend.name,
hasUnreadCount: true,
),
);
matchItem.conversation.lastMessage = csion!.lastMessage;
matchItem.conversation.unreadCount = unreadTotal.data;
chatList.refresh();
} else {
//
final res = await ImService.instance.getConversationListByFilter(
filter: V2TimConversationFilter(conversationGroup: myConversationType.ConversationType.noFriend.name), filter: V2TimConversationFilter(conversationGroup: myConversationType.ConversationType.noFriend.name),
nextSeq: 0, nextSeq: 0,
count: 1, count: 1,
); );
if (noFriendData.success && (noFriendData.data?.conversationList?.isNotEmpty == true)) { if (res.success && res.data != null) {
// final convList = res.data!.conversationList ?? [];
final ConversationViewModel matchItem = chatList.firstWhere( if (convList.isNotEmpty) {
(item) => item.conversation.conversationGroupList?.contains(myConversationType.ConversationType.noFriend.name) ?? false, // logger.i(res.data!.toJson());
orElse: () { // 1.2.converstaionviewmodel
//viewmodel会话上的分组被移除 final unread = await ImService.instance.getUnreadMessageCountByFilter(
logger.w("没有找到符合条件的元素,执行移除逻辑"); filter: V2TimConversationFilter(
return ConversationViewModel(conversation: V2TimConversation(conversationID: '')); conversationGroup: myConversationType.ConversationType.noFriend.name,
}, hasUnreadCount: true,
); ),
// );
final unreadTotal = await ImService.instance.getUnreadMessageCountByFilter( if (unread.success) {
filter: V2TimConversationFilter( var viewModelList = await ConversationViewModel.createConversationViewModel(convList: [convList.first]);
conversationGroup: myConversationType.ConversationType.noFriend.name, final conviewModel = viewModelList.first;
hasUnreadCount: true, conviewModel.faceUrl = 'assets/images/notify/msr.png';
), conviewModel.conversation.showName = '陌生人消息';
); conviewModel.conversation.unreadCount = unread.data;
// nofriend分组 // final createItem = ConversationViewModel(
if (matchItem.conversation.conversationID.isEmpty) { // conversation: conv,
// viewmodel // faceUrl: faceUrl,
await ConversationViewModel.createConversationViewModel(convList: noFriendData.data!.conversationList!); // );
} else { final newList = List<ConversationViewModel>.from(chatList);
matchItem.conversation.lastMessage = csion!.lastMessage; newList.add(conviewModel);
matchItem.conversation.unreadCount = unreadTotal.data; newList.sort((a, b) {
} final atime = a.conversation.lastMessage?.timestamp ?? 0;
} else { final btime = b.conversation.lastMessage?.timestamp ?? 0;
// return btime.compareTo(atime); //
chatList.removeWhere((chatItem) => chatItem.isCustomAdmin == myConversationType.ConversationType.noFriend.name); });
} chatList.value = newList;
}
chatList.refresh();
} else {
logger.w('构建陌生人消息入口--开始');
await createNoFriendMenu();
}
}
//
Future<void> createNoFriendMenu() 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) {
// logger.i(res.data!.toJson());
// 1.2.converstaionviewmodel
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 createItem = ConversationViewModel(
// conversation: conv,
// faceUrl: faceUrl,
// );
final newList = List<ConversationViewModel>.from(chatList);
newList.add(conviewModel);
newList.sort((a, b) {
final atime = a.conversation.lastMessage?.timestamp ?? 0;
final btime = b.conversation.lastMessage?.timestamp ?? 0;
return btime.compareTo(atime); //
});
chatList.value = newList;
} }
} }
} else {
logger.e('构建失败-----${res.desc}\n${res.data!.toJson()}');
} }
} }

View File

@ -13,7 +13,6 @@ 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();
@ -56,7 +55,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,4 +131,6 @@ 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,15 +51,12 @@ 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中包含:${chatItem.conversation.conversationID}'); logger.w('找到的chatList的ID:${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)) {
// //
@ -71,44 +68,29 @@ class GlobalBadge extends GetxController {
); );
chatItem.conversation.lastMessage = updatedConv.lastMessage; chatItem.conversation.lastMessage = updatedConv.lastMessage;
chatItem.conversation.unreadCount = unread.data; // chatItem.conversation.unreadCount = unread.data; //
update(); } else {
} else if (updatedConv.conversationID != '') {
// //
logger.w('非陌生人消息的会话,正常更新'); logger.w('不需要分组的会话,正常更新');
chatItem.conversation = updatedConv; chatItem.conversation = updatedConv;
update(); update();
} }
} else { } else {
logger.e('本地会话列表中不包含的会话:$updatedIds'); // 1nofriend分组会话2: logger.e('会话列表中不包含的会话:$updatedIds');
//
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) {
final V2TimConversation realConv = isReal.data; if (realConv.conversationID.isNotEmpty) {
if (realConv.lastMessage != null) {
// //
if (Get.isRegistered<NotifyNoFriendController>()) { if (Get.isRegistered<NotifyNoFriendController>()) {
logger.w('在陌生人会话列表');
final notifyCtl = Get.find<NotifyNoFriendController>(); final notifyCtl = Get.find<NotifyNoFriendController>();
// notifyCtl.updateLastMsg(conversation: realConv);
if (realConv.conversationGroupList?.contains(ConversationType.noFriend.name) ?? false) {
notifyCtl.updateLastMsg(conversation: realConv);
} else {
notifyCtl.del(conversation: 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 { } else {
// 2 //
willInsert.add(realConv); if (realConv.conversationGroupList?.isEmpty ?? false) {
willInsert.add(realConv);
}
} }
} }
} }
@ -117,17 +99,7 @@ class GlobalBadge extends GetxController {
} }
// //
if (willInsert.isNotEmpty) { if (willInsert.isNotEmpty) {
logger.w('收集到要插入的数据为:${willInsert.first.toLogString()}'); var viewModelList = await ConversationViewModel.createConversationViewModel(convList: willInsert);
// 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);
} }
// //
@ -144,23 +116,12 @@ 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();
} }
@ -210,7 +171,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;
@ -226,7 +187,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();
@ -240,7 +201,7 @@ class GlobalBadge extends GetxController {
/// total /// total
Future<void> refreshUnreadCount() async { Future<void> refreshUnreadCount() async {
initUnreadCount(); _initUnreadCount();
} }
/// ///

View File

@ -1,7 +1,6 @@
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';
@ -52,8 +51,6 @@ 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,4 +1,5 @@
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';
@ -61,7 +62,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,7 +3,6 @@ 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';
@ -50,12 +49,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);
} }
@ -178,15 +177,11 @@ 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,15 +710,4 @@ 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,16 +8,6 @@ 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,6 +19,7 @@ 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,32 +2155,18 @@ 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)
? GestureDetector( ? SizedBox(
onTap: () { height: 35.0,
// width: 35.0,
logger.e("点击了头像"); child: ClipRRect(
Get.toNamed('/vloger', arguments: {'memberId': data.sender}); borderRadius: const BorderRadius.all(Radius.circular(20.0)),
}, child: NetworkOrAssetImage(imageUrl: data.faceUrl),
child: SizedBox(
height: 35.0,
width: 35.0,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(20.0)),
child: NetworkOrAssetImage(imageUrl: data.faceUrl),
),
), ),
) )
: const SizedBox.shrink(), : const SizedBox.shrink(),
@ -2190,13 +2176,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(
// displayName ?? '未知昵称', data.friendRemark ?? data.nameCard ?? data.nickName ?? '未知昵称',
// 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,17 +3,16 @@ 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';
@ -42,8 +41,6 @@ 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; //
@ -155,27 +152,13 @@ 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.followType.value = isfd.data?.first.followType ?? 0; controller.isFriend.value = isfd.data;
if ([3].contains(controller.followType.value)) { print(isfd.data);
controller.isFriend.value = true; } else {
} else { controller.isFriend.value = false;
controller.isFriend.value = false; print(isfd.desc);
}
logger.i('当前聊天的关系为:${controller.followType.value}');
} }
} }
@ -311,9 +294,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();
// }, },
), ),
), ),
), ),
@ -900,12 +883,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);
@ -1220,7 +1203,6 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
// //
void contextMenuDialog() { void contextMenuDialog() {
return;
showDialog( showDialog(
context: context, context: context,
builder: (context) { builder: (context) {
@ -1313,66 +1295,115 @@ 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: 'report', value: 'remark',
// child: Row( child: Row(
// children: [ children: [
// Icon(Icons.report, color: Colors.white, size: 18), Icon(Icons.edit, 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: 'block', value: 'not',
// child: Row( child: Row(
// children: [ children: [
// Icon(Icons.block, color: Colors.white, size: 18), Icon(Icons.do_not_disturb_on, 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 'report': case 'remark':
// print('点击了举报'); print('点击了备注');
// break; setRemark();
// case 'block': break;
// print('点击了拉黑'); case 'not':
// break; print('点击了免打扰');
// } break;
// } case 'report':
// }, print('点击了举报');
// ), break;
case 'block':
print('点击了拉黑');
break;
case 'foucs':
print('点击了取关');
break;
}
}
},
),
], ],
), ),
body: Flex( body: Flex(
@ -1399,39 +1430,6 @@ 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(
@ -1440,92 +1438,22 @@ 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 Stack( return ListView(
controller: chatController,
reverse: true,
padding: const EdgeInsets.all(10.0),
children: [ children: [
ListView( ConstrainedBox(
controller: chatController, constraints: BoxConstraints(
reverse: true, minHeight: constraints.maxHeight - 20,
padding: EdgeInsets.only( ),
top: [1, 2].contains(showTipWidget) ? tipHeight + 10 : 0, // child: Column(
left: 10, mainAxisSize: MainAxisSize.min,
right: 10, children: msgWidgets,
bottom: 10,
), ),
children: [
ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight - ([1, 2].contains(showTipWidget) ? tipHeight : 0),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: msgWidgets,
),
),
],
), ),
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)),
),
)
],
),
),
),
], ],
); );
}); });
@ -1553,28 +1481,19 @@ 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 (editorFocusNode.hasFocus) { if (voiceBtnEnable) {
editorFocusNode.unfocus(); voiceBtnEnable = false;
} else {
editorFocusNode.requestFocus(); editorFocusNode.requestFocus();
}
if (toolbarEnable) {
// voiceBtnEnable = false;
// editorFocusNode.requestFocus();
// editorFocusNode.requestFocus();
} else { } else {
// voiceBtnEnable = true; voiceBtnEnable = true;
// toolbarEnable = true; editorFocusNode.unfocus();
// editorFocusNode.unfocus();
} }
}); });
}, },
@ -1678,31 +1597,29 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
const SizedBox( const SizedBox(
width: 10.0, width: 10.0,
), ),
InkWell(
// InkWell( child: const Icon(
// child: const Icon( Icons.add_reaction_rounded,
// Icons.add_reaction_rounded, color: Color(0xFF3B3B3B),
// color: Color(0xFF3B3B3B), size: 30.0,
// size: 30.0, ),
// ), onTap: () {
// onTap: () { handleEmojChooseState(0);
// handleEmojChooseState(0); },
// }, ),
// ), const SizedBox(
// const SizedBox( width: 8.0,
// width: 8.0, ),
// ), InkWell(
child: const Icon(
// InkWell( Icons.add,
// child: const Icon( color: Color(0xFF3B3B3B),
// Icons.add, size: 30.0,
// color: Color(0xFF3B3B3B), ),
// size: 30.0, onTap: () {
// ), handleEmojChooseState(1);
// onTap: () { },
// handleEmojChooseState(1); ),
// },
// ),
const SizedBox( const SizedBox(
width: 8.0, width: 8.0,
), ),
@ -2025,33 +1942,27 @@ 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)
? GestureDetector( ? SizedBox(
onTap: () { height: 35.0,
// width: 35.0,
logger.e("点击了头像"); child: ClipRRect(
Get.toNamed('/vloger', arguments: {'memberId': data.sender}); borderRadius: const BorderRadius.all(Radius.circular(20.0)),
child: Image.network(
// data.faceUrl ?? 'https://wuzhongjie.com.cn/download/logo.png',
}, errorBuilder: (context, error, stackTrace) {
child: SizedBox( return Image.asset(
height: 35.0, 'assets/images/pic1.jpg',
width: 35.0, height: 60.0,
child: ClipRRect( width: 60.0,
borderRadius: const BorderRadius.all(Radius.circular(20.0)), fit: BoxFit.cover,
child: NetworkOrAssetImage(imageUrl: data.faceUrl), );
},
), ),
), ),
) )
@ -2062,13 +1973,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(
// displayName ?? '未知昵称', data.friendRemark ?? data.nameCard ?? data.nickName ?? '未知昵称',
// 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: [
// //
@ -2100,7 +2011,17 @@ 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: NetworkOrAssetImage(imageUrl: data.faceUrl), child: Image.network(
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,18 +232,17 @@ class ChatPageState extends State<ChatPage> {
if (selected != null) { if (selected != null) {
switch (selected) { switch (selected) {
case 'group': case 'group':
logger.w('点击了发起群聊'); print('点击了发起群聊');
Get.toNamed('/group');
break; break;
case 'friend': case 'friend':
logger.w('点击了添加朋友'); print('点击了添加朋友');
break; break;
case 'scan': case 'scan':
logger.w('点击了扫一扫'); print('点击了扫一扫');
ScanUtil.openScanner(onResult: (code) { ScanUtil.openScanner(onResult: (code) {
logger.w('扫码结果:$code'); print('扫码结果:$code');
Get.snackbar('扫码成功', code); Get.snackbar('扫码成功', code);
// //
}); });
break; break;
} }
@ -280,174 +279,159 @@ class ChatPageState extends State<ChatPage> {
Text('群聊'), Text('群聊'),
], ],
), ),
GestureDetector( Column(
onTap: () { children: [
// SvgPicture.asset(
Get.toNamed('/eachFlow'); 'assets/images/svg/kefu.svg',
}, height: 36.0,
child: Column( width: 36.0,
children: [ ),
SvgPicture.asset( Text('互关'),
'assets/images/svg/kefu.svg', ],
height: 36.0,
width: 36.0,
),
Text('互关'),
],
),
), ),
GestureDetector( Column(
onTap: () { children: [
// SvgPicture.asset(
Get.toNamed('/fans'); 'assets/images/svg/comment.svg',
}, height: 36.0,
child: Column( width: 36.0,
children: [ ),
SvgPicture.asset( Text('粉丝'),
'assets/images/svg/comment.svg', ],
height: 36.0,
width: 36.0,
),
Text('粉丝'),
],
),
), ),
GestureDetector( Column(
onTap: () { children: [
// SvgPicture.asset(
Get.toNamed('/flow'); 'assets/images/svg/comment.svg',
}, height: 36.0,
child: Column( width: 36.0,
children: [ ),
SvgPicture.asset( Text('关注'),
'assets/images/svg/comment.svg', ],
height: 36.0,
width: 36.0,
),
Text('关注'),
],
),
), ),
], ],
), ),
), ),
Expanded( Expanded(
child: Obx( child: RefreshIndicator(
() { backgroundColor: Colors.white,
final chatList = controller.chatList; color: Color(0xFFFF5000),
displacement: 10.0,
onRefresh: handleRefresh,
child: Obx(() {
final chatList = controller.chatList;
return ListView.builder( return ListView.builder(
shrinkWrap: true, shrinkWrap: true,
physics: BouncingScrollPhysics(), physics: BouncingScrollPhysics(),
itemCount: chatList.length, itemCount: chatList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
// logger.w(chatList[index].conversation.conversationGroupList); final isNoFriend = chatList[index].conversation.conversationGroupList?.contains(ConversationType.noFriend.name) ?? false;
// logger.w(chatList[index].isCustomAdmin); final isAdmin = chatList[index].isCustomAdmin != null && chatList[index].isCustomAdmin != '0';
final isNoFriend = chatList[index].conversation.conversationGroupList?.contains(ConversationType.noFriend.name) ?? false;
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(
// color: chatList[index]['topMost'] == null ? Colors.white : Colors.grey[100], // // color: chatList[index]['topMost'] == null ? Colors.white : Colors.grey[100], //
child: InkWell( child: InkWell(
splashColor: Colors.grey[200], splashColor: Colors.grey[200],
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0), padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
child: Row( child: Row(
spacing: 10.0, spacing: 10.0,
children: <Widget>[ children: <Widget>[
// //
ClipOval( ClipOval(
child: NetworkOrAssetImage( child: NetworkOrAssetImage(
imageUrl: chatList[index].faceUrl, imageUrl: chatList[index].faceUrl,
width: 50, width: 50,
height: 50, height: 50,
),
), ),
),
// //
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
chatList[index].conversation.showName ?? '未知',
style: TextStyle(
fontSize: (isAdmin || isNoFriend) ? 20 : 16,
fontWeight: (isAdmin || isNoFriend) ? FontWeight.bold : FontWeight.normal),
),
const SizedBox(height: 2.0),
Text(
chatList[index].conversation.lastMessage != null
? parseMessageSummary(chatList[index].conversation.lastMessage!)
: '',
style: const TextStyle(color: Colors.grey, fontSize: 13.0),
overflow: TextOverflow.ellipsis,
),
],
),
),
//
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[ children: <Widget>[
Text( Visibility(
chatList[index].conversation.showName ?? '未知', visible: !(isAdmin || isNoFriend),
style: TextStyle( child: Text(
fontSize: (isAdmin || isNoFriend) ? 20 : 16, //
fontWeight: (isAdmin || isNoFriend) ? FontWeight.bold : FontWeight.normal), // DateTime.fromMillisecondsSinceEpoch(
// (chatList[index].conversation.lastMessage!.timestamp ?? 0) * 1000,
// ).toLocal().toString().substring(0, 16), //
Utils.formatTime(
chatList[index].conversation.lastMessage!.timestamp ?? DateTime.now().millisecondsSinceEpoch ~/ 1000),
style: const TextStyle(color: Colors.grey, fontSize: 12.0),
),
), ),
const SizedBox(height: 2.0), const SizedBox(height: 5.0),
Text( //
chatList[index].conversation.lastMessage != null ? parseMessageSummary(chatList[index].conversation.lastMessage!) : '', Visibility(
style: const TextStyle(color: Colors.grey, fontSize: 13.0), visible: (chatList[index].conversation.unreadCount ?? 0) > 0,
overflow: TextOverflow.ellipsis, child: FStyle.badge(chatList[index].conversation.unreadCount ?? 0),
), ),
], ],
), ),
), Visibility(
// visible: (isAdmin || isNoFriend),
child: const Icon(
Column( Icons.arrow_forward_ios,
crossAxisAlignment: CrossAxisAlignment.end, color: Colors.blueGrey,
children: <Widget>[ size: 14.0,
Visibility(
visible: !(isAdmin || isNoFriend),
child: Text(
//
// DateTime.fromMillisecondsSinceEpoch(
// (chatList[index].conversation.lastMessage!.timestamp ?? 0) * 1000,
// ).toLocal().toString().substring(0, 16), //
Utils.formatTime(chatList[index].conversation.lastMessage!.timestamp ?? DateTime.now().millisecondsSinceEpoch ~/ 1000),
style: const TextStyle(color: Colors.grey, fontSize: 12.0),
),
), ),
const SizedBox(height: 5.0),
//
Visibility(
visible: (chatList[index].conversation.unreadCount ?? 0) > 0,
child: FStyle.badge(chatList[index].conversation.unreadCount ?? 0),
),
],
),
Visibility(
visible: (isAdmin || isNoFriend),
child: const Icon(
Icons.arrow_forward_ios,
color: Colors.blueGrey,
size: 14.0,
), ),
), ],
], ),
), ),
onTap: () {
if (conversationTypeFromString(chatList[index].isCustomAdmin) != null) {
//
logger.e(chatList[index].isCustomAdmin);
Get.toNamed('/${chatList[index].isCustomAdmin}', arguments: chatList[index].conversation.lastMessage);
} else if (chatList[index].conversation.conversationGroupList!.contains(ConversationType.noFriend.name)) {
//
logger.e(chatList[index].conversation.conversationGroupList);
Get.toNamed('/noFriend');
} else {
// id查询会话详情
Get.toNamed('/chat', arguments: chatList[index].conversation);
}
},
onTapDown: (TapDownDetails details) {
posDX = details.globalPosition.dx;
posDY = details.globalPosition.dy;
},
onLongPress: () {
showContextMenu(context, chatList[index]);
},
), ),
onTap: () { );
if (conversationTypeFromString(chatList[index].isCustomAdmin) != null) { },
// );
logger.e(chatList[index].isCustomAdmin); })),
Get.toNamed('/${chatList[index].isCustomAdmin}', arguments: chatList[index].conversation.lastMessage);
} else if (chatList[index].conversation.conversationGroupList!.contains(ConversationType.noFriend.name)) {
//
logger.e(chatList[index].conversation.conversationGroupList);
Get.toNamed('/noFriend');
} else {
// id查询会话详情
Get.toNamed('/chat', arguments: chatList[index].conversation);
}
},
onTapDown: (TapDownDetails details) {
posDX = details.globalPosition.dx;
posDY = details.globalPosition.dy;
},
onLongPress: () {
showContextMenu(context, chatList[index]);
},
),
);
},
);
},
),
), ),
], ],
), ),

View File

@ -1,304 +0,0 @@
///
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: 18, fontWeight: FontWeight.bold), style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
), ),
actions: [], actions: [],
), ),
@ -137,8 +137,6 @@ 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,19 +20,11 @@ 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 {
logger.e('当前会话数据未拉取到,执行插入逻辑'); // id查询会话检测是否存在
// insert进去 convlist做去重处理 logger.e('会话不存在,更新会话失败');
convList.insert(0, conversation); // insert进去 convlist做去重处理
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

@ -1,219 +0,0 @@
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,9 +254,8 @@ 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(

View File

@ -1,304 +0,0 @@
///
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),
),
),
],
),
),
);
},
);
},
),
),
],
),
),
);
}
}

View File

@ -1,304 +0,0 @@
///
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,10 +369,7 @@ 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),
@ -466,37 +463,6 @@ 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;
@ -758,6 +724,104 @@ 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(
@ -772,46 +836,21 @@ 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('获赞')]),
GestureDetector( Column(children: [
onTap: () async { Text('${followInfo.value?.mutualFollowersCount ?? 0}', style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)),
// SizedBox(height: 3.0),
await Get.toNamed('/eachFlow'); Text('互关')
refreshData(); ]),
}, Column(children: [
child: Column(children: [ Text('${followInfo.value?.followingCount ?? 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: [
), Text('${followInfo.value?.followersCount ?? 0}', style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)),
GestureDetector( SizedBox(height: 3.0),
onTap: () async { Text('粉丝')
// ]),
await Get.toNamed('/flow');
refreshData();
},
child: Column(children: [
Text('${followInfo.value?.followingCount ?? 0}', style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)),
SizedBox(height: 3.0),
Text('关注')
]),
),
GestureDetector(
onTap: () async {
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

@ -1,304 +0,0 @@
///
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,7 +5,6 @@ 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';
@ -322,13 +321,9 @@ 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',
), ),
); );
}), }),
@ -428,7 +423,7 @@ class _UserInfoState extends State<UserInfo> {
maxLines: 1, maxLines: 1,
); );
} else { } else {
String wxText = val['openId'] == null || val['openId'] == '' ? '未授权' : '已授权'; String wxText = val['unionId'] == null || val['unionId'] == '' ? '未授权' : '已授权';
return Row( return Row(
children: [ children: [
Spacer(), Spacer(),
@ -478,13 +473,9 @@ class _UserInfoState extends State<UserInfo> {
child: CircleAvatar( child: CircleAvatar(
radius: 60, radius: 60,
backgroundColor: Colors.white, backgroundColor: Colors.white,
child: ClipOval( child: CircleAvatar(
child: NetworkOrAssetImage( radius: 57,
imageUrl: avatar, backgroundImage: avatar.isNotEmpty ? NetworkImage(avatar) : const AssetImage('assets/images/avatar/img11.jpg') as ImageProvider,
width: double.infinity,
height: double.infinity,
fit: BoxFit.cover,
),
), ),
), ),
), ),

View File

@ -2,7 +2,6 @@ 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';
@ -203,105 +202,105 @@ 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(
controller: scrollController, controller: scrollController,
physics: shouldFixHeader.value ? const OnlyDownScrollPhysics(parent: AlwaysScrollableScrollPhysics()) : const AlwaysScrollableScrollPhysics(), physics: shouldFixHeader.value ? const OnlyDownScrollPhysics(parent: AlwaysScrollableScrollPhysics()) : const AlwaysScrollableScrollPhysics(),
overscrollBehavior: OverscrollBehavior.outer, overscrollBehavior: OverscrollBehavior.outer,
headerSliverBuilder: (context, innerBoxIsScrolled) { headerSliverBuilder: (context, innerBoxIsScrolled) {
return [ return [
SliverAppBar( SliverAppBar(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
surfaceTintColor: Colors.transparent, surfaceTintColor: Colors.transparent,
expandedHeight: 180.0, expandedHeight: 180.0,
collapsedHeight: 120.0, collapsedHeight: 120.0,
pinned: true, pinned: true,
stretch: true, stretch: true,
leading: IconButton( leading: IconButton(
icon: const Icon(Icons.arrow_back), icon: const Icon(Icons.arrow_back),
onPressed: () { onPressed: () {
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,
}); });
},
),
onStretchTrigger: () async {
logger.i('触发 stretch 拉伸');
//
}, },
flexibleSpace: Obx(() {
userInfo.value;
return _buildFlexibleSpace();
}),
), ),
SliverToBoxAdapter( onStretchTrigger: () async {
child: Padding( logger.i('触发 stretch 拉伸');
padding: const EdgeInsets.all(10.0), //
child: Column( },
children: [ flexibleSpace: Obx(() {
Obx(() => _buildStatsCard()), userInfo.value;
const SizedBox(height: 10.0), return _buildFlexibleSpace();
Obx(() => _buildInfoDesc(context)), }),
const SizedBox(height: 10.0), ),
Obx(() => _buildFoucsButton(context)), SliverToBoxAdapter(
], child: Padding(
), padding: const EdgeInsets.all(10.0),
child: Column(
children: [
Obx(() => _buildStatsCard()),
const SizedBox(height: 10.0),
Obx(() => _buildInfoDesc(context)),
const SizedBox(height: 10.0),
Obx(() => _buildFoucsButton(context)),
],
), ),
), ),
SliverPersistentHeader( ),
pinned: true, SliverPersistentHeader(
delegate: CustomStickyHeader( pinned: true,
child: PreferredSize( delegate: CustomStickyHeader(
preferredSize: const Size.fromHeight(48.0), child: PreferredSize(
child: Container( preferredSize: const Size.fromHeight(48.0),
color: Colors.white, child: Container(
child: TabBar( color: Colors.white,
controller: tabController, child: TabBar(
tabs: tabList.map((item) { controller: tabController,
return Tab( tabs: tabList.map((item) {
child: Badge.count( return Tab(
backgroundColor: Colors.red, child: Badge.count(
count: item['badge'] ?? 0, backgroundColor: Colors.red,
isLabelVisible: item['badge'] != null, count: item['badge'] ?? 0,
alignment: Alignment.topRight, isLabelVisible: item['badge'] != null,
offset: const Offset(14, -6), alignment: Alignment.topRight,
child: Text(item['name'], style: const TextStyle(fontWeight: FontWeight.bold)), offset: const Offset(14, -6),
), child: Text(item['name'], style: const TextStyle(fontWeight: FontWeight.bold)),
); ),
}).toList(), );
isScrollable: true, // }).toList(),
tabAlignment: TabAlignment.start, isScrollable: true, //
overlayColor: WidgetStateProperty.all(Colors.transparent), tabAlignment: TabAlignment.start,
unselectedLabelColor: Colors.black87, overlayColor: WidgetStateProperty.all(Colors.transparent),
labelColor: Colors.black, unselectedLabelColor: Colors.black87,
indicator: const UnderlineTabIndicator(borderSide: BorderSide(color: Colors.transparent, width: 2.0)), labelColor: Colors.black,
indicatorSize: TabBarIndicatorSize.label, indicator: const UnderlineTabIndicator(borderSide: BorderSide(color: Colors.transparent, width: 2.0)),
unselectedLabelStyle: const TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei'), indicatorSize: TabBarIndicatorSize.label,
labelStyle: const TextStyle(fontSize: 18.0, fontFamily: 'Microsoft YaHei', fontWeight: FontWeight.bold), unselectedLabelStyle: const TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei'),
dividerHeight: 0, labelStyle: const TextStyle(fontSize: 18.0, fontFamily: 'Microsoft YaHei', fontWeight: FontWeight.bold),
padding: const EdgeInsets.symmetric(horizontal: 10.0), dividerHeight: 0,
labelPadding: const EdgeInsets.symmetric(horizontal: 15.0), padding: const EdgeInsets.symmetric(horizontal: 10.0),
), labelPadding: const EdgeInsets.symmetric(horizontal: 15.0),
), ),
), ),
), ),
), ),
]; ),
}, ];
body: TabBarView( },
controller: tabController, body: TabBarView(
children: [ controller: tabController,
// Tab 1: children: [
Obx(() => _buildGridTab(0)), // Tab 1:
], Obx(() => _buildGridTab(0)),
), ],
); ),
}), );
), }),
),
); );
} }
@ -388,6 +387,7 @@ 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,9 +395,8 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
child: Opacity( child: Opacity(
opacity: 1.0, opacity: 1.0,
child: NetworkOrAssetImage( child: NetworkOrAssetImage(
imageUrl: userInfo.value.customInfo?['coverBg'], imageUrl: coverBg,
width: double.infinity, width: double.infinity,
placeholderAsset: 'assets/images/bk.jpg',
), ),
), ),
), ),
@ -411,11 +410,7 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[ children: <Widget>[
ClipOval( ClipOval(
child: NetworkOrAssetImage( child: NetworkOrAssetImage(imageUrl: userInfo.value.faceUrl),
imageUrl: userInfo.value.faceUrl,
width: 80,
height: 80,
),
), ),
const SizedBox(width: 15.0), const SizedBox(width: 15.0),
Expanded( Expanded(
@ -545,42 +540,19 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
child: child, child: child,
); );
}, },
child: [1, 2, 3].contains(followed.value) child: [1, 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 {
logger.w('点击已关注/已回关:${followed.value}'); print('点击已关注');
final ctl = Get.find<ChatController>(); final res = await ImService.instance.unfollowUser(userIDList: [vlogerId]);
if (res.success) {
if (followed.value == 2) { // 1032
// followed.value = followed.value == 1 ? 0 : 2;
final res = await ImService.instance.followUser(userIDList: [vlogerId]); // group
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]);
if (res.success) {
// 1032
followed.value = followed.value == 1 ? 0 : 2;
ctl.mergeNoFriend(conversationID: 'c2c_${userInfo.value.userID}');
}
} }
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
@ -593,18 +565,16 @@ 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 : '未知状态'),
? '已互关'
: '未知状态'),
), ),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
Expanded( Expanded(
child: ElevatedButton( child: ElevatedButton(
onPressed: () async { onPressed: () async {
logger.w('私信'); print('私信');
// //
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;
@ -644,12 +614,12 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
child: ElevatedButton.icon( child: ElevatedButton.icon(
onPressed: () async { onPressed: () async {
// 02 // 02
logger.w('点击关注'); print('点击关注');
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;
@ -659,13 +629,14 @@ 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: Text('关注'), label: const Text('关注'),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: FStyle.primaryColor, backgroundColor: FStyle.primaryColor,
foregroundColor: Colors.white, foregroundColor: Colors.white,

View File

@ -1,11 +1,13 @@
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/api/common_api.dart';
import 'package:loopin/components/my_toast.dart';
import 'package:loopin/service/http.dart'; import 'package:loopin/service/http.dart';
import 'package:loopin/api/common_api.dart';
import 'package:loopin/utils/index.dart'; import 'package:loopin/utils/index.dart';
import 'package:loopin/components/my_toast.dart';
import '../../behavior/custom_scroll_behavior.dart'; import '../../behavior/custom_scroll_behavior.dart';
class SearchResultPage extends StatefulWidget { class SearchResultPage extends StatefulWidget {
@ -196,7 +198,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);
@ -268,78 +270,76 @@ 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}');
print('此用户UserId------------->$vlogerId'); if (doIFollowVloger == null || doIFollowVloger == false) {
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) {
return const Center( return const Center(
child: Padding( child: Padding(
padding: EdgeInsets.symmetric(vertical: 8.0), // padding: EdgeInsets.symmetric(vertical: 8.0), //
child: SizedBox( child: SizedBox(
width: 20, width: 20,
height: 20, height: 20,
child: CircularProgressIndicator(strokeWidth: 2), child: CircularProgressIndicator(strokeWidth: 2),
),
), ),
); ),
} );
bool hasMore;
switch (tabIndex) {
case 0:
hasMore = _videoHasMore;
break;
case 1:
hasMore = _productHasMore;
break;
case 2:
hasMore = _userHasMore;
break;
default:
hasMore = false;
}
if (!hasMore && _getCurrentResults(tabIndex).isNotEmpty) {
return const Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 8.0), //
child: Text(
'没有更多数据了',
style: TextStyle(
fontSize: 12, //
color: Colors.grey,
),
),
),
);
}
return Container();
} }
bool hasMore;
switch (tabIndex) {
case 0:
hasMore = _videoHasMore;
break;
case 1:
hasMore = _productHasMore;
break;
case 2:
hasMore = _userHasMore;
break;
default:
hasMore = false;
}
if (!hasMore && _getCurrentResults(tabIndex).isNotEmpty) {
return const Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 8.0), //
child: Text(
'没有更多数据了',
style: TextStyle(
fontSize: 12, //
color: Colors.grey,
),
),
),
);
}
return Container();
}
// Tab的数据 // Tab的数据
List<dynamic> _getCurrentResults(int tabIndex) { List<dynamic> _getCurrentResults(int tabIndex) {
switch (tabIndex) { switch (tabIndex) {
@ -476,7 +476,9 @@ 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 && !_isLoadingMore && _videoHasMore) { if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent &&
!_isLoadingMore &&
_videoHasMore) {
_loadMoreData(0); _loadMoreData(0);
} }
return false; return false;
@ -578,14 +580,14 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end, // mainAxisAlignment: MainAxisAlignment.end, //
children: [ children: [
Text( Text(
video['title']?.toString() ?? '无标题', video['title']?.toString() ?? '无标题',
style: const TextStyle( style: const TextStyle(
fontSize: 11, fontSize: 11,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
maxLines: 1, // 1 maxLines: 1, // 1
overflow: TextOverflow.ellipsis, // overflow: TextOverflow.ellipsis, //
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Row( Row(
@ -603,7 +605,9 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
) )
: null, : null,
), ),
child: video['avatar'] == null || video['avatar'].toString().isEmpty ? const Icon(Icons.person, size: 10, color: Colors.grey) : null, child: video['avatar'] == null || video['avatar'].toString().isEmpty
? const Icon(Icons.person, size: 10, color: Colors.grey)
: null,
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
Expanded( Expanded(
@ -623,7 +627,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.graceNumber(video['likeCounts'] ?? 0), Utils().formatLikeCount(video['likeCounts'] ?? 0),
style: const TextStyle( style: const TextStyle(
fontSize: 9, fontSize: 9,
color: Colors.grey, color: Colors.grey,
@ -684,7 +688,9 @@ 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 && !_isLoadingMore && _productHasMore) { if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent &&
!_isLoadingMore &&
_productHasMore) {
_loadMoreData(1); _loadMoreData(1);
} }
return false; return false;
@ -749,97 +755,97 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
return Container(); return Container();
} }
Widget _buildProductItem(Map<String, dynamic> product) { Widget _buildProductItem(Map<String, dynamic> product) {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
// //
print('点击了商品: ${product['id']}'); print('点击了商品: ${product['id']}');
Get.toNamed('/goods', arguments: {'goodsId': product['id']}); Get.toNamed('/goods', arguments: {'goodsId': product['id']});
}, },
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(_itemCornerRadius), borderRadius: BorderRadius.circular(_itemCornerRadius),
color: Colors.white, color: Colors.white,
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.grey.withOpacity(0.1), color: Colors.grey.withOpacity(0.1),
blurRadius: 4, blurRadius: 4,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
], ],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// - 100%
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(_itemCornerRadius),
topRight: Radius.circular(_itemCornerRadius),
),
child: Container(
width: double.infinity,
color: Colors.grey[200],
child: product['pic'] != null
? Image.network(
product['pic'].toString(),
fit: BoxFit.cover,
)
: SizedBox(
height: 110,
child: const Center(
child: Icon(Icons.shopping_bag, color: Colors.grey, size: 32),
),
),
),
),
// -
Expanded(
child: Padding(
padding: const EdgeInsets.all(6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end, //
children: [
Text(
product['name']?.toString() ?? '未知商品',
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w500,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'¥${product['price']?.toString() ?? '0.00'}',
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.pink,
),
),
Text(
'已售 ${product['sales']?.toString() ?? '0'}',
style: const TextStyle(
fontSize: 9,
color: Colors.grey,
),
),
],
),
],
),
),
),
],
),
), ),
); child: Column(
} crossAxisAlignment: CrossAxisAlignment.start,
children: [
// - 100%
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(_itemCornerRadius),
topRight: Radius.circular(_itemCornerRadius),
),
child: Container(
width: double.infinity,
color: Colors.grey[200],
child: product['pic'] != null
? Image.network(
product['pic'].toString(),
fit: BoxFit.cover,
)
: Container(
height: 110,
child: const Center(
child: Icon(Icons.shopping_bag, color: Colors.grey, size: 32),
),
),
),
),
// -
Expanded(
child: Padding(
padding: const EdgeInsets.all(6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end, //
children: [
Text(
product['name']?.toString() ?? '未知商品',
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w500,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'¥${product['price']?.toString() ?? '0.00'}',
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.pink,
),
),
Text(
'已售 ${product['sales']?.toString() ?? '0'}',
style: const TextStyle(
fontSize: 9,
color: Colors.grey,
),
),
],
),
],
),
),
),
],
),
),
);
}
// Tab // Tab
Widget _buildUserTab() { Widget _buildUserTab() {
@ -849,7 +855,9 @@ 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 && !_isLoadingMore && _userHasMore) { if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent &&
!_isLoadingMore &&
_userHasMore) {
_loadMoreData(2); _loadMoreData(2);
} }
return false; return false;
@ -864,7 +872,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);
}, },
), ),
), ),
@ -874,7 +882,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),
@ -904,7 +912,9 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
) )
: null, : null,
), ),
child: user['avatar'] == null ? const Icon(Icons.person, color: Colors.grey, size: 20) : null, child: user['avatar'] == null
? const Icon(Icons.person, color: Colors.grey, size: 20)
: null,
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
Expanded( Expanded(
@ -941,7 +951,9 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
), ),
// //
side: isFollowing ? BorderSide(color: Colors.grey[400]!, width: 0.5) : BorderSide.none, side: isFollowing
? 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,14 +1247,17 @@ class _RecommendModuleState extends State<RecommendModule> {
), ),
], ],
), ),
onTap: () async { onTap: ()async {
player.pause(); player.pause();
// //
final result = await Get.toNamed('/report', arguments: videoList[videoModuleController.videoPlayIndex.value]); final result = await Get.toNamed(
if (result != null) { '/report',
player.play(); arguments: videoList[videoModuleController
} .videoPlayIndex.value]);
}, if (result != null) {
player.play();
};
},
), ),
], ],
), ),

View File

@ -10,19 +10,15 @@ 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';
/* 引入路由页面 */ /* 引入路由页面 */
@ -46,11 +42,8 @@ final Map<String, Widget> routes = {
'/vloger': const Vloger(), '/vloger': const Vloger(),
'/report': const ReportPage(), '/report': const ReportPage(),
'/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(),
@ -63,18 +56,6 @@ 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,7 +10,6 @@ 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,34 +5,26 @@ import 'package:flutter/material.dart';
class FStyle { class FStyle {
// Badge // Badge
static badge(int count, {Color color = Colors.red, bool isdot = false, double height = 16.0, double width = 16.0}) { static badge(int count, {
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 child: isdot ? null : Text('$num', style: const TextStyle(color: Colors.white, fontSize: 10.0,)),
? 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 {
} }
// //
static String graceNumber(int number) { 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 {
} }
} }
static String _trimTrailingZero(double number) { String _trimTrailingZero(double number) {
return number.toStringAsFixed(1).replaceAll(RegExp(r'\.0$'), ''); return number.toStringAsFixed(1).replaceAll(RegExp(r'\.0$'), '');
} }
// //
static String formatChatTime(int timestamp) { String formatChatTime(int timestamp) {
// timestamp DateTime // timestamp DateTime
DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
@ -189,29 +189,31 @@ class Utils {
'${messageTime.minute.toString().padLeft(2, '0')}'; '${messageTime.minute.toString().padLeft(2, '0')}';
} }
static String _formatHourMinute(DateTime dt) { 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";
} }
static String _weekdayLabel(int weekday) { String _weekdayLabel(int weekday) {
const weekdays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]; const weekdays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
return weekdays[(weekday - 1) % 7]; return weekdays[(weekday - 1) % 7];
} }
//
static String getTipText(int realFollowType) { String formatLikeCount(int count) {
switch (realFollowType) { if (count >= 10000) {
case 0: return '${(count / 10000).toStringAsFixed(1)}w';
return '关注'; } else if (count >= 1000) {
case 1: return '${(count / 1000).toStringAsFixed(1)}k';
return '已关注';
case 2:
return '回关';
case 3:
return '已互关';
default:
return '未知状态';
} }
return count.toString();
}
//
static String fenToYuanSimple(dynamic fen) {
if (fen == null) {
return '0.00';
}
return (double.parse(fen) / 100).toStringAsFixed(2);
} }
} }

View File

@ -1,5 +1,6 @@
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';
@ -52,8 +53,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: