Skip to content

Tablet layouts, AGP 9, and the fixes from a full QA pass - #8

Merged
DesarrolloAntonio merged 107 commits into
developfrom
qa/2026-09-14
Oct 6, 2026
Merged

DesarrolloAntonio merged 107 commits into
developfrom
qa/2026-09-14

Conversation

@DesarrolloAntonio

@DesarrolloAntonio DesarrolloAntonio commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

Everything since develop (ff6f8bf, the build in the Play internal-testing draft): the adaptive/tablet work and the toolchain jump that came with it, then a month-long QA pass over the whole app with every fix verified on a device against a real server.

Toolchain and layout (the feat/adaptive-tablet work, never pushed as its own branch)

  • Kotlin / Compose Multiplatform / AGP 9 / compileSdk 37; Navigation 3 for Places and Collections.
  • Navigation rail and adaptive grids on wide windows; list–detail two-pane for Places and Collections.

QA campaign fixes — 3 P0, 16 P1, ~70 P2, each with a test seen failing first where a test could catch it

  • P0: "Remember me" kept the password in plain text in a backed-up file; saving a place from Edit removed it from every collection; the next account on the phone saw the previous one's data.
  • P1 (selection): saves that never reached the server (default-valued fields left out of the JSON, a price without its currency, stale route copies); a map that could not recover from a failed load; assembleRelease did not build at all on AGP 9.
  • Last stretch: location search shows the current query, not an older one; every editor asks before discarding typed text; People no longer takes over Calendar; Home's trip card adds places to that trip; one "Can't reach the server" message app-wide; the collection editors share one form shape (docs/design.md → "One shape for every form").

