Redone design

Fixed keyboar on web
This commit is contained in:
Sviatoslav Tsariov Yurievich 2025-03-12 16:41:01 +03:00
parent c008e89412
commit 2707d80723
48 changed files with 4436 additions and 123 deletions

4
.gitignore vendored
View File

@ -1,5 +1,5 @@
# Godot 4+ specific ignores
.godot/
addons/
build/
*.tmp
*.tmp
Talkpal.apk*

View File

@ -1,36 +0,0 @@
# Attribution
## Collaborators
### Role
Person 1
Person 2
[Person w/ Link]()
## Sourced / Unaffiliated
### Asset Type
#### Use Case
Author: [Name]()
Source: [Domain : webpage.html]()
License: [License]()
#### Godot Engine Logo
Author: Andrea Calabró
Source: [godotengine.org : press](https://godotengine.org/press/)
License: [CC BY 4.0 International](https://github.com/godotengine/godot/blob/master/LOGO_LICENSE.txt)
## Tools
#### Godot
Author: [Juan Linietsky, Ariel Manzur, and contributors](https://godotengine.org/contact)
Source: [godotengine.org](https://godotengine.org/)
License: [MIT License](https://github.com/godotengine/godot/blob/master/LICENSE.txt)
#### Git
Author: [Linus Torvalds](https://github.com/torvalds)
Source: [git-scm.com](https://git-scm.com/downloads)
License: [GNU General Public License version 2](https://opensource.org/licenses/GPL-2.0)
#### Godot Menus Template
Author: [Marek Belski](https://github.com/Maaack/Godot-Menus-Template/graphs/contributors)
Source: [github: Godot-Menus-Template](https://github.com/Maaack/Godot-Menus-Template)
License: [MIT License](LICENSE.txt)

View File

@ -0,0 +1,4 @@
[gd_resource type="Theme" format=3 uid="uid://bbboqrxd230bp"]
[resource]
default_font_size = 42

View File

@ -0,0 +1,4 @@
[gd_resource type="Theme" format=3 uid="uid://c66wo0qg82ssk"]
[resource]
default_font_size = 60

BIN
build/Talkpal.144x144.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dq3g3dgxcmfqm"
path="res://.godot/imported/Talkpal.144x144.png-9746dc3f6ae3502391455f6dcf0427f2.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://build/Talkpal.144x144.png"
dest_files=["res://.godot/imported/Talkpal.144x144.png-9746dc3f6ae3502391455f6dcf0427f2.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
build/Talkpal.180x180.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c7rrqhmvltgo7"
path="res://.godot/imported/Talkpal.180x180.png-543499b9301010cae1b5a8aa15a43453.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://build/Talkpal.180x180.png"
dest_files=["res://.godot/imported/Talkpal.180x180.png-543499b9301010cae1b5a8aa15a43453.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
build/Talkpal.512x512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cpi8avt0lny03"
path="res://.godot/imported/Talkpal.512x512.png-5f61f4bc15dc82ee2bceb9e0c65e6d42.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://build/Talkpal.512x512.png"
dest_files=["res://.godot/imported/Talkpal.512x512.png-5f61f4bc15dc82ee2bceb9e0c65e6d42.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://clefkbuu8dy15"
path="res://.godot/imported/Talkpal.apple-touch-icon.png-16693661a1927dfe41ba57e49aadb965.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://build/Talkpal.apple-touch-icon.png"
dest_files=["res://.godot/imported/Talkpal.apple-touch-icon.png-16693661a1927dfe41ba57e49aadb965.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@ -0,0 +1,213 @@
/**************************************************************************/
/* audio.worklet.js */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
class RingBuffer {
constructor(p_buffer, p_state, p_threads) {
this.buffer = p_buffer;
this.avail = p_state;
this.threads = p_threads;
this.rpos = 0;
this.wpos = 0;
}
data_left() {
return this.threads ? Atomics.load(this.avail, 0) : this.avail;
}
space_left() {
return this.buffer.length - this.data_left();
}
read(output) {
const size = this.buffer.length;
let from = 0;
let to_write = output.length;
if (this.rpos + to_write > size) {
const high = size - this.rpos;
output.set(this.buffer.subarray(this.rpos, size));
from = high;
to_write -= high;
this.rpos = 0;
}
if (to_write) {
output.set(this.buffer.subarray(this.rpos, this.rpos + to_write), from);
}
this.rpos += to_write;
if (this.threads) {
Atomics.add(this.avail, 0, -output.length);
Atomics.notify(this.avail, 0);
} else {
this.avail -= output.length;
}
}
write(p_buffer) {
const to_write = p_buffer.length;
const mw = this.buffer.length - this.wpos;
if (mw >= to_write) {
this.buffer.set(p_buffer, this.wpos);
this.wpos += to_write;
if (mw === to_write) {
this.wpos = 0;
}
} else {
const high = p_buffer.subarray(0, mw);
const low = p_buffer.subarray(mw);
this.buffer.set(high, this.wpos);
this.buffer.set(low);
this.wpos = low.length;
}
if (this.threads) {
Atomics.add(this.avail, 0, to_write);
Atomics.notify(this.avail, 0);
} else {
this.avail += to_write;
}
}
}
class GodotProcessor extends AudioWorkletProcessor {
constructor() {
super();
this.threads = false;
this.running = true;
this.lock = null;
this.notifier = null;
this.output = null;
this.output_buffer = new Float32Array();
this.input = null;
this.input_buffer = new Float32Array();
this.port.onmessage = (event) => {
const cmd = event.data['cmd'];
const data = event.data['data'];
this.parse_message(cmd, data);
};
}
process_notify() {
if (this.notifier) {
Atomics.add(this.notifier, 0, 1);
Atomics.notify(this.notifier, 0);
}
}
parse_message(p_cmd, p_data) {
if (p_cmd === 'start' && p_data) {
const state = p_data[0];
let idx = 0;
this.threads = true;
this.lock = state.subarray(idx, ++idx);
this.notifier = state.subarray(idx, ++idx);
const avail_in = state.subarray(idx, ++idx);
const avail_out = state.subarray(idx, ++idx);
this.input = new RingBuffer(p_data[1], avail_in, true);
this.output = new RingBuffer(p_data[2], avail_out, true);
} else if (p_cmd === 'stop') {
this.running = false;
this.output = null;
this.input = null;
this.lock = null;
this.notifier = null;
} else if (p_cmd === 'start_nothreads') {
this.output = new RingBuffer(p_data[0], p_data[0].length, false);
} else if (p_cmd === 'chunk') {
this.output.write(p_data);
}
}
static array_has_data(arr) {
return arr.length && arr[0].length && arr[0][0].length;
}
process(inputs, outputs, parameters) {
if (!this.running) {
return false; // Stop processing.
}
if (this.output === null) {
return true; // Not ready yet, keep processing.
}
const process_input = GodotProcessor.array_has_data(inputs);
if (process_input) {
const input = inputs[0];
const chunk = input[0].length * input.length;
if (this.input_buffer.length !== chunk) {
this.input_buffer = new Float32Array(chunk);
}
if (!this.threads) {
GodotProcessor.write_input(this.input_buffer, input);
this.port.postMessage({ 'cmd': 'input', 'data': this.input_buffer });
} else if (this.input.space_left() >= chunk) {
GodotProcessor.write_input(this.input_buffer, input);
this.input.write(this.input_buffer);
} else {
// this.port.postMessage('Input buffer is full! Skipping input frame.'); // Uncomment this line to debug input buffer.
}
}
const process_output = GodotProcessor.array_has_data(outputs);
if (process_output) {
const output = outputs[0];
const chunk = output[0].length * output.length;
if (this.output_buffer.length !== chunk) {
this.output_buffer = new Float32Array(chunk);
}
if (this.output.data_left() >= chunk) {
this.output.read(this.output_buffer);
GodotProcessor.write_output(output, this.output_buffer);
if (!this.threads) {
this.port.postMessage({ 'cmd': 'read', 'data': chunk });
}
} else {
// this.port.postMessage('Output buffer has not enough frames! Skipping output frame.'); // Uncomment this line to debug output buffer.
}
}
this.process_notify();
return true;
}
static write_output(dest, source) {
const channels = dest.length;
for (let ch = 0; ch < channels; ch++) {
for (let sample = 0; sample < dest[ch].length; sample++) {
dest[ch][sample] = source[sample * channels + ch];
}
}
}
static write_input(dest, source) {
const channels = source.length;
for (let ch = 0; ch < channels; ch++) {
for (let sample = 0; sample < source[ch].length; sample++) {
dest[sample * channels + ch] = source[ch][sample];
}
}
}
}
registerProcessor('godot-processor', GodotProcessor);

200
build/Talkpal.html Normal file
View File

@ -0,0 +1,200 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
<title>Talkpal</title>
<style>
html, body, #canvas {
margin: 0;
padding: 0;
border: 0;
}
body {
color: white;
background-color: black;
overflow: hidden;
touch-action: none;
}
#canvas {
display: block;
}
#canvas:focus {
outline: none;
}
#status, #status-splash, #status-progress {
position: absolute;
left: 0;
right: 0;
}
#status, #status-splash {
top: 0;
bottom: 0;
}
#status {
background-color: #242424;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
visibility: hidden;
}
#status-splash {
max-height: 100%;
max-width: 100%;
margin: auto;
}
#status-progress, #status-notice {
display: none;
}
#status-progress {
bottom: 10%;
width: 50%;
margin: 0 auto;
}
#status-notice {
background-color: #5b3943;
border-radius: 0.5rem;
border: 1px solid #9b3943;
color: #e0e0e0;
font-family: 'Noto Sans', 'Droid Sans', Arial, sans-serif;
line-height: 1.3;
margin: 0 2rem;
overflow: hidden;
padding: 1rem;
text-align: center;
z-index: 1;
}
</style>
<link id="-gd-engine-icon" rel="icon" type="image/png" href="Talkpal.icon.png" />
<link rel="apple-touch-icon" href="Talkpal.apple-touch-icon.png"/>
<link rel="manifest" href="Talkpal.manifest.json">
</head>
<body>
<canvas id="canvas">
Your browser does not support the canvas tag.
</canvas>
<noscript>
Your browser does not support JavaScript.
</noscript>
<div id="status">
<img id="status-splash" src="Talkpal.png" alt="">
<progress id="status-progress"></progress>
<div id="status-notice"></div>
</div>
<script src="Talkpal.js"></script>
<script>
const GODOT_CONFIG = {"args":[],"canvasResizePolicy":2,"ensureCrossOriginIsolationHeaders":true,"executable":"Talkpal","experimentalVK":true,"fileSizes":{"Talkpal.pck":6372256,"Talkpal.wasm":43016933},"focusCanvas":true,"gdextensionLibs":[],"serviceWorker":"Talkpal.service.worker.js"};
const GODOT_THREADS_ENABLED = false;
const engine = new Engine(GODOT_CONFIG);
(function () {
const statusOverlay = document.getElementById('status');
const statusProgress = document.getElementById('status-progress');
const statusNotice = document.getElementById('status-notice');
let initializing = true;
let statusMode = '';
function setStatusMode(mode) {
if (statusMode === mode || !initializing) {
return;
}
if (mode === 'hidden') {
statusOverlay.remove();
initializing = false;
return;
}
statusOverlay.style.visibility = 'visible';
statusProgress.style.display = mode === 'progress' ? 'block' : 'none';
statusNotice.style.display = mode === 'notice' ? 'block' : 'none';
statusMode = mode;
}
function setStatusNotice(text) {
while (statusNotice.lastChild) {
statusNotice.removeChild(statusNotice.lastChild);
}
const lines = text.split('\n');
lines.forEach((line) => {
statusNotice.appendChild(document.createTextNode(line));
statusNotice.appendChild(document.createElement('br'));
});
}
function displayFailureNotice(err) {
console.error(err);
if (err instanceof Error) {
setStatusNotice(err.message);
} else if (typeof err === 'string') {
setStatusNotice(err);
} else {
setStatusNotice('An unknown error occured');
}
setStatusMode('notice');
initializing = false;
}
const missing = Engine.getMissingFeatures({
threads: GODOT_THREADS_ENABLED,
});
if (missing.length !== 0) {
if (GODOT_CONFIG['serviceWorker'] && GODOT_CONFIG['ensureCrossOriginIsolationHeaders'] && 'serviceWorker' in navigator) {
// There's a chance that installing the service worker would fix the issue
Promise.race([
navigator.serviceWorker.getRegistration().then((registration) => {
if (registration != null) {
return Promise.reject(new Error('Service worker already exists.'));
}
return registration;
}).then(() => engine.installServiceWorker()),
// For some reason, `getRegistration()` can stall
new Promise((resolve) => {
setTimeout(() => resolve(), 2000);
}),
]).catch((err) => {
console.error('Error while registering service worker:', err);
}).then(() => {
window.location.reload();
});
} else {
// Display the message as usual
const missingMsg = 'Error\nThe following features required to run Godot projects on the Web are missing:\n';
displayFailureNotice(missingMsg + missing.join('\n'));
}
} else {
setStatusMode('progress');
engine.startGame({
'onProgress': function (current, total) {
if (current > 0 && total > 0) {
statusProgress.value = current;
statusProgress.max = total;
} else {
statusProgress.removeAttribute('value');
statusProgress.removeAttribute('max');
}
},
}).then(() => {
setStatusMode('hidden');
}, displayFailureNotice);
}
}());
</script>
</body>
</html>

BIN
build/Talkpal.icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cv3qgijbr0ixb"
path="res://.godot/imported/Talkpal.icon.png-2e39fac812df96c3f4fa6298212d67c1.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://build/Talkpal.icon.png"
dest_files=["res://.godot/imported/Talkpal.icon.png-2e39fac812df96c3f4fa6298212d67c1.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

905
build/Talkpal.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"background_color":"#000000","display":"standalone","icons":[{"sizes":"144x144","src":"Talkpal.144x144.png","type":"image/png"},{"sizes":"180x180","src":"Talkpal.180x180.png","type":"image/png"},{"sizes":"512x512","src":"Talkpal.512x512.png","type":"image/png"}],"name":"Talkpal","orientation":"any","start_url":"./Talkpal.html"}

View File

@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>You are offline</title>
<style>
html {
background-color: #000000;
color: #ffffff;
}
body {
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
margin: 2rem;
}
p {
margin-block: 1rem;
}
button {
display: block;
padding: 1rem 2rem;
margin: 3rem auto 0;
}
</style>
</head>
<body>
<h1>You are offline</h1>
<p>This application requires an Internet connection to run for the first time.</p>
<p>Press the button below to try reloading:</p>
<button type="button">Reload</button>
<script>
document.querySelector('button').addEventListener('click', () => {
window.location.reload();
});
</script>
</body>
</html>

BIN
build/Talkpal.pck Normal file

Binary file not shown.

BIN
build/Talkpal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

34
build/Talkpal.png.import Normal file
View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cbp1jcxnlheub"
path="res://.godot/imported/Talkpal.png-93a93329b7f18a5c427549562ff04a5f.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://build/Talkpal.png"
dest_files=["res://.godot/imported/Talkpal.png-93a93329b7f18a5c427549562ff04a5f.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@ -0,0 +1,166 @@
// This service worker is required to expose an exported Godot project as a
// Progressive Web App. It provides an offline fallback page telling the user
// that they need an Internet connection to run the project if desired.
// Incrementing CACHE_VERSION will kick off the install event and force
// previously cached resources to be updated from the network.
/** @type {string} */
const CACHE_VERSION = '1741733523|6678384895';
/** @type {string} */
const CACHE_PREFIX = 'Talkpal-sw-cache-';
const CACHE_NAME = CACHE_PREFIX + CACHE_VERSION;
/** @type {string} */
const OFFLINE_URL = 'Talkpal.offline.html';
/** @type {boolean} */
const ENSURE_CROSSORIGIN_ISOLATION_HEADERS = true;
// Files that will be cached on load.
/** @type {string[]} */
const CACHED_FILES = ["Talkpal.html","Talkpal.js","Talkpal.offline.html","Talkpal.icon.png","Talkpal.apple-touch-icon.png","Talkpal.worker.js","Talkpal.audio.worklet.js"];
// Files that we might not want the user to preload, and will only be cached on first load.
/** @type {string[]} */
const CACHABLE_FILES = ["Talkpal.wasm","Talkpal.pck"];
const FULL_CACHE = CACHED_FILES.concat(CACHABLE_FILES);
self.addEventListener('install', (event) => {
event.waitUntil(caches.open(CACHE_NAME).then((cache) => cache.addAll(CACHED_FILES)));
});
self.addEventListener('activate', (event) => {
event.waitUntil(caches.keys().then(
function (keys) {
// Remove old caches.
return Promise.all(keys.filter((key) => key.startsWith(CACHE_PREFIX) && key !== CACHE_NAME).map((key) => caches.delete(key)));
}
).then(function () {
// Enable navigation preload if available.
return ('navigationPreload' in self.registration) ? self.registration.navigationPreload.enable() : Promise.resolve();
}));
});
/**
* Ensures that the response has the correct COEP/COOP headers
* @param {Response} response
* @returns {Response}
*/
function ensureCrossOriginIsolationHeaders(response) {
if (response.headers.get('Cross-Origin-Embedder-Policy') === 'require-corp'
&& response.headers.get('Cross-Origin-Opener-Policy') === 'same-origin') {
return response;
}
const crossOriginIsolatedHeaders = new Headers(response.headers);
crossOriginIsolatedHeaders.set('Cross-Origin-Embedder-Policy', 'require-corp');
crossOriginIsolatedHeaders.set('Cross-Origin-Opener-Policy', 'same-origin');
const newResponse = new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: crossOriginIsolatedHeaders,
});
return newResponse;
}
/**
* Calls fetch and cache the result if it is cacheable
* @param {FetchEvent} event
* @param {Cache} cache
* @param {boolean} isCacheable
* @returns {Response}
*/
async function fetchAndCache(event, cache, isCacheable) {
// Use the preloaded response, if it's there
/** @type { Response } */
let response = await event.preloadResponse;
if (response == null) {
// Or, go over network.
response = await self.fetch(event.request);
}
if (ENSURE_CROSSORIGIN_ISOLATION_HEADERS) {
response = ensureCrossOriginIsolationHeaders(response);
}
if (isCacheable) {
// And update the cache
cache.put(event.request, response.clone());
}
return response;
}
self.addEventListener(
'fetch',
/**
* Triggered on fetch
* @param {FetchEvent} event
*/
(event) => {
const isNavigate = event.request.mode === 'navigate';
const url = event.request.url || '';
const referrer = event.request.referrer || '';
const base = referrer.slice(0, referrer.lastIndexOf('/') + 1);
const local = url.startsWith(base) ? url.replace(base, '') : '';
const isCachable = FULL_CACHE.some((v) => v === local) || (base === referrer && base.endsWith(CACHED_FILES[0]));
if (isNavigate || isCachable) {
event.respondWith((async () => {
// Try to use cache first
const cache = await caches.open(CACHE_NAME);
if (isNavigate) {
// Check if we have full cache during HTML page request.
/** @type {Response[]} */
const fullCache = await Promise.all(FULL_CACHE.map((name) => cache.match(name)));
const missing = fullCache.some((v) => v === undefined);
if (missing) {
try {
// Try network if some cached file is missing (so we can display offline page in case).
const response = await fetchAndCache(event, cache, isCachable);
return response;
} catch (e) {
// And return the hopefully always cached offline page in case of network failure.
console.error('Network error: ', e); // eslint-disable-line no-console
return caches.match(OFFLINE_URL);
}
}
}
let cached = await cache.match(event.request);
if (cached != null) {
if (ENSURE_CROSSORIGIN_ISOLATION_HEADERS) {
cached = ensureCrossOriginIsolationHeaders(cached);
}
return cached;
}
// Try network if don't have it in cache.
const response = await fetchAndCache(event, cache, isCachable);
return response;
})());
} else if (ENSURE_CROSSORIGIN_ISOLATION_HEADERS) {
event.respondWith((async () => {
let response = await fetch(event.request);
response = ensureCrossOriginIsolationHeaders(response);
return response;
})());
}
}
);
self.addEventListener('message', (event) => {
// No cross origin
if (event.origin !== self.origin) {
return;
}
const id = event.source.id || '';
const msg = event.data || '';
// Ensure it's one of our clients.
self.clients.get(id).then(function (client) {
if (!client) {
return; // Not a valid client.
}
if (msg === 'claim') {
self.skipWaiting().then(() => self.clients.claim());
} else if (msg === 'clear') {
caches.delete(CACHE_NAME);
} else if (msg === 'update') {
self.skipWaiting().then(() => self.clients.claim()).then(() => self.clients.matchAll()).then((all) => all.forEach((c) => c.navigate(c.url)));
}
});
});

BIN
build/Talkpal.wasm Normal file

Binary file not shown.

161
build/Talkpal.worker.js Normal file
View File

@ -0,0 +1,161 @@
/**
* @license
* Copyright 2015 The Emscripten Authors
* SPDX-License-Identifier: MIT
*/
// Pthread Web Worker startup routine:
// This is the entry point file that is loaded first by each Web Worker
// that executes pthreads on the Emscripten application.
'use strict';
var Module = {};
// Thread-local guard variable for one-time init of the JS state
var initializedJS = false;
function assert(condition, text) {
if (!condition) abort('Assertion failed: ' + text);
}
function threadPrintErr() {
var text = Array.prototype.slice.call(arguments).join(' ');
console.error(text);
}
function threadAlert() {
var text = Array.prototype.slice.call(arguments).join(' ');
postMessage({cmd: 'alert', text: text, threadId: Module['_pthread_self']()});
}
// We don't need out() for now, but may need to add it if we want to use it
// here. Or, if this code all moves into the main JS, that problem will go
// away. (For now, adding it here increases code size for no benefit.)
var out = () => { throw 'out() is not defined in worker.js.'; }
var err = threadPrintErr;
self.alert = threadAlert;
Module['instantiateWasm'] = (info, receiveInstance) => {
// Instantiate from the module posted from the main thread.
// We can just use sync instantiation in the worker.
var module = Module['wasmModule'];
// We don't need the module anymore; new threads will be spawned from the main thread.
Module['wasmModule'] = null;
var instance = new WebAssembly.Instance(module, info);
// TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193,
// the above line no longer optimizes out down to the following line.
// When the regression is fixed, we can remove this if/else.
return receiveInstance(instance);
}
// Turn unhandled rejected promises into errors so that the main thread will be
// notified about them.
self.onunhandledrejection = (e) => {
throw e.reason ?? e;
};
function handleMessage(e) {
try {
if (e.data.cmd === 'load') { // Preload command that is called once per worker to parse and load the Emscripten code.
// Until we initialize the runtime, queue up any further incoming messages.
let messageQueue = [];
self.onmessage = (e) => messageQueue.push(e);
// And add a callback for when the runtime is initialized.
self.startWorker = (instance) => {
Module = instance;
// Notify the main thread that this thread has loaded.
postMessage({ 'cmd': 'loaded' });
// Process any messages that were queued before the thread was ready.
for (let msg of messageQueue) {
handleMessage(msg);
}
// Restore the real message handler.
self.onmessage = handleMessage;
};
// Module and memory were sent from main thread
Module['wasmModule'] = e.data.wasmModule;
// Use `const` here to ensure that the variable is scoped only to
// that iteration, allowing safe reference from a closure.
for (const handler of e.data.handlers) {
Module[handler] = function() {
postMessage({ cmd: 'callHandler', handler, args: [...arguments] });
}
}
Module['wasmMemory'] = e.data.wasmMemory;
Module['buffer'] = Module['wasmMemory'].buffer;
Module['workerID'] = e.data.workerID;
Module['ENVIRONMENT_IS_PTHREAD'] = true;
if (typeof e.data.urlOrBlob == 'string') {
importScripts(e.data.urlOrBlob);
} else {
var objectUrl = URL.createObjectURL(e.data.urlOrBlob);
importScripts(objectUrl);
URL.revokeObjectURL(objectUrl);
}
Godot(Module);
} else if (e.data.cmd === 'run') {
// Pass the thread address to wasm to store it for fast access.
Module['__emscripten_thread_init'](e.data.pthread_ptr, /*isMainBrowserThread=*/0, /*isMainRuntimeThread=*/0, /*canBlock=*/1);
// Await mailbox notifications with `Atomics.waitAsync` so we can start
// using the fast `Atomics.notify` notification path.
Module['__emscripten_thread_mailbox_await'](e.data.pthread_ptr);
assert(e.data.pthread_ptr);
// Also call inside JS module to set up the stack frame for this pthread in JS module scope
Module['establishStackSpace']();
Module['PThread'].receiveObjectTransfer(e.data);
Module['PThread'].threadInitTLS();
if (!initializedJS) {
initializedJS = true;
}
try {
Module['invokeEntryPoint'](e.data.start_routine, e.data.arg);
} catch(ex) {
if (ex != 'unwind') {
// The pthread "crashed". Do not call `_emscripten_thread_exit` (which
// would make this thread joinable). Instead, re-throw the exception
// and let the top level handler propagate it back to the main thread.
throw ex;
}
}
} else if (e.data.cmd === 'cancel') { // Main thread is asking for a pthread_cancel() on this thread.
if (Module['_pthread_self']()) {
Module['__emscripten_thread_exit'](-1);
}
} else if (e.data.target === 'setimmediate') {
// no-op
} else if (e.data.cmd === 'checkMailbox') {
if (initializedJS) {
Module['checkMailbox']();
}
} else if (e.data.cmd) {
// The received message looks like something that should be handled by this message
// handler, (since there is a e.data.cmd field present), but is not one of the
// recognized commands:
err('worker.js received unknown command ' + e.data.cmd);
err(e.data);
}
} catch(ex) {
err('worker.js onmessage() captured an uncaught exception: ' + ex);
if (ex && ex.stack) err(ex.stack);
if (Module['__emscripten_thread_crashed']) {
Module['__emscripten_thread_crashed']();
}
throw ex;
}
};
self.onmessage = handleMessage;

905
build/web/Talkpal.js Normal file

File diff suppressed because one or more lines are too long

908
build/web/Talkpal.js.old Normal file

File diff suppressed because one or more lines are too long

View File

@ -63,7 +63,7 @@ permissions/access_coarse_location=false
permissions/access_fine_location=false
permissions/access_location_extra_commands=false
permissions/access_mock_location=false
permissions/access_network_state=false
permissions/access_network_state=true
permissions/access_surface_flinger=false
permissions/access_wifi_state=false
permissions/account_manager=false
@ -124,7 +124,7 @@ permissions/install_location_provider=false
permissions/install_packages=false
permissions/install_shortcut=false
permissions/internal_system_window=false
permissions/internet=false
permissions/internet=true
permissions/kill_background_processes=false
permissions/location_hardware=false
permissions/manage_accounts=false
@ -240,7 +240,7 @@ html/custom_html_shell=""
html/head_include=""
html/canvas_resize_policy=2
html/focus_canvas_on_start=true
html/experimental_virtual_keyboard=false
html/experimental_virtual_keyboard=true
progressive_web_app/enabled=true
progressive_web_app/ensure_cross_origin_isolation_headers=true
progressive_web_app/offline_page=""

View File

@ -40,7 +40,7 @@ project/assembly_name="Talkpal Frontend"
[editor_plugins]
enabled=PackedStringArray("res://addons/maaacks_menus_template/plugin.cfg", "res://addons/uuid/plugin.cfg")
enabled=PackedStringArray("res://addons/maaacks_menus_template/plugin.cfg", "res://addons/onscreenkeyboard/plugin.cfg", "res://addons/uuid/plugin.cfg")
[internationalization]

View File

@ -15,12 +15,14 @@ const DateUpdateInterval := 1.0
@onready var _timeline = $Panel/Timeline
@onready var _reservations = $Panel/Reservations
@onready var _date = $TopBar/DateButton
@onready var _room = $TopBar/RoomButton
var _date_update_timer = 0.0
func _process(delta):
_process_hour_size()
_process_date(delta)
_process_fonts()
func _process_hour_size():
var hour_size = get_viewport_rect().size.y/15
@ -37,6 +39,16 @@ func _process_date(delta):
var date = Time.get_date_dict_from_system()
_date.text = "%02d.%02d.%04d" % [date.day, date.month, date.year]
func _process_fonts():
_process_font_size(_date, 35)
_process_font_size(_room, 35)
func _process_font_size(obj, k):
var font_size = obj.get_theme_default_font_size()
var new_font_size = get_viewport_rect().size.y/k
if font_size != new_font_size:
obj.add_theme_font_size_override("font_size", new_font_size)
func _ready():
_connect_signals()
@ -44,7 +56,6 @@ func _ready():
_remove_reservations()
_fill_with_slots()
_update_schedule()
func _connect_signals():
await _main.ready
@ -94,6 +105,7 @@ func _compose_reservation(start_time, duration_time, title, color, id):
var reservation = ReservationScene.instantiate()
_reservations.add_child(reservation)
reservation.set_start_time(start_time)
reservation.set_duration_time(duration_time)
reservation.set_title(title)
@ -143,7 +155,9 @@ func _on_date_button_pressed():
print("emit change date signal")
func _on_reservations_updated(reservations):
await get_tree().create_timer(0.1, false).timeout
update()
func update():
_ready()
_update_schedule()

View File

@ -14,18 +14,29 @@ grow_horizontal = 2
theme_override_constants/separation = 25
script = ExtResource("1_2wgc4")
[node name="TopBar" type="HBoxContainer" parent="."]
[node name="TopBar" type="Control" parent="."]
custom_minimum_size = Vector2(1150, 50)
layout_mode = 2
size_flags_horizontal = 0
size_flags_vertical = 0
[node name="RoomButton" type="Button" parent="TopBar"]
visible = false
layout_mode = 2
layout_mode = 1
offset_right = 348.0
offset_bottom = 72.0
size_flags_horizontal = 0
theme = ExtResource("1_na0ey")
text = "Переговорка 1"
[node name="DateButton" type="Button" parent="TopBar"]
layout_mode = 2
size_flags_horizontal = 10
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -380.0
offset_bottom = 72.0
grow_horizontal = 0
size_flags_horizontal = 8
theme = ExtResource("1_na0ey")
text = "Вт, 14 июн. 2022"
@ -69,11 +80,11 @@ theme_override_colors/font_color = Color(0, 0, 0, 0)
text = "11:00 "
[node name="Handler" type="ColorRect" parent="Panel/CurrentTime/HBoxContainer"]
custom_minimum_size = Vector2(0, 5)
custom_minimum_size = Vector2(0, 10)
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 0
color = Color(1, 1, 1, 0.556863)
color = Color(1, 1, 0, 0.556863)
[node name="Reservations" type="Control" parent="Panel"]
layout_mode = 2
@ -86,3 +97,6 @@ grow_horizontal = 2
[node name="Reservation" parent="Panel/Reservations" instance=ExtResource("4_o5rhy")]
layout_mode = 2
offset_right = 1036.0
[node name="Control" type="Control" parent="."]
layout_mode = 2

View File

@ -64,6 +64,6 @@ func _on_section_panel_gui_input(event):
print("emit open reservation info signal")
var repo = _main.get_reservation_repo()
repo.set_current_reservation_id(_id)
repo.set_selected_reservation_id(_id)
_main.load_page(Main.Pages.ReservationEdit, true)

View File

@ -0,0 +1,304 @@
class_name CustomLineEdit
extends Control
# Internal state
var cursor_pos: int = 0
var selection: Vector2i = Vector2i(-1, -1)
var cursor_visible: bool = false
var has_focus: bool = false
# Visual settings
var cursor_color: Color = Color.BLACK
@export var text: String = "":
set(v):
text = v.left(max_length)
cursor_pos = clamp(cursor_pos, 0, text.length())
queue_redraw()
@export_multiline var placeholder: String = "":
set(v):
placeholder = v
queue_redraw()
@export_enum("Left", "Center", "Right") var align: int = 0:
set(v):
align = v
queue_redraw()
@export_range(0, 1024) var max_length: int = 256
@export var secret: bool = false:
set(v):
secret = v
queue_redraw()
@export var secret_char: String = "*" :
set(v):
secret_char = v[0] if v.length() > 0 else "*"
queue_redraw()
@export var editable: bool = true
@export var context_menu_enabled: bool = true
@export var clear_button_enabled: bool = false
@export var shortcut_keys_enabled: bool = true
@export var virtual_keyboard_enabled: bool = true
@export var caret_blink: bool = true
@export_range(0.1, 2.0) var caret_blink_interval: float = 0.5
@export var expand_to_text_length: bool = false
@export var select_all_on_focus: bool = false
@export_group("Colors")
@export var text_color: Color = Color.BLACK:
set(v):
text_color = v
queue_redraw()
@export var placeholder_color: Color = Color.DIM_GRAY:
set(v):
placeholder_color = v
queue_redraw()
@export var caret_color: Color = Color.BLACK:
set(v):
caret_color = v
queue_redraw()
@export var selection_color: Color = Color(0.7, 0.8, 1.0, 0.5):
set(v):
selection_color = v
queue_redraw()
@export_group("Font")
@export var font: Font = ThemeDB.fallback_font:
set(v):
font = v
queue_redraw()
@export_range(8, 64) var font_size: int = 16:
set(v):
font_size = v
queue_redraw()
func _ready():
focus_entered.connect(_on_focus_entered)
focus_exited.connect(_on_focus_exited)
set_process_input(true)
start_blink_timer()
func _on_focus_entered():
has_focus = true
cursor_visible = true
queue_redraw()
func _on_focus_exited():
has_focus = false
cursor_visible = false
queue_redraw()
func start_blink_timer():
var timer = Timer.new()
add_child(timer)
timer.wait_time = 0.5
timer.timeout.connect(_toggle_cursor_visibility)
timer.start()
func _toggle_cursor_visibility():
if has_focus:
cursor_visible = !cursor_visible
queue_redraw()
func _gui_input(event):
if event is InputEventKey and has_focus:
handle_key_input(event)
if event is InputEventMouseButton and event.pressed:
if get_rect().has_point(event.position) or true:
grab_focus()
handle_mouse_click(event)
else:
release_focus()
func handle_mouse_click(event: InputEventMouseButton):
var click_pos = get_local_mouse_position().x
var text_to_use = get_display_text()
var offset = get_text_offset(text_to_use)
var pos = 0
var accumulated_width = 0.0
var found = false
for i in range(text_to_use.length()):
var char_width = font.get_string_size(text_to_use.substr(i, 1), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x
if accumulated_width + char_width/2 > click_pos - offset:
pos = i
found = true
break
accumulated_width += char_width
if not found:
pos = text_to_use.length()
cursor_pos = pos
selection = Vector2i(-1, -1)
cursor_visible = true
queue_redraw()
func handle_key_input(event: InputEventKey):
var shift_pressed = event.shift_pressed
var ctrl_pressed = event.ctrl_pressed
# Handle selection
if shift_pressed and selection.x == -1:
selection = Vector2i(cursor_pos, cursor_pos)
# Handle text input
if event.unicode != 0 and !event.echo:
insert_text_at_cursor(char(event.unicode))
# Handle special keys
match event.keycode:
KEY_BACKSPACE:
delete_prev_char()
KEY_DELETE:
delete_next_char()
KEY_LEFT:
move_cursor(-1, ctrl_pressed, shift_pressed)
KEY_RIGHT:
move_cursor(1, ctrl_pressed, shift_pressed)
KEY_HOME:
cursor_pos = 0
adjust_selection(shift_pressed)
KEY_END:
cursor_pos = text.length()
adjust_selection(shift_pressed)
KEY_C:
if ctrl_pressed:
copy_to_clipboard()
KEY_V:
if ctrl_pressed:
paste_from_clipboard()
queue_redraw()
func insert_text_at_cursor(new_text: String):
var new_text_filtered = new_text.replace("\n", "").replace("\t", "")
if text.length() + new_text_filtered.length() > max_length:
return
if selection.x != -1:
delete_selection()
text = text.insert(cursor_pos, new_text_filtered)
cursor_pos += new_text_filtered.length()
selection = Vector2i(-1, -1)
func delete_prev_char():
if cursor_pos > 0:
if selection.x != -1:
delete_selection()
else:
text = text.erase(cursor_pos - 1, 1)
cursor_pos -= 1
func delete_next_char():
if cursor_pos < text.length():
if selection.x != -1:
delete_selection()
else:
text = text.erase(cursor_pos, 1)
func delete_selection():
var start = min(selection.x, selection.y)
var end = max(selection.x, selection.y)
text = text.erase(start, end - start)
cursor_pos = start
selection = Vector2i(-1, -1)
func move_cursor(direction: int, ctrl: bool, shift: bool):
var new_pos = cursor_pos
if ctrl:
new_pos = find_word_boundary(direction)
else:
new_pos += direction
cursor_pos = clamp(new_pos, 0, text.length())
adjust_selection(shift)
func adjust_selection(shift: bool):
if shift:
selection.y = cursor_pos
else:
selection = Vector2i(-1, -1)
func find_word_boundary(direction: int) -> int:
# Simplified word navigation
var pos = cursor_pos
if direction == -1:
while pos > 0 and text[pos-1] == " ":
pos -= 1
while pos > 0 and text[pos-1] != " ":
pos -= 1
else:
while pos < text.length() and text[pos] == " ":
pos += 1
while pos < text.length() and text[pos] != " ":
pos += 1
return pos
func copy_to_clipboard():
if selection.x != -1:
var start = min(selection.x, selection.y)
var end = max(selection.x, selection.y)
DisplayServer.clipboard_set(text.substr(start, end - start))
func paste_from_clipboard():
var clipboard = DisplayServer.clipboard_get()
insert_text_at_cursor(clipboard)
func get_display_text() -> String:
if secret and text.length() > 0:
return secret_char.repeat(text.length())
return text if text.length() > 0 else placeholder
func get_text_offset(display_text: String) -> float:
var text_width = font.get_string_size(display_text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x
match align:
HorizontalAlignment.HORIZONTAL_ALIGNMENT_CENTER:
return max(0, (size.x - text_width) / 2)
HorizontalAlignment.HORIZONTAL_ALIGNMENT_RIGHT:
return max(0, size.x - text_width)
_: # Left align
return 0.0
func _draw():
var display_text = get_display_text()
var text_offset = get_text_offset(display_text)
# Draw selection
print(selection)
if selection.x != -1:
draw_selection(text_offset, display_text)
# Draw text
var text_color = Color.DIM_GRAY if text == "" else Color.BLACK
draw_string(font, Vector2(text_offset, size.y / 2 + font_size / 2), display_text,
HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color)
# Draw cursor
if cursor_visible and has_focus:
draw_cursor(text_offset, display_text)
func draw_selection(offset: float, display_text: String):
var start = min(selection.x, selection.y)
var end = max(selection.x, selection.y)
var start_pos = offset + font.get_string_size(display_text.left(start), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x
var end_pos = offset + font.get_string_size(display_text.left(end), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x
draw_rect(Rect2(start_pos, 2, end_pos - start_pos, size.y - 4), selection_color, true)
func draw_cursor(offset: float, display_text: String):
var cursor_x = offset + font.get_string_size(display_text.left(cursor_pos), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x
draw_line(Vector2(cursor_x, 2), Vector2(cursor_x, size.y - 2), cursor_color, 2.0)

View File

@ -0,0 +1,44 @@
[gd_scene load_steps=2 format=3 uid="uid://dt03kci5qbkg3"]
[ext_resource type="Theme" uid="uid://c66wo0qg82ssk" path="res://assets/themes/errror.tres" id="1_lxolw"]
[node name="DialogBox" type="VBoxContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="Background" type="ColorRect" parent="."]
layout_mode = 2
size_flags_vertical = 3
color = Color(0.172549, 0.172549, 0.172549, 0.658824)
[node name="Control" type="Control" parent="Background"]
anchors_preset = 0
offset_right = 40.0
offset_bottom = 40.0
[node name="Box" type="ColorRect" parent="Background/Control"]
layout_mode = 1
offset_left = 133.0
offset_top = 411.0
offset_right = 133.0
offset_bottom = 411.0
color = Color(0, 0, 0, 0.572549)
[node name="Container" type="VBoxContainer" parent="Background/Control/Box"]
layout_mode = 2
offset_right = 934.0
offset_bottom = 178.0
theme_override_constants/separation = 50
[node name="Label" type="Label" parent="Background/Control/Box/Container"]
layout_mode = 2
theme = ExtResource("1_lxolw")
text = "Это время уже забронировано"
[node name="Button" type="Button" parent="Background/Control/Box/Container"]
layout_mode = 2
theme = ExtResource("1_lxolw")
text = "OK"

View File

@ -23,14 +23,28 @@ enum Type {
editable = value
set_editable(value)
@onready var _label = $FieldLabelControl/FieldLabel
@onready var _line = $Line
@onready var _calendar = $Calendar
@onready var _time = $Time
@onready var _text = $Text
@onready var _label = $Container/FieldLabelControl/FieldLabel
@onready var _line = $Container/Value/Line
@onready var _calendar = $Container/Value/Calendar
@onready var _time = $Container/Value/Time
@onready var _text = $Container/Value/Text
var _type
func _process(delta):
_process_fonts()
func _process_fonts():
_process_font_size(_label, 35)
_process_font_size(_line, 35)
_process_font_size(_text, 35)
func _process_font_size(obj, k):
var font_size = obj.get_theme_default_font_size()
var new_font_size = get_viewport_rect().size.y/k
if font_size != new_font_size:
obj.add_theme_font_size_override("font_size", new_font_size)
func _ready():
set_title(title)
set_type(type)

View File

@ -1,8 +1,8 @@
[gd_scene load_steps=6 format=3 uid="uid://d0fdiesiaajlq"]
[ext_resource type="Script" path="res://scenes/common/edit_field.gd" id="1_sdy7t"]
[ext_resource type="Theme" uid="uid://7lcget51crj1" path="res://assets/themes/medium.tres" id="2_haj3x"]
[ext_resource type="Theme" uid="uid://cmhwbyqu6nh38" path="res://assets/themes/big.tres" id="2_iq8f3"]
[ext_resource type="Theme" uid="uid://bbboqrxd230bp" path="res://assets/themes/edit_label_font.tres" id="3_4sp4y"]
[ext_resource type="PackedScene" uid="uid://drdv8adk1wi8f" path="res://scenes/common/calendar/calendar_edit.tscn" id="4_hlx5p"]
[ext_resource type="PackedScene" uid="uid://bhd0xvnvoqslr" path="res://scenes/common/time/time_edit.tscn" id="5_d0drs"]
@ -14,41 +14,55 @@ grow_horizontal = 2
theme_override_constants/separation = 25
script = ExtResource("1_sdy7t")
[node name="FieldLabelControl" type="Control" parent="."]
layout_mode = 2
[node name="FieldLabel" type="Label" parent="FieldLabelControl"]
layout_mode = 2
offset_left = 25.0
offset_top = 4.0
offset_right = 231.0
offset_bottom = 62.0
theme = ExtResource("2_haj3x")
[node name="Indent" type="Label" parent="."]
layout_mode = 2
theme = ExtResource("2_iq8f3")
text = " "
text = " "
[node name="Line" type="LineEdit" parent="."]
[node name="Container" type="VBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
theme = ExtResource("2_iq8f3")
max_length = 32
theme_override_constants/separation = 10
[node name="Text" type="TextEdit" parent="."]
[node name="FieldLabelControl" type="HBoxContainer" parent="Container"]
layout_mode = 2
[node name="Indent" type="Label" parent="Container/FieldLabelControl"]
layout_mode = 2
theme = ExtResource("2_iq8f3")
text = " "
[node name="FieldLabel" type="Label" parent="Container/FieldLabelControl"]
layout_mode = 2
theme = ExtResource("3_4sp4y")
theme_override_font_sizes/font_size = 16
[node name="Value" type="VBoxContainer" parent="Container"]
layout_mode = 2
[node name="Line" type="LineEdit" parent="Container/Value"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme = ExtResource("2_iq8f3")
theme_override_font_sizes/font_size = 16
max_length = 32
selecting_enabled = false
[node name="Text" type="TextEdit" parent="Container/Value"]
visible = false
layout_mode = 2
size_flags_horizontal = 3
theme = ExtResource("2_iq8f3")
theme_override_font_sizes/font_size = 16
wrap_mode = 1
scroll_fit_content_height = true
[node name="Calendar" parent="." instance=ExtResource("4_hlx5p")]
[node name="Calendar" parent="Container/Value" instance=ExtResource("4_hlx5p")]
visible = false
layout_mode = 2
[node name="Time" parent="." instance=ExtResource("5_d0drs")]
[node name="Time" parent="Container/Value" instance=ExtResource("5_d0drs")]
visible = false
layout_mode = 2
size_flags_horizontal = 3

View File

@ -12,6 +12,15 @@ var _hours := 0
var _minutes := 0
var _time_setting_page
func _process(delta):
_process_font_size()
func _process_font_size():
var font_size = _edit.get_theme_default_font_size()
var new_font_size = get_viewport_rect().size.y/35
if font_size != new_font_size:
_edit.add_theme_font_size_override("font_size", new_font_size)
func _ready():
initialize_signals()

View File

@ -14,7 +14,7 @@ script = ExtResource("1_2wxyg")
[node name="TimeEdit" type="LineEdit" parent="."]
visible = false
layout_mode = 2
text = "11:10 "
text = "14:35 "
placeholder_text = "hh:mm (a/p)m"
script = ExtResource("2_7d4ae")
current_time = true

View File

@ -9,6 +9,7 @@ const BgColors = {
"temporarily_free": Color("3a2c00")
}
const MinutesForTemporarilyFree := 15
const StatusCheckPeriod := 5.0
enum Status {
FREE,
@ -18,6 +19,7 @@ enum Status {
@onready var _reservation_repo : AbstractReservationRepo = $Repos/Reservation
@onready var _event_handler : EventHandler = $Repos/EventHandler
@onready var _reservation_service : ReservationService = $Services/ReservationService
@onready var _keyboard = $Keyboard/OnscreenKeyboard
@export var current_page : Pages:
set(value):
@ -33,6 +35,7 @@ enum Status {
@onready var _time_status_indent := $Left/TimeStatusContainer/Indent
@onready var _time_label := $Left/TimeStatusContainer/TimeLabel
@onready var _status_label := $Left/TimeStatusContainer/StatusLabel
@onready var _will_be_available_label := $Left/TimeStatusContainer/WillBeAvailableLabel
@onready var _background := $Background
@onready var _create_reservation_button := $RightBottom/CreateReservationButton
@ -44,6 +47,7 @@ enum Status {
var _current_page := Pages.Board
var _previous_page : Pages
var _status : Status
var _time_after_status_check := 0.0
func _ready():
initialize_signals()
@ -58,9 +62,13 @@ func initialize_signals():
func _process(delta):
_process_time_status_indent()
_process_status(delta)
var time = Time.get_time_dict_from_system()
_process_time(time)
_process_available_time(time)
_process_fonts()
func _process_time_status_indent():
var indent = get_viewport_rect().size.y/3
@ -70,6 +78,24 @@ func _process_time_status_indent():
func _process_time(time):
_time_label.text = "%02d:%02d" % [time.hour, time.minute]
func _process_available_time(time):
var reservation = _reservation_repo.get_current_reservation()
if _status == Status.BUSY and reservation:
var current_time_in_minutes = time.hour*60 + time.minute
var reservation_minutes = reservation.finish_time.hours*60 + reservation.finish_time.minutes
var result_minutes = reservation_minutes - current_time_in_minutes
_will_be_available_label.text = "(осталось %s мин)" % str(result_minutes)
else:
_will_be_available_label.text = ""
func _process_status(delta):
if _time_after_status_check >= StatusCheckPeriod:
_time_after_status_check = 0.0
_update_status()
_time_after_status_check += delta
func _update_status(reservations=null):
var time = Time.get_time_dict_from_system()
var current_time_in_minutes = time.hour*60 + time.minute
@ -101,11 +127,20 @@ func _update_ui_status(status: Status, text: String, color: Color):
_status_label.text = text
_background.color = color
func _process_fonts():
_process_font_size(_create_reservation_button, 35)
func _process_font_size(obj, k):
var font_size = obj.get_theme_default_font_size()
var new_font_size = get_viewport_rect().size.y/k
if font_size != new_font_size:
obj.add_theme_font_size_override("font_size", new_font_size)
func get_current_page():
return _current_page
func load_page(value, with_update=true):
_pages[_current_page].visible = false
var previous_page = _current_page
if _previous_page != _current_page:
if not _current_page == Pages.TimeSetting:
@ -114,10 +149,24 @@ func load_page(value, with_update=true):
_current_page = value
if with_update:
_pages[_current_page].update()
_pages[_current_page].visible = true
hide_keyboard()
_change_page_with_delay(previous_page)
_change_create_reservation_buttion_visibility(_current_page)
return _pages[_current_page]
func _change_page_with_delay(previous_page):
await get_tree().create_timer(0.1, false).timeout
_pages[previous_page].visible = false
_pages[_current_page].visible = true
func _change_create_reservation_buttion_visibility(page: Pages):
if page == Pages.Board:
_create_reservation_button.show()
else:
_create_reservation_button.hide()
func go_to_previous_page(with_update=true):
if _previous_page != null:
load_page(_previous_page, with_update)
@ -188,3 +237,6 @@ func _on_1_hour_button_pressed():
func get_date() -> String:
var date = Time.get_date_dict_from_system()
return "%02d.%02d.%04d" % [date.day, date.month, date.year]
func hide_keyboard():
_keyboard.hide()

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=14 format=3 uid="uid://bkrvh8vjpgqot"]
[gd_scene load_steps=16 format=3 uid="uid://bkrvh8vjpgqot"]
[ext_resource type="Script" path="res://scenes/main/main_tablet.gd" id="1_fr6s5"]
[ext_resource type="PackedScene" uid="uid://c431r28ef5edp" path="res://scenes/board/board.tscn" id="2_n47h4"]
@ -12,6 +12,8 @@
[ext_resource type="Script" path="res://src/infra/repos/backend/reservation_http.gd" id="10_v7sup"]
[ext_resource type="Script" path="res://src/domain/services/reservation.gd" id="11_5xy2x"]
[ext_resource type="Script" path="res://src/infra/repos/backend/event_handler_ws.gd" id="11_de30t"]
[ext_resource type="Script" path="res://addons/onscreenkeyboard/onscreen_keyboard.gd" id="12_mc4hf"]
[ext_resource type="PackedScene" uid="uid://dt03kci5qbkg3" path="res://scenes/common/dialog_box.tscn" id="14_cvh6c"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_uus54"]
bg_color = Color(0.6, 0.6, 0.6, 0)
@ -78,7 +80,7 @@ size_flags_vertical = 3
[node name="TimeLabel" type="Label" parent="Left/TimeStatusContainer"]
layout_mode = 2
theme = ExtResource("5_atujq")
text = "20:38"
text = "15:09"
horizontal_alignment = 1
[node name="StatusLabel" type="Label" parent="Left/TimeStatusContainer"]
@ -87,6 +89,11 @@ theme = ExtResource("5_atujq")
text = "Свободно"
horizontal_alignment = 1
[node name="WillBeAvailableLabel" type="Label" parent="Left/TimeStatusContainer"]
layout_mode = 2
theme = ExtResource("9_wpf8g")
horizontal_alignment = 1
[node name="ReserveNowBox" type="VBoxContainer" parent="Left/TimeStatusContainer"]
layout_mode = 2
theme_override_constants/separation = 20
@ -138,7 +145,8 @@ grow_vertical = 0
[node name="CreateReservationButton" type="Button" parent="RightBottom"]
layout_mode = 2
theme = ExtResource("9_wpf8g")
text = "Забронировать"
theme_override_font_sizes/font_size = 16
text = " Забронировать "
[node name="Repos" type="Node" parent="."]
@ -148,21 +156,34 @@ script = ExtResource("10_v7sup")
[node name="EventHandler" type="Node" parent="Repos"]
script = ExtResource("11_de30t")
[node name="Keyboard" type="Control" parent="."]
visible = false
layout_mode = 2
anchors_preset = 0
offset_left = -24.0
offset_top = -10.0
offset_right = 16.0
offset_bottom = 30.0
[node name="OnscreenKeyboard" type="PanelContainer" parent="Keyboard"]
visible = false
layout_mode = 0
offset_top = 595.0
offset_right = 1182.0
offset_bottom = 1252.0
script = ExtResource("12_mc4hf")
font_size = 72
[node name="Services" type="Node" parent="."]
[node name="ReservationService" type="Node" parent="Services"]
script = ExtResource("11_5xy2x")
[node name="DialogBox" type="VBoxContainer" parent="."]
[node name="Control" type="Control" parent="."]
layout_mode = 2
anchors_preset = 0
offset_right = 1152.0
[node name="DialogBox" parent="." instance=ExtResource("14_cvh6c")]
visible = false
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -20.0
offset_top = -20.0
offset_right = 20.0
offset_bottom = 20.0
grow_horizontal = 2
grow_vertical = 2

View File

@ -28,11 +28,15 @@ grow_horizontal = 2
theme_override_constants/separation = 35
script = ExtResource("1_prqr3")
[node name="Indent" type="BoxContainer" parent="."]
layout_mode = 2
[node name="TopBar" type="HBoxContainer" parent="."]
layout_mode = 2
theme_override_constants/separation = 50
[node name="BackButton" type="Button" parent="TopBar"]
visible = false
custom_minimum_size = Vector2(0, 100)
layout_mode = 2
theme = ExtResource("1_j1bkw")
@ -49,6 +53,7 @@ text = "Бронирование комнаты"
horizontal_alignment = 1
[node name="ApplyButton" type="Button" parent="TopBar"]
visible = false
custom_minimum_size = Vector2(0, 100)
layout_mode = 2
theme = ExtResource("1_j1bkw")
@ -86,7 +91,6 @@ title = "Конец"
type = 3
[node name="Splitter3" type="Panel" parent="."]
visible = false
custom_minimum_size = Vector2(0, 5)
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_yafv8")
@ -98,7 +102,6 @@ title = "Создатель"
editable = false
[node name="RoomField" parent="." instance=ExtResource("2_qfs8j")]
visible = false
layout_mode = 2
title = "Комната"
editable = false
@ -114,7 +117,6 @@ visible = false
layout_mode = 2
title = "Описание"
type = 1
editable = false
[node name="Splitter5" type="Panel" parent="."]
visible = false
@ -126,12 +128,60 @@ theme_override_styles/panel = SubResource("StyleBoxFlat_r7q2r")
layout_mode = 2
script = ExtResource("5_bbsyh")
[node name="BottomBar" type="BoxContainer" parent="."]
[node name="BottomBar" type="Control" parent="."]
custom_minimum_size = Vector2(1150, 0)
layout_mode = 2
size_flags_vertical = 10
[node name="Left" type="HBoxContainer" parent="BottomBar"]
layout_mode = 1
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_top = -100.0
offset_right = 401.0
grow_vertical = 0
theme_override_constants/separation = 50
[node name="BackButton" type="Button" parent="BottomBar/Left"]
custom_minimum_size = Vector2(0, 100)
layout_mode = 2
theme = ExtResource("1_j1bkw")
text = " Назад "
[node name="DeleteButton" type="Button" parent="BottomBar/Left"]
visible = false
custom_minimum_size = Vector2(0, 100)
layout_mode = 2
theme = ExtResource("1_j1bkw")
text = " Удалить "
alignment = 2
[node name="Right" type="HBoxContainer" parent="BottomBar"]
layout_mode = 1
anchors_preset = 3
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -371.0
offset_top = -100.0
grow_horizontal = 0
grow_vertical = 0
alignment = 2
[node name="ApplyButton" type="Button" parent="BottomBar/Right"]
custom_minimum_size = Vector2(0, 100)
layout_mode = 2
theme = ExtResource("1_j1bkw")
text = " Забронировать "
[node name="BottomBarOld" type="BoxContainer" parent="."]
visible = false
layout_mode = 2
alignment = 2
[node name="DeleteButton" type="Button" parent="BottomBar"]
[node name="DeleteButton" type="Button" parent="BottomBarOld"]
custom_minimum_size = Vector2(0, 100)
layout_mode = 2
theme = ExtResource("1_j1bkw")

View File

@ -30,6 +30,7 @@ layout_mode = 2
theme_override_constants/separation = 50
[node name="BackButton" type="Button" parent="TopBar"]
visible = false
custom_minimum_size = Vector2(0, 100)
layout_mode = 2
theme = ExtResource("2_57fpn")
@ -42,9 +43,11 @@ theme = ExtResource("2_57fpn")
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
theme_override_constants/shadow_offset_x = 3
theme_override_constants/shadow_offset_y = 3
text = "Изменение бронирования"
horizontal_alignment = 1
[node name="ApplyButton" type="Button" parent="TopBar"]
visible = false
custom_minimum_size = Vector2(0, 100)
layout_mode = 2
theme = ExtResource("2_57fpn")
@ -121,11 +124,59 @@ theme_override_styles/panel = SubResource("StyleBoxFlat_yafv8")
layout_mode = 2
script = ExtResource("5_xmu5c")
[node name="BottomBar" type="BoxContainer" parent="."]
[node name="BottomBar" type="Control" parent="."]
custom_minimum_size = Vector2(1150, 0)
layout_mode = 2
size_flags_vertical = 10
[node name="Left" type="HBoxContainer" parent="BottomBar"]
layout_mode = 1
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_top = -100.0
offset_right = 401.0
grow_vertical = 0
theme_override_constants/separation = 50
[node name="BackButton" type="Button" parent="BottomBar/Left"]
custom_minimum_size = Vector2(0, 100)
layout_mode = 2
theme = ExtResource("2_57fpn")
text = " Назад "
[node name="DeleteButton" type="Button" parent="BottomBar/Left"]
custom_minimum_size = Vector2(0, 100)
layout_mode = 2
theme = ExtResource("2_57fpn")
text = " Удалить "
alignment = 2
[node name="Right" type="HBoxContainer" parent="BottomBar"]
layout_mode = 1
anchors_preset = 3
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -371.0
offset_top = -100.0
grow_horizontal = 0
grow_vertical = 0
alignment = 2
[node name="ApplyButton" type="Button" parent="BottomBar/Right"]
custom_minimum_size = Vector2(0, 100)
layout_mode = 2
theme = ExtResource("2_57fpn")
text = " Изменить "
[node name="BottomBarOld" type="BoxContainer" parent="."]
visible = false
layout_mode = 2
alignment = 2
[node name="DeleteButton" type="Button" parent="BottomBar"]
[node name="DeleteButton" type="Button" parent="BottomBarOld"]
custom_minimum_size = Vector2(0, 100)
layout_mode = 2
theme = ExtResource("2_57fpn")

View File

@ -11,9 +11,10 @@ enum Types {
type = new_type
@onready var _main: Main = get_tree().get_current_scene()
@onready var _back_button := $TopBar/BackButton
@onready var _apply_button := $TopBar/ApplyButton
@onready var _delete_button := $BottomBar/DeleteButton
@onready var _title := $TopBar/Label
@onready var _back_button := $BottomBar/Left/BackButton
@onready var _apply_button := $BottomBar/Right/ApplyButton
@onready var _delete_button := $BottomBar/Left/DeleteButton
@onready var _error_box := $ErrorBox
@onready var _title_field := $TitleField
@ -24,6 +25,29 @@ enum Types {
@onready var _room_field := $RoomField
@onready var _description_field := $DescriptionField
func _process(delta):
_process_fonts()
_process_separation()
func _process_fonts():
_process_font_size(_title, 35)
_process_font_size(_back_button, 35)
_process_font_size(_apply_button, 35)
_process_font_size(_delete_button, 35)
_process_font_size(_error_box, 35)
func _process_font_size(obj, k):
var font_size = obj.get_theme_default_font_size()
var new_font_size = get_viewport_rect().size.y/k
if font_size != new_font_size:
obj.add_theme_font_size_override("font_size", new_font_size)
func _process_separation():
var separation = get_theme_constant("separation")
var new_separation = get_viewport_rect().size.y/28
if separation != new_separation:
add_theme_constant_override("separation", new_separation)
func _ready():
initialize_signals()
_error_box.set_message(String())
@ -50,7 +74,7 @@ func _on_delete_button_pressed():
func _load_info():
var repo = _main.get_reservation_repo()
var reservation_id = repo.get_current_reservation_id()
var reservation_id = repo.get_selected_reservation_id()
var reservation = await repo.get_reservation(reservation_id)
_title_field.set_value(reservation.title)
@ -94,9 +118,9 @@ func _update_reservation():
dto.color = randi_range(1, 3)
var repo = _main.get_reservation_repo()
var reservation_id = repo.get_current_reservation_id()
var reservation_id = repo.get_selected_reservation_id()
repo.change_reservation(reservation_id, dto)
repo.set_current_reservation_id(null)
repo.set_selected_reservation_id(null)
dto.queue_free()
@ -105,7 +129,7 @@ func _update_reservation():
func _delete_reservation():
var repo = _main.get_reservation_repo()
var reservation_id = repo.get_current_reservation_id()
var reservation_id = repo.get_selected_reservation_id()
repo.cancel_reservation(reservation_id)
_main.load_page(Main.Pages.Board)

View File

@ -6,10 +6,10 @@ class_name ReservationService
func is_time_busy(new_start_time_minutes, new_finish_time_minutes):
var repo = _main.get_reservation_repo()
var reservations = await repo.list_reservations({"date": _main.get_date()})
var current_reservation_id = repo.get_current_reservation_id()
var selected_reservation_id = repo.get_selected_reservation_id()
for reservation in reservations:
if reservation.id == current_reservation_id:
if reservation.id == selected_reservation_id:
continue
var other_start_time = reservation.start_time

View File

@ -5,7 +5,8 @@ class_name AbstractReservationRepo
signal connected
signal not_connected
var _current_reservation_id = null
var _selected_reservation_id = null
var _current_reservation = null
func create_reservation(dto: CreateReservationDTO):
pass
@ -22,8 +23,11 @@ func get_reservation(reservation_id):
func list_reservations(filters: Dictionary = {}):
pass
func set_current_reservation_id(value):
_current_reservation_id = value
func set_selected_reservation_id(value):
_selected_reservation_id = value
func get_current_reservation_id():
return _current_reservation_id
func get_selected_reservation_id():
return _selected_reservation_id
func get_current_reservation():
return _current_reservation

View File

@ -1,7 +1,7 @@
class_name EventHandlerWS
extends EventHandler
const BACKEND_URL = "http://127.0.0.1:5000/socket.io"
const BACKEND_URL = "http://192.168.1.178:5000/socket.io"
var _room_id: String = ""
var _jwt_token: String = ""
var _client: SocketIOClient
@ -40,10 +40,13 @@ func _on_socket_connect(_payload, _namespace, error: bool):
print("Socket.IO connected")
subscribe_to_room(_room_id)
func _on_socket_disconnect():
func _on_socket_disconnect(name_space: String):
print("Disconnected from server")
disconnected.emit()
print("Reconnecting...")
_ready()
func _on_socket_event(event: String, payload: Variant, _namespace):
print(event, payload)
match event:
@ -60,6 +63,7 @@ func subscribe_to_room(room_id: String):
)
func disconnect_from_server():
print("Disconnected from server")
_client.socketio_disconnect()
func _exit_tree():

View File

@ -2,14 +2,13 @@
extends AbstractReservationRepo
class_name ReservationRepoHTTP
const BASE_URL = "http://127.0.0.1:5000"
const BASE_URL = "http://192.168.1.178:5000"
const RESERVATION_ENDPOINT = "/reservation/"
const HEALTH_ENDPOINT = "/health/"
signal request_failed(error_message: String)
var _jwt_token: String = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTc0MDA0MDQzNywianRpIjoiOTM4NTUyMjMtMjhiNC00OWVhLWI3ZjUtZmYxMTg4YzI1Mjg2IiwidHlwZSI6ImFjY2VzcyIsInN1YiI6InRlc3RfdXNlciIsIm5iZiI6MTc0MDA0MDQzNywiY3NyZiI6ImMwYzI2MTU0LTdkNjItNGYyZi04ZjBhLWI0MjA0ODJlMmEzZCIsImV4cCI6MTc0MDA0MTMzN30.esPRTXNxrtziuOc2dUsc9XqbedErjcyvPlL3aUZIaaA"
var _timed_out := false
func _ready():
@ -52,13 +51,31 @@ func list_reservations(filters: Dictionary = {}) -> Array:
var endpoint = RESERVATION_ENDPOINT + query
var result = await _make_request(endpoint, HTTPClient.METHOD_GET)
return _parse_reservation_list(result) if result is Array else []
var reservations = _parse_reservation_list(result) if result is Array else []
func set_current_reservation_id(value):
_current_reservation_id = value
_set_current_reservation_id_from_reservations(reservations)
return reservations
func get_current_reservation_id():
return _current_reservation_id
func _set_current_reservation_id_from_reservations(reservations):
var time = Time.get_time_dict_from_system()
var current_time_in_minutes = time.hour*60 + time.minute
for reservation in reservations:
var start_time_in_minutes = reservation.start_time.hours*60 + reservation.start_time.minutes
var finish_time_in_minutes = reservation.finish_time.hours*60 + reservation.finish_time.minutes
if current_time_in_minutes >= start_time_in_minutes \
and current_time_in_minutes < finish_time_in_minutes:
_current_reservation = reservation
return
_current_reservation = null
func set_selected_reservation_id(value):
_selected_reservation_id = value
func get_selected_reservation_id():
return _selected_reservation_id
func set_jwt_token(token: String):
_jwt_token = token

View File

@ -230,6 +230,7 @@ func socketio_connect(name_space: String="/"):
# disconnect from socket.io server by namespace
func socketio_disconnect(name_space: String="/"):
print("FUCK")
if _connection_state == ConnectionState.CONNECTED:
# We should ONLY send disconnect packet when we're connected
_socketio_send_packet(SocketIOPacketType.DISCONNECT, name_space)