Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
3872b02
admin report list item carries thumbnailUrl
theobong Sep 16, 2026
2d59670
feed ranking contract: scored cursor, counts endpoint, realtime topic…
theobong Sep 16, 2026
f44415a
shared 0.49.0
theobong Sep 16, 2026
8b062c3
feed live updates: new-posts pill, count patching, ranked cursor adop…
theobong Sep 16, 2026
5ea06d9
donation card moved above event sign-ups
theobong Sep 16, 2026
d2b15d2
resolve the iOS API base URL from the install source at runtime
theobong Sep 16, 2026
76c1250
install-source probe hardening and env-scoped storage
theobong Sep 16, 2026
02c4cba
render the shell below a pushed route from its own nav stack
theobong Sep 16, 2026
bf241fc
cut the work that runs while a swipe transition is in flight
theobong Sep 16, 2026
cf34264
pressed hover and focus affordances across ui pressables
theobong Sep 16, 2026
5252119
44pt search field family across list bodies
theobong Sep 16, 2026
c425c9b
load-more pills consolidated onto sectionStyles
theobong Sep 16, 2026
a0b443b
text clamps and token substitutions in ui
theobong Sep 16, 2026
6d13d11
mobile overlay header ink and camera affordances
theobong Sep 16, 2026
b13f311
mobile auth affordances and dangerInk error rows
theobong Sep 16, 2026
37b50c2
web dark-mode token fixes: ring collision, hue ramps through scheme v…
theobong Sep 16, 2026
b897fd3
guest cancel view localized in all four locales
theobong Sep 16, 2026
1072e05
web transition, radius, type and a11y polish
theobong Sep 16, 2026
74bfa3c
native swipe capture reads the real touch-down x, not the pre-grant zero
theobong Sep 16, 2026
ede88b9
web token layer: canonical light palette, shadcn var collision, durat…
theobong Sep 16, 2026
e224af2
feed realtime: one cache pass per counts batch, overflow re-queued, p…
theobong Sep 16, 2026
d725982
event donation card hidden on cancelled and host views; receipt wraps…
theobong Sep 16, 2026
a26af45
a detail route with no entry snapshots the whole shell stack instead …
theobong Sep 16, 2026
bdbc4a5
mobile storage environment marker purges another environment's legacy…
theobong Sep 16, 2026
f285327
inbox row swipe guard pins the recorded touch-down x
theobong Sep 16, 2026
c9dc475
drop the explanatory comments added with these fixes
theobong Sep 16, 2026
391d01c
event announcements + consolidated analytics contract
theobong Sep 16, 2026
6bf64ab
shared 0.50.0
theobong Sep 16, 2026
31b3ebe
enums: announcement broadcast kind label in all four locales
civfix-agent Sep 16, 2026
350c50f
event announcements: read surfaces, deep-linkable detail, data hooks
civfix-agent Sep 16, 2026
bb97299
host comms: group chat + announcements replace the two message button…
civfix-agent Sep 16, 2026
b101102
event analytics in the app: chart primitives, a carousel card and a f…
civfix-agent Sep 16, 2026
f4e1e01
in-app organization management at /orgs/:slug/manage
civfix-agent Sep 16, 2026
e21719b
horizontal BarChart sizes itself from its parent rather than taking a…
civfix-agent Sep 16, 2026
4b59294
chat keeps the local photo when the server ack drops the attachment
theobong Sep 16, 2026
6bbc8ab
quote composer shows the post being quoted
theobong Sep 16, 2026
ea63c1c
social links render as brand icons on every profile surface
theobong Sep 16, 2026
9a2a768
review fixes: analytics gating, chat overlay, last-admin pre-disable,…
theobong Sep 16, 2026
0e004cc
web org page social links as icons
theobong Sep 17, 2026
4559605
address resolution contract: resolve-address endpoint, precision ladd…
theobong Sep 17, 2026
8965d4f
shared 0.51.0
theobong Sep 17, 2026
111159f
list cards clear their corner arc
theobong Sep 17, 2026
c784b5a
event dashboard drops the donation link row
theobong Sep 17, 2026
22daad0
event detail leads with what the event is
theobong Sep 17, 2026
b1f69cc
hosts can put a cover image on an event
theobong Sep 17, 2026
4c755f6
address resolution has a data hook
theobong Sep 17, 2026
4967715
addresses get a row you can tap, copy and navigate to
theobong Sep 17, 2026
388d1b6
hosts confirm the meeting address before an event goes live
theobong Sep 17, 2026
b550374
detail pages and tickets share one address row
theobong Sep 17, 2026
68b7f75
the report composer offers the address it already knows
theobong Sep 17, 2026
1750bc9
a missing resolve endpoint falls back instead of failing
theobong Sep 17, 2026
b0b1359
picking a place no longer doubles up the address
theobong Sep 17, 2026
0d214e7
editing an event keeps the address you already saved
theobong Sep 17, 2026
0274b45
a ticket and an ended event both keep their address and roster usable
theobong Sep 17, 2026
6cfb7cf
a report only sends an address the reporter actually wrote
theobong Sep 17, 2026
cd8f369
Open maps on Android via the https Google URL instead of a refused ge…
theobong Sep 17, 2026
b976925
org page: both event runs stay open with a real Load more, share move…
theobong Sep 17, 2026
5546f97
event dashboard: invitations get their own card, the attention nudges…
theobong Sep 17, 2026
3a8bcff
event detail drops the guest contact list
theobong Sep 17, 2026
6772791
invitations move from the event dashboard to your own profile
theobong Sep 17, 2026
118f568
the analytics carousel becomes graph-first and pages on a measured width
theobong Sep 17, 2026
ad9156c
merge origin/main
theobong Sep 22, 2026
b82209f
feed ranking: jitter knob, location-first default (0.54.0)
theobong Sep 22, 2026
7c310ba
push teardown unregister, 409 terminal, aps-environment mode
theobong Sep 22, 2026
bdd839f
feed batch cleanup
theobong Sep 22, 2026
a3fd6dd
storage marker write policy fix
theobong Sep 22, 2026
8480fc1
sign-out revokes the server session; lapsed push captures its bearer …
theobong Sep 22, 2026
4aec4e2
review fixes: og cover gate, thread routes, about api host, chart and…
theobong Sep 22, 2026
27d905e
sign-out releases the push token before revoking the session
theobong Sep 22, 2026
ca2df09
analytics labels, plurals, announcement emphasis
theobong Sep 22, 2026
c8c8f7e
host analytics summary contract (0.55.0)
theobong Sep 22, 2026
1ef64af
host analytics: 30-day all-events carousel and event+range analytics …
theobong Sep 22, 2026
a0352d7
analytics by-event rows keyed by event id
theobong Sep 22, 2026
947f0fd
analytics: whole-event signups tile fix
theobong Sep 22, 2026
cf89c61
analytics carousel: inert panels, quiet footer press, wider gutter
theobong Sep 22, 2026
2749250
carousel chart budgets for its label row
theobong Sep 22, 2026
e5d2893
profile donation card reuses the org card, row variant removed
theobong Sep 22, 2026
bf5a0b4
bar chart edge labels clamp inside the chart width
theobong Sep 22, 2026
702f71c
carousel panel drops its corner clip
theobong Sep 22, 2026
649a10d
event donation card shows for every viewer
theobong Sep 22, 2026
728902c
donate card spacing on profiles, org donate above events
theobong Sep 22, 2026
f7d2979
own profile: event dashboard row sits under the affiliation
theobong Sep 22, 2026
d3a55d6
Revert "own profile: event dashboard row sits under the affiliation"
theobong Sep 22, 2026
bfc4e95
Merge remote-tracking branch 'origin/main' into feat/feed-and-polish-…
theobong Sep 23, 2026
7ff07c2
shared 0.56.0
theobong Sep 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 5 additions & 3 deletions .github/workflows/deploy-mobile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ name: Deploy mobile (TestFlight)
# Builds apps/community-mobile for iOS and uploads it to App Store Connect, where TestFlight hands it
# to the internal tester group. Same shape as deploy-web.yml, one lane:
#
# push to main -> the `testflight` profile -> staging API (api.civfix.dev)
# manual run, profile=production -> the `production` profile -> prod API (api.civfix.org)
# push to main -> the `testflight` profile -> staging API (api.civfix.dev), baked
# manual run, profile=production -> the `production` profile -> API chosen at runtime by install
# source: api.civfix.dev in TestFlight, api.civfix.org from the
# App Store (src/lib/apiUrl.ts + src/lib/nativeBetaInstall.ts)
#
# Nothing here submits for App Store review. A production build lands in App Store Connect exactly
# like a staging one; attaching it to a version and submitting is the manual step, so a prod mobile
Expand Down Expand Up @@ -145,7 +147,7 @@ jobs:
if (api !== expectedApi) {
throw new Error(`profile '${profileName}' bakes EXPO_PUBLIC_API_URL='${api}', expected '${expectedApi}'`)
}
console.log(`profile ${profileName}: EXPO_PUBLIC_API_URL='${api || "(unset -> https://api.civfix.org)"}'`)
console.log(`profile ${profileName}: EXPO_PUBLIC_API_URL='${api || "(unset -> resolved at runtime: api.civfix.dev in TestFlight, api.civfix.org from the App Store)"}'`)
NODE
echo "TARGET=$target" >> "$GITHUB_ENV"

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ build cannot ship the staging API URL to civfix.org or the production one to the
The mobile app deploys through `.github/workflows/deploy-mobile.yml` on the same lane: a push to
`main` that touches the app or the packages builds the `testflight` profile (staging API) on a
GitHub-hosted Mac and uploads it to App Store Connect, where TestFlight hands it to the internal
testers; a manual run with `profile=production` uploads a prod-API build, and attaching that build to
testers; a manual run with `profile=production` uploads a build that bakes no API URL at all and
picks one at runtime from its iOS install source (`api.civfix.dev` while it is handed out through
TestFlight, `api.civfix.org` once it is downloaded from the App Store), and attaching that build to
a version and submitting it for review stays a human step in App Store Connect. The runner executes
the same `scripts/store-build.sh` a developer runs locally (`eas build --local`, then a direct
`fastlane pilot upload` to App Store Connect — no EAS Submit queue), so CI and laptop builds share
Expand Down
42 changes: 42 additions & 0 deletions apps/community-mobile/APP-REVIEW-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,48 @@
Paste the relevant section into **App Store Connect → App Review Information → Notes**
(and into the Play Console reviewer notes where the equivalent field exists).

