bug。fix

This commit is contained in:
roxy 2026-05-23 12:59:03 +08:00
parent 71993f438b
commit 6da7e28fbc
29 changed files with 1024 additions and 135 deletions

View File

@ -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',

View File

@ -1676,6 +1676,14 @@ class _GiftPageState extends State<GiftPage> 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<String> _profileIdentityKeys(SocialChatUserProfile? profile) {
return <String>{
profile?.id?.trim() ?? "",
profile?.account?.trim() ?? "",
profile?.getID().trim() ?? "",
}..remove("");
}
String get batchKey {
final sortedAcceptUserIds = List<String>.from(acceptUserIds)..sort();
final type =

View File

@ -1449,19 +1449,7 @@ class _PersonDetailPageState extends State<PersonDetailPage> {
}
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<PersonDetailPage> {
}
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<PersonDetailPage> {
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();
}
}

View File

@ -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<Duration> _roomRocketGiftRefreshDelays = [
Duration(milliseconds: 700),
Duration(milliseconds: 1600),
Duration(milliseconds: 3200),
];
static const List<Duration> _roomRocketPostLaunchRefreshDelays = [
Duration(milliseconds: 800),
Duration(seconds: 2),
@ -389,7 +391,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
Timer? _roomEntryEffectTimer;
Timer? _roomRocketLaunchAnimationTimer;
ValueNotifier<String?>? _roomRocketLaunchTopAvatarNotifier;
Timer? _roomRocketGiftRefreshTimer;
final List<Timer> _roomRocketGiftRefreshTimers = [];
final List<Timer> _roomRocketPostLaunchRefreshTimers = [];
final List<Timer> _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();

View File

@ -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<String, dynamic> data,
) {
final currentIds = _cpInviteProfileIdentityValues(currentProfile);
if (currentIds.isEmpty) {
return false;
}
final inviterIds = <String>{
..._cpInviteInviterIdentityValues(data),
invite.actualAccount?.trim() ?? "",
invite.account?.trim() ?? "",
}..remove("");
final receiverIds = _cpInviteReceiverIdentityValues(data);
return inviterIds.any(currentIds.contains) &&
receiverIds.any(currentIds.contains);
}
Set<String> _cpInviteProfileIdentityValues(SocialChatUserProfile profile) {
return <String>{
profile.id?.trim() ?? "",
profile.account?.trim() ?? "",
profile.getID().trim(),
}..remove("");
}
Set<String> _cpInviteInviterIdentityValues(Map<String, dynamic> data) {
final content = _cpInviteContentMap(data);
final values = <String?>[
@ -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(

View File

@ -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<String> _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) {

View File

@ -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'] ??

View File

@ -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<SCRoomRocketKingUserRes> rocketKingsForLevel(int level) {
final normalizedLevel = level.clamp(1, 99).toInt();
final byLevel = _rocketKingsByLevel?[normalizedLevel];

View File

@ -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;

View File

@ -99,5 +99,5 @@ String scBuildCpRelationEntryBroadcastText({
required String? relationType,
}) {
return "$ownerName's ${scCpRelationEntryLabel(relationType)} "
"$joinedName enter the room";
"$joinedName has joined the party";
}

View File

@ -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(

View File

@ -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 <Shadow>[],
),
);
}

View File

@ -26,7 +26,7 @@ class RoomPlayWidget extends StatefulWidget {
class _RoomPlayWidgetState extends State<RoomPlayWidget> {
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<RoomPlayWidget> {
final fullHeight = _railHeight(
actionCount: actions.length,
showToggle: false,
showBackground: false,
);
final availableHeight =
(constraints.maxHeight - _railBottom.w)
@ -78,17 +79,20 @@ class _RoomPlayWidgetState extends State<RoomPlayWidget> {
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<RoomPlayWidget> {
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),
);
}

View File

@ -760,9 +760,7 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
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<RoomUserInfoCard> {
}
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<RoomUserInfoCard> {
}
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();
}
}

View File

@ -165,7 +165,7 @@ class _RoomMicRelationEffectState extends State<RoomMicRelationEffect> {
}
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 [

View File

@ -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: [

View File

@ -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);
}
}
}

View File

@ -0,0 +1,2 @@
sdk.dir=/Users/nigger/Library/Android/sdk
flutter.sdk=/Users/nigger/Desktop/develop/flutter

View File

@ -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

View File

@ -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 --")

View File

@ -0,0 +1,5 @@
#
# Generated file, do not edit.
#
command script import --relative-to-command-file flutter_lldb_helper.py

View File

@ -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"

View File

@ -0,0 +1,19 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GeneratedPluginRegistrant_h
#define GeneratedPluginRegistrant_h
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN
@interface GeneratedPluginRegistrant : NSObject
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry;
@end
NS_ASSUME_NONNULL_END
#endif /* GeneratedPluginRegistrant_h */

View File

@ -0,0 +1,21 @@
//
// Generated file. Do not edit.
//
// clang-format off
#import "GeneratedPluginRegistrant.h"
#if __has_include(<pag/FlutterPagPlugin.h>)
#import <pag/FlutterPagPlugin.h>
#else
@import pag;
#endif
@implementation GeneratedPluginRegistrant
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
[FlutterPagPlugin registerWithRegistrar:[registry registrarForPlugin:@"FlutterPagPlugin"]];
}
@end

View File

@ -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"

View File

@ -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"

View File

@ -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', () {

View File

@ -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,

View File

@ -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');
},
);
});
}