valtech-components 4.0.395 → 4.0.397

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -58,7 +58,7 @@ import fixWebmDuration from 'fix-webm-duration';
58
58
  * Current version of valtech-components.
59
59
  * This is automatically updated during the publish process.
60
60
  */
61
- const VERSION = '4.0.395';
61
+ const VERSION = '4.0.397';
62
62
 
63
63
  // Control de estado de refresco (singleton a nivel de módulo)
64
64
  let isRefreshing = false;
@@ -61587,13 +61587,14 @@ const COLLECTION_PATH = 'miniGames/chessimals/games';
61587
61587
  const GAME_TTL_MS = 24 * 60 * 60 * 1000;
61588
61588
  class ChessimalsOnlineService {
61589
61589
  constructor() {
61590
- this.auth = inject(Auth);
61590
+ this.firebaseAuth = inject(Auth);
61591
+ this.auth = inject(AuthService);
61591
61592
  this.firestore = inject(Firestore);
61592
61593
  this.firebase = inject(FirebaseService);
61593
61594
  this.profiles = inject(MiniGamePlayerProfileService);
61594
61595
  }
61595
61596
  get currentUid() {
61596
- return this.auth.currentUser?.uid;
61597
+ return this.firebaseAuth.currentUser?.uid;
61597
61598
  }
61598
61599
  normalizeGameId(value) {
61599
61600
  const raw = (value ?? '').trim().toUpperCase();
@@ -61663,11 +61664,11 @@ class ChessimalsOnlineService {
61663
61664
  this.assertNotExpired(state);
61664
61665
  if (this.isParticipant(state, uid))
61665
61666
  return;
61666
- if (state.status === 'waiting' && state.online?.lobby?.uid === uid)
61667
+ if (this.isHostWaiting(state, uid))
61667
61668
  return;
61668
61669
  if (state.status !== 'waiting' || !state.online?.lobby)
61669
61670
  return;
61670
- if (state.online.pendingGuest?.uid === uid)
61671
+ if (this.isPendingGuest(state, uid))
61671
61672
  return;
61672
61673
  const now = new Date();
61673
61674
  const guest = this.createParticipant(uid, now.toISOString());
@@ -61692,7 +61693,7 @@ class ChessimalsOnlineService {
61692
61693
  throw new Error('CHESSIMALS_GAME_NOT_FOUND');
61693
61694
  const state = snapshot.data();
61694
61695
  this.assertNotExpired(state);
61695
- if (state.status !== 'waiting' || state.online?.lobby?.uid !== uid || !state.online.pendingGuest)
61696
+ if (!this.isHostWaiting(state, uid) || !state.online.pendingGuest)
61696
61697
  return;
61697
61698
  const now = new Date();
61698
61699
  const [hostSide, guestSide] = Math.random() > 0.5 ? ['white', 'black'] : ['black', 'white'];
@@ -61728,7 +61729,7 @@ class ChessimalsOnlineService {
61728
61729
  throw new Error('CHESSIMALS_GAME_NOT_FOUND');
61729
61730
  const state = snapshot.data();
61730
61731
  this.assertNotExpired(state);
61731
- if (state.status !== 'waiting' || state.online?.lobby?.uid !== uid || !state.online.pendingGuest)
61732
+ if (!this.isHostWaiting(state, uid) || !state.online.pendingGuest)
61732
61733
  return;
61733
61734
  const now = new Date();
61734
61735
  const online = { ...state.online, updatedAt: now.toISOString() };
@@ -61830,8 +61831,12 @@ class ChessimalsOnlineService {
61830
61831
  const participants = state.online?.participants;
61831
61832
  if (!participants?.white || !participants.black || !state.online)
61832
61833
  return;
61833
- const requests = Array.from(new Set([...(state.online.rematchRequests ?? []), uid]));
61834
- const bothReady = [participants.white.uid, participants.black.uid].every((item) => requests.includes(item));
61834
+ const currentParticipant = this.participantForSide(state, this.participantSide(state, uid));
61835
+ const requestKey = this.participantKey(currentParticipant);
61836
+ if (!requestKey)
61837
+ return;
61838
+ const requests = Array.from(new Set([...(state.online.rematchRequests ?? []), requestKey]));
61839
+ const bothReady = [participants.white, participants.black].every((item) => this.participantRequestKeys(item).some((key) => requests.includes(key)));
61835
61840
  const now = new Date();
61836
61841
  if (!bothReady) {
61837
61842
  transaction.set(ref, {
@@ -61862,20 +61867,62 @@ class ChessimalsOnlineService {
61862
61867
  });
61863
61868
  }
61864
61869
  participantSide(state, uid = this.currentUid) {
61865
- if (!uid)
61866
- return undefined;
61867
- const participants = state.online?.participants;
61868
- if (participants?.white?.uid === uid)
61869
- return 'white';
61870
- if (participants?.black?.uid === uid)
61871
- return 'black';
61870
+ const entries = this.participantEntries(state);
61871
+ const currentSeatKey = this.currentSeatKey(uid);
61872
+ if (currentSeatKey) {
61873
+ const seatMatch = entries.find(([, participant]) => this.participantKey(participant) === currentSeatKey);
61874
+ if (seatMatch)
61875
+ return seatMatch[0];
61876
+ }
61877
+ if (uid) {
61878
+ const currentProfile = this.profiles.activeProfile();
61879
+ if (currentProfile) {
61880
+ const legacyProfileMatch = entries.find(([, participant]) => participant.uid === uid
61881
+ && participant.avatar === currentProfile.avatar
61882
+ && this.normalizeColor(participant.color) === this.normalizeColor(currentProfile.color));
61883
+ if (legacyProfileMatch)
61884
+ return legacyProfileMatch[0];
61885
+ }
61886
+ const uidMatches = entries.filter(([, participant]) => participant.uid === uid);
61887
+ if (uidMatches.length === 1)
61888
+ return uidMatches[0][0];
61889
+ }
61890
+ const accountUserId = this.auth.user()?.userId;
61891
+ if (accountUserId) {
61892
+ const accountMatches = entries.filter(([, participant]) => participant.accountUserId === accountUserId);
61893
+ if (accountMatches.length === 1)
61894
+ return accountMatches[0][0];
61895
+ }
61872
61896
  return undefined;
61873
61897
  }
61874
61898
  isParticipant(state, uid = this.currentUid) {
61875
61899
  return !!this.participantSide(state, uid);
61876
61900
  }
61877
61901
  isHostWaiting(state, uid = this.currentUid) {
61878
- return !!uid && state.status === 'waiting' && state.online?.lobby?.uid === uid;
61902
+ return state.status === 'waiting' && this.matchesCurrentParticipant(state.online?.lobby, uid);
61903
+ }
61904
+ isPendingGuest(state, uid = this.currentUid) {
61905
+ return state.status === 'waiting' && this.matchesCurrentParticipant(state.online?.pendingGuest, uid);
61906
+ }
61907
+ hasCurrentRematchRequest(state, uid = this.currentUid) {
61908
+ const side = this.participantSide(state, uid);
61909
+ const participant = this.participantForSide(state, side);
61910
+ return this.participantRequestKeys(participant).some((key) => (state.online?.rematchRequests ?? []).includes(key));
61911
+ }
61912
+ hasOpponentRematchRequest(state, uid = this.currentUid) {
61913
+ const side = this.participantSide(state, uid);
61914
+ if (!side)
61915
+ return false;
61916
+ const opponent = this.participantForSide(state, side === 'white' ? 'black' : 'white');
61917
+ return this.participantRequestKeys(opponent).some((key) => (state.online?.rematchRequests ?? []).includes(key));
61918
+ }
61919
+ participantKey(participant) {
61920
+ return participant?.seatKey
61921
+ || this.buildSeatKey(participant?.uid, participant?.profileId)
61922
+ || participant?.profileId
61923
+ || participant?.accountUserId
61924
+ || participant?.uid
61925
+ || '';
61879
61926
  }
61880
61927
  shareUrl(gameId, baseRoute = '/mini-games') {
61881
61928
  const origin = typeof window === 'undefined' ? '' : window.location.origin;
@@ -61905,7 +61952,7 @@ class ChessimalsOnlineService {
61905
61952
  }
61906
61953
  async ensureUser() {
61907
61954
  await this.firebase.whenFirebaseAuthReady();
61908
- const user = this.auth.currentUser;
61955
+ const user = this.firebaseAuth.currentUser;
61909
61956
  if (user?.uid) {
61910
61957
  await user.getIdToken();
61911
61958
  return user.uid;
@@ -61921,16 +61968,88 @@ class ChessimalsOnlineService {
61921
61968
  }
61922
61969
  }
61923
61970
  createParticipant(uid, joinedAt) {
61971
+ const user = this.auth.user();
61972
+ const firebaseUser = this.firebaseAuth.currentUser;
61924
61973
  const profile = this.profiles.activeProfile();
61974
+ const handle = this.normalizeHandle(user?.handle);
61975
+ const avatarUrl = user?.avatarUrl?.trim() || firebaseUser?.photoURL?.trim() || undefined;
61976
+ const email = user?.email?.trim() || firebaseUser?.email?.trim() || undefined;
61977
+ const profileId = profile?.id ?? user?.userId ?? uid;
61925
61978
  return {
61926
61979
  uid,
61927
- profileId: profile?.id ?? 'local-player',
61928
- name: this.profiles.displayName(profile, 'Jugador'),
61980
+ accountUserId: user?.userId,
61981
+ profileId,
61982
+ seatKey: this.buildSeatKey(uid, profileId),
61983
+ name: user?.name?.trim()
61984
+ || firebaseUser?.displayName?.trim()
61985
+ || email
61986
+ || this.profiles.displayName(profile, 'Jugador'),
61987
+ handle,
61988
+ email,
61929
61989
  avatar: profile?.avatar ?? '♟️',
61990
+ avatarUrl,
61930
61991
  color: profile?.color ?? '#ff00b2',
61931
61992
  joinedAt,
61932
61993
  };
61933
61994
  }
61995
+ normalizeHandle(handle) {
61996
+ const trimmed = handle?.trim();
61997
+ if (!trimmed)
61998
+ return undefined;
61999
+ return trimmed.startsWith('@') ? trimmed : `@${trimmed}`;
62000
+ }
62001
+ buildSeatKey(uid, profileId) {
62002
+ const normalizedUid = uid?.trim();
62003
+ const normalizedProfileId = profileId?.trim();
62004
+ if (!normalizedUid || !normalizedProfileId)
62005
+ return undefined;
62006
+ return `${normalizedUid}:${normalizedProfileId}`;
62007
+ }
62008
+ currentSeatKey(uid = this.currentUid) {
62009
+ return this.buildSeatKey(uid, this.profiles.activeProfile()?.id);
62010
+ }
62011
+ participantForSide(state, side) {
62012
+ if (!side)
62013
+ return undefined;
62014
+ return state.online?.participants?.[side];
62015
+ }
62016
+ participantEntries(state) {
62017
+ const participants = state.online?.participants;
62018
+ return ['white', 'black']
62019
+ .map((side) => [side, participants?.[side]])
62020
+ .filter((entry) => !!entry[1]);
62021
+ }
62022
+ participantRequestKeys(participant) {
62023
+ if (!participant)
62024
+ return [];
62025
+ const keys = [
62026
+ this.participantKey(participant),
62027
+ participant.uid,
62028
+ participant.accountUserId,
62029
+ participant.profileId,
62030
+ ];
62031
+ return Array.from(new Set(keys.filter((key) => !!key)));
62032
+ }
62033
+ matchesCurrentParticipant(participant, uid = this.currentUid) {
62034
+ if (!participant)
62035
+ return false;
62036
+ const seatKey = this.currentSeatKey(uid);
62037
+ if (seatKey && this.participantKey(participant) === seatKey)
62038
+ return true;
62039
+ const profile = this.profiles.activeProfile();
62040
+ if (uid && profile) {
62041
+ return participant.uid === uid
62042
+ && participant.avatar === profile.avatar
62043
+ && this.normalizeColor(participant.color) === this.normalizeColor(profile.color);
62044
+ }
62045
+ if (uid && participant.uid === uid)
62046
+ return true;
62047
+ const accountUserId = this.auth.user()?.userId;
62048
+ return !!accountUserId && participant.accountUserId === accountUserId;
62049
+ }
62050
+ normalizeColor(color) {
62051
+ return (color ?? '').trim().toLowerCase();
62052
+ }
61934
62053
  withUpdatedAt(state, date = new Date()) {
61935
62054
  if (!state.online)
61936
62055
  return state;
@@ -61949,10 +62068,13 @@ class ChessimalsOnlineService {
61949
62068
  };
61950
62069
  }
61951
62070
  createMoveLogEntry(args) {
62071
+ const participant = this.participantForSide(args.state, args.side);
61952
62072
  return this.withoutUndefined({
61953
62073
  id: args.id,
61954
62074
  at: new Date().toISOString(),
61955
62075
  uid: args.uid,
62076
+ profileId: participant?.profileId,
62077
+ seatKey: this.participantKey(participant) || undefined,
61956
62078
  side: args.side,
61957
62079
  status: args.status,
61958
62080
  code: args.code,
@@ -61977,8 +62099,8 @@ class ChessimalsOnlineService {
61977
62099
  if (!state.online)
61978
62100
  return undefined;
61979
62101
  const side = this.participantSide(state, uid);
61980
- const isLobbyHost = state.online.lobby?.uid === uid;
61981
- const isPendingGuest = state.online.pendingGuest?.uid === uid;
62102
+ const isLobbyHost = this.isHostWaiting(state, uid);
62103
+ const isPendingGuest = this.isPendingGuest(state, uid);
61982
62104
  if (!side && !isLobbyHost && !isPendingGuest)
61983
62105
  return undefined;
61984
62106
  const opponent = side
@@ -63172,7 +63294,7 @@ class ChessimalsGameComponent {
63172
63294
  return 'spectator';
63173
63295
  if (this.online.isHostWaiting(game, uid))
63174
63296
  return 'waiting';
63175
- if (game.status === 'waiting' && game.online?.pendingGuest?.uid === uid)
63297
+ if (this.online.isPendingGuest(game, uid))
63176
63298
  return 'pending';
63177
63299
  return this.online.participantSide(game, uid) ?? 'spectator';
63178
63300
  });
@@ -63194,9 +63316,8 @@ class ChessimalsGameComponent {
63194
63316
  const uid = this.onlineUid();
63195
63317
  if (!uid || this.onlineRole() === 'spectator')
63196
63318
  return 'none';
63197
- const requests = game.online?.rematchRequests ?? [];
63198
- const hasMine = requests.includes(uid);
63199
- const hasOther = requests.some((item) => item !== uid);
63319
+ const hasMine = this.online.hasCurrentRematchRequest(game, uid);
63320
+ const hasOther = this.online.hasOpponentRematchRequest(game, uid);
63200
63321
  if (hasOther && !hasMine)
63201
63322
  return 'incoming';
63202
63323
  if (hasMine)
@@ -63918,13 +64039,33 @@ class ChessimalsGameComponent {
63918
64039
  const uid = this.onlineUid();
63919
64040
  return !!uid
63920
64041
  && game.mode === 'online'
63921
- && game.status === 'waiting'
63922
- && game.online?.lobby?.uid === uid
64042
+ && this.online.isHostWaiting(game, uid)
63923
64043
  && !!game.online.pendingGuest;
63924
64044
  }
63925
64045
  pendingGuestName() {
63926
64046
  return this.game().online?.pendingGuest?.name ?? this.t('defaultPlayerName');
63927
64047
  }
64048
+ pendingGuestHandle() {
64049
+ return this.game().online?.pendingGuest?.handle ?? '';
64050
+ }
64051
+ pendingGuestAvatar() {
64052
+ return this.game().online?.pendingGuest?.avatar ?? '♟️';
64053
+ }
64054
+ pendingGuestAvatarUrl() {
64055
+ return this.game().online?.pendingGuest?.avatarUrl ?? '';
64056
+ }
64057
+ pendingGuestAvatarProps() {
64058
+ const guest = this.game().online?.pendingGuest;
64059
+ return {
64060
+ user: {
64061
+ name: guest?.name,
64062
+ handle: guest?.handle,
64063
+ avatarUrl: guest?.avatarUrl,
64064
+ },
64065
+ size: 'small',
64066
+ bordered: true,
64067
+ };
64068
+ }
63928
64069
  waitingTitle() {
63929
64070
  if (this.canReviewPendingGuest())
63930
64071
  return this.t('chessimalsJoinRequestTitle');
@@ -64079,12 +64220,12 @@ class ChessimalsGameComponent {
64079
64220
  this.game.set(state);
64080
64221
  this.selectedBoardSize.set(state.boardSize);
64081
64222
  this.resumeSplashVisible.set(false);
64082
- const previousPendingUid = previous.id === state.id ? previous.online?.pendingGuest?.uid : undefined;
64083
- const nextPendingUid = state.online?.pendingGuest?.uid;
64223
+ const previousPendingKey = previous.id === state.id ? this.online.participantKey(previous.online?.pendingGuest) : '';
64224
+ const nextPendingKey = this.online.participantKey(state.online?.pendingGuest);
64084
64225
  if (state.status === 'waiting' &&
64085
- state.online?.lobby?.uid === this.online.currentUid &&
64086
- !!nextPendingUid &&
64087
- nextPendingUid !== previousPendingUid) {
64226
+ this.online.isHostWaiting(state, this.online.currentUid) &&
64227
+ !!nextPendingKey &&
64228
+ nextPendingKey !== previousPendingKey) {
64088
64229
  this.audio.chessimalsJoinRequest();
64089
64230
  void this.haptics.success();
64090
64231
  }
@@ -64312,12 +64453,19 @@ class ChessimalsGameComponent {
64312
64453
  </div>
64313
64454
  @if (canReviewPendingGuest()) {
64314
64455
  <div class="chessimals-pending-player">
64315
- <span class="chessimals-player-strip__avatar" aria-hidden="true">
64316
- {{ game().online?.pendingGuest?.avatar ?? '♟️' }}
64317
- </span>
64456
+ @if (pendingGuestAvatarUrl()) {
64457
+ <val-user-avatar [props]="pendingGuestAvatarProps()" />
64458
+ } @else {
64459
+ <span class="chessimals-player-strip__avatar" aria-hidden="true">
64460
+ {{ pendingGuestAvatar() }}
64461
+ </span>
64462
+ }
64318
64463
  <div>
64319
64464
  <small>{{ t('chessimalsJoinRequestPlayer') }}</small>
64320
64465
  <strong>{{ pendingGuestName() }}</strong>
64466
+ @if (pendingGuestHandle()) {
64467
+ <small>{{ pendingGuestHandle() }}</small>
64468
+ }
64321
64469
  </div>
64322
64470
  </div>
64323
64471
  }
@@ -64876,12 +65024,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
64876
65024
  </div>
64877
65025
  @if (canReviewPendingGuest()) {
64878
65026
  <div class="chessimals-pending-player">
64879
- <span class="chessimals-player-strip__avatar" aria-hidden="true">
64880
- {{ game().online?.pendingGuest?.avatar ?? '♟️' }}
64881
- </span>
65027
+ @if (pendingGuestAvatarUrl()) {
65028
+ <val-user-avatar [props]="pendingGuestAvatarProps()" />
65029
+ } @else {
65030
+ <span class="chessimals-player-strip__avatar" aria-hidden="true">
65031
+ {{ pendingGuestAvatar() }}
65032
+ </span>
65033
+ }
64882
65034
  <div>
64883
65035
  <small>{{ t('chessimalsJoinRequestPlayer') }}</small>
64884
65036
  <strong>{{ pendingGuestName() }}</strong>
65037
+ @if (pendingGuestHandle()) {
65038
+ <small>{{ pendingGuestHandle() }}</small>
65039
+ }
64885
65040
  </div>
64886
65041
  </div>
64887
65042
  }