The **Environment** section below is the exception: it is an internal pre-submission checklist, not
reviewer-facing copy. Do not paste it into App Store Connect.

---

## Environment — App Review runs against STAGING (internal; do not paste)

**The binary a reviewer runs talks to `api.civfix.dev`, not `api.civfix.org`.** This is deliberate
and needs to be understood before every submission.

The `production` EAS profile bakes no `EXPO_PUBLIC_API_URL`; the app picks its API at launch from its
iOS install source — a TestFlight/beta install (`StoreKit/sandboxReceipt`) resolves to
`https://api.civfix.dev`, an App Store download (`StoreKit/receipt`) to `https://api.civfix.org`
(`src/lib/apiUrl.ts`, `src/lib/nativeBetaInstall.ts`; the mechanism is written up in `README.md`).
App Review installs through the beta/sandbox path — the same fact behind StoreKit's 21007 sandbox
receipt status — so the reviewer's copy sees a sandbox receipt and runs against **staging**.

Consequences, accepted knowingly: the approval verdict is rendered against a binary whose production
behaviour was never exercised by the reviewer, and anything the reviewer is asked to find has to exist
on staging. There is no OTA update channel in this project that could flip an override for review
only, and routing beta installs to staging is the property this app deliberately wants — testers must
never write to the live civic record.

