diff options
| author | Mohamed Bassem <me@mbassem.com> | 2026-02-01 12:29:54 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-02-01 12:29:54 +0000 |
| commit | 65f6e83f11c82b0ec762e11f3392a80e614ee69a (patch) | |
| tree | 945d8d73122f07fe6a77c2bd3ac9db566939ba3b /apps/mobile | |
| parent | e516a525bca6f319a2f003e9677624e968b277bf (diff) | |
| download | karakeep-65f6e83f11c82b0ec762e11f3392a80e614ee69a.tar.zst | |
refactor: migrate trpc to the new react query integration mode (#2438)
* refactor: migrate trpc to the new react query integration mode
* more fixes
* more migrations
* upgrade trpc client
Diffstat (limited to 'apps/mobile')
21 files changed, 250 insertions, 170 deletions
diff --git a/apps/mobile/app/dashboard/(tabs)/highlights.tsx b/apps/mobile/app/dashboard/(tabs)/highlights.tsx index 7879081b..8a0a8ae3 100644 --- a/apps/mobile/app/dashboard/(tabs)/highlights.tsx +++ b/apps/mobile/app/dashboard/(tabs)/highlights.tsx @@ -4,11 +4,13 @@ import HighlightList from "@/components/highlights/HighlightList"; import CustomSafeAreaView from "@/components/ui/CustomSafeAreaView"; import FullPageSpinner from "@/components/ui/FullPageSpinner"; import PageTitle from "@/components/ui/PageTitle"; +import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query"; -import { api } from "@karakeep/shared-react/trpc"; +import { useTRPC } from "@karakeep/shared-react/trpc"; export default function Highlights() { - const apiUtils = api.useUtils(); + const api = useTRPC(); + const queryClient = useQueryClient(); const { data, isPending, @@ -17,12 +19,14 @@ export default function Highlights() { fetchNextPage, isFetchingNextPage, refetch, - } = api.highlights.getAll.useInfiniteQuery( - {}, - { - initialCursor: null, - getNextPageParam: (lastPage) => lastPage.nextCursor, - }, + } = useInfiniteQuery( + api.highlights.getAll.infiniteQueryOptions( + {}, + { + initialCursor: null, + getNextPageParam: (lastPage) => lastPage.nextCursor, + }, + ), ); if (error) { @@ -34,7 +38,7 @@ export default function Highlights() { } const onRefresh = () => { - apiUtils.highlights.getAll.invalidate(); + queryClient.invalidateQueries(api.highlights.getAll.pathFilter()); }; return ( diff --git a/apps/mobile/app/dashboard/(tabs)/lists.tsx b/apps/mobile/app/dashboard/(tabs)/lists.tsx index 45c23a28..5719c67c 100644 --- a/apps/mobile/app/dashboard/(tabs)/lists.tsx +++ b/apps/mobile/app/dashboard/(tabs)/lists.tsx @@ -8,9 +8,10 @@ import CustomSafeAreaView from "@/components/ui/CustomSafeAreaView"; import FullPageSpinner from "@/components/ui/FullPageSpinner"; import PageTitle from "@/components/ui/PageTitle"; import { Text } from "@/components/ui/Text"; -import { api } from "@/lib/trpc"; +import { useTRPC } from "@/lib/trpc"; import { useColorScheme } from "@/lib/useColorScheme"; import { condProps } from "@/lib/utils"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; import { Plus } from "lucide-react-native"; import { useBookmarkLists } from "@karakeep/shared-react/hooks/lists"; @@ -84,8 +85,9 @@ export default function Lists() { const [showChildrenOf, setShowChildrenOf] = useState<Record<string, boolean>>( {}, ); - const apiUtils = api.useUtils(); - const { data: listStats } = api.lists.stats.useQuery(); + const api = useTRPC(); + const queryClient = useQueryClient(); + const { data: listStats } = useQuery(api.lists.stats.queryOptions()); // Check if there are any shared lists const hasSharedLists = useMemo(() => { @@ -116,8 +118,8 @@ export default function Lists() { } const onRefresh = () => { - apiUtils.lists.list.invalidate(); - apiUtils.lists.stats.invalidate(); + queryClient.invalidateQueries(api.lists.list.pathFilter()); + queryClient.invalidateQueries(api.lists.stats.pathFilter()); }; const links: ListLink[] = [ diff --git a/apps/mobile/app/dashboard/(tabs)/settings.tsx b/apps/mobile/app/dashboard/(tabs)/settings.tsx index 106baec5..2610aa37 100644 --- a/apps/mobile/app/dashboard/(tabs)/settings.tsx +++ b/apps/mobile/app/dashboard/(tabs)/settings.tsx @@ -13,7 +13,8 @@ import { Text } from "@/components/ui/Text"; import { useServerVersion } from "@/lib/hooks"; import { useSession } from "@/lib/session"; import useAppSettings from "@/lib/settings"; -import { api } from "@/lib/trpc"; +import { useTRPC } from "@/lib/trpc"; +import { useQuery } from "@tanstack/react-query"; export default function Dashboard() { const { logout } = useSession(); @@ -22,6 +23,7 @@ export default function Dashboard() { setSettings, isLoading: isSettingsLoading, } = useAppSettings(); + const api = useTRPC(); const imageQuality = useSharedValue(0); const imageQualityMin = useSharedValue(0); @@ -31,7 +33,7 @@ export default function Dashboard() { imageQuality.value = settings.imageQuality * 100; }, [settings]); - const { data, error } = api.users.whoami.useQuery(); + const { data, error } = useQuery(api.users.whoami.queryOptions()); const { data: serverVersion, isLoading: isServerVersionLoading, diff --git a/apps/mobile/app/dashboard/(tabs)/tags.tsx b/apps/mobile/app/dashboard/(tabs)/tags.tsx index c0ac2d49..470ff3f3 100644 --- a/apps/mobile/app/dashboard/(tabs)/tags.tsx +++ b/apps/mobile/app/dashboard/(tabs)/tags.tsx @@ -8,7 +8,8 @@ import FullPageSpinner from "@/components/ui/FullPageSpinner"; import PageTitle from "@/components/ui/PageTitle"; import { SearchInput } from "@/components/ui/SearchInput"; import { Text } from "@/components/ui/Text"; -import { api } from "@/lib/trpc"; +import { useTRPC } from "@/lib/trpc"; +import { useQueryClient } from "@tanstack/react-query"; import { usePaginatedSearchTags } from "@karakeep/shared-react/hooks/tags"; import { useDebounce } from "@karakeep/shared-react/hooks/use-debounce"; @@ -23,7 +24,8 @@ interface TagItem { export default function Tags() { const [refreshing, setRefreshing] = useState(false); const [searchQuery, setSearchQuery] = useState(""); - const apiUtils = api.useUtils(); + const api = useTRPC(); + const queryClient = useQueryClient(); // Debounce search query to avoid too many API calls const debouncedSearch = useDebounce(searchQuery, 300); @@ -56,7 +58,7 @@ export default function Tags() { } const onRefresh = () => { - apiUtils.tags.list.invalidate(); + queryClient.invalidateQueries(api.tags.list.pathFilter()); }; const tags: TagItem[] = data.tags.map((tag) => ({ diff --git a/apps/mobile/app/dashboard/bookmarks/[slug]/index.tsx b/apps/mobile/app/dashboard/bookmarks/[slug]/index.tsx index 8fd04115..567ac605 100644 --- a/apps/mobile/app/dashboard/bookmarks/[slug]/index.tsx +++ b/apps/mobile/app/dashboard/bookmarks/[slug]/index.tsx @@ -12,7 +12,8 @@ import BottomActions from "@/components/bookmarks/BottomActions"; import FullPageError from "@/components/FullPageError"; import FullPageSpinner from "@/components/ui/FullPageSpinner"; import useAppSettings from "@/lib/settings"; -import { api } from "@/lib/trpc"; +import { useTRPC } from "@/lib/trpc"; +import { useQuery } from "@tanstack/react-query"; import { Settings } from "lucide-react-native"; import { useColorScheme } from "nativewind"; @@ -25,6 +26,7 @@ export default function BookmarkView() { const { colorScheme } = useColorScheme(); const isDark = colorScheme === "dark"; const { settings } = useAppSettings(); + const api = useTRPC(); const [bookmarkLinkType, setBookmarkLinkType] = useState<BookmarkLinkType>( settings.defaultBookmarkView, @@ -38,10 +40,12 @@ export default function BookmarkView() { data: bookmark, error, refetch, - } = api.bookmarks.getBookmark.useQuery({ - bookmarkId: slug, - includeContent: false, - }); + } = useQuery( + api.bookmarks.getBookmark.queryOptions({ + bookmarkId: slug, + includeContent: false, + }), + ); if (error) { return <FullPageError error={error.message} onRetry={refetch} />; diff --git a/apps/mobile/app/dashboard/bookmarks/[slug]/manage_lists.tsx b/apps/mobile/app/dashboard/bookmarks/[slug]/manage_lists.tsx index 8402bb0b..1070207b 100644 --- a/apps/mobile/app/dashboard/bookmarks/[slug]/manage_lists.tsx +++ b/apps/mobile/app/dashboard/bookmarks/[slug]/manage_lists.tsx @@ -5,15 +5,18 @@ import { useLocalSearchParams } from "expo-router"; import CustomSafeAreaView from "@/components/ui/CustomSafeAreaView"; import { Text } from "@/components/ui/Text"; import { useToast } from "@/components/ui/Toast"; +import { useQuery } from "@tanstack/react-query"; +import type { ZBookmarkList } from "@karakeep/shared/types/lists"; import { useAddBookmarkToList, useBookmarkLists, useRemoveBookmarkFromList, } from "@karakeep/shared-react/hooks/lists"; -import { api } from "@karakeep/shared-react/trpc"; +import { useTRPC } from "@karakeep/shared-react/trpc"; const ListPickerPage = () => { + const api = useTRPC(); const { slug: bookmarkId } = useLocalSearchParams(); if (typeof bookmarkId !== "string") { throw new Error("Unexpected param type"); @@ -26,13 +29,16 @@ const ListPickerPage = () => { showProgress: false, }); }; - const { data: existingLists } = api.lists.getListsOfBookmark.useQuery( - { - bookmarkId, - }, - { - select: (data) => new Set(data.lists.map((l) => l.id)), - }, + const { data: existingLists } = useQuery( + api.lists.getListsOfBookmark.queryOptions( + { + bookmarkId, + }, + { + select: (data: { lists: ZBookmarkList[] }) => + new Set(data.lists.map((l) => l.id)), + }, + ), ); const { data } = useBookmarkLists(); diff --git a/apps/mobile/app/dashboard/bookmarks/[slug]/manage_tags.tsx b/apps/mobile/app/dashboard/bookmarks/[slug]/manage_tags.tsx index 984bc224..64d057f2 100644 --- a/apps/mobile/app/dashboard/bookmarks/[slug]/manage_tags.tsx +++ b/apps/mobile/app/dashboard/bookmarks/[slug]/manage_tags.tsx @@ -6,17 +6,19 @@ import FullPageSpinner from "@/components/ui/FullPageSpinner"; import { Text } from "@/components/ui/Text"; import { useToast } from "@/components/ui/Toast"; import { useColorScheme } from "@/lib/useColorScheme"; +import { useQuery } from "@tanstack/react-query"; import { Check, Plus } from "lucide-react-native"; import { useAutoRefreshingBookmarkQuery, useUpdateBookmarkTags, } from "@karakeep/shared-react/hooks/bookmarks"; -import { api } from "@karakeep/shared-react/trpc"; +import { useTRPC } from "@karakeep/shared-react/trpc"; const NEW_TAG_ID = "new-tag"; const ListPickerPage = () => { + const api = useTRPC(); const { colors } = useColorScheme(); const { slug: bookmarkId } = useLocalSearchParams(); @@ -34,22 +36,24 @@ const ListPickerPage = () => { }); }; - const { data: allTags, isPending: isAllTagsPending } = api.tags.list.useQuery( - {}, - { - select: React.useCallback( - (data: { tags: { id: string; name: string }[] }) => { - return data.tags - .map((t) => ({ - id: t.id, - name: t.name, - lowered: t.name.toLowerCase(), - })) - .sort((a, b) => a.lowered.localeCompare(b.lowered)); - }, - [], - ), - }, + const { data: allTags, isPending: isAllTagsPending } = useQuery( + api.tags.list.queryOptions( + {}, + { + select: React.useCallback( + (data: { tags: { id: string; name: string }[] }) => { + return data.tags + .map((t) => ({ + id: t.id, + name: t.name, + lowered: t.name.toLowerCase(), + })) + .sort((a, b) => a.lowered.localeCompare(b.lowered)); + }, + [], + ), + }, + ), ); const { data: existingTags } = useAutoRefreshingBookmarkQuery({ bookmarkId, diff --git a/apps/mobile/app/dashboard/lists/[slug]/edit.tsx b/apps/mobile/app/dashboard/lists/[slug]/edit.tsx index 6ccc2f26..e0654722 100644 --- a/apps/mobile/app/dashboard/lists/[slug]/edit.tsx +++ b/apps/mobile/app/dashboard/lists/[slug]/edit.tsx @@ -7,7 +7,8 @@ import FullPageSpinner from "@/components/ui/FullPageSpinner"; import { Input } from "@/components/ui/Input"; import { Text } from "@/components/ui/Text"; import { useToast } from "@/components/ui/Toast"; -import { api } from "@/lib/trpc"; +import { useTRPC } from "@/lib/trpc"; +import { useQuery } from "@tanstack/react-query"; import { useEditBookmarkList } from "@karakeep/shared-react/hooks/lists"; @@ -16,6 +17,7 @@ const EditListPage = () => { const [text, setText] = useState(""); const [query, setQuery] = useState(""); const { toast } = useToast(); + const api = useTRPC(); const { mutate, isPending: editIsPending } = useEditBookmarkList({ onSuccess: () => { dismiss(); @@ -41,9 +43,11 @@ const EditListPage = () => { throw new Error("Unexpected param type"); } - const { data: list, isLoading: fetchIsPending } = api.lists.get.useQuery({ - listId, - }); + const { data: list, isLoading: fetchIsPending } = useQuery( + api.lists.get.queryOptions({ + listId, + }), + ); const dismiss = () => { router.back(); diff --git a/apps/mobile/app/dashboard/lists/[slug]/index.tsx b/apps/mobile/app/dashboard/lists/[slug]/index.tsx index 11379588..97f797c6 100644 --- a/apps/mobile/app/dashboard/lists/[slug]/index.tsx +++ b/apps/mobile/app/dashboard/lists/[slug]/index.tsx @@ -5,14 +5,16 @@ import UpdatingBookmarkList from "@/components/bookmarks/UpdatingBookmarkList"; import FullPageError from "@/components/FullPageError"; import CustomSafeAreaView from "@/components/ui/CustomSafeAreaView"; import FullPageSpinner from "@/components/ui/FullPageSpinner"; -import { api } from "@/lib/trpc"; +import { useTRPC } from "@/lib/trpc"; import { MenuView } from "@react-native-menu/menu"; +import { useMutation, useQuery } from "@tanstack/react-query"; import { Ellipsis } from "lucide-react-native"; import { ZBookmarkList } from "@karakeep/shared/types/lists"; export default function ListView() { const { slug } = useLocalSearchParams(); + const api = useTRPC(); if (typeof slug !== "string") { throw new Error("Unexpected param type"); } @@ -20,7 +22,7 @@ export default function ListView() { data: list, error, refetch, - } = api.lists.get.useQuery({ listId: slug }); + } = useQuery(api.lists.get.queryOptions({ listId: slug })); return ( <CustomSafeAreaView> @@ -58,17 +60,22 @@ function ListActionsMenu({ listId: string; role: ZBookmarkList["userRole"]; }) { - const { mutate: deleteList } = api.lists.delete.useMutation({ - onSuccess: () => { - router.replace("/dashboard/lists"); - }, - }); + const api = useTRPC(); + const { mutate: deleteList } = useMutation( + api.lists.delete.mutationOptions({ + onSuccess: () => { + router.replace("/dashboard/lists"); + }, + }), + ); - const { mutate: leaveList } = api.lists.leaveList.useMutation({ - onSuccess: () => { - router.replace("/dashboard/lists"); - }, - }); + const { mutate: leaveList } = useMutation( + api.lists.leaveList.mutationOptions({ + onSuccess: () => { + router.replace("/dashboard/lists"); + }, + }), + ); const handleDelete = () => { Alert.alert("Delete List", "Are you sure you want to delete this list?", [ diff --git a/apps/mobile/app/dashboard/search.tsx b/apps/mobile/app/dashboard/search.tsx index ab89ce8d..0e59c607 100644 --- a/apps/mobile/app/dashboard/search.tsx +++ b/apps/mobile/app/dashboard/search.tsx @@ -7,9 +7,13 @@ import CustomSafeAreaView from "@/components/ui/CustomSafeAreaView"; import FullPageSpinner from "@/components/ui/FullPageSpinner"; import { SearchInput } from "@/components/ui/SearchInput"; import { Text } from "@/components/ui/Text"; -import { api } from "@/lib/trpc"; +import { useTRPC } from "@/lib/trpc"; import AsyncStorage from "@react-native-async-storage/async-storage"; -import { keepPreviousData } from "@tanstack/react-query"; +import { + keepPreviousData, + useInfiniteQuery, + useQueryClient, +} from "@tanstack/react-query"; import { useSearchHistory } from "@karakeep/shared-react/hooks/search-history"; import { useDebounce } from "@karakeep/shared-react/hooks/use-debounce"; @@ -29,7 +33,12 @@ export default function Search() { removeItem: (k: string) => AsyncStorage.removeItem(k), }); - const onRefresh = api.useUtils().bookmarks.searchBookmarks.invalidate; + const api = useTRPC(); + const queryClient = useQueryClient(); + + const onRefresh = () => { + queryClient.invalidateQueries(api.bookmarks.searchBookmarks.pathFilter()); + }; const { data, @@ -39,14 +48,16 @@ export default function Search() { isFetching, fetchNextPage, isFetchingNextPage, - } = api.bookmarks.searchBookmarks.useInfiniteQuery( - { text: query }, - { - placeholderData: keepPreviousData, - gcTime: 0, - initialCursor: null, - getNextPageParam: (lastPage) => lastPage.nextCursor, - }, + } = useInfiniteQuery( + api.bookmarks.searchBookmarks.infiniteQueryOptions( + { text: query }, + { + placeholderData: keepPreviousData, + gcTime: 0, + initialCursor: null, + getNextPageParam: (lastPage) => lastPage.nextCursor, + }, + ), ); const filteredHistory = useMemo(() => { diff --git a/apps/mobile/app/dashboard/tags/[slug].tsx b/apps/mobile/app/dashboard/tags/[slug].tsx index 3f294328..170cb04d 100644 --- a/apps/mobile/app/dashboard/tags/[slug].tsx +++ b/apps/mobile/app/dashboard/tags/[slug].tsx @@ -4,15 +4,21 @@ import UpdatingBookmarkList from "@/components/bookmarks/UpdatingBookmarkList"; import FullPageError from "@/components/FullPageError"; import CustomSafeAreaView from "@/components/ui/CustomSafeAreaView"; import FullPageSpinner from "@/components/ui/FullPageSpinner"; -import { api } from "@/lib/trpc"; +import { useTRPC } from "@/lib/trpc"; +import { useQuery } from "@tanstack/react-query"; export default function TagView() { const { slug } = useLocalSearchParams(); + const api = useTRPC(); if (typeof slug !== "string") { throw new Error("Unexpected param type"); } - const { data: tag, error, refetch } = api.tags.get.useQuery({ tagId: slug }); + const { + data: tag, + error, + refetch, + } = useQuery(api.tags.get.queryOptions({ tagId: slug })); return ( <CustomSafeAreaView> diff --git a/apps/mobile/app/sharing.tsx b/apps/mobile/app/sharing.tsx index 3e2b6bfb..6d9167db 100644 --- a/apps/mobile/app/sharing.tsx +++ b/apps/mobile/app/sharing.tsx @@ -5,8 +5,9 @@ import { useShareIntentContext } from "expo-share-intent"; import { Button } from "@/components/ui/Button"; import { Text } from "@/components/ui/Text"; import useAppSettings from "@/lib/settings"; -import { api } from "@/lib/trpc"; +import { useTRPC } from "@/lib/trpc"; import { useUploadAsset } from "@/lib/upload"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; import { z } from "zod"; import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks"; @@ -18,8 +19,11 @@ type Mode = | { type: "error" }; function SaveBookmark({ setMode }: { setMode: (mode: Mode) => void }) { + const api = useTRPC(); + const queryClient = useQueryClient(); + const onSaved = (d: ZBookmark & { alreadyExists: boolean }) => { - invalidateAllBookmarks(); + queryClient.invalidateQueries(api.bookmarks.getBookmarks.pathFilter()); setMode({ type: d.alreadyExists ? "alreadyExists" : "success", bookmarkId: d.id, @@ -36,9 +40,6 @@ function SaveBookmark({ setMode }: { setMode: (mode: Mode) => void }) { }, }); - const invalidateAllBookmarks = - api.useUtils().bookmarks.getBookmarks.invalidate; - useEffect(() => { if (isLoading) { return; @@ -77,12 +78,14 @@ function SaveBookmark({ setMode }: { setMode: (mode: Mode) => void }) { } }, [isLoading]); - const { mutate, isPending } = api.bookmarks.createBookmark.useMutation({ - onSuccess: onSaved, - onError: () => { - setMode({ type: "error" }); - }, - }); + const { mutate, isPending } = useMutation( + api.bookmarks.createBookmark.mutationOptions({ + onSuccess: onSaved, + onError: () => { + setMode({ type: "error" }); + }, + }), + ); return ( <View className="flex flex-row gap-3"> diff --git a/apps/mobile/app/signin.tsx b/apps/mobile/app/signin.tsx index 03cbba5a..5c6370ca 100644 --- a/apps/mobile/app/signin.tsx +++ b/apps/mobile/app/signin.tsx @@ -13,7 +13,8 @@ import { Button } from "@/components/ui/Button"; import { Input } from "@/components/ui/Input"; import { Text } from "@/components/ui/Text"; import useAppSettings from "@/lib/settings"; -import { api } from "@/lib/trpc"; +import { useTRPC } from "@/lib/trpc"; +import { useMutation } from "@tanstack/react-query"; import { Bug, Edit3 } from "lucide-react-native"; enum LoginType { @@ -24,6 +25,7 @@ enum LoginType { export default function Signin() { const { settings, setSettings } = useAppSettings(); const router = useRouter(); + const api = useTRPC(); const [error, setError] = useState<string | undefined>(); const [loginType, setLoginType] = useState<LoginType>(LoginType.Password); @@ -43,33 +45,37 @@ export default function Signin() { }; const { mutate: login, isPending: userNamePasswordRequestIsPending } = - api.apiKeys.exchange.useMutation({ - onSuccess: (resp) => { - setSettings({ ...settings, apiKey: resp.key, apiKeyId: resp.id }); - }, - onError: (e) => { - if (e.data?.code === "UNAUTHORIZED") { - setError("Wrong username or password"); - } else { - setError(`${e.message}`); - } - }, - }); + useMutation( + api.apiKeys.exchange.mutationOptions({ + onSuccess: (resp) => { + setSettings({ ...settings, apiKey: resp.key, apiKeyId: resp.id }); + }, + onError: (e) => { + if (e.data?.code === "UNAUTHORIZED") { + setError("Wrong username or password"); + } else { + setError(`${e.message}`); + } + }, + }), + ); const { mutate: validateApiKey, isPending: apiKeyValueRequestIsPending } = - api.apiKeys.validate.useMutation({ - onSuccess: () => { - const apiKey = apiKeyRef.current; - setSettings({ ...settings, apiKey: apiKey }); - }, - onError: (e) => { - if (e.data?.code === "UNAUTHORIZED") { - setError("Invalid API key"); - } else { - setError(`${e.message}`); - } - }, - }); + useMutation( + api.apiKeys.validate.mutationOptions({ + onSuccess: () => { + const apiKey = apiKeyRef.current; + setSettings({ ...settings, apiKey: apiKey }); + }, + onError: (e) => { + if (e.data?.code === "UNAUTHORIZED") { + setError("Invalid API key"); + } else { + setError(`${e.message}`); + } + }, + }), + ); if (settings.apiKey) { return <Redirect href="dashboard" />; diff --git a/apps/mobile/components/bookmarks/BookmarkCard.tsx b/apps/mobile/components/bookmarks/BookmarkCard.tsx index 6c3ef070..38fed737 100644 --- a/apps/mobile/components/bookmarks/BookmarkCard.tsx +++ b/apps/mobile/components/bookmarks/BookmarkCard.tsx @@ -15,9 +15,10 @@ import { router, useRouter } from "expo-router"; import * as Sharing from "expo-sharing"; import { Text } from "@/components/ui/Text"; import useAppSettings from "@/lib/settings"; -import { api } from "@/lib/trpc"; +import { useTRPC } from "@/lib/trpc"; import { buildApiHeaders } from "@/lib/utils"; import { MenuView } from "@react-native-menu/menu"; +import { useQuery } from "@tanstack/react-query"; import { Ellipsis, ShareIcon, Star } from "lucide-react-native"; import type { ZBookmark } from "@karakeep/shared/types/bookmarks"; @@ -477,20 +478,23 @@ export default function BookmarkCard({ }: { bookmark: ZBookmark; }) { - const { data: bookmark } = api.bookmarks.getBookmark.useQuery( - { - bookmarkId: initialData.id, - }, - { - initialData, - refetchInterval: (query) => { - const data = query.state.data; - if (!data) { - return false; - } - return getBookmarkRefreshInterval(data); + const api = useTRPC(); + const { data: bookmark } = useQuery( + api.bookmarks.getBookmark.queryOptions( + { + bookmarkId: initialData.id, + }, + { + initialData, + refetchInterval: (query) => { + const data = query.state.data; + if (!data) { + return false; + } + return getBookmarkRefreshInterval(data); + }, }, - }, + ), ); const router = useRouter(); diff --git a/apps/mobile/components/bookmarks/BookmarkLinkPreview.tsx b/apps/mobile/components/bookmarks/BookmarkLinkPreview.tsx index 4478bdda..aa073c69 100644 --- a/apps/mobile/components/bookmarks/BookmarkLinkPreview.tsx +++ b/apps/mobile/components/bookmarks/BookmarkLinkPreview.tsx @@ -6,8 +6,9 @@ import { WebViewSourceUri } from "react-native-webview/lib/WebViewTypes"; import { Text } from "@/components/ui/Text"; import { useAssetUrl } from "@/lib/hooks"; import { useReaderSettings, WEBVIEW_FONT_FAMILIES } from "@/lib/readerSettings"; -import { api } from "@/lib/trpc"; +import { useTRPC } from "@/lib/trpc"; import { useColorScheme } from "@/lib/useColorScheme"; +import { useQuery } from "@tanstack/react-query"; import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks"; @@ -65,16 +66,19 @@ export function BookmarkLinkReaderPreview({ }) { const { isDarkColorScheme: isDark } = useColorScheme(); const { settings: readerSettings } = useReaderSettings(); + const api = useTRPC(); const { data: bookmarkWithContent, error, isLoading, refetch, - } = api.bookmarks.getBookmark.useQuery({ - bookmarkId: bookmark.id, - includeContent: true, - }); + } = useQuery( + api.bookmarks.getBookmark.queryOptions({ + bookmarkId: bookmark.id, + includeContent: true, + }), + ); if (isLoading) { return <FullPageSpinner />; diff --git a/apps/mobile/components/bookmarks/UpdatingBookmarkList.tsx b/apps/mobile/components/bookmarks/UpdatingBookmarkList.tsx index e627ee16..b5aeaa40 100644 --- a/apps/mobile/components/bookmarks/UpdatingBookmarkList.tsx +++ b/apps/mobile/components/bookmarks/UpdatingBookmarkList.tsx @@ -1,4 +1,5 @@ -import { api } from "@/lib/trpc"; +import { useTRPC } from "@/lib/trpc"; +import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query"; import type { ZGetBookmarksRequest } from "@karakeep/shared/types/bookmarks"; import { BookmarkTypes } from "@karakeep/shared/types/bookmarks"; @@ -14,7 +15,8 @@ export default function UpdatingBookmarkList({ query: Omit<ZGetBookmarksRequest, "sortOrder" | "includeContent">; // Sort order is not supported in mobile yet header?: React.ReactElement; }) { - const apiUtils = api.useUtils(); + const api = useTRPC(); + const queryClient = useQueryClient(); const { data, isPending, @@ -23,12 +25,14 @@ export default function UpdatingBookmarkList({ fetchNextPage, isFetchingNextPage, refetch, - } = api.bookmarks.getBookmarks.useInfiniteQuery( - { ...query, useCursorV2: true, includeContent: false }, - { - initialCursor: null, - getNextPageParam: (lastPage) => lastPage.nextCursor, - }, + } = useInfiniteQuery( + api.bookmarks.getBookmarks.infiniteQueryOptions( + { ...query, useCursorV2: true, includeContent: false }, + { + initialCursor: null, + getNextPageParam: (lastPage) => lastPage.nextCursor, + }, + ), ); if (error) { @@ -40,8 +44,8 @@ export default function UpdatingBookmarkList({ } const onRefresh = () => { - apiUtils.bookmarks.getBookmarks.invalidate(); - apiUtils.bookmarks.getBookmark.invalidate(); + queryClient.invalidateQueries(api.bookmarks.getBookmarks.pathFilter()); + queryClient.invalidateQueries(api.bookmarks.getBookmark.pathFilter()); }; return ( diff --git a/apps/mobile/components/highlights/HighlightCard.tsx b/apps/mobile/components/highlights/HighlightCard.tsx index 7e0b4a2b..c8f97e32 100644 --- a/apps/mobile/components/highlights/HighlightCard.tsx +++ b/apps/mobile/components/highlights/HighlightCard.tsx @@ -2,7 +2,8 @@ import { ActivityIndicator, Alert, Pressable, View } from "react-native"; import * as Haptics from "expo-haptics"; import { useRouter } from "expo-router"; import { Text } from "@/components/ui/Text"; -import { api } from "@/lib/trpc"; +import { useTRPC } from "@/lib/trpc"; +import { useQuery } from "@tanstack/react-query"; import dayjs from "dayjs"; import relativeTime from "dayjs/plugin/relativeTime"; import { ExternalLink, Trash2 } from "lucide-react-native"; @@ -29,6 +30,7 @@ export default function HighlightCard({ }) { const { toast } = useToast(); const router = useRouter(); + const api = useTRPC(); const onError = () => { toast({ @@ -64,13 +66,15 @@ export default function HighlightCard({ ], ); - const { data: bookmark } = api.bookmarks.getBookmark.useQuery( - { - bookmarkId: highlight.bookmarkId, - }, - { - retry: false, - }, + const { data: bookmark } = useQuery( + api.bookmarks.getBookmark.queryOptions( + { + bookmarkId: highlight.bookmarkId, + }, + { + retry: false, + }, + ), ); const handleBookmarkPress = () => { diff --git a/apps/mobile/lib/providers.tsx b/apps/mobile/lib/providers.tsx index 01d2d5b5..4a7def1d 100644 --- a/apps/mobile/lib/providers.tsx +++ b/apps/mobile/lib/providers.tsx @@ -2,7 +2,7 @@ import { useEffect } from "react"; import FullPageSpinner from "@/components/ui/FullPageSpinner"; import { Toaster } from "sonner-native"; -import { TRPCProvider } from "@karakeep/shared-react/providers/trpc-provider"; +import { TRPCSettingsProvider } from "@karakeep/shared-react/providers/trpc-provider"; import { ReaderSettingsProvider } from "./readerSettings"; import useAppSettings from "./settings"; @@ -20,11 +20,11 @@ export function Providers({ children }: { children: React.ReactNode }) { } return ( - <TRPCProvider settings={settings}> + <TRPCSettingsProvider settings={settings}> <ReaderSettingsProvider> {children} <Toaster /> </ReaderSettingsProvider> - </TRPCProvider> + </TRPCSettingsProvider> ); } diff --git a/apps/mobile/lib/session.ts b/apps/mobile/lib/session.ts index 8eb646cb..9f539693 100644 --- a/apps/mobile/lib/session.ts +++ b/apps/mobile/lib/session.ts @@ -1,12 +1,16 @@ import { useCallback } from "react"; +import { useMutation } from "@tanstack/react-query"; import useAppSettings from "./settings"; -import { api } from "./trpc"; +import { useTRPC } from "./trpc"; export function useSession() { const { settings, setSettings } = useAppSettings(); + const api = useTRPC(); - const { mutate: deleteKey } = api.apiKeys.revoke.useMutation(); + const { mutate: deleteKey } = useMutation( + api.apiKeys.revoke.mutationOptions(), + ); const logout = useCallback(() => { if (settings.apiKeyId) { diff --git a/apps/mobile/lib/trpc.ts b/apps/mobile/lib/trpc.ts index e56968b8..915c265d 100644 --- a/apps/mobile/lib/trpc.ts +++ b/apps/mobile/lib/trpc.ts @@ -1,5 +1,3 @@ -import { createTRPCReact } from "@trpc/react-query"; - -import type { AppRouter } from "@karakeep/trpc/routers/_app"; - -export const api = createTRPCReact<AppRouter>(); +// Re-export from shared-react to ensure there's only one TRPCProvider context +// This is necessary because the hooks in shared-react use useTRPC from shared-react +export { TRPCProvider, useTRPC } from "@karakeep/shared-react/trpc"; diff --git a/apps/mobile/lib/upload.ts b/apps/mobile/lib/upload.ts index 06f007f7..13abae16 100644 --- a/apps/mobile/lib/upload.ts +++ b/apps/mobile/lib/upload.ts @@ -1,5 +1,5 @@ import ReactNativeBlobUtil from "react-native-blob-util"; -import { useMutation } from "@tanstack/react-query"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks"; import { @@ -8,7 +8,7 @@ import { } from "@karakeep/shared/types/uploads"; import type { Settings } from "./settings"; -import { api } from "./trpc"; +import { useTRPC } from "./trpc"; import { buildApiHeaders } from "./utils"; export function useUploadAsset( @@ -18,13 +18,13 @@ export function useUploadAsset( onError?: (e: string) => void; }, ) { - const invalidateAllBookmarks = - api.useUtils().bookmarks.getBookmarks.invalidate; + const api = useTRPC(); + const queryClient = useQueryClient(); - const { mutate: createBookmark, isPending: isCreatingBookmark } = - api.bookmarks.createBookmark.useMutation({ + const { mutate: createBookmark, isPending: isCreatingBookmark } = useMutation( + api.bookmarks.createBookmark.mutationOptions({ onSuccess: (d) => { - invalidateAllBookmarks(); + queryClient.invalidateQueries(api.bookmarks.getBookmarks.pathFilter()); if (options.onSuccess) { options.onSuccess(d); } @@ -34,7 +34,8 @@ export function useUploadAsset( options.onError(e.message); } }, - }); + }), + ); const { mutate: uploadAsset, isPending: isUploading } = useMutation({ mutationFn: async (file: { type: string; name: string; uri: string }) => { |
