Skip to content

Commit

Permalink
feat: replace vercel with github pages
Browse files Browse the repository at this point in the history
  • Loading branch information
lozinsky committed Jul 23, 2024
1 parent f3779d0 commit c27b432
Show file tree
Hide file tree
Showing 32 changed files with 1,383 additions and 3,099 deletions.
1 change: 0 additions & 1 deletion .env.example

This file was deleted.

49 changes: 49 additions & 0 deletions .github/workflows/build-and-pages-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Build and pages deploy

on:
workflow_run:
workflows:
- Lint and test
types:
- completed

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: 'pages-deploy'
cancel-in-progress: true

jobs:
build-and-pages-deploy:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
name: Build and pages deploy
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deploy.outputs.page_url }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Prepare
uses: ./.github/actions/prepare

- name: Build
run: npm run build
env:
PUBLIC_BASE_PATH: /${{ github.event.repository.name }}/

- name: Setup pages
uses: actions/configure-pages@v5

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: './build/client/'

- name: Pages deploy
id: deploy
uses: actions/deploy-pages@v4
37 changes: 0 additions & 37 deletions .github/workflows/build-and-vercel-deploy.yml

This file was deleted.

3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,3 @@ npm-debug.log

.cache/
build/

.vercel
.env
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Binairo · [![Lint and test](https://github.com/lozinsky/binairo/actions/workflows/lint-and-test.yml/badge.svg)](https://github.com/lozinsky/binairo/actions/workflows/lint-and-test.yml) [![Build and vercel deploy](https://github.com/lozinsky/binairo/actions/workflows/build-and-vercel-deploy.yml/badge.svg)](https://github.com/lozinsky/binairo/actions/workflows/build-and-vercel-deploy.yml)
# Binairo · [![Lint and test](https://github.com/lozinsky/binairo/actions/workflows/lint-and-test.yml/badge.svg)](https://github.com/lozinsky/binairo/actions/workflows/lint-and-test.yml) [![Build and pages deploy](https://github.com/lozinsky/binairo/actions/workflows/build-and-pages-deploy.yml/badge.svg)](https://github.com/lozinsky/binairo/actions/workflows/build-and-pages-deploy.yml)

Binairo, also known as Takuzu, is a logic puzzle involving placement of two symbols, often 1s and 0s, on a rectangular grid. The objective is to fill the grid with 1s and 0s, where there is an equal number of 1s and 0s in each row and column and no more than two of either number adjacent to each other. Additionally, there can be no identical rows or columns. Similar to Sudoku, each puzzle begins with several squares in the grid already filled.

## Features

- fully playable with disabled javascript
- fully playable with disabled javascript (if `ssr` enabled but currently not)
- saves your progress in cookies
- supports from 4 to 12 board sizes
- has a dark mode
Expand Down
20 changes: 17 additions & 3 deletions app/globals.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
import 'core-js/modules/es.array.to-reversed';
import 'core-js/modules/es.array.with';
import 'core-js/modules/web.immediate';
import { type TransferHandler, transferHandlers } from 'comlink';

import { Board, type BoardValue } from '~/lib/board';

const boardTransferHandler: TransferHandler<Board, BoardValue> = {
canHandle(value) {
return value instanceof Board;
},
deserialize(value) {
return Board.from(value);
},
serialize(value) {
return [value.valueOf(), []];
},
};

transferHandlers.set('Board', boardTransferHandler);
4 changes: 2 additions & 2 deletions app/lib/board-generator/generate-board.bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const cases: ReadonlyArray<readonly [number, number]> = [
];

for (const [size, progress] of cases) {
bench(`generates ${size} board`, async () => {
await generateBoard(size, progress, Random.stable());
bench(`generates ${size} board`, () => {
generateBoard(size, progress, Random.stable());
});
}
4 changes: 1 addition & 3 deletions app/lib/board-generator/generate-board.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/* eslint-disable @typescript-eslint/no-floating-promises */

import { expect, test } from 'vitest';

import { Random } from '~/shared/random';
Expand All @@ -13,5 +11,5 @@ test.each([
[10, 0.6],
[12, 0.6],
])('generates board', (size, progress) => {
expect(generateBoard(size, progress, Random.stable())).resolves.toMatchSnapshot();
expect(generateBoard(size, progress, Random.stable())).toMatchSnapshot();
});
5 changes: 1 addition & 4 deletions app/lib/board-generator/generate-board.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
import { MatrixSelection } from '~/lib/matrix';
import { expectToBeDefined } from '~/shared/expect';
import { type Random, shuffle } from '~/shared/random';
import { type Abortable, setImmediate } from '~/shared/timers';

import { generateBoardLines } from './generate-board-lines';

Expand All @@ -27,7 +26,7 @@ function isValidBoard(target: Board) {
return true;
}

export async function generateBoard(size: number, progress: number, random: Random, options?: Abortable) {
export function generateBoard(size: number, progress: number, random: Random) {
const lines = generateBoardLines(size);
const combinations = shuffle(lines, random);

Expand Down Expand Up @@ -58,8 +57,6 @@ export async function generateBoard(size: number, progress: number, random: Rand
board = Board.blank(size);
index = 0;
attempt = 0;

await setImmediate(undefined, options);
}
}
} while (index < size);
Expand Down
105 changes: 55 additions & 50 deletions app/root.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { type LinkDescriptor, type SerializeFrom } from '@remix-run/node';
import {
Links,
Meta,
type MetaArgs_SingleFetch as MetaArgs,
type MetaDescriptor,
Outlet,
Scripts,
ScrollRestoration,
type ShouldRevalidateFunctionArgs,
useLoaderData,
} from '@remix-run/react';
import { Analytics } from '@vercel/analytics/react';
import { type LinkDescriptor, type MetaDescriptor, unstable_defineLoader as defineLoader } from '@vercel/remix';
import { type ReactNode } from 'react';
import { IntlProvider } from 'react-intl';

import '~/globals';
Expand All @@ -18,48 +18,41 @@ import { RootLayout } from '~/components/ui/root-layout';
import { RootLayoutContent } from '~/components/ui/root-layout-content';
import { RootLayoutHeader } from '~/components/ui/root-layout-header';
import { RandomSeedContext } from '~/hooks/use-random';
import { getAppearance } from '~/services/appearance.server';
import { type Messages } from '~/services/intl';
import { getIntl } from '~/services/intl.server';
import { getSession } from '~/services/session.server';
import { getErrorResponse } from '~/shared/http';
import { DEFAULT_APPEARANCE } from '~/services/appearance';
import { getAppearance } from '~/services/appearance.client';
import { DEFAULT_LOCALE, type Messages } from '~/services/intl';
import { getIntl } from '~/services/intl.client';
import { getSession } from '~/services/session.client';
import { Random } from '~/shared/random';

import root from './root.css?url';

export const config = {
runtime: 'edge',
};

export function shouldRevalidate({ defaultShouldRevalidate, formAction }: ShouldRevalidateFunctionArgs) {
if (formAction?.startsWith('/settings')) {
if (formAction?.startsWith(`${import.meta.env.BASE_URL}settings`)) {
return defaultShouldRevalidate;
}

return false;
}

export const loader = defineLoader(async ({ request }) => {
try {
const session = await getSession(request);
const appearance = getAppearance(session);
const intl = getIntl(session, request.headers);
const title = intl.formatMessage({ id: 'metaTitle' });
const description = intl.formatMessage({ id: 'metaDescription' });
const random = Random.create();
export async function clientLoader() {
const session = await getSession(document.cookie);
const appearance = getAppearance(session);
const intl = await getIntl(session);
const title = intl.formatMessage({ id: 'metaTitle' });
const description = intl.formatMessage({ id: 'metaDescription' });
const random = Random.create();

return {
appearance,
intl: { locale: intl.locale, messages: intl.messages as Messages },
meta: { description, title },
random: { seed: random.seed },
};
} catch (error) {
throw getErrorResponse(error);
}
});
return {
appearance,
locale: intl.locale,
messages: intl.messages as Messages,
meta: { description, title },
seed: random.seed,
};
}

export function meta({ data }: MetaArgs<typeof loader>): MetaDescriptor[] {
export function meta({ data }: { data?: SerializeFrom<typeof clientLoader> }): MetaDescriptor[] {
if (data === undefined) {
return [];
}
Expand All @@ -69,40 +62,52 @@ export function meta({ data }: MetaArgs<typeof loader>): MetaDescriptor[] {

export function links(): LinkDescriptor[] {
return [
{ href: '/manifest.webmanifest', rel: 'manifest' },
{ href: '/favicon.ico', rel: 'icon', sizes: '64x64' },
{ href: '/favicon.svg', rel: 'icon', type: 'image/svg+xml' },
{ href: '/apple-touch-icon.png', rel: 'apple-touch-icon' },
{ href: `${import.meta.env.BASE_URL}manifest.webmanifest`, rel: 'manifest' },
{ href: `${import.meta.env.BASE_URL}favicon.ico`, rel: 'icon', sizes: '64x64' },
{ href: `${import.meta.env.BASE_URL}favicon.svg`, rel: 'icon', type: 'image/svg+xml' },
{ href: `${import.meta.env.BASE_URL}apple-touch-icon.png`, rel: 'apple-touch-icon' },
{ href: root, rel: 'stylesheet' },
];
}

export default function Root() {
const { appearance, intl, random } = useLoaderData<typeof loader>();
export function Layout({ children }: { children: ReactNode }) {
const data = useLoaderData() as SerializeFrom<typeof clientLoader> | undefined;
const { appearance, locale } = data ?? { appearance: DEFAULT_APPEARANCE, locale: DEFAULT_LOCALE };

return (
<html data-appearance={appearance} lang={intl.locale}>
<html data-appearance={appearance} lang={locale}>
<head>
<meta charSet='utf-8' />
<meta content='width=device-width, initial-scale=1' name='viewport' />
<Meta />
<Links />
</head>
<body className='h-dvh bg-base-100 text-base-content'>
<IntlProvider locale={intl.locale} messages={intl.messages}>
<RandomSeedContext.Provider value={random.seed}>
<RootLayout>
<RootLayoutHeader />
<RootLayoutContent>
<Outlet />
</RootLayoutContent>
</RootLayout>
</RandomSeedContext.Provider>
</IntlProvider>
{children}
<ScrollRestoration />
<Scripts />
<Analytics debug={false} />
</body>
</html>
);
}

export function HydrateFallback() {
return null;
}

export default function Root() {
const { locale, messages, seed } = useLoaderData<typeof clientLoader>();

return (
<IntlProvider locale={locale} messages={messages}>
<RandomSeedContext.Provider value={seed}>
<RootLayout>
<RootLayoutHeader />
<RootLayoutContent>
<Outlet />
</RootLayoutContent>
</RootLayout>
</RandomSeedContext.Provider>
</IntlProvider>
);
}
26 changes: 10 additions & 16 deletions app/routes/_index/route.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,25 @@
import { useLoaderData } from '@remix-run/react';
import { unstable_defineLoader as defineLoader } from '@vercel/remix';
import { FormattedMessage } from 'react-intl';

import { BrandLogo } from '~/components/ui/brand-logo';
import { ButtonLink } from '~/components/ui/button-link';
import { Menu } from '~/components/ui/menu';
import { MenuGroup } from '~/components/ui/menu-group';
import { MenuItem } from '~/components/ui/menu-item';
import { getGame } from '~/services/game.server';
import { getSession } from '~/services/session.server';
import { getErrorResponse } from '~/shared/http';
import { getGame } from '~/services/game.client';
import { getSession } from '~/services/session.client';

export const loader = defineLoader(async ({ request }) => {
try {
const session = await getSession(request);
const game = getGame(session);
export async function clientLoader() {
const session = await getSession(document.cookie);
const game = getGame(session);

return {
board: game === null ? null : game.board.toString(),
};
} catch (error) {
throw getErrorResponse(error);
}
});
return {
board: game === null ? null : game.board.toString(),
};
}

export default function Route() {
const { board } = useLoaderData<typeof loader>();
const { board } = useLoaderData<typeof clientLoader>();

return (
<Menu>
Expand Down
Loading

0 comments on commit c27b432

Please sign in to comment.