**Pre-submission checklist — all of these are about the STAGING environment:**

- [ ] `api.civfix.dev` is up and healthy (`/readyz`), and staging is on the same commit as the build
being submitted.
- [ ] Staging carries reviewer-visible demo content: at least one event, organization or profile
that shows a **Donate** card, so the "Reviewer steps" under *External donation links* can
actually be followed. Without it the reviewer finds nothing and the note reads as false.
- [ ] Staging carries a demo event with a ticket QR, so the check-in scanner note can be followed.
- [ ] A working reviewer sign-in exists on staging and the credentials in App Store Connect →
App Review Information match it. **This is currently missing:** `REVIEWER_OTP_BYPASS` and
`REVIEWER_OTP_CODE` are absent from `civfix-infra/secrets/staging/api.sops.env` (and from the
prod one), so the reviewer-OTP path is fail-closed on staging today. Add both to the staging
SOPS file and redeploy before submitting, or give the reviewer an account whose OTP they can
actually receive.
- [ ] Share links opened from the reviewer's build point at `civfix.dev` and resolve there; iOS
universal links are pinned to `civfix.org` only, so those links open in the browser rather than
deep-linking back into the app. Harmless, but do not write a reviewer step that depends on a
share link re-entering the app.

---

## External donation links
Expand Down
98 changes: 87 additions & 11 deletions apps/community-mobile/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,89 @@ pnpm --filter community-mobile exec expo run:ios # or run:android

