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
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ import {
parseBoardRouteParams,
} from '@/app/lib/url-utils';
import { parseBoardRouteParamsWithSlugs } from '@/app/lib/url-utils.server';
import {
getLayoutById,
MOONBOARD_SETS,
MOONBOARD_SIZE,
MoonBoardLayoutKey,
} from '@/app/lib/moonboard-config';
import { convertLitUpHoldsStringToMap } from '@/app/components/board-renderer/util';
import ClimbViewActions from '@/app/components/climb-view/climb-view-actions';
import { Metadata } from 'next';
Expand Down Expand Up @@ -105,31 +111,55 @@ export default async function DynamicResultsPage(props: { params: Promise<BoardR
// Need to redirect to new slug-based URL
const currentClimb = await getClimb(parsedParams);

// Get the names for slug generation
const layouts = await import('@/app/lib/data/queries').then((m) => m.getLayouts(parsedParams.board_name));
const sizes = await import('@/app/lib/data/queries').then((m) =>
m.getSizes(parsedParams.board_name, parsedParams.layout_id),
);
const sets = await import('@/app/lib/data/queries').then((m) =>
m.getSets(parsedParams.board_name, parsedParams.layout_id, parsedParams.size_id),
);

const layout = layouts.find((l) => l.id === parsedParams.layout_id);
const size = sizes.find((s) => s.id === parsedParams.size_id);
const selectedSets = sets.filter((s) => parsedParams.set_ids.includes(s.id));

if (layout && size && selectedSets.length > 0) {
const newUrl = constructClimbViewUrlWithSlugs(
parsedParams.board_name,
layout.name,
size.name,
size.description,
selectedSets.map((s) => s.name),
parsedParams.angle,
parsedParams.climb_uuid,
currentClimb.name,
if (parsedParams.board_name === 'moonboard') {
// MoonBoard uses static config instead of generated product-sizes data
const layoutEntry = getLayoutById(parsedParams.layout_id);
if (layoutEntry) {
const [layoutKey, layoutData] = layoutEntry;
const sets = MOONBOARD_SETS[layoutKey as MoonBoardLayoutKey] || [];
const selectedSets = sets.filter((s) => parsedParams.set_ids.includes(s.id));

if (selectedSets.length > 0) {
const newUrl = constructClimbViewUrlWithSlugs(
parsedParams.board_name,
layoutData.name,
MOONBOARD_SIZE.name,
MOONBOARD_SIZE.description,
selectedSets.map((s) => s.name),
parsedParams.angle,
parsedParams.climb_uuid,
currentClimb.name,
);
permanentRedirect(newUrl);
}
}
} else {
// Aurora boards: get names from generated data for slug generation
const layouts = await import('@/app/lib/data/queries').then((m) => m.getLayouts(parsedParams.board_name));
const sizes = await import('@/app/lib/data/queries').then((m) =>
m.getSizes(parsedParams.board_name, parsedParams.layout_id),
);
const sets = await import('@/app/lib/data/queries').then((m) =>
m.getSets(parsedParams.board_name, parsedParams.layout_id, parsedParams.size_id),
);
permanentRedirect(newUrl);

const layout = layouts.find((l) => l.id === parsedParams.layout_id);
const size = sizes.find((s) => s.id === parsedParams.size_id);
const selectedSets = sets.filter((s) => parsedParams.set_ids.includes(s.id));

if (layout && size && selectedSets.length > 0) {
const newUrl = constructClimbViewUrlWithSlugs(
parsedParams.board_name,
layout.name,
size.name,
size.description,
selectedSets.map((s) => s.name),
parsedParams.angle,
parsedParams.climb_uuid,
currentClimb.name,
);
permanentRedirect(newUrl);
}
}
}
// Fetch beta links server-side
Expand Down Expand Up @@ -179,11 +209,14 @@ export default async function DynamicResultsPage(props: { params: Promise<BoardR
litUpHoldsMap,
};

const auroraAppUrl = constructClimbInfoUrl(
boardDetails,
currentClimb.uuid,
currentClimb.angle || parsedParams.angle,
);
// MoonBoard doesn't have an Aurora-style app URL
const auroraAppUrl = parsedParams.board_name !== 'moonboard'
? constructClimbInfoUrl(
boardDetails,
currentClimb.uuid,
currentClimb.angle || parsedParams.angle,
)
: undefined;

return (
<div className={styles.pageContainer}>
Expand Down
7 changes: 2 additions & 5 deletions packages/web/app/components/climb-card/climb-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,14 @@ function ClimbCardWithActions({
const cover = <ClimbCardCover climb={climb} boardDetails={boardDetails} onClick={onCoverClick} onDoubleClick={onCoverDoubleClick} />;
const cardTitle = <ClimbTitle climb={climb} layout="horizontal" showSetterInfo />;

// Build exclude list - MoonBoard doesn't have a view details page yet
const excludeActions: ('tick' | 'openInApp' | 'mirror' | 'share' | 'addToList' | 'viewDetails')[] = [
// Build exclude list
const excludeActions: ('tick' | 'openInApp' | 'mirror' | 'share' | 'addToList')[] = [
'tick',
'openInApp',
'mirror',
'share',
'addToList',
];
if (boardDetails.board_name === 'moonboard') {
excludeActions.push('viewDetails');
}

return (
<div data-testid="climb-card">
Expand Down
17 changes: 14 additions & 3 deletions packages/web/app/components/climb-view/climb-view-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import { ClimbActions } from '../climb-actions';
type ClimbViewActionsProps = {
climb: Climb;
boardDetails: BoardDetails;
auroraAppUrl: string;
auroraAppUrl?: string;
angle: number;
};

const ClimbViewActions = ({ climb, boardDetails, auroraAppUrl, angle }: ClimbViewActionsProps) => {
const isMoonBoard = boardDetails.board_name === 'moonboard';

const getBackToListUrl = () => {
const { board_name, layout_name, size_name, size_description, set_names } = boardDetails;

Expand All @@ -27,6 +29,15 @@ const ClimbViewActions = ({ climb, boardDetails, auroraAppUrl, angle }: ClimbVie
return `/${board_name}/${boardDetails.layout_id}/${boardDetails.size_id}/${boardDetails.set_ids.join(',')}/${angle}/list`;
};

// MoonBoard doesn't have an Aurora app, so exclude 'openInApp'
const mobileDropdownActions: ('tick' | 'share' | 'addToList' | 'openInApp')[] = isMoonBoard
? ['tick', 'share', 'addToList']
: ['tick', 'share', 'addToList', 'openInApp'];

const desktopActions: ('favorite' | 'tick' | 'queue' | 'share' | 'addToList' | 'openInApp')[] = isMoonBoard
? ['favorite', 'tick', 'queue', 'share', 'addToList']
: ['favorite', 'tick', 'queue', 'share', 'addToList', 'openInApp'];

return (
<div className={styles.container}>
{/* Mobile view: Show back button + key actions + overflow menu */}
Expand All @@ -50,7 +61,7 @@ const ClimbViewActions = ({ climb, boardDetails, auroraAppUrl, angle }: ClimbVie
boardDetails={boardDetails}
angle={angle}
viewMode="dropdown"
include={['tick', 'share', 'addToList', 'openInApp']}
include={mobileDropdownActions}
auroraAppUrl={auroraAppUrl}
/>
</div>
Expand All @@ -66,7 +77,7 @@ const ClimbViewActions = ({ climb, boardDetails, auroraAppUrl, angle }: ClimbVie
boardDetails={boardDetails}
angle={angle}
viewMode="button"
include={['favorite', 'tick', 'queue', 'share', 'addToList', 'openInApp']}
include={desktopActions}
auroraAppUrl={auroraAppUrl}
/>
</div>
Expand Down
11 changes: 7 additions & 4 deletions packages/web/app/lib/data/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ import {
} from '@/app/lib/__generated__/product-sizes-data';

export const getClimb = async (params: ParsedBoardRouteParametersWithUuid): Promise<Climb> => {
// Get hardcoded size edges (eliminates database query)
const sizeEdges = getSizeEdges(params.board_name, params.size_id);
if (!sizeEdges) {
throw new Error(`Invalid size_id ${params.size_id} for board ${params.board_name}`);
// MoonBoard uses grid-based rendering with a fixed size, so it has no entries in PRODUCT_SIZES.
// Skip the size edges validation for MoonBoard.
if (params.board_name !== 'moonboard') {
const sizeEdges = getSizeEdges(params.board_name, params.size_id);
if (!sizeEdges) {
throw new Error(`Invalid size_id ${params.size_id} for board ${params.board_name}`);
}
}

const result = await sql`
Expand Down
Loading