diff --git a/.eslintrc.json b/.eslintrc.json index bffb357..d5147b1 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,3 +1,7 @@ { - "extends": "next/core-web-vitals" + "extends": "next/core-web-vitals", + "plugins": ["eslint-plugin-react-compiler"], + "rules": { + "react-compiler/react-compiler": "error" + } } diff --git a/app/ClientProviders.tsx b/app/ClientProviders.tsx new file mode 100644 index 0000000..a35c8ea --- /dev/null +++ b/app/ClientProviders.tsx @@ -0,0 +1,27 @@ +"use client"; + +import { NovuProvider } from "@novu/react"; +import React, { ComponentProps, FC, PropsWithChildren } from "react"; +import NotificationFilterStatusProvider from "@/app/NotificationFilterStatus"; + +const ClientProviders: FC< + PropsWithChildren< + Pick< + ComponentProps, + "applicationIdentifier" | "subscriberId" + > + > +> = ({ applicationIdentifier, subscriberId, children }) => { + return ( + + + {children} + + + ); +}; + +export default ClientProviders; diff --git a/app/CustomThemeForm.tsx b/app/CustomThemeForm.tsx index dce8b65..76f7946 100644 --- a/app/CustomThemeForm.tsx +++ b/app/CustomThemeForm.tsx @@ -10,14 +10,17 @@ import { Theme, Tooltip, useThemeContext, -} from "@radix-ui/themes"; +} from "@v1s10n_4/radix-ui-themes"; import React, { FC } from "react"; -import { radiusPropDef, themePropDefs } from "@radix-ui/themes/dist/esm/props"; +import { + radiusPropDef, + themePropDefs, +} from "@v1s10n_4/radix-ui-themes/dist/esm/props"; import InvertIcon from "pixelarticons/svg/invert.svg"; import ChessIcon from "pixelarticons/svg/chess.svg"; import MoonIcon from "pixelarticons/svg/moon.svg"; import SunIcon from "pixelarticons/svg/sun.svg"; -import { getMatchingGrayColor } from "@radix-ui/themes/dist/esm/helpers"; +import { getMatchingGrayColor } from "@v1s10n_4/radix-ui-themes/dist/esm/helpers"; export const CustomThemeForm: FC = ({ appearance, diff --git a/app/CustomThemePopover.tsx b/app/CustomThemePopover.tsx index 0bd8cc1..756f56d 100644 --- a/app/CustomThemePopover.tsx +++ b/app/CustomThemePopover.tsx @@ -5,7 +5,7 @@ import { IconButtonProps, Popover, useThemeContext, -} from "@radix-ui/themes"; +} from "@v1s10n_4/radix-ui-themes"; import Fill from "pixelarticons/svg/fill.svg"; import FillHalf from "pixelarticons/svg/fill-half.svg"; import CloseIcon from "pixelarticons/svg/close.svg"; diff --git a/app/NotificationCenter.tsx b/app/NotificationCenter.tsx new file mode 100644 index 0000000..45ad689 --- /dev/null +++ b/app/NotificationCenter.tsx @@ -0,0 +1,102 @@ +"use client"; +import { + Badge, + Button, + Card, + IconButton, + Inset, + ScrollArea, + Separator, + VisuallyHidden, +} from "@v1s10n_4/radix-ui-themes"; +import { + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerHeader, + DrawerTitle, + DrawerTrigger, +} from "@/components/Drawer"; +import BellIcon from "@/app/bell.svg"; +import BellRingIcon from "@/app/bell-ring.svg"; +import React, { ComponentProps, FC } from "react"; +import { useCounts } from "@novu/react"; +import NotificationList from "@/app/NotificationList"; + +const NotificationTrigger: FC> = (props) => { + const { counts, isFetching, isLoading } = useCounts({ + filters: [{ read: false }], + }); + return ( + +
+ {counts && counts[0].count > 0 && (isLoading || isFetching) ? ( + + ) : ( + + )} + + {counts && counts[0].count > 0 && ( + + {counts[0].count} + + )} + + Notification Center ({counts && counts[0].count} unread) + +
+
+ ); +}; + +const NotificationCenter = () => { + return ( + + + + + + + + + Notifications + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default NotificationCenter; diff --git a/app/NotificationFilterStatus.tsx b/app/NotificationFilterStatus.tsx new file mode 100644 index 0000000..9d6707d --- /dev/null +++ b/app/NotificationFilterStatus.tsx @@ -0,0 +1,38 @@ +"use client"; +import React, { ReactNode, useState } from "react"; + +type StatusContextProps = { + status: "all" | "unread" | "archived"; + setStatus: (status: "all" | "unread" | "archived") => void; +}; + +const StatusContext = React.createContext( + undefined +); + +export const useNotificationFilterStatus = () => { + const context = React.useContext(StatusContext); + if (context === undefined) { + throw new Error( + "useNotificationFilterStatus must be used within a StatusProvider" + ); + } + + return context; +}; + +export const NotificationFilterStatusProvider = ({ + children, +}: { + children: ReactNode; +}) => { + const [status, setStatus] = useState("all"); + + return ( + + {children} + + ); +}; + +export default NotificationFilterStatusProvider; diff --git a/app/NotificationItem.tsx b/app/NotificationItem.tsx new file mode 100644 index 0000000..8239343 --- /dev/null +++ b/app/NotificationItem.tsx @@ -0,0 +1,57 @@ +"use client"; +import { Avatar, Box, Card, IconButton, Text } from "@v1s10n_4/radix-ui-themes"; +import type { Notification } from "@novu/react"; +import MailDeleteIcon from "pixelarticons/svg/mail-delete.svg"; +import MailCheckIcon from "pixelarticons/svg/mail-check.svg"; +import React, { FC } from "react"; +import { timeAgo } from "@/lib/utils"; + +const NotificationItem: FC<{ notification: Notification }> = ({ + notification, +}) => { + return ( + + {notification.avatar && ( + + )} + + + {notification.body} + + + + + + {notification.isRead ? ( + notification.unread()} + > + + + ) : ( + notification.read()} + > + + + )} + + ); +}; + +export default NotificationItem; diff --git a/app/NotificationList.tsx b/app/NotificationList.tsx new file mode 100644 index 0000000..c064f77 --- /dev/null +++ b/app/NotificationList.tsx @@ -0,0 +1,59 @@ +"use client"; +import React, { useMemo } from "react"; +import { useNotifications } from "@novu/react"; +import { + Button, + Flex, + ScrollArea, + Skeleton, + Text, +} from "@v1s10n_4/radix-ui-themes"; +import { useNotificationFilterStatus } from "@/app/NotificationFilterStatus"; +import NotificationItem from "@/app/NotificationItem"; + +export default function NotificationList() { + const { status } = useNotificationFilterStatus(); + const filter = useMemo(() => { + if (status === "unread") { + return { read: false }; + } else if (status === "archived") { + return { archived: true }; + } + + return { archived: false }; + }, [status]); + const { notifications, isLoading, isFetching, hasMore, fetchMore, error } = + useNotifications(filter); + + const handleLoadMore = async () => { + if (hasMore && !isLoading) { + await fetchMore(); + } + }; + + return ( + + + {isLoading && + Array(4) + .fill(0) + .map((_, i) => )} + {notifications?.map((notification) => ( + + ))} + {!notifications?.length && ( + + You’re all caught up! Check back soon for new alerts. + + )} + {hasMore && ( + + + + )} + + + ); +} diff --git a/app/Notifications.tsx b/app/Notifications.tsx new file mode 100644 index 0000000..db19aed --- /dev/null +++ b/app/Notifications.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import { auth } from "@/auth"; +import dynamic from "next/dynamic"; +import { Spinner } from "@v1s10n_4/radix-ui-themes"; + +const ClientProviders = dynamic(() => import("@/app/ClientProviders"), { + ssr: false, + loading: () => , +}); + +const NotificationCenter = dynamic(() => import("@/app/NotificationCenter"), { + ssr: false, + loading: () => , +}); + +const Notifications = async () => { + const session = await auth(); + return ( + + + + ); +}; + +export default Notifications; diff --git a/app/RootDrawer.tsx b/app/RootDrawer.tsx index a0243e7..881e3f7 100644 --- a/app/RootDrawer.tsx +++ b/app/RootDrawer.tsx @@ -15,7 +15,7 @@ import { RadioCards, Text, useThemeContext, -} from "@radix-ui/themes"; +} from "@v1s10n_4/radix-ui-themes"; import { usePathname, useRouter } from "next/navigation"; import ChatIcon from "pixelarticons/svg/chat.svg"; import ListIcon from "pixelarticons/svg/list.svg"; diff --git a/app/RootNav.tsx b/app/RootNav.tsx index 3d15ca9..4609c9a 100644 --- a/app/RootNav.tsx +++ b/app/RootNav.tsx @@ -1,7 +1,7 @@ "use client"; import { Card } from "@/components/Card"; import Icon from "@/components/Icon/Icon"; -import { Box, Flex, RadioCards, Tooltip } from "@radix-ui/themes"; +import { Box, Flex, RadioCards, Tooltip } from "@v1s10n_4/radix-ui-themes"; import { usePathname, useRouter } from "next/navigation"; import ChatIcon from "pixelarticons/svg/chat.svg"; import ListIcon from "pixelarticons/svg/list.svg"; diff --git a/app/account/CardForm.tsx b/app/account/CardForm.tsx index d5284e8..6b9bd38 100644 --- a/app/account/CardForm.tsx +++ b/app/account/CardForm.tsx @@ -1,7 +1,7 @@ "use client"; import { CardContent, CardFooter } from "@/components/Card"; import SubmitButton from "@/components/SubmitButton"; -import { Separator, Text } from "@radix-ui/themes"; +import { Separator, Text } from "@v1s10n_4/radix-ui-themes"; import React, { FC, PropsWithChildren, useActionState } from "react"; import { SafeParseSuccess } from "zod"; import { typeToFlattenedError } from "zod/lib/ZodError"; diff --git a/app/account/ProfileHeader.tsx b/app/account/ProfileHeader.tsx index 011ba05..2a2919c 100644 --- a/app/account/ProfileHeader.tsx +++ b/app/account/ProfileHeader.tsx @@ -3,7 +3,7 @@ import { signOutAction } from "@/app/actions"; import { Card } from "@/components/Card"; import { HitPlaceholder } from "@/components/Placeholder"; import { User } from "@/db"; -import { Avatar, Button, Flex, Heading, Text } from "@radix-ui/themes"; +import { Avatar, Button, Flex, Heading, Text } from "@v1s10n_4/radix-ui-themes"; import Image from "next/image"; import LogOutIcon from "pixelarticons/svg/logout.svg"; import React, { FC } from "react"; diff --git a/app/account/QRCodeDrawer.tsx b/app/account/QRCodeDrawer.tsx index f1230ce..b85a59f 100644 --- a/app/account/QRCodeDrawer.tsx +++ b/app/account/QRCodeDrawer.tsx @@ -18,7 +18,7 @@ import { Spinner, Tooltip, VisuallyHidden, -} from "@radix-ui/themes"; +} from "@v1s10n_4/radix-ui-themes"; import { toString } from "qrcode"; import React, { FC, Suspense } from "react"; diff --git a/app/account/ReferralLink.tsx b/app/account/ReferralLink.tsx index 03cc281..bc52437 100644 --- a/app/account/ReferralLink.tsx +++ b/app/account/ReferralLink.tsx @@ -4,7 +4,13 @@ import QRCodeDrawer from "@/app/account/QRCodeDrawer"; import CopyButton from "@/components/CopyButton"; import { db, User } from "@/db"; import { referralLinks } from "@/db/schema/referral_links"; -import { Card, Flex, IconButton, Spinner, Text } from "@radix-ui/themes"; +import { + Card, + Flex, + IconButton, + Spinner, + Text, +} from "@v1s10n_4/radix-ui-themes"; import { and, desc, eq } from "drizzle-orm"; import React, { FC, Suspense } from "react"; diff --git a/app/account/ReviewCard.tsx b/app/account/ReviewCard.tsx index 2c3cd79..255e714 100644 --- a/app/account/ReviewCard.tsx +++ b/app/account/ReviewCard.tsx @@ -15,7 +15,13 @@ import { import { HitPlaceholder } from "@/components/Placeholder"; import { ReviewTask, User } from "@/db"; import { canReviewOwnContribution } from "@/lib/utils"; -import { Callout, Flex, Link as RLink, Strong, Text } from "@radix-ui/themes"; +import { + Callout, + Flex, + Link as RLink, + Strong, + Text, +} from "@v1s10n_4/radix-ui-themes"; import Image from "next/image"; import Link from "next/link"; import ArrowRight from "pixelarticons/svg/arrow-right.svg"; diff --git a/app/account/ReviewsCarousel.tsx b/app/account/ReviewsCarousel.tsx index 66e767f..6517fff 100644 --- a/app/account/ReviewsCarousel.tsx +++ b/app/account/ReviewsCarousel.tsx @@ -1,7 +1,7 @@ "use client"; import { Carousel, CarouselApi, CarouselContent } from "@/components/Carousel"; import { SliderActions } from "@/components/SliderActions"; -import { Badge } from "@radix-ui/themes"; +import { Badge } from "@v1s10n_4/radix-ui-themes"; import React, { FC, PropsWithChildren } from "react"; export const ReviewsCarousel: FC = ({ children }) => { diff --git a/app/account/ReviewsSection.tsx b/app/account/ReviewsSection.tsx index 68c2c59..b1e5c2f 100644 --- a/app/account/ReviewsSection.tsx +++ b/app/account/ReviewsSection.tsx @@ -18,7 +18,7 @@ import { Skeleton, Tabs, Text, -} from "@radix-ui/themes"; +} from "@v1s10n_4/radix-ui-themes"; import React, { FC, Suspense } from "react"; type ReviewsSectionProps = { user: User }; diff --git a/app/account/actions.ts b/app/account/actions.ts index a4fbba7..de39213 100644 --- a/app/account/actions.ts +++ b/app/account/actions.ts @@ -10,7 +10,10 @@ import { invaders } from "@/db/schema/invaders"; import { referralLinks } from "@/db/schema/referral_links"; import { reviewTasks } from "@/db/schema/reviewTasks"; import { users } from "@/db/schema/users"; -import { canReviewOwnContribution } from "@/lib/utils"; +import { + canReviewOthersContribution, + canReviewOwnContribution, +} from "@/lib/utils"; import { getTag, getTags } from "@/utils/revalidation-tags"; import { createFetchRequester } from "@algolia/requester-fetch"; import { del, put } from "@vercel/blob"; @@ -19,6 +22,15 @@ import { eq } from "drizzle-orm"; import { revalidateTag, unstable_cache } from "next/cache"; import { wait } from "next/dist/lib/wait"; +const headers: HeadersInit = { + "api-token": process.env.API_SECRET!, +}; + +const base = process.env.VERCEL_URL + ? `https://${process.env.VERCEL_URL}` + : process.env.URL!; +const apiUrl = `${base}/api/`; + export const updateUsername = async (_prevState: any, formData: FormData) => { const session = await auth(); if (!session) return signIn(); @@ -100,11 +112,48 @@ export const createReferralLink = async ( export const deleteContribution = async (id: ReviewTask["id"]) => { const session = await auth(); if (!session) return signIn(); + const contribution = await db.query.reviewTasks.findFirst({ where: eq(reviewTasks.id, id), + with: { + entity: true, + }, }); - if (!contribution || contribution.editor_id !== session.user.id) - return { success: false }; + + if (!contribution) return { success: false }; + + const isOwnContribution = contribution.editor_id === session.user.id; + + if (!isOwnContribution) { + if (session.user.role === "user" || session.user.role === "poweruser") { + return { success: false }; + } + + const res = await fetch(`${apiUrl}/notification`, { + headers, + method: "POST", + body: JSON.stringify({ + to: { + subscriberId: contribution.editor_id, + }, + payload: { + approved: false, + entity_name: contribution.entity.name, + }, + }), + }); + if (!res.ok) { + console.log("reject contribution notif:", res.status, res.statusText); + } + const json = await res.json(); + if (json.data.error) { + console.log( + "Error while triggering rejected review notification", + json.data.error + ); + } + } + await deleteImageFromVercel(contribution.proof_image); await db.delete(reviewTasks).where(eq(reviewTasks.id, id)); revalidateTag(getTag("review", id.toString())); @@ -125,7 +174,8 @@ export const acceptContribution = async (id: ReviewTask["id"]) => { if ( !contribution || (contribution.editor_id === session.user.id && - !canReviewOwnContribution(session.user.role)) + !canReviewOwnContribution(session.user.role)) || + !canReviewOthersContribution(session.user.role) ) { return { success: false }; } @@ -185,6 +235,31 @@ export const acceptContribution = async (id: ReviewTask["id"]) => { } catch (err) { return { success: false }; } finally { + if (contribution.editor_id !== session.user.id) { + const res = await fetch(`${apiUrl}/notification`, { + headers, + method: "POST", + body: JSON.stringify({ + to: { + subscriberId: contribution.editor_id, + }, + payload: { + approved: true, + entity_name: contribution.entity.name, + }, + }), + }); + if (!res.ok) { + console.log("accept contribution notif:", res.status, res.statusText); + } + const json = await res.json(); + if (json.data.error) { + console.log( + "Error while triggering accepted review notification", + json.data.error + ); + } + } revalidateTag(getTag("review", id.toString())); await wait(1); revalidateTag(getTag("all reviews")); diff --git a/app/account/page.tsx b/app/account/page.tsx index 5df7833..a29148b 100644 --- a/app/account/page.tsx +++ b/app/account/page.tsx @@ -26,7 +26,7 @@ import { Section, Separator, TextField, -} from "@radix-ui/themes"; +} from "@v1s10n_4/radix-ui-themes"; import React, { FC, Suspense } from "react"; diff --git a/app/account/utils.tsx b/app/account/utils.tsx index 42d1e2f..9954266 100644 --- a/app/account/utils.tsx +++ b/app/account/utils.tsx @@ -9,7 +9,13 @@ import { import SubmitButton from "@/components/SubmitButton"; import { Invader, ReviewTask, User } from "@/db"; import { getState } from "@/utils/data"; -import { Flex, Inset, Separator, Skeleton, Text } from "@radix-ui/themes"; +import { + Flex, + Inset, + Separator, + Skeleton, + Text, +} from "@v1s10n_4/radix-ui-themes"; import React, { FC } from "react"; export const ReferralLinkSkeleton = () => ( diff --git a/app/api/get-thumbnail/route.ts b/app/api/get-thumbnail/route.ts index 1dd74b6..658ca83 100644 --- a/app/api/get-thumbnail/route.ts +++ b/app/api/get-thumbnail/route.ts @@ -2,6 +2,7 @@ import { NextRequest } from "next/server"; import sharp from "sharp"; export const runtime = "nodejs"; +export const dynamic = "force-static"; export async function GET(request: NextRequest): Promise { const param = request.nextUrl.searchParams.get("url"); if (!param) return Response.json({ error: true, data: null }); diff --git a/app/api/invaders/[invaderName]/OG/route.tsx b/app/api/invaders/[invaderName]/OG/route.tsx index 7b0e2be..0b9883b 100644 --- a/app/api/invaders/[invaderName]/OG/route.tsx +++ b/app/api/invaders/[invaderName]/OG/route.tsx @@ -4,6 +4,7 @@ import { ImageResponse } from "next/og"; import { NextRequest } from "next/server"; export const runtime = "edge"; +export const dynamic = "force-static"; type RouteParams = { params: { invaderName: string } }; diff --git a/app/api/invaders/[invaderName]/route.ts b/app/api/invaders/[invaderName]/route.ts index 0bab669..0d63f65 100644 --- a/app/api/invaders/[invaderName]/route.ts +++ b/app/api/invaders/[invaderName]/route.ts @@ -4,6 +4,7 @@ import { eq } from "drizzle-orm"; import { NextRequest } from "next/server"; export const runtime = "edge"; +export const dynamic = "force-static"; type RouteParams = { params: { invaderName: string } }; diff --git a/app/api/invaders/route.ts b/app/api/invaders/route.ts index 25ade56..19dc227 100644 --- a/app/api/invaders/route.ts +++ b/app/api/invaders/route.ts @@ -2,6 +2,7 @@ import { db } from "@/db"; import { invaders } from "@/db/schema/invaders"; export const runtime = "edge"; +export const dynamic = "force-static"; export async function GET(): Promise { const list = await db.select().from(invaders); diff --git a/app/api/map/invaders/route.ts b/app/api/map/invaders/route.ts index 3cee1c0..4cdc18d 100644 --- a/app/api/map/invaders/route.ts +++ b/app/api/map/invaders/route.ts @@ -3,6 +3,7 @@ import { invaders } from "@/db/schema/invaders"; import { isNotNull } from "drizzle-orm"; export const runtime = "edge"; +export const dynamic = "force-static"; export async function GET(): Promise { const list = await db diff --git a/app/api/notification/route.ts b/app/api/notification/route.ts new file mode 100644 index 0000000..e15baea --- /dev/null +++ b/app/api/notification/route.ts @@ -0,0 +1,10 @@ +import { NextRequest } from "next/server"; +import { contributionReviewed } from "@/novu/workflows"; + +export const runtime = "nodejs"; + +export async function POST(request: NextRequest): Promise { + const json = await request.json(); + const res = await contributionReviewed.trigger(json); + return Response.json({ error: !!res.data.error, data: res.data }); +} diff --git a/app/api/novu/route.ts b/app/api/novu/route.ts new file mode 100644 index 0000000..3a0fc26 --- /dev/null +++ b/app/api/novu/route.ts @@ -0,0 +1,10 @@ +import { contributionReviewed } from "@/novu/workflows"; +// @ts-ignore +import { serve } from "@novu/framework/next"; +// import { serve } from "@novu/framework/dist/servers/next"; + +export const runtime = "nodejs"; +// the workflows collection can hold as many workflow definitions as you need +export const { GET, POST, OPTIONS } = serve({ + workflows: [contributionReviewed], +}); diff --git a/app/api/upload-image/route.ts b/app/api/upload-image/route.ts index c9447eb..b481185 100644 --- a/app/api/upload-image/route.ts +++ b/app/api/upload-image/route.ts @@ -18,11 +18,12 @@ export async function POST(request: NextRequest): Promise { const buffer = await safeData.data.arrayBuffer(); const thumbnailBuffer = await sharp(buffer) .resize({ withoutEnlargement: true, width: 900 }) - .toFormat("avif", { quality: 80, effort: 6, chromaSubsampling: "4:4:4" }) + .webp({ quality: 80 }) + // .toFormat("avif", { quality: 80, effort: 6, chromaSubsampling: "4:4:4" }) .toBuffer(); const blobRes = await put( - `reviews/${safeData.data.name}.avif`, + `reviews/${safeData.data.name}.webp`, thumbnailBuffer, { access: "public", diff --git a/app/auth-error/utils.tsx b/app/auth-error/utils.tsx index 7c7c69c..9d4ec56 100644 --- a/app/auth-error/utils.tsx +++ b/app/auth-error/utils.tsx @@ -1,4 +1,4 @@ -import { Button } from "@radix-ui/themes"; +import { Button } from "@v1s10n_4/radix-ui-themes"; import Link from "next/link"; export type ErrorPageParam = "Configuration" | "AccessDenied" | "Verification"; diff --git a/app/bell-ring.svg b/app/bell-ring.svg new file mode 100644 index 0000000..8b0a886 --- /dev/null +++ b/app/bell-ring.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/bell.svg b/app/bell.svg new file mode 100644 index 0000000..6dc71fe --- /dev/null +++ b/app/bell.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/fesse/InvalidateTag.tsx b/app/fesse/InvalidateTag.tsx index 5e2a5ce..0624c28 100644 --- a/app/fesse/InvalidateTag.tsx +++ b/app/fesse/InvalidateTag.tsx @@ -2,7 +2,7 @@ import { invalidateTag } from "@/app/fesse/actions"; import SubmitButton from "@/components/SubmitButton"; import { TagName } from "@/utils/revalidation-tags"; -import { Text, TextField } from "@radix-ui/themes"; +import { Text, TextField } from "@v1s10n_4/radix-ui-themes"; import Repeat from "pixelarticons/svg/repeat.svg"; import { FC, useActionState } from "react"; diff --git a/app/globals.css b/app/globals.css index 648a6cb..0684b31 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,5 +1,5 @@ @import "tailwindcss/base"; -@import "@radix-ui/themes/styles.css"; +@import "@v1s10n_4/radix-ui-themes/styles.css"; @tailwind components; @tailwind utilities; diff --git a/app/help/page.tsx b/app/help/page.tsx index c156c8e..7d24036 100644 --- a/app/help/page.tsx +++ b/app/help/page.tsx @@ -9,7 +9,7 @@ import { Separator, Text, TextArea, -} from "@radix-ui/themes"; +} from "@v1s10n_4/radix-ui-themes"; import { Metadata } from "next"; import { redirect } from "next/navigation"; diff --git a/app/help/thanks/page.tsx b/app/help/thanks/page.tsx index 5e078c7..4b293c1 100644 --- a/app/help/thanks/page.tsx +++ b/app/help/thanks/page.tsx @@ -6,7 +6,7 @@ import { Heading, Section, Text, -} from "@radix-ui/themes"; +} from "@v1s10n_4/radix-ui-themes"; export const runtime = "edge"; diff --git a/app/highscores/HighscoreItem.tsx b/app/highscores/HighscoreItem.tsx index 70447ce..9e3ebab 100644 --- a/app/highscores/HighscoreItem.tsx +++ b/app/highscores/HighscoreItem.tsx @@ -1,5 +1,5 @@ import { HighScore } from "@/types/FlashInvadersAPI"; -import { Table } from "@radix-ui/themes"; +import { Table } from "@v1s10n_4/radix-ui-themes"; export const HighscoreItem = ({ rank, diff --git a/app/highscores/layout.tsx b/app/highscores/layout.tsx index 8bd3c58..4693c91 100644 --- a/app/highscores/layout.tsx +++ b/app/highscores/layout.tsx @@ -1,5 +1,11 @@ import SubmitButton from "@/components/SubmitButton"; -import { Container, Flex, Heading, Section, TextField } from "@radix-ui/themes"; +import { + Container, + Flex, + Heading, + Section, + TextField, +} from "@v1s10n_4/radix-ui-themes"; import { redirect } from "next/navigation"; import Search from "pixelarticons/svg/search.svg"; import { FC, PropsWithChildren } from "react"; diff --git a/app/highscores/loading.tsx b/app/highscores/loading.tsx index 35f80d0..ae8a517 100644 --- a/app/highscores/loading.tsx +++ b/app/highscores/loading.tsx @@ -1,5 +1,5 @@ import { getRandomLengthString } from "@/app/highscores/utils"; -import { Skeleton, Table } from "@radix-ui/themes"; +import { Skeleton, Table } from "@v1s10n_4/radix-ui-themes"; const HighscoresLoading = () => ( diff --git a/app/highscores/page.tsx b/app/highscores/page.tsx index 3761fbb..932e51b 100644 --- a/app/highscores/page.tsx +++ b/app/highscores/page.tsx @@ -2,7 +2,7 @@ import HighscoreItem from "@/app/highscores/HighscoreItem"; import { FlashInvadersAPI } from "@/app/highscores/utils"; import { HighScoresResponse } from "@/types/FlashInvadersAPI"; import { getRequestConfig } from "@/utils/revalidation-tags"; -import { Table } from "@radix-ui/themes"; +import { Table } from "@v1s10n_4/radix-ui-themes"; const getHighScores: () => Promise = async () => { const { highscores, fetchOptions } = FlashInvadersAPI; diff --git a/app/highscores/search/[userName]/page.tsx b/app/highscores/search/[userName]/page.tsx index e346134..1f83722 100644 --- a/app/highscores/search/[userName]/page.tsx +++ b/app/highscores/search/[userName]/page.tsx @@ -2,7 +2,7 @@ import HighscoreItem from "@/app/highscores/HighscoreItem"; import { FlashInvadersAPI } from "@/app/highscores/utils"; import { UserSearchResponse } from "@/types/FlashInvadersAPI"; import { getRequestConfig } from "@/utils/revalidation-tags"; -import { Table } from "@radix-ui/themes"; +import { Table } from "@v1s10n_4/radix-ui-themes"; type Params = { params: { userName: string } }; export const revalidate = 0; diff --git a/app/highscores/search/layout.tsx b/app/highscores/search/layout.tsx index 2ff3817..3a18614 100644 --- a/app/highscores/search/layout.tsx +++ b/app/highscores/search/layout.tsx @@ -1,4 +1,4 @@ -import { Button, Flex } from "@radix-ui/themes"; +import { Button, Flex } from "@v1s10n_4/radix-ui-themes"; import Link from "next/link"; import { FC, PropsWithChildren } from "react"; diff --git a/app/layout.tsx b/app/layout.tsx index ee68d3f..0f51b40 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -10,16 +10,19 @@ import { Skeleton, Text, Theme, -} from "@radix-ui/themes"; +} from "@v1s10n_4/radix-ui-themes"; import { Analytics } from "@vercel/analytics/react"; import { SpeedInsights } from "@vercel/speed-insights/next"; import { clsx } from "clsx"; import { Metadata, Viewport } from "next"; import { SessionProvider } from "next-auth/react"; import localFont from "next/font/local"; +import Script from "next/script"; import React, { ReactNode, Suspense } from "react"; +import Notifications from "@/app/Notifications"; export const runtime = "edge"; +export const fetchCache = "default-cache"; const sixtyfour = localFont({ // src: "../public/assets/fonts/Sixtyfour[BLED,SCAN].woff2", @@ -216,6 +219,9 @@ export default function RootLayout({ children }: { children: ReactNode }) { content="width=device-width, initial-scale=1, maximum-scale=1" > + {process.env.LOCAL === "true" && ( +