From 7ebb780834e862e3cdb125d3a83e11dd1026b24a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Sat, 31 Aug 2024 10:44:06 +0200 Subject: [PATCH 01/15] build: add async-std to dependencies --- Cargo.lock | 429 ++++++++++++++++++++++++++++++++++++++++++++++++++--- Cargo.toml | 1 + 2 files changed, 406 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7fcda7a..b45af6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,6 +77,17 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + [[package]] name = "async-channel" version = "2.3.1" @@ -89,6 +100,119 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-executor" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand 2.1.0", + "futures-lite 2.3.0", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.3.1", + "async-executor", + "async-io 2.3.4", + "async-lock 3.4.0", + "blocking", + "futures-lite 2.3.0", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite 1.13.0", + "log", + "parking", + "polling 2.8.0", + "rustix 0.37.27", + "slab", + "socket2 0.4.10", + "waker-fn", +] + +[[package]] +name = "async-io" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +dependencies = [ + "async-lock 3.4.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.3.0", + "parking", + "polling 3.7.3", + "rustix 0.38.34", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.3.1", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-channel 1.9.0", + "async-global-executor", + "async-io 1.13.0", + "async-lock 2.8.0", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite 1.13.0", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + [[package]] name = "async-task" version = "4.7.1" @@ -226,6 +350,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.6.0" @@ -250,10 +380,10 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ - "async-channel", + "async-channel 2.3.1", "async-task", "futures-io", - "futures-lite", + "futures-lite 2.3.0", "piper", ] @@ -343,7 +473,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -534,7 +664,7 @@ version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf97ee7261bb708fa3402fa9c17a54b70e90e3cb98afb3dc8999d5512cb03f94" dependencies = [ - "bitflags", + "bitflags 2.6.0", "byteorder", "chrono", "diesel_derives", @@ -1016,6 +1146,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "euclid" version = "0.22.10" @@ -1026,6 +1166,12 @@ dependencies = [ "serde", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "event-listener" version = "5.3.1" @@ -1043,10 +1189,19 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "event-listener", + "event-listener 5.3.1", "pin-project-lite", ] +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + [[package]] name = "fastrand" version = "2.1.0" @@ -1122,13 +1277,31 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + [[package]] name = "futures-lite" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ + "fastrand 2.1.0", "futures-core", + "futures-io", + "parking", "pin-project-lite", ] @@ -1362,6 +1535,8 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" dependencies = [ + "futures-channel", + "futures-core", "js-sys", "wasm-bindgen", ] @@ -1453,6 +1628,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "hex" version = "0.4.3" @@ -1617,6 +1798,15 @@ dependencies = [ "serde", ] +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + [[package]] name = "internment" version = "0.7.5" @@ -1659,6 +1849,17 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1680,7 +1881,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" dependencies = [ - "bitflags", + "bitflags 2.6.0", "serde", "unicode-segmentation", ] @@ -1698,6 +1899,15 @@ dependencies = [ "semver", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1710,6 +1920,18 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.12" @@ -1725,6 +1947,9 @@ name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +dependencies = [ + "value-bag", +] [[package]] name = "longest-increasing-subsequence" @@ -1790,10 +2015,10 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "wasi", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1894,7 +2119,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1952,10 +2177,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand", + "fastrand 2.1.0", "futures-io", ] +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "polling" +version = "3.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix 0.38.34", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -2068,7 +2324,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags", + "bitflags 2.6.0", ] [[package]] @@ -2121,6 +2377,33 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.37.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys 0.4.14", + "windows-sys 0.52.0", +] + [[package]] name = "rustversion" version = "1.0.17" @@ -2433,6 +2716,16 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "socket2" version = "0.5.7" @@ -2440,7 +2733,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -2583,6 +2876,7 @@ checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" name = "todo-baggins" version = "0.1.0" dependencies = [ + "async-std", "chrono", "diesel", "dioxus", @@ -2609,9 +2903,9 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.7", "tokio-macros", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -2686,7 +2980,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ - "bitflags", + "bitflags 2.6.0", "bytes", "futures-util", "http 1.1.0", @@ -2922,6 +3216,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "value-bag" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" + [[package]] name = "vcpkg" version = "0.2.15" @@ -2934,6 +3234,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "waker-fn" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3058,7 +3364,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] @@ -3067,7 +3382,31 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -3076,28 +3415,46 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -3110,24 +3467,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index ac211f5..23b97f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ serde_json = "1.0.125" tracing = "0.1.40" tracing-wasm = "0.2.1" serde_with = { version = "3.9.0", features = ["chrono_0_4"] } +async-std = "1.12.0" [features] default = [] -- 2.47.1 From 1233325f0b135f6a9b173663bc4180ac456aef21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Sat, 31 Aug 2024 10:44:55 +0200 Subject: [PATCH 02/15] feat: add style assets --- Dioxus.toml | 9 +++- assets/styles/fonts.css | 17 ++++++++ assets/styles/input_number_arrows.css | 10 +++++ assets/styles/input_range.css | 63 +++++++++++++++++++++++++++ src/styles/tailwind.css | 18 -------- 5 files changed, 97 insertions(+), 20 deletions(-) create mode 100644 assets/styles/fonts.css create mode 100644 assets/styles/input_number_arrows.css create mode 100644 assets/styles/input_range.css diff --git a/Dioxus.toml b/Dioxus.toml index e396b3a..44e6de0 100644 --- a/Dioxus.toml +++ b/Dioxus.toml @@ -31,10 +31,15 @@ watch_path = ["src", "assets"] # CSS style file -style = ["/styles/tailwind_output.css"] +style = [ + "/styles/tailwind_output.css", + "/styles/fonts.css", + "/styles/input_number_arrows.css", + "/styles/input_range.css" +] # Javascript code file -script = [] +script = ["https://kit.fontawesome.com/3c1b409f8f.js"] [web.resource.dev] diff --git a/assets/styles/fonts.css b/assets/styles/fonts.css new file mode 100644 index 0000000..e2cb5c0 --- /dev/null +++ b/assets/styles/fonts.css @@ -0,0 +1,17 @@ +@layer base { + @font-face { + font-family: Inter; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url("/fonts/inter_variable.woff2") format("woff2"); + } + + @font-face { + font-family: Inter; + font-style: italic; + font-weight: 100 900; + font-display: swap; + src: url("/fonts/inter_variable_italic.woff2") format("woff2"); + } +} diff --git a/assets/styles/input_number_arrows.css b/assets/styles/input_number_arrows.css new file mode 100644 index 0000000..7c952dc --- /dev/null +++ b/assets/styles/input_number_arrows.css @@ -0,0 +1,10 @@ +input::-webkit-outer-spin-button, +input::-webkit-inner-spin-button { + display: none; + -webkit-appearance: none; + margin: 0; +} + +input[type=number] { + -moz-appearance:textfield; +} diff --git a/assets/styles/input_range.css b/assets/styles/input_range.css new file mode 100644 index 0000000..4c56e50 --- /dev/null +++ b/assets/styles/input_range.css @@ -0,0 +1,63 @@ +input[type=range], +input[type=range]::-webkit-slider-runnable-track, +input[type=range]::-webkit-slider-thumb { + appearance: none; +} + +input[type=range] { + background: transparent; +} + +input[type=range]::-moz-range-thumb { + width: 1.25rem; + height: 1.25rem; + background: rgba(228, 228, 231); + border: 0; + border-radius: 0.5rem; +} + +input[type=range]::-moz-range-progress { + background: #525259; + height: 0.5rem; + border-radius: 0.25rem; +} + +input[type=range]::-moz-range-track { + background: rgba(39, 39, 42, 0.5); + height: 0.5rem; + border-radius: 0.25rem; +} + +input[type=range].input-range-reverse::-moz-range-progress { + background: #2d2d31; + height: 0.5rem; + border-radius: 0.25rem; +} + +input[type=range].input-range-reverse::-moz-range-track { + background: rgba(113, 113, 122, 0.5); + height: 0.5rem; + border-radius: 0.25rem; +} + +input[type=range]::-webkit-slider-thumb { + width: 1.25rem; + height: 1.25rem; + background: rgba(228, 228, 231); + border: 0; + border-radius: 0.5rem; + position: relative; + top: -0.4rem; +} + +input[type=range]::-webkit-slider-runnable-track { + background: rgba(39, 39, 42, 0.5); + height: 0.5rem; + border-radius: 0.25rem; +} + +input[type=range].input-range-reverse::-webkit-slider-runnable-track { + background: rgba(39, 39, 42, 0.5); + height: 0.5rem; + border-radius: 0.25rem; +} diff --git a/src/styles/tailwind.css b/src/styles/tailwind.css index e671a7c..4f70d83 100644 --- a/src/styles/tailwind.css +++ b/src/styles/tailwind.css @@ -13,21 +13,3 @@ html, body, #main { } /* stylelint-enable */ - -@layer base { - @font-face { - font-family: Inter; - font-style: normal; - font-weight: 100 900; - font-display: swap; - src: url("/fonts/inter_variable.woff2") format("woff2"); - } - - @font-face { - font-family: Inter; - font-style: italic; - font-weight: 100 900; - font-display: swap; - src: url("/fonts/inter_variable_italic.woff2") format("woff2"); - } -} -- 2.47.1 From d7c56b7d926079dc9c90788b13bb2fccc325c8fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Sat, 31 Aug 2024 10:47:49 +0200 Subject: [PATCH 03/15] feat: ability to view tasks in different categories --- src/components/app.rs | 2 +- src/components/bottom_panel.rs | 44 ++ src/components/category_input.rs | 104 +++++ src/components/create_task_button.rs | 19 + src/components/home.rs | 4 - src/components/layout.rs | 30 ++ src/components/mod.rs | 10 + src/components/navigation.rs | 83 ++++ src/components/navigation_item.rs | 21 + .../pages/category_calendar_page.rs | 31 ++ src/components/pages/category_done_page.rs | 27 ++ src/components/pages/category_inbox_page.rs | 27 ++ .../pages/category_long_term_page.rs | 27 ++ .../pages/category_next_steps_page.rs | 27 ++ .../pages/category_someday_maybe_page.rs | 27 ++ src/components/pages/category_today_page.rs | 39 ++ src/components/pages/category_trash_page.rs | 27 ++ .../pages/category_waiting_for_page.rs | 27 ++ src/components/pages/mod.rs | 11 + src/components/pages/not_found_page.rs | 11 + src/components/pages/projects_page.rs | 7 + src/components/reoccurrence_input.rs | 79 ++++ src/components/sticky_bottom.rs | 14 + src/components/task_form.rs | 376 +++++++++++------- src/components/task_list.rs | 66 +++ src/models/category.rs | 86 +++- src/models/task.rs | 2 +- src/route/mod.rs | 44 +- src/server/tasks.rs | 24 +- 29 files changed, 1127 insertions(+), 169 deletions(-) create mode 100644 src/components/bottom_panel.rs create mode 100644 src/components/category_input.rs create mode 100644 src/components/create_task_button.rs create mode 100644 src/components/layout.rs create mode 100644 src/components/navigation.rs create mode 100644 src/components/navigation_item.rs create mode 100644 src/components/pages/category_calendar_page.rs create mode 100644 src/components/pages/category_done_page.rs create mode 100644 src/components/pages/category_inbox_page.rs create mode 100644 src/components/pages/category_long_term_page.rs create mode 100644 src/components/pages/category_next_steps_page.rs create mode 100644 src/components/pages/category_someday_maybe_page.rs create mode 100644 src/components/pages/category_today_page.rs create mode 100644 src/components/pages/category_trash_page.rs create mode 100644 src/components/pages/category_waiting_for_page.rs create mode 100644 src/components/pages/mod.rs create mode 100644 src/components/pages/not_found_page.rs create mode 100644 src/components/pages/projects_page.rs create mode 100644 src/components/reoccurrence_input.rs create mode 100644 src/components/sticky_bottom.rs create mode 100644 src/components/task_list.rs diff --git a/src/components/app.rs b/src/components/app.rs index 0ccd3a2..5597b7e 100644 --- a/src/components/app.rs +++ b/src/components/app.rs @@ -7,7 +7,7 @@ use dioxus::prelude::*; pub(crate) fn App() -> Element { rsx! { div { - class: "min-h-screen text-white bg-neutral-800", + class: "min-h-screen text-zinc-200 bg-zinc-800", Router:: {} } } diff --git a/src/components/bottom_panel.rs b/src/components/bottom_panel.rs new file mode 100644 index 0000000..1332e78 --- /dev/null +++ b/src/components/bottom_panel.rs @@ -0,0 +1,44 @@ +use std::thread::sleep; +use dioxus::prelude::*; +use crate::components::navigation::Navigation; +use crate::components::task_form::TaskForm; +use crate::components::task_list::TaskList; +use crate::models::category::Category; +use crate::route::Route; + +#[component] +pub(crate) fn BottomPanel(creating_task: bool) -> Element { + let mut expanded = use_signal(|| creating_task); + let navigation_expanded = use_signal(|| false); + + use_effect(use_reactive(&creating_task, move |creating_task| { + if creating_task { + expanded.set(true); + } else { + spawn(async move { + async_std::task::sleep(std::time::Duration::from_millis(500)).await; + expanded.set(false); + }); + } + })); + + rsx! { + div { + class: format!( + "bg-zinc-700/50 rounded-t-xl border-t-zinc-600 border-t backdrop-blur drop-shadow-[0_-5px_10px_rgba(0,0,0,0.2)] transition-[height] duration-[500ms] ease-[cubic-bezier(0.79,0.14,0.15,0.86)] {}", + match (creating_task, navigation_expanded()) { + (false, false) => "h-[64px]", + (false, true) => "h-[128px]", + (true, _) => "h-[448px]", + } + ), + if expanded() { + TaskForm {} + } else { + Navigation { + expanded: navigation_expanded, + } + } + } + } +} diff --git a/src/components/category_input.rs b/src/components/category_input.rs new file mode 100644 index 0000000..1ea3b8d --- /dev/null +++ b/src/components/category_input.rs @@ -0,0 +1,104 @@ +use crate::models::category::Category; +use crate::server::tasks::get_tasks_in_category; +use chrono::NaiveDate; +use dioxus::core_macro::rsx; +use dioxus::dioxus_core::Element; +use dioxus::prelude::*; +use std::fmt::format; + +#[component] +pub(crate) fn CategoryInput(selected_category: Signal, class: Option<&'static str>) -> Element { + rsx! { + div { + class: format!("flex flex-row gap-2 {}", class.unwrap_or("")), + button { + r#type: "button", + class: format!( + "py-2 rounded-lg grow basis-0 {}", + if selected_category() == Category::SomedayMaybe { "bg-zinc-500/50" } + else { "bg-zinc-800/50" } + ), + onclick: move |_| { + selected_category.set(Category::SomedayMaybe); + }, + i { + class: "fa-solid fa-question" + } + }, + button { + r#type: "button", + class: format!( + "py-2 rounded-lg grow basis-0 {}", + if selected_category() == Category::LongTerm { "bg-zinc-500/50" } + else { "bg-zinc-800/50" } + ), + onclick: move |_| { + selected_category.set(Category::LongTerm); + }, + i { + class: "fa-solid fa-water" + } + }, + button { + r#type: "button", + class: format!( + "py-2 rounded-lg grow basis-0 {}", + if let Category::WaitingFor(_) = selected_category() { "bg-zinc-500/50" } + else { "bg-zinc-800/50" } + ), + onclick: move |_| { + selected_category.set(Category::WaitingFor(String::new())); + }, + i { + class: "fa-solid fa-hourglass-half" + } + }, + button { + r#type: "button", + class: format!( + "py-2 rounded-lg grow basis-0 {}", + if selected_category() == Category::NextSteps { "bg-zinc-500/50" } + else { "bg-zinc-800/50" } + ), + onclick: move |_| { + selected_category.set(Category::NextSteps); + }, + i { + class: "fa-solid fa-forward" + } + }, + button { + r#type: "button", + class: format!( + "py-2 rounded-lg grow basis-0 {}", + if let Category::Calendar { .. } = selected_category() { "bg-zinc-500/50" } + else { "bg-zinc-800/50" } + ), + onclick: move |_| { + selected_category.set(Category::Calendar { + date: NaiveDate::default(), + reoccurrence: None, + time: None, + }); + }, + i { + class: "fa-solid fa-calendar-days" + } + }, + button { + r#type: "button", + class: format!( + "py-2 rounded-lg grow basis-0 {}", + if selected_category() == Category::Inbox { "bg-zinc-500/50" } + else { "bg-zinc-800/50" } + ), + onclick: move |_| { + selected_category.set(Category::Inbox); + }, + i { + class: "fa-solid fa-inbox" + } + } + } + } +} diff --git a/src/components/create_task_button.rs b/src/components/create_task_button.rs new file mode 100644 index 0000000..b87a855 --- /dev/null +++ b/src/components/create_task_button.rs @@ -0,0 +1,19 @@ +use dioxus::prelude::*; +use crate::components::task_list::TaskList; +use crate::models::category::Category; +use crate::route::Route; + +#[component] +pub(crate) fn CreateTaskButton(creating: Signal) -> Element { + rsx! { + button { + class: "m-4 py-3 px-5 self-end text-center bg-zinc-300/50 rounded-xl border-t-zinc-200 border-t backdrop-blur drop-shadow-[0_-5px_10px_rgba(0,0,0,0.2)] text-2xl text-zinc-200", + onclick: move |_| { + creating.set(!creating()); + }, + i { + class: format!("min-w-6 fa-solid {}", if creating() { "fa-xmark" } else { "fa-plus" }), + } + } + } +} diff --git a/src/components/home.rs b/src/components/home.rs index 827fad4..4dd9aac 100644 --- a/src/components/home.rs +++ b/src/components/home.rs @@ -1,13 +1,9 @@ -use crate::components::project_form::ProjectForm; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; -use crate::components::task_form::TaskForm; #[component] pub(crate) fn Home() -> Element { rsx! { - ProjectForm {} - TaskForm {} } } diff --git a/src/components/layout.rs b/src/components/layout.rs new file mode 100644 index 0000000..5ddb57c --- /dev/null +++ b/src/components/layout.rs @@ -0,0 +1,30 @@ +use crate::components::bottom_panel::BottomPanel; +use crate::components::navigation::Navigation; +use crate::components::task_list::TaskList; +use crate::models::category::Category; +use crate::route::Route; +use chrono::NaiveDate; +use dioxus::core_macro::rsx; +use dioxus::dioxus_core::Element; +use dioxus::prelude::*; +use crate::components::create_task_button::CreateTaskButton; +use crate::components::sticky_bottom::StickyBottom; +use crate::components::task_form::TaskForm; +use crate::server::tasks::get_tasks_in_category; + +#[component] +pub(crate) fn Layout() -> Element { + let creating_task = use_signal(|| false); + + rsx! { + Outlet:: {} + StickyBottom { + CreateTaskButton { + creating: creating_task, + } + BottomPanel { + creating_task: creating_task(), + } + } + } +} diff --git a/src/components/mod.rs b/src/components/mod.rs index c632a34..e60f716 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -2,3 +2,13 @@ pub(crate) mod app; pub(crate) mod home; pub(crate) mod project_form; pub(crate) mod task_form; +pub(crate) mod task_list; +pub(crate) mod pages; +pub(crate) mod navigation; +pub(crate) mod create_task_button; +pub(crate) mod bottom_panel; +pub(crate) mod sticky_bottom; +pub(crate) mod category_input; +pub(crate) mod reoccurrence_input; +pub(crate) mod layout; +mod navigation_item; diff --git a/src/components/navigation.rs b/src/components/navigation.rs new file mode 100644 index 0000000..8104116 --- /dev/null +++ b/src/components/navigation.rs @@ -0,0 +1,83 @@ +use crate::components::navigation_item::NavigationItem; +use crate::components::task_list::TaskList; +use crate::models::category::Category; +use crate::route::Route; +use dioxus::prelude::*; + +#[component] +pub(crate) fn Navigation(expanded: Signal) -> Element { + rsx! { + div { + class: "grid grid-cols-5 justify-stretch", + button { + class: format!( + "py-4 text-center text-2xl {}", + if expanded() { "text-zinc-200" } + else { "text-zinc-500" } + ), + onclick: move |_| expanded.set(!expanded()), + i { + class: "fa-solid fa-bars" + } + }, + NavigationItem { + route: Route::CategoryNextStepsPage, + i { + class: "fa-solid fa-forward" + } + }, + NavigationItem { + route: Route::CategoryCalendarPage, + i { + class: "fa-solid fa-calendar-days" + } + }, + NavigationItem { + route: Route::CategoryTodayPage, + i { + class: "fa-solid fa-calendar-day" + } + }, + NavigationItem { + route: Route::CategoryInboxPage, + i { + class: "fa-solid fa-inbox" + } + }, + {if expanded() { + rsx! { + NavigationItem { + route: Route::ProjectsPage, + i { + class: "fa-solid fa-list" + } + }, + NavigationItem { + route: Route::CategoryTrashPage, + i { + class: "fa-solid fa-trash-can" + } + }, + NavigationItem { + route: Route::CategoryDonePage, + i { + class: "fa-solid fa-check" + } + }, + NavigationItem { + route: Route::CategoryLongTermPage, + i { + class: "fa-solid fa-water" + } + }, + NavigationItem { + route: Route::CategoryWaitingForPage, + i { + class: "fa-solid fa-hourglass-half" + } + } + } + } else { None }} + } + } +} diff --git a/src/components/navigation_item.rs b/src/components/navigation_item.rs new file mode 100644 index 0000000..3393245 --- /dev/null +++ b/src/components/navigation_item.rs @@ -0,0 +1,21 @@ +use dioxus::prelude::*; +use crate::components::task_list::TaskList; +use crate::models::category::Category; +use crate::route::Route; + +#[component] +pub(crate) fn NavigationItem(route: Route, children: Element) -> Element { + let current_route = use_route::(); + + rsx! { + Link { + to: route.clone(), + class: format!( + "py-4 text-center text-2xl {}", + if current_route == route { "text-zinc-200" } + else { "text-zinc-500" } + ), + children + } + } +} diff --git a/src/components/pages/category_calendar_page.rs b/src/components/pages/category_calendar_page.rs new file mode 100644 index 0000000..6c434ce --- /dev/null +++ b/src/components/pages/category_calendar_page.rs @@ -0,0 +1,31 @@ +use crate::components::bottom_panel::BottomPanel; +use crate::components::navigation::Navigation; +use crate::components::task_list::TaskList; +use crate::models::category::Category; +use crate::route::Route; +use chrono::NaiveDate; +use dioxus::core_macro::rsx; +use dioxus::dioxus_core::Element; +use dioxus::prelude::*; +use crate::components::create_task_button::CreateTaskButton; +use crate::components::sticky_bottom::StickyBottom; +use crate::components::task_form::TaskForm; +use crate::server::tasks::get_tasks_in_category; + +#[component] +pub(crate) fn CategoryCalendarPage() -> Element { + let tasks = use_server_future( + move || get_tasks_in_category(Category::Calendar { + date: NaiveDate::default(), + reoccurrence: None, + time: None, + }) + )?.unwrap().unwrap(); + + rsx! { + TaskList { + tasks: tasks, + class: "pb-36" + } + } +} diff --git a/src/components/pages/category_done_page.rs b/src/components/pages/category_done_page.rs new file mode 100644 index 0000000..b524ec8 --- /dev/null +++ b/src/components/pages/category_done_page.rs @@ -0,0 +1,27 @@ +use crate::components::bottom_panel::BottomPanel; +use crate::components::navigation::Navigation; +use crate::components::task_list::TaskList; +use crate::models::category::Category; +use crate::route::Route; +use chrono::NaiveDate; +use dioxus::core_macro::rsx; +use dioxus::dioxus_core::Element; +use dioxus::prelude::*; +use crate::components::create_task_button::CreateTaskButton; +use crate::components::sticky_bottom::StickyBottom; +use crate::components::task_form::TaskForm; +use crate::server::tasks::get_tasks_in_category; + +#[component] +pub(crate) fn CategoryDonePage() -> Element { + let tasks = use_server_future( + move || get_tasks_in_category(Category::Done) + )?.unwrap().unwrap(); + + rsx! { + TaskList { + tasks: tasks, + class: "pb-36" + } + } +} diff --git a/src/components/pages/category_inbox_page.rs b/src/components/pages/category_inbox_page.rs new file mode 100644 index 0000000..ca592bc --- /dev/null +++ b/src/components/pages/category_inbox_page.rs @@ -0,0 +1,27 @@ +use crate::components::bottom_panel::BottomPanel; +use crate::components::navigation::Navigation; +use crate::components::task_list::TaskList; +use crate::models::category::Category; +use crate::route::Route; +use chrono::NaiveDate; +use dioxus::core_macro::rsx; +use dioxus::dioxus_core::Element; +use dioxus::prelude::*; +use crate::components::create_task_button::CreateTaskButton; +use crate::components::sticky_bottom::StickyBottom; +use crate::components::task_form::TaskForm; +use crate::server::tasks::get_tasks_in_category; + +#[component] +pub(crate) fn CategoryInboxPage() -> Element { + let tasks = use_server_future( + move || get_tasks_in_category(Category::Inbox) + )?.unwrap().unwrap(); + + rsx! { + TaskList { + tasks: tasks, + class: "pb-36" + } + } +} diff --git a/src/components/pages/category_long_term_page.rs b/src/components/pages/category_long_term_page.rs new file mode 100644 index 0000000..4cc90de --- /dev/null +++ b/src/components/pages/category_long_term_page.rs @@ -0,0 +1,27 @@ +use crate::components::bottom_panel::BottomPanel; +use crate::components::navigation::Navigation; +use crate::components::task_list::TaskList; +use crate::models::category::Category; +use crate::route::Route; +use chrono::NaiveDate; +use dioxus::core_macro::rsx; +use dioxus::dioxus_core::Element; +use dioxus::prelude::*; +use crate::components::create_task_button::CreateTaskButton; +use crate::components::sticky_bottom::StickyBottom; +use crate::components::task_form::TaskForm; +use crate::server::tasks::get_tasks_in_category; + +#[component] +pub(crate) fn CategoryLongTermPage() -> Element { + let tasks = use_server_future( + move || get_tasks_in_category(Category::LongTerm) + )?.unwrap().unwrap(); + + rsx! { + TaskList { + tasks: tasks, + class: "pb-36" + } + } +} diff --git a/src/components/pages/category_next_steps_page.rs b/src/components/pages/category_next_steps_page.rs new file mode 100644 index 0000000..bfbe17f --- /dev/null +++ b/src/components/pages/category_next_steps_page.rs @@ -0,0 +1,27 @@ +use crate::components::bottom_panel::BottomPanel; +use crate::components::navigation::Navigation; +use crate::components::task_list::TaskList; +use crate::models::category::Category; +use crate::route::Route; +use chrono::NaiveDate; +use dioxus::core_macro::rsx; +use dioxus::dioxus_core::Element; +use dioxus::prelude::*; +use crate::components::create_task_button::CreateTaskButton; +use crate::components::sticky_bottom::StickyBottom; +use crate::components::task_form::TaskForm; +use crate::server::tasks::get_tasks_in_category; + +#[component] +pub(crate) fn CategoryNextStepsPage() -> Element { + let tasks = use_server_future( + move || get_tasks_in_category(Category::NextSteps) + )?.unwrap().unwrap(); + + rsx! { + TaskList { + tasks: tasks, + class: "pb-36" + } + } +} diff --git a/src/components/pages/category_someday_maybe_page.rs b/src/components/pages/category_someday_maybe_page.rs new file mode 100644 index 0000000..bb70a17 --- /dev/null +++ b/src/components/pages/category_someday_maybe_page.rs @@ -0,0 +1,27 @@ +use crate::components::bottom_panel::BottomPanel; +use crate::components::navigation::Navigation; +use crate::components::task_list::TaskList; +use crate::models::category::Category; +use crate::route::Route; +use chrono::NaiveDate; +use dioxus::core_macro::rsx; +use dioxus::dioxus_core::Element; +use dioxus::prelude::*; +use crate::components::create_task_button::CreateTaskButton; +use crate::components::sticky_bottom::StickyBottom; +use crate::components::task_form::TaskForm; +use crate::server::tasks::get_tasks_in_category; + +#[component] +pub(crate) fn CategorySomedayMaybePage() -> Element { + let tasks = use_server_future( + move || get_tasks_in_category(Category::SomedayMaybe) + )?.unwrap().unwrap(); + + rsx! { + TaskList { + tasks: tasks, + class: "pb-36" + } + } +} diff --git a/src/components/pages/category_today_page.rs b/src/components/pages/category_today_page.rs new file mode 100644 index 0000000..d01f477 --- /dev/null +++ b/src/components/pages/category_today_page.rs @@ -0,0 +1,39 @@ +use crate::components::bottom_panel::BottomPanel; +use crate::components::create_task_button::CreateTaskButton; +use crate::components::navigation::Navigation; +use crate::components::sticky_bottom::StickyBottom; +use crate::components::task_form::TaskForm; +use crate::components::task_list::TaskList; +use crate::models::category::Category; +use crate::models::task::Task; +use crate::route::Route; +use crate::schema::tasks::category; +use crate::server::tasks::get_tasks_in_category; +use chrono::{Local, NaiveDate}; +use dioxus::core_macro::rsx; +use dioxus::dioxus_core::Element; +use dioxus::prelude::*; + +#[component] +pub(crate) fn CategoryTodayPage() -> Element { + let tasks = use_server_future( + move || get_tasks_in_category(Category::Calendar { + date: NaiveDate::default(), + reoccurrence: None, + time: None, + }) + )?.unwrap().unwrap().iter().filter(|task| { + if let Category::Calendar { date, .. } = task.category() { + *date == Local::now().date_naive() + } else { + panic!("Unexpected category."); + } + }).cloned().collect::>(); + + rsx! { + TaskList { + tasks: tasks, + class: "pb-36" + } + } +} diff --git a/src/components/pages/category_trash_page.rs b/src/components/pages/category_trash_page.rs new file mode 100644 index 0000000..538c85d --- /dev/null +++ b/src/components/pages/category_trash_page.rs @@ -0,0 +1,27 @@ +use crate::components::bottom_panel::BottomPanel; +use crate::components::navigation::Navigation; +use crate::components::task_list::TaskList; +use crate::models::category::Category; +use crate::route::Route; +use chrono::NaiveDate; +use dioxus::core_macro::rsx; +use dioxus::dioxus_core::Element; +use dioxus::prelude::*; +use crate::components::create_task_button::CreateTaskButton; +use crate::components::sticky_bottom::StickyBottom; +use crate::components::task_form::TaskForm; +use crate::server::tasks::get_tasks_in_category; + +#[component] +pub(crate) fn CategoryTrashPage() -> Element { + let tasks = use_server_future( + move || get_tasks_in_category(Category::Trash) + )?.unwrap().unwrap(); + + rsx! { + TaskList { + tasks: tasks, + class: "pb-36" + } + } +} diff --git a/src/components/pages/category_waiting_for_page.rs b/src/components/pages/category_waiting_for_page.rs new file mode 100644 index 0000000..3145bfc --- /dev/null +++ b/src/components/pages/category_waiting_for_page.rs @@ -0,0 +1,27 @@ +use crate::components::bottom_panel::BottomPanel; +use crate::components::navigation::Navigation; +use crate::components::task_list::TaskList; +use crate::models::category::Category; +use crate::route::Route; +use chrono::NaiveDate; +use dioxus::core_macro::rsx; +use dioxus::dioxus_core::Element; +use dioxus::prelude::*; +use crate::components::create_task_button::CreateTaskButton; +use crate::components::sticky_bottom::StickyBottom; +use crate::components::task_form::TaskForm; +use crate::server::tasks::get_tasks_in_category; + +#[component] +pub(crate) fn CategoryWaitingForPage() -> Element { + let tasks = use_server_future( + move || get_tasks_in_category(Category::WaitingFor(String::new())) + )?.unwrap().unwrap(); + + rsx! { + TaskList { + tasks: tasks, + class: "pb-36" + } + } +} diff --git a/src/components/pages/mod.rs b/src/components/pages/mod.rs new file mode 100644 index 0000000..59bb5ef --- /dev/null +++ b/src/components/pages/mod.rs @@ -0,0 +1,11 @@ +pub(crate) mod category_inbox_page; +pub(crate) mod category_calendar_page; +pub(crate) mod category_today_page; +pub(crate) mod category_waiting_for_page; +pub(crate) mod category_long_term_page; +pub(crate) mod category_next_steps_page; +pub(crate) mod category_someday_maybe_page; +pub(crate) mod category_done_page; +pub(crate) mod category_trash_page; +pub(crate) mod not_found_page; +pub(crate) mod projects_page; diff --git a/src/components/pages/not_found_page.rs b/src/components/pages/not_found_page.rs new file mode 100644 index 0000000..73e3ebd --- /dev/null +++ b/src/components/pages/not_found_page.rs @@ -0,0 +1,11 @@ +use dioxus::prelude::*; +use crate::components::task_list::TaskList; +use crate::models::category::Category; +use crate::route::Route; + +#[component] +pub(crate) fn NotFoundPage(route: Vec) -> Element { + rsx! { + {"404"} + } +} diff --git a/src/components/pages/projects_page.rs b/src/components/pages/projects_page.rs new file mode 100644 index 0000000..5c8b018 --- /dev/null +++ b/src/components/pages/projects_page.rs @@ -0,0 +1,7 @@ +use dioxus::prelude::*; + +#[component] +pub(crate) fn ProjectsPage() -> Element { + rsx! { + } +} diff --git a/src/components/reoccurrence_input.rs b/src/components/reoccurrence_input.rs new file mode 100644 index 0000000..30099a3 --- /dev/null +++ b/src/components/reoccurrence_input.rs @@ -0,0 +1,79 @@ +use crate::models::category::{Category, Reoccurrence, ReoccurrenceInterval}; +use crate::server::tasks::get_tasks_in_category; +use chrono::NaiveDate; +use dioxus::core_macro::rsx; +use dioxus::dioxus_core::Element; +use dioxus::prelude::*; +use std::fmt::format; + +#[component] +pub(crate) fn ReoccurrenceIntervalInput( + reoccurrence_interval: Signal>, + class_buttons: Option<&'static str> +) -> Element { + rsx! { + button { + r#type: "button", + class: format!( + "py-2 rounded-lg {} {}", + class_buttons.unwrap_or(""), + if reoccurrence_interval().is_none() { "bg-zinc-500/50" } + else { "bg-zinc-800/50" } + ), + onclick: move |_| { + reoccurrence_interval.set(None); + }, + i { + class: "fa-solid fa-ban" + } + }, + button { + r#type: "button", + class: format!( + "py-2 rounded-lg {} {}", + class_buttons.unwrap_or(""), + if let Some(ReoccurrenceInterval::Day) = reoccurrence_interval() + { "bg-zinc-500/50" } + else { "bg-zinc-800/50" } + ), + onclick: move |_| { + reoccurrence_interval.set(Some(ReoccurrenceInterval::Day)) + }, + i { + class: "fa-solid fa-sun" + } + }, + button { + r#type: "button", + class: format!( + "py-2 rounded-lg {} {}", + class_buttons.unwrap_or(""), + if let Some(ReoccurrenceInterval::Month) = reoccurrence_interval() + { "bg-zinc-500/50" } + else { "bg-zinc-800/50" } + ), + onclick: move |_| { + reoccurrence_interval.set(Some(ReoccurrenceInterval::Month)) + }, + i { + class: "fa-solid fa-moon" + } + }, + button { + r#type: "button", + class: format!( + "py-2 rounded-lg {} {}", + class_buttons.unwrap_or(""), + if let Some(ReoccurrenceInterval::Year) = reoccurrence_interval() + { "bg-zinc-500/50" } + else { "bg-zinc-800/50" } + ), + onclick: move |_| { + reoccurrence_interval.set(Some(ReoccurrenceInterval::Year)) + }, + i { + class: "fa-solid fa-earth-europe" + } + } + } +} diff --git a/src/components/sticky_bottom.rs b/src/components/sticky_bottom.rs new file mode 100644 index 0000000..9dbb9d7 --- /dev/null +++ b/src/components/sticky_bottom.rs @@ -0,0 +1,14 @@ +use dioxus::prelude::*; +use crate::components::task_list::TaskList; +use crate::models::category::Category; +use crate::route::Route; + +#[component] +pub(crate) fn StickyBottom(children: Element) -> Element { + rsx! { + div { + class: "fixed bottom-0 left-0 right-0 flex flex-col", + {children} + } + } +} diff --git a/src/components/task_form.rs b/src/components/task_form.rs index a280437..981dcd8 100644 --- a/src/components/task_form.rs +++ b/src/components/task_form.rs @@ -1,45 +1,65 @@ -use chrono::Duration; -use crate::models::category::{CalendarTime, Category}; +use std::fmt::Display; +use crate::components::category_input::CategoryInput; +use crate::components::reoccurrence_input::ReoccurrenceIntervalInput; +use crate::models::category::{CalendarTime, Category, Reoccurrence, ReoccurrenceInterval}; use crate::models::task::NewTask; use crate::server::projects::get_projects; use crate::server::tasks::create_task; +use chrono::{Duration, NaiveDate}; use dioxus::core_macro::{component, rsx}; use dioxus::dioxus_core::Element; use dioxus::prelude::*; +use crate::route::Route; + +const REMINDER_OFFSETS: [Option; 17] = [ + None, + Some(Duration::days(1)), + Some(Duration::hours(12)), + Some(Duration::hours(11)), + Some(Duration::hours(10)), + Some(Duration::hours(9)), + Some(Duration::hours(8)), + Some(Duration::hours(7)), + Some(Duration::hours(6)), + Some(Duration::hours(5)), + Some(Duration::hours(4)), + Some(Duration::hours(3)), + Some(Duration::hours(2)), + Some(Duration::hours(1)), + Some(Duration::minutes(30)), + Some(Duration::minutes(10)), + Some(Duration::zero()), +]; #[component] pub(crate) fn TaskForm() -> Element { - let categories = vec![ - Category::Inbox, - Category::SomedayMaybe, - Category::WaitingFor(String::new()), - Category::NextSteps, - Category::Calendar { - date: chrono::Local::now().date_naive(), - reoccurance_interval: None, - time: None, - }, - Category::LongTerm, - ]; let projects = use_server_future(get_projects)?.unwrap().unwrap(); - let mut selected_category_index = use_signal::(|| 0); - let mut category_calendar_is_reoccurring = use_signal::(|| false); - let mut category_calendar_has_time = use_signal::(|| false); - let mut category_calendar_has_reminder = use_signal::(|| false); + let route = use_route::(); + let mut selected_category = use_signal(|| match route { + Route::CategorySomedayMaybePage => Category::SomedayMaybe, + Route::CategoryWaitingForPage => Category::WaitingFor(String::new()), + Route::CategoryNextStepsPage => Category::NextSteps, + Route::CategoryCalendarPage | Route::CategoryTodayPage => Category::Calendar { + date: NaiveDate::default(), + reoccurrence: None, + time: None, + }, + Route::CategoryLongTermPage => Category::LongTerm, + _ => Category::Inbox, + }); + let mut category_calendar_reoccurrence_interval = use_signal(|| None); + let mut category_calendar_has_time = use_signal(|| false); + let mut category_calendar_reminder_offset_index = use_signal(|| REMINDER_OFFSETS.len() - 1); rsx! { form { onsubmit: move |event| { - let categories = categories.clone(); async move { let new_task = NewTask::new( event.values().get("title").unwrap().as_value(), event.values().get("deadline").unwrap().as_value().parse().ok(), - match &categories[ - event.values().get("category_index").unwrap() - .as_value().parse::().unwrap() - ] { + match &selected_category() { Category::WaitingFor(_) => Category::WaitingFor( event.values().get("category_waiting_for").unwrap() .as_value() @@ -47,24 +67,24 @@ pub(crate) fn TaskForm() -> Element { Category::Calendar { .. } => Category::Calendar { date: event.values().get("category_calendar_date").unwrap() .as_value().parse().unwrap(), - reoccurance_interval: - event.values().get("category_calendar_is_reoccurring").map( - |_| Duration::days( - event.values().get("category_calendar_reoccurance_interval") + reoccurrence: category_calendar_reoccurrence_interval().map( + |reoccurrence_interval| Reoccurrence::new( + event.values().get("category_calendar_date").unwrap() + .as_value().parse().unwrap(), + reoccurrence_interval, + event.values().get("category_calendar_reoccurrence_length") .unwrap().as_value().parse().unwrap() ) ), time: event.values().get("category_calendar_time").unwrap() - .as_value().parse().ok().map(|time| + .as_value().parse().ok().map(|time| CalendarTime::new( time, - event.values().get("category_calendar_has_reminder").map( - |_| Duration::minutes( - event.values() - .get("category_calendar_reminder_offset").unwrap() - .as_value().parse().unwrap() - ) - ) + REMINDER_OFFSETS[ + event.values() + .get("category_calendar_reminder_offset_index").unwrap() + .as_value().parse::().unwrap() + ] ) ) }, @@ -77,145 +97,207 @@ pub(crate) fn TaskForm() -> Element { } }, class: "p-4 flex flex-col gap-4", - input { - r#type: "text", - name: "title", - required: true, - placeholder: "title", - class: "p-2 bg-neutral-700 rounded", - }, - select { - name: "category_index", - oninput: move |event| { - selected_category_index.set(event.value().parse().unwrap()); - }, - class: "p-2 bg-neutral-700 rounded", - option { - value: 0, - "inbox" - }, - option { - value: 1, - "someday maybe" - }, - option { - value: 2, - "waiting for" - }, - option { - value: 3, - "next steps" - }, - option { - value: 4, - "calendar" - }, - option { - value: 5, - "long term" - }, - }, - match categories[selected_category_index()] { - Category::WaitingFor(_) => rsx !{ - input { - r#type: "text", - name: "category_waiting_for", - required: true, - class: "p-2 bg-neutral-700 rounded", + div { + class: "flex flex-row items-center gap-3", + label { + r#for: "input_title", + class: "min-w-6 text-center", + i { + class: "fa-solid fa-pen-clip text-zinc-400/50" }, }, - Category::Calendar { .. } => rsx !{ - input { - r#type: "date", - name: "category_calendar_date", - required: true, - class: "p-2 bg-neutral-700 rounded", + input { + r#type: "text", + name: "title", + required: true, + class: "py-2 px-3 grow bg-zinc-800/50 rounded-lg", + id: "input_title" + }, + }, + div { + class: "flex flex-row items-center gap-3", + label { + r#for: "input_project", + class: "min-w-6 text-center", + i { + class: "fa-solid fa-list text-zinc-400/50" + } + }, + select { + name: "project_id", + class: "px-3.5 py-2.5 bg-zinc-800/50 rounded-lg grow", + id: "input_project", + option { + value: 0, + "None" }, + for project in projects { + option { + value: project.id().to_string(), + {project.title()} + } + } + }, + }, + div { + class: "flex flex-row items-center gap-3", + label { + r#for: "input_deadline", + class: "min-w-6 text-center", + i { + class: "fa-solid fa-bomb text-zinc-400/50" + } + }, + input { + r#type: "date", + name: "deadline", + class: "py-2 px-3 bg-zinc-800/50 rounded-lg grow basis-0", + id: "input_deadline" + } + }, + div { + class: "flex flex-row items-center gap-3", + label { + class: "min-w-6 text-center", + i { + class: "fa-solid fa-layer-group text-zinc-400/50" + } + }, + CategoryInput { + selected_category: selected_category.clone(), + class: "grow" + } + } + match selected_category() { + Category::WaitingFor(_) => rsx! { div { - input { - r#type: "checkbox", - name: "category_calendar_is_reoccurring", - id: "category_calendar_is_reoccurring", - onchange: move |event| { - category_calendar_is_reoccurring.set(event.checked()); + class: "flex flex-row items-center gap-3", + label { + r#for: "input_deadline", + class: "min-w-6 text-center", + i { + class: "fa-solid fa-hourglass-end text-zinc-400/50" } }, - label { - r#for: "category_calendar_is_reoccurring", - " is reoccurring" - } - }, - if category_calendar_is_reoccurring() { input { - r#type: "number", - name: "category_calendar_reoccurance_interval", + r#type: "text", + name: "category_waiting_for", required: true, - min: 1, - placeholder: "reoccurance interval (days)", - class: "p-2 bg-neutral-700 rounded", + class: "py-2 px-3 bg-zinc-800/50 rounded-lg grow", + id: "input_category_waiting_for" + }, + } + }, + Category::Calendar { .. } => rsx! { + div { + class: "flex flex-row items-center gap-3", + label { + r#for: "input_category_calendar_date", + class: "min-w-6 text-center", + i { + class: "fa-solid fa-clock text-zinc-400/50" + } + }, + div { + class: "grow flex flex-row gap-2", + input { + r#type: "date", + name: "category_calendar_date", + required: true, + initial_value: chrono::Local::now().format("%Y-%m-%d").to_string(), + class: "py-2 px-3 bg-zinc-800/50 rounded-lg grow", + id: "input_category_calendar_date" + }, + input { + r#type: "time", + name: "category_calendar_time", + class: "py-2 px-3 bg-zinc-800/50 rounded-lg grow", + id: "input_category_calendar_time", + oninput: move |event| { + category_calendar_has_time.set(!event.value().is_empty()); + } + } } }, - input { - r#type: "time", - name: "category_calendar_time", - class: "p-2 bg-neutral-700 rounded", - oninput: move |event| { - category_calendar_has_time.set(!event.value().is_empty()); + div { + class: "flex flex-row items-center gap-3", + label { + r#for: "category_calendar_reoccurrence_length", + class: "min-w-6 text-center", + i { + class: "fa-solid fa-repeat text-zinc-400/50" + } + }, + div { + class: "grow grid grid-cols-6 gap-2", + ReoccurrenceIntervalInput { + reoccurrence_interval: category_calendar_reoccurrence_interval + }, + input { + r#type: "number", + inputmode: "numeric", + name: "category_calendar_reoccurrence_length", + disabled: category_calendar_reoccurrence_interval().is_none(), + required: true, + min: 1, + initial_value: + if category_calendar_reoccurrence_interval().is_none() { "" } + else { "1" }, + class: "py-2 px-3 bg-zinc-800/50 rounded-lg col-span-2 text-right", + id: "category_calendar_reoccurrence_length" + } } }, if category_calendar_has_time() { div { + class: "flex flex-row items-center gap-3", + label { + r#for: "category_calendar_reminder_offset_index", + class: "min-w-6 text-center", + i { + class: "fa-solid fa-bell text-zinc-400/50" + } + }, input { - r#type: "checkbox", - name: "category_calendar_has_reminder", - value: 0, + r#type: "range", + name: "category_calendar_reminder_offset_index", + min: 0, + max: REMINDER_OFFSETS.len() as i64 - 1, + initial_value: REMINDER_OFFSETS.len() as i64 - 1, + class: "grow input-range-reverse", id: "category_calendar_has_reminder", - onchange: move |event| { - category_calendar_has_reminder.set(event.checked()); + oninput: move |event| { + category_calendar_reminder_offset_index.set( + event.value().parse().unwrap() + ); } }, label { - r#for: "category_calendar_has_reminder", - " set a reminder" + r#for: "category_calendar_reminder_offset_index", + class: "pr-3 min-w-16 text-right", + {REMINDER_OFFSETS[category_calendar_reminder_offset_index()].map( + |offset| if offset.num_hours() < 1 { + format!("{} min", offset.num_minutes()) + } else { + format!("{} h", offset.num_hours()) + } + ).unwrap_or_else(|| "none".to_string())} } } } - if category_calendar_has_reminder() { - input { - r#type: "number", - name: "category_calendar_reminder_offset", - required: true, - min: 0, - placeholder: "reminder offset (minutes)", - class: "p-2 bg-neutral-700 rounded", - } - } }, _ => None }, - input { - r#type: "date", - name: "deadline", - class: "p-2 bg-neutral-700 rounded", - }, - select { - name: "project_id", - class: "p-2 bg-neutral-700 rounded", - option { - value: 0, - "none" - }, - for project in projects { - option { - value: project.id().to_string(), - {project.title()} + div { + class: "flex flex-row justify-end mt-auto", + button { + r#type: "submit", + class: "py-2 px-4 bg-zinc-300/50 rounded-lg", + i { + class: "fa-solid fa-floppy-disk" } } - }, - button { - r#type: "submit", - "create" } } -} + } } diff --git a/src/components/task_list.rs b/src/components/task_list.rs new file mode 100644 index 0000000..0612a2e --- /dev/null +++ b/src/components/task_list.rs @@ -0,0 +1,66 @@ +use crate::models::category::Category; +use crate::models::task::Task; +use crate::server::tasks::get_tasks_in_category; +use dioxus::core_macro::rsx; +use dioxus::dioxus_core::Element; +use dioxus::prelude::*; + +#[component] +pub(crate) fn TaskList(tasks: Vec, class: Option<&'static str>) -> Element { + rsx! { + div { + class: format!("pt-3 px-8 flex flex-col {}", class.unwrap_or("")), + for task in tasks { + div { + class: format!( + "pt-5 {} flex flex-row gap-4", + if task.deadline().is_some() { + "pb-0.5" + } else if let Category::Calendar { time, .. } = task.category() { + if time.is_some() { + "pb-0.5" + } else { + "pb-5" + } + } else { + "pb-5" + } + ), + i { + class: "fa-regular fa-square text-3xl text-zinc-600", + }, + div { + class: "flex flex-col", + div { + class: "mt-1 grow", + {task.title()} + }, + div { + class: "flex flex-row gap-3", + if let Some(deadline) = task.deadline() { + div { + class: "text-sm text-zinc-400", + i { + class: "fa-solid fa-bomb" + }, + {deadline.format(" %m. %d.").to_string()} + } + } + if let Category::Calendar { time, .. } = task.category() { + if let Some(calendar_time) = time { + div { + class: "text-sm text-zinc-400", + i { + class: "fa-solid fa-clock" + }, + {calendar_time.time().format(" %k:%M").to_string()} + } + } + } + } + } + } + } + } + } +} diff --git a/src/models/category.rs b/src/models/category.rs index c6ca42c..ad35ba6 100644 --- a/src/models/category.rs +++ b/src/models/category.rs @@ -1,10 +1,12 @@ +use crate::schema::tasks; use chrono::{Duration, NaiveDate, NaiveTime}; use diesel::deserialize::FromSql; use diesel::pg::{Pg, PgValue}; use diesel::serialize::{Output, ToSql}; -use diesel::sql_types::Jsonb; -use diesel::{AsExpression, FromSqlRow}; +use diesel::sql_types::{Bool, Jsonb}; +use diesel::{AsExpression, BoxableExpression, FromSqlRow, PgJsonbExpressionMethods}; use serde::{Deserialize, Serialize}; +use serde_json::json; use serde_with::DurationSeconds; use std::io::Write; @@ -18,8 +20,7 @@ pub enum Category { NextSteps, Calendar { date: NaiveDate, - #[serde_as(as = "Option>")] - reoccurance_interval: Option, + reoccurrence: Option, time: Option, }, LongTerm, @@ -27,17 +28,26 @@ pub enum Category { Trash, } -#[serde_with::serde_as] -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct CalendarTime { - time: NaiveTime, - #[serde_as(as = "Option>")] - reminder_offset: Option, +impl Category { + pub fn eq_sql_predicate(&self) -> Box> { + use crate::schema::tasks::dsl::*; + + match self { + Category::Inbox => Box::new(category.contains(json!("Inbox"))), + Category::SomedayMaybe => Box::new(category.contains(json!("SomedayMaybe"))), + Category::WaitingFor(_) => Box::new(category.has_key("WaitingFor")), + Category::NextSteps => Box::new(category.contains(json!("NextSteps"))), + Category::Calendar { .. } => Box::new(category.has_key("Calendar")), + Category::LongTerm => Box::new(category.contains(json!("LongTerm"))), + Category::Done => Box::new(category.contains(json!("Done"))), + Category::Trash => Box::new(category.contains(json!("Trash"))), + } + } } -impl CalendarTime { - pub fn new(time: NaiveTime, reminder_offset: Option) -> Self { - Self { time, reminder_offset } +impl PartialEq for Category { + fn eq(&self, other: &Self) -> bool { + std::mem::discriminant(self) == std::mem::discriminant(other) } } @@ -63,3 +73,53 @@ impl FromSql for Category { serde_json::from_str(str).map_err(Into::into) } } + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub enum ReoccurrenceInterval { + Day, + Month, + Year, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Reoccurrence { + start_date: NaiveDate, + interval: ReoccurrenceInterval, + length: u32, +} + +impl Reoccurrence { + pub fn new(start_date: NaiveDate, interval: ReoccurrenceInterval, length: u32) -> Self { + Self { start_date, interval, length } + } + + pub fn interval(&self) -> &ReoccurrenceInterval { + &self.interval + } + + pub fn length(&self) -> u32 { + self.length + } +} + +#[serde_with::serde_as] +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct CalendarTime { + time: NaiveTime, + #[serde_as(as = "Option>")] + reminder_offset: Option, +} + +impl CalendarTime { + pub fn new(time: NaiveTime, reminder_offset: Option) -> Self { + Self { time, reminder_offset } + } + + pub fn time(&self) -> NaiveTime { + self.time + } + + pub fn reminder_offset(&self) -> Option { + self.reminder_offset + } +} diff --git a/src/models/task.rs b/src/models/task.rs index d67256a..058874d 100644 --- a/src/models/task.rs +++ b/src/models/task.rs @@ -7,7 +7,7 @@ use crate::schema::tasks; const TITLE_LENGTH_MIN: u64 = 1; const TITLE_LENGTH_MAX: u64 = 255; -#[derive(Queryable, Selectable, Serialize, Deserialize, Clone, Debug)] +#[derive(Queryable, Selectable, Serialize, Deserialize, PartialEq, Clone, Debug)] #[diesel(table_name = crate::schema::tasks)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct Task { diff --git a/src/route/mod.rs b/src/route/mod.rs index 275f297..8beedcf 100644 --- a/src/route/mod.rs +++ b/src/route/mod.rs @@ -1,8 +1,48 @@ use crate::components::home::Home; +use crate::components::pages::category_inbox_page::CategoryInboxPage; +use crate::components::pages::category_next_steps_page::CategoryNextStepsPage; +use crate::components::pages::category_today_page::CategoryTodayPage; +use crate::components::pages::category_trash_page::CategoryTrashPage; +use crate::components::pages::category_waiting_for_page::CategoryWaitingForPage; +use crate::components::pages::category_someday_maybe_page::CategorySomedayMaybePage; +use crate::components::pages::category_done_page::CategoryDonePage; +use crate::components::pages::category_calendar_page::CategoryCalendarPage; +use crate::components::pages::category_long_term_page::CategoryLongTermPage; +use crate::components::pages::projects_page::ProjectsPage; +use crate::components::pages::not_found_page::NotFoundPage; +use crate::components::layout::Layout; use dioxus::prelude::*; +use crate::models::category::Category; #[derive(Clone, Routable, Debug, PartialEq)] +#[rustfmt::skip] pub(crate) enum Route { - #[route("/")] - Home {}, + #[layout(Layout)] + #[redirect("/", || Route::CategoryTodayPage {})] + #[route("/today")] + CategoryTodayPage, + #[route("/inbox")] + CategoryInboxPage, + #[route("/someday-maybe")] + CategorySomedayMaybePage, + #[route("/waiting-for")] + CategoryWaitingForPage, + #[route("/next-steps")] + CategoryNextStepsPage, + #[route("/calendar")] + CategoryCalendarPage, + #[route("/long-term")] + CategoryLongTermPage, + #[route("/done")] + CategoryDonePage, + #[route("/trash")] + CategoryTrashPage, + #[route("/projects")] + ProjectsPage, + #[end_layout] + #[redirect("/", || Route::CategoryTodayPage)] + #[route("/:..route")] + NotFoundPage { + route: Vec, + }, } diff --git a/src/server/tasks.rs b/src/server/tasks.rs index 0b7c086..62c383a 100644 --- a/src/server/tasks.rs +++ b/src/server/tasks.rs @@ -2,10 +2,11 @@ use crate::errors::error::Error; use crate::errors::error_vec::ErrorVec; use crate::models::task::{NewTask, Task}; use crate::server::database_connection::establish_database_connection; -use diesel::{RunQueryDsl, SelectableHelper}; +use diesel::{QueryDsl, RunQueryDsl, SelectableHelper}; use dioxus::prelude::*; use validator::Validate; use crate::errors::task_create_error::TaskCreateError; +use crate::models::category::Category; #[server] pub(crate) async fn create_task(new_task: NewTask) @@ -43,3 +44,24 @@ pub(crate) async fn create_task(new_task: NewTask) Ok(new_task) } + +#[server] +pub(crate) async fn get_tasks_in_category(filtered_category: Category) + -> Result, ServerFnError>> { + use crate::schema::tasks::dsl::*; + + let mut connection = establish_database_connection() + .map_err::, _>( + |_| vec![Error::ServerInternal].into() + )?; + + let results = tasks + .select(Task::as_select()) + .filter(filtered_category.eq_sql_predicate()) + .load::(&mut connection) + .map_err::, _>( + |_| vec![Error::ServerInternal].into() + )?; + + Ok(results) +} -- 2.47.1 From 3379d9a070ddf25c26a82efc67f4b5346169a34c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Thu, 5 Sep 2024 17:09:50 +0200 Subject: [PATCH 04/15] build: add dependencies --- Cargo.lock | 22 ++++++++++++++++++++++ Cargo.toml | 3 ++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index b45af6b..3e9311c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -471,6 +471,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "pure-rust-locales", "serde", "wasm-bindgen", "windows-targets 0.52.6", @@ -960,6 +961,17 @@ dependencies = [ "tracing-wasm", ] +[[package]] +name = "dioxus-query" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c84184c06ee2823957aa3ef939da6a04f0ab934f007745eb65dadb105bde241" +dependencies = [ + "dioxus-lib", + "futures-util", + "instant", +] + [[package]] name = "dioxus-router" version = "0.5.6" @@ -1805,6 +1817,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -2279,6 +2294,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pure-rust-locales" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1190fd18ae6ce9e137184f207593877e70f39b015040156b1e05081cdfe3733a" + [[package]] name = "quote" version = "1.0.36" @@ -2881,6 +2902,7 @@ dependencies = [ "diesel", "dioxus", "dioxus-logger", + "dioxus-query", "dotenvy", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 23b97f1..dee2047 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -chrono = { version = "0.4.38", features = ["serde"] } +chrono = { version = "0.4.38", features = ["serde", "unstable-locales"] } diesel = { version = "2.2.2", features = ["chrono", "postgres", "postgres_backend", "serde_json"] } dioxus = { version = "0.5", features = ["fullstack", "router"] } @@ -22,6 +22,7 @@ tracing = "0.1.40" tracing-wasm = "0.2.1" serde_with = { version = "3.9.0", features = ["chrono_0_4"] } async-std = "1.12.0" +dioxus-query = "0.5.1" [features] default = [] -- 2.47.1 From 7c16bc45460d5f6651e34715258d0f8099c3e86f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Thu, 5 Sep 2024 17:15:31 +0200 Subject: [PATCH 05/15] feat: create a module for Dioxus query --- src/main.rs | 1 + src/query/mod.rs | 22 ++++++++++++++++++++++ src/query/tasks.rs | 24 ++++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 src/query/mod.rs create mode 100644 src/query/tasks.rs diff --git a/src/main.rs b/src/main.rs index b7109a5..a7a75b6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ mod models; mod route; mod schema; mod server; +mod query; use components::app::App; use dioxus::prelude::*; diff --git a/src/query/mod.rs b/src/query/mod.rs new file mode 100644 index 0000000..5bbe59c --- /dev/null +++ b/src/query/mod.rs @@ -0,0 +1,22 @@ +use crate::errors::error::Error; +use crate::errors::error_vec::ErrorVec; +use crate::models::category::Category; +use crate::models::task::Task; + +pub(crate) mod tasks; + +#[derive(PartialEq, Debug)] +pub(crate) enum QueryValue { + Tasks(Vec), +} + +#[derive(Debug)] +pub(crate) enum QueryErrors { + Error(ErrorVec), +} + +#[derive(PartialEq, Eq, Hash, Clone, Debug)] +pub(crate) enum QueryKey { + Tasks, + TasksInCategory(Category), +} diff --git a/src/query/tasks.rs b/src/query/tasks.rs new file mode 100644 index 0000000..b1f5272 --- /dev/null +++ b/src/query/tasks.rs @@ -0,0 +1,24 @@ +use dioxus::prelude::ServerFnError; +use dioxus_query::prelude::{use_get_query, QueryResult, UseQuery}; +use crate::models::category::Category; +use crate::query::{QueryErrors, QueryKey, QueryValue}; +use crate::server::tasks::get_tasks_in_category; + + + +pub(crate) fn use_tasks_in_category_query(category: Category) + -> UseQuery { + use_get_query([QueryKey::TasksInCategory(category), QueryKey::Tasks], fetch_tasks_in_category) +} + +async fn fetch_tasks_in_category(keys: Vec) -> QueryResult { + if let Some(QueryKey::TasksInCategory(category)) = keys.first() { + match get_tasks_in_category(category.clone()).await { + Ok(tasks) => Ok(QueryValue::Tasks(tasks)), + Err(ServerFnError::WrappedServerError(errors)) => Err(QueryErrors::Error(errors)), + Err(error) => panic!("Unexpected error: {:?}", error) + }.into() + } else { + panic!("Unexpected query keys: {:?}", keys); + } +} -- 2.47.1 From 27ba44188e191273cc34107a1c0580dc8e59fb76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Thu, 5 Sep 2024 17:19:35 +0200 Subject: [PATCH 06/15] feat: use Dioxus query for fetching data --- src/components/app.rs | 6 +- src/components/mod.rs | 2 +- .../pages/category_calendar_page.rs | 84 +++++++-- src/components/pages/category_done_page.rs | 19 +- src/components/pages/category_inbox_page.rs | 19 +- .../pages/category_long_term_page.rs | 19 +- .../pages/category_next_steps_page.rs | 19 +- src/components/pages/category_page.rs | 32 ++++ .../pages/category_someday_maybe_page.rs | 19 +- src/components/pages/category_today_page.rs | 173 +++++++++++++++--- src/components/pages/category_trash_page.rs | 19 +- .../pages/category_waiting_for_page.rs | 19 +- src/components/pages/mod.rs | 1 + src/components/task_form.rs | 9 +- src/components/task_list.rs | 8 +- src/models/category.rs | 10 +- src/models/task.rs | 1 + 17 files changed, 290 insertions(+), 169 deletions(-) create mode 100644 src/components/pages/category_page.rs diff --git a/src/components/app.rs b/src/components/app.rs index 5597b7e..708b0f5 100644 --- a/src/components/app.rs +++ b/src/components/app.rs @@ -2,12 +2,16 @@ use crate::route::Route; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; +use dioxus_query::prelude::{use_init_query_client}; +use crate::query::{QueryErrors, QueryKey, QueryValue}; #[component] pub(crate) fn App() -> Element { + use_init_query_client::(); + rsx! { div { - class: "min-h-screen text-zinc-200 bg-zinc-800", + class: "min-h-screen text-zinc-200 bg-zinc-800 pt-4 pb-36", Router:: {} } } diff --git a/src/components/mod.rs b/src/components/mod.rs index e60f716..bd961d8 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -11,4 +11,4 @@ pub(crate) mod sticky_bottom; pub(crate) mod category_input; pub(crate) mod reoccurrence_input; pub(crate) mod layout; -mod navigation_item; +pub(crate) mod navigation_item; diff --git a/src/components/pages/category_calendar_page.rs b/src/components/pages/category_calendar_page.rs index 6c434ce..7876621 100644 --- a/src/components/pages/category_calendar_page.rs +++ b/src/components/pages/category_calendar_page.rs @@ -1,31 +1,77 @@ -use crate::components::bottom_panel::BottomPanel; -use crate::components::navigation::Navigation; -use crate::components::task_list::TaskList; use crate::models::category::Category; -use crate::route::Route; -use chrono::NaiveDate; +use chrono::{Datelike, Local, Locale}; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; -use crate::components::create_task_button::CreateTaskButton; -use crate::components::sticky_bottom::StickyBottom; -use crate::components::task_form::TaskForm; -use crate::server::tasks::get_tasks_in_category; +use dioxus_query::prelude::QueryResult; +use crate::components::task_list::TaskList; +use crate::query::QueryValue; +use crate::query::tasks::use_tasks_in_category_query; +use crate::models::task::Task; + +const CALENDAR_LENGTH_DAYS: usize = 366 * 3; #[component] pub(crate) fn CategoryCalendarPage() -> Element { - let tasks = use_server_future( - move || get_tasks_in_category(Category::Calendar { - date: NaiveDate::default(), - reoccurrence: None, - time: None, - }) - )?.unwrap().unwrap(); + let tasks = use_tasks_in_category_query(Category::Calendar { + date: Local::now().date_naive(), + reoccurrence: None, + time: None, + }); + let tasks_query_result = tasks.result(); rsx! { - TaskList { - tasks: tasks, - class: "pb-36" + match tasks_query_result.value() { + QueryResult::Ok(QueryValue::Tasks(tasks)) + | QueryResult::Loading(Some(QueryValue::Tasks(tasks))) => { + let today_date = Local::now().date_naive(); + + rsx! { + div { + class: "pt-4 flex flex-col gap-8", + for date_current in today_date.iter_days().take(CALENDAR_LENGTH_DAYS) { + div { + class: "flex flex-col gap-4", + div { + class: "px-8 flex flex-row items-center gap-2 font-bold", + div { + class: "pt-1", + { + date_current + .format_localized( + format!( + "%A %-d. %B{}", + if date_current.year() != today_date.year() {" %Y"} + else {""} + ).as_str(), + Locale::en_US + ) + .to_string() + } + } + } + TaskList { + tasks: tasks.iter().filter(|task| { + if let Category::Calendar { date, .. } = task.category() { + *date == date_current + } else { + panic!("Unexpected category."); + } + }).cloned().collect::>() + } + } + } + } + } + }, + QueryResult::Loading(None) => rsx! { + // TODO: Add a loading indicator. + }, + QueryResult::Err(errors) => rsx! { + div { + "Errors occurred: {errors:?}" + } + } } } } diff --git a/src/components/pages/category_done_page.rs b/src/components/pages/category_done_page.rs index b524ec8..ad65d31 100644 --- a/src/components/pages/category_done_page.rs +++ b/src/components/pages/category_done_page.rs @@ -1,27 +1,14 @@ -use crate::components::bottom_panel::BottomPanel; -use crate::components::navigation::Navigation; -use crate::components::task_list::TaskList; use crate::models::category::Category; -use crate::route::Route; -use chrono::NaiveDate; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; -use crate::components::create_task_button::CreateTaskButton; -use crate::components::sticky_bottom::StickyBottom; -use crate::components::task_form::TaskForm; -use crate::server::tasks::get_tasks_in_category; +use crate::components::pages::category_page::CategoryPage; #[component] pub(crate) fn CategoryDonePage() -> Element { - let tasks = use_server_future( - move || get_tasks_in_category(Category::Done) - )?.unwrap().unwrap(); - rsx! { - TaskList { - tasks: tasks, - class: "pb-36" + CategoryPage { + category: Category::Done, } } } diff --git a/src/components/pages/category_inbox_page.rs b/src/components/pages/category_inbox_page.rs index ca592bc..debc408 100644 --- a/src/components/pages/category_inbox_page.rs +++ b/src/components/pages/category_inbox_page.rs @@ -1,27 +1,14 @@ -use crate::components::bottom_panel::BottomPanel; -use crate::components::navigation::Navigation; -use crate::components::task_list::TaskList; use crate::models::category::Category; -use crate::route::Route; -use chrono::NaiveDate; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; -use crate::components::create_task_button::CreateTaskButton; -use crate::components::sticky_bottom::StickyBottom; -use crate::components::task_form::TaskForm; -use crate::server::tasks::get_tasks_in_category; +use crate::components::pages::category_page::CategoryPage; #[component] pub(crate) fn CategoryInboxPage() -> Element { - let tasks = use_server_future( - move || get_tasks_in_category(Category::Inbox) - )?.unwrap().unwrap(); - rsx! { - TaskList { - tasks: tasks, - class: "pb-36" + CategoryPage { + category: Category::Inbox, } } } diff --git a/src/components/pages/category_long_term_page.rs b/src/components/pages/category_long_term_page.rs index 4cc90de..437de37 100644 --- a/src/components/pages/category_long_term_page.rs +++ b/src/components/pages/category_long_term_page.rs @@ -1,27 +1,14 @@ -use crate::components::bottom_panel::BottomPanel; -use crate::components::navigation::Navigation; -use crate::components::task_list::TaskList; use crate::models::category::Category; -use crate::route::Route; -use chrono::NaiveDate; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; -use crate::components::create_task_button::CreateTaskButton; -use crate::components::sticky_bottom::StickyBottom; -use crate::components::task_form::TaskForm; -use crate::server::tasks::get_tasks_in_category; +use crate::components::pages::category_page::CategoryPage; #[component] pub(crate) fn CategoryLongTermPage() -> Element { - let tasks = use_server_future( - move || get_tasks_in_category(Category::LongTerm) - )?.unwrap().unwrap(); - rsx! { - TaskList { - tasks: tasks, - class: "pb-36" + CategoryPage { + category: Category::LongTerm, } } } diff --git a/src/components/pages/category_next_steps_page.rs b/src/components/pages/category_next_steps_page.rs index bfbe17f..c1086f3 100644 --- a/src/components/pages/category_next_steps_page.rs +++ b/src/components/pages/category_next_steps_page.rs @@ -1,27 +1,14 @@ -use crate::components::bottom_panel::BottomPanel; -use crate::components::navigation::Navigation; -use crate::components::task_list::TaskList; use crate::models::category::Category; -use crate::route::Route; -use chrono::NaiveDate; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; -use crate::components::create_task_button::CreateTaskButton; -use crate::components::sticky_bottom::StickyBottom; -use crate::components::task_form::TaskForm; -use crate::server::tasks::get_tasks_in_category; +use crate::components::pages::category_page::CategoryPage; #[component] pub(crate) fn CategoryNextStepsPage() -> Element { - let tasks = use_server_future( - move || get_tasks_in_category(Category::NextSteps) - )?.unwrap().unwrap(); - rsx! { - TaskList { - tasks: tasks, - class: "pb-36" + CategoryPage { + category: Category::NextSteps, } } } diff --git a/src/components/pages/category_page.rs b/src/components/pages/category_page.rs new file mode 100644 index 0000000..aa0e3dd --- /dev/null +++ b/src/components/pages/category_page.rs @@ -0,0 +1,32 @@ +use crate::components::task_list::TaskList; +use crate::models::category::Category; +use crate::query::tasks::use_tasks_in_category_query; +use crate::query::QueryValue; +use dioxus::core_macro::rsx; +use dioxus::dioxus_core::Element; +use dioxus::prelude::*; +use dioxus_query::prelude::QueryResult; + +#[component] +pub(crate) fn CategoryPage(category: Category) -> Element { + let tasks_query = use_tasks_in_category_query(category); + let tasks_query_result = tasks_query.result(); + + match tasks_query_result.value() { + QueryResult::Ok(QueryValue::Tasks(tasks)) + | QueryResult::Loading(Some(QueryValue::Tasks(tasks))) => rsx! { + TaskList { + tasks: tasks.clone(), + class: "pb-36" + } + }, + QueryResult::Loading(None) => rsx! { + // TODO: Add a loading indicator. + }, + QueryResult::Err(errors) => rsx! { + div { + "Errors occurred: {errors:?}" + } + } + } +} diff --git a/src/components/pages/category_someday_maybe_page.rs b/src/components/pages/category_someday_maybe_page.rs index bb70a17..7a2070f 100644 --- a/src/components/pages/category_someday_maybe_page.rs +++ b/src/components/pages/category_someday_maybe_page.rs @@ -1,27 +1,14 @@ -use crate::components::bottom_panel::BottomPanel; -use crate::components::navigation::Navigation; -use crate::components::task_list::TaskList; use crate::models::category::Category; -use crate::route::Route; -use chrono::NaiveDate; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; -use crate::components::create_task_button::CreateTaskButton; -use crate::components::sticky_bottom::StickyBottom; -use crate::components::task_form::TaskForm; -use crate::server::tasks::get_tasks_in_category; +use crate::components::pages::category_page::CategoryPage; #[component] pub(crate) fn CategorySomedayMaybePage() -> Element { - let tasks = use_server_future( - move || get_tasks_in_category(Category::SomedayMaybe) - )?.unwrap().unwrap(); - rsx! { - TaskList { - tasks: tasks, - class: "pb-36" + CategoryPage { + category: Category::SomedayMaybe, } } } diff --git a/src/components/pages/category_today_page.rs b/src/components/pages/category_today_page.rs index d01f477..6fb573e 100644 --- a/src/components/pages/category_today_page.rs +++ b/src/components/pages/category_today_page.rs @@ -1,39 +1,158 @@ -use crate::components::bottom_panel::BottomPanel; -use crate::components::create_task_button::CreateTaskButton; -use crate::components::navigation::Navigation; -use crate::components::sticky_bottom::StickyBottom; -use crate::components::task_form::TaskForm; use crate::components::task_list::TaskList; use crate::models::category::Category; use crate::models::task::Task; -use crate::route::Route; -use crate::schema::tasks::category; -use crate::server::tasks::get_tasks_in_category; -use chrono::{Local, NaiveDate}; -use dioxus::core_macro::rsx; -use dioxus::dioxus_core::Element; +use crate::query::tasks::use_tasks_in_category_query; +use crate::query::QueryValue; +use chrono::{Local, Locale}; use dioxus::prelude::*; +use dioxus_query::prelude::QueryResult; #[component] pub(crate) fn CategoryTodayPage() -> Element { - let tasks = use_server_future( - move || get_tasks_in_category(Category::Calendar { - date: NaiveDate::default(), - reoccurrence: None, - time: None, - }) - )?.unwrap().unwrap().iter().filter(|task| { - if let Category::Calendar { date, .. } = task.category() { - *date == Local::now().date_naive() - } else { - panic!("Unexpected category."); - } - }).cloned().collect::>(); + let today_date = Local::now().date_naive(); + + let calendar_tasks_query = use_tasks_in_category_query(Category::Calendar { + date: today_date, + reoccurrence: None, + time: None, + }); + let calendar_tasks_query_result = calendar_tasks_query.result(); + + let long_term_tasks_query = use_tasks_in_category_query(Category::LongTerm); + let long_term_tasks_query_result = long_term_tasks_query.result(); rsx! { - TaskList { - tasks: tasks, - class: "pb-36" + div { + class: "pt-4 flex flex-col gap-8", + match long_term_tasks_query_result.value() { + QueryResult::Ok(QueryValue::Tasks(tasks)) + | QueryResult::Loading(Some(QueryValue::Tasks(tasks))) => rsx! { + div { + class: "flex flex-col gap-4", + div { + class: "px-8 flex flex-row items-center gap-2 font-bold", + i { + class: "fa-solid fa-water text-xl" + } + div { + class: "mt-1", + "Long-term" + } + } + div { + for task in tasks { + div { + key: "{task.id()}", + class: format!( + "px-8 pt-5 {} flex flex-row gap-4", + if task.deadline().is_some() { + "pb-0.5" + } else { + "pb-5" + } + ), + div { + class: "flex flex-col", + div { + class: "mt grow font-medium", + {task.title()} + }, + div { + class: "flex flex-row gap-3", + if let Some(deadline) = task.deadline() { + div { + class: "text-sm text-zinc-400", + i { + class: "fa-solid fa-bomb" + }, + {deadline.format(" %m. %d.").to_string()} + } + } + } + } + } + } + } + } + }, + QueryResult::Loading(None) => rsx! { + // TODO: Add a loading indicator. + }, + QueryResult::Err(errors) => rsx! { + div { + "Errors occurred: {errors:?}" + } + } + } + match calendar_tasks_query_result.value() { + QueryResult::Ok(QueryValue::Tasks(tasks)) + | QueryResult::Loading(Some(QueryValue::Tasks(tasks))) => { + let today_tasks = tasks.iter().filter(|task| { + if let Category::Calendar { date, .. } = task.category() { + *date == today_date + } else { + panic!("Unexpected category."); + } + }).cloned().collect::>(); + let overdue_tasks = tasks.iter().filter(|task| { + if let Category::Calendar { date, .. } = task.category() { + *date < today_date + } else { + panic!("Unexpected category."); + } + }).cloned().collect::>(); + + rsx! { + if !overdue_tasks.is_empty() { + div { + class: "flex flex-col gap-4", + div { + class: "px-8 flex flex-row items-center gap-2 font-bold", + i { + class: "fa-solid fa-calendar-xmark text-xl" + } + div { + class: "mt-1", + "Overdue" + } + } + TaskList { + tasks: overdue_tasks, + class: "pb-3" + } + } + } + div { + class: "flex flex-col gap-4", + div { + class: "px-8 flex flex-row items-center gap-2 font-bold", + i { + class: "fa-solid fa-calendar-check text-xl" + } + div { + class: "mt-1", + { + today_date + .format_localized("Today, %A %-d. %B", Locale::en_US) + .to_string() + } + } + } + TaskList { + tasks: today_tasks + } + } + } + }, + QueryResult::Loading(None) => rsx! { + // TODO: Add a loading indicator. + }, + QueryResult::Err(errors) => rsx! { + div { + "Errors occurred: {errors:?}" + } + } + } } } } diff --git a/src/components/pages/category_trash_page.rs b/src/components/pages/category_trash_page.rs index 538c85d..36b75f7 100644 --- a/src/components/pages/category_trash_page.rs +++ b/src/components/pages/category_trash_page.rs @@ -1,27 +1,14 @@ -use crate::components::bottom_panel::BottomPanel; -use crate::components::navigation::Navigation; -use crate::components::task_list::TaskList; use crate::models::category::Category; -use crate::route::Route; -use chrono::NaiveDate; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; -use crate::components::create_task_button::CreateTaskButton; -use crate::components::sticky_bottom::StickyBottom; -use crate::components::task_form::TaskForm; -use crate::server::tasks::get_tasks_in_category; +use crate::components::pages::category_page::CategoryPage; #[component] pub(crate) fn CategoryTrashPage() -> Element { - let tasks = use_server_future( - move || get_tasks_in_category(Category::Trash) - )?.unwrap().unwrap(); - rsx! { - TaskList { - tasks: tasks, - class: "pb-36" + CategoryPage { + category: Category::Trash, } } } diff --git a/src/components/pages/category_waiting_for_page.rs b/src/components/pages/category_waiting_for_page.rs index 3145bfc..062373a 100644 --- a/src/components/pages/category_waiting_for_page.rs +++ b/src/components/pages/category_waiting_for_page.rs @@ -1,27 +1,14 @@ -use crate::components::bottom_panel::BottomPanel; -use crate::components::navigation::Navigation; -use crate::components::task_list::TaskList; use crate::models::category::Category; -use crate::route::Route; -use chrono::NaiveDate; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; -use crate::components::create_task_button::CreateTaskButton; -use crate::components::sticky_bottom::StickyBottom; -use crate::components::task_form::TaskForm; -use crate::server::tasks::get_tasks_in_category; +use crate::components::pages::category_page::CategoryPage; #[component] pub(crate) fn CategoryWaitingForPage() -> Element { - let tasks = use_server_future( - move || get_tasks_in_category(Category::WaitingFor(String::new())) - )?.unwrap().unwrap(); - rsx! { - TaskList { - tasks: tasks, - class: "pb-36" + CategoryPage { + category: Category::WaitingFor(String::new()), } } } diff --git a/src/components/pages/mod.rs b/src/components/pages/mod.rs index 59bb5ef..9dcc2c3 100644 --- a/src/components/pages/mod.rs +++ b/src/components/pages/mod.rs @@ -9,3 +9,4 @@ pub(crate) mod category_done_page; pub(crate) mod category_trash_page; pub(crate) mod not_found_page; pub(crate) mod projects_page; +pub(crate) mod category_page; diff --git a/src/components/task_form.rs b/src/components/task_form.rs index 981dcd8..99e1879 100644 --- a/src/components/task_form.rs +++ b/src/components/task_form.rs @@ -1,4 +1,3 @@ -use std::fmt::Display; use crate::components::category_input::CategoryInput; use crate::components::reoccurrence_input::ReoccurrenceIntervalInput; use crate::models::category::{CalendarTime, Category, Reoccurrence, ReoccurrenceInterval}; @@ -9,6 +8,8 @@ use chrono::{Duration, NaiveDate}; use dioxus::core_macro::{component, rsx}; use dioxus::dioxus_core::Element; use dioxus::prelude::*; +use dioxus_query::prelude::use_query_client; +use crate::query::{QueryErrors, QueryKey, QueryValue}; use crate::route::Route; const REMINDER_OFFSETS: [Option; 17] = [ @@ -52,6 +53,8 @@ pub(crate) fn TaskForm() -> Element { let mut category_calendar_has_time = use_signal(|| false); let mut category_calendar_reminder_offset_index = use_signal(|| REMINDER_OFFSETS.len() - 1); + let query_client = use_query_client::(); + rsx! { form { onsubmit: move |event| { @@ -94,6 +97,10 @@ pub(crate) fn TaskForm() -> Element { .as_value().parse::().ok().filter(|&id| id > 0), ); let _ = create_task(new_task).await; + query_client.invalidate_queries(&[ + QueryKey::Tasks, + QueryKey::TasksInCategory(selected_category()) + ]); } }, class: "p-4 flex flex-col gap-4", diff --git a/src/components/task_list.rs b/src/components/task_list.rs index 0612a2e..c9b04cb 100644 --- a/src/components/task_list.rs +++ b/src/components/task_list.rs @@ -1,6 +1,5 @@ use crate::models::category::Category; use crate::models::task::Task; -use crate::server::tasks::get_tasks_in_category; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; @@ -9,11 +8,12 @@ use dioxus::prelude::*; pub(crate) fn TaskList(tasks: Vec, class: Option<&'static str>) -> Element { rsx! { div { - class: format!("pt-3 px-8 flex flex-col {}", class.unwrap_or("")), + class: format!("flex flex-col {}", class.unwrap_or("")), for task in tasks { div { + key: "{task.id()}", class: format!( - "pt-5 {} flex flex-row gap-4", + "px-8 pt-5 {} flex flex-row gap-4", if task.deadline().is_some() { "pb-0.5" } else if let Category::Calendar { time, .. } = task.category() { @@ -32,7 +32,7 @@ pub(crate) fn TaskList(tasks: Vec, class: Option<&'static str>) -> Element div { class: "flex flex-col", div { - class: "mt-1 grow", + class: "mt-1 grow font-medium", {task.title()} }, div { diff --git a/src/models/category.rs b/src/models/category.rs index ad35ba6..f20417d 100644 --- a/src/models/category.rs +++ b/src/models/category.rs @@ -11,7 +11,7 @@ use serde_with::DurationSeconds; use std::io::Write; #[serde_with::serde_as] -#[derive(AsExpression, FromSqlRow, Serialize, Deserialize, Clone, Debug)] +#[derive(AsExpression, FromSqlRow, Serialize, Deserialize, Hash, Clone, Debug)] #[diesel(sql_type = Jsonb)] pub enum Category { Inbox, @@ -51,6 +51,8 @@ impl PartialEq for Category { } } +impl Eq for Category {} + impl ToSql for Category { fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> diesel::serialize::Result { let json = serde_json::to_string(self)?; @@ -74,14 +76,14 @@ impl FromSql for Category { } } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Hash, Clone, Debug)] pub enum ReoccurrenceInterval { Day, Month, Year, } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Hash, Clone, Debug)] pub struct Reoccurrence { start_date: NaiveDate, interval: ReoccurrenceInterval, @@ -103,7 +105,7 @@ impl Reoccurrence { } #[serde_with::serde_as] -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Hash, Clone, Debug)] pub struct CalendarTime { time: NaiveTime, #[serde_as(as = "Option>")] diff --git a/src/models/task.rs b/src/models/task.rs index 058874d..0d1d46d 100644 --- a/src/models/task.rs +++ b/src/models/task.rs @@ -1,3 +1,4 @@ +use std::cmp::Ordering; use diesel::prelude::*; use serde::{Deserialize, Serialize}; use validator::Validate; -- 2.47.1 From dfefeab69e73c2055bb60a76562f95620cf39554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Thu, 5 Sep 2024 18:18:03 +0200 Subject: [PATCH 07/15] feat: display a project form upon clicking the create button on the projects page --- src/components/bottom_panel.rs | 39 +++++++++++----- src/components/create_task_button.rs | 2 +- src/components/layout.rs | 16 +++---- .../pages/category_calendar_page.rs | 3 +- src/components/pages/category_page.rs | 3 +- src/components/pages/category_today_page.rs | 6 ++- src/components/pages/projects_page.rs | 29 ++++++++++++ src/components/project_form.rs | 44 +++++++++++++++---- src/components/task_form.rs | 11 ++--- src/models/project.rs | 2 +- src/models/task.rs | 5 +-- src/query/mod.rs | 4 ++ src/query/projects.rs | 20 +++++++++ 13 files changed, 138 insertions(+), 46 deletions(-) create mode 100644 src/query/projects.rs diff --git a/src/components/bottom_panel.rs b/src/components/bottom_panel.rs index 1332e78..9dc512b 100644 --- a/src/components/bottom_panel.rs +++ b/src/components/bottom_panel.rs @@ -1,18 +1,17 @@ -use std::thread::sleep; use dioxus::prelude::*; use crate::components::navigation::Navigation; +use crate::components::project_form::ProjectForm; use crate::components::task_form::TaskForm; -use crate::components::task_list::TaskList; -use crate::models::category::Category; use crate::route::Route; #[component] -pub(crate) fn BottomPanel(creating_task: bool) -> Element { - let mut expanded = use_signal(|| creating_task); +pub(crate) fn BottomPanel(display_form: Signal) -> Element { + let mut expanded = use_signal(|| display_form()); let navigation_expanded = use_signal(|| false); + let current_route = use_route(); - use_effect(use_reactive(&creating_task, move |creating_task| { - if creating_task { + use_effect(use_reactive(&display_form, move |creating_task| { + if creating_task() { expanded.set(true); } else { spawn(async move { @@ -26,14 +25,30 @@ pub(crate) fn BottomPanel(creating_task: bool) -> Element { div { class: format!( "bg-zinc-700/50 rounded-t-xl border-t-zinc-600 border-t backdrop-blur drop-shadow-[0_-5px_10px_rgba(0,0,0,0.2)] transition-[height] duration-[500ms] ease-[cubic-bezier(0.79,0.14,0.15,0.86)] {}", - match (creating_task, navigation_expanded()) { - (false, false) => "h-[64px]", - (false, true) => "h-[128px]", - (true, _) => "h-[448px]", + match (display_form(), current_route, navigation_expanded()) { + (false, _, false) => "h-[64px]", + (false, _, true) => "h-[128px]", + (true, Route::ProjectsPage, _) => "h-[128px]", + (true, _, _) => "h-[448px]", } ), if expanded() { - TaskForm {} + match current_route { + Route::ProjectsPage => rsx! { + ProjectForm { + on_successful_submit: move |_| { + display_form.set(false); + } + } + }, + _ => rsx! { + TaskForm { + on_successful_submit: move |_| { + display_form.set(false); + } + } + } + } } else { Navigation { expanded: navigation_expanded, diff --git a/src/components/create_task_button.rs b/src/components/create_task_button.rs index b87a855..acb235d 100644 --- a/src/components/create_task_button.rs +++ b/src/components/create_task_button.rs @@ -4,7 +4,7 @@ use crate::models::category::Category; use crate::route::Route; #[component] -pub(crate) fn CreateTaskButton(creating: Signal) -> Element { +pub(crate) fn CreateButton(creating: Signal) -> Element { rsx! { button { class: "m-4 py-3 px-5 self-end text-center bg-zinc-300/50 rounded-xl border-t-zinc-200 border-t backdrop-blur drop-shadow-[0_-5px_10px_rgba(0,0,0,0.2)] text-2xl text-zinc-200", diff --git a/src/components/layout.rs b/src/components/layout.rs index 5ddb57c..ed89b5f 100644 --- a/src/components/layout.rs +++ b/src/components/layout.rs @@ -1,29 +1,23 @@ use crate::components::bottom_panel::BottomPanel; -use crate::components::navigation::Navigation; -use crate::components::task_list::TaskList; -use crate::models::category::Category; use crate::route::Route; -use chrono::NaiveDate; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; -use crate::components::create_task_button::CreateTaskButton; +use crate::components::create_task_button::CreateButton; use crate::components::sticky_bottom::StickyBottom; -use crate::components::task_form::TaskForm; -use crate::server::tasks::get_tasks_in_category; #[component] pub(crate) fn Layout() -> Element { - let creating_task = use_signal(|| false); + let display_form = use_signal(|| false); rsx! { Outlet:: {} StickyBottom { - CreateTaskButton { - creating: creating_task, + CreateButton { + creating: display_form, } BottomPanel { - creating_task: creating_task(), + display_form: display_form, } } } diff --git a/src/components/pages/category_calendar_page.rs b/src/components/pages/category_calendar_page.rs index 7876621..1a610b3 100644 --- a/src/components/pages/category_calendar_page.rs +++ b/src/components/pages/category_calendar_page.rs @@ -71,7 +71,8 @@ pub(crate) fn CategoryCalendarPage() -> Element { div { "Errors occurred: {errors:?}" } - } + }, + value => panic!("Unexpected query result: {value:?}") } } } diff --git a/src/components/pages/category_page.rs b/src/components/pages/category_page.rs index aa0e3dd..0dd2b0c 100644 --- a/src/components/pages/category_page.rs +++ b/src/components/pages/category_page.rs @@ -27,6 +27,7 @@ pub(crate) fn CategoryPage(category: Category) -> Element { div { "Errors occurred: {errors:?}" } - } + }, + value => panic!("Unexpected query result: {value:?}") } } diff --git a/src/components/pages/category_today_page.rs b/src/components/pages/category_today_page.rs index 6fb573e..085b71b 100644 --- a/src/components/pages/category_today_page.rs +++ b/src/components/pages/category_today_page.rs @@ -82,7 +82,8 @@ pub(crate) fn CategoryTodayPage() -> Element { div { "Errors occurred: {errors:?}" } - } + }, + value => panic!("Unexpected query result: {value:?}") } match calendar_tasks_query_result.value() { QueryResult::Ok(QueryValue::Tasks(tasks)) @@ -151,7 +152,8 @@ pub(crate) fn CategoryTodayPage() -> Element { div { "Errors occurred: {errors:?}" } - } + }, + value => panic!("Unexpected query result: {value:?}") } } } diff --git a/src/components/pages/projects_page.rs b/src/components/pages/projects_page.rs index 5c8b018..fd7f34e 100644 --- a/src/components/pages/projects_page.rs +++ b/src/components/pages/projects_page.rs @@ -1,7 +1,36 @@ use dioxus::prelude::*; +use dioxus_query::prelude::QueryResult; +use crate::query::projects::use_projects_query; +use crate::query::QueryValue; #[component] pub(crate) fn ProjectsPage() -> Element { + let projects_query = use_projects_query(); + rsx! { + match projects_query.result().value() { + QueryResult::Ok(QueryValue::Projects(projects)) + | QueryResult::Loading(Some(QueryValue::Projects(projects))) => rsx! { + div { + class: "flex flex-col", + for project in projects { + div { + key: "{project.id()}", + class: "px-8 py-4", + {project.title()} + } + } + } + }, + QueryResult::Loading(None) => rsx! { + // TODO: Add a loading indicator. + }, + QueryResult::Err(errors) => rsx! { + div { + "Errors occurred: {errors:?}" + } + }, + value => panic!("Unexpected query result: {value:?}") + } } } diff --git a/src/components/project_form.rs b/src/components/project_form.rs index 6d64abf..fc7ad2f 100644 --- a/src/components/project_form.rs +++ b/src/components/project_form.rs @@ -3,9 +3,13 @@ use crate::server::projects::create_project; use dioxus::core_macro::{component, rsx}; use dioxus::dioxus_core::Element; use dioxus::prelude::*; +use dioxus_query::prelude::use_query_client; +use crate::query::{QueryErrors, QueryKey, QueryValue}; #[component] -pub(crate) fn ProjectForm() -> Element { +pub(crate) fn ProjectForm(on_successful_submit: EventHandler<()>) -> Element { + let query_client = use_query_client::(); + rsx! { form { onsubmit: move |event| { @@ -14,17 +18,39 @@ pub(crate) fn ProjectForm() -> Element { event.values().get("title").unwrap().as_value() ); let _ = create_project(new_project).await; + query_client.invalidate_queries(&[ + QueryKey::Projects + ]); + on_successful_submit.call(()); } }, - input { - r#type: "text", - name: "title", - required: true, - placeholder: "title" + class: "p-4 flex flex-col gap-4", + div { + class: "flex flex-row items-center gap-3", + label { + r#for: "input_title", + class: "min-w-6 text-center", + i { + class: "fa-solid fa-pen-clip text-zinc-400/50" + } + } + input { + r#type: "text", + name: "title", + required: true, + class: "py-2 px-3 grow bg-zinc-800/50 rounded-lg", + id: "input_title" + } } - button { - r#type: "submit", - "create" + div { + class: "flex flex-row justify-end mt-auto", + button { + r#type: "submit", + class: "py-2 px-4 bg-zinc-300/50 rounded-lg", + i { + class: "fa-solid fa-floppy-disk" + } + } } } } diff --git a/src/components/task_form.rs b/src/components/task_form.rs index 99e1879..95cabd1 100644 --- a/src/components/task_form.rs +++ b/src/components/task_form.rs @@ -1,6 +1,6 @@ use crate::components::category_input::CategoryInput; use crate::components::reoccurrence_input::ReoccurrenceIntervalInput; -use crate::models::category::{CalendarTime, Category, Reoccurrence, ReoccurrenceInterval}; +use crate::models::category::{CalendarTime, Category, Reoccurrence}; use crate::models::task::NewTask; use crate::server::projects::get_projects; use crate::server::tasks::create_task; @@ -33,11 +33,11 @@ const REMINDER_OFFSETS: [Option; 17] = [ ]; #[component] -pub(crate) fn TaskForm() -> Element { +pub(crate) fn TaskForm(on_successful_submit: EventHandler<()>) -> Element { let projects = use_server_future(get_projects)?.unwrap().unwrap(); let route = use_route::(); - let mut selected_category = use_signal(|| match route { + let selected_category = use_signal(|| match route { Route::CategorySomedayMaybePage => Category::SomedayMaybe, Route::CategoryWaitingForPage => Category::WaitingFor(String::new()), Route::CategoryNextStepsPage => Category::NextSteps, @@ -49,7 +49,7 @@ pub(crate) fn TaskForm() -> Element { Route::CategoryLongTermPage => Category::LongTerm, _ => Category::Inbox, }); - let mut category_calendar_reoccurrence_interval = use_signal(|| None); + let category_calendar_reoccurrence_interval = use_signal(|| None); let mut category_calendar_has_time = use_signal(|| false); let mut category_calendar_reminder_offset_index = use_signal(|| REMINDER_OFFSETS.len() - 1); @@ -101,6 +101,7 @@ pub(crate) fn TaskForm() -> Element { QueryKey::Tasks, QueryKey::TasksInCategory(selected_category()) ]); + on_successful_submit.call(()); } }, class: "p-4 flex flex-col gap-4", @@ -171,7 +172,7 @@ pub(crate) fn TaskForm() -> Element { } }, CategoryInput { - selected_category: selected_category.clone(), + selected_category: selected_category, class: "grow" } } diff --git a/src/models/project.rs b/src/models/project.rs index 00aec2e..44898e6 100644 --- a/src/models/project.rs +++ b/src/models/project.rs @@ -6,7 +6,7 @@ use validator::Validate; const TITLE_LENGTH_MIN: u64 = 1; const TITLE_LENGTH_MAX: u64 = 255; -#[derive(Queryable, Selectable, Serialize, Deserialize, Clone, Debug)] +#[derive(Queryable, Selectable, Serialize, Deserialize, PartialEq, Clone, Debug)] #[diesel(table_name = crate::schema::projects)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct Project { diff --git a/src/models/task.rs b/src/models/task.rs index 0d1d46d..4799ff2 100644 --- a/src/models/task.rs +++ b/src/models/task.rs @@ -1,9 +1,8 @@ -use std::cmp::Ordering; +use crate::models::category::Category; +use crate::schema::tasks; use diesel::prelude::*; use serde::{Deserialize, Serialize}; use validator::Validate; -use crate::models::category::Category; -use crate::schema::tasks; const TITLE_LENGTH_MIN: u64 = 1; const TITLE_LENGTH_MAX: u64 = 255; diff --git a/src/query/mod.rs b/src/query/mod.rs index 5bbe59c..7e1566a 100644 --- a/src/query/mod.rs +++ b/src/query/mod.rs @@ -1,13 +1,16 @@ use crate::errors::error::Error; use crate::errors::error_vec::ErrorVec; use crate::models::category::Category; +use crate::models::project::Project; use crate::models::task::Task; pub(crate) mod tasks; +pub(crate) mod projects; #[derive(PartialEq, Debug)] pub(crate) enum QueryValue { Tasks(Vec), + Projects(Vec), } #[derive(Debug)] @@ -19,4 +22,5 @@ pub(crate) enum QueryErrors { pub(crate) enum QueryKey { Tasks, TasksInCategory(Category), + Projects, } diff --git a/src/query/projects.rs b/src/query/projects.rs new file mode 100644 index 0000000..174f330 --- /dev/null +++ b/src/query/projects.rs @@ -0,0 +1,20 @@ +use crate::query::{QueryErrors, QueryKey, QueryValue}; +use crate::server::projects::get_projects; +use dioxus::prelude::ServerFnError; +use dioxus_query::prelude::{use_get_query, QueryResult, UseQuery}; + +pub(crate) fn use_projects_query() -> UseQuery { + use_get_query([QueryKey::Projects, QueryKey::Tasks], fetch_projects) +} + +async fn fetch_projects(keys: Vec) -> QueryResult { + if let Some(QueryKey::Projects) = keys.first() { + match get_projects().await { + Ok(projects) => Ok(QueryValue::Projects(projects)), + Err(ServerFnError::WrappedServerError(errors)) => Err(QueryErrors::Error(errors)), + Err(error) => panic!("Unexpected error: {:?}", error) + }.into() + } else { + panic!("Unexpected query keys: {:?}", keys); + } +} -- 2.47.1 From 528bf195416d8bbbeee7f56c5fb4c1eda39036ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Thu, 5 Sep 2024 19:56:10 +0200 Subject: [PATCH 08/15] style: CSS formatting --- assets/styles/input_number_arrows.css | 6 ++--- assets/styles/input_range.css | 34 +++++++++++++-------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/assets/styles/input_number_arrows.css b/assets/styles/input_number_arrows.css index 7c952dc..4330152 100644 --- a/assets/styles/input_number_arrows.css +++ b/assets/styles/input_number_arrows.css @@ -1,10 +1,10 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { display: none; - -webkit-appearance: none; + appearance: none; margin: 0; } -input[type=number] { - -moz-appearance:textfield; +input[type="number"] { + appearance:textfield; } diff --git a/assets/styles/input_range.css b/assets/styles/input_range.css index 4c56e50..53013c5 100644 --- a/assets/styles/input_range.css +++ b/assets/styles/input_range.css @@ -1,63 +1,63 @@ -input[type=range], -input[type=range]::-webkit-slider-runnable-track, -input[type=range]::-webkit-slider-thumb { +input[type="range"], +input[type="range"]::-webkit-slider-runnable-track, +input[type="range"]::-webkit-slider-thumb { appearance: none; } -input[type=range] { +input[type="range"] { background: transparent; } -input[type=range]::-moz-range-thumb { +input[type="range"]::-moz-range-thumb { width: 1.25rem; height: 1.25rem; - background: rgba(228, 228, 231); + background: rgba(228 228 231); border: 0; border-radius: 0.5rem; } -input[type=range]::-moz-range-progress { +input[type="range"]::-moz-range-progress { background: #525259; height: 0.5rem; border-radius: 0.25rem; } -input[type=range]::-moz-range-track { - background: rgba(39, 39, 42, 0.5); +input[type="range"]::-moz-range-track { + background: rgba(39 39 42 / 50%); height: 0.5rem; border-radius: 0.25rem; } -input[type=range].input-range-reverse::-moz-range-progress { +input[type="range"].input-range-reverse::-moz-range-progress { background: #2d2d31; height: 0.5rem; border-radius: 0.25rem; } -input[type=range].input-range-reverse::-moz-range-track { +input[type="range"].input-range-reverse::-moz-range-track { background: rgba(113, 113, 122, 0.5); height: 0.5rem; border-radius: 0.25rem; } -input[type=range]::-webkit-slider-thumb { +input[type="range"]::-webkit-slider-thumb { width: 1.25rem; height: 1.25rem; - background: rgba(228, 228, 231); + background: rgba(228 228 231); border: 0; border-radius: 0.5rem; position: relative; top: -0.4rem; } -input[type=range]::-webkit-slider-runnable-track { - background: rgba(39, 39, 42, 0.5); +input[type="range"]::-webkit-slider-runnable-track { + background: rgba(39 39 42 / 50%); height: 0.5rem; border-radius: 0.25rem; } -input[type=range].input-range-reverse::-webkit-slider-runnable-track { - background: rgba(39, 39, 42, 0.5); +input[type="range"].input-range-reverse::-webkit-slider-runnable-track { + background: rgba(39 39 42 / 50%); height: 0.5rem; border-radius: 0.25rem; } -- 2.47.1 From c8eac5f55016aa32fc375028387c58f624c17f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Thu, 5 Sep 2024 20:19:33 +0200 Subject: [PATCH 09/15] style: ignore a redundant closure warning --- src/components/bottom_panel.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/bottom_panel.rs b/src/components/bottom_panel.rs index 9dc512b..2b78b51 100644 --- a/src/components/bottom_panel.rs +++ b/src/components/bottom_panel.rs @@ -6,6 +6,7 @@ use crate::route::Route; #[component] pub(crate) fn BottomPanel(display_form: Signal) -> Element { + #[allow(clippy::redundant_closure)] let mut expanded = use_signal(|| display_form()); let navigation_expanded = use_signal(|| false); let current_route = use_route(); @@ -46,7 +47,7 @@ pub(crate) fn BottomPanel(display_form: Signal) -> Element { on_successful_submit: move |_| { display_form.set(false); } - } + } } } } else { -- 2.47.1 From f205af75634bce88a7e4bbf0f1a9ce0414ea9102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Thu, 5 Sep 2024 20:23:09 +0200 Subject: [PATCH 10/15] fix: remove unused imports --- src/components/category_input.rs | 2 -- src/components/create_task_button.rs | 3 --- src/components/navigation.rs | 2 -- src/components/navigation_item.rs | 2 -- src/components/pages/not_found_page.rs | 3 --- src/components/reoccurrence_input.rs | 5 +---- src/components/sticky_bottom.rs | 3 --- src/route/mod.rs | 2 -- 8 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/components/category_input.rs b/src/components/category_input.rs index 1ea3b8d..b9ffbab 100644 --- a/src/components/category_input.rs +++ b/src/components/category_input.rs @@ -1,10 +1,8 @@ use crate::models::category::Category; -use crate::server::tasks::get_tasks_in_category; use chrono::NaiveDate; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; -use std::fmt::format; #[component] pub(crate) fn CategoryInput(selected_category: Signal, class: Option<&'static str>) -> Element { diff --git a/src/components/create_task_button.rs b/src/components/create_task_button.rs index acb235d..25d69a2 100644 --- a/src/components/create_task_button.rs +++ b/src/components/create_task_button.rs @@ -1,7 +1,4 @@ use dioxus::prelude::*; -use crate::components::task_list::TaskList; -use crate::models::category::Category; -use crate::route::Route; #[component] pub(crate) fn CreateButton(creating: Signal) -> Element { diff --git a/src/components/navigation.rs b/src/components/navigation.rs index 8104116..854e635 100644 --- a/src/components/navigation.rs +++ b/src/components/navigation.rs @@ -1,6 +1,4 @@ use crate::components::navigation_item::NavigationItem; -use crate::components::task_list::TaskList; -use crate::models::category::Category; use crate::route::Route; use dioxus::prelude::*; diff --git a/src/components/navigation_item.rs b/src/components/navigation_item.rs index 3393245..5bfe82b 100644 --- a/src/components/navigation_item.rs +++ b/src/components/navigation_item.rs @@ -1,6 +1,4 @@ use dioxus::prelude::*; -use crate::components::task_list::TaskList; -use crate::models::category::Category; use crate::route::Route; #[component] diff --git a/src/components/pages/not_found_page.rs b/src/components/pages/not_found_page.rs index 73e3ebd..599ec1c 100644 --- a/src/components/pages/not_found_page.rs +++ b/src/components/pages/not_found_page.rs @@ -1,7 +1,4 @@ use dioxus::prelude::*; -use crate::components::task_list::TaskList; -use crate::models::category::Category; -use crate::route::Route; #[component] pub(crate) fn NotFoundPage(route: Vec) -> Element { diff --git a/src/components/reoccurrence_input.rs b/src/components/reoccurrence_input.rs index 30099a3..7f9360f 100644 --- a/src/components/reoccurrence_input.rs +++ b/src/components/reoccurrence_input.rs @@ -1,10 +1,7 @@ -use crate::models::category::{Category, Reoccurrence, ReoccurrenceInterval}; -use crate::server::tasks::get_tasks_in_category; -use chrono::NaiveDate; +use crate::models::category::ReoccurrenceInterval; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; -use std::fmt::format; #[component] pub(crate) fn ReoccurrenceIntervalInput( diff --git a/src/components/sticky_bottom.rs b/src/components/sticky_bottom.rs index 9dbb9d7..972bc3f 100644 --- a/src/components/sticky_bottom.rs +++ b/src/components/sticky_bottom.rs @@ -1,7 +1,4 @@ use dioxus::prelude::*; -use crate::components::task_list::TaskList; -use crate::models::category::Category; -use crate::route::Route; #[component] pub(crate) fn StickyBottom(children: Element) -> Element { diff --git a/src/route/mod.rs b/src/route/mod.rs index 8beedcf..6bb83d2 100644 --- a/src/route/mod.rs +++ b/src/route/mod.rs @@ -1,4 +1,3 @@ -use crate::components::home::Home; use crate::components::pages::category_inbox_page::CategoryInboxPage; use crate::components::pages::category_next_steps_page::CategoryNextStepsPage; use crate::components::pages::category_today_page::CategoryTodayPage; @@ -12,7 +11,6 @@ use crate::components::pages::projects_page::ProjectsPage; use crate::components::pages::not_found_page::NotFoundPage; use crate::components::layout::Layout; use dioxus::prelude::*; -use crate::models::category::Category; #[derive(Clone, Routable, Debug, PartialEq)] #[rustfmt::skip] -- 2.47.1 From 2b39283bf541ace354da27e0c881090d5f31a297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Thu, 5 Sep 2024 20:42:55 +0200 Subject: [PATCH 11/15] fix: implement `Hash` for `Category` manually --- src/models/category.rs | 9 ++++++++- src/query/tasks.rs | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/models/category.rs b/src/models/category.rs index f20417d..f5a7838 100644 --- a/src/models/category.rs +++ b/src/models/category.rs @@ -1,3 +1,4 @@ +use std::hash::Hash; use crate::schema::tasks; use chrono::{Duration, NaiveDate, NaiveTime}; use diesel::deserialize::FromSql; @@ -11,7 +12,7 @@ use serde_with::DurationSeconds; use std::io::Write; #[serde_with::serde_as] -#[derive(AsExpression, FromSqlRow, Serialize, Deserialize, Hash, Clone, Debug)] +#[derive(AsExpression, FromSqlRow, Serialize, Deserialize, Clone, Debug)] #[diesel(sql_type = Jsonb)] pub enum Category { Inbox, @@ -45,6 +46,12 @@ impl Category { } } +impl Hash for Category { + fn hash(&self, state: &mut H) { + std::mem::discriminant(self).hash(state); + } +} + impl PartialEq for Category { fn eq(&self, other: &Self) -> bool { std::mem::discriminant(self) == std::mem::discriminant(other) diff --git a/src/query/tasks.rs b/src/query/tasks.rs index b1f5272..1cf22fc 100644 --- a/src/query/tasks.rs +++ b/src/query/tasks.rs @@ -13,7 +13,7 @@ pub(crate) fn use_tasks_in_category_query(category: Category) async fn fetch_tasks_in_category(keys: Vec) -> QueryResult { if let Some(QueryKey::TasksInCategory(category)) = keys.first() { - match get_tasks_in_category(category.clone()).await { + match get_tasks_in_category(category.clone()).await { Ok(tasks) => Ok(QueryValue::Tasks(tasks)), Err(ServerFnError::WrappedServerError(errors)) => Err(QueryErrors::Error(errors)), Err(error) => panic!("Unexpected error: {:?}", error) -- 2.47.1 From 50e375212c594fdb7193f9d6813888ca6eb8b8ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Fri, 6 Sep 2024 07:10:42 +0200 Subject: [PATCH 12/15] style: CSS formatting --- assets/styles/input_range.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/styles/input_range.css b/assets/styles/input_range.css index 53013c5..8cb8a42 100644 --- a/assets/styles/input_range.css +++ b/assets/styles/input_range.css @@ -35,7 +35,7 @@ input[type="range"].input-range-reverse::-moz-range-progress { } input[type="range"].input-range-reverse::-moz-range-track { - background: rgba(113, 113, 122, 0.5); + background: rgba(113 113 122 / 50%); height: 0.5rem; border-radius: 0.25rem; } -- 2.47.1 From fb66a19a09ac273afde936ceba5b8be41fcbfe64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Fri, 6 Sep 2024 07:39:12 +0200 Subject: [PATCH 13/15] docs: add explaining comments --- src/components/bottom_panel.rs | 2 ++ src/route/mod.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/components/bottom_panel.rs b/src/components/bottom_panel.rs index 2b78b51..c90d542 100644 --- a/src/components/bottom_panel.rs +++ b/src/components/bottom_panel.rs @@ -6,6 +6,7 @@ use crate::route::Route; #[component] pub(crate) fn BottomPanel(display_form: Signal) -> Element { + // A signal for delaying the application of styles. #[allow(clippy::redundant_closure)] let mut expanded = use_signal(|| display_form()); let navigation_expanded = use_signal(|| false); @@ -16,6 +17,7 @@ pub(crate) fn BottomPanel(display_form: Signal) -> Element { expanded.set(true); } else { spawn(async move { + // Necessary for a smooth – not instant – height transition. async_std::task::sleep(std::time::Duration::from_millis(500)).await; expanded.set(false); }); diff --git a/src/route/mod.rs b/src/route/mod.rs index 6bb83d2..e0eb5c3 100644 --- a/src/route/mod.rs +++ b/src/route/mod.rs @@ -12,6 +12,8 @@ use crate::components::pages::not_found_page::NotFoundPage; use crate::components::layout::Layout; use dioxus::prelude::*; +// All variants have the same postfix because they have to match the component names. +#[allow(clippy::enum_variant_names)] #[derive(Clone, Routable, Debug, PartialEq)] #[rustfmt::skip] pub(crate) enum Route { -- 2.47.1 From 733fbb31cde561cf338bfa6414109e31fd03bbd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Fri, 6 Sep 2024 07:40:31 +0200 Subject: [PATCH 14/15] feat: make icons on the today page consistent --- src/components/pages/category_today_page.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/pages/category_today_page.rs b/src/components/pages/category_today_page.rs index 085b71b..47df487 100644 --- a/src/components/pages/category_today_page.rs +++ b/src/components/pages/category_today_page.rs @@ -32,7 +32,7 @@ pub(crate) fn CategoryTodayPage() -> Element { div { class: "px-8 flex flex-row items-center gap-2 font-bold", i { - class: "fa-solid fa-water text-xl" + class: "fa-solid fa-water text-xl w-6 text-center" } div { class: "mt-1", @@ -110,7 +110,7 @@ pub(crate) fn CategoryTodayPage() -> Element { div { class: "px-8 flex flex-row items-center gap-2 font-bold", i { - class: "fa-solid fa-calendar-xmark text-xl" + class: "fa-solid fa-calendar-xmark text-xl w-6 text-center" } div { class: "mt-1", @@ -128,7 +128,7 @@ pub(crate) fn CategoryTodayPage() -> Element { div { class: "px-8 flex flex-row items-center gap-2 font-bold", i { - class: "fa-solid fa-calendar-check text-xl" + class: "fa-solid fa-calendar-check text-xl w-6 text-center" } div { class: "mt-1", -- 2.47.1 From faf6f93f84a6323dcd9da1463c9fae2e2a3a76fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= <66163112+matous-volf@users.noreply.github.com> Date: Fri, 6 Sep 2024 07:40:50 +0200 Subject: [PATCH 15/15] chore: update .idea --- .idea/dataSources.local.xml | 2 +- .idea/webResources.xml | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 .idea/webResources.xml diff --git a/.idea/dataSources.local.xml b/.idea/dataSources.local.xml index a0dc61f..5cb6af1 100644 --- a/.idea/dataSources.local.xml +++ b/.idea/dataSources.local.xml @@ -1,6 +1,6 @@ - + " diff --git a/.idea/webResources.xml b/.idea/webResources.xml new file mode 100644 index 0000000..7356908 --- /dev/null +++ b/.idea/webResources.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file -- 2.47.1