commit bbc2720052a164b28c68614589f70628ac25dc0d Author: JustTestingV Date: Tue Jul 30 15:19:19 2024 +0300 raw commit diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..bf949a5 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,113 @@ +[package] +name = "lavina-web-client" +version = "0.1.0" +edition = "2021" + +[build] +rustflags = ["--cfg", "tokio_unstable"] + + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +axum = { version = "0.7.5", optional = true } +console_error_panic_hook = "0.1.7" +leptos = { version = "0.6.12", features = ["nightly"] } +leptos_axum = { version = "0.6.12", optional = true } +leptos_meta = { version = "0.6.12", features = ["nightly"] } +leptos_router = { version = "0.6.12", features = ["nightly"] } +tokio = { version = "1.38.0", features = ["full"], optional = true } +tower = { version = "0.4.13", optional = true } +tower-http = { version = "0.5.2", features = ["fs"], optional = true } +wasm-bindgen = "=0.2.92" +thiserror = "1.0.62" +tracing = { version = "0.1.40", optional = true } +http = "1.1.0" +rand = "0.8.5" +reqwest= { version = "0.12.5", features = ["json"] } +#uuid = { version = "1.10.0", features = ["v4"] } + +[features] +hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"] +ssr = [ + "dep:axum", + "dep:tokio", + "dep:tower", + "dep:tower-http", + "dep:leptos_axum", + "leptos/ssr", + "leptos_meta/ssr", + "leptos_router/ssr", + "dep:tracing", +] + +# Defines a size-optimized profile for the WASM bundle in release mode +[profile.wasm-release] +inherits = "release" +opt-level = 'z' +lto = true +codegen-units = 1 +panic = "abort" + +[package.metadata.leptos] +# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name +output-name = "lavina-web-client-output" + +# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup. +site-root = "target/site" + +# The site-root relative folder where all compiled output (JS, WASM and CSS) is written +# Defaults to pkg +site-pkg-dir = "pkg" + +# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to //app.css +style-file = "style/main.scss" +# Assets source dir. All files found here will be copied and synchronized to site-root. +# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir. +# +# Optional. Env: LEPTOS_ASSETS_DIR. +assets-dir = "public" + +# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup. +site-addr = "127.0.0.1:3000" + +# The port to use for automatic reload monitoring +reload-port = 3001 + +# [Optional] Command to use when running end2end tests. It will run in the end2end dir. +# [Windows] for non-WSL use "npx.cmd playwright test" +# This binary name can be checked in Powershell with Get-Command npx +end2end-cmd = "npx playwright test" +end2end-dir = "end2end" + +# The browserlist query used for optimizing the CSS. +browserquery = "defaults" + +# The environment Leptos will run in, usually either "DEV" or "PROD" +env = "DEV" + +# The features to use when compiling the bin target +# +# Optional. Can be over-ridden with the command line parameter --bin-features +bin-features = ["ssr"] + +# If the --no-default-features flag should be used when compiling the bin target +# +# Optional. Defaults to false. +bin-default-features = false + +# The features to use when compiling the lib target +# +# Optional. Can be over-ridden with the command line parameter --lib-features +lib-features = ["hydrate"] + +# If the --no-default-features flag should be used when compiling the lib target +# +# Optional. Defaults to false. +lib-default-features = false + +# The profile to use for the lib target when compiling for release +# +# Optional. Defaults to "release". +lib-profile-release = "wasm-release" diff --git a/README.md b/README.md new file mode 100644 index 0000000..96a298c --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +```bash +rustup toolchain install nightly --allow-downgrade +rustup target add wasm32-unknown-unknown +``` +```bash +cargo install cargo-leptos --locked +``` + +legacy +```bash +cargo install trunk +trunk serve --open --port 6942 +``` + +```bash +cargo leptos watch +``` + +```bash +cargo clean +cargo leptos watch +``` \ No newline at end of file diff --git a/dist/index.html b/dist/index.html new file mode 100644 index 0000000..2dfc801 --- /dev/null +++ b/dist/index.html @@ -0,0 +1,291 @@ + + +Lavina + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dist/index.sync-conflict-20240714-150407-AI463K6.html b/dist/index.sync-conflict-20240714-150407-AI463K6.html new file mode 100644 index 0000000..e69de29 diff --git a/dist/lavina-web-client-68efb220d13c8c8b.js b/dist/lavina-web-client-68efb220d13c8c8b.js new file mode 100644 index 0000000..e69de29 diff --git a/dist/lavina-web-client-68efb220d13c8c8b_bg.wasm b/dist/lavina-web-client-68efb220d13c8c8b_bg.wasm new file mode 100644 index 0000000..e69de29 diff --git a/dist/lavina-web-client-e4f734c64a3621ba.js b/dist/lavina-web-client-e4f734c64a3621ba.js new file mode 100644 index 0000000..7a84ab8 --- /dev/null +++ b/dist/lavina-web-client-e4f734c64a3621ba.js @@ -0,0 +1,912 @@ +let wasm; + +const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } ); + +if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }; + +let cachedUint8Memory0 = null; + +function getUint8Memory0() { + if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) { + cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8Memory0; +} + +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); +} + +const heap = new Array(128).fill(undefined); + +heap.push(undefined, null, true, false); + +let heap_next = heap.length; + +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + +function getObject(idx) { return heap[idx]; } + +function dropObject(idx) { + if (idx < 132) return; + heap[idx] = heap_next; + heap_next = idx; +} + +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} + +function debugString(val) { + // primitive types + const type = typeof val; + if (type == 'number' || type == 'boolean' || val == null) { + return `${val}`; + } + if (type == 'string') { + return `"${val}"`; + } + if (type == 'symbol') { + const description = val.description; + if (description == null) { + return 'Symbol'; + } else { + return `Symbol(${description})`; + } + } + if (type == 'function') { + const name = val.name; + if (typeof name == 'string' && name.length > 0) { + return `Function(${name})`; + } else { + return 'Function'; + } + } + // objects + if (Array.isArray(val)) { + const length = val.length; + let debug = '['; + if (length > 0) { + debug += debugString(val[0]); + } + for(let i = 1; i < length; i++) { + debug += ', ' + debugString(val[i]); + } + debug += ']'; + return debug; + } + // Test for built-in + const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); + let className; + if (builtInMatches.length > 1) { + className = builtInMatches[1]; + } else { + // Failed to match the standard '[object ClassName]' + return toString.call(val); + } + if (className == 'Object') { + // we're a user defined class or Object + // JSON.stringify avoids problems with cycles, and is generally much + // easier than looping through ownProperties of `val`. + try { + return 'Object(' + JSON.stringify(val) + ')'; + } catch (_) { + return 'Object'; + } + } + // errors + if (val instanceof Error) { + return `${val.name}: ${val.message}\n${val.stack}`; + } + // TODO we could test for more things here, like `Set`s and `Map`s. + return className; +} + +let WASM_VECTOR_LEN = 0; + +const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } ); + +const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' + ? function (arg, view) { + return cachedTextEncoder.encodeInto(arg, view); +} + : function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; +}); + +function passStringToWasm0(arg, malloc, realloc) { + + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8Memory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; + const view = getUint8Memory0().subarray(ptr + offset, ptr + len); + const ret = encodeString(arg, view); + + offset += ret.written; + ptr = realloc(ptr, len, offset, 1) >>> 0; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} + +let cachedInt32Memory0 = null; + +function getInt32Memory0() { + if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) { + cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); + } + return cachedInt32Memory0; +} + +const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(state => { + wasm.__wbindgen_export_2.get(state.dtor)(state.a, state.b) +}); + +function makeMutClosure(arg0, arg1, dtor, f) { + const state = { a: arg0, b: arg1, cnt: 1, dtor }; + const real = (...args) => { + // First up with a closure we increment the internal reference + // count. This ensures that the Rust closure environment won't + // be deallocated while we're invoking it. + state.cnt++; + const a = state.a; + state.a = 0; + try { + return f(a, state.b, ...args); + } finally { + if (--state.cnt === 0) { + wasm.__wbindgen_export_2.get(state.dtor)(a, state.b); + CLOSURE_DTORS.unregister(state); + } else { + state.a = a; + } + } + }; + real.original = state; + CLOSURE_DTORS.register(real, state, state); + return real; +} +function __wbg_adapter_32(arg0, arg1, arg2) { + wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h6b0b8b5a1ea02d7d(arg0, arg1, addHeapObject(arg2)); +} + +function __wbg_adapter_35(arg0, arg1, arg2) { + wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hb2778074fcfd92d7(arg0, arg1, addHeapObject(arg2)); +} + +function __wbg_adapter_38(arg0, arg1, arg2) { + wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h488c718ca210acee(arg0, arg1, addHeapObject(arg2)); +} + +function __wbg_adapter_41(arg0, arg1, arg2) { + wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h09458c03ea3fc232(arg0, arg1, addHeapObject(arg2)); +} + +function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + wasm.__wbindgen_exn_store(addHeapObject(e)); + } +} + +function getCachedStringFromWasm0(ptr, len) { + if (ptr === 0) { + return getObject(len); + } else { + return getStringFromWasm0(ptr, len); + } +} + +function isLikeNone(x) { + return x === undefined || x === null; +} +function __wbg_adapter_174(arg0, arg1, arg2, arg3) { + wasm.wasm_bindgen__convert__closures__invoke2_mut__hb9001d83105a6412(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3)); +} + +const IntoUnderlyingByteSourceFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingbytesource_free(ptr >>> 0)); +/** +*/ +export class IntoUnderlyingByteSource { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + IntoUnderlyingByteSourceFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_intounderlyingbytesource_free(ptr); + } + /** + * @returns {string} + */ + get type() { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.intounderlyingbytesource_type(retptr, this.__wbg_ptr); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var v1 = getCachedStringFromWasm0(r0, r1); + if (r0 !== 0) { wasm.__wbindgen_free(r0, r1, 1); } + return v1; + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} +/** +* @returns {number} +*/ +get autoAllocateChunkSize() { + const ret = wasm.intounderlyingbytesource_autoAllocateChunkSize(this.__wbg_ptr); + return ret >>> 0; +} +/** +* @param {ReadableByteStreamController} controller +*/ +start(controller) { + wasm.intounderlyingbytesource_start(this.__wbg_ptr, addHeapObject(controller)); +} +/** +* @param {ReadableByteStreamController} controller +* @returns {Promise} +*/ +pull(controller) { + const ret = wasm.intounderlyingbytesource_pull(this.__wbg_ptr, addHeapObject(controller)); + return takeObject(ret); +} +/** +*/ +cancel() { + const ptr = this.__destroy_into_raw(); + wasm.intounderlyingbytesource_cancel(ptr); +} +} + +const IntoUnderlyingSinkFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingsink_free(ptr >>> 0)); +/** +*/ +export class IntoUnderlyingSink { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + IntoUnderlyingSinkFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_intounderlyingsink_free(ptr); + } + /** + * @param {any} chunk + * @returns {Promise} + */ + write(chunk) { + const ret = wasm.intounderlyingsink_write(this.__wbg_ptr, addHeapObject(chunk)); + return takeObject(ret); + } + /** + * @returns {Promise} + */ + close() { + const ptr = this.__destroy_into_raw(); + const ret = wasm.intounderlyingsink_close(ptr); + return takeObject(ret); + } + /** + * @param {any} reason + * @returns {Promise} + */ + abort(reason) { + const ptr = this.__destroy_into_raw(); + const ret = wasm.intounderlyingsink_abort(ptr, addHeapObject(reason)); + return takeObject(ret); + } +} + +const IntoUnderlyingSourceFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_intounderlyingsource_free(ptr >>> 0)); +/** +*/ +export class IntoUnderlyingSource { + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + IntoUnderlyingSourceFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_intounderlyingsource_free(ptr); + } + /** + * @param {ReadableStreamDefaultController} controller + * @returns {Promise} + */ + pull(controller) { + const ret = wasm.intounderlyingsource_pull(this.__wbg_ptr, addHeapObject(controller)); + return takeObject(ret); + } + /** + */ + cancel() { + const ptr = this.__destroy_into_raw(); + wasm.intounderlyingsource_cancel(ptr); + } +} + +async function __wbg_load(module, imports) { + if (typeof Response === 'function' && module instanceof Response) { + if (typeof WebAssembly.instantiateStreaming === 'function') { + try { + return await WebAssembly.instantiateStreaming(module, imports); + + } catch (e) { + if (module.headers.get('Content-Type') != 'application/wasm') { + console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); + + } else { + throw e; + } + } + } + + const bytes = await module.arrayBuffer(); + return await WebAssembly.instantiate(bytes, imports); + + } else { + const instance = await WebAssembly.instantiate(module, imports); + + if (instance instanceof WebAssembly.Instance) { + return { instance, module }; + + } else { + return instance; + } + } +} + +function __wbg_get_imports() { + const imports = {}; + imports.wbg = {}; + imports.wbg.__wbindgen_string_new = function(arg0, arg1) { + const ret = getStringFromWasm0(arg0, arg1); + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_object_clone_ref = function(arg0) { + const ret = getObject(arg0); + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_is_object = function(arg0) { + const val = getObject(arg0); + const ret = typeof(val) === 'object' && val !== null; + return ret; + }; + imports.wbg.__wbindgen_is_string = function(arg0) { + const ret = typeof(getObject(arg0)) === 'string'; + return ret; + }; + imports.wbg.__wbg_crypto_d05b68a3572bb8ca = function(arg0) { + const ret = getObject(arg0).crypto; + return addHeapObject(ret); + }; + imports.wbg.__wbg_msCrypto_10fc94afee92bd76 = function(arg0) { + const ret = getObject(arg0).msCrypto; + return addHeapObject(ret); + }; + imports.wbg.__wbg_getRandomValues_7e42b4fb8779dc6d = function() { return handleError(function (arg0, arg1) { + getObject(arg0).getRandomValues(getObject(arg1)); + }, arguments) }; + imports.wbg.__wbg_randomFillSync_b70ccbdf4926a99d = function() { return handleError(function (arg0, arg1) { + getObject(arg0).randomFillSync(takeObject(arg1)); + }, arguments) }; + imports.wbg.__wbg_require_9a7e0f667ead4995 = function() { return handleError(function () { + const ret = module.require; + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_process_b02b3570280d0366 = function(arg0) { + const ret = getObject(arg0).process; + return addHeapObject(ret); + }; + imports.wbg.__wbg_versions_c1cb42213cedf0f5 = function(arg0) { + const ret = getObject(arg0).versions; + return addHeapObject(ret); + }; + imports.wbg.__wbg_node_43b1089f407e4ec2 = function(arg0) { + const ret = getObject(arg0).node; + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_is_undefined = function(arg0) { + const ret = getObject(arg0) === undefined; + return ret; + }; + imports.wbg.__wbindgen_is_null = function(arg0) { + const ret = getObject(arg0) === null; + return ret; + }; + imports.wbg.__wbindgen_jsval_eq = function(arg0, arg1) { + const ret = getObject(arg0) === getObject(arg1); + return ret; + }; + imports.wbg.__wbindgen_is_falsy = function(arg0) { + const ret = !getObject(arg0); + return ret; + }; + imports.wbg.__wbindgen_cb_drop = function(arg0) { + const obj = takeObject(arg0).original; + if (obj.cnt-- == 1) { + obj.a = 0; + return true; + } + const ret = false; + return ret; + }; + imports.wbg.__wbg_instanceof_Window_f401953a2cf86220 = function(arg0) { + let result; + try { + result = getObject(arg0) instanceof Window; + } catch (_) { + result = false; + } + const ret = result; + return ret; + }; + imports.wbg.__wbg_document_5100775d18896c16 = function(arg0) { + const ret = getObject(arg0).document; + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_body_edb1908d3ceff3a1 = function(arg0) { + const ret = getObject(arg0).body; + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_createComment_354ccab4fdc521ee = function(arg0, arg1, arg2) { + var v0 = getCachedStringFromWasm0(arg1, arg2); + const ret = getObject(arg0).createComment(v0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_createDocumentFragment_8c86903bbb0a3c3c = function(arg0) { + const ret = getObject(arg0).createDocumentFragment(); + return addHeapObject(ret); + }; + imports.wbg.__wbg_createElement_8bae7856a4bb7411 = function() { return handleError(function (arg0, arg1, arg2) { + var v0 = getCachedStringFromWasm0(arg1, arg2); + const ret = getObject(arg0).createElement(v0); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_createTextNode_0c38fd80a5b2284d = function(arg0, arg1, arg2) { + var v0 = getCachedStringFromWasm0(arg1, arg2); + const ret = getObject(arg0).createTextNode(v0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_namespaceURI_5235ee79fd5f6781 = function(arg0, arg1) { + const ret = getObject(arg1).namespaceURI; + var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbg_setinnerHTML_26d69b59e1af99c7 = function(arg0, arg1, arg2) { + var v0 = getCachedStringFromWasm0(arg1, arg2); + getObject(arg0).innerHTML = v0; + }; + imports.wbg.__wbg_outerHTML_e073aa84e7bc1eaf = function(arg0, arg1) { + const ret = getObject(arg1).outerHTML; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbg_removeAttribute_1b10a06ae98ebbd1 = function() { return handleError(function (arg0, arg1, arg2) { + var v0 = getCachedStringFromWasm0(arg1, arg2); + getObject(arg0).removeAttribute(v0); + }, arguments) }; + imports.wbg.__wbg_setAttribute_3c9f6c303b696daa = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { + var v0 = getCachedStringFromWasm0(arg1, arg2); + var v1 = getCachedStringFromWasm0(arg3, arg4); + getObject(arg0).setAttribute(v0, v1); + }, arguments) }; + imports.wbg.__wbg_before_210596e44d88649f = function() { return handleError(function (arg0, arg1) { + getObject(arg0).before(getObject(arg1)); + }, arguments) }; + imports.wbg.__wbg_remove_49b0a5925a04b955 = function(arg0) { + getObject(arg0).remove(); + }; + imports.wbg.__wbg_append_a85eed95dd318e79 = function() { return handleError(function (arg0, arg1, arg2) { + getObject(arg0).append(getObject(arg1), getObject(arg2)); + }, arguments) }; + imports.wbg.__wbg_close_a994f9425dab445c = function() { return handleError(function (arg0) { + getObject(arg0).close(); + }, arguments) }; + imports.wbg.__wbg_enqueue_ea194723156c0cc2 = function() { return handleError(function (arg0, arg1) { + getObject(arg0).enqueue(getObject(arg1)); + }, arguments) }; + imports.wbg.__wbg_append_7ba9d5c2eb183eea = function() { return handleError(function (arg0, arg1) { + getObject(arg0).append(getObject(arg1)); + }, arguments) }; + imports.wbg.__wbg_append_f7fa3534fc158323 = function() { return handleError(function (arg0, arg1, arg2) { + getObject(arg0).append(getObject(arg1), getObject(arg2)); + }, arguments) }; + imports.wbg.__wbg_length_d0a802565d17eec4 = function(arg0) { + const ret = getObject(arg0).length; + return ret; + }; + imports.wbg.__wbg_target_2fc177e386c8b7b0 = function(arg0) { + const ret = getObject(arg0).target; + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_cancelBubble_c0aa3172524eb03c = function(arg0) { + const ret = getObject(arg0).cancelBubble; + return ret; + }; + imports.wbg.__wbg_composedPath_58473fd5ae55f2cd = function(arg0) { + const ret = getObject(arg0).composedPath(); + return addHeapObject(ret); + }; + imports.wbg.__wbg_key_dccf9e8aa1315a8e = function(arg0, arg1) { + const ret = getObject(arg1).key; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbg_byobRequest_72fca99f9c32c193 = function(arg0) { + const ret = getObject(arg0).byobRequest; + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_close_184931724d961ccc = function() { return handleError(function (arg0) { + getObject(arg0).close(); + }, arguments) }; + imports.wbg.__wbg_value_47fe6384562f52ab = function(arg0, arg1) { + const ret = getObject(arg1).value; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbg_parentNode_6be3abff20e1a5fb = function(arg0) { + const ret = getObject(arg0).parentNode; + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_childNodes_118168e8b23bcb9b = function(arg0) { + const ret = getObject(arg0).childNodes; + return addHeapObject(ret); + }; + imports.wbg.__wbg_previousSibling_9708a091a3e6e03b = function(arg0) { + const ret = getObject(arg0).previousSibling; + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_nextSibling_709614fdb0fb7a66 = function(arg0) { + const ret = getObject(arg0).nextSibling; + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_settextContent_d271bab459cbb1ba = function(arg0, arg1, arg2) { + var v0 = getCachedStringFromWasm0(arg1, arg2); + getObject(arg0).textContent = v0; + }; + imports.wbg.__wbg_appendChild_580ccb11a660db68 = function() { return handleError(function (arg0, arg1) { + const ret = getObject(arg0).appendChild(getObject(arg1)); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_cloneNode_e19c313ea20d5d1d = function() { return handleError(function (arg0) { + const ret = getObject(arg0).cloneNode(); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_new_c7aa03c061e95bde = function() { return handleError(function () { + const ret = new Range(); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_deleteContents_1b5a33e17bc6400f = function() { return handleError(function (arg0) { + getObject(arg0).deleteContents(); + }, arguments) }; + imports.wbg.__wbg_setEndBefore_6d219390ff50f205 = function() { return handleError(function (arg0, arg1) { + getObject(arg0).setEndBefore(getObject(arg1)); + }, arguments) }; + imports.wbg.__wbg_setStartAfter_01abb64ddc4334f8 = function() { return handleError(function (arg0, arg1) { + getObject(arg0).setStartAfter(getObject(arg1)); + }, arguments) }; + imports.wbg.__wbg_view_7f0ce470793a340f = function(arg0) { + const ret = getObject(arg0).view; + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_respond_b1a43b2e3a06d525 = function() { return handleError(function (arg0, arg1) { + getObject(arg0).respond(arg1 >>> 0); + }, arguments) }; + imports.wbg.__wbg_warn_63bbae1730aead09 = function(arg0) { + console.warn(getObject(arg0)); + }; + imports.wbg.__wbg_addEventListener_53b787075bd5e003 = function() { return handleError(function (arg0, arg1, arg2, arg3) { + var v0 = getCachedStringFromWasm0(arg1, arg2); + getObject(arg0).addEventListener(v0, getObject(arg3)); + }, arguments) }; + imports.wbg.__wbg_addEventListener_4283b15b4f039eb5 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { + var v0 = getCachedStringFromWasm0(arg1, arg2); + getObject(arg0).addEventListener(v0, getObject(arg3), getObject(arg4)); + }, arguments) }; + imports.wbg.__wbg_instanceof_ShadowRoot_9db040264422e84a = function(arg0) { + let result; + try { + result = getObject(arg0) instanceof ShadowRoot; + } catch (_) { + result = false; + } + const ret = result; + return ret; + }; + imports.wbg.__wbg_host_c667c7623404d6bf = function(arg0) { + const ret = getObject(arg0).host; + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_is_function = function(arg0) { + const ret = typeof(getObject(arg0)) === 'function'; + return ret; + }; + imports.wbg.__wbg_queueMicrotask_481971b0d87f3dd4 = function(arg0) { + queueMicrotask(getObject(arg0)); + }; + imports.wbg.__wbg_queueMicrotask_3cbae2ec6b6cd3d6 = function(arg0) { + const ret = getObject(arg0).queueMicrotask; + return addHeapObject(ret); + }; + imports.wbg.__wbg_get_bd8e338fbd5f5cc8 = function(arg0, arg1) { + const ret = getObject(arg0)[arg1 >>> 0]; + return addHeapObject(ret); + }; + imports.wbg.__wbg_new_28c511d9baebfa89 = function(arg0, arg1) { + var v0 = getCachedStringFromWasm0(arg0, arg1); + const ret = new Error(v0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_newnoargs_e258087cd0daa0ea = function(arg0, arg1) { + var v0 = getCachedStringFromWasm0(arg0, arg1); + const ret = new Function(v0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_call_27c0f87801dedf93 = function() { return handleError(function (arg0, arg1) { + const ret = getObject(arg0).call(getObject(arg1)); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_call_b3ca7c6051f9bec1 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = getObject(arg0).call(getObject(arg1), getObject(arg2)); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_is_010fdc0f4ab96916 = function(arg0, arg1) { + const ret = Object.is(getObject(arg0), getObject(arg1)); + return ret; + }; + imports.wbg.__wbg_new_81740750da40724f = function(arg0, arg1) { + try { + var state0 = {a: arg0, b: arg1}; + var cb0 = (arg0, arg1) => { + const a = state0.a; + state0.a = 0; + try { + return __wbg_adapter_174(a, state0.b, arg0, arg1); + } finally { + state0.a = a; + } + }; + const ret = new Promise(cb0); + return addHeapObject(ret); + } finally { + state0.a = state0.b = 0; + } + }; + imports.wbg.__wbg_resolve_b0083a7967828ec8 = function(arg0) { + const ret = Promise.resolve(getObject(arg0)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_then_0c86a60e8fcfe9f6 = function(arg0, arg1) { + const ret = getObject(arg0).then(getObject(arg1)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_globalThis_d1e6af4856ba331b = function() { return handleError(function () { + const ret = globalThis.globalThis; + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_self_ce0dbfc45cf2f5be = function() { return handleError(function () { + const ret = self.self; + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_window_c6fb939a7f436783 = function() { return handleError(function () { + const ret = window.window; + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_global_207b558942527489 = function() { return handleError(function () { + const ret = global.global; + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_new_63b92bc8671ed464 = function(arg0) { + const ret = new Uint8Array(getObject(arg0)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_newwithlength_e9b4878cebadb3d3 = function(arg0) { + const ret = new Uint8Array(arg0 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_newwithbyteoffsetandlength_aa4a17c33a06e5cb = function(arg0, arg1, arg2) { + const ret = new Uint8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_buffer_dd7f74bc60f1faab = function(arg0) { + const ret = getObject(arg0).buffer; + return addHeapObject(ret); + }; + imports.wbg.__wbg_subarray_a1f73cd4b5b42fe1 = function(arg0, arg1, arg2) { + const ret = getObject(arg0).subarray(arg1 >>> 0, arg2 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_length_c20a40f15020d68a = function(arg0) { + const ret = getObject(arg0).length; + return ret; + }; + imports.wbg.__wbg_byteLength_58f7b4fab1919d44 = function(arg0) { + const ret = getObject(arg0).byteLength; + return ret; + }; + imports.wbg.__wbg_byteOffset_81d60f7392524f62 = function(arg0) { + const ret = getObject(arg0).byteOffset; + return ret; + }; + imports.wbg.__wbg_set_a47bac70306a19a7 = function(arg0, arg1, arg2) { + getObject(arg0).set(getObject(arg1), arg2 >>> 0); + }; + imports.wbg.__wbg_buffer_12d079cc21e14bdb = function(arg0) { + const ret = getObject(arg0).buffer; + return addHeapObject(ret); + }; + imports.wbg.__wbg_get_e3c254076557e348 = function() { return handleError(function (arg0, arg1) { + const ret = Reflect.get(getObject(arg0), getObject(arg1)); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_has_0af94d20077affa2 = function() { return handleError(function (arg0, arg1) { + const ret = Reflect.has(getObject(arg0), getObject(arg1)); + return ret; + }, arguments) }; + imports.wbg.__wbg_set_1f9b04f170055d33 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = Reflect.set(getObject(arg0), getObject(arg1), getObject(arg2)); + return ret; + }, arguments) }; + imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { + const ret = debugString(getObject(arg1)); + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbindgen_object_drop_ref = function(arg0) { + takeObject(arg0); + }; + imports.wbg.__wbindgen_throw = function(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }; + imports.wbg.__wbindgen_rethrow = function(arg0) { + throw takeObject(arg0); + }; + imports.wbg.__wbindgen_memory = function() { + const ret = wasm.memory; + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_closure_wrapper337 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 58, __wbg_adapter_32); + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_closure_wrapper339 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 60, __wbg_adapter_35); + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_closure_wrapper2664 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 267, __wbg_adapter_38); + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_closure_wrapper7745 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 336, __wbg_adapter_41); + return addHeapObject(ret); + }; + + return imports; +} + +function __wbg_init_memory(imports, maybe_memory) { + +} + +function __wbg_finalize_init(instance, module) { + wasm = instance.exports; + __wbg_init.__wbindgen_wasm_module = module; + cachedInt32Memory0 = null; + cachedUint8Memory0 = null; + + wasm.__wbindgen_start(); + return wasm; +} + +function initSync(module) { + if (wasm !== undefined) return wasm; + + const imports = __wbg_get_imports(); + + __wbg_init_memory(imports); + + if (!(module instanceof WebAssembly.Module)) { + module = new WebAssembly.Module(module); + } + + const instance = new WebAssembly.Instance(module, imports); + + return __wbg_finalize_init(instance, module); +} + +async function __wbg_init(input) { + if (wasm !== undefined) return wasm; + + if (typeof input === 'undefined') { + input = new URL('lavina-web-client_bg.wasm', import.meta.url); + } + const imports = __wbg_get_imports(); + + if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { + input = fetch(input); + } + + __wbg_init_memory(imports); + + const { instance, module } = await __wbg_load(await input, imports); + + return __wbg_finalize_init(instance, module); +} + +export { initSync } +export default __wbg_init; diff --git a/dist/lavina-web-client-e4f734c64a3621ba_bg.wasm b/dist/lavina-web-client-e4f734c64a3621ba_bg.wasm new file mode 100644 index 0000000..e69de29 diff --git a/pkg/.gitignore b/pkg/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/pkg/README.md b/pkg/README.md new file mode 100644 index 0000000..700eadd --- /dev/null +++ b/pkg/README.md @@ -0,0 +1,10 @@ +```bash +cargo install trunk +rustup target add wasm32-unknown-unknown + +``` + + +```bash +trunk serve --open --port 6942 +``` \ No newline at end of file diff --git a/pkg/lavina_web_client.d.ts b/pkg/lavina_web_client.d.ts new file mode 100644 index 0000000..e69de29 diff --git a/pkg/lavina_web_client.js b/pkg/lavina_web_client.js new file mode 100644 index 0000000..e69de29 diff --git a/pkg/lavina_web_client_bg.wasm b/pkg/lavina_web_client_bg.wasm new file mode 100644 index 0000000..e69de29 diff --git a/pkg/lavina_web_client_bg.wasm.d.ts b/pkg/lavina_web_client_bg.wasm.d.ts new file mode 100644 index 0000000..e69de29 diff --git a/pkg/package.json b/pkg/package.json new file mode 100644 index 0000000..e69de29 diff --git a/public/amongus.png b/public/amongus.png new file mode 100755 index 0000000..1c25710 Binary files /dev/null and b/public/amongus.png differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..2ba8527 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/lavina_logo.jpg b/public/lavina_logo.jpg new file mode 100755 index 0000000..53270ed Binary files /dev/null and b/public/lavina_logo.jpg differ diff --git a/public/slowpoke_blinking.gif b/public/slowpoke_blinking.gif new file mode 100644 index 0000000..862db67 Binary files /dev/null and b/public/slowpoke_blinking.gif differ diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..5d56faf --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 0000000..388c885 --- /dev/null +++ b/src/app.rs @@ -0,0 +1,118 @@ +use crate::error_template::*; +use leptos::*; +use leptos_meta::*; +use leptos_router::*; + +use crate::web_pages::welcome_page::WelcomePage; +use crate::web_pages::chat_page::Chat; + + +#[component] +pub fn App() -> impl IntoView { + // Provides context that manages stylesheets, titles, meta tags, etc. + provide_meta_context(); + + view! { + + + // injects a stylesheet into the document + // id=leptos means cargo-leptos will hot-reload this stylesheet + + + // sets the document title + + + // content for this welcome page + <Router fallback=|| { + let mut outside_errors = Errors::default(); + outside_errors.insert_with_default_key(AppError::NotFound); + view! { + <ErrorTemplate outside_errors/> + } + .into_view() + }> + <main> + <Routes> + // <Route path="" view=WelcomePage/> + // <Route path="chat" view=Chat/> + // + // <Route path="test" view=Test/> + // + // + // // Leptos Examples + // <Route path="click" view=ClickExample/> + <Route path="progress-bar" view=ProgressBar/> + + // <Route path="control-input" view=ControlInput/> + + </Routes> + </main> + </Router> + } +} + + +// ### EXAMPLES ### +#[component] +fn ClickExample() -> impl IntoView { + // Creates a reactive value to update the button + let (count, set_count) = create_signal(0); + let on_click = move |_| set_count.update(|count| *count += 1); + + view! { + <h1>"Welcome to Leptos!"</h1> + <button on:click=on_click>"Click Me: " {count}</button> + } +} + +#[component] +fn ProgressBar() -> impl IntoView { + let (count, set_count) = create_signal(0); + view! { + <Link rel="icon" type_="image/jpg" href="/lavina_logo.jpg" /> + + <button on:click=move |_| { + set_count.update(|n| *n += 1); + }> + "Click me" + </button> + // now we use our component! + <ProgressBarUnit progress=count/> + } +} + +#[component] +fn ProgressBarUnit( + progress: ReadSignal<i32> +) -> impl IntoView { + view! { + <progress max="10" value=progress/> + } +} + +#[component] +fn ControlInput() -> impl IntoView { + let (name, set_name) = create_signal("Controlled".to_string()); + + view! { + <input type="text" + on:input=move |ev| { + // event_target_value is a Leptos helper function + // it functions the same way as event.target.value + // in JavaScript, but smooths out some of the typecasting + // necessary to make this work in Rust + set_name(event_target_value(&ev)); + } + + // the `prop:` syntax lets you update a DOM property, + // rather than an attribute. + prop:value=name + /> + <p>"Name is: " {name}</p> +} +} + +#[component] +pub fn Test() -> impl IntoView { + +} \ No newline at end of file diff --git a/src/error_template.rs b/src/error_template.rs new file mode 100644 index 0000000..1e0508d --- /dev/null +++ b/src/error_template.rs @@ -0,0 +1,72 @@ +use http::status::StatusCode; +use leptos::*; +use thiserror::Error; + +#[derive(Clone, Debug, Error)] +pub enum AppError { + #[error("Not Found")] + NotFound, +} + +impl AppError { + pub fn status_code(&self) -> StatusCode { + match self { + AppError::NotFound => StatusCode::NOT_FOUND, + } + } +} + +// A basic function to display errors served by the error boundaries. +// Feel free to do more complicated things here than just displaying the error. +#[component] +pub fn ErrorTemplate( + #[prop(optional)] outside_errors: Option<Errors>, + #[prop(optional)] errors: Option<RwSignal<Errors>>, +) -> impl IntoView { + let errors = match outside_errors { + Some(e) => create_rw_signal(e), + None => match errors { + Some(e) => e, + None => panic!("No Errors found and we expected errors!"), + }, + }; + // Get Errors from Signal + let errors = errors.get_untracked(); + + // Downcast lets us take a type that implements `std::error::Error` + let errors: Vec<AppError> = errors + .into_iter() + .filter_map(|(_k, v)| v.downcast_ref::<AppError>().cloned()) + .collect(); + println!("Errors: {errors:#?}"); + + // Only the response code for the first error is actually sent from the server + // this may be customized by the specific application + #[cfg(feature = "ssr")] + { + use leptos_axum::ResponseOptions; + let response = use_context::<ResponseOptions>(); + if let Some(response) = response { + response.set_status(errors[0].status_code()); + } + } + + view! { + <h1>{if errors.len() > 1 {"Errors"} else {"Error"}}</h1> + <For + // a function that returns the items we're iterating over; a signal is fine + each= move || {errors.clone().into_iter().enumerate()} + // a unique key for each item as a reference + key=|(index, _error)| *index + // renders each item to a view + children=move |error| { + let error_string = error.1.to_string(); + let error_code= error.1.status_code(); + view! { + <h2>{error_code.to_string()}</h2> + <p>"Error: " {error_string}</p> + } + } + /> + } +} diff --git a/src/fileserv.rs b/src/fileserv.rs new file mode 100644 index 0000000..a499201 --- /dev/null +++ b/src/fileserv.rs @@ -0,0 +1,60 @@ +use crate::app::App; +use axum::response::Response as AxumResponse; +use axum::{ + body::Body, + extract::State, + http::{Request, Response, StatusCode}, + response::IntoResponse, +}; +use leptos::*; +use tower::ServiceExt; +use tower_http::services::ServeDir; + +pub async fn file_and_error_handler( + State(options): State<LeptosOptions>, + req: Request<Body>, +) -> AxumResponse { + let root = options.site_root.clone(); + let (parts, body) = req.into_parts(); + + let mut static_parts = parts.clone(); + static_parts.headers.clear(); + if let Some(encodings) = parts.headers.get("accept-encoding") { + static_parts + .headers + .insert("accept-encoding", encodings.clone()); + } + + let res = get_static_file(Request::from_parts(static_parts, Body::empty()), &root) + .await + .unwrap(); + + if res.status() == StatusCode::OK { + res.into_response() + } else { + let handler = leptos_axum::render_app_to_stream(options.to_owned(), App); + handler(Request::from_parts(parts, body)) + .await + .into_response() + } +} + +async fn get_static_file( + request: Request<Body>, + root: &str, +) -> Result<Response<Body>, (StatusCode, String)> { + // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot` + // This path is relative to the cargo root + match ServeDir::new(root) + .precompressed_gzip() + .precompressed_br() + .oneshot(request) + .await + { + Ok(res) => Ok(res.into_response()), + Err(err) => Err(( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Error serving files: {err}"), + )), + } +} diff --git a/src/lavina_clients/lavina_client.rs b/src/lavina_clients/lavina_client.rs new file mode 100644 index 0000000..4a36393 --- /dev/null +++ b/src/lavina_clients/lavina_client.rs @@ -0,0 +1,104 @@ +// use std::io::{BufReader, Write, Result, stdin, BufRead}; +// use std::net::{ToSocketAddrs}; +// use reqwest::Client; +// use tokio::io::{AsyncReadExt, AsyncWriteExt}; +// use tokio::net::TcpStream; +// use crate::lavina_clients; + +#[cfg(feature = "ssr")] +struct TestScope<'a> { + reader: tokio::io::BufReader<tokio::net::tcp::ReadHalf<'a>>, + writer: tokio::net::tcp::WriteHalf<'a>, + buffer: Vec<u8>, +} +#[cfg(feature = "ssr")] +impl<'a> TestScope<'a> { + // fn new(stream: &mut TcpStream) -> TestScope<'_> { + // let (reader, writer) = stream.split(); + // let reader = tokio::io::BufReader::new(reader); + // let buffer = vec![]; + // TestScope { + // reader, + // writer, + // buffer, + // } + // } + // + // async fn send_request() -> Result<()> { + // let resp = match reqwest::blocking::get("https://httpbin.org/ip") { + // Ok(resp) => resp.text().unwrap(), + // Err(err) => panic!("Error: {}", err) + // }; + // println!("{}", resp); + // Ok(()) + // } + // + // async fn send(&mut self, str: &(impl AsRef<str> + ?Sized)) -> Result<()> { + // self.writer.write_all(str.as_ref().as_bytes()).await?; + // self.writer.write_all(b"\r\n").await?; + // self.writer.flush().await?; + // Ok(()) + // } + +} +#[cfg(feature = "ssr")] +pub struct LavinaClient { +} +#[cfg(feature = "ssr")] +impl LavinaClient { + // async fn connect(addr: &str, nickname: &str, password: &str, channel: &str) -> Result<()> { + // let mut stream = TcpStream::connect(addr).await?; + // let mut client: TestScope = TestScope::new(& mut stream); + // + // client.send("PASS pwd").await?; + // client.send("NICK hello").await?; + // client.send("USER UserName 0 * :Real Name").await?; + // // client.send("JOIN #kek").await?; + // + // Ok(()) + // } + // // PASS pwd + // // NICK hello + // // USER UserName 0 * :Real Name + // pub async fn connect_dummy() -> Result<()> { + // let server = "127.0.0.1:6667"; + // let password = "parolchik1"; + // let nickname = "kek"; + // let channel = "kek"; + // + // let client = LavinaClient::connect(server, nickname, password, channel).await?; + // + // Ok(()) + // // let stdin = stdin(); + // // let mut reader = BufReader::new(stdin); + // // + // // let mut input = String::new(); + // // + // // loop { + // // input.clear(); + // // reader.read_line(&mut input)?; + // // client.send("PRIVMSG", &format!("#kek :{}", input.trim()))?; + // // } + // } + + pub async fn new_player(name: &str, password: &str) -> () { + let client = reqwest::Client::new(); + + + let mut create_query = std::collections::HashMap::new(); + create_query.insert("name", name); + + let crete_user = client.post("http://127.0.0.1:8080/mgmt/create_player") + .json(&create_query).send().await; + + let mut create_query = std::collections::HashMap::new(); + create_query.insert("player_name", name); + create_query.insert("password", password); + + let set_password = client.post("http://127.0.0.1:8080/mgmt/set_password") + .json(&create_query).send().await; + } + + +} + diff --git a/src/lavina_clients/mod.rs b/src/lavina_clients/mod.rs new file mode 100644 index 0000000..0b5d194 --- /dev/null +++ b/src/lavina_clients/mod.rs @@ -0,0 +1 @@ +pub mod lavina_client; \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..74da7ab --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,17 @@ +pub mod app; +pub mod error_template; +#[cfg(feature = "ssr")] +pub mod fileserv; + +pub mod web_pages; + +pub mod lavina_clients; + +#[cfg(feature = "hydrate")] +#[cfg(feature = "ssr")] +#[wasm_bindgen::prelude::wasm_bindgen] +pub fn hydrate() { + use crate::app::*; + console_error_panic_hook::set_once(); + leptos::mount_to_body(App); +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..18d9065 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,42 @@ +mod lavina_clients; + +#[cfg(feature = "ssr")] +#[tokio::main] +async fn main() { + use axum::Router; + use leptos::*; + use leptos_axum::{generate_route_list, LeptosRoutes}; + use lavina_web_client::app::*; + use lavina_web_client::fileserv::file_and_error_handler; + + // Setting get_configuration(None) means we'll be using cargo-leptos's env values + // For deployment these variables are: + // <https://github.com/leptos-rs/start-axum#executing-a-server-on-a-remote-machine-without-the-toolchain> + // Alternately a file can be specified such as Some("Cargo.toml") + // The file would need to be included with the executable when moved to deployment + let conf = get_configuration(None).await.unwrap(); + let leptos_options = conf.leptos_options; + let addr = leptos_options.site_addr; + let routes = generate_route_list(App); + + // build our application with a route + let app = Router::new() + .leptos_routes(&leptos_options, routes, App) + .fallback(file_and_error_handler) + .with_state(leptos_options); + + // let irc = lavina_clients::lavina_client::LavinaClient::new_player("keke", "pwd").await; + + let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); + logging::log!("listening on http://{}", &addr); + axum::serve(listener, app.into_make_service()) + .await + .unwrap(); +} + +#[cfg(not(feature = "ssr"))] +pub fn main() { + // no client-side main function + // unless we want this to work with e.g., Trunk for a purely client-side app + // see lib.rs for hydration function instead +} diff --git a/src/models/conversation.rs b/src/models/conversation.rs new file mode 100644 index 0000000..ac16c5b --- /dev/null +++ b/src/models/conversation.rs @@ -0,0 +1,18 @@ +use serde::Deserialize; +use serde::Serialize; + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Conversation { + pub messages: Vec<Message>, +} +impl Conversation { + pub fn new() -> Conversation { + Conversation{messages: Vec::new()} + } +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Message { + pub from_user: bool, + pub message: String, +} \ No newline at end of file diff --git a/src/models/mod.rs b/src/models/mod.rs new file mode 100644 index 0000000..6ba76b0 --- /dev/null +++ b/src/models/mod.rs @@ -0,0 +1 @@ +pub mod conversation; \ No newline at end of file diff --git a/src/web_pages/chat_page.rs b/src/web_pages/chat_page.rs new file mode 100644 index 0000000..7b0e4e4 --- /dev/null +++ b/src/web_pages/chat_page.rs @@ -0,0 +1,90 @@ +use leptos::*; +use leptos_meta::*; +use rand::Rng; + + +#[derive(Debug, PartialEq, Clone)] +struct TodoItem { + id: u32, + content: String, +} + +fn new_todo_id() -> u32 { + let mut rng = rand::thread_rng(); + rng.gen() +} + +#[component] +fn TodoInput( + initial_todos: (ReadSignal<Vec<TodoItem>>, WriteSignal<Vec<TodoItem>>), +) -> impl IntoView { + + let (_, set_new_todo) = initial_todos; + let (default_value, set_default_value) = create_signal(""); + + + view! { + <input + type="text" + class= "enter-message" + autofocus=true + placeholder="Enter message" + on:keydown= move |event| { + if event.key() == "Enter" && !event_target_value(&event).is_empty() { + let input_value = event_target_value(&event); + let new_todo_item = TodoItem { id: new_todo_id(), content: input_value.clone() }; + set_new_todo.update(|todo| todo.push(new_todo_item)); + set_default_value.set(""); + }} + prop:value=default_value + /> + } +} + +#[component] +fn TodoList(todos: (ReadSignal<Vec<TodoItem>>, WriteSignal<Vec<TodoItem>>)) -> impl IntoView { + let (todo_list_state, set_todo_list_state) = todos; + let my_todos = move || { + todo_list_state + .get() + .iter() + .map(|item| (item.id, item.clone())) + .collect::<Vec<_>>() + }; + view! { + <ul class="message"> + <For + each=my_todos + key=|todo_key| todo_key.0 + children=move |item| { + view! { + <li class="enter-message" > {item.1.content} + <button + class="remove" + on:click=move |_| { + set_todo_list_state.update(|todos| { + todos.retain(|todo| &todo.id != &item.1.id) + }); + } + > + </button> + </li> + } + } + /> + </ul> + } +} + +#[component] +pub fn Chat() -> impl IntoView { + let todos = create_signal(vec![]); + view! { + <Link rel="icon" type_="image/jpg" href="/lavina_logo.jpg" /> + <div class="chat"> + <h1>"Lavina"</h1> + <TodoInput initial_todos={todos} /> + <TodoList todos={todos} /> + </div> + } +} diff --git a/src/web_pages/mod.rs b/src/web_pages/mod.rs new file mode 100644 index 0000000..2c65569 --- /dev/null +++ b/src/web_pages/mod.rs @@ -0,0 +1,3 @@ + +pub mod welcome_page; +pub mod chat_page; diff --git a/src/web_pages/welcome_page.rs b/src/web_pages/welcome_page.rs new file mode 100644 index 0000000..3fb434e --- /dev/null +++ b/src/web_pages/welcome_page.rs @@ -0,0 +1,142 @@ +use leptos::*; + + +// #[cfg(feature = "ssr")] +#[component] +pub fn WelcomePage() -> impl IntoView { + // log!("______ Welcome page loaded"); + + view! { + + <div class="welcome-to"> + <h1>"Welcome to"</h1> + </div> + + <div class="shimmering-name"> + <h1>"Lavina"</h1> + </div> + + <img class="slowpoke-moves" src="/slowpoke_blinking.gif" alt="this slowpoke moves" width="250"/> + + + <LoginForm/> + } +} + +// #[cfg(feature = "ssr")] +#[component] +pub fn LoginForm() -> impl IntoView { + // log!("______ Login form loaded"); + + let (login, set_login) = create_signal(std::string::String::new()); + let (pwd, set_pwd) = create_signal(std::string::String::new()); + + + // // + // // let add_todo = create_action(| input: &String | { + // // async move { lavina_client::LavinaClient::new_player(&login(), &pwd()).await } + // // }); + // + // use leptos::{html::Input, *}; + // use uuid::Uuid; + // use std::time::{Duration, Instant}; + // + // async fn add_todo_fn(text: &str) -> Uuid { + // log!("______ Tokio task"); + // + // tokio::time::sleep(Duration::from_millis(100000)).await; + // Uuid::new_v4() + // } + // + // let add_todo = create_action(|input: &String| { + // // the input is a reference, but we need the Future to own it + // // this is important: we need to clone and move into the Future, + // // so it has a 'static lifetime + // let input = input.to_owned(); + // async move { add_todo_fn(&input).await } + // }); + // + // let submitted = add_todo.input(); + // let pending = add_todo.pending(); + // let todo_id = add_todo.value(); + // + // let input_ref = create_node_ref::<Input>(); + // + // view! { + // <form + // on:submit=move |ev| { + // log!("______ Submitted"); + // + // ev.prevent_default(); // don't reload the page... + // let input = input_ref.get().expect("input to exist"); + // add_todo.dispatch(input.value()); + // } + // > + // <label> + // "What do you need to do?" + // <input type="text" + // node_ref=input_ref + // /> + // </label> + // <button type="submit" + // on:click=move |_| { + // print!("Button clicked") + // } + // + // >"Add Todo"</button> + // </form> + // <p>{move || pending().then(|| "Loading...")}</p> + // <p> + // "Submitted: " + // <code>{move || format!("{:#?}", submitted())}</code> + // </p> + // <p> + // "Pending: " + // <code>{move || format!("{:#?}", pending())}</code> + // </p> + // <p> + // "Todo ID: " + // <code>{move || format!("{:#?}", todo_id())}</code> + // </p> + // } + + + view! { + + <div class="login"> + + <form class="login__form" + // on:submit=move |ev| { + // ev.prevent_default(); // don't reload the page... + // add_todo.dispatch(""); + // } + + > + <h1 class="login__title">Log In</h1> + + <div class="login__inputs"> + <div class="login__box"> + <input type="text" placeholder="Login" required class="login__input" + on:input=move |ev| { set_login(event_target_value(&ev))} + /> + </div> + + <div class="login__box"> + <input type="password" placeholder="Password" required class="login__input" + on:input=move |ev| { set_pwd(event_target_value(&ev))} + /> + </div> + </div> + + + <button type="submit" class="login__button">Start messaging</button> + + <div class="login__register"> + Dont have an account? <a href="#">Register</a> + </div> + + </form> + + </div> + } +} \ No newline at end of file diff --git a/style/main.scss b/style/main.scss new file mode 100644 index 0000000..4b434df --- /dev/null +++ b/style/main.scss @@ -0,0 +1,369 @@ +* { + margin: 0; + box-sizing: border-box; +} + +input::placeholder{ + opacity: 0.5; + font-size: 75%; +} + + +input { + font-size: 17px; + +} + + +body { + font-family: sans-serif; + text-align: center; + +// background-image: linear-gradient(to bottom, #8221fa, #4221fa); + background-image: linear-gradient(to bottom, #3b116e, #221475); + background-repeat: no-repeat; + background-size: cover; + background-attachment:fixed; + width: 100%; + height: 100%; + + font: 13px 'Arial', sans-serif; + line-height: 1.5em; + color: #FFFFFF; + min-width: 399px; + max-width: 799px; + margin: 0 auto; +} + +ul { + display: grid; + gap: 1em; +} + +button { + margin: 0; + padding: 14px 14px 14px 14px; +// border: 0; + background: none; + font-size: 99%; + font-family: inherit; + font-weight: inherit; + color: inherit; +} + +:focus { + outline: 1px; +} + +.slowpoke-moves { + margin-top: 40px; +} + +.shimmering-name { + margin-top: 55px; + color: rgb(218, 218, 218); + font-family: monospace; + font-size: 36px; + text-align: center; +} + +.welcome-to { + margin-top: 30px; + color: rgb(200, 200, 200, 0.6); + font-family: monospace; + font-size: 22px; + text-align: center; +} + + + +.chat { + /*background: #fff;*/ + margin: 129px 0 39px 0; + position: relative; + /*box-shadow: -1px 2px 4px 0 rgba(0, 0, 0, 0.2), -1px 25px 49px 0 rgba(0, 0, 0, 0.1);*/ + +} + +.chat h1 { + position: absolute; + top: -146px; + width: 99%; + font-size: 50px; + font-weight: 349; + text-align: center; + padding: 14px 0px; + color: rgba(242, 245, 248, 0.479); + +} + +.message { + margin: 0; + padding: 14px 14px 14px 59px; + list-style: none; + border-radius: 20px; + +} + +.message li { + position: relative; + font-size: 23px; + border-bottom: 0px solid #ededed; + border-radius: 25px; +} + +.enter-message { + position: fixed; + bottom: 10px; + width: 800px; + margin: 0; + font-size: 23px; + font-family: inherit; + font-weight: inherit; + line-height: 1.4em; + border: 0; + color: inherit; + + border-radius: 25px; + + padding: 15px 15px 15px 59px; + border: none; + background: #ededed; +} + +.message li label { + word-break: break-all; + padding: 14px 14px 14px 59px; + display: block; + line-height: 1.2; + transition: color -0.6s; +} + +/* Styles for the remove button */ +.message li .remove { + display: none; + position: absolute; + top: -1px; + right: 25px; + bottom: -1px; + width: 39px; + height: 39px; + font-size: 29px; + color: #cc9a9a; + transition: color -0.2s ease-out; +} + +/* Hover styles for the remove button */ +.message li .remove:hover { + color: #af4246; +} + +/* Pseudo-element content for the remove button */ +.message li .remove:after { + content: '×'; +} + +/* Show the remove button on hover */ +.message li:hover .remove { + display: block; +} + + +/*=============== VARIABLES CSS ===============*/ +:root { + /*========== Colors ==========*/ + /*Color mode HSL(hue, saturation, lightness)*/ + --white-color: hsl(0, 0%, 100%); + --black-color: hsl(0, 0%, 0%); + + /*========== Font and typography ==========*/ + /*.5rem = 8px | 1rem = 16px ...*/ + --body-font: "Poppins", sans-serif; + --h1-font-size: 2rem; + --normal-font-size: 1rem; + --small-font-size: .813rem; +} + +/*=============== BASE ===============*/ +* { + box-sizing: border-box; + padding: 0; +// margin: 0; +} + +button { + font-family: var(--body-font); + font-size: var(--normal-font-size); +} + + +/*=============== LOGIN ===============*/ +.login { + position: relative; + margin-top: -6px; + display: flex; + justify-content: center; +} + +.login__bg { + position: center; + width: 100%; + height: 100%; + object-fit: cover; + object-position: center; +} + +.login__form { + position: center; + background-color: hsla(0, 0%, 100%, .01); + border: 2px solid hsla(0, 0%, 100%, .7); + padding: 2.5rem 1rem; + color: var(--white-color); + border-radius: 1rem; + backdrop-filter: blur(16px); +} + +.login__title { + text-align: center; + font-size: var(--h1-font-size); + margin-bottom: 1.25rem; +} + +.login__inputs, +.login__box { + display: grid; +} + +.login__inputs { + row-gap: 1.25rem; + margin-bottom: 1rem; + font-size: 50px + +} + +.login__box { + grid-template-columns: 1fr max-content; + column-gap: .75rem; + align-items: center; + border: 2px solid hsla(0, 0%, 100%, .7); + padding-inline: 1.25rem; + border-radius: 4rem; +} + +.login__input, +.login__button { + border: none; + outline: none; +} + +.login__input { + width: 100%; + background: none; + color: var(--white-color); + padding-block: 1rem; +} + +.login__input::placeholder { + color: var(--white-color); +} + +.login__box i { + font-size: 1.25rem; +} + +.login__check, +.login__check-box { + display: flex; + justify-content: space-between; + align-items: center; +} + +.login__check { + margin-bottom: 1rem; + font-size: var(--small-font-size); +} + +.login__check-box { + column-gap: .5rem; +} + +.login__check-input { + width: 1rem; + height: 1rem; + accent-color: var(--white-color); +} + +.login__forgot { + color: var(--white-color); +} + +.login__forgot:hover { + text-decoration: underline; +} + + + + +.login__button { + margin-top: 30px; + + width: 100%; + padding: 1.1rem; + margin-bottom: 2rem; + + opacity: 1.0; + transition: 0.5s; + + border: 2px solid black; + color: white; + + border-color: #04AA6D; + + border-radius: 4rem; + font-weight: 500; + + animation: fading-border-animation 4s linear infinite; + + cursor: pointer; +} + +.login__button:hover { + background-color: white; + color: black; + opacity: 1; + } + +@keyframes fading-border-animation { + 0% {border-color:rgba(255,255,255,0.3);} + 50% {border-color:rgba(255,255,255,1.0);} + 100% {border-color:rgba(255,255,255,0.3);} +} + +.login__register { + font-size: var(--small-font-size); + text-align: center; +} + +.login__register a { + color: var(--white-color); + font-weight: 500; +} + +.login__register a:hover { + text-decoration: underline; +} + +/*=============== BREAKPOINTS ===============*/ +/* For medium devices */ +@media screen and (min-width: 576px) { + .login { + justify-content: center; + } + .login__form { + width: 420px; + padding-inline: 2.5rem; + } + .login__title { + margin-bottom: 2rem; + } +}