diff --git a/package.json b/package.json index bba1d90..277b104 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,12 @@ "@vue/composition-api": "^1.0.0-beta.1", "core-js": "^3.6.5", "plyr": "^3.6.2", - "pretty-checkbox": "^3.0.3", "register-service-worker": "^1.7.1", "vue": "^2.6.11", "vue-router": "^3.2.0", - "vuex": "^3.4.0" + "vuex": "^3.4.0", + "vuex-composition-helpers": "^1.0.18", + "vuex-persist": "^2.2.0" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "^2.33.0", diff --git a/src/App.vue b/src/App.vue index a049fb5..84118a1 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,5 +1,5 @@ @@ -48,7 +54,19 @@ export default defineComponent({ } onMounted(() => { - chilledcow.video = new Plyr('#player') + chilledcow.video = new Plyr('#player', { + youtube: { + noCookie: true, + rel: 0, + showinfo: 0, + iv_load_policy: 3, + modestbranding: 1 + }, + quality: { + default: 240, + options: [240] + } + }) chilledcow.video.on('play', onPlay) chilledcow.video.on('pause', onPause) chilledcow.video.on('ready', () => { diff --git a/src/components/IntervalInput.vue b/src/components/IntervalInput.vue index 1e2be53..6fa9067 100644 --- a/src/components/IntervalInput.vue +++ b/src/components/IntervalInput.vue @@ -8,30 +8,33 @@ + + diff --git a/src/hooks/useInterval.ts b/src/hooks/useInterval.ts new file mode 100644 index 0000000..6870ef5 --- /dev/null +++ b/src/hooks/useInterval.ts @@ -0,0 +1,13 @@ +import { useGetters, useActions } from 'vuex-composition-helpers' +import { GetterTree } from 'vuex' +import { State, RootActions } from '@/store' + +export const useInterval = () => { + const { interval } = useGetters>(['interval']) + const { setInterval } = useActions(['setInterval']) + + return { + interval, + setInterval + } +} diff --git a/src/hooks/useMusic.ts b/src/hooks/useMusic.ts index d5c138b..1a1fcf8 100644 --- a/src/hooks/useMusic.ts +++ b/src/hooks/useMusic.ts @@ -1,9 +1,13 @@ -import { ref } from '@vue/composition-api' +import { useGetters, useActions } from 'vuex-composition-helpers' +import { GetterTree } from 'vuex' +import { State, RootActions } from '@/store' export const useMusic = () => { - const hasMusic = ref(true) + const { withMusic } = useGetters>(['withMusic']) + const { setWithMusic } = useActions(['setWithMusic']) return { - hasMusic + withMusic, + setWithMusic } } diff --git a/src/hooks/useTimer.ts b/src/hooks/useTimer.ts index 273ea27..8a81c80 100644 --- a/src/hooks/useTimer.ts +++ b/src/hooks/useTimer.ts @@ -1,6 +1,7 @@ import { ref, computed, watch } from '@vue/composition-api' import { StopwatchState } from '@/types/StopwatchState' import { notify } from '@/utils/notification' +import { useInterval } from './useInterval' const format = (num: number) => { return num.toString().padStart(2, '0') @@ -16,9 +17,9 @@ const formatSeconds = (seconds: number) => { export const useTimer = () => { const timeState = ref('stopped') const seconds = ref(0) - const interval = ref(5) + const { interval } = useInterval() const sessionSeconds = ref(interval.value * 60) - const isProgrammer1Turn = ref(true) + const isDev1Turn = ref(true) const time = computed(() => formatSeconds(seconds.value)) const session = computed(() => formatSeconds(sessionSeconds.value)) @@ -36,14 +37,15 @@ export const useTimer = () => { stopwatchId = setInterval(() => { seconds.value++ - if (seconds.value % intervalSeconds.value === 0) { + const isTimeToChange = seconds.value % intervalSeconds.value === 0 + if (isTimeToChange) { sessionSeconds.value = intervalSeconds.value - isProgrammer1Turn.value = !isProgrammer1Turn.value + isDev1Turn.value = !isDev1Turn.value - const dev = isProgrammer1Turn.value ? 'Dev 1' : 'Dev 2' + const dev = isDev1Turn.value ? 'Dev 1' : 'Dev 2' - notify('Changement !', { - body: `${dev}, à votre tour !` + notify('Change!', { + body: `${dev}, your turn!` }) } }, 1000) @@ -69,7 +71,7 @@ export const useTimer = () => { pause() seconds.value = 0 sessionSeconds.value = intervalSeconds.value - isProgrammer1Turn.value = true + isDev1Turn.value = true } return { @@ -80,6 +82,6 @@ export const useTimer = () => { timeState, time, session, - isProgrammer1Turn + isDev1Turn } } diff --git a/src/registerServiceWorker.ts b/src/registerServiceWorker.ts index b04f313..9190c3e 100644 --- a/src/registerServiceWorker.ts +++ b/src/registerServiceWorker.ts @@ -25,8 +25,8 @@ if (process.env.NODE_ENV === 'production') { setNotificationInstance((title, options?) => sw.showNotification(title, options) ) - notify('Une nouvelle version est disponible !', { - body: `Fermer l'appli pour la mettre à jour.` + notify('An update is available!', { + body: `Reload the app to install the update.` }) }, offline() { diff --git a/src/store/index.ts b/src/store/index.ts index 0107c20..421ae72 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,11 +1,55 @@ import Vue from 'vue' -import Vuex from 'vuex' +import Vuex, { ActionContext, ActionTree } from 'vuex' +import VuexPersistence from 'vuex-persist' Vue.use(Vuex) -export default new Vuex.Store({ - state: {}, - mutations: {}, - actions: {}, - modules: {} +export interface State { + interval: number + withMusic: boolean +} + +export interface RootActions extends ActionTree { + setInterval: (ctx: ActionContext, payload: number) => void + setWithMusic: (ctx: ActionContext, payload: boolean) => void +} + +const vuexLocal = new VuexPersistence({ + key: 'binome', + storage: window.localStorage }) + +const SET_INTERVAL = 'SET_INTERVAL' +const WITH_MUSIC = 'WITH_MUSIC' + +const store = new Vuex.Store({ + state: { + interval: 5, + withMusic: true + }, + getters: { + interval: ({ interval }) => interval, + withMusic: ({ withMusic }) => withMusic + }, + mutations: { + [SET_INTERVAL](state, interval: number) { + state.interval = interval + }, + [WITH_MUSIC](state, withMusic: boolean) { + state.withMusic = withMusic + } + }, + actions: { + setInterval({ commit }, interval: number) { + if (interval > 0) { + commit(SET_INTERVAL, interval) + } + }, + setWithMusic({ commit }, withMusic: number) { + commit(WITH_MUSIC, withMusic) + } + }, + plugins: [vuexLocal.plugin] +}) + +export default store diff --git a/src/styles/app.scss b/src/styles/app.scss index 1f8ea2b..8c35249 100644 --- a/src/styles/app.scss +++ b/src/styles/app.scss @@ -1,9 +1,7 @@ @import url('https://fonts.googleapis.com/css2?family=Fira+Mono&display=swap'); -@import '~pretty-checkbox/src/pretty-checkbox'; -$color: #f8efba; -$bgcolor: #2c3a47; -$font-family: 'Fira Mono', monospace; +@import './variables'; +@import './plume'; body { text-align: center; diff --git a/src/styles/plume.scss b/src/styles/plume.scss new file mode 100644 index 0000000..33c892e --- /dev/null +++ b/src/styles/plume.scss @@ -0,0 +1,172 @@ +:root { + --pm-placeholder-color: #999; + --pm-form-field-margin-bottom: 32px; + --pm-form-caption-font-size: 0.8em; + --pm-fieldset-border-style: solid; + --pm-fieldset-border-width: 1px; + --pm-fieldset-border-color: rgba(127, 127, 127, 0.4); + --pm-label-font-size: 1em; + --pm-label-font-weight: 600; + --pm-label-margin-bottom: 24px; + --pm-input-height: 40px; + --pm-input-box-shadow: none; + --pm-input-padding: 8px 16px; + --pm-input-border-style: solid; + --pm-input-border-radius: none; + --pm-input-border-width: 0 0 2px 0; + --pm-input-border-color: rgba(127, 127, 127, 0.4); + --pm-input-background-color: rgba(200, 200, 200, 0.1); + --pm-input-switch-size: 24px; + --pm-input-switch-border-width: 2px; + --pm-textarea-padding: 16px; + --pm-textarea-min-height: 100px; +} +.plume fieldset { + padding: 12px; + border-style: var(--pm-fieldset-border-style); + border-width: var(--pm-fieldset-border-width); + border-color: var(--pm-fieldset-border-color); + border-radius: var(--pm-input-border-radius); +} +.plume fieldset legend { + padding: 0 10px; + color: var(--pm-primary-color); +} +.plume .pm-field, +.plume fieldset { + width: 100%; + display: block; + margin-bottom: var(--pm-form-field-margin-bottom); +} +.plume label, +.plume legend { + display: block; + color: var(--pm-app-text-color); + font-weight: var(--pm-label-font-weight); + margin-bottom: var(--pm-label-margin-bottom); +} +.plume label[data-pm-holder], +.plume legend[data-pm-holder] { + font-weight: 300; + margin: 0 4px; + align-items: center; + display: inline-flex; + height: var(--pm-input-height); +} +.plume label.pm-required::after, +.plume legend.pm-required::after { + content: '*'; + margin-left: 8px; + color: var(--pm-color-error); +} +.plume + input:not([type='range']):not([type='button']):not([type='reset']):not([type='submit']):not([type='checkbox']):not([type='radio']), +.plume textarea { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} +.plume + input:not([type='range']):not([type='button']):not([type='reset']):not([type='submit']):not([type='checkbox']):not([type='radio']), +.plume select, +.plume textarea { + width: 100%; + display: block; + box-sizing: border-box; + height: var(--pm-input-height); + padding: var(--pm-input-padding); + box-shadow: var(--pm-input-box-shadow); + border-style: var(--pm-input-border-style); + border-color: var(--pm-input-border-color); + border-width: var(--pm-input-border-width); + border-radius: var(--pm-input-border-radius); + background-color: var(--pm-input-background-color); +} +.plume input[type='checkbox'], +.plume input[type='radio'] { + margin: 0 8px; + cursor: pointer; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + position: relative; + display: inline-block; + width: var(--pm-input-switch-size); + height: var(--pm-input-switch-size); + border: solid var(--pm-input-switch-border-width); + border-color: var(--pm-input-border-color); +} +.plume input[type='checkbox']::before { + top: 2px; + left: 2px; + content: ''; + opacity: 0; + visibility: hidden; + position: absolute; + background-color: var(--pm-primary-color); + height: calc(100% - 4px); + width: calc(100% - 4px); +} +.plume input[type='checkbox']:checked, +.plume input[type='radio']:checked { + border-color: var(--pm-primary-color); +} +.plume input[type='checkbox']:checked::before, +.plume input[type='radio']:checked::before { + opacity: 1; + visibility: visible; +} +.plume input[type='radio'], +.plume input[type='radio']:checked::before { + border-radius: 100%; +} +.plume input[type='checkbox'][data-pm-checkbox-toggle] { + border-radius: 25px; + height: var(--pm-input-switch-size); + cursor: pointer; + width: calc(var(--pm-input-switch-size) * 2); +} +.plume input[type='checkbox'][data-pm-checkbox-toggle]::before { + top: 2px; + left: 2px; + content: ''; + opacity: 1; + visibility: visible; + position: absolute; + border-radius: 100%; + height: calc(100% - 4px); + width: calc( + var(--pm-input-switch-size) - (var(--pm-input-switch-border-width) * 2) - + 4px + ); + background-color: var(--pm-input-border-color); +} +.plume input[type='checkbox'][data-pm-checkbox-toggle]:checked { + border-color: var(--pm-input-border-color); +} +.plume input[type='checkbox'][data-pm-checkbox-toggle]:checked::before { + content: ''; + top: 2px; + left: calc(var(--pm-input-switch-size) + 2px); + background-color: var(--pm-primary-color); +} +.plume:not(.pm-no-effects) input[type='checkbox']::after, +.plume:not(.pm-no-effects) input[type='checkbox']::before { + transition: 0.1s; +} +.plume:not(.pm-no-effects) [data-pm-success]:not([data-pm-success=''])::after { + -webkit-animation: slide-down 0.3s ease-out; + animation: slide-down 0.3s ease-out; +} +.plume:not(.pm-no-effects) [data-pm-error]:not([data-pm-error=''])::after { + -webkit-animation: slide-down 0.3s ease-out; + animation: slide-down 0.3s ease-out; +} +.plume:not(.pm-no-effects) [data-pm-warning]:not([data-pm-warning=''])::after { + -webkit-animation: slide-down 0.3s ease-out; + animation: slide-down 0.3s ease-out; +} +.plume:not(.pm-no-effects) [data-pm-info]:not([data-pm-info=''])::after { + -webkit-animation: slide-down 0.3s ease-out; + animation: slide-down 0.3s ease-out; +} diff --git a/src/styles/variables.scss b/src/styles/variables.scss new file mode 100644 index 0000000..0d9fc58 --- /dev/null +++ b/src/styles/variables.scss @@ -0,0 +1,8 @@ +$color: #f8efba; +$bgcolor: #2c3a47; +$font-family: 'Fira Mono', monospace; + +:root { + --pm-app-surface-color: #{#2c3a47}; + --pm-primary-color: #{$color}; +} diff --git a/src/views/Home.vue b/src/views/Home.vue index a86d8e3..8286709 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -1,67 +1,40 @@