Skip to content

Commit

Permalink
feat: pull events from buddydatasheets
Browse files Browse the repository at this point in the history
  • Loading branch information
CostasAK committed Jul 28, 2024
1 parent 67d2196 commit 18c9267
Show file tree
Hide file tree
Showing 18 changed files with 1,087 additions and 40 deletions.
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module.exports = {
"plugin:react/jsx-runtime",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended",
"plugin:@tanstack/eslint-plugin-query/recommended",
"plugin:import/recommended",
"plugin:import/typescript",
"plugin:jsx-a11y/recommended",
Expand Down
13 changes: 12 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,33 +30,44 @@
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.2",
"@tanstack/query-sync-storage-persister": "^5.51.15",
"@tanstack/react-query": "^5.51.15",
"@tanstack/react-query-devtools": "^5.51.15",
"@tanstack/react-query-persist-client": "^5.51.15",
"@uidotdev/usehooks": "^2.4.1",
"axios": "^1.7.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"dayjs": "^1.11.12",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsx-a11y": "^6.9.0",
"lz-string": "^1.5.0",
"papaparse": "^5.4.1",
"prettier": "^3.3.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-helmet-async": "^2.0.5",
"react-markdown": "^9.0.1",
"react-router-dom": "^6.25.1",
"tailwind-merge": "^2.4.0",
"tailwindcss-animate": "^1.0.7"
"tailwindcss-animate": "^1.0.7",
"zod": "^3.23.8"
},
"devDependencies": {
"@commitlint/cli": "^19.3.0",
"@commitlint/config-conventional": "^19.2.2",
"@commitlint/cz-commitlint": "^18.6.1",
"@svgr/core": "^8.1.0",
"@svgr/plugin-jsx": "^8.1.0",
"@tanstack/eslint-plugin-query": "^5.51.15",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.4.8",
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.5.2",
"@types/eslint__js": "^8.42.3",
"@types/node": "^20.14.12",
"@types/papaparse": "^5.3.14",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
Expand Down
918 changes: 918 additions & 0 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions src/api/buddy-data-sheets.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Link } from "@/components/link";
import Markdown from "react-markdown";
import { z } from "zod";

export const SHEETS = {
Timers: {
gid: 0,
schema: z.array(
z.object({
title: z.string(),
description: z.string().transform((x) => (
<Markdown
components={{
a(props) {
const { children, ...rest } = props;
return <Link {...rest}>{children}</Link>;
},
}}
>
{x}
</Markdown>
)),
type: z.enum(["maintenance", "event", "reset"]),
start: z.string().transform((x) => new Date(x).getTime()),
end: z.string().transform((x) => new Date(x).getTime()),
}),
),
},
};
31 changes: 31 additions & 0 deletions src/api/buddy-data.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { SHEETS } from "@/api/buddy-data-sheets";
import { queryClient } from "@/api/query-client";
import axios from "axios";
import { parse } from "papaparse";

export type BuddyDataSheets = keyof typeof SHEETS;

const buddyData = axios.create({
baseURL:
"https://docs.google.com/spreadsheets/d/e/2PACX-1vT6RyWZRbuYrNfSnbHEa2PBXgETOgZtfhYgN6QlTqDr5PRVM9IlG93otgT_S_cppwdxAhoIqTc_EAsM",
method: "GET",
headers: {},
});

const urlParams = (gid: number) => `pub?single=true&output=csv&gid=${gid}`;

export const queryBuddyData = async (sheet: BuddyDataSheets) => {
const { data } = await buddyData({
url: urlParams(SHEETS[sheet].gid),
});

console.log(parse(data, { header: true }).data);

return SHEETS[sheet].schema.parse(parse(data, { header: true }).data);
};