Release

  • New app icon, design 3a "Mapa teal" (the backend's folded map in this app's teal), generated for Android (adaptive + themed + legacy), iOS and Play by scripts/icon/generate_icon.py; iOS/Play PNGs without alpha.
  • Version 0.0.9-alpha (9) — 8 is the bundle already in the Play draft.
  • The release APK asks only for INTERNET, CAMERA, ACCESS_NETWORK_STATE and READ_EXTERNAL_STORAGE (≤ Android 12); NEARBY_WIFI_DEVICES was unused, and READ_PHONE_STATE / WRITE_EXTERNAL_STORAGE were implied by the Compose Hot Reload runtime.
  • Terms of Use and Privacy Policy rewritten for this app (they were another app's, with a placeholder date).
  • CI now also builds an unsigned assembleRelease on every PR.
  • README "What's next": the web features this app will and won't have.

Tests run

  • ./gradlew allTests testDebugUnitTest connectedDebugAndroidTest on an API 37 emulator — green on the last full run (476 tests ran, 64 instrumented; the rest up to date)
  • Manual smoke on Android against a self-hosted AdventureLog server, debug build, per screen
  • Release (R8) build driven against the real server at b4a73cd: create/edit/delete of every item kind, photos, camera, attachments, share, PDF, invitations, settings
  • Release build not re-run after the last fixes (verified on debug)
  • iOS not run at all (the framework links in CI); the themed icon not seen on a device; minimum OS (API 24) not tested

Not in this PR: screenshots — the QA reports and screen captures live in a local, unversioned folder.

🤖 Generated with Claude Code

DesarrolloAntonio and others added 30 commits September 3, 2026 16:49
upcomingTrips.firstOrNull() meant the second and third trips the server
sent were fetched and thrown away; the web lists all of them. Recently
updated had a bare count with no way to reach the other 19 of 22 places.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The Collection section was built but never showed: collections came from
_locationState.map reading allCollections.value once, and the location
usually finishes loading before that flow's first emission lands, so the
filter ran against an empty list forever. Switched to combine().

That uncovered a second bug once the chip became reachable: tapping it
called navigate() on the app-level NavController, but the collection
detail route only exists on MainShell's own nested one - an
IllegalArgumentException that killed the app on every tap. Registered
the collection screens on the outer graph too, reusing the existing
"detail/{locationId}" route for the read path; the rarer write actions
(edit collection, add transportation, edit an unrelated adventure) fall
back to going home rather than duplicating those destinations again.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…y completed ones

The percentage used completeCount, on the assumption that's what the web does. Checked
live: this account has 6 visited / 0 complete out of 250, and the web's own bar reads
"2%" — that's 6/250, not 0/250. The web counts any visit as progress; only the per-country
row distinguishes complete from partial.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ogged

The list rendered days in chronological order with no scroll-to-today, so an account
with a year of history landed a "See all" tap on September 2025 instead of anywhere
near the present. The web opens its calendar on the current month for the same reason
this needed fixing here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…t just All

The add button existed in the All tab's Transportations section header
but was never copied into the dedicated Transportations tab - its empty
state invited you to "start adding transportation methods" with no way
to actually do it. This is likely why it was never verified end to end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…d say what got deleted

The form defaulted both timezone fields to the literal string "UTC" - the server's
choices don't include it ('"UTC" is not a valid choice'), so every transportation
creation failed. Default to the device's own IANA zone instead, which the server
always accepts.

Also: deleting a transportation reported "Adventure deleted successfully" - the
delete-confirmation snackbar was shared with place deletion and never distinguished
which one happened. DeleteState.Success now carries its own message.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Archiving succeeded on the server but the collection stayed on screen until the
process died: setArchived was the one mutation that neither updated the cached
list nor bumped the version counter paging invalidates on, unlike create, delete
and update. Restoring appeared to work only because the Archived tab isn't paged.

The header count was wrong in the other direction - it came from
/api/collections/all/, which includes archived collections, while the list under
it comes from /api/collections/, which doesn't. Six collections, five cards.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Everything was already wired - the clustering marker calls onAdventureClick with
the id, and MainShell already takes an onOpenLocationById that reaches
goToDetailById. The two were just never connected: the map's callback was an
empty TODO, so tapping a pin showed its name and went nowhere. The web's pin
popup offers "Ver detalles" for the same journey.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…d on grey

The "Route Map" on the transportation form was a placeholder that shipped: a
300dp grey box printing "Map View (No coordinates set)", or the raw lat/lng pair
as text once you had searched for one. The signature was already right, so this
makes it an expect/actual like every other map in the app - Google Maps on
Android with a marker per end and a line between them, framed to hold both;
MapKit on iOS. With neither end set it now says what to do instead of naming a
state nobody asked about.

Also drops the 4dp elevation on that card, which no other card in the app has.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Last of the TODO stubs sitting at the end of a finished chain: the button was
drawn, the callback was declared, and the body was a comment. The web has the
same button ("Abrir mapa") above its country list.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Starting the app with no network logged the user out for good: the token check
is a plain network call, and its catch treated every exception as a bad token -
so an unreachable server cleared the stored session, and reconnecting did not
bring it back. Found by turning the emulator's network off and cold-starting.

Now only a 401 or 403 clears it. Anything else keeps the session and carries on
with the stored profile; the screens that need the network still fail on their
own and say so, which is recoverable in a way a logout is not. ApiResponse
already drew this distinction - this use case just never used it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Home stated the problem and stopped there. The commonest reason to reach it is
no signal - which is over the moment it is over - so the only way back was to
restart the app or switch tabs and return. The calendar has offered "Try again"
all along; this is the same, calling loadDashboard().

Worth having now that a network failure at startup no longer logs people out:
they land here instead, which is the point where they need it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Same shape as the archive bug, found by looking for its siblings rather than by
tripping over them:

- duplicateLocation and duplicateCollection announced a copy the list did not
  show until the screen was left and re-entered. Duplicating a collection was
  the visible one: refresh() refills the cached flow the header counts from but
  not the counter paging invalidates on, so the header said seven above six.
- refreshCollections now bumps that counter, which is what accepting an
  invitation relies on too.
- updateLocation left selectedLocation holding the pre-edit copy, and
  getLocation answers from it whenever the ids match - so opening a place you
  had just edited showed what it used to say. deleteLocation left the deleted
  one there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
loadInitialData runs at construction, before anything has asked the server
anything, and its catch deleted the stored token. So a field this build no
longer understands - or a half-written value - logged the user out permanently,
over a parse the next build might well manage. Now it leaves the token alone
and says so in the log. The user starts at the login screen either way; this
way there is still a session there to come back to.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Editing your name with no connection reverted the field and told you to try
again - which meant typing it again first. The revert is right when the server
refuses ("that username is taken"): that is a verdict on the value, and the
form should show what the server holds. Being unable to reach the server is not
a verdict on anything.

The account calls returned the server's own message and nothing else, so the
screen could not tell those apart. They now return an AccountError carrying that
same message plus whether the server actually answered, and only a refusal
reverts. The message stays the server's words wherever there are any - that part
of the design was deliberate and is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Kotlin 2.2.21 → 2.4.10, Compose Multiplatform 1.8.2 → 1.12.0, AGP 8.13 → 9.3.2,
Gradle 8.13 → 9.7.1, compileSdk 35 → 37. Measured a week ago as one atomic
piece of work - every library bump failed on the same wall - and that held.

Two things the notes did not predict:

- AGP 9 refuses to apply com.android.application/library alongside the Kotlin
  Multiplatform plugin, and points at com.android.kotlin.multiplatform.library
  instead, which is library-only and so cannot replace the application plugin
  here. android.builtInKotlin=false and android.newDsl=false keep the previous
  behaviour; migrating the library modules to the new plugin is its own job.
- Compose 1.12 wants compileSdk 37, not the 36 the notes said.

targetSdk deliberately stays at 35: it changes runtime behaviour, and mixing
that into a toolchain upgrade makes any breakage twice as hard to attribute.

Android and iosSimulatorArm64 both compile.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… screen

First runtime casualty of the Compose 1.12 jump, and it took every screen with
it: the app bar's content sat mid-screen and the content below it was pushed
off entirely. The cause was one modifier - the title slot wrapped its content in
Box(Modifier.fillMaxHeight()), which under the looser slot constraints of the
new version grew the bar to fill half the display.

The app bar centres its own title, so the fill was never needed.

Compiling was not evidence of anything here; this only showed up on the device.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Coil 3.3.0 → 3.6.1, Koin 4.1.1 → 4.2.2, Ktor 3.3.2 → 3.5.2. All three failed a
week ago against the old toolchain - Coil and Koin wanted a newer compileSdk,
Ktor's klibs wanted a newer Kotlin - and all three go through untouched now
that the wall is gone. No source changes were needed.

Android and iOS compile; the app runs, loads its data through Ktor, resolves
its graph through Koin and draws its photographs through Coil.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The five destinations move from a NavigationBar the shell drew itself to items
handed to NavigationSuiteScaffold, which picks the shape the window deserves: a
bottom bar on a phone, a rail down the left on a tablet. A bar pinned to the
bottom edge of a 1600px display is a long way from where the hands are.

Verified both ways on the resizable emulator - rail at 1600x2560, bar back at
1080x2400.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Places, Collections, World and Home's recent places were LazyColumns, which on a
tablet means one photograph stretched across 1600px. They are adaptive grids
now: a minimum card width, and the window works out how many fit - one on a
phone, two on a tablet, three on something wider, without anything naming a
device.

The rows that would read worse cut in half keep the full line: Home's featured
trip, its stats card and every section header, World's search, progress and
filters, and both lists' paging spinners.

Verified at 1600x2560 and back at 1080x2400, where nothing changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…forms

Adds navigation3-runtime (androidx, which does publish native klibs),
navigation3-ui (JetBrains, 1.1.1) and material3 adaptive-navigation3
(1.3.0-beta02 - a different version line from the navigation-suite artifact,
which tracks Compose and is 1.12.0-alpha03).

The probe is not wired to anything. It exists because the two-pane migration
should start from a shape known to compile for Android *and* iOS, and three
things had to be found out the hard way first:

- entry is a member of EntryProviderScope, so importing it fails to resolve.
- NavDisplay takes sceneStrategies, a list, not a single sceneStrategy.
- rememberNavBackStack's no-configuration overload is Android-only. Shared code
  must pass a SavedStateConfiguration or it builds on Android and breaks on iOS
  - which is exactly the kind of thing that would have surfaced late.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The first screen on Navigation 3. A NavDisplay with a ListDetailSceneStrategy
decides from the window whether the place opens beside the list or on top of it:
two panes on a tablet, the same full-screen page as before on a phone. Choosing
between places means comparing them, and the old behaviour threw the list away
to show you one.

Nested rather than a rewrite: a NavDisplay is only a composable, so it sits
inside the existing NavHost and the other tabs carry on unmigrated. That keeps
this to one screen instead of every module at once.

Two things worth writing down, both found by running it:

- SavedStateConfiguration.DEFAULT cannot serialise a NavKey. The back stack is
  persisted by serialising its keys and NavKey is open, so the subclasses have
  to be declared in a polymorphic module - it compiles either way and throws the
  moment the screen composes.
- The detail pane hides its back arrow when both panes are visible. There is
  nothing to go back to; the list never left.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The second Navigation 3 screen, the same shape as Places: the list on the left,
the collection's contents on the right, and the old full-screen page on a phone.
A trip is chosen by looking between them, which is hard when opening one hides
the rest.

Both list screens now register their own route and pass registerListRoute=false
to the feature's graph, which still owns the add and edit destinations.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
360dp per column meant a foldable (674-841dp) and the list side of a two-pane
screen both fell one pixel short of a second column and showed a single card
in the middle of an empty page. 300dp fits two.

The dashboard was worse, because its rows spanned every column: a progress bar
drawn 780dp long stops reading as a proportion. The four stats now pair off
into two columns on a foldable and four on a tablet, matching the web, and the
trip and event rows flow into the grid instead of each owning a full line.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Half a tablet holding the sentence "Pick a place to see it here." is a worse
answer than showing a place. Both two-pane screens now open their first item
once the list loads. Phones have no second pane to fill, so nothing opens by
itself there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A search field, a region dropdown and a hero card, each drawn the full width
of a landscape tablet. Capped at 720dp they are the size of the thing they are
for again.

The cap has to go through a Box. A spanned lazy-grid item is measured at an
exact width, and widthIn coerces its own maximum back up into that fixed
range - so the modifier applied directly to the content compiles, runs, and
changes nothing. The Box takes the exact width and hands its child loose
constraints.

Also names the collection in the detail pane. The name used to come from the
shell's breadcrumb, which reads the outer route; the two-pane screen never
sets one, so the pane opened on a description with nothing to attach it to.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Capping elements one at a time was the wrong fix: it left a screen holding a
full-bleed progress card, a 720dp search field and a three-column grid, each
finding its own edge. Three measurements on one page.

There is now a single content column - 1040dp, centred - and everything lives
inside it: the app-bar title, the search field, the filters, the stats, the
cards. They share a left and a right edge at every window size, and the space
left over on a tablet becomes margin instead of a fourth width.

The app bar is inset to the same column. It is transparent, so this only moves
the title and the actions; without it the greeting starts at the window edge
while the page under it starts 120dp further in. Two filter rows also carried
their own 16dp padding on top of the grid's, which put them on an edge of
their own.

Below 1040dp none of this does anything, so phones and foldables are unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…olour

Two changes that turned out to be the same finding.

Material You was on by default, so on Android 12+ the app took its palette from
the phone's wallpaper. Every screenshot in this branch was lavender because the
emulator's wallpaper is lavender; #006973 - the colour in Color.kt, the colour
of the launcher icon - never appeared on a modern device at all. That is what
"it feels generic" was: the app had no colour of its own. Now it does, and the
switch stays in Settings for anyone who prefers the wallpaper.

The redesign needed no new palette. Its twenty-two colours are the light scheme
already in Color.kt, tone for tone.

Past 1000dp the dashboard becomes two columns - the trip and the photographs on
the left, a 348dp rail of numbers and dates on the right - which is the redesign's
own `minmax(0,1fr) 348px`, and what the empty third of a tablet was for. Below
that everything stacks, unchanged.

The hero carries the trip's photograph: beside the text when there is room for
both, above it when there is not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…screenshots

The first attempt was transcribed by eye from screenshots of the mock, and it
showed: a grey hero instead of the cyan one, icons the design does not have,
two separate lists it had deliberately merged. The mock's own HTML was readable
the whole time. It is now in design/, and this is built against it.

- The hero is the primary container. It used the secondary one unless the trip
  had already started, which is where the washed-out grey-green came from. It
  carries the dates, the two actions, and the photograph beside the text.
- The stats lose their icons: four of them down the left edge were a column of
  decoration pushing the numbers into the middle of the card. Counts are
  monospaced so four rows line up as a column of figures.
- "Upcoming trips" and "Coming up" become one dated list. A trip starting on the
  5th and an event on the 5th are the same kind of fact, and two lists made the
  reader merge them by eye.
- Recent places get their own small card - photograph on top, facts underneath
  on white - because a name laid over a bright sky disappears at that size.

Two layout traps, both the same shape: an Image inside height(IntrinsicSize.Min)
reports its natural height and grew the card to nine hundred dp, and a Card with
enabled=false is not "not clickable", it is greyed out.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…, round actions

The bar was a MediumTopAppBar: a collapsing two-row thing with the greeting on
its own line under the icons. The redesign has one row - name, one line saying
where you stand, and the search and the avatar as two circles beside it.

Home is the only screen with the subtitle for now. The others print their own
count inside the page, and "22 places" twice, one line above the other, reads
as a mistake rather than as emphasis.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DesarrolloAntonio and others added 27 commits September 16, 2026 09:22
The map's error state had no control at all and nothing reloaded it: with no
network it showed "Error loading map data" and stayed there for the rest of
the session — leaving the tab and coming back did not help, only restarting
the app did (`refresh()` had no caller). It now offers Try again, and coming
back to a map that is in that state retries by itself.

Test: MapViewModelTest.aMapThatFailedCanBeTriedAgain, seen red on two breaks.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
On a wide window Places and Collections are two panes. Deleting the
selected one left its detail open on the right, Edit and Share and all,
until something else was chosen — the server had already returned 404.

The delete result now carries the id of what left, and each pane drops
any detail key pointing at it.

Covered by PaneBackStackTest (3 tests: the shown one goes, another stays,
and the same on the collections side), each seen red.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… sides

Found with three real accounts - one owning, one invited, one granted
nothing - and fixed together because they are one experience:

- The share sheet's messages were drawn by the screen behind it. A modal
  sheet is its own window, so nothing it raised was ever seen: not the
  server's "Invite already sent to this user", not an offline failure.
  The sheet now says what happened itself.
- A guest was offered the owner's whole menu, and the server refused every
  owner-only entry. Collections now carry their owner, and the collection
  and place sheets offer a guest only what the server allows (open, the
  share card, the PDF; edit and duplicate a place).
- Every repository answered 401 and 403 alike, so a refusal of one record
  read "Session expired. Please log in again." A 403 is now its own case
  with its own words.
- An invitation that arrived while the app was open could not be reached:
  invites were read once, and the empty list could not even be pulled,
  since a plain Box passes no drag to PullToRefreshBox. A refresh now asks
  again, and the empty states are pullable.
- Home shows pending invitations and opens Collections on Invites, and a
  person in People opens their profile numbers.

Tests: CollectionsSharingViewModelTest (5), RefusalIsNotASessionTest (2),
OwnershipTest (3), UsersViewModelTest (+2), GuestActionsTest (4),
DashboardScreenTest (+2), SharedComponentsTest (+1) - each seen red.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…screens looked like

The queued P2s:
- A checklist saved unchanged is no longer sent: the server recreates every
  item on any save, ignoring the ids it is given (CO-04).
- The transport date and time buttons open their pickers; dates are stored
  as the web stores them - local time in the chosen zone, as UTC (CO-06).
- Deleting a note, checklist or stay asks first (CO-07).
- A collection save that fails keeps the form and what was typed (CO-08).
- Duplicate and Share externally work on a place inside a collection, and
  it can be removed from just that collection (CO-09, CO-10).
- Tapping the words of a Public row toggles it (CO-11).

Found while verifying, by looking at the screenshots:
- A link typed while creating a collection was dropped (CO-12).
- Transport dates are stacked and labelled - the arrival was pushed out of
  its card - an arrival before the departure is refused, and "Constrain to
  Collection Dates" limits the pickers instead of doing nothing.
- Note, checklist and stay dates use a calendar field with a label that stays.
- The place sheet opens fully, so Delete is not cut off; "Rating" appears
  once; multi-line fields have no icon floating halfway down; the empty
  Places tab no longer talks about adventures.

Tests seen red: AddEditChecklistViewModelTest (+2), TransportDatesTest (5),
AddEditCollectionViewModelTest (3), RemoveLocationFromCollectionUseCaseTest,
TransportDatePickersTest (5), CollectionTabsTest (+2), PublicRowsTest (2),
FormFieldsTest (3).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ed like

- A pull to refresh shows that it is working: the screen set its own flag
  on and off in the same call, before the request started (WO-01).
- A region ticked on a country's page reaches the world list: the cached
  country's count follows the tick and the list follows the cache (WO-02).
- Region rows are checkboxes with their state, not buttons with an icon
  described "Visited" (WO-03). The filter chips already exposed theirs -
  the campaign had read the wrong attribute.
- "Can't reach the server. Check your connection." on the World paths,
  as everywhere else (WO-04).

Found by looking at the screenshots:
- The country page had no back button: onNavigateBack arrived and nothing
  drew it.
- The tick on the selected "All" chip was dark teal on teal.
- Mostly white flags (Japan) had no edge against the white card.
- "3 / 47" sat on two baselines.

Tests seen red: RegionTickCountsTest (2), WorldViewModelTest (3),
CountryDetailScreenTest (2). SettingsViewModelTest pinned the old wording
and now asserts the new one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Chosen filters survive process death (SavedStateHandle).
- The stats card sits above the map instead of covering its top edge.
- City and region markers say what they are to a screen reader.
- Counts show a dash while unknown (loading, failed, regions not yet in)
  instead of zeros; the region count is the visited regions themselves.
- Filter sheet: one drag handle, layer rows are switches, opaque card.
- 403 read as an ended session in seven places the 09 round missed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… looked like

- Rows open what they name: a visit its place, a trip or anything dated
  inside one the trip; the event's resource id is now read.
- Today is marked where it falls, and the list opens on it.
- "Show everything before" loads the rest of the journal on request,
  says when there is nothing, and can be retried offline.
- Filter chips say which one is on (MetaChip selected), here and in the
  Collections filter; Completed gets its own emoji so the tick is not doubled.
- The date column no longer hangs below one-line cards.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…over

The photo's slot stays and is filled on purpose: a tonal surface with the
category's emoji (a line pin without one), from placeholderColors() so dark
theme follows. Replaces the gradient and the Static Maps thumbnail that never
loaded, so feature/ui no longer needs a Maps key.

- Places card: ink on a tonal band instead of white on a scrim, and dashed
  "+ Add photo" / "+ Category" chips that open the editor.
- Home strip: every line always drawn, so cards side by side keep one height.
- Detail page: a compact bar (back, edit, share) and a "No photo yet" card
  instead of a 300dp stand-in cover. The name stays in the page heading only.
- "Add photo" (card and detail) opens the editor on its images section,
  expanded and scrolled to, via createEditRoute(openImages = true).
- Detail page provides LocalContentColor (titles were black on dark theme
  when opened from Home); the shell consumes the bar insets it pads for.

Verified on the Pixel 6a, light and dark, with a test place created and
deleted. Not reachable on this server: a place with no category (the API
requires one) and a photo-less place in the Home strip (the dashboard
endpoint leaves it out).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The convention plugin added consumer-proguard-rules.pro to every library
module and only core/network has one. AGP 8 ignored the missing file,
AGP 9 fails :core:model:mergeReleaseConsumerProguardFiles, so
assembleRelease could not run at all. Add the file only where it exists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…er one

Every keystroke started its own geocoding request and whichever answered
last won: "Valencia" listed places called "Valen" in Norway, and "Madrid
Atocha" said "No results found" because "Madrid A" took five seconds.
Both the place and the transport forms now cancel the previous search and
wait 300 ms before sending (QA RL-05).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The editor's checkboxes had no label, so a screen reader said "checkbox,
not checked" without saying which item. The whole row is now the toggle,
read with the item's text (and tappable on the text too), and Remove says
which item it removes (QA RL-06).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The header read "0 collections · Invites" above two pending invitations.
On that tab it now says "2 invitations" (QA RL-08).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cancel (and Back) left the checklist editor at once, losing the items
typed so far. Like the place form, it now asks "Discard this checklist?"
(or "Discard changes?" when editing) whenever something would be lost,
including an item typed but not added yet (QA RL-09).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The note, lodging, transport and collection forms left on Cancel or Back
at once, losing whatever had been typed. They now ask the same question
as the place and checklist forms, through one shared rememberDiscardGuard
in feature/ui; an untouched form still leaves straight away (QA RL-09).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
versionCode 8 is the bundle already uploaded to the Play internal-testing
draft, and Play refuses an upload that reuses a code (QA #21).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Release runs tasks debug never does (R8, consumer ProGuard rules). On
AGP 9 assembleRelease stopped working and only the main-branch release
job would have noticed. The build job now also runs an unsigned
assembleRelease (QA RL-03).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
People was pushed with a plain navigate, so it landed inside whichever
section was open. Each section's stack is saved and restored, so after
People had been opened from Calendar, Calendar kept reopening People
until the app restarted. People now opens as a section of its own, like
Calendar and Settings (QA RL-10).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three of Home's open product decisions (QA HM-09), as the owner chose:
- the featured trip's "Add a place" opens the form for that trip, and
  the new place is created inside it (the form took no trip before);
- Coming up rows for visits and stays open what the calendar opens for
  them, the place or the trip, instead of nothing;
- "Recently updated" no longer shows the library total over a handful
  of cards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The owner's choices for the look-and-feel items left from QA 04:
- a filled field keeps its label ("Reservation", "Price") instead of
  losing it with the placeholder; this is the shared StyledTextField;
- the stay, note and checklist forms put their card at the width of
  everything else, and Save/Cancel as on the collection form;
- the stay's price shows its currency, not a "$" over euros;
- the transport form has a title, and its card says when and where;
- Places inside a collection gets a "+" that adds a place to it, in
  place of a count bubble the chip already showed.
The view chips cut at the edge stay as they are: the cut says it scrolls.
SectionCard also applies the modifier it was always given.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The decisions behind the CO-19 round: labels that stay, cards at the
form's width, Save across and Cancel under, a title on every item form,
a question before discarding, currencies by code, and a "+" in every
section that lists something.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NEARBY_WIFI_DEVICES had no code behind it. READ_PHONE_STATE and
WRITE_EXTERNAL_STORAGE were never declared: the merger implied them for
the Compose Hot Reload runtime, which reaches the release build without
a targetSdkVersion. Play shows all three on the store page and in Data
safety. The release APK now lists INTERNET, CAMERA, READ_EXTERNAL_STORAGE
(up to Android 12) and ACCESS_NETWORK_STATE.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The scope decision from the QA pass (queue #5): MFA/SSO sign-in, Spanish,
leaving a collection and attachment upload come next; the rest of what
the web client has is out of scope for now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The terms were another app's ("Shiori", under Apache 2.0) and the privacy
policy still read "Effective as of [Insert Date Here]" while claiming the
app reached no third party. Both are rewritten, approved by the owner,
from what the code does on this date: an independent MIT-licensed client,
no data collected, the session token (not the password) kept on the
phone, and Google Maps, Wikipedia and the user's own server as the only
places anything goes (QA #4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The app said the same thing four ways - "No internet connection.",
"Network unavailable", "Network error", "Can't reach the server..." -
and three of them blamed the phone's network, usually fine when the
server is asleep or the VPN is off. Every network failure now reads
"Can't reach the server. Check your connection." (CANT_REACH_SERVER in
core/domain); sign-in keeps its own, which also names the address.
Offline, the collections list printed the raw "Unable to resolve host
..." with the server's address; it now says the same as every screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
android-actions/setup-android@v3 asks for "tools platform-tools" by
default, and the SDK repository no longer has "tools", so the iOS job
failed at its first step on PR #8. Gradle fetches the rest itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Design 3a from the Claude Design file "App Icon.dc.html", chosen by the
owner: the AdventureLog project's folded map, redrawn flat - a white map
in three panels with light-cyan continents on the app's teal. Every
asset comes from one definition in scripts/icon/generate_icon.py: the
Android adaptive layers (with a monochrome layer for themed icons on
Android 13+), the legacy PNGs for Android 7, the 1024 px iOS icon and
the 512 px Play icon. On Android the map is drawn at 0.66 rather than
the design's 0.72, which put two corners past the safe circle where a
round launcher clipped them. The iOS and Play PNGs no longer carry an
alpha channel, which the App Store refuses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AGP 9.2, Navigation 3, the tests that exist now, signing that is wired
but waits on its secrets, the Releases page's newest build, and the iOS
notes as they stand (the discard prompt works on Cancel; the date-picker
fix is in but untested on a device). The banner points at the scope in
"What's next" instead of repeating an older list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@DesarrolloAntonio
DesarrolloAntonio merged commit ef6134b into develop Oct 6, 2026
4 checks passed
@DesarrolloAntonio
DesarrolloAntonio deleted the qa/2026-09-14 branch October 29, 2026 07:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant