The timer hook preserved the values of the original local variables,
which caused issues when hiding controls for videos. The callback can be
changed so that it always sees the latest value with useRef, and it can
be simplified significantly using a function rather than state class.
* feat(mobile): open in browser
* chore: open in browser instead of webview
* chore: allow archived asset
* fix: moved openinbrowser above unstack
* feat: deeplink into favorites, trash & archived
* fix: use remoteId (for tests to succeed)
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
* use cookiejar
* cookie duping hook
* remove old pref
* handle network switching on logout
* remove bootstrapCookies
* dead code
* fix cast
* use constants
* use new event name
* update api
* fix(mobile): correct local asset dimensions
We are constraining the size of videos so that they play nicely with
hero animations, and don't stretch in weird ways. This however caused a
regression as we are not account for local assets on Android which have
un-oriented dimensions.
* post-orientation width and height in local sync
* migration
* no need to handle it in asset viewer
---------
Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>
Consolidate video state into a single asset-scoped provider, and reduce
dependency on global state generally. Overall this should fix a few
timing issues and race conditions with videos specifically, and make
future changes in this area easier.
* feat: add playbackStyle to local asset entity and related database schema
* implement conversion function for playbackStyle in local sync service
* implement conversion function for playbackStyle in local sync service
* refactor: remove deducedPlaybackStyle from TrashedLocalAssetEntityData
* add playbackStyle column to trashed local asset entity
* make playbackStyle non-nullable across the mobile codebase
* Streamline playbackStyle backfill:
- only backfill local assets playbackStyle in flutter/dart code
- only update trashed local assets in db migration
* bump target database version to 23 and update migration logic for playbackStyle
* set playback_style to 0 in merged_asset.drift as its a getter in base asset
* run make pigeon
* Populate playbackStyle for trashed assets during native migration
* init
* fix
* styling
* temporary workaround for 500 error
**Root cause:**
The autogenerated Dart OpenAPI client (`UsersApi.createProfileImage()`) had two issues:
1. It set `Content-Type: multipart/form-data` without a boundary, which overrode the correct header that Dart's `MultipartRequest` would set (`multipart/form-data; boundary=...`).
2. It added the file to both `mp.fields` and `mp.files`, creating a duplicate text field.
**Result:**
Multer on the server failed to parse the multipart body, so `@UploadedFile()` was `undefined` → accessing `file.path` in `UserService.createProfileImage()` threw → **500 Internal Server Error**.
**Workaround:**
Bypass the autogenerated method in `UserApiRepository.createProfileImage()` and send the multipart request directly using the same `ApiClient` (basePath + auth), ensuring:
- No manual `Content-Type` header (let `MultipartRequest` set it with boundary)
- File only in `mp.files`, not `mp.fields`
- Proper filename fallback
* Revert "temporary workaround for 500 error"
This reverts commit 8436cd402632ca7be9272a1c72fdaf0763dcefb6.
* generate route for ProfilePictureCropPage
* add route import
* simplify
* try this
* Revert "try this"
This reverts commit fcf37d2801055c49010ddb4fd271feb900ee645a.
* try patching
* Reapply "temporary workaround for 500 error"
This reverts commit faeed810c21e4c9f0839dfff1f34aa6183469e56.
* Revert "Reapply "temporary workaround for 500 error""
This reverts commit a14a0b76d14975af98ef91748576a79cef959635.
* fix upload
* Refactor image conversion logic by introducing a new utility function. Replace inline image-to-Uint8List conversion with the new utility in EditImagePage, DriftEditImagePage, and ProfilePictureCropPage.
* use toast over snack
* format
* Revert "try patching"
This reverts commit 68a616522a1eee88c4a9755a314c0017e6450c0f.
* Enhance toast notification in ProfilePictureCropPage to include success type for better user feedback.
* Revert "simplify"
This reverts commit 8e85057a40.
* format
* add tests
* refactor to use statefulwidget
* format
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
* set album cover from asset
* add to correct kebab group
* add to album selection
* add to legacy control bottom bar
* add tests
* format
* analyze
* Revert "add to legacy control bottom bar"
This reverts commit 9d68e12a08.
* remove unnecessary event emission
* lint
* fix tests
* fix: button order and remove unncessary check
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
The existing implementation for showing asset details uses a bottom
sheet, and is not in sync with the preview or scroll intent. Other apps
use inline details, which is much cleaner and feels better to use.
* chore(mobile): i18n: "open_asset_info" in viewer kebab menu
* feat(mobile): move some top buttons into kebabu menu
* refactor(mobile): viewer kebab menu to use context-based button generation
* feat(mobile): refactor action button and kebab menu to use ConsumerWidget for improved state management
* feat(mobile): pass original theme to ViewerKebabMenu for consistent styling
* chore: styling
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
* fixed the timezone issue in the Immich mobile app's metadata sheet to match the web app's behavior
* format dart
* now uses the shared applyTimezoneOffset() utility function from mobile/lib/utils/timezone.dart
* add tests
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>
* fix(mobile): persist album sorting in settings
* fix(mobile): persist album layout
* fix: fixed store model id
* fix: corrupted AppSettingsEnum
* chore: refactor to remove RemoteAlbumSortMode
* refactor: use t instead of tr
* fix(mobile): ignore patch releases for app version alerts
* chore: make difference type nullable to indicate when versions match
* chore: add error handling for semver parsing
* chore: tests
* feat: view similar photos on mobile
# Conflicts:
# mobile/lib/models/search/search_filter.model.dart
# mobile/lib/utils/action_button.utils.dart
* fix: bottom sheet is unusable after navigating to search
* feat(mobile): open DriftSearchPage as root route
* reset search state on tab navigation
* fix tests
---------
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
* chore: add unawaited_futures lint as warning
# Conflicts:
# mobile/analysis_options.yaml
* remove unused dcm lints
They will be added back later on a case by case basis
* fix warning
# Conflicts:
# mobile/lib/presentation/pages/drift_remote_album.page.dart
* auto gen file
* review changes
* conflict resolution
---------
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
* feat: improved update messaging on app bar server info
* chore: message improvements
* chore: failed to fetch version error message
* feat: open latest release when tapping "Update" on server out of date message
* fix: text alignment states
* chore: code review updates
* Apply suggestion from @alextran1502
Co-authored-by: Alex <alex.tran1502@gmail.com>
* Apply suggestion from @alextran1502
Co-authored-by: Alex <alex.tran1502@gmail.com>
* chore: lots of rework of the version checking code to be cleaner
Added a semver utility class to simplify comparisons, broke the update notification logic into own widget, reworked view construction and colors.
* fix: show warnign without having to tap on app bar icon
* chore: colors
---------
Co-authored-by: Alex <alex.tran1502@gmail.com>