The short version
3.0 is the biggest release since the App Store launch. Same product, same no-ads, no-tracking stance — but this version closes a bunch of long-standing gaps that have been on the roadmap since day one. Weather, status banners, an anonymous server-side backup of your saved meets that powers organizer change alerts, Waze, and a complete polish pass on meet detail, the map sheet, the menu, and the login screen.
If you only read one section, read Headlines. If you want the full list, every change end users will actually notice is below, grouped by area.
And a first transparency report
Alongside this release we are publishing the April 2026 Transparency Report — a plain-language log of exactly what data the app collected this month, what changed, what we removed, who we relied on, and any government or legal request we received (this month: zero). It is the first one; we plan to publish one every month.
If a third party shows up in the user-facing render path, you will see it there. If a number on the privacy page would have changed, you will see that too. Read it alongside this changelog so the privacy-relevant items in 3.0 — Open-Meteo as a server-proxied weather source, the per-install saved-meets backup, push delivery telemetry — are framed in the same place we publish the what and the why.
Headlines
- Weather at the meet. Every meet detail page now shows the forecast for the meet's location and start time, including a precipitation chance and conditions summary. We proxy the data ourselves through a small
/api/weatherendpoint backed by Open-Meteo — see the April Transparency Report for the privacy details. - Cancellation and postponement banner. When an organizer marks a meet
cancelled,postponed, orupdated, the meet detail page now shows a high-contrast banner at the top with the reason and the time it was changed. This is the safety-critical "do not drive 45 minutes for nothing" feature, and it shows even on out-of-date native shells. - Deep links open the app. Tap a
https://detmeets.com/meet/123link in Instagram, Messages, or Mail and it opens directly in the native iOS app at that meet — cold start or warm./saved,/menu, and/mapare also supported; everything else falls through to the system browser as before. - Anonymous server-side backup of saved meets. Each install gets an opaque random identifier on first launch; saved meets and reminder choices are mirrored to the server keyed against that identifier. Two important caveats up front: the server has no idea who you are, and this is per-install, not cross-device — phone Safari, the iOS app, and your laptop browser are three different installs with three unrelated ids and three separate buckets. The real reason the backup exists is so the server can send you the meet-change push notifications below.
- Waze is in the maps picker alongside Apple Maps and Google Maps. The picker now also tells you which one will be listed first based on your preferred provider.
- Smart reminder presets. Saving a meet now offers a clean set of reminder presets (day before, hour before, time-to-leave) instead of just one fixed slot.
- "Time to head out" nudge. When a saved meet is close in time and the app is foregrounded inside the leave-now window, you'll get a one-off local notification with the live drive estimate. No background polling, no surveillance — only when the app is open.
- App version 3.0 is live in the App Store (build 19). The native shell now also has a built-in update gate, so out-of-date apps can be told to grab the new version when a new server-side feature requires it.
Meet detail
Weather card
A new card near the top of meet detail shows:
- Forecast temperature at the start time, in °F.
- Precipitation chance for the same window.
- A condition label — "Clear", "Partly cloudy", "Rain likely", "Snow possible", and so on, derived from WMO weather codes returned by Open-Meteo.
The card has its own loading and error states, and it stays out of the way (no card at all) when we don't have coordinates for the meet, when the feature is disabled by env, or when the native shell is below the minimum build. The forecast time line was simplified late in the cycle to drop the redundant hour suffix; if you remember the line saying "at 7p.m." mid-week and looking weird, that's why it doesn't anymore.
Operational status banner
If the meet has been updated, postponed, or cancelled, you now see a banner at the top of the page with:
- A bold human-readable label ("Cancelled", "Postponed", "Schedule updated").
- The organizer-supplied reason if there is one.
- The timestamp the change was made, formatted in your local time.
The banner is intentionally not gated on the native-shell minimum build — out-of-date apps and web visitors all see it. There is no version of the app where you can miss this signal because your phone is behind on updates.
Quick stats, redesigned
The "Quick stats" row at the top of meet detail was rewritten:
- Time row with start, end, and total duration formatted properly when a meet spans multiple days (e.g. "2d 4h").
- Drive estimate row with miles and minutes. On the iOS app, this calls Apple MapKit's
MKDirectionsthrough a small native bridge — same routing data Apple Maps uses, including live traffic when MapKit has it. The row also surfaces a confidence label so you know what you're looking at: live traffic, typical traffic, or estimated only. Web and Android keep a crow-flight heuristic fallback. - Hosts/organizer row with stable display labels for known hosts (Instagram, websites, Discord, etc.).
Sticky action bar
The bar at the bottom of meet detail (Save / Share / Add to calendar / Open in Maps) was rebuilt for narrow viewports — fewer overflow issues, better tap targets, and the Open in Maps button now drives the new picker described below.
Status ribbon, hero parallax, schedule rows
- A small status ribbon is layered onto the hero image when a meet is cancelled or postponed, so the page reads correctly even when scrolled past the banner.
- The hero parallax respects the system "Reduce motion" setting; on devices that ask for less motion, the image just sits still.
- The multi-day schedule rows ("Day 1: 6 p.m. – 9 p.m.", "Day 2: 10 a.m. – 4 p.m.") wrap correctly on narrow widths and cope with "Time TBD" segments.
Loading skeleton
The loading state at /meet/[id] now matches the live page layout — hero, banner placeholder, quick stats rows, sticky action bar — instead of a flat spinner, so there's no layout pop when content arrives.
Maps
Waze in the picker
When you tap Open in Maps on a phone, the picker now offers:
- Apple Maps
- Google Maps
- Waze (new)
Plus a small hint line ("Apple Maps will be listed first" / "Google Maps will be listed first" / "Waze will be listed first") based on your device default and your stored preference. We removed iOS routing-app declarations along the way to keep the App Store happy; opening into a navigation app continues to work via standard URL schemes.
Default maps app
The first time you tap Open in Maps, you're asked to pick a default — Apple Maps, Google Maps, or Waze. The choice is what drives the "X will be listed first" hint above, and it persists on-device (it also rides along with the rest of your user prefs to the server, per-install). You can change the default later from Menu, or skip the prompt entirely and the picker will keep listing every option in its standard order.
Map sheet polish
The bottom sheet on the full-screen map got a long overflow pass:
- Long descriptions and unbroken strings now wrap inside the sheet instead of pushing it sideways off the screen.
- The pin icon next to the address line is fixed-size; the venue name is what wraps if it's long.
- Co-located meet rows in the swipe carousel use proper basis/min-width so each card is exactly one screen wide.
- The map page itself loads the heavy Leaflet bundle after first paint, so the rest of the page is interactive while the tiles boot.
Map empty states
Filtered empty states ("nothing matches your filters", "map still waking up", "turn on location for Near me") are spelled out clearly so you're never staring at an empty map wondering whether it's broken.
Saved meets
Anonymous server-side backup
Saved meets and reminder choices are now mirrored to the server, anonymously, when sync is enabled. Important: this is per-install, not cross-device. Specifically:
- On first launch, each install generates an opaque random
installationIdand stores it on-device. Phone Safari, the iOS app, and your laptop browser each generate their own id — there is no flow to link them, and the server cannot tell that two installs belong to the same person. - Saved entries are keyed against that single installation identifier. Saving a meet on your phone does not make it appear in your laptop browser. If you want the same saves on two devices, save them on both for now.
- The server has no email, no username, no account, and no way to resolve the identifier back to a person.
- Sync is last-write-wins per meet (newer
savedAtIsowins) when an install pulls its own data back, mostly relevant after a reinstall on the same native shell.
The reason the backup exists is so the server knows which installs have saved a given meet, so we can send the meet-change push notifications below. The "see it on another device" use case will need a real account flow before it can work, and that is on the What's next list (sort of — see Roadmap).
Saved list
- The saved list now stores a richer snapshot of each meet alongside the entry (cover, venue, address, lat/lon, recommended/charity flags, ops status). The list still works without a network connection, and "Next saved meet" can compute drive ETA offline using the snapshot's coords.
- The past expansion (showing meets that already happened, collapsed by default) is faster and more obvious.
- Loading placeholders match the real row layout, so the list doesn't reflow when entries arrive.
Pull-to-refresh
The native pull-to-refresh was rebuilt:
- The control follows your finger smoothly with reduced motion respected.
- Haptic taps fire at the threshold and on commit.
- Snap-back is gentler if you don't pull far enough.
Reminders
Smart reminder presets
Saving a meet now offers a curated set of reminder presets instead of a single time picker:
- "Day before, evening"
- "Morning of"
- "1 hour before"
- "30 min before"
- "Time to leave" (computes from drive estimate)
"Time to leave" is dynamic, not a fixed offset. Every other preset in the list above is a known number of minutes/hours before the meet's start time, so we can schedule it once and forget it. "Time to leave" is different: it computes from your live drive ETA at fire time, not when you set the reminder, so traffic at 6 p.m. on a Friday gets the credit it deserves and the alert lands when leaving now actually means arriving on time.
You can pick any combination; we de-duplicate close-together reminders so you don't get double-pinged.
"Time to head out" nudge
If you have a saved meet starting soon and you open the app inside the leave-now window computed from your live ETA, you'll get a one-off local notification: "Time to head out — about N min drive." This is not a background poller — it only runs when you foreground the app, so there's no constant location drain. It can also be turned off entirely; see the menu.
Notification body styling
- Reminder notifications get the multi-line, emoji-led body that landed in 2.3 (countdown · time · place · address). On Android, they ride a dedicated channel with inbox-style lines and an expanded "big text" body where supported.
- Notification Center grouping (iOS). Reminders and organizer-driven meet-change pushes share a single thread identifier (
detmeets-meet-reminders), so multiple alerts for the same saved meet collapse into one row on the lock screen and in Notification Center instead of stacking up as separate noise. Tapping the group still routes you to the right meet detail page.
Home feed
- Default browse mode is now "All". The previous behavior of "restoring" your last filter chip on cold start has been dropped — opening the app should always show you the full slate first.
- "Getting your location…" state when you tap Near me with location turned on but the device hasn't returned a fix yet, so the feed isn't briefly empty.
- Search bar has slightly wider tap targets and now plays nice with the OS's autofill suggestions on mobile.
Menu
The native Menu screen was rebuilt around the actually-useful items and trimmed of dev-only ones:
- Calendar subscription actions stay (Subscribe in Calendar app + Copy feed URL).
- App and web version blocks now show app version, web content version, and a deploy SHA — handy for bug reports.
- Default maps app picker lives in Menu now — same Apple / Google / Waze choice that's surfaced first-run from the directions sheet. Change it any time.
- Haptics toggle. If you don't want the small taps on save / pull-to-refresh / gesture commit, turn them off in Menu. The setting persists on-device and rides along with the rest of your user prefs to the server, per-install.
- Cloud sync toggles surface here.
- The old Copy install ID debug clipboard action was removed for privacy hygiene; staff still have access in admin.
- The test push row was removed for the public app; staff still have it in
/admin/quality.
Native shell
A few things specific to the iOS app (and the Android shell that's not yet shipped). All of these are no-ops on the plain web.
Deep links into the app
Tapping a https://detmeets.com/... link from outside the app — Instagram, Messages, Mail, a group chat — now lands you directly in the native iOS app at the right screen instead of bouncing through Safari first.
- Cold start. If the app was closed when you tapped the link, we read it via
App.getLaunchUrl()and route to it as soon as the WebView is ready. - Warm. If the app was already running in the background, the same routing happens through
App.addListener("appUrlOpen"). - Whitelist. Only safe in-app routes are accepted:
/meet/[id],/saved,/menu, and/map. Links pointing at any other host or any other path fall through to the system browser as before. There's no way for an arbitrary URL to push you into an unexpected app screen.
Android hardware back button
On Android, the hardware back button now navigates browser history. When there's nothing left to go back to, the app minimizes instead of force-closing — so the next time you switch back to it you're where you left off, not staring at a fresh launch screen.
Foreground resume
When you bring the app to the front, four short things run in parallel — none of which run in the background:
- Any pending saved-meets cloud push is flushed, so your latest saves are mirrored before you start using the app again.
- Your anonymous backup is refreshed from the server (handy if the local store was cleared).
- The "time to head out" sweep runs once, in case a saved meet just hit its leave-now window.
- We check whether a new featured blog post needs a notification.
This is the only time any of those happen automatically. Closing the app puts everything to sleep until next foreground.
Login
The login form was trimmed down to the essentials. Username-only with a single password field. Helper text and unused legacy fields were removed. The form prefers the username autofill hint on iOS so password managers fill correctly.
Account
- Profile form polish (cleaner errors, better required-field handling).
- Password change is now a single round-trip; the must-change-password flag is cleared server-side after a successful Better Auth
change-passwordcall. No more "your password updated, but…" half-failures. - Staff sessions can be listed and revoked from the admin user dialog (admin-only; same screen since 2.x).
Permission onboarding
The first-run notification/location prompt sheet and the smart-reminder prompt are now suppressed on legal and static routes (/privacy, /terms, /support, /download). Linking someone to the privacy page no longer pops a permission sheet over the legal text — that was off-putting and the wrong moment to ask.
Open Settings deep-link
When you've previously denied location or notifications and the app needs them — say, you tapped Near me after blocking location, or you want reminders to actually fire — the affordance now opens iOS Settings directly via the app-settings: URL scheme instead of telling you to navigate Settings → Detroit Meets manually. One tap, you're there. (Android falls back to a textual hint until the package-intent path is wired up alongside the Play Store submission.)
Errors and loading
- New global error.tsx with retry and a one-line description, plus the small client-side payload that gets posted to
/api/report-client-errorso production failures land in our logs. - New not-found page with a useful path back to the home feed.
- Action-sheet fallback for non-iOS so menus that look like an iOS sheet still work on the web and on Android.
- TRPC errors are formatted with a small helper so user-facing messages are consistent. Server errors that reach the UI now read as plain English — "Please sign in to continue" / "You don't have access to do that" / "Slow down for a moment, then try again" / "We couldn't find that. It may have been removed." / "You're offline. Reconnect and try again." Raw
TRPCErrorcodes never leak through to a button or a toast.
Admin (staff only)
If you're not an organizer or staff member you won't see any of this, but for completeness:
- New Quality dashboard at
/admin/quality:- Push send-rate and failure counts.
- Meet-change alert rules with tests.
- Content quality checks (operational status freshness, schedule sanity).
- Push test card lets staff fire a single targeted test scenario at a registered device.
- Meet form dialog for inline edits without leaving the list.
- Admin app header/layout polish, and a clean Forbidden screen for non-staff who land on an admin route.
Performance and infrastructure
A representative slice — most of this is invisible:
- New composite btree index on
car_meet (published, endsAt, startsAt)so upcoming queries hit a single covering index. - New lighter
**PublicMeetListing** payload for home / map / saved screens (no agenda, no audit columns); meet detail keeps the heavierPublicMeetpayload. - Map page is dynamically imported on the client (
ssr: false). - Long meet grids only prefetch the first six meet detail routes.
- Public POST endpoints that don't require a session (
/api/push/register,/api/report-client-error) are now per-IP rate-limited. - New runtime endpoint
**/api/config/native-shell** so we can change the native-shell minimum-build threshold without rebuilding the web bundle. - New endpoints:
**/api/weather** (Open-Meteo proxy),**/api/push/opened** (delivery telemetry),**/api/meet/[id]/exists** (cheap precondition check used by the native shell). - New tRPC routers:
**meets** (organizer ops),**quality** (admin telemetry),**sync** (anonymous cloud sync). - Drizzle migrations 0007 (anonymous sync schema), 0008 (status reason length), 0009 (the new index).
- Vitest is wired up; new tests cover meet quality, push alert rules, saved-meets merge, native-shell min-build resolution, smart reminder presets, the blog featured-push client, and internal app-path utilities.
- Removed the runtime Nominatim geocoding fallback from the map render path. New meets get geocoded once at publish; map and home read directly from stored coordinates.
Privacy
A few items here that are worth highlighting in the changelog itself; for the full breakdown read the April Transparency Report.
- Open-Meteo weather proxy: weather data is fetched server-side from Open-Meteo and returned to the app. The third party only ever sees Detroit Meets's server IP, never yours. Your visit to the meet detail page is not visible to Open-Meteo.
- Anonymous installation identifier is what powers cloud sync and push registration. It's a random opaque string generated on first launch, with no contact info attached. The Privacy Policy was updated to disclose it.
- Push delivery telemetry is opt-in and aggregate — we record whether a push was opened, not what device opened it.
- Removed runtime Nominatim geocoding — see Performance section above.
- Removed
Copy install IDclipboard action from the public menu.
What's next
- Android. The Capacitor Android shell exists in the repo, the release tooling is wired up, and we're running the same polish pass we did for iOS before we submit. We will announce the Play Store listing here and in the app the day it goes live.
- Better organizer tooling. Recurrence support, co-organizer access, faster create flow, and a dashboard that does not feel like homework if you run more than one meet. No new data being collected; this is UI work on existing fields.
- Better pop-up meet support. Impromptu cruise nights and last-minute parking lot meetups are a real part of the scene. Better tooling for organizers to post them in under a minute, and better surfacing for people who want to know what is happening tonight, not next weekend.
- Offline-friendly. More of the app working when you have no signal in a parking structure or a basement venue. The richer saved-meet snapshots that landed in 3.0 are step one; full offline meet detail and map-tile caching are next.
- Real cross-device sync for saved meets. 3.0 ships an anonymous server-side backup that is per-install — your phone, the iOS app, and your laptop browser are three separate buckets, and there is no flow to link them. The next pass adds an actual way to share a saved-meets list across devices (likely an opt-in pairing code rather than a full account, to keep the no-signup posture). Until that ships, treat saves as device-local.
- Map tile provider naming. We finalized the migration off third-party Mapbox tiles in April; we'll commit to the new tile provider in writing in next month's transparency report.
- Woodward activity tracking. Still the feature I want most, and the one I will not ship half-baked. Live signal for how active Woodward is on a given night so you can tell whether it is worth the drive before you commit. Getting closer.
- Photo spot finder. Surfacing good Michigan shoot locations — lighting, backdrop, space, noise-complaint risk — without making you dig through buried Reddit threads. For photographers and owners who are tired of guessing.
- More stuff I'm not ready to announce yet. Same rule as last time — I'd rather ship it than hype it. It'll show up when it's ready.
Thank you
3.0 took a lot of late nights, a lot of "wait, is this actually ready" debates with a few patient people, and a lot of bug reports from organizers who tolerated half-broken weather cards while we got the proxy right. If you sent feedback at any point in the last six weeks, you are part of this version.
Detroit Meets on the App Store · April 2026 Transparency Report · @alex.30mm on Instagram