Skip to content
Open
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
9 changes: 9 additions & 0 deletions packages/backend/.env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Generic configuration for backend development
# This file is tracked in git and should NOT contain production secrets

PORT=8080
DATABASE_URL=postgresql://postgres:password@db.localtest.me:5432/main
REDIS_URL=redis://localhost:6379

# Must match the value in packages/web/.env.local so auth token validation works
NEXTAUTH_SECRET=68cJgCDE39gaXwi8LTVW4WioyhGxwcAd
2 changes: 1 addition & 1 deletion packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"main": "dist/index.js",
"scripts": {
"dev": "NODE_ENV=development DOTENV_CONFIG_PATH=.env.local tsx watch src/index.ts",
"dev": "NODE_ENV=development DOTENV_CONFIG_PATH=.env.development tsx watch src/index.ts",
"build": "tsc",
"typecheck": "tsc --noEmit",
"start": "tsx src/index.ts",
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { ParsedBoardRouteParameters, BoardRouteParameters, BoardDetails } from '
import { parseBoardRouteParams, constructClimbListWithSlugs } from '@/app/lib/url-utils';
import { parseBoardRouteParamsWithSlugs } from '@/app/lib/url-utils.server';
import { permanentRedirect } from 'next/navigation';
import QueueControlBar from '@/app/components/queue-control/queue-control-bar';
import { getBoardDetails } from '@/app/lib/__generated__/product-sizes-data';
import { getMoonBoardDetails } from '@/app/lib/moonboard-config';
import BoardSeshHeader from '@/app/components/board-page/header';
Expand All @@ -14,14 +13,12 @@ import { PartyProvider } from '@/app/components/party-manager/party-context';
import { BoardSessionBridge } from '@/app/components/persistent-session';
import { Metadata } from 'next';
import BoardPageSkeleton from '@/app/components/board-page/board-page-skeleton';
import BottomTabBar from '@/app/components/bottom-tab-bar/bottom-tab-bar';
import { BluetoothProvider } from '@/app/components/board-bluetooth-control/bluetooth-context';
import { UISearchParamsProvider } from '@/app/components/queue-control/ui-searchparams-provider';
import { QueueBridgeInjector } from '@/app/components/queue-control/queue-bridge-context';
import LastUsedBoardTracker from '@/app/components/board-page/last-used-board-tracker';
import { themeTokens } from '@/app/theme/theme-config';
import { getAllBoardConfigs } from '@/app/lib/server-board-configs';
import { BoardRouteBottomBarRegistrar } from '@/app/components/bottom-tab-bar/board-route-bottom-bar-context';
import layoutStyles from './layout.module.css';

// Helper to get board details for any board type
function getBoardDetailsUniversal(parsedParams: ParsedBoardRouteParameters): BoardDetails {
Expand Down Expand Up @@ -167,7 +164,6 @@ export default async function BoardLayout(props: PropsWithChildren<BoardLayoutPr

return (
<div style={{ minHeight: '100dvh', display: 'flex', flexDirection: 'column', padding: 0, background: 'var(--semantic-surface)' }}>
<BoardRouteBottomBarRegistrar />
<LastUsedBoardTracker
url={listUrl}
boardName={boardDetails.board_name}
Expand All @@ -183,6 +179,7 @@ export default async function BoardLayout(props: PropsWithChildren<BoardLayoutPr
<PartyProvider>
<BluetoothProvider boardDetails={boardDetails}>
<UISearchParamsProvider>
<QueueBridgeInjector boardDetails={boardDetails} angle={angle} />
<BoardSeshHeader boardDetails={boardDetails} angle={angle} boardConfigs={boardConfigs} />

<main
Expand All @@ -199,11 +196,6 @@ export default async function BoardLayout(props: PropsWithChildren<BoardLayoutPr
{children}
</Suspense>
</main>

<div className={layoutStyles.bottomBarWrapper} data-testid="bottom-bar-wrapper">
<QueueControlBar boardDetails={boardDetails} angle={angle} />
<BottomTabBar boardDetails={boardDetails} angle={angle} boardConfigs={boardConfigs} />
</div>
</UISearchParamsProvider>
</BluetoothProvider>
</PartyProvider>
Expand Down
9 changes: 0 additions & 9 deletions packages/web/app/b/[board_slug]/[angle]/layout.module.css

This file was deleted.

12 changes: 2 additions & 10 deletions packages/web/app/b/[board_slug]/[angle]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,19 @@ import { resolveBoardBySlug, boardToRouteParams } from '@/app/lib/board-slug-uti
import { getBoardDetails } from '@/app/lib/__generated__/product-sizes-data';
import { getMoonBoardDetails } from '@/app/lib/moonboard-config';
import { ParsedBoardRouteParameters, BoardDetails } from '@/app/lib/types';
import QueueControlBar from '@/app/components/queue-control/queue-control-bar';
import BoardSeshHeader from '@/app/components/board-page/header';
import { GraphQLQueueProvider } from '@/app/components/graphql-queue';
import { ConnectionSettingsProvider } from '@/app/components/connection-manager/connection-settings-context';
import { PartyProvider } from '@/app/components/party-manager/party-context';
import { BoardSessionBridge } from '@/app/components/persistent-session';
import BoardPageSkeleton from '@/app/components/board-page/board-page-skeleton';
import BottomTabBar from '@/app/components/bottom-tab-bar/bottom-tab-bar';
import { BluetoothProvider } from '@/app/components/board-bluetooth-control/bluetooth-context';
import { UISearchParamsProvider } from '@/app/components/queue-control/ui-searchparams-provider';
import { BoardProvider } from '@/app/components/board-provider/board-provider-context';
import { QueueBridgeInjector } from '@/app/components/queue-control/queue-bridge-context';
import LastUsedBoardTracker from '@/app/components/board-page/last-used-board-tracker';
import { getAllBoardConfigs } from '@/app/lib/server-board-configs';
import { constructBoardSlugListUrl } from '@/app/lib/url-utils';
import { BoardRouteBottomBarRegistrar } from '@/app/components/bottom-tab-bar/board-route-bottom-bar-context';
import layoutStyles from './layout.module.css';
import { themeTokens } from '@/app/theme/theme-config';

interface BoardSlugRouteParams {
Expand Down Expand Up @@ -77,7 +74,6 @@ export default async function BoardSlugLayout(props: PropsWithChildren<{ params:

return (
<div style={{ minHeight: '100dvh', display: 'flex', flexDirection: 'column', padding: 0, background: 'var(--semantic-surface)' }}>
<BoardRouteBottomBarRegistrar />
<LastUsedBoardTracker
url={listUrl}
boardName={boardDetails.board_name}
Expand All @@ -94,6 +90,7 @@ export default async function BoardSlugLayout(props: PropsWithChildren<{ params:
<PartyProvider>
<BluetoothProvider boardDetails={boardDetails}>
<UISearchParamsProvider>
<QueueBridgeInjector boardDetails={boardDetails} angle={angle} />
<BoardSeshHeader boardDetails={boardDetails} angle={angle} boardConfigs={boardConfigs} isAngleAdjustable={board.isAngleAdjustable} />

<main
Expand All @@ -110,11 +107,6 @@ export default async function BoardSlugLayout(props: PropsWithChildren<{ params:
{children}
</Suspense>
</main>

<div className={layoutStyles.bottomBarWrapper} data-testid="bottom-bar-wrapper">
<QueueControlBar boardDetails={boardDetails} angle={angle} />
<BottomTabBar boardDetails={boardDetails} angle={angle} boardConfigs={boardConfigs} />
</div>
</UISearchParamsProvider>
</BluetoothProvider>
</PartyProvider>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
.bottomBarWrapper {
position: fixed;
bottom: 0;
left: 0;
right: 0;
left: 2dvw;
right: 2dvw;
z-index: 10;
background: none;
border-radius: 16px;
}

@media (min-width: 768px) {
.bottomBarWrapper {
left: 0;
right: 0;
padding-bottom: 16px;
border-radius: 0;
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/web/app/components/graphql-queue/QueueContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const GraphQLQueueProvider = ({ parsedParams, boardDetails, children, bas
// On mount, the reducer starts with empty state. The sync effect must not
// write to persistent session until restoration (from memory or IndexedDB)
// has finished — otherwise it overwrites valid data with empty state,
// causing PersistentQueueControlBar to see an empty queue and unmount.
// causing the queue bridge to see an empty queue and hide the control bar.
// Uses useState (not useRef) so the sync effect only sees hasRestored=true
// in the render where state.queue already contains the restored data.
// With a ref, the restore and sync effects run in the same render cycle:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
import React from 'react';
import { PartyProfileProvider } from '../party-manager/party-profile-context';
import { PersistentSessionProvider } from '../persistent-session';
import PersistentQueueControlBar from '../queue-control/persistent-queue-control-bar';
import { QueueBridgeProvider, useQueueBridgeBoardInfo } from '../queue-control/queue-bridge-context';
import QueueControlBar from '../queue-control/queue-control-bar';
import BottomTabBar from '../bottom-tab-bar/bottom-tab-bar';
import { BoardRouteBottomBarProvider, useBoardRouteBottomBar } from '../bottom-tab-bar/board-route-bottom-bar-context';
import { BoardProvider } from '../board-provider/board-provider-context';
import { ConnectionSettingsProvider } from '../connection-manager/connection-settings-context';
import { BluetoothProvider } from '../board-bluetooth-control/bluetooth-context';
import ErrorBoundary from '../error-boundary';
import bottomBarStyles from '../bottom-tab-bar/bottom-bar-wrapper.module.css';
import { BoardConfigData } from '@/app/lib/server-board-configs';
Expand All @@ -19,38 +22,43 @@ interface PersistentSessionWrapperProps {
* Root-level wrapper that provides:
* 1. PartyProfileProvider - user profile from IndexedDB and NextAuth session
* 2. PersistentSessionProvider - WebSocket connection management that persists across navigation
* 3. BoardRouteBottomBarProvider - tracks whether a board route has its own bottom bar
* 4. RootBottomBar - persistent queue control bar + bottom tab bar on all non-board pages
* 3. QueueBridgeProvider - bridges queue context from board routes to the persistent bottom bar
* 4. RootBottomBar - always-rendered queue control bar + bottom tab bar
*/
export default function PersistentSessionWrapper({ children, boardConfigs }: PersistentSessionWrapperProps) {
return (
<PartyProfileProvider>
<PersistentSessionProvider>
<BoardRouteBottomBarProvider>
<QueueBridgeProvider>
{children}
<RootBottomBar boardConfigs={boardConfigs} />
</BoardRouteBottomBarProvider>
</QueueBridgeProvider>
</PersistentSessionProvider>
</PartyProfileProvider>
);
}

/**
* Persistent bottom bar rendered at the root level.
* Hides itself when a board route registers its own bottom bar.
* Always renders — the QueueBridge provides queue context from whichever provider is active.
* QueueControlBar is only shown when there is an active queue (board details available).
*/
function RootBottomBar({ boardConfigs }: { boardConfigs: BoardConfigData }) {
const { hasBoardRouteBottomBar } = useBoardRouteBottomBar();

if (hasBoardRouteBottomBar) {
return null;
}
const { boardDetails, angle, hasActiveQueue } = useQueueBridgeBoardInfo();

return (
<div className={bottomBarStyles.bottomBarWrapper}>
<ErrorBoundary>
<PersistentQueueControlBar />
</ErrorBoundary>
{hasActiveQueue && boardDetails && (
<ErrorBoundary>
<BoardProvider boardName={boardDetails.board_name}>
<ConnectionSettingsProvider>
<BluetoothProvider boardDetails={boardDetails}>
<QueueControlBar boardDetails={boardDetails} angle={angle} />
</BluetoothProvider>
</ConnectionSettingsProvider>
</BoardProvider>
</ErrorBoundary>
)}
<BottomTabBar boardConfigs={boardConfigs} />
</div>
);
Expand Down
Loading
Loading