Recoded with Vuex, also TS, JS, VueJS, HTML, CSS, SCSS, ESLint, Babel. Decomposited logic to vuex-store from vue-app views. Fixed delete-gamepad method. Added unmounted hooks deletions of event-listeners. Local improvements. Updated dependencies. Rebuild prod.

This commit is contained in:
Eugene Serb 2022-08-05 18:56:49 +03:00
parent 15fd2d5b5d
commit 06f4c26226
16 changed files with 192 additions and 83 deletions

View File

@ -15,4 +15,4 @@
webvisor: true
});</script><style>[v-cloak] {
display: none;
}</style><script defer="defer" src="/js/chunk-vendors.5d0a04f4.js"></script><script defer="defer" src="/js/app.621e321e.js"></script><link href="/css/app.a72a8a93.css" rel="stylesheet"></head><body><header class="header"><div class="header-wrapper container"><div class="logo-wrapper"><span class="logo-wrapper__logo" translate="no">Wavelovers</span></div><nav class="menu-wrapper"><ul class="navigation"><li class="navigation__item"><a href="/" target="_self" class="navigation__link">Home</a></li><li class="navigation__item"><a href="/faq.html" target="_self" class="navigation__link">FAQ</a></li><li class="navigation__item"><a href="/about.html" target="_self" class="navigation__link">About</a></li><li class="navigation__item"><a href="/donate.html" target="_self" class="navigation__link">Donate</a></li></ul></nav></div></header><main class="page container"><h1 class="visually-hidden">Wavelovers</h1><div id="app" v-cloak></div></main><footer class="footer"><div class="footer-wrapper container"><div class="annotation"><span class="annotation__text">© 2022 Wavelovers. Content licensed under </span><a href="https://wavelovers.ru/LICENSE.md" target="_blank">GNU General Public License v3.0</a><br><span class="annotation__text">This site is open source. </span><a href="https://github.com/eugene-serb/wavelovers/" target="_blank">Improve this page.</a></div><div class="annotation created-by"><span class="annotation__text">Created by</span><a href="https://eugene-serb.github.io/" target="_blank" translate="no">Eugene Serb</a></div></div></footer><noscript>You need to enable JavaScript to run this app.</noscript><noscript><div><img src="https://mc.yandex.ru/watch/89252711" style="position:absolute; left:-9999px;" alt=""/></div></noscript></body></html>
}</style><script defer="defer" src="/js/chunk-vendors.d42e1256.js"></script><script defer="defer" src="/js/app.b6a47de1.js"></script><link href="/css/app.a72a8a93.css" rel="stylesheet"></head><body><header class="header"><div class="header-wrapper container"><div class="logo-wrapper"><span class="logo-wrapper__logo" translate="no">Wavelovers</span></div><nav class="menu-wrapper"><ul class="navigation"><li class="navigation__item"><a href="/" target="_self" class="navigation__link">Home</a></li><li class="navigation__item"><a href="/faq.html" target="_self" class="navigation__link">FAQ</a></li><li class="navigation__item"><a href="/about.html" target="_self" class="navigation__link">About</a></li><li class="navigation__item"><a href="/donate.html" target="_self" class="navigation__link">Donate</a></li></ul></nav></div></header><main class="page container"><h1 class="visually-hidden">Wavelovers</h1><div id="app" v-cloak></div></main><footer class="footer"><div class="footer-wrapper container"><div class="annotation"><span class="annotation__text">© 2022 Wavelovers. Content licensed under </span><a href="https://wavelovers.ru/LICENSE.md" target="_blank">GNU General Public License v3.0</a><br><span class="annotation__text">This site is open source. </span><a href="https://github.com/eugene-serb/wavelovers/" target="_blank">Improve this page.</a></div><div class="annotation created-by"><span class="annotation__text">Created by</span><a href="https://eugene-serb.github.io/" target="_blank" translate="no">Eugene Serb</a></div></div></footer><noscript>You need to enable JavaScript to run this app.</noscript><noscript><div><img src="https://mc.yandex.ru/watch/89252711" style="position:absolute; left:-9999px;" alt=""/></div></noscript></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
docs/js/app.b6a47de1.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

45
package-lock.json generated
View File

