v0,2,1
This commit is contained in:
		@@ -8,7 +8,7 @@
 | 
			
		||||
  },
 | 
			
		||||
  "package": {
 | 
			
		||||
    "productName": "domino-client",
 | 
			
		||||
    "version": "0.2.0"
 | 
			
		||||
    "version": "0.2.1"
 | 
			
		||||
  },
 | 
			
		||||
  "tauri": {
 | 
			
		||||
    "allowlist": {
 | 
			
		||||
@@ -72,7 +72,7 @@
 | 
			
		||||
        "fullscreen": false,
 | 
			
		||||
        "height": 720,
 | 
			
		||||
        "resizable": true,
 | 
			
		||||
        "title": "Domino v0.2.0",
 | 
			
		||||
        "title": "Domino v0.2.1",
 | 
			
		||||
        "width": 1280,
 | 
			
		||||
        "minHeight": 720,
 | 
			
		||||
        "minWidth": 1280,
 | 
			
		||||
 
 | 
			
		||||
@@ -15,9 +15,3 @@
 | 
			
		||||
  --bulma-danger-s: 74%;
 | 
			
		||||
  --bulma-danger-l: 37%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tabs li.is-disabled {
 | 
			
		||||
  pointer-events: none;
 | 
			
		||||
  cursor: default;
 | 
			
		||||
  opacity: 0.3;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,35 @@
 | 
			
		||||
@import './base.css';
 | 
			
		||||
 | 
			
		||||
html {
 | 
			
		||||
html,
 | 
			
		||||
body,
 | 
			
		||||
#app {
 | 
			
		||||
  font-size: 16px;
 | 
			
		||||
  overflow-y: auto;
 | 
			
		||||
  overflow-x: hidden;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#app.game {
 | 
			
		||||
  overflow-y: hidden;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tabs li.is-disabled {
 | 
			
		||||
  pointer-events: none;
 | 
			
		||||
  cursor: default;
 | 
			
		||||
  opacity: 0.3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.box.is-no-shadow {
 | 
			
		||||
  box-shadow: none;
 | 
			
		||||
  border: 1px solid var(--bulma-input-border-color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.landing .unauthenticated-layout {
 | 
			
		||||
  height: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.is-ellipsis {
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  text-overflow: ellipsis;
 | 
			
		||||
  white-space: nowrap;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -143,3 +143,10 @@ export function createStringMatrix(
 | 
			
		||||
 | 
			
		||||
  return matrix
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function isMobile() {
 | 
			
		||||
  const userAgent = navigator.userAgent || navigator.vendor || window.opera
 | 
			
		||||
  return /android|avantgo|blackberry|bada\/|bb|meego|ip(hone|od|ad)|opera m(ob|in)i|phone|tablet|mobi|ipad|playbook|silk|windows (phone|ce)|webos|kindle|mobile|palm|fennec|gobrowser|hiptop|iemobile|iris|w3c|wap|opera mini|iemobile/i.test(
 | 
			
		||||
    userAgent,
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ let options = ref<MatchSessionOptions>({
 | 
			
		||||
 | 
			
		||||
const winTargetPointsList = [20, 50, 80, 100, 150, 200]
 | 
			
		||||
const winTargetRoundsList = [1, 2, 3, 4, 5, 6]
 | 
			
		||||
const turnWaitSecondsList = [15, 20, 30, 40, 50, 60]
 | 
			
		||||
const turnWaitSecondsList = [5, 10, 15, 20, 30, 40, 50, 60]
 | 
			
		||||
 | 
			
		||||
const backgroundOptiopnList = [
 | 
			
		||||
  {
 | 
			
		||||
@@ -58,86 +58,90 @@ function startSingleMatch() {
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <div class="grid">
 | 
			
		||||
      <div class="cell">
 | 
			
		||||
        <div class="field">
 | 
			
		||||
          <label class="label">{{ $t('mode') }}</label>
 | 
			
		||||
          <div class="control">
 | 
			
		||||
            <div class="buttons has-addons">
 | 
			
		||||
              <button
 | 
			
		||||
                class="button"
 | 
			
		||||
                :class="{ 'is-primary is-selected': isSinglePlayer }"
 | 
			
		||||
                @click="
 | 
			
		||||
                  () => {
 | 
			
		||||
                    console.log('options :>> ', options)
 | 
			
		||||
                    options.numPlayers = 1
 | 
			
		||||
                  }
 | 
			
		||||
                "
 | 
			
		||||
              >
 | 
			
		||||
                {{ $t('singleplayer') }}
 | 
			
		||||
              </button>
 | 
			
		||||
              <button
 | 
			
		||||
                class="button"
 | 
			
		||||
                :class="{ 'is-primary is-selected': isMultiPlayer }"
 | 
			
		||||
                @click="
 | 
			
		||||
                  () => {
 | 
			
		||||
                    console.log('options :>> ', options)
 | 
			
		||||
                    options.numPlayers = 2
 | 
			
		||||
                  }
 | 
			
		||||
                "
 | 
			
		||||
              >
 | 
			
		||||
                {{ $t('multiplayer') }}
 | 
			
		||||
              </button>
 | 
			
		||||
    <div class="fixed-grid has-3-cols has-1-cols-mobile">
 | 
			
		||||
      <div class="grid">
 | 
			
		||||
        <div class="cell">
 | 
			
		||||
          <div class="field">
 | 
			
		||||
            <label class="label">{{ $t('mode') }}</label>
 | 
			
		||||
            <div class="control">
 | 
			
		||||
              <div class="buttons has-addons">
 | 
			
		||||
                <button
 | 
			
		||||
                  class="button"
 | 
			
		||||
                  :class="{ 'is-primary is-selected': isSinglePlayer }"
 | 
			
		||||
                  @click="
 | 
			
		||||
                    () => {
 | 
			
		||||
                      console.log('options :>> ', options)
 | 
			
		||||
                      options.numPlayers = 1
 | 
			
		||||
                    }
 | 
			
		||||
                  "
 | 
			
		||||
                >
 | 
			
		||||
                  {{ $t('singleplayer') }}
 | 
			
		||||
                </button>
 | 
			
		||||
                <button
 | 
			
		||||
                  class="button"
 | 
			
		||||
                  :class="{ 'is-primary is-selected': isMultiPlayer }"
 | 
			
		||||
                  @click="
 | 
			
		||||
                    () => {
 | 
			
		||||
                      console.log('options :>> ', options)
 | 
			
		||||
                      options.numPlayers = 2
 | 
			
		||||
                    }
 | 
			
		||||
                  "
 | 
			
		||||
                >
 | 
			
		||||
                  {{ $t('multiplayer') }}
 | 
			
		||||
                </button>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="cell">
 | 
			
		||||
        <div class="field" v-if="isMultiPlayer">
 | 
			
		||||
          <label class="label">{{ $t('players-number') }}</label>
 | 
			
		||||
          <div class="control">
 | 
			
		||||
            <div class="buttons has-addons">
 | 
			
		||||
              <button
 | 
			
		||||
                class="button is-primary"
 | 
			
		||||
                @click="options.numPlayers--"
 | 
			
		||||
                :disabled="options.numPlayers <= 2"
 | 
			
		||||
              >
 | 
			
		||||
                -
 | 
			
		||||
              </button>
 | 
			
		||||
              <button class="button is-primary" disabled>{{ options.numPlayers }}</button>
 | 
			
		||||
              <button
 | 
			
		||||
                class="button is-primary"
 | 
			
		||||
                @click="options.numPlayers++"
 | 
			
		||||
                :disabled="options.numPlayers >= 4"
 | 
			
		||||
              >
 | 
			
		||||
                +
 | 
			
		||||
              </button>
 | 
			
		||||
        <div class="cell">
 | 
			
		||||
          <div class="field" v-if="isMultiPlayer">
 | 
			
		||||
            <label class="label">{{ $t('players-number') }}</label>
 | 
			
		||||
            <div class="control">
 | 
			
		||||
              <div class="buttons has-addons">
 | 
			
		||||
                <button
 | 
			
		||||
                  class="button is-primary"
 | 
			
		||||
                  @click="options.numPlayers--"
 | 
			
		||||
                  :disabled="options.numPlayers <= 2"
 | 
			
		||||
                >
 | 
			
		||||
                  -
 | 
			
		||||
                </button>
 | 
			
		||||
                <button class="button is-primary" disabled>{{ options.numPlayers }}</button>
 | 
			
		||||
                <button
 | 
			
		||||
                  class="button is-primary"
 | 
			
		||||
                  @click="options.numPlayers++"
 | 
			
		||||
                  :disabled="options.numPlayers >= 4"
 | 
			
		||||
                >
 | 
			
		||||
                  +
 | 
			
		||||
                </button>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="cell">
 | 
			
		||||
        <div class="field" v-if="isMultiPlayer">
 | 
			
		||||
          <div class="control">
 | 
			
		||||
            <label for="teamed" class="checkbox">
 | 
			
		||||
              <input v-model="options.teamed" name="teamed" type="checkbox" />
 | 
			
		||||
              {{ $t('crossed-game-teamed') }}
 | 
			
		||||
            </label>
 | 
			
		||||
        <div class="cell">
 | 
			
		||||
          <div class="field" v-if="isMultiPlayer">
 | 
			
		||||
            <div class="control">
 | 
			
		||||
              <label for="teamed" class="checkbox">
 | 
			
		||||
                <input v-model="options.teamed" name="teamed" type="checkbox" />
 | 
			
		||||
                {{ $t('crossed-game-teamed') }}
 | 
			
		||||
              </label>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="grid" v-if="info?.development">
 | 
			
		||||
      <div class="cell">
 | 
			
		||||
        <div class="field">
 | 
			
		||||
          <label class="label">{{ $t('session-name') }}</label>
 | 
			
		||||
          <div class="control">
 | 
			
		||||
            <input
 | 
			
		||||
              type="text"
 | 
			
		||||
              class="input"
 | 
			
		||||
              v-model="options.sessionName"
 | 
			
		||||
              placeholder="$t('session-name-placeholder')"
 | 
			
		||||
            />
 | 
			
		||||
    <div class="flex-grid has-2-cols has-1-cols-mobile" v-if="info?.development">
 | 
			
		||||
      <div class="grid">
 | 
			
		||||
        <div class="cell">
 | 
			
		||||
          <div class="field">
 | 
			
		||||
            <label class="label">{{ $t('session-name') }}</label>
 | 
			
		||||
            <div class="control">
 | 
			
		||||
              <input
 | 
			
		||||
                type="text"
 | 
			
		||||
                class="input"
 | 
			
		||||
                v-model="options.sessionName"
 | 
			
		||||
                placeholder="$t('session-name-placeholder')"
 | 
			
		||||
              />
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										86
									
								
								src/components/MultiplayerSetupComponent.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/components/MultiplayerSetupComponent.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="block">
 | 
			
		||||
    <h2 class="title is-4">{{ sessionState?.name }}</h2>
 | 
			
		||||
    <h6 class="title is-size-5">{{ $t('players') }}</h6>
 | 
			
		||||
    <div v-for="player in sessionState?.players" :key="player.id">
 | 
			
		||||
      <div class="fixed-grid" style="max-width: 300px">
 | 
			
		||||
        <div class="grid">
 | 
			
		||||
          <div class="cell">{{ player.name }}</div>
 | 
			
		||||
          <div class="cell has-text-centered">
 | 
			
		||||
            <div class="mb-2">
 | 
			
		||||
              <span
 | 
			
		||||
                class="tag"
 | 
			
		||||
                :class="{ 'is-success': player.ready, 'is-danger': !player.ready }"
 | 
			
		||||
                >{{ $t(player.ready ? 'ready' : 'unready') }}</span
 | 
			
		||||
              >
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="mt-4" v-if="amIHost && gameOptions?.teamed && playersToTeamUpWith.length > 0">
 | 
			
		||||
      <div class="field">
 | 
			
		||||
        <label for="background" class="label">Team up with</label>
 | 
			
		||||
        <div class="control">
 | 
			
		||||
          <div class="select">
 | 
			
		||||
            <select v-model="teamedWith" name="teamedWidth">
 | 
			
		||||
              <option v-for="player in playersToTeamUpWith" :key="player.id" :value="player.id">
 | 
			
		||||
                {{ player.name }}
 | 
			
		||||
              </option>
 | 
			
		||||
            </select>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="buttons mt-6">
 | 
			
		||||
      <button class="button is-dark" @click="() => emit('ready')">
 | 
			
		||||
        <span v-if="!readyForStart">{{ $t('ready') }}</span
 | 
			
		||||
        ><span v-else>{{ $t('unready') }}</span>
 | 
			
		||||
      </button>
 | 
			
		||||
      <button
 | 
			
		||||
        class="button is-success"
 | 
			
		||||
        :disabled="!canStart"
 | 
			
		||||
        @click="() => emit('start', teamedWith)"
 | 
			
		||||
        v-if="amIHost"
 | 
			
		||||
      >
 | 
			
		||||
        <span>{{ $t('start') }}</span>
 | 
			
		||||
      </button>
 | 
			
		||||
 | 
			
		||||
      <button class="button is-danger" @click="() => emit('cancel')">
 | 
			
		||||
        <span>{{ $t('cancel') }}</span>
 | 
			
		||||
      </button>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { useGameStore } from '@/stores/game'
 | 
			
		||||
import { useGameOptionsStore } from '@/stores/gameOptions'
 | 
			
		||||
import { storeToRefs } from 'pinia'
 | 
			
		||||
import { computed, ref } from 'vue'
 | 
			
		||||
 | 
			
		||||
let teamedWith = ref<string | undefined>(undefined)
 | 
			
		||||
const emit = defineEmits(['ready', 'start', 'cancel'])
 | 
			
		||||
 | 
			
		||||
const gameStore = useGameStore()
 | 
			
		||||
const gameOptionsStore = useGameOptionsStore()
 | 
			
		||||
 | 
			
		||||
const { sessionState, amIHost, readyForStart } = storeToRefs(gameStore)
 | 
			
		||||
const { gameOptions } = storeToRefs(gameOptionsStore)
 | 
			
		||||
 | 
			
		||||
const playersToTeamUpWith = computed(() => {
 | 
			
		||||
  return (
 | 
			
		||||
    sessionState?.value?.players.filter((player) => player.id !== sessionState?.value?.creator) ||
 | 
			
		||||
    []
 | 
			
		||||
  )
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const canStart = computed(() => {
 | 
			
		||||
  const players = sessionState?.value?.players || []
 | 
			
		||||
  const options = gameOptions.value
 | 
			
		||||
  const allReady = (players.length || 0) > 0 && players.every((player) => player.ready)
 | 
			
		||||
  return (!options?.teamed && allReady) || (options?.teamed && !!teamedWith.value && allReady)
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
							
								
								
									
										69
									
								
								src/components/SessionBoxComponent.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/components/SessionBoxComponent.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="card">
 | 
			
		||||
    <div class="card-content">
 | 
			
		||||
      <p class="title is-6">{{ props.session.name }}</p>
 | 
			
		||||
      <div class="fixed-grid">
 | 
			
		||||
        <div class="grid">
 | 
			
		||||
          <div class="cell has-text-weight-light">{{ $t('id-session-_id') }}</div>
 | 
			
		||||
          <div class="cell has-text-weight-medium">{{ props.session._id }}</div>
 | 
			
		||||
          <div class="cell has-text-weight-light">{{ $t('host-session-host') }}</div>
 | 
			
		||||
          <div class="cell has-text-weight-medium">{{ creatorName }}</div>
 | 
			
		||||
          <div class="cell has-text-weight-light">{{ $t('players-session-players-length') }}</div>
 | 
			
		||||
          <div class="cell has-text-weight-medium">
 | 
			
		||||
            {{ props.session.numPlayers }} / {{ props.session.options.numPlayers }}
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="cell has-text-weight-light" v-if="info.development">
 | 
			
		||||
            {{ $t('seed-session-seed') }}
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="cell has-text-weight-medium is-family-monospace" v-if="info.development">
 | 
			
		||||
            <span class="is-ellipsis is-display-inline-block" style="max-width: 100px">{{
 | 
			
		||||
              props.session.seed
 | 
			
		||||
            }}</span>
 | 
			
		||||
            <a class="" @click="() => emit('copy', props.session.seed)">
 | 
			
		||||
              {{ $t('copy') }}
 | 
			
		||||
            </a>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="cell has-text-weight-light">{{ $t('status-session-status') }}</div>
 | 
			
		||||
          <div class="cell has-text-weight-medium">{{ props.session.status }}</div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="buttons is-centered mt-6">
 | 
			
		||||
        <button
 | 
			
		||||
          class="button is-primary"
 | 
			
		||||
          @click.once.prevent="() => emit('join', props.session._id)"
 | 
			
		||||
        >
 | 
			
		||||
          <span>{{ $t('join') }}</span>
 | 
			
		||||
        </button>
 | 
			
		||||
        <button
 | 
			
		||||
          v-if="amIHost || info.development"
 | 
			
		||||
          class="button is-text"
 | 
			
		||||
          @click.once.prevent="() => emit('delete', props.session._id)"
 | 
			
		||||
        >
 | 
			
		||||
          <span>{{ $t('delete') }}</span>
 | 
			
		||||
        </button>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import type { MatchSessionDto } from '@/common/interfaces'
 | 
			
		||||
import type { InfoService } from '@/services/InfoService'
 | 
			
		||||
import { useGameStore } from '@/stores/game'
 | 
			
		||||
import { storeToRefs } from 'pinia'
 | 
			
		||||
import { computed, inject } from 'vue'
 | 
			
		||||
const emit = defineEmits(['join', 'delete', 'copy'])
 | 
			
		||||
const gameStore = useGameStore()
 | 
			
		||||
const { amIHost } = storeToRefs(gameStore)
 | 
			
		||||
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
  session: MatchSessionDto
 | 
			
		||||
}>()
 | 
			
		||||
const info: InfoService = inject<InfoService>('info') as InfoService
 | 
			
		||||
const creatorName = computed(() => {
 | 
			
		||||
  return props.session.players.find((player) => player.id === props.session.creator)?.name || ''
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
@@ -102,7 +102,7 @@ if (info.tauri) {
 | 
			
		||||
                >Check for update</a
 | 
			
		||||
              >
 | 
			
		||||
              <hr class="navbar-divider" />
 | 
			
		||||
              <div class="navbar-item">Version 0.2.0</div>
 | 
			
		||||
              <div class="navbar-item">Version 0.2.1</div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								src/detect.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/detect.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
export default {
 | 
			
		||||
  install(app: any) {
 | 
			
		||||
    app.config.globalProperties.$isMobile = () => {
 | 
			
		||||
      const userAgent = navigator.userAgent || navigator.vendor || window.opera
 | 
			
		||||
      return /android|avantgo|blackberry|bada\/|bb|meego|ip(hone|od|ad)|opera m(ob|in)i|phone|tablet|mobi|ipad|playbook|silk|windows (phone|ce)|webos|kindle|mobile|palm|fennec|gobrowser|hiptop|iemobile|iris|w3c|wap|opera mini|iemobile/i.test(
 | 
			
		||||
        userAgent,
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
@@ -16,6 +16,7 @@ import { DIRECTION_INDEXES, DIRECTIONS, ORIENTATION_ANGLES } from '@/common/cons
 | 
			
		||||
import type { OtherHand } from './OtherHand'
 | 
			
		||||
import { SoundManager } from '@/game/utilities/SoundManager'
 | 
			
		||||
import { t } from '@/i18n'
 | 
			
		||||
import { gsap } from 'gsap'
 | 
			
		||||
 | 
			
		||||
export class Board extends EventEmitter {
 | 
			
		||||
  private _scale: number = 1
 | 
			
		||||
@@ -225,7 +226,7 @@ export class Board extends EventEmitter {
 | 
			
		||||
        const availableMoves = this.nextTileValidMoves(tileDto, move.type)
 | 
			
		||||
        const availablePositions = this.nextTileValidPoints(tileDto, move.type, availableMoves)
 | 
			
		||||
 | 
			
		||||
        let directionIndex = DIRECTIONS.indexOf(direction)
 | 
			
		||||
        let directionIndex = DIRECTION_INDEXES[direction]
 | 
			
		||||
        let availablePosition: [number, number] | undefined = availablePositions[directionIndex]
 | 
			
		||||
        let endlessLoop: number = 0
 | 
			
		||||
        while (endlessLoop < 4 && availablePosition === undefined) {
 | 
			
		||||
@@ -234,7 +235,10 @@ export class Board extends EventEmitter {
 | 
			
		||||
          endlessLoop++
 | 
			
		||||
        }
 | 
			
		||||
        if (endlessLoop >= 4) {
 | 
			
		||||
          throw new Error('No available position')
 | 
			
		||||
          console.log('availableMoves :>>', availableMoves)
 | 
			
		||||
          console.log('availablePositions :>>', availablePositions)
 | 
			
		||||
          // throw new Error('No available position')
 | 
			
		||||
          directionIndex = DIRECTION_INDEXES[direction]
 | 
			
		||||
        }
 | 
			
		||||
        direction = DIRECTIONS[directionIndex]
 | 
			
		||||
        isLeft ? (this.leftDirection = direction) : (this.rightDirection = direction)
 | 
			
		||||
@@ -260,23 +264,37 @@ export class Board extends EventEmitter {
 | 
			
		||||
  async animateTile(tile: Tile, x: number, y: number, orientation: string, move: Movement) {
 | 
			
		||||
    const targetX = this.scaleX(x)
 | 
			
		||||
    const targetY = this.scaleY(y)
 | 
			
		||||
    const animation: AnimationOptions = {
 | 
			
		||||
      x: targetX,
 | 
			
		||||
      y: targetY,
 | 
			
		||||
      rotation: ORIENTATION_ANGLES[orientation],
 | 
			
		||||
      duration: 20,
 | 
			
		||||
    }
 | 
			
		||||
    const tempAlpha = tile.alpha
 | 
			
		||||
    tile.alpha = 0
 | 
			
		||||
    const clonedTile = tile.clone()
 | 
			
		||||
    clonedTile.addTo(this.tilesContainer)
 | 
			
		||||
    const pos = this.getAnimationInitialPosition(move)
 | 
			
		||||
    clonedTile.setPosition(this.scaleX(pos.x), this.scaleY(pos.y))
 | 
			
		||||
    await clonedTile.animateTo(animation)
 | 
			
		||||
    clonedTile.removeFromParent()
 | 
			
		||||
    tile.setOrientation(orientation)
 | 
			
		||||
    tile.setPosition(targetX, targetY)
 | 
			
		||||
    tile.alpha = tempAlpha
 | 
			
		||||
 | 
			
		||||
    return new Promise((resolve) => {
 | 
			
		||||
      gsap.timeline({ repeat: 0 }).fromTo(
 | 
			
		||||
        clonedTile.getSprite(),
 | 
			
		||||
        {
 | 
			
		||||
          x: this.scaleX(pos.x),
 | 
			
		||||
          y: this.scaleY(pos.y),
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          x: targetX,
 | 
			
		||||
          y: targetY,
 | 
			
		||||
          duration: 1,
 | 
			
		||||
          rotation: ORIENTATION_ANGLES[orientation],
 | 
			
		||||
          // rotate: ,
 | 
			
		||||
          onComplete: () => {
 | 
			
		||||
            clonedTile.removeFromParent()
 | 
			
		||||
            tile.setOrientation(orientation)
 | 
			
		||||
            tile.setPosition(targetX, targetY)
 | 
			
		||||
            tile.alpha = tempAlpha
 | 
			
		||||
            resolve('done')
 | 
			
		||||
          },
 | 
			
		||||
          ease: 'power4.out',
 | 
			
		||||
        },
 | 
			
		||||
      )
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getAnimationInitialPosition(move: Movement): { x: number; y: number } {
 | 
			
		||||
@@ -345,6 +363,7 @@ export class Board extends EventEmitter {
 | 
			
		||||
    try {
 | 
			
		||||
      // const { tileDto: tileDto } = move
 | 
			
		||||
      // const tile = this.getTileInHand(tileDto?.id ?? '')
 | 
			
		||||
      this.interactionContainer.removeChildren()
 | 
			
		||||
      this.movements.push(move)
 | 
			
		||||
      if (tile === undefined) {
 | 
			
		||||
        return
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import type { Dimension } from '@/common/interfaces'
 | 
			
		||||
import { BlurFilter, Container, Graphics, Point, Text, TextStyle } from 'pixi.js'
 | 
			
		||||
import { BlurFilter, Container, Graphics, Point, Text } from 'pixi.js'
 | 
			
		||||
import { gsap } from 'gsap'
 | 
			
		||||
import { SoundManager } from '@/game/utilities/SoundManager'
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,8 @@ import Config from './Config'
 | 
			
		||||
import { createText, grayStyle } from './utilities/fonts'
 | 
			
		||||
import { t } from '@/i18n'
 | 
			
		||||
import { DIRECTION_INDEXES, DIRECTIONS } from '@/common/constants'
 | 
			
		||||
import { SoundManager } from './utilities/SoundManager'
 | 
			
		||||
 | 
			
		||||
export class Game extends EventEmitter {
 | 
			
		||||
  public board!: Board
 | 
			
		||||
  public hand!: Hand
 | 
			
		||||
@@ -30,6 +32,7 @@ export class Game extends EventEmitter {
 | 
			
		||||
  private backgroundLayer: Container = new Container()
 | 
			
		||||
  private gameSummaryView!: GameSummayView
 | 
			
		||||
  private players: PlayerDto[] = []
 | 
			
		||||
  private soundManager: SoundManager = new SoundManager()
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private options: MatchSessionOptions,
 | 
			
		||||
@@ -197,7 +200,6 @@ export class Game extends EventEmitter {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    const freeEnds = this.board.freeEnds
 | 
			
		||||
    console.log('freeEnds :>> ', freeEnds, this.playerId)
 | 
			
		||||
    const move: Movement = {
 | 
			
		||||
      id: '',
 | 
			
		||||
      tile,
 | 
			
		||||
@@ -215,8 +217,6 @@ export class Game extends EventEmitter {
 | 
			
		||||
        dirIndex += 1
 | 
			
		||||
        safeCount -= 1
 | 
			
		||||
      }
 | 
			
		||||
      console.log('validMoves :>> ', validMoves)
 | 
			
		||||
      console.log('validPoints :>> ', validPoints)
 | 
			
		||||
      const validPoint = validPoints[dirIndex % 4]
 | 
			
		||||
      if (validPoint !== undefined) {
 | 
			
		||||
        move.x = validPoint[0]
 | 
			
		||||
@@ -227,6 +227,7 @@ export class Game extends EventEmitter {
 | 
			
		||||
    }
 | 
			
		||||
    this.currentMove = move
 | 
			
		||||
    this.board.updateBoard(move, this.hand.tileMoved(tile))
 | 
			
		||||
    this.hand.afterMove()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async sendPassEvent() {
 | 
			
		||||
@@ -236,7 +237,7 @@ export class Game extends EventEmitter {
 | 
			
		||||
      playerId: this.playerId,
 | 
			
		||||
    }
 | 
			
		||||
    this.socketService &&
 | 
			
		||||
      this.socketService.sendMessage('client:player-move', {
 | 
			
		||||
      this.socketService.send('client:player-move', {
 | 
			
		||||
        sessionId: this.sessionId,
 | 
			
		||||
        move: move,
 | 
			
		||||
      })
 | 
			
		||||
@@ -287,6 +288,7 @@ export class Game extends EventEmitter {
 | 
			
		||||
      this.hand.setActive(true)
 | 
			
		||||
      this.hand.prepareForMove(this.board.count === 0, this.board.freeEnds)
 | 
			
		||||
      this.board.setPlayerTurn(currentPlayer)
 | 
			
		||||
      this.soundManager.play('snd-ding-1')
 | 
			
		||||
    } else {
 | 
			
		||||
      this.board.setServerPlayerTurn(currentPlayer)
 | 
			
		||||
      this.highLightPlayer(currentPlayer)
 | 
			
		||||
@@ -323,17 +325,20 @@ export class Game extends EventEmitter {
 | 
			
		||||
    this.board.on('game:tile-animation-ended', async (tile) => {
 | 
			
		||||
      if (tile !== null && tile !== undefined && tile.playerId === this.playerId) {
 | 
			
		||||
        this.socketService &&
 | 
			
		||||
          this.socketService.sendMessage('client:player-move', {
 | 
			
		||||
          this.socketService.send('client:player-move', {
 | 
			
		||||
            sessionId: this.sessionId,
 | 
			
		||||
            move: this.currentMove,
 | 
			
		||||
          })
 | 
			
		||||
      } else {
 | 
			
		||||
        this.socketService &&
 | 
			
		||||
          this.socketService.sendMessage('client:animation-ended', {
 | 
			
		||||
            sessionId: this.sessionId,
 | 
			
		||||
            userId: this.playerId,
 | 
			
		||||
          })
 | 
			
		||||
        // this.socketService.sendMessage('client:player-move', {
 | 
			
		||||
        //   sessionId: this.sessionId,
 | 
			
		||||
        //   move: this.currentMove,
 | 
			
		||||
        // })
 | 
			
		||||
      }
 | 
			
		||||
      this.socketService &&
 | 
			
		||||
        this.socketService.sendMessage('client:animation-ended', {
 | 
			
		||||
          sessionId: this.sessionId,
 | 
			
		||||
          userId: this.playerId,
 | 
			
		||||
        })
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ import { createText, playerNameText, whiteStyle } from './utilities/fonts'
 | 
			
		||||
import Config from '@/game/Config'
 | 
			
		||||
import { TimerText } from './TimerText'
 | 
			
		||||
import { Button } from './Button'
 | 
			
		||||
import gsap from 'gsap'
 | 
			
		||||
 | 
			
		||||
export class Hand extends EventEmitter {
 | 
			
		||||
  tiles: Tile[] = []
 | 
			
		||||
@@ -112,10 +113,16 @@ export class Hand extends EventEmitter {
 | 
			
		||||
      this.buttonPass.disabled = false
 | 
			
		||||
    }
 | 
			
		||||
    this.availableTiles.forEach((tile) => {
 | 
			
		||||
      tile.animateTo({
 | 
			
		||||
        x: tile.x,
 | 
			
		||||
      gsap.to(tile.getSprite(), {
 | 
			
		||||
        duration: 0.3,
 | 
			
		||||
        y: tile.y - 10,
 | 
			
		||||
        alpha: 1,
 | 
			
		||||
        ease: 'power4.out',
 | 
			
		||||
      })
 | 
			
		||||
      // tile.animateTo({
 | 
			
		||||
      //   x: tile.x,
 | 
			
		||||
      //   y: tile.y - 10,
 | 
			
		||||
      // })
 | 
			
		||||
      tile.interactive = true
 | 
			
		||||
    })
 | 
			
		||||
    if (this.timer) {
 | 
			
		||||
@@ -146,15 +153,21 @@ export class Hand extends EventEmitter {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  afterMove() {
 | 
			
		||||
    this.availableTiles.forEach((tile) => {
 | 
			
		||||
      tile.animateTo({
 | 
			
		||||
        x: tile.x,
 | 
			
		||||
        y: tile.y + 10,
 | 
			
		||||
      })
 | 
			
		||||
      tile.setPosition(tile.x, tile.y + 10)
 | 
			
		||||
      tile.interactive = false
 | 
			
		||||
    })
 | 
			
		||||
    this.timer && this.timer.reset()
 | 
			
		||||
    this.availableTiles.forEach((tile) => {
 | 
			
		||||
      if (tile.selected) return
 | 
			
		||||
      gsap.to(tile.getSprite(), {
 | 
			
		||||
        duration: 0.3,
 | 
			
		||||
        y: tile.y + 10,
 | 
			
		||||
        alpha: 0.7,
 | 
			
		||||
        ease: 'power4.out',
 | 
			
		||||
      })
 | 
			
		||||
      // tile.animateTo({
 | 
			
		||||
      //   x: tile.x,
 | 
			
		||||
      //   y: tile.y + 10,
 | 
			
		||||
      // })
 | 
			
		||||
      // tile.setPosition(tile.x, tile.y + 10)
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  hasMoves(tile: TileDto, freeEnds?: [number, number]): boolean {
 | 
			
		||||
@@ -215,7 +228,12 @@ export class Hand extends EventEmitter {
 | 
			
		||||
    if (!tile) return
 | 
			
		||||
 | 
			
		||||
    this.afterMove()
 | 
			
		||||
    this.tiles = this.tiles.filter((t) => t.id !== tileDto.id)
 | 
			
		||||
    this.tiles = this.tiles.filter((t) => t.id !== tile.id)
 | 
			
		||||
    this.tiles.forEach((t) => {
 | 
			
		||||
      t.interactive = false
 | 
			
		||||
      t.alpha = 0.7
 | 
			
		||||
      t.clearFilters()
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    tile.interactive = false
 | 
			
		||||
    tile.clearFilters()
 | 
			
		||||
@@ -290,9 +308,9 @@ export class Hand extends EventEmitter {
 | 
			
		||||
        newTile.setFilters([
 | 
			
		||||
          new GlowFilter({
 | 
			
		||||
            distance: 10,
 | 
			
		||||
            outerStrength: 2,
 | 
			
		||||
            outerStrength: 1,
 | 
			
		||||
            innerStrength: 1,
 | 
			
		||||
            color: 0xffffff,
 | 
			
		||||
            color: 0xffff66,
 | 
			
		||||
            quality: 0.5,
 | 
			
		||||
          }),
 | 
			
		||||
        ])
 | 
			
		||||
@@ -300,7 +318,7 @@ export class Hand extends EventEmitter {
 | 
			
		||||
      newTile.on('pointerout', () => {
 | 
			
		||||
        if (!newTile.selected) {
 | 
			
		||||
          this.emit('tileHover')
 | 
			
		||||
          newTile.alpha = 0.7
 | 
			
		||||
          newTile.alpha = 1
 | 
			
		||||
          newTile.getSprite().filters = []
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
@@ -319,18 +337,18 @@ export class Hand extends EventEmitter {
 | 
			
		||||
    this.scoreLayer.removeChildren()
 | 
			
		||||
    const name = createText({
 | 
			
		||||
      text: this.player?.name ?? '-',
 | 
			
		||||
      x: 100,
 | 
			
		||||
      y: 50,
 | 
			
		||||
      x: 65,
 | 
			
		||||
      y: 25,
 | 
			
		||||
      style: playerNameText,
 | 
			
		||||
    })
 | 
			
		||||
    const text = createText({
 | 
			
		||||
      text: `${this.score}`,
 | 
			
		||||
      x: 100,
 | 
			
		||||
      x: 65,
 | 
			
		||||
      // x: this.width - 5,
 | 
			
		||||
      y: 80,
 | 
			
		||||
      style: whiteStyle(36, 'bold'),
 | 
			
		||||
      y: 75,
 | 
			
		||||
      style: whiteStyle(60, 'bold'),
 | 
			
		||||
    })
 | 
			
		||||
    text.anchor.set(1, 0.5)
 | 
			
		||||
    text.anchor.set(0.5, 0.5)
 | 
			
		||||
    this.scoreLayer.addChild(name)
 | 
			
		||||
    this.scoreLayer.addChild(text)
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ export abstract class SpriteBase {
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    protected ticker?: Ticker,
 | 
			
		||||
    protected scale: number = 1
 | 
			
		||||
    protected scale: number = 1,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.ticker = ticker
 | 
			
		||||
    this.scale = scale
 | 
			
		||||
@@ -84,58 +84,6 @@ export abstract class SpriteBase {
 | 
			
		||||
    this.sprite.filters = []
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  animateTo(options: AnimationOptions): Promise<void> {
 | 
			
		||||
    return new Promise((resolve) => {
 | 
			
		||||
      const {
 | 
			
		||||
        x: targetX,
 | 
			
		||||
        y: targetY,
 | 
			
		||||
        rotation: targetRotation,
 | 
			
		||||
        duration = 10,
 | 
			
		||||
        width: targetWidth,
 | 
			
		||||
        height: targetHeight
 | 
			
		||||
      } = options
 | 
			
		||||
 | 
			
		||||
      const initialX = this.sprite.x
 | 
			
		||||
      const initialY = this.sprite.y
 | 
			
		||||
      const initialRotation = this.sprite.rotation
 | 
			
		||||
      const initialWidth = this.sprite.width
 | 
			
		||||
      const initialHeight = this.sprite.height
 | 
			
		||||
 | 
			
		||||
      const deltaX = targetX ? targetX - this.sprite.x : null
 | 
			
		||||
      const deltaY = targetY ? targetY - this.sprite.y : null
 | 
			
		||||
      const deltaRotation = targetRotation ? targetRotation - this.sprite.rotation : null
 | 
			
		||||
      const deltaWidth = targetWidth ? targetWidth - this.sprite.width : null
 | 
			
		||||
      const deltaHeight = targetHeight ? targetHeight - this.sprite.height : null
 | 
			
		||||
 | 
			
		||||
      let elapsed: number = 0
 | 
			
		||||
 | 
			
		||||
      const tick: any = (delta: any) => {
 | 
			
		||||
        elapsed += delta.deltaTime
 | 
			
		||||
        const progress = Math.min(elapsed / duration, 1)
 | 
			
		||||
 | 
			
		||||
        // Linear interpolation
 | 
			
		||||
        if (deltaX !== null) this.sprite.x = initialX + deltaX * progress
 | 
			
		||||
        if (deltaY !== null) this.sprite.y = initialY + deltaY * progress
 | 
			
		||||
 | 
			
		||||
        // Rotation interpolation
 | 
			
		||||
        if (deltaRotation !== null)
 | 
			
		||||
          this.sprite.rotation = initialRotation + deltaRotation * progress
 | 
			
		||||
 | 
			
		||||
        // Scale interpolation
 | 
			
		||||
        // this.sprite.width = initialWidth + deltaWidth * progress
 | 
			
		||||
        // this.sprite.height = initialHeight + deltaHeight * progress
 | 
			
		||||
 | 
			
		||||
        //
 | 
			
		||||
        if (progress === 1) {
 | 
			
		||||
          this.ticker?.remove(tick)
 | 
			
		||||
          resolve()
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this.ticker?.add(tick)
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addTo(container: any) {
 | 
			
		||||
    this.container = container
 | 
			
		||||
    container.addChild(this.sprite)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,9 +2,10 @@ import { Container, Point, Text, TextStyle } from 'pixi.js'
 | 
			
		||||
import { createText, timerStyle } from './utilities/fonts'
 | 
			
		||||
 | 
			
		||||
import { gsap } from 'gsap'
 | 
			
		||||
import { Timer } from './utilities/Timer'
 | 
			
		||||
 | 
			
		||||
export class TimerText extends Container {
 | 
			
		||||
  private intervalHandle?: any
 | 
			
		||||
  private timer: Timer
 | 
			
		||||
  private text?: Text
 | 
			
		||||
  animation: boolean = false
 | 
			
		||||
  textStyle: TextStyle = timerStyle()
 | 
			
		||||
@@ -16,41 +17,35 @@ export class TimerText extends Container {
 | 
			
		||||
    private point: Point,
 | 
			
		||||
  ) {
 | 
			
		||||
    super()
 | 
			
		||||
    this.timer = new Timer(seconds)
 | 
			
		||||
    this.timer.on('timeout', this.onTImeOut.bind(this))
 | 
			
		||||
    this.timer.on('tick', this.onTick.bind(this))
 | 
			
		||||
    this.countdown = seconds
 | 
			
		||||
    this.render()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private onTImeOut() {
 | 
			
		||||
    this.timer.reset()
 | 
			
		||||
    this.emit('timeout')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private onTick(seconds: number) {
 | 
			
		||||
    !this.animation && this.text?.destroy()
 | 
			
		||||
    this.countdown = seconds
 | 
			
		||||
    this.render()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  reset() {
 | 
			
		||||
    this.stop()
 | 
			
		||||
    this.timer.stop()
 | 
			
		||||
    this.timer.reset()
 | 
			
		||||
    this.countdown = this.seconds
 | 
			
		||||
    this.isInitiated = false
 | 
			
		||||
    this.render()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  start() {
 | 
			
		||||
    clearInterval(this.intervalHandle)
 | 
			
		||||
    this.isInitiated = true
 | 
			
		||||
    this.removeChildren()
 | 
			
		||||
    this.render()
 | 
			
		||||
    this.intervalHandle = setInterval(() => {
 | 
			
		||||
      this.countdown--
 | 
			
		||||
      !this.animation && this.text?.destroy()
 | 
			
		||||
      this.render()
 | 
			
		||||
      if (this.countdown === 0) {
 | 
			
		||||
        clearInterval(this.intervalHandle)
 | 
			
		||||
        this.emit('timeout')
 | 
			
		||||
      }
 | 
			
		||||
    }, 1000)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  destroy() {
 | 
			
		||||
    this.stop()
 | 
			
		||||
    this.destroy()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  stop() {
 | 
			
		||||
    clearInterval(this.intervalHandle)
 | 
			
		||||
    this.emit('stop', this.countdown)
 | 
			
		||||
    this.timer.reset()
 | 
			
		||||
    this.timer.start()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private render() {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ export class Timer extends EventEmitter {
 | 
			
		||||
      this.countdown--
 | 
			
		||||
      if (this.countdown === 0) {
 | 
			
		||||
        clearInterval(this.intervalHandle)
 | 
			
		||||
        console.log('tic')
 | 
			
		||||
        this.emit('timeout')
 | 
			
		||||
      } else {
 | 
			
		||||
        this.emit('tick', this.countdown)
 | 
			
		||||
 
 | 
			
		||||
@@ -14,11 +14,11 @@
 | 
			
		||||
  "scoreboard": "Scoreboard",
 | 
			
		||||
  "available-sessions": "Available Sessions",
 | 
			
		||||
  "no-sessions-available": "No sessions available",
 | 
			
		||||
  "id-session-_id": "ID: {0}",
 | 
			
		||||
  "players-session-players-length": "Players: {0}",
 | 
			
		||||
  "id-session-_id": "ID:",
 | 
			
		||||
  "players-session-players-length": "Players:",
 | 
			
		||||
  "copy": "Copy",
 | 
			
		||||
  "seed-session-seed": "Seed: {0}",
 | 
			
		||||
  "status-session-status": "Status: {0}",
 | 
			
		||||
  "seed-session-seed": "Seed:",
 | 
			
		||||
  "status-session-status": "Status:",
 | 
			
		||||
  "delete": "Delete",
 | 
			
		||||
  "join": "Join",
 | 
			
		||||
  "welcome-to-the-user-username-s-home-page": "Welcome to the {0}'s Home Page",
 | 
			
		||||
@@ -35,7 +35,7 @@
 | 
			
		||||
  "crossed-game-teamed": "Crossed game",
 | 
			
		||||
  "create-match-session": "Create Match Session",
 | 
			
		||||
  "ready": "Ready",
 | 
			
		||||
  "unready": "Unready",
 | 
			
		||||
  "unready": "Not ready",
 | 
			
		||||
  "start": "Start",
 | 
			
		||||
  "cancel": "Cancel",
 | 
			
		||||
  "game": {
 | 
			
		||||
@@ -59,16 +59,18 @@
 | 
			
		||||
  "n-rounds": "One Round|{count} Rounds",
 | 
			
		||||
  "n-of-m-rounds": "{0} of {1} Rounds",
 | 
			
		||||
  "create-session": "Create Session",
 | 
			
		||||
  "join-a-multiplayer-session": "Join a Multiplayer Session (No sessions)|Join a Multiplayer Session ({count})|Join a Multiplayer Session ({count})",
 | 
			
		||||
  "join-a-multiplayer-session": "Multiplayer (0)|Multiplayer ({count})|Multiplayer ({count})",
 | 
			
		||||
  "tournaments": "Tournaments",
 | 
			
		||||
  "start-game": "Start Game",
 | 
			
		||||
  "player": "Player",
 | 
			
		||||
  "final-score": "Final Score",
 | 
			
		||||
  "round-index": "Round #{0}",
 | 
			
		||||
  "first-actor-to-win-this-options-wintarget-this-options-wintype": "First {0} to win {1}",
 | 
			
		||||
  "first-actor-to-win-this-options-wintarget-this-options-wintype": "First {0} to reach {1}",
 | 
			
		||||
  "team": "team",
 | 
			
		||||
  "winner-name": "Winner: {0}",
 | 
			
		||||
  "blocked": "Blocked",
 | 
			
		||||
  "round-summary": "Round Summary",
 | 
			
		||||
  "match-finished": "Match Finished"
 | 
			
		||||
  "match-finished": "Match Finished",
 | 
			
		||||
  "host-session-host": "Host:",
 | 
			
		||||
  "players": "Players"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,10 +12,10 @@
 | 
			
		||||
  "scoreboard": "Marcador",
 | 
			
		||||
  "winner": "Ganador",
 | 
			
		||||
  "final-scoreboard": "Puntaje Final",
 | 
			
		||||
  "id-session-_id": "ID: {0}",
 | 
			
		||||
  "id-session-_id": "ID:",
 | 
			
		||||
  "seed-placeholder": "¡Escriba la semilla de la sesión aquí!",
 | 
			
		||||
  "session-name-placeholder": "Nombre de la sesión",
 | 
			
		||||
  "status-session-status": "Estado: {0}",
 | 
			
		||||
  "status-session-status": "Estado:",
 | 
			
		||||
  "unready": "No preparado",
 | 
			
		||||
  "welcome-to-the-user-username-s-home-page": "Bienvenido a la página de inicio de {0}",
 | 
			
		||||
  "available-sessions": "Sesiones disponibles",
 | 
			
		||||
@@ -38,11 +38,11 @@
 | 
			
		||||
  "join": "Unirse",
 | 
			
		||||
  "name": "Nombre",
 | 
			
		||||
  "no-sessions-available": "No hay sesiones disponibles",
 | 
			
		||||
  "players-session-players-length": "Jugadores: {0}",
 | 
			
		||||
  "players-session-players-length": "Jugadores:",
 | 
			
		||||
  "ready": "Listo",
 | 
			
		||||
  "red-fabric": "Tela roja",
 | 
			
		||||
  "seed": "Semilla",
 | 
			
		||||
  "seed-session-seed": "Semilla: {0}",
 | 
			
		||||
  "seed-session-seed": "Semilla:",
 | 
			
		||||
  "start": "Comenzar",
 | 
			
		||||
  "yellow-fabric": "Tela amarilla",
 | 
			
		||||
  "back": "Volver",
 | 
			
		||||
@@ -70,5 +70,7 @@
 | 
			
		||||
  "blocked": "Cerrado",
 | 
			
		||||
  "round-summary": "Resumen de la ronda",
 | 
			
		||||
  "match-finished": "Partida terminado",
 | 
			
		||||
  "team": "equipo"
 | 
			
		||||
  "team": "equipo",
 | 
			
		||||
  "host-session-host": "Creada por:",
 | 
			
		||||
  "players": "Jugadores"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -105,4 +105,16 @@ router.beforeEach((to, from, next) => {
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
router.beforeResolve((to, from, next) => {
 | 
			
		||||
  const app = document.querySelector('#app')
 | 
			
		||||
  if (app) {
 | 
			
		||||
    if (from.name !== undefined) {
 | 
			
		||||
      app.classList.replace(<string>from.name, <string>to.name)
 | 
			
		||||
    } else {
 | 
			
		||||
      app.classList.add(<string>to.name)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  next()
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export default router
 | 
			
		||||
 
 | 
			
		||||
@@ -50,9 +50,10 @@ export class SocketIoClientService extends ServiceBase {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addEvents(): void {
 | 
			
		||||
    this.socket.on('disconnect', () => {
 | 
			
		||||
    this.socket.on('disconnect', (reason) => {
 | 
			
		||||
      this.isConnected = false
 | 
			
		||||
      console.log('SOCKET: Disconnected from server')
 | 
			
		||||
      console.log(`Reason: ${reason}`)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.socket.on('reconnect', () => {
 | 
			
		||||
@@ -87,6 +88,15 @@ export class SocketIoClientService extends ServiceBase {
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  send(event: string, data: any): void {
 | 
			
		||||
    if (this.isConnected) {
 | 
			
		||||
      this.socket?.emit(event, data)
 | 
			
		||||
      this.logger.trace(`SOCKET: sendEvent :>> ${event}`, data)
 | 
			
		||||
    } else {
 | 
			
		||||
      this.logger.trace('Not connected to server')
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  sendMessage(event: string, data: any): void {
 | 
			
		||||
    if (this.isConnected) {
 | 
			
		||||
      this.socket?.emit('client:event', { event, data })
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,6 @@ if (!playerState?.value) {
 | 
			
		||||
function makeMove(move: any) {
 | 
			
		||||
  moveToMake.value = move
 | 
			
		||||
  canMakeMove.value = false
 | 
			
		||||
  console.log('makemove :>> ', move)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
onBeforeUnmount(() => {
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,8 @@ import MatchConfiguration from '@/components/MatchConfiguration.vue'
 | 
			
		||||
import { useI18n } from 'vue-i18n'
 | 
			
		||||
import { SessionExpiredError } from '@/common/errors/SessionExpiredError'
 | 
			
		||||
import type { AuthenticationService } from '@/services/AuthenticationService'
 | 
			
		||||
import SessionBoxComponent from '@/components/SessionBoxComponent.vue'
 | 
			
		||||
import MultiplayerSetupComponent from '@/components/MultiplayerSetupComponent.vue'
 | 
			
		||||
 | 
			
		||||
let teamedWith = ref<string | undefined>(undefined)
 | 
			
		||||
 | 
			
		||||
@@ -31,8 +33,7 @@ const authService: AuthenticationService = inject<AuthenticationService>(
 | 
			
		||||
  'auth',
 | 
			
		||||
) as AuthenticationService
 | 
			
		||||
 | 
			
		||||
const { sessionState, isSessionStarted, playerState, amIHost, readyForStart } =
 | 
			
		||||
  storeToRefs(gameStore)
 | 
			
		||||
const { sessionState, isSessionStarted, playerState, amIHost } = storeToRefs(gameStore)
 | 
			
		||||
const { gameOptions } = storeToRefs(gameOptionsStore)
 | 
			
		||||
const { updateSessionState, updatePlayerState, updateGameState } = gameStore
 | 
			
		||||
const { t } = useI18n()
 | 
			
		||||
@@ -43,17 +44,13 @@ eventBus.subscribe('window-before-unload', () => {
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
async function createMatch(options: MatchSessionOptions) {
 | 
			
		||||
  logger.debug('Creating match')
 | 
			
		||||
  await socketService.connect()
 | 
			
		||||
  gameOptions.value = options
 | 
			
		||||
  const sessionId = await gameService.createMatchSession(options)
 | 
			
		||||
  socketService.joinRoom(`room-${sessionId}`)
 | 
			
		||||
  logger.debug('Match created successfully')
 | 
			
		||||
  // router.push({ name: 'match', params: { id } })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function setPlayerReady() {
 | 
			
		||||
  logger.debug('Starting game')
 | 
			
		||||
  if (!sessionState.value) {
 | 
			
		||||
    logger.error('No session found')
 | 
			
		||||
    return
 | 
			
		||||
@@ -68,14 +65,15 @@ async function setPlayerReady() {
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function startMatch() {
 | 
			
		||||
async function startMatch(teamedWith?: string) {
 | 
			
		||||
  console.log('teamedWith :>> ', teamedWith)
 | 
			
		||||
  const sessionId = sessionState?.value?.id
 | 
			
		||||
  const playerId = playerState?.value?.id
 | 
			
		||||
  if (sessionId) {
 | 
			
		||||
    await socketService.sendMessageWithAck('client:start-session', {
 | 
			
		||||
      sessionId: sessionId,
 | 
			
		||||
      playerId: playerId,
 | 
			
		||||
      teamedWith: teamedWith.value,
 | 
			
		||||
      teamedWith: teamedWith,
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -117,20 +115,6 @@ async function deleteMatch(id: string) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const playersToTeamUpWith = computed(() => {
 | 
			
		||||
  return (
 | 
			
		||||
    sessionState?.value?.players.filter((player) => player.id !== sessionState?.value?.creator) ||
 | 
			
		||||
    []
 | 
			
		||||
  )
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const canStart = computed(() => {
 | 
			
		||||
  const players = sessionState?.value?.players || []
 | 
			
		||||
  const options = gameOptions.value
 | 
			
		||||
  const allReady = (players.length || 0) > 0 && players.every((player) => player.ready)
 | 
			
		||||
  return (!options?.teamed && allReady) || (options?.teamed && !!teamedWith.value && allReady)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const isMultiplayer = computed(
 | 
			
		||||
  () => (sessionState?.value?.options?.numPlayers || gameOptions.value?.numPlayers || 0) > 1,
 | 
			
		||||
)
 | 
			
		||||
@@ -167,18 +151,18 @@ function copy(sessionSeed: string) {
 | 
			
		||||
  copyToclipboard(sessionSeed)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let selectedTab = ref('create-tab')
 | 
			
		||||
let selectedTab = ref('join-tab')
 | 
			
		||||
const isCreateTab = computed(() => selectedTab.value === 'create-tab')
 | 
			
		||||
const isJoinTab = computed(() => selectedTab.value === 'join-tab')
 | 
			
		||||
const isTournamentTab = computed(() => selectedTab.value === 'torunaments-tab')
 | 
			
		||||
 | 
			
		||||
const tabs = computed<any[]>((): any => [
 | 
			
		||||
  { label: t('create-session'), id: 'create-tab', disabled: false },
 | 
			
		||||
  {
 | 
			
		||||
    label: t('join-a-multiplayer-session', matchSessions.value.length),
 | 
			
		||||
    label: t('join-a-multiplayer-session', matchSessions.value?.length ?? 0),
 | 
			
		||||
    id: 'join-tab',
 | 
			
		||||
    disabled: matchSessions.value.length <= 0,
 | 
			
		||||
    disabled: false, //matchSessions.value.length <= 0,
 | 
			
		||||
  },
 | 
			
		||||
  { label: t('create-session'), id: 'create-tab', disabled: false },
 | 
			
		||||
  { label: t('tournaments'), id: 'torunaments-tab', disabled: true },
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
@@ -229,38 +213,28 @@ async function onStartSingleMatch(options: MatchSessionOptions) {
 | 
			
		||||
        <section class="section available-sessions" v-if="isJoinTab">
 | 
			
		||||
          <div class="block">
 | 
			
		||||
            <div v-if="matchSessions.length === 0">
 | 
			
		||||
              <p>{{ $t('no-sessions-available') }}</p>
 | 
			
		||||
              <p class="has-text-centered mt-6 pt-6">{{ $t('no-sessions-available') }}</p>
 | 
			
		||||
              <div class="buttons is-justify-content-center mt-6">
 | 
			
		||||
                <button class="button is-primary is-small" @click="() => tabClick(tabs[1])">
 | 
			
		||||
                  {{ $t('create-session') }}
 | 
			
		||||
                </button>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div v-else class="fixed-grid has-3-cols">
 | 
			
		||||
              <div class="grid">
 | 
			
		||||
                <div class="cell" v-for="session in matchSessions" :key="session.id">
 | 
			
		||||
                  <div class="card">
 | 
			
		||||
                    <div class="card-content">
 | 
			
		||||
                      <p class="title is-6">{{ session.name }}</p>
 | 
			
		||||
                      <p>{{ $t('id-session-_id', [session._id]) }}</p>
 | 
			
		||||
                      <p>{{ $t('players-session-players-length', [session.players.length]) }}</p>
 | 
			
		||||
                      <p>
 | 
			
		||||
                        {{ $t('seed-session-seed', [session.seed]) }}
 | 
			
		||||
                        <button class="button is-small is-ghost" @click="() => copy(session.seed)">
 | 
			
		||||
                          {{ $t('copy') }}
 | 
			
		||||
                        </button>
 | 
			
		||||
                      </p>
 | 
			
		||||
                      <p>{{ $t('status-session-status', [session.status]) }}</p>
 | 
			
		||||
                      <div class="buttons is-centered mt-6">
 | 
			
		||||
                        <button
 | 
			
		||||
                          class="button is-primary"
 | 
			
		||||
                          @click.once.prevent="() => joinMatch(session._id)"
 | 
			
		||||
                        >
 | 
			
		||||
                          <span>{{ $t('join') }}</span>
 | 
			
		||||
                        </button>
 | 
			
		||||
                        <button
 | 
			
		||||
                          class="button is-text"
 | 
			
		||||
                          @click.once.prevent="() => deleteMatch(session._id)"
 | 
			
		||||
                        >
 | 
			
		||||
                          <span>{{ $t('delete') }}</span>
 | 
			
		||||
                        </button>
 | 
			
		||||
                      </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
            <div v-else>
 | 
			
		||||
              <div class="buttons is-justify-content-center mt-6">
 | 
			
		||||
                <button class="button is-primary is-small" @click="() => tabClick(tabs[1])">
 | 
			
		||||
                  {{ $t('create-session') }}
 | 
			
		||||
                </button>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="fixed-grid has-3-cols">
 | 
			
		||||
                <div class="grid">
 | 
			
		||||
                  <div class="cell" v-for="session in matchSessions" :key="session.id">
 | 
			
		||||
                    <SessionBoxComponent
 | 
			
		||||
                      :session="session"
 | 
			
		||||
                      @join="joinMatch"
 | 
			
		||||
                      @delete="deleteMatch"
 | 
			
		||||
                      @copy="copy"
 | 
			
		||||
                    />
 | 
			
		||||
                  </div>
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
@@ -272,45 +246,12 @@ async function onStartSingleMatch(options: MatchSessionOptions) {
 | 
			
		||||
        <section class="section" v-if="isTournamentTab"></section>
 | 
			
		||||
        <!-- Tournaments End -->
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="block" v-if="isSessionStarted && isMultiplayer">
 | 
			
		||||
        <h2 class="title is-4">{{ sessionState?.name }}</h2>
 | 
			
		||||
        <h6 class="title is-size-5">Players</h6>
 | 
			
		||||
        <div v-for="player in sessionState?.players" :key="player.id">
 | 
			
		||||
          <p>{{ player.name }} ({{ player.ready ? 'Ready' : 'Not ready' }})</p>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="mt-4" v-if="amIHost && gameOptions?.teamed && playersToTeamUpWith.length > 0">
 | 
			
		||||
          <div class="field">
 | 
			
		||||
            <label for="background" class="label">Team up with</label>
 | 
			
		||||
            <div class="control">
 | 
			
		||||
              <div class="select">
 | 
			
		||||
                <select v-model="teamedWith" name="teamedWidth">
 | 
			
		||||
                  <option v-for="player in playersToTeamUpWith" :key="player.id" :value="player.id">
 | 
			
		||||
                    {{ player.name }}
 | 
			
		||||
                  </option>
 | 
			
		||||
                </select>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="buttons mt-6">
 | 
			
		||||
          <button class="button is-dark" @click="setPlayerReady">
 | 
			
		||||
            <span v-if="!readyForStart">{{ $t('ready') }}</span
 | 
			
		||||
            ><span v-else>{{ $t('unready') }}</span>
 | 
			
		||||
          </button>
 | 
			
		||||
          <button
 | 
			
		||||
            class="button is-success"
 | 
			
		||||
            :disabled="!canStart"
 | 
			
		||||
            @click="startMatch"
 | 
			
		||||
            v-if="amIHost"
 | 
			
		||||
          >
 | 
			
		||||
            <span>{{ $t('start') }}</span>
 | 
			
		||||
          </button>
 | 
			
		||||
 | 
			
		||||
          <button class="button is-danger" @click="cancelMatch">
 | 
			
		||||
            <span>{{ $t('cancel') }}</span>
 | 
			
		||||
          </button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <MultiplayerSetupComponent
 | 
			
		||||
        v-if="isSessionStarted && isMultiplayer"
 | 
			
		||||
        @ready="setPlayerReady"
 | 
			
		||||
        @start="startMatch"
 | 
			
		||||
        @cancel="cancelMatch"
 | 
			
		||||
      />
 | 
			
		||||
    </section>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 
 | 
			
		||||
@@ -52,45 +52,56 @@ async function login() {
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="login">
 | 
			
		||||
    <h1 class="title">{{ $t('login') }}</h1>
 | 
			
		||||
    <div class="message is-danger">
 | 
			
		||||
      <div class="message-body" v-if="errorLogin">
 | 
			
		||||
        {{ $t('invalid-username-or-password') }}
 | 
			
		||||
  <div class="container login">
 | 
			
		||||
    <div class="is-flex is-justify-content-center is-align-items-center">
 | 
			
		||||
      <div class="login-form-container box is-no-shadow" style="width: 400px">
 | 
			
		||||
        <div class="message is-danger">
 | 
			
		||||
          <div class="message-body" v-if="errorLogin">
 | 
			
		||||
            {{ $t('invalid-username-or-password') }}
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <form class="form" @submit.prevent="login">
 | 
			
		||||
          <div class="field">
 | 
			
		||||
            <label class="label">{{ $t('username') }}</label>
 | 
			
		||||
            <div class="control">
 | 
			
		||||
              <input
 | 
			
		||||
                class="input"
 | 
			
		||||
                type="text"
 | 
			
		||||
                v-model="username"
 | 
			
		||||
                :placeholder="t('username-placeholder')"
 | 
			
		||||
              />
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="field">
 | 
			
		||||
            <label class="label">{{ $t('password') }}</label>
 | 
			
		||||
            <div class="control">
 | 
			
		||||
              <input
 | 
			
		||||
                class="input"
 | 
			
		||||
                type="password"
 | 
			
		||||
                v-model="password"
 | 
			
		||||
                :placeholder="t('password-placeholder')"
 | 
			
		||||
              />
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div class="field is-grouped">
 | 
			
		||||
            <div class="control">
 | 
			
		||||
              <button class="button is-primary" type="submit">{{ $t('login-button') }}</button>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </form>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <form class="form" @submit.prevent="login">
 | 
			
		||||
      <div class="field">
 | 
			
		||||
        <label class="label">{{ $t('username') }}</label>
 | 
			
		||||
        <div class="control">
 | 
			
		||||
          <input
 | 
			
		||||
            class="input"
 | 
			
		||||
            type="text"
 | 
			
		||||
            v-model="username"
 | 
			
		||||
            :placeholder="t('username-placeholder')"
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="field">
 | 
			
		||||
        <label class="label">{{ $t('password') }}</label>
 | 
			
		||||
        <div class="control">
 | 
			
		||||
          <input
 | 
			
		||||
            class="input"
 | 
			
		||||
            type="password"
 | 
			
		||||
            v-model="password"
 | 
			
		||||
            :placeholder="t('password-placeholder')"
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="field is-grouped">
 | 
			
		||||
        <div class="control">
 | 
			
		||||
          <button class="button is-primary" type="submit">{{ $t('login-button') }}</button>
 | 
			
		||||
        </div>
 | 
			
		||||
        <!-- <div class="control">
 | 
			
		||||
          <button class="button">Cancel</button>
 | 
			
		||||
        </div> -->
 | 
			
		||||
      </div>
 | 
			
		||||
    </form>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
.login,
 | 
			
		||||
.login > div {
 | 
			
		||||
  height: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-form-container {
 | 
			
		||||
  width: 400px;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								types/type.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								types/type.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -6,4 +6,5 @@ declare module 'socket.io' {
 | 
			
		||||
 | 
			
		||||
declare interface Window {
 | 
			
		||||
  __TAURI__: any
 | 
			
		||||
  opera: any
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user