Commit Graph
73 Commits
Author SHA1 Message Date
Santo Shakil 638b6ac553 fix(mobile): don't stack an upload with itself (#31851) 2026-09-28 22:15:04 +06:00
Santo Shakil fbff5f41be fix(mobile): ask before deleting local only photos on android 10 and below (#31146)
* ask before deleting local only photos on android 10 and below

* move the delete consent prompt into _cleanupLocalAssets

* ask on android 11 as well before deleting local photos

* ask before deleting only where android shows no prompt

* split the local delete prompt into its own helper

* pick the local delete dialog in one flat branch and rename notBackedUp to isLocalOnly
2026-09-25 20:19:01 +06:00
Santo Shakil a4af86282d fix(mobile): resume after the app was launched in the background (#31557)
* run the first resume after a background launch with a full local sync

* keep the untouched lines of the swift file as they were

* check the background launch at the first resume even after a pause
2026-09-24 10:15:44 +06:00
Santo Shakil 2466eaeecd fix(mobile): play the server copy when the local video file cannot be read (#31606)
* play the server copy when the local video file cannot be read

* flatten the video source choice and add tests for it

* drop the platform override from the video viewer test
2026-09-24 10:15:33 +06:00
Santo Shakil 3e2e8e0fe8 fix(mobile): retry the background backup when connectivity is restored (#30909)
* fix(mobile): retry the background backup when connectivity is restored

* return the backup result from the zone and drop the unrelated upload test

* flip the background worker bool to needsRetry

* decide the background retry from the upload callbacks

* run the background worker offline again and retry on reconnect with a separate connected gated request

* retry the background backup when any upload fails and keep one worker at a time

* always hand the retry to the connected worker and release the worker lock if startWork throws

* respect the charging only setting on the connected retry worker
2026-09-23 17:27:45 +06:00
Santo Shakil 909b8aac93 fix(mobile): clamp out of range datetimes so timeline queries cannot crash (#30490)
* fix(mobile): clamp out of range datetimes so timeline queries cannot crash

* replace the per column converters with a clamping custom type

* heal each table in one statement, rename the clamp type, read dates as stored

* one medium test for the clamp, drop the dart side bounds, private clamp type
2026-09-18 17:41:15 +00:00
Santo Shakil 398a04e11d fix(mobile): sync status page goes blank when the counts query fails (#31642)
* sync status page stays usable when the counts query fails

* sync status error text through i18n
2026-09-17 18:14:11 +00:00
Santo Shakil e8c6218431 fix(mobile): show the real date for android local photos with no exif (#29193)
* fix(mobile): show the real date for android local photos with no exif

photos with no exif date showed their copy-to-phone date in the "on this device" albums instead of the real date. fall back to the earlier of date_modified/date_added (matches the server + ios), plus a migration to fix the rows already saved wrong.

* persist migration progress when the date backfill fails

* fix local date migration for trash and epoch rows

* cover epoch dates in both local asset tables

* simplify the created date pick and rework the migration test

* heal no-exif dates from mediastore in the v27 migration

* check gallery access from dart and skip dates that do not parse

* let the date heal run once and never block the version

* read the trashed assets through the same cast the sync uses
2026-09-17 19:05:07 +06:00
Santo Shakil c68ecb3892 feat(mobile): write the previous checksum when the upload finishes (#31577)
* write the previous checksum when the upload finishes

* ask the server for the checksum when the upload has none
2026-09-16 23:47:02 +06:00
Santo Shakil 962ffaacc1 feat(mobile): stack edited photos over the original on upload (#31082)
* feat(mobile): stack edited photos over the original on upload

* stack through the asset service and give the live photo still its task metadata

* add plain photo stacking tests and the 2x2 edit detection matrix
2026-09-15 11:22:14 -07:00
Santo Shakil aa8c5be42d chore(mobile): name pr builds after their pr number (#31541) 2026-09-14 19:34:14 +06:00
Santo Shakil b1a93688d3 fix(mobile): refresh the memory lane after resume (#31239) 2026-09-03 20:00:22 -05:00
Santo Shakil 7211efa6cb chore(mobile): log app resume and pause (#31207) 2026-09-02 04:28:06 -07:00
Santo Shakil c17a119735 fix(mobile): refresh server info when the websocket connects (#31144)
refresh the server features and config when the websocket connects
2026-08-31 19:09:35 +06:00
Santo Shakil 006e7fd0d5 fix(mobile): keep the original filename when sharing downloaded assets (#30267)
* fix(mobile): keep the original filename when sharing downloaded assets

* use ordinal suffixes for duplicate share filenames

* rename display name maps to totals and duplicates seen

* drop the display name totals map and keep the first copy name clean

* fold the ordinal suffix into the share display name derivation

* resolve share names after download and test through the public flow

* use the renamed store repository in the share tests

* add docstrings to the share file helpers

* simplify the share name resolver to a single set

* note the share_plus cache folder behavior on the resolver

* drop the version pin from the resolver comment

* pin the share_plus version in the resolver comment

* undo the accidental reformat, keep the version pin

* explain the test harness and cut the teardown and wait noise

* say why the storage is stubbed, drop the platform override and busy wait for the share cleanup

* spy on the cleanup in the share tests instead of waiting for it
2026-08-31 19:05:00 +06:00
Santo Shakil 2237b28813 fix(mobile): permanently delete local copies when moving to the locked folder (#29730)
* permanently delete local copies when moving to the locked folder

* simplify the delete path and move the stubs into the mock defaults

* drop the kept count from the partial message and log the mismatch instead
2026-08-22 07:42:11 +05:30
Santo Shakil a490cea1c4 fix(mobile): dedupe stale remote_asset rows on sync (#28445)
* fix(mobile): dedupe stale remote_asset rows on sync

queue a pre-delete on the matching partial-index tuple before each
upsert in updateAssetsV1/V2 so the second insert does not crash on
SqliteException(2067) when the server re-issues a new id for the
same (ownerId, checksum). closes #22522 #27186.

* run the dedupe deletes before the inserts and keep only the last duplicate per payload

* switch the asset upsert to insert or replace

* trim the dedupe tests to the essentials
2026-08-21 11:13:48 +00:00
Santo Shakil a066b5f301 fix(mobile): order album/place/person timelines by local date (#29338)
* fix(mobile): order album/place/person timelines by local date

* fold the timeline date ordering into one helper and tighten the tests

* drop the redundant none guard from the place asset query

* format
2026-08-20 13:52:33 +06:00
Santo Shakil 1539ae8b07 fix(mobile): resend an upload once when the connection dies before a response (#30843) 2026-08-18 22:38:52 +06:00
Santo Shakil 9ab12b0c4e fix(mobile): mark finished downloads complete instead of leaving them stuck (#29023)
* wire download status to the ui and dismiss finished entries

* mark finished downloads with a checkmark instead of dismissing

* use the theme color for the done icon and guard status updates on a known task
2026-08-17 16:10:26 +06:00
Santo Shakil 0ff47f4178 fix(mobile): don't let a frozen sync block syncing on resume (#29870)
* fix(mobile): don't let a frozen sync block syncing on resume

* share the task lists between the cancel paths

* fix analyzer issues in resume integration test

* isolate the resume e2e test and pin the worker pool setup
2026-08-10 21:39:38 +06:00
Santo Shakil 9862e50aab fix(mobile): decode remote thumbnails at displayed size (#29965)
* fix(mobile): decode remote thumbnails at displayed size

* use nullable decode size and reuse the tapped thumbnail in the viewer

* clean up decode size naming and guards
2026-08-10 21:14:23 +06:00
Santo Shakil 35c2a90fca fix(mobile): video playback controls dead for backed up videos opened from search (#30587)
* key video player state by asset id instead of hero tag

* expose id on BaseAsset instead of a separate playerKey
2026-08-09 23:54:55 +06:00
Santo Shakil f0386ce089 fix(mobile): handle asset websocket events (#30499) 2026-08-06 00:15:21 +06:00
Santo Shakil b2eb62dfa5 fix(mobile): keep backup remainder from going negative (#29011)
* recount the backup counters when a run starts

* clear the error before the recount

* create the cancel token before the recount
2026-08-05 20:17:05 +05:30
Santo Shakil 00e813ba84 fix(mobile): stop long images squishing on ios (#29367)
* fix(mobile): stop long images squishing on ios

* fix(mobile): bound the original to 16384 with fast resize

* refactor(mobile): reuse the shared request options for the original

* fix(mobile): fix squished long image previews on ios

* fix(mobile): retry with exact resize when fast resize overshoots the texture bound

* fix(mobile): clamp preview targets to a minimum of one pixel

* fix nullable cast flagged by the stricter lints
2026-08-04 19:08:27 +00:00
Santo Shakil 8d2fdd6b8f fix(mobile): stop websocket reconnect loop draining battery when server is unreachable (#29901)
* fix(mobile): stop websocket reconnect loop draining battery when server is unreachable

* fix(mobile): reconnect when the websocket is inactive and guard resume against pause

* move the test mocks to the service mocks file
2026-08-04 18:51:28 +00:00
Santo Shakil d8e1ead6f4 fix(mobile): stale local renders after editing a photo on device (#30415)
* fix(mobile): key local image caches by content so on-device edits re-render

* use Object.hash and the local asset factory
2026-08-05 00:37:49 +06:00
Santo Shakil 7121e0a75c fix(mobile): run one more sync round when a request arrives mid sync (#30478)
* fix(mobile): run one more sync round when a request arrives mid sync

* fix(mobile): only queue the extra sync round when the caller asks for it

* fix(mobile): skip the queued sync round when the sync fails

* clear the sync task in whenComplete and gate the queued round on the result

* simplify the queued flag gate
2026-08-04 14:59:19 +00:00
Santo Shakil e5c3bdad17 fix(mobile): sync stack changes from the websocket (#30479) 2026-08-03 19:53:29 +05:30
Santo Shakil cee08a2320 fix(server): remove the asset row when an upload fails after creating it (#30349) 2026-08-03 14:59:05 +02:00
Santo Shakil 534b8746e4 fix(mobile): refresh person thumbnail when the featured photo changes (#29350) 2026-07-29 22:20:26 +05:30
Santo Shakil 36dfc98527 fix(mobile): birthday picker date order follows locale (#29419)
* fix(mobile): birthday picker date order follows locale

* test(mobile): cover date order locales for the birthday picker
2026-07-23 02:32:17 +05:30
Santo Shakil d5adfb97dd fix(mobile): treat wired ethernet as unmetered on ios (#29351)
check the metered flags instead of the interface type, so ethernet counts like android already does.
2026-07-21 11:02:36 -05:00
Santo Shakil 943bfafb7a fix(mobile): show real error when an asset can't be added to an album (#29754)
* fix(mobile): show real error when an asset can't be added to an album

* fix(mobile): show added toast when an add-to-album partly succeeds

* docs(mobile): explain why a partial add-to-album shows added
2026-07-21 10:56:50 -05:00
Santo Shakil 14a2776453 fix(mobile): add album picker to the partner view bottom sheet (#30099) 2026-07-21 10:05:34 -05:00
Santo Shakil 4938fd4c89 fix(mobile): back up files moved into a watched folder on android (#29872) 2026-07-21 19:09:50 +05:30
Santo Shakil e81a79224c fix(mobile): stop double close animation when dismissing an asset (#29413) 2026-07-21 19:08:45 +05:30
Santo Shakil 2cdbd0d00f fix(mobile): apply exif orientation to remote raw photos on android (#29906) 2026-07-14 19:10:41 -04:00
Santo Shakil 9c8d718ddc fix(mobile): send a full datetime from the android memories widget (#29689) 2026-07-14 09:25:06 -05:00
Santo ShakilandAlex c4f084f4a5 fix(mobile): play videos from search results and folder view (#29693)
* fix(mobile): play videos from search results and folder view

* pr suggestion

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-07-08 20:22:35 +00:00
Santo ShakilandAlex 2ad554fc61 fix(mobile): update timeline args on layout constraint changes (#29653)
* fix(mobile): update timeline args on layout constraint changes

* chore(mobile): temporary blank timeline diagnostics

* chore(mobile): error logs when the timeline goes blank

* chore(mobile): remove blank timeline debug logging

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-07-08 14:13:32 -05:00
Santo Shakilandmertalev 58b1c5e8fb fix(mobile): fix 10-bit heic and avif colors on android (#29631)
* fix(mobile): fix 10-bit heic and avif colors on android

* fix(mobile): share the decode pool across flutter engines

* vectorize conversion

---------

Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com>
2026-07-06 21:16:09 +00:00
Santo Shakil af2efda310 fix(mobile): apply exif orientation to android raw photos (#29337) 2026-06-29 10:59:09 -04:00
Santo Shakil 6e1143e799 fix(mobile): hide video thumbnail when video is ready (#29012) 2026-06-26 22:20:40 -05:00
Santo Shakil 29949bebe4 fix(mobile): only toggle backup from the switch, not the whole row (#29236)
tapping anywhere on the enable backup row flipped backup on or off, so it was easy to toggle by accident. now only the switch does it.
2026-06-26 20:00:08 +05:30
Santo Shakil e5b50a55a4 fix(mobile): blank notifications page after enabling notifications (#29232)
the old notification toggles were removed in a cleanup, so once notifications were enabled the page had nothing left and went blank. show a "notifications enabled" status tile with a shortcut to the system notification settings instead.
2026-06-24 09:15:28 -05:00
Santo Shakil 9a5e7a8e47 fix(mobile): endless spinner on album selection when device has no albums (#28994)
* fix(mobile): endless spinner on album selection when device has no albums

* use a page scoped future provider for the loading state

* refactor(mobile): decide album selection empty state in the parent
2026-06-18 21:38:15 +05:30
Santo Shakil c35abb2f66 fix(mobile): re-lock locked folder when the app is backgrounded (#29089)
* fix(mobile): re-lock locked folder when the app is backgrounded

* fix(mobile): simplify locked folder overlay condition

* fix(mobile): lock locked folder on pause instead of resume
2026-06-18 21:10:18 +05:30
Santo Shakil 793487e52c fix(mobile): refresh memories on resume and day change (#28983)
* fix(mobile): refresh memories on resume and day change

* fix(mobile): invalidate memories once after the resume sync
2026-06-18 20:46:20 +05:30
Santo Shakil cda499f2b5 fix(mobile): keep toasts off the dynamic island when keyboard is open (#29159) 2026-06-16 18:51:29 +00:00
Santo Shakil 12b7cd066b fix(mobile): show memories with no showAt/hideAt in the timeline lane (#29158) 2026-06-16 18:46:52 +00:00
Santo Shakil 9ee412110f fix(mobile): stop sync albums crashing on the main isolate (#29133)
the album sync provider read cancellationProvider, which only exists in the background isolate and throws on the main one. moved the cancel signal onto the isolate call path.

fixes #29125
fixes #29119
2026-06-16 09:14:33 -05:00
Santo Shakil abd62d9295 fix(mobile): show like and comment options on album photo deep links (#29020) 2026-06-12 14:55:26 -05:00
Santo Shakil 59d036a2ed fix(mobile): give android notification channels proper names (#28986) 2026-06-11 15:07:37 +00:00
Santo Shakil e2954b6411 fix(mobile): show albums whose assets are all trashed (#28985) 2026-06-11 09:41:02 -05:00
Santo Shakil d227ba2d51 fix(mobile): stale details after editing asset date (#28977) 2026-06-10 21:32:02 -05:00
Santo Shakil 9cb94343d1 fix(mobile): keep timezone when editing asset date time (#28978)
* fix(mobile): keep timezone when editing asset date time

* fix(mobile): negative utc offsets with minutes off by an hour
2026-06-10 21:31:31 -05:00
Santo Shakil 07813135b5 fix(mobile): deduplicate people in asset details panel (#28972) 2026-06-10 14:37:45 +00:00
Santo Shakil 11c1025271 fix(mobile): add album picker to archive bottom sheet (#28953) 2026-06-09 14:45:32 -05:00
Santo Shakil c928787b3e fix(mobile): show error when creating an album fails (#28942)
it failed silently when the server was down. also disable create for blank titles.
2026-06-09 16:41:32 +00:00
Santo Shakil fe9ca4f40a fix(mobile): show memory and folder dates in local time (#28941) 2026-06-09 10:55:43 -05:00
Santo Shakil 8d553d6e9c chore: add santo to mobile codeowners (#28863) 2026-06-05 17:24:07 +00:00
Santo Shakil 429e181c8f fix(mobile): run iOS bg task phases in parallel (#28293)
onIosUpload runs sync local, sync remote, hash and handle backup
sequentially. on the bg refresh task path that's a 20s budget from
iOS, and sync + hash usually eat all of it before backup gets a turn
to enqueue any candidates.

these phases don't actually depend on each other. local + remote sync
touch different tables. hash works off whatever's already in drift.
handle backup reads candidates and just enqueues to URLSession bg.
anything one phase produces in this fire shows up to the others on
the next fire, and server-side dedup catches the rare race where
backup enqueues something sync remote was about to mark as already
uploaded.

so this runs all four concurrently via Future.wait, with hash getting
the full maxSeconds-1 budget instead of a fixed 5s. outer budget
timeout still caps everything before iOS expires.

second small change: getAssetsToHash orders by createdAt DESC instead
of id ASC to match getCandidates. when hash runs inside a refresh
fire it processes recent photos first.
2026-06-03 20:13:52 -05:00
Santo Shakil 3ab3d5cf43 fix(mobile): don't force-unwrap nil localizedTitle in ios getAlbums (#28452)
crashes on ios 26 when a PHAssetCollection returns nil for
localizedTitle. fall back to localIdentifier. ref #28428
2026-05-15 18:12:28 -05:00
Santo Shakil df016f9228 fix(mobile): mounted check in ThumbnailTile hero flight listener (#28451)
When the user pops back from the asset viewer mid-flight, the hero
animation can fire its status listener after _ThumbnailTileState has
been disposed. setState then throws a null check on State._element.

Guard the listener with `if (!mounted) return;` — same pattern as
#28300 in the album sync action.
2026-05-15 21:41:04 +00:00
Santo Shakil 17779c1e74 fix(mobile): cronet thumbnail buffer overflow regression from #28439 (#28450)
The hybrid added in onReadCompleted reuses Cronet's ByteBuffer between
reads to save a JNI wrap call when no grow is needed. That reuse breaks
advance() — Cronet's position() is cumulative across reads, so the same
K bytes get counted on every subsequent iteration. b.offset overshoots
b.capacity, the reuse branch keeps firing on a now-empty buffer, and
request.read() throws the original IllegalArgumentException again.

Always pass a fresh wrap from wrapRemaining() so byteBuffer.position()
reflects only this iteration's bytes. Same shape as the original PR
had before the broken optimization was layered on top.
2026-05-15 17:25:31 -04:00
Santo Shakil 01d6a244d8 fix(mobile): cronet buffer overflow on compressed thumbnails (#28439)
CronetImageFetcher sized the response buffer from Content-Length, which is
the compressed wire size. Cronet auto-decompresses gzip/br responses and
writes decompressed bytes into the buffer, exceeding it and throwing
IllegalArgumentException: ByteBuffer is already full on the next read. Use
the growable path; Content-Length becomes an initial alloc hint only,
capped at 128 MB so an untrusted server can't overflow Int.MAX_VALUE or
OOM us upfront. Reuse Cronet's ByteBuffer between reads when no grow is
needed.
2026-05-15 14:48:23 -04:00
Santo ShakilandAlex b031548791 fix(mobile): don't block app open on slow validateAccessToken (#28405)
* fix(mobile): don't block app open on slow validateAccessToken

AuthGuard.onNavigation was async so auto_route awaited the body through validateAccessToken's OS timeout. now it's sync and the validate runs in bg. kicks to login on 401.

* fix(mobile): handle re-login race in AuthGuard validate

if user logs out + logs back in during a slow validate, the old 401 was logging them out again. now we check the token hasn't changed before redirecting, and dedupe in-flight calls.

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-05-13 11:52:43 -05:00
Santo Shakil f632d320f5 fix(mobile): clear linkedRemoteAlbumId in reset() so FK refs dont dangle (#28382)
* fix(mobile): clear linkedRemoteAlbumId in reset() so FK refs dont dangle

reset() runs with foreign_keys off before wiping remote_* tables, so the ON DELETE SET NULL cascade on linkedRemoteAlbumId doesnt fire. local rows keep pointing at deleted remote ids.

affects logout (clearLocalData calls reset()) and the server SyncResetV1 path (30 day idle, etc). after re-login, syncLinkedAlbum either silently warns or fires 400s (those are covered by #28299).

null the column manually inside the same transaction. cascade still works for normal SyncAlbumDeleteV1.

verified on pixel 9a with this branch built locally: logged out, deleted album from web, logged back in. without fix linkedRemoteAlbumId stayed dangling. with fix all three local rows have linkedRemoteAlbumId = NULL after the logout reset, and recovery is clean once manageLinkedAlbums runs again.

* fix(mobile): always re-enable foreign_keys in reset() + simplify the update

re-enable foreign_keys inside a try/finally so it always runs even if the transaction throws. without this, a failed reset would leave the connection with foreign_keys = OFF and silently disable cascades for everything after (per copilot review).

also drop the where filter on the linkedRemoteAlbumId update, unconditional update-all is simpler and we wipe everything in reset anyway (per ganka review).
2026-05-12 13:43:15 -05:00
Santo Shakil 571e6a8560 chore(mobile): drop deprecated deviceAssetId / deviceId from upload fields (#28384)
server removed both fields from AssetMediaCreateDto in #27818. zod silently strips unknown fields so uploads still work, but we send dead weight on every request.

drop from foreground + background upload paths + share intent path. deviceAssetId stays as the internal background_downloader taskId, just not in the multipart form fields anymore.
2026-05-12 09:12:26 -04:00
Santo Shakil 7c6750941e fix(mobile): mounted check before setState in album sync action (#28300)
_manualSyncAlbums fires a setState 1s after sync via Future.delayed
with no mounted check. if the widget is gone by then, setState throws
null check and the global error logger logs it severe.
2026-05-08 18:55:03 +00:00
Santo Shakil 238895cad9 fix(mobile): restore notification plugin init (#28284)
#27666 removed LocalNotificationService with the legacy stack, which
was the only place calling FlutterLocalNotificationsPlugin().initialize().
without it, ios never prompts for the notification perm on fresh
installs so background_downloader notifications get dropped silently.

restores the init in the same spot the deleted call used to live.
2026-05-08 10:45:52 +07:00