Skip to content

Commit

Permalink
feat(web): collection page
Browse files Browse the repository at this point in the history
  • Loading branch information
yjl9903 committed Oct 5, 2024
1 parent 6c02a00 commit 98a4a7d
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 30 deletions.
14 changes: 8 additions & 6 deletions apps/frontend/web/app/components/Resources/pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export interface PaginationProps {

complete: boolean;

timestamp: Date;
timestamp?: Date;

link?: (page: number) => string;

Expand All @@ -25,11 +25,13 @@ export const Pagination = (props: PaginationProps) => {

return (
<div className="mt-4 flex lt-md:flex-col font-sm">
<div className="text-base-400 py-1 pl3 lt-sm:pl1">
<span className="mr1 text-sm i-carbon-update-now op-80"></span>
<span className="select-none">数据更新于 </span>
<span>{formatChinaTime(timestamp)}</span>
</div>
{timestamp && (
<div className="text-base-400 py-1 pl3 lt-sm:pl1">
<span className="mr1 text-sm i-carbon-update-now op-80"></span>
<span className="select-none">数据更新于 </span>
<span>{formatChinaTime(timestamp)}</span>
</div>
)}
<div className="flex-auto"></div>
{(page !== 1 || !complete) && (
<div className="flex lt-md:(mt-4 justify-center) items-center gap-2 text-base-500">
Expand Down
18 changes: 10 additions & 8 deletions apps/frontend/web/app/components/Resources/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Tag } from './tag';
import { formatChinaTime } from './utils';
import { Pagination, PaginationProps } from './pagination';

export interface ResourcesTableProps extends PaginationProps {
export interface ResourcesTableProps extends Partial<PaginationProps> {
className?: string;

resources: Resource<{ tracker: true }>[];
Expand Down Expand Up @@ -66,13 +66,15 @@ export default function ResourcesTable(props: ResourcesTableProps) {
</tbody>
</table>
</div>
<Pagination
timestamp={props.timestamp}
page={props.page}
link={props.link}
navigate={props.navigate}
complete={props.complete ?? false}
/>
{props.page !== undefined && (
<Pagination
timestamp={props.timestamp}
page={props.page}
link={props.link}
navigate={props.navigate}
complete={props.complete ?? false}
/>
)}
</div>
);
}
Expand Down
13 changes: 7 additions & 6 deletions apps/frontend/web/app/layouts/Sidebar/sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import clsx from 'clsx';
import { memo, useCallback, useMemo, useRef, useState } from 'react';
import { NavLink, useLocation, useNavigate } from '@remix-run/react';
import { toast } from 'sonner';
import { format } from 'date-fns';
import { NavLink, useLocation } from '@remix-run/react';
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
import { memo, useCallback, useMemo, useRef, useState } from 'react';

import { findFansub } from 'animegarden';

Expand All @@ -18,14 +20,13 @@ import {
DropdownMenuTrigger
} from '~/components/ui/dropdown-menu';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '~/components/ui/tooltip';
import { resolveFilterOptions } from '~/routes/resources.($page)/Filter';
import { base64URLencode } from '~/utils/json';

import { stringifySearch } from '../Search/utils';

import './sidebar.css';
import { isOpenSidebar } from './atom';
import { toast } from 'sonner';
import { resolveFilterOptions } from '@/routes/resources.($page)/Filter';
import { format } from 'date-fns';

type CollectionItem = Collection['items'][0];

Expand Down Expand Up @@ -132,7 +133,7 @@ const Collection = memo((props: { collection: Collection }) => {
<div>
<div className="px2 flex items-center text-sm">
<NavLink
to={`/collection/filter/${JSON.stringify(collection)}`}
to={`/collection/filter/${base64URLencode(JSON.stringify(collection))}`}
className={'block text-xs text-base-500 text-link-active'}
>
<span className="select-none">{collection.name}</span>
Expand Down
52 changes: 51 additions & 1 deletion apps/frontend/web/app/routes/collection.filter.$filter/route.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import type { MetaFunction } from '@remix-run/cloudflare';
import { json, redirect, type LoaderFunctionArgs, type MetaFunction } from '@remix-run/cloudflare';
import { NavLink, useLoaderData } from '@remix-run/react';

import Layout from '~/layouts/Layout';
import { Collection } from '~/states/collection';
import { safeParseJSON } from '~/utils/json';
import { fetchResources } from '~/utils/fetch';
import ResourcesTable from '~/components/Resources';
import clsx from 'clsx';

export const meta: MetaFunction = () => {
return [
Expand All @@ -9,10 +15,54 @@ export const meta: MetaFunction = () => {
];
};

export const loader = async ({ request, params }: LoaderFunctionArgs) => {
console.log(params.filter);
const filter = safeParseJSON<Collection>(params.filter, { decode: true });
if (filter.result) {
const collection = filter.result;

try {
const resp = await Promise.all(
collection.items.map(async (item) => ({
item,
result: await fetchResources(item)
}))
);

return json({
data: {
name: collection.name,
items: resp
}
});
} catch (error) {
console.error(error);
}
}

return redirect(`/`);
};

export default function Resources() {
const { data: collection } = useLoaderData<typeof loader>();

return (
<Layout>
<div className="w-full pt-12 pb-24">
<div className="space-y-8">
{collection.items.map((item) => (
<div key={item.item.searchParams} className={clsx('py-4 rounded-md border drop-md')}>
<div className="mb-4 px-4 pb-4 border-b">
<h2 className="text-xl font-bold text-link-active">
<NavLink to={`/resources/1${item.item.searchParams}`}>{item.item.name}</NavLink>
</h2>
</div>
<div className="px-4">
<ResourcesTable resources={item.result.resources}></ResourcesTable>
</div>
</div>
))}
</div>
</div>
</Layout>
);
Expand Down
21 changes: 13 additions & 8 deletions apps/frontend/web/app/utils/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
type ProviderType,
type FilterOptions,
fetchResources as rawFetchResources,
fetchResourceDetail as rawFetchResourceDetail
fetchResourceDetail as rawFetchResourceDetail,
FetchResourcesOptions
} from 'animegarden';

export const baseURL = import.meta.env.SSR
Expand Down Expand Up @@ -39,13 +40,17 @@ export async function fetchResources(
} = {}
) {
try {
return await rawFetchResources(options.fetch ?? ofetch, {
baseURL,
tracker: true,
signal: options.signal,
retry: options.retry,
...filter
});
const resp = await rawFetchResources<FetchResourcesOptions & { tracker: true }>(
options.fetch ?? ofetch,
{
baseURL,
signal: options.signal,
retry: options.retry,
...filter,
tracker: true
} as const
);
return resp;
} catch (error) {
console.error(error);
return {
Expand Down
27 changes: 27 additions & 0 deletions apps/frontend/web/app/utils/json.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export function safeParseJSON<T>(
text: string | undefined,
options?: { decode?: boolean }
): { result: T; error: undefined } | { result: undefined; error: unknown } {
if (!text) return { result: undefined, error: new Error('text is undefined') };
try {
if (options?.decode) {
text = base64URLdecode(text);
}
return { result: JSON.parse(text) as T, error: undefined };
} catch (error) {
console.log(error);
return { result: undefined, error };
}
}

export function base64URLencode(str: string) {
const base64Encoded = btoa(encodeURIComponent(str));
return base64Encoded.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

export function base64URLdecode(str: string) {
const base64Encoded = str.replace(/-/g, '+').replace(/_/g, '/');
const padding = str.length % 4 === 0 ? '' : '='.repeat(4 - (str.length % 4));
const base64WithPadding = base64Encoded + padding;
return decodeURIComponent(atob(base64WithPadding));
}
2 changes: 1 addition & 1 deletion packages/animegarden/src/garden/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ interface FetchResourcesResult<T extends FetchResourcesOptions> {
export async function fetchResources<T extends FetchResourcesOptions = FetchResourcesOptions>(
fetch: (request: RequestInfo, init?: RequestInit) => Promise<Response>,
options: T = {} as T
): Promise<FetchResourcesResult<FetchResourcesOptions>> {
): Promise<FetchResourcesResult<T>> {
const { baseURL = DefaultBaseURL, retry = 0 } = options;

const url = stringifySearchURL(baseURL, options);
Expand Down

0 comments on commit 98a4a7d

Please sign in to comment.