diff --git a/src/client/app/common/views/components/user-lists-index.vue b/src/client/app/common/views/components/user-lists-index.vue new file mode 100644 index 0000000000..0aec9658e8 --- /dev/null +++ b/src/client/app/common/views/components/user-lists-index.vue @@ -0,0 +1,82 @@ +<template> +<div class="xkxvokkjlptzyewouewmceqcxhpgzprp"> + <button class="ui" @click="add">{{ $t('create-list') }}</button> + <a v-for="list in lists" :key="list.id" @click="choice(list)">{{ list.title }}</a> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import XEditor from '../../../common/views/components/user-lists.vue'; +import i18n from '../../../i18n'; + +export default Vue.extend({ + i18n: i18n('common/views/components/user-lists.vue'), + data() { + return { + fetching: true, + lists: [] + }; + }, + mounted() { + this.$root.api('users/lists/list').then(lists => { + this.fetching = false; + this.lists = lists; + }); + }, + methods: { + add() { + this.$root.dialog({ + title: this.$t('list-name'), + input: true + }).then(async ({ canceled, result: title }) => { + if (canceled) return; + const list = await this.$root.api('users/lists/create', { + title + }); + + this.$emit('choosen', list); + }); + }, + choice(list) { + this.$emit('choosen', list); + }, + close() { + (this as any).$refs.window.close(); + } + } +}); +</script> + +<style lang="stylus" scoped> +.xkxvokkjlptzyewouewmceqcxhpgzprp + padding 16px + + > button + display block + margin-bottom 16px + color var(--primaryForeground) + background var(--primary) + width 100% + border-radius 38px + user-select none + cursor pointer + padding 0 16px + min-width 100px + line-height 38px + font-size 14px + font-weight 700 + + &:hover + background var(--primaryLighten10) + + &:active + background var(--primaryDarken10) + + > a + display block + padding 16px + border solid 1px var(--faceDivider) + border-radius 4px + +</style> diff --git a/src/client/app/common/views/components/user-lists.vue b/src/client/app/common/views/components/user-lists.vue new file mode 100644 index 0000000000..b6803cd7f7 --- /dev/null +++ b/src/client/app/common/views/components/user-lists.vue @@ -0,0 +1,175 @@ +<template> +<div class="vchtoekanapleubgzioubdtmlkribzfd"> + <div v-if="game"> + <x-gameroom :game="game" :self-nav="selfNav" @go-index="goIndex"/> + </div> + <div class="matching" v-else-if="matching"> + <h1>{{ this.$t('matching.waiting-for').split('{}')[0] }}<b><mk-user-name :user="matching"/></b>{{ this.$t('matching.waiting-for').split('{}')[1] }}<mk-ellipsis/></h1> + <div class="cancel"> + <form-button round @click="cancel">{{ $t('matching.cancel') }}</form-button> + </div> + </div> + <div v-else-if="gameId"> + ... + </div> + <div class="index" v-else> + <x-index @go="nav" @matching="onMatching"/> + </div> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import i18n from '../../../../../i18n'; +import XGameroom from './reversi.gameroom.vue'; +import XIndex from './reversi.index.vue'; +import Progress from '../../../../scripts/loading'; + +export default Vue.extend({ + i18n: i18n('common/views/components/games/reversi/reversi.vue'), + components: { + XGameroom, + XIndex + }, + + props: { + gameId: { + type: String, + required: false + }, + selfNav: { + type: Boolean, + require: false, + default: true + } + }, + + data() { + return { + game: null, + matching: null, + connection: null, + pingClock: null + }; + }, + + watch: { + game() { + this.$emit('gamed', this.game); + }, + + gameId() { + this.fetch(); + } + }, + + mounted() { + this.fetch(); + + if (this.$store.getters.isSignedIn) { + this.connection = this.$root.stream.useSharedConnection('gamesReversi'); + + this.connection.on('matched', this.onMatched); + + this.pingClock = setInterval(() => { + if (this.matching) { + this.connection.send('ping', { + id: this.matching.id + }); + } + }, 3000); + } + }, + + beforeDestroy() { + if (this.connection) { + this.connection.dispose(); + clearInterval(this.pingClock); + } + }, + + methods: { + fetch() { + if (this.gameId == null) { + this.game = null; + } else { + Progress.start(); + this.$root.api('games/reversi/games/show', { + gameId: this.gameId + }).then(game => { + this.game = game; + Progress.done(); + }); + } + }, + + async nav(game, actualNav = true) { + if (this.selfNav) { + // 受け取ったゲーム情報が省略されたものなら完全な情報を取得する + if (game != null && (game.settings == null || game.settings.map == null)) { + game = await this.$root.api('games/reversi/games/show', { + gameId: game.id + }); + } + + this.game = game; + } else { + this.$emit('nav', game, actualNav); + } + }, + + onMatching(user) { + this.matching = user; + }, + + cancel() { + this.matching = null; + this.$root.api('games/reversi/match/cancel'); + }, + + accept(invitation) { + this.$root.api('games/reversi/match', { + userId: invitation.parent.id + }).then(game => { + if (game) { + this.matching = null; + + this.nav(game); + } + }); + }, + + onMatched(game) { + this.matching = null; + this.game = game; + this.nav(game, false); + }, + + goIndex() { + this.nav(null); + } + } +}); +</script> + +<style lang="stylus" scoped> +.vchtoekanapleubgzioubdtmlkribzfd + color var(--text) + background var(--bg) + + > .matching + > h1 + margin 0 + padding 24px + font-size 20px + text-align center + font-weight normal + + > .cancel + margin 0 auto + padding 24px 0 0 0 + max-width 200px + text-align center + border-top dashed 1px #c4cdd4 + +</style> diff --git a/src/client/app/desktop/views/components/user-lists-window.vue b/src/client/app/desktop/views/components/user-lists-window.vue index 4f0af4a278..7ef04822a6 100644 --- a/src/client/app/desktop/views/components/user-lists-window.vue +++ b/src/client/app/desktop/views/components/user-lists-window.vue @@ -1,85 +1,31 @@ <template> <mk-window ref="window" width="450px" height="500px" @closed="destroyDom"> <template #header><fa icon="list"/> {{ $t('title') }}</template> - - <div class="xkxvokkjlptzyewouewmceqcxhpgzprp"> - <button class="ui" @click="add">{{ $t('create-list') }}</button> - <a v-for="list in lists" :key="list.id" @click="choice(list)">{{ list.title }}</a> - </div> + <x-lists :class="$style.content" @listd="l => list = l"> </mk-window> </template> <script lang="ts"> import Vue from 'vue'; import i18n from '../../../i18n'; +import { url } from '../../../config'; export default Vue.extend({ - i18n: i18n('desktop/views/components/user-lists-window.vue'), + i18n: i18n('desktop/views/components/game-window.vue'), + components: { + XLists: () => import('../../../common/views/components/user-lists.vue').then(m => m.default) + }, data() { return { - fetching: true, - lists: [] + list: null }; - }, - mounted() { - this.$root.api('users/lists/list').then(lists => { - this.fetching = false; - this.lists = lists; - }); - }, - methods: { - add() { - this.$root.dialog({ - title: this.$t('list-name'), - input: true - }).then(async ({ canceled, result: title }) => { - if (canceled) return; - const list = await this.$root.api('users/lists/create', { - title - }); - - this.$emit('choosen', list); - }); - }, - choice(list) { - this.$emit('choosen', list); - }, - close() { - (this as any).$refs.window.close(); - } } }); </script> -<style lang="stylus" scoped> -.xkxvokkjlptzyewouewmceqcxhpgzprp - padding 16px - - > button - display block - margin-bottom 16px - color var(--primaryForeground) - background var(--primary) - width 100% - border-radius 38px - user-select none - cursor pointer - padding 0 16px - min-width 100px - line-height 38px - font-size 14px - font-weight 700 - - &:hover - background var(--primaryLighten10) - - &:active - background var(--primaryDarken10) - - > a - display block - padding 16px - border solid 1px var(--faceDivider) - border-radius 4px +<style lang="stylus" module> +.content + height 100% + overflow auto </style>