bug fix
This commit is contained in:
parent
7bce08a3b4
commit
e9ee3e9c02
@ -581,6 +581,14 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
// 监听消息扩展更新
|
// 监听消息扩展更新
|
||||||
onRecvMessageExtensionsChanged: (msgID, extensions) {
|
onRecvMessageExtensionsChanged: (msgID, extensions) {
|
||||||
for (var ext in extensions) {
|
for (var ext in extensions) {
|
||||||
|
if (ext.extensionKey == scChatBubbleMessageExtensionKey) {
|
||||||
|
_MessageItem.updateChatBubbleExtensionCache(
|
||||||
|
msgID,
|
||||||
|
ext.extensionValue,
|
||||||
|
);
|
||||||
|
setState(() {});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (ext.extensionKey == "packetID") {
|
if (ext.extensionKey == "packetID") {
|
||||||
String packetID = ext.extensionValue;
|
String packetID = ext.extensionValue;
|
||||||
SCMessageUtils.redPacketFutureCache[packetID] =
|
SCMessageUtils.redPacketFutureCache[packetID] =
|
||||||
@ -985,6 +993,9 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
|
|
||||||
class _MessageItem extends StatelessWidget {
|
class _MessageItem extends StatelessWidget {
|
||||||
static bool _isOpeningCpInviteDialog = false;
|
static bool _isOpeningCpInviteDialog = false;
|
||||||
|
static final Map<String, Future<PropsResources?>>
|
||||||
|
_chatBubbleExtensionFutures = {};
|
||||||
|
static final Map<String, PropsResources?> _chatBubbleExtensionCache = {};
|
||||||
|
|
||||||
final V2TimMessage message;
|
final V2TimMessage message;
|
||||||
final V2TimMessage? preMessage;
|
final V2TimMessage? preMessage;
|
||||||
@ -2630,7 +2641,74 @@ class _MessageItem extends StatelessWidget {
|
|||||||
if (chatBubbleUrl.isNotEmpty) {
|
if (chatBubbleUrl.isNotEmpty) {
|
||||||
return _buildVipTextBubble(content: content, imageUrl: chatBubbleUrl);
|
return _buildVipTextBubble(content: content, imageUrl: chatBubbleUrl);
|
||||||
}
|
}
|
||||||
return _buildDefaultTextBubble(content);
|
final msgID = message.msgID?.trim();
|
||||||
|
if (msgID == null ||
|
||||||
|
msgID.isEmpty ||
|
||||||
|
message.elemType != MessageElemType.V2TIM_ELEM_TYPE_TEXT) {
|
||||||
|
return _buildDefaultTextBubble(content);
|
||||||
|
}
|
||||||
|
return FutureBuilder<PropsResources?>(
|
||||||
|
future: _chatBubbleFromMessageExtension(message),
|
||||||
|
builder: (_, snapshot) {
|
||||||
|
final extensionBubbleUrl = _resolveChatBubbleImageUrl(snapshot.data);
|
||||||
|
if (extensionBubbleUrl.isNotEmpty) {
|
||||||
|
return _buildVipTextBubble(
|
||||||
|
content: content,
|
||||||
|
imageUrl: extensionBubbleUrl,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _buildDefaultTextBubble(content);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void updateChatBubbleExtensionCache(
|
||||||
|
String msgID,
|
||||||
|
String extensionValue,
|
||||||
|
) {
|
||||||
|
final id = msgID.trim();
|
||||||
|
if (id.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_chatBubbleExtensionCache[id] = scDecodeChatBubbleSnapshotValue(
|
||||||
|
extensionValue,
|
||||||
|
);
|
||||||
|
_chatBubbleExtensionFutures.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<PropsResources?> _chatBubbleFromMessageExtension(
|
||||||
|
V2TimMessage message,
|
||||||
|
) {
|
||||||
|
final msgID = message.msgID?.trim();
|
||||||
|
if (msgID == null || msgID.isEmpty) {
|
||||||
|
return Future.value(null);
|
||||||
|
}
|
||||||
|
if (_chatBubbleExtensionCache.containsKey(msgID)) {
|
||||||
|
return Future.value(_chatBubbleExtensionCache[msgID]);
|
||||||
|
}
|
||||||
|
return _chatBubbleExtensionFutures.putIfAbsent(msgID, () async {
|
||||||
|
if (message.isSupportMessageExtension == false) {
|
||||||
|
_chatBubbleExtensionCache[msgID] = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final result = await TencentImSDKPlugin.v2TIMManager
|
||||||
|
.getMessageManager()
|
||||||
|
.getMessageExtensions(msgID: msgID);
|
||||||
|
final extensions = result.data ?? const <V2TimMessageExtension>[];
|
||||||
|
for (final extension in extensions) {
|
||||||
|
if (extension.extensionKey == scChatBubbleMessageExtensionKey) {
|
||||||
|
final bubble = scDecodeChatBubbleSnapshotValue(
|
||||||
|
extension.extensionValue,
|
||||||
|
);
|
||||||
|
_chatBubbleExtensionCache[msgID] = bubble;
|
||||||
|
return bubble;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
_chatBubbleExtensionCache[msgID] = null;
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDefaultTextBubble(String content) {
|
Widget _buildDefaultTextBubble(String content) {
|
||||||
|
|||||||
@ -776,14 +776,7 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
targetGroupKey,
|
targetGroupKey,
|
||||||
targetUserIds,
|
targetUserIds,
|
||||||
);
|
);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
_handleStandardGiftComboVisuals(
|
|
||||||
msg,
|
|
||||||
giftPhoto,
|
|
||||||
targetGroupKey,
|
|
||||||
targetUserIds,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _enqueueCpGiftFloatingIfNeeded(Msg msg) {
|
void _enqueueCpGiftFloatingIfNeeded(Msg msg) {
|
||||||
@ -946,45 +939,6 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleStandardGiftComboVisuals(
|
|
||||||
Msg msg,
|
|
||||||
String giftPhoto,
|
|
||||||
String? targetGroupKey,
|
|
||||||
List<String> targetUserIds,
|
|
||||||
) {
|
|
||||||
if ((msg.number ?? 0) <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!_shouldPlaySeatFlightGiftAnimation(msg) ||
|
|
||||||
targetGroupKey == null ||
|
|
||||||
targetUserIds.isEmpty ||
|
|
||||||
giftPhoto.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final flightBatchKey = _buildGiftFlightBatchKey(msg, targetUserIds);
|
|
||||||
if (targetUserIds.length > 1 &&
|
|
||||||
!_markGiftFlightBatchForPlayback(flightBatchKey)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final sessionKey = _buildLuckyGiftComboSessionKey(msg, targetGroupKey);
|
|
||||||
final session = _luckyGiftComboSessions.putIfAbsent(
|
|
||||||
sessionKey,
|
|
||||||
() => _LuckyGiftComboSession(),
|
|
||||||
);
|
|
||||||
session.endTimer?.cancel();
|
|
||||||
session.clearQueueTimer?.cancel();
|
|
||||||
|
|
||||||
_enqueueTrackedSeatFlightAnimations(
|
|
||||||
sessionKey: sessionKey,
|
|
||||||
giftPhoto: giftPhoto,
|
|
||||||
targetUserIds: targetUserIds,
|
|
||||||
animationCount: _resolveTrackedAnimationCount(msg),
|
|
||||||
batchKey: flightBatchKey,
|
|
||||||
);
|
|
||||||
|
|
||||||
_scheduleGiftAnimationSessionEnd(sessionKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleLuckyGiftComboVisuals(
|
void _handleLuckyGiftComboVisuals(
|
||||||
Msg msg,
|
Msg msg,
|
||||||
String giftPhoto,
|
String giftPhoto,
|
||||||
@ -1357,26 +1311,6 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
_giftVisualBatchDedupeTimers.clear();
|
_giftVisualBatchDedupeTimers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _shouldPlaySeatFlightGiftAnimation(Msg msg) {
|
|
||||||
final gift = msg.gift;
|
|
||||||
if (gift == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final giftPhoto = (gift.giftPhoto ?? "").trim();
|
|
||||||
if (giftPhoto.isEmpty) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final giftPhotoExt = _normalizedGiftResourceExtension(giftPhoto);
|
|
||||||
if (_isAnimatedGiftResource(giftPhotoExt)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final giftSourceUrl = (gift.giftSourceUrl ?? "").trim();
|
|
||||||
final sourceExt = _normalizedGiftResourceExtension(giftSourceUrl);
|
|
||||||
return !_isAnimatedGiftResource(sourceExt);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _isAnimatedGiftResource(String extension) {
|
bool _isAnimatedGiftResource(String extension) {
|
||||||
return extension == ".svga" || extension == ".mp4" || extension == ".vap";
|
return extension == ".svga" || extension == ".mp4" || extension == ".vap";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,6 +41,7 @@ import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation_result.dart';
|
|||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_info.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_info.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_image_elem.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_image_elem.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:tencent_cloud_chat_sdk/models/v2_tim_message_extension.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_receipt.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_receipt.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_msg_create_info_result.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_msg_create_info_result.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
|
||||||
@ -2644,14 +2645,18 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
if (createTextMessageRes.code == 0) {
|
if (createTextMessageRes.code == 0) {
|
||||||
// 文本信息创建成功
|
// 文本信息创建成功
|
||||||
String id = createTextMessageRes.data!.id!;
|
String id = createTextMessageRes.data!.id!;
|
||||||
|
final chatBubble =
|
||||||
|
AccountStorage().getCurrentUser()?.userProfile?.getChatBox();
|
||||||
final chatBubbleCloudCustomData = scEncodeChatBubbleCloudCustomData(
|
final chatBubbleCloudCustomData = scEncodeChatBubbleCloudCustomData(
|
||||||
AccountStorage().getCurrentUser()?.userProfile?.getChatBox(),
|
chatBubble,
|
||||||
|
);
|
||||||
|
final chatBubbleSnapshotValue = scEncodeChatBubbleSnapshotValue(
|
||||||
|
chatBubble,
|
||||||
);
|
);
|
||||||
// 发送文本消息
|
// 发送文本消息
|
||||||
// 在sendMessage时,若只填写receiver则发个人用户单聊消息
|
// 在sendMessage时,若只填写receiver则发个人用户单聊消息
|
||||||
// 若只填写groupID则发群组消息
|
// 若只填写groupID则发群组消息
|
||||||
// 若填写了receiver与groupID则发群内的个人用户,消息在群聊中显示,只有指定receiver能看见
|
// 若填写了receiver与groupID则发群内的个人用户,消息在群聊中显示,只有指定receiver能看见
|
||||||
String receiveId = toConversation.userID!;
|
|
||||||
V2TimValueCallback<V2TimMessage> sendMessageRes = await TencentImSDKPlugin
|
V2TimValueCallback<V2TimMessage> sendMessageRes = await TencentImSDKPlugin
|
||||||
.v2TIMManager
|
.v2TIMManager
|
||||||
.getMessageManager()
|
.getMessageManager()
|
||||||
@ -2660,11 +2665,20 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
receiver: toConversation.userID!, // 接收人id
|
receiver: toConversation.userID!, // 接收人id
|
||||||
needReadReceipt: true,
|
needReadReceipt: true,
|
||||||
groupID: '', // 是否需要已读回执
|
groupID: '', // 是否需要已读回执
|
||||||
|
isSupportMessageExtension: chatBubbleSnapshotValue != null,
|
||||||
cloudCustomData: chatBubbleCloudCustomData,
|
cloudCustomData: chatBubbleCloudCustomData,
|
||||||
);
|
);
|
||||||
if (sendMessageRes.code == 0) {
|
if (sendMessageRes.code == 0) {
|
||||||
// 发送成功
|
// 发送成功
|
||||||
_onNew1v1Message(sendMessageRes.data);
|
_onNew1v1Message(sendMessageRes.data);
|
||||||
|
if (chatBubbleSnapshotValue != null) {
|
||||||
|
unawaited(
|
||||||
|
_setC2CChatBubbleMessageExtension(
|
||||||
|
sendMessageRes.data?.msgID,
|
||||||
|
chatBubbleSnapshotValue,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
SCTts.show(
|
SCTts.show(
|
||||||
'create fail,code:${sendMessageRes.code},${sendMessageRes.desc}',
|
'create fail,code:${sendMessageRes.code},${sendMessageRes.desc}',
|
||||||
@ -2673,6 +2687,33 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _setC2CChatBubbleMessageExtension(
|
||||||
|
String? msgID,
|
||||||
|
String snapshotValue,
|
||||||
|
) async {
|
||||||
|
final id = msgID?.trim();
|
||||||
|
if (id == null || id.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await TencentImSDKPlugin.v2TIMManager
|
||||||
|
.getMessageManager()
|
||||||
|
.setMessageExtensions(
|
||||||
|
msgID: id,
|
||||||
|
extensions: [
|
||||||
|
V2TimMessageExtension(
|
||||||
|
extensionKey: scChatBubbleMessageExtensionKey,
|
||||||
|
extensionValue: snapshotValue,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
if (kDebugMode) {
|
||||||
|
debugPrint('[chat_bubble] set extension failed msgID=$id error=$error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///发送单聊自定义消息
|
///发送单聊自定义消息
|
||||||
Future<void> sendC2CCustomMsg(
|
Future<void> sendC2CCustomMsg(
|
||||||
String msg,
|
String msg,
|
||||||
|
|||||||
@ -322,37 +322,82 @@ class SCConfigRepositoryImp implements SocialChatConfigRepository {
|
|||||||
///sys/version/manage/release/latest
|
///sys/version/manage/release/latest
|
||||||
@override
|
@override
|
||||||
Future<SCVersionManageLatestRes> versionManageLatest() async {
|
Future<SCVersionManageLatestRes> versionManageLatest() async {
|
||||||
final result = await http.get<SCVersionManageLatestRes>(
|
const apiName = 'releaseLatest';
|
||||||
"ee9584f714ded864780e47dab2cf4a2e84ac21c90fcd0966a13d2ce9e8845eb8e580afbe66f9f0fef79429cd5c1e0687",
|
const path = '/sys/version/manage/release/latest';
|
||||||
fromJson: (json) {
|
const queryParams = <String, dynamic>{};
|
||||||
_debugVersionResponse('releaseLatest', json);
|
_debugVersionRequest(apiName, path, queryParams);
|
||||||
return SCVersionManageLatestRes.fromJson(json);
|
try {
|
||||||
},
|
final result = await http.get<SCVersionManageLatestRes>(
|
||||||
);
|
"ee9584f714ded864780e47dab2cf4a2e84ac21c90fcd0966a13d2ce9e8845eb8e580afbe66f9f0fef79429cd5c1e0687",
|
||||||
return result;
|
queryParams: queryParams,
|
||||||
|
fromJson: (json) {
|
||||||
|
_debugVersionResponse(apiName, path, json);
|
||||||
|
return SCVersionManageLatestRes.fromJson(json);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
_debugVersionError(apiName, path, queryParams, error);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///sys/version/manage/latest/review
|
///sys/version/manage/latest/review
|
||||||
@override
|
@override
|
||||||
Future<VersionManageLatesReviewRes> versionManageLatestReview() async {
|
Future<VersionManageLatesReviewRes> versionManageLatestReview() async {
|
||||||
final result = await http.get<VersionManageLatesReviewRes>(
|
const apiName = 'latestReview';
|
||||||
"ee9584f714ded864780e47dab2cf4a2e11ce42bdd061186d4efe3305b73f10fe574aff257ce7e668d08f4caccd1c6232",
|
const path = '/sys/version/manage/latest/review';
|
||||||
fromJson: (json) {
|
const queryParams = <String, dynamic>{};
|
||||||
_debugVersionResponse('latestReview', json);
|
_debugVersionRequest(apiName, path, queryParams);
|
||||||
return VersionManageLatesReviewRes.fromJson(json);
|
try {
|
||||||
},
|
final result = await http.get<VersionManageLatesReviewRes>(
|
||||||
);
|
"ee9584f714ded864780e47dab2cf4a2e11ce42bdd061186d4efe3305b73f10fe574aff257ce7e668d08f4caccd1c6232",
|
||||||
return result;
|
queryParams: queryParams,
|
||||||
|
fromJson: (json) {
|
||||||
|
_debugVersionResponse(apiName, path, json);
|
||||||
|
return VersionManageLatesReviewRes.fromJson(json);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
_debugVersionError(apiName, path, queryParams, error);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _debugVersionResponse(String name, dynamic json) {
|
void _debugVersionRequest(
|
||||||
|
String name,
|
||||||
|
String path,
|
||||||
|
Map<String, dynamic> queryParams,
|
||||||
|
) {
|
||||||
|
debugPrint(
|
||||||
|
'[SCVersionUtils][API] $name request path=$path params=$queryParams',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _debugVersionResponse(String name, String path, dynamic json) {
|
||||||
try {
|
try {
|
||||||
debugPrint('[SCVersionUtils] $name rawBody=${jsonEncode(json)}');
|
debugPrint(
|
||||||
|
'[SCVersionUtils][API] $name response path=$path rawBody=${jsonEncode(json)}',
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('[SCVersionUtils] $name rawBody=$json encodeError=$e');
|
debugPrint(
|
||||||
|
'[SCVersionUtils][API] $name response path=$path rawBody=$json encodeError=$e',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _debugVersionError(
|
||||||
|
String name,
|
||||||
|
String path,
|
||||||
|
Map<String, dynamic> queryParams,
|
||||||
|
Object error,
|
||||||
|
) {
|
||||||
|
debugPrint(
|
||||||
|
'[SCVersionUtils][API] $name failed path=$path params=$queryParams error=$error',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
///sys/config/customer-service
|
///sys/config/customer-service
|
||||||
@override
|
@override
|
||||||
Future<SocialChatUserProfile> customerService() async {
|
Future<SocialChatUserProfile> customerService() async {
|
||||||
|
|||||||
@ -3,14 +3,19 @@ import 'dart:convert';
|
|||||||
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
||||||
|
|
||||||
const String scChatBubbleCloudDataKey = 'scChatBubble';
|
const String scChatBubbleCloudDataKey = 'scChatBubble';
|
||||||
|
const String scChatBubbleMessageExtensionKey = scChatBubbleCloudDataKey;
|
||||||
|
|
||||||
String? scEncodeChatBubbleCloudCustomData(PropsResources? chatBubble) {
|
String? scEncodeChatBubbleCloudCustomData(PropsResources? chatBubble) {
|
||||||
if (!_hasChatBubbleImage(chatBubble)) {
|
final snapshot = scChatBubbleSnapshotJson(chatBubble);
|
||||||
|
if (snapshot == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return jsonEncode(<String, dynamic>{
|
return jsonEncode(<String, dynamic>{scChatBubbleCloudDataKey: snapshot});
|
||||||
scChatBubbleCloudDataKey: chatBubble!.toJson(),
|
}
|
||||||
});
|
|
||||||
|
String? scEncodeChatBubbleSnapshotValue(PropsResources? chatBubble) {
|
||||||
|
final snapshot = scChatBubbleSnapshotJson(chatBubble);
|
||||||
|
return snapshot == null ? null : jsonEncode(snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
PropsResources? scDecodeChatBubbleFromCloudCustomData(String? cloudCustomData) {
|
PropsResources? scDecodeChatBubbleFromCloudCustomData(String? cloudCustomData) {
|
||||||
@ -31,6 +36,48 @@ PropsResources? scDecodeChatBubbleFromCloudCustomData(String? cloudCustomData) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PropsResources? scDecodeChatBubbleSnapshotValue(String? snapshotValue) {
|
||||||
|
final raw = snapshotValue?.trim();
|
||||||
|
if (raw == null || raw.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final decoded = jsonDecode(raw);
|
||||||
|
if (decoded is Map) {
|
||||||
|
return PropsResources.fromJson(decoded);
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic>? scChatBubbleSnapshotJson(PropsResources? resource) {
|
||||||
|
if (!_hasChatBubbleImage(resource)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final map = <String, dynamic>{};
|
||||||
|
void put(String key, String? value) {
|
||||||
|
final text = value?.trim();
|
||||||
|
if (text != null && text.isNotEmpty) {
|
||||||
|
map[key] = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
put('id', resource?.id);
|
||||||
|
put('name', resource?.name);
|
||||||
|
put('type', resource?.type);
|
||||||
|
put('cover', resource?.cover);
|
||||||
|
put('expand', resource?.expand);
|
||||||
|
put('sourceUrl', resource?.sourceUrl);
|
||||||
|
put('imSendCoverUrl', resource?.imSendCoverUrl);
|
||||||
|
put('imOpenedUrl', resource?.imOpenedUrl);
|
||||||
|
put('imOpenedUrlTwo', resource?.imOpenedUrlTwo);
|
||||||
|
put('imNotOpenedUrl', resource?.imNotOpenedUrl);
|
||||||
|
put('roomSendCoverUrl', resource?.roomSendCoverUrl);
|
||||||
|
put('roomOpenedUrl', resource?.roomOpenedUrl);
|
||||||
|
put('roomNotOpenedUrl', resource?.roomNotOpenedUrl);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
bool _hasChatBubbleImage(PropsResources? resource) {
|
bool _hasChatBubbleImage(PropsResources? resource) {
|
||||||
if (resource == null) {
|
if (resource == null) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -29,10 +29,17 @@ class SCVersionUtils {
|
|||||||
'review=${_versionInfoDebug(versionInfo.review)} '
|
'review=${_versionInfoDebug(versionInfo.review)} '
|
||||||
'latest=${_versionInfoDebug(versionInfo.latest)}',
|
'latest=${_versionInfoDebug(versionInfo.latest)}',
|
||||||
);
|
);
|
||||||
|
final review = _selectCurrentPlatformVersion(
|
||||||
|
versionInfo.review,
|
||||||
|
source: 'latestReview.review',
|
||||||
|
);
|
||||||
if (versionInfo.review != null) {
|
if (versionInfo.review != null) {
|
||||||
SCGlobalConfig.isReview =
|
SCGlobalConfig.isReview =
|
||||||
"${versionInfo.review?.version}" == SCGlobalConfig.version;
|
review != null && "${review.version}" == SCGlobalConfig.version;
|
||||||
_debugLog('checkReview isReview=${SCGlobalConfig.isReview}');
|
_debugLog(
|
||||||
|
'checkReview isReview=${SCGlobalConfig.isReview} '
|
||||||
|
'selectedReview=${_versionInfoDebug(review)}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
final latest = await _resolveLatestVersion(versionInfo.latest);
|
final latest = await _resolveLatestVersion(versionInfo.latest);
|
||||||
final shouldForceUpdate = latest != null && _shouldForceUpdate(latest);
|
final shouldForceUpdate = latest != null && _shouldForceUpdate(latest);
|
||||||
@ -96,20 +103,56 @@ class SCVersionUtils {
|
|||||||
static Future<SCVersionManageLatestRes?> _resolveLatestVersion(
|
static Future<SCVersionManageLatestRes?> _resolveLatestVersion(
|
||||||
SCVersionManageLatestRes? latest,
|
SCVersionManageLatestRes? latest,
|
||||||
) async {
|
) async {
|
||||||
if (!_isVersionInfoEmpty(latest)) {
|
final selectedLatest = _selectCurrentPlatformVersion(
|
||||||
return latest;
|
latest,
|
||||||
|
source: 'latestReview.latest',
|
||||||
|
);
|
||||||
|
if (selectedLatest != null) {
|
||||||
|
return selectedLatest;
|
||||||
}
|
}
|
||||||
_debugLog('checkReview latestReview latest empty, fallback releaseLatest');
|
_debugLog(
|
||||||
|
'checkReview latestReview latest unavailable for current platform, '
|
||||||
|
'fallback releaseLatest',
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
final fallback = await SCConfigRepositoryImp().versionManageLatest();
|
final fallback = await SCConfigRepositoryImp().versionManageLatest();
|
||||||
_debugLog('checkReview fallbackLatest=${_versionInfoDebug(fallback)}');
|
_debugLog('checkReview fallbackLatest=${_versionInfoDebug(fallback)}');
|
||||||
return _isVersionInfoEmpty(fallback) ? latest : fallback;
|
return _selectCurrentPlatformVersion(fallback, source: 'releaseLatest');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_debugLog('checkReview fallback releaseLatest failed error=$e');
|
_debugLog('checkReview fallback releaseLatest failed error=$e');
|
||||||
return latest;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SCVersionManageLatestRes? _selectCurrentPlatformVersion(
|
||||||
|
SCVersionManageLatestRes? version, {
|
||||||
|
required String source,
|
||||||
|
}) {
|
||||||
|
if (_isVersionInfoEmpty(version)) {
|
||||||
|
_debugLog('checkReview ignore $source reason=empty');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final currentVersion = version!;
|
||||||
|
if (_isCurrentPlatformVersion(currentVersion)) {
|
||||||
|
_debugLog(
|
||||||
|
'checkReview select $source currentPlatform=${_currentPlatformName()} '
|
||||||
|
'version=${currentVersion.version} '
|
||||||
|
'buildVersion=${currentVersion.buildVersion} '
|
||||||
|
'downloadUrl=${currentVersion.downloadUrl}',
|
||||||
|
);
|
||||||
|
return currentVersion;
|
||||||
|
}
|
||||||
|
_debugLog(
|
||||||
|
'checkReview ignore $source reason=platform_mismatch '
|
||||||
|
'currentPlatform=${_currentPlatformName()} '
|
||||||
|
'responsePlatform=${currentVersion.platform} '
|
||||||
|
'version=${currentVersion.version} '
|
||||||
|
'buildVersion=${currentVersion.buildVersion} '
|
||||||
|
'downloadUrl=${currentVersion.downloadUrl}',
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
static bool _isVersionInfoEmpty(SCVersionManageLatestRes? version) {
|
static bool _isVersionInfoEmpty(SCVersionManageLatestRes? version) {
|
||||||
if (version == null) {
|
if (version == null) {
|
||||||
return true;
|
return true;
|
||||||
@ -145,6 +188,25 @@ class SCVersionUtils {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool _isCurrentPlatformVersion(SCVersionManageLatestRes version) {
|
||||||
|
return _normalizePlatform(version.platform) == _currentPlatformKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
static String _currentPlatformName() {
|
||||||
|
return Platform.isIOS ? 'iOS' : 'Android';
|
||||||
|
}
|
||||||
|
|
||||||
|
static String _currentPlatformKey() {
|
||||||
|
return Platform.isIOS ? 'ios' : 'android';
|
||||||
|
}
|
||||||
|
|
||||||
|
static String _normalizePlatform(String? platform) {
|
||||||
|
return (platform ?? '').trim().toLowerCase().replaceAll(
|
||||||
|
RegExp(r'[^a-z0-9]'),
|
||||||
|
'',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
static List<int> _versionParts(String? version) {
|
static List<int> _versionParts(String? version) {
|
||||||
final normalized = version?.trim() ?? "";
|
final normalized = version?.trim() ?? "";
|
||||||
if (normalized.isEmpty) {
|
if (normalized.isEmpty) {
|
||||||
@ -277,6 +339,14 @@ class SCVersionUtils {
|
|||||||
|
|
||||||
static Future<void> _openUpdateUrl(SCVersionManageLatestRes latest) async {
|
static Future<void> _openUpdateUrl(SCVersionManageLatestRes latest) async {
|
||||||
final url = _resolveDownloadUrl(latest);
|
final url = _resolveDownloadUrl(latest);
|
||||||
|
final urlSource =
|
||||||
|
(latest.downloadUrl ?? "").trim().isNotEmpty
|
||||||
|
? 'api_downloadUrl'
|
||||||
|
: (Platform.isIOS ? 'local_appStore' : 'local_googlePlay');
|
||||||
|
_debugLog(
|
||||||
|
'openUpdateUrl currentPlatform=${_currentPlatformName()} '
|
||||||
|
'responsePlatform=${latest.platform} source=$urlSource url=$url',
|
||||||
|
);
|
||||||
if (url.isEmpty) {
|
if (url.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user