@ -10,7 +10,8 @@
"license": "GNU GPL v3",
"dependencies": {
"core-js": "^3.8.3",
"vue": "^3.2.13"
"vue": "^3.2.13",
"vuex": "^4.0.0"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.4.0",
@ -18,6 +19,7 @@
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-plugin-typescript": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"@vue/eslint-config-typescript": "^9.1.0",
"eslint": "^7.32.0",
@ -115,9 +117,9 @@
}
},
"node_modules/@babel/generator": {
"version": "7.18.10",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.10.tgz",
"integrity": "sha512-0+sW7e3HjQbiHbj1NeU/vN8ornohYlacAfZIaXhdoGweQqgcNy69COVciYYqEXJ/v+9OBA7Frxm4CVAuNqKeNA==",
"version": "7.18.12",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz",
"integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==",
"dev": true,
"dependencies": {
"@babel/types": "^7.18.10",
@ -3092,6 +3094,11 @@
"integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
"dev": true
},
"node_modules/@vue/devtools-api": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.2.1.tgz",
"integrity": "sha512-OEgAMeQXvCoJ+1x8WyQuVZzFo0wcyCmUR3baRVLmKBo1LmYZWMlRiXlux5jd0fqVJu6PfDbOrZItVqUEzLobeQ=="
},
"node_modules/@vue/eslint-config-typescript": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-9.1.0.tgz",
@ -11171,6 +11178,17 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true
},
"node_modules/vuex": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz",
"integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==",
"dependencies": {
"@vue/devtools-api": "^6.0.0-beta.11"
},
"peerDependencies": {
"vue": "^3.0.2"
}
},
"node_modules/watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
@ -12024,9 +12042,9 @@
}
},
"@babel/generator": {
"version": "7.18.10",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.10.tgz",
"integrity": "sha512-0+sW7e3HjQbiHbj1NeU/vN8ornohYlacAfZIaXhdoGweQqgcNy69COVciYYqEXJ/v+9OBA7Frxm4CVAuNqKeNA==",
"version": "7.18.12",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz",
"integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==",
"dev": true,
"requires": {
"@babel/types": "^7.18.10",
@ -14208,6 +14226,11 @@
}
}
},
"@vue/devtools-api": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.2.1.tgz",
"integrity": "sha512-OEgAMeQXvCoJ+1x8WyQuVZzFo0wcyCmUR3baRVLmKBo1LmYZWMlRiXlux5jd0fqVJu6PfDbOrZItVqUEzLobeQ=="
},
"@vue/eslint-config-typescript": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-9.1.0.tgz",
@ -20188,6 +20211,14 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true
},
"vuex": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz",
"integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==",
"requires": {
"@vue/devtools-api": "^6.0.0-beta.11"
}
},
"watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",

View File

