Merge branch 'develop' of gnuher.de:various/git/codes/go/knowyt into develop
This commit is contained in:
commit
e63db9a6dd
4
Makefile
4
Makefile
@ -60,3 +60,7 @@ clean:
|
|||||||
rm -rf client/.output/
|
rm -rf client/.output/
|
||||||
rm -rf client/.nuxt/
|
rm -rf client/.nuxt/
|
||||||
$(MAKE) -C server clean
|
$(MAKE) -C server clean
|
||||||
|
|
||||||
|
reset-data:
|
||||||
|
rm -rf server/data/
|
||||||
|
git checkout server/data/
|
@ -1,20 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
node: true
|
|
||||||
},
|
|
||||||
parserOptions: {
|
|
||||||
parser: '@babel/eslint-parser',
|
|
||||||
requireConfigFile: false
|
|
||||||
},
|
|
||||||
extends: [
|
|
||||||
'@nuxtjs',
|
|
||||||
'plugin:nuxt/recommended',
|
|
||||||
'prettier'
|
|
||||||
],
|
|
||||||
plugins: [
|
|
||||||
],
|
|
||||||
// add your custom rules here
|
|
||||||
rules: {}
|
|
||||||
}
|
|
90
_client/.gitignore
vendored
90
_client/.gitignore
vendored
@ -1,90 +0,0 @@
|
|||||||
# Created by .ignore support plugin (hsz.mobi)
|
|
||||||
### Node template
|
|
||||||
# Logs
|
|
||||||
/logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# TypeScript v1 declaration files
|
|
||||||
typings/
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variables file
|
|
||||||
.env
|
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
|
||||||
.cache
|
|
||||||
|
|
||||||
# next.js build output
|
|
||||||
.next
|
|
||||||
|
|
||||||
# nuxt.js build output
|
|
||||||
.nuxt
|
|
||||||
|
|
||||||
# Nuxt generate
|
|
||||||
dist
|
|
||||||
|
|
||||||
# vuepress build output
|
|
||||||
.vuepress/dist
|
|
||||||
|
|
||||||
# Serverless directories
|
|
||||||
.serverless
|
|
||||||
|
|
||||||
# IDE / Editor
|
|
||||||
.idea
|
|
||||||
|
|
||||||
# Service worker
|
|
||||||
sw.*
|
|
||||||
|
|
||||||
# macOS
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# Vim swap files
|
|
||||||
*.swp
|
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"semi": false,
|
|
||||||
"singleQuote": true
|
|
||||||
}
|
|
Binary file not shown.
@ -1,38 +0,0 @@
|
|||||||
const fs = require('fs')
|
|
||||||
const packageJson = fs.readFileSync('./package.json')
|
|
||||||
const version = JSON.parse(packageJson).version || 0
|
|
||||||
|
|
||||||
export default {
|
|
||||||
ssr: false,
|
|
||||||
srcDir: 'src/',
|
|
||||||
target: 'static',
|
|
||||||
head: {
|
|
||||||
title: 'Know Your Teammates',
|
|
||||||
meta: [
|
|
||||||
{ charset: 'utf-8' },
|
|
||||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
|
||||||
],
|
|
||||||
link: [
|
|
||||||
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
css: [
|
|
||||||
'@/assets/css/fonts',
|
|
||||||
'@/assets/css/base',
|
|
||||||
],
|
|
||||||
components: true,
|
|
||||||
modules: ['@nuxtjs/axios'],
|
|
||||||
plugins: [
|
|
||||||
{ src: '~/plugins/engine', mode: 'client' },
|
|
||||||
{ src: '~/plugins/formatter', mode: 'client' },
|
|
||||||
{ src: '~/plugins/i18n', mode: 'client' },
|
|
||||||
],
|
|
||||||
axios: { proxy: true },
|
|
||||||
publicRuntimeConfig: {
|
|
||||||
serverBaseUrl: '/',
|
|
||||||
version,
|
|
||||||
},
|
|
||||||
proxy: {
|
|
||||||
'/api/': 'http://localhost:32039',
|
|
||||||
},
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "knowyt",
|
|
||||||
"version": "1.20",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"dev": "nuxt",
|
|
||||||
"build": "nuxt build",
|
|
||||||
"start": "nuxt start",
|
|
||||||
"generate": "nuxt generate"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@nuxtjs/axios": "^5.13.6",
|
|
||||||
"build-url": "^6.0.1",
|
|
||||||
"core-js": "^3.15.1",
|
|
||||||
"nuxt": "^2.15.7",
|
|
||||||
"url": "^0.11.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@babel/eslint-parser": "^7.14.7",
|
|
||||||
"sass": "^1.36.0",
|
|
||||||
"sass-loader": "^10"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
@import './colors';
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: $primary-background-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
$primary-background-color: #282838;
|
|
||||||
$primary-text-color: #ffffff;
|
|
||||||
$error-text-color: #ff8000;
|
|
||||||
$backdrop-color: rgba(40, 40, 56, 90%);
|
|
||||||
|
|
||||||
|
|
||||||
// Text
|
|
||||||
$text-primary-text-color: #ffffff;
|
|
||||||
$text-secondary-text-color: #a0a0a0;
|
|
||||||
|
|
||||||
$text-primary-hover-text-color: #ffffc0;
|
|
||||||
$text-secondary-hover-text-color: #e0e0e0;
|
|
||||||
|
|
||||||
|
|
||||||
// Box
|
|
||||||
$primary-box-background-color: #282838;
|
|
||||||
$primary-box-border-color: #ffffff;
|
|
||||||
$primary-box-text-color: #ffffff;
|
|
||||||
|
|
||||||
$primary-box-hover-background-color: #8040e0;
|
|
||||||
$primary-box-hover-text-color: #ffffff;
|
|
||||||
|
|
||||||
$primary-box-animation-color: rgba(128, 128, 64, 0.5);
|
|
||||||
|
|
||||||
$secondary-box-border-color: #ffffff;
|
|
||||||
$secondary-box-background-color: rgba(64, 32, 128, 0.5);
|
|
||||||
$secondary-box-text-color: $text-primary-text-color;
|
|
||||||
|
|
||||||
$secondary-box-hover-background-color: #6040c0;
|
|
||||||
$secondary-box-hover-border-color: #ffffff;
|
|
||||||
$secondary-box-hover-text-color: $text-primary-hover-text-color;
|
|
||||||
|
|
||||||
$secondary-box-disabled-text-color: #606060;
|
|
||||||
|
|
||||||
|
|
||||||
// Button
|
|
||||||
$button-background-color: #00a0e0;
|
|
||||||
$button-border-color: #007098;
|
|
||||||
$button-text-color: #304048;
|
|
||||||
|
|
||||||
$button-hover-background-color: $button-background-color;
|
|
||||||
$button-hover-border-color: $button-border-color;
|
|
||||||
$button-hover-text-color: #ffffc0;
|
|
||||||
|
|
||||||
$button-disabled-background-color: #006080;
|
|
||||||
$button-disabled-border-color: #004060;
|
|
||||||
$button-disabled-text-color: #102028;
|
|
||||||
|
|
||||||
$button-secondary-background-color: #808040;
|
|
||||||
$button-secondary-text-color: #a0a0a0;
|
|
||||||
$button-secondary-border-color: #ffffff;
|
|
||||||
$button-secondary-hover-text-color: #e0e0e0;
|
|
||||||
|
|
||||||
|
|
||||||
// Input
|
|
||||||
$input-background-color: $primary-background-color;
|
|
||||||
$input-text-color: #ffffff;
|
|
||||||
$input-border-color: #00a0e0;
|
|
||||||
|
|
||||||
$input-inactive-background-color: $input-background-color;
|
|
||||||
$input-inactive-text-color: $input-text-color;
|
|
||||||
$input-inactive-border-color: #ffffff;
|
|
@ -1,4 +0,0 @@
|
|||||||
@import './colors.scss';
|
|
||||||
|
|
||||||
$primary-font: 'Wendy One';
|
|
||||||
$secondary-font: 'Dosis';
|
|
@ -1,25 +0,0 @@
|
|||||||
@font-face {
|
|
||||||
font-family: 'Wendy One';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: url(/fonts/wendy-one/WendyOne-Regular.woff2) format('woff2');
|
|
||||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Dosis';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: url(/fonts/dosis/dosis-300.woff2) format('woff2');
|
|
||||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Dosis';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 800;
|
|
||||||
font-display: swap;
|
|
||||||
src: url(/fonts/dosis/dosis-800.woff2) format('woff2');
|
|
||||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="add-new-quote">
|
|
||||||
<button class="add-new-quote__button" @click="$emit('createQuote')">+</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.add-new-quote {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
margin: 32px 0;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&__button {
|
|
||||||
width: 100px;
|
|
||||||
height: 48px;
|
|
||||||
background-color: $button-background-color;
|
|
||||||
border: 4px solid $button-border-color;
|
|
||||||
border-radius: 8px;
|
|
||||||
color: $button-text-color;
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 800;
|
|
||||||
text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: $button-hover-background-color;
|
|
||||||
border-color: $button-hover-border-color;
|
|
||||||
color: $button-hover-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,68 +0,0 @@
|
|||||||
<template>
|
|
||||||
<button class="button" :class="{ disabled, border }" @click="click">
|
|
||||||
<slot />
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
border: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
click() {
|
|
||||||
this.$emit('click')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.button {
|
|
||||||
display: inline-block;
|
|
||||||
margin: auto;
|
|
||||||
padding: 4px 24px;
|
|
||||||
background-color: inherit;
|
|
||||||
color: $text-secondary-text-color;
|
|
||||||
text-align: center;
|
|
||||||
font-family: $secondary-font;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 24px;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $text-secondary-hover-text-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.border {
|
|
||||||
background-color: $button-background-color;
|
|
||||||
color: $button-text-color;
|
|
||||||
border: 4px solid $button-border-color;
|
|
||||||
border-radius: 8px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: $button-hover-border-color;
|
|
||||||
background-color: $button-hover-background-color;
|
|
||||||
color: $button-hover-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled,
|
|
||||||
&:hover.disabled {
|
|
||||||
cursor: default;
|
|
||||||
background-color: $button-disabled-background-color;
|
|
||||||
border-color: $button-disabled-border-color;
|
|
||||||
color: $button-disabled-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,163 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="collect-quote">
|
|
||||||
<div class="collect-quote__backdrop" />
|
|
||||||
<div class="collect-quote__container">
|
|
||||||
<div class="collect-quote__quote-container">
|
|
||||||
<div class="collect-quote__title">{{ $t('preview') }}</div>
|
|
||||||
<div class="collect-quote__quote">
|
|
||||||
<Quote :text="quote.quote" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="collect-quote__inputgroup">
|
|
||||||
<button class="collect-quote__button-close" @click="close">X</button>
|
|
||||||
<form class="collect-quote__textinput-container">
|
|
||||||
<input type="hidden" name="id" :value="quote.id" />
|
|
||||||
<textarea
|
|
||||||
class="collect-quote__textinput"
|
|
||||||
v-model="quote.quote"
|
|
||||||
:placeholder="$t('enter-quote')"
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
<div class="collect-quote__cta-container">
|
|
||||||
<Button class="collect-quote__cta" @click="save">{{ $t('save') }}</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: ['quote'],
|
|
||||||
beforeMount() {
|
|
||||||
this.$i18n.map({
|
|
||||||
'preview': { de: 'Vorschau:', en: 'Preview:' },
|
|
||||||
'enter-quote': { de: 'hier eintragen ...', en: 'enter here ...' },
|
|
||||||
'save': { de: 'Speichern', en: 'save' },
|
|
||||||
})
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
save() {
|
|
||||||
this.$emit('saveQuote', this.quote)
|
|
||||||
},
|
|
||||||
close() {
|
|
||||||
this.$emit('close')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.collect-quote {
|
|
||||||
color: $primary-text-color;
|
|
||||||
|
|
||||||
&__backdrop {
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__container {
|
|
||||||
position: absolute;
|
|
||||||
display: flex;
|
|
||||||
left: 10%;
|
|
||||||
top: 15%;
|
|
||||||
width: 80%;
|
|
||||||
height: 400px;
|
|
||||||
background-color: $primary-box-background-color;
|
|
||||||
border: 4px solid $primary-box-border-color;
|
|
||||||
border-radius: 20px;
|
|
||||||
color: $primary-box-text-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__quote-container {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 50%;
|
|
||||||
height: 100%;
|
|
||||||
border-right: 1px solid $primary-box-border-color;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__title {
|
|
||||||
font-size: 32px;
|
|
||||||
font-family: "$primary-font";
|
|
||||||
margin: 48px 0 16px 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__quote {
|
|
||||||
align-self: center;
|
|
||||||
max-width: 400px;
|
|
||||||
margin: 0 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__inputgroup {
|
|
||||||
width: 50%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__textinput-container {
|
|
||||||
height: 100%;
|
|
||||||
margin: 48px 48px 0 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__textinput {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: transparent;
|
|
||||||
color: $primary-text-color;
|
|
||||||
font-size: 16px;
|
|
||||||
border: 1px solid #808080;
|
|
||||||
outline: 0;
|
|
||||||
resize: none;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
border-width: 3px;
|
|
||||||
border-image: linear-gradient(to right, #e0e0e0 0, #c0c0c0 30%, #e0e0e0 100%);
|
|
||||||
border-image-slice: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__button-close {
|
|
||||||
align-self: flex-end;
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
margin: 2px;
|
|
||||||
padding: 10px;
|
|
||||||
background: none;
|
|
||||||
border: 0;
|
|
||||||
font-size: 24px;
|
|
||||||
color: $primary-box-text-color;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: $primary-box-hover-background-color;
|
|
||||||
color: $primary-box-hover-text-color;
|
|
||||||
border-radius: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__cta-container {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__cta {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
margin: 48px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,67 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<CollectQuotesExplain />
|
|
||||||
<AddNewQuote v-if="quotes.length > 0" @createQuote="createQuote"/>
|
|
||||||
<NoQuotesYet v-if="quotes.length == 0" />
|
|
||||||
<QuoteListItem
|
|
||||||
v-for="quote in quotes"
|
|
||||||
:key="quote.id"
|
|
||||||
:quote="quote"
|
|
||||||
@editQuote="editQuote"
|
|
||||||
/>
|
|
||||||
<AddNewQuote @createQuote="createQuote" />
|
|
||||||
<CollectQuote
|
|
||||||
v-if="showCollectQuoteDialog"
|
|
||||||
:quote="collectQuote"
|
|
||||||
@close="closeCollectQuoteDialog"
|
|
||||||
@saveQuote="saveQuote"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showCollectQuoteDialog: false,
|
|
||||||
collectQuote: {},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
quotes() {
|
|
||||||
var quotes = [...this.$store.state.myQuotes.quotes]
|
|
||||||
quotes.sort((a, b) => {
|
|
||||||
return a.id.localeCompare(b.id)
|
|
||||||
})
|
|
||||||
return quotes
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
closeCollectQuoteDialog() {
|
|
||||||
this.showCollectQuoteDialog = false
|
|
||||||
},
|
|
||||||
editQuote(quote) {
|
|
||||||
this.showCollectQuoteDialog = true
|
|
||||||
this.collectQuote = {
|
|
||||||
id: quote.id,
|
|
||||||
quote: quote.quote,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async saveQuote(quote) {
|
|
||||||
this.showCollectQuoteDialog = false
|
|
||||||
await this.$engine.saveQuote(quote.id, quote.quote)
|
|
||||||
await this.$engine.getMyQuotes()
|
|
||||||
},
|
|
||||||
createQuote() {
|
|
||||||
this.showCollectQuoteDialog = true
|
|
||||||
this.collectQuote = {
|
|
||||||
id: ':new:',
|
|
||||||
quote: '',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
async fetch() {
|
|
||||||
await this.$engine.getMyQuotes()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,170 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="collect-quotes-explain">
|
|
||||||
<Infobox>
|
|
||||||
<div class="collect-quotes-explain__open-close-toggle" @click="toggleOpenClose">
|
|
||||||
<span v-if="explainOpen">X</span>
|
|
||||||
<span v-else>></span>
|
|
||||||
</div>
|
|
||||||
<div v-if="!explainOpen">
|
|
||||||
<div class="collect-quotes-explain__explain" @click="toggleOpenClose">
|
|
||||||
<p>{{ $t('explain-game-closed') }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="explainOpen">
|
|
||||||
<div class="collect-quotes-explain__explain">
|
|
||||||
<p>{{ $t('explain-game-p-1') }}</p>
|
|
||||||
<p>{{ $t('explain-game-p-2') }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="collect-quotes-explain__example">
|
|
||||||
<div class="collect-quotes-explain__example-title">{{ $t('examples') }}</div>
|
|
||||||
<div class="collect-quotes-explain__example-subtitle">{{ $t('click-for-next') }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="collect-quotes-explain__example-container" @click="showNextExampleQuote">
|
|
||||||
<transition name="collect-quotes-explain-fade" mode="out-in">
|
|
||||||
<div class="collect-quotes-explain__example-text" :key="quoteNr">
|
|
||||||
{{ $t(`example-quote-${quoteNr}`) }}
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Infobox>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
quoteNr: 0,
|
|
||||||
lastUpdate: new Date().getTime(),
|
|
||||||
explainOpen: true,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
showNextExampleQuote() {
|
|
||||||
this.quoteNr = (this.quoteNr + 1) % 7
|
|
||||||
this.lastUpdate = new Date().getTime()
|
|
||||||
},
|
|
||||||
toggleOpenClose() {
|
|
||||||
this.explainOpen = !this.explainOpen
|
|
||||||
},
|
|
||||||
},
|
|
||||||
beforeMount() {
|
|
||||||
this.$i18n.map({
|
|
||||||
'explain-game-closed': { de: 'Erklärung zum Spiel', en: 'about the game' },
|
|
||||||
'explain-game-p-1': {
|
|
||||||
de: 'Das Spiel besteht aus zwei Phasen. In der ersten Phase (in der wir uns gerade befinden) werden von den Mitspielenden Aussagen gesammelt. In der zweiten Phase versucht man, die Aussagen den richtigen Personen zuzuordnen.',
|
|
||||||
en: 'The game has two phases. During the first phase (in which we are currently), all players are asked to enter statements about themselves. During the second phase, players try to assign the statements to the right source.'
|
|
||||||
},
|
|
||||||
'explain-game-p-2': { de: 'Schreibe in 3-5 einzelnen Aussagen etwas über dich.', en: 'Please enter 3-5 statements about yourself.' },
|
|
||||||
'examples': { de: 'Beispiele', en: 'Examples' },
|
|
||||||
'click-for-next': { de: ' (anklicken für nächstes)', en: '(click to proceed)' },
|
|
||||||
'example-quote-0': {
|
|
||||||
de: 'Um mir mein Studium zu finanzieren habe ich den Taxischein gemacht. Ich bin jedoch nie gefahren.',
|
|
||||||
en: 'To raise money as a student, I did my taxi license. But I never actually worked as a taxi driver.',
|
|
||||||
},
|
|
||||||
'example-quote-1': {
|
|
||||||
de: 'Ich mag jede Nudelsorte ausser Spaghetti, die kann ich nicht ausstehen.',
|
|
||||||
en: 'I love all kinds of pasta but I can\'t stand spaghetti.',
|
|
||||||
},
|
|
||||||
'example-quote-2': {
|
|
||||||
de: 'Etwa 5 Jahre lang habe ich Kontrabass im Jugendorchester gespielt.',
|
|
||||||
en: 'For about 5 years, I\'ve been playing double bass in youth orchestra.',
|
|
||||||
},
|
|
||||||
'example-quote-3': {
|
|
||||||
de: 'Zuerst wollte ich Baugestaltung und Architekturgeschichte studieren. Der Studiengang war aber so voll, dass ich mich nach nur drei Vorlesungen umentschieden und statt dessen Informatik studiert habe.',
|
|
||||||
en: 'I started out to study building design and architectural history. But the course was so full that I switched to computer science instead.',
|
|
||||||
},
|
|
||||||
'example-quote-4': {
|
|
||||||
de: 'Nach dem Abi habe ich fast ein Jahr lang in einer Tierarztpraxis gejobbt.',
|
|
||||||
en: 'After highschool I had a temporary job at a veterinary clinic for almost a year.',
|
|
||||||
},
|
|
||||||
'example-quote-5': {
|
|
||||||
de: 'Ich habe drei Mal meinem/meiner Partner:in zuliebe angefangen, "Herr der Ringe" zu schauen und bin jedes Mal dabei eingeschlafen.',
|
|
||||||
en: 'To please my friend, I tried to watch \'Lord of the rings\' three times but everytime I fell asleep.',
|
|
||||||
},
|
|
||||||
'example-quote-6': {
|
|
||||||
de: 'Ich habe vier Meerschweinchen: Tick, Trick, Track und Alfred.',
|
|
||||||
en: 'I\'ve got four Guinea pigs: Huey, Dewey, Louie and Alfred.',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
this.quoteNr = Math.floor(Math.random() * 7),
|
|
||||||
this.timer = window.setInterval(function() {
|
|
||||||
if (new Date().getTime() > this.lastUpdate + 15000) {
|
|
||||||
this.showNextExampleQuote()
|
|
||||||
}
|
|
||||||
}.bind(this), 2000)
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
window.clearInterval(this.timer)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.collect-quotes-explain {
|
|
||||||
&__open-close-toggle {
|
|
||||||
float: right;
|
|
||||||
margin: 16px -16px 16px 16px;
|
|
||||||
padding: 12px;
|
|
||||||
border: 1px solid #ffffff;
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: $primary-background-color;
|
|
||||||
font-family: "$primary-font";
|
|
||||||
color: #ffffff;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: $primary-box-background-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__explain {
|
|
||||||
margin-bottom: 40px;
|
|
||||||
font-size: 24px;
|
|
||||||
font-family: $secondary-font;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__example {
|
|
||||||
font-family: $secondary-font;
|
|
||||||
color: #ffffff;
|
|
||||||
&-title {
|
|
||||||
font-size: 24px;
|
|
||||||
}
|
|
||||||
&-subtitle {
|
|
||||||
font-size: 16px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-container {
|
|
||||||
height: 8em;
|
|
||||||
}
|
|
||||||
&-text {
|
|
||||||
font-family: $secondary-font;
|
|
||||||
font-size: 24px;
|
|
||||||
color: #ffffff;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
content: '„';
|
|
||||||
}
|
|
||||||
&:after {
|
|
||||||
content: '“';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.collect-quotes-explain-fade-enter-active,
|
|
||||||
.collect-quotes-explain-fade-leave-active {
|
|
||||||
transition: all 0.4s ease;
|
|
||||||
}
|
|
||||||
.collect-quotes-explain-fade-enter,
|
|
||||||
.collect-quotes-explain-fade-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,73 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="confirm-button">
|
|
||||||
<div class="confirm-button__content" @click="click">
|
|
||||||
<div v-if="hasSelection" class="confirm-button__content__has-selection">
|
|
||||||
{{ $t('save-selection') }}
|
|
||||||
</div>
|
|
||||||
<div v-else class="confirm-button__content__no-selection">
|
|
||||||
{{ $t('skip-round') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
beforeMount() {
|
|
||||||
this.$i18n.map({
|
|
||||||
'save-selection': { de: 'Speichern', en: 'save' },
|
|
||||||
'skip-round': { de: 'überspringen' ,en: 'skip round' },
|
|
||||||
})
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
hasSelection() {
|
|
||||||
return Object.keys(this.$store.state.selection.selection).length > 0
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
click() {
|
|
||||||
const { selection } = this.$store.state.selection
|
|
||||||
this.$engine.saveSelection(Object.keys(selection))
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.confirm-button {
|
|
||||||
position: absolute;
|
|
||||||
right: 32px;
|
|
||||||
bottom: 5%;
|
|
||||||
|
|
||||||
&__content {
|
|
||||||
width: 100px;
|
|
||||||
height: 32px;
|
|
||||||
padding: 4px 36px;
|
|
||||||
background-color: $button-background-color;
|
|
||||||
border: 4px solid $button-border-color;
|
|
||||||
border-radius: 8px;
|
|
||||||
color: $button-text-color;
|
|
||||||
font-family: $secondary-font;
|
|
||||||
font-weight: 800;
|
|
||||||
text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&__has-selection {
|
|
||||||
font-size: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__no-selection {
|
|
||||||
padding: 3px 0;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: $button-hover-background-color;
|
|
||||||
border-color: $button-hover-border-color;
|
|
||||||
color: $button-hover-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,198 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="create-team">
|
|
||||||
<Button v-if="!showModal" :border="false" @click="openModal">
|
|
||||||
{{ $t('create-team') }}
|
|
||||||
</Button>
|
|
||||||
<!-- <div v-if="!showModal" class="create-team__button" @click="openModal">
|
|
||||||
{{ $t('create-team') }}
|
|
||||||
</div> -->
|
|
||||||
<template v-if="showModal">
|
|
||||||
<div class="create-team__backdrop" />
|
|
||||||
<div class="create-team__modal">
|
|
||||||
<template v-if="authcode">
|
|
||||||
<div class="create-team__modal-success-message">
|
|
||||||
<p>{{ $t('your-pin-is') }}</p>
|
|
||||||
<div class="create-team__modal-pin">
|
|
||||||
{{ authcode}}
|
|
||||||
</div>
|
|
||||||
<p>{{ $t('remember-pin-and-log-in') }}</p>
|
|
||||||
</div>
|
|
||||||
<PlayButton />
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<div class="create-team__modal-content">
|
|
||||||
<div class="create-team__modal-close" @click="closeModal" />
|
|
||||||
<p>{{ $t('create-team-explain') }}</p>
|
|
||||||
<table class="create-team__modal-content-table">
|
|
||||||
<tr>
|
|
||||||
<td>{{ $t('your-name') }}</td>
|
|
||||||
<td><input v-model="name" size="16" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ $t('team-name') }}</td>
|
|
||||||
<td><input v-model="teamname" size="16" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ $t('lang') }}</td>
|
|
||||||
<td>
|
|
||||||
<select v-model="lang">
|
|
||||||
<option value="de">de</option>
|
|
||||||
<option value="en">en</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
class="create-team__modal-cta"
|
|
||||||
:disabled="name.length == 0 || teamname.length == 0"
|
|
||||||
@click="createTeam"
|
|
||||||
>
|
|
||||||
{{ $t('create-team') }}
|
|
||||||
</Button>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
beforeMount() {
|
|
||||||
this.$i18n.map({
|
|
||||||
'create-team': { de: 'Team erstellen', en: 'create team' },
|
|
||||||
'your-name': { de: 'Dein Name', en: 'your name' },
|
|
||||||
'team-name': { de: 'Teamname', en: 'team name' },
|
|
||||||
'create-team-explain': {
|
|
||||||
de: 'Erstellt eine neues Team und einen ersten Useraccount für Dich als Gamemaster.',
|
|
||||||
en: 'Creates a new team and a first user account for you as a gamemaster.',
|
|
||||||
},
|
|
||||||
'lang': { de: 'Sprache', en: 'language' },
|
|
||||||
'your-pin-is': { de: 'Deine PIN lautet:', en: 'your pin code is:' },
|
|
||||||
'remember-pin-and-log-in': {
|
|
||||||
de: 'Schreibe sie Dir am besten gleich auf und logge dich anschließend damit ein.',
|
|
||||||
en: 'Write it down now, then log in with your pin code.',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
let lang = navigator.language ? navigator.language.substr(0, 2) : ''
|
|
||||||
if (lang != 'de' && lang != 'en') {
|
|
||||||
lang = 'en'
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
name: '',
|
|
||||||
lang,
|
|
||||||
teamname: '',
|
|
||||||
authcode: '',
|
|
||||||
showModal: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
openModal() {
|
|
||||||
this.showModal = true
|
|
||||||
this.authcode = ''
|
|
||||||
},
|
|
||||||
closeModal() {
|
|
||||||
this.showModal = false
|
|
||||||
},
|
|
||||||
async createTeam() {
|
|
||||||
this.showModal = false
|
|
||||||
const user = await this.$engine.createTeam(this.name, this.teamname, this.lang)
|
|
||||||
this.showModal = true
|
|
||||||
this.authcode = user.data.authcode
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.create-team {
|
|
||||||
&__backdrop {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 15;
|
|
||||||
background-color: $backdrop-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__button {
|
|
||||||
display: inline;
|
|
||||||
font-family: $secondary-font;
|
|
||||||
font-weight: 800;
|
|
||||||
font-size: 24px;
|
|
||||||
color: $button-secondary-text-color;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $button-secondary-hover-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__modal {
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 10%;
|
|
||||||
margin-left: -300px;
|
|
||||||
width: 600px;
|
|
||||||
height: 400px;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
z-index: 16;
|
|
||||||
border: 1px solid $primary-box-border-color;
|
|
||||||
border-radius: 20px;
|
|
||||||
background-color: $primary-box-background-color;
|
|
||||||
font-family: $secondary-font;
|
|
||||||
font-size: 20px;
|
|
||||||
color: #ffffff;
|
|
||||||
|
|
||||||
&-content {
|
|
||||||
padding: 0 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-content-table {
|
|
||||||
margin: 36px 0 0 -12px;
|
|
||||||
border-collapse: separate;
|
|
||||||
border-spacing: 12px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-cta {
|
|
||||||
margin: auto 24px 24px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-success-message {
|
|
||||||
font-size: 24px;
|
|
||||||
text-align: center;
|
|
||||||
margin: 1em 2em 2em 2em;
|
|
||||||
}
|
|
||||||
&-pin {
|
|
||||||
font-size: 32px;
|
|
||||||
font-weight: 800;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-close {
|
|
||||||
float: right;
|
|
||||||
margin: 16px -16px 16px 16px;
|
|
||||||
padding: 12px;
|
|
||||||
border: 1px solid #ffffff;
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: $primary-background-color;
|
|
||||||
font-family: "$primary-font";
|
|
||||||
color: #ffffff;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: $primary-box-background-color;
|
|
||||||
}
|
|
||||||
&::after {
|
|
||||||
content: 'x';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,35 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="enginedebug">
|
|
||||||
<pre class="enginedebug__json">{{ userJson }}</pre>
|
|
||||||
<pre class="enginedebug__json">{{ engineJson }}</pre>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
computed: {
|
|
||||||
userJson() {
|
|
||||||
return JSON.stringify(this.$store.state.engine.user, null, 2)
|
|
||||||
},
|
|
||||||
engineJson() {
|
|
||||||
return JSON.stringify(this.$store.state.engine.json, null, 2)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.enginedebug {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(0, 32, 64, 0.75);
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
&__json {
|
|
||||||
color: #808080;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,172 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="final">
|
|
||||||
<div class="final__container">
|
|
||||||
<div class="final__table-outer-border">
|
|
||||||
<div class="final__table-head">
|
|
||||||
<div class="final__title">{{ title }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="final__table-body">
|
|
||||||
<table class="final__table">
|
|
||||||
<tr class="final__table-row" v-for="player in players" :key="player.id">
|
|
||||||
<td class="final__table-col-name">{{ player.name }}</td>
|
|
||||||
<td class="final__table-col-score">{{ player.score }}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<hr class="final__progress" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
computed: {
|
|
||||||
title() {
|
|
||||||
return this.$store.state.game.name
|
|
||||||
},
|
|
||||||
players() {
|
|
||||||
var players = [...this.$store.state.players.players]
|
|
||||||
players.sort((a, b) => {
|
|
||||||
return b.score - a.score
|
|
||||||
})
|
|
||||||
return players
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.final {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
&__container {
|
|
||||||
margin: 64px 0;
|
|
||||||
width: 600px;
|
|
||||||
min-height: 600px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__table-outer-border {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding: 4px;
|
|
||||||
border-radius: 32px;
|
|
||||||
background: linear-gradient(45deg, $primary-box-border-color 0, $primary-box-border-color 68%, #ffffff 70%, $primary-box-border-color 72%, $primary-box-border-color 100%);
|
|
||||||
background-size: 1000% 1000%;
|
|
||||||
animation: finals-gradient 10s linear infinite 5s;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(45deg, rgba(128, 96, 192, 0) 50%, $primary-background-color 60%);
|
|
||||||
background-size: 1000% 1000%;
|
|
||||||
animation: finals-gradient__fade-in 5s ease forwards;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__table-head {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
height: 100px;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background-color: $primary-box-background-color;
|
|
||||||
border-radius: 32px 32px 0 0;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__title {
|
|
||||||
color: $primary-box-text-color;
|
|
||||||
font-family: "$primary-font";
|
|
||||||
font-size: 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__table-body {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: $primary-box-background-color;
|
|
||||||
border-radius: 0 0 32px 32px;
|
|
||||||
color: $primary-box-text-color;
|
|
||||||
font-family: $secondary-font;
|
|
||||||
font-size: 32px;
|
|
||||||
|
|
||||||
}
|
|
||||||
&__table {
|
|
||||||
width: 100%;
|
|
||||||
padding: 64px;
|
|
||||||
|
|
||||||
&-row {
|
|
||||||
opacity: 0;
|
|
||||||
animation: finals-text 1s ease forwards;
|
|
||||||
&:nth-child(1) { animation-delay: 5.5s; }
|
|
||||||
&:nth-child(2) { animation-delay: 4.5s; }
|
|
||||||
&:nth-child(3) { animation-delay: 3.5s; }
|
|
||||||
&:nth-child(4) { animation-delay: 2.4s; }
|
|
||||||
&:nth-child(5) { animation-delay: 2.2s; }
|
|
||||||
&:nth-child(6) { animation-delay: 2s; }
|
|
||||||
&:nth-child(7) { animation-delay: 1.9s; }
|
|
||||||
&:nth-child(8) { animation-delay: 1.8s; }
|
|
||||||
&:nth-child(9) { animation-delay: 1.7s; }
|
|
||||||
&:nth-child(10) { animation-delay: 1.6s; }
|
|
||||||
&:nth-child(11) { animation-delay: 1.5s; }
|
|
||||||
&:nth-child(12) { animation-delay: 1.4s; }
|
|
||||||
&:nth-child(13) { animation-delay: 1.3s; }
|
|
||||||
&:nth-child(14) { animation-delay: 1.2s; }
|
|
||||||
&:nth-child(15) { animation-delay: 1.1s; }
|
|
||||||
&:nth-child(16) { animation-delay: 1s; }
|
|
||||||
}
|
|
||||||
|
|
||||||
&-col-name {
|
|
||||||
max-width: 300px;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-col-score {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__progress {
|
|
||||||
opacity: 0;
|
|
||||||
color: #ffffff;
|
|
||||||
animation: finals-progress 4s ease forwards;
|
|
||||||
animation-delay: 2s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes finals-gradient__fade-in {
|
|
||||||
0% { background-position: 100% 50%; }
|
|
||||||
100% { background-position: 0% 50%; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes finals-gradient {
|
|
||||||
0% { background-position: 130% 50%; }
|
|
||||||
10% { background-position: 130% 50%; }
|
|
||||||
90% { background-position: 0% 50%; }
|
|
||||||
100% { background-position: 0% 50%; }
|
|
||||||
}
|
|
||||||
@keyframes finals-text {
|
|
||||||
0% { opacity: 0; }
|
|
||||||
100% { opacity: 1; }
|
|
||||||
}
|
|
||||||
@keyframes finals-progress {
|
|
||||||
0% { margin-left: 50%; margin-right: 50%; opacity: 0.5; }
|
|
||||||
85% { margin-left: 20px; margin-right: 20px; opacity: 0.5; }
|
|
||||||
100% { margin-left: 20px; margin-right: 20px; opacity: 0; }
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,106 +0,0 @@
|
|||||||
<template>
|
|
||||||
<nav v-if="isGamemaster || isAdmin" class="gamecontrols">
|
|
||||||
<template v-if="isGamemaster">
|
|
||||||
<template v-if="$route.path != '/gamemaster'">
|
|
||||||
<button @click="go('/gamemaster')">admin interface</button>
|
|
||||||
<button :disabled="buttonsDisabled.collect" @click="collectQuotes">Collect Quotes</button>
|
|
||||||
<button :disabled="buttonsDisabled.start" @click="startGame">Start</button>
|
|
||||||
<button :disabled="buttonsDisabled.continue" @click="continueGame">Continue</button>
|
|
||||||
<button :disabled="buttonsDisabled.idle" @click="resetGame">Idle</button>
|
|
||||||
<button :disabled="buttonsDisabled.finish" @click="finishGame">Finish Game</button>
|
|
||||||
</template>
|
|
||||||
<template v-if="$route.path != '/play'">
|
|
||||||
<button @click="go('/play')">back to game</button>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
<template v-if="isAdmin">
|
|
||||||
<template v-if="$route.path != '/admin'">
|
|
||||||
<button @click="go('/admin')">admin interface</button>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
<button class="button-logout" @click="logout">logout</button>
|
|
||||||
</nav>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
computed: {
|
|
||||||
user() {
|
|
||||||
return this.$store.state.engine.user || {}
|
|
||||||
},
|
|
||||||
isGamemaster() {
|
|
||||||
const user = this.$store.state.engine.user
|
|
||||||
return user && user.role === 'gamemaster'
|
|
||||||
},
|
|
||||||
isAdmin() {
|
|
||||||
const user = this.$store.state.engine.user
|
|
||||||
return user && user.role === 'admin'
|
|
||||||
},
|
|
||||||
buttonsDisabled() {
|
|
||||||
const { state, phase } = this.$store.state.game
|
|
||||||
return {
|
|
||||||
collect: state !== 'idle',
|
|
||||||
start: state !== 'idle',
|
|
||||||
continue: state !== 'play' && ['select-quote', 'reveal-show-count', 'reveal-source'].indexOf(phase) == -1,
|
|
||||||
idle: state === 'idle',
|
|
||||||
finish: ['play', 'idle'].indexOf(state) == -1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
collectQuotes() {
|
|
||||||
this.$engine.collectQuotes()
|
|
||||||
},
|
|
||||||
startGame() {
|
|
||||||
this.$engine.startGame()
|
|
||||||
},
|
|
||||||
resetGame() {
|
|
||||||
this.$engine.resetGame()
|
|
||||||
},
|
|
||||||
continueGame() {
|
|
||||||
this.$engine.continueGame()
|
|
||||||
},
|
|
||||||
finishGame() {
|
|
||||||
this.$engine.finishGame()
|
|
||||||
},
|
|
||||||
go(path) {
|
|
||||||
this.$router.push({ path })
|
|
||||||
},
|
|
||||||
async logout() {
|
|
||||||
const user = this.$store.state.engine.user
|
|
||||||
if (user && user.isCameo) {
|
|
||||||
await this.$axios.$get('/api/cameo')
|
|
||||||
this.go('/admin')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.authCode = ''
|
|
||||||
await this.$axios.$get('/api/logout')
|
|
||||||
this.go('/')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.gamecontrols {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
padding: 8px 1em;
|
|
||||||
border-bottom: 1px solid $primary-box-border-color;
|
|
||||||
|
|
||||||
button {
|
|
||||||
font-family: $secondary-font;
|
|
||||||
margin: 0 2px;
|
|
||||||
padding: 0 1rem;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-logout {
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,21 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="infobox">
|
|
||||||
<div class="infobox__container">
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.infobox {
|
|
||||||
&__container {
|
|
||||||
margin: 20px 80px;
|
|
||||||
padding: 0 40px;
|
|
||||||
border: 1px solid white;
|
|
||||||
border-radius: 20px;
|
|
||||||
background-color: $primary-box-background-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,59 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="lobby">
|
|
||||||
<div class="lobby__message">
|
|
||||||
<div class="lobby__teamname">{{ teamname }}</div>
|
|
||||||
{{ $t('waiting-for-gamemaster') }}
|
|
||||||
</div>
|
|
||||||
<div class="lobby__players">
|
|
||||||
<PlayerList :players="players" :hide-scores="true" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
beforeMount() {
|
|
||||||
this.$i18n.map({
|
|
||||||
'waiting-for-gamemaster': {
|
|
||||||
de: 'bitte warten, bis das Spiel vom Gamemaster gestartet wird ...',
|
|
||||||
en: 'waiting for gamemaster to start game ...',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
teamname() {
|
|
||||||
return this.$store.state.game.name
|
|
||||||
},
|
|
||||||
players() {
|
|
||||||
return this.$store.state.players.players
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.lobby {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
&__message {
|
|
||||||
flex-grow: 1;
|
|
||||||
margin-top: 60px;
|
|
||||||
text-align: center;
|
|
||||||
font-family: $secondary-font;
|
|
||||||
font-size: 24px;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
&__teamname {
|
|
||||||
font-size: 36px;
|
|
||||||
margin-bottom: 36px;
|
|
||||||
}
|
|
||||||
&__players {
|
|
||||||
width: 200px;
|
|
||||||
margin: 16px 16px 0 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,30 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="no-quotes-yet">
|
|
||||||
{{ $t('click-to-add-first-quote') }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
beforeMount() {
|
|
||||||
this.$i18n.map({
|
|
||||||
'click-to-add-first-quote': {
|
|
||||||
de: 'Klicke, um deine erste Aussage zu hinterlegen.',
|
|
||||||
en: 'Click to add your first statement.',
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.no-quotes-yet {
|
|
||||||
width: 100%;
|
|
||||||
font-family: $secondary-font;
|
|
||||||
font-size: 18px;
|
|
||||||
text-align: center;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,79 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="play">
|
|
||||||
<div :class="['play__layout', { 'play__layout__fade-out': fadeOut }]">
|
|
||||||
<div class="play__layout-playground">
|
|
||||||
<QuoteContainer :text="quote" />
|
|
||||||
<Sources :sources="sources" :selectable="selectable" />
|
|
||||||
</div>
|
|
||||||
<div class="play__layout-right-column">
|
|
||||||
<PlayerList :players="players" />
|
|
||||||
<ConfirmButton v-if="selectable"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
computed: {
|
|
||||||
fadeOut() {
|
|
||||||
return this.$store.state.game.phase === 'round-end'
|
|
||||||
},
|
|
||||||
gamePhase() {
|
|
||||||
return this.$store.state.game.phase
|
|
||||||
},
|
|
||||||
quote() {
|
|
||||||
return this.$store.state.round.quote
|
|
||||||
},
|
|
||||||
sources() {
|
|
||||||
return this.$store.state.round.sources
|
|
||||||
},
|
|
||||||
players() {
|
|
||||||
return this.$store.state.players.players
|
|
||||||
},
|
|
||||||
selectable() {
|
|
||||||
const userId = this.$store.state.engine.user.id
|
|
||||||
const selection = this.$store.state.round.selections[userId]
|
|
||||||
|
|
||||||
if (this.$store.state.game.phase != 'select-quote') return false
|
|
||||||
if (typeof selection === 'undefined') return true
|
|
||||||
return !selection
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.play {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
&__layout {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
perspective: 1200px;
|
|
||||||
|
|
||||||
&-playground {
|
|
||||||
position: relative;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-right-column {
|
|
||||||
width: 200px;
|
|
||||||
margin: 16px 16px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__fade-out {
|
|
||||||
animation: play-fade-out 0.7s linear;
|
|
||||||
animation-fill-mode: forwards;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes play-fade-out {
|
|
||||||
to { opacity: 0; }
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,150 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div v-if="!$fetchState.pending" class="playbutton">
|
|
||||||
<template v-if="user && user.name">
|
|
||||||
<div class="playbutton__greeting">
|
|
||||||
{{ $t('hi') }}
|
|
||||||
{{ user.name }}!
|
|
||||||
</div>
|
|
||||||
<Button @click="$router.push('/play')">{{ $t('play-button') }}</Button>
|
|
||||||
<div class="playbutton__logout" @click="logout">
|
|
||||||
{{ $t('logout') }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<input
|
|
||||||
v-model="authCode"
|
|
||||||
class="playbutton__authinput"
|
|
||||||
type="text"
|
|
||||||
size="6"
|
|
||||||
maxlength="6"
|
|
||||||
:placeholder="$t('auth-code')"
|
|
||||||
/>
|
|
||||||
<Button :disabled="loginDisabled" @click="login">{{ $t('login-go') }}</Button>
|
|
||||||
<div class="playbutton__errormessage" v-if="errorMessage">
|
|
||||||
{{ errorMessage }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
beforeMount() {
|
|
||||||
this.$i18n.map({
|
|
||||||
'hi': { de: 'Hallo', en: 'Hi' },
|
|
||||||
'auth-code': { de: 'Code', en: 'code' },
|
|
||||||
'login-go': { de: 'Los', en: 'Go' },
|
|
||||||
'play-button': { de: 'Spielen!', en: 'Play!' },
|
|
||||||
'logout': { de: 'Ausloggen/Sitzung wechseln', en: 'logout/switch session' },
|
|
||||||
})
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
authCode: '',
|
|
||||||
errorMessage: '',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async fetch() {
|
|
||||||
this.$engine.fetchUserInfo()
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
loginDisabled() {
|
|
||||||
return this.authCode.length < 6
|
|
||||||
},
|
|
||||||
user() {
|
|
||||||
return this.$store.state.engine.user
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async login(e) {
|
|
||||||
try {
|
|
||||||
await this.$axios.$get(`/api/login?code=${this.authCode}`)
|
|
||||||
this.$fetch()
|
|
||||||
} catch(e) {
|
|
||||||
this.errorMessage = 'login failed'
|
|
||||||
}
|
|
||||||
this.authCode = ''
|
|
||||||
},
|
|
||||||
async logout() {
|
|
||||||
this.authCode = ''
|
|
||||||
try {
|
|
||||||
await this.$axios.$get('/api/logout')
|
|
||||||
window.location.reload()
|
|
||||||
} catch(e) {
|
|
||||||
this.errorMessage = 'logout failed'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.playbutton {
|
|
||||||
font-family: $secondary-font;
|
|
||||||
font-weight: 800;
|
|
||||||
font-size: 36px;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
&__greeting {
|
|
||||||
color: $button-text-color;
|
|
||||||
font-size: 18px;
|
|
||||||
margin: 0 0 1em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__playbutton {
|
|
||||||
width: 200px;
|
|
||||||
height: 48px;
|
|
||||||
padding: 4px 36px;
|
|
||||||
border: 4px solid $button-border-color;
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: $button-background-color;
|
|
||||||
color: $button-text-color;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: $button-hover-background-color;
|
|
||||||
border-color: $button-hover-border-color;
|
|
||||||
color: $button-hover-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__logout {
|
|
||||||
margin-top: 64px;
|
|
||||||
font-size: 24px;
|
|
||||||
color: $button-secondary-text-color;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $button-secondary-hover-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__authinput {
|
|
||||||
display: inline;
|
|
||||||
height: 36px;
|
|
||||||
border: 1px solid $input-inactive-border-color;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-family: $secondary-font;
|
|
||||||
font-weight: 800;
|
|
||||||
font-size: 24px;
|
|
||||||
background-color: $input-inactive-background-color;
|
|
||||||
color: $input-inactive-text-color;
|
|
||||||
outline: none;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
border: 1px solid $input-border-color;
|
|
||||||
background-color: $input-background-color;
|
|
||||||
color: $input-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__errormessage {
|
|
||||||
padding: 8px;
|
|
||||||
font-size: 18px;
|
|
||||||
color: $error-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,62 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="player">
|
|
||||||
<div :class="['player__row', { 'player__row__idle': this.player.isIdle }]">
|
|
||||||
<div class="player__status">
|
|
||||||
<span v-if="gamePhase === 'select-quote'">{{ hasSelected ? '☑' : '☐' }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="player__name">
|
|
||||||
{{ player.name }}
|
|
||||||
</div>
|
|
||||||
<div v-if="!hideScore" class="player__score">
|
|
||||||
{{ player.score || 0 }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: ['player', 'hideScore'],
|
|
||||||
computed: {
|
|
||||||
gamePhase() {
|
|
||||||
return this.$store.state.game.phase
|
|
||||||
},
|
|
||||||
hasSelected() {
|
|
||||||
return this.$store.state.round.selections[this.player.id]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.player {
|
|
||||||
&__row {
|
|
||||||
display: flex;
|
|
||||||
color: $secondary-box-text-color;
|
|
||||||
font-family: $secondary-font;
|
|
||||||
font-size: 18px;
|
|
||||||
|
|
||||||
&__idle {
|
|
||||||
color: $secondary-box-disabled-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__name {
|
|
||||||
display: inline;
|
|
||||||
flex-grow: 1;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
&__status {
|
|
||||||
width: 20px;
|
|
||||||
}
|
|
||||||
&__score {
|
|
||||||
width: 32px;
|
|
||||||
margin-left: 16px;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,24 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="player-list">
|
|
||||||
<div v-for="player in players" :key="player.id">
|
|
||||||
<Player :player="player" :hide-score="hideScores" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: ['players', 'hideScores'],
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.player-list {
|
|
||||||
padding: 8px;
|
|
||||||
border: 1px solid $secondary-box-border-color;
|
|
||||||
border-radius: 10px;
|
|
||||||
background-color: $secondary-box-background-color;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,31 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="quote" v-if="text">
|
|
||||||
<div class="quote__quote">{{ text }}</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: ['text'],
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.quote {
|
|
||||||
&__quote {
|
|
||||||
text-align: justify;
|
|
||||||
font-family: $secondary-font;
|
|
||||||
font-size: 24px;
|
|
||||||
color: $primary-text-color;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
content: '„';
|
|
||||||
}
|
|
||||||
&:after {
|
|
||||||
content: '“';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,25 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="quote-container">
|
|
||||||
<Quote :text="text" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: ['text'],
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.quote-container {
|
|
||||||
display: flex;
|
|
||||||
position: absolute;
|
|
||||||
left: 35%;
|
|
||||||
top: 35%;
|
|
||||||
width: 30%;
|
|
||||||
height: 30%;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,82 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="quote-list-item">
|
|
||||||
<div class="quote-list-item__separator" />
|
|
||||||
<div class="quote-list-item__container">
|
|
||||||
<div class="quote-list-item__quote">
|
|
||||||
{{ quote.quote }}
|
|
||||||
</div>
|
|
||||||
<div class="quote-list-item__actions">
|
|
||||||
<div class="quote-list-item__icon quote-list-item__icon-edit" @click="edit" />
|
|
||||||
<div class="quote-list-item__icon quote-list-item__icon-delete" @click="remove" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: ['quote'],
|
|
||||||
methods: {
|
|
||||||
async edit() {
|
|
||||||
this.$emit('editQuote', this.quote)
|
|
||||||
},
|
|
||||||
async remove() {
|
|
||||||
if (confirm('Eintrag wirklich löschen?')) {
|
|
||||||
this.$engine.removeQuote(this.quote.id)
|
|
||||||
await this.$engine.getMyQuotes()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.quote-list-item__container ~ .quote-list-item__container {
|
|
||||||
}
|
|
||||||
.quote-list-item {
|
|
||||||
& ~ & &__separator {
|
|
||||||
height: 1px;
|
|
||||||
margin: 0 40%;
|
|
||||||
background-color: $primary-text-color;
|
|
||||||
}
|
|
||||||
&__container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 48px 32px;
|
|
||||||
}
|
|
||||||
&__quote {
|
|
||||||
color: $primary-text-color;
|
|
||||||
}
|
|
||||||
&__actions {
|
|
||||||
display: flex;
|
|
||||||
margin: 0 32px;
|
|
||||||
}
|
|
||||||
&__icon {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
margin-left: 16px;
|
|
||||||
background-color: $secondary-box-background-color;
|
|
||||||
border: 1px solid $secondary-box-border-color;
|
|
||||||
border-radius: 8px;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 48px;
|
|
||||||
font-size: 32px;
|
|
||||||
color: $secondary-box-text-color;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: $secondary-box-hover-background-color;
|
|
||||||
border-color: $secondary-box-hover-border-color;
|
|
||||||
color: $secondary-box-hover-text-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-edit::after {
|
|
||||||
content: '✎';
|
|
||||||
}
|
|
||||||
&-delete::after {
|
|
||||||
content: '🗑';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,139 +0,0 @@
|
|||||||
fade-out<template>
|
|
||||||
<div class="ready-set">
|
|
||||||
<div :class="['ready-set__container', fadeOut ]">
|
|
||||||
<div class="ready-set__box1" />
|
|
||||||
<div class="ready-set__box2" />
|
|
||||||
<div class="ready-set__box3" />
|
|
||||||
<div
|
|
||||||
:class="['ready-set__text', 'ready-set__text__popup', textSize]"
|
|
||||||
:key="text"
|
|
||||||
>
|
|
||||||
{{ text }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: ['text'],
|
|
||||||
computed: {
|
|
||||||
fadeOut() {
|
|
||||||
if (this.text === '' ){
|
|
||||||
return 'ready-set__container__fade-out'
|
|
||||||
}
|
|
||||||
return
|
|
||||||
},
|
|
||||||
textSize() {
|
|
||||||
if (this.text.length < 3) {
|
|
||||||
return 'ready-set__text__size-big'
|
|
||||||
} else if (this.text.length < 5) {
|
|
||||||
return 'ready-set__text__size-medium'
|
|
||||||
} else if (this.text.length > 8) {
|
|
||||||
return 'ready-set__text__size-small'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.ready-set {
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
&__container {
|
|
||||||
position: relative;
|
|
||||||
width: 600px;
|
|
||||||
height: 600px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
&__fade-out {
|
|
||||||
animation: fade-out 0.3s linear;
|
|
||||||
animation-fill-mode: forwards;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__box1 {
|
|
||||||
position: absolute;
|
|
||||||
left: 100px;
|
|
||||||
top: 100px;
|
|
||||||
transform: rotate(45deg);
|
|
||||||
width: 370px;
|
|
||||||
height: 370px;
|
|
||||||
background-color: $primary-box-background-color;
|
|
||||||
border: 15px solid $primary-box-border-color;
|
|
||||||
border-radius: 50px;
|
|
||||||
z-index: 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__box2 {
|
|
||||||
position: absolute;
|
|
||||||
left: 90px;
|
|
||||||
top: 90px;
|
|
||||||
width: 420px;
|
|
||||||
height: 420px;
|
|
||||||
border-radius: 150px;
|
|
||||||
background-color: $primary-box-animation-color;
|
|
||||||
animation: spin-rev 5s linear infinite;
|
|
||||||
z-index: 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__box3 {
|
|
||||||
position: absolute;
|
|
||||||
left: 90px;
|
|
||||||
top: 90px;
|
|
||||||
width: 420px;
|
|
||||||
height: 420px;
|
|
||||||
border-radius: 150px;
|
|
||||||
background-color: $primary-box-animation-color;
|
|
||||||
z-index: 3;
|
|
||||||
animation: spin 6s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__text {
|
|
||||||
position: absolute;
|
|
||||||
width: 600px;
|
|
||||||
height: 600px;
|
|
||||||
line-height: 600px;
|
|
||||||
font-size: 100px;
|
|
||||||
font-family: '$primary-font';
|
|
||||||
color: #ffff80;
|
|
||||||
text-align: center;
|
|
||||||
z-index: 10;
|
|
||||||
|
|
||||||
&__size-small {
|
|
||||||
font-size: 64px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__size-medium {
|
|
||||||
font-size: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__size-big {
|
|
||||||
font-size: 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__popup {
|
|
||||||
animation: pop 0.5s ease-in-out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes spin { 100% { transform: rotate(360deg); } }
|
|
||||||
@keyframes spin-rev { 100% { transform: rotate(-360deg); } }
|
|
||||||
@keyframes pop {
|
|
||||||
0% { transform: scale(1.7); }
|
|
||||||
25% { transform: scale(2); }
|
|
||||||
100% { transform: scale(1); }
|
|
||||||
}
|
|
||||||
@keyframes fade-out {
|
|
||||||
25% { transform: scale(1.2); }
|
|
||||||
100% { transform: scale(0); }
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,145 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="source">
|
|
||||||
<div
|
|
||||||
:class="getCssClass"
|
|
||||||
@click="clicked"
|
|
||||||
>
|
|
||||||
<div class="source__source">
|
|
||||||
{{ source.name }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="badgeCount" class="source__badge">
|
|
||||||
<div class="source__badge-count">
|
|
||||||
{{ badgeCount }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: ['source', 'selectable'],
|
|
||||||
computed: {
|
|
||||||
getCssClass() {
|
|
||||||
return {
|
|
||||||
'source__container': true,
|
|
||||||
'source__container-selectable': this.selectable,
|
|
||||||
'source__container__selected': this.isSelected(),
|
|
||||||
'source__container__hidden': this.isWrongSource(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
badgeCount() {
|
|
||||||
if (this.isWrongSource()) return 0
|
|
||||||
|
|
||||||
const revelation = this.$store.state.round.revelation || {}
|
|
||||||
const votes = revelation.votes || {}
|
|
||||||
const list = votes[this.source.id] || []
|
|
||||||
|
|
||||||
return list.length
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
isWrongSource() {
|
|
||||||
const revelationSources = this.$store.state.round.revelation.sources || {}
|
|
||||||
const isRightOrWrong = revelationSources[this.source.id]
|
|
||||||
if (typeof isRightOrWrong === 'undefined') return false
|
|
||||||
|
|
||||||
return !isRightOrWrong
|
|
||||||
},
|
|
||||||
isSelected() {
|
|
||||||
return this.$store.state.selection.selection[this.source.id]
|
|
||||||
},
|
|
||||||
clicked() {
|
|
||||||
if (!this.selectable) return
|
|
||||||
|
|
||||||
if (this.isSelected()) {
|
|
||||||
this.$store.commit('selection/unselect', this.source.id)
|
|
||||||
} else {
|
|
||||||
this.$store.commit('selection/clearSelection')
|
|
||||||
this.$store.commit('selection/select', this.source.id)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.source {
|
|
||||||
position: absolute;
|
|
||||||
width: 180px;
|
|
||||||
height: 30px;
|
|
||||||
margin: -15px 0 0 -90px;
|
|
||||||
|
|
||||||
&__container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
max-width: 180px;
|
|
||||||
height: 1.4em;
|
|
||||||
padding: 4px;
|
|
||||||
border: 1px solid $secondary-box-border-color;
|
|
||||||
border-radius: 10px;
|
|
||||||
background-color: $secondary-box-background-color;
|
|
||||||
color: $secondary-box-text-color;
|
|
||||||
|
|
||||||
&-selectable {
|
|
||||||
&:hover {
|
|
||||||
background-color: #c0a0f0;
|
|
||||||
color: $primary-background-color;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
&.source__container__selected {
|
|
||||||
background-color: $primary-text-color;
|
|
||||||
box-shadow: 0 0 10px $primary-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__selected {
|
|
||||||
background-color: #d0d0d0;
|
|
||||||
color: $primary-background-color;
|
|
||||||
box-shadow: 0 0 10px #d0d0d0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__hidden {
|
|
||||||
animation: source-fade-out 0.7s linear;
|
|
||||||
animation-fill-mode: forwards;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__source {
|
|
||||||
width: 100%;
|
|
||||||
font-family: "$primary-font";
|
|
||||||
text-align: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__badge {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
position: absolute;
|
|
||||||
right: -12px;
|
|
||||||
top: -16px;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
background-color: $primary-box-background-color;
|
|
||||||
border: 2px solid #ffffff;
|
|
||||||
border-radius: 14px;
|
|
||||||
color: #c0c060;
|
|
||||||
font-family: "$primary-font";
|
|
||||||
font-size: 16px;
|
|
||||||
|
|
||||||
&-count {
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes source-fade-out {
|
|
||||||
to { opacity: 10%; }
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,91 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="sources">
|
|
||||||
<div :class="['sources__container', 'sources__container-' + sources.length]">
|
|
||||||
<div class="sources__container-ring">
|
|
||||||
<Source
|
|
||||||
v-for="(source, idx) in sources"
|
|
||||||
:class="['sources__source-' + idx]"
|
|
||||||
:source="source"
|
|
||||||
:selectable="selectable"
|
|
||||||
:key="source.id"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: ['sources', 'selectable'],
|
|
||||||
mounted() {
|
|
||||||
this.$store.commit('selection/clearSelection')
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.sources {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
&__container {
|
|
||||||
position: absolute;
|
|
||||||
left: 15%;
|
|
||||||
top: 10%;
|
|
||||||
width: 70%;
|
|
||||||
height: 80%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__container-ring {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__container-2,
|
|
||||||
&__container-3 {
|
|
||||||
.sources__source-0 { left: 10%; top: 10%; }
|
|
||||||
.sources__source-1 { left: 90%; top: 10%; }
|
|
||||||
.sources__source-2 { left: 50%; top: 100%; }
|
|
||||||
}
|
|
||||||
&__container-4,
|
|
||||||
&__container-5,
|
|
||||||
&__container-6 {
|
|
||||||
.sources__source-0 { left: 100%; top: 20%; }
|
|
||||||
.sources__source-1 { left: 100%; top: 80%; }
|
|
||||||
.sources__source-2 { left: 0; top: 80%; }
|
|
||||||
.sources__source-3 { left: 0; top: 20%; }
|
|
||||||
.sources__source-4 { left: 50%; top: 0; }
|
|
||||||
.sources__source-5 { left: 50%; top: 100%; }
|
|
||||||
}
|
|
||||||
&__container-7,
|
|
||||||
&__container-9 {
|
|
||||||
.sources__source-0 { left: 95%; top: 20%; }
|
|
||||||
.sources__source-1 { left: 95%; top: 80%; }
|
|
||||||
.sources__source-2 { left: 5%; top: 80%; }
|
|
||||||
.sources__source-3 { left: 5%; top: 20%; }
|
|
||||||
.sources__source-4 { left: 50%; top: 0; }
|
|
||||||
.sources__source-5 { left: 30%; top: 100%; }
|
|
||||||
.sources__source-6 { left: 70%; top: 100%; }
|
|
||||||
.sources__source-7 { left: 0%; top: 50%; }
|
|
||||||
.sources__source-8 { left: 100%; top: 50%; }
|
|
||||||
}
|
|
||||||
&__container-8,
|
|
||||||
&__container-10 {
|
|
||||||
.sources__source-0 { left: 95%; top: 25%; }
|
|
||||||
.sources__source-1 { left: 95%; top: 75%; }
|
|
||||||
.sources__source-2 { left: 5%; top: 75%; }
|
|
||||||
.sources__source-3 { left: 5%; top: 25%; }
|
|
||||||
.sources__source-4 { left: 30%; top: 0; }
|
|
||||||
.sources__source-5 { left: 70%; top: 0; }
|
|
||||||
.sources__source-6 { left: 30%; top: 100%; }
|
|
||||||
.sources__source-7 { left: 70%; top: 100%; }
|
|
||||||
.sources__source-8 { left: 0%; top: 50%; }
|
|
||||||
.sources__source-9 { left: 100%; top: 50%; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,61 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="titlebox">
|
|
||||||
<div class="titlebox__titlebox">
|
|
||||||
<div class="titlebox__titleborderbox2" />
|
|
||||||
<div class="titlebox__titleborderbox3" />
|
|
||||||
<div class="titlebox__titleborderbox1">
|
|
||||||
<h1 class="titlebox__title">
|
|
||||||
Know Your Teammates!
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.titlebox {
|
|
||||||
&__titlebox {
|
|
||||||
position: relative;
|
|
||||||
padding: 80px 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
&__titleborderbox1,
|
|
||||||
&__titleborderbox2,
|
|
||||||
&__titleborderbox3 {
|
|
||||||
border: 1px solid $primary-box-border-color;
|
|
||||||
border-radius: 16px;
|
|
||||||
background-color: $primary-box-background-color;
|
|
||||||
}
|
|
||||||
&__titleborderbox1 {
|
|
||||||
display: flex;
|
|
||||||
width: 600px;
|
|
||||||
height: 200px;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
z-index: 3;
|
|
||||||
}
|
|
||||||
&__titleborderbox2 {
|
|
||||||
position: absolute;
|
|
||||||
top: 120px;
|
|
||||||
width: 720px;
|
|
||||||
height: 120px;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
&__titleborderbox3 {
|
|
||||||
position: absolute;
|
|
||||||
top: 160px;
|
|
||||||
width: 760px;
|
|
||||||
height: 40px;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
&__title {
|
|
||||||
font-size: 64px;
|
|
||||||
font-family: $primary-font;
|
|
||||||
color: $primary-box-text-color;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,137 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="admin-edit-player">
|
|
||||||
<Infobox>
|
|
||||||
<div class="admin-edit-player__close" @click="$emit('close')" />
|
|
||||||
<table class="admin-edit-player__table">
|
|
||||||
<tr>
|
|
||||||
<td>Name: </td>
|
|
||||||
<td><input v-model="name" /></td>
|
|
||||||
<td v-if="id" class="admin-edit-player__table-cell-delete">
|
|
||||||
<span class="admin-edit-player__button-delete" @click="deletePlayer">delete player</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Score: </td>
|
|
||||||
<td><input v-model="score" /></td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Authcode: </td>
|
|
||||||
<td><input v-model="authcode" size="6" maxlength="6" /></td>
|
|
||||||
<td><span class="admin-edit-player__button-generate-code" @click="generateCode">generate code</span></td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="!showId">
|
|
||||||
<td colspan="3" @click="showId=true"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="showId">
|
|
||||||
<td>id: </td>
|
|
||||||
<td colspan="2">{{ id }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="3"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td>
|
|
||||||
<button class="admin-edit-player__button-save" @click="save">{{id ? 'save' : 'create'}}</button>
|
|
||||||
</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</Infobox>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: ['player'],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showId: false,
|
|
||||||
name: '',
|
|
||||||
score: 0,
|
|
||||||
authcode: '',
|
|
||||||
...this.player,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async save() {
|
|
||||||
await this.$engine.savePlayer({
|
|
||||||
id: this.id,
|
|
||||||
name: this.name,
|
|
||||||
score: this.score,
|
|
||||||
authcode: this.authcode,
|
|
||||||
})
|
|
||||||
this.$emit('close')
|
|
||||||
},
|
|
||||||
async deletePlayer() {
|
|
||||||
if (confirm(`"${this.name}" löschen?`)) {
|
|
||||||
await this.$engine.deletePlayer({ id: this.id })
|
|
||||||
this.$emit('close')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
generateCode() {
|
|
||||||
this.authcode = ''
|
|
||||||
for (var i = 0; i < 6; i++) {
|
|
||||||
this.authcode += '' + Math.floor(Math.floor(Math.random() * 10000) / 100) % 10
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.admin-edit-player {
|
|
||||||
font-family: $secondary-font;
|
|
||||||
&__close {
|
|
||||||
float: right;
|
|
||||||
margin: 16px -16px 16px 16px;
|
|
||||||
padding: 12px;
|
|
||||||
border: 1px solid #ffffff;
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: $primary-background-color;
|
|
||||||
font-family: "$primary-font";
|
|
||||||
color: #ffffff;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: $primary-box-background-color;
|
|
||||||
}
|
|
||||||
&::after {
|
|
||||||
content: 'x';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__table {
|
|
||||||
margin: 64px;
|
|
||||||
}
|
|
||||||
&__button-save {
|
|
||||||
padding: 0.3em 2em;
|
|
||||||
}
|
|
||||||
&__table-cell-delete {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
&__button-delete {
|
|
||||||
margin-left: 3em;
|
|
||||||
font-size: 16px;
|
|
||||||
font-family: $secondary-font;
|
|
||||||
color: #e06060;
|
|
||||||
cursor: pointer;
|
|
||||||
&:hover {
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&__button-generate-code {
|
|
||||||
margin-left: 3em;
|
|
||||||
font-size: 16px;
|
|
||||||
font-family: $secondary-font;
|
|
||||||
color: #d0d0d0;
|
|
||||||
cursor: pointer;
|
|
||||||
&:hover {
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,37 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="admin-tile">
|
|
||||||
<div v-if="title" class="admin-tile__title">
|
|
||||||
{{ title }}
|
|
||||||
<slot name="headerAction" />
|
|
||||||
</div>
|
|
||||||
<div class="admin-tile__body">
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: ['title'],
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.admin-tile {
|
|
||||||
min-width: 240px;
|
|
||||||
margin: 16px;
|
|
||||||
|
|
||||||
&__title {
|
|
||||||
font-size: 32px;
|
|
||||||
font-family: $secondary-font;
|
|
||||||
border-bottom: 1px solid #ffffff;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
&__body {
|
|
||||||
font-family: $secondary-font;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,89 +0,0 @@
|
|||||||
<template>
|
|
||||||
<AdminTile class="admin-tile-gameinfo" title="Game">
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td>Name:</td>
|
|
||||||
<td>{{ gameinfo.name }}</td>
|
|
||||||
<td><div class="admin-tile-gameinfo__edit-game-name" @click="editName()"></div></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Sprache:</td>
|
|
||||||
<td v-if="!editLangShowDropdown">{{ gameinfo.lang }}</td>
|
|
||||||
<td v-if="editLangShowDropdown">
|
|
||||||
<select v-model="lang" @change="editLangChange">
|
|
||||||
<option value="de">de</option>
|
|
||||||
<option value="en">en</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
<td><div class="admin-tile-gameinfo__edit-game-lang" @click="editLang()"></div></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Erstellt:</td>
|
|
||||||
<td colspan="2">{{ $formatter.date(gameinfo.created) }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td># Players:</td>
|
|
||||||
<td colspan="2">{{ players.length }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td># Quotes played:</td>
|
|
||||||
<td colspan="2">
|
|
||||||
{{ gameinfo.numQuotesTotal - gameinfo.numQuotesLeft }} / {{ gameinfo.numQuotesTotal }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="!showId">
|
|
||||||
<td colspan="3" @click="showId=true"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="showId">
|
|
||||||
<td colspan="3">{{ gameinfo.id }}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</AdminTile>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: ['gameinfo', 'players'],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showId: false,
|
|
||||||
lang: '---',
|
|
||||||
editLangShowDropdown: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async editName() {
|
|
||||||
const name = window.prompt('new game name: ')
|
|
||||||
if (name) {
|
|
||||||
const g = this.$store.state.engine.user.game
|
|
||||||
await this.$engine.setGameName({ g, name })
|
|
||||||
await this.$engine.fetchGameInfo({ g })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
editLang() {
|
|
||||||
this.editLangShowDropdown = true
|
|
||||||
},
|
|
||||||
async editLangChange() {
|
|
||||||
this.editLangShowDropdown = false
|
|
||||||
const g = this.$store.state.engine.user.game
|
|
||||||
await this.$engine.setGameLang({ g, lang: this.lang })
|
|
||||||
await this.$engine.fetchGameInfo({ g })
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.admin-tile-gameinfo {
|
|
||||||
&__edit-game-name,
|
|
||||||
&__edit-game-lang {
|
|
||||||
cursor: pointer;
|
|
||||||
&:hover {
|
|
||||||
color: #a0a0a0;
|
|
||||||
}
|
|
||||||
&::after {
|
|
||||||
content: '✎';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,81 +0,0 @@
|
|||||||
<template>
|
|
||||||
<AdminTile class="admin-tile-games" title="Games">
|
|
||||||
<table class="admin-tile-games__table">
|
|
||||||
<tr>
|
|
||||||
<th class="admin-tile-games__table-head">Game name</th>
|
|
||||||
<th class="admin-tile-games__table-head">Lang</th>
|
|
||||||
<th class="admin-tile-games__table-head">Status</th>
|
|
||||||
<th class="admin-tile-games__table-head"># players</th>
|
|
||||||
<th class="admin-tile-games__table-head">Gamemaster(s)</th>
|
|
||||||
</tr>
|
|
||||||
<tr
|
|
||||||
class="admin-tile-games__game"
|
|
||||||
@click="selectGame(id)"
|
|
||||||
v-for="id in Object.keys(games || {})"
|
|
||||||
:key="id"
|
|
||||||
>
|
|
||||||
<td>{{ games[id].name }}</td>
|
|
||||||
<td>{{ games[id].lang }}</td>
|
|
||||||
<td>{{ games[id].state }}</td>
|
|
||||||
<td>{{ games[id].players.length }}</td>
|
|
||||||
<td>{{ gamemasters[id] }}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</AdminTile>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: ['games'],
|
|
||||||
computed: {
|
|
||||||
gamemasters() {
|
|
||||||
const masters = {}
|
|
||||||
for (const gameId of Object.keys(this.games || {})) {
|
|
||||||
const game = this.games[gameId]
|
|
||||||
for (const player of game.players) {
|
|
||||||
if (player.role === 'gamemaster') {
|
|
||||||
if (masters[gameId]) {
|
|
||||||
masters[gameId] += ', '
|
|
||||||
masters[gameId] += player.name
|
|
||||||
} else {
|
|
||||||
masters[gameId] = player.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return masters
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async selectGame(gameId) {
|
|
||||||
const game = this.games[gameId]
|
|
||||||
for (const player of game.players) {
|
|
||||||
if (player.role === 'gamemaster') {
|
|
||||||
await this.$axios.$get(`/api/cameo?code=${player.authcode}`)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.$router.push('/gamemaster')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.admin-tile-games {
|
|
||||||
&__table {
|
|
||||||
margin-left: -12px;
|
|
||||||
border-collapse: separate;
|
|
||||||
border-spacing: 12px 0;
|
|
||||||
}
|
|
||||||
&__table-head {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
&__game {
|
|
||||||
cursor: pointer;
|
|
||||||
&:hover {
|
|
||||||
color: #a0a0a0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,20 +0,0 @@
|
|||||||
<template>
|
|
||||||
<AdminTile title="Myself">
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td>Name:</td>
|
|
||||||
<td>{{ user.name }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Role:</td>
|
|
||||||
<td>{{ user.role }}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</AdminTile>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: ['user'],
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,104 +0,0 @@
|
|||||||
<template>
|
|
||||||
<AdminTile class="admin-tile-players" title="Players">
|
|
||||||
<template v-slot:headerAction>
|
|
||||||
<span
|
|
||||||
v-if="!isReloading"
|
|
||||||
class="admin-tile-players__action-reload"
|
|
||||||
@click="reload"
|
|
||||||
>
|
|
||||||
⟳
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<table class="admin-tile-players__table">
|
|
||||||
<tr v-if="players.length">
|
|
||||||
<th class="admin-tile-players__table-head">Name</th>
|
|
||||||
<th class="admin-tile-players__table-head"># Quotes</th>
|
|
||||||
<th class="admin-tile-players__table-head">Score</th>
|
|
||||||
<th class="admin-tile-players__table-head">zuletzt eingeloggt</th>
|
|
||||||
</tr>
|
|
||||||
<tr
|
|
||||||
class="admin-tile-players__player"
|
|
||||||
@click="editPlayer(player)"
|
|
||||||
v-for="player in players"
|
|
||||||
:key="player.id"
|
|
||||||
>
|
|
||||||
<td>{{ player.name }}{{ player.role === 'gamemaster' ? ' 👑' : '' }}</td>
|
|
||||||
<td>{{ player.numQuotes }}</td>
|
|
||||||
<td>{{ player.score }}</td>
|
|
||||||
<td>{{ getPlayerStatus(player) }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="4">
|
|
||||||
<button class="admin-tile-players__add-player" @click="newPlayer()">+</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</AdminTile>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: ['gameinfo', 'players'],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
isReloading: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
editPlayer(player) {
|
|
||||||
this.$emit('edit', player)
|
|
||||||
},
|
|
||||||
newPlayer() {
|
|
||||||
this.$emit('edit', { id: null, name: '', score: 0, authcode: '' })
|
|
||||||
},
|
|
||||||
reload() {
|
|
||||||
this.$emit('reload')
|
|
||||||
this.isReloading = true
|
|
||||||
setTimeout(() => { this.isReloading = false }, 500)
|
|
||||||
},
|
|
||||||
getPlayerStatus(player) {
|
|
||||||
if (player.isPlaying && !player.isIdle) {
|
|
||||||
return 'online'
|
|
||||||
} else {
|
|
||||||
if (player.lastLoggedIn) {
|
|
||||||
return this.$formatter.datetime(player.lastLoggedIn)
|
|
||||||
} else if (player.isPlaying) {
|
|
||||||
return 'idle'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '-'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.admin-tile-players {
|
|
||||||
&__action-reload {
|
|
||||||
float: right;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #c0c0c0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&__table {
|
|
||||||
margin-left: -12px;
|
|
||||||
border-collapse: separate;
|
|
||||||
border-spacing: 12px 0;
|
|
||||||
}
|
|
||||||
&__table-head {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
&__player {
|
|
||||||
cursor: pointer;
|
|
||||||
&:hover {
|
|
||||||
color: #a0a0a0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&__add-player {
|
|
||||||
padding: 0 3em;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,15 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="layout-default">
|
|
||||||
<Nuxt />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
html,
|
|
||||||
body,
|
|
||||||
#__nuxt,
|
|
||||||
#__layout,
|
|
||||||
.layout-default {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,52 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="page-admin">
|
|
||||||
<template v-if="user.role === 'admin'">
|
|
||||||
<GameControls />
|
|
||||||
<div class="page-admin__body">
|
|
||||||
<div class="page-admin__tiles">
|
|
||||||
<AdminTileMyself :user="user" />
|
|
||||||
<AdminTileGames :games="games.games" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<p>You are not an admin.</p>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
async fetch() {
|
|
||||||
await this.$engine.fetchUserInfo()
|
|
||||||
await this.$engine.fetchGames()
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
isAdmin() {
|
|
||||||
return this.$store.state.engine.user?.role === 'admin'
|
|
||||||
},
|
|
||||||
user() {
|
|
||||||
return this.$store.state.engine.user || {}
|
|
||||||
},
|
|
||||||
games() {
|
|
||||||
return this.$store.state.engine.games || {}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.page-admin {
|
|
||||||
color: #ffffff;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
&__tiles {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
padding: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,109 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="page-gamemaster">
|
|
||||||
<template v-if="user.role === 'gamemaster'">
|
|
||||||
<GameControls />
|
|
||||||
<div class="page-gamemaster__body">
|
|
||||||
<div v-if="overlay.open" class="page-gamemaster__player-overlay">
|
|
||||||
<AdminEditPlayer :player="overlay.player" @close="editPlayerClose"/>
|
|
||||||
</div>
|
|
||||||
<div class="page-gamemaster__tiles">
|
|
||||||
<AdminTileMyself :user="user" />
|
|
||||||
<AdminTileGameinfo :gameinfo="gameinfo" :players="players" />
|
|
||||||
<AdminTilePlayers
|
|
||||||
:gameinfo="gameinfo"
|
|
||||||
:players="players"
|
|
||||||
@edit="editPlayer"
|
|
||||||
@reload="$fetch"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<p>You are not a game master.</p>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
async fetch() {
|
|
||||||
await this.$engine.fetchUserInfo()
|
|
||||||
await this.$engine.fetchGameInfo({ g: this.$store.state.engine.user.game })
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
overlay: {
|
|
||||||
open: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
user() {
|
|
||||||
return this.$store.state.engine.user || {}
|
|
||||||
},
|
|
||||||
gameinfo() {
|
|
||||||
return this.$store.state.engine.gameinfo || {}
|
|
||||||
},
|
|
||||||
players() {
|
|
||||||
const gameinfo = this.$store.state.engine.gameinfo || {}
|
|
||||||
const players = [...gameinfo.players || []]
|
|
||||||
return players.sort((a, b) => { return a.name.localeCompare(b.name) })
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
editPlayer(player) {
|
|
||||||
this.overlay.open = true
|
|
||||||
this.overlay.player = player
|
|
||||||
},
|
|
||||||
async editPlayerClose() {
|
|
||||||
this.overlay.open = false
|
|
||||||
await this.$engine.fetchGameInfo({ g: this.$store.state.engine.user.game })
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.page-gamemaster {
|
|
||||||
color: #ffffff;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
&__body {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__tiles {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
padding: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__player-overlay {
|
|
||||||
position: absolute;
|
|
||||||
left: 10%;
|
|
||||||
top: 10%;
|
|
||||||
width: 800px;
|
|
||||||
}
|
|
||||||
&__player-overlay-close {
|
|
||||||
float: right;
|
|
||||||
margin: 16px -16px 16px 16px;
|
|
||||||
padding: 12px;
|
|
||||||
border: 1px solid #ffffff;
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: $primary-background-color;
|
|
||||||
font-family: "$primary-font";
|
|
||||||
color: #ffffff;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: $primary-box-background-color;
|
|
||||||
}
|
|
||||||
&::after {
|
|
||||||
content: 'x';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,65 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="startpage">
|
|
||||||
<TitleBox />
|
|
||||||
<PlayButton class="startpage__buttonline" />
|
|
||||||
<div class="startpage__video">
|
|
||||||
<a href="https://www.sirlab.de/knowyt/know-your-teammates-kurzanleitung.mp4" target="_blank">
|
|
||||||
🎬 {{ $t('video-user') }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<CreateTeam v-if="!$store.state.engine.user" class="startpage__creategame" />
|
|
||||||
<div class="startpage__copyright">
|
|
||||||
v{{ $config.version }}, © 2021-2022, Settel
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
beforeMount() {
|
|
||||||
this.$i18n.map({
|
|
||||||
'video-user': { de: 'Erklärvideo (3min)', en: 'Video (3 mins, german)' },
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~/assets/css/components';
|
|
||||||
|
|
||||||
.startpage {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
&__buttonline {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__creategame {
|
|
||||||
margin: auto 0 2em 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__video {
|
|
||||||
margin: 2em 0;
|
|
||||||
text-align: center;
|
|
||||||
font-family: $secondary-font;
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 800;
|
|
||||||
color: $text-secondary-text-color;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $text-secondary-hover-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__copyright {
|
|
||||||
position: absolute;
|
|
||||||
right: 1em;
|
|
||||||
bottom: 0;
|
|
||||||
color: #000000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,116 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="page-play">
|
|
||||||
<div class="page-play__container">
|
|
||||||
<div class="page-play__container-game">
|
|
||||||
<div class="page-play__gamecontrols">
|
|
||||||
<GameControls />
|
|
||||||
</div>
|
|
||||||
<div class="page-play__area">
|
|
||||||
<Lobby v-if="gameState === 'idle'" />
|
|
||||||
<ReadySet v-if="gameState === 'ready-set'" :text="gamePhase" />
|
|
||||||
<Play v-if="gameState === 'play'" />
|
|
||||||
<CollectQuotes v-if="gameState === 'collect'" />
|
|
||||||
<Final v-if="gameState === 'final'" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="connectionLost" class="page-play__error-box">
|
|
||||||
<div>{{ $t('connection-lost') }}</div>
|
|
||||||
<button @click="reload">{{ $t('reload') }}</button>
|
|
||||||
</div>
|
|
||||||
<div v-if="showDebugPanel" class="page-play__container-debug">
|
|
||||||
<EngineDebug />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
beforeMount() {
|
|
||||||
this.$i18n.map({
|
|
||||||
'connection-lost': {
|
|
||||||
de: 'Verbindung zum Server unterbrochen',
|
|
||||||
en: 'connection to server lost',
|
|
||||||
},
|
|
||||||
'reload': { de: 'neu laden', en: 'reload' },
|
|
||||||
})
|
|
||||||
},
|
|
||||||
async mounted() {
|
|
||||||
await this.$engine.start()
|
|
||||||
},
|
|
||||||
async beforeDestroy() {
|
|
||||||
await this.$engine.stop()
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
showDebugPanel() {
|
|
||||||
const { hash } = this.$route
|
|
||||||
return hash.indexOf('debug') > -1
|
|
||||||
},
|
|
||||||
teamname() {
|
|
||||||
return this.$store.state.game.name
|
|
||||||
},
|
|
||||||
gameState() {
|
|
||||||
return this.$store.state.game.state
|
|
||||||
},
|
|
||||||
gamePhase() {
|
|
||||||
return this.$store.state.game.phase
|
|
||||||
},
|
|
||||||
connectionLost() {
|
|
||||||
return this.$engine.isActive && this.$store.state.engine.version == -1
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
reload() {
|
|
||||||
window.location.reload()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.page-play {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
&__container {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
&-game {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
&-debug {
|
|
||||||
max-width: 350px;
|
|
||||||
max-height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__controls {
|
|
||||||
flex: 0 1 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__area {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__error-box {
|
|
||||||
position: absolute;
|
|
||||||
left: 30%;
|
|
||||||
top: 10%;
|
|
||||||
width: 40%;
|
|
||||||
height: 5em;
|
|
||||||
padding: 1em;
|
|
||||||
border: 1px solid #ffffff;
|
|
||||||
border-radius: 20px;
|
|
||||||
background-color: #804040;
|
|
||||||
color: #ffffff;
|
|
||||||
text-align: center;
|
|
||||||
button {
|
|
||||||
margin: 1em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,12 +0,0 @@
|
|||||||
import buildUrl from 'build-url'
|
|
||||||
|
|
||||||
export default async function(path, queryParams) {
|
|
||||||
const { $axios, $config } = this.context
|
|
||||||
|
|
||||||
const url = buildUrl($config.serverBaseUrl, {
|
|
||||||
path,
|
|
||||||
queryParams,
|
|
||||||
})
|
|
||||||
|
|
||||||
return await $axios.get(url)
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
export default async function() {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
await this.callApi('/api/collectQuotes', {
|
|
||||||
g: store.state.engine.user?.game,
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
export default async function() {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
await this.callApi('/api/continueGame', {
|
|
||||||
g: store.state.engine.user?.game,
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
export default async function(quote) {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
await this.callApi('/api/saveQuote', {
|
|
||||||
g: store.state.engine.user?.game,
|
|
||||||
id: ':new:',
|
|
||||||
quote,
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
export default async function(name, teamname, lang) {
|
|
||||||
return await this.callApi('/api/createGame', {
|
|
||||||
name,
|
|
||||||
teamname,
|
|
||||||
lang,
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
export default async function(player) {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
await this.callApi('/api/deletePlayer', {
|
|
||||||
g: store.state.engine.user?.game,
|
|
||||||
id: player.id,
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
export default async function({ g }) {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await this.callApi('/api/gameinfo', { g })
|
|
||||||
store.commit('engine/setGameInfo', response.data)
|
|
||||||
} catch(e) {
|
|
||||||
store.commit('engine/setGameInfo', undefined)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
export default async function() {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await this.callApi('/api/games')
|
|
||||||
store.commit('engine/setGames', response.data)
|
|
||||||
} catch(e) {
|
|
||||||
store.commit('engine/setGames', undefined)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
export default async function() {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
if (this.shouldStop || !this.isActive) {
|
|
||||||
this.isActive = false
|
|
||||||
this.shouldStop = false
|
|
||||||
console.debug('engine stopped')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let delay = 0
|
|
||||||
try {
|
|
||||||
const response = await this.callApi('/api/sync', {
|
|
||||||
v: store.state.engine.version + 1,
|
|
||||||
g: store.state.engine.user?.game,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.parseSyncData(response.data)
|
|
||||||
} catch (e) {
|
|
||||||
if (!e.response) {
|
|
||||||
// request aborted or other causes
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const { status, statusText } = e.response
|
|
||||||
if (status != 200) {
|
|
||||||
console.warn(`HTTP ${status} ${statusText}`)
|
|
||||||
delay = 5000
|
|
||||||
store.commit('engine/setVersion', -1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const now = new Date().getTime()
|
|
||||||
const last = this.lastFetched.splice(0, 1)
|
|
||||||
this.lastFetched.push(now)
|
|
||||||
|
|
||||||
if (now - last < 1000) {
|
|
||||||
console.warn('engine: respawning too fast, throttling down')
|
|
||||||
delay = 5000
|
|
||||||
}
|
|
||||||
window.setTimeout(() => {
|
|
||||||
this.fetchUpdate()
|
|
||||||
}, delay)
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
export default async function() {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await this.callApi('/api/userinfo')
|
|
||||||
store.commit('engine/setUser', response.data)
|
|
||||||
} catch(e) {
|
|
||||||
store.commit('engine/setUser', undefined)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
export default async function() {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
await this.callApi('/api/finishGame', {
|
|
||||||
g: store.state.engine.user?.game,
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
export default async function() {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await this.callApi('/api/getQuotes', {
|
|
||||||
g: store.state.engine.user?.game,
|
|
||||||
})
|
|
||||||
store.commit('myQuotes/setQuotes', response.data.quotes)
|
|
||||||
} catch(e) {
|
|
||||||
store.commit('myQuotes/setQuotes', undefined)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
import callApi from './callApi'
|
|
||||||
import start from './start'
|
|
||||||
import stop from './stop'
|
|
||||||
import fetchUpdate from './fetchUpdate'
|
|
||||||
import fetchUserInfo from './fetchUserInfo'
|
|
||||||
import fetchGameInfo from './fetchGameInfo'
|
|
||||||
import fetchGames from './fetchGames'
|
|
||||||
import setGameName from './setGameName'
|
|
||||||
import setGameLang from './setGameLang'
|
|
||||||
import savePlayer from './savePlayer'
|
|
||||||
import deletePlayer from './deletePlayer'
|
|
||||||
import collectQuotes from './collectQuotes'
|
|
||||||
import startGame from './startGame'
|
|
||||||
import resetGame from './resetGame'
|
|
||||||
import continueGame from './continueGame'
|
|
||||||
import finishGame from './finishGame'
|
|
||||||
import parseSyncData from './parseSyncData'
|
|
||||||
import saveSelection from './saveSelection'
|
|
||||||
import getMyQuotes from './getMyQuotes'
|
|
||||||
import saveQuote from './saveQuote'
|
|
||||||
import createQuote from './createQuote'
|
|
||||||
import removeQuote from './removeQuote'
|
|
||||||
import createTeam from './createTeam'
|
|
||||||
|
|
||||||
export default (context, inject) => {
|
|
||||||
const engine = {
|
|
||||||
context,
|
|
||||||
lastFetched: [0, 0, 0, 0, 0],
|
|
||||||
isActive: false,
|
|
||||||
shouldStop: false,
|
|
||||||
|
|
||||||
callApi,
|
|
||||||
start,
|
|
||||||
stop,
|
|
||||||
fetchUpdate,
|
|
||||||
fetchUserInfo,
|
|
||||||
fetchGameInfo,
|
|
||||||
fetchGames,
|
|
||||||
setGameName,
|
|
||||||
setGameLang,
|
|
||||||
savePlayer,
|
|
||||||
deletePlayer,
|
|
||||||
collectQuotes,
|
|
||||||
getMyQuotes,
|
|
||||||
saveQuote,
|
|
||||||
createQuote,
|
|
||||||
removeQuote,
|
|
||||||
startGame,
|
|
||||||
resetGame,
|
|
||||||
continueGame,
|
|
||||||
finishGame,
|
|
||||||
parseSyncData,
|
|
||||||
saveSelection,
|
|
||||||
createTeam,
|
|
||||||
}
|
|
||||||
|
|
||||||
inject('engine', engine)
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
export default function(data) {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
store.commit('engine/setJson', data)
|
|
||||||
store.commit('game/setStateAndPhase', data.game)
|
|
||||||
store.commit('players/setPlayers', data.game)
|
|
||||||
store.commit('round/setRound', data.game)
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
export default async function(id, quote) {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
await this.callApi('/api/removeQuote', {
|
|
||||||
g: store.state.engine.user?.game,
|
|
||||||
id,
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
export default async function() {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
await this.callApi('/api/resetGame', {
|
|
||||||
g: store.state.engine.user?.game,
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
export default async function(player) {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
await this.callApi('/api/savePlayer', {
|
|
||||||
g: store.state.engine.user?.game,
|
|
||||||
id: player.id,
|
|
||||||
name: player.name,
|
|
||||||
score: player.score,
|
|
||||||
authcode: player.authcode,
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
export default async function(id, quote) {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
await this.callApi('/api/saveQuote', {
|
|
||||||
g: store.state.engine.user?.game,
|
|
||||||
id,
|
|
||||||
quote,
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
export default async function(selection) {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
if (selection.length === 0) {
|
|
||||||
selection = ['-']
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.callApi('/api/saveSelection', {
|
|
||||||
g: store.state.engine.user?.game,
|
|
||||||
selection: selection[0],
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
export default async function({ g, lang }) {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
await this.callApi('/api/setGameLang', {
|
|
||||||
g,
|
|
||||||
lang,
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
export default async function({ g, name }) {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
await this.callApi('/api/setGameName', {
|
|
||||||
g,
|
|
||||||
name,
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
export default async function() {
|
|
||||||
const { store, redirect } = this.context
|
|
||||||
if (!store.state.engine.user) {
|
|
||||||
if (!await this.fetchUserInfo()) {
|
|
||||||
redirect('/')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.isActive && !this.shouldStop) {
|
|
||||||
console.warn('attempt to start already running engine!')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const role = store.state.engine.user?.role || ''
|
|
||||||
if (role === 'admin') {
|
|
||||||
console.debug('user is admin, engine not started')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isActive = true
|
|
||||||
this.shouldStop = false
|
|
||||||
this.fetchUpdate()
|
|
||||||
console.debug('engine started')
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
export default async function() {
|
|
||||||
const { store } = this.context
|
|
||||||
|
|
||||||
await this.callApi('/api/startGame', {
|
|
||||||
g: store.state.engine.user?.game,
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
export default function() {
|
|
||||||
if (!this.isActive) return
|
|
||||||
this.shouldStop = true
|
|
||||||
console.debug('shutting down engine')
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
export default (context, inject) => {
|
|
||||||
const leftPad2Digits = (val) => val < 10 ? '0' + val : '' + val
|
|
||||||
|
|
||||||
const date = (time) => {
|
|
||||||
if (!time) return
|
|
||||||
|
|
||||||
const d = new Date(time * 1000)
|
|
||||||
return `${1900 + d.getYear()}-` +
|
|
||||||
`${leftPad2Digits(d.getMonth() + 1)}-` +
|
|
||||||
`${leftPad2Digits(d.getDate())}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const datetime = (time) => {
|
|
||||||
if (!time) return
|
|
||||||
|
|
||||||
const d = new Date(time * 1000)
|
|
||||||
return `${date(time)}, ` +
|
|
||||||
`${leftPad2Digits(d.getHours())}:` +
|
|
||||||
`${leftPad2Digits(d.getMinutes())}:` +
|
|
||||||
`${leftPad2Digits(d.getSeconds())}`
|
|
||||||
}
|
|
||||||
|
|
||||||
inject('formatter', {
|
|
||||||
date,
|
|
||||||
datetime,
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
let lang = 'DE'
|
|
||||||
let i18nMap = {}
|
|
||||||
let defaultLang = navigator.language ? navigator.language.substr(0, 2) : 'en'
|
|
||||||
|
|
||||||
export default (context, inject) => {
|
|
||||||
const translate = (key) => {
|
|
||||||
const t = i18nMap[key]
|
|
||||||
if (!t) {
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
return t[lang] || t[defaultLang] || key
|
|
||||||
}
|
|
||||||
inject('t', translate)
|
|
||||||
inject('i18n', {
|
|
||||||
setLang: (_lang) => {
|
|
||||||
lang = _lang
|
|
||||||
},
|
|
||||||
map: (_map) => {
|
|
||||||
i18nMap = {
|
|
||||||
...i18nMap,
|
|
||||||
..._map,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 8.0 KiB |
@ -1,95 +0,0 @@
|
|||||||
Copyright (c) 2011, Edgar Tolentino and Pablo Impallari (www.impallari.com|impallari@gmail.com),
|
|
||||||
Copyright (c) 2011, Igino Marini. (www.ikern.com|mail@iginomarini.com),
|
|
||||||
with Reserved Font Names "Dosis".
|
|
||||||
|
|
||||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
||||||
This license is copied below, and is also available with a FAQ at:
|
|
||||||
http://scripts.sil.org/OFL
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------------------------------
|
|
||||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
||||||
-----------------------------------------------------------
|
|
||||||
|
|
||||||
PREAMBLE
|
|
||||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
||||||
development of collaborative font projects, to support the font creation
|
|
||||||
efforts of academic and linguistic communities, and to provide a free and
|
|
||||||
open framework in which fonts may be shared and improved in partnership
|
|
||||||
with others.
|
|
||||||
|
|
||||||
The OFL allows the licensed fonts to be used, studied, modified and
|
|
||||||
redistributed freely as long as they are not sold by themselves. The
|
|
||||||
fonts, including any derivative works, can be bundled, embedded,
|
|
||||||
redistributed and/or sold with any software provided that any reserved
|
|
||||||
names are not used by derivative works. The fonts and derivatives,
|
|
||||||
however, cannot be released under any other type of license. The
|
|
||||||
requirement for fonts to remain under this license does not apply
|
|
||||||
to any document created using the fonts or their derivatives.
|
|
||||||
|
|
||||||
DEFINITIONS
|
|
||||||
"Font Software" refers to the set of files released by the Copyright
|
|
||||||
Holder(s) under this license and clearly marked as such. This may
|
|
||||||
include source files, build scripts and documentation.
|
|
||||||
|
|
||||||
"Reserved Font Name" refers to any names specified as such after the
|
|
||||||
copyright statement(s).
|
|
||||||
|
|
||||||
"Original Version" refers to the collection of Font Software components as
|
|
||||||
distributed by the Copyright Holder(s).
|
|
||||||
|
|
||||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
||||||
or substituting -- in part or in whole -- any of the components of the
|
|
||||||
Original Version, by changing formats or by porting the Font Software to a
|
|
||||||
new environment.
|
|
||||||
|
|
||||||
"Author" refers to any designer, engineer, programmer, technical
|
|
||||||
writer or other person who contributed to the Font Software.
|
|
||||||
|
|
||||||
PERMISSION & CONDITIONS
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
||||||
redistribute, and sell modified and unmodified copies of the Font
|
|
||||||
Software, subject to the following conditions:
|
|
||||||
|
|
||||||
1) Neither the Font Software nor any of its individual components,
|
|
||||||
in Original or Modified Versions, may be sold by itself.
|
|
||||||
|
|
||||||
2) Original or Modified Versions of the Font Software may be bundled,
|
|
||||||
redistributed and/or sold with any software, provided that each copy
|
|
||||||
contains the above copyright notice and this license. These can be
|
|
||||||
included either as stand-alone text files, human-readable headers or
|
|
||||||
in the appropriate machine-readable metadata fields within text or
|
|
||||||
binary files as long as those fields can be easily viewed by the user.
|
|
||||||
|
|
||||||
3) No Modified Version of the Font Software may use the Reserved Font
|
|
||||||
Name(s) unless explicit written permission is granted by the corresponding
|
|
||||||
Copyright Holder. This restriction only applies to the primary font name as
|
|
||||||
presented to the users.
|
|
||||||
|
|
||||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
||||||
Software shall not be used to promote, endorse or advertise any
|
|
||||||
Modified Version, except to acknowledge the contribution(s) of the
|
|
||||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
||||||
permission.
|
|
||||||
|
|
||||||
5) The Font Software, modified or unmodified, in part or in whole,
|
|
||||||
must be distributed entirely under this license, and must not be
|
|
||||||
distributed under any other license. The requirement for fonts to
|
|
||||||
remain under this license does not apply to any document created
|
|
||||||
using the Font Software.
|
|
||||||
|
|
||||||
TERMINATION
|
|
||||||
This license becomes null and void if any of the above conditions are
|
|
||||||
not met.
|
|
||||||
|
|
||||||
DISCLAIMER
|
|
||||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
||||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
||||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
||||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
||||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
Binary file not shown.
Binary file not shown.
@ -1,93 +0,0 @@
|
|||||||
Copyright (c) 2012, Alejandro Inler (alejandroinler@gmail.com), with Reserved Font Name 'Wendy'
|
|
||||||
|
|
||||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
||||||
This license is copied below, and is also available with a FAQ at:
|
|
||||||
http://scripts.sil.org/OFL
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------------------------------
|
|
||||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
||||||
-----------------------------------------------------------
|
|
||||||
|
|
||||||
PREAMBLE
|
|
||||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
||||||
development of collaborative font projects, to support the font creation
|
|
||||||
efforts of academic and linguistic communities, and to provide a free and
|
|
||||||
open framework in which fonts may be shared and improved in partnership
|
|
||||||
with others.
|
|
||||||
|
|
||||||
The OFL allows the licensed fonts to be used, studied, modified and
|
|
||||||
redistributed freely as long as they are not sold by themselves. The
|
|
||||||
fonts, including any derivative works, can be bundled, embedded,
|
|
||||||
redistributed and/or sold with any software provided that any reserved
|
|
||||||
names are not used by derivative works. The fonts and derivatives,
|
|
||||||
however, cannot be released under any other type of license. The
|
|
||||||
requirement for fonts to remain under this license does not apply
|
|
||||||
to any document created using the fonts or their derivatives.
|
|
||||||
|
|
||||||
DEFINITIONS
|
|
||||||
"Font Software" refers to the set of files released by the Copyright
|
|
||||||
Holder(s) under this license and clearly marked as such. This may
|
|
||||||
include source files, build scripts and documentation.
|
|
||||||
|
|
||||||
"Reserved Font Name" refers to any names specified as such after the
|
|
||||||
copyright statement(s).
|
|
||||||
|
|
||||||
"Original Version" refers to the collection of Font Software components as
|
|
||||||
distributed by the Copyright Holder(s).
|
|
||||||
|
|
||||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
||||||
or substituting -- in part or in whole -- any of the components of the
|
|
||||||
Original Version, by changing formats or by porting the Font Software to a
|
|
||||||
new environment.
|
|
||||||
|
|
||||||
"Author" refers to any designer, engineer, programmer, technical
|
|
||||||
writer or other person who contributed to the Font Software.
|
|
||||||
|
|
||||||
PERMISSION & CONDITIONS
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
||||||
redistribute, and sell modified and unmodified copies of the Font
|
|
||||||
Software, subject to the following conditions:
|
|
||||||
|
|
||||||
1) Neither the Font Software nor any of its individual components,
|
|
||||||
in Original or Modified Versions, may be sold by itself.
|
|
||||||
|
|
||||||
2) Original or Modified Versions of the Font Software may be bundled,
|
|
||||||
redistributed and/or sold with any software, provided that each copy
|
|
||||||
contains the above copyright notice and this license. These can be
|
|
||||||
included either as stand-alone text files, human-readable headers or
|
|
||||||
in the appropriate machine-readable metadata fields within text or
|
|
||||||
binary files as long as those fields can be easily viewed by the user.
|
|
||||||
|
|
||||||
3) No Modified Version of the Font Software may use the Reserved Font
|
|
||||||
Name(s) unless explicit written permission is granted by the corresponding
|
|
||||||
Copyright Holder. This restriction only applies to the primary font name as
|
|
||||||
presented to the users.
|
|
||||||
|
|
||||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
||||||
Software shall not be used to promote, endorse or advertise any
|
|
||||||
Modified Version, except to acknowledge the contribution(s) of the
|
|
||||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
||||||
permission.
|
|
||||||
|
|
||||||
5) The Font Software, modified or unmodified, in part or in whole,
|
|
||||||
must be distributed entirely under this license, and must not be
|
|
||||||
distributed under any other license. The requirement for fonts to
|
|
||||||
remain under this license does not apply to any document created
|
|
||||||
using the Font Software.
|
|
||||||
|
|
||||||
TERMINATION
|
|
||||||
This license becomes null and void if any of the above conditions are
|
|
||||||
not met.
|
|
||||||
|
|
||||||
DISCLAIMER
|
|
||||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
||||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
||||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
||||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
||||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
Binary file not shown.
Binary file not shown.
@ -1,29 +0,0 @@
|
|||||||
export const state = () => ({
|
|
||||||
json: {},
|
|
||||||
version: -1,
|
|
||||||
user: undefined,
|
|
||||||
gameinfo: undefined,
|
|
||||||
games: undefined,
|
|
||||||
})
|
|
||||||
|
|
||||||
export const mutations = {
|
|
||||||
setJson(state, json) {
|
|
||||||
state.json = json
|
|
||||||
state.version = parseInt(json.version, 10)
|
|
||||||
},
|
|
||||||
setVersion(state, version) {
|
|
||||||
state.version = version
|
|
||||||
},
|
|
||||||
setUser(state, user) {
|
|
||||||
state.user = user
|
|
||||||
if (user) {
|
|
||||||
this.$i18n.setLang(user.lang)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setGameInfo(state, gameinfo) {
|
|
||||||
state.gameinfo = gameinfo
|
|
||||||
},
|
|
||||||
setGames(state, games) {
|
|
||||||
state.games = games
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
export const state = () => ({
|
|
||||||
state: "",
|
|
||||||
phase: "",
|
|
||||||
name: "",
|
|
||||||
})
|
|
||||||
|
|
||||||
export const mutations = {
|
|
||||||
setStateAndPhase(state, game) {
|
|
||||||
if (game) {
|
|
||||||
const { state: gameState, phase, name } = game
|
|
||||||
state.state = gameState
|
|
||||||
state.phase = phase
|
|
||||||
state.name = name
|
|
||||||
} else {
|
|
||||||
state.state = ""
|
|
||||||
state.phase = ""
|
|
||||||
state.name = ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
export const state = () => ({
|
|
||||||
quotes: [],
|
|
||||||
})
|
|
||||||
|
|
||||||
export const mutations = {
|
|
||||||
setQuotes(state, list) {
|
|
||||||
if (typeof list === 'undefined') {
|
|
||||||
state.quotes = []
|
|
||||||
} else {
|
|
||||||
state.quotes = list
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
export const state = () => ({
|
|
||||||
players: [],
|
|
||||||
})
|
|
||||||
|
|
||||||
export const mutations = {
|
|
||||||
setPlayers(state, game) {
|
|
||||||
if (game) {
|
|
||||||
state.players = game.players
|
|
||||||
} else {
|
|
||||||
state.players = []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
export const state = () => ({
|
|
||||||
quote: "",
|
|
||||||
sources: [],
|
|
||||||
selections: {},
|
|
||||||
revelation: {},
|
|
||||||
})
|
|
||||||
|
|
||||||
export const mutations = {
|
|
||||||
setRound(state, game) {
|
|
||||||
if (game && game.round) {
|
|
||||||
state.quote = game.round.quote
|
|
||||||
state.sources = game.round.sources
|
|
||||||
state.selections = game.round.selections
|
|
||||||
state.revelation = game.round.revelation
|
|
||||||
} else {
|
|
||||||
state.quote = ""
|
|
||||||
state.sources = []
|
|
||||||
state.selections = {}
|
|
||||||
state.revelation = {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
export const state = () => ({
|
|
||||||
selection: {},
|
|
||||||
})
|
|
||||||
|
|
||||||
export const mutations = {
|
|
||||||
select(state, id) {
|
|
||||||
state.selection = {
|
|
||||||
...state.selection,
|
|
||||||
[id]: true,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
unselect(state, id) {
|
|
||||||
var selection = state.selection
|
|
||||||
delete selection[id]
|
|
||||||
state.selection = {
|
|
||||||
...selection
|
|
||||||
}
|
|
||||||
},
|
|
||||||
clearSelection(state) {
|
|
||||||
state.selection = {}
|
|
||||||
},
|
|
||||||
}
|
|
8057
_client/yarn.lock
8057
_client/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -25,7 +25,7 @@ const newQuote = ref({} as Quote)
|
|||||||
const createNewQuote = () => {
|
const createNewQuote = () => {
|
||||||
showNewQuoteCard.value = false
|
showNewQuoteCard.value = false
|
||||||
newQuote.value = {
|
newQuote.value = {
|
||||||
id: '',
|
id: ':new:' + Date.now(),
|
||||||
quote: '',
|
quote: '',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ const editModeEnd = async () => {
|
|||||||
|
|
||||||
const saveQuote = async () => {
|
const saveQuote = async () => {
|
||||||
if (props.quote.quote) {
|
if (props.quote.quote) {
|
||||||
useEngine().saveQuote(props.quote.id || ':new:', props.quote.quote)
|
await useEngine().saveQuote(props.quote.id, props.quote.quote)
|
||||||
}
|
}
|
||||||
|
|
||||||
await editModeEnd()
|
await editModeEnd()
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr class="gameinfo-tile__row">
|
<tr class="gameinfo-tile__row">
|
||||||
<td>{{ $t('num-quotes-played') }}:</td>
|
<td>{{ $t('num-quotes-played') }}:</td>
|
||||||
<td colspan="2">{{ gameinfo.numQuotesLeft - gameinfo.numQuotesLeft }} / {{ gameinfo.numQuotesTotal }}</td>
|
<td colspan="2">{{ gameinfo.numQuotesTotal - gameinfo.numQuotesLeft }} / {{ gameinfo.numQuotesTotal }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="gameinfo-tile__row" v-if="!showId" @click="showId = true">
|
<tr class="gameinfo-tile__row" v-if="!showId" @click="showId = true">
|
||||||
<td colspan="3"> </td>
|
<td colspan="3"> </td>
|
||||||
|
@ -21,8 +21,8 @@ type EngineResponse = {
|
|||||||
|
|
||||||
const { $t } = useI18n({
|
const { $t } = useI18n({
|
||||||
'connection-broken': { en: 'connection to server broken.', de: 'Verbindung zum Server ist unterbrochen.' },
|
'connection-broken': { en: 'connection to server broken.', de: 'Verbindung zum Server ist unterbrochen.' },
|
||||||
'retrying': { en: 'retrying', de: 'verbinde erneut'},
|
'retrying': { en: 'retrying', de: 'verbinde erneut' },
|
||||||
})
|
})
|
||||||
|
|
||||||
export async function fetchUpdate(this: EngineContext) {
|
export async function fetchUpdate(this: EngineContext) {
|
||||||
if (this.shouldStop || !this.isActive) {
|
if (this.shouldStop || !this.isActive) {
|
||||||
@ -44,6 +44,12 @@ export async function fetchUpdate(this: EngineContext) {
|
|||||||
throw Error('unexpected response from /api/sync')
|
throw Error('unexpected response from /api/sync')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response.game.id != userInfoStore.gameId) {
|
||||||
|
// happens when user changes game and an old request is still pending
|
||||||
|
console.warn('response gameId does not match current gameId')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.version = parseInt(response.version)
|
this.version = parseInt(response.version)
|
||||||
this.isConnected.value = true
|
this.isConnected.value = true
|
||||||
this.retry.value = 0
|
this.retry.value = 0
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
import { useUserinfoStore, Userinfo } from "@/stores/UserinfoStore"
|
import { useUserinfoStore, Userinfo } from '@/stores/UserinfoStore'
|
||||||
import useI18n from "./useI18n"
|
import { useEngineStore } from '@/stores/EngineStore'
|
||||||
|
import { useGameinfoStore } from '@/stores/GameinfoStore'
|
||||||
|
import { usePlayersStore } from '@/stores/PlayersStore'
|
||||||
|
import { useRoundStore } from '@/stores/RoundStore'
|
||||||
|
import useI18n from './useI18n'
|
||||||
import { $fetch } from 'ohmyfetch'
|
import { $fetch } from 'ohmyfetch'
|
||||||
|
|
||||||
export type AllowRole = '' | 'player' | 'gamemaster' | 'admin'
|
export type AllowRole = '' | 'player' | 'gamemaster' | 'admin'
|
||||||
@ -49,6 +53,11 @@ export default (): useAuth => {
|
|||||||
|
|
||||||
logout: async (): Promise<void> => {
|
logout: async (): Promise<void> => {
|
||||||
await $fetch('/api/logout')
|
await $fetch('/api/logout')
|
||||||
|
useEngineStore().reset()
|
||||||
|
useGameinfoStore().reset()
|
||||||
|
usePlayersStore().reset()
|
||||||
|
useUserinfoStore().reset()
|
||||||
|
useRoundStore().reset()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,5 +10,8 @@ export const useEngineStore = defineStore('EngineStore', {
|
|||||||
setJson(json: any): void {
|
setJson(json: any): void {
|
||||||
this.json = json
|
this.json = json
|
||||||
},
|
},
|
||||||
|
reset(): void {
|
||||||
|
this.json = {}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -33,5 +33,13 @@ export const useGameinfoStore = defineStore('GameinfoStore', {
|
|||||||
setGameinfo(gameInfo: Gameinfo): void {
|
setGameinfo(gameInfo: Gameinfo): void {
|
||||||
this.gameInfo = gameInfo
|
this.gameInfo = gameInfo
|
||||||
},
|
},
|
||||||
|
reset(): void {
|
||||||
|
this.gameInfo = {
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
state: '',
|
||||||
|
phase: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -12,5 +12,8 @@ export const usePlayersStore = defineStore('PlayersStore', {
|
|||||||
players = players || []
|
players = players || []
|
||||||
this.players.splice(0, this.players.length, ...players)
|
this.players.splice(0, this.players.length, ...players)
|
||||||
},
|
},
|
||||||
|
reset(): void {
|
||||||
|
this.players.splice(0, 0)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -24,5 +24,16 @@ export const useRoundStore = defineStore('RoundStore', {
|
|||||||
this.round.revelation.votes = round.revelation?.votes || {} as RevelationVotes
|
this.round.revelation.votes = round.revelation?.votes || {} as RevelationVotes
|
||||||
this.round.revelation.sources = round.revelation?.sources || {} as RevelationSources
|
this.round.revelation.sources = round.revelation?.sources || {} as RevelationSources
|
||||||
},
|
},
|
||||||
|
reset(): void {
|
||||||
|
this.round = {
|
||||||
|
quote: '',
|
||||||
|
sources: [],
|
||||||
|
selections: {},
|
||||||
|
revelation: {
|
||||||
|
votes: {},
|
||||||
|
sources: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -34,5 +34,15 @@ export const useUserinfoStore = defineStore('UserinfoStore', {
|
|||||||
setUserInfo(userInfo: Userinfo): void {
|
setUserInfo(userInfo: Userinfo): void {
|
||||||
this.userInfo = userInfo
|
this.userInfo = userInfo
|
||||||
},
|
},
|
||||||
|
reset(): void {
|
||||||
|
this.userInfo = {
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
role: '',
|
||||||
|
game: '',
|
||||||
|
lang: 'en',
|
||||||
|
isCameo: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package application
|
package application
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"sirlab.de/go/knowyt/applicationConfig"
|
"sirlab.de/go/knowyt/applicationConfig"
|
||||||
"sirlab.de/go/knowyt/game"
|
"sirlab.de/go/knowyt/game"
|
||||||
"sirlab.de/go/knowyt/user"
|
"sirlab.de/go/knowyt/user"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewApplication(config applicationConfig.ApplicationConfig) (*Application, error) {
|
func NewApplication(config applicationConfig.ApplicationConfig) (*Application, error) {
|
||||||
@ -13,6 +14,7 @@ func NewApplication(config applicationConfig.ApplicationConfig) (*Application, e
|
|||||||
users: make(map[string]*user.User),
|
users: make(map[string]*user.User),
|
||||||
games: make(map[string]*game.Game),
|
games: make(map[string]*game.Game),
|
||||||
playerATime: make(map[string]time.Time),
|
playerATime: make(map[string]time.Time),
|
||||||
|
debounceMap: make(map[string]bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := app.loadUsers(); err != nil {
|
if err := app.loadUsers(); err != nil {
|
||||||
|
@ -2,9 +2,10 @@ package application
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/google/uuid"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"sirlab.de/go/knowyt/game"
|
"sirlab.de/go/knowyt/game"
|
||||||
"sirlab.de/go/knowyt/user"
|
"sirlab.de/go/knowyt/user"
|
||||||
)
|
)
|
||||||
@ -26,14 +27,16 @@ func (app *Application) SaveQuote(usr *user.User, w http.ResponseWriter, r *http
|
|||||||
|
|
||||||
quoteText := r.URL.Query().Get("quote")
|
quoteText := r.URL.Query().Get("quote")
|
||||||
quoteId := r.URL.Query().Get("id")
|
quoteId := r.URL.Query().Get("id")
|
||||||
if quoteId == ":new:" {
|
if quoteId[:5] == ":new:" {
|
||||||
quoteNewUuid := uuid.NewString()
|
if app.debounceQuote(usr.GetId() + ":" + quoteId) {
|
||||||
err = gm.CreateQuote(
|
quoteNewUuid := uuid.NewString()
|
||||||
app.getQuoteFileNameFromId(gm, quoteNewUuid),
|
err = gm.CreateQuote(
|
||||||
usr.GetId(),
|
app.getQuoteFileNameFromId(gm, quoteNewUuid),
|
||||||
quoteNewUuid,
|
usr.GetId(),
|
||||||
quoteText,
|
quoteNewUuid,
|
||||||
)
|
quoteText,
|
||||||
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
err = gm.SaveQuote(
|
err = gm.SaveQuote(
|
||||||
app.getQuoteFileNameFromId(gm, quoteId),
|
app.getQuoteFileNameFromId(gm, quoteId),
|
||||||
@ -58,3 +61,11 @@ func (app *Application) getQuoteFileNameFromId(gm *game.Game, id string) string
|
|||||||
quoteFileNameShort := id + ".json"
|
quoteFileNameShort := id + ".json"
|
||||||
return path.Join(gameDirName, "quotes", quoteFileNameShort)
|
return path.Join(gameDirName, "quotes", quoteFileNameShort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *Application) debounceQuote(id string) bool {
|
||||||
|
if app.debounceMap[id] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
app.debounceMap[id] = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
package application
|
package application
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"sirlab.de/go/knowyt/applicationConfig"
|
"sirlab.de/go/knowyt/applicationConfig"
|
||||||
"sirlab.de/go/knowyt/game"
|
"sirlab.de/go/knowyt/game"
|
||||||
"sirlab.de/go/knowyt/user"
|
"sirlab.de/go/knowyt/user"
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Application struct {
|
type Application struct {
|
||||||
@ -14,4 +15,5 @@ type Application struct {
|
|||||||
users map[string]*user.User
|
users map[string]*user.User
|
||||||
games map[string]*game.Game
|
games map[string]*game.Game
|
||||||
playerATime map[string]time.Time
|
playerATime map[string]time.Time
|
||||||
|
debounceMap map[string]bool
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package game
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"sirlab.de/go/knowyt/quote"
|
"sirlab.de/go/knowyt/quote"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user