Then start the bundler with `pnpm --filter community-mobile start` (runs `expo start --dev-client`).

## Store builds: TestFlight (staging API) vs App Store (prod API)
## Store-distribution profiles: `testflight` vs `production`

Two store-distribution profiles exist in `apps/community-mobile/eas.json`, differing only in the
baked API base URL and their EAS Update channel:

- `testflight` - dev/testing builds for TestFlight. Bakes
`EXPO_PUBLIC_API_URL=https://api.civfix.dev`, so testers hit the staging API. Update channel
`testflight`.
- `production` - official App Store releases. Sets no `EXPO_PUBLIC_API_URL`,
so release builds fall back to the prod API `https://api.civfix.org`
(`src/lib/apiUrl.ts`). Update channel `production`.
- `production` - official App Store releases. Sets no `EXPO_PUBLIC_API_URL`, so the base URL is
chosen at RUNTIME by install source: `https://api.civfix.dev` while that build is handed out
through TestFlight, `https://api.civfix.org` once the same build is downloaded from the App Store
(`src/lib/apiUrl.ts`, `src/lib/nativeBetaInstall.ts`). Update channel `production`.

### How the runtime split is decided (iOS only)

**This split exists on iOS and nowhere else.** `src/lib/nativeBetaInstall.ts` returns `false` for any
other platform, so an Android release build - including one handed to internal-track testers - always
resolves to the production API. Android's own testing tracks have no equivalent on-device marker.

iOS ships the App Store and TestFlight copies of a build with different StoreKit receipts: a store
download gets `StoreKit/receipt` in the app's data container, a TestFlight install gets
`StoreKit/sandboxReceipt`. `src/lib/nativeBetaInstall.ts` reads those two paths synchronously through
`expo-file-system`, so `API_URL` is a plain module constant and one session can never straddle two
APIs. `expo-application`'s `getIosApplicationReleaseTypeAsync()` cannot make this call: it reads the
embedded provisioning profile, which reports `APP_STORE` for TestFlight and App Store alike.

Precedence is `EXPO_PUBLIC_API_URL` (when baked) -> `__DEV__` localhost -> the receipt probe.

**Both receipts can be on disk at once.** Moving between TestFlight and the App Store is an in-place
update and the previous receipt is not removed, so mere presence decides nothing: the **newer** file
wins (`src/lib/storeKitReceipt.ts`). Everything ambiguous resolves to production - no sandbox receipt,
a tie, an unreadable modification time, an unreadable container, a probe that throws: all `false`. A
store download therefore cannot be routed to staging by any failure mode of this probe.

**The container path is an assumption.** The probe reconstructs `Bundle.main.appStoreReceiptURL` as
`<data container>/StoreKit/<name>`, derived from `Paths.document.parentDirectory`. If Apple ever
changes that layout the probe goes permanently `false` - production for everyone, which is the safe
direction but silent. A dev build logs the resolved container and both receipt stats under
`[install-source]` so the assumption can be checked on a real device.

**Identity-bearing state is scoped to the API it was written against** (`src/lib/storageScope.ts`).
Four ids carry the API host as a suffix: the app MMKV instance (`civfix.app` - cached user, last
identity, persisted query cache, prefs), the session token in the keychain
(`civfix.session.token`), the secure-blob MMKV instance (`civfix.secure`) and its keychain encryption
key (`civfix.secure-blobs.key`). Production deliberately keeps the legacy un-suffixed ids so existing
App Store users are not signed out by this change.

Three stores are deliberately NOT scoped, because none of them holds identity or server state: the map
filter prefs including recent-search history (`civfix.ui.filters`, `@civfix/ui`
`map/filterStorage.native.ts`), the sidebar width (`civfix.ui.sidebar`,
`shell/sidebarStorage.native.ts`), and the push `device_id` (`civfix.device_id`, `src/lib/deviceId.ts`),
which must stay stable per install for the backend's push-token ownership guard to recognise a
same-device handoff.

**Scoping alone does not protect the TestFlight -> App Store upgrade**, and that is the whole point of
`src/lib/storageEnvMarker.ts` + `src/lib/legacyStorageReset.ts`. Production resolves to the LEGACY ids,
so a prod build cannot tell its own leftovers from a pre-namespacing TestFlight install's staging
leftovers sitting under the same ids. Every run therefore records its environment in an unscoped
keychain item (`civfix.storage.env`), and `adoptStorageEnvironment()` runs at boot before the first
session read: on a production boot whose marker names a non-production environment, it clears the four
legacy ids (the blob encryption key is emptied through its own store rather than deleted, so an
already-open MMKV instance is never re-keyed mid-process) and the in-memory query cache. The marker
lives in the keychain, not MMKV, because the keychain is where the dangerous leftover survives an app
DELETE - so delete-and-reinstall into the App Store copy is covered too.

