* feat(shared-link): enhance shared link UI and functionality with new expiry options and improved layout
* rebase & cleanup
---------
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
* add bulk_tag_assets_action_button to general_bottom_sheet.widget
include create tag tile in 'Add Tags' action modal
* follow provider -> svc -> repo pattern for tags
* rebase and cleanup
---------
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
* feat(mobile): slideshow view
* move slideshow settings to metadata store
* remove watch in initState
* wrap progress bar in safearea
* show slideshow button on remote albums
* fix crash on unknown assets
* always show slideshow option
* add zoom effect
* add padding to slideshow settings
* chore: styling tweak
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
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.
fix(mobile): use correct delete for trashed assets
When viewing a trashed asset, the viewer bottom bar now shows the permanent delete button instead of the trash button, which had no effect on already-trashed assets.
* 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>
* 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).
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.
fix(mobile): deduplicate assets in person view timeline
Previously, assets with multiple face records for the same person (e.g.,
manual Digikam imports and Immich ML detections) appeared multiple times
in the person timeline. This was caused by an inner join on the
assetFaceEntity without proper deduplication.
This commit refactors the timeline queries to use a subquery approach
instead of joins and grouping. This ensures:
- _getPersonBucketAssets: Only unique assets are fetched, even if
multiple face records exist for a single asset.
- _watchPersonBucket: Asset counts in timeline headers are accurate
and represent unique assets.
- Performance: Database overhead is reduced by avoiding complex joins
and explicit groupBy operations on large result sets.
Signed-off-by: thowdev <12428285+thowdev@users.noreply.github.com>
_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.
#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.
* fix(mobile): view similar defaults to images only
* fix(mobile): reset filter chips when pre-filter is applied
---------
Co-authored-by: shenlong <139912620+shenlong-tanwen@users.noreply.github.com>
* refactor: app metadata
* refactor to per row store
* cleanup
* more test
* review changes
* more refactor
* refactor
---------
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>