Skip to content
2 changes: 1 addition & 1 deletion apps/app/app/+native-intent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function redirectSystemPath({ path }: { path: string; initial: boolean })
if (webFromUrl) return `/?web=${encodeURIComponent(webFromUrl)}`;

try {
/** share extension → openHostApp(`/?web=${encodeURIComponent('/archive?tab=wish')}`) */
/** share extension → openHostApp(`/?web=${encodeURIComponent('/archive/wish')}`) */
const url = new URL(path, 'piki://app');
const web = url.searchParams.get('web');

Expand Down
2 changes: 1 addition & 1 deletion apps/app/components/ShareBottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function ShareBottomSheetContent({ url, text }: ShareExtensionProps) {

const handleOpenWishlist = () => {
/** openHostApp path 규칙: `/{path}?{query}` — `web=...`만 넘기면 `/web=...` 라우트로 해석됨 */
openHostApp(`/?web=${encodeURIComponent('/archive?tab=wish')}`);
openHostApp(`/?web=${encodeURIComponent('/archive/wish')}`);
};

useEffect(() => {
Expand Down
18 changes: 6 additions & 12 deletions apps/app/hooks/useShareIntent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,9 @@ import { useCallback, useEffect, useRef } from 'react';
import { toShareIntentPayload } from '@/utils/serializeShareIntent';
import { WebBridge } from '@/utils/webBridge';

const ARCHIVE_PATH = '/archive';
const ARCHIVE_WISH_TAB = 'wish';
const WISHLIST_PATH = '/archive/wish';

const isArchiveWishTab = (uri: URL) => {
if (uri.pathname !== ARCHIVE_PATH) return false;

const tab = uri.searchParams.get('tab');
return tab === null || tab === ARCHIVE_WISH_TAB;
};
const isWishlistPath = (uri: URL) => uri.pathname === WISHLIST_PATH;

type Props = {
onChangeWebviewUri: (uri: string) => void;
Expand Down Expand Up @@ -47,14 +41,14 @@ export const useShareIntent = ({ onChangeWebviewUri, webviewUri }: Props) => {
const nextUri = new URL(webviewUri);

/** 이미 아카이브 위시 탭: 즉시 전송 */
if (isArchiveWishTab(nextUri)) {
if (isWishlistPath(nextUri)) {
sendShareIntent();
return;
}

/** 다른 페이지: payload 보관 후 아카이브 위시 탭으로 이동 → WEB_REQ_READY 수신 시 전송 */
nextUri.pathname = ARCHIVE_PATH;
nextUri.search = `?tab=${ARCHIVE_WISH_TAB}`;
/** 다른 페이지: payload 보관 후 아카이브 위시로 이동 → WEB_REQ_READY 수신 시 전송 */
nextUri.pathname = WISHLIST_PATH;
nextUri.search = '';
nextUri.hash = '';
onChangeWebviewUri(nextUri.toString());
} catch {
Expand Down
21 changes: 21 additions & 0 deletions apps/web/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,27 @@ const nextConfig = async () => {
],
},

/**
* 구버전 앱 공유 시트·기존 딥링크의 /archive(?tab=) 경로 호환
*
* NOTE: 구버전 앱 사라지면 이 설정 제거 필요
*/
async redirects() {
return [
{
source: '/archive',
has: [{ type: 'query', key: 'tab', value: 'tournament' }],
destination: '/archive/tournament',
permanent: false,
},
{
source: '/archive',
destination: '/archive/wish',
permanent: false,
},
];
},

async rewrites() {
return [
{
Expand Down
8 changes: 6 additions & 2 deletions apps/web/src/apis/getWishlist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ import { clientApi } from '@/apis/client';
import { serverApi } from '@/apis/server';
import { ENDPOINTS } from '@/consts/api';
import type { ApiResponseT } from '@/types/api';
import type { ItemT } from '@/types/item';
import type { WishItemT, WishT } from '@/types/wish';

import type { WishlistEntryT } from '../app/archive/_types/wish';
import type { WishItemT } from '../app/archive/_types/wish';
type WishlistEntryT = {
wish: WishT;
item: ItemT;
};

type WishlistApiResponseT = ApiResponseT<WishlistEntryT[]> & {
pageResponse: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { Suspense } from 'react';

import { Header, HeaderIcon } from '@/components/header';

import WishTab from './WishTab';

type WishlistLayoutProps = {
type Props = {
title: string;
children: React.ReactNode;
};

function WishlistLayout({ children }: WishlistLayoutProps) {
function ArchivePageLayout({ title, children }: Props) {
return (
<div className="flex min-h-dvh flex-col bg-bg-layer-basement px-5">
<div className="sticky top-0 z-20 inline-flex w-full flex-col items-start gap-5 bg-bg-layer-basement pt-padding-top pb-6">
Expand All @@ -22,16 +19,13 @@ function WishlistLayout({ children }: WishlistLayoutProps) {
}
/>
<h1 className="text-[28px] leading-[137.5%] font-bold tracking-[-0.708px] text-text-neutral-primary">
내 보관함
{title}
</h1>
</div>
<Suspense>
<WishTab />
</Suspense>
</div>
{children}
</div>
);
}

export default WishlistLayout;
export default ArchivePageLayout;
57 changes: 0 additions & 57 deletions apps/web/src/app/archive/_components/WishTab.tsx

This file was deleted.

This file was deleted.

18 changes: 0 additions & 18 deletions apps/web/src/app/archive/_types/wish.ts

This file was deleted.

27 changes: 0 additions & 27 deletions apps/web/src/app/archive/page.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use client';

import { Suspense, useState } from 'react';

import BottomTabBar from '@/components/bottom-tab-bar';
import type { TournamentStatusT } from '@/types/tournament';

import ArchivePageLayout from '../../_common/_components/ArchivePageLayout';
import TournamentHistoryList from './TournamentHistoryList';
import TournamentStatusTab, { type TournamentStatusTabT } from './TournamentStatusTab';

const STATUS_BY_TAB: Record<TournamentStatusTabT, TournamentStatusT[]> = {
'in-progress': ['PENDING', 'IN_PROGRESS'],
completed: ['COMPLETED'],
};

function ArchiveTournamentClient() {
const [activeTab, setActiveTab] = useState<TournamentStatusTabT>('in-progress');

return (
<ArchivePageLayout title="내 토너먼트">
<TournamentStatusTab activeTab={activeTab} onTabChange={setActiveTab} />
<Suspense
fallback={
<main className="flex flex-1 flex-col items-center justify-center pb-24">
<p className="body-1-semibold text-text-neutral-tertiary">
토너먼트를 불러오는 중이에요
</p>
</main>
}
>
<TournamentHistoryList key={activeTab} statuses={STATUS_BY_TAB[activeTab]} />
</Suspense>
<div className="fixed bottom-10 left-1/2 z-20 flex -translate-x-1/2 items-center gap-3">
<BottomTabBar />
</div>
</ArchivePageLayout>
);
}

export default ArchiveTournamentClient;
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import { HydrationBoundary, dehydrate } from '@tanstack/react-query';

import { getTournamentList } from '@/apis/getTournamentList';
import type { TournamentStatusT } from '@/types/tournament';
import { getQueryClient } from '@/utils/queryClient';

import TournamentHistoryContentClient from './client';
import ArchiveTournamentClient from './ArchiveTournamentClient';

const DEFAULT_STATUSES: TournamentStatusT[] = ['PENDING', 'IN_PROGRESS'];

async function TournamentHistoryContent() {
const queryClient = getQueryClient();

await queryClient.prefetchQuery({
queryKey: ['tournamentList', []],
queryFn: () => getTournamentList(),
queryKey: ['tournamentList', DEFAULT_STATUSES],
queryFn: () => getTournamentList(DEFAULT_STATUSES),
});

return (
<HydrationBoundary state={dehydrate(queryClient)}>
<TournamentHistoryContentClient />
<ArchiveTournamentClient />
</HydrationBoundary>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@
import { TrophyIconFill } from '@/assets/icons';
import TournamentCard from '@/components/tournament-card';
import { useGetTournamentList } from '@/hooks/useGetTournamentList';
import type { TournamentStatusT } from '@/types/tournament';

function TournamentHistoryList() {
const { tournamentListData } = useGetTournamentList();
type Props = {
statuses: TournamentStatusT[];
};

function TournamentHistoryList({ statuses }: Props) {
const { tournamentListData } = useGetTournamentList(statuses);

if (tournamentListData.length === 0)
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use client';

import { cn } from '@/utils/cn';

export type TournamentStatusTabT = 'in-progress' | 'completed';

type Props = {
activeTab: TournamentStatusTabT;
onTabChange: (tab: TournamentStatusTabT) => void;
};

const TABS: { value: TournamentStatusTabT; label: string }[] = [
{ value: 'in-progress', label: '진행 중' },
{ value: 'completed', label: '완료' },
];

function TournamentStatusTab({ activeTab, onTabChange }: Props) {
return (
<div className="flex w-full border-b border-border-neutral-muted">
{TABS.map(tab => (
<button
key={tab.value}
type="button"
onClick={() => onTabChange(tab.value)}
className={cn(
'relative flex h-11 flex-1 cursor-pointer items-center justify-center body-1-semibold transition-colors',
activeTab === tab.value ? 'text-text-neutral-primary' : 'text-text-neutral-tertiary'
)}
>
{tab.label}
{activeTab === tab.value && (
<span className="absolute inset-x-0 bottom-0 h-0.5 bg-text-neutral-primary" />
)}
</button>
))}
</div>
);
}

export default TournamentStatusTab;
7 changes: 7 additions & 0 deletions apps/web/src/app/archive/tournament/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import TournamentHistoryContent from './_components/TournamentHistoryContent';

async function ArchiveTournamentPage() {
return <TournamentHistoryContent />;
}

export default ArchiveTournamentPage;
Loading
Loading