cheogram-www

created pr with cheogram-www/tauri-media-handling.1 on 2026-10-28T15:15:38Z · by 3c31bc8c
commented on 2026-10-28T15:19:01Z · by 3c31bc8c
The following changes since commit 62ea2af732f009407e7d2de6588cd10657f4e89b: npm run tauri init (2026-10-28 10:28:04 -0400) are available in the Git repository at: https://tangled.org/mrry.band/cheogram-www tauri-media-handling for you to fetch changes up to d1d3a238ce8271bab76def99868cfaf5402e1a54: Tauri file system media store (2026-10-28 11:11:55 -0400) ---------------------------------------------------------------- Eric Roberts (1): Tauri file system media store package.json | 1 + src-tauri/Cargo.lock | 231 +++++++----- src-tauri/Cargo.toml | 9 + src-tauri/src/lib.rs | 50 ++- src-tauri/src/media.rs | 460 ++++++++++++++++++++++ src-tauri/src/media/store.rs | 588 +++++++++++++++++++++++++++++ src-tauri/tauri.conf.json | 1 + src/App.tsx | 2 +- src/components/MessageAttachments.test.tsx | 25 ++ src/components/MessageAttachments.tsx | 10 +- src/media-store-tauri.test.ts | 89 +++++ src/media-store-tauri.ts | 49 +++ src/media-store.ts | 7 + 13 files changed, 1421 insertions(+), 101 deletions(-) create mode 100644 src-tauri/src/media.rs create mode 100644 src-tauri/src/media/store.rs create mode 100644 src/media-store-tauri.test.ts create mode 100644 src/media-store-tauri.ts create mode 100644 src/media-store.ts
added cheogram-www/tauri-media-handling.2 on 2026-10-28T15:44:56Z · by 3c31bc8c
1: d1d3a23 ! 1: 5c9c8ae Tauri file system media store
added cheogram-www/tauri-media-handling.3 on 2026-10-28T16:08:30Z · by 9e834e0d
1: 5c9c8ae < -: ------- Tauri file system media store
-: ------- > 1: 615caaf Review comments
added cheogram-www/tauri-media-handling.4 on 2026-10-28T21:31:45Z · by 3c31bc8c
1: 615caaf < -: ------- Review comments
-: ------- > 1: 58e4016 Tauri file system media store
added cheogram-www/tauri-media-handling.5 on 2026-10-28T21:33:19Z · by 3c31bc8c
1: 58e4016 = 1: b0c5136 Tauri file system media store
added cheogram-www/tauri-media-handling.6 on 2026-10-28T21:33:59Z · by 3c31bc8c
1: b0c5136 ! 1: f6bda1a Tauri file system media store
added cheogram-www/tauri-media-handling.7 on 2026-10-29T14:13:32Z · by 3c31bc8c
1: f6bda1a ! 1: efe5296 Tauri file system media store
cmds
checkout latest patchset:
ssh pr.pico.sh pull cheogram-www:tauri-media-handling | git am -3
checkout specific patchset revision:
ssh pr.pico.sh pull cheogram-www:tauri-media-handling [rev] | git am -3
add changes to patch request:
git format-patch main --stdout | ssh pr.pico.sh cheogram-www:tauri-media-handling
add comment to patch request:
ssh pr.pico.sh comment cheogram-www:tauri-media-handling "lgtm!"
+5 -0 src-tauri/src/lib.rs #
......@@ -12,12 +12,17 @@ pub fn run() {
1212 media::has_media,
1313 media::remove_media
1414 ])
15+ // Is this not the protocol that tauri uses by default? Do we need to register it manually?
1516 .register_asynchronous_uri_scheme_protocol("tauri", media::protocol)
1617 .setup(|app| {
1718 app.manage(std::sync::Arc::new(media::MediaCache::new(
19+ // This is fine for now, but maybe we'll want some to be in cache dir and some in
20+ // more "permanent" dir eventually? We do than on Android anyway
1821 app.path().app_cache_dir()?.join("media"),
1922 )?));
2023 if cfg!(debug_assertions) {
24+ // Should we have this kind of debug log controlled by an env var so it can be
25+ // enabled at runtime instead of compile time?
2126 app.handle().plugin(
2227 tauri_plugin_log::Builder::default()
2328 .level(log::LevelFilter::Info)
+16 -0 src-tauri/src/media.rs #
......@@ -22,9 +22,14 @@ struct MediaUpload {
2222
2323 impl tauri::Resource for MediaUpload {}
2424
25+// I'm not sure this whole wrapper is needed. It seems basically equivalent to
26+// Mutex<Option<Upload>> and I'n not even sure we need the option, the Upload could know if it is
27+// closed or whatever, it's already a mutable thing. If we even need to care about it being closed
28+// anywhere.
2529 impl MediaUpload {
2630 fn new(cache: Arc<MediaCache>, mime: HeaderValue) -> io::Result<Self> {
2731 let upload = Upload::new(&cache, mime)?;
32+ // currently the Upload knows abut the cache but we store it alongside as well?
2833 Ok(Self {
2934 cache,
3035 pending: Mutex::new(Some(upload)),
......@@ -62,6 +67,9 @@ pub async fn begin_media<R: tauri::Runtime>(
6267 ) -> Result<tauri::ResourceId, String> {
6368 let mime = HeaderValue::from_str(&mime).map_err(|error| error.to_string())?;
6469 let cache = cache.inner().clone();
70+ // This spawn_blocking moves the MediaUpload::new call to a thread pool so it won't block
71+ // the async runtime, then waits on it. Fine. Except that doesn't seem to do any blocking I/O
72+ // work? And if it did maybe we could write it to be async native?
6573 let upload = tauri::async_runtime::spawn_blocking(move || MediaUpload::new(cache, mime))
6674 .await
6775 .map_err(|error| error.to_string())?
......@@ -83,6 +91,7 @@ pub async fn write_media<R: tauri::Runtime>(
8391 let data: Vec<u8> = match request.body() {
8492 tauri::ipc::InvokeBody::Raw(data) => data.clone(),
8593 tauri::ipc::InvokeBody::Json(value) => {
94+ // We allow uploading a json array of numbers?
8695 serde_json::from_value(value.clone()).map_err(|error| error.to_string())?
8796 }
8897 };
......@@ -90,6 +99,8 @@ pub async fn write_media<R: tauri::Runtime>(
9099 .resources_table()
91100 .get::<MediaUpload>(rid)
92101 .map_err(|error| error.to_string())?;
102+ // So this blocking is legit, but I do wonder if we couldn't make the write call async native
103+ // instead of wrapping it in a spawn_blocking from outside.
93104 tauri::async_runtime::spawn_blocking(move || upload.write(&data))
94105 .await
95106 .map_err(|error| error.to_string())?
......@@ -105,6 +116,7 @@ pub async fn finish_media<R: tauri::Runtime>(
105116 .resources_table()
106117 .get::<MediaUpload>(rid)
107118 .map_err(|error| error.to_string())?;
119+ // Same here: can finish be async native?
108120 tauri::async_runtime::spawn_blocking(move || upload.finish())
109121 .await
110122 .map_err(|error| error.to_string())?
......@@ -122,6 +134,8 @@ pub async fn has_media(
122134 }
123135 let hash = MediaHash::from_hex(&algorithm, &hex).map_err(|error| error.to_string())?;
124136 let cache = cache.inner().clone();
137+ // I'm not going to comment in every place. But in general it seems we built a sync/blocking
138+ // intrface which we never use and then we wrap it in spawn_blocking at every call site.
125139 tauri::async_runtime::spawn_blocking(move || {
126140 cache.has(&hash).map(|found| found.then(|| hash.url()))
127141 })
......@@ -198,6 +212,7 @@ fn parse_path(path: &str) -> Option<MediaHash> {
198212 }
199213
200214 fn error_response(status: StatusCode) -> Response<Vec<u8>> {
215+ // Is a body required?
201216 Response::builder().status(status).body(vec![]).unwrap()
202217 }
203218
......@@ -209,6 +224,7 @@ pub fn protocol<R: tauri::Runtime>(
209224 let app = context.app_handle().clone();
210225 let path = request.uri().path().to_owned();
211226 let head = request.method() == tauri::http::Method::HEAD;
227+ // Does path ever legitimately == /.well-known/ni ? I don't think so
212228 if path.starts_with("/.well-known/ni/") || path == "/.well-known/ni" {
213229 let Some(hash) = parse_path(&path) else {
214230 responder.respond(error_response(StatusCode::BAD_REQUEST));
+5 -1 src-tauri/src/media/store.rs #
......@@ -14,6 +14,9 @@ pub(super) enum MediaHash {
1414
1515 impl MediaHash {
1616 pub(super) fn from_bytes(algorithm: &str, bytes: &[u8]) -> Option<Self> {
17+ // I thilnk the native format should be bytes, so Sha1(Vec<u8>) or whatever above,
18+ // rather than hex strings we never actually use and just decode to bytes again to make the
19+ // url...
1720 match (algorithm, bytes.len()) {
1821 ("sha-1", 20) => Some(Self::Sha1(hex::encode(bytes))),
1922 ("sha-256", 32) => Some(Self::Sha256(hex::encode(bytes))),
......@@ -105,6 +108,7 @@ impl ResolvedMedia {
105108 }
106109 }
107110
111+// So this is more like a place to write an upload yeah? Maybe call it UploadSlot or something?
108112 pub(super) struct Upload {
109113 file: NamedTempFile,
110114 mime: HeaderValue,
......@@ -115,7 +119,7 @@ pub(super) struct Upload {
115119 impl Upload {
116120 pub(super) fn new(cache: &MediaCache, mime: HeaderValue) -> io::Result<Self> {
117121 Ok(Self {
118- file: NamedTempFile::new_in(cache.root.join("tmp"))?,
122+ file: NamedTempFile::new_in(cache.root.join("tmp"))?, // this should probably go in /tmp or another actual temp store?
119123 mime,
120124 sha256: Sha256::new(),
121125 sha1: Sha1::new(),
+1 -1 src-tauri/tauri.conf.json #
......@@ -12,7 +12,7 @@
1212 "app": {
1313 "windows": [
1414 {
15- "create": false,
15+ "create": false, // why?
1616 "title": "Cheogram",
1717 "titleBarStyle": "Transparent",
1818 "backgroundColor": "#7401cf",
+3 -0 src/components/MessageAttachments.tsx #
......@@ -121,6 +121,9 @@ const AttachmentContent = ({
121121 }) => {
122122 const { uri, embeddable } = fetchedAttachment;
123123 const href =
124+ // Hmm, I guess this is fine. I wonder if we shouldn't just have
125+ // Borogove always append this to the .well-known version
126+ // Along with maybe ct (which is actually standard, though not needed for our case right now)
124127 attachment.name && uri.startsWith("/.well-known/ni/")
125128 ? `${uri}?name=${encodeURIComponent(attachment.name)}`
126129 : uri;
+2 -0 src/media-store-tauri.ts #
......@@ -1,6 +1,8 @@
11 import { Hash, type persistence } from "borogove";
22 import { invoke, Resource } from "@tauri-apps/api/core";
33
4+// Any reason we're a function that returns an object we declare as
5+// conforming to an interface rather than a class implementing the interface?
46 export async function MediaStoreTauri() {
57 return {
68 // Instead of using KV we store data on the filesystem because the rust
Back to top