ReviewOS

also looking at this

stacks/wildloop

feat: native trail maps and complete native mobile screens

#1017
Draft chris wants to merge codex/trail-experience into main
60 files +2505 -823

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

tests/unit/native-trail-detail-screen.test.tsmodified+21-0
Changes to tests/unit/native-trail-detail-screen.test.ts
@@ -214,6 +214,27 @@ describe('native trail details', () => {
214214 expect(nativeNodeProps(screen, 'trail-review-composer').visible).toBe(false)
215215 expect(nativeNodeProps(screen, 'trail-review-content').value).toBe('')
216216 })
217 it('does not save a trail using stale account credentials', async () => {
218 const keychain = new Map([['auth_token', 'test_token']])
219 const screen = await open(true, TRAIL.geometry, [OWN], OWN, 200, keychain)
220 keychain.set('auth_token', 'replacement-token')
221 const before = requests(screen).length
222 await screen.event('trail-save', 'onPress')
223 expect(requests(screen).slice(before).filter(r => r.payload.module === 'Network' && ['PUT','DELETE'].includes(r.payload.args[0].method))).toHaveLength(0)
224 await waitForText(screen, 'Your account changed. Please try again.')
225 })
226 it('ignores a photo-picker result from the previous account', async () => {
227 const screen = await open()
228 await screen.event('trail-write-review', 'onPress')
229 const before = requests(screen).length
230 await screen.event('trail-review-add-photo', 'onPress')
231 const picker = await waitForRequest(screen, before, r => r.payload.module === 'Files')
232 await screen.scope.craft.snapshots.set('wildloop-session', {user:{id:13},version:2})
233 const beforeResponse = screen.sent.length
234 answer(screen, picker, {path:'/tmp/original-account-photo.jpg',name:'original-account-photo.jpg',type:'image/jpeg',size:20000})
235 await Bun.sleep(0)
236 expect(JSON.stringify(screen.sent.slice(beforeResponse))).not.toContain('original-account-photo.jpg')
237 })
217238 it('shows the trailhead for trails without recorded geometry', async () => {
218239 const screen = await open(false, null)
219240 expect(shownText(screen)).toContain('No recorded route line. The map shows the trailhead.')