*What it cannot detect:* the FIRST upgrade off a build that predates the marker. There the marker is
absent, and an absent marker beside a legacy session token is genuinely ambiguous - equally a
long-standing App Store user whose session is legitimately theirs. Signing all of those out is the
worse failure, so an absent marker keeps the state: that one upgrade still sends a staging token to
prod, is rejected with a 401 and signs out, after briefly painting the cached staging user. Every later
environment flip on that install is covered, because by then a marker exists. Note also that a purge
clears `civfix.app` wholesale, so the locale, theme and onboarding-seen prefs of the discarded
environment go with it.

**Share links do not follow the split.** `app.config.js` `ios.associatedDomains` pins
`applinks:civfix.org` / `applinks:www.civfix.org` only, so a `civfix.dev` link produced by a
staging-resolved build opens in the browser rather than deep-linking into the app. Universal links
work only against the production domain; adding `civfix.dev` would need a new entitlement and a
matching apple-app-site-association file on that host.

**App Review runs against staging.** A reviewer's copy is installed through the beta/sandbox path, so
the probe returns `true` and review sessions hit `api.civfix.dev`. That is a deliberate, recorded
property - see the "Environment" section of `APP-REVIEW-NOTES.md` and its pre-submission checklist.

### Hand-driven Xcode archive (`scripts/prep-archive.sh`)

Expand All @@ -93,8 +165,8 @@ read `.env` at *archive* time.
So prep the native project with the target's `.env` in place, then archive by hand:

```sh
pnpm --filter community-mobile prep:testflight # -> https://api.civfix.dev
pnpm --filter community-mobile prep:appstore # -> https://api.civfix.org
pnpm --filter community-mobile prep:testflight # -> bakes https://api.civfix.dev
pnpm --filter community-mobile prep:appstore # -> bakes NOTHING; resolved at runtime
apps/community-mobile/scripts/prep-archive.sh appstore --platform android
```

Expand All @@ -116,9 +188,11 @@ for every third-party dependency. Then: open `ios/civfix.xcworkspace`, destinati

Two things this script exists to stop:

- `.env` is gitignored and *persists*. A `testflight` prep silently governs every later local build
on that machine, so the `appstore` target writes `https://api.civfix.org` **explicitly** rather
than leaning on the fallback in `src/lib/apiUrl.ts`. Re-run the script before switching targets.
- `.env` is gitignored and *persists*. A `testflight` prep would otherwise silently govern every
later local build on that machine. The script **rewrites `.env` on every run**, so the `appstore`
target's omission of `EXPO_PUBLIC_API_URL` is a real omission and not a leftover - and the banner
refuses to let you archive unless the built config carries **no `apiUrl` at all**. Baking one there
would freeze the runtime split to a single API. Re-run the script before switching targets.
- `expo prebuild` deletes `DEVELOPMENT_TEAM` from the pbxproj on every run unless `ios.appleTeamId`
is set, so a team picked by hand in Xcode's Signing & Capabilities pane vanishes on the next
prebuild. The script reads the existing team back out and feeds it in via `CIVFIX_APPLE_TEAM_ID`.
Expand All @@ -143,7 +217,7 @@ Android ships by hand from this directory: prep the target, re-apply the machine
Temurin 25).

```sh
scripts/prep-archive.sh testflight --platform android # or appstore -> https://api.civfix.org
scripts/prep-archive.sh testflight --platform android # or appstore; Android always resolves to https://api.civfix.org
scripts/android-release-patches.sh
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-22.jdk/Contents/Home
cd android && ./gradlew --no-daemon :app:assembleRelease # or :app:bundleRelease for the Play .aab
Expand Down Expand Up @@ -312,7 +386,9 @@ to `eas build`), and never map the `testflight` channel onto a prod-published br
Config plugins for the native modules (camera/mic/location permission strings, Google sign-in URL
scheme, Apple auth, notifications) are declared in `apps/community-mobile/app.config.js`. The API base
URL comes from `EXPO_PUBLIC_API_URL`, surfaced via `extra.apiUrl`; when unset, dev builds fall back
to `http://localhost:8080` and release builds to `https://api.civfix.org` (`src/lib/apiUrl.ts`).
to `http://localhost:8080` and release builds to whichever API the install source implies -
`https://api.civfix.dev` from TestFlight, `https://api.civfix.org` from the App Store
(`src/lib/apiUrl.ts`).