@ -22,7 +22,8 @@
},
"dependencies": {
"core-js": "^3.8.3",
"vue": "^3.2.13"
"vue": "^3.2.13",
"vuex": "^4.0.0"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.4.0",
@ -30,6 +31,7 @@
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-plugin-typescript": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"@vue/eslint-config-typescript": "^9.1.0",
"eslint": "^7.32.0",

View File

@ -31,7 +31,7 @@
},
},
methods: {
change(index: number): void {
change: function (index: number): void {
this.$emit('change', index as number);
},
},

View File

@ -31,7 +31,7 @@
PatternItem: PatternItem,
},
methods: {
change(index: number): void {
change: function (index: number): void {
this.$emit('change', index as number);
},
},

View File

@ -17,12 +17,10 @@
import PatternList from '@/components/PatternList.vue';
import GamepadList from '@/components/GamepadList.vue';
import MessageItem from '@/components/MessageItem.vue';
import IGamepadEvent from '@/models/IGamepadEvent';
import IGamepad from '@/models/IGamepad';
import TPattern from '@/models/TPattern';
import TPatternUnit from '@/models/TPatternUnit';
import Vibrator from '@/models/Vibrator';
import store from '@/store/index';
export default defineComponent({
name: 'WaveloversApp',
components: {
@ -30,77 +28,40 @@
GamepadList: GamepadList,
MessageItem: MessageItem,
},
data: () => {
return {
gamepads: [] as Vibrator[],
patterns: [] as TPattern[],
isActive: false,
mode: 0,
};
computed: {
gamepads: function (): Vibrator[] {
return store.getters.gamepads as Vibrator[];
},
patterns: function (): TPattern[] {
return store.getters.patterns as TPattern[];
},
mode: function (): number {
return store.getters.mode as number;
},
isActive: function (): boolean {
return store.getters.isActive as boolean;
},
},
methods: {
loadPatterns: async function () {
const url = 'https://wavelovers.ru/assets/patterns.json';
try {
const response = await fetch(url);
if (response.ok) {
let json = await response.json();
this.patterns = json;
} else {
console.log('Connect to the Internet for download more patterns...');
}
} catch (error) {
console.log(error);
}
},
addEventListeners(): void {
window.addEventListener('gamepadconnected', (event: GamepadEvent) => this.addGamepad(event));
window.addEventListener('gamepaddisconnected', (event: GamepadEvent) => this.deleteGamepad(event));
window.addEventListener('gamepadconnected', (event: GamepadEvent) => store.dispatch('addGamepad', event));
window.addEventListener('gamepaddisconnected', (event: GamepadEvent) => store.dispatch('deleteGamepad', event));
},
addGamepad(event: GamepadEvent) {
const ievent: IGamepadEvent = event as unknown as IGamepadEvent;
if (this.gamepads.length >= 1) {
return;
} else {
this.gamepads.push(new Vibrator(ievent.gamepad as IGamepad));
}
},
deleteGamepad(event: GamepadEvent): void {
this.gamepads.forEach((gamepad, index) => {
if (gamepad.unit.id === event.gamepad.id) {
this.gamepads.splice(index, 1);
}
});
removeEventListeners(): void {
window.removeEventListener('gamepadconnected', (event: GamepadEvent) => store.dispatch('addGamepad', event));
window.removeEventListener('gamepaddisconnected', (event: GamepadEvent) => store.dispatch('deleteGamepad', event));
},
change(index: number): void {
if (this.mode === index) {
this.isActive = !this.isActive;
} else {
this.isActive = true;
this.mode = index;
}
if (this.isActive === true) {
this.reset();
this.vibrate();
} else {
this.reset();
}
},
vibrate(): void {
this.gamepads.forEach(gamepad => {
gamepad.vibrate(this.patterns[this.mode].pattern as TPatternUnit[]);
});
},
reset(): void {
this.gamepads.forEach(gamepad => {
gamepad.reset();
});
store.dispatch('change', index as number);
},
},
mounted() {
this.loadPatterns();
store.dispatch('loadPatterns');
this.addEventListeners();
},
unmounted() {
this.removeEventListeners();
},
});
</script>

View File

@ -1,5 +1,5 @@
import { createApp } from 'vue';
import App from './App.vue';
import store from './store';
createApp(App).mount('#app');
createApp(App).use(store).mount('#app');

109
src/store/index.ts Normal file
View File

@ -0,0 +1,109 @@
import { createStore } from 'vuex';
import IGamepad from '@/models/IGamepad';
import IGamepadEvent from '@/models/IGamepadEvent';
import TPattern from '@/models/TPattern';
import TPatternUnit from '@/models/TPatternUnit';
import Vibrator from '@/models/Vibrator';
export default createStore({
state: {
gamepads: [] as Vibrator[],
patterns: [] as TPattern[],
mode: 0 as number,
isActive: false as boolean,
},
getters: {
gamepads: function (state): Vibrator[] {
return state.gamepads as Vibrator[];
},
patterns: function (state): TPattern[] {
return state.patterns as TPattern[];
},
mode: function (state): number {
return state.mode as number;
},
isActive: function (state): boolean {
return state.isActive as boolean;
},
},
mutations: {
setPatterns: function (state, patterns: TPattern[]): void {
state.patterns = patterns as TPattern[];
},
setMode: function (state, mode: number): void {
state.mode = mode as number;
},
setIsActive: function (state, isActive: boolean): void {
state.isActive = isActive as boolean;
},
addGamepad: function (state, gamepad: Vibrator): void {
state.gamepads.push(gamepad as Vibrator);
},
deleteGamepad: function (state, index: number): void {
state.gamepads.splice(index, 1);
},
},
actions: {
loadPatterns: async function (context): Promise<void> {
const url = 'https://wavelovers.ru/assets/patterns.json';
try {
const response: Response = await fetch(url);
if (response.ok) {
const json: TPattern[] = await response.json();
context.commit('setPatterns', json as TPattern[]);
} else {
console.log('Connect to the Internet for download more patterns...');
}
} catch (error) {
console.log(error);
}
},
setMode: function (context, index: number): void {
context.commit('setMode', index as number);
},
setIsActive: function (context, isActive: boolean): void {
context.commit('setIsActive', isActive as boolean);
},
addGamepad: function (context, event: GamepadEvent): void {
const iEvent: IGamepadEvent = event as unknown as IGamepadEvent;
if (context.getters.gamepads.length >= 1) {
return;
} else {
context.commit('addGamepad', new Vibrator(iEvent.gamepad as IGamepad));
}
},
deleteGamepad: function (context, event: GamepadEvent): void {
context.getters.gamepads.forEach((gamepad: Vibrator, index: number) => {
if (gamepad.unit.id === event.gamepad.id) {
context.commit('deleteGamepad', index as number);
}
});
},
vibrate: function (context): void {
context.getters.gamepads.forEach((gamepad: Vibrator) => {
gamepad.vibrate(context.getters.patterns[context.getters.mode].pattern as TPatternUnit[]);
});
},
reset: function (context): void {
context.getters.gamepads.forEach((gamepad: Vibrator) => {
gamepad.reset();
});
},
change: function (context, index: number): void {
if (context.getters.mode === index) {
context.dispatch('setIsActive', !context.getters.isActive);
} else {
context.dispatch('setIsActive', true);
context.dispatch('setMode', index);
}
if (context.getters.isActive === true) {
context.dispatch('reset');
context.dispatch('vibrate');
} else {
context.dispatch('reset');
}
},
},
modules: {},
});