Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

193 migrate to components api #197

Merged
merged 12 commits into from
Feb 5, 2024
Merged
18 changes: 0 additions & 18 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 28 additions & 7 deletions client/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ require('@rushstack/eslint-patch/modern-module-resolution');

module.exports = {
extends: [
'plugin:vue/vue3-essential',
'plugin:vue/vue3-strongly-recommended',
'plugin:vue/vue3-recommended',
'eslint:recommended',
'plugin:@intlify/vue-i18n/recommended',
'plugin:@typescript-eslint/recommended-type-checked',
'@vue/eslint-config-typescript',
'@vue/eslint-config-prettier/skip-formatting',
Expand All @@ -20,11 +19,33 @@ module.exports = {
parser: '@typescript-eslint/parser'
},
rules: {
'@typescript-eslint/consistent-type-imports': ['error'],
'@typescript-eslint/no-unused-vars': ['off'],
'@typescript-eslint/no-unsafe-assignment': ['off'],
'vue/multi-word-component-names': ['off'],
'@typescript-eslint/consistent-type-imports': 2,
'@typescript-eslint/prefer-for-of': 2,
'@typescript-eslint/unbound-method': 0,
'@typescript-eslint/no-unused-vars': 0,
'@typescript-eslint/no-unsafe-assignment': 0,
'vue/multi-word-component-names': 0,
'vue/require-typed-ref': 2,
'vue/component-api-style': [2, ['script-setup']],
'vue/block-lang': [2, { script: { lang: 'ts' } }],
'vue/define-macros-order': 2,
'vue/define-props-declaration': 2,
'vue/no-ref-object-reactivity-loss': 2,
'vue/no-undef-properties': 2,
'vue/no-unused-components': [2, { ignoreWhenBindingPresent: true }],
'vue/no-unused-properties': 2,
'vue/no-unused-refs': 2,
'@intlify/vue-i18n/valid-message-syntax': 2,
'@intlify/vue-i18n/key-format-style': [2, 'camelCase', {allowArray: true}],
'@intlify/vue-i18n/no-duplicate-keys-in-locale': 2,
'@intlify/vue-i18n/no-missing-keys-in-other-locales': 2,
'prettier/prettier': 2
},
ignorePatterns: ['/*', '!/src']
ignorePatterns: ['/*', '!/src'],
settings: {
'vue-i18n': {
localeDir: './src/locales/*.{ts}', // extension is glob formatting!
messageSyntaxVersion: '^9.9.1'
}
}
};
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"portal-vue": "^3.0.0",
"socket.io-client": "^4.7.2",
"vue": "^3.2.47",
"vue-i18n": "^9.8.0",
"vue-i18n": "^9.9.1",
"vue-router": "^4.2.5",
"vue-slider-component": "^4.1.0-beta.7",
"@types/bootstrap": "^5.2.10",
Expand Down
18 changes: 7 additions & 11 deletions client/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
<script setup lang="ts">
import { PortalTarget } from 'portal-vue';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
</script>

<template>
<div class="container min-vh-100">
<div class="d-flex min-vh-100 flex-column justify-content-evenly align-items-center">
<img class="w-lg-50 w-100 my-2" src="@/assets/images/logo.png" :alt="$t('strikeOrSike')" />
<img class="w-lg-50 w-100 my-2" src="@/assets/images/logo.png" :alt="t('strikeOrSike')" />
<router-view class="mt-2 mb-5" />
<portal-target name="banner" class="mb-2" />
</div>
</div>
</template>

<script lang="ts">
import { PortalTarget } from 'portal-vue';
import { defineComponent } from 'vue';
export default defineComponent({
components: {
PortalTarget
}
});
</script>

<style lang="scss">
@import '@/styles/app';
</style>
48 changes: 24 additions & 24 deletions client/src/components/countdown/Countdown.vue
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
<template>
<div class="w-100 d-flex justify-content-center bounce">
<h1 class="flip">{{ $n(timer) }}</h1>
</div>
</template>

<script lang="ts">
<script setup lang="ts">
import timerMp3 from '@/assets/audio/countdown.mp3';
import { AudioWrap } from '@/mixins/audiowrap.js';
import { useGameStore } from '@/stores/game.js';
import { onMounted, watch } from 'vue';
import { useSettingsStore } from '@/stores/settings.js';
import { mapState } from 'pinia';
import { defineComponent } from 'vue';
import { useI18n } from 'vue-i18n';
import { useGameStore } from '@/stores/game.js';
const timerAudio = new AudioWrap(timerMp3);

const timer = new AudioWrap(timerMp3);
const gameStore = useGameStore();
const settingsStore = useSettingsStore();

export default defineComponent({
computed: {
...mapState(useGameStore, ['timer']),
...mapState(useSettingsStore, ['volume'])
},
watch: {
volume(val: number) {
timer.volume = val;
}
},
mounted() {
timer.play();
}
onMounted(() => {
timerAudio.play();
});

watch(
() => settingsStore.volume,
(val: number) => {
timerAudio.volume = val;
}
);

const { n } = useI18n();
</script>

<template>
<div class="w-100 d-flex justify-content-center bounce">
<h1 class="flip">{{ n(gameStore.timer) }}</h1>
</div>
</template>

<style lang="scss" scoped>
h1 {
font-family: $header-font;
Expand Down
81 changes: 41 additions & 40 deletions client/src/components/endGame/EndGame.vue
Original file line number Diff line number Diff line change
@@ -1,52 +1,53 @@
<script setup lang="ts">
import ClickMp3 from '@/assets/audio/click2.mp3';
import { AudioWrap } from '@/mixins/audiowrap.js';
import { computed } from 'vue';
import { useGameStore } from '@/stores/game.js';
import { useI18n } from 'vue-i18n';

const click = new AudioWrap(ClickMp3);

const gameStore = useGameStore();

const rank = computed<number[]>(() => {
let result = [];
let lastScore = -1;
let lastRank = 1;
for (const [rank, score] of gameStore.scores.entries()) {
if (score.points < lastScore) {
result.push(rank + 1);
lastRank = rank + 1;
} else {
result.push(lastRank);
}
lastScore = score.points;
}
return result;
});

function toLobby() {
click.play();
gameStore.setScene('Lobby');
}

const { n } = useI18n();
</script>

<template>
<div class="w-100 d-flex flex-column justify-content-start align-items-center gap-3 py-3 px-4">
<h1 v-t="'playerScores'" class="mt-2 text-center display-1 text-red font-fancy" />

<div class="list-group w-75 w-xl-50">
<div v-for="(score, index) in scores" :key="score.player.id" class="list-group-item">
{{ rank[index] }}. {{ score.player.name }}: {{ $n(score.points) }}
<div v-for="(score, index) in gameStore.scores" :key="score.player.id" class="list-group-item">
<span
v-t="{
path: 'scoreFormat',
args: { rank: rank[index], player: score.player.name, score: n(score.points) }
}" />
</div>
</div>

<div class="flex-grow-1" />
<button v-t="'toLobby'" class="btn btn-blue w-75 w-lg-50 fs-4 mb-4" @click="toLobby" />
</div>
</template>

<script lang="ts">
import { mapActions, mapState } from 'pinia';
import ClickMp3 from '@/assets/audio/click2.mp3';
import { AudioWrap } from '@/mixins/audiowrap.js';
import { useGameStore } from '@/stores/game.js';
import { defineComponent } from 'vue';

const click = new AudioWrap(ClickMp3);

export default defineComponent({
computed: {
...mapState(useGameStore, ['scores']),
rank() {
let result = [];
let lastScore = -1;
let lastRank = 1;
for (const [rank, score] of this.scores.entries()) {
if (score.points < lastScore) {
result.push(rank + 1);
lastRank = rank + 1;
} else {
result.push(lastRank);
}
lastScore = score.points;
}
return result;
}
},
methods: {
...mapActions(useGameStore, ['setScene']),
toLobby() {
click.play();
this.setScene('lobby');
}
}
});
</script>
106 changes: 46 additions & 60 deletions client/src/components/endRound/EndRound.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,47 @@
<script setup lang="ts">
import NotificationCount from '@/components/gameShared/NotificationCount.vue';
import PlayerChooser from '@/components/endRound/PlayerChooser.vue';
import ResponseList from '@/components/gameShared/ResponseList.vue';
import Prompt from '@/components/gameShared/Prompt.vue';
import { onMounted, ref, watch } from 'vue';
import { AudioWrap } from '@/mixins/audiowrap.js';
import ClickMp3 from '@/assets/audio/click2.mp3';
import socket from '@/socket/socket.js';
import { useRoomStore } from '@/stores/room.js';
import { useGameStore } from '@/stores/game.js';
import { useI18n } from 'vue-i18n';

const roomStore = useRoomStore();
const gameStore = useGameStore();

const responsesId = ref('');
const selectedId = ref('');
const pressedVote = ref(false);

watch(selectedId, async (val: string) => {
if (val) {
await gameStore.getResponses(val).then(() => {
responsesId.value = val;
});
}
});

onMounted(() => {
selectedId.value = roomStore.self!.id;
});

function sendVote() {
new AudioWrap(ClickMp3).play();
pressedVote.value = !pressedVote.value;
socket.emit('pollVote', 'startNextRound');
}

const { t, n } = useI18n();
</script>

<template>
<div class="w-100 d-flex flex-column justify-content-between align-items-center py-3 px-4">
<prompt :prompt="prompt" />
<prompt :prompt="gameStore.prompt" />
<response-list :selectable="false" :height="45" :player-id="responsesId" />
<player-chooser v-model="selectedId" class="w-50 w-lg-25 fs-4 mb-3" />
<button
Expand All @@ -10,68 +51,13 @@
'text-white-50 shadow-none': pressedVote
}"
@click="sendVote">
{{ hasNextRound ? $t('startNextRound') : $t('viewResults') }}
{{ gameStore.hasNextRound ? t('startNextRound') : t('viewResults') }}
<notification-count
v-if="startNextRoundCount"
v-if="gameStore.startNextRoundCount"
class="position-absolute top-0 start-100 translate-middle fs-6"
:next-majority="startNextRoundNext">
{{ $n(startNextRoundCount) }}
:next-majority="gameStore.startNextRoundNext">
{{ n(gameStore.startNextRoundCount) }}
</notification-count>
</button>
</div>
</template>

<script lang="ts">
import Prompt from '@/components/gameShared/Prompt.vue';
import ResponseList from '@/components/gameShared/ResponseList.vue';
import NotificationCount from '@/components/gameShared/NotificationCount.vue';
import ClickMp3 from '@/assets/audio/click2.mp3';
import PlayerChooser from '@/components/endRound/PlayerChooser.vue';
import { AudioWrap } from '@/mixins/audiowrap.js';
import socket from '@/socket/socket';
import { useGameStore } from '@/stores/game.js';
import { useRoomStore } from '@/stores/room.js';
import { mapState, mapActions } from 'pinia';
import { defineComponent } from 'vue';

export default defineComponent({
components: {
PlayerChooser,
Prompt,
ResponseList,
NotificationCount
},
data() {
return {
responsesId: '',
selectedId: '',
pressedVote: false
};
},
computed: {
...mapState(useGameStore, ['prompt', 'hasNextRound', 'startNextRoundCount', 'startNextRoundNext']),
...mapState(useRoomStore, ['self'])
},
watch: {
selectedId(val: string) {
if (val) {
void this.getResponses(val).then(() => {
this.responsesId = val;
});
}
}
},
mounted() {
this.selectedId = this.self!.id;
},
methods: {
sendVote() {
this.pressedVote = !this.pressedVote;

new AudioWrap(ClickMp3).play();
socket.emit('pollVote', 'startNextRound');
},
...mapActions(useGameStore, ['getResponses'])
}
});
</script>
Loading
Loading