export const ensureBuddyData = async (sheet: BuddyDataSheets) =>
await queryClient.ensureQueryData({
queryKey: ["buddyData", sheet],
queryFn: () => queryBuddyData(sheet),
});
36 changes: 36 additions & 0 deletions src/api/query-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { MINUTE, SECOND, WEEK } from "@/constants/time";
import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister";
import { QueryClient } from "@tanstack/react-query";
import { removeOldestQuery } from "@tanstack/react-query-persist-client";
import { compress, decompress } from "lz-string";

const CACHE_MAX_AGE = 2 * WEEK;
const MAX_RETRY_DELAY = 64 * SECOND;

export const queryClient = new QueryClient({
defaultOptions: {
queries: {
gcTime: CACHE_MAX_AGE,
staleTime: 5 * SECOND,
retry: true,
retryDelay: (attemptIndex) =>
attemptIndex < 6
? Math.min(
Math.ceil(SECOND * (2 ** attemptIndex + Math.random())),
MAX_RETRY_DELAY,
)
: MAX_RETRY_DELAY,
refetchInterval: MINUTE,
},
},
});

const persister = createSyncStoragePersister({
storage: window.localStorage,
retry: removeOldestQuery,
key: "FFXIV Buddy React Query Persister",
serialize: (data) => compress(JSON.stringify(data)),
deserialize: (data) => JSON.parse(decompress(data)),
});

export const persistOptions = { persister, maxAge: CACHE_MAX_AGE };
14 changes: 11 additions & 3 deletions src/components/render-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { persistOptions, queryClient } from "@/api/query-client";
import { TooltipProvider } from "@/components/ui/tooltip";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client";
import { PropsWithChildren } from "react";
import { Helmet, HelmetProvider } from "react-helmet-async";

Expand All @@ -8,9 +11,14 @@ export const RenderWrapper = ({ children }: Props) => (
<HelmetProvider>
<Helmet
defaultTitle={import.meta.env.VITE_APP_NAME}
titleTemplate={`%s - ${import.meta.env.VITE_APP_NAME}`}
titleTemplate={`%s · ${import.meta.env.VITE_APP_NAME}`}
/>

<TooltipProvider delayDuration={500}>{children}</TooltipProvider>
<PersistQueryClientProvider
client={queryClient}
persistOptions={persistOptions}
>
<TooltipProvider delayDuration={500}>{children}</TooltipProvider>
<ReactQueryDevtools initialIsOpen={false} />
</PersistQueryClientProvider>
</HelmetProvider>
);
8 changes: 5 additions & 3 deletions src/components/timer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ const iconUrls = {
reset: lodestoneUpdates,
};

export type TimerType = "maintenance" | "event" | "reset";

type TimerIconProps = {
type: "maintenance" | "event" | "reset";
type: TimerType;
};

