[work] adding an access and created_at metadata to items for sorting
This commit is contained in:
parent
0a77ead382
commit
2f5d7b4d15
15 changed files with 443 additions and 260 deletions
355
Cargo.lock
generated
355
Cargo.lock
generated
|
|
@ -730,12 +730,6 @@ version = "0.22.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
||||
|
||||
[[package]]
|
||||
name = "basic-toml"
|
||||
version = "0.1.10"
|
||||
|
|
@ -832,6 +826,15 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block2"
|
||||
version = "0.5.1"
|
||||
|
|
@ -1159,6 +1162,12 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cmov"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746"
|
||||
|
||||
[[package]]
|
||||
name = "cocoa"
|
||||
version = "0.25.0"
|
||||
|
|
@ -1237,12 +1246,6 @@ version = "3.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e57e3272f0190c3f1584272d613719ba5fc7df7f4942fe542e63d949cf3a649b"
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
|
|
@ -1578,6 +1581,15 @@ dependencies = [
|
|||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "css-color"
|
||||
version = "0.2.8"
|
||||
|
|
@ -1628,6 +1640,15 @@ dependencies = [
|
|||
"dtor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctutils"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
|
||||
dependencies = [
|
||||
"cmov",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cursor-icon"
|
||||
version = "1.2.0"
|
||||
|
|
@ -1731,17 +1752,6 @@ dependencies = [
|
|||
"libdbus-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"pem-rfc7468",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.5.8"
|
||||
|
|
@ -1809,10 +1819,19 @@ version = "0.10.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"const-oid",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
"block-buffer 0.10.4",
|
||||
"crypto-common 0.1.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
|
||||
dependencies = [
|
||||
"block-buffer 0.12.0",
|
||||
"crypto-common 0.2.2",
|
||||
"ctutils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2101,13 +2120,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "etcetera"
|
||||
version = "0.8.0"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
|
||||
checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"home",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2308,9 +2326,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.1"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
||||
checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
|
|
@ -3086,8 +3104,6 @@ version = "0.15.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash 0.1.5",
|
||||
]
|
||||
|
||||
|
|
@ -3110,11 +3126,11 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
|||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
|
||||
checksum = "ea0b22561a9c04a7cb1a302c013e0259cd3b4bb619f145b32f72b8b4bcbed230"
|
||||
dependencies = [
|
||||
"hashbrown 0.15.5",
|
||||
"hashbrown 0.16.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3149,20 +3165,20 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
|||
|
||||
[[package]]
|
||||
name = "hkdf"
|
||||
version = "0.12.4"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
|
||||
checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018"
|
||||
dependencies = [
|
||||
"hmac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.11.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3232,6 +3248,15 @@ version = "1.10.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
||||
|
||||
[[package]]
|
||||
name = "hybrid-array"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.9.0"
|
||||
|
|
@ -4199,9 +4224,6 @@ name = "lazy_static"
|
|||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
dependencies = [
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "leb128fmt"
|
||||
|
|
@ -4562,12 +4584,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
version = "0.10.6"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
|
||||
checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"digest",
|
||||
"digest 0.11.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4909,22 +4931,6 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint-dig"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libm",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-traits",
|
||||
"rand 0.8.6",
|
||||
"smallvec",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.2.1"
|
||||
|
|
@ -4951,17 +4957,6 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.2"
|
||||
|
|
@ -5297,7 +5292,7 @@ dependencies = [
|
|||
"serde_json",
|
||||
"serde_repr",
|
||||
"serde_with",
|
||||
"sha2",
|
||||
"sha2 0.10.9",
|
||||
"thiserror 2.0.18",
|
||||
"time",
|
||||
"tokio",
|
||||
|
|
@ -5544,15 +5539,6 @@ dependencies = [
|
|||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem-rfc7468"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.2"
|
||||
|
|
@ -5705,27 +5691,6 @@ dependencies = [
|
|||
"futures-io",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkcs1"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
|
||||
dependencies = [
|
||||
"der",
|
||||
"pkcs8",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
|
||||
dependencies = [
|
||||
"der",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.33"
|
||||
|
|
@ -6060,8 +6025,6 @@ version = "0.8.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
|
|
@ -6071,7 +6034,7 @@ version = "0.9.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_chacha",
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
|
|
@ -6086,16 +6049,6 @@ dependencies = [
|
|||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
|
|
@ -6111,9 +6064,6 @@ name = "rand_core"
|
|||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom 0.2.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
|
|
@ -6195,7 +6145,7 @@ dependencies = [
|
|||
"paste",
|
||||
"profiling",
|
||||
"rand 0.9.4",
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_chacha",
|
||||
"simd_helpers",
|
||||
"thiserror 2.0.18",
|
||||
"v_frame",
|
||||
|
|
@ -6551,26 +6501,6 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"digest",
|
||||
"num-bigint-dig",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"pkcs1",
|
||||
"pkcs8",
|
||||
"rand_core 0.6.4",
|
||||
"signature",
|
||||
"spki",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtrb"
|
||||
version = "0.3.4"
|
||||
|
|
@ -6607,7 +6537,7 @@ version = "8.11.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1"
|
||||
dependencies = [
|
||||
"sha2",
|
||||
"sha2 0.10.9",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
|
|
@ -7056,7 +6986,18 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.3.0",
|
||||
"digest 0.11.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -7067,7 +7008,18 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.3.0",
|
||||
"digest 0.11.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -7095,16 +7047,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.9"
|
||||
|
|
@ -7291,21 +7233,11 @@ dependencies = [
|
|||
"bitflags 2.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx"
|
||||
version = "0.8.6"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc"
|
||||
checksum = "378620ccc25c62c89d8be1c819e76a88d59bdcc3304733330788948e619bfd71"
|
||||
dependencies = [
|
||||
"sqlx-core",
|
||||
"sqlx-macros",
|
||||
|
|
@ -7316,12 +7248,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sqlx-core"
|
||||
version = "0.8.6"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6"
|
||||
checksum = "05b44e85bf579a8eeb4ceaa77a3a523baf2bf0e9bac7e40f405d537b5d2d5ccb"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"cfg-if",
|
||||
"chrono",
|
||||
"crc",
|
||||
"crossbeam-queue",
|
||||
"either",
|
||||
|
|
@ -7330,16 +7264,15 @@ dependencies = [
|
|||
"futures-intrusive",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"hashbrown 0.15.5",
|
||||
"hashbrown 0.16.1",
|
||||
"hashlink",
|
||||
"indexmap 2.14.0",
|
||||
"log",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sha2 0.10.9",
|
||||
"smallvec",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
|
|
@ -7350,9 +7283,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sqlx-macros"
|
||||
version = "0.8.6"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d"
|
||||
checksum = "bd2b84f2bc39a5705ef27ec785a11c934a41bbd4a24941e257927cddc26b60bf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -7363,81 +7296,68 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sqlx-macros-core"
|
||||
version = "0.8.6"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b"
|
||||
checksum = "fb8d96de5fdc85a5c4ec813432b523ec637e80ba98f046555f75f7908ddac7c3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dotenvy",
|
||||
"either",
|
||||
"heck 0.5.0",
|
||||
"hex",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sha2 0.10.9",
|
||||
"sqlx-core",
|
||||
"sqlx-mysql",
|
||||
"sqlx-postgres",
|
||||
"sqlx-sqlite",
|
||||
"syn",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-mysql"
|
||||
version = "0.8.6"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526"
|
||||
checksum = "90b8020fe17c5f2c245bfa2505d7ef59c5604839527c740266ad2214acebea27"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"base64 0.22.1",
|
||||
"bitflags 2.11.1",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"crc",
|
||||
"digest",
|
||||
"digest 0.11.3",
|
||||
"dotenvy",
|
||||
"either",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"generic-array",
|
||||
"hex",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"itoa",
|
||||
"log",
|
||||
"md-5",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"rand 0.8.6",
|
||||
"rsa",
|
||||
"serde",
|
||||
"sha1",
|
||||
"sha2",
|
||||
"smallvec",
|
||||
"sha1 0.11.0",
|
||||
"sha2 0.11.0",
|
||||
"sqlx-core",
|
||||
"stringprep",
|
||||
"thiserror 2.0.18",
|
||||
"tracing",
|
||||
"whoami",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-postgres"
|
||||
version = "0.8.6"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46"
|
||||
checksum = "87a2bdd6e83f6b3ea525ca9fee568030508b58355a43d0b2c1674d5f79dcd65e"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"base64 0.22.1",
|
||||
"bitflags 2.11.1",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"crc",
|
||||
"dotenvy",
|
||||
"etcetera",
|
||||
|
|
@ -7447,16 +7367,14 @@ dependencies = [
|
|||
"hex",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"home",
|
||||
"itoa",
|
||||
"log",
|
||||
"md-5",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"rand 0.8.6",
|
||||
"rand 0.10.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sha2 0.11.0",
|
||||
"smallvec",
|
||||
"sqlx-core",
|
||||
"stringprep",
|
||||
|
|
@ -7467,12 +7385,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sqlx-sqlite"
|
||||
version = "0.8.6"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea"
|
||||
checksum = "488e99c397a62007e4229aec669a179816339afc6d2620ca6fa420dbee2e982c"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"chrono",
|
||||
"flume",
|
||||
"form_urlencoded",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
|
|
@ -7482,7 +7402,6 @@ dependencies = [
|
|||
"log",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"serde_urlencoded",
|
||||
"sqlx-core",
|
||||
"thiserror 2.0.18",
|
||||
"tracing",
|
||||
|
|
@ -8463,7 +8382,7 @@ dependencies = [
|
|||
"httparse",
|
||||
"log",
|
||||
"rand 0.9.4",
|
||||
"sha1",
|
||||
"sha1 0.10.6",
|
||||
"thiserror 2.0.18",
|
||||
"utf-8",
|
||||
]
|
||||
|
|
@ -8811,12 +8730,6 @@ dependencies = [
|
|||
"wit-bindgen 0.51.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasite"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.121"
|
||||
|
|
@ -9254,13 +9167,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "whoami"
|
||||
version = "1.6.1"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d"
|
||||
dependencies = [
|
||||
"libredox",
|
||||
"wasite",
|
||||
]
|
||||
checksum = "998767ef88740d1f5b0682a9c53c24431453923962269c2db68ee43788c5a40d"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ tracing-subscriber = { version = "0.3.18", features = ["fmt", "std", "chrono", "
|
|||
strum = "0.26.3"
|
||||
strum_macros = "0.26.4"
|
||||
ron = "0.8.1"
|
||||
sqlx = { version = "0.8.2", features = ["sqlite", "runtime-tokio"] }
|
||||
sqlx = { version = "0.9", features = ["sqlite", "sqlite-deserialize", "runtime-tokio", "chrono"] }
|
||||
dirs = "6.0.0"
|
||||
tokio = "1.41.1"
|
||||
crisp = { git = "https://git.tfcconnection.org/chris/crisp", version = "0.1.3" }
|
||||
|
|
|
|||
20
migrations/20260527180153_access_time.sql
Normal file
20
migrations/20260527180153_access_time.sql
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
-- Add migration script here
|
||||
ALTER TABLE songs
|
||||
ADD COLUMN created_at INTEGER;
|
||||
ALTER TABLE songs
|
||||
ADD COLUMN accessed_at INTEGER;
|
||||
|
||||
ALTER TABLE images
|
||||
ADD COLUMN created_at INTEGER;
|
||||
ALTER TABLE images
|
||||
ADD COLUMN accessed_at INTEGER;
|
||||
|
||||
ALTER TABLE videos
|
||||
ADD COLUMN created_at INTEGER;
|
||||
ALTER TABLE videos
|
||||
ADD COLUMN accessed_at INTEGER;
|
||||
|
||||
ALTER TABLE presentations
|
||||
ADD COLUMN created_at INTEGER;
|
||||
ALTER TABLE presentations
|
||||
ADD COLUMN accessed_at INTEGER;
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
[lru]
|
||||
capacity = 64
|
||||
# [lru]
|
||||
# capacity = 64
|
||||
|
||||
[semanticHighlighting]
|
||||
operator.enable = false
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use crate::core::model::Sort;
|
||||
use crate::{Background, Slide, SlideBuilder, TextAlignment};
|
||||
|
||||
use super::content::Content;
|
||||
|
|
@ -8,7 +9,8 @@ use crisp::types::{Keyword, Symbol, Value};
|
|||
use itertools::Itertools;
|
||||
use miette::{IntoDiagnostic, Result, miette};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{SqliteConnection, SqlitePool, query, query_as};
|
||||
use sqlx::types::chrono::{DateTime, Local};
|
||||
use sqlx::{AssertSqlSafe, SqliteConnection, SqlitePool, query, query_as};
|
||||
use std::mem::replace;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
|
@ -19,6 +21,10 @@ pub struct Image {
|
|||
pub id: i32,
|
||||
pub title: String,
|
||||
pub path: PathBuf,
|
||||
#[serde(skip)]
|
||||
pub created_at: DateTime<Local>,
|
||||
#[serde(skip)]
|
||||
pub accessed_at: DateTime<Local>,
|
||||
}
|
||||
|
||||
impl From<PathBuf> for Image {
|
||||
|
|
@ -33,6 +39,8 @@ impl From<PathBuf> for Image {
|
|||
id: 0,
|
||||
title,
|
||||
path: value.canonicalize().unwrap_or(value),
|
||||
created_at: Local::now(),
|
||||
accessed_at: Local::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -150,6 +158,7 @@ impl Model<Image> {
|
|||
let mut model = Self {
|
||||
items: vec![],
|
||||
kind: LibraryKind::Image,
|
||||
sorting_method: Sort::AccessTime,
|
||||
};
|
||||
|
||||
let mut db = db.acquire().await.expect("probs");
|
||||
|
|
@ -161,7 +170,7 @@ impl Model<Image> {
|
|||
pub async fn load_from_db(&mut self, db: &mut SqliteConnection) {
|
||||
let result = query_as!(
|
||||
Image,
|
||||
r#"SELECT title as "title!", file_path as "path!", id as "id: i32" from images"#
|
||||
r#"SELECT title as "title!", file_path as "path!", id as "id: i32", accessed_at as "accessed_at!: DateTime<Local>", created_at as "created_at!: DateTime<Local>" from images"#
|
||||
)
|
||||
.fetch_all(db)
|
||||
.await;
|
||||
|
|
@ -176,6 +185,23 @@ impl Model<Image> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sort(&mut self) {
|
||||
match self.sorting_method {
|
||||
Sort::AccessTime => {
|
||||
self.items.sort_by(|a, b| b.accessed_at.cmp(&a.accessed_at))
|
||||
}
|
||||
Sort::CreatedTime => todo!(),
|
||||
Sort::Title => todo!(),
|
||||
Sort::Secondary => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_sort(mut self, method: Sort) -> Self {
|
||||
self.sorting_method = method;
|
||||
self.sort();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn remove_images(
|
||||
|
|
@ -193,7 +219,7 @@ pub async fn remove_images(
|
|||
ids.iter().map(ToString::to_string).join(", ")
|
||||
);
|
||||
|
||||
query(&delete)
|
||||
query(AssertSqlSafe(delete))
|
||||
.execute(&*db)
|
||||
.await
|
||||
.into_diagnostic()
|
||||
|
|
@ -280,7 +306,7 @@ pub async fn update_image(
|
|||
}
|
||||
|
||||
pub async fn get_from_db(database_id: i32, db: &mut SqliteConnection) -> Result<Image> {
|
||||
query_as!(Image, r#"SELECT title as "title!", file_path as "path!", id as "id: i32" from images where id = ?"#, database_id).fetch_one(db).await.into_diagnostic()
|
||||
query_as!(Image, r#"SELECT title as "title!", file_path as "path!", id as "id: i32", accessed_at as "accessed_at!: DateTime<Local>", created_at as "created_at!: DateTime<Local>" from images where id = ?"#, database_id).fetch_one(db).await.into_diagnostic()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -301,6 +327,7 @@ mod test {
|
|||
let mut image_model: Model<Image> = Model {
|
||||
items: vec![],
|
||||
kind: LibraryKind::Image,
|
||||
sorting_method: Sort::AccessTime,
|
||||
};
|
||||
let mut db = add_db()
|
||||
.await
|
||||
|
|
@ -323,6 +350,7 @@ mod test {
|
|||
let mut image_model: Model<Image> = Model {
|
||||
items: vec![],
|
||||
kind: LibraryKind::Image,
|
||||
sorting_method: Sort::AccessTime,
|
||||
};
|
||||
let result = image_model.add_item(image.clone());
|
||||
let new_image = test_image("A newer image".into());
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use tracing::debug;
|
|||
pub struct Model<T> {
|
||||
pub items: Vec<T>,
|
||||
pub kind: LibraryKind,
|
||||
pub sorting_method: Sort,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash, Serialize, Deserialize)]
|
||||
|
|
@ -23,6 +24,14 @@ pub enum LibraryKind {
|
|||
Presentation,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Copy, Serialize, Deserialize)]
|
||||
pub enum Sort {
|
||||
AccessTime,
|
||||
CreatedTime,
|
||||
Title,
|
||||
Secondary, // This can be author or file name
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct KindWrapper(pub (LibraryKind, i32));
|
||||
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ use mupdf::{Colorspace, Document, Matrix};
|
|||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::prelude::FromRow;
|
||||
use sqlx::sqlite::SqliteRow;
|
||||
use sqlx::{Row, SqliteConnection, SqlitePool, query};
|
||||
use sqlx::types::chrono::{DateTime, Local};
|
||||
use sqlx::{AssertSqlSafe, Row, SqliteConnection, SqlitePool, query};
|
||||
use std::mem::replace;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use tracing::{debug, error};
|
||||
|
||||
use crate::core::model::Sort;
|
||||
use crate::{Background, Slide, SlideBuilder, TextAlignment};
|
||||
|
||||
use super::content::Content;
|
||||
|
|
@ -36,6 +38,10 @@ pub struct Presentation {
|
|||
pub title: String,
|
||||
pub path: PathBuf,
|
||||
pub kind: PresKind,
|
||||
#[serde(skip)]
|
||||
pub created_at: DateTime<Local>,
|
||||
#[serde(skip)]
|
||||
pub accessed_at: DateTime<Local>,
|
||||
}
|
||||
|
||||
impl Eq for Presentation {}
|
||||
|
|
@ -89,6 +95,8 @@ impl From<PathBuf> for Presentation {
|
|||
title,
|
||||
path: value.canonicalize().unwrap_or(value),
|
||||
kind,
|
||||
created_at: Local::now(),
|
||||
accessed_at: Local::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -280,6 +288,8 @@ impl FromRow<'_, SqliteRow> for Presentation {
|
|||
ending_index: row.try_get(5)?,
|
||||
}
|
||||
},
|
||||
created_at: Local::now(),
|
||||
accessed_at: Local::now(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -289,6 +299,7 @@ impl Model<Presentation> {
|
|||
let mut model = Self {
|
||||
items: vec![],
|
||||
kind: LibraryKind::Presentation,
|
||||
sorting_method: Sort::AccessTime,
|
||||
};
|
||||
model.load_from_db(db).await;
|
||||
model
|
||||
|
|
@ -296,7 +307,7 @@ impl Model<Presentation> {
|
|||
|
||||
pub async fn load_from_db(&mut self, db: Arc<SqlitePool>) {
|
||||
let result = query!(
|
||||
r#"SELECT id as "id: i32", title, file_path as "path", html, starting_index, ending_index from presentations"#
|
||||
r#"SELECT id as "id: i32", title, file_path as "path", html, starting_index, ending_index, accessed_at as "accessed_at!: DateTime<Local>", created_at as "created_at!: DateTime<Local>" from presentations"#
|
||||
)
|
||||
.fetch_all(&*db)
|
||||
.await;
|
||||
|
|
@ -341,12 +352,31 @@ impl Model<Presentation> {
|
|||
},
|
||||
)
|
||||
},
|
||||
created_at: presentation.created_at,
|
||||
accessed_at: presentation.accessed_at,
|
||||
});
|
||||
}
|
||||
}
|
||||
Err(e) => error!("There was an error in converting presentations: {e}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sort(&mut self) {
|
||||
match self.sorting_method {
|
||||
Sort::AccessTime => {
|
||||
self.items.sort_by(|a, b| b.accessed_at.cmp(&a.accessed_at))
|
||||
}
|
||||
Sort::CreatedTime => todo!(),
|
||||
Sort::Title => todo!(),
|
||||
Sort::Secondary => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_sort(mut self, method: Sort) -> Self {
|
||||
self.sorting_method = method;
|
||||
self.sort();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn remove_presentations(
|
||||
|
|
@ -364,7 +394,7 @@ pub async fn remove_presentations(
|
|||
ids.iter().map(ToString::to_string).join(", ")
|
||||
);
|
||||
|
||||
query(&delete)
|
||||
query(AssertSqlSafe(delete))
|
||||
.execute(&*db)
|
||||
.await
|
||||
.into_diagnostic()
|
||||
|
|
@ -480,7 +510,7 @@ pub async fn get_presentation_from_db(
|
|||
database_id: i32,
|
||||
db: &mut SqliteConnection,
|
||||
) -> Result<Presentation> {
|
||||
let row = query(r#"SELECT id as "id: i32", title, file_path as "path", html from presentations where id = $1"#).bind(database_id).fetch_one(db).await.into_diagnostic()?;
|
||||
let row = query(r#"SELECT id as "id: i32", title, file_path as "path", html, accessed_at as "accessed_at!: DateTime<Local>", created_at as "created_at!: DateTime<Local>" from presentations where id = $1"#).bind(database_id).fetch_one(db).await.into_diagnostic()?;
|
||||
Presentation::from_row(&row).into_diagnostic()
|
||||
}
|
||||
|
||||
|
|
@ -498,6 +528,8 @@ mod test {
|
|||
starting_index: 0,
|
||||
ending_index: 67,
|
||||
},
|
||||
created_at: Local::now(),
|
||||
accessed_at: Local::now(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -517,6 +549,7 @@ mod test {
|
|||
let mut presentation_model: Model<Presentation> = Model {
|
||||
items: vec![],
|
||||
kind: LibraryKind::Presentation,
|
||||
sorting_method: Sort::AccessTime,
|
||||
};
|
||||
let db = Arc::new(add_db().await.expect("Getting db error"));
|
||||
presentation_model.load_from_db(db).await;
|
||||
|
|
|
|||
|
|
@ -374,6 +374,7 @@ mod test {
|
|||
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
use sqlx::types::chrono::Local;
|
||||
|
||||
fn test_song() -> Song {
|
||||
Song {
|
||||
|
|
@ -390,6 +391,8 @@ mod test {
|
|||
"~/docs/notes/lessons/20240327T133649--12-isaiah-and-jesus__lesson_project_tfc.html",
|
||||
),
|
||||
kind: PresKind::Html,
|
||||
created_at: Local::now(),
|
||||
accessed_at: Local::now(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ use serde::{Deserialize, Serialize};
|
|||
use std::collections::VecDeque;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::core::model::Sort;
|
||||
|
||||
pub const SETTINGS_VERSION: u64 = 1;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
|
|
@ -32,6 +34,10 @@ pub struct Settings {
|
|||
pub app_theme: AppTheme,
|
||||
pub obs_url: Option<url::Url>,
|
||||
pub genius_token: Option<String>,
|
||||
pub song_sort: Option<Sort>,
|
||||
pub image_sort: Option<Sort>,
|
||||
pub video_sort: Option<Sort>,
|
||||
pub presentation_sort: Option<Sort>,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
|
|
@ -40,6 +46,10 @@ impl Default for Settings {
|
|||
app_theme: AppTheme::System,
|
||||
obs_url: None,
|
||||
genius_token: None,
|
||||
song_sort: None,
|
||||
image_sort: None,
|
||||
video_sort: None,
|
||||
presentation_sort: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,12 +13,13 @@ use itertools::Itertools;
|
|||
use miette::{IntoDiagnostic, Result, miette};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::sqlite::SqliteRow;
|
||||
use sqlx::{FromRow, Row, SqlitePool, query};
|
||||
use sqlx::types::chrono::{DateTime, Local};
|
||||
use sqlx::{AssertSqlSafe, FromRow, Row, SqlitePool, query};
|
||||
use tracing::{debug, error};
|
||||
|
||||
use crate::core::content::Content;
|
||||
use crate::core::kinds::ServiceItemKind;
|
||||
use crate::core::model::{LibraryKind, Model};
|
||||
use crate::core::model::{LibraryKind, Model, Sort};
|
||||
use crate::core::service_items::ServiceTrait;
|
||||
use crate::core::slide::{self, Background, TextAlignment};
|
||||
use crate::ui::text_svg::{Color, Font, Stroke, shadow, stroke};
|
||||
|
|
@ -49,6 +50,10 @@ pub struct Song {
|
|||
pub verse_map: Option<HashMap<VerseName, String>>,
|
||||
pub lyric_video: Option<PathBuf>,
|
||||
pub music_video: Option<PathBuf>,
|
||||
#[serde(skip)]
|
||||
pub created_at: DateTime<Local>,
|
||||
#[serde(skip)]
|
||||
pub accessed_at: DateTime<Local>,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
|
|
@ -379,6 +384,20 @@ impl FromRow<'_, SqliteRow> for Song {
|
|||
.try_get::<String, &str>("music_video")
|
||||
.map_or(None, |vid| Some(PathBuf::from(vid)));
|
||||
|
||||
let created_at = row
|
||||
.try_get::<DateTime<Local>, &str>("created_at")
|
||||
.unwrap_or_else(|e| {
|
||||
error!(?e);
|
||||
Local::now()
|
||||
});
|
||||
|
||||
let accessed_at = row
|
||||
.try_get::<DateTime<Local>, &str>("created_at")
|
||||
.unwrap_or_else(|e| {
|
||||
error!(?e);
|
||||
Local::now()
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
id: row.try_get("id")?,
|
||||
title: row.try_get("title")?,
|
||||
|
|
@ -428,6 +447,8 @@ impl FromRow<'_, SqliteRow> for Song {
|
|||
verse_map,
|
||||
lyric_video,
|
||||
music_video,
|
||||
created_at,
|
||||
accessed_at,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
|
@ -646,6 +667,7 @@ impl Model<Song> {
|
|||
let mut model = Self {
|
||||
items: vec![],
|
||||
kind: LibraryKind::Song,
|
||||
sorting_method: Sort::AccessTime,
|
||||
};
|
||||
|
||||
model.load_from_db(db).await;
|
||||
|
|
@ -677,6 +699,23 @@ impl Model<Song> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sort(&mut self) {
|
||||
match self.sorting_method {
|
||||
Sort::AccessTime => {
|
||||
self.items.sort_by(|a, b| b.accessed_at.cmp(&a.accessed_at))
|
||||
}
|
||||
Sort::CreatedTime => todo!(),
|
||||
Sort::Title => todo!(),
|
||||
Sort::Secondary => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_sort(mut self, method: Sort) -> Self {
|
||||
self.sorting_method = method;
|
||||
self.sort();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn remove_songs(
|
||||
|
|
@ -694,7 +733,7 @@ pub async fn remove_songs(
|
|||
ids.iter().map(ToString::to_string).join(", ")
|
||||
);
|
||||
|
||||
query(&delete)
|
||||
query(AssertSqlSafe(delete))
|
||||
.execute(&*db)
|
||||
.await
|
||||
.into_diagnostic()
|
||||
|
|
@ -847,6 +886,8 @@ pub async fn update_song(
|
|||
let style = ron::ser::to_string(&song.font_style).into_diagnostic()?;
|
||||
let weight = ron::ser::to_string(&song.font_weight).into_diagnostic()?;
|
||||
|
||||
let accessed_at = song.accessed_at;
|
||||
|
||||
// debug!(
|
||||
// ?stroke_size,
|
||||
// ?stroke_color,
|
||||
|
|
@ -857,7 +898,7 @@ pub async fn update_song(
|
|||
// );
|
||||
|
||||
let result = query!(
|
||||
r#"UPDATE songs SET title = $2, lyrics = $3, author = $4, ccli = $5, verse_order = $6, audio = $7, font = $8, font_size = $9, background = $10, horizontal_text_alignment = $11, vertical_text_alignment = $12, stroke_color = $13, shadow_color = $14, stroke_size = $15, shadow_size = $16, shadow_offset_x = $17, shadow_offset_y = $18, style = $19, weight = $20, lyric_video = $21, music_video = $22 WHERE id = $1"#,
|
||||
r#"UPDATE songs SET title = $2, lyrics = $3, author = $4, ccli = $5, verse_order = $6, audio = $7, font = $8, font_size = $9, background = $10, horizontal_text_alignment = $11, vertical_text_alignment = $12, stroke_color = $13, shadow_color = $14, stroke_size = $15, shadow_size = $16, shadow_offset_x = $17, shadow_offset_y = $18, style = $19, weight = $20, lyric_video = $21, music_video = $22, accessed_at = $23 WHERE id = $1"#,
|
||||
song.id,
|
||||
song.title,
|
||||
lyrics,
|
||||
|
|
@ -879,7 +920,8 @@ pub async fn update_song(
|
|||
style,
|
||||
weight,
|
||||
lyric_video,
|
||||
music_video
|
||||
music_video,
|
||||
accessed_at
|
||||
)
|
||||
.execute(&*db)
|
||||
.await
|
||||
|
|
@ -1273,6 +1315,7 @@ You saved my soul"
|
|||
let song_model: Model<Song> = Model {
|
||||
items: vec![],
|
||||
kind: LibraryKind::Song,
|
||||
sorting_method: Sort::AccessTime,
|
||||
// db: crate::core::model::get_db().await,
|
||||
};
|
||||
song_model
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use crate::core::model::Sort;
|
||||
use crate::{Background, SlideBuilder, TextAlignment};
|
||||
|
||||
use super::content::Content;
|
||||
|
|
@ -9,13 +10,14 @@ use crisp::types::{Keyword, Symbol, Value};
|
|||
use itertools::Itertools;
|
||||
use miette::{IntoDiagnostic, Result, miette};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{SqliteConnection, SqlitePool, query, query_as};
|
||||
use sqlx::types::chrono::{DateTime, Local};
|
||||
use sqlx::{AssertSqlSafe, Decode, SqliteConnection, SqlitePool, query, query_as};
|
||||
use std::mem::replace;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use tracing::error;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, Decode)]
|
||||
pub struct Video {
|
||||
pub id: i32,
|
||||
pub title: String,
|
||||
|
|
@ -23,6 +25,10 @@ pub struct Video {
|
|||
pub start_time: Option<f32>,
|
||||
pub end_time: Option<f32>,
|
||||
pub looping: bool,
|
||||
#[serde(skip)]
|
||||
pub accessed_at: DateTime<Local>,
|
||||
#[serde(skip)]
|
||||
pub created_at: DateTime<Local>,
|
||||
}
|
||||
|
||||
impl From<&Video> for Value {
|
||||
|
|
@ -178,13 +184,14 @@ impl Model<Video> {
|
|||
let mut model = Self {
|
||||
items: vec![],
|
||||
kind: LibraryKind::Video,
|
||||
sorting_method: Sort::AccessTime,
|
||||
};
|
||||
model.load_from_db(db).await;
|
||||
model
|
||||
}
|
||||
|
||||
pub async fn load_from_db(&mut self, db: Arc<SqlitePool>) {
|
||||
let result = query_as!(Video, r#"SELECT title as "title!", file_path as "path!", start_time as "start_time!: f32", end_time as "end_time!: f32", loop as "looping!", id as "id: i32" from videos"#).fetch_all(&*db).await;
|
||||
let result = query_as!(Video, r#"SELECT title as "title!", file_path as "path!", start_time as "start_time!: f32", end_time as "end_time!: f32", loop as "looping!", id as "id: i32", accessed_at as "accessed_at!: DateTime<Local>", created_at as "created_at!: DateTime<Local>" from videos"#).fetch_all(&*db).await;
|
||||
match result {
|
||||
Ok(v) => {
|
||||
for video in v {
|
||||
|
|
@ -196,6 +203,23 @@ impl Model<Video> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sort(&mut self) {
|
||||
match self.sorting_method {
|
||||
Sort::AccessTime => {
|
||||
self.items.sort_by(|a, b| b.accessed_at.cmp(&a.accessed_at))
|
||||
}
|
||||
Sort::CreatedTime => todo!(),
|
||||
Sort::Title => todo!(),
|
||||
Sort::Secondary => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_sort(mut self, method: Sort) -> Self {
|
||||
self.sorting_method = method;
|
||||
self.sort();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn remove_videos(
|
||||
|
|
@ -213,7 +237,7 @@ pub async fn remove_videos(
|
|||
ids.iter().map(ToString::to_string).join(", ")
|
||||
);
|
||||
|
||||
query(&delete)
|
||||
query(AssertSqlSafe(delete))
|
||||
.execute(&*db)
|
||||
.await
|
||||
.into_diagnostic()
|
||||
|
|
@ -306,7 +330,7 @@ pub async fn update_video(
|
|||
}
|
||||
|
||||
pub async fn get_from_db(database_id: i32, db: &mut SqliteConnection) -> Result<Video> {
|
||||
query_as!(Video, r#"SELECT title as "title!", file_path as "path!", start_time as "start_time!: f32", end_time as "end_time!: f32", loop as "looping!", id as "id: i32" from videos where id = ?"#, database_id).fetch_one(db).await.into_diagnostic()
|
||||
query_as!(Video, r#"SELECT title as "title!", file_path as "path!", start_time as "start_time!: f32", end_time as "end_time!: f32", loop as "looping!", id as "id: i32", accessed_at as "accessed_at!: DateTime<Local>", created_at as "created_at!: DateTime<Local>" from videos where id = ?"#, database_id).fetch_one(db).await.into_diagnostic()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -329,6 +353,7 @@ mod test {
|
|||
let mut video_model: Model<Video> = Model {
|
||||
items: vec![],
|
||||
kind: LibraryKind::Video,
|
||||
sorting_method: Sort::AccessTime,
|
||||
};
|
||||
let db = Arc::new(add_db().await.expect(""));
|
||||
video_model.load_from_db(db).await;
|
||||
|
|
@ -346,6 +371,7 @@ mod test {
|
|||
let mut video_model: Model<Video> = Model {
|
||||
items: vec![],
|
||||
kind: LibraryKind::Video,
|
||||
sorting_method: Sort::AccessTime,
|
||||
};
|
||||
let result = video_model.add_item(video.clone());
|
||||
let new_video = test_video("A newer video".into());
|
||||
|
|
|
|||
|
|
@ -23,13 +23,14 @@ use itertools::Itertools;
|
|||
use miette::{Context, IntoDiagnostic, Result};
|
||||
use rapidfuzz::distance::levenshtein;
|
||||
use sqlx::sqlite::SqliteConnectOptions;
|
||||
use sqlx::types::chrono::Local;
|
||||
use sqlx::{SqlitePool, migrate};
|
||||
use tracing::{debug, error, warn};
|
||||
|
||||
use crate::core::content::Content;
|
||||
use crate::core::images::{self, Image};
|
||||
use crate::core::kinds::ServiceItemKind;
|
||||
use crate::core::model::{KindWrapper, LibraryKind, Model};
|
||||
use crate::core::model::{KindWrapper, LibraryKind, Model, Sort};
|
||||
use crate::core::presentations::{self, Presentation};
|
||||
use crate::core::service_items::ServiceItem;
|
||||
use crate::core::songs::{self, Song, insert_song};
|
||||
|
|
@ -94,6 +95,7 @@ pub enum Action {
|
|||
pub enum Message {
|
||||
AddItem,
|
||||
DeleteItem,
|
||||
AccessItem(Option<(LibraryKind, i32)>),
|
||||
OpenItem(Option<(LibraryKind, i32)>),
|
||||
OpenContextItem,
|
||||
HoverLibrary(Option<LibraryKind>),
|
||||
|
|
@ -129,6 +131,18 @@ pub enum Message {
|
|||
HoverPoint(cosmic::iced::Point),
|
||||
}
|
||||
|
||||
impl Action {
|
||||
fn chain_task(self, task: Task<Message>) -> Self {
|
||||
match self {
|
||||
Action::Task(inner_task) => {
|
||||
let task = inner_task.chain(task);
|
||||
Self::Task(task)
|
||||
}
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Library {
|
||||
pub async fn new(db: Arc<SqlitePool>) -> Self {
|
||||
// let db = add_db().await.expect("probs");
|
||||
|
|
@ -136,10 +150,18 @@ impl<'a> Library {
|
|||
error!(?e);
|
||||
}
|
||||
Self {
|
||||
song_library: Model::new_song_model(Arc::clone(&db)).await,
|
||||
image_library: Model::new_image_model(Arc::clone(&db)).await,
|
||||
video_library: Model::new_video_model(Arc::clone(&db)).await,
|
||||
presentation_library: Model::new_presentation_model(Arc::clone(&db)).await,
|
||||
song_library: Model::new_song_model(Arc::clone(&db))
|
||||
.await
|
||||
.set_sort(Sort::AccessTime),
|
||||
image_library: Model::new_image_model(Arc::clone(&db))
|
||||
.await
|
||||
.set_sort(Sort::AccessTime),
|
||||
video_library: Model::new_video_model(Arc::clone(&db))
|
||||
.await
|
||||
.set_sort(Sort::AccessTime),
|
||||
presentation_library: Model::new_presentation_model(Arc::clone(&db))
|
||||
.await
|
||||
.set_sort(Sort::AccessTime),
|
||||
library_open: None,
|
||||
library_hovered: None,
|
||||
selected_items: None,
|
||||
|
|
@ -201,7 +223,7 @@ impl<'a> Library {
|
|||
return Action::CreateSong;
|
||||
}
|
||||
Message::AddSongFromEditor(song) => {
|
||||
let after_task = Task::done(Message::OpenItem(Some((
|
||||
let after_task = Task::done(Message::AccessItem(Some((
|
||||
LibraryKind::Song,
|
||||
self.song_library.items.len() as i32,
|
||||
))));
|
||||
|
|
@ -247,7 +269,7 @@ impl<'a> Library {
|
|||
let _index = self.video_library.items.len();
|
||||
// Check if empty
|
||||
let mut tasks = Vec::new();
|
||||
let after_task = Task::done(Message::OpenItem(Some((
|
||||
let after_task = Task::done(Message::AccessItem(Some((
|
||||
LibraryKind::Video,
|
||||
self.video_library.items.len() as i32,
|
||||
))));
|
||||
|
|
@ -295,7 +317,7 @@ impl<'a> Library {
|
|||
let _index = self.presentation_library.items.len();
|
||||
// Check if empty
|
||||
let mut tasks = Vec::new();
|
||||
let after_task = Task::done(Message::OpenItem(Some((
|
||||
let after_task = Task::done(Message::AccessItem(Some((
|
||||
LibraryKind::Presentation,
|
||||
self.presentation_library.items.len() as i32,
|
||||
))));
|
||||
|
|
@ -321,7 +343,7 @@ impl<'a> Library {
|
|||
let _index = self.image_library.items.len();
|
||||
// Check if empty
|
||||
let mut tasks = Vec::new();
|
||||
let after_task = Task::done(Message::OpenItem(Some((
|
||||
let after_task = Task::done(Message::AccessItem(Some((
|
||||
LibraryKind::Image,
|
||||
self.image_library.items.len() as i32,
|
||||
))));
|
||||
|
|
@ -343,10 +365,79 @@ impl<'a> Library {
|
|||
return Action::Task(Task::batch(tasks).chain(after_task));
|
||||
}
|
||||
Message::OpenItem(item) => {
|
||||
// if let Some((kind, _)) = item {
|
||||
// match kind {
|
||||
// LibraryKind::Song => {
|
||||
// self.song_library.sort();
|
||||
// debug!(?self.song_library);
|
||||
// }
|
||||
// LibraryKind::Video => self.video_library.sort(),
|
||||
// LibraryKind::Image => self.image_library.sort(),
|
||||
// LibraryKind::Presentation => self.presentation_library.sort(),
|
||||
// }
|
||||
// }
|
||||
return Action::OpenItem(item);
|
||||
}
|
||||
Message::AccessItem(item) => {
|
||||
debug!(?item);
|
||||
self.context_menu = None;
|
||||
self.editing_item = item;
|
||||
return Action::OpenItem(item);
|
||||
let task = Task::done(Message::OpenItem(item));
|
||||
if let Some((kind, item)) = item {
|
||||
match kind {
|
||||
LibraryKind::Song
|
||||
if let Some(song) = self
|
||||
.song_library
|
||||
.items
|
||||
.clone()
|
||||
.get_mut(item as usize) =>
|
||||
{
|
||||
song.accessed_at = Local::now();
|
||||
debug!(?song);
|
||||
return self
|
||||
.update(Message::UpdateSong(song.clone()))
|
||||
.chain_task(task);
|
||||
}
|
||||
LibraryKind::Video
|
||||
if let Some(video) = self
|
||||
.video_library
|
||||
.items
|
||||
.clone()
|
||||
.get_mut(item as usize) =>
|
||||
{
|
||||
video.accessed_at = Local::now();
|
||||
debug!(?video);
|
||||
return self
|
||||
.update(Message::UpdateVideo(video.clone()))
|
||||
.chain_task(task);
|
||||
}
|
||||
LibraryKind::Image
|
||||
if let Some(image) = self
|
||||
.image_library
|
||||
.items
|
||||
.clone()
|
||||
.get_mut(item as usize) =>
|
||||
{
|
||||
image.accessed_at = Local::now();
|
||||
return self
|
||||
.update(Message::UpdateImage(image.clone()))
|
||||
.chain_task(task);
|
||||
}
|
||||
LibraryKind::Presentation
|
||||
if let Some(presentation) = self
|
||||
.presentation_library
|
||||
.items
|
||||
.clone()
|
||||
.get_mut(item as usize) =>
|
||||
{
|
||||
presentation.accessed_at = Local::now();
|
||||
return self
|
||||
.update(Message::UpdatePresentation(presentation.clone()))
|
||||
.chain_task(task);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
Message::OpenContextItem => {
|
||||
let Some(kind) = self.library_open else {
|
||||
|
|
@ -355,7 +446,7 @@ impl<'a> Library {
|
|||
let Some(index) = self.context_menu else {
|
||||
return Action::None;
|
||||
};
|
||||
return self.update(Message::OpenItem(Some((kind, index))));
|
||||
return self.update(Message::AccessItem(Some((kind, index))));
|
||||
}
|
||||
Message::HoverLibrary(library_kind) => {
|
||||
self.library_hovered = library_kind;
|
||||
|
|
@ -726,7 +817,7 @@ impl<'a> Library {
|
|||
.on_enter(Message::HoverItem(Some((
|
||||
model.kind, i32_index,
|
||||
))))
|
||||
.on_double_click(Message::OpenItem(Some((
|
||||
.on_double_click(Message::AccessItem(Some((
|
||||
model.kind, i32_index,
|
||||
))))
|
||||
.on_right_press(Message::OpenContext(Some(i32_index)))
|
||||
|
|
@ -953,7 +1044,7 @@ impl<'a> Library {
|
|||
};
|
||||
|
||||
let menu_items = column![
|
||||
menu_item("Open", Message::OpenItem(Some((library, id)))),
|
||||
menu_item("Open", Message::AccessItem(Some((library, id)))),
|
||||
menu_item("Delete", Message::DeleteItem),
|
||||
]
|
||||
.spacing(theme::spacing().space_s)
|
||||
|
|
@ -1173,7 +1264,7 @@ impl<'a> Library {
|
|||
|
||||
let after_task = match last_item {
|
||||
Some(ServiceItemKind::Image(_image)) => {
|
||||
Task::done(Message::OpenItem(Some((
|
||||
Task::done(Message::AccessItem(Some((
|
||||
LibraryKind::Image,
|
||||
self.image_library
|
||||
.items
|
||||
|
|
@ -1184,7 +1275,7 @@ impl<'a> Library {
|
|||
}
|
||||
|
||||
Some(ServiceItemKind::Video(_image)) => {
|
||||
Task::done(Message::OpenItem(Some((
|
||||
Task::done(Message::AccessItem(Some((
|
||||
LibraryKind::Video,
|
||||
self.video_library
|
||||
.items
|
||||
|
|
@ -1194,7 +1285,7 @@ impl<'a> Library {
|
|||
))))
|
||||
}
|
||||
Some(ServiceItemKind::Presentation(_image)) => {
|
||||
Task::done(Message::OpenItem(Some((
|
||||
Task::done(Message::AccessItem(Some((
|
||||
LibraryKind::Presentation,
|
||||
self.presentation_library
|
||||
.items
|
||||
|
|
|
|||
|
|
@ -501,6 +501,8 @@ impl PresentationEditor {
|
|||
},
|
||||
_ => current_presentation.kind.clone(),
|
||||
},
|
||||
created_at: current_presentation.created_at,
|
||||
accessed_at: current_presentation.accessed_at,
|
||||
};
|
||||
let second_presentation = Presentation {
|
||||
id: 0,
|
||||
|
|
@ -513,6 +515,8 @@ impl PresentationEditor {
|
|||
},
|
||||
_ => current_presentation.kind.clone(),
|
||||
},
|
||||
created_at: current_presentation.created_at,
|
||||
accessed_at: current_presentation.accessed_at,
|
||||
};
|
||||
Ok((first_presentation, second_presentation))
|
||||
} else {
|
||||
|
|
@ -539,6 +543,8 @@ impl PresentationEditor {
|
|||
},
|
||||
_ => current_presentation.kind.clone(),
|
||||
},
|
||||
created_at: current_presentation.created_at,
|
||||
accessed_at: current_presentation.accessed_at,
|
||||
};
|
||||
let second_presentation = Presentation {
|
||||
id: 0,
|
||||
|
|
@ -551,6 +557,8 @@ impl PresentationEditor {
|
|||
},
|
||||
_ => current_presentation.kind.clone(),
|
||||
},
|
||||
created_at: current_presentation.created_at,
|
||||
accessed_at: current_presentation.accessed_at,
|
||||
};
|
||||
Ok((first_presentation, second_presentation))
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1166,6 +1166,7 @@ mod test {
|
|||
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
use sqlx::types::chrono::Local;
|
||||
|
||||
#[test]
|
||||
fn test_next_slide() {
|
||||
|
|
@ -1254,6 +1255,8 @@ mod test {
|
|||
starting_index: 0,
|
||||
ending_index: 67,
|
||||
},
|
||||
created_at: Local::now(),
|
||||
accessed_at: Local::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
BIN
test.db
BIN
test.db
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue