diff --git a/package-lock.json b/package-lock.json index 06b56a33..2849bf60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -667,7 +667,7 @@ }, "ansi-escapes": { "version": "3.1.0", - "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", "dev": true }, @@ -735,7 +735,7 @@ }, "array-flatten": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, "array-union": { @@ -799,7 +799,7 @@ }, "util": { "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -1304,7 +1304,7 @@ }, "callsites": { "version": "0.2.0", - "resolved": "http://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", "dev": true }, @@ -1678,7 +1678,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -1915,7 +1915,7 @@ }, "css-select": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { @@ -2176,7 +2176,7 @@ "dependencies": { "domelementtype": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", "dev": true } @@ -2251,7 +2251,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -2538,7 +2538,7 @@ "dependencies": { "doctrine": { "version": "1.5.0", - "resolved": "http://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", "dev": true, "requires": { @@ -2630,7 +2630,7 @@ }, "events": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", "dev": true }, @@ -3005,7 +3005,7 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "requires": { "debug": "2.6.9", @@ -3079,7 +3079,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -3140,7 +3140,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -3735,7 +3735,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, @@ -4334,7 +4334,7 @@ }, "is-accessor-descriptor": { "version": "0.1.6", - "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { @@ -4401,7 +4401,7 @@ }, "is-data-descriptor": { "version": "0.1.4", - "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { @@ -4690,7 +4690,7 @@ }, "json5": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" }, "jsonlint": { @@ -4988,7 +4988,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "mem": { @@ -5029,7 +5029,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -5760,7 +5760,7 @@ }, "os-tmpdir": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, @@ -5778,7 +5778,7 @@ }, "p-is-promise": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=", "dev": true }, @@ -5837,7 +5837,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -5910,7 +5910,7 @@ }, "path-browserify": { "version": "0.0.0", - "resolved": "http://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", "dev": true }, @@ -5927,7 +5927,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { @@ -7930,7 +7930,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -8132,7 +8132,7 @@ }, "require-uncached": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { @@ -8274,7 +8274,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -8643,7 +8643,7 @@ }, "sprintf-js": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, @@ -8690,7 +8690,7 @@ }, "stream-browserify": { "version": "2.0.1", - "resolved": "http://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", "dev": true, "requires": { @@ -8715,7 +8715,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -8764,7 +8764,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -8808,7 +8808,7 @@ }, "string_decoder": { "version": "0.10.31", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true }, @@ -8837,7 +8837,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, @@ -9095,7 +9095,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -9215,7 +9215,7 @@ }, "tty-browserify": { "version": "0.0.0", - "resolved": "http://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, @@ -9369,7 +9369,7 @@ }, "underscore": { "version": "1.6.0", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" }, "unherit": { @@ -9714,7 +9714,7 @@ }, "vm-browserify": { "version": "0.0.4", - "resolved": "http://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", "dev": true, "requires": { @@ -9939,7 +9939,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { diff --git a/src/app.js b/src/app.js index a84cf7ee..4f336ac0 100644 --- a/src/app.js +++ b/src/app.js @@ -1,8 +1,7 @@ import Router from './modules/router'; -import MainApp from './applications/menu/menu_app'; +import MainApp from './applications/main/main_app'; import Terminal from './applications/terminal/terminal_app'; import Snake from './applications/snake/game_app'; -import Game from './applications/frame/game_app'; import '../static/favicon.ico'; import './style.pcss'; @@ -10,5 +9,11 @@ import './style.pcss'; new Router(document.body, MainApp) .registerApp('terminal', Terminal) .registerApp('snake', Snake) - .registerApp('test', Game, 'https://www.innogames.com/games/all-games/') .start(); + + +if ('serviceWorker' in navigator) { + navigator.serviceWorker + .register('./sw.js') + .catch(err => console.error({ err })); +} diff --git a/src/applications/base_app.js b/src/applications/base_app.js index e6dd72a5..6c3cd511 100644 --- a/src/applications/base_app.js +++ b/src/applications/base_app.js @@ -1,3 +1,5 @@ +import bus from '../modules/bus'; + export default class BaseApp { constructor(appURL, parent, MainView, Views) { this.url = appURL; @@ -18,6 +20,9 @@ export default class BaseApp { this.started = false; } + setBar(bar) { + this.bar = bar; + } changeView(viewUrl, params) { // write view change animations in overridden method @@ -37,11 +42,8 @@ export default class BaseApp { } launch(resource) { - if (resource) { - this.animateLaunch(resource); - return; - } - if (this.started) this.resume(); + if (resource) this.animateLaunch(resource); + else if (this.started) this.resume(); else this.start(); } @@ -88,12 +90,18 @@ export default class BaseApp { }; launchAnimation.play(); + + const name = resource.getAttribute('name'); + console.log(name); + setTimeout(() => bus.emit('addTile', name), 1000); } start() { this.started = true; this.active = true; this.currentView.show(); + + this.bar.show(); } stop() { @@ -101,12 +109,26 @@ export default class BaseApp { this.active = false; this.currentView.hide(); this.parent.innerHTML = ''; + this.bar.hide(); } pause() { this.parent.style.background = 'none'; this.active = false; - this.currentView.hide(); + const barAnimation = this.bar.hide(); + if (barAnimation) { + barAnimation.pause(); + barAnimation.onfinish = () => { + this.currentView.hide(); + }; + barAnimation.play(); + } else { + this.currentView.hide(); + } + } + + animateClose() { + } resume() { @@ -114,7 +136,8 @@ export default class BaseApp { this.start(); return; } - this.active = true; + this.bar.show(); this.currentView.show(); + this.active = true; } } diff --git a/src/applications/chat/templates/room.pug b/src/applications/chat/templates/room.pug index 401ccf8f..65772b33 100644 --- a/src/applications/chat/templates/room.pug +++ b/src/applications/chat/templates/room.pug @@ -1,6 +1,6 @@ .room(room_id= room.room_id ) img( - src= "https://static.rasseki.com" + room.avatar + src= "https://snakewave.com" + room.avatar ).room__avatar .room__text .room__name= room.room_id diff --git a/src/applications/element.js b/src/applications/element.js index 5771189e..6010b431 100644 --- a/src/applications/element.js +++ b/src/applications/element.js @@ -12,8 +12,6 @@ export default class Element { // sure? if (wrapperClass) { this.wrapper.classList.add(...wrapperClass); - } else { - this.wrapper.classList.add('wrapper'); } // sure? diff --git a/src/applications/frame/components/frame.css b/src/applications/frame/components/frame.pcss similarity index 56% rename from src/applications/frame/components/frame.css rename to src/applications/frame/components/frame.pcss index 025a149b..cfa38f2c 100644 --- a/src/applications/frame/components/frame.css +++ b/src/applications/frame/components/frame.pcss @@ -1,6 +1,5 @@ .frame { overflow: hidden; - // height: 100%; - height: 95vh; + height: 100%; width: 100%; } diff --git a/src/applications/frame/components/frame_view.js b/src/applications/frame/components/frame_view.js index 217824d4..6b837c26 100644 --- a/src/applications/frame/components/frame_view.js +++ b/src/applications/frame/components/frame_view.js @@ -2,15 +2,18 @@ import Element from '../../element'; import template from './frame.pug'; -// import './styles/frame.css'; +import './frame.pcss'; export default class FrameView extends Element { constructor(parent, wrapper, source) { super(template, parent, wrapper); this.source = source; + this.noRender = true; } render() { + // console.log('here'); + // [this.frame] = this.wrapper.getElementsByClassName('frame'); super.render({ source: this.source }); } diff --git a/src/applications/main/components/app-table/app-table.js b/src/applications/main/components/app-table/app-table.js new file mode 100644 index 00000000..0072287f --- /dev/null +++ b/src/applications/main/components/app-table/app-table.js @@ -0,0 +1,39 @@ +import { getAllApps } from '../../../../modules/network'; + +import Element from '../../../element'; + +import '../app_tile/app_tile'; + +import './app-table.pcss'; +import template from './app-table.pug'; +import appTemplate from './app.pug'; + +import bus from '../../../../modules/bus'; + +export default class AppTable extends Element { + constructor(parent, wrapper) { + super(template, parent, wrapper || parent); + this.wrapper.addEventListener('click', (ev) => { + if (!(ev.target instanceof HTMLImageElement)) return; + const name = ev.target.getAttribute('name'); + console.log('emitted'); + bus.emit('about', name); + }); + } + + async render(askedCategory) { + console.log('rendering matrix'); + let category; + if (!askedCategory) category = 'all'; + + if (category === 'all') { + const { apps, err } = await getAllApps(); + console.log(apps, err); + if (!err) { + apps.apps.forEach((app) => { + this.wrapper.innerHTML += appTemplate({ app }); + }); + } + } + } +} diff --git a/src/applications/main/components/app-table/app-table.pcss b/src/applications/main/components/app-table/app-table.pcss new file mode 100644 index 00000000..cd236233 --- /dev/null +++ b/src/applications/main/components/app-table/app-table.pcss @@ -0,0 +1,28 @@ +.app-table { + grid-area: app-table; + display: flex; + flex-direction: row; + justify-content: center; + + flex-wrap: wrap; + + scroll-snap-type: y mandatory; + overflow: scroll; + + padding: 0 10px; + border-left: 1px solid rgba(100%, 100%, 100%, 0.6); + border-right: 1px solid rgba(100%, 100%, 100%, 0.6); + + &__tile { + scroll-snap-align: start; + + margin: 10px; + scroll-margin: 10px; + height: calc(64vh / 3 - 27px); + width: 29vh; + } + + &__card { + box-shadow: 0 0 15px 1px rgba(0, 0, 0, 0.4); + } +} diff --git a/src/applications/main/components/app-table/app-table.pug b/src/applications/main/components/app-table/app-table.pug new file mode 100644 index 00000000..e69de29b diff --git a/src/applications/main/components/app-table/app.pug b/src/applications/main/components/app-table/app.pug new file mode 100644 index 00000000..9877981c --- /dev/null +++ b/src/applications/main/components/app-table/app.pug @@ -0,0 +1,6 @@ +.tile.app-table__tile + a.tile__card.app-table__card + img.tile__background( + name= app.name + src='https://snakewave.com/' + app.image + ) diff --git a/src/applications/main/components/app-table/mock.js b/src/applications/main/components/app-table/mock.js new file mode 100644 index 00000000..9f26494a --- /dev/null +++ b/src/applications/main/components/app-table/mock.js @@ -0,0 +1,92 @@ +const storeApps = { + popular: [ + { + link: '/terminal', + image: '/img/terminal.jpg', + name: 'Купи популярное говно', + }, + { + link: '/terminal', + image: '/img/terminal.jpg', + name: 'Купи популярное говно', + }, + { + link: '/terminal', + image: '/img/terminal.jpg', + name: 'Купи популярное говно', + }, + { + link: '/terminal', + image: '/img/terminal.jpg', + name: 'Купи популярное говно', + }, + ], + '2018_2': [ + { + link: '/terminal', + image: '/img/terminal.jpg', + name: 'Купи говно', + }, + { + link: '/terminal', + image: '/img/terminal.jpg', + name: 'Купи говно', + }, + { + link: '/terminal', + image: '/img/terminal.jpg', + name: 'Купи говно', + }, + { + link: '/terminal', + image: '/img/terminal.jpg', + name: 'Купи говно', + }, + ], + '2018_1': [ + { + link: '/terminal', + image: '/img/terminal.jpg', + name: 'Купи старое говно', + }, + { + link: '/terminal', + image: '/img/terminal.jpg', + name: 'Купи старое говно', + }, + { + link: '/terminal', + image: '/img/terminal.jpg', + name: 'Купи старое говно', + }, + { + link: '/terminal', + image: '/img/terminal.jpg', + name: 'Купи старое говно', + }, + ], + '2017_2': [ + { + link: '/terminal', + image: '/img/terminal.jpg', + name: 'Купи древнее говно', + }, + { + link: '/terminal', + image: '/img/terminal.jpg', + name: 'Купи древнее говно', + }, + { + link: '/terminal', + image: '/img/terminal.jpg', + name: 'Купи древнее говно', + }, + { + link: '/terminal', + image: '/img/terminal.jpg', + name: 'Купи древнее говно', + }, + ], +}; + +export default storeApps; diff --git a/src/applications/menu/components/app_tile/app_tile.js b/src/applications/main/components/app_tile/app_tile.js similarity index 66% rename from src/applications/menu/components/app_tile/app_tile.js rename to src/applications/main/components/app_tile/app_tile.js index 2f6bbf4a..5fcd58ce 100644 --- a/src/applications/menu/components/app_tile/app_tile.js +++ b/src/applications/main/components/app_tile/app_tile.js @@ -6,10 +6,12 @@ import './app_tile.pcss'; export default class AppTile extends Element { - constructor(parent, data) { + constructor(parent, data, cssClass) { super(template, parent); this.data = data; this.render(data); + const [element] = this.wrapper.getElementsByClassName('app__tile'); + element.classList.add(cssClass); } render(data) { diff --git a/src/applications/menu/components/app_tile/app_tile.pcss b/src/applications/main/components/app_tile/app_tile.pcss similarity index 73% rename from src/applications/menu/components/app_tile/app_tile.pcss rename to src/applications/main/components/app_tile/app_tile.pcss index fb25aa3d..139c51a1 100644 --- a/src/applications/menu/components/app_tile/app_tile.pcss +++ b/src/applications/main/components/app_tile/app_tile.pcss @@ -6,13 +6,28 @@ justify-content: center; height: 90%; - width: calc((100vw - 2*var(--grid-padding-x)) / 3 - 20px); margin: 0 10px; scroll-margin: 10px; scroll-snap-align: start; + transition: all 0.3s ease-out; + + &:hover > .app__card { + box-shadow: 0 0 15px 2px #4462C4; + transform: scale(1.03); + } + + &:hover > .app__card::after { + transform: scale(1.03) translateY(3px); + box-shadow: 0px 0px 60px 5px #4462C4; + } + + &:hover .tile__name { + opacity: 0; + } } + &__card { display: flex; justify-content: center; @@ -20,6 +35,7 @@ background-color: black; position: relative; box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); + transition: all 0.3s ease-out; &:after { width: 70%; @@ -29,6 +45,7 @@ border-radius: 100px; box-shadow: 0px 0px 60px 3px black; + transition: all 0.3s ease-out; } } } diff --git a/src/applications/menu/components/app_tile/app_tile.pug b/src/applications/main/components/app_tile/app_tile.pug similarity index 65% rename from src/applications/menu/components/app_tile/app_tile.pug rename to src/applications/main/components/app_tile/app_tile.pug index f6574545..201050a5 100644 --- a/src/applications/menu/components/app_tile/app_tile.pug +++ b/src/applications/main/components/app_tile/app_tile.pug @@ -1,9 +1,10 @@ .tile.app__tile a.tile__card.app__card( + name= tile.name href= tile.link ) img.tile__background( - src= tile.image + src= 'https://snakewave.com/' + tile.image ) div .tile__name= tile.name diff --git a/src/applications/main/components/bar/bar.js b/src/applications/main/components/bar/bar.js new file mode 100644 index 00000000..30ef13bd --- /dev/null +++ b/src/applications/main/components/bar/bar.js @@ -0,0 +1,55 @@ +import Element from '../../../element'; + +import './bar.pcss'; + +import template from './bar.pug'; + +import userService from '../../../../modules/userservice'; + +export default class Bar extends Element { + constructor(parent, wrapper) { + super(template, parent, wrapper || parent); + super.render(); + this.hidden = true; + this.show = this.show.bind(this); + this.trigger = this.wrapper.getElementsByClassName('hover-mock'); + this.trigger.onclick = () => { + this.show(); + }; + } + + show() { + const { err, loggedIn } = userService.isLoggedIn(); + if (err || !loggedIn) return false; + if (!this.hidden) return false; + const [bar] = this.wrapper.getElementsByClassName('bar'); + this.hidden = false; + return bar.animate({ + transform: [ + 'translateY(-100px)', + 'translateY(0px)', + ], + }, { + duration: 300, + fill: 'forwards', + easing: 'cubic-bezier(.36,1.08,.55,.93)', + }); + } + + hide() { + clearTimeout(this.hideTimeout); + if (this.hidden) return false; + const [bar] = this.wrapper.getElementsByClassName('bar'); + this.hidden = true; + return bar.animate({ + transform: [ + 'translateY(0px)', + 'translateY(-65px)', + ], + }, { + duration: 100, + fill: 'forwards', + easing: 'cubic-bezier(.36,1.08,.55,.93)', + }); + } +} diff --git a/src/applications/main/components/bar/bar.pcss b/src/applications/main/components/bar/bar.pcss new file mode 100644 index 00000000..91ed9a54 --- /dev/null +++ b/src/applications/main/components/bar/bar.pcss @@ -0,0 +1,30 @@ +.bar { + position: absolute; + height: 50px; + width: 50px; + margin: 10px; + top: 0px; + right: 0px; + background-color: rgb(100%, 100%, 100%, 0.6); + border-radius: 5px; + z-index: 100; + + display: flex; + justify-content: center; + align-items: center; + font-size: 40px; + color: black; + text-decoration: none; + transform: translateY(-100px); +} + +.hover-mock { + position: absolute; + height: 7px; + width: 7px; + margin: 10px; + border-radius: 50%; + border: 2px solid black; + top: 0px; + right: 0px; +} diff --git a/src/applications/main/components/bar/bar.pug b/src/applications/main/components/bar/bar.pug new file mode 100644 index 00000000..eb92fb4d --- /dev/null +++ b/src/applications/main/components/bar/bar.pug @@ -0,0 +1 @@ +a.bar(href="/").material-icons home diff --git a/src/applications/main/components/description/description.js b/src/applications/main/components/description/description.js new file mode 100644 index 00000000..06da6b06 --- /dev/null +++ b/src/applications/main/components/description/description.js @@ -0,0 +1,63 @@ +import Element from '../../../element'; + +import bus from '../../../../modules/bus'; +import { getApp, addApp } from '../../../../modules/network'; + +import template from './description.pug'; +import './description.pcss'; + + +export default class Description extends Element { + constructor(parent, wrapper) { + super(template, parent, wrapper || parent); + + bus.listen('about', this.render.bind(this)); + + this.wrapper.addEventListener('click', async (ev) => { + console.log(ev.target); + if (ev.target.classList.contains('add-button')) { + const { err } = await addApp(this.shownApp.name); + if (err) console.error(err); + else { + const element = ev.target; + bus.emit('appInstalled'); + const firstAnimation = element.animate({ + color: [ + 'rgba(100%, 100%, 100%, 0.8)', + 'rgba(100%, 100%, 100%, 0)', + ], + }, { + duration: 200, + fill: 'forwards', + easing: 'cubic-bezier(0.6, 0.04, 0.98, 0.335)', + }); + firstAnimation.pause(); + firstAnimation.onfinish = () => { + firstAnimation.onfinish = null; + element.innerText = 'installed'; + element.classList.remove('add-button'); + firstAnimation.reverse(); + }; + firstAnimation.play(); + } + } + }); + + this.render(); + } + + async render(appName) { + if (appName) { + const { err, app } = await getApp(appName); + if (err) console.error(err); + console.log(app); + + this.shownApp = app; + super.render({ app }); + return; + } + + this.shownApp = null; + super.render(); + } +} diff --git a/src/applications/main/components/description/description.pcss b/src/applications/main/components/description/description.pcss new file mode 100644 index 00000000..323269e4 --- /dev/null +++ b/src/applications/main/components/description/description.pcss @@ -0,0 +1,78 @@ +.description-container { + grid-area: description; + + display: flex; + flex-direction: row; + justify-content: flex-end; +} + +.description { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + padding-right: 10px; + + + &__tile { + width: 90%; + } + + &__background { + width: 100%; + object-fit: cover; + border-radius: 5px; + // border-radius: 0 5px 5px 0; + } + + &__title { + color: white; + font-family: Gilroy-Light; + font-size: 34px; + margin: 0 15px; + } + + &__about { + color: rgba(100%, 100%, 100%, 0.8); + font-family: Gilroy-Light; + font-size: 22px; + margin: 40px 15px; + } + + &__button { + display: flex; + justify-content: center; + align-items: center; + + height: 40px; + width: 90%; + margin-top: auto; + + color: rgba(100%, 100%, 100%, 0.8); + text-transform: uppercase; + font-family: Gilroy-Light; + font-size: 22px; + border-radius: 5px; + background-color: rgba(100%, 100%, 100%, 0.1); + + transition: all 0.3s ease-out; + } + + &__installs { + color: rgba(100%, 100%, 100%, 0.8); + font-family: Gilroy-Light; + font-size: 22px; + } +} + +.remove-button { + &:hover { + color: #fa2f2f; + } +} + +.add-button { + &:hover { + background-color: #4462C4; + } +} diff --git a/src/applications/main/components/description/description.pug b/src/applications/main/components/description/description.pug new file mode 100644 index 00000000..ca366715 --- /dev/null +++ b/src/applications/main/components/description/description.pug @@ -0,0 +1,16 @@ +.description + if app + .description__tile + img.description__background(src='https://snakewave.com/' + app.image) + .description__title= app.name + .description__installs= 'Installs: ' + app.installs + + .description__about= app.about + + + if app.installed + .description__button installed + else + .description__button.add-button add + else + .description__about Click on app to see infо about it diff --git a/src/applications/main/components/formAboutUs/formAboutUs.js b/src/applications/main/components/formAboutUs/formAboutUs.js new file mode 100644 index 00000000..cec0bbfa --- /dev/null +++ b/src/applications/main/components/formAboutUs/formAboutUs.js @@ -0,0 +1,30 @@ +import Element from '../../../element'; + +import bus from '../../../../modules/bus'; +// import { getApp, addApp } from '../../../../modules/network'; + +import template from './formAboutUs.pug'; +import './formAboutUs.pcss'; + + +export default class FormAboutUs extends Element { + constructor(parent, wrapper) { + super(template, parent, wrapper || parent); + + bus.listen('about', this.render.bind(this)); + this.render(); + } + + render(appName) { + // if (appName) { + // const { err, app } = await getApp(appName); + // if (err) console.error(err); + // this.shownApp = app; + // super.render({ app }); + // return; + // } + + // this.shownApp = null; + super.render(); + } +} diff --git a/src/applications/main/components/formAboutUs/formAboutUs.pcss b/src/applications/main/components/formAboutUs/formAboutUs.pcss new file mode 100644 index 00000000..3a8e0ec0 --- /dev/null +++ b/src/applications/main/components/formAboutUs/formAboutUs.pcss @@ -0,0 +1,106 @@ +.form-about-us-container { + grid-area: form-about-us; + + display: flex; + flex-direction: row; + justify-content: flex-end; +} + +.form-about-us { + font-family: Gilroy-Light; + font-size: 22px; + color: white; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + padding-right: 10px; + color: rgba(100%, 100%, 100%, .6); + + + &__tile { + width: 90%; + } + + &__background { + width: 100%; + object-fit: cover; + border-radius: 5px; + // border-radius: 0 5px 5px 0; + } + + &__title { + color: white; + font-family: Gilroy-Light; + font-size: 34px; + margin: 0 15px; + } + + &__about { + color: rgba(100%, 100%, 100%, 0.8); + font-family: Gilroy-Light; + font-size: 22px; + margin: 40px 15px; + } + + &__button { + display: flex; + justify-content: center; + align-items: center; + + height: 40px; + width: 90%; + margin-top: auto; + + color: rgba(100%, 100%, 100%, 0.8); + text-transform: uppercase; + font-family: Gilroy-Light; + font-size: 22px; + border-radius: 5px; + background-color: rgba(100%, 100%, 100%, 0.1); + + transition: all 0.3s ease-out; + } + + &__installs { + color: rgba(100%, 100%, 100%, 0.8); + font-family: Gilroy-Light; + font-size: 22px; + } +} + +.remove-button { + &:hover { + color: #fa2f2f; + } +} + +.add-button { + &:hover { + background-color: #4462C4; + } +} + +.form-about-us_text { + font-family: Gilroy-Light; + font-size: 22px; + text-align: center; +} + +.about_from { + padding: 7px; +} + +.about_button { + color: white; + font-family: Gilroy-Light; + padding: 7px; + width: 100px; + justify-content: center; + align-self: center; +} + +.about_form { + display: flex; + flex-direction: column; +} diff --git a/src/applications/main/components/formAboutUs/formAboutUs.pug b/src/applications/main/components/formAboutUs/formAboutUs.pug new file mode 100644 index 00000000..09179ed7 --- /dev/null +++ b/src/applications/main/components/formAboutUs/formAboutUs.pug @@ -0,0 +1,11 @@ +.form-about-us + .form-about-us_text Place for your feedback and suggestions for new games + form.about_form + .about_text(hidden) From: + input.about_from(type="text" name="fname" hidden) + br + .about_text(hidden) You may suggest us a new game + textarea.about_textarea(type="text-area" name="lname") + br + button.tile__card.menu__card.about_button(type="reset" value="Submit") Send + diff --git a/src/applications/main/components/icon-block/icon-block.js b/src/applications/main/components/icon-block/icon-block.js new file mode 100644 index 00000000..9d784d97 --- /dev/null +++ b/src/applications/main/components/icon-block/icon-block.js @@ -0,0 +1,29 @@ +import Element from '../../../element'; + + +import template from './icon-block.pug'; +import './icon-block.pcss'; + + +export default class IconBlock extends Element { + constructor(parent, wrapper) { + super(template, parent, wrapper || parent); + this.render(); + + const date = new Date(); + const timeout = (60 - date.getSeconds()) * 1000; + setTimeout(() => { + this.render(); + setInterval(() => this.render(), 60000); + }, timeout); + } + + render() { + const date = new Date(); + const hours = date.getHours(); + const minutes = date.getMinutes(); + const time = `${hours}:${(minutes < 10) ? 0 : ''}${minutes}`; + + super.render({ time }); + } +} diff --git a/src/applications/main/components/icon-block/icon-block.pcss b/src/applications/main/components/icon-block/icon-block.pcss new file mode 100644 index 00000000..110cdb54 --- /dev/null +++ b/src/applications/main/components/icon-block/icon-block.pcss @@ -0,0 +1,16 @@ +.icon-block { +} + +.iconblock { + display: flex; + flex-direction: row; + justify-content: flex-end; + align-items: center; +} + +.clock { + color: white; + font-family: Gilroy-Light; + font-size: 26px; + margin: 0 23px; +} diff --git a/src/applications/main/components/icon-block/icon-block.pug b/src/applications/main/components/icon-block/icon-block.pug new file mode 100644 index 00000000..3a69cb8d --- /dev/null +++ b/src/applications/main/components/icon-block/icon-block.pug @@ -0,0 +1 @@ +.clock= time diff --git a/src/applications/main/components/list/list.js b/src/applications/main/components/list/list.js new file mode 100644 index 00000000..45a29db2 --- /dev/null +++ b/src/applications/main/components/list/list.js @@ -0,0 +1,17 @@ +import Element from '../../../element'; + +import './list.pcss'; + +import template from './list.pug'; + + +export default class List extends Element { + constructor(parent, wrapper) { + super(template, parent, wrapper || parent); + } + + render(categories) { + console.trace(); + super.render({ categories }); + } +} diff --git a/src/applications/main/components/list/list.pcss b/src/applications/main/components/list/list.pcss new file mode 100644 index 00000000..d98a617a --- /dev/null +++ b/src/applications/main/components/list/list.pcss @@ -0,0 +1,29 @@ +.list { + display: flex; + flex-direction: column; + justify-content: center; + + grid-area: list; + width: 250px; +} + +.category { + display: flex; + justify-content: flex-start; + align-items: center; + + padding: 0 0 0 15px; + width: 235px; + height: 50px; + + border-radius: 5px 0 0 5px; + color: rgba(100%, 100%, 100%, 0.6); + font-family: Gilroy-Light; + font-size: 26px; + text-transform: capitalize; + + &:hover { + color: white; + background-color: rgba(100%, 100%, 100%,0.1); + } +} diff --git a/src/applications/main/components/list/list.pug b/src/applications/main/components/list/list.pug new file mode 100644 index 00000000..da8ba3bb --- /dev/null +++ b/src/applications/main/components/list/list.pug @@ -0,0 +1,2 @@ +for val in categories + .category= val diff --git a/src/applications/main/components/menu/menu.js b/src/applications/main/components/menu/menu.js new file mode 100644 index 00000000..03299e5c --- /dev/null +++ b/src/applications/main/components/menu/menu.js @@ -0,0 +1,112 @@ +import Element from '../../../element'; +import { getApp } from '../../../../modules/network'; + +import '../tile-panel/tile-panel.pcss'; +import '../tile/tile.pcss'; +import './menu.pcss'; + +import template from './menu.pug'; +import tileTemplate from './tile.pug'; +import appTileTemplate from './menu_app-tile.pug'; +import bus from '../../../../modules/bus'; + +const tiles = [ + { + link: '/home', + name: 'Home', + icon: 'home', + }, + { + link: '/store', + name: 'Store', + icon: 'local_mall', + }, + { + link: '/about', + name: 'About us', + icon: 'accessible_forward', + }, +]; + + +export default class Menu extends Element { + constructor(parent, wrapper) { + super(template, parent, wrapper); + super.render(); + [this.panel] = this.wrapper.getElementsByClassName('menu__tile-panel'); + this.render(); + + bus.listen('addTile', this.addTile.bind(this)); + } + + async addTile(appName) { + const { err, app } = await getApp(appName); + if (err) { + console.error(err); + return false; + } + + const foundTiles = this.panel.querySelectorAll(`[name="${app.name}"]`); + const childs = this.panel.childNodes; + + console.log(foundTiles); + + if (foundTiles.length !== 0) foundTiles[0].remove(); + + if (childs.length <= 3) { + this.panel.innerHTML += appTileTemplate({ tile: app }); + return true; + } + console.log(childs); + + const mock = document.createElement('div'); + mock.innerHTML = appTileTemplate({ tile: app }); + const [elem] = mock.getElementsByClassName('menu__tile'); + + this.panel.insertBefore(elem, childs[3]); + return true; + } + + show() { + if (!this.rendered) this.render(); + + const showAnimation = this.wrapper.animate({ + transform: [ + 'translateY(250px)', + 'translateY(0px)', + ], + }, { + duration: 200, + fill: 'forwards', + easing: 'cubic-bezier(.36,1.08,.55,.93)', + }); + showAnimation.pause(); + showAnimation.onfinish = () => super.show(); + showAnimation.play(); + } + + hide() { + const hideAnimation = this.wrapper.animate({ + transform: [ + 'translateY(0px)', + 'translateY(250px)', + ], + }, { + duration: 200, + fill: 'forwards', + easing: 'cubic-bezier(.36,1.08,.55,.93)', + }); + hideAnimation.pause(); + hideAnimation.onfinish = () => super.hide(); + hideAnimation.play(); + } + + render() { + this.panel.innerHTML = ''; + tiles.forEach((tile) => { + this.panel.innerHTML += tileTemplate({ tile }); + // this.tileList += tile; + }); + console.log('menu rendered'); + } +} diff --git a/src/applications/main/components/menu/menu.pcss b/src/applications/main/components/menu/menu.pcss new file mode 100644 index 00000000..9918aacb --- /dev/null +++ b/src/applications/main/components/menu/menu.pcss @@ -0,0 +1,138 @@ +@import '../../../../config.pcss'; + +.menu { + grid-area: menu; + display: flex; + flex-direction: column; + justify-content: center; + + &__tile-panel { + overflow: hidden; + width: 100%; + height: 100%; + align-items: center; + } + + &__tile { + justify-content: flex-end; + + height: 17vh; + width: calc((100vw - 2 * var(--grid-padding-x)) / 4 - 20px); + // max-width: 300px; + margin: 0 10px; + + &:hover > .tile__card { + transform: translateY(-10px); + background-color: #4462C4; + } + + &:hover > .tile__card::after { + transform: scale(.9); + box-shadow: 0px 0px 30px 2px #4462C4; + } + + &:hover .tile__name, &:hover .menu__icon { + color: white; + transform: scale(1.2); + } + } + + &__app-tile { + } + + &__app-card { + } + + &__tile-background { + object-fit: contain; + + width: 90%; + height: 90%; + } + + &__card { + display: flex; + justify-content: center; + align-items: center; + + text-decoration: none !important; + background-color: rgba(100%, 100%, 100%,0.1); + + &:after { + bottom: -4px; + transition: all 0.4s ease-out; + } + } + + &__icon { + color: rgb(100%, 100%, 100%, 0.6); + font-size: 56px; + + transition: all 0.3s ease-out; + } +} + +@media (--boss) { + .menu__tile { + width: 10vw; + } +} + +@media (--large) { + .menu__tile { + width: calc((100vw - 2 * var(--grid-padding-x)) / 8 - 20px); + } +} + +@media (min-width: 1800px) and (max-width: 2100px) { + .menu__tile { + width: calc((100vw - 2 * var(--grid-padding-x)) / 7 - 20px); + } +}; + +@media (min-width: 1500px) and (max-width: 1800px) { + .menu__tile { + width: calc((100vw - 2 * var(--grid-padding-x)) / 6 - 20px); + } +}; + +@media (min-width: 1200px) and (max-width: 1500px) { + .menu__tile { + width: calc((100vw - 2 * var(--grid-padding-x)) / 5 - 20px); + } +}; + +@media (--small) { + .menu__tile { + width: calc((100vw - 2 * var(--small-grid-padding-x)) / 3 - 20px); + } +} + +@media (--minimal) { + .menu__tile-panel { + width: calc(100vw - 2 * var(--minimal-grid-padding-x)); + + display: flex; + flex-direction: row; + justify-content: flex-start; + scroll-snap-type: x mandatory; + overflow: scroll; + } + + .menu__tile { + scroll-snap-align: start; + scroll-margin: 10px; + } +} + +@media (--tablet) { + .menu__tile { + width: calc((100vw - 2 * var(--minimal-grid-padding-x)) / 3 - 20px); + } +}; + +@media (--mobile) { + .menu__tile { + width: calc((100vw - 2 * var(--minimal-grid-padding-x)) / 2 - 20px); + } +}; diff --git a/src/applications/main/components/menu/menu.pug b/src/applications/main/components/menu/menu.pug new file mode 100644 index 00000000..40f561c6 --- /dev/null +++ b/src/applications/main/components/menu/menu.pug @@ -0,0 +1 @@ +.tile-panel.menu__tile-panel diff --git a/src/applications/main/components/menu/menu_app-tile.pug b/src/applications/main/components/menu/menu_app-tile.pug new file mode 100644 index 00000000..e36dca22 --- /dev/null +++ b/src/applications/main/components/menu/menu_app-tile.pug @@ -0,0 +1,10 @@ +.tile.menu__tile(name= tile.name) + a.tile__card.menu__card( + href= tile.link + name= tile.name + ) + img.menu__tile-background( + src= 'https://snakewave.com/' + tile.image + ) + div + .tile__name= tile.name diff --git a/src/applications/main/components/menu/tile.pug b/src/applications/main/components/menu/tile.pug new file mode 100644 index 00000000..215c30df --- /dev/null +++ b/src/applications/main/components/menu/tile.pug @@ -0,0 +1,7 @@ +.tile.menu__tile(name= tile.name) + a.tile__card.menu__card( + href= tile.link + ) + i.material-icons.menu__icon= tile.icon + div + .tile__name= tile.name diff --git a/src/applications/main/components/profile/profile.js b/src/applications/main/components/profile/profile.js new file mode 100644 index 00000000..3827b9ef --- /dev/null +++ b/src/applications/main/components/profile/profile.js @@ -0,0 +1,68 @@ +import Element from '../../../element'; +import { updateProfile } from '../../../../modules/network'; + +import './profile.pcss'; + +import '../../../../../static/img/triss.jpg'; + +import template from './profile.pug'; + + +export default class Profile extends Element { + constructor(parent, wrapper) { + super(template, parent, wrapper || parent); + + this.photoUpload = this.photoUpload.bind(this); + } + + update() { + this.render(); + } + + photoUpload() { + [this.avatarUpload] = document.getElementsByClassName('profile-avatar-upload'); + this.avatarUpload.click(); + } + + show() { + this.render(); + [this.avatarWrapper] = document.getElementsByClassName('profile__avatar-wrapper'); + this.avatarWrapper.addEventListener('click', this.photoUpload); + super.show(); + [this.avatarUpload] = document.getElementsByClassName('profile-avatar-upload'); + this.avatarUpload.addEventListener('change', this.updateAvatar); + } + + async updateAvatar() { + let form = document.getElementById('uploadForm'); + const registerData = new FormData(form); + console.log(registerData); + const { err } = await updateProfile(registerData); + if (err) { + console.error(err); + } + console.log('sendAvatar'); + this.render(); + } + + render() { + super.render(); + } + // async render() { + // const { loggedIn } = userService.isLoggedIn(); + + // if (!loggedIn) { + // const user = { username: '' }; + // super.render({ user, authorized: false }); + // return; + // } + + + // const authorized = true; + // const { user } = userService.getUser(); + // super.render({ user, authorized }); + +// const [profileButton] = this.wrapper.getElementsByClassName('userblock__avatar'); +// profileButton.addEventListener('click', () => bus.emit('link', '/profile')); +// } +} diff --git a/src/applications/main/components/profile/profile.pcss b/src/applications/main/components/profile/profile.pcss new file mode 100644 index 00000000..5796870c --- /dev/null +++ b/src/applications/main/components/profile/profile.pcss @@ -0,0 +1,75 @@ +.profile__user-data { + grid-area: user-data; + + display: flex; + flex-direction: column; + // align-items: center; + justify-content: center; +} + +.profile__user-data-wrapper { + + width: 34vh; + grid-area: user-data; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; +} + +.profile__avatar-wrapper { + --icon-diameter: 32vh; + height: var(--icon-diameter); + width: var(--icon-diameter); + + border-radius: 5%; + + overflow: hidden; + margin: 5px; + + cursor: pointer; +} + +.profile__avatar { + width: inherit; + height: inherit; + margin: 0; +} + +.profile__name { + text-align: center; + color: white; + font-family: Gilroy-Light; + font-size: 26px; + margin: 0 15px; +} + +.profile__edit-button { + + height: 5vh; + width: 21vh; + align-self: center; + + color: white; + font-family: Gilroy-Light; + border-radius: 5px; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; +} + +.overlay { + opacity:0; + transition: .5s ease; + +} + +.profile__avatar-wrapper:hover .avatar-overlay { + opacity: 1; +} \ No newline at end of file diff --git a/src/applications/main/components/profile/profile.pug b/src/applications/main/components/profile/profile.pug new file mode 100644 index 00000000..e05eeba3 --- /dev/null +++ b/src/applications/main/components/profile/profile.pug @@ -0,0 +1,15 @@ +.profile__user-data-wrapper + .profile__avatar-wrapper + img.profile__avatar(src='/img/triss.jpg') + .avatar-overlay + form#uploadForm(name='upload-form' enctype='multipart/form-data') + input.profile-avatar-upload(name="avatar" type="file" hidden) + + .profile__name Triss Merigold + +//- if !authorized +//- a.userblock__button(href='/login') login +//- else +//- img.icon__img(src= "https://static.rasseki.com" + user.avatar) + +//- .userblock__name= user.username diff --git a/src/applications/main/components/profile_discription/profile_description.js b/src/applications/main/components/profile_discription/profile_description.js new file mode 100644 index 00000000..7e1fe4b0 --- /dev/null +++ b/src/applications/main/components/profile_discription/profile_description.js @@ -0,0 +1,31 @@ +import Element from '../../../element'; + +import bus from '../../../../modules/bus'; +import { getApp, addApp } from '../../../../modules/network'; + +import template from './profile_description.pug'; +import './profile_description.pcss'; + + +export default class ProfileDescription extends Element { + constructor(parent, wrapper) { + super(template, parent, wrapper || parent); + + bus.listen('profile', this.render.bind(this)); + + this.render(); + } + + async render(appName) { + if (appName) { + const { err, app } = await getApp(appName); + if (err) console.error(err); + this.shownApp = app; + super.render({ app }); + return; + } + + this.shownApp = null; + super.render(); + } +} diff --git a/src/applications/main/components/profile_discription/profile_description.pcss b/src/applications/main/components/profile_discription/profile_description.pcss new file mode 100644 index 00000000..3ac85603 --- /dev/null +++ b/src/applications/main/components/profile_discription/profile_description.pcss @@ -0,0 +1,78 @@ +.profile-description-container { + grid-area: profile-description; + + display: flex; + flex-direction: row; + justify-content: flex-end; +} + +.profile-description { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + padding-right: 10px; + + + &__tile { + width: 90%; + } + + &__background { + width: 100%; + object-fit: cover; + border-radius: 5px; + // border-radius: 0 5px 5px 0; + } + + &__title { + color: white; + font-family: Gilroy-Light; + font-size: 34px; + margin: 0 15px; + } + + &__about { + color: rgba(100%, 100%, 100%, 0.8); + font-family: Gilroy-Light; + font-size: 22px; + margin: 40px 15px; + } + + &__button { + display: flex; + justify-content: center; + align-items: center; + + height: 40px; + width: 90%; + margin-top: auto; + + color: rgba(100%, 100%, 100%, 0.8); + text-transform: uppercase; + font-family: Gilroy-Light; + font-size: 22px; + border-radius: 5px; + background-color: rgba(100%, 100%, 100%, 0.1); + + transition: all 0.3s ease-out; + } + + &__installs { + color: rgba(100%, 100%, 100%, 0.8); + font-family: Gilroy-Light; + font-size: 22px; + } +} + +.remove-button { + &:hover { + color: #fa2f2f; + } +} + +.add-button { + &:hover { + background-color: #4462C4; + } +} diff --git a/src/applications/main/components/profile_discription/profile_description.pug b/src/applications/main/components/profile_discription/profile_description.pug new file mode 100644 index 00000000..2e4dc416 --- /dev/null +++ b/src/applications/main/components/profile_discription/profile_description.pug @@ -0,0 +1,12 @@ +.profile-description + if app + .profile-description__tile + img.profile-description__background(src='https://rasseki.com/' + app.image) + .profile-description__title= app.name + .profile-description__installs= 'Installs: ' + app.installs + + .profile-description__about= app.about + .profile-description__spent-time= app.spent_time + + else + .profile-description__about Click on app to see amount of time spent in the game diff --git a/src/applications/menu/components/tile-panel/tile-panel.pcss b/src/applications/main/components/tile-panel/tile-panel.pcss similarity index 82% rename from src/applications/menu/components/tile-panel/tile-panel.pcss rename to src/applications/main/components/tile-panel/tile-panel.pcss index 35df37f2..d46ae80c 100644 --- a/src/applications/menu/components/tile-panel/tile-panel.pcss +++ b/src/applications/main/components/tile-panel/tile-panel.pcss @@ -4,5 +4,6 @@ align-items: center; flex-wrap: nowrap; + scroll-behavior: smooth; overflow-y: visible; } diff --git a/src/applications/menu/components/tile/tile.pcss b/src/applications/main/components/tile/tile.pcss similarity index 95% rename from src/applications/menu/components/tile/tile.pcss rename to src/applications/main/components/tile/tile.pcss index b7a22c52..75e5e31a 100644 --- a/src/applications/menu/components/tile/tile.pcss +++ b/src/applications/main/components/tile/tile.pcss @@ -21,6 +21,8 @@ &__background { border-radius: 5px; + object-fit: cover; + width: 100%; height: 100%; } diff --git a/src/applications/menu/components/userblock/userblock.js b/src/applications/main/components/userblock/userblock.js similarity index 57% rename from src/applications/menu/components/userblock/userblock.js rename to src/applications/main/components/userblock/userblock.js index 1f5b8dd1..8bc0ad71 100644 --- a/src/applications/menu/components/userblock/userblock.js +++ b/src/applications/main/components/userblock/userblock.js @@ -22,20 +22,8 @@ export default class UserBlock extends Element { } async render() { - const { loggedIn } = userService.isLoggedIn(); - - if (!loggedIn) { - const user = { username: '' }; - super.render({ user, authorized: false }); - return; - } - - - const authorized = true; const { user } = userService.getUser(); - super.render({ user, authorized }); - - const [profileButton] = this.wrapper.getElementsByClassName('userblock__avatar'); - profileButton.addEventListener('click', () => bus.emit('link', '/profile')); + console.log(user); + super.render({ user }); } } diff --git a/src/applications/menu/components/userblock/userblock.pcss b/src/applications/main/components/userblock/userblock.pcss similarity index 95% rename from src/applications/menu/components/userblock/userblock.pcss rename to src/applications/main/components/userblock/userblock.pcss index cd896922..24194f19 100644 --- a/src/applications/menu/components/userblock/userblock.pcss +++ b/src/applications/main/components/userblock/userblock.pcss @@ -1,4 +1,6 @@ .userblock { + grid-area: userblock; + display: flex; flex-direction: row; align-items: center; diff --git a/src/applications/main/components/userblock/userblock.pug b/src/applications/main/components/userblock/userblock.pug new file mode 100644 index 00000000..c8923d8d --- /dev/null +++ b/src/applications/main/components/userblock/userblock.pug @@ -0,0 +1 @@ +.userblock__name= user.username diff --git a/src/applications/menu/menu_app.js b/src/applications/main/main_app.js similarity index 59% rename from src/applications/menu/menu_app.js rename to src/applications/main/main_app.js index 7c041c55..71aa07ba 100644 --- a/src/applications/menu/menu_app.js +++ b/src/applications/main/main_app.js @@ -3,9 +3,14 @@ import './styles/fonts.pcss'; import BaseApp from '../base_app'; import Enviroment from './views/enviroment/env'; -import LibraryView from './views/library/library'; +import HomeView from './views/home/home'; import StoreView from './views/store/store'; +import LoaderView from './views/loader/loader'; +import ProfileView from './views/profile/profile'; +import Bar from './components/bar/bar'; import AppContainer from './views/app_container/app_container'; +import AboutView from './views/about/about'; +import bus from '../../modules/bus'; // import LoginView from './views/login'; // import RegisterView from './views/register'; // import LeaderboardView from './views/leaderboard'; @@ -21,18 +26,35 @@ export default class MenuApp extends BaseApp { const env = new Enviroment(parent, parent); super(appUrl, env.contentPlace); - const [libraryPlace] = env.wrapper.getElementsByClassName('library'); - this.views.main = new LibraryView(libraryPlace, libraryPlace); + const [loaderPlace] = env.wrapper.getElementsByClassName('loader-container'); + this.loader = new LoaderView(loaderPlace, loaderPlace); + this.loader.show(); + + const [homePlace] = env.wrapper.getElementsByClassName('home-page'); + this.views.main = new HomeView(homePlace, homePlace); this.currentView = this.views.main; const [storePlace] = env.wrapper.getElementsByClassName('store'); const storeView = new StoreView(storePlace, storePlace, this.views); this.views.store = storeView; + const [profilePlace] = env.wrapper.getElementsByClassName('profile'); + const profileView = new ProfileView(profilePlace, profilePlace, this.views); + this.views.profile = profileView; + + const [aboutPlace] = env.wrapper.getElementsByClassName('about'); + const aboutView = new AboutView(aboutPlace, aboutPlace, this.views); + this.views.about = aboutView; + this.env = env; this.views.env = this.env; this.menu = this.env.menu; + this.bar = new Bar( + this.env.appContainerPlace, + this.env.appContainerPlace, + ); + this.appContainer = new AppContainer( this.env.appContainerPlace, this.env.appContainerPlace, @@ -50,17 +72,23 @@ export default class MenuApp extends BaseApp { }, { once: true }); } + // start() { + + // } + start() { this.appContainer.hide(); this.env.show(); - this.animateLaunch(); - super.start(); + this.started = true; + this.active = true; + this.currentView.show(); } pause() { this.active = false; this.env.mainContainer.classList.add('blurred'); this.appContainer.show(); + document.activeElement.blur(); } resume() { @@ -68,9 +96,19 @@ export default class MenuApp extends BaseApp { this.start(); return; } - this.animateLaunch(); + if (this.loader.active) { + console.log('loader active'); + bus.emit('link', '/terminal'); + } else { + console.log('loader is not active'); + this.animateLaunch(); + this.env.title.focus(); + this.active = true; + } + } - this.active = true; + stop() { + this.pause(); } changeView(viewUrl, params) { @@ -83,7 +121,7 @@ export default class MenuApp extends BaseApp { } this.env.setTitle(this.currentView.title); - this.currentView.render(params); + if (!this.currentView.rendered) this.currentView.render(); this.currentView.show(); } } diff --git a/src/applications/menu/styles/fonts.pcss b/src/applications/main/styles/fonts.pcss similarity index 100% rename from src/applications/menu/styles/fonts.pcss rename to src/applications/main/styles/fonts.pcss diff --git a/src/applications/menu/templates/editprofile.pug b/src/applications/main/templates/editprofile.pug similarity index 100% rename from src/applications/menu/templates/editprofile.pug rename to src/applications/main/templates/editprofile.pug diff --git a/src/applications/menu/templates/leaderboard.pug b/src/applications/main/templates/leaderboard.pug similarity index 100% rename from src/applications/menu/templates/leaderboard.pug rename to src/applications/main/templates/leaderboard.pug diff --git a/src/applications/menu/templates/login.pug b/src/applications/main/templates/login.pug similarity index 100% rename from src/applications/menu/templates/login.pug rename to src/applications/main/templates/login.pug diff --git a/src/applications/menu/templates/profile.pug b/src/applications/main/templates/profile.pug similarity index 77% rename from src/applications/menu/templates/profile.pug rename to src/applications/main/templates/profile.pug index 965a0840..02c6d856 100644 --- a/src/applications/menu/templates/profile.pug +++ b/src/applications/main/templates/profile.pug @@ -1,5 +1,5 @@ .block#profile - img(src= "https://static.rasseki.com" + user.avatar) + img(src= "https://snakewave.com" + user.avatar) h3= user.username h3= 'Score: ' + user.score a.button( diff --git a/src/applications/menu/templates/register.pug b/src/applications/main/templates/register.pug similarity index 100% rename from src/applications/menu/templates/register.pug rename to src/applications/main/templates/register.pug diff --git a/src/applications/menu/templates/settings.pug b/src/applications/main/templates/settings.pug similarity index 100% rename from src/applications/menu/templates/settings.pug rename to src/applications/main/templates/settings.pug diff --git a/src/applications/main/views/about/about.js b/src/applications/main/views/about/about.js new file mode 100644 index 00000000..e12c61a4 --- /dev/null +++ b/src/applications/main/views/about/about.js @@ -0,0 +1,65 @@ +import Element from '../../../element'; +import List from '../../components/list/list'; +import FormAboutUs from '../../components/formAboutUs/formAboutUs'; + +import template from './about.pug'; +import '../../components/app_tile/app_tile.pcss'; +import './about.pcss'; + +import '../../../../../static/img/terminal.jpg'; + +const categories = [ + 'Дмитрий Палий', + 'Глазачева Ксения', + 'Лебедев Илья', + 'Липко Дмитрий', +]; + +export default class AboutView extends Element { + constructor(parent, wrapper, views) { + super(template, parent, wrapper); + super.render(); + + this.title = 'About us'; + + this.parentViews = views; + // [this.panel] = this.wrapper.getElementsByClassName('about'); + [this.listPlace] = document.getElementsByClassName('about-list'); + [this.formAboutUsPlace] = document.getElementsByClassName('form-about-us'); + + this.list = new List(this.listPlace); + this.formAboutUs = new FormAboutUs(this.formAboutUsPlace); + this.listPlace.addEventListener('click', (event) => { + const categoryName = event.target.innerText.toLowerCase(); + if (categoryName) this.render(categoryName); + }); + + this.list.render(categories); + } + + show() { + const [grid] = document.getElementsByClassName('grid-common'); + if (!grid.classList.contains('about__grid')) { + grid.classList.add('about__grid'); + } + + this.formAboutUs.show(); + // this.description.show(); + this.list.show(); + super.show(); + // this.parentViews.env.menu.hide(); + } + + hide() { + const [grid] = document.getElementsByClassName('grid-common'); + if (grid.classList.contains('about__grid')) { + grid.classList.remove('about__grid'); + } + + this.formAboutUs.hide(); + // this.description.hide(); + this.list.hide(); + super.hide(); + // this.parentViews.env.menu.show(); + } +} diff --git a/src/applications/main/views/about/about.pcss b/src/applications/main/views/about/about.pcss new file mode 100644 index 00000000..f2ac0ed8 --- /dev/null +++ b/src/applications/main/views/about/about.pcss @@ -0,0 +1,79 @@ +@import '../../../../config.pcss'; + +.about { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + + &__grid { + overflow: hidden; + grid-template-rows: 14vh 14vh calc(50vh - 30px) 22vh; + grid-template-columns: 300px auto; + grid-row-gap: 10px; + grid-auto-rows: 0; + + grid-template-areas: + "userblock icon-set" + "title title" + "about_list description" + "menu menu"; + + width: calc(100vw - 2 * var(--grid-padding-x)); + padding: 0 var(--grid-padding-x); + } + + &__tile-panel { + height: 80%; + width: calc((100vw - 2* var(--grid-padding-x) - 600px)); + + scroll-snap-type: x mandatory; + overflow-x: auto; + overflow-y: visible; + &::-webkit-scrollbar { + display: none; + } + } +} + + +@media (--boss) { + .about__grid { + grid-template-columns: 300px 1fr 300px; + } +} + +@media (--small) { + .about__tile-panel { + width: calc(100vw - 2 * var(--small-grid-padding-x)); + } +} + +@media (--minimal) { + .about__tile-panel { + width: calc(100vw - 2 * var(--minimal-grid-padding-x)); + } +} + +.about_list { + grid-area: about_list; +} + +.about_from { + border-radius: 20px; + width: 400px; +} + +.about_textarea { + font-family: Gilroy-Light; + border-radius: 20px; + height: 100px; + width: 400px; + padding: 15px; + outline: none; + resize: none; +} + +button.tile__card.menu__card { + border: none; +} diff --git a/src/applications/main/views/about/about.pug b/src/applications/main/views/about/about.pug new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/applications/main/views/about/about.pug @@ -0,0 +1 @@ + diff --git a/src/applications/menu/views/app_container/app_container.js b/src/applications/main/views/app_container/app_container.js similarity index 72% rename from src/applications/menu/views/app_container/app_container.js rename to src/applications/main/views/app_container/app_container.js index 09e4c234..43e5e011 100644 --- a/src/applications/menu/views/app_container/app_container.js +++ b/src/applications/main/views/app_container/app_container.js @@ -7,7 +7,8 @@ import template from './app_container.pug'; export default class AppContainer extends Element { constructor(parent, wrapper) { super(template, parent, wrapper || parent); - super.render(); + this.render(); + [this.screen] = this.wrapper.getElementsByClassName('screen'); } hide() { @@ -19,4 +20,8 @@ export default class AppContainer extends Element { super.show(); this.wrapper.style['z-index'] = 2; } + + render() { + this.wrapper.innerHTML += this.template(); + } } diff --git a/src/applications/menu/views/app_container/app_container.pcss b/src/applications/main/views/app_container/app_container.pcss similarity index 65% rename from src/applications/menu/views/app_container/app_container.pcss rename to src/applications/main/views/app_container/app_container.pcss index c6f1f646..b2f40891 100644 --- a/src/applications/menu/views/app_container/app_container.pcss +++ b/src/applications/main/views/app_container/app_container.pcss @@ -5,13 +5,13 @@ z-index: 3; height: 100vh; width: 100vw; - - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; } +.screen { + z-index: 50; + width: 100%; + height: 100%; +} .floating { position: absolute; diff --git a/src/applications/main/views/app_container/app_container.pug b/src/applications/main/views/app_container/app_container.pug new file mode 100644 index 00000000..ba113150 --- /dev/null +++ b/src/applications/main/views/app_container/app_container.pug @@ -0,0 +1 @@ +.screen diff --git a/src/applications/menu/views/editprofile.js b/src/applications/main/views/editprofile.js similarity index 100% rename from src/applications/menu/views/editprofile.js rename to src/applications/main/views/editprofile.js diff --git a/src/applications/menu/views/enviroment/env.js b/src/applications/main/views/enviroment/env.js similarity index 81% rename from src/applications/menu/views/enviroment/env.js rename to src/applications/main/views/enviroment/env.js index a7c912d3..29d8cd4a 100644 --- a/src/applications/menu/views/enviroment/env.js +++ b/src/applications/main/views/enviroment/env.js @@ -3,6 +3,7 @@ import './env.pcss'; import Element from '../../../element'; import UserBlock from '../../components/userblock/userblock'; import Menu from '../../components/menu/menu'; +import IconBlock from '../../components/icon-block/icon-block'; import template from './env.pug'; @@ -16,11 +17,14 @@ export default class Enviroment extends Element { super.render(); [this.title] = this.wrapper.getElementsByClassName('title__text'); - [this.mainContainer] = this.wrapper.getElementsByClassName('grid-body'); + [this.mainContainer] = this.wrapper.getElementsByClassName('grid-common'); const [userblockPlace] = this.wrapper.getElementsByClassName('userblock'); const [menuPlace] = this.wrapper.getElementsByClassName('menu'); + const [iconsPlace] = this.wrapper.getElementsByClassName('iconblock'); this.userblock = new UserBlock(userblockPlace, userblockPlace); this.menu = new Menu(menuPlace, menuPlace); + this.iconBlock = new IconBlock(iconsPlace, iconsPlace); + [this.appContainerPlace] = this.wrapper.getElementsByClassName('application'); } @@ -32,11 +36,13 @@ export default class Enviroment extends Element { show() { super.show(); + this.iconBlock.show(); this.userblock.show(); this.menu.show(); } render() { + this.iconBlock.render(); this.userblock.render(); this.menu.render(); } diff --git a/src/applications/menu/views/enviroment/env.pcss b/src/applications/main/views/enviroment/env.pcss similarity index 55% rename from src/applications/menu/views/enviroment/env.pcss rename to src/applications/main/views/enviroment/env.pcss index a6014f0e..5eda1fdd 100644 --- a/src/applications/menu/views/enviroment/env.pcss +++ b/src/applications/main/views/enviroment/env.pcss @@ -5,35 +5,17 @@ body { margin: 0px; } -.grid-body { +.grid-common { display: grid; overflow: hidden; height: calc(100vh); - width: calc(100vw - 2 * var(--grid-padding-x)); - /* max-width: 1400px; */ - padding: 0 var(--grid-padding-x); - - - grid-template-rows: 14% 19% 1fr 27%; - grid-template-columns: repeat(2, 1fr); - grid-row-gap: 10px; - grid-auto-rows: 0; - /* overflow-y: hidden; */ - - grid-template-areas: - "userblock icon-set" - "title title" - "content content" - "menu menu"; } @media (--mobile) { - .grid-body { + .grid-common { width: calc(100vw - 2 * var(--minimal-grid-padding-x)); - padding: 0 var(--minimal-grid-padding-x); - grid-template-rows: 14% 14% 1fr 27%; grid-template-columns: 1fr; grid-template-areas: @@ -44,24 +26,6 @@ body { } } -@media (--tablet) { - .grid-body { - width: calc(100vw - 2 * var(--minimal-grid-padding-x)); - padding: 0 var(--minimal-grid-padding-x); - - grid-template-rows: 14% 14% 1fr 27%; - } -} - -@media (--small) { - .grid-body { - width: calc(100vw - 2 * var(--small-grid-padding-x)); - padding: 0 var(--small-grid-padding-x); - - grid-template-rows: 14% 14% 1fr 27%; - } -} - .title { grid-area: title; display: flex; diff --git a/src/applications/main/views/enviroment/env.pug b/src/applications/main/views/enviroment/env.pug new file mode 100644 index 00000000..9ed1b872 --- /dev/null +++ b/src/applications/main/views/enviroment/env.pug @@ -0,0 +1,19 @@ +.loader-container +.grid-common.home-page__grid + .userblock + .iconblock + .title + div.title__text Home + .list + .app-table + .description-container + .content.store + .content.home-page + .menu + .profile__user-data + .profile-description-container + //- .about + .about-list + .form-about-us + +.application diff --git a/src/applications/main/views/home/home.js b/src/applications/main/views/home/home.js new file mode 100644 index 00000000..cecf8330 --- /dev/null +++ b/src/applications/main/views/home/home.js @@ -0,0 +1,93 @@ +import Element from '../../../element'; +import AppTile from '../../components/app_tile/app_tile'; + +import template from './home.pug'; +import '../../components/app_tile/app_tile.pcss'; +import './home.pcss'; + +import { getMyApps } from '../../../../modules/network'; +import GameApp from '../../../frame/game_app'; +import bus from '../../../../modules/bus'; + +export default class HomeView extends Element { + constructor(parent, wrapper) { + super(template, parent, wrapper); + super.render(); + + this.apps = null; + this.title = 'Home'; + + [this.panel] = this.wrapper.getElementsByClassName('home-page__tile-panel'); + + this.render = this.render.bind(this); + bus.listen('appInstalled', this.render); + this.render(); + } + + scroller(ev) { + if (ev.which === 37 || ev.which === 39) ev.preventDefault(); + if (ev.which === 39 && (Date.now() - this.lastLeft) > 400) { + this.scrollPanelLeft(); + this.lastLeft = Date.now(); + } else if (ev.which === 37 && (Date.now() - this.lastRight) > 400) { + this.scrollPanelRight(); + this.lastRight = Date.now(); + } + } + + startScroller() { + this.lastLeft = Date.now(); + this.lastRight = Date.now(); + document.addEventListener('keydown', this.scroller); + } + + stopScroller() { + document.removeEventListener('keydown', this.scroller); + } + + scrollPanelLeft() { + const [tile] = this.panel.children; + this.panel.scrollLeft += tile.offsetWidth; + } + + scrollPanelRight() { + const [tile] = this.panel.children; + this.panel.scrollLeft -= tile.offsetWidth; + } + + show() { + const [grid] = document.getElementsByClassName('grid-common'); + if (!grid.classList.contains('home-page__grid')) { + grid.classList.add('home-page__grid'); + } + this.startScroller(); + super.show(); + } + + hide() { + const [grid] = document.getElementsByClassName('grid-common'); + if (grid.classList.contains('home-page__grid')) { + grid.classList.remove('home-page__grid'); + } + this.stopScroller(); + super.hide(); + } + + async render() { + const { err, apps } = await getMyApps(); + if (err) { + console.log(err); + } else { + console.log(apps); + if (!this.apps || this.apps !== apps) { + this.apps = apps; + this.panel.innerHTML = ''; + apps.forEach((app) => { + bus.emit('regApp', app.link, GameApp, app.url); + const tile = new AppTile(this.panel, app, 'home-page__tile'); + tile.show(); + }); + } + } + } +} diff --git a/src/applications/main/views/home/home.pcss b/src/applications/main/views/home/home.pcss new file mode 100644 index 00000000..b17134e0 --- /dev/null +++ b/src/applications/main/views/home/home.pcss @@ -0,0 +1,107 @@ +@import '../../../../config.pcss'; + +.home-page { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + + &__grid { + padding: 0 var(--grid-padding-x); + + grid-template-rows: 14% 19% 1fr 22%; + grid-template-columns: repeat(2, 1fr); + grid-row-gap: 10px; + grid-auto-rows: 0; + + grid-template-areas: + "userblock icon-set" + "title title" + "content content" + "menu menu"; + + width: calc(100vw - 2 * var(--grid-padding-x)); + padding: 0 var(--grid-padding-x); + } + + &__tile-panel { + height: 80%; + width: calc(100vw - 2 * var(--grid-padding-x)); + + scroll-snap-type: x mandatory; + // overflow-x: auto; + + overflow: scroll; + &::-webkit-scrollbar { + display: none; + } + } + + &__tile { + width: calc((100vw - 2*var(--grid-padding-x)) / 3 - 20px); + } +} + +@media (--minimal) { + .home-page__tile-panel { + width: calc(100vw - 2 * var(--minimal-grid-padding-x)); + } + .grid-common { + grid-template-rows: 14% 14% 1fr 22%; + } +} + +@media (--mobile) { + .home-page__grid { + grid-template-areas: + "userblock" + "title" + "content" + "menu"; + width: calc(100vw - 2 * var(--minimal-grid-padding-x)); + padding: 0 var(--minimal-grid-padding-x); + } + + .home-page__tile { + width: calc(100vw - 2*var(--minimal-grid-padding-x) - 20px); + } +} + +@media (--tablet) { + .home-page__grid { + width: calc(100vw - 2 * var(--minimal-grid-padding-x)); + padding: 0 var(--minimal-grid-padding-x); + } + + .home-page__tile { + width: calc((100vw - 2*var(--minimal-grid-padding-x)) / 2 - 20px); + } +} + +@media (--small) { + .home-page__grid { + width: calc(100vw - 2 * var(--small-grid-padding-x)); + padding: 0 var(--small-grid-padding-x); + grid-template-rows: 14% 14% 1fr 22%; + } + + .home-page__tile-panel { + width: calc(100vw - 2 * var(--small-grid-padding-x)); + } + + .home-page__tile { + width: calc((100vw - 2*var(--small-grid-padding-x)) / 3 - 20px); + } +} + +@media (--large) { + .home-page__tile { + width: calc((100vw - 2*var(--grid-padding-x)) / 4 - 20px); + } +} + +@media (--boss) { + .home-page__tile { + width: calc((100vw - 2*var(--grid-padding-x)) / 5 - 20px); + } +} diff --git a/src/applications/main/views/home/home.pug b/src/applications/main/views/home/home.pug new file mode 100644 index 00000000..732c58e5 --- /dev/null +++ b/src/applications/main/views/home/home.pug @@ -0,0 +1 @@ +.tile-panel.home-page__tile-panel diff --git a/src/applications/main/views/loader/loader.js b/src/applications/main/views/loader/loader.js new file mode 100644 index 00000000..2d899672 --- /dev/null +++ b/src/applications/main/views/loader/loader.js @@ -0,0 +1,45 @@ +import Element from '../../../element'; + +import template from './loader.pug'; +import './loader.pcss'; + +import userService from '../../../../modules/userservice'; +import bus from '../../../../modules/bus'; + +export default class Loader extends Element { + constructor(parent, wrapper) { + super(template, parent, wrapper); + this.checkUser = this.checkUser.bind(this); + this.hide = super.hide.bind(this); + this.show = this.show.bind(this); + + bus.listen('userUpdated', () => { + const { loggedIn } = userService.isLoggedIn(); + if (loggedIn) this.hide(); + else this.show(); + }); + } + + show() { + super.show(); + this.checkUser(); + } + + checkUser() { + bus.ignore('userUpdated', this.checkUser); + const { err, loggedIn } = userService.isLoggedIn(); + if (err) bus.listen('userUpdated', this.checkUser); + else if (loggedIn) { + this.hide(); + console.log('logged in'); + setTimeout(this.hide, 500); + } else { + const mockBlock = document.createElement('a'); + mockBlock.classList.add('mock-block'); + mockBlock.setAttribute('href', '/terminal'); + this.wrapper.appendChild(mockBlock); + mockBlock.click(); + mockBlock.hidden = true; + } + } +} diff --git a/src/applications/main/views/loader/loader.pcss b/src/applications/main/views/loader/loader.pcss new file mode 100644 index 00000000..659f7ae0 --- /dev/null +++ b/src/applications/main/views/loader/loader.pcss @@ -0,0 +1,61 @@ +.loader-container { + background-color: black; + height: 100vh; + width: 100vw; + + display: flex; + justify-content: center; + align-items: center; + position: absolute; + top: 0; + left: 0; + z-index: 2; +} + + +.loader { + display: inline-block; + position: absolute; + width: 64px; + height: 64px; + + & div { + display: inline-block; + position: absolute; + left: 16px; + width: 13px; + background: greenyellow; + animation: loader 1.2s cubic-bezier(0, 0.5, 0.5, 1) infinite; + + &:nth-child(1) { + left: 6px; + animation-delay: -0.24s; + } + + &:nth-child(2) { + left: 26px; + animation-delay: -0.12s; + } + + &:nth-child(3) { + left: 45px; + animation-delay: 0; + } + } +} + +.mock-block { + height: 100px; + width: 100px; +} + +@keyframes loader { + 0% { + top: 6px; + height: 51px; + } + 50%, 100% { + top: 19px; + height: 26px; + } +} diff --git a/src/applications/main/views/loader/loader.pug b/src/applications/main/views/loader/loader.pug new file mode 100644 index 00000000..2375ed76 --- /dev/null +++ b/src/applications/main/views/loader/loader.pug @@ -0,0 +1,4 @@ +.loader + div + div + div diff --git a/src/applications/menu/views/profile.js b/src/applications/main/views/profile.js similarity index 100% rename from src/applications/menu/views/profile.js rename to src/applications/main/views/profile.js diff --git a/src/applications/main/views/profile/profile.js b/src/applications/main/views/profile/profile.js new file mode 100644 index 00000000..a3650eed --- /dev/null +++ b/src/applications/main/views/profile/profile.js @@ -0,0 +1,60 @@ +import Element from '../../../element'; + +import template from './profile.pug'; +import '../../components/app_tile/app_tile.pcss'; +import './profile.pcss'; + +import bus from '../../../../modules/bus'; + +import Profile from '../../components/profile/profile'; +import AppTable from '../../components/app-table/app-table'; +import ProfileDescription from '../../components/profile_discription/profile_description'; + + +export default class ProfileView extends Element { + constructor(parent, wrapper) { + super(template, parent, wrapper); + super.render(); + + bus.listen('profile', this.render.bind(this)); + this.title = 'Profile'; + + [this.panel] = this.wrapper.getElementsByClassName('profile-page__tile-panel'); + [this.userDataPlace] = document.getElementsByClassName('profile__user-data'); + [this.appTablePlace] = document.getElementsByClassName('app-table'); + [this.profileDescriptionPlace] = document.getElementsByClassName('profile-description-container'); + + this.userData = new Profile(this.userDataPlace, this.userDataPlace); + this.profileDescription = new ProfileDescription(this.profileDescriptionPlace); + } + + show() { + const [grid] = document.getElementsByClassName('grid-common'); + if (!grid.classList.contains('profile__grid')) { + grid.classList.add('profile__grid'); + } + // this.startScroller(); + this.appTable.show(); + this.userData.show(); + this.profileDescription.show(); + super.show(); + } + + hide() { + const [grid] = document.getElementsByClassName('grid-common'); + if (grid.classList.contains('profile__grid')) { + grid.classList.remove('profile__grid'); + } + // this.stopScroller(); + this.userData.hide(); + this.appTable.hide(); + this.profileDescription.hide(); + super.render(); + } + + render() { + this.userData.render(); + this.appTable.render(); + this.profileDescription.render(); + } +} diff --git a/src/applications/main/views/profile/profile.pcss b/src/applications/main/views/profile/profile.pcss new file mode 100644 index 00000000..2d60c0a4 --- /dev/null +++ b/src/applications/main/views/profile/profile.pcss @@ -0,0 +1,56 @@ +@import '../../../../config.pcss'; + +.profile { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + + &__grid { + overflow: hidden; + grid-template-rows: 14vh 14vh calc(50vh - 30px) 22vh; + grid-template-columns: 250px 1fr 250px; + grid-row-gap: 10px; + grid-auto-rows: 0; + + grid-template-areas: + "userblock userblock icon-set" + "title app-table profile-description" + "user-data app-table profile-description" + "menu menu menu"; + + width: calc(100vw - 2 * var(--grid-padding-x)); + padding: 0 var(--grid-padding-x); + } + + &__tile-panel { + height: 80%; + width: calc((100vw - 2* var(--grid-padding-x) - 600px)); + + scroll-snap-type: x mandatory; + overflow-x: auto; + overflow-y: visible; + &::-webkit-scrollbar { + display: none; + } + } +} + + +@media (--boss) { + .profile__grid { + grid-template-columns: 300px 1fr 300px; + } +} + +@media (--small) { + .profile__tile-panel { + width: calc(100vw - 2 * var(--small-grid-padding-x)); + } +} + +@media (--minimal) { + .profile__tile-panel { + width: calc(100vw - 2 * var(--minimal-grid-padding-x)); + } +} diff --git a/src/applications/main/views/profile/profile.pug b/src/applications/main/views/profile/profile.pug new file mode 100644 index 00000000..4aea7fc4 --- /dev/null +++ b/src/applications/main/views/profile/profile.pug @@ -0,0 +1 @@ +.tile-panel.profile-page__tile-panel diff --git a/src/applications/main/views/store/store.js b/src/applications/main/views/store/store.js new file mode 100644 index 00000000..57ec1b6b --- /dev/null +++ b/src/applications/main/views/store/store.js @@ -0,0 +1,70 @@ +import Element from '../../../element'; +import List from '../../components/list/list'; +import Description from '../../components/description/description'; +import AppTable from '../../components/app-table/app-table'; + +import template from './store.pug'; +import '../../components/app_tile/app_tile.pcss'; +import './store.pcss'; + +import '../../../../../static/img/terminal.jpg'; + +const categories = [ + 'new', + 'all', + 'popular', + '2018_2', + '2018_1', + '2017_2', +]; + +export default class StoreView extends Element { + constructor(parent, wrapper, views) { + super(template, parent, wrapper); + super.render(); + + this.title = 'Store'; + + this.parentViews = views; + [this.panel] = this.wrapper.getElementsByClassName('store__tile-panel'); + [this.listPlace] = document.getElementsByClassName('list'); + [this.descriptionPlace] = document.getElementsByClassName('description-container'); + [this.appTablePlace] = document.getElementsByClassName('app-table'); + + this.appTable = new AppTable(this.appTablePlace, this.appTablePlace); + this.description = new Description(this.descriptionPlace); + this.list = new List(this.listPlace, this.listPlace); + this.listPlace.addEventListener('click', (event) => { + const categoryName = event.target.innerText.toLowerCase(); + if (categoryName) this.render(categoryName); + }); + + this.list.render(categories); + } + + show() { + const [grid] = document.getElementsByClassName('grid-common'); + if (!grid.classList.contains('store__grid')) { + grid.classList.add('store__grid'); + } + + this.appTable.show(); + this.description.show(); + this.list.show(); + super.show(); + // this.parentViews.env.menu.hide(); + } + + hide() { + const [grid] = document.getElementsByClassName('grid-common'); + if (grid.classList.contains('store__grid')) { + grid.classList.remove('store__grid'); + } + + this.appTable.hide(); + this.description.hide(); + this.list.hide(); + super.hide(); + // this.parentViews.env.menu.show(); + } +} diff --git a/src/applications/main/views/store/store.pcss b/src/applications/main/views/store/store.pcss new file mode 100644 index 00000000..0229f467 --- /dev/null +++ b/src/applications/main/views/store/store.pcss @@ -0,0 +1,56 @@ +@import '../../../../config.pcss'; + +.store { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + + &__grid { + overflow: hidden; + grid-template-rows: 14vh 14vh calc(50vh - 30px) 22vh; + grid-template-columns: 250px 1fr 250px; + grid-row-gap: 10px; + grid-auto-rows: 0; + + grid-template-areas: + "userblock userblock icon-set" + "title app-table description" + "list app-table description" + "menu menu menu"; + + width: calc(100vw - 2 * var(--grid-padding-x)); + padding: 0 var(--grid-padding-x); + } + + &__tile-panel { + height: 80%; + width: calc((100vw - 2* var(--grid-padding-x) - 600px)); + + scroll-snap-type: x mandatory; + overflow-x: auto; + overflow-y: visible; + &::-webkit-scrollbar { + display: none; + } + } +} + + +@media (--boss) { + .store__grid { + grid-template-columns: 300px 1fr 300px; + } +} + +@media (--small) { + .store__tile-panel { + width: calc(100vw - 2 * var(--small-grid-padding-x)); + } +} + +@media (--minimal) { + .store__tile-panel { + width: calc(100vw - 2 * var(--minimal-grid-padding-x)); + } +} diff --git a/src/applications/main/views/store/store.pug b/src/applications/main/views/store/store.pug new file mode 100644 index 00000000..f6f56f50 --- /dev/null +++ b/src/applications/main/views/store/store.pug @@ -0,0 +1 @@ +.nothing diff --git a/src/applications/menu/components/menu/menu.js b/src/applications/menu/components/menu/menu.js deleted file mode 100644 index f3fcb0d4..00000000 --- a/src/applications/menu/components/menu/menu.js +++ /dev/null @@ -1,75 +0,0 @@ -import Element from '../../../element'; - -import '../tile-panel/tile-panel.pcss'; -import '../tile/tile.pcss'; -import './menu.pcss'; - -import template from './menu.pug'; - -const tiles = [ - { - link: '/library', - name: 'Library', - icon: 'apps', - }, - { - link: '/store', - name: 'Store', - icon: 'local_mall', - }, - { - link: '/settings', - name: 'Settings', - icon: 'settings', - }, - { - link: '/about', - name: 'About us', - icon: 'accessible_forward', - }, -]; - - -export default class Menu extends Element { - constructor(parent, wrapper) { - super(template, parent, wrapper); - } - - show() { - if (!this.rendered) this.render(); - - const showAnimation = this.wrapper.animate({ - transform: [ - 'translateY(250px)', - 'translateY(0px)', - ], - }, { - duration: 200, - fill: 'forwards', - easing: 'cubic-bezier(.36,1.08,.55,.93)', - }); - showAnimation.pause(); - showAnimation.onfinish = () => super.show(); - showAnimation.play(); - } - - hide() { - const hideAnimation = this.wrapper.animate({ - transform: [ - 'translateY(0px)', - 'translateY(250px)', - ], - }, { - duration: 200, - fill: 'forwards', - easing: 'cubic-bezier(.36,1.08,.55,.93)', - }); - hideAnimation.pause(); - hideAnimation.onfinish = () => super.hide(); - hideAnimation.play(); - } - - render() { - super.render({ tiles }); - } -} diff --git a/src/applications/menu/components/menu/menu.pcss b/src/applications/menu/components/menu/menu.pcss deleted file mode 100644 index 32d0b464..00000000 --- a/src/applications/menu/components/menu/menu.pcss +++ /dev/null @@ -1,57 +0,0 @@ -.menu { - grid-area: menu; - display: flex; - flex-direction: column; - justify-content: center; - - &__tile-panel { - overflow: hidden; - width: 100%; - height: 70%; - align-items: center; - } - - &__tile { - justify-content: flex-end; - - height: 90%; - width: 25vh; - margin: 0 10px; - - &:hover > .tile__card { - transform: translateY(-10px); - background-color: #4462C4; - } - - &:hover > .tile__card::after { - transform: scale(.9); - box-shadow: 0px 0px 30px 2px #4462C4; - } - - &:hover .tile__name, &:hover .menu__icon { - color: white; - transform: scale(1.2); - } - } - - &__card { - display: flex; - justify-content: center; - align-items: center; - - text-decoration: none !important; - background-color: rgba(100%, 100%, 100%,0.1); - - &:after { - bottom: -4px; - transition: all 0.4s ease-out; - } - } - - &__icon { - color: rgb(100%, 100%, 100%, 0.6); - font-size: 56px; - - transition: all 0.3s ease-out; - } -} diff --git a/src/applications/menu/components/menu/menu.pug b/src/applications/menu/components/menu/menu.pug deleted file mode 100644 index ea1903da..00000000 --- a/src/applications/menu/components/menu/menu.pug +++ /dev/null @@ -1,9 +0,0 @@ -.tile-panel.menu__tile-panel - each tile in tiles - .tile.menu__tile - a.tile__card.menu__card( - href= tile.link - ) - i.material-icons.menu__icon= tile.icon - div - .tile__name= tile.name diff --git a/src/applications/menu/components/userblock/userblock.pug b/src/applications/menu/components/userblock/userblock.pug deleted file mode 100644 index a0459497..00000000 --- a/src/applications/menu/components/userblock/userblock.pug +++ /dev/null @@ -1,10 +0,0 @@ -.userblock__avatar-wrapper - //- if !authorized - //- a.userblock__button(href='/login') login - //- else - //- img.icon__img(src= "https://static.rasseki.com" + user.avatar) - img.userblock__avatar( - src='/img/triss.jpg' - ) -.userblock__name Triss Merigold - //- .userblock__name= user.username diff --git a/src/applications/menu/views/app_container/app_container.pug b/src/applications/menu/views/app_container/app_container.pug deleted file mode 100644 index ddf131de..00000000 --- a/src/applications/menu/views/app_container/app_container.pug +++ /dev/null @@ -1,2 +0,0 @@ -.nothing - .do_not_touch_this diff --git a/src/applications/menu/views/enviroment/env.pug b/src/applications/menu/views/enviroment/env.pug deleted file mode 100644 index 78f6dc85..00000000 --- a/src/applications/menu/views/enviroment/env.pug +++ /dev/null @@ -1,9 +0,0 @@ -.grid-body - .userblock - .title - div.title__text Library - .content.store - .content.library - .menu - -.application diff --git a/src/applications/menu/views/leaderboard.js b/src/applications/menu/views/leaderboard.js deleted file mode 100644 index 3254602f..00000000 --- a/src/applications/menu/views/leaderboard.js +++ /dev/null @@ -1,31 +0,0 @@ -import { getLeaders } from '../../../modules/network'; -import Element from '../../element'; -import '../styles/leaderboard.pcss'; - -const template = require('../templates/leaderboard.pug'); - - -export default class LeaderboardView extends Element { - constructor(parent) { - super(template, parent); - } - - // TODO: FIXME: check order!!! - async render(count, page) { - const { err, leaders } = getLeaders(count, page); - if (err) { - // TODO: show error - console.error(err); - return; - } - super.render({ - users: leaders, - // pagesCount: (data.total / count), - }); - // const pagination = document.querySelector('.pagination'); - // pagination.addEventListener('click', (event) => { - // const link = event.target; - // getUsers(count * (link.getAttribute('page') - 1), count); - // }); - } -} diff --git a/src/applications/menu/views/library/library.js b/src/applications/menu/views/library/library.js deleted file mode 100644 index e7b6c047..00000000 --- a/src/applications/menu/views/library/library.js +++ /dev/null @@ -1,62 +0,0 @@ -import Element from '../../../element'; -import AppTile from '../../components/app_tile/app_tile'; - -import template from './library.pug'; -import '../../components/app_tile/app_tile.pcss'; -import './library.pcss'; - -import '../../../../../static/img/terminal.jpg'; -import '../../../../../static/img/snake.jpg'; - -const apps = [ - { - link: '/terminal', - image: '/img/terminal.jpg', - name: 'Terminal', - }, - { - link: '/snake', - image: '/img/snake.jpg', - name: 'Snake', - }, - { - link: '/test', - image: '/img/terminal.jpg', - name: 'Do not click here', - }, - { - link: '/snake', - image: '/img/snake.jpg', - name: 'Snake', - }, - { - link: '/terminal', - image: '/img/terminal.jpg', - name: 'Terminal', - }, - { - link: '/snake', - image: '/img/snake.jpg', - name: 'Snake', - }, -]; - - -export default class LibraryView extends Element { - constructor(parent, wrapper) { - super(template, parent, wrapper); - super.render(); - - this.title = 'Library'; - - [this.panel] = this.wrapper.getElementsByClassName('library__tile-panel'); - } - - render() { - if (this.panel.innerHTML !== '') return; - apps.forEach((app) => { - const tile = new AppTile(this.panel, app); - tile.show(); - }); - } -} diff --git a/src/applications/menu/views/library/library.pcss b/src/applications/menu/views/library/library.pcss deleted file mode 100644 index 387c4102..00000000 --- a/src/applications/menu/views/library/library.pcss +++ /dev/null @@ -1,32 +0,0 @@ -@import '../../../../config.pcss'; - -.library { - display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; - - - &__tile-panel { - height: 80%; - width: calc(100vw - 2 * var(--grid-padding-x)); - - scroll-snap-type: x mandatory; - overflow-x: auto; - &::-webkit-scrollbar { - display: none; - } - } -} - -@media (--small) { - .library__tile-panel { - width: calc(100vw - 2 * var(--small-grid-padding-x)); - } -} - -@media (--minimal) { - .library__tile-panel { - width: calc(100vw - 2 * var(--minimal-grid-padding-x)); - } -} diff --git a/src/applications/menu/views/library/library.pug b/src/applications/menu/views/library/library.pug deleted file mode 100644 index 3e30e477..00000000 --- a/src/applications/menu/views/library/library.pug +++ /dev/null @@ -1 +0,0 @@ -.tile-panel.library__tile-panel diff --git a/src/applications/menu/views/login.js b/src/applications/menu/views/login.js deleted file mode 100644 index 575ed436..00000000 --- a/src/applications/menu/views/login.js +++ /dev/null @@ -1,42 +0,0 @@ -import bus from '../../../modules/bus'; -import Element from '../../element'; -import { login } from '../../../modules/network'; - -import '../styles/login.pcss'; - -const template = require('../templates/login.pug'); - - -export default class LoginView extends Element { - constructor(parent) { - super(template, parent); - } - - show() { - super.show(); - this.render(); - } - - render() { - super.render(); - // ↓ will be redone soon ↓ TODO: FIXME: - const loginForm = document.getElementById('loginForm'); - - loginForm.addEventListener('submit', async (event) => { - event.preventDefault(); - - const loginData = new FormData(loginForm); - const { err } = await login(loginData); - - if (!err) { - bus.emit('link', '/'); - bus.emit('checkUser'); - } else { - console.error(err); - // ↓ will be redone soon ↓ TODO: FIXME: - const errorMessage = document.querySelector('#errorMessage'); - errorMessage.classList.add('show'); - } - }); - } -} diff --git a/src/applications/menu/views/menu.js b/src/applications/menu/views/menu.js deleted file mode 100644 index a1e56b62..00000000 --- a/src/applications/menu/views/menu.js +++ /dev/null @@ -1,30 +0,0 @@ -import Element from '../../element'; - -const template = require('../templates/menu.pug'); - -const buttons = { - '/terminal': 'Terminalium', - '/snake': 'Anacondium', - '/chat': 'Chatium', - '/leaderboard': 'Leaderboardium', - '/settings': 'Settium', -}; - - -export default class MenuView extends Element { - constructor(parent) { - super(template, parent); - } - - show() { - super.show(); - if (!this.rendered) { - // render only one time, because menu is unchangeable - this.render(); - } - } - - render() { - super.render({ buttons }); - } -} diff --git a/src/applications/menu/views/register.js b/src/applications/menu/views/register.js deleted file mode 100644 index e100b052..00000000 --- a/src/applications/menu/views/register.js +++ /dev/null @@ -1,38 +0,0 @@ -import { register } from '../../../modules/network'; -import bus from '../../../modules/bus'; -import Element from '../../element'; -import '../styles/registration.pcss'; - -const template = require('../templates/register.pug'); - - -export default class RegisterView extends Element { - constructor(parent) { - super(template, parent); - } - - show() { - super.show(); - this.render(); - } - - render() { - super.render(); - // TODO: FIXME: remove id - const registerForm = document.getElementById('registerForm'); - - registerForm.addEventListener('submit', async (event) => { - event.preventDefault(); - const registerData = new FormData(registerForm); - const { err } = await register(registerData); - if (err) { - // TODO: show error - console.error(err); - return; - } - - bus.emit('link', '/'); - bus.emit('checkUser'); - }); - } -} diff --git a/src/applications/menu/views/settings.js b/src/applications/menu/views/settings.js deleted file mode 100644 index 3fa57f23..00000000 --- a/src/applications/menu/views/settings.js +++ /dev/null @@ -1,16 +0,0 @@ -import Element from '../../element'; - -const template = require('../templates/settings.pug'); - - -export default class SettingsView extends Element { - constructor(parent) { - super(template, parent); - this.single = true; - } - - show() { - super.show(); - this.render(); - } -} diff --git a/src/applications/menu/views/store/store.js b/src/applications/menu/views/store/store.js deleted file mode 100644 index 12028f57..00000000 --- a/src/applications/menu/views/store/store.js +++ /dev/null @@ -1,57 +0,0 @@ -import Element from '../../../element'; -import AppTile from '../../components/app_tile/app_tile'; - -import template from './store.pug'; -import '../../components/app_tile/app_tile.pcss'; -import './store.pcss'; - -import '../../../../../static/img/terminal.jpg'; - -const storeApps = [ - { - link: '/terminal', - image: '/img/terminal.jpg', - name: 'Купи говно', - }, - { - link: '/terminal', - image: '/img/terminal.jpg', - name: 'Купи говно', - }, - { - link: '/terminal', - image: '/img/terminal.jpg', - name: 'Купи говно', - }, -]; - - -export default class StoreView extends Element { - constructor(parent, wrapper, views) { - super(template, parent, wrapper); - super.render(); - - this.title = 'Store'; - - this.parentViews = views; - [this.panel] = this.wrapper.getElementsByClassName('store__tile-panel'); - } - - render() { - if (this.panel.innerHTML !== '') return; - storeApps.forEach((app) => { - const tile = new AppTile(this.panel, app); - tile.show(); - }); - } - - show() { - super.show(); - this.parentViews.env.menu.hide(); - } - - hide() { - super.hide(); - this.parentViews.env.menu.show(); - } -} diff --git a/src/applications/menu/views/store/store.pcss b/src/applications/menu/views/store/store.pcss deleted file mode 100644 index a4ba3ed1..00000000 --- a/src/applications/menu/views/store/store.pcss +++ /dev/null @@ -1,34 +0,0 @@ -@import '../../../../config.pcss'; - -.store { - display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; - - - &__tile-panel { - height: 80%; - width: calc(100vw - 2 * var(--grid-padding-x)); - - scroll-snap-type: x mandatory; - // scroll-snap-stop: always; - overflow-x: auto; - overflow-y: visible; - &::-webkit-scrollbar { - display: none; - } - } -} - -@media (--small) { - .store__tile-panel { - width: calc(100vw - 2 * var(--small-grid-padding-x)); - } -} - -@media (--minimal) { - .store__tile-panel { - width: calc(100vw - 2 * var(--minimal-grid-padding-x)); - } -} diff --git a/src/applications/menu/views/store/store.pug b/src/applications/menu/views/store/store.pug deleted file mode 100644 index e719bfc3..00000000 --- a/src/applications/menu/views/store/store.pug +++ /dev/null @@ -1 +0,0 @@ -.tile-panel.store__tile-panel diff --git a/src/applications/snake/error_message/errorMessage.js b/src/applications/snake/error_message/errorMessage.js index 89bd4ad1..3984163c 100644 --- a/src/applications/snake/error_message/errorMessage.js +++ b/src/applications/snake/error_message/errorMessage.js @@ -1,6 +1,6 @@ import Element from '../../element'; import ErrorMessageTemplate from './errorMessage.pug'; -import style from './errorMessage.pcss'; +import './errorMessage.pcss'; let instance; export default class ErrorMessage extends Element { diff --git a/src/applications/snake/error_message/errorMessage.pcss b/src/applications/snake/error_message/errorMessage.pcss index 0dfa1aeb..ce2fd734 100644 --- a/src/applications/snake/error_message/errorMessage.pcss +++ b/src/applications/snake/error_message/errorMessage.pcss @@ -6,4 +6,5 @@ .error-message-text { color: #FF0000; + font-family: 'Gilroy-ExtraBold'; } \ No newline at end of file diff --git a/src/applications/snake/game/controllers/audioController.js b/src/applications/snake/game/controllers/audioController.js index 5abf9b50..7ef89c80 100644 --- a/src/applications/snake/game/controllers/audioController.js +++ b/src/applications/snake/game/controllers/audioController.js @@ -1,45 +1,112 @@ import AudioModel from '../models/audioModel'; import busController from '../../modules/busController'; -// import gameOfThrones from '../../static/audio/game_of_thrones.mp3'; -// import radioactive from '../../static/audio/radioactive.mp3'; -// import starWars from '../../static/audio/star_wars.mp3'; -// import turnDown from '../../static/audio/turn_down_for_what.mp3'; +import gameOfThrones from '../../static/audio/game_of_thrones.mp3'; +import radioactive from '../../static/audio/radioactive.mp3'; +import starWars from '../../static/audio/star_wars.mp3'; +import turnDown from '../../static/audio/turn_down_for_what.mp3'; +import pickFood from '../../static/audio/pickup.mp3'; +import deadMain from '../../static/audio/dead_main.wav'; +import deadUx from '../../static/audio/dead_ux.wav'; export default class AudioController { constructor() { this.busController = busController; this.root = '../static/audio'; this.mainAudios = [ - // gameOfThrones, - // radioactive, - // starWars, - // turnDown, + gameOfThrones, + radioactive, + starWars, + turnDown, ]; - this.init(); + this.deadAudios = [ + deadMain, + deadUx, + ]; + + this.pickFood = this.pickFood.bind(this); + this.dead = this.dead.bind(this); + this.pauseMusic = this.pauseMusic.bind(this); + this.events = { + pickFood: this.pickFood, + DEAD: this.dead, + STATUS_DEAD: this.dead, + KeyS: this.pauseMusic, + }; + + this.setNewMainAudio(); + this.setNewDead(); + + this.pause = false; + this.pickFood = new AudioModel({ path: pickFood }); + this.busController = busController; } - init() { - const audios = this.mainAudios.length; - const randomAudioIndex = Math.floor(Math.random() * (audios - 1)); - const mainAudio = this.mainAudios[randomAudioIndex]; - this.mainAudio = new AudioModel(mainAudio); + static getRandom(list) { + return list[Math.floor(Math.random() * (list.length - 0.1))]; } start() { this.mainAudio.play(); + this.mainAudio.riseVolume(); + this.setBusListeners(); + } + + setNewMainAudio() { + const mainAudio = AudioController.getRandom(this.mainAudios); + this.mainAudio = new AudioModel({ path: mainAudio, loop: true, volume: 0 }); + } + + pauseMusic() { + if (this.pause) { + this.mainAudio.play(); + } else { + this.mainAudio.pause(); + } + this.pause = !this.pause; + } + + setNewDead() { + const dead = AudioController.getRandom(this.deadAudios); + this.dead = new AudioModel({ path: dead }); } pause() { this.mainAudio.pause(); + this.setNewMainAudio(); + this.removeBusListeners(); } resume() { this.mainAudio.play(); + this.setBusListeners(); } destroy() { this.mainAudio.pause(); + this.removeBusListeners(); + } + + pickFood() { + if (!this.pause) { + this.pickFood.play(); + } + } + + dead() { + if (!this.pause) { + this.mainAudio.stop(); + this.dead.play(); + this.setNewDead(); + } + } + + setBusListeners() { + this.busController.setBusListeners(this.events); + } + + removeBusListeners() { + this.busController.removeBusListeners(this.events); } } diff --git a/src/applications/snake/game/controllers/foodController.js b/src/applications/snake/game/controllers/foodController.js index 6304e9d6..18d7bb16 100644 --- a/src/applications/snake/game/controllers/foodController.js +++ b/src/applications/snake/game/controllers/foodController.js @@ -23,9 +23,9 @@ export default class FoodController { setNewPlace() { busController.emit('food', this.food.getLetter(), this.food.getPosition()); - if (this.food.position) { - this.level.removeFood(this.food.position); - } + // if (this.food.position) { + // this.level.removeFood(this.food.position); + // } const emptyCell = this.level.getEmptyCell(); this.food.setPosition(emptyCell); this.level.setFood(emptyCell); diff --git a/src/applications/snake/game/controllers/frameSpeedController.js b/src/applications/snake/game/controllers/frameSpeedController.js index a2491874..e016c1f7 100644 --- a/src/applications/snake/game/controllers/frameSpeedController.js +++ b/src/applications/snake/game/controllers/frameSpeedController.js @@ -10,7 +10,7 @@ export default class FrameSpeedController { update() { const currentFrameSpeed = this.frameSpeed.getSpeed(); - if (currentFrameSpeed > 60) { + if (currentFrameSpeed > 50) { this.delta = -0.5; } diff --git a/src/applications/snake/game/controllers/levelController.js b/src/applications/snake/game/controllers/levelController.js index 0d33914e..9dce283d 100644 --- a/src/applications/snake/game/controllers/levelController.js +++ b/src/applications/snake/game/controllers/levelController.js @@ -59,28 +59,28 @@ export default class LevelController { } } - // for(rc = 0; rc < rwc; rc += 1) { - // // calculate a position for a random wall, somewhere within the walls of the grid. - // rwx = Math.floor(3 + Math.random() * (level.width - 6)); - // rwy = Math.floor(3 + Math.random() * (level.height - 6)); - // rwl = Math.floor(3 + Math.random() * 5); - // rwdx = ((rwx < level.width / 2) ? 1 : -1); - // rwdy = ((rwy < level.height / 2) ? 1 : -1); - - // for(ri = 0; ri < rwl; ri += 1) { - // i = level.index(rwx, rwy); - - // // don't try to make a new block when one already exists! - // if(level.wdata[i] !== 0) { - // break; - // } - - // level.wdata[i] = level.odata[i] = 2; - - // rwx += rwdx; - // rwy += rwdy; - // } - // } + const rwc = 5; + for (let rc = 0; rc < rwc; rc += 1) { + // calculate a position for a random wall, somewhere within the walls of the grid. + let rwx = Math.floor(3 + Math.random() * (levelWidth - 6)); + let rwy = Math.floor(3 + Math.random() * (levelHeight - 6)); + const rwl = Math.floor(3 + Math.random() * 5); + const rwdx = ((rwx < levelWidth / 2) ? 1 : -1); + const rwdy = ((rwy < levelHeight / 2) ? 1 : -1); + + for (let ri = 0; ri < rwl; ri += 1) { + // const i = this.level.index(rwx, rwy); + + // if(level.wdata[i] !== 0) { + // break; + // } + + this.level.setWall({ x: rwx, y: rwy }); + + rwx += rwdx; + rwy += rwdy; + } + } wallLength = this.maxWallLength; for (let y = 0; y < levelHeight; y += 1) { diff --git a/src/applications/snake/game/controllers/snackeController.js b/src/applications/snake/game/controllers/snackeController.js index 837c093c..eda2afc2 100644 --- a/src/applications/snake/game/controllers/snackeController.js +++ b/src/applications/snake/game/controllers/snackeController.js @@ -19,10 +19,20 @@ export default class SnakeController { ArrowLeft: 'LEFT', ArrowRight: 'RIGHT', }; + + this.reverseDirection = { + UP: 'DOWN', + DOWN: 'UP', + LEFT: 'RIGHT', + RIGHT: 'LEFT', + }; } setDirection(keyboardDirection) { - this.snake.setDirection(this.keyboardDirections[keyboardDirection]); + const direction = this.keyboardDirections[keyboardDirection]; + if (this.snake.direction !== this.reverseDirection[direction]) { + this.snake.setDirection(this.keyboardDirections[keyboardDirection]); + } } init() { @@ -32,7 +42,6 @@ export default class SnakeController { y: this.snake.getStartPosition().y, }); } - console.log('head init', this.snake.getSegments()[0]); } findEmptyPlace() { @@ -56,9 +65,7 @@ export default class SnakeController { } isColisionWithSelf(position) { - return this.snake.getSegments().some((segment) => { - return segment.x === position.x && segment.y === position.y; - }); + return this.snake.getSegments().some((segment) => segment.x === position.x && segment.y === position.y); } move() { diff --git a/src/applications/snake/game/core/arcade/arcadeMode.js b/src/applications/snake/game/core/arcade/arcadeMode.js new file mode 100644 index 00000000..d50f65b4 --- /dev/null +++ b/src/applications/snake/game/core/arcade/arcadeMode.js @@ -0,0 +1,180 @@ +import GameCore from '../core'; +import keyboardController from '../../../modules/keyboardController'; +import busController from '../../../modules/busController'; +import GAME_MODE from '../modes'; + +import LevelController from '../../controllers/levelController'; +import SnakeController from '../../controllers/snackeController'; +import FoodsController from '../../controllers/foodsController'; +import AudioController from '../../controllers/audioController'; +import PlayerController from '../../controllers/playerController'; +import FrameSpeedController from '../../controllers/frameSpeedController'; + +import LevelView from '../../views/levelView'; +import SnakeView from '../../views/snakeView'; +import FoodsView from '../../views/foodsView'; +import PlayerView from '../../views/playerView'; + +import LevelModel from '../../models/levelModel'; +import SnakeModel from '../../models/snakeModel'; +import FoodsModel from '../../models/foodsModel'; +import PlayerModel from '../../models/playerModel'; +import FrameSpeedModel from '../../models/frameSpeedModel'; + +import DeadMessage from '../../dead_message/dead_message'; +import DeadMenuTemplate from './dead_menu.pug'; +import ErrorMessage from '../../../error_message/errorMessage'; + +export default class ArcadeMode extends GameCore { + constructor(scene, gameInitData) { + super(scene); + + this.gameloop = this.gameloop.bind(this); + this.gameloopRequestId = null; + this.paused = false; + + this.snakeText = gameInitData.snakeText; + + this.cellCount = gameInitData.cellCount; + + this.scene = scene; + this.keyboardController = keyboardController; + this.keyboardController.setOrintation(gameInitData.orientation); + this.busController = busController; + + this.controllers = []; + + this.frameSpeed = new FrameSpeedModel(); + this.frameSpeedController = new FrameSpeedController(this.frameSpeed); + this.controllers.push(this.frameSpeedController); + + this.level = new LevelModel(this.cellCount); + + this.player = new PlayerModel(gameInitData.userToken); + this.playerView = new PlayerView(); + this.playerController = new PlayerController(this.player, this.playerView); + + this.levelController = new LevelController(this.level); + this.controllers.push(this.levelController); + this.scene.push(new LevelView(this.level)); + + // передаем колличество еды на поле + this.foods = new FoodsModel(10); + this.foodsController = new FoodsController(this.foods, this.level); + this.controllers.push(this.foodsController); + this.scene.push(new FoodsView(this.foods)); + + this.snake = new SnakeModel(this.snakeText); + this.snakeController = new SnakeController(this.snake, this.level); + this.controllers.push(this.snakeController); + this.scene.push(new SnakeView(this.snake)); + + this.audioController = new AudioController(); + this.errorMessage = new ErrorMessage(); + this.deadMessage = new DeadMessage(); + + this.resume = this.resume.bind(this); + this.resumeEvents = { + Space: this.resume, + }; + + this.pause = this.pause.bind(this); + this.stopEvents = { + Space: this.pause, + }; + this.events = { + DEAD: this.dead.bind(this), + // Space: this.pause, + }; + + this.setBusListeners(); + } + + setBusListeners() { + busController.setBusListeners(this.stopEvents); + busController.setBusListeners(this.events); + } + + removeBusListeners() { + busController.removeBusListeners(this.events); + busController.removeBusListeners(this.resumeEvents); + busController.removeBusListeners(this.events); + } + + start() { + this.errorMessage.setErrorMessage('You are in develop version'); + this.controllers.forEach(controller => controller.init()); + this.playerController.init(); + super.start(); + + this.audioController.start(); + this.lastFrame = performance.now(); + this.gameloopRequestId = requestAnimationFrame(this.gameloop); + } + + + update() { + this.controllers.forEach(controller => controller.update()); + } + + gameloop(now) { + if (!this.isDead) { + setTimeout((_) => { + this.lastFrame = now; + + if (this.keyboardController.isCommand()) { + this.snakeController.setDirection(this.keyboardController.getLastCommand()); + } + + if (!this.paused) { + this.update(); + } + + this.scene.renderScene(); + + this.gameloopRequestId = requestAnimationFrame(this.gameloop); + }, 1000 / this.frameSpeed.getSpeed()); + } + } + + dead() { + this.isDead = true; + const deadButtons = { + 'PLAY AGAIN': { + href: '/game', + params: `mode=${GAME_MODE.ARCADE}&type=${GAME_MODE.SINGLPLAYER}`, + }, + MENU: { + href: '/snake', + params: 'menu', + }, + }; + + this.deadMessage.show(DeadMenuTemplate({ deadButtons }), this.player.score); + } + + pause() { + this.paused = true; + busController.removeBusListeners(this.stopEvents); + busController.setBusListeners(this.resumeEvents); + super.pause(); + this.audioController.pause(); + } + + resume() { + busController.removeBusListeners(this.resumeEvents); + busController.setBusListeners(this.stopEvents); + super.resume(); + this.audioController.resume(); + this.paused = false; + } + + destroy() { + this.isDead = true; + super.destroy(); + this.removeBusListeners(); + this.deadMessage.hide(); + this.audioController.destroy(); + cancelAnimationFrame(this.gameloopRequestId); + } +} diff --git a/src/applications/snake/game/core/arcade/dead_menu.pug b/src/applications/snake/game/core/arcade/dead_menu.pug new file mode 100644 index 00000000..96cb7329 --- /dev/null +++ b/src/applications/snake/game/core/arcade/dead_menu.pug @@ -0,0 +1,3 @@ +each data, text in deadButtons + - href = `${data.href}?${data.params}`; + a.snakemenu-button(href=href,src=data.href, params=data.params)=text \ No newline at end of file diff --git a/src/applications/snake/game/core/arcadeMode.js b/src/applications/snake/game/core/arcadeMode.js deleted file mode 100644 index c14e66b2..00000000 --- a/src/applications/snake/game/core/arcadeMode.js +++ /dev/null @@ -1,139 +0,0 @@ -import GameCore from './core'; -import events from './events'; -import keyboardController from '../../modules/keyboardController'; -import busController from '../../modules/busController'; -import Size from '../models/size'; - - -import LevelController from '../controllers/levelController'; -import SnakeController from '../controllers/snackeController'; -import FoodsController from '../controllers/foodsController'; -import AudioController from '../controllers/audioController'; -import PlayerController from '../controllers/playerController'; -import FrameSpeedController from '../controllers/frameSpeedController'; - -import LevelView from '../views/levelView'; -import SnakeView from '../views/snakeView'; -import FoodsView from '../views/foodsView'; - -import LevelModel from '../models/levelModel'; -import SnakeModel from '../models/snakeModel'; -import FoodsModel from '../models/foodsModel'; -import PlayerModel from '../models/playerModel'; -import FrameSpeedModel from '../models/frameSpeedModel'; - -export default class ArcadeMode extends GameCore { - constructor(scene, gameInitData) { - super(scene); - - this.gameloop = this.gameloop.bind(this); - this.gameloopRequestId = null; - this.paused = false; - - this.snakeText = gameInitData.snakeText; - this.startX = gameInitData.DOMRect.x; - this.startY = gameInitData.DOMRect.y; - - this.cellCount = gameInitData.cellCount; - - this.scene = scene; - this.keyboardController = keyboardController; - this.busController = busController; - - this.controllers = []; - - this.frameSpeed = new FrameSpeedModel(); - this.frameSpeedController = new FrameSpeedController(this.frameSpeed); - this.controllers.push(this.frameSpeedController); - - this.level = new LevelModel(this.cellCount); - - this.player = new PlayerModel(); - this.playerController = new PlayerController(this.player); - - this.levelController = new LevelController(this.level); - this.controllers.push(this.levelController); - this.scene.push(new LevelView(this.level)); - - this.snake = new SnakeModel(this.snakeText, this.startX, this.startY); - this.snakeController = new SnakeController(this.snake, this.level); - this.controllers.push(this.snakeController); - this.scene.push(new SnakeView(this.snake)); - - // передаем колличество еды на поле - this.foods = new FoodsModel(10); - this.foodsController = new FoodsController(this.foods, this.level); - this.controllers.push(this.foodsController); - this.scene.push(new FoodsView(this.foods)); - - this.audioController = new AudioController(); - - this.resume = this.resume.bind(this); - this.resumeEvents = { - Space: this.resume, - }; - - this.pause = this.pause.bind(this); - this.stopEvents = { - Space: this.pause, - }; - - busController.setBusListeners(this.stopEvents); - } - - start() { - this.controllers.forEach(controller => controller.init()); - super.start(); - - this.audioController.start(); - this.lastFrame = performance.now(); - this.gameloopRequestId = requestAnimationFrame(this.gameloop); - } - - - update() { - this.controllers.forEach(controller => controller.update()); - } - - gameloop(now) { - setTimeout((_) => { - this.lastFrame = now; - - if (this.keyboardController.isCommand()) { - this.snakeController.setDirection(this.keyboardController.getLastCommand()); - } - - if (!this.paused) { - this.update(); - } - - this.scene.renderScene(); - - this.gameloopRequestId = requestAnimationFrame(this.gameloop); - }, 1000 / this.frameSpeed.getSpeed()); - } - - pause() { - this.paused = true; - busController.removeBusListeners(this.stopEvents); - busController.setBusListeners(this.resumeEvents); - super.pause(); - this.audioController.pause(); - } - - resume() { - busController.removeBusListeners(this.resumeEvents); - busController.setBusListeners(this.stopEvents); - super.resume(); - this.audioController.resume(); - this.paused = false; - } - - destroy() { - super.destroy(); - busController.removeBusListeners(this.stopEvents); - busController.removeBusListeners(this.resumeEvents); - this.audioController.destroy(); - cancelAnimationFrame(this.gameloopRequestId); - } -} diff --git a/src/applications/snake/game/core/core.js b/src/applications/snake/game/core/core.js index af9a6999..ae74916b 100644 --- a/src/applications/snake/game/core/core.js +++ b/src/applications/snake/game/core/core.js @@ -3,12 +3,10 @@ import busController from '../../modules/busController'; export default class GameCore { constructor(scene) { this.scene = scene; - this.close = this.close.bind(this); } start() { this.scene.start(); - busController.setBusListeners({ Backspace: this.close }); } pause() { @@ -19,12 +17,7 @@ export default class GameCore { // this.scene.resume(); } - close() { - busController.emit('link', '/snake'); - } - destroy() { // this.scene.destroy(); - busController.removeBusListeners({ Backspace: this.close }); } } diff --git a/src/applications/snake/game/core/multiplayer/dead_menu.pug b/src/applications/snake/game/core/multiplayer/dead_menu.pug index ef2e7092..96cb7329 100644 --- a/src/applications/snake/game/core/multiplayer/dead_menu.pug +++ b/src/applications/snake/game/core/multiplayer/dead_menu.pug @@ -1,2 +1,3 @@ each data, text in deadButtons - a.snakemenu-button(href=data.href, params=data.params)=text \ No newline at end of file + - href = `${data.href}?${data.params}`; + a.snakemenu-button(href=href,src=data.href, params=data.params)=text \ No newline at end of file diff --git a/src/applications/snake/game/core/multiplayer/loader/loader.js b/src/applications/snake/game/core/multiplayer/loader/loader.js index cba16e9f..f03f4b37 100644 --- a/src/applications/snake/game/core/multiplayer/loader/loader.js +++ b/src/applications/snake/game/core/multiplayer/loader/loader.js @@ -15,6 +15,8 @@ export default class Loader { this.controllers = []; this.views = []; + this.startTmer = this.startTmer.bind(this); + this.snakes = 1; for (let i = 0; i < this.snakes; i += 1) { const snake = new SnakeModel(); @@ -28,16 +30,42 @@ export default class Loader { controller.init(); }); this.gameloop(); + this.bigin = (new Date()).getTime(); + + this.timer = document.createElement('div'); + this.timer.classList.add('multiplayer-timer'); + const [root] = document.getElementsByClassName('snakegame-container'); + root.appendChild(this.timer); + this.startTmer(); } - gameloop() { - this.timerId = setTimeout((_) => { + hideTimer() { + this.timer.hidden = true; + } + + showTimer() { + this.timer.hidden = false; + } + + startTmer() { + let s = (new Date()).getTime() - this.bigin; + s = parseInt(s / 1000, 10); + const m = parseInt(s / 60, 10); + s -= m * 60; + + this.timer.innerHTML = `${m}:${s}`; + // console.log(`${m}:${s}`); + this.timerId = setTimeout(this.startTmer, 1000); + } + + gameloop() { + this.loaderId = setTimeout((_) => { this.clearCanvas(); this.controllers.forEach((controller) => { controller.update(); }); - + this.views.forEach((view) => { view.render(); }); @@ -47,7 +75,11 @@ export default class Loader { } stop() { - clearInterval(this.timerId); + if (this.timerId) { + clearInterval(this.timerId); + } + this.timer.remove(); + clearInterval(this.loaderId); cancelAnimationFrame(this.gameloopRequestId); } diff --git a/src/applications/snake/game/core/multiplayer/loader/snakeController.js b/src/applications/snake/game/core/multiplayer/loader/snakeController.js index 7adeca9d..a237a0f2 100644 --- a/src/applications/snake/game/core/multiplayer/loader/snakeController.js +++ b/src/applications/snake/game/core/multiplayer/loader/snakeController.js @@ -36,23 +36,31 @@ export default class SnakeController { case 1: if (newX < this.loaderParams.widthCellCount) { newX += 1; - break; + } else { + this.direction = 3; } + break; case 2: if (newX > 1) { newX -= 1; - break; + } else { + this.direction = 3; } + break; case 3: if (newY < this.loaderParams.heightCellCount) { newY += 1; - break; + } else { + this.direction = 1; } - case 2: + break; + case 4: if (newY > 1) { newY -= 1; - break; + } else { + this.direction = 1; } + break; default: break; } diff --git a/src/applications/snake/game/core/multiplayer/loader/snakeView.js b/src/applications/snake/game/core/multiplayer/loader/snakeView.js index 627b5a91..8e6f659d 100644 --- a/src/applications/snake/game/core/multiplayer/loader/snakeView.js +++ b/src/applications/snake/game/core/multiplayer/loader/snakeView.js @@ -5,19 +5,21 @@ export default class SnakeView { this.snake = snake; this.context = context; this.loaderInfo = loaderInfo; + // this.loaderInfo.cellSize.height = 8; + // this.loaderInfo.cellSize.width = 8; } render() { - this.context.fillStyle = 'greenyellow'; + this.context.fillStyle = config.snakeColor; this.context.strokeStyle = config.snakeColor; this.context.lineWidth = 1; - const size = 0; + const size = 2; if (this.snake.segments) { this.snake.getSegments().forEach((segment) => { this.context.beginPath(); - this.context.rect(segment.x * (this.loaderInfo.cellSize.width + size), - segment.y * (this.loaderInfo.cellSize.height + size), + this.context.rect(segment.x * this.loaderInfo.cellSize.width, + segment.y * this.loaderInfo.cellSize.height, this.loaderInfo.cellSize.width + size, this.loaderInfo.cellSize.height + size); this.context.fill(); this.context.stroke(); diff --git a/src/applications/snake/game/core/multiplayer/multiplayer.pcss b/src/applications/snake/game/core/multiplayer/multiplayer.pcss index ca90e05a..8a98dc7d 100644 --- a/src/applications/snake/game/core/multiplayer/multiplayer.pcss +++ b/src/applications/snake/game/core/multiplayer/multiplayer.pcss @@ -39,4 +39,17 @@ .player_white { color: white; +} + +.player_dead::before { + content: "- "; +} + +.multiplayer-timer { + text-align: center; + position: absolute; + top: 50%; + font-family: 'Press Start 2P', cursive; + font-size: 30px; + width: 100%; } \ No newline at end of file diff --git a/src/applications/snake/game/core/multiplayer/online.js b/src/applications/snake/game/core/multiplayer/online.js index 4939dcb0..f6f7acdf 100644 --- a/src/applications/snake/game/core/multiplayer/online.js +++ b/src/applications/snake/game/core/multiplayer/online.js @@ -1,22 +1,24 @@ import GameCore from '../core'; import busController from '../../../modules/busController'; -import GAME_MODE from '../modes'; import LevelController from '../../controllers/levelController'; -import SnakeController from '../../controllers/snackeController'; +// import SnakeController from '../../controllers/snackeController'; import FoodsController from '../../controllers/foodsController'; import AudioController from '../../controllers/audioController'; import keyboardController from '../../../modules/keyboardController'; import LevelView from '../../views/levelView'; -import SnakeView from '../../views/snakeView'; +// import SnakeView from '../../views/snakeView'; import FoodsView from '../../views/foodsView'; import EnemiesView from '../../views/enemiesView'; import LevelModel from '../../models/levelModel'; -import SnakeModel from '../../models/snakeModel'; +// import SnakeModel from '../../models/snakeModel'; import FoodsModel from '../../models/foodsModel'; import EnemiesModel from '../../models/enemiesModel'; +import globalUser from '../../../globalUser'; +import PlayerModel from '../../models/playerModel'; +import PlayersModel from '../../models/playersModel'; import WsMessageParser from '../../../modules/wsMessageParser'; import WsPostman from '../../../modules/wsPostman'; @@ -38,43 +40,45 @@ export default class OnlineGame extends GameCore { this.lastFrame = 0; this.framesPerSecond = 30; - this.cellCount = gameInitData.cellCount; + this.gameInitData = gameInitData; + this.cellCount = this.gameInitData.cellCount; this.scene = scene; this.keyboardController = keyboardController; + this.keyboardController.setOrintation(gameInitData.orientation); this.busController = busController; - this.controllers = []; this.level = new LevelModel(this.cellCount); this.levelController = new LevelController(this.level); - this.controllers.push(this.levelController); this.scene.push(new LevelView(this.level)); - this.snake = new SnakeModel(); - this.snakeController = new SnakeController(this.snake, this.level); - this.controllers.push(this.snakeController); - this.scene.push(new SnakeView(this.snake)); - this.foods = new FoodsModel(10); this.foodsController = new FoodsController(this.foods, this.level); - this.controllers.push(this.foodsController); this.scene.push(new FoodsView(this.foods)); - this.enemies = new EnemiesModel(gameInitData.userToken); + this.boosts = new FoodsModel(10); + this.boostsController = new FoodsController(this.boosts, this.level); + this.scene.push(new FoodsView(this.boosts, '#400040')); + + this.player = new PlayerModel(); + + this.enemies = new EnemiesModel(this.player); this.scene.push(new EnemiesView(this.enemies)); - this.wsMessageParser.setModel('snakes', this.snake); this.wsMessageParser.setModel('snakes', this.enemies); this.wsMessageParser.setModel('food', this.foods); this.wsMessageParser.setModel('walls', this.level); - + this.wsMessageParser.setModel('boosters', this.boosts); + + this.playersModel = new PlayersModel(); this.deadMessage = new DeadMessage(); - // this.audioController = new AudioController(); + this.audioController = new AudioController(); this.events = { STATUS_DEAD: this.dead.bind(this), + win: this.win.bind(this), }; } @@ -91,6 +95,7 @@ export default class OnlineGame extends GameCore { this.wsPostman.startGame(); super.start(); + this.audioController.start(); this.setBusListeners(); this.lastFrame = performance.now(); this.gameloopRequestId = requestAnimationFrame(this.gameloop); @@ -113,29 +118,54 @@ export default class OnlineGame extends GameCore { }, 1000 / this.framesPerSecond); } - dead() { - this.isDead = true; - const deadButtons = { - 'PLAY AGAIN': { - href: '/game', - params: `mode=${GAME_MODE.MULTIPLYER}&type=${GAME_MODE.SINGLPLAYER}`, - }, - MENU: { - href: '/snake', - params: 'menu', - }, - }; - const template = DeadMenuTemplate({ deadButtons }); - this.deadMessage.show(template, 25); + win(message) { + if (message.payload.user_token === globalUser.userToken) { + const deadButtons = { + 'PLAY AGAIN': { + href: '/game', + params: `mode=${this.gameInitData.mode}&type=${this.gameInitData.type}`, + }, + MENU: { + href: '/snake', + params: 'menu', + }, + }; + const template = DeadMenuTemplate({ deadButtons }); + this.deadMessage.show(template, this.player.score, 'You Won!'); + } + } + + dead(message) { + if (message.payload.user_token === globalUser.userToken) { + this.isDead = true; + this.playersModel.setDead(message.payload.user_serial); + + const deadButtons = { + 'PLAY AGAIN': { + href: '/game', + params: `mode=${this.gameInitData.mode}&type=${this.gameInitData.type}`, + }, + MENU: { + href: '/snake', + params: 'menu', + }, + }; + const template = DeadMenuTemplate({ deadButtons }); + this.deadMessage.show(template, this.player.score); + } else { + this.playersModel.setDead(message.payload.user_serial); + } + // this.multiplayer.dead(); } destroy() { + this.wsPostman.sendGameExit(); this.wsPostman.sendRemoveFromRoom(); super.destroy(); this.deadMessage.hide(); this.removeBusListeners(); - // this.audioController.destroy(); + this.audioController.destroy(); clearTimeout(this.timerId); cancelAnimationFrame(this.gameloopRequestId); } diff --git a/src/applications/snake/game/core/multiplayer/ready_message/ready_message.js b/src/applications/snake/game/core/multiplayer/ready_message/ready_message.js index b16d1156..1c18327e 100644 --- a/src/applications/snake/game/core/multiplayer/ready_message/ready_message.js +++ b/src/applications/snake/game/core/multiplayer/ready_message/ready_message.js @@ -27,28 +27,22 @@ export default class ReadyMessage extends BaseMenu { busController.setBusListeners(this.events); } + acceptMembers() { + + } + removeListeners() { - busController.removeBusListeners(this.events); + busController.removeBusListeners(this.events); } quickSearchStart() { - console.log('quickSearchStart'); this.wsPostman.quickSearchAccept(); } quickSearchQuit() { - console.log('quickSearchQuit'); this.wsPostman.quickSearchAbort(); } - quickSearchQuit(message) { - console.log('accepted', message.payload); - } - - acceptMembers() { - - } - show() { this.setListeners(); this.startTimer(); @@ -57,6 +51,7 @@ export default class ReadyMessage extends BaseMenu { startTimer() { [this.timer] = this.parent.getElementsByClassName('timer'); + this.timer.innerHTML = 30; this.begin = new Date().getSeconds(); this.timerLoop(); } @@ -64,7 +59,18 @@ export default class ReadyMessage extends BaseMenu { timerLoop() { this.timerId = setInterval(() => { const now = new Date(); - const distance = this.time - (now.getSeconds() - this.begin); + const nowSeconds = now.getSeconds(); + let distance; + if (nowSeconds > this.begin) { + distance = this.time - (nowSeconds - this.begin); + } else { + distance = this.begin - nowSeconds - this.time; + } + if (!distance || distance > 30) { + distance = 30; + } else if (distance < 0) { + distance = 0; + } this.timer.innerHTML = distance; }, 1000); } diff --git a/src/applications/snake/game/core/multiplayer/waitingPlayers.js b/src/applications/snake/game/core/multiplayer/waitingPlayers.js index 33735436..1f8c3760 100644 --- a/src/applications/snake/game/core/multiplayer/waitingPlayers.js +++ b/src/applications/snake/game/core/multiplayer/waitingPlayers.js @@ -3,6 +3,8 @@ import GAME_MODE from '../modes'; import Loader from './loader/loader'; import busController from '../../../modules/busController'; import ReadyMessage from './ready_message/ready_message'; +import ErrorMessage from '../../../error_message/errorMessage'; +import config from '../../../modules/view_config'; export default class WaitingPlayers { constructor(canvas, gameParams, gameInfo) { @@ -10,10 +12,21 @@ export default class WaitingPlayers { this.gameParams = gameParams; this.gameInfo = gameInfo; this.loader = new Loader(this.canvas, this.gameParams); - + this.errorMessage = new ErrorMessage(); + + this.updateTable = this.updateTable.bind(this); + this.quickSearchReady = this.quickSearchReady.bind(this); + this.removed = this.removed.bind(this); + this.kick = this.kick.bind(this); + this.acceptStatus = this.acceptStatus.bind(this); + this.hideReadyMesage = this.hideReadyMesage.bind(this); this.events = { - quick_search_status: this.updateTable.bind(this), - quick_search_ready: this.quickSearchReady.bind(this), + quick_search_added: this.updateTable, + // quick_search_accept_status: this.acceptStatus, + quick_search_ready: this.quickSearchReady, + quick_search_removed: this.removed, + quick_search_kick: this.kick, + QUICKSEARCH_START: this.hideReadyMesage, }; } @@ -29,7 +42,7 @@ export default class WaitingPlayers { this.setBusListeners(); this.quickSearchStart(); this.setEnviroment(); - this.loader.start(); + this.loader.start(this.canvas); } quickSearchStart() { @@ -53,18 +66,30 @@ export default class WaitingPlayers { } quickSearchReady(message) { + this.loader.hideTimer(); this.readyMessage = new ReadyMessage(message.payload.accept_timeout); this.readyMessage.show(); } - setEnviroment() { - this.canvas.width = this.gameParams.windowWidth; - this.canvas.height = this.gameParams.windowHeight; + kick(message) { + this.errorMessage.setErrorMessage('You were kiked from the room'); + busController.emit('link', '/snake'); + } + hideReadyMesage() { + this.readyMessage.hide(); + this.loader.showTimer(); + } + + acceptStatus() { + this.readyMessage.hide(); + } + + setEnviroment() { [this.container] = document.getElementsByClassName('snakegame-container__multiplayer'); [this.canvas] = this.container.getElementsByClassName('snakegame-canvas'); - this.canvas.classList.remove('game-board__purple'); + this.canvas.classList.remove(config.gameCanvasBorder); [this.score] = this.container.getElementsByClassName('main-score'); this.score.hidden = true; @@ -72,28 +97,37 @@ export default class WaitingPlayers { [this.game_mode] = this.container.getElementsByClassName('game_mode'); this.temp_mode = this.game_mode.innerHTML; this.game_mode.innerHTML = 'WAITING FOR PLAYERS'; + + this.canvas.width = this.gameParams.windowWidth * 0.99; + this.canvas.height = this.gameParams.windowHeight * 0.99; } removeEnviroment() { - this.canvas.classList.add('game-board__purple'); + this.canvas.classList.add(config.gameCanvasBorder); this.score.hidden = false; this.game_mode.innerHTML = this.temp_mode; } + removed(message) { + this.loader.showTimer(); + this.errorMessage.setErrorMessage('Player left the room'); + this.updateTable(message); + this.readyMessage.hide(); + } + updateTable(message) { - console.log('wait player'); const playersCollection = document.getElementsByClassName('players'); this.players = Array.prototype.slice.call(playersCollection); message.payload.members.forEach((member) => { - this.member = playersCollection.item(member.user_serial); - this.players.splice(this.players.indexOf(this.member), 1); - [this.player] = this.member.getElementsByClassName('player'); - // this.player.innerHTML = member.user_serial; - this.member.hidden = false; - this.player.innerHTML = `player${member.user_serial}`; - - [this.score] = this.member.getElementsByClassName('score'); - this.score.innerHTML = 0; + this.player = playersCollection.item(member.user_serial); + this.players.splice(this.players.indexOf(this.player), 1); + [this.playerName] = this.player.getElementsByClassName('player'); + this.player.hidden = false; + if (member.user_name) { + this.playerName.innerHTML = `${member.user_name}`; + } else { + this.playerName.innerHTML = `player${member.user_serial}`; + } }); this.players.forEach(player => player.hidden = true); diff --git a/src/applications/snake/game/core/offline/offline.js b/src/applications/snake/game/core/offline/offline.js index 3d8987ba..f0a074aa 100644 --- a/src/applications/snake/game/core/offline/offline.js +++ b/src/applications/snake/game/core/offline/offline.js @@ -28,6 +28,7 @@ export default class OfflineGame extends GameCore { constructor(scene, gameInitData) { super(scene); + keyboardController.setOrintation(gameInitData.orientation); this.gameloop = this.gameloop.bind(this); this.gameloopRequestId = null; this.framesPerSecond = 10; @@ -38,8 +39,6 @@ export default class OfflineGame extends GameCore { this.scene = scene; this.busController = busController; - this.deadMessage = new DeadMessage(); - this.controllers = []; this.level = new LevelModel(this.cellCount); @@ -64,9 +63,11 @@ export default class OfflineGame extends GameCore { this.audioController = new AudioController(); this.errorMessage = new ErrorMessage(); + this.deadMessage = new DeadMessage(); this.events = { DEAD: this.dead.bind(this), + // Space: this.pause, }; } @@ -79,7 +80,6 @@ export default class OfflineGame extends GameCore { } start() { - this.errorMessage.setErrorMessage('You are offline'); this.controllers.forEach(controller => controller.init()); this.playerController.init(); super.start(); diff --git a/src/applications/snake/game/dead_message/dead_message.js b/src/applications/snake/game/dead_message/dead_message.js index ba416309..8a32d42c 100644 --- a/src/applications/snake/game/dead_message/dead_message.js +++ b/src/applications/snake/game/dead_message/dead_message.js @@ -1,6 +1,6 @@ import BaseMenu from '../../game_menu/base_menu/base_menu'; import DeadMessageTemplate from './dead_message.pug'; -import style from './dead_message.pcss'; +import './dead_message.pcss'; export default class DeadMessage extends BaseMenu { constructor() { @@ -12,11 +12,15 @@ export default class DeadMessage extends BaseMenu { super.hide(); } - show(deadMenuContent, score) { + show(deadMenuContent, score, preText) { const [deadScore] = this.parent.getElementsByClassName('dead-message__score'); deadScore.innerHTML = score; const [deadMenu] = this.parent.getElementsByClassName('dead-menu'); deadMenu.innerHTML = deadMenuContent; + if (preText) { + const [deadText] = this.parent.getElementsByClassName('dead-message__text'); + deadText.innerHTML = preText; + } super.show(); } diff --git a/src/applications/snake/game/game.js b/src/applications/snake/game/game.js index 0a530951..67279a45 100644 --- a/src/applications/snake/game/game.js +++ b/src/applications/snake/game/game.js @@ -1,27 +1,35 @@ import GAME_MODE from './core/modes'; import OfflineGame from './core/offline/offline'; import OnlineGame from './core/multiplayer/online'; -import ArcadeGame from './core/arcadeMode'; +import ArcadeGame from './core/arcade/arcadeMode'; import GameScene from './core/gameScene'; import Size from './models/size'; import WaitingPlayers from './core/multiplayer/waitingPlayers'; import busController from '../modules/busController'; import config from './utils/game_config'; +import ErrorMessage from '../error_message/errorMessage'; import './game.pcss'; +import '../static/images/home.svg'; +import '../static/images/arrow.png'; import WsPostman from '../modules/wsPostman'; let GameConstructor; export default class Game { constructor(gameInfo, canvas, gameInitData) { this.gameInitData = gameInitData; + this.gameInfo = gameInfo; this.canvas = canvas; + this.wsPostman = new WsPostman(); + this.errorMessage = new ErrorMessage(); + this.busController = busController; this.countGameParams(); + this.close = this.close.bind(this); + this.busController.setBusListeners({ Backspace: this.close }); switch (gameInfo.mode) { case GAME_MODE.CLASSIC: { if (gameInfo.type === GAME_MODE.SINGLPLAYER) { - // GameConstructor = OnlineGame; GameConstructor = OfflineGame; } else { GameConstructor = OfflineGame; @@ -37,6 +45,11 @@ export default class Game { } case GAME_MODE.MULTIPLAYER: { + // if (!this.wsPostman.isReady()) { + // this.errorMessage.setErrorMessage('You are offline'); + // this.busController.emit('link', '/snake'); + // return; + // } GameConstructor = OnlineGame; this.waitingPlayers = new WaitingPlayers(this.canvas, this.gameInitData, gameInfo); this.waitingPlayers.start(); @@ -54,11 +67,15 @@ export default class Game { this.canvas, this.windowSize, this.cellSize, - this.gameInitData.otientation, // FIXME: typo? + this.gameInitData.orientation, ); this.gameCore = new GameConstructor(this.gameScene, this.gameInitData); } + close() { + this.busController.emit('link', '/snake'); + } + countGameParams() { let cellWidth; if (this.gameInitData.windowWidth > this.gameInitData.windowHeight) { @@ -68,7 +85,7 @@ export default class Game { this.gameInitData.windowHeight / this.gameInitData.heightCellCount, ), ); - this.gameInitData.otientation = config.HORIZONTAL; + this.gameInitData.orientation = config.HORIZONTAL; } else { cellWidth = Math.floor( Math.min( @@ -76,7 +93,7 @@ export default class Game { this.gameInitData.windowHeight / this.gameInitData.widthCellCount, ), ); - this.gameInitData.otientation = config.VERTICAL; + this.gameInitData.orientation = config.VERTICAL; } // реальные размеры одной ячейки @@ -92,12 +109,13 @@ export default class Game { // размерность поля игры this.gameInitData.cellCount = new Size(this.gameInitData.widthCellCount, this.gameInitData.heightCellCount); + + this.gameInitData.mode = this.gameInfo.mode; + this.gameInitData.type = this.gameInfo.type; } start(message) { - console.log('start quick search'); if (this.events) { - this.wsPostman = new WsPostman(); this.wsPostman.setRoomToken(message.payload.room_token); busController.removeBusListeners(this.events); this.waitingPlayers.stop(); @@ -121,5 +139,6 @@ export default class Game { if (this.waitingPlayers) { this.waitingPlayers.stop(); } + busController.removeBusListeners({ Backspace: this.close }); } } diff --git a/src/applications/snake/game/game.pcss b/src/applications/snake/game/game.pcss index bb97ce8d..415e4bfa 100644 --- a/src/applications/snake/game/game.pcss +++ b/src/applications/snake/game/game.pcss @@ -1,26 +1,33 @@ -.main-score__purple { - color: #A757BD; +.main-score__greenred { + color: #a1ff0a; } .main-score__greenyellow { - color: greenyellow; + color: #4d00ff; } .main-score__pink { color: pink; } +.main-score__cyan { + color: cyan; +} + .home { - // content:url(../static/images/home.png); - background: url('../static/images/home.svg'); + background-image: url('./img/arrow.png'); display: none; - width: 52px; - + width: 24px; + height: 24px; + justify-self: center; + align-self: center; + margin-left: 3px; } - @media screen and (max-width: 555px) { - .home { - display: grid; - grid-area: home; - } - } \ No newline at end of file +@media screen and (max-width: 555px) { + .home { + display: grid; + grid-area: home; + } +} + diff --git a/src/applications/snake/game/models/audioModel.js b/src/applications/snake/game/models/audioModel.js index ef99ecf6..8b6d0864 100644 --- a/src/applications/snake/game/models/audioModel.js +++ b/src/applications/snake/game/models/audioModel.js @@ -1,10 +1,11 @@ export default class AudioModel { - constructor(path, volume = 1, loop = false) { + constructor({ path, volume = 1, loop = false }) { this.path = path; this.audio = new Audio(); this.audio.src = `./${path}`; this.audio.loop = loop; this.audio.volume = volume; + this.riseVolume = this.riseVolume.bind(this); } pause() { @@ -16,6 +17,13 @@ export default class AudioModel { this.pause(); } + riseVolume() { + if (this.audio.volume < 0.9) { + this.audio.volume += 0.1; + setTimeout(this.riseVolume, 1500); + } + } + play() { if (this.audio.playing) { this.stop(); diff --git a/src/applications/snake/game/models/enemiesModel.js b/src/applications/snake/game/models/enemiesModel.js index 6c17fa6d..11a70afe 100644 --- a/src/applications/snake/game/models/enemiesModel.js +++ b/src/applications/snake/game/models/enemiesModel.js @@ -1,8 +1,11 @@ import EnemyModel from './enemyModel'; +import PlayersModel from './playersModel'; export default class EnemiesModel { - constructor() { + constructor(player) { this.enemySnakes = []; + this.players = new PlayersModel(); + this.player = player; } push(snake) { @@ -14,11 +17,13 @@ export default class EnemiesModel { this.segments = []; if (snakes) { snakes.forEach((snake) => { - // if (snake.user_token !== this.userToken) { const enemy = new EnemyModel(); enemy.setState(snake); this.push(enemy); - // } + this.players.setState(snake.user_serial, snake.score); + if (snake.user_token === this.player.userToken) { + this.player.score = snake.score; + } }); } } diff --git a/src/applications/snake/game/models/enemyModel.js b/src/applications/snake/game/models/enemyModel.js index 7799b921..4817be75 100644 --- a/src/applications/snake/game/models/enemyModel.js +++ b/src/applications/snake/game/models/enemyModel.js @@ -1,5 +1,3 @@ -import globalUser from '../../globalUser'; - export default class EnemyModel { constructor() { this.segments = []; @@ -9,8 +7,8 @@ export default class EnemyModel { this.user_serial = snake.user_serial; snake.body.forEach((segment) => { this.segments.push({ - x: segment.position.X, - y: segment.position.Y, + x: segment.position.x, + y: segment.position.y, }); }); } diff --git a/src/applications/snake/game/models/foodModel.js b/src/applications/snake/game/models/foodModel.js index 24b14413..a0dbddeb 100644 --- a/src/applications/snake/game/models/foodModel.js +++ b/src/applications/snake/game/models/foodModel.js @@ -23,7 +23,10 @@ export default class FoodModel { } getX() { - return this.position.x; + if (this.position) { + return this.position.x; + } + return undefined; } getLetter() { @@ -31,7 +34,10 @@ export default class FoodModel { } getY() { - return this.position.y; + if (this.position) { + return this.position.y; + } + return undefined; } getPosition() { diff --git a/src/applications/snake/game/models/foodsModel.js b/src/applications/snake/game/models/foodsModel.js index 960cdcac..b3b7d9bf 100644 --- a/src/applications/snake/game/models/foodsModel.js +++ b/src/applications/snake/game/models/foodsModel.js @@ -14,10 +14,12 @@ export default class FoodsModel { setState(foods) { this.foods = []; - foods.forEach((apple) => { - const newFood = new FoodModel(); - newFood.setPosition(new Position(apple.position.X, apple.position.Y)); - this.push(newFood); - }); + if (foods) { + foods.forEach((apple) => { + const newFood = new FoodModel(); + newFood.setPosition(new Position(apple.position.x, apple.position.y)); + this.push(newFood); + }); + } } } diff --git a/src/applications/snake/game/models/levelModel.js b/src/applications/snake/game/models/levelModel.js index 53fa5b3a..58411f82 100644 --- a/src/applications/snake/game/models/levelModel.js +++ b/src/applications/snake/game/models/levelModel.js @@ -93,8 +93,10 @@ export default class levelModel { setState(walls) { this.walls = []; - walls.forEach((wall) => { - this.setWall(new Position(wall.X, wall.Y)); - }); + if (walls) { + walls.forEach((wall) => { + this.setWall(new Position(wall.x, wall.y)); + }); + } } } diff --git a/src/applications/snake/game/models/playerModel.js b/src/applications/snake/game/models/playerModel.js index 391df87c..ce126d4d 100644 --- a/src/applications/snake/game/models/playerModel.js +++ b/src/applications/snake/game/models/playerModel.js @@ -1,12 +1,10 @@ +import globalUser from '../../globalUser'; + export default class Player { - constructor(userToken) { + constructor() { this.score = 0; this.isDead = false; - this.userToken = userToken; - } - - setSnake(snake) { - this.snake = snake; + this.userToken = globalUser.userToken; } setDead() { diff --git a/src/applications/snake/game/models/playersModel.js b/src/applications/snake/game/models/playersModel.js index bbe8fac4..ab57ec6e 100644 --- a/src/applications/snake/game/models/playersModel.js +++ b/src/applications/snake/game/models/playersModel.js @@ -1,9 +1,21 @@ +let instance; export default class PlayersModel { constructor() { + if (!instance) { + this.players = document.getElementsByClassName('players'); + instance = this; + } + return instance; + } + setState(userSerial, score) { + const player = this.players.item(userSerial); + const [scoreElement] = player.getElementsByClassName('score'); + scoreElement.innerHTML = score; } - setState() { - + setDead(userSerial) { + const player = this.players.item(userSerial); + player.classList.add('player_dead'); } } diff --git a/src/applications/snake/game/models/snakeModel.js b/src/applications/snake/game/models/snakeModel.js index 8a998536..3987dc2c 100644 --- a/src/applications/snake/game/models/snakeModel.js +++ b/src/applications/snake/game/models/snakeModel.js @@ -1,4 +1,5 @@ import globalUser from '../../globalUser'; +import PlayersModel from './playersModel'; export default class SnakeModel { constructor() { @@ -27,6 +28,7 @@ export default class SnakeModel { this.direction = this.directions.RIGHT; this.prevDirection = this.direction; + this.playersModel = new PlayersModel(); this.destroyed = false; } @@ -86,11 +88,12 @@ export default class SnakeModel { if (snakes) { snakes.forEach((snake) => { if (snake.user_token === this.userToken) { - // this.playerId = snake.playerId + this.playersModel.setState(snake.user_serial, snake.score); + this.score = snake.score; snake.body.forEach((segment) => { this.segments.push({ - x: segment.position.X, - y: segment.position.Y, + x: segment.position.x, + y: segment.position.y, }); }); } diff --git a/src/applications/snake/game/templates/snakegame.pug b/src/applications/snake/game/templates/snakegame.pug index 6d5b4a28..a6e7a35e 100644 --- a/src/applications/snake/game/templates/snakegame.pug +++ b/src/applications/snake/game/templates/snakegame.pug @@ -1,13 +1,16 @@ .game_mode ARCADE a.home(href='/snake') -.main-score.main-score__purple +- if (!mainScoreCollor) mainScoreCollor='main-score__greenred'; +- if (!gameCanvasBorder) gameCanvasBorder='game-board__greenred'; +.main-score(class=mainScoreCollor) span.score__text Score: span.score__value 0 + .canvas-wrapper - canvas.snakegame-canvas.game-border__purple + canvas.snakegame-canvas(class=gameCanvasBorder) .game-board-wrapper(hidden) .game-board-name Players - .game-board.game-board__purple.game-board__hidden-border + .game-board.game-board__hidden-border(class=gameBoardBorder) .players.scores(user_serial=0) .player.player_greenyellow .score.player_greenyellow @@ -19,4 +22,14 @@ a.home(href='/snake') .score.player_green .players.scores(user_serial=3) .player.player_blue - .score.player_blue \ No newline at end of file + .score.player_blue +.gamehotkeys + .gamehotkey + .gamehotkey-key(class=mainScoreCollor) Backspace + .gamehotkey_descroption - go out + .gamehotkey + .gamehotkey-key(class=mainScoreCollor) S + .gamehotkey_descroption - stop the music + .gamehotkey + .gamehotkey-key(class=mainScoreCollor) T + .gamehotkey_descroption - change theme diff --git a/src/applications/snake/game/utils/canvas.js b/src/applications/snake/game/utils/canvas.js index 19267b2e..c8bb4e7d 100644 --- a/src/applications/snake/game/utils/canvas.js +++ b/src/applications/snake/game/utils/canvas.js @@ -84,7 +84,7 @@ export default class Canvas { this.context.closePath(); } - setBlackBackground(width, height) { + setBlackBackground() { this.context.clearRect(0, 0, this.canvas.width * this.cellSize.width, this.canvas.height * this.cellSize.height); } diff --git a/src/applications/snake/game/utils/game_view.js b/src/applications/snake/game/utils/game_view.js index 22590929..bd8a570e 100644 --- a/src/applications/snake/game/utils/game_view.js +++ b/src/applications/snake/game/utils/game_view.js @@ -4,6 +4,8 @@ import GAME_MODE from '../core/modes'; import SnakeGameTemplate from '../templates/snakegame.pug'; import '../../static/images/home.svg'; +import config from '../../modules/view_config'; + import Element from '../../../element'; @@ -21,7 +23,12 @@ export default class GameView extends Element { render(gameParams) { this.gameParams = gameParams; - super.render(); + const renderParams = { + mainScoreCollor: config.mainScoreCollor, + gameCanvasBorder: config.gameCanvasBorder, + gameBoardBorder: config.gameBoardBorder, + }; + super.render(renderParams); } setMultiplayerEnviroment() { @@ -31,14 +38,23 @@ export default class GameView extends Element { [this.game_mode] = this.parent.getElementsByClassName('game_mode'); this.game_mode.innerHTML = 'MULTIPLAYER'; + [this.main_score] = this.parent.getElementsByClassName('main-score'); + this.main_score.classList.add('main-score_miltiplayer'); + [this.gameBoard] = document.getElementsByClassName('game-board-wrapper'); this.gameBoard.hidden = false; } + setArcadeEnvironmemnt() { + [this.game_mode] = this.parent.getElementsByClassName('game_mode'); + this.game_mode.innerHTML = 'ARCADE'; + } + - removeMultiplayerEnviroment() { + removeMultiplayerEnvironment() { this.snakegameContainer.classList.remove('snakegame-container__multiplayer'); this.gameBoard.hidden = true; + this.main_score.classList.remove('main-score_miltiplayer'); } setSinglplayerEnviroment() { @@ -56,11 +72,14 @@ export default class GameView extends Element { if (this.gameParams.mode === GAME_MODE.MULTIPLAYER) { this.setMultiplayerEnviroment(); + } else if (this.gameParams.mode === GAME_MODE.ARCADE) { + this.setArcadeEnvironmemnt(); } else { this.setSinglplayerEnviroment(); } this.gameParams.onLine = navigator.onLine; + const [canvasWrapper] = this.wrapper.getElementsByClassName('canvas-wrapper'); this.gameInitData = { userToken: this.userToken, @@ -74,17 +93,17 @@ export default class GameView extends Element { } startGame() { - console.log('game init data', this.gameInitData); const [canvas] = this.wrapper.getElementsByClassName('snakegame-canvas'); this.game = new Game(this.gameParams, canvas, this.gameInitData); - // this.game.start(); } hide() { super.hide(); if (this.gameParams.mode === GAME_MODE.MULTIPLAYER) { - this.removeMultiplayerEnviroment(); + this.removeMultiplayerEnvironment(); + } + if (this.game) { + this.game.destroy(); } - this.game.destroy(); } } diff --git a/src/applications/snake/game/views/foodView.js b/src/applications/snake/game/views/foodView.js index f717be35..ef5fc351 100644 --- a/src/applications/snake/game/views/foodView.js +++ b/src/applications/snake/game/views/foodView.js @@ -5,12 +5,18 @@ export default class FoodView { this.foodModel = foodModel; } - render(canvas, foodModel) { + render(canvas, foodModel, color) { if (foodModel) { this.foodModel = foodModel; } - const fillStyle = config.foodColor; + let fillStyle; + if (color) { + fillStyle = color; + } else { + fillStyle = config.foodColor; + } + const strokeStyle = config.foodColor; if (this.foodModel.position) { diff --git a/src/applications/snake/game/views/foodsView.js b/src/applications/snake/game/views/foodsView.js index c5995a40..d8ec1831 100644 --- a/src/applications/snake/game/views/foodsView.js +++ b/src/applications/snake/game/views/foodsView.js @@ -1,13 +1,18 @@ import FoodView from './foodView'; export default class FoodsView { - constructor(foods) { + constructor(foods, color) { this.foods = foods; this.letters = false; this.foodView = new FoodView(); + this.color = color; } render(canvas) { - this.foods.foods.forEach(food => this.foodView.render(canvas, food)); + if (this.color) { + this.foods.foods.forEach(food => this.foodView.render(canvas, food, this.color)); + } else { + this.foods.foods.forEach(food => this.foodView.render(canvas, food)); + } } } diff --git a/src/applications/snake/game_app.js b/src/applications/snake/game_app.js index a02d4cd9..7bc3ba29 100644 --- a/src/applications/snake/game_app.js +++ b/src/applications/snake/game_app.js @@ -21,15 +21,15 @@ import ErrorMessage from './error_message/errorMessage'; // import './styles/style.pcss'; import './style.pcss'; -// import './styles/font.pcss'; -// import './static/fonts/PressStart2P.ttf'; +import './styles/font.pcss'; +import '../../../static/fonts/PressStart2P.ttf'; export default class GameApp extends BaseApp { constructor(appUrl, parent) { const env = new GameEnv(parent); const Views = { game: SnakeGameView, - singlplayer: SinglplayerView, + singleplayer: SinglplayerView, multiplayer: MultiplayerMenu, hotkeys: HotKeys, }; @@ -49,6 +49,7 @@ export default class GameApp extends BaseApp { } start() { + // window.onscroll = function () { window.scrollTo(0, 0); }; this.parent.style.background = 'black'; this.styleChanger.start(); this.env.show(); @@ -59,18 +60,21 @@ export default class GameApp extends BaseApp { pause() { this.styleChanger.stop(); + this.keyboardController.stop(); this.env.hide(); super.pause(); } resume() { this.parent.style.background = 'black'; + this.keyboardController.start(); this.styleChanger.start(); this.env.show(); super.resume(); } stop() { + this.keyboardController.stop(); this.styleChanger.stop(); this.webSocket.close(); super.stop(); diff --git a/src/applications/snake/game_menu/base_menu/base_menu.js b/src/applications/snake/game_menu/base_menu/base_menu.js index 4bf714b3..78075c85 100644 --- a/src/applications/snake/game_menu/base_menu/base_menu.js +++ b/src/applications/snake/game_menu/base_menu/base_menu.js @@ -1,7 +1,9 @@ import busController from '../../modules/busController'; import Element from '../../../element'; +import ErrorMessage from '../../error_message/errorMessage'; import config from '../../modules/view_config'; +import globalUser from '../../globalUser'; import './base_menu.pcss'; @@ -11,6 +13,8 @@ export default class BaseMenu extends Element { this.firstFocus = undefined; this.menuClass = menuClass; this.busController = busController; + this.errorMessage = new ErrorMessage(); + this.onClick = this.onClick.bind(this); if (isHorizontal) { this.eventsMethods = { @@ -37,12 +41,24 @@ export default class BaseMenu extends Element { } else { this.menu = this.wrapper; } + if (this.menu) { + this.menu.addEventListener('click', this.onClick); + } super.show(); this.setFirstFosus(); this.setBusListeners(); } + onClick() { + if (window.innerWidth > 768) { + this.errorMessage.setErrorMessage('Use keyboard arrows'); + } + } + hide() { + if (this.menu) { + this.menu.removeEventListener('click', this.onClick); + } this.removeBusListeners(); super.hide(); } @@ -62,15 +78,35 @@ export default class BaseMenu extends Element { processLine() { this.stop(); this.hide(); - let href = this.getFocus()[0].getAttribute('src'); + const focus = this.getFocus()[0]; + let href = focus.getAttribute('src'); if (!href) { - href = this.getFocus()[0].getAttribute('href'); + href = focus.getAttribute('href'); + } + + console.log('focus.innerHTML', focus.innerHTML); + if (focus.innerHTML === 'Multiplayer') { + if (globalUser) { + const isloginUser = globalUser.isLoginUser(); + console.log('islogin', isloginUser); + if (isloginUser) { + console.log('redirect to /multiplayer'); + this.busController.emit('link', '/multiplayer'); + } else { + const error = focus.getAttribute('error'); + if (error) { + console.log('main_menu', error); + this.errorMessage.setErrorMessage(error); + } + } + } } + if (href) { - const params = this.getFocus()[0].getAttribute('params'); + const params = focus.getAttribute('params'); this.busController.emit('link', href, params); } else { - const event = this.getFocus()[0].getAttribute('event'); + const event = focus.getAttribute('event'); this.busController.emit(event); } } @@ -119,7 +155,6 @@ export default class BaseMenu extends Element { element.classList.add(config.snakemenuButtonFocus); } - goBack(link) { busController.emit('link', link); } diff --git a/src/applications/snake/game_menu/base_menu/base_menu.pcss b/src/applications/snake/game_menu/base_menu/base_menu.pcss index 4f8fdbf7..2b735529 100644 --- a/src/applications/snake/game_menu/base_menu/base_menu.pcss +++ b/src/applications/snake/game_menu/base_menu/base_menu.pcss @@ -45,22 +45,28 @@ } } -.snakemenu__purple-border { +.snakemenu__greenred-border { border-style: solid; border-width: 2px; - border-color: #3F0070; + border-color: #ff0a27; } .snakemenu__yellowgreen-border { border-style: solid; border-width: 2px; - border-color: yellowgreen; + border-color: #4d00ff; } .snakemenu__pink-border { border-style: solid; border-width: 2px; - border-color: pink; + border-color: #7fffbf; +} + +.snakemenu__cyan-border { + border-style: solid; + border-width: 2px; + border-color: #00ffff; } .snakemenu-button { @@ -72,19 +78,20 @@ align-self: center; cursor: pointer; margin: 0 20px; + pointer-events: none; } .snakemenu-button_focus::before { content: "> "; } -.snakemenu-button__focus-purple { - color: rgb(138, 4, 165); +.snakemenu-button__focus-greenred { + color: #a1ff0a; font-size: 18px; } .snakemenu-button__focus-pink { - color: red; + color: #ff7fff; font-size: 18px; } @@ -93,14 +100,24 @@ font-size: 18px; } +.snakemenu-button__focus-cyan { + color: #ff0080; + font-size: 18px; +} + @media (max-width: 768px) { - .snakemenu-button__focus-purple, + .snakemenu-button__focus-greenred, .snakemenu-button__focus-pink, - .snakemenu-button__focus-greenyellow { + .snakemenu-button__focus-greenyellow, + .snakemenu-button__focus-cyan { color: white; font-size: 14px; } + .snakemenu-button { + pointer-events: auto; + } + .snakemenu-button:hover { color: greenyellow; font-size: 18px; @@ -122,8 +139,8 @@ } } -.gamename_purple { - color: rgb(138, 4, 165); +.gamename_greenred { + color: #a1ff0a; } .gamename_greenyellow { @@ -131,5 +148,9 @@ } .gamename_pink { - color: red; + color: #FF80FF; +} + +.gamename_cyan { + color: cyan; } diff --git a/src/applications/snake/game_menu/hotkeys/hotkeys.js b/src/applications/snake/game_menu/hotkeys/hotkeys.js index f6f8bdee..a57c7c0f 100644 --- a/src/applications/snake/game_menu/hotkeys/hotkeys.js +++ b/src/applications/snake/game_menu/hotkeys/hotkeys.js @@ -18,6 +18,10 @@ const hotkeys = [ key: 'Q', info: 'Quit the game', }, + { + key: 'S', + info: 'Play/pause music', + }, { key: 'Backspase', info: 'Go back', diff --git a/src/applications/snake/game_menu/hotkeys/hotkeys.pug b/src/applications/snake/game_menu/hotkeys/hotkeys.pug index 1d333fe7..4d4a6c2a 100644 --- a/src/applications/snake/game_menu/hotkeys/hotkeys.pug +++ b/src/applications/snake/game_menu/hotkeys/hotkeys.pug @@ -1,9 +1,9 @@ .main-content - .gamename.gamename_purple ОНАCONDA - .hotkeys-block.snakemenu__purple-border + .gamename.gamename_greenred ОНАCONDA + .hotkeys-block.snakemenu__greenred-border - hotkeys.forEach((hotkey) => { .hotky-combination - span.hotkey-key.gamename_purple=hotkey.key + span.hotkey-key.gamename_greenred=hotkey.key span.hotkey-info=hotkey.info - }); .hotkeysmenu diff --git a/src/applications/snake/game_menu/main_menu/main_menu.pcss b/src/applications/snake/game_menu/main_menu/main_menu.pcss index 2fd7b235..d71207d3 100644 --- a/src/applications/snake/game_menu/main_menu/main_menu.pcss +++ b/src/applications/snake/game_menu/main_menu/main_menu.pcss @@ -1,3 +1,4 @@ .snakemenu-main { + touch-action: none; grid-template-rows: repeat(4, 1fr); } \ No newline at end of file diff --git a/src/applications/snake/game_menu/main_menu/main_menu.pug b/src/applications/snake/game_menu/main_menu/main_menu.pug index 98fe6af6..b5b33f10 100644 --- a/src/applications/snake/game_menu/main_menu/main_menu.pug +++ b/src/applications/snake/game_menu/main_menu/main_menu.pug @@ -1,5 +1,9 @@ .main-content - .gamename.gamename_purple ОНАCONDA - .snakemenu-main.snakemenu__purple-border - each value, key in buttons - a.snakemenu-button(href=key)=value \ No newline at end of file + .gamename.gamename_greenred ОНАCONDA + .snakemenu-main.snakemenu__greenred-border + each value, key in buttons + - if (key === '/multiplayer') { + a.snakemenu-button.multiplayermenu-button(href=key src=key)=value + - } else { + a.snakemenu-button(href=key src=key)=value + - } \ No newline at end of file diff --git a/src/applications/snake/game_menu/main_menu/main_menu_view.js b/src/applications/snake/game_menu/main_menu/main_menu_view.js index f8a2248b..244ffbd1 100644 --- a/src/applications/snake/game_menu/main_menu/main_menu_view.js +++ b/src/applications/snake/game_menu/main_menu/main_menu_view.js @@ -3,11 +3,15 @@ import BaseMenu from '../base_menu/base_menu'; import MainMenuTemplate from './main_menu.pug'; import './main_menu.pcss'; +import ErrorMesage from '../../error_message/errorMessage'; +import globalUser from '../../globalUser'; +import busController from '../../modules/busController'; + const buttons = { - '/singlplayer': 'Singlplayer', + '/singleplayer': 'Singleplayer', '/multiplayer': 'Multiplayer', '/hotkeys': 'Hot keys', - '/': 'exit', + '/terminal': 'exit', }; export default class MainMenuView extends BaseMenu { @@ -16,10 +20,53 @@ export default class MainMenuView extends BaseMenu { this.render(); this.goBack = this.goBack.bind(this); this.noRender = true; + this.errorMessage = new ErrorMesage(); + this.unauthorizedMessage = this.unauthorizedMessage.bind(this); } goBack() { - super.goBack('/'); + super.goBack('/terminal'); + } + + show() { + super.show(); + this.setEnvironment(); + } + + async setEnvironment() { + console.log('checkglobaluser setEnvironment'); + if (globalUser) { + const isloginUser = globalUser.isLoginUser(); + console.log('islogin', isloginUser); + if (!isloginUser) { + console.log('nologin'); + [this.multiplayerButton] = document.getElementsByClassName('multiplayermenu-button'); + if (this.multiplayerButton) { + this.multiplayerButton.setAttribute('href', '/snake'); + this.multiplayerButton.setAttribute('src', '/snake'); + this.multiplayerButton.setAttribute('error', 'Register to play in multiplayer'); + this.multiplayerButton.addEventListener('click', this.unauthorizedMessage); + this.multiplayerButton.removeEventListener('touchstart', this.unauthorizedMessage, { passive: false }); + } + } else if (this.multiplayerButton) { + this.multiplayerButton.setAttribute('href', '/multiplayer'); + this.multiplayerButton.setAttribute('src', '/multiplayer'); + } + } + } + + unauthorizedMessage(e) { + e.stopPropagation(); + e.preventDefault(); + + const isloginUser = globalUser.isLoginUser(); + if (isloginUser) { + this.busController.emit('link', '/multiplayer'); + } else { + this.errorMessage.setErrorMessage('Register to play in multiplayer'); + } + this.multiplayerButton.removeEventListener('click', this.unauthorizedMessage); + this.multiplayerButton.removeEventListener('touchstart', this.unauthorizedMessage, { passive: false }); } pause() { @@ -29,4 +76,12 @@ export default class MainMenuView extends BaseMenu { render() { super.render({ buttons }); } + + hide() { + if (this.multiplayerButton) { + this.multiplayerButton.removeEventListener('click', this.unauthorizedMessage); + this.multiplayerButton.removeEventListener('touchstart', this.unauthorizedMessage); + } + super.hide(); + } } diff --git a/src/applications/snake/game_menu/multiplayer/multiplayer.pug b/src/applications/snake/game_menu/multiplayer/multiplayer.pug index 30f20b68..7ddbe7e1 100644 --- a/src/applications/snake/game_menu/multiplayer/multiplayer.pug +++ b/src/applications/snake/game_menu/multiplayer/multiplayer.pug @@ -1,6 +1,6 @@ .main-content - .gamename.gamename_purple ОНАCONDA - .snakemenu-multiplayer.snakemenu__purple-border + .gamename.gamename_greenred ОНАCONDA + .snakemenu-multiplayer.snakemenu__greenred-border each data, text in buttons - href = `${data.href}?${data.params}`; a.snakemenu-button(href=href,src=data.href, params=data.params)=text \ No newline at end of file diff --git a/src/applications/snake/game_menu/multiplayer/multiplayer_menu.js b/src/applications/snake/game_menu/multiplayer/multiplayer_menu.js index 85fad87c..d2476480 100644 --- a/src/applications/snake/game_menu/multiplayer/multiplayer_menu.js +++ b/src/applications/snake/game_menu/multiplayer/multiplayer_menu.js @@ -14,7 +14,7 @@ const buttons = { }, '4 PLAYERS': { href: '/game', - params: `mode=${GAME_MODE.MULTIPLAYER}&type=${GAME_MODE.FOUR_PLAYERS}` + params: `mode=${GAME_MODE.MULTIPLAYER}&type=${GAME_MODE.FOUR_PLAYERS}`, }, BACK: { href: '/snake', @@ -23,7 +23,7 @@ const buttons = { export default class MultiplayerMenu extends BaseMenu { constructor(parent) { - // super(MultiplayerTemplate, parent, ['snakemenu__purple-border', 'snakemenu-multiplayer']); + // super(MultiplayerTemplate, parent, ['snakemenu__greenred-border', 'snakemenu-multiplayer']); super(MultiplayerTemplate, parent, ['snakepage-multiplayer'], false, 'snakemenu-multiplayer'); this.render(); } diff --git a/src/applications/snake/game_menu/singlplayer/singlplayer.pug b/src/applications/snake/game_menu/singlplayer/singlplayer.pug index 547b4bd6..1fc81b5e 100644 --- a/src/applications/snake/game_menu/singlplayer/singlplayer.pug +++ b/src/applications/snake/game_menu/singlplayer/singlplayer.pug @@ -1,5 +1,6 @@ .main-content - .gamename.gamename_purple ОНАCONDA - .snakemenu-singlplayer.snakemenu__purple-border + .gamename.gamename_greenred ОНАCONDA + .snakemenu-singlplayer.snakemenu__greenred-border each data, text in buttons - a.snakemenu-button(href=data.href, params=data.params)=text \ No newline at end of file + - href = `${data.href}?${data.params}`; + a.snakemenu-button(href=href, src=data.href params=data.params)=text \ No newline at end of file diff --git a/src/applications/snake/globalUser.js b/src/applications/snake/globalUser.js index 63b420a8..ee643142 100644 --- a/src/applications/snake/globalUser.js +++ b/src/applications/snake/globalUser.js @@ -1,3 +1,5 @@ +import userService from '../../modules/userservice'; + class GlobalUser { setUserToken(userToken) { this.userToken = userToken; @@ -6,6 +8,15 @@ class GlobalUser { setState(data) { this.setUserToken(data); } + + isLoginUser() { + const { err, loggedIn } = userService.isLoggedIn(); + console.log(err, loggedIn, this.userToken); + if (err || !loggedIn) { + return false; + } + return true; + } } export default new GlobalUser(); diff --git a/src/applications/snake/modules/busController.js b/src/applications/snake/modules/busController.js index 7c8f752e..94986cd8 100644 --- a/src/applications/snake/modules/busController.js +++ b/src/applications/snake/modules/busController.js @@ -20,7 +20,6 @@ class BusController { } emit(event, ...data) { - console.log('emit', event); bus.emit(event, ...data); } } diff --git a/src/applications/snake/modules/keyboardController.js b/src/applications/snake/modules/keyboardController.js index 9a48bac9..24865007 100644 --- a/src/applications/snake/modules/keyboardController.js +++ b/src/applications/snake/modules/keyboardController.js @@ -1,8 +1,16 @@ import bus from './busController'; import swipeDetector from './swipeDetector'; +import config from '../game/utils/game_config'; +let enter = 0; class KeyboardController { constructor() { + if (window.innerWidth > window.innerHeight) { + this.orientation = config.HORIZONTAL; + } else { + this.orientation = config.VERTICAL; + } + this.controls = [ 8, // backspace // 9, // tab @@ -10,6 +18,7 @@ class KeyboardController { // 27, // esc // 16, // shift // 32, // space + 83, // s turn off the music 81, // q exit 84, // t key (change theme) ]; @@ -28,18 +37,35 @@ class KeyboardController { ]; + this.verticalDirection = { + ArrowUp: 'ArrowLeft', + ArrowDown: 'ArrowRight', + ArrowLeft: 'ArrowUp', + ArrowRight: 'ArrowDown', + }; + this.swipeDetector = swipeDetector; this.isSpace = false; + this.acceptInput = this.acceptInput.bind(this); } start() { // magic here - setTimeout(() => document.addEventListener('keydown', this.acceptInput.bind(this), 1)); + // console.log('start keyboard'); + // setTimeout(() => document.addEventListener('keydown', this.acceptInput), 1000); + enter = 0; + setTimeout(() => { enter += 1; }, 1000); + document.addEventListener('keydown', this.acceptInput); this.swipeDetector.start(); } + setOrintation(orientation) { + this.orientation = orientation; + this.lastCommand = undefined; + } + stop() { - document.removeEventListener('keydown', this.acceptInput.bind(this)); + document.removeEventListener('keydown', this.acceptInput); this.swipeDetector.stop(); } @@ -91,7 +117,7 @@ class KeyboardController { */ acceptInput(e) { - const keyCode = e.which || e.keyCode; + let keyCode = e.which || e.keyCode; /* if (e.ctrlKey) { @@ -111,12 +137,20 @@ class KeyboardController { } */ + if (keyCode === 13 && enter === 0) { + enter += 1; + keyCode = -1; + } + if (this.isControlKey(keyCode)) { - // console.log('emit', e.code); bus.emit(e.code); } else if (this.isSnakeControls(keyCode)) { + let direction = e.key; + if (this.orientation === config.VERTICAL) { + direction = this.verticalDirection[direction]; + } bus.emit(e.code); - this.lastCommand = e.key; + this.lastCommand = direction; } if (e.code === 'Space') { diff --git a/src/applications/snake/modules/style_changer.js b/src/applications/snake/modules/style_changer.js index 2d5a2c47..2fb0cd0f 100644 --- a/src/applications/snake/modules/style_changer.js +++ b/src/applications/snake/modules/style_changer.js @@ -1,72 +1,79 @@ import busController from './busController'; import viewConfig from './view_config'; -const defaultStyle = { - snakemenu_main_border: 'snakemenu__purple-border', - mainScoreColor: 'main-score__purple', - snakemenuButtonFocus: 'snakemenu-button__focus-purple', - gameBoardBorder: 'game-board__purple', - gameCanvasBorder: 'game-canvas__purple', - gameName: 'gamename_purple', - first_player: 'player_greenyellow', - second_player: 'player_yellow', - third_player: 'player_green', - fourth_player: 'player_blue', +const greenredStyle = { + classes: { + snakemenu_main_border: 'snakemenu__greenred-border', + mainScoreCollor: 'main-score__greenred', + snakemenuButtonFocus: 'snakemenu-button__focus-greenred', + gameBoardBorder: 'game-board__greenred', + gameCanvasBorder: 'game-board__greenred', + gameName: 'gamename_greenred', + }, config_colors: { - wallColor: '#FF40FF', - snakeColor: '#00FFFF', - foodColor: '#40FF00', - snakemenuButtonFocus: 'snakemenu-button__focus-purple', - enemiesColors: ['greenyellow', 'yellow', 'red', 'blue'], + wallColor: '#ff0a27', + snakeColor: '#0a27ff', + foodColor: '#a1ff0a', }, }; + const yellowgreenStyle = { - snakemenu_main_border: 'snakemenu__yellowgreen-border', - mainScoreColor: 'main-score__greenyellow', - snakemenuButtonFocus: 'snakemenu-button__focus-greenyellow', - gameBoardBorder: 'game-board__greenyellow', - gameCanvasBorder: 'game-canvas__greenyellow', - gameName: 'gamename_greenyellow', - first_player: 'player_white', - second_player: 'player_yellow', - third_player: 'player_red', - fourth_player: 'player_green', + classes: { + snakemenu_main_border: 'snakemenu__yellowgreen-border', + mainScoreCollor: 'main-score__greenyellow', + snakemenuButtonFocus: 'snakemenu-button__focus-greenyellow', + gameBoardBorder: 'game-board__greenyellow', + gameCanvasBorder: 'game-board__greenyellow', + gameName: 'gamename_greenyellow', + }, + config_colors: { - wallColor: '#FF0000', + wallColor: '#ff00b3', snakeColor: '#00FF00', - foodColor: '#C0FF00', - snakemenuButtonFocus: 'snakemenu-button__focus-greenyellow', - enemiesColors: ['white', 'yellow', 'red', 'green'], + foodColor: '#4d00ff', }, }; const pinkStyle = { - snakemenu_main_border: 'snakemenu__pink-border', - mainScoreColor: 'main-score__pink', - snakemenuButtonFocus: 'snakemenu-button__focus-pink', - gameBoardBorder: 'game-board__pink', - gameCanvasBorder: 'game-canvas__pink', - gameName: 'gamename_pink', - first_player: 'player_greenyellow', - second_player: 'player_pink', - third_player: 'player_red', - fourth_player: 'player_blue', - config_colors: { - wallColor: '#000761', - snakeColor: '#00FFFF', - foodColor: '#FE00DD', + classes: { + snakemenu_main_border: 'snakemenu__pink-border', + mainScoreCollor: 'main-score__pink', snakemenuButtonFocus: 'snakemenu-button__focus-pink', - enemiesColors: ['greenyellow', 'pink', 'red', 'blue'], + gameBoardBorder: 'game-board__pink', + gameCanvasBorder: 'game-board__pink', + gameName: 'gamename_pink', + }, + config_colors: { + wallColor: '#7fffbf', + snakeColor: '#7fbfff', + foodColor: '#ffff7f', + }, +}; + +const cyanStyle = { + classes: { + snakemenu_main_border: 'snakemenu__cyan-border', + mainScoreCollor: 'main-score__cyan', + snakemenuButtonFocus: 'snakemenu-button__focus-cyan', + gameBoardBorder: 'game-board__cyan', + gameCanvasBorder: 'game-board__cyan', + gameName: 'gamename_cyan', + }, + config_colors: { + wallColor: '#ff0080', + snakeColor: '#80ff00', + foodColor: '#ff8000', }, }; class StyleChanger { constructor() { - this.styles = [defaultStyle, yellowgreenStyle, pinkStyle]; - this.currentStyle = defaultStyle; + this.styles = [greenredStyle, yellowgreenStyle, pinkStyle, cyanStyle]; + this.currentStyle = greenredStyle; this.currentStyleIndex = 0; this.changeStyle = this.changeStyle.bind(this); + viewConfig.setColors(this.styles[0].config_colors, this.styles[0].classes); } start() { @@ -79,14 +86,14 @@ class StyleChanger { changeStyle() { const [nextStyle, nextStyleIndex] = this.nextStyle(); - Object.keys(this.currentStyle).forEach((styleKey) => { - const styledElements = document.getElementsByClassName(this.currentStyle[styleKey]); + Object.keys(this.currentStyle.classes).forEach((styleKey) => { + const styledElements = document.getElementsByClassName(this.currentStyle.classes[styleKey]); for (let i = styledElements.length - 1; i >= 0; i -= 1) { - styledElements[i].classList.add(nextStyle[styleKey]); - styledElements[i].classList.remove(this.currentStyle[styleKey]); + styledElements[i].classList.add(nextStyle.classes[styleKey]); + styledElements[i].classList.remove(this.currentStyle.classes[styleKey]); } }); - viewConfig.setColors(nextStyle.config_colors); + viewConfig.setColors(nextStyle.config_colors, nextStyle.classes); this.currentStyle = nextStyle; this.currentStyleIndex = nextStyleIndex; } diff --git a/src/applications/snake/modules/swipeDetector.js b/src/applications/snake/modules/swipeDetector.js index 30860b79..ebabc460 100644 --- a/src/applications/snake/modules/swipeDetector.js +++ b/src/applications/snake/modules/swipeDetector.js @@ -67,7 +67,12 @@ class SwipeDetector { if ((e.target instanceof HTMLAnchorElement) && (e.target.getAttribute('type') !== 'submit')) { e.preventDefault(); - bus.emit('link', e.target.pathname, e.target.search); + if (e.target.pathname) { + bus.emit('link', e.target.pathname, e.target.search); + } else { + const event = e.target.getAttribute('event'); + bus.emit(event); + } } if (this.dtime <= this.allowedTime) { diff --git a/src/applications/snake/modules/view_config.js b/src/applications/snake/modules/view_config.js index c243e4b9..3033bded 100644 --- a/src/applications/snake/modules/view_config.js +++ b/src/applications/snake/modules/view_config.js @@ -1,32 +1,43 @@ class Config { constructor() { - this.setDefaultColors(); + // this.setDefaultColors(); } - setColors(colorsConfig) { + setColors(colorsConfig, classConfig) { const { wallColor, snakeColor, foodColor, - snakemenuButtonFocus, - enemiesColors, } = colorsConfig; + const { + gameCanvasBorder, + snakemenuButtonFocus, + mainScoreCollor, + gameBoardBorder, + } = classConfig; + this.wallColor = wallColor; this.snakeColor = snakeColor; this.foodColor = foodColor; + this.enemiesColors = ['greenyellow', 'yellow', 'green', 'blue']; + + this.gameCanvasBorder = gameCanvasBorder; this.snakemenuButtonFocus = snakemenuButtonFocus; - this.enemiesColors = enemiesColors; + this.mainScoreCollor = mainScoreCollor; + this.gameBoardBorder = gameBoardBorder; } - setDefaultColors() { - // розовые стены голубая змея зеленая еда - this.wallColor = 'blue'; - this.snakeColor = '#00FFFF'; - this.foodColor = 'white'; - this.snakemenuButtonFocus = 'snakemenu-button__focus-purple'; - this.enemiesColors = ['greenyellow', 'yellow', 'red', 'blue']; - } + // setDefaultColors() { + // // розовые стены голубая змея зеленая еда + // this.wallColor = 'blue'; + // this.snakeColor = '#00FFFF'; + // this.foodColor = 'white'; + // this.snakemenuButtonFocus = 'snakemenu-button__focus-greenred'; + // this.enemiesColors = ['greenyellow', 'yellow', 'red', 'blue']; + // this.gameCanvasBorder = 'game-board__greenred'; + // this.snakemenuButtonFocus = 'snakemenu-button__focus-greenred'; + // } } export default new Config(); diff --git a/src/applications/snake/modules/wsMessageParser.js b/src/applications/snake/modules/wsMessageParser.js index c8dfbe31..8631396b 100644 --- a/src/applications/snake/modules/wsMessageParser.js +++ b/src/applications/snake/modules/wsMessageParser.js @@ -1,5 +1,4 @@ import busController from './busController'; -import config from './wsConfig'; let instance; @@ -17,25 +16,30 @@ export default class WsMessageParser { * @param {message from ws for parsing} message */ parse(message) { - console.log('ws_message', message); + // console.log('ws_message', message); if (message.status === 'STATUS_OK' || message.status === 'STATUS_DEAD' || message.status === 'quick_search_status' + || message.status === 'quick_search_accept_status' || message.status === 'quick_search_ready' - || message.status === 'quick_search_done') { + || message.status === 'quick_search_done' + || message.status === 'quick_search_added' + || message.status === 'quick_search_removed' + || message.status === 'quick_search_kick' + || message.status === 'quick_search_failed' + || message.status === 'win') { busController.emit(message.status, message); - } - if (typeof message === 'string') { - busController.emit('data', message); - // } else if (message.status === 'STATUS_TICK') { - } else { + } else if (message.status === 'STATUS_TICK' + || message.status === 'STATUS_TOKEN') { busController.emit('STATUS_TICK', message.payload); - const a = Object.keys(message.payload); - Object.keys(message.payload).forEach((key) => { if (Object.keys(this.models).indexOf(key) !== -1) { - this.models[key].forEach(model => model.setState(message.payload[key]), key); + this.models[key].forEach((model) => { + if (model && typeof model.setState === 'function') { + model.setState(message.payload[key]); + } + }, key); } }); } @@ -47,4 +51,8 @@ export default class WsMessageParser { } this.models[route].push(model); } + + emit(action) { + busController.emit(action); + } } diff --git a/src/applications/snake/modules/wsPostman.js b/src/applications/snake/modules/wsPostman.js index 68002fcb..9b7db7b6 100644 --- a/src/applications/snake/modules/wsPostman.js +++ b/src/applications/snake/modules/wsPostman.js @@ -1,4 +1,4 @@ -import config from './wsConfig'; +import { getProfile } from '../../../modules/network'; let instance; @@ -11,6 +11,19 @@ export default class WsPostman { return instance; } + async sendLogin() { + const result = await getProfile(); + console.log('result profile', result, result.profile.username); + this.ws.send(result.profile.username); + } + + isReady() { + if (this.ws.getReadyState() === 1) { + return true; + } + return false; + } + setRoomToken(roomToken) { this.roomToken = roomToken; } diff --git a/src/applications/snake/static/audio/dead_main.wav b/src/applications/snake/static/audio/dead_main.wav new file mode 100644 index 00000000..fb68fd08 Binary files /dev/null and b/src/applications/snake/static/audio/dead_main.wav differ diff --git a/src/applications/snake/static/audio/dead_ux.wav b/src/applications/snake/static/audio/dead_ux.wav new file mode 100644 index 00000000..fd5890f4 Binary files /dev/null and b/src/applications/snake/static/audio/dead_ux.wav differ diff --git a/src/applications/snake/static/audio/death.mp3 b/src/applications/snake/static/audio/death.mp3 new file mode 100644 index 00000000..e1d7a2f1 Binary files /dev/null and b/src/applications/snake/static/audio/death.mp3 differ diff --git a/src/applications/snake/static/audio/game_of_thrones.mp3 b/src/applications/snake/static/audio/game_of_thrones.mp3 new file mode 100644 index 00000000..c95afe8e Binary files /dev/null and b/src/applications/snake/static/audio/game_of_thrones.mp3 differ diff --git a/src/applications/snake/static/audio/pickup.mp3 b/src/applications/snake/static/audio/pickup.mp3 new file mode 100644 index 00000000..15109e04 Binary files /dev/null and b/src/applications/snake/static/audio/pickup.mp3 differ diff --git a/src/applications/snake/static/audio/radioactive.mp3 b/src/applications/snake/static/audio/radioactive.mp3 new file mode 100644 index 00000000..63383491 Binary files /dev/null and b/src/applications/snake/static/audio/radioactive.mp3 differ diff --git a/src/applications/snake/static/audio/star_wars.mp3 b/src/applications/snake/static/audio/star_wars.mp3 new file mode 100644 index 00000000..fde6ee28 Binary files /dev/null and b/src/applications/snake/static/audio/star_wars.mp3 differ diff --git a/src/applications/snake/static/audio/turn_down_for_what.mp3 b/src/applications/snake/static/audio/turn_down_for_what.mp3 new file mode 100644 index 00000000..51aa62ab Binary files /dev/null and b/src/applications/snake/static/audio/turn_down_for_what.mp3 differ diff --git a/src/applications/snake/static/images/arrow.png b/src/applications/snake/static/images/arrow.png new file mode 100644 index 00000000..aea63511 Binary files /dev/null and b/src/applications/snake/static/images/arrow.png differ diff --git a/src/applications/snake/style.pcss b/src/applications/snake/style.pcss index 7b8fa20b..ace95339 100644 --- a/src/applications/snake/style.pcss +++ b/src/applications/snake/style.pcss @@ -2,8 +2,8 @@ .game-container { height: 100%; - /* cursor: none; */ display: grid; + cursor: default; justify-items: center; align-items: center; color: white; @@ -25,9 +25,10 @@ window { display: grid; grid-template-columns: repeat(3, 1fr); - grid-template-rows: 1fr 4fr; + grid-template-rows: 1fr 1fr 9fr; grid-template-areas: - ". mode score" + "mode mode mode" + "home score ." "game game game"; /* position: relative; */ @@ -39,11 +40,15 @@ window { grid-template-columns: 1fr 1fr 3fr 1fr 1fr; grid-template-areas: "mode mode mode mode mode" - ". home score . ." + "home . score . ." "game game game game game"; /* position: relative; */ } + + .home { + display: grid; + } } .snakegame-container_hidden { @@ -70,6 +75,10 @@ window { grid-area: score; } +.main-score_miltiplayer { + display:none; +} + .canvas-wrapper { width: 100%; height: 100%; @@ -93,7 +102,7 @@ window { .snakegame-container__multiplayer { grid-template-areas: - ". mode mode mode score" + "mode mode mode mode mode" "game game game game board" "game game game game board"; } @@ -111,16 +120,16 @@ window { margin-right: 10px } -.game-board__purple { +.game-board__greenred { border-style: solid; border-width: 2px; - border-color: #3F0070; + border-color: #0a27ff; } .game-board__greenyellow { border-style: solid; border-width: 2px; - border-color: greenyellow; + border-color: #4d00ff; } .game-board__pink { @@ -129,6 +138,12 @@ window { border-color: pink; } +.game-board__cyan { + border-style: solid; + border-width: 2px; + border-color: cyan; +} + .game-board-name { text-align: center; margin: 10px; @@ -137,13 +152,12 @@ window { @media screen and (max-width: 768px) { .snakegame-container__multiplayer { - grid-template-columns: auto; + grid-template-columns: 1fr 6fr; grid-template-rows: 2fr 1fr 10fr; grid-template-areas: - "mode" - /* "score" */ - "board" - "game"; + "mode mode" + "home board" + "game game"; } .game-board-name { @@ -160,4 +174,21 @@ window { .game-board__hidden-border { border-width: 0px; } + + .gamehotkeys { + display: none; + } +} + +.gamehotkeys { + font-size: 14px; + position: absolute; + right: 15px; + top: 15px; + font-family: 'Press Start 2P', cursive; +} + +.gamehotkey { + flex-direction: row; + display: flex; } diff --git a/src/applications/terminal/styles/terminal.pcss b/src/applications/terminal/styles/terminal.pcss index 637576a6..af764744 100644 --- a/src/applications/terminal/styles/terminal.pcss +++ b/src/applications/terminal/styles/terminal.pcss @@ -1,3 +1,5 @@ +@import '../../../config.pcss'; + /* CRUTCHES */ ::-webkit-scrollbar { @@ -5,7 +7,14 @@ } /* END OF CRUTCHES */ +.terminal-wrapper { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + background-color: black; +} .terminal { background: black; @@ -27,7 +36,7 @@ @media (--minimal) { .terminal { - width: 95vw; + width: 93vw; } } diff --git a/src/applications/terminal/templates/terminal.pug b/src/applications/terminal/templates/terminal.pug index ed64fe7f..127264c1 100644 --- a/src/applications/terminal/templates/terminal.pug +++ b/src/applications/terminal/templates/terminal.pug @@ -1 +1,2 @@ -.terminal +.terminal-wrapper + .terminal diff --git a/src/applications/terminal/terminal_app.js b/src/applications/terminal/terminal_app.js index a3b24dcf..3ec1caf3 100644 --- a/src/applications/terminal/terminal_app.js +++ b/src/applications/terminal/terminal_app.js @@ -2,27 +2,78 @@ import TerminalView from './terminal_view'; import BaseApp from '../base_app'; import messages from './messages'; import bus from '../../modules/bus'; +import { register, logout, login } from '../../modules/network'; +import userService from '../../modules/userservice'; class TerminalApp extends BaseApp { constructor(url, parent) { super(url, parent, TerminalView); - this.intro = 'stanford@wave:~/$'; + this.username = null; this.listeners = { keydown: this.handleKeypress.bind(this), click: this.focusInput.bind(this), + keyup: this.handleKeyup.bind(this), }; this.commands = { - help: this.help, + // me: this.me, + register: this.register, + login: this.login, + snake: () => { + bus.emit('link', '/snake'); + this.view.addInput(this.intro); + }, history: this.history, clear: this.clear, - snake: () => bus.emit('link', '/snake'), - exit: () => bus.emit('link', '/'), + logout: this.logout, + help: this.help, + exit: async () => { + const { err, loggedIn } = await userService.isLoggedIn(); + console.log(err, loggedIn); + + if (err || !loggedIn) { + const frases = [ + ' Not now, dear.', + ' Why are you so serious?', + ' I have headache.', + ' Don\'t leave me alone...', + ' Nope :3', + ' You don\'t like me?', + ]; + this.view.printString(frases[Math.floor(Math.random() * frases.length)]); + this.view.addInput(this.intro); + } else { + bus.emit('link', '/'); + this.view.addInput(this.intro); + } + }, }; + this.register = this.register.bind(this); + this.login = this.login.bind(this); + this.setUsername = this.setUsername.bind(this); + this.setUsername(); this.commandHistory = []; + this.currentCommandIndex = -1; + bus.listen('userUpdated', this.setUsername); + } + + setUsername() { + bus.ignore('userUpdated', this.setUsername); + const { err, loggedIn } = userService.isLoggedIn(); + if (err) bus.listen('userUpdated', this.setUsername); + else if (!loggedIn) { + this.username = 'guest'; + } else { + const { user } = userService.getUser(); + this.username = user.username; + } + } + + get intro() { + return `${this.username}@wave:~/&`; } get view() { @@ -39,11 +90,21 @@ class TerminalApp extends BaseApp { /* service methods */ start() { + this.setUsername(); this.parent.style.background = 'black'; super.start(); this.addListeners(); this.view.printBlock(messages.hello); - this.view.addInput(this.intro); + + if (this.username !== null) { + this.view.addInput(this.intro); + } else { + const callback = () => { + bus.ignore('userUpdated', callback); + setTimeout(() => this.view.addInput(this.intro), 100); + }; + bus.listen('userUpdated', callback); + } } stop() { @@ -67,21 +128,164 @@ class TerminalApp extends BaseApp { /* terminal commands */ + register() { + bus.ignore('userUpdated', this.register); + const { err, loggedIn } = userService.isLoggedIn(); + if (err) bus.listen('userUpdated', this.register); + else if (loggedIn) { + this.view.printString(`I already know you, ${this.username}.`); + this.view.addInput(this.intro); + return; + } + + this.terminal.removeEventListener('keydown', this.listeners.keydown); + let name, password, password2; + const processData = async (value) => { + password2 = value; + if (password === password2) { + const formdata = new FormData(); + formdata.append('username', name); + formdata.append('password', password); + const { err: regErr } = await register(formdata); + if (!regErr) { + this.view.printString(`Hello, ${name}!`); + bus.emit('checkUser'); + bus.emit('appInstalled'); + this.username = name; + this.view.addInput(this.intro); + } else { + this.view.printString('This name already in use!'); + this.view.addInput(this.intro); + } + } else { + this.view.printString('Passwords don\'t match.'); + this.view.addInput(this.intro); + } + this.terminal.addEventListener('keydown', this.listeners.keydown); + }; + const repeatPassword = (value) => { + password = value; + if (password.length < 3) { + this.view.printString('Even my mom hacks this short password.'); + this.ask(' password:', repeatPassword, true); + } else if (!password.match(/[\S]{4,}/)) { + this.view.printString('Maybe better without gaps.'); + this.ask(' password:', repeatPassword); + } else { + this.ask(' repeat password:', processData, true); + } + }; + + const askPassword = (value) => { + name = value; + if (name.length < 3) { + this.view.printString('I hope you have something longer in stock.'); + this.ask(' your name:', askPassword); + } else if (!name.match(/[\S]{4,}/)) { + this.view.printString('Maybe better without gaps.'); + this.ask(' your name:', askPassword); + } else { + this.ask(' password:', repeatPassword, true); + } + }; + + this.ask(' your name:', askPassword); + } + + login() { + bus.ignore('userUpdated', this.login); + const { err, loggedIn } = userService.isLoggedIn(); + if (err) bus.listen('userUpdated', this.login); + else if (loggedIn) { + this.view.printString(`I already know you, ${this.username}.`); + this.view.addInput(this.intro); + return; + } + + this.terminal.removeEventListener('keydown', this.listeners.keydown); + let name, password; + const processData = async (value) => { + password = value; + if (password.length < 3) { + this.view.printString(`Looks like you remember only ${password.length} characters.`); + this.ask(' password:', processData, true); + } else if (!password.match(/[\S]{3,}/)) { + this.view.printString('Maybe better without gaps.'); + this.ask(' password:', processData, true); + } else if (password) { + const formdata = new FormData(); + formdata.append('username', name); + formdata.append('password', password); + const { err: regErr } = await login(formdata); + if (!regErr) { + this.view.printString(`Hello, ${name}!`); + bus.emit('checkUser'); + this.username = name; + this.view.addInput(this.intro); + } else { + this.view.printString('Wrong name or password.'); + this.view.addInput(this.intro); + } + this.terminal.addEventListener('keydown', this.listeners.keydown); + } + }; + + const askPassword = (value) => { + name = value; + if (name.length < 3) { + this.view.printString(`Your name seems to be longer than ${name.length} characters.`); + this.ask(' your name:', askPassword); + } else if (!name.match(/[\S]{3,}/)) { + this.view.printString('Maybe better without gaps.'); + this.ask(' your name:', askPassword); + } else { + this.ask(' password:', processData, true); + } + }; + this.ask(' your name:', askPassword); + } + + async logout() { + const { err } = await logout(); + console.log(err); + if (!err) { + this.view.printString('Ok'); + bus.emit('checkUser', 'logout'); + this.username = 'guest'; + this.view.addInput(this.intro); + } else if (err.status === 401) { + this.view.printString('Already.'); + this.view.addInput(this.intro); + } else { + this.view.printString('Internal error, try later.'); + this.view.addInput(this.intro); + } + } + help() { this.view.printString('Available commands:'); Object.keys(this.commands).forEach((key) => { this.view.printString(` * ${key}`); }); + this.view.addInput(this.intro); } history() { this.commandHistory.forEach((command) => { this.view.printString(` * ${command}`); }); + this.view.addInput(this.intro); } clear() { this.view.clear(); + this.view.addInput(this.intro); + } + + break() { + this.view.processInput(); + this.view.printString('Break!'); + this.view.addInput(this.intro); } @@ -92,6 +296,41 @@ class TerminalApp extends BaseApp { ev.preventDefault(); this.handleCommand(); break; + case 38: + ev.preventDefault(); + this.toggleUp(); + break; + case 40: + ev.preventDefault(); + this.toggleDown(); + break; + case 67: + if (this.ctrlDown) { + ev.preventDefault(); + this.break(); + } + break; + case 76: + if (this.ctrlDown) { + ev.preventDefault(); + this.clear(); + } + break; + case 91: + case 17: + this.ctrlDown = true; + break; + default: + break; + } + } + + handleKeyup(ev) { + switch (ev.keyCode) { + case 91: + case 17: + this.ctrlDown = false; + break; default: break; } @@ -100,7 +339,6 @@ class TerminalApp extends BaseApp { /** reads and process command from input */ handleCommand() { const command = this.view.processInput(); - if (command) { if (this.commands.hasOwnProperty(command)) { this.commands[command].call(this); @@ -108,15 +346,29 @@ class TerminalApp extends BaseApp { this.view.printString(); this.view.printString(`${command}: command not found`); this.view.printString(); + this.view.addInput(this.intro); } - this.commandHistory.push(command); + this.commandHistory.unshift(command); + } else { + this.view.addInput(this.intro); } - this.view.addInput(this.intro); } + ask(message, process, hideInput) { + const callback = async (ev) => { + if (ev.which === 13) { + ev.preventDefault(); + await process(this.view.processInput()); + this.terminal.removeEventListener('keydown', callback); + } + }; + + if (hideInput) this.view.addPasswordInput(message); + else this.view.addInput(message); + this.terminal.addEventListener('keydown', callback); + } focusInput() { - console.log('focusing!'); const input = this.view.getInput(); if (input) { input.focus(); @@ -136,6 +388,31 @@ class TerminalApp extends BaseApp { this.terminal.removeEventListener(key, this.listeners[key]); }); } + + toggleCommandHistory(direction) { + let newIndex = this.currentCommandIndex + direction; + + if (newIndex < -1) newIndex = -1; + if (newIndex >= this.commandHistory.length) newIndex = this.commandHistory.length - 1; + + if (newIndex !== this.currentCommandIndex) { + this.currentCommandIndex = newIndex; + } + + if (newIndex > -1) { + this.view.setInput(this.commandHistory[newIndex]); + } else { + this.view.setInput(''); + } + } + + toggleUp() { + this.toggleCommandHistory(1); + } + + toggleDown() { + this.toggleCommandHistory(-1); + } } diff --git a/src/applications/terminal/terminal_view.js b/src/applications/terminal/terminal_view.js index f8f3c424..8262a86c 100644 --- a/src/applications/terminal/terminal_view.js +++ b/src/applications/terminal/terminal_view.js @@ -41,7 +41,6 @@ class TerminalView extends Element { this.terminal.innerHTML += textblockTemplate({ text }); } - /* working with inputs */ getInput() { if (this.input) return this.input; @@ -53,10 +52,18 @@ class TerminalView extends Element { processInput() { if (!this.input) return null; const { value } = this.input; + const attr = this.input.getAttribute('type'); + let newValue = ''; + if (attr === 'password') { + let { length } = value; + while (length--) { + newValue += '•'; + } + } else newValue = value; const line = this.input.parentElement; line.removeChild(this.input); - line.innerHTML += messageTemplate({ string: value }); + line.innerHTML += messageTemplate({ string: newValue }); this.input = null; return value; @@ -69,6 +76,21 @@ class TerminalView extends Element { this.focusInput(); } + setInput(value) { + if (!this.input) { + [this.input] = this.terminal.getElementsByClassName('terminal__input'); + } + this.input.value = value; + } + + addPasswordInput(intro) { + if (!this.rendered) this.render(); + + this.terminal.innerHTML += inputTemplate({ intro }); + this.getInput().setAttribute('type', 'password'); + this.focusInput(); + } + focusInput() { [this.input] = this.terminal.getElementsByClassName('terminal__input'); this.input.focus(); diff --git a/src/modules/ajax.js b/src/modules/ajax.js index a7df7c72..ff8bbeca 100644 --- a/src/modules/ajax.js +++ b/src/modules/ajax.js @@ -1,7 +1,7 @@ import bus from './bus'; -const URL = 'http://api.localhost:3000'; +const URL = 'https://api.rasseki.com'; // errors which are handled by another modules const errorEvents = { diff --git a/src/modules/network.js b/src/modules/network.js index 429fb625..7b6c2cdf 100644 --- a/src/modules/network.js +++ b/src/modules/network.js @@ -1,6 +1,88 @@ import ajax from './ajax'; +/* apps */ + +async function getAllApps() { + try { + const apps = await ajax.GET({ + path: '/apps', + }); + return { apps }; + } catch (err) { + return { err }; + } +} + +async function getMyApps() { + try { + const apps = await ajax.GET({ + path: '/me/apps', + }); + return { apps: apps.apps }; + } catch (err) { + return { err }; + } +} + +async function addApp(name) { + try { + const formdata = new FormData(); + formdata.append('name', name); + return await ajax.POST({ + path: '/me/apps', + body: formdata, + }); + } catch (err) { + return { err }; + } +} + +async function getApp(name) { + try { + const app = await ajax.GET({ + path: `/apps/${name}`, + }); + return { app }; + } catch (err) { + return { err }; + } +} + +// async function addApp(name) { +// try { +// return await ajax.POST({ +// path: '/me/apps', +// body: { name }, +// }); +// } catch (err) { +// return { err }; +// } +// } + +// /apps GET +// 200 OK {apps:[{name,cover,description,installations,price,year},...]} + +// /me/apps POST {name:''} +// 401 UNAUTHORIZED +// 200 OK + +// /me/apps DELETE {name:''} +// 401 UNAUTHORIZED +// 200 OK + +// /apps/popular GET +// 200 OK {apps:[{name,cover,description,installations,price,year},...]} + +// /apps/{name} GET +// 200 OK {name,cover,description,installations,price,year} +// 404 NOT FOUND + +// /me/apps GET +// 200 OK {name,cover,description,installations,price,year,time_total} !!!!!! +// 401 UNAUTHORIZED + + /* current user */ async function register(form) { @@ -91,11 +173,17 @@ async function logout() { export { + getAllApps, + getMyApps, + addApp, + getApp, + register, getProfile, updateProfile, getUser, getLeaders, + login, logout, }; diff --git a/src/modules/router.js b/src/modules/router.js index 26cb15a6..ac12f5bc 100644 --- a/src/modules/router.js +++ b/src/modules/router.js @@ -1,6 +1,5 @@ import bus from './bus'; - /* utils */ /** proceeds urlencoded params to array */ @@ -21,8 +20,11 @@ function splitParams(string) { /** removes slashes from path string */ function clearPath(string) { + let path; if (string === '/') return string; - const path = string.slice(1); + if (string) { + path = string.slice(1); + } if (path.slice(-1) === '/') return path.slice(0, -1); return path; } @@ -35,7 +37,8 @@ export default class Router { this.mainApp = new MainApp('/', this.root); this.routes['/'] = this.mainApp; - this.appContainer = this.mainApp.appContainer.wrapper; + this.appContainer = this.mainApp.appContainer.screen; + this.appBar = this.mainApp.bar; this.listeners = [ { @@ -52,12 +55,22 @@ export default class Router { } + checkRegister(appUrl) { + return appUrl in this.routes; + } + registerApp(url, App, source) { - const app = new App(url, this.appContainer, source); if (url === '/') { console.error('MainApp already registered'); return this; } + if (url[0] === '/') url = url.slice(1); + if (url in this.routes) { + console.log('Url already set.'); + return false; + } + const app = new App(url, this.appContainer, source); + app.setBar(this.appBar); this.routes[url] = app; return this; } @@ -69,9 +82,11 @@ export default class Router { } this.mainApp.start(); + this.currentApp = this.mainApp; this.openFromAddressBar(); bus.listen('link', this.open.bind(this)); + bus.listen('regApp', this.registerApp.bind(this)); this.listeners.forEach((listener) => { const { target, event, method } = listener; @@ -98,29 +113,15 @@ export default class Router { }); } - // let app; - // if (this.routes.hasOwnProperty(path)) { - // app = this.routes[path]; - // if (app === this.mainApp) app.changeView('main', params); - // } else if (this.mainApp.views.hasOwnProperty(path)) { - // app = this.mainApp; - // app.changeView(path, params); - // } else { - // this.open('/'); - // return; - // } if (!app) { this.open('/'); return; } - this.currentApp = app; - if (!app.active) { - Object.values(this.routes).forEach((knownApp) => { - if (knownApp.active) knownApp.pause(); - }); + this.currentApp.pause(); + this.currentApp = app; app.launch(target); } @@ -143,7 +144,14 @@ export default class Router { if (target instanceof HTMLAnchorElement && (target.getAttribute('type') !== 'submit')) { event.preventDefault(); - this.open(target.pathname, target.search, target); + if (target.pathname) { + this.open(target.pathname, target.search, target); + } else { + const busEvent = target.getAttribute('event'); + if (busEvent) { + bus.emit(busEvent); + } + } } } } diff --git a/src/modules/userservice.js b/src/modules/userservice.js index 808ba6da..0bd6cb40 100644 --- a/src/modules/userservice.js +++ b/src/modules/userservice.js @@ -25,7 +25,7 @@ class UserService { if (err === 'updating') return { err }; throw new Error(); } else if (!loggedIn) { - bus.emit('link', '/login'); + bus.emit('link', '/terminal'); return { err: 'unathorized' }; } diff --git a/src/modules/webSocket.js b/src/modules/webSocket.js index 18fa5f6e..e5c45753 100644 --- a/src/modules/webSocket.js +++ b/src/modules/webSocket.js @@ -1,25 +1,41 @@ +import WsPostman from '../applications/snake/modules/wsPostman'; + export default class Ws { constructor(mesageParser) { // this.host = window.location.host; this.mesageParser = mesageParser; - // const address = `${window.location.protocol.replace('http', 'ws')}//${this.host}/ws`; - this.address = 'ws://localhost:9605/conn/ws'; + if (window.location.host === 'localhost:3000' + || window.location.host === '127.0.0.1:3000') { + this.host = 'localhost:9605'; + } else { + this.host = 'snake.rasseki.com'; + } + this.address = `${window.location.protocol.replace('http', 'ws')}//${this.host}/conn/ws`; } connect() { this.ws = new WebSocket(this.address); + this.ws.onerror = (error) => { + console.log('WebSocket connection failed', error); + }; + this.ws.onopen = (event) => { console.log(`WebSocket on address ${this.address} opened`); + this.wsPostman = new WsPostman(this.ws); this.ws.onmessage = this.handleMessage.bind(this); - + this.wsPostman.sendLogin(); this.ws.onclose = () => { this.mesageParser.emit('close'); }; }; } + getReadyState() { + return this.ws.readyState; + } + handleMessage(event) { try { const message = JSON.parse(event.data); @@ -31,7 +47,7 @@ export default class Ws { send(data) { // костыль - this.waitForConnection(() => { console.log('send', data); this.ws.send(JSON.stringify(data)); }, 0); + this.waitForConnection(() => { this.ws.send(JSON.stringify(data)); }, 0); } waitForConnection(callback, interval) { diff --git a/src/style.pcss b/src/style.pcss index 48c81838..4c51f230 100644 --- a/src/style.pcss +++ b/src/style.pcss @@ -2,13 +2,6 @@ body { -webkit-tap-highlight-color: rgba(0,0,0,0); -webkit-tap-highlight-color: transparent; user-select: none; - user-drag: none; -} - -.wrapper { - // height: 100%; - // width: 100%; - overflow: visible; } .game-wrapper { diff --git a/static/img/igor.png b/static/img/igor.png new file mode 100644 index 00000000..eaf98a2d Binary files /dev/null and b/static/img/igor.png differ diff --git a/webpack.config.js b/webpack.config.js index 85cdea22..a048aacf 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,6 +1,5 @@ const HtmlWebpackPlugin = require('html-webpack-plugin'); const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin'); -const HardSourceWebpackPlugin = require('hard-source-webpack-plugin'); const path = require('path'); @@ -17,9 +16,6 @@ const config = { new HtmlWebpackPlugin({ template: 'src/index.html', }), - new HardSourceWebpackPlugin({ - cacheDirectory: '.cache/', - }), new ServiceWorkerWebpackPlugin({ entry: path.join(__dirname, 'src/sw.js'), }), @@ -51,7 +47,7 @@ const config = { loader: 'pug-loader', }, { - test: /\.mp3$/, + test: /\.(mp3|wav)$/, loader: 'file-loader?name=music/[hash].[ext]', }, { @@ -59,7 +55,7 @@ const config = { loader: 'file-loader?name=img/[name].[ext]', }, { - test: /\.ico$/, + test: /\.(ico)$/, loader: 'file-loader?name=favicon.ico', }, {