Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm

- name: Install dependencies
run: npm install
run: npm ci

- name: Typecheck
run: npm run typecheck
Expand Down
19 changes: 13 additions & 6 deletions .github/workflows/deploy-demo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ name: Deploy Demo

on:
workflow_dispatch:
inputs:
alias:
description: EAS Hosting alias to deploy
required: true
default: demo
type: string

jobs:
deploy-demo:
Expand All @@ -16,9 +22,10 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm

- name: Install dependencies
run: npm install
run: npm ci

- name: Typecheck
run: npm run typecheck
Expand All @@ -29,15 +36,15 @@ jobs:
- name: Web export
run: npm run build:web
env:
EXPO_PUBLIC_AUTH_REDIRECT_URL: https://arcade-radar--demo.expo.app/
EXPO_PUBLIC_AUTH_REDIRECT_URL: https://arcade-radar--${{ inputs.alias }}.expo.app/
EXPO_PUBLIC_SUPABASE_KEY: ${{ secrets.EXPO_PUBLIC_SUPABASE_KEY }}
EXPO_PUBLIC_SUPABASE_URL: ${{ secrets.EXPO_PUBLIC_SUPABASE_URL }}

- name: Deploy demo alias
run: npx eas-cli@latest deploy --alias demo --non-interactive
- name: Deploy alias
run: npx eas-cli@latest deploy --alias "${{ inputs.alias }}" --non-interactive
env:
EAS_NO_VCS: 1
EXPO_PUBLIC_AUTH_REDIRECT_URL: https://arcade-radar--demo.expo.app/
EXPO_PUBLIC_AUTH_REDIRECT_URL: https://arcade-radar--${{ inputs.alias }}.expo.app/
EXPO_PUBLIC_SUPABASE_KEY: ${{ secrets.EXPO_PUBLIC_SUPABASE_KEY }}
EXPO_PUBLIC_SUPABASE_URL: ${{ secrets.EXPO_PUBLIC_SUPABASE_URL }}
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,3 @@ yarn-error.*

expo-env.d.ts
# @end expo-cli

package-lock.json
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ Required GitHub repository secrets:
- `EXPO_PUBLIC_SUPABASE_KEY`
- `EXPO_TOKEN` for the manual EAS demo deploy workflow

The CI workflow uses `npm install` instead of `npm ci` because this project
currently ignores `package-lock.json`.
The workflows use `npm ci`, so keep `package-lock.json` committed whenever
dependencies change.

## Demo Deployment

Expand All @@ -99,6 +99,9 @@ For mixer demos, use the stable EAS alias instead of one-off deploy URLs:
npx eas-cli@latest deploy --alias demo
```

The GitHub `Deploy Demo` workflow also accepts an alias input. Leave it as
`demo` for the mixer, or use another alias such as `staging` later.

Use this URL for QR codes and Supabase Auth redirects:

```text
Expand Down
27 changes: 5 additions & 22 deletions src/app/demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import {
import { SafeAreaView } from "react-native-safe-area-context";

import { theme } from "@/constants/theme";
import {
buildDemoSearchParams,
featuredDemoSearches,
} from "@/lib/demo";
import { env } from "@/lib/env";

const demoUrl = env.authRedirectUrl || "https://arcade-radar--demo.expo.app/";
Expand All @@ -30,24 +34,6 @@ const talkingPoints = [
"The map stack avoids Google Maps Platform costs.",
];

const featuredDemoSearches = [
{
game: "Street Fighter III: 3rd Strike",
location: "75201",
title: "Fighting game run",
},
{
game: "Marvel vs. Capcom 2",
location: "75080",
title: "Rare cabinet search",
},
{
game: "DanceDanceRevolution",
location: "76051",
title: "Rhythm game search",
},
];

export default function DemoScreen() {
const { width } = useWindowDimensions();
const isWideLayout = width >= 1100;
Expand Down Expand Up @@ -89,10 +75,7 @@ export default function DemoScreen() {
key={search.title}
href={{
pathname: "/",
params: {
game: search.game,
location: search.location,
},
params: buildDemoSearchParams(search),
}}
asChild
>
Expand Down
38 changes: 38 additions & 0 deletions src/lib/demo.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { describe, expect, it } from 'vitest';

import {
buildDemoSearchParams,
buildExpoAliasUrl,
defaultDemoAlias,
featuredDemoSearches,
} from './demo';

describe('demo deployment helpers', () => {
it('builds stable EAS alias URLs', () => {
expect(buildExpoAliasUrl('demo')).toBe(
'https://arcade-radar--demo.expo.app/',
);
expect(buildExpoAliasUrl(' staging ')).toBe(
'https://arcade-radar--staging.expo.app/',
);
expect(buildExpoAliasUrl('')).toBe(
`https://arcade-radar--${defaultDemoAlias}.expo.app/`,
);
});
});

describe('featuredDemoSearches', () => {
it('contains QR-demo-ready searches with location and game params', () => {
expect(featuredDemoSearches.length).toBeGreaterThanOrEqual(3);

for (const search of featuredDemoSearches) {
expect(search.title).not.toHaveLength(0);
expect(search.game).not.toHaveLength(0);
expect(search.location).toMatch(/^\d{5}$/);
expect(buildDemoSearchParams(search)).toEqual({
game: search.game,
location: search.location,
});
}
});
});
38 changes: 38 additions & 0 deletions src/lib/demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
export interface FeaturedDemoSearch {
game: string;
location: string;
title: string;
}

export const defaultDemoAlias = 'demo';

export const featuredDemoSearches: FeaturedDemoSearch[] = [
{
game: 'Street Fighter III: 3rd Strike',
location: '75201',
title: 'Fighting game run',
},
{
game: 'Marvel vs. Capcom 2',
location: '75080',
title: 'Rare cabinet search',
},
{
game: 'DanceDanceRevolution',
location: '76051',
title: 'Rhythm game search',
},
];

export function buildExpoAliasUrl(alias: string): string {
const normalizedAlias = alias.trim() || defaultDemoAlias;

return `https://arcade-radar--${normalizedAlias}.expo.app/`;
}

export function buildDemoSearchParams(search: FeaturedDemoSearch) {
return {
game: search.game,
location: search.location,
};
}
25 changes: 25 additions & 0 deletions src/lib/scout.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { describe, expect, it } from 'vitest';

import { getScoutErrorMessage } from './scout';

describe('getScoutErrorMessage', () => {
it('returns plain string errors', () => {
expect(getScoutErrorMessage('Already submitted')).toBe('Already submitted');
});

it('prefers structured error messages', () => {
expect(getScoutErrorMessage({ message: 'Authentication required' })).toBe(
'Authentication required',
);
});

it('falls back to details when message is missing', () => {
expect(getScoutErrorMessage({ details: 'Duplicate pending report' })).toBe(
'Duplicate pending report',
);
});

it('serializes unknown objects as a last resort', () => {
expect(getScoutErrorMessage({ code: 'P0001' })).toBe('{"code":"P0001"}');
});
});
Loading