Ranked feed, announcements, event analytics, org management, event addresses and covers, runtime iOS env, UI and swipe fixes (#100, #119, #120, #122, #123, #124) - #21
Conversation
…s, FEED_RANKING schema
…ars, ink var typos
…ion units, console opacity
…ending ids cleared on identity change
…; non-pager pills keep accent ink
…of dropping null-identity rows
… state, plus the appstore bake tripwire
|
|
||
| function apsEnvironmentMode() { | ||
| const profile = process.env.EAS_BUILD_PROFILE | ||
| if (!profile) return "development" |
There was a problem hiding this comment.
Use production APNs entitlement
The documented prep:testflight and prep:appstore archive paths run expo prebuild without EAS_BUILD_PROFILE, so this fallback selects the development APNs mode for both targets. The generated TestFlight and App Store archives therefore receive the sandbox entitlement and production APNs will not deliver their push notifications. Pass the archive target into the config or select production mode for store-distributed local archives.
Artifacts
- The executed Node script loads Expo config for the TestFlight and App Store local archive environments with no EAS build profile, proving the exact conditions under review.
- The executed validation output shows both targets resolving expo-notifications to development mode with EAS_BUILD_PROFILE unset, confirming the production APNs entitlement is wrong.
- The executed git check shows no diff for the relevant tracked configuration and archive-script files, confirming validation did not edit tracked files.
Ran code and verified through T-Rex
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/community-mobile/app.config.js
Line: 40
Comment:
**Use production APNs entitlement**
The documented `prep:testflight` and `prep:appstore` archive paths run `expo prebuild` without `EAS_BUILD_PROFILE`, so this fallback selects the development APNs mode for both targets. The generated TestFlight and App Store archives therefore receive the sandbox entitlement and production APNs will not deliver their push notifications. Pass the archive target into the config or select production mode for store-distributed local archives.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Comments Outside DiffThese findings sit on lines the diff does not cover, so they could not be posted inline. Each one leaves this list once its file changes.
|
| title={t("card.view_full")} | ||
| titleLines={1} | ||
| chevron | ||
| pressedHighlight={false} |
There was a problem hiding this comment.
The “View full analytics” row sets pressedHighlight={false}, disabling the ListRow hover and pressed backgrounds. The row remains clickable, but users receive no visual response while hovering over or pressing this navigation action; restore the highlight or provide equivalent feedback.
| pressedHighlight={false} | |
| pressedHighlight |
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/ui/src/bodies/host/dashboard/AnalyticsCarouselCard.tsx
Line: 102
Comment:
**Restore pressed feedback**
The “View full analytics” row sets `pressedHighlight={false}`, disabling the `ListRow` hover and pressed backgrounds. The row remains clickable, but users receive no visual response while hovering over or pressing this navigation action; restore the highlight or provide equivalent feedback.
```suggestion
pressedHighlight
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| const isEnded = hasEventEnded(cleanup, now) | ||
| const isLive = !isCancelled && !isDone | ||
| const isUpcoming = isLive && !isEnded | ||
| const donation = useMemo(() => donationLinkFor(cleanup), [cleanup]) |
There was a problem hiding this comment.
The event page now resolves donation links without considering whether the viewer hosts the event or whether the event is cancelled. An eligible link therefore renders the Donate card and action for both an event host and a cancelled-event visitor. This can solicit donations through an event that is no longer taking place; restore the host and cancellation visibility checks when selecting the link.
Artifacts
- Authored and executed focused test constructs an eligible cleanup for a host and a cancelled visitor, renders the resulting card markup, and compares it with the prior visibility gate; both restricted viewers now receive the card.
- Captured output of the Vitest command run in `/home/user/repo`, showing the focused test passed with exit code 0; the host and cancelled visitor cases were exercised successfully.
Ran code and verified through T-Rex
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/ui/src/bodies/EventDetailBody.tsx
Line: 216
Comment:
**Hide invalid donation cards**
The event page now resolves donation links without considering whether the viewer hosts the event or whether the event is cancelled. An eligible link therefore renders the Donate card and action for both an event host and a cancelled-event visitor. This can solicit donations through an event that is no longer taking place; restore the host and cancellation visibility checks when selecting the link.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Resolves civfix/issue-tracker#119
Resolves civfix/issue-tracker#120
Resolves civfix/issue-tracker#123
Resolves civfix/issue-tracker#124
Part of civfix/issue-tracker#100 and civfix/issue-tracker#118
Client half of civfix/issue-tracker#122
What changed
On web and mobile: (1) the home feed adopts the new ranked order and gains live updates — a "new posts" pill while you're on the feed, and like/reply counts that tick up in place; (2) event hosts can send announcements — a composer with an audience picker and a live recipient count, a list of what they sent, an announcements section on the event page, and a page per announcement anyone can open from a link; (3) "Open full console" is gone from the app and an "Analytics" card with a swipeable set of panels takes its place on host tools, with a full "Event analytics" page behind it; (4) host tools no longer repeats its own header buttons as rows, and "Invite a team member" is folded into "Team"; (5) "Email attendees" and "Message attendees" are replaced by "Open group chat" and "Make announcement"; (6) organizations can be managed in the app — name, logo, description, website, donation and social links, and members — with the handle left permanent; (7) the iOS app picks its API at runtime, so a TestFlight install talks to staging and an App Store install to production from one binary; (8) the donation card on an event page moved under the title; (9) three fixes — a photo no longer vanishes from a chat message you just sent, the quote composer shows the post you are quoting, and social links render as brand icons on profiles, people and organization pages; (10) a wide UI polish pass plus mobile navigation fixes (back-swipe, map pin flicker, edge-drag vs row swipe).
Then a second batch, all about addresses and the event page: (11) every event now carries a meeting address a human confirmed — creating or editing one shows the street address for the pin you dropped and asks you to confirm or correct it, and where no street address can be found you have to type one before the event can be published; (12) addresses on an event page, a report page and your ticket are now live — tap one to centre it on the map, copy it, or open it in a maps app; (13) the report composer offers the address it worked out for your pin, never blocks you from filing, and no longer files an address you never touched as if you had typed it; (14) hosts can put a cover image on an event, which replaces the map picture at the top of the event page and becomes the picture that shows when the event is shared; (15) the event page was rebuilt to lead with what the event is and who is hosting, share and repost became round icon buttons beside the title, "Message crew" moved under "View ticket", "Who's going" is gone and the attendee list now lives on the host's row — which also works after the event has ended; (16) the "Donation link" row is gone from the event dashboard; (17) list cards no longer clip their first and last rows against the rounded corner.
A third batch tidies the host surfaces: (18) an organization's "Upcoming events" and "Past events" both stay open and grow three at a time behind a "Load more" link, share moved up next to the name as a round icon, the blue verified check marks are gone (the wording that says what an organization is verified AS stays), and "Manage" runs the full width on a phone; (19) on the event dashboard the "Needs attention" card is gone and a graph-first analytics carousel for your most relevant hosted event takes the space, while pending invitations get a card of their own on your profile that only shows when there are any; (20) the event page no longer carries a "Guests" section - guest contact now lives only in the host's roster export.
A fourth batch, from the follow-up session on this PR: (21) iOS push notifications survive switching accounts on one phone — signing out now releases the device's notification registration and ends the session on the server before clearing the phone, and a registration left behind by a previous sign-in can be taken over again instead of failing silently forever; (22) the feed gets a gentle reshuffle on every pull-to-refresh so it never looks identical twice, while pages you already fetched stay consistent — and nearby content now outweighs everything else in the ranking; (23) beta (TestFlight) builds say which backend they talk to on the about card, and store builds carry the correct notification environment; (24) trying the TestFlight beta no longer signs you out of the App Store app when you return to it; (25) announcement, analytics and organization-manage notifications tapped from inside a thread open in place instead of collapsing the stack; (26) sharing an unlisted event no longer puts its cover image in the link preview, organization pages show a loading state instead of a blank flash on a cold open, and the small social-link buttons are easier to tap; (27) analytics was redesigned around the whole portfolio — the dashboard card sums ALL your events over the last 30 days (sign-ups, check-in ring, hours by event, impact rows), the full page swaps the lifecycle tabs for an event picker plus a 7/30/90-day/up-to-a-year range with a tappable per-event breakdown, the uninterpretable cumulative and reach charts are gone, the funnel starts at sign-ups so it always matches the tiles, and 'View full analytics' presses with a proper edge-to-edge highlight.
Before you start
Verify
Ranked feed + live updates — [Web] [Mobile]
Making an announcement — [Web] [Mobile]
Event analytics after the redesign — [Web] [Mobile]
Host tools no longer repeats itself — [Web] [Mobile]
Organization settings in the app — [Web] [Mobile]
The organization page shows both event runs — [Web] [Mobile]
Invitations sit on your own profile, and the dashboard leads with analytics — [Web] [Mobile]
3a. As B, open A's profile while A still has an invitation pending. — Expect: no "Invitations" card there — invitations only ever appear on your own profile, never on someone else's.
4a. With an invitation still pending, open "Event dashboard" and scroll it end to end. — Expect: NO "Invitations" card anywhere on it — invitations have left the dashboard entirely — and no "Needs attention" card either; that whole section, including its "Log volunteer hours" nudge, is gone.
A photo you just sent stays in the message — [Web] [Mobile]
Quoting a post shows what you are quoting — [Web] [Mobile]
Social links show as brand icons — [Web] [Mobile]
Donation prominence — [Web] [Mobile]
Confirming the meeting address before you publish — [Web] [Mobile]
Editing an event keeps the address you saved — [Web] [Mobile]
Tapping an address — [Web] [Mobile]
The report composer offers the address it already knows — [Web] [Mobile]
A cover image on your event — [Web] [Mobile]
The event page leads with what the event is — [Web] [Mobile]
5a. Open "Host mode" on that event and look at the roster, then run check-in. — Expect: the guest is there by name in both, tagged "Guest".
5b. On the sign-up slots on the event page, look for that guest. — Expect: they are NOT listed against any shift — a guest is only counted in the number going. Export the roster from the web host console instead. — Expect: the guest's row carries their email and phone; that export is now the only place a host gets guest contact.
List cards clear their corners — [Web] [Mobile]
iOS runtime API selection — [Mobile]
scripts/store-build.sh appstore). — Expect: the build gate passes and prints that the API is resolved at runtime.UI polish spot checks — [Web] [Mobile]
Swipe navigation — [Mobile]
The feed reshuffles on refresh, and still ends cleanly — [Web] [Mobile]
Push notifications survive switching accounts — [Mobile] (TestFlight build, two accounts)
An announcement notification opens the announcement — [Mobile]
A beta build says which backend it uses — [Mobile]
Trying the beta no longer logs out the App Store app — [Mobile] (one device, both builds)
Regression
Posting, likes, replies, reposts — [Web] [Mobile]
Feed, thread and profile cards — [Web] [Mobile]
Chat and inbox — [Web] [Mobile]
Event sign-ups, shifts and tickets — [Web] [Mobile]
Event dashboard — [Web] [Mobile]
The event page in its other states — [Web] [Mobile]
Link previews for everything else — [Web]
Creating and editing an event, end to end — [Web] [Mobile]
Filing a report, end to end — [Web] [Mobile]
Your ticket and check-in — [Web] [Mobile]
Organization and person pages — [Web] [Mobile]
Team on an event — [Web] [Mobile]
Settings and map layers — [Web] [Mobile]
Deep links and back navigation — [Web] [Mobile]
/cleanups/<id>/broadcast. — Expect: you land on the event page, not a blank shell.Report camera — [Mobile]
Feed states and chat socket — [Web] [Mobile]
Web visual sweep in both schemes — [Web]
Signing out with no connection — [Mobile]
The push toggle — [Mobile]
Not covered
How the new organization page, the reshaped dashboard and the profile actually LOOK on a device — the always-open event lists, the round share button, the "Invitations" card in the profile's flow and the analytics carousel in the dashboard's flow were not run in a simulator or a browser in this session.
Visual confirmation of the rebuilt analytics carousel is device-only. The graph-first panels, the measured-width paging and the fix for charts rendering tiny or the "Shifts" bars painting outside the card were reasoned from the code and covered by unit tests; nobody watched them draw on a real phone or in a browser in this session.
Guest contact now leaves the product through the host's roster export only. Whether hosts who relied on the on-page "Guests" list will find that export is a product question this change does not answer.
Guests are deliberately absent from sign-up slot lists — they are only counted in the going number. That is existing behaviour, not something this change fixed, and it is worth confirming a host is not surprised by it.
How any of the new work actually LOOKS on a device: the list-card spacing fix, the round header icon buttons, the cover image at the top of an event page and the address sheets were not run in a simulator or a browser in this session. Every claim about them here is read from the code, not seen.
The external-maps step per platform: the iOS "Address" sheet, Android's own app chooser and the clipboard were not exercised on either OS. Which maps app Android offers depends on what the tester has installed.
What a real lookup returns. The street / intersection / place / city-only rungs were built and tested against canned responses only — no live provider was called in this session, so the exact wording a real address comes back with (and how often the "we couldn't find a street address" path fires in a given city) is unverified.
On Android the "open in a maps app" control hands the location to the system chooser, and nothing in this session ran on Android. Worth watching: if the tap appears to do nothing at all on Android, that is the case to report, not a tester error.
A host standing at a pin that cannot be given an address can no longer publish until they type one, and the same now applies to editing an old event that never had an address. That is the intended behaviour, but how often it bites real hosts in a real city was not measured.
Which lookup provider answers is a server config and spend question, not something this PR decides — see the backend PR's note. It matters here only because it changes how often a pin gives a real street address rather than sending the host to type one.
Whether an address that is only roughly located should be shown at all, or coarsened, is a product and privacy decision left open; today it is shown with the "Near …" wording.
Setting a donation link for ONE event is still not possible anywhere in the app — only your own (Settings) and your organization's (Organization settings). Removing the dashboard row did not change that, but it does mean the app now has no row at all that mentions donation links outside Settings.
The social/link preview picture can only be checked against a deployed web build; a local export does not inject it. A cover whose image address is signed or carries a query string falls back to the brand picture — not exercised here.
Real push notifications and real announcement emails, and tapping a push to land on an announcement — no device and no mailer in this session; the in-app notification row is what the plan checks.
How the charts, the carousel and the social brand icons actually look — no simulator or browser run was made in this session, so their rendered appearance (spacing, colours, legibility of the bar/area/ring charts at small sizes) is unverified.
The full lifecycle of a chat photo the checks hold or reject: there is no practical way to make the checker reject a photo on demand, so only the normal "still being checked" path is a tester step. Note the app keeps a copy of a photo you just sent only for the current session and only for the last thirty messages — after a full reload it depends on the backend PR being deployed, and if the checks do reject your photo you will keep seeing it for the rest of that session while nobody else does.
The public organization page as it loads from a shared link on the web is a separate page from the one you reach inside the app, and it still lists social links as words rather than icons. Same address, two renderings — a known gap, not fixed here.
The organization's "needs at least one admin" refusal is not reachable from this screen: the actions are hidden before you can hit it, and an owner's seat already counts as an admin seat. Covered by the backend PR.
The web host console at its own address still exists and still works, but nothing in the app links to its dashboard any more (the product decision that removed "Open full console"); only the "Tickets" row on "Host mode" and the footer link on the public organization page reach it, both web-only.
Sharing or exporting analytics was deliberately cut from this change — the analytics page has no share or export action. CSV export still lives in the web host console only.
Real TestFlight→App Store and App Store→TestFlight install transitions (receipt detection and the one-time storage cleanup) — cannot be exercised before the build is on both channels; the logic is unit-tested, and the first upgrade off a build older than this one is documented as signing the user out rather than cleaning up preemptively.
App Review runs against staging by design now — before submitting, staging needs the reviewer sign-in enabled and a visible "Donate" card (noted in the review notes file).
Android install-source behavior: unchanged (always production for store builds) — not re-tested.
The seen-post rotation on repeated refreshes and popularity caps — server-side behavior, covered by the backend PR's plan.
Screen-reader passes beyond the specific announcements listed.
Deep links to civfix.dev generated by TestFlight sessions do not deep-link back into the app (pre-existing; one-line note in the app README).
Real notification delivery end-to-end (including the account-switch recovery) needs a TestFlight install on a physical phone — simulators cannot obtain push tokens, so this session verified the entire server half directly instead, plus every client flow the simulator can reach.
The App-Store↔TestFlight storage round trip is proven by tests, not by installing two real builds in sequence on one device.
The unlisted-event link preview: paste an unlisted event's link into a chat app that unfurls links and confirm no cover image appears (a public event's cover still should).
The analytics "Page views" and "Donation-link taps" numbers only appear with real web traffic (production web beacons); local and TestFlight runs will not show those tiles — absence there is correct, not a bug.