Commit Graph
86 Commits
Author SHA1 Message Date
iu cbff7a340c test(android): grant media permissions before UI E2E and dismiss all-files prompt
E2EFlowTest failed because the system media-permission dialog stole focus and
left no compose hierarchy (orchestrator clears package data between tests).
Grant media access in an outer rule before the activity launches, and dismiss the
all-files access prompt so it does not block taps.
2026-10-13 22:13:39 +03:00
iu fa210e6c7f feat(android): album/photo attribute-actions and device gallery sync fixes
Album panel and photo sheet with attributes/actions for every album (server and
device), local album rename/cover/delete, EXIF attribute settings, and a unified
device-gallery sync check:

- add repository "Атрибуты и действия" panel (swipe left) on server and device
  albums: cover, info, navigation, rename/delete; album settings screen; photo
  sheet (swipe up) with info, tags and actions; EXIF attribute settings backed by
  profile preferences
- local albums use local metadata and actions: app-only display name and cover
  (new device_album_meta table), delete removes photos from the phone via the
  system delete consent
- unified mode x state policy (GallerySyncPolicy/SyncCheck) runs on start and on
  album open: upload/offload in move, restore originals in copy/off
- fix move->copy restore, offload of any linked-but-local original, provenance
  recovery that no longer overwrites the album mode, serialized reconcile and
  duplicate-upload suppression; prompt for "all files" access on start
- album card caption: name (with linked server name when different) and storage
  icons (memory card / cloud) with item count

Sync delta specs into android-client and archive the change
add-mobile-album-photo-actions.

Verified: unit/Robolectric suite green, :app:assembleDebug, :app:lint, and the
DeviceSyncE2ETest instrumented suite on the emulator (7/7).
2026-10-13 21:49:12 +03:00
iu d1bb964685 Merge commit '15947c030132e8809ac8f867d0c942c43f7f1178' into develop 2026-10-13 17:12:05 +03:00
iu 15947c0301 test(android): restore data-layer unit tests hidden by the data/ gitignore rule
These network, repo, upload and image tests existed only in the main checkout because the old unanchored data/ rule ignored them. With the rule anchored to /data/ they now belong to the tracked data layer.
2026-10-13 17:11:50 +03:00
iu 2b54333a57 Merge commit '467a42a' into develop 2026-10-13 17:10:54 +03:00
iu 467a42ac4d chore(openspec): archive add-phone-album-sync and sync main specs
Sync the deltas into phpne...
2026-10-13 17:10:50 +03:00
iu 38b5b03f59 feat(android): sync device albums with a server album
Add phone album sync to the Android client: MediaStore source (device
albums), a Room index with upload provenance, on-device previews, and a
mixed server + device album list where a linked pair collapses into a
single device card with a link indicator and mode label.

Three modes over one pipeline: A (off), B (upload copies, originals stay
local), C (upload originals and offload them locally to previews). The
sync engine tracks device changes (add/remove/rename/move/content), matches
assets to server photos by sha256 content hash, deletes only this client's
uploads, and reflects server changes without touching local originals.
Offload is automatic; the zoomed viewer pulls the server original.

Backend: expose the original content checksum in photo data (Postgres and
SQLite), additive with no migration.

Also anchor the `.gitignore` `data/` rule to `/data/` so the Android
`com.galo.android.data` package is tracked, and add the halo launcher icon.
2026-10-13 17:10:47 +03:00
iu b7316f9e00 Merge commit '327f820' into develop 2026-10-13 12:41:16 +03:00
iu 327f820c3b chore(openspec): archive add-mobile-photo-zoom and sync main specs 2026-10-13 12:41:04 +03:00
iu beeb4c7c09 feat(viewer): show a loading spinner while the zoomed original loads
Show a spinner in the top-right of the photo viewer while the original is
being loaded on zoom, so the wait is visible. Only the original triggers it;
the fitted preview and a re-shown preview never do.

Cover the visibility gate with a unit test.

OpenSpec change: add-mobile-photo-zoom
2026-10-13 12:40:53 +03:00
iu 13d5e0f290 Merge commit 'f6df9e2' into develop 2026-10-13 12:35:21 +03:00
iu f6df9e23f2 feat(viewer): add pinch-to-zoom for photos on Android
Add pinch/pan/double-tap zoom to the full-screen photo viewer, mirroring the
web viewer semantics:

- scale is a multiplier of the natural size, capped at 200%; the fitted view
  is the lower bound
- double tap toggles fitted view <-> 100% natural anchored at the tap
- the original loads on zoom, with the preview kept as a placeholder so
  entering zoom never flashes a black surface
- the pager is disabled while zoomed (drags pan the image) and the zoom resets
  when leaving a photo
- video and 360 panoramas are unaffected

Implement PhotoZoom (zoom math) and ZoomableImage (gestures), wire it into
ViewerContent/MediaPage, and cover it with unit, Compose and device tests.

OpenSpec change: add-mobile-photo-zoom
2026-10-13 12:30:49 +03:00
iu 28828f1dcd chore(openspec): archive add-make-install-android and sync main specs 2026-10-13 12:06:59 +03:00
iu d9f6a163e5 feat(make): add install-android-app target and default help listing
- add `help` target and make it the default goal (bare `make` lists targets)
- add `install-android-app`: build debug APK and install on the selected device
- device selection via SERIAL/$ANDROID_SERIAL; several devices list and stop
- document the target in android/README.md
2026-10-13 12:06:34 +03:00
iu 43f5520236 chore(openspec): archive add-user-avatar and sync main specs 2026-10-12 16:17:40 +03:00
iu 04617d1cdb feat(profile): add round avatar with upload and cropping
- backend: user_avatar_objects binding (PG+SQLite), GC preserves avatar blobs,
  avatar service (EXIF-stripping WebP 128/512), public avatar read route
- web: avatar upload/crop on profile, round avatar in header/members/comments/author
- android: avatar upload/crop, round avatar on profile and albums header
- openspec: add-user-avatar proposal/design/specs/tasks
2026-10-12 16:16:47 +03:00
iu 657a04309e chore(openspec): archive add-user-profile and sync main specs 2026-10-12 14:27:19 +03:00
iu 86aff695df feat(auth): add user profile, phone login, and account deletion 2026-10-12 14:26:32 +03:00
iu 1cb9aaf198 chore(openspec): archive fix-trash-counter-sync and sync main specs 2026-10-12 12:58:34 +03:00
iu 713c1ca6d3 fix(ui): keep trash counter in sync with trash changes
Reload the rail trash count after every operation that changes the
trash contents, not just navigation and empty-trash: moving photos to
trash (grid, panel, viewer), restoring, permanent delete and deleting
an album. Previously the counter stayed stale until the next route
change.
2026-10-12 12:58:18 +03:00
iu bf3641f9e0 chore(openspec): archive add-android-app and sync main specs 2026-10-12 12:15:13 +03:00
iu 9e85e52a73 chore(openspec): archive fix-photo-tag-sync and sync main specs 2026-10-12 12:15:08 +03:00
iu 3666f4ea6d chore(openspec): archive host-agnostic-object-urls and sync main specs 2026-10-12 12:15:03 +03:00
iu aa2f7552bc feat(android): add native Android client
Kotlin + Jetpack Compose client for a self-hosted instance: manual
server setup and health check, auth with Keystore-backed tokens, album
and photo browsing with SSE live updates, upload queue in WorkManager
with HEIC-to-JPEG conversion, and Room/Coil offline cache. Add an
android Makefile target and revise project.md §17.
2026-10-12 12:14:54 +03:00
iu e4975aafb5 fix(ui): propagate photo tag edits to grid and viewer
Add a shared photo-updates signal the panel publishes on rename or tag
add/remove; the album grid and viewer subscribe and patch the changed
photo into their lists, so the 360 badge and panorama mode update without
a reload.
2026-10-12 12:14:48 +03:00
iu eef7c6c5d0 fix(storage): emit origin-relative object URLs
Build signed object URLs as path-only (/api/objects/...) instead of
prefixing APP_BASE_URL, so media loads from whatever host or reverse
proxy served the app. Remove the now-unused base URL from the signer and
drop the APP_BASE_URL / Server.BaseURL config.
2026-10-12 12:14:46 +03:00
iu 4d0e24347c chore(openspec): archive fix-pano-drag-direction and sync main specs 2026-10-11 21:02:06 +03:00
iu 6528abad82 fix(ui): correct horizontal drag direction in 360 panorama viewer
The panorama viewer inverted horizontal drag relative to vertical: the
yaw sign was opposite, so dragging right turned the view right instead
of moving the scene right. Flip yaw to match the pitch convention
(drag the world) and lock the behavior with a ui-workspace scenario.
2026-10-11 21:02:01 +03:00
iu a72d8876d6 chore(openspec): archive trash-permanent-delete-and-empty and sync main specs 2026-10-11 20:55:04 +03:00
iu 3b745fa737 feat(photos): fix trash permanent delete and add empty trash
Permanently deleting a photo whose album was deleted failed because
authorization resolved through the album. Fall back to uploader
ownership when the photo is albumless. Add a bulk DELETE /trash that
removes every trashed photo the caller may delete (uncapped listing)
and returns the deleted count, with an «Очистить корзину» action on the
trash screen.
2026-10-11 20:54:54 +03:00
iu d5d3f55db8 chore: ignore python bytecode 2026-10-11 20:41:20 +03:00
iu b2e0dd6d30 chore(openspec): archive add-video-support and sync main specs 2026-10-11 20:38:22 +03:00
iu d38ede0201 feat(video): add video upload with remux/transcode playback
Add video as a media kind in photos: streaming upload with per-kind
limits, ffprobe metadata, poster frame reusing the image variant
pipeline, and a playback object (remux when web-compatible, else
h264/yuv420p/aac transcode). Video jobs run on a separate pool with a
long lease. Frontend gains a video viewer, grid badge/duration and
video upload.
2026-10-11 20:37:53 +03:00
iu d059d042f0 chore(openspec): archive thumbnail-variants and sync main specs 2026-10-11 19:57:12 +03:00
iu f1b3b01bdf feat(photos): add square medium variant for sharp grid thumbnails 2026-10-11 19:57:00 +03:00
iu 581e479f03 chore(openspec): archive thumbnail-density-control and sync main specs 2026-10-11 18:24:59 +03:00
iu 511de4ec9c fix(ui): use minus/plus icons for thumbnail size step buttons 2026-10-11 18:24:35 +03:00
iu 41e5dfaeb1 feat(ui): fix thumbnail size slider direction and add step buttons 2026-10-11 18:21:47 +03:00
iu 6ccb7764e8 chore: refresh generated OpenSpec commands and skills 2026-10-11 18:10:34 +03:00
iu c2b0d9c66f chore(openspec): archive viewer-thumbnail-hover and sync main specs 2026-10-11 18:10:07 +03:00
iu a7867054a4 feat(ui): hover zoom on viewer thumbnail strip
- Thumbnails scale their image on pointer hover and keyboard focus
- Tile size and strip layout stay unchanged
2026-10-11 18:09:54 +03:00
iu 83cc178e79 chore(openspec): archive mobile-tap-open-and-panel and sync main specs 2026-10-11 17:58:30 +03:00
iu ebae341c9a feat(ui): open photo on tap for touch and fix narrow-screen panel
- Touch/pen tap opens the viewer; mouse click still selects, dblclick opens
- Panel and rail emit a single transform utility so the panel actually
  slides in below 1280px (Tailwind v4 utility ordering made the base
  translate-x-full win over the conditional translate-x-0)
- Add touch-action: manipulation to grid cells
2026-10-11 17:58:04 +03:00
iu 9b7d81d8fc chore(openspec): archive pano360-viewer and sync main specs 2026-10-11 17:41:54 +03:00
iu fc4233cd57 feat(ui): 360 panorama viewer by pano360 tag
- Detect 'pano360' tag and open the photo in a spherical viewer
- Mini-WebGL equirect viewer (no deps): drag to look, wheel FOV, Space reset
- Load original as origin-clean bitmap; fall back to flat image without WebGL
- 360 indicator in viewer and badge on grid tiles
2026-10-11 17:41:28 +03:00
iu 3311de57c5 chore(openspec): archive grid-keyboard-navigation and sync main specs 2026-10-11 14:51:44 +03:00
iu 7359495a73 feat(ui): keyboard navigation and focus restore in album grid
- Cursor separate from selection; arrow keys move it, Space/Shift+Arrow/Enter/Esc act
- Click on empty grid space clears selection back to album properties
- Focus returns to the last viewed photo after closing the viewer
2026-10-11 14:51:00 +03:00
iu d99ac77587 chore(openspec): archive completed changes and sync main specs
- fix-large-album-load -> photos, realtime, media-objects
- fix-album-cover -> photos, albums
2026-10-11 14:16:38 +03:00
iu d7a37328f0 fix(photos): faster large-album loading and album cover lifecycle
Large albums opened slowly and flickered; the default album cover was
also lost in the media-objects refactor.

Large-album loading:
- quantize signed object URLs within a window and send Cache-Control so
  previews are reused instead of re-fetched
- publish photo.updated (not photo.created) when variants are ready, so
  clients update in place without reordering the grid
- worker concurrency + newest-first variant priority; raise default batch

Album cover:
- first uploaded photo becomes the cover when none is set
- clear the cover when the cover photo is trashed or moved out
- "set as album cover" in the photo context menu and actions panel

OpenSpec changes: fix-large-album-load, fix-album-cover
2026-10-11 14:15:59 +03:00
iu 6361e48b20 feat(observability): configurable logging, request ids and error logs
- internal/logging: LOG_LEVEL/LOG_FORMAT parsing and slog handler
- request_id middleware (X-Request-ID or generated, echoed) + debug access log
- httpx registers errors; middleware logs internal causes with request_id
- health excluded, objects/sse debug-only, no query/body/secrets in logs
- new observability capability spec; README/.env.example docs
2026-10-11 13:40:11 +03:00