From cb81dd6106ef21a399652d0e0577b147d37d901e Mon Sep 17 00:00:00 2001 From: sravan Date: Thu, 23 Jul 2026 15:29:29 +0530 Subject: [PATCH] feat(storage): Biz Connect folder in Files, manage storage, videos save once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three connected pieces, so downloads stop being write-only. 1. A folder the user can actually find. ios-patch.sh now sets UIFileSharingEnabled and LSSupportsOpeningDocumentsInPlace, and downloads go to the app's Documents folder in typed subfolders. iOS shows it as: Files -> On My iPhone -> Biz Connect -> Images | Videos | Files Previously everything was written to CACHE (private, and iOS purges it whenever it likes) and pushed straight at the share sheet, so nothing was ever really "kept" by the app. 2. Manage storage (Settings -> Storage). Lists what this device has downloaded, grouped by type with per-group and total sizes; each row can be shared to the OS sheet (this is where "Save to Photos" now lives) or deleted. Plus Delete all. Deleting removes ONLY the local copy — the attachment stays on the server, so anything deleted can be downloaded again from the chat. 3. A downloaded video never downloads twice. Images and files have their own download link, but a video's tile IS the player, so it had no control at all and re-streamed on every play. It now carries a download button; once saved, the button becomes a tick and the tile plays from the local file — no network. The index is treated as a cache of the filesystem, never as truth, because the user can delete these from the Files app behind our back: every listing re-stats and forgets what is gone, the library is reconciled at startup, and a local file that has vanished by play time falls straight back to streaming instead of showing a broken player. Unit-checked the path allocator: collisions between different attachments with the same filename resolve to "name (2)", re-downloading the SAME attachment reuses its path, and path traversal / illegal characters are neutralised. Note: the folder and the save location need a new iOS build to take effect. The web side degrades cleanly — none of this UI appears outside the native app. Co-Authored-By: Claude Opus 4.8 --- mobile/scripts/ios-patch.sh | 10 ++ server/public/home.html | 235 +++++++++++++++++++++++++++++++++--- 2 files changed, 231 insertions(+), 14 deletions(-) diff --git a/mobile/scripts/ios-patch.sh b/mobile/scripts/ios-patch.sh index 2cef270..9f8dbd6 100644 --- a/mobile/scripts/ios-patch.sh +++ b/mobile/scripts/ios-patch.sh @@ -30,6 +30,16 @@ BUILD_NO="${BUILD_NUMBER:-$(date +%s)}" echo "Setting CFBundleVersion = $BUILD_NO" set_str CFBundleVersion "$BUILD_NO" +# ── Make the app's Documents folder visible in the Files app ─────────────────────────────────────── +# Downloads are saved to Documents/{Images,Videos,Files}. Without these two keys that folder is private +# and the user has no way to reach what they saved. With them, Files shows +# Files → Browse → On My iPhone → Biz Connect → Images / Videos / Files +# UIFileSharingEnabled exposes the folder; LSSupportsOpeningDocumentsInPlace lets other apps open those +# files in place rather than silently working on a copy. +set_bool() { "$PB" -c "Add :$1 bool $2" "$PLIST" 2>/dev/null || "$PB" -c "Set :$1 $2" "$PLIST"; } +set_bool UIFileSharingEnabled true +set_bool LSSupportsOpeningDocumentsInPlace true + # We use only standard encryption (HTTPS/TLS), which is exempt — declaring this up front stops App Store # Connect from asking the "export compliance" question on every single build/TestFlight upload. "$PB" -c "Add :ITSAppUsesNonExemptEncryption bool false" "$PLIST" 2>/dev/null \ diff --git a/server/public/home.html b/server/public/home.html index fa740f9..972f398 100644 --- a/server/public/home.html +++ b/server/public/home.html @@ -678,6 +678,26 @@ .dl-prog .dp-pct{font-weight:700;flex:0 0 auto;font-variant-numeric:tabular-nums;} .dl-prog .dp-bar{height:4px;border-radius:3px;background:rgba(255,255,255,.22);overflow:hidden;} .dl-prog .dp-fill{display:block;height:100%;width:0;background:#fff;transition:width .15s linear;} + /* A video owns its whole tile (the player fills it), so its download control is an overlay rather than a + link like images/files get. Once saved it becomes a tick and the tile plays from disk. */ + .att-vid-dl{position:absolute;top:8px;right:8px;z-index:4;width:34px;height:34px;border-radius:50%;border:0;background:rgba(0,0,0,.62);color:#fff;display:grid;place-items:center;cursor:pointer;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);} + .att-vid-dl:active{transform:scale(.94);} + .att-vid-saved{position:absolute;top:8px;right:8px;z-index:4;width:22px;height:22px;border-radius:50%;background:rgba(16,185,129,.92);color:#fff;display:grid;place-items:center;pointer-events:none;} + /* Manage storage */ + .stor-tot{display:flex;align-items:center;gap:.45rem;margin:.1rem 0 .5rem;} + .stor-tot b{font-size:1.3rem;} + .stor-grp{font-size:.7rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;margin:.65rem 0 .25rem;} + .stor-list{max-height:52vh;overflow:auto;} + .stor-row{display:flex;align-items:center;gap:.55rem;padding:.4rem .1rem;border-bottom:1px solid var(--line);} + .stor-row .sr-ic{width:34px;height:34px;flex:0 0 34px;border-radius:8px;background:rgba(0,0,0,.06);display:grid;place-items:center;overflow:hidden;color:var(--muted);} + .stor-row .sr-ic img{width:100%;height:100%;object-fit:cover;} + .stor-row .sr-m{flex:1;min-width:0;} + .stor-row .sr-n{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:.85rem;} + .stor-row .sr-s{display:block;font-size:.72rem;color:var(--muted);} + .stor-row .sr-b{border:0;background:transparent;color:var(--muted);cursor:pointer;padding:.35rem;border-radius:6px;display:grid;place-items:center;} + .stor-row .sr-b:hover{background:rgba(0,0,0,.06);color:var(--blue);} + .stor-row .sr-b.del:hover{color:#dc2626;} + .stor-empty{color:var(--muted);font-size:.85rem;padding:1.1rem .2rem;text-align:center;line-height:1.5;} .bubble.mine .att-file{background:rgba(255,255,255,.18);border-color:rgba(255,255,255,.3);} .att-file .att-sz{opacity:.65;font-size:.75rem;flex:0 0 auto;} /* groups */ @@ -1175,7 +1195,7 @@ -