function TimerIcon({ type }: TimerIconProps) {
Expand Down Expand Up @@ -87,7 +89,7 @@ const timerVariants = cva(

export interface TimerProps extends TimerIconProps {
title: string;
description?: string | string[] | ReactNode;
description?: ReactNode;
start: number;
end?: number;
period?: number;
Expand Down Expand Up @@ -152,7 +154,7 @@ export function Timer({
<TimerIcon type={type} />
{title}
</DialogTitle>
{period && (
{period > 0 && (
<DialogDescription>
{"Every " +
humanizeDuration(period)
Expand Down
9 changes: 9 additions & 0 deletions src/hooks/use-query-buddy-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { BuddyDataSheets, queryBuddyData } from "@/api/buddy-data";
import { useQuery } from "@tanstack/react-query";

export const useQueryBuddyData = (sheet: BuddyDataSheets) => {
return useQuery({
queryKey: ["buddyData", sheet],
queryFn: () => queryBuddyData(sheet),
});
};
4 changes: 2 additions & 2 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ img[src$=".png"] {
@apply box-content;
}
ul {
@apply list-image-dot2 pl-6;
@apply list-disc list-image-dot2 pl-6;
}
ul > ul {
@apply list-image-dot3;
@apply list-disc list-image-dot3 pl-3;
}
ol {
@apply list-inside list-decimal;
Expand Down
15 changes: 13 additions & 2 deletions src/layout/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import { cn } from "@/utils/cn";
import { PropsWithChildren } from "react";
import { Helmet } from "react-helmet-async";

interface PageProps extends PropsWithChildren {
className?: string;
title?: string;
}

export default function Page({ children, className }: PageProps) {
return <section className={cn("mx-auto", className)}>{children}</section>;
export default function Page({ children, className, title }: PageProps) {
return (
<section className={cn("mx-auto", className)}>
{title && (
<Helmet>
<title>{title}</title>
</Helmet>
)}
{children}
</section>
);
}
4 changes: 2 additions & 2 deletions src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import "./index.css";

import { NoMatch } from "@/pages/no-match";
import { NoMatch } from "@/routes/no-match";
import { pages } from "@/routes/pages";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
Expand All @@ -10,7 +10,7 @@ import {
RouterProvider,
} from "react-router-dom";
import { RenderWrapper } from "./components/render-wrapper";
import ErrorPage from "./pages/error";
import ErrorPage from "./routes/error";
import Root from "./routes/root";

const rootElement = document.getElementById("root")!;
Expand Down
2 changes: 1 addition & 1 deletion src/routes/checklist.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ export function Component() {
}, []);

return (
<Page className="columns-md gap-8">
<Page title="Checklist" className="columns-md gap-8">
{todos.map((category, i) => (
<TodoCategory key={i} {...category} className="mb-6" />
))}
Expand Down
File renamed without changes.
File renamed without changes.
6 changes: 6 additions & 0 deletions src/routes/timers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ensureBuddyData } from "@/api/buddy-data";
import { Component } from "@/routes/timers/timers";

export const loader = async () => await ensureBuddyData("Timers");

export { Component };
35 changes: 10 additions & 25 deletions src/routes/timers.tsx → src/routes/timers/timers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,10 @@ import Page from "@/layout/page";
import { Timer, TimerProps } from "@/components/timer";
import { RESETS } from "@/constants/resets";
import { MINUTE } from "@/constants/time";
import { useQueryBuddyData } from "@/hooks/use-query-buddy-data";
import { useSyncedInterval } from "@/hooks/use-synced-interval";
import { nextTime } from "@/utils/next-time";

// async function load(url: string) {
// const obj = await (await fetch(url)).json();
// return obj;
// }
// const event_promise = load(
// "https://raw.githubusercontent.com/CostasAK/ffxiv-timers/events/event.json",
// );

interface TimerType extends Omit<TimerProps, "now"> {}

function nextSortTime(timer: TimerType, now: number) {
Expand All @@ -40,27 +33,19 @@ function sortTimers(timers: TimerType[], now: number) {
}

export function Component() {
const now = useSyncedInterval(MINUTE);
const { data: events } = useQueryBuddyData("Timers");

// const [events, setEvents] = useState([]);
// event_promise.then((result) =>
// setEvents(
// result.map((event: TimerProps) => {
// if (event?.start) {
// event.start = new Date(event.start).getTime();
// }
// if (event?.end) {
// event.end = new Date(event.end).getTime();
// }
// return event;
// }),
// ),
// );
const now = useSyncedInterval(MINUTE);

const timers = sortTimers(RESETS, now);
const timers = events
? sortTimers([...events, ...RESETS], now)
: sortTimers(RESETS, now);

return (
<Page className="flex max-w-screen-lg flex-row flex-wrap items-stretch justify-around overflow-hidden rounded border-b-4 border-sky-700 shadow">
<Page
title="Timers"
className="flex max-w-screen-lg flex-row flex-wrap items-stretch justify-around overflow-hidden rounded border-b-4 border-sky-700 shadow"
>
{timers.map((timer) => (
<Timer
key={timer.title}
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"lib": ["DOM", "DOM.Iterable", "ESNext", "dom", "dom.iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
Expand Down

0 comments on commit 18c9267

Please sign in to comment.