diff --git a/lib/debug/room_cp_progress_dialog_preview.dart b/lib/debug/room_cp_progress_dialog_preview.dart index 498ac4b..90a0474 100644 --- a/lib/debug/room_cp_progress_dialog_preview.dart +++ b/lib/debug/room_cp_progress_dialog_preview.dart @@ -48,7 +48,7 @@ class _RoomCpProgressDialogPreviewPage extends StatelessWidget { const RoomCpProgressDialog( daysValue: '999', daysLabel: 'Days', - levelText: 'Lv.1 Simple Love', + levelText: 'Lv.0', currentValue: 1, targetValue: 1, expAwayText: '1,000', diff --git a/lib/modules/gift/gift_page.dart b/lib/modules/gift/gift_page.dart index 76eb819..acc4b09 100644 --- a/lib/modules/gift/gift_page.dart +++ b/lib/modules/gift/gift_page.dart @@ -1676,6 +1676,14 @@ class _GiftPageState extends State with TickerProviderStateMixin { if (!_isCpGift(request.gift)) { return; } + if (request.isSelfRecipientRequest) { + _giftFxLog( + 'skip local CP waiting dialog reason=self_recipient ' + 'giftId=${request.gift.id} ' + 'acceptUserIds=${request.acceptUserIds.join(",")}', + ); + return; + } final context = navigatorKey.currentState?.context; final currentProfile = AccountStorage().getCurrentUser()?.userProfile; final receiverProfile = @@ -2359,9 +2367,40 @@ class _GiftSendRequest { if (!scIsCpGift(gift)) { return null; } + if (isSelfRecipientRequest) { + return null; + } return scCpRelationTypeForGift(gift); } + bool get isSelfRecipientRequest { + final currentProfile = AccountStorage().getCurrentUser()?.userProfile; + final currentKeys = _profileIdentityKeys(currentProfile); + if (currentKeys.isEmpty) { + return false; + } + for (final userId in acceptUserIds) { + if (currentKeys.contains(userId.trim())) { + return true; + } + } + for (final acceptUser in acceptUsers) { + final acceptKeys = _profileIdentityKeys(acceptUser.user); + if (acceptKeys.any(currentKeys.contains)) { + return true; + } + } + return false; + } + + Set _profileIdentityKeys(SocialChatUserProfile? profile) { + return { + profile?.id?.trim() ?? "", + profile?.account?.trim() ?? "", + profile?.getID().trim() ?? "", + }..remove(""); + } + String get batchKey { final sortedAcceptUserIds = List.from(acceptUserIds)..sort(); final type = diff --git a/lib/modules/user/profile/person_detail_page.dart b/lib/modules/user/profile/person_detail_page.dart index 92e1160..bceb80d 100644 --- a/lib/modules/user/profile/person_detail_page.dart +++ b/lib/modules/user/profile/person_detail_page.dart @@ -1449,19 +1449,7 @@ class _PersonDetailPageState extends State { } String _normalizeProfileRelationType(String? relationType) { - final value = relationType?.trim().toUpperCase() ?? ""; - switch (value) { - case "BROTHER": - case "BROTHERS": - case "SWORN_BROTHER": - case "SWORN_BROTHERS": - return "BROTHER"; - case "SISTER": - case "SISTERS": - return "SISTERS"; - default: - return "CP"; - } + return scNormalizeCpRelationType(relationType); } bool _isOwnProfile(SocialChatUserProfile? profile) { @@ -1639,7 +1627,12 @@ class _PersonDetailPageState extends State { } String _profileCpLevelAsset(CPRes relation) { - return "$_cpProfileAssetBase/sc_cp_profile_cp_level_${_profileRelationLevel(relation)}.png"; + return "$_cpProfileAssetBase/sc_cp_profile_cp_level_${_profileRelationAssetLevel(relation)}.png"; + } + + int _profileRelationAssetLevel(CPRes relation) { + final level = _profileRelationLevel(relation); + return level <= 0 ? 1 : level; } int _profileRelationLevel(CPRes relation) { @@ -1648,7 +1641,7 @@ class _PersonDetailPageState extends State { String _profileRelationLevelText(CPRes relation) { return _profileRelationLevelDisplayText( - _firstNonBlankProfileValue([relation.levelText, "Lv.1 Simple Love"]), + _firstNonBlankProfileValue([relation.levelText, "Lv.0"]), relation, ); } @@ -2639,10 +2632,7 @@ class _ProfileRelationProgressData { ) { final currentValue = payload.cpValue; return _ProfileRelationProgressData( - levelText: - payload.levelText.trim().isEmpty - ? "Lv.1 Simple Love" - : payload.levelText, + levelText: scCpRelationLevelDisplayText(payload.levelText), currentValue: currentValue, targetValue: currentValue > 0 ? currentValue : 1, expAwayText: SCStringUtils.formatCompactNumber(0), @@ -2762,16 +2752,19 @@ class _ProfileRelationProgressData { ) { final fallbackText = fallback.trim(); if (level == null) { - return fallbackText.isEmpty ? "Lv.1 Simple Love" : fallbackText; + return scCpRelationLevelDisplayText(fallbackText); } final levelName = level.levelName?.trim(); + if (level.level != null && levelName != null && levelName.isNotEmpty) { + return scCpRelationLevelDisplayText("Lv.${level.level} $levelName"); + } if (levelName != null && levelName.isNotEmpty) { - return levelName; + return scCpRelationLevelDisplayText(levelName); } if (level.level != null) { return "Lv.${level.level}"; } - return fallbackText.isEmpty ? "Lv.1 Simple Love" : fallbackText; + return scCpRelationLevelDisplayText(fallbackText); } static String _levelShortText(int? level, {String? fallback}) { @@ -2780,9 +2773,9 @@ class _ProfileRelationProgressData { } final fallbackText = fallback?.trim() ?? ""; if (fallbackText.isNotEmpty) { - return fallbackText; + return scCpRelationLevelDisplayText(fallbackText); } - return "Lv. 1"; + return "Lv. 0"; } static int _levelIndex(String levelText) { @@ -2790,7 +2783,7 @@ class _ProfileRelationProgressData { } static int _nextLevelIndex(int level) { - return (level + 1).clamp(1, 5).toInt(); + return (level + 1).clamp(0, 5).toInt(); } } diff --git a/lib/services/audio/rtc_manager.dart b/lib/services/audio/rtc_manager.dart index 4046c67..f26efd1 100644 --- a/lib/services/audio/rtc_manager.dart +++ b/lib/services/audio/rtc_manager.dart @@ -324,9 +324,11 @@ class RealTimeCommunicationManager extends ChangeNotifier { static const String _roomRocketRewardDialogTag = 'showRoomRocketRewardDialog'; static const String _roomRocketPagLaunchDialogTag = 'showRoomRocketPagLaunchDialog'; - static const Duration _roomRocketGiftRefreshDelay = Duration( - milliseconds: 700, - ); + static const List _roomRocketGiftRefreshDelays = [ + Duration(milliseconds: 700), + Duration(milliseconds: 1600), + Duration(milliseconds: 3200), + ]; static const List _roomRocketPostLaunchRefreshDelays = [ Duration(milliseconds: 800), Duration(seconds: 2), @@ -389,7 +391,7 @@ class RealTimeCommunicationManager extends ChangeNotifier { Timer? _roomEntryEffectTimer; Timer? _roomRocketLaunchAnimationTimer; ValueNotifier? _roomRocketLaunchTopAvatarNotifier; - Timer? _roomRocketGiftRefreshTimer; + final List _roomRocketGiftRefreshTimers = []; final List _roomRocketPostLaunchRefreshTimers = []; final List _roomRocketEntryReadyFallbackTimers = []; Timer? _roomRedPacketPresenceTimer; @@ -546,8 +548,7 @@ class RealTimeCommunicationManager extends ChangeNotifier { _roomRocketLaunchAnimationTimer = null; SmartDialog.dismiss(tag: _roomRocketPagLaunchDialogTag); _releaseRoomRocketLaunchTopAvatarNotifier(); - _roomRocketGiftRefreshTimer?.cancel(); - _roomRocketGiftRefreshTimer = null; + _cancelRoomRocketGiftStatusRefresh(); _cancelRoomRocketEntryReadyFallbacks(); _cancelRoomRocketPostLaunchStatusRefresh(); } @@ -3602,6 +3603,45 @@ class RealTimeCommunicationManager extends ChangeNotifier { _scheduleRoomRocketAssetPreload(resolvedRoomId, res); } + void updateRoomRocketStatusFromRealtime( + SCRoomRocketStatusRes update, { + String? roomId, + }) { + final resolvedRoomId = + (roomId ?? + update.roomId ?? + currenRoom?.roomProfile?.roomProfile?.id ?? + "") + .trim(); + if (resolvedRoomId.isEmpty || + resolvedRoomId != + (currenRoom?.roomProfile?.roomProfile?.id ?? "").trim()) { + return; + } + if (!_hasRoomRocketRealtimeStatusFields(update)) { + return; + } + final merged = + roomRocketStatus?.mergeRealtimeUpdate(update, roomId: resolvedRoomId) ?? + update; + roomRocketStatus = merged; + notifyListeners(); + if (!merged.isEnabled) { + return; + } + _scheduleRoomRocketAssetPreload(resolvedRoomId, merged); + } + + bool _hasRoomRocketRealtimeStatusFields(SCRoomRocketStatusRes update) { + return update.currentLevel != null || + update.level != null || + update.currentEnergy != null || + update.maxEnergy != null || + update.displayPercent != null || + update.energyPercent != null || + (update.levels?.isNotEmpty ?? false); + } + void _scheduleRoomRocketAssetPreload( String roomId, SCRoomRocketStatusRes status, { @@ -3639,11 +3679,25 @@ class RealTimeCommunicationManager extends ChangeNotifier { if (resolvedRoomId.isEmpty) { return; } - _roomRocketGiftRefreshTimer?.cancel(); - _roomRocketGiftRefreshTimer = Timer(_roomRocketGiftRefreshDelay, () { - _roomRocketGiftRefreshTimer = null; - unawaited(refreshRoomRocketStatus(roomId: resolvedRoomId)); - }); + _cancelRoomRocketGiftStatusRefresh(); + for (final delay in _roomRocketGiftRefreshDelays) { + final timer = Timer(delay, () { + _roomRocketGiftRefreshTimers.removeWhere((item) => !item.isActive); + if (resolvedRoomId != + (currenRoom?.roomProfile?.roomProfile?.id ?? "").trim()) { + return; + } + unawaited(refreshRoomRocketStatus(roomId: resolvedRoomId)); + }); + _roomRocketGiftRefreshTimers.add(timer); + } + } + + void _cancelRoomRocketGiftStatusRefresh() { + for (final timer in _roomRocketGiftRefreshTimers) { + timer.cancel(); + } + _roomRocketGiftRefreshTimers.clear(); } void _scheduleRoomRocketEntryReadyFallback(String roomId) { @@ -4464,8 +4518,7 @@ class RealTimeCommunicationManager extends ChangeNotifier { _roomRocketLaunchAnimationTimer = null; SmartDialog.dismiss(tag: _roomRocketPagLaunchDialogTag); _releaseRoomRocketLaunchTopAvatarNotifier(); - _roomRocketGiftRefreshTimer?.cancel(); - _roomRocketGiftRefreshTimer = null; + _cancelRoomRocketGiftStatusRefresh(); _cancelRoomRocketPostLaunchStatusRefresh(); RoomRocketAssetPreloader.cancelCurrentRoom(); _stopRoomRedPacketPresenceHeartbeat(); @@ -4528,6 +4581,9 @@ class RealTimeCommunicationManager extends ChangeNotifier { @override void dispose() { _stopRoomStatePolling(); + _cancelRoomRocketGiftStatusRefresh(); + _cancelRoomRocketPostLaunchStatusRefresh(); + _cancelRoomRocketEntryReadyFallbacks(); _clearRoomMusicPublishingStates(); unawaited(releaseRtcEngineForAppTermination()); super.dispose(); diff --git a/lib/services/audio/rtm_manager.dart b/lib/services/audio/rtm_manager.dart index 4beaeb9..31f8cf3 100644 --- a/lib/services/audio/rtm_manager.dart +++ b/lib/services/audio/rtm_manager.dart @@ -55,6 +55,7 @@ import 'package:yumi/shared/data_sources/models/message/room_rocket_launch_broad import 'package:yumi/shared/data_sources/models/message/sc_floating_message.dart'; import 'package:yumi/shared/business_logic/models/res/sc_broad_cast_luck_gift_push.dart'; import 'package:yumi/shared/business_logic/models/res/sc_room_rocket_api_res.dart'; +import 'package:yumi/shared/business_logic/models/res/sc_room_rocket_status_res.dart'; import 'package:yumi/shared/business_logic/models/res/broad_cast_mic_change_push.dart' hide Data; import 'package:yumi/shared/business_logic/models/res/gift_res.dart'; @@ -934,6 +935,18 @@ class RealTimeMessagingManager extends ChangeNotifier { ); return; } + if (_cpInviteTargetsCurrentAsSenderAndReceiver( + invite, + currentProfile, + data, + )) { + _cpInviteLog( + 'skip CP_BUILD reason=self_recipient applyId=$applyId ' + 'relationType=${invite.relationType}', + ); + await SmartDialog.dismiss(tag: RoomCpInviteDialog.dialogTag); + return; + } final isSender = _cpInviteSenderMatchesCurrent( invite, currentProfile, @@ -1124,6 +1137,17 @@ class RealTimeMessagingManager extends ChangeNotifier { } final currentProfile = AccountStorage().getCurrentUser()?.userProfile; if (context != null && invite != null && currentProfile != null) { + if (_cpInviteTargetsCurrentAsSenderAndReceiver( + invite, + currentProfile, + data, + )) { + _cpInviteLog( + 'skip CP invite result reason=self_recipient applyId=$applyId ' + 'result=${result.name}', + ); + return true; + } final isSender = _cpInviteSenderMatchesCurrent( invite, currentProfile, @@ -1759,6 +1783,33 @@ class RealTimeMessagingManager extends ChangeNotifier { return currentIds.any(inviteIds.contains); } + bool _cpInviteTargetsCurrentAsSenderAndReceiver( + SCSystemInvitMessageRes invite, + SocialChatUserProfile currentProfile, + Map data, + ) { + final currentIds = _cpInviteProfileIdentityValues(currentProfile); + if (currentIds.isEmpty) { + return false; + } + final inviterIds = { + ..._cpInviteInviterIdentityValues(data), + invite.actualAccount?.trim() ?? "", + invite.account?.trim() ?? "", + }..remove(""); + final receiverIds = _cpInviteReceiverIdentityValues(data); + return inviterIds.any(currentIds.contains) && + receiverIds.any(currentIds.contains); + } + + Set _cpInviteProfileIdentityValues(SocialChatUserProfile profile) { + return { + profile.id?.trim() ?? "", + profile.account?.trim() ?? "", + profile.getID().trim(), + }..remove(""); + } + Set _cpInviteInviterIdentityValues(Map data) { final content = _cpInviteContentMap(data); final values = [ @@ -3671,7 +3722,11 @@ class RealTimeMessagingManager extends ChangeNotifier { _publishRoomRocketRegionBroadcastIfNeeded(launch); } } - unawaited(rtcProvider.refreshRoomRocketStatus(roomId: roomId)); + rtcProvider.updateRoomRocketStatusFromRealtime( + SCRoomRocketStatusRes.fromJson(data), + roomId: roomId, + ); + rtcProvider.scheduleRoomRocketStatusRefreshForGift(roomId: roomId); } void _handleVoiceRoomRocketLaunchBroadcast( diff --git a/lib/services/auth/user_profile_manager.dart b/lib/services/auth/user_profile_manager.dart index 57ff770..cacbefa 100644 --- a/lib/services/auth/user_profile_manager.dart +++ b/lib/services/auth/user_profile_manager.dart @@ -13,6 +13,7 @@ import 'package:yumi/shared/business_logic/models/res/room_user_card_res.dart'; import 'package:yumi/shared/business_logic/models/res/sc_user_identity_res.dart'; import 'package:yumi/shared/business_logic/models/res/sc_vip_res.dart'; import 'package:yumi/shared/tools/sc_cp_relation_level_utils.dart'; +import 'package:yumi/shared/tools/sc_cp_relation_notice_utils.dart'; class SocialChatUserProfileManager extends ChangeNotifier { static const List _cpProfileRelationTypes = [ @@ -306,19 +307,7 @@ class SocialChatUserProfileManager extends ChangeNotifier { } String _normalizeCpRelationType(String? relationType) { - final value = relationType?.trim().toUpperCase() ?? ""; - switch (value) { - case "BROTHER": - case "BROTHERS": - case "SWORN_BROTHER": - case "SWORN_BROTHERS": - return "BROTHER"; - case "SISTER": - case "SISTERS": - return "SISTERS"; - default: - return "CP"; - } + return scNormalizeCpRelationType(relationType); } int? _cpDismissCostFromCabin(SCCpCabinRes? cabin) { diff --git a/lib/shared/business_logic/models/res/login_res.dart b/lib/shared/business_logic/models/res/login_res.dart index 46f384b..453a8b2 100644 --- a/lib/shared/business_logic/models/res/login_res.dart +++ b/lib/shared/business_logic/models/res/login_res.dart @@ -1157,7 +1157,19 @@ class CPRes { _days = _asString(source['days']); _firstDay = _asString(source['firstDay']); _cpValue = _asDouble(source['cpValue']); - _relationType = _asString(source['relationType']); + _relationType = _asString( + _firstValue(source, const [ + 'relationType', + 'relation_type', + 'cpRelationType', + 'cp_relation_type', + 'relationshipType', + 'relationship_type', + 'relation', + 'cpType', + 'cp_type', + ]), + ); _status = _asString(source['status']); _levelText = _asString( source['levelText'] ?? diff --git a/lib/shared/business_logic/models/res/sc_room_rocket_status_res.dart b/lib/shared/business_logic/models/res/sc_room_rocket_status_res.dart index a87ab2a..b0e0a39 100644 --- a/lib/shared/business_logic/models/res/sc_room_rocket_status_res.dart +++ b/lib/shared/business_logic/models/res/sc_room_rocket_status_res.dart @@ -104,13 +104,27 @@ class SCRoomRocketStatusRes { _date = json['date'] ?? json['dayKey'] ?? json['day_key']; _roundNo = _intValue(json['roundNo'] ?? json['round_no']); _displayRound = _boolValue(json['displayRound'] ?? json['display_round']); - _currentLevel = _intValue(json['currentLevel'] ?? json['current_level']); + _currentLevel = _intValue( + json['currentLevel'] ?? + json['current_level'] ?? + json['toLevel'] ?? + json['to_level'] ?? + json['nextLevel'] ?? + json['next_level'], + ); _needEnergy = _intValue(json['needEnergy'] ?? json['need_energy']); _displayPercent = _numValue( json['displayPercent'] ?? json['display_percent'], ); _shake = _boolValue(json['shake']); - _level = json['level'] ?? json['rocket_level'] ?? _currentLevel; + _level = + json['level'] ?? + json['rocket_level'] ?? + json['toLevel'] ?? + json['to_level'] ?? + json['nextLevel'] ?? + json['next_level'] ?? + _currentLevel; _levelName = json['levelName'] ?? json['level_name']; _currentEnergy = _intValue(json['currentEnergy'] ?? json['current_energy']); _maxEnergy = @@ -346,6 +360,67 @@ class SCRoomRocketStatusRes { return null; } + SCRoomRocketStatusRes mergeRealtimeUpdate( + SCRoomRocketStatusRes update, { + String? roomId, + }) { + final updateLevels = update.levels; + final updateRewardPreviewByLevel = update.rewardPreviewByLevel; + final updateRocketKings = update.rocketKings; + final updateRocketKingsByLevel = update.rocketKingsByLevel; + return SCRoomRocketStatusRes( + roomId: update.roomId ?? roomId ?? _roomId, + date: update.date ?? _date, + level: update.level ?? _level, + levelName: update.levelName ?? _levelName, + currentEnergy: update.currentEnergy ?? _currentEnergy, + maxEnergy: update.maxEnergy ?? _maxEnergy, + energyPercent: update.energyPercent ?? _energyPercent, + status: update.status ?? _status, + statusDesc: update.statusDesc ?? _statusDesc, + totalContributors: update.totalContributors ?? _totalContributors, + claimExpireTime: update.claimExpireTime ?? _claimExpireTime, + claimCount: update.claimCount ?? _claimCount, + canClaim: update.canClaim ?? _canClaim, + hasClaimed: update.hasClaimed ?? _hasClaimed, + myContribution: update.myContribution ?? _myContribution, + myContributionRate: update.myContributionRate ?? _myContributionRate, + configured: update.configured ?? _configured, + enabled: update.enabled ?? _enabled, + roundNo: update.roundNo ?? _roundNo, + displayRound: update.displayRound ?? _displayRound, + currentLevel: + update.currentLevel ?? update.level?.toInt() ?? _currentLevel, + needEnergy: update.needEnergy ?? _needEnergy, + displayPercent: update.displayPercent ?? _displayPercent, + shake: update.shake ?? _shake, + levels: + updateLevels != null && updateLevels.isNotEmpty + ? updateLevels + : _levels, + rewardPreview: update.rewardPreview ?? _rewardPreview, + rewardPreviewByLevel: + updateRewardPreviewByLevel != null && + updateRewardPreviewByLevel.isNotEmpty + ? updateRewardPreviewByLevel + : _rewardPreviewByLevel, + rocketKings: + updateRocketKings != null && updateRocketKings.isNotEmpty + ? updateRocketKings + : _rocketKings, + rocketKingsByLevel: + updateRocketKingsByLevel != null && + updateRocketKingsByLevel.isNotEmpty + ? updateRocketKingsByLevel + : _rocketKingsByLevel, + timezone: update.timezone ?? _timezone, + resetRemainingSeconds: + update.resetRemainingSeconds ?? _resetRemainingSeconds, + resetTime: update.resetTime ?? _resetTime, + serverTime: update.serverTime ?? _serverTime, + ); + } + List rocketKingsForLevel(int level) { final normalizedLevel = level.clamp(1, 99).toInt(); final byLevel = _rocketKingsByLevel?[normalizedLevel]; diff --git a/lib/shared/tools/sc_cp_relation_level_utils.dart b/lib/shared/tools/sc_cp_relation_level_utils.dart index 12c6355..796c695 100644 --- a/lib/shared/tools/sc_cp_relation_level_utils.dart +++ b/lib/shared/tools/sc_cp_relation_level_utils.dart @@ -58,20 +58,23 @@ String scCpRelationLevelDisplayText(String? rawText) { final text = rawText?.trim() ?? ''; final level = scCpRelationLevelIndexOrNull(text); if (text.isEmpty) { - return 'Lv.1 Simple Love'; + return 'Lv.0'; + } + if (_isSimpleLoveLevelOneText(text, level)) { + return 'Lv.1'; } final compactLevelPattern = RegExp( r'^(?:Lv\.?|Level)?\s*\d+$', caseSensitive: false, ); - if (compactLevelPattern.hasMatch(text) && level == 1) { - return 'Lv.1 Simple Love'; + if (compactLevelPattern.hasMatch(text) && level != null) { + return 'Lv.$level'; } return text; } int scCpRelationLevelIndex(String? levelText) { - return scCpRelationLevelIndexOrNull(levelText) ?? 1; + return scCpRelationLevelIndexOrNull(levelText) ?? 0; } int? scCpRelationLevelIndexOrNull(String? levelText) { @@ -80,14 +83,14 @@ int? scCpRelationLevelIndexOrNull(String? levelText) { return null; } final levelMatch = RegExp( - r'(?:Lv\.?|Level|等级|级)\s*([1-5])|([1-5])\s*(?:级|Level)', + r'(?:Lv\.?|Level|等级|级)\s*([0-5])|([0-5])\s*(?:级|Level)', caseSensitive: false, ).firstMatch(text); - final fallbackMatch = RegExp(r'[1-5]').firstMatch(text); + final fallbackMatch = RegExp(r'[0-5]').firstMatch(text); final rawLevel = levelMatch?.group(1) ?? levelMatch?.group(2) ?? fallbackMatch?.group(0); final level = int.tryParse(rawLevel ?? ''); - return level?.clamp(1, 5).toInt(); + return level?.clamp(0, 5).toInt(); } _CpRelationLevelSource? _currentLevelFromDynamicConfigs( @@ -136,10 +139,13 @@ _CpRelationLevelSource? _currentLevelFromSources( String? _formattedLevelText({int? level, String? name}) { final levelName = name?.trim() ?? ''; + if (level == 1 && _isSimpleLoveName(levelName)) { + return 'Lv.1'; + } if (level != null && levelName.isNotEmpty) { final nameLevel = scCpRelationLevelIndexOrNull(levelName); if (nameLevel == level) { - return levelName; + return scCpRelationLevelDisplayText(levelName); } return 'Lv.$level $levelName'; } @@ -149,6 +155,23 @@ String? _formattedLevelText({int? level, String? name}) { return _blankAsNull(levelName); } +bool _isSimpleLoveLevelOneText(String text, int? level) { + if (level != 1) { + return false; + } + return RegExp( + r'^(?:Lv\.?|Level)?\s*1\s+Simple\s+Love$', + caseSensitive: false, + ).hasMatch(text.trim()); +} + +bool _isSimpleLoveName(String text) { + return RegExp( + r'^(?:Lv\.?|Level)?\s*1?\s*Simple\s+Love$', + caseSensitive: false, + ).hasMatch(text.trim()); +} + String? _blankAsNull(String? value) { final text = value?.trim() ?? ''; return text.isEmpty ? null : text; diff --git a/lib/shared/tools/sc_cp_relation_notice_utils.dart b/lib/shared/tools/sc_cp_relation_notice_utils.dart index 3ffb9bb..fb544d1 100644 --- a/lib/shared/tools/sc_cp_relation_notice_utils.dart +++ b/lib/shared/tools/sc_cp_relation_notice_utils.dart @@ -99,5 +99,5 @@ String scBuildCpRelationEntryBroadcastText({ required String? relationType, }) { return "$ownerName's ${scCpRelationEntryLabel(relationType)} " - "$joinedName enter the room"; + "$joinedName has joined the party"; } diff --git a/lib/ui_kit/widgets/room/cp/room_cp_progress_dialog.dart b/lib/ui_kit/widgets/room/cp/room_cp_progress_dialog.dart index baa0f85..6067730 100644 --- a/lib/ui_kit/widgets/room/cp/room_cp_progress_dialog.dart +++ b/lib/ui_kit/widgets/room/cp/room_cp_progress_dialog.dart @@ -3,6 +3,7 @@ import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:yumi/shared/tools/sc_cp_relation_level_utils.dart'; import 'package:yumi/ui_kit/components/sc_compontent.dart'; import 'package:yumi/ui_kit/components/sc_debounce_widget.dart'; @@ -31,11 +32,11 @@ class RoomCpProgressDialog extends StatelessWidget { required this.rightUser, this.daysValue = '999', this.daysLabel = 'Days', - this.levelText = 'Lv.1 Simple Love', + this.levelText = 'Lv.0', this.currentValue = 0, this.targetValue = 1, this.expAwayText = '0', - this.nextLevelText = 'Lv. 2', + this.nextLevelText = 'Lv. 1', this.cancelText = 'Cancel', this.dismissTag = dialogTag, this.onCancel, @@ -62,11 +63,11 @@ class RoomCpProgressDialog extends StatelessWidget { required RoomCpProgressDialogUser rightUser, String daysValue = '999', String daysLabel = 'Days', - String levelText = 'Lv.1 Simple Love', + String levelText = 'Lv.0', double currentValue = 0, double targetValue = 1, String expAwayText = '0', - String nextLevelText = 'Lv. 2', + String nextLevelText = 'Lv. 1', String cancelText = 'Cancel', String dismissTag = dialogTag, VoidCallback? onCancel, @@ -164,6 +165,7 @@ class RoomCpProgressDialog extends StatelessWidget { right: 0, top: 180.w, child: _ExpAwayText( + levelText: levelText, expAwayText: expAwayText, nextLevelText: nextLevelText, ), @@ -203,7 +205,9 @@ class _DaysBadge extends StatelessWidget { final value = daysValue.trim().isEmpty ? '--' : daysValue.trim(); final label = daysLabel.trim().isEmpty ? 'Days' : daysLabel.trim(); final level = - levelText.trim().isEmpty ? 'Lv.1 Simple Love' : levelText.trim(); + levelText.trim().isEmpty + ? 'Lv.0' + : scCpRelationLevelDisplayText(levelText); return SizedBox( width: 114.w, @@ -407,16 +411,39 @@ class _ProgressBar extends StatelessWidget { } class _ExpAwayText extends StatelessWidget { - const _ExpAwayText({required this.expAwayText, required this.nextLevelText}); + const _ExpAwayText({ + required this.levelText, + required this.expAwayText, + required this.nextLevelText, + }); + final String levelText; final String expAwayText; final String nextLevelText; @override Widget build(BuildContext context) { + if (scCpRelationLevelIndex(levelText) >= 5) { + return Text( + 'Maximum level reached.', + maxLines: 1, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + style: TextStyle( + color: const Color(0xff67576F), + fontSize: 10.sp, + fontWeight: FontWeight.w400, + height: 1, + decoration: TextDecoration.none, + ), + ); + } + final exp = expAwayText.trim().isEmpty ? '0' : expAwayText.trim(); final nextLevel = - nextLevelText.trim().isEmpty ? 'Lv. 2' : nextLevelText.trim(); + nextLevelText.trim().isEmpty + ? 'Lv. 1' + : scCpRelationLevelDisplayText(nextLevelText); return Text.rich( TextSpan( diff --git a/lib/ui_kit/widgets/room/cp/sc_cp_corner_label.dart b/lib/ui_kit/widgets/room/cp/sc_cp_corner_label.dart index 179b6d5..e4e1514 100644 --- a/lib/ui_kit/widgets/room/cp/sc_cp_corner_label.dart +++ b/lib/ui_kit/widgets/room/cp/sc_cp_corner_label.dart @@ -6,25 +6,22 @@ class SCCpCornerLabel extends StatelessWidget { @override Widget build(BuildContext context) { - return ShaderMask( - blendMode: BlendMode.srcIn, - shaderCallback: (bounds) { - return const LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [Color(0xffFF91A4), Color(0xffFF3164)], - ).createShader(bounds); - }, - child: Text( - "CP", - textScaler: TextScaler.noScaling, - style: TextStyle( - color: Colors.white, - fontSize: 10.sp, - height: 1, - fontWeight: FontWeight.w700, - decoration: TextDecoration.none, - ), + return Text( + "CP", + textScaler: TextScaler.noScaling, + style: TextStyle( + foreground: + Paint() + ..shader = const LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Color(0xffFF91A4), Color(0xffFF3164)], + ).createShader(Rect.fromLTWH(0, 0, 20.w, 10.w)), + fontSize: 10.sp, + height: 1, + fontWeight: FontWeight.w700, + decoration: TextDecoration.none, + shadows: const [], ), ); } diff --git a/lib/ui_kit/widgets/room/room_play_widget.dart b/lib/ui_kit/widgets/room/room_play_widget.dart index 696c3af..a9e4bab 100644 --- a/lib/ui_kit/widgets/room/room_play_widget.dart +++ b/lib/ui_kit/widgets/room/room_play_widget.dart @@ -26,7 +26,7 @@ class RoomPlayWidget extends StatefulWidget { class _RoomPlayWidgetState extends State { static const double _railEnd = 8; - static const double _railBottom = 15; + static const double _railBottom = 30; static const double _railWidth = 58; static const double _railVerticalPadding = 8; static const double _actionExtent = 56; @@ -69,6 +69,7 @@ class _RoomPlayWidgetState extends State { final fullHeight = _railHeight( actionCount: actions.length, showToggle: false, + showBackground: false, ); final availableHeight = (constraints.maxHeight - _railBottom.w) @@ -78,17 +79,20 @@ class _RoomPlayWidgetState extends State { actions.length > _compactActionCount && availableHeight < fullHeight; final isExpanded = !shouldFold || _rightActionsExpanded; + final isCollapsed = shouldFold && !isExpanded; final visibleActions = isExpanded ? actions : actions.take(_compactActionCount).toList(); final railHeight = _railHeight( actionCount: visibleActions.length, showToggle: shouldFold, + showBackground: isCollapsed, ); Widget rail = _RoomSideActionRail( actions: visibleActions, showToggle: shouldFold, expanded: isExpanded, + showBackground: isCollapsed, onToggle: () { setState(() { _rightActionsExpanded = !_rightActionsExpanded; @@ -144,11 +148,16 @@ class _RoomPlayWidgetState extends State { false; } - double _railHeight({required int actionCount, required bool showToggle}) { + double _railHeight({ + required int actionCount, + required bool showToggle, + required bool showBackground, + }) { final actionGaps = actionCount > 1 ? actionCount - 1 : 0; final toggleHeight = showToggle ? _toggleExtent + (actionCount > 0 ? _actionGap : 0) : 0.0; - return (_railVerticalPadding * 2 + + final verticalPadding = showBackground ? _railVerticalPadding * 2 : 0.0; + return (verticalPadding + toggleHeight + actionCount * _actionExtent + actionGaps * _actionGap) @@ -180,6 +189,7 @@ class _RoomSideActionRail extends StatelessWidget { required this.actions, required this.showToggle, required this.expanded, + required this.showBackground, required this.onToggle, required this.actionBuilder, }); @@ -187,6 +197,7 @@ class _RoomSideActionRail extends StatelessWidget { final List<_RoomSideAction> actions; final bool showToggle; final bool expanded; + final bool showBackground; final VoidCallback onToggle; final Widget Function(_RoomSideAction action) actionBuilder; @@ -223,12 +234,16 @@ class _RoomSideActionRail extends StatelessWidget { width: _RoomPlayWidgetState._railWidth.w, padding: EdgeInsets.symmetric( horizontal: 6.w, - vertical: _RoomPlayWidgetState._railVerticalPadding.w, - ), - decoration: BoxDecoration( - color: Colors.black.withValues(alpha: 0.21), - borderRadius: BorderRadius.circular(8.w), + vertical: + showBackground ? _RoomPlayWidgetState._railVerticalPadding.w : 0, ), + decoration: + showBackground + ? BoxDecoration( + color: Colors.black.withValues(alpha: 0.21), + borderRadius: BorderRadius.circular(8.w), + ) + : null, child: Column(mainAxisSize: MainAxisSize.min, children: children), ); } diff --git a/lib/ui_kit/widgets/room/room_user_info_card.dart b/lib/ui_kit/widgets/room/room_user_info_card.dart index 4ff6c1d..f86ba07 100644 --- a/lib/ui_kit/widgets/room/room_user_info_card.dart +++ b/lib/ui_kit/widgets/room/room_user_info_card.dart @@ -760,9 +760,7 @@ class _RoomUserInfoCardState extends State { levelText: cp == null ? "" - : _cpLevelDisplayText( - _firstNonBlank([cp.levelText, "Lv.1 Simple Love"]), - ), + : _cpLevelDisplayText(_firstNonBlank([cp.levelText, "Lv.0"])), ); } @@ -835,7 +833,12 @@ class _RoomUserInfoCardState extends State { } String _cpLevelAsset(String levelText) { - return "$_cpProfileAssetBase/sc_cp_profile_cp_level_${_cpLevelIndex(levelText)}.png"; + return "$_cpProfileAssetBase/sc_cp_profile_cp_level_${_cpLevelAssetIndex(levelText)}.png"; + } + + int _cpLevelAssetIndex(String levelText) { + final level = _cpLevelIndex(levelText); + return level <= 0 ? 1 : level; } int _cpLevelIndex(String levelText) { @@ -910,19 +913,7 @@ class _RoomUserInfoCardState extends State { } String _normalizeCpRelationType(String? relationType) { - final value = relationType?.trim().toUpperCase() ?? ""; - switch (value) { - case "BROTHER": - case "BROTHERS": - case "SWORN_BROTHER": - case "SWORN_BROTHERS": - return "BROTHER"; - case "SISTER": - case "SISTERS": - return "SISTERS"; - default: - return "CP"; - } + return scNormalizeCpRelationType(relationType); } String _avatarFallback(String? avatarUrl) { @@ -1818,8 +1809,7 @@ class _RoomCpProgressData { factory _RoomCpProgressData.fallback(_RoomProfileCpData cp) { final currentValue = cp.cpValue; return _RoomCpProgressData( - levelText: - cp.levelText.trim().isEmpty ? "Lv.1 Simple Love" : cp.levelText, + levelText: scCpRelationLevelDisplayText(cp.levelText), currentValue: currentValue, targetValue: currentValue > 0 ? currentValue : 1, expAwayText: SCStringUtils.formatCompactNumber(0), @@ -1932,16 +1922,19 @@ class _RoomCpProgressData { static String _levelDisplayText(_CpProgressLevel? level, String fallback) { final fallbackText = fallback.trim(); if (level == null) { - return fallbackText.isEmpty ? "Lv.1 Simple Love" : fallbackText; + return scCpRelationLevelDisplayText(fallbackText); } final levelName = level.levelName?.trim(); + if (level.level != null && levelName != null && levelName.isNotEmpty) { + return scCpRelationLevelDisplayText("Lv.${level.level} $levelName"); + } if (levelName != null && levelName.isNotEmpty) { - return levelName; + return scCpRelationLevelDisplayText(levelName); } if (level.level != null) { return "Lv.${level.level}"; } - return fallbackText.isEmpty ? "Lv.1 Simple Love" : fallbackText; + return scCpRelationLevelDisplayText(fallbackText); } static String _levelShortText(int? level, {String? fallback}) { @@ -1950,9 +1943,9 @@ class _RoomCpProgressData { } final fallbackText = fallback?.trim() ?? ""; if (fallbackText.isNotEmpty) { - return fallbackText; + return scCpRelationLevelDisplayText(fallbackText); } - return "Lv. 1"; + return "Lv. 0"; } static int _levelIndex(String levelText) { @@ -1960,7 +1953,7 @@ class _RoomCpProgressData { } static int _nextLevelIndex(int level) { - return (level + 1).clamp(1, 5).toInt(); + return (level + 1).clamp(0, 5).toInt(); } } diff --git a/lib/ui_kit/widgets/room/seat/room_mic_relation_effect.dart b/lib/ui_kit/widgets/room/seat/room_mic_relation_effect.dart index 4d90203..e2e3e09 100644 --- a/lib/ui_kit/widgets/room/seat/room_mic_relation_effect.dart +++ b/lib/ui_kit/widgets/room/seat/room_mic_relation_effect.dart @@ -165,7 +165,7 @@ class _RoomMicRelationEffectState extends State { } List<_RoomMicRelationAsset> _cpAssetsForLevel(int? cpLevel) { - final level = (cpLevel ?? 1).clamp(1, 5).toInt(); + final level = (cpLevel ?? 0).clamp(0, 5).toInt(); switch (level) { case 2: return const [ diff --git a/lib/ui_kit/widgets/store/store_bag_page_helpers.dart b/lib/ui_kit/widgets/store/store_bag_page_helpers.dart index 1c2e272..482e161 100644 --- a/lib/ui_kit/widgets/store/store_bag_page_helpers.dart +++ b/lib/ui_kit/widgets/store/store_bag_page_helpers.dart @@ -715,16 +715,6 @@ Widget _buildBagCardSkeleton(double progress, {required bool showTitle}) { ), ), ), - PositionedDirectional( - bottom: 0, - end: 0, - child: _buildStoreBagSkeletonBone( - progress, - width: 18.w, - height: 18.w, - shape: BoxShape.circle, - ), - ), Column( mainAxisSize: showTitle ? MainAxisSize.max : MainAxisSize.min, children: [ diff --git a/local_packages/pag-1.0.7-patched/example/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/local_packages/pag-1.0.7-patched/example/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java new file mode 100644 index 0000000..e03f0c5 --- /dev/null +++ b/local_packages/pag-1.0.7-patched/example/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java @@ -0,0 +1,24 @@ +package io.flutter.plugins; + +import androidx.annotation.Keep; +import androidx.annotation.NonNull; +import io.flutter.Log; + +import io.flutter.embedding.engine.FlutterEngine; + +/** + * Generated file. Do not edit. + * This file is generated by the Flutter tool based on the + * plugins that support the Android platform. + */ +@Keep +public final class GeneratedPluginRegistrant { + private static final String TAG = "GeneratedPluginRegistrant"; + public static void registerWith(@NonNull FlutterEngine flutterEngine) { + try { + flutterEngine.getPlugins().add(new com.example.flutter_pag_plugin.FlutterPagPlugin()); + } catch (Exception e) { + Log.e(TAG, "Error registering plugin pag, com.example.flutter_pag_plugin.FlutterPagPlugin", e); + } + } +} diff --git a/local_packages/pag-1.0.7-patched/example/android/local.properties b/local_packages/pag-1.0.7-patched/example/android/local.properties new file mode 100644 index 0000000..a7100ac --- /dev/null +++ b/local_packages/pag-1.0.7-patched/example/android/local.properties @@ -0,0 +1,2 @@ +sdk.dir=/Users/nigger/Library/Android/sdk +flutter.sdk=/Users/nigger/Desktop/develop/flutter \ No newline at end of file diff --git a/local_packages/pag-1.0.7-patched/example/ios/Flutter/Generated.xcconfig b/local_packages/pag-1.0.7-patched/example/ios/Flutter/Generated.xcconfig new file mode 100644 index 0000000..6891991 --- /dev/null +++ b/local_packages/pag-1.0.7-patched/example/ios/Flutter/Generated.xcconfig @@ -0,0 +1,14 @@ +// This is a generated file; do not edit or check into version control. +FLUTTER_ROOT=/Users/nigger/Desktop/develop/flutter +FLUTTER_APPLICATION_PATH=/Users/nigger/Documents/GitHub/chatapp3-flutter/local_packages/pag-1.0.7-patched/example +COCOAPODS_PARALLEL_CODE_SIGN=true +FLUTTER_TARGET=lib/main.dart +FLUTTER_BUILD_DIR=build +FLUTTER_BUILD_NAME=1.0.0 +FLUTTER_BUILD_NUMBER=1 +EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386 +EXCLUDED_ARCHS[sdk=iphoneos*]=armv7 +DART_OBFUSCATION=false +TRACK_WIDGET_CREATION=true +TREE_SHAKE_ICONS=false +PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/local_packages/pag-1.0.7-patched/example/ios/Flutter/ephemeral/flutter_lldb_helper.py b/local_packages/pag-1.0.7-patched/example/ios/Flutter/ephemeral/flutter_lldb_helper.py new file mode 100644 index 0000000..a88caf9 --- /dev/null +++ b/local_packages/pag-1.0.7-patched/example/ios/Flutter/ephemeral/flutter_lldb_helper.py @@ -0,0 +1,32 @@ +# +# Generated file, do not edit. +# + +import lldb + +def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict): + """Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages.""" + base = frame.register["x0"].GetValueAsAddress() + page_len = frame.register["x1"].GetValueAsUnsigned() + + # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the + # first page to see if handled it correctly. This makes diagnosing + # misconfiguration (e.g. missing breakpoint) easier. + data = bytearray(page_len) + data[0:8] = b'IHELPED!' + + error = lldb.SBError() + frame.GetThread().GetProcess().WriteMemory(base, data, error) + if not error.Success(): + print(f'Failed to write into {base}[+{page_len}]', error) + return + +def __lldb_init_module(debugger: lldb.SBDebugger, _): + target = debugger.GetDummyTarget() + # Caveat: must use BreakpointCreateByRegEx here and not + # BreakpointCreateByName. For some reasons callback function does not + # get carried over from dummy target for the later. + bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$") + bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__)) + bp.SetAutoContinue(True) + print("-- LLDB integration loaded --") diff --git a/local_packages/pag-1.0.7-patched/example/ios/Flutter/ephemeral/flutter_lldbinit b/local_packages/pag-1.0.7-patched/example/ios/Flutter/ephemeral/flutter_lldbinit new file mode 100644 index 0000000..e3ba6fb --- /dev/null +++ b/local_packages/pag-1.0.7-patched/example/ios/Flutter/ephemeral/flutter_lldbinit @@ -0,0 +1,5 @@ +# +# Generated file, do not edit. +# + +command script import --relative-to-command-file flutter_lldb_helper.py diff --git a/local_packages/pag-1.0.7-patched/example/ios/Flutter/flutter_export_environment.sh b/local_packages/pag-1.0.7-patched/example/ios/Flutter/flutter_export_environment.sh new file mode 100755 index 0000000..f29a0d1 --- /dev/null +++ b/local_packages/pag-1.0.7-patched/example/ios/Flutter/flutter_export_environment.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=/Users/nigger/Desktop/develop/flutter" +export "FLUTTER_APPLICATION_PATH=/Users/nigger/Documents/GitHub/chatapp3-flutter/local_packages/pag-1.0.7-patched/example" +export "COCOAPODS_PARALLEL_CODE_SIGN=true" +export "FLUTTER_TARGET=lib/main.dart" +export "FLUTTER_BUILD_DIR=build" +export "FLUTTER_BUILD_NAME=1.0.0" +export "FLUTTER_BUILD_NUMBER=1" +export "DART_OBFUSCATION=false" +export "TRACK_WIDGET_CREATION=true" +export "TREE_SHAKE_ICONS=false" +export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/local_packages/pag-1.0.7-patched/example/ios/Runner/GeneratedPluginRegistrant.h b/local_packages/pag-1.0.7-patched/example/ios/Runner/GeneratedPluginRegistrant.h new file mode 100644 index 0000000..7a89092 --- /dev/null +++ b/local_packages/pag-1.0.7-patched/example/ios/Runner/GeneratedPluginRegistrant.h @@ -0,0 +1,19 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GeneratedPluginRegistrant_h +#define GeneratedPluginRegistrant_h + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface GeneratedPluginRegistrant : NSObject ++ (void)registerWithRegistry:(NSObject*)registry; +@end + +NS_ASSUME_NONNULL_END +#endif /* GeneratedPluginRegistrant_h */ diff --git a/local_packages/pag-1.0.7-patched/example/ios/Runner/GeneratedPluginRegistrant.m b/local_packages/pag-1.0.7-patched/example/ios/Runner/GeneratedPluginRegistrant.m new file mode 100644 index 0000000..e151365 --- /dev/null +++ b/local_packages/pag-1.0.7-patched/example/ios/Runner/GeneratedPluginRegistrant.m @@ -0,0 +1,21 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#import "GeneratedPluginRegistrant.h" + +#if __has_include() +#import +#else +@import pag; +#endif + +@implementation GeneratedPluginRegistrant + ++ (void)registerWithRegistry:(NSObject*)registry { + [FlutterPagPlugin registerWithRegistrar:[registry registrarForPlugin:@"FlutterPagPlugin"]]; +} + +@end diff --git a/local_packages/pag-1.0.7-patched/example/pubspec.lock b/local_packages/pag-1.0.7-patched/example/pubspec.lock new file mode 100644 index 0000000..6cb2a0d --- /dev/null +++ b/local_packages/pag-1.0.7-patched/example/pubspec.lock @@ -0,0 +1,204 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.13.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.4.1" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.19.1" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.9" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.3.3" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + url: "https://pub.flutter-io.cn" + source: hosted + version: "11.0.2" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.2" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.12.19" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.13.0" + meta: + dependency: transitive + description: + name: meta + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.17.0" + pag: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "1.0.7" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.9.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.10.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.7.10" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360" + url: "https://pub.flutter-io.cn" + source: hosted + version: "15.2.0" +sdks: + dart: ">=3.9.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/local_packages/pag-1.0.7-patched/pubspec.lock b/local_packages/pag-1.0.7-patched/pubspec.lock new file mode 100644 index 0000000..7568c25 --- /dev/null +++ b/local_packages/pag-1.0.7-patched/pubspec.lock @@ -0,0 +1,189 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.13.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.4.1" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.19.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.3.3" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + url: "https://pub.flutter-io.cn" + source: hosted + version: "11.0.2" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.2" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.12.19" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.13.0" + meta: + dependency: transitive + description: + name: meta + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.17.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.9.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.10.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.7.10" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360" + url: "https://pub.flutter-io.cn" + source: hosted + version: "15.2.0" +sdks: + dart: ">=3.9.0-0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/test/gift_res_cp_relation_type_test.dart b/test/gift_res_cp_relation_type_test.dart index 14e3c2a..aad6705 100644 --- a/test/gift_res_cp_relation_type_test.dart +++ b/test/gift_res_cp_relation_type_test.dart @@ -40,6 +40,17 @@ void main() { expect(scCpRelationDisplayDays('12'), '13'); expect(scCpRelationDisplayDays(''), '1'); }); + + test('relation entry notice uses party copy', () { + expect( + scBuildCpRelationEntryBroadcastText( + ownerName: 'A', + joinedName: 'B', + relationType: 'CP', + ), + "A's cp B has joined the party", + ); + }); }); group('scCpRelationTypeForGift', () { diff --git a/test/room_rocket_status_test.dart b/test/room_rocket_status_test.dart index 4352a85..2707acd 100644 --- a/test/room_rocket_status_test.dart +++ b/test/room_rocket_status_test.dart @@ -195,6 +195,52 @@ void main() { expect(levels?.last.progressImageUrl, 'progress-2'); }); + test('parses realtime level advancement payloads', () { + final status = SCRoomRocketStatusRes.fromJson({ + 'room_id': 'room-1', + 'from_level': 1, + 'to_level': 2, + 'display_percent': 0, + }); + + expect(status.currentLevel, 2); + expect(status.level, 2); + expect(status.displayPercent, 0); + }); + + test('merges realtime rocket status without dropping level assets', () { + final current = SCRoomRocketStatusRes.fromJson({ + 'room_id': 'room-1', + 'current_level': 1, + 'display_percent': 98, + 'levels': [ + { + 'level': 1, + 'rocket_icon_url': 'icon-1', + 'progress_bar_url': 'progress-1', + }, + { + 'level': 2, + 'rocket_icon_url': 'icon-2', + 'progress_bar_url': 'progress-2', + }, + ], + }); + final realtimeUpdate = SCRoomRocketStatusRes.fromJson({ + 'room_id': 'room-1', + 'to_level': 2, + 'display_percent': 0, + }); + + final merged = current.mergeRealtimeUpdate(realtimeUpdate); + final levels = RoomRocketApiMapper.levelsFromStatus(merged); + + expect(merged.currentLevel, 2); + expect(merged.displayPercent, 0); + expect(levels?.last.imageUrl, 'icon-2'); + expect(levels?.last.progressImageUrl, 'progress-2'); + }); + test('parses disabled rocket status from go api', () { final status = SCRoomRocketStatusRes.fromJson({ 'configured': true, diff --git a/test/sc_cp_relation_pair_utils_test.dart b/test/sc_cp_relation_pair_utils_test.dart index a5f96b0..15ebc69 100644 --- a/test/sc_cp_relation_pair_utils_test.dart +++ b/test/sc_cp_relation_pair_utils_test.dart @@ -53,6 +53,22 @@ void main() { ); }); + test('detects close-friend relation aliases parsed from payload', () { + final left = SocialChatUserProfile(id: '300'); + final right = SocialChatUserProfile( + id: '400', + closeFriendList: [ + CPRes.fromJson({ + 'meUserId': '300', + 'cpUserId': '400', + 'cpRelationType': 'sis', + }), + ], + ); + + expect(scCpRelationTypeBetweenProfiles(left, right), 'SISTERS'); + }); + test('does not match unrelated profiles', () { final left = SocialChatUserProfile( id: '100', @@ -78,5 +94,34 @@ void main() { expect(scCpRelationLevelTextFromCabin(cabin), 'Lv.2 Sweet Love'); }); + + test( + 'supports zero based CP levels and hides hardcoded simple love copy', + () { + expect(scCpRelationLevelIndex('Lv.0'), 0); + expect(scCpRelationLevelDisplayText(null), 'Lv.0'); + expect(scCpRelationLevelDisplayText('Lv.1 Simple Love'), 'Lv.1'); + + final cabin = SCCpCabinRes( + cpVal: 0, + cpCabinConfigCO: const [ + {'level': 0, 'levelName': 'Start', 'requiredCpValue': 0}, + {'level': 1, 'levelName': 'Simple Love', 'requiredCpValue': 100}, + ], + ); + + expect(scCpRelationLevelTextFromCabin(cabin), 'Lv.0 Start'); + + final levelOneCabin = SCCpCabinRes( + cpVal: 100, + cpCabinConfigCO: const [ + {'level': 0, 'levelName': 'Start', 'requiredCpValue': 0}, + {'level': 1, 'levelName': 'Simple Love', 'requiredCpValue': 100}, + ], + ); + + expect(scCpRelationLevelTextFromCabin(levelOneCabin), 'Lv.1'); + }, + ); }); }