Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade website to react router v7 #1214

Merged
merged 12 commits into from
Jan 29, 2025
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
.env
.moon/cache
.moon/docker
.react-router
.next/
.pnp.*
Thumbs.db
Expand Down
38 changes: 38 additions & 0 deletions apps/frontend/app/components/common.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
Form,
Link,
useFetcher,
useLocation,
useNavigate,
useRevalidator,
} from "@remix-run/react";
Expand Down Expand Up @@ -70,6 +71,7 @@ import {
IconMoodHappy,
IconMoodSad,
IconRefresh,
IconRotateClockwise,
IconScaleOutline,
IconSearch,
IconServer,
Expand All @@ -95,6 +97,7 @@ import {
getMetadataIcon,
getSurroundingElements,
openConfirmationModal,
redirectToQueryParam,
reviewYellow,
} from "~/lib/generals";
import {
Expand Down Expand Up @@ -1358,3 +1361,38 @@ export const DisplayListDetailsAndRefresh = (props: {
</Group>
);
};

export const ExpireCacheKeyButton = (props: {
cacheId: string;
confirmationText?: string;
}) => {
const submit = useConfirmSubmit();
const location = useLocation();

return (
<Form
replace
method="POST"
action={withQuery($path("/actions"), {
intent: "expireCacheKey",
[redirectToQueryParam]: location.pathname,
})}
>
<input type="hidden" name="cacheId" value={props.cacheId} />
<ActionIcon
type="submit"
variant="subtle"
onClick={(e) => {
if (!props.confirmationText) return;
const form = e.currentTarget.form;
if (form) {
e.preventDefault();
openConfirmationModal(props.confirmationText, () => submit(form));
}
}}
>
<IconRotateClockwise />
</ActionIcon>
</Form>
);
};
7 changes: 6 additions & 1 deletion apps/frontend/app/lib/utilities.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,17 @@ export const getUserPreferences = async (request: Request) => {
return userDetails.preferences;
};

export const getUserCollectionsList = async (request: Request) => {
export const getUserCollectionsListRaw = async (request: Request) => {
const { userCollectionsList } = await serverGqlService.authenticatedRequest(
request,
UserCollectionsListDocument,
{},
);
return userCollectionsList;
};

export const getUserCollectionsList = async (request: Request) => {
const userCollectionsList = await getUserCollectionsListRaw(request);
return userCollectionsList.response;
};

Expand Down
64 changes: 12 additions & 52 deletions apps/frontend/app/routes/_dashboard._index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
import {
ActionIcon,
Alert,
Container,
Group,
Stack,
Text,
} from "@mantine/core";
import { Alert, Container, Group, Stack, Text } from "@mantine/core";
import type {
ActionFunctionArgs,
LoaderFunctionArgs,
MetaArgs,
} from "@remix-run/node";
import { Form, useLoaderData } from "@remix-run/react";
import { useLoaderData } from "@remix-run/react";
import {
type CalendarEventPartFragment,
CollectionContentsDocument,
Expand All @@ -24,32 +17,23 @@ import {
UserUpcomingCalendarEventsDocument,
} from "@ryot/generated/graphql/backend/graphql";
import { isNumber } from "@ryot/ts-utils";
import {
IconBackpack,
IconInfoCircle,
IconRotateClockwise,
} from "@tabler/icons-react";
import { IconBackpack, IconInfoCircle } from "@tabler/icons-react";
import CryptoJS from "crypto-js";
import type { ReactNode } from "react";
import { $path } from "remix-routes";
import { ClientOnly } from "remix-utils/client-only";
import invariant from "tiny-invariant";
import { match } from "ts-pattern";
import { withQuery } from "ufo";
import { useLocalStorage } from "usehooks-ts";
import {
ApplicationGrid,
DisplaySummarySection,
ExpireCacheKeyButton,
ProRequiredAlert,
} from "~/components/common";
import { DisplayCollectionEntity } from "~/components/common";
import { MetadataDisplayItem } from "~/components/media";
import { dayjsLib, openConfirmationModal } from "~/lib/generals";
import {
useConfirmSubmit,
useCoreDetails,
useUserPreferences,
} from "~/lib/hooks";
import { dayjsLib } from "~/lib/generals";
import { useCoreDetails, useUserPreferences } from "~/lib/hooks";
import {
getUserCollectionsList,
getUserPreferences,
Expand Down Expand Up @@ -179,7 +163,6 @@ export default function Page() {
.length > 0 ? (
<Section key={v} lot={v}>
<SectionTitleWithRefreshIcon
bypassConfirm
text="In Progress"
cacheId={loaderData.inProgressCollectionContents.cacheId}
/>
Expand All @@ -202,6 +185,7 @@ export default function Page() {
<SectionTitleWithRefreshIcon
text="Recommendations"
cacheId={loaderData.userMetadataRecommendations.cacheId}
confirmationText="Are you sure you want to refresh the recommendations?"
/>
{coreDetails.isServerKeyValidated ? (
<ApplicationGrid>
Expand Down Expand Up @@ -240,39 +224,15 @@ export default function Page() {
const SectionTitleWithRefreshIcon = (props: {
text: string;
cacheId: string;
bypassConfirm?: true;
confirmationText?: string;
}) => {
const submit = useConfirmSubmit();

return (
<Group justify="space-between">
<SectionTitle text={props.text} />
<Form
replace
method="POST"
action={withQuery($path("/actions"), {
intent: "expireCacheKey",
})}
>
<input type="hidden" name="cacheId" value={props.cacheId} />
<ActionIcon
type="submit"
variant="subtle"
onClick={(e) => {
if (props.bypassConfirm) return;
const form = e.currentTarget.form;
if (form) {
e.preventDefault();
openConfirmationModal(
"Are you sure you want to refresh the recommendations?",
() => submit(form),
);
}
}}
>
<IconRotateClockwise />
</ActionIcon>
</Form>
<ExpireCacheKeyButton
cacheId={props.cacheId}
confirmationText={props.confirmationText}
/>
</Group>
);
};
Expand Down
55 changes: 33 additions & 22 deletions apps/frontend/app/routes/_dashboard.collections.list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ import { $path } from "remix-routes";
import { match } from "ts-pattern";
import { withQuery } from "ufo";
import { z } from "zod";
import { DebouncedSearchInput, ProRequiredAlert } from "~/components/common";
import {
DebouncedSearchInput,
ExpireCacheKeyButton,
ProRequiredAlert,
} from "~/components/common";
import {
PRO_REQUIRED_MESSAGE,
clientGqlService,
Expand All @@ -89,17 +93,19 @@ import {
import {
createToastHeaders,
getEnhancedCookieName,
getUserCollectionsListRaw,
redirectUsingEnhancedCookieSearchParams,
serverGqlService,
} from "~/lib/utilities.server";

export const loader = async ({ request }: LoaderFunctionArgs) => {
const cookieName = await getEnhancedCookieName("collections.list", request);
await redirectUsingEnhancedCookieSearchParams(request, cookieName);
const [{ usersList }] = await Promise.all([
const [{ usersList }, userCollectionsList] = await Promise.all([
serverGqlService.authenticatedRequest(request, UsersListDocument, {}),
getUserCollectionsListRaw(request),
]);
return { usersList, cookieName };
return { usersList, cookieName, userCollectionsList };
};

export const meta = (_args: MetaArgs<typeof loader>) => {
Expand Down Expand Up @@ -223,25 +229,30 @@ export default function Page() {
return (
<Container size="sm">
<Stack>
<Flex align="center" gap="md">
<Title>Your collections</Title>
<ActionIcon
color="green"
variant="outline"
onClick={() => setToUpdateCollection({})}
>
<IconPlus size={20} />
</ActionIcon>
<Modal
centered
size="lg"
withCloseButton={false}
opened={toUpdateCollection !== null}
onClose={() => setToUpdateCollection(null)}
>
<CreateOrUpdateModal toUpdateCollection={toUpdateCollection} />
</Modal>
</Flex>
<Group justify="space-between" wrap="nowrap">
<Flex align="center" gap="md">
<Title>Your collections</Title>
<ActionIcon
color="green"
variant="outline"
onClick={() => setToUpdateCollection({})}
>
<IconPlus size={20} />
</ActionIcon>
<Modal
centered
size="lg"
withCloseButton={false}
opened={toUpdateCollection !== null}
onClose={() => setToUpdateCollection(null)}
>
<CreateOrUpdateModal toUpdateCollection={toUpdateCollection} />
</Modal>
</Flex>
<ExpireCacheKeyButton
cacheId={loaderData.userCollectionsList.cacheId}
/>
</Group>
<DebouncedSearchInput
initialValue={query}
enhancedQueryParams={loaderData.cookieName}
Expand Down
7 changes: 3 additions & 4 deletions apps/frontend/app/routes/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ export const loader = async () => redirect($path("/"));
export const action = async ({ request }: ActionFunctionArgs) => {
const formData = await request.clone().formData();
const intent = getActionIntent(request);
const redirectToForm = formData.get(redirectToQueryParam);
let redirectTo = redirectToForm ? redirectToForm.toString() : undefined;
const { searchParams } = new URL(request.url);
const redirectToSearchParams = searchParams.get(redirectToQueryParam);
let redirectTo = redirectToSearchParams || undefined;
let returnData = {};
const headers = new Headers();
let status = undefined;
Expand Down Expand Up @@ -420,7 +421,6 @@ export const action = async ({ request }: ActionFunctionArgs) => {
: "Progress updated successfully",
}),
);
redirectTo = submission[redirectToQueryParam];
})
.with("individualProgressUpdate", async () => {
const submission = processSubmission(formData, bulkUpdateSchema);
Expand Down Expand Up @@ -564,7 +564,6 @@ const progressUpdateSchema = z
date: z.string().optional(),
metadataLot: z.nativeEnum(MediaLot),
providerWatchedOn: z.string().optional(),
[redirectToQueryParam]: z.string().optional(),
showAllEpisodesBefore: zodBoolAsString.optional(),
animeAllEpisodesBefore: zodCheckboxAsString.optional(),
podcastAllEpisodesBefore: zodCheckboxAsString.optional(),
Expand Down
26 changes: 13 additions & 13 deletions apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
"@formkit/auto-animate": "0.8.2",
"@hello-pangea/dnd": "17.0.0",
"@lukemorales/query-key-factory": "1.3.4",
"@mantine/carousel": "7.16.1",
"@mantine/charts": "7.16.1",
"@mantine/code-highlight": "7.16.1",
"@mantine/core": "7.16.1",
"@mantine/dates": "7.16.1",
"@mantine/form": "7.16.1",
"@mantine/hooks": "7.16.1",
"@mantine/modals": "7.16.1",
"@mantine/notifications": "7.16.1",
"@mantine/carousel": "7.16.2",
"@mantine/charts": "7.16.2",
"@mantine/code-highlight": "7.16.2",
"@mantine/core": "7.16.2",
"@mantine/dates": "7.16.2",
"@mantine/form": "7.16.2",
"@mantine/hooks": "7.16.2",
"@mantine/modals": "7.16.2",
"@mantine/notifications": "7.16.2",
"@remix-pwa/sw": "3.0.10",
"@remix-pwa/worker-runtime": "2.1.4",
"@remix-run/node": "2.15.2",
Expand All @@ -30,8 +30,8 @@
"@ryot/graphql": "workspace:*",
"@ryot/ts-utils": "workspace:*",
"@tabler/icons-react": "3.17.0",
"@tanstack/react-query": "5.64.2",
"@tanstack/react-query-devtools": "5.64.2",
"@tanstack/react-query": "5.65.1",
"@tanstack/react-query-devtools": "5.65.1",
"clsx": "2.1.1",
"cookie": "1.0.2",
"crypto-js": "4.2.0",
Expand All @@ -45,7 +45,7 @@
"html2canvas": "1.4.1",
"humanize-duration-ts": "2.1.1",
"immer": "10.1.1",
"isbot": "5.1.21",
"isbot": "5.1.22",
"jotai": "2.11.1",
"js-cookie": "3.0.5",
"jwt-decode": "4.0.0",
Expand All @@ -54,7 +54,7 @@
"react": "18.3.1",
"react-dom": "18.3.1",
"react-virtuoso": "4.12.3",
"recharts": "2.15.0",
"recharts": "2.15.1",
"remix-routes": "1.7.7",
"remix-utils": "7.7.0",
"tailwind-merge": "2.6.0",
Expand Down
2 changes: 1 addition & 1 deletion apps/website/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ COPY --from=website-builder --chown=ryot:ryot /app/apps/website/node_modules ./n
COPY --from=website-builder --chown=ryot:ryot /app/apps/website/package.json ./package.json
COPY --from=website-builder --chown=ryot:ryot /app/apps/website/build ./build
COPY --chown=ryot:ryot apps/website/app/drizzle/migrations app/drizzle/migrations
CMD npx remix-serve ./build/server/index.js
CMD npx react-router-serve ./build/server/index.js
4 changes: 2 additions & 2 deletions apps/website/app/entry.client.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { RemixBrowser } from "@remix-run/react";
import { StrictMode, startTransition } from "react";
import { hydrateRoot } from "react-dom/client";
import { HydratedRouter } from "react-router/dom";

startTransition(() => {
hydrateRoot(
document,
<StrictMode>
<RemixBrowser />
<HydratedRouter />
</StrictMode>,
);
});
Loading