valtech-components 4.0.396 → 4.0.398

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.396';
61
+ const VERSION = '4.0.398';
62
62
 
63
63
  // Control de estado de refresco (singleton a nivel de módulo)
64
64
  let isRefreshing = false;
@@ -61664,11 +61664,11 @@ class ChessimalsOnlineService {
61664
61664
  this.assertNotExpired(state);
61665
61665
  if (this.isParticipant(state, uid))
61666
61666
  return;
61667
- if (state.status === 'waiting' && state.online?.lobby?.uid === uid)
61667
+ if (this.isHostWaiting(state, uid))
61668
61668
  return;
61669
61669
  if (state.status !== 'waiting' || !state.online?.lobby)
61670
61670
  return;
61671
- if (state.online.pendingGuest?.uid === uid)
61671
+ if (this.isPendingGuest(state, uid))
61672
61672
  return;
61673
61673
  const now = new Date();
61674
61674
  const guest = this.createParticipant(uid, now.toISOString());
@@ -61693,7 +61693,7 @@ class ChessimalsOnlineService {
61693
61693
  throw new Error('CHESSIMALS_GAME_NOT_FOUND');
61694
61694
  const state = snapshot.data();
61695
61695
  this.assertNotExpired(state);
61696
- if (state.status !== 'waiting' || state.online?.lobby?.uid !== uid || !state.online.pendingGuest)
61696
+ if (!this.isHostWaiting(state, uid) || !state.online.pendingGuest)
61697
61697
  return;
61698
61698
  const now = new Date();
61699
61699
  const [hostSide, guestSide] = Math.random() > 0.5 ? ['white', 'black'] : ['black', 'white'];
@@ -61729,7 +61729,7 @@ class ChessimalsOnlineService {
61729
61729
  throw new Error('CHESSIMALS_GAME_NOT_FOUND');
61730
61730
  const state = snapshot.data();
61731
61731
  this.assertNotExpired(state);
61732
- if (state.status !== 'waiting' || state.online?.lobby?.uid !== uid || !state.online.pendingGuest)
61732
+ if (!this.isHostWaiting(state, uid) || !state.online.pendingGuest)
61733
61733
  return;
61734
61734
  const now = new Date();
61735
61735
  const online = { ...state.online, updatedAt: now.toISOString() };
@@ -61831,8 +61831,12 @@ class ChessimalsOnlineService {
61831
61831
  const participants = state.online?.participants;
61832
61832
  if (!participants?.white || !participants.black || !state.online)
61833
61833
  return;
61834
- const requests = Array.from(new Set([...(state.online.rematchRequests ?? []), uid]));
61835
- 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)));
61836
61840
  const now = new Date();
61837
61841
  if (!bothReady) {
61838
61842
  transaction.set(ref, {
@@ -61863,20 +61867,62 @@ class ChessimalsOnlineService {
61863
61867
  });
61864
61868
  }
61865
61869
  participantSide(state, uid = this.currentUid) {
61866
- if (!uid)
61867
- return undefined;
61868
- const participants = state.online?.participants;
61869
- if (participants?.white?.uid === uid)
61870
- return 'white';
61871
- if (participants?.black?.uid === uid)
61872
- 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
+ }
61873
61896
  return undefined;
61874
61897
  }
61875
61898
  isParticipant(state, uid = this.currentUid) {
61876
61899
  return !!this.participantSide(state, uid);
61877
61900
  }
61878
61901
  isHostWaiting(state, uid = this.currentUid) {
61879
- 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
+ || '';
61880
61926
  }
61881
61927
  shareUrl(gameId, baseRoute = '/mini-games') {
61882
61928
  const origin = typeof window === 'undefined' ? '' : window.location.origin;
@@ -61927,15 +61973,19 @@ class ChessimalsOnlineService {
61927
61973
  const profile = this.profiles.activeProfile();
61928
61974
  const handle = this.normalizeHandle(user?.handle);
61929
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;
61930
61978
  return {
61931
61979
  uid,
61932
- profileId: user?.userId ?? profile?.id ?? 'local-player',
61980
+ accountUserId: user?.userId,
61981
+ profileId,
61982
+ seatKey: this.buildSeatKey(uid, profileId),
61933
61983
  name: user?.name?.trim()
61934
61984
  || firebaseUser?.displayName?.trim()
61935
- || user?.email?.trim()
61936
- || firebaseUser?.email?.trim()
61985
+ || email
61937
61986
  || this.profiles.displayName(profile, 'Jugador'),
61938
61987
  handle,
61988
+ email,
61939
61989
  avatar: profile?.avatar ?? '♟️',
61940
61990
  avatarUrl,
61941
61991
  color: profile?.color ?? '#ff00b2',
@@ -61948,6 +61998,58 @@ class ChessimalsOnlineService {
61948
61998
  return undefined;
61949
61999
  return trimmed.startsWith('@') ? trimmed : `@${trimmed}`;
61950
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
+ }
61951
62053
  withUpdatedAt(state, date = new Date()) {
61952
62054
  if (!state.online)
61953
62055
  return state;
@@ -61966,10 +62068,13 @@ class ChessimalsOnlineService {
61966
62068
  };
61967
62069
  }
61968
62070
  createMoveLogEntry(args) {
62071
+ const participant = this.participantForSide(args.state, args.side);
61969
62072
  return this.withoutUndefined({
61970
62073
  id: args.id,
61971
62074
  at: new Date().toISOString(),
61972
62075
  uid: args.uid,
62076
+ profileId: participant?.profileId,
62077
+ seatKey: this.participantKey(participant) || undefined,
61973
62078
  side: args.side,
61974
62079
  status: args.status,
61975
62080
  code: args.code,
@@ -61994,8 +62099,8 @@ class ChessimalsOnlineService {
61994
62099
  if (!state.online)
61995
62100
  return undefined;
61996
62101
  const side = this.participantSide(state, uid);
61997
- const isLobbyHost = state.online.lobby?.uid === uid;
61998
- const isPendingGuest = state.online.pendingGuest?.uid === uid;
62102
+ const isLobbyHost = this.isHostWaiting(state, uid);
62103
+ const isPendingGuest = this.isPendingGuest(state, uid);
61999
62104
  if (!side && !isLobbyHost && !isPendingGuest)
62000
62105
  return undefined;
62001
62106
  const opponent = side
@@ -63189,7 +63294,7 @@ class ChessimalsGameComponent {
63189
63294
  return 'spectator';
63190
63295
  if (this.online.isHostWaiting(game, uid))
63191
63296
  return 'waiting';
63192
- if (game.status === 'waiting' && game.online?.pendingGuest?.uid === uid)
63297
+ if (this.online.isPendingGuest(game, uid))
63193
63298
  return 'pending';
63194
63299
  return this.online.participantSide(game, uid) ?? 'spectator';
63195
63300
  });
@@ -63204,6 +63309,15 @@ class ChessimalsGameComponent {
63204
63309
  return true;
63205
63310
  return this.onlineRole() === game.currentTurn;
63206
63311
  });
63312
+ this.canControlCurrentGame = computed(() => {
63313
+ if (this.projectionMode)
63314
+ return false;
63315
+ const game = this.game();
63316
+ if (game.mode !== 'online')
63317
+ return true;
63318
+ const role = this.onlineRole();
63319
+ return role === 'white' || role === 'black';
63320
+ });
63207
63321
  this.rematchState = computed(() => {
63208
63322
  const game = this.game();
63209
63323
  if (game.mode !== 'online' || game.status !== 'finished')
@@ -63211,9 +63325,8 @@ class ChessimalsGameComponent {
63211
63325
  const uid = this.onlineUid();
63212
63326
  if (!uid || this.onlineRole() === 'spectator')
63213
63327
  return 'none';
63214
- const requests = game.online?.rematchRequests ?? [];
63215
- const hasMine = requests.includes(uid);
63216
- const hasOther = requests.some((item) => item !== uid);
63328
+ const hasMine = this.online.hasCurrentRematchRequest(game, uid);
63329
+ const hasOther = this.online.hasOpponentRematchRequest(game, uid);
63217
63330
  if (hasOther && !hasMine)
63218
63331
  return 'incoming';
63219
63332
  if (hasMine)
@@ -63508,6 +63621,8 @@ class ChessimalsGameComponent {
63508
63621
  this.screen.set('home');
63509
63622
  }
63510
63623
  requestRestartGame() {
63624
+ if (!this.canControlCurrentGame())
63625
+ return;
63511
63626
  this.confirmAction.set('restart');
63512
63627
  this.audio.flip();
63513
63628
  }
@@ -63524,7 +63639,7 @@ class ChessimalsGameComponent {
63524
63639
  this.audio.flip();
63525
63640
  }
63526
63641
  requestResign() {
63527
- if (this.game().status === 'finished')
63642
+ if (this.game().status === 'finished' || !this.canControlCurrentGame())
63528
63643
  return;
63529
63644
  this.confirmAction.set('resign');
63530
63645
  this.audio.flip();
@@ -63935,8 +64050,7 @@ class ChessimalsGameComponent {
63935
64050
  const uid = this.onlineUid();
63936
64051
  return !!uid
63937
64052
  && game.mode === 'online'
63938
- && game.status === 'waiting'
63939
- && game.online?.lobby?.uid === uid
64053
+ && this.online.isHostWaiting(game, uid)
63940
64054
  && !!game.online.pendingGuest;
63941
64055
  }
63942
64056
  pendingGuestName() {
@@ -64040,6 +64154,8 @@ class ChessimalsGameComponent {
64040
64154
  async requestOnlineRematch() {
64041
64155
  if (this.game().mode !== 'online')
64042
64156
  return;
64157
+ if (!this.canControlCurrentGame())
64158
+ return;
64043
64159
  if (this.rematchState() === 'requested' || this.rematchLoading())
64044
64160
  return;
64045
64161
  const accepting = this.rematchState() === 'incoming';
@@ -64117,12 +64233,12 @@ class ChessimalsGameComponent {
64117
64233
  this.game.set(state);
64118
64234
  this.selectedBoardSize.set(state.boardSize);
64119
64235
  this.resumeSplashVisible.set(false);
64120
- const previousPendingUid = previous.id === state.id ? previous.online?.pendingGuest?.uid : undefined;
64121
- const nextPendingUid = state.online?.pendingGuest?.uid;
64236
+ const previousPendingKey = previous.id === state.id ? this.online.participantKey(previous.online?.pendingGuest) : '';
64237
+ const nextPendingKey = this.online.participantKey(state.online?.pendingGuest);
64122
64238
  if (state.status === 'waiting' &&
64123
- state.online?.lobby?.uid === this.online.currentUid &&
64124
- !!nextPendingUid &&
64125
- nextPendingUid !== previousPendingUid) {
64239
+ this.online.isHostWaiting(state, this.online.currentUid) &&
64240
+ !!nextPendingKey &&
64241
+ nextPendingKey !== previousPendingKey) {
64126
64242
  this.audio.chessimalsJoinRequest();
64127
64243
  void this.haptics.success();
64128
64244
  }
@@ -64601,11 +64717,11 @@ class ChessimalsGameComponent {
64601
64717
  }
64602
64718
 
64603
64719
  <div class="chessimals-actions">
64604
- @if (game().status !== 'finished') {
64720
+ @if (game().status !== 'finished' && canControlCurrentGame()) {
64605
64721
  <button class="chessimals-secondary" type="button" (click)="requestResign()">
64606
64722
  {{ t('chessimalsResign') }}
64607
64723
  </button>
64608
- } @else if (game().mode === 'online' && onlineRole() !== 'spectator') {
64724
+ } @else if (game().mode === 'online' && canControlCurrentGame()) {
64609
64725
  <button
64610
64726
  class="chessimals-secondary"
64611
64727
  type="button"
@@ -65172,11 +65288,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
65172
65288
  }
65173
65289
 
65174
65290
  <div class="chessimals-actions">
65175
- @if (game().status !== 'finished') {
65291
+ @if (game().status !== 'finished' && canControlCurrentGame()) {
65176
65292
  <button class="chessimals-secondary" type="button" (click)="requestResign()">
65177
65293
  {{ t('chessimalsResign') }}
65178
65294
  </button>
65179
- } @else if (game().mode === 'online' && onlineRole() !== 'spectator') {
65295
+ } @else if (game().mode === 'online' && canControlCurrentGame()) {
65180
65296
  <button
65181
65297
  class="chessimals-secondary"
65182
65298
  type="button"