aboutsummaryrefslogtreecommitdiffhomepage
path: root/frontend/src/atoms
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/atoms')
-rw-r--r--frontend/src/atoms/articles.ts46
-rw-r--r--frontend/src/atoms/auth.ts46
-rw-r--r--frontend/src/atoms/feeds.ts10
-rw-r--r--frontend/src/atoms/index.ts15
4 files changed, 117 insertions, 0 deletions
diff --git a/frontend/src/atoms/articles.ts b/frontend/src/atoms/articles.ts
new file mode 100644
index 0000000..46c57a4
--- /dev/null
+++ b/frontend/src/atoms/articles.ts
@@ -0,0 +1,46 @@
+import type { InfiniteData } from "@tanstack/query-core";
+import { atom } from "jotai";
+import { atomWithInfiniteQuery } from "jotai-tanstack-query";
+import type { components } from "../api/generated";
+import { api } from "../services/api-client";
+
+type ArticleConnection = components["schemas"]["ArticleConnection"];
+
+export const articleViewAtom = atom<"read" | "unread">("unread");
+export const articleFeedFilterAtom = atom<string | null>(null);
+
+export const articlesInfiniteAtom = atomWithInfiniteQuery<
+ ArticleConnection,
+ Error,
+ InfiniteData<ArticleConnection, string | null>,
+ string[],
+ string | null
+>((get) => {
+ const view = get(articleViewAtom);
+ const feedId = get(articleFeedFilterAtom);
+
+ return {
+ queryKey: ["articles", view, feedId ?? "all"],
+ queryFn: async ({ pageParam }) => {
+ const query: { feedId?: string; after?: string } = {};
+ if (feedId) query.feedId = feedId;
+ if (pageParam) query.after = pageParam;
+
+ if (view === "read") {
+ const { data } = await api.GET("/api/articles/read", {
+ params: { query },
+ });
+ return data ?? { articles: [], pageInfo: { hasNextPage: false } };
+ }
+ const { data } = await api.GET("/api/articles/unread", {
+ params: { query },
+ });
+ return data ?? { articles: [], pageInfo: { hasNextPage: false } };
+ },
+ initialPageParam: null as string | null,
+ getNextPageParam: (lastPage: ArticleConnection) =>
+ lastPage.pageInfo.hasNextPage
+ ? (lastPage.pageInfo.endCursor ?? null)
+ : null,
+ };
+});
diff --git a/frontend/src/atoms/auth.ts b/frontend/src/atoms/auth.ts
new file mode 100644
index 0000000..dd6f06c
--- /dev/null
+++ b/frontend/src/atoms/auth.ts
@@ -0,0 +1,46 @@
+import { atom, useSetAtom } from "jotai";
+import { useEffect } from "react";
+import type { components } from "../api/generated";
+import { api } from "../services/api-client";
+
+type User = components["schemas"]["User"];
+
+export const userAtom = atom<User | null>(null);
+export const authLoadingAtom = atom<boolean>(true);
+export const isLoggedInAtom = atom<boolean>((get) => get(userAtom) !== null);
+
+export const loginAtom = atom(
+ null,
+ async (
+ _get,
+ set,
+ { username, password }: { username: string; password: string },
+ ) => {
+ const { data, error } = await api.POST("/api/auth/login", {
+ body: { username, password },
+ });
+ if (error) {
+ throw new Error(error.message);
+ }
+ set(userAtom, data.user);
+ },
+);
+
+export const logoutAtom = atom(null, async (_get, set) => {
+ await api.POST("/api/auth/logout");
+ set(userAtom, null);
+});
+
+export function useAuthInit() {
+ const setUser = useSetAtom(userAtom);
+ const setAuthLoading = useSetAtom(authLoadingAtom);
+
+ useEffect(() => {
+ api.GET("/api/auth/me").then(({ data }) => {
+ if (data) {
+ setUser(data);
+ }
+ setAuthLoading(false);
+ });
+ }, [setUser, setAuthLoading]);
+}
diff --git a/frontend/src/atoms/feeds.ts b/frontend/src/atoms/feeds.ts
new file mode 100644
index 0000000..5c39735
--- /dev/null
+++ b/frontend/src/atoms/feeds.ts
@@ -0,0 +1,10 @@
+import { atomWithSuspenseQuery } from "jotai-tanstack-query";
+import { api } from "../services/api-client";
+
+export const feedsAtom = atomWithSuspenseQuery(() => ({
+ queryKey: ["feeds"],
+ queryFn: async () => {
+ const { data } = await api.GET("/api/feeds");
+ return data ?? [];
+ },
+}));
diff --git a/frontend/src/atoms/index.ts b/frontend/src/atoms/index.ts
new file mode 100644
index 0000000..fdcf7e9
--- /dev/null
+++ b/frontend/src/atoms/index.ts
@@ -0,0 +1,15 @@
+export {
+ articleFeedFilterAtom,
+ articlesInfiniteAtom,
+ articleViewAtom,
+} from "./articles";
+export {
+ authLoadingAtom,
+ isLoggedInAtom,
+ loginAtom,
+ logoutAtom,
+ useAuthInit,
+ userAtom,
+} from "./auth";
+
+export { feedsAtom } from "./feeds";