yumi-flutter/lib/services/auth/user_profile_manager.dart
2026-07-02 19:13:13 +08:00

1095 lines
33 KiB
Dart

import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:yumi/app_localizations.dart';
import 'package:yumi/ui_kit/components/sc_tts.dart';
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
import 'package:yumi/shared/data_sources/sources/repositories/sc_room_repository_imp.dart';
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
import 'package:yumi/shared/data_sources/sources/repositories/sc_vip_repository_imp.dart';
import 'package:yumi/shared/business_logic/models/req/sc_user_profile_cmd.dart';
import 'package:yumi/shared/business_logic/models/res/country_res.dart';
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
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/data_sources/models/enum/sc_props_type.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 = [
'CP',
'BROTHER',
'SISTERS',
];
SCUserProfileCmd? editUser;
void updateUserAvatar(String avatar) {
editUser ??= SCUserProfileCmd();
editUser!.setUserAvatar = avatar;
notifyListeners();
}
void updateUserSex(num sex) {
editUser ??= SCUserProfileCmd();
editUser!.setUserSex = sex;
}
void updateUserNickname(String nickname) {
editUser ??= SCUserProfileCmd();
editUser!.setUserNickname = nickname;
}
void updateBornYear(num bornYear) {
editUser ??= SCUserProfileCmd();
editUser!.setBornYear = bornYear;
}
void updateBornMonth(num bornMonth) {
editUser ??= SCUserProfileCmd();
editUser!.setBornMonth = bornMonth;
}
void updateBornDay(num bornDay) {
editUser ??= SCUserProfileCmd();
editUser!.setBornDay = bornDay;
}
void updateAge(num age) {
editUser ??= SCUserProfileCmd();
editUser!.setAge = age;
}
void setCountry(Country country) {
editUser ??= SCUserProfileCmd();
editUser!.setCountryCode = country.alphaTwo!;
editUser!.setCountryId = country.id!;
editUser!.setCountryName = country.countryName!;
}
void resetEditUserData() {
editUser = null;
notifyListeners();
}
SocialChatUserProfile? get currentUserProfile =>
AccountStorage().getCurrentUser()?.userProfile;
void syncCurrentUserProfile(SocialChatUserProfile? profile) {
if (profile == null) {
return;
}
final currentUser = AccountStorage().getCurrentUser();
if (currentUser != null) {
currentUser.setUserProfile(profile);
AccountStorage().setCurrentUser(currentUser);
}
if ((userProfile?.id ?? "").isNotEmpty && userProfile?.id == profile.id) {
userProfile = profile;
}
notifyListeners();
}
void syncCurrentUserChatBubble(
PropsResources? chatBubble, {
String? expireTime,
}) {
final profile = currentUserProfile;
if (profile == null) {
return;
}
final useProps = <UseProps>[
...?profile.useProps?.where(
(item) => item.propsResources?.type != SCPropsType.CHAT_BUBBLE.name,
),
];
if (chatBubble != null) {
useProps.add(
UseProps(
expireTime: expireTime ?? "4102444800000",
propsResources: chatBubble,
userId: profile.id,
),
);
}
syncCurrentUserProfile(profile.copyWith(useProps: useProps));
}
Future fetchUserProfileData({
bool loadGuardCount = true,
bool refreshFamilyData = false,
}) async {
var us = AccountStorage().getCurrentUser();
String userId = us?.userProfile?.id ?? "";
if (userId.isEmpty) {
return;
}
var userInfo = await SCAccountRepository().loadUserInfo(userId);
userInfo = await _withCurrentVipLevel(userInfo, userId);
userInfo = await _withCpCabinProfiles(userInfo, userId);
syncCurrentUserProfile(userInfo);
}
SCUserIdentityRes? userIdentity;
void getUserIdentity() async {
if (kDebugMode) {
final profile = AccountStorage().getCurrentUser()?.userProfile;
debugPrint(
'[SCUserIdentity] request start userId=${profile?.id}, account=${profile?.account}',
);
}
try {
userIdentity = await SCAccountRepository().userIdentity();
if (kDebugMode) {
debugPrint(
'[SCUserIdentity] manager stored admin=${userIdentity?.admin}, '
'manager=${userIdentity?.manager}, yumiManager=${userIdentity?.yumiManager}',
);
}
notifyListeners();
} catch (error) {
if (kDebugMode) {
debugPrint('[SCUserIdentity] request failed error=$error');
}
}
}
/// 当前正在查看的个人详情资料,不用于承载登录用户自己的 Me 页数据。
SocialChatUserProfile? userProfile;
Object? userProfileLoadError;
bool isUserProfileLoading = false;
int _userInfoRequestSerial = 0;
Future<void> getUserInfoById(String userId) {
return _loadUserInfoById(userId, clearBeforeLoad: true);
}
Future<void> reloadUserInfoById(String userId) {
return _loadUserInfoById(userId, clearBeforeLoad: false);
}
Future<void> _loadUserInfoById(
String userId, {
required bool clearBeforeLoad,
}) async {
final requestSerial = ++_userInfoRequestSerial;
final normalizedUserId = userId.trim();
if (clearBeforeLoad) {
userProfile = null;
}
userProfileLoadError = null;
isUserProfileLoading = true;
notifyListeners();
if (normalizedUserId.isEmpty) {
if (requestSerial == _userInfoRequestSerial) {
userProfileLoadError = ArgumentError('userId is empty');
isUserProfileLoading = false;
notifyListeners();
}
return;
}
try {
final loadedProfile = await SCAccountRepository().loadUserInfo(
normalizedUserId,
);
if (requestSerial != _userInfoRequestSerial) {
return;
}
userProfile = loadedProfile;
userProfileLoadError = null;
isUserProfileLoading = false;
notifyListeners();
unawaited(
_hydrateUserInfoById(
profile: loadedProfile,
userId: normalizedUserId,
requestSerial: requestSerial,
),
);
} catch (error, stackTrace) {
if (requestSerial != _userInfoRequestSerial) {
return;
}
userProfile = null;
userProfileLoadError = error;
_debugProfileLoadFailure(
scope: 'detail',
userId: normalizedUserId,
error: error,
stackTrace: stackTrace,
);
} finally {
if (requestSerial == _userInfoRequestSerial && isUserProfileLoading) {
isUserProfileLoading = false;
notifyListeners();
}
}
}
///房间资料卡
RoomUserCardRes? userCardInfo;
Object? userCardLoadError;
bool isUserCardLoading = false;
bool isUserCardRelationLoading = false;
int _userCardRequestSerial = 0;
int _userCardSessionSerial = 0;
int? _activeUserCardSessionSerial;
// List<UserCountGuardRes>? userCountGuardResList;
int beginRoomUserCardSession() {
final sessionSerial = ++_userCardSessionSerial;
_activeUserCardSessionSerial = sessionSerial;
return sessionSerial;
}
Future<RoomUserCardRes?> roomUserCard(
String roomId,
String userId, {
int? sessionSerial,
}) async {
final activeSessionSerial = sessionSerial ?? beginRoomUserCardSession();
_activeUserCardSessionSerial = activeSessionSerial;
final requestSerial = ++_userCardRequestSerial;
final normalizedRoomId = roomId.trim();
final normalizedUserId = userId.trim();
userCardInfo = null;
userCardLoadError = null;
isUserCardLoading = true;
isUserCardRelationLoading = false;
notifyListeners();
if (normalizedUserId.isEmpty) {
if (requestSerial == _userCardRequestSerial) {
userCardLoadError = ArgumentError('userId is empty');
isUserCardLoading = false;
notifyListeners();
}
return null;
}
try {
final card = await _loadHydratedRoomUserCard(
roomId: normalizedRoomId,
userId: normalizedUserId,
);
if (requestSerial != _userCardRequestSerial ||
activeSessionSerial != _activeUserCardSessionSerial) {
return null;
}
userCardInfo = card;
userCardLoadError = null;
isUserCardLoading = false;
isUserCardRelationLoading = false;
notifyListeners();
return card;
} catch (error, stackTrace) {
if (requestSerial != _userCardRequestSerial) {
return null;
}
userCardInfo = null;
userCardLoadError = error;
isUserCardRelationLoading = false;
_debugProfileLoadFailure(
scope: 'roomCard',
userId: normalizedUserId,
error: error,
stackTrace: stackTrace,
);
} finally {
if (requestSerial == _userCardRequestSerial && isUserCardLoading) {
isUserCardLoading = false;
notifyListeners();
}
}
return null;
}
Future<RoomUserCardRes?> preloadRoomUserCardForOpen(
String roomId,
String userId, {
int? sessionSerial,
}) {
return roomUserCard(roomId, userId, sessionSerial: sessionSerial);
}
Future<RoomUserCardRes?> _loadHydratedRoomUserCard({
required String roomId,
required String userId,
}) async {
final card = await SCChatRoomRepository().roomUserCard(roomId, userId);
return _withCurrentVipLevelForCard(card, userId);
}
Future<void> _hydrateUserInfoById({
required SocialChatUserProfile profile,
required String userId,
required int requestSerial,
}) async {
try {
var mergedProfile = await _withCurrentVipLevel(profile, userId);
mergedProfile = await _withCpCabinProfiles(mergedProfile, userId);
if (requestSerial != _userInfoRequestSerial) {
return;
}
userProfile = mergedProfile;
userProfileLoadError = null;
notifyListeners();
} catch (error, stackTrace) {
_debugProfileLoadFailure(
scope: 'detailHydrate',
userId: userId,
error: error,
stackTrace: stackTrace,
);
}
}
void clearRoomUserCard({int? sessionSerial}) {
if (sessionSerial != null &&
sessionSerial != _activeUserCardSessionSerial) {
return;
}
_userCardRequestSerial++;
_activeUserCardSessionSerial = null;
userCardInfo = null;
userCardLoadError = null;
isUserCardLoading = false;
isUserCardRelationLoading = false;
}
Future<RoomUserCardRes?> _withCurrentVipLevelForCard(
RoomUserCardRes? card,
String userId,
) async {
final profile = card?.userProfile;
if (card == null || profile == null) {
return card;
}
var mergedProfile = await _withCurrentVipLevel(profile, userId);
mergedProfile = await _withCpCabinProfiles(mergedProfile, userId);
if (mergedProfile == profile) {
return card;
}
return card.copyWith(userProfile: mergedProfile);
}
void _debugProfileLoadFailure({
required String scope,
required String userId,
required Object error,
required StackTrace stackTrace,
}) {
if (kDebugMode) {
debugPrint(
'[ProfileLoad][$scope] failed userId=$userId error=$error\n$stackTrace',
);
}
}
Future<SocialChatUserProfile> _withCpCabinProfiles(
SocialChatUserProfile profile,
String userId,
) async {
if (userId.trim().isEmpty) {
return profile;
}
final fallbackCpPairs = <CPRes>[];
final fallbackCloseFriendPairs = <CPRes>[];
_addCpRelationPairs(
cpPairs: fallbackCpPairs,
closeFriendPairs: fallbackCloseFriendPairs,
relations: profile.cpList,
fallbackRelationType: 'CP',
);
_addCpRelationPairs(
cpPairs: fallbackCpPairs,
closeFriendPairs: fallbackCloseFriendPairs,
relations: profile.closeFriendList,
);
final cpPairs = <CPRes>[];
final closeFriendPairs = <CPRes>[];
_addCpRelationPairs(
cpPairs: cpPairs,
closeFriendPairs: closeFriendPairs,
relations: fallbackCpPairs,
);
_addCpRelationPairs(
cpPairs: cpPairs,
closeFriendPairs: closeFriendPairs,
relations: fallbackCloseFriendPairs,
);
var loadedAnyCabin = false;
if (kDebugMode) {
final currentUserId =
AccountStorage().getCurrentUser()?.userProfile?.id?.trim() ?? "";
debugPrint(
'[CP][Profile] source userId=$userId currentUserId=$currentUserId '
'profileCp=${profile.cpList?.length ?? 0} '
'profileCloseFriends=${profile.closeFriendList?.length ?? 0}',
);
}
await Future.wait(
_cpProfileRelationTypes.map((relationType) async {
final pairs = await _safeLoadCpRelationshipPairs(userId, relationType);
_addCpRelationPairs(
cpPairs: cpPairs,
closeFriendPairs: closeFriendPairs,
relations: pairs,
fallbackRelationType: relationType,
);
}),
);
await Future.wait(
_cpProfileRelationTypes.map((relationType) async {
final cabin = await _safeLoadCpCabin(userId, relationType);
if (cabin != null) {
loadedAnyCabin = true;
}
final cpPair = cabin?.cpPairUserProfileCO;
if (cpPair == null || !_hasCpPair(cpPair)) {
return;
}
final normalizedType = _normalizeCpRelationType(
cpPair.relationType ?? relationType,
);
final cabinLevelText = scCpRelationLevelTextFromCabin(
cabin,
fallbackText: cpPair.levelText,
);
final rightsLevelText = await _safeLoadCurrentUserCpLevelText(
userId: userId,
cpPair: cpPair,
relationType: normalizedType,
fallbackText: cabinLevelText,
);
final enrichedPair = cpPair.copyWith(
relationType: normalizedType,
cpValue: cpPair.cpValue ?? cabin?.cpVal?.toDouble(),
levelText: rightsLevelText ?? cabinLevelText,
dismissCost: cpPair.dismissCost ?? _cpDismissCostFromCabin(cabin),
);
_addCpRelationPair(
cpPairs: cpPairs,
closeFriendPairs: closeFriendPairs,
relation: enrichedPair,
fallbackRelationType: normalizedType,
);
}),
);
if (kDebugMode) {
debugPrint(
'[CP][Profile] hydrate userId=$userId cp=${cpPairs.length} '
'closeFriends=${closeFriendPairs.length}',
);
}
if (!loadedAnyCabin) {
return profile.copyWith(
cpList: cpPairs,
closeFriendList: closeFriendPairs,
isCpRelation: cpPairs.isNotEmpty,
);
}
return profile.copyWith(
cpList: cpPairs,
closeFriendList: closeFriendPairs,
isCpRelation: cpPairs.isNotEmpty,
);
}
void _addCpRelationPairs({
required List<CPRes> cpPairs,
required List<CPRes> closeFriendPairs,
required List<CPRes>? relations,
String? fallbackRelationType,
}) {
for (final relation in relations ?? const <CPRes>[]) {
_addCpRelationPair(
cpPairs: cpPairs,
closeFriendPairs: closeFriendPairs,
relation: relation,
fallbackRelationType: fallbackRelationType,
);
}
}
void _addCpRelationPair({
required List<CPRes> cpPairs,
required List<CPRes> closeFriendPairs,
required CPRes relation,
String? fallbackRelationType,
}) {
if (!_hasCpPair(relation)) {
return;
}
final relationTypeSource =
(relation.relationType?.trim().isNotEmpty ?? false)
? relation.relationType
: fallbackRelationType;
if ((relationTypeSource ?? "").trim().isEmpty) {
return;
}
final normalizedType = _normalizeCpRelationType(relationTypeSource);
final normalizedRelation = relation.copyWith(relationType: normalizedType);
final target = normalizedType == 'CP' ? cpPairs : closeFriendPairs;
final relationKey = _cpRelationPairKey(normalizedRelation);
final existingIndex = target.indexWhere(
(item) => _cpRelationPairKey(item) == relationKey,
);
if (existingIndex >= 0) {
target[existingIndex] = normalizedRelation;
} else {
target.add(normalizedRelation);
}
}
String _cpRelationPairKey(CPRes relation) {
final type = _normalizeCpRelationType(relation.relationType);
final userIds = <String>[
relation.meUserId?.trim() ?? "",
relation.cpUserId?.trim() ?? "",
]..removeWhere((value) => value.isEmpty);
if (userIds.length >= 2) {
userIds.sort();
return '$type|id|${userIds.join("|")}';
}
final accounts = <String>[
relation.meAccount?.trim() ?? "",
relation.cpAccount?.trim() ?? "",
]..removeWhere((value) => value.isEmpty);
if (accounts.length >= 2) {
accounts.sort();
return '$type|account|${accounts.join("|")}';
}
return [
type,
relation.meUserId?.trim() ?? "",
relation.meAccount?.trim() ?? "",
relation.cpUserId?.trim() ?? "",
relation.cpAccount?.trim() ?? "",
relation.cpUserNickname?.trim() ?? "",
relation.meUserNickname?.trim() ?? "",
].join('|');
}
Future<List<CPRes>> _safeLoadCpRelationshipPairs(
String userId,
String relationType,
) async {
try {
final pairs = await SCAccountRepository().cpRelationshipPairs(
relationType: relationType,
userId: userId,
);
if (kDebugMode) {
debugPrint(
'[CP][Profile] pair loaded userId=$userId relationType=$relationType '
'count=${pairs.length}',
);
}
return pairs;
} catch (error) {
if (kDebugMode) {
debugPrint(
'[CP][Profile] pair failed userId=$userId relationType=$relationType error=$error',
);
}
return const <CPRes>[];
}
}
Future<SCCpCabinRes?> _safeLoadCpCabin(
String userId,
String relationType,
) async {
try {
final cabin = await SCAccountRepository().cpCabin(
userId,
relationType: relationType,
);
if (kDebugMode) {
debugPrint(
'[CP][Profile] cabin loaded userId=$userId relationType=$relationType '
'hasPair=${_hasCpPair(cabin.cpPairUserProfileCO)} cpVal=${cabin.cpVal}',
);
}
return cabin;
} catch (error) {
if (kDebugMode) {
debugPrint(
'[CP][Profile] cabin failed userId=$userId '
'relationType=$relationType error=$error',
);
}
return null;
}
}
Future<String?> _safeLoadCurrentUserCpLevelText({
required String userId,
required CPRes cpPair,
required String relationType,
String? fallbackText,
}) async {
final partnerUserId = _cpPartnerUserIdForCurrentUser(cpPair);
if (partnerUserId.isEmpty) {
return null;
}
try {
final config = await SCAccountRepository().cpRightsConfig(
relationType: relationType,
cpUserId: partnerUserId,
);
return scCpRelationLevelTextFromRightsConfig(
config,
fallbackText: fallbackText,
);
} catch (error) {
if (kDebugMode) {
debugPrint(
'[CP][Profile] rightsConfig failed userId=$userId '
'partnerUserId=$partnerUserId relationType=$relationType '
'error=$error',
);
}
return null;
}
}
String _cpPartnerUserIdForCurrentUser(CPRes cpPair) {
final currentUserId =
AccountStorage().getCurrentUser()?.userProfile?.id?.trim() ?? "";
if (currentUserId.isEmpty) {
return "";
}
final meUserId = cpPair.meUserId?.trim() ?? "";
final cpUserId = cpPair.cpUserId?.trim() ?? "";
if (currentUserId == meUserId) {
return cpUserId;
}
if (currentUserId == cpUserId) {
return meUserId;
}
return "";
}
bool _hasCpPair(CPRes? pair) {
return (pair?.cpUserId?.trim().isNotEmpty ?? false) ||
(pair?.cpUserAvatar?.trim().isNotEmpty ?? false) ||
(pair?.cpUserNickname?.trim().isNotEmpty ?? false) ||
(pair?.meUserId?.trim().isNotEmpty ?? false) ||
(pair?.meUserAvatar?.trim().isNotEmpty ?? false) ||
(pair?.meUserNickname?.trim().isNotEmpty ?? false);
}
String _normalizeCpRelationType(String? relationType) {
return scNormalizeCpRelationType(relationType);
}
int? _cpDismissCostFromCabin(SCCpCabinRes? cabin) {
final directCost = cabin?.dismissCost;
if (directCost != null) {
return directCost;
}
final config = cabin?.currentCpCabinConfigCO;
if (config is! Map) {
return null;
}
return _cpIntValue(
_firstCpConfigValue(config, const [
"dismissCost",
"dismissGold",
"dismissCoins",
"dismissCoin",
"cancelCost",
"cancelGold",
"removeCost",
"partWaysCost",
"consumeGold",
"goldCost",
"cost",
"coins",
"priceGold",
]),
);
}
dynamic _firstCpConfigValue(Map source, List<String> keys) {
for (final key in keys) {
if (source.containsKey(key) && source[key] != null) {
return source[key];
}
}
return null;
}
int? _cpIntValue(dynamic value) {
if (value is int) {
return value;
}
if (value is num) {
return value.toInt();
}
return int.tryParse(value?.toString() ?? "");
}
Future<SocialChatUserProfile> _withCurrentVipLevel(
SocialChatUserProfile profile,
String userId,
) async {
if (!_isCurrentUserId(userId)) {
return profile;
}
final profileVipLevel = profile.vipLevel?.trim();
if (profileVipLevel != null &&
profileVipLevel.isNotEmpty &&
profile.getHeaddress() != null) {
return profile;
}
final vipStatus = await _loadCurrentVipStatus();
final currentVipLevel = currentUserProfile?.vipLevel?.trim();
final resolvedVipLevel =
profileVipLevel != null && profileVipLevel.isNotEmpty
? profileVipLevel
: currentVipLevel != null && currentVipLevel.isNotEmpty
? currentVipLevel
: _vipLevelFromStatus(vipStatus);
var nextProfile = profile;
if (resolvedVipLevel != null && resolvedVipLevel.isNotEmpty) {
nextProfile = nextProfile.copyWith(vipLevel: resolvedVipLevel);
}
return socialChatProfileWithAvatarFrameResource(
nextProfile,
_avatarFramePropsFromVipStatus(vipStatus),
expireTime: _expireTimeFromVipStatus(vipStatus),
);
}
bool _isCurrentUserId(String userId) {
final currentUserId =
AccountStorage().getCurrentUser()?.userProfile?.id?.trim();
final normalizedUserId = userId.trim();
return currentUserId != null &&
currentUserId.isNotEmpty &&
currentUserId == normalizedUserId;
}
Future<SCVipStatusRes?> _loadCurrentVipStatus() async {
final repository = SCVipRepositoryImp();
try {
final home = await repository.vipHome();
final state = home.state;
if (state != null && state.levelInt > 0) {
return state;
}
} catch (_) {}
try {
return repository.vipStatus();
} catch (_) {
return null;
}
}
PropsResources? _avatarFramePropsFromVipStatus(SCVipStatusRes? status) {
if (status?.isActive != true) {
return null;
}
final resource = status?.avatarFrame;
return socialChatAvatarFrameProps(
id: resource?.resourceId,
name: resource?.name,
sourceUrl: resource?.sourceResourceUrl,
cover: resource?.coverUrl ?? resource?.cover,
code: "VIP_AVATAR_FRAME",
);
}
String? _expireTimeFromVipStatus(SCVipStatusRes? status) {
final expireAt = status?.expireAt?.trim();
if (expireAt == null || expireAt.isEmpty) {
return null;
}
final normalized =
expireAt.contains("T") ? expireAt : expireAt.replaceFirst(" ", "T");
return DateTime.tryParse(normalized)?.millisecondsSinceEpoch.toString();
}
String? _vipLevelFromStatus(SCVipStatusRes? status) {
if (status == null) {
return null;
}
if (status.active == false) {
return '0';
}
final level = status.levelInt;
return level > 0 ? level.toString() : '0';
}
void followUser(String userId) async {
var result = await SCAccountRepository().followUser(userId);
if (result) {
if (userCardInfo != null) {
userCardInfo = userCardInfo?.copyWith(follow: !userCardInfo!.follow!);
notifyListeners();
}
}
}
double myBalance = 0.0;
void balance() async {
myBalance = await SCAccountRepository().balance();
notifyListeners();
}
updateBalance(double m) {
myBalance = m;
notifyListeners();
}
void inviteHostOpt(BuildContext ct, String id, String status) async {
final successMessage = SCAppLocalizations.of(ct)!.operationSuccessful;
try {
await SCAccountRepository().inviteHost(id, status);
getUserIdentity();
SCTts.show(successMessage);
} catch (e) {}
}
void inviteAgentOpt(BuildContext ct, String id, String status) async {
final successMessage = SCAppLocalizations.of(ct)!.operationSuccessful;
try {
await SCAccountRepository().inviteAgent(id, status);
getUserIdentity();
SCTts.show(successMessage);
} catch (e) {}
}
void inviteBDOpt(BuildContext ct, String id, String status) async {
final successMessage = SCAppLocalizations.of(ct)!.operationSuccessful;
try {
await SCAccountRepository().inviteBD(id, status);
getUserIdentity();
SCTts.show(successMessage);
} catch (e) {}
}
void inviteBDLeader(BuildContext ct, String id, String status) async {
final successMessage = SCAppLocalizations.of(ct)!.operationSuccessful;
try {
await SCAccountRepository().inviteBDLeader(id, status);
getUserIdentity();
SCTts.show(successMessage);
} catch (e) {}
}
void inviteRechargeAgent(BuildContext ct, String id, String status) async {
final successMessage = SCAppLocalizations.of(ct)!.operationSuccessful;
try {
await SCAccountRepository().inviteRechargeAgent(id, status);
getUserIdentity();
SCTts.show(successMessage);
} catch (e) {}
}
Future<bool> cpRlationshipProcessApply(
BuildContext ct,
String id,
bool status,
) async {
final successMessage = SCAppLocalizations.of(ct)!.operationSuccessful;
try {
await SCAccountRepository().cpRelationshipProcessApply(id, status);
await fetchUserProfileData(loadGuardCount: false);
await refreshLoadedCpProfiles();
SCTts.show(successMessage);
return true;
} catch (e) {
return false;
}
}
Future<void> refreshLoadedCpProfiles() async {
await _refreshLoadedCpProfilesForUserIds(null);
}
Future<void> refreshLoadedCpProfilesForUsers(Set<String> userIds) async {
final normalizedUserIds =
userIds.map((userId) => userId.trim()).where((userId) {
return userId.isNotEmpty;
}).toSet();
if (normalizedUserIds.isEmpty) {
return;
}
await _refreshLoadedCpProfilesForUserIds(normalizedUserIds);
}
void removeLoadedCpRelation({
required String userId,
required String peerUserId,
required String relationType,
}) {
final normalizedUserId = userId.trim();
final normalizedPeerUserId = peerUserId.trim();
final normalizedRelationType = _normalizeCpRelationType(relationType);
if (normalizedUserId.isEmpty ||
normalizedPeerUserId.isEmpty ||
normalizedRelationType.isEmpty) {
return;
}
var changed = false;
final currentProfile = currentUserProfile;
if (currentProfile != null) {
final nextCurrentProfile = _profileWithoutCpRelation(
currentProfile,
userId: normalizedUserId,
peerUserId: normalizedPeerUserId,
relationType: normalizedRelationType,
);
if (!identical(nextCurrentProfile, currentProfile)) {
final currentUser = AccountStorage().getCurrentUser();
currentUser?.setUserProfile(nextCurrentProfile);
if (currentUser != null) {
AccountStorage().setCurrentUser(currentUser);
}
changed = true;
}
}
final detailProfile = userProfile;
if (detailProfile != null) {
final nextDetailProfile = _profileWithoutCpRelation(
detailProfile,
userId: normalizedUserId,
peerUserId: normalizedPeerUserId,
relationType: normalizedRelationType,
);
if (!identical(nextDetailProfile, detailProfile)) {
userProfile = nextDetailProfile;
changed = true;
}
}
final cardProfile = userCardInfo?.userProfile;
if (cardProfile != null) {
final nextCardProfile = _profileWithoutCpRelation(
cardProfile,
userId: normalizedUserId,
peerUserId: normalizedPeerUserId,
relationType: normalizedRelationType,
);
if (!identical(nextCardProfile, cardProfile)) {
userCardInfo = userCardInfo?.copyWith(userProfile: nextCardProfile);
changed = true;
}
}
if (changed) {
notifyListeners();
}
}
SocialChatUserProfile _profileWithoutCpRelation(
SocialChatUserProfile profile, {
required String userId,
required String peerUserId,
required String relationType,
}) {
var changed = false;
List<CPRes>? filterRelations(List<CPRes>? relations) {
if (relations == null) {
return null;
}
final filtered =
relations.where((relation) {
return !_matchesCpRelation(
relation,
userId: userId,
peerUserId: peerUserId,
relationType: relationType,
);
}).toList();
if (filtered.length != relations.length) {
changed = true;
}
return filtered;
}
final nextCpList = filterRelations(profile.cpList);
final nextCloseFriendList = filterRelations(profile.closeFriendList);
if (!changed) {
return profile;
}
return profile.copyWith(
cpList: nextCpList,
closeFriendList: nextCloseFriendList,
isCpRelation: nextCpList?.isNotEmpty == true,
);
}
bool _matchesCpRelation(
CPRes relation, {
required String userId,
required String peerUserId,
required String relationType,
}) {
if (_normalizeCpRelationType(relation.relationType) != relationType) {
return false;
}
final relationUserIds = <String>{
relation.meUserId?.trim() ?? "",
relation.cpUserId?.trim() ?? "",
}..remove("");
return relationUserIds.contains(userId) &&
relationUserIds.contains(peerUserId);
}
Future<void> _refreshLoadedCpProfilesForUserIds(Set<String>? userIds) async {
String refreshedUserId(String? userId) {
final normalizedUserId = userId?.trim() ?? "";
if (normalizedUserId.isEmpty) {
return "";
}
return userIds == null || userIds.contains(normalizedUserId)
? normalizedUserId
: "";
}
final currentProfile = currentUserProfile;
final currentUserId = refreshedUserId(currentProfile?.id);
if (currentProfile != null && currentUserId.isNotEmpty) {
syncCurrentUserProfile(
await _withCpCabinProfiles(currentProfile, currentUserId),
);
}
final detailUserId = refreshedUserId(userProfile?.id);
if (userProfile != null && detailUserId.isNotEmpty) {
userProfile = await _withCpCabinProfiles(userProfile!, detailUserId);
}
final cardProfile = userCardInfo?.userProfile;
final cardUserId = refreshedUserId(cardProfile?.id);
if (userCardInfo != null && cardProfile != null && cardUserId.isNotEmpty) {
userCardInfo = userCardInfo?.copyWith(
userProfile: await _withCpCabinProfiles(cardProfile, cardUserId),
);
}
notifyListeners();
}
}