feat: add fullstack storage and real-time sync

This commit is contained in:
Malte Schröder
2025-12-23 17:37:40 +01:00
parent d6b31101c4
commit 22f4d74935
6 changed files with 556 additions and 97 deletions

491
Cargo.lock generated
View File

@@ -17,6 +17,21 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.100" version = "1.0.100"
@@ -44,6 +59,12 @@ dependencies = [
"zbus", "zbus",
] ]
[[package]]
name = "askama_escape"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3df27b8d5ddb458c5fb1bbc1ce172d4a38c614a97d550b0ac89003897fb01de4"
[[package]] [[package]]
name = "async-broadcast" name = "async-broadcast"
version = "0.7.2" version = "0.7.2"
@@ -113,7 +134,7 @@ dependencies = [
"futures-util", "futures-util",
"log", "log",
"pin-project-lite", "pin-project-lite",
"tungstenite", "tungstenite 0.27.0",
] ]
[[package]] [[package]]
@@ -158,12 +179,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
dependencies = [ dependencies = [
"axum-core", "axum-core",
"axum-macros",
"base64",
"bytes", "bytes",
"form_urlencoded", "form_urlencoded",
"futures-util", "futures-util",
"http", "http",
"http-body", "http-body",
"http-body-util", "http-body-util",
"hyper",
"hyper-util",
"itoa", "itoa",
"matchit", "matchit",
"memchr", "memchr",
@@ -175,10 +200,14 @@ dependencies = [
"serde_json", "serde_json",
"serde_path_to_error", "serde_path_to_error",
"serde_urlencoded", "serde_urlencoded",
"sha1",
"sync_wrapper", "sync_wrapper",
"tokio",
"tokio-tungstenite 0.28.0",
"tower", "tower",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
"tracing",
] ]
[[package]] [[package]]
@@ -197,8 +226,49 @@ dependencies = [
"sync_wrapper", "sync_wrapper",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
"tracing",
] ]
[[package]]
name = "axum-extra"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9963ff19f40c6102c76756ef0a46004c0d58957d87259fc9208ff8441c12ab96"
dependencies = [
"axum",
"axum-core",
"bytes",
"futures-util",
"headers",
"http",
"http-body",
"http-body-util",
"mime",
"pin-project-lite",
"rustversion",
"serde_core",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "axum-macros"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
]
[[package]]
name = "base16"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d27c3610c36aee21ce8ac510e6224498de4228ad772a171ed65643a24693a5a8"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.22.1" version = "0.22.1"
@@ -349,6 +419,19 @@ dependencies = [
"encoding_rs", "encoding_rs",
] ]
[[package]]
name = "chrono"
version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
dependencies = [
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-link 0.2.1",
]
[[package]] [[package]]
name = "ciborium" name = "ciborium"
version = "0.2.2" version = "0.2.2"
@@ -693,6 +776,20 @@ dependencies = [
"syn 2.0.111", "syn 2.0.111",
] ]
[[package]]
name = "dashmap"
version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
dependencies = [
"cfg-if",
"crossbeam-utils",
"hashbrown 0.14.5",
"lock_api",
"once_cell",
"parking_lot_core",
]
[[package]] [[package]]
name = "data-encoding" name = "data-encoding"
version = "2.9.0" version = "2.9.0"
@@ -770,14 +867,19 @@ dependencies = [
"dioxus-devtools", "dioxus-devtools",
"dioxus-document", "dioxus-document",
"dioxus-fullstack", "dioxus-fullstack",
"dioxus-fullstack-macro",
"dioxus-history", "dioxus-history",
"dioxus-hooks", "dioxus-hooks",
"dioxus-html", "dioxus-html",
"dioxus-liveview",
"dioxus-logger", "dioxus-logger",
"dioxus-server",
"dioxus-signals", "dioxus-signals",
"dioxus-ssr",
"dioxus-stores", "dioxus-stores",
"dioxus-web", "dioxus-web",
"manganis", "manganis",
"serde",
"subsecond", "subsecond",
"warnings", "warnings",
] ]
@@ -919,7 +1021,7 @@ dependencies = [
"tokio", "tokio",
"tracing", "tracing",
"tray-icon", "tray-icon",
"tungstenite", "tungstenite 0.27.0",
"webbrowser", "webbrowser",
"wry", "wry",
] ]
@@ -934,12 +1036,14 @@ dependencies = [
"dioxus-core", "dioxus-core",
"dioxus-devtools-types", "dioxus-devtools-types",
"dioxus-signals", "dioxus-signals",
"futures-channel",
"futures-util",
"serde", "serde",
"serde_json", "serde_json",
"subsecond", "subsecond",
"thiserror 2.0.17", "thiserror 2.0.17",
"tracing", "tracing",
"tungstenite", "tungstenite 0.27.0",
] ]
[[package]] [[package]]
@@ -983,6 +1087,7 @@ dependencies = [
"async-tungstenite", "async-tungstenite",
"axum", "axum",
"axum-core", "axum-core",
"axum-extra",
"base64", "base64",
"bytes", "bytes",
"ciborium", "ciborium",
@@ -1007,6 +1112,7 @@ dependencies = [
"http", "http",
"http-body", "http-body",
"http-body-util", "http-body-util",
"inventory",
"js-sys", "js-sys",
"mime", "mime",
"pin-project", "pin-project",
@@ -1018,9 +1124,15 @@ dependencies = [
"serde_qs", "serde_qs",
"serde_urlencoded", "serde_urlencoded",
"thiserror 2.0.17", "thiserror 2.0.17",
"tokio",
"tokio-stream",
"tokio-tungstenite 0.27.0",
"tokio-util", "tokio-util",
"tower",
"tower-http",
"tower-layer",
"tracing", "tracing",
"tungstenite", "tungstenite 0.27.0",
"url", "url",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
@@ -1156,6 +1268,34 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "dioxus-liveview"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f690466a88cc93d7f87e1735aab9cb4a83c70f452ed344a32559577e80505da4"
dependencies = [
"axum",
"dioxus-cli-config",
"dioxus-core",
"dioxus-devtools",
"dioxus-document",
"dioxus-history",
"dioxus-html",
"dioxus-interpreter-js",
"futures-channel",
"futures-util",
"generational-box",
"rustc-hash 2.1.1",
"serde",
"serde_json",
"slab",
"thiserror 2.0.17",
"tokio",
"tokio-stream",
"tokio-util",
"tracing",
]
[[package]] [[package]]
name = "dioxus-logger" name = "dioxus-logger"
version = "0.7.2" version = "0.7.2"
@@ -1168,6 +1308,42 @@ dependencies = [
"tracing-wasm", "tracing-wasm",
] ]
[[package]]
name = "dioxus-router"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18282604175f38d8c9291946ad6b34899657e47aef994fbbe6defb501a000f33"
dependencies = [
"dioxus-cli-config",
"dioxus-core",
"dioxus-core-macro",
"dioxus-fullstack-core",
"dioxus-history",
"dioxus-hooks",
"dioxus-html",
"dioxus-router-macro",
"dioxus-signals",
"percent-encoding",
"rustversion",
"tracing",
"url",
]
[[package]]
name = "dioxus-router-macro"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47340b339c2c3f042b190f541b7241e2547b2e703f813d34ea24b963330c6757"
dependencies = [
"base16",
"digest",
"proc-macro2",
"quote",
"sha2",
"slab",
"syn 2.0.111",
]
[[package]] [[package]]
name = "dioxus-rsx" name = "dioxus-rsx"
version = "0.7.2" version = "0.7.2"
@@ -1181,6 +1357,64 @@ dependencies = [
"syn 2.0.111", "syn 2.0.111",
] ]
[[package]]
name = "dioxus-server"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d608c33c39f032469c6eb59f361dc2724799724d8b3e15c824d1047e664c087"
dependencies = [
"anyhow",
"async-trait",
"axum",
"base64",
"bytes",
"chrono",
"ciborium",
"dashmap",
"dioxus-cli-config",
"dioxus-core",
"dioxus-core-macro",
"dioxus-devtools",
"dioxus-document",
"dioxus-fullstack-core",
"dioxus-history",
"dioxus-hooks",
"dioxus-html",
"dioxus-interpreter-js",
"dioxus-logger",
"dioxus-router",
"dioxus-signals",
"dioxus-ssr",
"enumset",
"futures",
"futures-channel",
"futures-util",
"generational-box",
"http",
"http-body-util",
"hyper",
"hyper-util",
"inventory",
"lru",
"parking_lot",
"pin-project",
"rustc-hash 2.1.1",
"serde",
"serde_json",
"serde_qs",
"subsecond",
"thiserror 2.0.17",
"tokio",
"tokio-tungstenite 0.27.0",
"tokio-util",
"tower",
"tower-http",
"tracing",
"tracing-futures",
"url",
"walkdir",
]
[[package]] [[package]]
name = "dioxus-signals" name = "dioxus-signals"
version = "0.7.2" version = "0.7.2"
@@ -1197,6 +1431,18 @@ dependencies = [
"warnings", "warnings",
] ]
[[package]]
name = "dioxus-ssr"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "088efddedd39fc29d007bc91c8a61b25130355149ea5313469f96fb695c5e3ab"
dependencies = [
"askama_escape",
"dioxus-core",
"dioxus-core-types",
"rustc-hash 2.1.1",
]
[[package]] [[package]]
name = "dioxus-stores" name = "dioxus-stores"
version = "0.7.2" version = "0.7.2"
@@ -1232,6 +1478,7 @@ dependencies = [
"dioxus-core-types", "dioxus-core-types",
"dioxus-devtools", "dioxus-devtools",
"dioxus-document", "dioxus-document",
"dioxus-fullstack-core",
"dioxus-history", "dioxus-history",
"dioxus-html", "dioxus-html",
"dioxus-interpreter-js", "dioxus-interpreter-js",
@@ -1310,7 +1557,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
dependencies = [ dependencies = [
"libloading 0.8.9", "libloading 0.7.4",
] ]
[[package]] [[package]]
@@ -1529,6 +1776,12 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]] [[package]]
name = "foreign-types" name = "foreign-types"
version = "0.3.2" version = "0.3.2"
@@ -1971,21 +2224,6 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "gloo-storage"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbc8031e8c92758af912f9bc08fbbadd3c6f3cfcbf6b64cdf3d6a81f0139277a"
dependencies = [
"gloo-utils",
"js-sys",
"serde",
"serde_json",
"thiserror 1.0.69",
"wasm-bindgen",
"web-sys",
]
[[package]] [[package]]
name = "gloo-timers" name = "gloo-timers"
version = "0.3.0" version = "0.3.0"
@@ -2074,6 +2312,25 @@ dependencies = [
"syn 2.0.111", "syn 2.0.111",
] ]
[[package]]
name = "h2"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386"
dependencies = [
"atomic-waker",
"bytes",
"fnv",
"futures-core",
"futures-sink",
"http",
"indexmap",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]] [[package]]
name = "half" name = "half"
version = "2.7.1" version = "2.7.1"
@@ -2085,11 +2342,22 @@ dependencies = [
"zerocopy", "zerocopy",
] ]
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.16.1" version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash",
]
[[package]] [[package]]
name = "headers" name = "headers"
@@ -2178,6 +2446,12 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "http-range-header"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c"
[[package]] [[package]]
name = "httparse" name = "httparse"
version = "1.10.1" version = "1.10.1"
@@ -2200,9 +2474,11 @@ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"h2",
"http", "http",
"http-body", "http-body",
"httparse", "httparse",
"httpdate",
"itoa", "itoa",
"pin-project-lite", "pin-project-lite",
"pin-utils", "pin-utils",
@@ -2247,9 +2523,36 @@ dependencies = [
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"socket2", "socket2",
"system-configuration",
"tokio", "tokio",
"tower-layer",
"tower-service", "tower-service",
"tracing", "tracing",
"windows-registry",
]
[[package]]
name = "iana-time-zone"
version = "0.1.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
] ]
[[package]] [[package]]
@@ -2367,7 +2670,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown 0.16.1",
] ]
[[package]] [[package]]
@@ -2618,6 +2921,15 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86"
[[package]]
name = "lru"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f"
dependencies = [
"hashbrown 0.16.1",
]
[[package]] [[package]]
name = "lru-slab" name = "lru-slab"
version = "0.1.2" version = "0.1.2"
@@ -2771,10 +3083,12 @@ name = "mflow"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"dioxus", "dioxus",
"getrandom 0.3.4", "getrandom 0.2.16",
"gloo-storage", "gloo-timers",
"once_cell",
"serde", "serde",
"serde_json", "serde_json",
"tokio",
] ]
[[package]] [[package]]
@@ -4390,6 +4704,27 @@ dependencies = [
"syn 2.0.111", "syn 2.0.111",
] ]
[[package]]
name = "system-configuration"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
dependencies = [
"bitflags 2.10.0",
"core-foundation 0.9.4",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "system-deps" name = "system-deps"
version = "6.2.2" version = "6.2.2"
@@ -4599,6 +4934,7 @@ dependencies = [
"bytes", "bytes",
"libc", "libc",
"mio", "mio",
"parking_lot",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry", "signal-hook-registry",
"socket2", "socket2",
@@ -4628,6 +4964,42 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "tokio-stream"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
"tokio-util",
]
[[package]]
name = "tokio-tungstenite"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "489a59b6730eda1b0171fcfda8b121f4bee2b35cba8645ca35c5f7ba3eb736c1"
dependencies = [
"futures-util",
"log",
"tokio",
"tungstenite 0.27.0",
]
[[package]]
name = "tokio-tungstenite"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857"
dependencies = [
"futures-util",
"log",
"tokio",
"tungstenite 0.28.0",
]
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.17" version = "0.7.17"
@@ -4638,6 +5010,7 @@ dependencies = [
"futures-core", "futures-core",
"futures-io", "futures-io",
"futures-sink", "futures-sink",
"futures-util",
"pin-project-lite", "pin-project-lite",
"tokio", "tokio",
] ]
@@ -4730,6 +5103,7 @@ dependencies = [
"tokio", "tokio",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
"tracing",
] ]
[[package]] [[package]]
@@ -4740,14 +5114,24 @@ checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
dependencies = [ dependencies = [
"bitflags 2.10.0", "bitflags 2.10.0",
"bytes", "bytes",
"futures-core",
"futures-util", "futures-util",
"http", "http",
"http-body", "http-body",
"http-body-util",
"http-range-header",
"httpdate",
"iri-string", "iri-string",
"mime",
"mime_guess",
"percent-encoding",
"pin-project-lite", "pin-project-lite",
"tokio",
"tokio-util",
"tower", "tower",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
"tracing",
] ]
[[package]] [[package]]
@@ -4768,6 +5152,7 @@ version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [ dependencies = [
"log",
"pin-project-lite", "pin-project-lite",
"tracing-attributes", "tracing-attributes",
"tracing-core", "tracing-core",
@@ -4793,6 +5178,16 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "tracing-futures"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
dependencies = [
"pin-project",
"tracing",
]
[[package]] [[package]]
name = "tracing-subscriber" name = "tracing-subscriber"
version = "0.3.22" version = "0.3.22"
@@ -4865,6 +5260,23 @@ dependencies = [
"utf-8", "utf-8",
] ]
[[package]]
name = "tungstenite"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442"
dependencies = [
"bytes",
"data-encoding",
"http",
"httparse",
"log",
"rand 0.9.2",
"sha1",
"thiserror 2.0.17",
"utf-8",
]
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.19.0" version = "1.19.0"
@@ -5351,8 +5763,8 @@ dependencies = [
"windows-implement", "windows-implement",
"windows-interface", "windows-interface",
"windows-link 0.1.3", "windows-link 0.1.3",
"windows-result", "windows-result 0.3.4",
"windows-strings", "windows-strings 0.4.2",
] ]
[[package]] [[package]]
@@ -5410,6 +5822,17 @@ dependencies = [
"windows-link 0.1.3", "windows-link 0.1.3",
] ]
[[package]]
name = "windows-registry"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
dependencies = [
"windows-link 0.2.1",
"windows-result 0.4.1",
"windows-strings 0.5.1",
]
[[package]] [[package]]
name = "windows-result" name = "windows-result"
version = "0.3.4" version = "0.3.4"
@@ -5419,6 +5842,15 @@ dependencies = [
"windows-link 0.1.3", "windows-link 0.1.3",
] ]
[[package]]
name = "windows-result"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
dependencies = [
"windows-link 0.2.1",
]
[[package]] [[package]]
name = "windows-strings" name = "windows-strings"
version = "0.4.2" version = "0.4.2"
@@ -5428,6 +5860,15 @@ dependencies = [
"windows-link 0.1.3", "windows-link 0.1.3",
] ]
[[package]]
name = "windows-strings"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
dependencies = [
"windows-link 0.2.1",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.45.0" version = "0.45.0"

View File

@@ -1,22 +1,27 @@
[package] [package]
name = "mflow" name = "mflow"
version = "0.1.0" version = "0.1.0"
authors = ["malxte"]
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
dioxus = { version = "0.7.1", features = [] } # Version an CLI anpassen (0.6 ist stabil für Fullstack)
dioxus = { version = "0.7.1", features = ["fullstack"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
getrandom = { version = "0.3", features = ["wasm_js"] } once_cell = "1.19"
# Wichtig: "time" und "sync" Features hinzufügen
tokio = { version = "1", features = ["sync", "time", "rt"] }
gloo-timers = { version = "0.3", features = ["futures"] }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { version = "1", features = ["full"] }
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
gloo-storage = "0.3" getrandom = { version = "0.2", features = ["js"] }
[features] [features]
default = ["desktop"] default = ["desktop"]
web = ["dioxus/web"]
desktop = ["dioxus/desktop"] desktop = ["dioxus/desktop"]
web = ["dioxus/web"]
mobile = ["dioxus/mobile"] mobile = ["dioxus/mobile"]
server = ["dioxus/server"]

View File

@@ -62,4 +62,4 @@ Die Anwendung ist dann unter `http://localhost:8080` erreichbar.
## 📝 Lizenz ## 📝 Lizenz
Dieses Projekt wurde erstellt von [malxte]. Dieses Projekt wurde erstellt von [Bytemalte].

View File

@@ -1,6 +1 @@
[ []
{
"name": "Hallo der erste Test!",
"content": "Das ist mein erstes Projekt!"
}
]

View File

@@ -4,9 +4,11 @@ mod ui;
const MAIN_CSS: Asset = asset!("/assets/main.css"); const MAIN_CSS: Asset = asset!("/assets/main.css");
fn main() { fn main() {
// Erkennt automatisch ob Web (Client) oder Server (Fullstack)
dioxus::launch(App); dioxus::launch(App);
} }
#[allow(non_snake_case)]
#[component] #[component]
fn App() -> Element { fn App() -> Element {
rsx! { rsx! {

132
src/ui.rs
View File

@@ -1,59 +1,64 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(not(target_arch = "wasm32"))]
use std::fs;
#[cfg(target_arch = "wasm32")]
use gloo_storage::{LocalStorage, Storage};
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)] #[derive(Clone, PartialEq, Serialize, Deserialize, Debug)]
struct Project { pub struct Project {
name: String, pub name: String,
content: String, pub content: String,
} }
const STORAGE_KEY: &str = "mflow_projects"; // --- SERVER LOGIK ---
#[cfg(feature = "server")]
static PROJECTS: once_cell::sync::Lazy<std::sync::RwLock<Vec<Project>>> =
once_cell::sync::Lazy::new(|| {
let data = std::fs::read_to_string("projects.json").unwrap_or_else(|_| "[]".to_string());
std::sync::RwLock::new(serde_json::from_str(&data).unwrap_or_default())
});
#[server]
pub async fn get_projects() -> Result<Vec<Project>, ServerFnError> {
Ok(PROJECTS.read().unwrap().clone())
}
#[server]
pub async fn update_all_projects(new_projects: Vec<Project>) -> Result<(), ServerFnError> {
let mut lock = PROJECTS.write().unwrap();
*lock = new_projects.clone();
let data = serde_json::to_string_pretty(&*lock)?;
let _ = std::fs::write("projects.json", data);
Ok(())
}
#[component] #[component]
pub fn Hero() -> Element { pub fn Hero() -> Element {
// --- Initiales Laden --- let mut projects = use_signal(Vec::<Project>::new);
let mut projects = use_signal(|| {
#[cfg(target_arch = "wasm32")]
{
LocalStorage::get::<Vec<Project>>(STORAGE_KEY).unwrap_or_default()
}
#[cfg(not(target_arch = "wasm32"))]
{
fs::read_to_string("projects.json")
.ok()
.and_then(|data| serde_json::from_str::<Vec<Project>>(&data).ok())
.unwrap_or_default()
}
});
let mut selected_index = use_signal(|| None::<usize>); let mut selected_index = use_signal(|| None::<usize>);
let mut is_typing = use_signal(|| false);
// Modal-States
let mut show_confirm = use_signal(|| false); let mut show_confirm = use_signal(|| false);
let mut index_to_delete = use_signal(|| None::<usize>); let mut index_to_delete = use_signal(|| None::<usize>);
// --- Automatisches Speichern --- // Echtzeit-Sync (Polling)
use_effect(move || { use_resource(move || async move {
let current_projects = projects.read(); loop {
if !is_typing() {
#[cfg(target_arch = "wasm32")] if let Ok(p) = get_projects().await {
{ if p != projects.read().clone() {
let _ = LocalStorage::set(STORAGE_KEY, &*current_projects); projects.set(p);
} }
#[cfg(not(target_arch = "wasm32"))] }
{ }
let data = serde_json::to_string_pretty(&*current_projects).unwrap_or_default(); #[cfg(target_arch = "wasm32")]
let _ = fs::write("projects.json", data); gloo_timers::future::TimeoutFuture::new(1000).await;
#[cfg(not(target_arch = "wasm32"))]
tokio::time::sleep(std::time::Duration::from_millis(1000)).await;
} }
}); });
rsx! { rsx! {
div { id: "hero", div { id: "hero",
// Sidebar // SIDEBAR
div { id: "sidebar", div { id: "sidebar",
h2 { "Projekte" } h2 { "Projekte" }
div { class: "project-list", div { class: "project-list",
@@ -77,30 +82,38 @@ pub fn Hero() -> Element {
} }
button { id: "add-project", button { id: "add-project",
onclick: move |_| { onclick: move |_| {
projects.write().push(Project { name: "Neues Projekt".into(), content: "".into() }); let mut p = projects.write();
p.push(Project { name: "Neues Projekt".into(), content: "".into() });
let current = p.clone();
spawn(async move { let _ = update_all_projects(current).await; });
}, },
"+ Projekt hinzufügen" "+ Projekt hinzufügen"
} }
} }
// Editor Bereich // EDITOR
div { id: "open-project", div { id: "open-project",
if let Some(idx) = selected_index() { if let Some(idx) = selected_index() {
{ input {
let name = projects.read()[idx].name.clone(); class: "title-input",
let content = projects.read()[idx].content.clone(); value: "{projects.read()[idx].name}",
rsx! { onfocusin: move |_| is_typing.set(true),
input { onfocusout: move |_| is_typing.set(false),
class: "title-input", oninput: move |e| {
value: "{name}", projects.write()[idx].name = e.value();
oninput: move |e| projects.write()[idx].name = e.value() let current = projects.read().clone();
} spawn(async move { let _ = update_all_projects(current).await; });
textarea { }
class: "content-editor", }
value: "{content}", textarea {
placeholder: "Schreibe hier deine Notizen...", class: "content-editor",
oninput: move |e| projects.write()[idx].content = e.value() value: "{projects.read()[idx].content}",
} onfocusin: move |_| is_typing.set(true),
onfocusout: move |_| is_typing.set(false),
oninput: move |e| {
projects.write()[idx].content = e.value();
let current = projects.read().clone();
spawn(async move { let _ = update_all_projects(current).await; });
} }
} }
} else { } else {
@@ -108,19 +121,22 @@ pub fn Hero() -> Element {
} }
} }
// Bestätigungs-Modal // DELETE MODAL
if show_confirm() { if show_confirm() {
div { class: "modal-overlay", div { class: "modal-overlay",
div { class: "modal-box", div { class: "modal-box",
h3 { "Projekt löschen?" } h3 { "Projekt löschen?" }
p { "Diese Aktion kann nicht rückgängig gemacht werden." } p { style: "color: #8b949e;", "Diese Aktion kann nicht rückgängig gemacht werden." }
div { class: "modal-buttons", div { class: "modal-buttons",
button { class: "btn-cancel", onclick: move |_| show_confirm.set(false), "Abbrechen" } button { class: "btn-cancel", onclick: move |_| show_confirm.set(false), "Abbrechen" }
button { button {
class: "btn-confirm", class: "btn-confirm",
onclick: move |_| { onclick: move |_| {
if let Some(i) = index_to_delete() { if let Some(i) = index_to_delete() {
projects.write().remove(i); let mut p = projects.write();
p.remove(i);
let current = p.clone();
spawn(async move { let _ = update_all_projects(current).await; });
selected_index.set(None); selected_index.set(None);
} }
show_confirm.set(false); show_confirm.set(false);