chore(web): update to leptos 0.7

it was painful, but hopefully worth it
everything seems ok, lmk if stuff broke
things will need further improving.....
This commit is contained in:
əlemi 2025-01-17 02:19:52 +01:00
parent 5ed13e41a6
commit fab8742bc1
Signed by: alemi
GPG key ID: A4895B84D311642C
28 changed files with 643 additions and 579 deletions

548
Cargo.lock generated
View file

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "addr2line"
@ -113,6 +113,17 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "any_spawner"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41058deaa38c9d9dd933d6d238d825227cffa668e2839b52879f6619c63eee3b"
dependencies = [
"futures",
"thiserror 2.0.10",
"wasm-bindgen-futures",
]
[[package]]
name = "anyhow"
version = "1.0.95"
@ -144,6 +155,17 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "async-lock"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
dependencies = [
"event-listener",
"event-listener-strategy",
"pin-project-lite",
]
[[package]]
name = "async-recursion"
version = "1.1.1"
@ -205,9 +227,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "attribute-derive"
version = "0.9.2"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f1ee502851995027b06f99f5ffbeffa1406b38d0b318a1ebfa469332c6cbafd"
checksum = "0053e96dd3bec5b4879c23a138d6ef26f2cb936c9cdc96274ac2b9ed44b5bb54"
dependencies = [
"attribute-derive-macro",
"derive-where",
@ -219,14 +241,14 @@ dependencies = [
[[package]]
name = "attribute-derive-macro"
version = "0.9.2"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3601467f634cfe36c4780ca9c75dea9a5b34529c1f2810676a337e7e0997f954"
checksum = "463b53ad0fd5b460af4b1915fe045ff4d946d025fb6c4dc3337752eaa980f71b"
dependencies = [
"collection_literals",
"interpolator",
"manyhow",
"proc-macro-utils 0.8.0",
"proc-macro-utils",
"proc-macro2",
"quote",
"quote-use",
@ -534,33 +556,6 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "ciborium"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
[[package]]
name = "ciborium-ll"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "clap"
version = "4.5.25"
@ -702,6 +697,12 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "const_str_slice_concat"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f67855af358fcb20fac58f9d714c94e2b228fe5694c1c9b4ead4a366343eda1b"
[[package]]
name = "convert_case"
version = "0.6.0"
@ -786,12 +787,6 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "crypto-common"
version = "0.1.6"
@ -837,19 +832,6 @@ dependencies = [
"syn 2.0.95",
]
[[package]]
name = "dashmap"
version = "5.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
dependencies = [
"cfg-if",
"hashbrown 0.14.5",
"lock_api",
"once_cell",
"parking_lot_core",
]
[[package]]
name = "dashmap"
version = "6.1.0"
@ -958,6 +940,15 @@ dependencies = [
"serde",
]
[[package]]
name = "either_of"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2dc0006c5cf511f802ddcffc0a6df9dcc1912f5f0e448f6641b3b035f14f43d"
dependencies = [
"pin-project-lite",
]
[[package]]
name = "encoding_rs"
version = "0.8.35"
@ -1021,6 +1012,16 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "event-listener-strategy"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2"
dependencies = [
"event-listener",
"pin-project-lite",
]
[[package]]
name = "fancy-regex"
version = "0.11.0"
@ -1150,6 +1151,7 @@ dependencies = [
"futures-core",
"futures-task",
"futures-util",
"num_cpus",
]
[[package]]
@ -1318,7 +1320,7 @@ dependencies = [
"gloo-events",
"gloo-utils 0.1.7",
"serde",
"serde-wasm-bindgen 0.5.0",
"serde-wasm-bindgen",
"serde_urlencoded",
"thiserror 1.0.69",
"wasm-bindgen",
@ -1457,6 +1459,12 @@ dependencies = [
"web-sys",
]
[[package]]
name = "guardian"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "493913a18c0d7bebb75127a26a432162c59edbe06f6cf712001e3e769345e8b5"
[[package]]
name = "h2"
version = "0.4.7"
@ -1476,16 +1484,6 @@ dependencies = [
"tracing",
]
[[package]]
name = "half"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
dependencies = [
"cfg-if",
"crunchy",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@ -1533,6 +1531,12 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "hex"
version = "0.4.3"
@ -1657,6 +1661,20 @@ dependencies = [
"tracing",
]
[[package]]
name = "hydration_context"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d35485b3dcbf7e044b8f28c73f04f13e7b509c2466fd10cb2a8a447e38f8a93a"
dependencies = [
"futures",
"once_cell",
"or_poisoned",
"pin-project-lite",
"serde",
"throw_error",
]
[[package]]
name = "hyper"
version = "1.5.2"
@ -1925,15 +1943,6 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8"
[[package]]
name = "inventory"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b31349d02fe60f80bbbab1a9402364cad7460626d6030494b08ac4a2075bf81"
dependencies = [
"rustversion",
]
[[package]]
name = "ipnet"
version = "2.10.1"
@ -1955,6 +1964,15 @@ dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.14"
@ -1992,17 +2010,34 @@ dependencies = [
[[package]]
name = "leptos"
version = "0.6.15"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cbb3237c274dadf00dcc27db96c52601b40375117178fb24a991cda073624f0"
checksum = "21c31c9d022c77702c53e02830d08b28320aca9c0899a19c443096c114623fa5"
dependencies = [
"any_spawner",
"cfg-if",
"either_of",
"futures",
"getrandom",
"hydration_context",
"leptos_config",
"leptos_dom",
"leptos_hot_reload",
"leptos_macro",
"leptos_reactive",
"leptos_server",
"oco_ref",
"or_poisoned",
"paste",
"reactive_graph",
"rustc-hash",
"send_wrapper",
"serde",
"serde_qs",
"server_fn",
"slotmap",
"tachys",
"thiserror 2.0.10",
"throw_error",
"tracing",
"typed-builder",
"typed-builder-macro",
@ -2012,12 +2047,11 @@ dependencies = [
[[package]]
name = "leptos-use"
version = "0.13.13"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "789bf9f4337e6ebd8f1b407e3f762fdc538d48dc145f9d1dce2338014b38f4dd"
checksum = "bb81bee40ad4fd7c0e8a84ed4b4396b27928d8d91a271c3cca9de6ba58f1cafc"
dependencies = [
"cfg-if",
"chrono",
"codee",
"cookie",
"default-struct-builder",
@ -2027,7 +2061,8 @@ dependencies = [
"lazy_static",
"leptos",
"paste",
"thiserror 1.0.69",
"send_wrapper",
"thiserror 2.0.10",
"unic-langid",
"wasm-bindgen",
"wasm-bindgen-futures",
@ -2036,52 +2071,38 @@ dependencies = [
[[package]]
name = "leptos_config"
version = "0.6.15"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62ed778611380ddea47568ac6ad6ec5158d39b5bd59e6c4dcd24efc15dc3dc0d"
checksum = "5d874993c7664d757677d056c8f46b5cb5365fe622005e1bf26050f4996e7e52"
dependencies = [
"config",
"regex",
"serde",
"thiserror 1.0.69",
"thiserror 2.0.10",
"typed-builder",
]
[[package]]
name = "leptos_dom"
version = "0.6.15"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8401c46c86c1f4c16dcb7881ed319fcdca9cda9b9e78a6088955cb423afcf119"
checksum = "a462aaeec85bc4ecfb26bf324437b92690bf3add1e30eb29b3acc08b20e8b4cb"
dependencies = [
"async-recursion",
"cfg-if",
"drain_filter_polyfill",
"futures",
"getrandom",
"html-escape",
"indexmap",
"itertools",
"js-sys",
"leptos_reactive",
"once_cell",
"pad-adapter",
"paste",
"rustc-hash",
"serde",
"serde_json",
"server_fn",
"smallvec",
"or_poisoned",
"reactive_graph",
"send_wrapper",
"tachys",
"tracing",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "leptos_hot_reload"
version = "0.6.15"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cb53d4794240b684a2f4be224b84bee9e62d2abc498cf2bcd643cd565e01d96"
checksum = "07eb295ad2f3b2af190da62af339b84fd01ce3c71702f09eb69a57310fcf0c6d"
dependencies = [
"anyhow",
"camino",
@ -2097,15 +2118,15 @@ dependencies = [
[[package]]
name = "leptos_macro"
version = "0.6.15"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b13bc3db70715cd8218c4535a5af3ae3c0e5fea6f018531fc339377b36bc0e0"
checksum = "90291b25ee576bc9c299d3371cc8f09bf60ea939a8de61fa8b744650aff76e24"
dependencies = [
"attribute-derive",
"cfg-if",
"convert_case",
"html-escape",
"itertools",
"itertools 0.13.0",
"leptos_hot_reload",
"prettyplease",
"proc-macro-error2",
@ -2119,71 +2140,59 @@ dependencies = [
]
[[package]]
name = "leptos_reactive"
version = "0.6.15"
name = "leptos_router"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4161acbf80f59219d8d14182371f57302bc7ff81ee41aba8ba1ff7295727f23"
checksum = "9a193dbd62b9617a5d7d199ea70c570da01a1bbe798e617373b6351845be6778"
dependencies = [
"base64",
"cfg-if",
"any_spawner",
"either_of",
"futures",
"indexmap",
"gloo-net 0.6.0",
"js-sys",
"oco_ref",
"paste",
"pin-project",
"rustc-hash",
"self_cell",
"serde",
"serde-wasm-bindgen 0.6.5",
"serde_json",
"slotmap",
"thiserror 1.0.69",
"leptos",
"leptos_router_macro",
"once_cell",
"or_poisoned",
"reactive_graph",
"send_wrapper",
"tachys",
"thiserror 2.0.10",
"tracing",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "leptos_router"
version = "0.6.15"
name = "leptos_router_macro"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d71dea7d42c0d29c40842750232d3425ed1cf10e313a1f898076d20871dad32"
checksum = "34bc3f80ad810b22058f12d278bb0bf929779cc0bc1289a06980d896f62743f0"
dependencies = [
"cfg-if",
"gloo-net 0.6.0",
"itertools",
"js-sys",
"lazy_static",
"leptos",
"linear-map",
"once_cell",
"percent-encoding",
"send_wrapper",
"serde",
"serde_json",
"serde_qs 0.13.0",
"thiserror 1.0.69",
"tracing",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"proc-macro-error2",
"proc-macro2",
"quote",
]
[[package]]
name = "leptos_server"
version = "0.6.15"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a97eb90a13f71500b831c7119ddd3bdd0d7ae0a6b0487cade4fddeed3b8c03f"
checksum = "18caffe32c245ddb35697edd898ccb3393efce67672a707a14eebd0db2e8249a"
dependencies = [
"inventory",
"lazy_static",
"leptos_macro",
"leptos_reactive",
"any_spawner",
"base64",
"codee",
"futures",
"hydration_context",
"or_poisoned",
"reactive_graph",
"send_wrapper",
"serde",
"serde_json",
"server_fn",
"thiserror 1.0.69",
"tachys",
"tracing",
]
@ -2215,10 +2224,6 @@ name = "linear-map"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee"
dependencies = [
"serde",
"serde_test",
]
[[package]]
name = "linked-hash-map"
@ -2266,9 +2271,9 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
[[package]]
name = "manyhow"
version = "0.10.4"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91ea592d76c0b6471965708ccff7e6a5d277f676b90ab31f4d3f3fc77fade64"
checksum = "b33efb3ca6d3b07393750d4030418d594ab1139cee518f0dc88db70fec873587"
dependencies = [
"manyhow-macros",
"proc-macro2",
@ -2278,11 +2283,11 @@ dependencies = [
[[package]]
name = "manyhow-macros"
version = "0.10.4"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c64621e2c08f2576e4194ea8be11daf24ac01249a4f53cd8befcbb7077120ead"
checksum = "46fce34d199b78b6e6073abf984c9cf5fd3e9330145a93ee0738a7443e371495"
dependencies = [
"proc-macro-utils 0.8.0",
"proc-macro-utils",
"proc-macro2",
"quote",
]
@ -2427,6 +2432,12 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
[[package]]
name = "next_tuple"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60993920e071b0c9b66f14e2b32740a4e27ffc82854dcd72035887f336a09a28"
[[package]]
name = "nodeinfo"
version = "0.0.2"
@ -2520,6 +2531,16 @@ dependencies = [
"libm",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "object"
version = "0.36.7"
@ -2531,9 +2552,9 @@ dependencies = [
[[package]]
name = "oco_ref"
version = "0.1.1"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c51ebcefb2f0b9a5e0bea115532c8ae4215d1b01eff176d0f4ba4192895c2708"
checksum = "64b94982fe39a861561cf67ff17a7849f2cedadbbad960a797634032b7abb998"
dependencies = [
"serde",
"thiserror 1.0.69",
@ -2611,6 +2632,12 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "or_poisoned"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c04f5d74368e4d0dfe06c45c8627c81bd7c317d52762d118fb9b3076f6420fd"
[[package]]
name = "ordered-float"
version = "3.9.2"
@ -2638,7 +2665,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39b0deead1528fd0e5947a8546a9642a9777c25f6e1e26f34c97b204bbb465bd"
dependencies = [
"heck 0.4.1",
"itertools",
"itertools 0.12.1",
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
@ -2651,12 +2678,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "pad-adapter"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56d80efc4b6721e8be2a10a5df21a30fa0b470f1539e53d8b4e6e75faf938b63"
[[package]]
name = "parking"
version = "2.2.1"
@ -2927,17 +2948,6 @@ dependencies = [
"syn 2.0.95",
]
[[package]]
name = "proc-macro-utils"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f59e109e2f795a5070e69578c4dc101068139f74616778025ae1011d4cd41a8"
dependencies = [
"proc-macro2",
"quote",
"smallvec",
]
[[package]]
name = "proc-macro-utils"
version = "0.10.0"
@ -3025,7 +3035,7 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82ebfb7faafadc06a7ab141a6f67bcfb24cb8beb158c6fe933f2f035afa99f35"
dependencies = [
"proc-macro-utils 0.10.0",
"proc-macro-utils",
"proc-macro2",
"quote",
"syn 2.0.95",
@ -3067,6 +3077,56 @@ dependencies = [
"getrandom",
]
[[package]]
name = "reactive_graph"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fbf210c04505e128fb7f64acecc23c71f82f56c7d481b190e1010b7bada2cb9"
dependencies = [
"any_spawner",
"async-lock",
"futures",
"guardian",
"hydration_context",
"or_poisoned",
"pin-project-lite",
"rustc-hash",
"send_wrapper",
"serde",
"slotmap",
"thiserror 2.0.10",
"tracing",
"web-sys",
]
[[package]]
name = "reactive_stores"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80bb1913eeb71f74028213455ee971550c2b3cb91b6acd5efa8a0f8dc59f5039"
dependencies = [
"guardian",
"itertools 0.13.0",
"or_poisoned",
"paste",
"reactive_graph",
"reactive_stores_macro",
"rustc-hash",
]
[[package]]
name = "reactive_stores_macro"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d86e4f08f361b05d11422398cef4bc4cf356f2fdd2f06a96646b0e9cd902226"
dependencies = [
"convert_case",
"proc-macro-error2",
"proc-macro2",
"quote",
"syn 2.0.95",
]
[[package]]
name = "redox_syscall"
version = "0.5.8"
@ -3239,10 +3299,11 @@ dependencies = [
[[package]]
name = "rstml"
version = "0.11.2"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe542870b8f59dd45ad11d382e5339c9a1047cde059be136a7016095bbdefa77"
checksum = "51187e564f12336ef40cd04f6f4d805d6919188001dcf1e0a021898ea0fe28ce"
dependencies = [
"derive-where",
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
@ -3275,9 +3336,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustc-hash"
version = "1.1.0"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497"
[[package]]
name = "rustix"
@ -3556,12 +3617,6 @@ dependencies = [
"libc",
]
[[package]]
name = "self_cell"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe"
[[package]]
name = "send_wrapper"
version = "0.6.0"
@ -3602,17 +3657,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "serde-wasm-bindgen"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
dependencies = [
"js-sys",
"serde",
"wasm-bindgen",
]
[[package]]
name = "serde_default"
version = "0.2.0"
@ -3667,17 +3711,6 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_qs"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c"
dependencies = [
"percent-encoding",
"serde",
"thiserror 1.0.69",
]
[[package]]
name = "serde_qs"
version = "0.13.0"
@ -3698,15 +3731,6 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_test"
version = "1.0.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f901ee573cab6b3060453d2d5f0bae4e6d628c23c0a962ff9b5f1d7c8d4f1ed"
dependencies = [
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
@ -3721,25 +3745,26 @@ dependencies = [
[[package]]
name = "server_fn"
version = "0.6.15"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fae7a3038a32e5a34ba32c6c45eb4852f8affaf8b794ebfcd4b1099e2d62ebe"
checksum = "f5dd7fcccd3ef2081da086c1f8595b506627abbbbc9f64be0141d2251219570e"
dependencies = [
"bytes",
"ciborium",
"const_format",
"dashmap 5.5.3",
"dashmap",
"futures",
"gloo-net 0.6.0",
"http 1.2.0",
"js-sys",
"once_cell",
"pin-project-lite",
"send_wrapper",
"serde",
"serde_json",
"serde_qs 0.12.0",
"serde_qs",
"server_fn_macro_default",
"thiserror 1.0.69",
"thiserror 2.0.10",
"throw_error",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
@ -3750,9 +3775,9 @@ dependencies = [
[[package]]
name = "server_fn_macro"
version = "0.6.15"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faaaf648c6967aef78177c0610478abb5a3455811f401f3c62d10ae9bd3901a1"
checksum = "e0bbac4f01a714b0490247ac625bdb7055548210556c39e8f56a2dbbe3abc70b"
dependencies = [
"const_format",
"convert_case",
@ -3764,9 +3789,9 @@ dependencies = [
[[package]]
name = "server_fn_macro_default"
version = "0.6.15"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f2aa8119b558a17992e0ac1fd07f080099564f24532858811ce04f742542440"
checksum = "f07dfd1744a5f5612f00f69fe035b0bfafdf12bb46d76e785673078a9e56b170"
dependencies = [
"server_fn_macro",
"syn 2.0.95",
@ -3902,7 +3927,6 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
dependencies = [
"serde",
"version_check",
]
@ -4406,6 +4430,39 @@ dependencies = [
"libc",
]
[[package]]
name = "tachys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d777e4426a597296b020edcb5c3d8f25a3ccd8adfd22eb5154ac81da946aef9f"
dependencies = [
"any_spawner",
"const_str_slice_concat",
"drain_filter_polyfill",
"either_of",
"futures",
"html-escape",
"indexmap",
"itertools 0.13.0",
"js-sys",
"linear-map",
"next_tuple",
"oco_ref",
"once_cell",
"or_poisoned",
"parking_lot",
"paste",
"reactive_graph",
"reactive_stores",
"rustc-hash",
"send_wrapper",
"slotmap",
"throw_error",
"tracing",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "tap"
version = "1.0.1"
@ -4497,6 +4554,15 @@ dependencies = [
"once_cell",
]
[[package]]
name = "throw_error"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4ef8bf264c6ae02a065a4a16553283f0656bd6266fc1fcb09fd2e6b5e91427b"
dependencies = [
"pin-project-lite",
]
[[package]]
name = "time"
version = "0.3.37"
@ -4799,18 +4865,18 @@ checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
[[package]]
name = "typed-builder"
version = "0.18.2"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77739c880e00693faef3d65ea3aad725f196da38b22fdc7ea6ded6e1ce4d3add"
checksum = "7e14ed59dc8b7b26cacb2a92bad2e8b1f098806063898ab42a3bd121d7d45e75"
dependencies = [
"typed-builder-macro",
]
[[package]]
name = "typed-builder-macro"
version = "0.18.2"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f718dfaf347dcb5b983bfc87608144b0bad87970aebcbea5ce44d2a30c08e63"
checksum = "560b82d656506509d43abe30e0ba64c56b1953ab3d4fe7ba5902747a7a3cedd5"
dependencies = [
"proc-macro2",
"quote",
@ -4922,7 +4988,6 @@ dependencies = [
"sha2",
"sha256",
"thiserror 2.0.10",
"tokio",
"toml",
"tracing",
"uriproxy",
@ -4957,7 +5022,6 @@ dependencies = [
"chrono",
"clap",
"futures",
"mdhtml",
"openssl",
"reqwest",
"sea-orm",
@ -4998,7 +5062,6 @@ dependencies = [
"tower-http",
"tracing",
"upub",
"uriproxy",
]
[[package]]
@ -5011,7 +5074,7 @@ dependencies = [
"codee",
"console_error_panic_hook",
"cookie",
"dashmap 6.1.0",
"dashmap",
"futures",
"jrd",
"lazy_static",
@ -5038,7 +5101,6 @@ name = "upub-worker"
version = "0.3.0"
dependencies = [
"apb",
"async-trait",
"chrono",
"mdhtml",
"regex",

View file

@ -24,10 +24,10 @@ serde_json = "1.0"
serde_default = "0.2"
serde-inline-default = "0.2"
dashmap = "6.1"
leptos = { version = "0.6.15", features = ["csr", "tracing"] } # locked because upgrading is hell
leptos_router = { version = "0.6.15", features = ["csr"] } # locked because upgrading is hell
leptos-use = "0.13.13" # locked because upgrading is hell
codee = { version = "0.2", features = ["json_serde"] }
leptos = { version = "0.7", features = ["csr", "tracing"] }
leptos_router = { version = "0.7", features = ["tracing"] }
leptos-use = "0.15"
codee = { version = "0.2", features = ["json_serde"] } # WHYYY LEPTOS-USE AKSJFOASHGOAEG
reqwest = { version = "0.12", features = ["json"] }
apb = { path = "../apb", features = ["unstructured", "activitypub-fe", "activitypub-counters", "litepub", "did-core"] }
uriproxy = { path = "../utils/uriproxy/" }

View file

@ -1,4 +1,4 @@
use leptos::*;
use leptos::prelude::*;
use crate::prelude::*;
use apb::{Activity, ActivityMut, Base, Object};
@ -36,7 +36,7 @@ pub fn ActivityLine(activity: crate::Object, children: Children) -> impl IntoVie
{kind.as_ref().to_string()}
</a>
{activity_url}
<PrivacyMarker privacy=privacy to=&to cc=&cc />
<PrivacyMarker privacy=privacy to=to cc=cc />
</code>
</td>
</tr>
@ -62,7 +62,7 @@ pub fn Item(
match item.object_type().unwrap_or(apb::ObjectType::Object) {
// special case for placeholder activities
apb::ObjectType::Note | apb::ObjectType::Document(_) =>
Some(view! { <Object object=item.clone() />{sep.clone()} }.into_view()),
Some(view! { <Object object=item.clone() />{sep.clone()} }.into_any()),
// everything else
apb::ObjectType::Activity(t) => {
let object_id = item.object().id().unwrap_or_default();
@ -70,7 +70,7 @@ pub fn Item(
apb::ActivityType::Create | apb::ActivityType::Announce =>
cache::OBJECTS.get(&object_id).map(|obj| {
view! { <Object object=obj /> }
}.into_view()),
}.into_any()),
apb::ActivityType::Follow =>
cache::OBJECTS.get(&object_id).map(|obj| {
view! {
@ -79,11 +79,11 @@ pub fn Item(
<FollowRequestButtons activity_id=id.clone() actor_id=object_id />
</div>
}
}.into_view()),
}.into_any()),
_ => None,
};
if !seen {
let (not_seen, not_seen_tx) = create_signal(!seen);
let (not_seen, not_seen_tx) = signal(!seen);
let id = id.clone();
Some(view! {
<div class:notification=not_seen>
@ -97,7 +97,7 @@ pub fn Item(
{object}
</div>
{sep.clone()}
}.into_view())
}.into_any())
} else {
Some(view! {
<div>
@ -105,11 +105,11 @@ pub fn Item(
{object}
</div>
{sep.clone()}
}.into_view())
}.into_any())
}
},
// should never happen
t => Some(view! { <p><code>type not implemented : {t.as_ref().to_string()}</code></p> }.into_view()),
t => Some(view! { <p><code>type not implemented : {t.as_ref().to_string()}</code></p> }.into_any()),
}
}
}
@ -128,7 +128,7 @@ fn AckBtn(id: String, tx: WriteSignal<bool>) -> impl IntoView {
.set_activity_type(Some(apb::ActivityType::View))
.set_object(apb::Node::link(id.clone()));
let id = id.clone();
spawn_local(async move {
leptos::task::spawn_local(async move {
if let Err(e) = Http::post(&auth.outbox(), &payload, auth).await {
tracing::error!("failed marking notification as seen: {e}");
} else {

View file

@ -1,5 +1,5 @@
use leptos::*;
use leptos_router::*;
use leptos::prelude::*;
use leptos_router::hooks::use_params;
use crate::prelude::*;
use std::sync::Arc;
@ -11,7 +11,8 @@ pub fn FollowList(outgoing: bool) -> impl IntoView {
let follow___ = if outgoing { "following" } else { "followers" };
let params = use_params::<IdParam>();
let auth = use_context::<Auth>().expect("missing auth context");
let resource = create_local_resource(
// TODO this was a LocalResource!
let resource = Resource::new(
move || params.get().ok().and_then(|x| x.id).unwrap_or_default(),
move |id| {
async move {
@ -28,10 +29,10 @@ pub fn FollowList(outgoing: bool) -> impl IntoView {
view! {
<div class="tl ml-3-r mr-3-r pl-1 pt-1 pb-1">
{move || match resource.get() {
None => view! { <Loader /> }.into_view(),
None => view! { <Loader /> }.into_any(),
Some(Err(e)) => {
tracing::error!("could not load followers: {e}");
view! { <code class="cw center color">{follow___}" unavailable"</code> }.into_view()
view! { <code class="cw center color">{follow___}" unavailable"</code> }.into_any()
},
Some(Ok(mut arr)) => {
// TODO cheap fix: server gives us follows from oldest to newest
@ -50,10 +51,10 @@ pub fn FollowList(outgoing: bool) -> impl IntoView {
view! {
<ActorBanner object=actor />
<hr />
}.into_view()
}.into_any()
}
/ >
}.into_view()
}.into_any()
},
}}
</div>

View file

@ -1,5 +1,5 @@
use leptos::*;
use leptos_router::*;
use leptos::{either::Either, prelude::*, reactive::signal::signal};
use leptos_router::{components::Outlet, hooks::use_params};
use crate::{app::FeedRoute, prelude::*, FALLBACK_IMAGE_URL};
use apb::{ActivityMut, Actor, Base, Object, ObjectMut, Shortcuts};
@ -11,8 +11,9 @@ pub fn ActorHeader() -> impl IntoView {
let config = use_context::<Signal<crate::Config>>().expect("missing config context");
let relevant_tl = use_context::<Signal<Option<Timeline>>>().expect("missing relevant timeline context");
let matched_route = use_context::<ReadSignal<crate::app::FeedRoute>>().expect("missing route context");
let (loading, set_loading) = create_signal(false);
let actor = create_local_resource(
let (loading, set_loading) = signal(false);
// TODO this was a LocalResource!
let actor = Resource::new(
move || params.get().ok().and_then(|x| x.id).unwrap_or_default(),
move |id| {
async move {
@ -27,8 +28,8 @@ pub fn ActorHeader() -> impl IntoView {
}
);
move || match actor.get() {
None => view! { <Loader /> }.into_view(),
Some(None) => view! { <code class="center cw color">"could not resolve user"</code> }.into_view(),
None => view! { <Loader /> }.into_any(),
Some(None) => view! { <code class="center cw color">"could not resolve user"</code> }.into_any(),
Some(Some(actor)) => {
let avatar_url = actor.icon_url().unwrap_or(FALLBACK_IMAGE_URL.into());
let background_url = actor.image_url().unwrap_or(FALLBACK_IMAGE_URL.into());
@ -104,7 +105,7 @@ pub fn ActorHeader() -> impl IntoView {
<small class="mr-s">following</small>
</span>
</a>
}.into_view()
}.into_any()
} else {
view! {
<a class="clean dim" href="#follow" on:click=move |_| send_follow_request(_uid.clone())>
@ -113,7 +114,7 @@ pub fn ActorHeader() -> impl IntoView {
<small class="mr-s">follow</small>
</span>
</a>
}.into_view()
}.into_any()
}}
</div>
</div>
@ -137,14 +138,14 @@ pub fn ActorHeader() -> impl IntoView {
</span>
{move || if auth.present() {
if loading.get() {
Some(view! {
Some(Either::Left(view! {
<span style="float: right">
<span class="hidden-on-mobile">"fetching "</span><span class="dots"></span>
</span>
})
}))
} else {
let uid = __uid.clone();
Some(view! {
Some(Either::Right(view! {
<span style="float: right">
<a
class="clean"
@ -154,7 +155,7 @@ pub fn ActorHeader() -> impl IntoView {
<span class="emoji ml-2">""</span><span class="hidden-on-mobile">"fetch"</span>
</a>
</span>
})
}))
}
} else {
None
@ -162,7 +163,7 @@ pub fn ActorHeader() -> impl IntoView {
</p>
<hr class="color" />
<Outlet />
}.into_view()
}.into_any()
},
}
}
@ -180,7 +181,7 @@ async fn send_follow_response(kind: apb::ActivityType, target: String, to: Strin
fn send_follow_request(target: String) {
let auth = use_context::<Auth>().expect("missing auth context");
spawn_local(async move {
leptos::task::spawn_local(async move {
let payload = apb::new()
.set_activity_type(Some(apb::ActivityType::Follow))
.set_object(apb::Node::link(target.clone()))
@ -193,7 +194,7 @@ fn send_follow_request(target: String) {
fn unfollow(target: String) {
let auth = use_context::<Auth>().expect("missing auth context");
spawn_local(async move {
leptos::task::spawn_local(async move {
let payload = apb::new()
.set_activity_type(Some(apb::ActivityType::Undo))
.set_to(apb::Node::links(vec![target.clone()]))
@ -208,11 +209,11 @@ fn unfollow(target: String) {
})
}
fn fetch_cb(ev: ev::MouseEvent, set_loading: WriteSignal<bool>, uid: String, auth: Auth, config: Signal<crate::Config>, relevant_tl: Signal<Option<Timeline>>) {
fn fetch_cb(ev: leptos::ev::MouseEvent, set_loading: WriteSignal<bool>, uid: String, auth: Auth, config: Signal<crate::Config>, relevant_tl: Signal<Option<Timeline>>) {
let api = Uri::api(U::Actor, &uid, false);
ev.prevent_default();
set_loading.set(true);
spawn_local(async move {
leptos::task::spawn_local(async move {
if let Err(e) = Http::fetch::<serde_json::Value>(&format!("{api}/outbox?fetch=true"), auth).await {
tracing::error!("failed fetching outbox for {uid}: {e}");
}

View file

@ -1,12 +1,12 @@
use leptos::*;
use leptos_router::*;
use leptos::prelude::*;
use leptos_router::hooks::use_params;
use crate::prelude::*;
#[component]
pub fn ActorPosts() -> impl IntoView {
let feeds = use_context::<Feeds>().expect("missing feeds context");
let params = use_params::<IdParam>();
create_effect(move |_| {
Effect::new(move |_| {
let id = params.get().ok().and_then(|x| x.id).unwrap_or_default();
let tl_url = format!("{}/outbox/page", Uri::api(U::Actor, &id, false));
if !feeds.user.next.get_untracked().starts_with(&tl_url) {
@ -22,7 +22,7 @@ pub fn ActorPosts() -> impl IntoView {
pub fn ActorLikes() -> impl IntoView {
let feeds = use_context::<Feeds>().expect("missing feeds context");
let params = use_params::<IdParam>();
create_effect(move |_| {
Effect::new(move |_| {
let id = params.get().ok().and_then(|x| x.id).unwrap_or_default();
let likes_url = format!("{}/likes/page", Uri::api(U::Actor, &id, false));
if !feeds.user.next.get_untracked().starts_with(&likes_url) {

View file

@ -1,9 +1,8 @@
use apb::Collection;
use leptos::*;
use leptos_router::*;
use leptos::{either::Either, prelude::*};
use leptos_router::{components::*, hooks::use_location, path};
use crate::prelude::*;
use codee::string::{FromToStringCodec, JsonSerdeCodec};
use leptos_use::{
signal_debounced, storage::use_local_storage, use_cookie_with_options, use_element_size, use_window_scroll,
UseCookieOptions, UseElementSizeReturn
@ -59,31 +58,30 @@ impl Feeds {
}
}
#[component]
pub fn App() -> impl IntoView {
let (token, set_token) = use_cookie_with_options::<String, FromToStringCodec>(
let (token, set_token) = use_cookie_with_options::<String, codee::string::FromToStringCodec>(
"token",
UseCookieOptions::default()
.same_site(cookie::SameSite::Strict)
// .secure(true)
.path("/")
);
let (userid, set_userid) = use_cookie_with_options::<String, FromToStringCodec>(
let (userid, set_userid) = use_cookie_with_options::<String, codee::string::FromToStringCodec>(
"user_id",
UseCookieOptions::default()
.same_site(cookie::SameSite::Strict)
// .secure(true)
.path("/")
);
let (config, set_config, _) = use_local_storage::<crate::Config, JsonSerdeCodec>("config");
let (config, set_config, _) = use_local_storage::<crate::Config, codee::string::JsonSerdeCodec>("config");
let (privacy, set_privacy) = create_signal(Privacy::Private);
let (privacy, set_privacy) = signal(Privacy::Private);
let auth = Auth { token, userid };
let (be_version, set_be_version) = create_signal("?.?.?".to_string());
spawn_local(async move {
let (be_version, set_be_version) = signal("?.?.?".to_string());
leptos::task::spawn_local(async move {
match Http::fetch::<serde_json::Value>(&format!("{URL_BASE}/nodeinfo/2.0.json"), auth).await {
Err(e) => tracing::error!("failed fetching backend version: {e} - {e:?}"),
Ok(nodeinfo) => {
@ -115,19 +113,19 @@ pub fn App() -> impl IntoView {
let screen_width = document().body().map(|x| x.client_width()).unwrap_or_default();
tracing::info!("detected width of {screen_width}");
let (menu, set_menu) = create_signal(screen_width < 768);
let (advanced, set_advanced) = create_signal(false);
let (menu, set_menu) = signal(screen_width < 768);
let (advanced, set_advanced) = signal(false);
let title_target = move || if auth.present() { "/web/home" } else { "/web/global" };
// refresh token immediately and every hour
let refresh_token = move || spawn_local(async move { Auth::refresh(auth.token, set_token, set_userid).await; });
let refresh_token = move || leptos::task::spawn_local(async move { Auth::refresh(auth.token, set_token, set_userid).await; });
refresh_token();
set_interval(refresh_token, std::time::Duration::from_secs(3600));
// refresh notifications
let (notifications, set_notifications) = create_signal(0);
let fetch_notifications = move || spawn_local(async move {
let (notifications, set_notifications) = signal(0);
let fetch_notifications = move || leptos::task::spawn_local(async move {
let actor_id = userid.get_untracked().unwrap_or_default();
let notif_url = format!("{actor_id}/notifications");
match Http::fetch::<serde_json::Value>(&notif_url, auth).await {
@ -162,60 +160,57 @@ pub fn App() -> impl IntoView {
<div class:hidden=move || !auth.present() >
<PrivacySelector setter=set_privacy />
<hr class="mt-1 mb-1" />
{move || if advanced.get() { view! {
{move || if advanced.get() { Either::Left(view! {
<AdvancedPostBox advanced=set_advanced/>
}} else { view! {
})} else { Either::Right(view! {
<PostBox advanced=set_advanced/>
}}}
})}}
<hr class="only-on-mobile sep mb-0 pb-0" />
</div>
</div>
<div class="col-main" class:w-100=menu >
<Router // TODO maybe set base="/web" ?
trailing_slash=TrailingSlash::Redirect
fallback=|| view! { <NotFound /> }
>
<Router>
<main>
<Routes>
<Route path="/" view=move || view! { <Redirect path="/web" /> } />
<Route path="/web" view=Scrollable >
<Route path="" view=move ||
<Routes fallback=NotFound>
<Route path=path!("/") view=move || view! { <Redirect path="/web" /> } />
<ParentRoute path=path!("/web") view=Scrollable >
<Route path=path!("") view=move ||
if auth.present() {
view! { <Redirect path="home" /> }
} else {
view! { <Redirect path="global" /> }
}
/>
<Route path="home" view=move || view! { <Feed tl=feeds.home /> } />
<Route path="global" view=move || view! { <Feed tl=feeds.global /> } />
<Route path="local" view=move || view! { <Feed tl=feeds.server /> } />
<Route path="notifications" view=move || view! { <Feed tl=feeds.notifications ignore_filters=true /> } />
<Route path="tags/:id" view=move || view! { <HashtagFeed tl=feeds.tag /> } />
<Route path=path!("home") view=move || view! { <Feed tl=feeds.home /> } />
<Route path=path!("global") view=move || view! { <Feed tl=feeds.global /> } />
<Route path=path!("local") view=move || view! { <Feed tl=feeds.server /> } />
<Route path=path!("notifications") view=move || view! { <Feed tl=feeds.notifications ignore_filters=true /> } />
<Route path=path!("tags/:id") view=move || view! { <HashtagFeed tl=feeds.tag /> } />
<Route path="about" view=AboutPage />
<Route path="config" view=move || view! { <ConfigPage setter=set_config /> } />
<Route path="explore" view=DebugPage />
<Route path=path!("about") view=AboutPage />
<Route path=path!("config") view=move || view! { <ConfigPage setter=set_config /> } />
<Route path=path!("explore") view=DebugPage />
<Route path="actors/:id" view=ActorHeader > // TODO can we avoid this?
<Route path="" view=ActorPosts />
<Route path="likes" view=ActorLikes />
<Route path="following" view=move || view! { <FollowList outgoing=true /> } />
<Route path="followers" view=move || view! { <FollowList outgoing=false /> } />
</Route>
<ParentRoute path=path!("actors/:id") view=ActorHeader > // TODO can we avoid this?
<Route path=path!("") view=ActorPosts />
<Route path=path!("likes") view=ActorLikes />
<Route path=path!("following") view=move || view! { <FollowList outgoing=true /> } />
<Route path=path!("followers") view=move || view! { <FollowList outgoing=false /> } />
</ParentRoute>
<Route path="objects/:id" view=ObjectView >
<Route path="" view=ObjectContext />
<Route path="replies" view=ObjectReplies />
<Route path="likes" view=ObjectLikes />
<ParentRoute path=path!("objects/:id") view=ObjectView >
<Route path=path!("") view=ObjectContext />
<Route path=path!("replies") view=ObjectReplies />
<Route path=path!("likes") view=ObjectLikes />
// <Route path="announced" view=ObjectAnnounced />
</Route>
</ParentRoute>
// <Route path="/web/activities/:id" view=move || view! { <ActivityPage tl=context_tl /> } />
<Route path="search" view=SearchPage />
<Route path="register" view=RegisterPage />
</Route>
<Route path=path!("search") view=SearchPage />
<Route path=path!("register") view=RegisterPage />
</ParentRoute>
</Routes>
</main>
</Router>
@ -244,7 +239,7 @@ fn Scrollable() -> impl IntoView {
// TODO this is terrible!! omg maybe it should receive from context current timeline?? idk this
// is awful and i patched it another time instead of doing it properly...
// at least im going to provide a route enum to use in other places
let (route, set_route) = create_signal(FeedRoute::Home);
let (route, set_route) = signal(FeedRoute::Home);
let relevant_timeline = Signal::derive(move || {
let path = location.pathname.get();
if path.contains("/web/home") {
@ -325,7 +320,7 @@ fn Scrollable() -> impl IntoView {
None => "?".to_string(),
}
});
let element = create_node_ref();
let element = NodeRef::new();
let should_load = use_scroll_limit(element, 500.0);
provide_context(should_load);
view! {
@ -361,21 +356,23 @@ pub fn Loader() -> impl IntoView {
}
}
pub fn use_scroll_limit<El, T>(el: El, offset: f64) -> Signal<bool>
where
El: Into<leptos_use::core::ElementMaybeSignal<T, web_sys::Element>> + Clone + 'static,
T: Into<web_sys::Element> + Clone + 'static,
pub fn use_scroll_limit<T, Marker>(el: NodeRef<T>, offset: f64) -> Signal<bool>
where
T: leptos::html::ElementType,
NodeRef<T>: leptos_use::core::IntoElementMaybeSignal<web_sys::Element, Marker>,
{
let (load, set_load) = create_signal(false);
let (load, set_load) = signal(false);
let (_x, y) = use_window_scroll();
let UseElementSizeReturn { height: screen_height, .. } = use_element_size("html");
let html_node_ref = NodeRef::new();
leptos::html::html().node_ref(html_node_ref);
let UseElementSizeReturn { height: screen_height, .. } = use_element_size(html_node_ref);
let UseElementSizeReturn { height, .. } = use_element_size(el);
let scroll_state = Signal::derive(move || (y.get(), height.get(), screen_height.get()));
let scroll_state_throttled = signal_debounced(
scroll_state,
50.
);
let _ = watch(
let _ = Effect::watch(
move || scroll_state_throttled.get(),
move |(y, height, screen), _, _| {
let before = load.get();

View file

@ -1,4 +1,4 @@
use leptos::*;
use leptos::prelude::*;
use reqwest::Method;
use crate::{components::AuthResponse, URL_BASE};
@ -28,7 +28,7 @@ impl Auth {
}
pub fn present(&self) -> bool {
self.token.get().map_or(false, |x| !x.is_empty())
self.token.get().is_some_and(|x| !x.is_empty())
}
pub fn outbox(&self) -> String {

View file

@ -1,4 +1,4 @@
use leptos::*;
use leptos::prelude::*;
use crate::prelude::*;
#[component]
@ -8,8 +8,8 @@ pub fn LoginBox(
) -> impl IntoView {
let auth = use_context::<Auth>().expect("missing auth context");
let config = use_context::<Signal<crate::Config>>().expect("missing config context");
let username_ref: NodeRef<html::Input> = create_node_ref();
let password_ref: NodeRef<html::Input> = create_node_ref();
let username_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let password_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let feeds = use_context::<Feeds>().expect("missing feeds context");
view! {
<div>
@ -25,10 +25,10 @@ pub fn LoginBox(
<div class:hidden=move || auth.present() >
<form on:submit=move|ev| {
ev.prevent_default();
logging::log!("logging in...");
tracing::info!("logging in...");
let email = username_ref.get().map(|x| x.value()).unwrap_or("".into());
let password = password_ref.get().map(|x| x.value()).unwrap_or("".into());
spawn_local(async move {
leptos::task::spawn_local(async move {
let Ok(res) = reqwest::Client::new()
.post(format!("{URL_BASE}/auth"))
.json(&LoginForm { email, password })
@ -39,7 +39,7 @@ pub fn LoginBox(
.json::<AuthResponse>()
.await
else { if let Some(rf) = password_ref.get() { rf.set_value("") }; return };
logging::log!("logged in until {}", auth_response.expires);
tracing::info!("logged in until {}", auth_response.expires);
// update our username and token cookies
let username = auth_response.user.split('/').last().unwrap_or_default().to_string();
userid_tx.set(Some(auth_response.user));

View file

@ -10,7 +10,7 @@ pub use user::*;
mod post;
pub use post::*;
use leptos::*;
use leptos::prelude::*;
#[component]
pub fn DateTime(t: Option<chrono::DateTime<chrono::Utc>>) -> impl IntoView {
@ -38,10 +38,10 @@ pub fn DateTime(t: Option<chrono::DateTime<chrono::Utc>>) -> impl IntoView {
}
#[component]
pub fn PrivacyMarker<'a>(
pub fn PrivacyMarker(
privacy: Privacy,
#[prop(optional)] to: &'a [String],
#[prop(optional)] cc: &'a [String],
#[prop(optional)] to: Vec<String>,
#[prop(optional)] cc: Vec<String>,
#[prop(optional)] big: bool,
) -> impl IntoView {
let to_txt = if to.is_empty() { String::new() } else { format!("to: {}", to.join(", ")) };

View file

@ -1,4 +1,4 @@
use leptos::*;
use leptos::prelude::*;
use crate::prelude::*;
#[component]
@ -20,7 +20,7 @@ pub fn Breadcrumb(
#[component]
pub fn Navigator(notifications: ReadSignal<u64>) -> impl IntoView {
let auth = use_context::<Auth>().expect("missing auth context");
let (query, set_query) = create_signal("".to_string());
let (query, set_query) = signal("".to_string());
view! {
<form action={move|| format!("/web/search?q={}", query.get())}>
<table class="align">

View file

@ -1,6 +1,6 @@
use apb::{ActivityMut, Base, BaseMut, Object, ObjectMut};
use leptos::*;
use leptos::prelude::*;
use crate::prelude::*;
#[derive(Debug, Clone, Copy, Default)]
@ -135,7 +135,7 @@ pub fn PrivacySelector(setter: WriteSignal<Privacy>) -> impl IntoView {
let p = privacy.get();
let (to, cc) = p.address(&auth.username());
view! {
<PrivacyMarker privacy=p to=&to cc=&cc big=true />
<PrivacyMarker privacy=p to=to cc=cc big=true />
}
}}
</td>
@ -149,16 +149,16 @@ pub fn PostBox(advanced: WriteSignal<bool>) -> impl IntoView {
let auth = use_context::<Auth>().expect("missing auth context");
let privacy = use_context::<PrivacyControl>().expect("missing privacy context");
let reply = use_context::<ReplyControls>().expect("missing reply controls");
let (posting, set_posting) = create_signal(false);
let (error, set_error) = create_signal(None);
let (content, set_content) = create_signal("".to_string());
let summary_ref: NodeRef<html::Input> = create_node_ref();
let (posting, set_posting) = signal(false);
let (error, set_error) = signal(None);
let (content, set_content) = signal("".to_string());
let summary_ref: NodeRef<leptos::html::Input> = NodeRef::new();
// TODO is this too abusive with resources? im even checking if TLD exists...
// TODO debounce this!
let mentions = create_local_resource(
move || content.get(),
move |c| async move {
let mentions = LocalResource::new(
move || async move {
let c = content.get();
let mut out = Vec::new();
for word in c.split(' ') {
if word.starts_with('@') {
@ -203,13 +203,14 @@ pub fn PostBox(advanced: WriteSignal<bool>) -> impl IntoView {
{move ||
mentions.get()
.map(|x| x
.take()
.into_iter()
.map(|u| match u {
TextMatch::Mention { href, .. } => match cache::OBJECTS.get(&href) {
Some(u) => view! { <span class="nowrap"><span class="emoji mr-s ml-s">"📨"</span><ActorStrip object=u /></span> }.into_view(),
None => view! { <span class="nowrap"><span class="emoji mr-s ml-s">"📨"</span><a href={Uri::web(U::Actor, &href)}>{href}</a></span> }.into_view(),
TextMatch::Mention { href: ref h, .. } => match cache::OBJECTS.get(h) {
Some(u) => view! { <span class="nowrap"><span class="emoji mr-s ml-s">"📨"</span><ActorStrip object=u /></span> }.into_any(),
None => view! { <span class="nowrap"><span class="emoji mr-s ml-s">"📨"</span><a href={Uri::web(U::Actor, h)}>{h.to_string()}</a></span> }.into_any(),
},
TextMatch::Hashtag { name } => view! { <code class="color">#{name}</code> }.into_view(),
TextMatch::Hashtag { name } => view! { <code class="color">#{name}</code> }.into_any(),
})
.collect_view()
)
@ -233,10 +234,11 @@ pub fn PostBox(advanced: WriteSignal<bool>) -> impl IntoView {
return;
}
set_posting.set(true);
spawn_local(async move {
leptos::task::spawn_local(async move {
let summary = get_if_some(summary_ref);
let (mut to_vec, cc_vec) = privacy.get().address(&auth.username());
let mut mention_tags : Vec<serde_json::Value> = mentions.get()
.map(|x| x.take())
.unwrap_or_default()
.into_iter()
.map(|x| match x {
@ -272,7 +274,7 @@ pub fn PostBox(advanced: WriteSignal<bool>) -> impl IntoView {
}
}
}
for mention in mentions.get().as_deref().unwrap_or(&[]) {
for mention in mentions.get().map(|x| x.take()).as_deref().unwrap_or(&[]) {
if let TextMatch::Mention { href, .. } = mention {
to_vec.push(href.clone());
}
@ -306,21 +308,21 @@ pub fn PostBox(advanced: WriteSignal<bool>) -> impl IntoView {
#[component]
pub fn AdvancedPostBox(advanced: WriteSignal<bool>) -> impl IntoView {
let auth = use_context::<Auth>().expect("missing auth context");
let (posting, set_posting) = create_signal(false);
let (error, set_error) = create_signal(None);
let (value, set_value) = create_signal("Like".to_string());
let (embedded, set_embedded) = create_signal(false);
let sensitive_ref: NodeRef<html::Input> = create_node_ref();
let summary_ref: NodeRef<html::Input> = create_node_ref();
let content_ref: NodeRef<html::Textarea> = create_node_ref();
let context_ref: NodeRef<html::Input> = create_node_ref();
let name_ref: NodeRef<html::Input> = create_node_ref();
let reply_ref: NodeRef<html::Input> = create_node_ref();
let to_ref: NodeRef<html::Input> = create_node_ref();
let object_id_ref: NodeRef<html::Input> = create_node_ref();
let bto_ref: NodeRef<html::Input> = create_node_ref();
let cc_ref: NodeRef<html::Input> = create_node_ref();
let bcc_ref: NodeRef<html::Input> = create_node_ref();
let (posting, set_posting) = signal(false);
let (error, set_error) = signal(None);
let (value, set_value) = signal("Like".to_string());
let (embedded, set_embedded) = signal(false);
let sensitive_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let summary_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let content_ref: NodeRef<leptos::html::Textarea> = NodeRef::new();
let context_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let name_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let reply_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let to_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let object_id_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let bto_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let cc_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let bcc_ref: NodeRef<leptos::html::Input> = NodeRef::new();
view! {
<div>
@ -385,7 +387,7 @@ pub fn AdvancedPostBox(advanced: WriteSignal<bool>) -> impl IntoView {
<button class="w-100" type="button" prop:disabled=posting on:click=move |_| {
set_posting.set(true);
spawn_local(async move {
leptos::task::spawn_local(async move {
let content = content_ref.get().filter(|x| !x.value().is_empty()).map(|x| x.value());
let summary = get_if_some(summary_ref);
let name = get_if_some(name_ref);
@ -435,13 +437,13 @@ pub fn AdvancedPostBox(advanced: WriteSignal<bool>) -> impl IntoView {
}
}
fn get_if_some(node: NodeRef<html::Input>) -> Option<String> {
fn get_if_some(node: NodeRef<leptos::html::Input>) -> Option<String> {
node.get()
.map(|x| x.value())
.filter(|x| !x.is_empty())
}
fn get_vec_if_some(node: NodeRef<html::Input>) -> Vec<String> {
fn get_vec_if_some(node: NodeRef<leptos::html::Input>) -> Vec<String> {
node.get()
.map(|x| x.value())
.filter(|x| !x.is_empty())
@ -453,7 +455,7 @@ fn get_vec_if_some(node: NodeRef<html::Input>) -> Vec<String> {
}
#[allow(unused)]
fn get_checked(node: NodeRef<html::Input>) -> bool {
fn get_checked(node: NodeRef<leptos::html::Input>) -> bool {
node.get()
.map(|x| x.checked())
.unwrap_or_default()

View file

@ -1,4 +1,4 @@
use leptos::*;
use leptos::prelude::*;
use crate::{prelude::*, FALLBACK_IMAGE_URL};
use apb::{Activity, ActivityMut, Actor, Base, Object, ObjectMut, Shortcuts};
@ -25,7 +25,7 @@ pub fn ActorBanner(object: crate::Object) -> impl IntoView {
match object.as_ref() {
serde_json::Value::String(id) => view! {
<div><b>?</b>" "<a class="clean hover" href={Uri::web(U::Actor, id)}>{Uri::pretty(id, 50)}</a></div>
},
}.into_any(),
serde_json::Value::Object(_) => {
let uid = object.id().unwrap_or_default().to_string();
let uri = Uri::web(U::Actor, &uid);
@ -45,11 +45,11 @@ pub fn ActorBanner(object: crate::Object) -> impl IntoView {
</tr>
</table>
</div>
}
}.into_any()
},
_ => view! {
<div><b>invalid actor</b></div>
}
}.into_any()
}
}
@ -78,7 +78,7 @@ pub fn FollowRequestButtons(activity_id: String, actor_id: String) -> impl IntoV
on:click=move |_| {
let activity_id = _activity_id.clone();
let actor_id = _from_actor.clone();
spawn_local(async move {
leptos::task::spawn_local(async move {
send_follow_response(
apb::ActivityType::Accept(apb::AcceptType::Accept),
activity_id,
@ -93,7 +93,7 @@ pub fn FollowRequestButtons(activity_id: String, actor_id: String) -> impl IntoV
on:click=move |_| {
let activity_id = activity_id.clone();
let actor_id = from_actor.clone();
spawn_local(async move {
leptos::task::spawn_local(async move {
send_follow_response(
apb::ActivityType::Reject(apb::RejectType::Reject),
activity_id,

View file

@ -146,7 +146,7 @@ impl DashmapCache<String> {
pub fn resolve(&self, user: &str, domain: &str) -> Option<String> {
if let Some(x) = self.resource(user, domain) { return Some(x); }
let (_self, user, domain) = (self.clone(), user.to_string(), domain.to_string());
leptos::spawn_local(async move { _self.fetch(&user, &domain).await });
leptos::task::spawn_local(async move { _self.fetch(&user, &domain).await });
None
}
@ -186,7 +186,7 @@ impl DashmapCache<String> {
}
}
use leptos_router::Params; // TODO can i remove this?
use leptos_router::params::Params; // TODO can i remove this?
#[derive(Clone, leptos::Params, PartialEq)]
pub struct IdParam {
id: Option<String>,
@ -201,7 +201,7 @@ impl Http {
data: Option<&T>,
auth: Auth,
) -> reqwest::Result<reqwest::Response> {
use leptos::SignalGetUntracked;
use leptos::prelude::GetUntracked;
let mut req = reqwest::Client::new()
.request(method, url);

View file

@ -10,5 +10,5 @@ fn main() {
.without_time()
.init();
leptos::mount_to_body(upub_web::App);
leptos::mount::mount_to_body(upub_web::App);
}

View file

@ -1,4 +1,4 @@
use leptos::*;
use leptos::prelude::*;
use crate::{prelude::*, URL_SENSITIVE};
use base64::prelude::*;
@ -16,7 +16,7 @@ pub fn Attachment(
sensitive: bool
) -> impl IntoView {
let config = use_context::<Signal<crate::Config>>().expect("missing config context");
let (expand, set_expand) = create_signal(false);
let (expand, set_expand) = signal(false);
let href = object.url().id().ok().unwrap_or_default();
let uncloaked = uncloak(href.split('/').last()).unwrap_or_default();
let media_type = object.media_type()
@ -53,7 +53,7 @@ pub fn Attachment(
on:click=move |_| set_expand.set(!expand.get())
/>
</p>
}.into_view(),
}.into_any(),
"video" => {
let _href = href.clone();
@ -67,7 +67,7 @@ pub fn Attachment(
<a href={href.clone()} target="_blank">video clip</a>
</video>
</div>
}.into_view()
}.into_any()
},
"audio" =>
@ -78,7 +78,7 @@ pub fn Attachment(
<a href={href} target="_blank">audio clip</a>
</audio>
</p>
}.into_view(),
}.into_any(),
"link" | "text" =>
view! {
@ -87,7 +87,7 @@ pub fn Attachment(
{Uri::pretty(&uncloaked, 50)}
</a>
</p>
}.into_view(),
}.into_any(),
_ =>
view! {
@ -99,7 +99,7 @@ pub fn Attachment(
view! { <p class="tiny-text"><small>{name.to_string()}</small></p> }
})}
</p>
}.into_view(),
}.into_any(),
}
}

View file

@ -1,6 +1,6 @@
use apb::Object;
use leptos::*;
use leptos_router::*;
use leptos::prelude::*;
use leptos_router::hooks::use_params;
use crate::prelude::*;
@ -17,7 +17,7 @@ pub fn ObjectContext() -> impl IntoView {
.and_then(|x| x.context().id().ok())
.unwrap_or_default()
);
create_effect(move |_| {
Effect::new(move |_| {
let tl_url = format!("{}/context/page", Uri::api(U::Object, &context_id.get(), false));
if !feeds.context.next.get_untracked().starts_with(&tl_url) {
feeds.context.reset(Some(tl_url));

View file

@ -1,6 +1,6 @@
use std::sync::Arc;
use leptos::*;
use leptos::{either::Either, prelude::*};
use crate::{prelude::*, URL_SENSITIVE};
use apb::{ActivityMut, Base, Collection, CollectionMut, Object, ObjectMut, Shortcuts};
@ -38,8 +38,9 @@ pub fn Object(object: crate::Object, #[prop(default = true)] controls: bool) ->
.map(|x| {
// TODO this isn't guaranteed to work every time...
let name = x.split('/').last().unwrap_or_default().to_string();
let uri = Uri::web(U::Actor, &x);
view! {
<a class="clean dim" href={Uri::web(U::Actor, &x)}>
<a class="clean dim" href={uri}>
<span class="border-button ml-s" title={x}>
<code class="color mr-s">&</code>
<small class="mr-s">
@ -90,7 +91,7 @@ pub fn Object(object: crate::Object, #[prop(default = true)] controls: bool) ->
Ok(apb::LinkType::Hashtag) => {
let name = apb::Link::name(link.as_ref()).unwrap_or_default().replace('#', "");
let href = Uri::web(U::Hashtag, &name);
Some(view! {
Some(Either::Left(view! {
<a class="clean dim" href={href}>
<span class="border-button ml-s" >
<code class="color mr-s">#</code>
@ -99,7 +100,7 @@ pub fn Object(object: crate::Object, #[prop(default = true)] controls: bool) ->
</small>
</span>
</a>" "
})
}))
},
Ok(apb::LinkType::Mention) => {
let uid = apb::Link::href(link.as_ref()).unwrap_or_default();
@ -113,23 +114,24 @@ pub fn Object(object: crate::Object, #[prop(default = true)] controls: bool) ->
)
};
let href = Uri::web(U::Actor, &uid);
Some(view! {
let title = format!("@{username}@{domain}");
Some(Either::Right(view! {
<a class="clean dim" href={href}>
<span class="border-button ml-s" title={format!("@{username}@{domain}")} >
<span class="border-button ml-s" title={title} >
<code class="color mr-s">@</code>
<small class="mr-s">
{username}
</small>
</span>
</a>" "
})
}))
},
_ => None,
}
}).collect_view();
let post_image = object.image().inner().and_then(|x| x.url().id()).ok().map(|x| {
let (expand, set_expand) = create_signal(false);
let (expand, set_expand) = signal(false);
view! {
<img
class="flex-pic box cursor"
@ -158,7 +160,7 @@ pub fn Object(object: crate::Object, #[prop(default = true)] controls: bool) ->
{post_inner}
{quote_block}
</article>
}.into_view(),
}.into_any(),
// lemmy with Page, peertube with Video
Ok(apb::ObjectType::Document(t)) => view! {
<article class="float-container ml-1 mr-1" >
@ -171,7 +173,7 @@ pub fn Object(object: crate::Object, #[prop(default = true)] controls: bool) ->
{quote_block}
</div>
</article>
}.into_view(),
}.into_any(),
// wordpress, ... ?
Ok(apb::ObjectType::Article) => view! {
<article>
@ -180,15 +182,15 @@ pub fn Object(object: crate::Object, #[prop(default = true)] controls: bool) ->
{post_inner}
{quote_block}
</article>
}.into_view(),
}.into_any(),
// everything else
Ok(t) => view! {
<h3>{t.as_ref().to_string()}</h3>
{post_inner}
{quote_block}
}.into_view(),
}.into_any(),
// object without type?
Err(_) => view! { <code>missing object type</code> }.into_view(),
Err(_) => view! { <code>missing object type</code> }.into_any(),
};
view! {
<table class="align w-100 ml-s mr-s">
@ -198,7 +200,7 @@ pub fn Object(object: crate::Object, #[prop(default = true)] controls: bool) ->
{object.in_reply_to().id().ok().map(|reply| view! {
<small><i><a class="clean" href={Uri::web(U::Object, &reply)} title={reply}>reply</a></i></small>
})}
<PrivacyMarker privacy=privacy to=&to cc=&cc />
<PrivacyMarker privacy=privacy to=to cc=cc />
<a class="clean hover ml-s" href={Uri::web(U::Object, &object.id().unwrap_or_default())}>
<DateTime t=object.published().ok() />
</a>
@ -228,7 +230,7 @@ pub fn Object(object: crate::Object, #[prop(default = true)] controls: bool) ->
pub fn Summary(summary: Option<String>, children: Children) -> impl IntoView {
let config = use_context::<Signal<crate::Config>>().expect("missing config context");
match summary.filter(|x| !x.is_empty()) {
None => children().into_view(),
None => children().into_any(),
Some(summary) => view! {
<details class="pa-s" prop:open=move || !config.get().collapse_content_warnings>
<summary>
@ -236,7 +238,7 @@ pub fn Summary(summary: Option<String>, children: Children) -> impl IntoView {
</summary>
{children()}
</details>
}.into_view(),
}.into_any(),
}
}
@ -249,8 +251,8 @@ pub fn LikeButton(
#[prop(optional)]
private: bool,
) -> impl IntoView {
let (count, set_count) = create_signal(n);
let (clicked, set_clicked) = create_signal(!liked);
let (count, set_count) = signal(n);
let (clicked, set_clicked) = signal(!liked);
let auth = use_context::<Auth>().expect("missing auth context");
let privacy = use_context::<PrivacyControl>().expect("missing privacy context");
view! {
@ -274,7 +276,7 @@ pub fn LikeButton(
.set_to(apb::Node::links(to))
.set_cc(apb::Node::links(cc));
let target = target.clone();
spawn_local(async move {
leptos::task::spawn_local(async move {
match Http::post(&auth.outbox(), &payload, auth).await {
Ok(()) => {
set_clicked.set(false);
@ -327,8 +329,8 @@ pub fn ReplyButton(n: i32, target: String) -> impl IntoView {
#[component]
pub fn RepostButton(n: i32, target: String, author: String) -> impl IntoView {
let (count, set_count) = create_signal(n);
let (clicked, set_clicked) = create_signal(true);
let (count, set_count) = signal(n);
let (clicked, set_clicked) = signal(true);
let auth = use_context::<Auth>().expect("missing auth context");
let privacy = use_context::<PrivacyControl>().expect("missing privacy context");
view! {
@ -348,7 +350,7 @@ pub fn RepostButton(n: i32, target: String, author: String) -> impl IntoView {
.set_object(apb::Node::link(target.clone()))
.set_to(apb::Node::links(to))
.set_cc(apb::Node::links(cc));
spawn_local(async move {
leptos::task::spawn_local(async move {
match Http::post(&auth.outbox(), &payload, auth).await {
Ok(()) => set_count.set(count.get() + 1),
Err(e) => tracing::error!("failed sending like: {e}"),

View file

@ -1,5 +1,5 @@
use leptos::*;
use leptos_router::*;
use leptos::prelude::*;
use leptos_router::hooks::use_params;
use crate::prelude::*;
@ -10,7 +10,7 @@ pub fn ObjectReplies() -> impl IntoView {
let id = Signal::derive(move ||
params.with(|p| p.as_ref().ok().and_then(|x| x.id.as_ref()).cloned()).unwrap_or_default()
);
create_effect(move |_| {
Effect::new(move |_| {
let tl_url = format!("{}/replies/page", Uri::api(U::Object, &id.get(), false));
if !feeds.replies.next.get_untracked().starts_with(&tl_url) {
feeds.replies.reset(Some(tl_url));
@ -30,7 +30,7 @@ pub fn ObjectLikes() -> impl IntoView {
let id = Signal::derive(move ||
params.with(|p| p.as_ref().ok().and_then(|x| x.id.as_ref()).cloned()).unwrap_or_default()
);
create_effect(move |_| {
Effect::new(move |_| {
let tl_url = format!("{}/likes/page", Uri::api(U::Object, &id.get(), false));
if !feeds.object_likes.next.get_untracked().starts_with(&tl_url) {
feeds.object_likes.reset(Some(tl_url));

View file

@ -1,6 +1,7 @@
use ev::MouseEvent;
use leptos::*;
use leptos_router::*;
use leptos::{either::Either, ev::MouseEvent};
use leptos::prelude::*;
use leptos_router::components::Outlet;
use leptos_router::hooks::use_params_map;
use crate::{app::FeedRoute, prelude::*, Config};
use apb::Object;
@ -12,44 +13,46 @@ pub fn ObjectView() -> impl IntoView {
let auth = use_context::<Auth>().expect("missing auth context");
let config = use_context::<Signal<Config>>().expect("missing config context");
let relevant_tl = use_context::<Signal<Option<Timeline>>>().expect("missing relevant timeline context");
let (loading, set_loading) = create_signal(false);
let id = Signal::derive(move || params.get().get("id").cloned().unwrap_or_default());
let object = create_local_resource(
move || (id.get(), loading.get()),
move |(oid, _loading)| async move {
tracing::info!("rerunning fetcher");
let obj = cache::OBJECTS.resolve(&oid, U::Object, auth).await?;
let (loading, set_loading) = signal(false);
let id = Signal::derive(move || params.get().get("id").unwrap_or_default());
let object = LocalResource::new(
move || {
let (oid, _loading) = (id.get(), loading.get());
async move {
tracing::info!("rerunning fetcher");
let obj = cache::OBJECTS.resolve(&oid, U::Object, auth).await?;
// TODO these two can be parallelized
if let Ok(author) = obj.attributed_to().id() {
cache::OBJECTS.resolve(&author, U::Actor, auth).await;
// TODO these two can be parallelized
if let Ok(author) = obj.attributed_to().id() {
cache::OBJECTS.resolve(&author, U::Actor, auth).await;
}
if let Ok(quote) = obj.quote_url().id() {
cache::OBJECTS.resolve(&quote, U::Object, auth).await;
}
Some(obj)
// if let Ok(ctx) = obj.context().id() {
// let tl_url = format!("{}/context/page", Uri::api(U::Object, ctx, false));
// if !feeds.context.next.get_untracked().starts_with(&tl_url) {
// feeds.context.reset(Some(tl_url));
// }
// }
}
if let Ok(quote) = obj.quote_url().id() {
cache::OBJECTS.resolve(&quote, U::Object, auth).await;
}
Some(obj)
// if let Ok(ctx) = obj.context().id() {
// let tl_url = format!("{}/context/page", Uri::api(U::Object, ctx, false));
// if !feeds.context.next.get_untracked().starts_with(&tl_url) {
// feeds.context.reset(Some(tl_url));
// }
// }
}
);
view! {
{move || match object.get() {
None => view! { <Loader /> }.into_view(),
{move || match object.get().map(|x| x.take()) {
None => view! { <Loader /> }.into_any(),
Some(None) => {
let raw_id = params.get().get("id").cloned().unwrap_or_default();
let raw_id = params.get().get("id").unwrap_or_default();
let uid = uriproxy::uri(URL_BASE, uriproxy::UriClass::Object, &raw_id);
view! { <p class="center"><code>loading failed</code><sup><small><a class="clean" href={uid} target="_blank">""</a></small></sup></p> }.into_view()
view! { <p class="center"><code>loading failed</code><sup><small><a class="clean" href={uid} target="_blank">""</a></small></sup></p> }.into_any()
},
Some(Some(o)) => {
tracing::info!("redrawing object");
view! { <Object object=o.clone() /> }.into_view()
view! { <Object object=o.clone() /> }.into_any()
},
}}
@ -59,13 +62,13 @@ pub fn ObjectView() -> impl IntoView {
<span class:tab-active=move || matches!(matched_route.get(), FeedRoute::ObjectLikes)><a class="clean" href=move || format!("/web/objects/{}/likes", id.get())><span class="emoji ml-2">""</span><span class:hidden-on-mobile=move || !matches!(matched_route.get(), FeedRoute::ObjectLikes)>" likes"</span></a></span>
{move || if auth.present() {
if loading.get() {
Some(view! {
Some(Either::Left(view! {
<span style="float: right">
"fetching "<span class="dots"></span>
</span>
})
}))
} else {
Some(view! {
Some(Either::Right(view! {
<span style="float: right">
<a
class="clean"
@ -75,7 +78,7 @@ pub fn ObjectView() -> impl IntoView {
<span class="emoji ml-2">""</span>"fetch"
</a>
</span>
})
}))
}
} else {
None
@ -96,7 +99,7 @@ fn fetch_cb(ev: MouseEvent, set_loading: WriteSignal<bool>, oid: String, auth: A
let api = Uri::api(U::Object, &oid, false);
ev.prevent_default();
set_loading.set(true);
spawn_local(async move {
leptos::task::spawn_local(async move {
if let Err(e) = Http::fetch::<serde_json::Value>(&format!("{api}/replies?fetch=true"), auth).await {
tracing::error!("failed crawling replies for {oid}: {e}");
}

View file

@ -1,4 +1,4 @@
use leptos::*;
use leptos::prelude::*;
#[component]
pub fn AboutPage() -> impl IntoView {

View file

@ -1,5 +1,5 @@
use apb::{ActivityMut, DocumentMut, Object, ObjectMut};
use leptos::*;
use leptos::prelude::*;
use crate::{prelude::*, DEFAULT_COLOR};
#[component]
@ -15,10 +15,10 @@ pub fn ConfigPage(setter: WriteSignal<crate::Config>) -> impl IntoView {
set_color_rgb.set(parse_hex(&previous_color));
set_color.set(previous_color);
let display_name_ref: NodeRef<html::Input> = create_node_ref();
let summary_ref: NodeRef<html::Textarea> = create_node_ref();
let avatar_url_ref: NodeRef<html::Input> = create_node_ref();
let banner_url_ref: NodeRef<html::Input> = create_node_ref();
let display_name_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let summary_ref: NodeRef<leptos::html::Textarea> = NodeRef::new();
let avatar_url_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let banner_url_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let myself = cache::OBJECTS.get(&auth.userid.get_untracked().unwrap_or_default());
let (curr_display_name, curr_summary, curr_icon, curr_banner) = match myself.as_ref() {
@ -187,7 +187,7 @@ pub fn ConfigPage(setter: WriteSignal<crate::Config>) -> impl IntoView {
.set_published(Some(chrono::Utc::now()))
));
spawn_local(async move {
leptos::task::spawn_local(async move {
if let Err(e) = Http::post(&format!("{id}/outbox"), &payload, auth).await {
tracing::error!("could not send update activity: {e}");
}

View file

@ -1,41 +1,44 @@
use leptos::*;
use leptos_router::*;
use leptos::prelude::*;
use leptos_router::{hooks::{use_navigate, use_query_map}, NavigateOptions};
use crate::prelude::*;
#[component]
pub fn DebugPage() -> impl IntoView {
let query_params = use_query_map();
let auth = use_context::<Auth>().expect("missing auth context");
let (cached, set_cached) = create_signal(false);
let (error, set_error) = create_signal(false);
let (plain, set_plain) = create_signal(false);
let (text, set_text) = create_signal("".to_string());
let (cached, set_cached) = signal(false);
let (error, set_error) = signal(false);
let (plain, set_plain) = signal(false);
let (text, set_text) = signal("".to_string());
let navigate = use_navigate();
let cached_query = move || (
query_params.with(|params| params.get("q").cloned().unwrap_or_default()),
query_params.with(|params| params.get("q").unwrap_or_default()),
cached.get(),
);
let object = create_local_resource(
cached_query,
move |(query, cached)| async move {
set_text.set(query.clone());
set_error.set(false);
if query.is_empty() { return serde_json::Value::Null };
if cached {
match cache::OBJECTS.get(&query) {
Some(x) => (*x).clone(),
None => {
set_error.set(true);
serde_json::Value::Null
},
let object = LocalResource::new(
move || {
let (query, cached) = cached_query();
async move {
set_text.set(query.clone());
set_error.set(false);
if query.is_empty() { return serde_json::Value::Null };
if cached {
match cache::OBJECTS.get(&query) {
Some(x) => (*x).clone(),
None => {
set_error.set(true);
serde_json::Value::Null
},
}
} else {
debug_fetch(&format!("{URL_BASE}/fetch?uri={query}"), auth, set_error).await
}
} else {
debug_fetch(&format!("{URL_BASE}/fetch?uri={query}"), auth, set_error).await
}
}
);
let loading = object.loading();
let loading = true; // object.loading(); // TODO no longer exists?
view! {
@ -69,12 +72,12 @@ pub fn DebugPage() -> impl IntoView {
</form>
</div>
<pre class="ma-1" class:striped=error>
{move || match object.get() {
None => view! { <p class="center"><span class="dots"></span></p> }.into_view(),
{move || match object.get().map(|x| x.take()) {
None => view! { <p class="center"><span class="dots"></span></p> }.into_any(),
Some(o) => if plain.get() {
serde_json::to_string_pretty(&o).unwrap_or_else(|e| e.to_string()).into_view()
serde_json::to_string_pretty(&o).unwrap_or_else(|e| e.to_string()).into_any()
} else {
view! { <DocumentNode obj=o /> }.into_view()
view! { <DocumentNode obj=o /> }.into_any()
},
}}
</pre>
@ -91,7 +94,7 @@ pub fn DebugPage() -> impl IntoView {
onclick={move ||
format!(
"javascript:navigator.clipboard.writeText(`{}`)",
object.get().map(|x| serde_json::to_string(&x).unwrap_or_default()).unwrap_or_default()
object.get().map(|x| serde_json::to_string(&x.take()).unwrap_or_default()).unwrap_or_default()
)
} >copy</a>
</p>
@ -132,14 +135,15 @@ fn DocumentNode(obj: serde_json::Value, #[prop(optional)] depth: usize) -> impl
let prefix = " ".repeat(depth);
let newline_replace = format!("\n{prefix} ");
match obj {
serde_json::Value::Null => view! { <b>null</b> }.into_view(),
serde_json::Value::Bool(x) => view! { <b>{x}</b> }.into_view(),
serde_json::Value::Number(n) => view! { <b>{n.to_string()}</b> }.into_view(),
serde_json::Value::Null => view! { <b>null</b> }.into_any(),
serde_json::Value::Bool(x) => view! { <b>{x}</b> }.into_any(),
serde_json::Value::Number(n) => view! { <b>{n.to_string()}</b> }.into_any(),
serde_json::Value::String(s) => {
if s.starts_with("https://") || s.starts_with("http://") {
let href = format!("/web/explore?q={s}");
view! {
<a href=format!("/web/explore?q={s}")>{s}</a>
}.into_view()
<a href=href>{s}</a>
}.into_any()
} else {
let pretty_string = s
.replace("<br/>", "<br/>\n")
@ -147,11 +151,11 @@ fn DocumentNode(obj: serde_json::Value, #[prop(optional)] depth: usize) -> impl
.replace('\n', &newline_replace);
view! {
"\""<span class="json-text"><i>{pretty_string}</i></span>"\""
}.into_view()
}.into_any()
}
},
serde_json::Value::Array(arr) => if arr.is_empty() {
view! { "[]" }.into_view()
view! { "[]" }.into_any()
} else {
view! {
"[\n"
@ -159,10 +163,10 @@ fn DocumentNode(obj: serde_json::Value, #[prop(optional)] depth: usize) -> impl
{prefix.clone()}" "<DocumentNode obj=x depth=depth+1 />"\n"
}).collect_view()}
{prefix.clone()}"]"
}.into_view()
}.into_any()
},
serde_json::Value::Object(map) => if map.is_empty() {
view! { "{}" }.into_view()
view! { "{}" }.into_any()
} else {
view! {
"{\n"
@ -174,7 +178,7 @@ fn DocumentNode(obj: serde_json::Value, #[prop(optional)] depth: usize) -> impl
.collect_view()
}
{prefix.clone()}"}"
}.into_view()
}.into_any()
},
}
}

View file

@ -1,4 +1,4 @@
use leptos::*;
use leptos::prelude::*;
use reqwest::Method;
use crate::prelude::*;
@ -28,13 +28,13 @@ pub struct RegisterForm {
#[component]
pub fn RegisterPage() -> impl IntoView {
let auth = use_context::<Auth>().expect("missing auth context");
let username_ref: NodeRef<html::Input> = create_node_ref();
let password_ref: NodeRef<html::Input> = create_node_ref();
let display_name_ref: NodeRef<html::Input> = create_node_ref();
let summary_ref: NodeRef<html::Input> = create_node_ref();
let avatar_url_ref: NodeRef<html::Input> = create_node_ref();
let banner_url_ref: NodeRef<html::Input> = create_node_ref();
let (error, set_error) = create_signal(None);
let username_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let password_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let display_name_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let summary_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let avatar_url_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let banner_url_ref: NodeRef<leptos::html::Input> = NodeRef::new();
let (error, set_error) = signal(None);
view! {
<div class="two-col">
<div class="border ma-2 pa-1">
@ -49,26 +49,20 @@ pub fn RegisterPage() -> impl IntoView {
let banner_url = get_ref!(banner_url_ref);
if email.is_none() || password.is_none() {
set_error.set(Some(
view! { <blockquote>no credentials provided</blockquote> }
));
set_error.set(Some("no credentials provided".to_string()));
return;
}
spawn_local(async move {
leptos::task::spawn_local(async move {
let payload = RegisterForm {
username: email.unwrap_or_default(),
password: password.unwrap_or_default(),
display_name, summary, avatar_url, banner_url
};
match Http::request(Method::PUT, &format!("{URL_BASE}/auth"), Some(&payload), auth).await {
Err(e) => set_error.set(Some(
view! { <blockquote>{e.to_string()}</blockquote> }
)),
Err(e) => set_error.set(Some(e.to_string())),
Ok(res) => match res.error_for_status() {
Err(e) => set_error.set(Some(
view! { <blockquote>{e.to_string()}</blockquote> }
)),
Err(e) => set_error.set(Some(e.to_string())),
Ok(_) => {
reset_ref!(username_ref);
reset_ref!(password_ref);
@ -76,9 +70,7 @@ pub fn RegisterPage() -> impl IntoView {
reset_ref!(summary_ref);
reset_ref!(avatar_url_ref);
reset_ref!(banner_url_ref);
set_error.set(Some(
view! { <blockquote>registration successful! your user may need to be approved by an administrator before you can login</blockquote> }
));
set_error.set(Some("registration successful! your user may need to be approved by an administrator before you can login".to_string()));
},
},
}
@ -123,7 +115,7 @@ pub fn RegisterPage() -> impl IntoView {
<input class="w-100" type="submit" value="register" />
</form>
</div>
<p>{error}</p>
<p>{error.get().map(|msg| view! { <blockquote>{msg}</blockquote> })}</p>
</div>
}
}

View file

@ -1,8 +1,8 @@
use std::sync::Arc;
use apb::Collection;
use leptos::*;
use leptos_router::*;
use leptos::prelude::*;
use leptos_router::hooks::use_query_map;
use crate::prelude::*;
#[component]
@ -10,28 +10,28 @@ pub fn SearchPage() -> impl IntoView {
let auth = use_context::<Auth>().expect("missing auth context");
let query = Signal::derive(||
use_query_map().with(|x| x.get("q").cloned().unwrap_or_default())
use_query_map().with(|x| x.get("q").unwrap_or_default())
);
let user = create_local_resource(
move || use_query_map().get().get("q").cloned().unwrap_or_default(),
move |q| {
let user = LocalResource::new(
move || {
let q = use_query_map().get().get("q").unwrap_or_default();
let user_fetch = Uri::api(U::Actor, &q, true);
async move { Some(Arc::new(Http::fetch::<serde_json::Value>(&user_fetch, auth).await.ok()?)) }
}
);
let object = create_local_resource(
move || use_query_map().get().get("q").cloned().unwrap_or_default(),
move |q| {
let object = LocalResource::new(
move || {
let q = use_query_map().get().get("q").unwrap_or_default();
let object_fetch = Uri::api(U::Object, &q, true);
async move { Some(Arc::new(Http::fetch::<serde_json::Value>(&object_fetch, auth).await.ok()?)) }
}
);
let text_search = create_local_resource(
move || use_query_map().get().get("q").cloned().unwrap_or_default(),
move |q| {
let text_search = LocalResource::new(
move || {
let q = use_query_map().get().get("q").unwrap_or_default();
let search = format!("{URL_BASE}/search?q={q}");
async move {
let items = Http::fetch::<serde_json::Value>(&search, auth).await.ok()?;
@ -59,10 +59,10 @@ pub fn SearchPage() -> impl IntoView {
<code class="cw center color ml-s w-100">actor</code>
</summary>
<div class="pb-1">
{move || match user.get() {
None => view! { <p class="center"><small>searching...</small></p> },
Some(None) => view! { <p class="center"><code>N/A</code></p> },
Some(Some(u)) => view! { <p><ActorBanner object=u /></p> },
{move || match user.get().map(|x| x.take()) {
None => view! { <p class="center"><small>searching...</small></p> }.into_any(),
Some(None) => view! { <p class="center"><code>N/A</code></p> }.into_any(),
Some(Some(u)) => view! { <p><ActorBanner object=u /></p> }.into_any(),
}}
</div>
</details>
@ -74,10 +74,10 @@ pub fn SearchPage() -> impl IntoView {
<code class="cw center color ml-s w-100">object</code>
</summary>
<div class="pb-1">
{move || match object.get() {
None => view! { <p class="center"><small>searching...</small></p> },
Some(None) => view!{ <p class="center"><code>N/A</code></p> },
Some(Some(o)) => view! { <p><Object object=o /></p> },
{move || match object.get().map(|x| x.take()) {
None => view! { <p class="center"><small>searching...</small></p> }.into_any(),
Some(None) => view!{ <p class="center"><code>N/A</code></p> }.into_any(),
Some(Some(o)) => view! { <p><Object object=o /></p> }.into_any(),
}}
</div>
</details>
@ -103,8 +103,8 @@ pub fn SearchPage() -> impl IntoView {
<code class="cw center color ml-s w-100">full text</code>
</summary>
<div class="pb-1">
{move || match text_search.get() {
None => Some(view! { <p class="center"><small>searching...</small></p> }.into_view()),
{move || match text_search.get().map(|x| x.take()) {
None => Some(view! { <p class="center"><small>searching...</small></p> }.into_any()),
Some(None) => None,
Some(Some(items)) => Some(view! {
// TODO ughhh too many clones
@ -113,10 +113,10 @@ pub fn SearchPage() -> impl IntoView {
key=|id| id.clone()
children=move |item| {
cache::OBJECTS.get(&item)
.map(|x| view! { <Item item=x always=true /> }.into_view())
.map(|x| view! { <Item item=x always=true /> }.into_any())
}
/ >
}.into_view())
}.into_any())
}}
</div>
</details>

View file

@ -1,5 +1,5 @@
use leptos::*;
use leptos_router::use_params;
use leptos::{either::Either, prelude::*};
use leptos_router::hooks::use_params;
use crate::prelude::*;
use super::Timeline;
@ -12,7 +12,7 @@ pub fn Feed(
let auth = use_context::<Auth>().expect("missing auth context");
let config = use_context::<Signal<crate::Config>>().expect("missing config context");
if let Some(auto_scroll) = use_context::<Signal<bool>>() {
let _ = leptos::watch(
let _ = Effect::watch(
move || auto_scroll.get(),
move |at_end, _, _| if *at_end { tl.spawn_more(auth, config) },
true,
@ -26,13 +26,13 @@ pub fn Feed(
let:id
>
{match cache::OBJECTS.get(&id) {
Some(i) => view! {
Some(i) => Either::Left(view! {
<Item item=i sep=true always=ignore_filters />
}.into_view(),
None => view! {
}),
None => Either::Right(view! {
<p><code>{id}</code>" "[<a href={uri}>go</a>]</p>
<hr />
}.into_view(),
}),
}}
</For>
</div>
@ -43,7 +43,7 @@ pub fn Feed(
#[component]
pub fn HashtagFeed(tl: Timeline) -> impl IntoView {
let params = use_params::<IdParam>();
create_effect(move |_| {
Effect::new(move |_| {
let current_tag = tl.next.get_untracked()
.split('/')
.last()

View file

@ -4,7 +4,7 @@ pub mod thread;
use std::{collections::BTreeSet, pin::Pin, sync::Arc};
use apb::{Activity, ActivityMut, Base, Object};
use leptos::*;
use leptos::prelude::*;
use crate::prelude::*;
#[derive(Debug, Clone, Copy)]
@ -17,10 +17,10 @@ pub struct Timeline {
impl Timeline {
pub fn new(url: String) -> Self {
let feed = create_rw_signal(vec![]);
let next = create_rw_signal(url);
let over = create_rw_signal(false);
let loading = create_rw_signal(false);
let feed = RwSignal::new(vec![]);
let next = RwSignal::new(url);
let over = RwSignal::new(false);
let loading = RwSignal::new(false);
Timeline { feed, next, over, loading }
}
@ -53,7 +53,7 @@ impl Timeline {
pub fn spawn_more(&self, auth: Auth, config: Signal<crate::Config>) {
let _self = *self;
spawn_local(async move {
leptos::task::spawn_local(async move {
_self.more(auth, config).await
});
}

View file

@ -1,5 +1,5 @@
use apb::{Activity, Base, Object};
use leptos::*;
use leptos::prelude::*;
use crate::prelude::*;
use super::Timeline;
@ -8,7 +8,7 @@ pub fn Thread(tl: Timeline, root: String) -> impl IntoView {
let auth = use_context::<Auth>().expect("missing auth context");
let config = use_context::<Signal<crate::Config>>().expect("missing config context");
if let Some(auto_scroll) = use_context::<Signal<bool>>() {
let _ = leptos::watch(
let _ = Effect::watch(
move || auto_scroll.get(),
move |new, old, _| {
match old {
@ -72,5 +72,5 @@ fn FeedRecursive(tl: Timeline, root: String) -> impl IntoView {
</div>
}
/ >
}
}.into_any()
}