## pnpm + Expo + the shared packages

Expand Down
9 changes: 9 additions & 0 deletions apps/community-mobile/app.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ function resolveSourceCommit() {
}
}

const EAS_DEVELOPMENT_PROFILES = ["development"]

function apsEnvironmentMode() {
const profile = process.env.EAS_BUILD_PROFILE
if (!profile) return "development"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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

Evidence from the check

  • 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.

Command output from the check

  • 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.

Command output from the check

  • The executed git check shows no diff for the relevant tracked configuration and archive-script files, confirming validation did not edit tracked files.

View artifacts

T-Rex 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.

Fix in Claude Code

return EAS_DEVELOPMENT_PROFILES.includes(profile) ? "development" : "production"
}

const SPLASH_BG_LIGHT = tokens.color.neutral.paper

const APP_LINK_HOSTS = ["civfix.org", "www.civfix.org"]
Expand Down Expand Up @@ -229,6 +237,7 @@ module.exports = ({ config }) => ({
"expo-notifications",
{
color: "#FF7A6B",
mode: apsEnvironmentMode(),
},
],
[
Expand Down
20 changes: 17 additions & 3 deletions apps/community-mobile/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,12 @@ import {
ToastProvider,
setBrandAboutPresenter,
setOnboardingTourPresenter,
setApiHost,
setScanPresenter,
setSourceCommit,
setWebOrigin,
} from "@civfix/ui"
import { CARTO_API_KEY, DONATE_BROWSER_MODE, SOURCE_COMMIT, WEB_ORIGIN } from "@/config"
import { API_URL, CARTO_API_KEY, DONATE_BROWSER_MODE, SOURCE_COMMIT, WEB_ORIGIN } from "@/config"
import { nativeCamera, setCameraNavigator } from "@/lib/nativeCamera"
import { nativeCalendarFile } from "@/lib/nativeCalendarFile"
import { nativeClipboard } from "@/lib/nativeClipboard"
Expand All @@ -72,6 +73,7 @@ import { chatSocket } from "@/lib/ws"
import { usePrefsStore } from "@/store/prefsStore"
import { resolveActiveLocale } from "@/lib/locale"
import { goHome } from "@/lib/goHome"
import { adoptStorageEnvironment } from "@/lib/legacyStorageReset"
import { isExternalUrl } from "@/lib/links"
import { codeScannerSupported } from "@/lib/scannerSupport"
import {
Expand Down Expand Up @@ -178,6 +180,7 @@ async function openInAppBrowser(url: string): Promise<void> {

setWebOrigin(WEB_ORIGIN)
setSourceCommit(SOURCE_COMMIT)
setApiHost(API_URL)

const mobileCapabilities: PlatformCapabilities = {
...makeFakeCapabilities(),
Expand Down Expand Up @@ -330,7 +333,11 @@ function useBootstrap() {
setUnauthorizedHandler(() => {
useAuthStore.getState().markUnauthed()
})
void hydrate()
void adoptStorageEnvironment()
.catch(() => false)
.finally(() => {
void hydrate()
})
Comment thread
greptile-apps[bot] marked this conversation as resolved.
return () => setUnauthorizedHandler(null)
}, [hydrate])
}
Expand Down Expand Up @@ -628,7 +635,11 @@ export function ErrorBoundary({ error, retry }: ErrorBoundaryProps) {
<Text style={crash.title}>{copy.title}</Text>
<Text style={crash.body}>{copy.body}</Text>
{__DEV__ ? <Text style={crash.detail}>{String(error?.message ?? error)}</Text> : null}
<Pressable accessibilityRole="button" onPress={onRetry} style={crash.action}>
<Pressable
accessibilityRole="button"
onPress={onRetry}
style={({ pressed }) => [crash.action, pressed ? crash.actionPressed : null]}
>
<Text style={crash.actionLabel}>{copy.action}</Text>
</Pressable>
</View>
Expand Down Expand Up @@ -686,6 +697,9 @@ function crashStyles(t: Theme) {
borderRadius: t.radius.pill,
backgroundColor: t.colors.brand.bloom,
},
actionPressed: {
opacity: 0.85,
},
actionLabel: {
fontSize: t.fontSize["16"],
fontWeight: "600",
Expand Down
Loading
Loading