diff --git a/package-lock.json b/package-lock.json index 8c115ac56..2362a5269 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "hasInstallScript": true, "dependencies": { "@bam.tech/react-native-image-resizer": "3.0.11", + "@comapeo/core-react": "2.0.1", "@comapeo/ipc": "2.1.0", "@expo-google-fonts/rubik": "0.2.3", "@formatjs/intl-getcanonicallocales": "2.5.4", @@ -2459,6 +2460,19 @@ "yauzl-promise": "^4.0.0" } }, + "node_modules/@comapeo/core-react": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@comapeo/core-react/-/core-react-2.0.1.tgz", + "integrity": "sha512-q7MCj9l3scVAjxAhf0Db5KjatN9VcidxwgUSzmP/qn7Qph7LB8Xyx7+Gh/bhZjkF9nTzrUd1Tkv7tD6DpjBw9g==", + "license": "MIT", + "peerDependencies": { + "@comapeo/core": "*", + "@comapeo/ipc": "*", + "@comapeo/schema": "*", + "@tanstack/react-query": "^5", + "react": "^18 || ^19" + } + }, "node_modules/@comapeo/core/node_modules/@sinclair/typebox": { "version": "0.33.22", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.33.22.tgz", diff --git a/package.json b/package.json index 67717bd7b..10d220fb7 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ }, "dependencies": { "@bam.tech/react-native-image-resizer": "3.0.11", + "@comapeo/core-react": "2.0.1", "@comapeo/ipc": "2.1.0", "@expo-google-fonts/rubik": "0.2.3", "@formatjs/intl-getcanonicallocales": "2.5.4", diff --git a/src/frontend/Navigation/Stack/index.tsx b/src/frontend/Navigation/Stack/index.tsx index 90157d3ea..b2daf1b78 100644 --- a/src/frontend/Navigation/Stack/index.tsx +++ b/src/frontend/Navigation/Stack/index.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import {useClientApi} from '@comapeo/core-react'; import {createNativeStackNavigator} from '@react-navigation/native-stack'; import {NativeStackNavigationOptions} from '@react-navigation/native-stack/lib/typescript/src/types'; import {WHITE} from '../../lib/styles'; @@ -17,7 +18,6 @@ import {getInitialRouteName} from '../../utils/navigation'; import {createDefaultScreenGroup} from './AppScreens'; import {createOnboardingScreens} from './OnboardingScreens'; import {useSuspenseQuery} from '@tanstack/react-query'; -import {useApi} from '../../contexts/ApiContext'; import {Loading} from '../../sharedComponents/Loading'; import {useSecurityContext} from '../../contexts/SecurityContext'; import {useNavigationFromRoot} from '../../hooks/useNavigationWithTypes'; @@ -63,7 +63,7 @@ function RootStackNavigatorChild() { store => store.value, ); const {data: presets} = usePresetsQuery(); - const mapeoApi = useApi(); + const mapeoApi = useClientApi(); const deviceInfo = useSuspenseQuery({ queryKey: [DEVICE_INFO_KEY], diff --git a/src/frontend/contexts/ActiveProjectContext.tsx b/src/frontend/contexts/ActiveProjectContext.tsx index 6f49a7515..185825979 100644 --- a/src/frontend/contexts/ActiveProjectContext.tsx +++ b/src/frontend/contexts/ActiveProjectContext.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; +import {useClientApi} from '@comapeo/core-react'; import {type MapeoProjectApi} from '@comapeo/ipc'; import {usePersistedProjectId} from '../hooks/persistedState/usePersistedProjectId'; import {useProject, useCreateProject} from '../hooks/server/projects'; import {Loading} from '../sharedComponents/Loading'; -import {useApi} from './ApiContext'; const ActiveProjectContext = React.createContext< {projectId: string; projectApi: MapeoProjectApi} | undefined @@ -13,7 +13,7 @@ const ActiveProjectContext = React.createContext< export const ActiveProjectProvider = ({ children, }: React.PropsWithChildren<{}>) => { - const mapeoApi = useApi(); + const mapeoApi = useClientApi(); const activeProjectId = usePersistedProjectId(store => store.projectId); const setActiveProjectId = usePersistedProjectId(store => store.setProjectId); diff --git a/src/frontend/contexts/ApiContext.tsx b/src/frontend/contexts/ApiContext.tsx deleted file mode 100644 index 0a1dbb672..000000000 --- a/src/frontend/contexts/ApiContext.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import * as React from 'react'; -import {type MapeoClientApi} from '@comapeo/ipc'; - -// TODO: support passing api mock for unit tests -const ApiContext = React.createContext(undefined); - -export type ApiProviderProps = { - api: MapeoClientApi; - children?: React.ReactNode; -}; - -export const ApiProvider = ({api, children}: ApiProviderProps): JSX.Element => { - return {children}; -}; - -export function useApi() { - const api = React.useContext(ApiContext); - if (!api) { - throw new Error('No Api set, use ApiProvider to set one'); - } - return api; -} diff --git a/src/frontend/contexts/AppProviders.tsx b/src/frontend/contexts/AppProviders.tsx index 903ddfe1e..7fffea7a2 100644 --- a/src/frontend/contexts/AppProviders.tsx +++ b/src/frontend/contexts/AppProviders.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import {ClientApiProvider} from '@comapeo/core-react'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; import {QueryClient, QueryClientProvider} from '@tanstack/react-query'; import {StyleSheet} from 'react-native'; @@ -19,7 +20,6 @@ import { } from './LocalDiscoveryContext'; import {type MapeoClientApi} from '@comapeo/ipc'; import {ServerLoading} from '../ServerLoading'; -import {ApiProvider} from './ApiContext'; import {MessagePortLike} from '../lib/MessagePortLike'; import {IntlProvider} from './IntlContext'; import {BottomSheetModalProvider} from '@gorhom/bottom-sheet'; @@ -55,7 +55,7 @@ export const AppProviders = ({ - + @@ -67,7 +67,7 @@ export const AppProviders = ({ - + diff --git a/src/frontend/hooks/server/deviceInfo.ts b/src/frontend/hooks/server/deviceInfo.ts index db6ca1c55..6c1ac7e59 100644 --- a/src/frontend/hooks/server/deviceInfo.ts +++ b/src/frontend/hooks/server/deviceInfo.ts @@ -1,12 +1,11 @@ +import {useClientApi} from '@comapeo/core-react'; import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'; import {deviceType, DeviceType} from 'expo-device'; -import {useApi} from '../../contexts/ApiContext'; - export const DEVICE_INFO_KEY = 'deviceInfo'; export const useDeviceInfo = () => { - const mapeoApi = useApi(); + const mapeoApi = useClientApi(); return useQuery({ queryKey: [DEVICE_INFO_KEY], @@ -17,7 +16,7 @@ export const useDeviceInfo = () => { }; export const useEditDeviceInfo = () => { - const mapeoApi = useApi(); + const mapeoApi = useClientApi(); const queryClient = useQueryClient(); return useMutation({ diff --git a/src/frontend/hooks/server/invites.ts b/src/frontend/hooks/server/invites.ts index 066c4122d..a7a253641 100644 --- a/src/frontend/hooks/server/invites.ts +++ b/src/frontend/hooks/server/invites.ts @@ -1,10 +1,10 @@ +import {useClientApi} from '@comapeo/core-react'; import { useMutation, useQueryClient, useSuspenseQuery, } from '@tanstack/react-query'; -import {useApi} from '../../contexts/ApiContext'; import {useActiveProject} from '../../contexts/ActiveProjectContext'; import {usePersistedProjectId} from '../persistedState/usePersistedProjectId'; import {ALL_PROJECTS_KEY, PROJECT_MEMBERS_KEY} from './projects'; @@ -12,7 +12,7 @@ import {ALL_PROJECTS_KEY, PROJECT_MEMBERS_KEY} from './projects'; export const INVITE_KEY = 'pending_invites'; export function usePendingInvites() { - const mapeoApi = useApi(); + const mapeoApi = useClientApi(); return useSuspenseQuery({ queryKey: [INVITE_KEY], queryFn: async () => { @@ -22,7 +22,7 @@ export function usePendingInvites() { } export function useAcceptInvite() { - const mapeoApi = useApi(); + const mapeoApi = useClientApi(); const queryClient = useQueryClient(); const switchActiveProject = usePersistedProjectId( state => state.setProjectId, @@ -46,7 +46,7 @@ export function useAcceptInvite() { } export function useRejectInvite() { - const mapeoApi = useApi(); + const mapeoApi = useClientApi(); const queryClient = useQueryClient(); return useMutation({ mutationFn: async ({inviteId}: {inviteId: string}) => { diff --git a/src/frontend/hooks/server/maps.ts b/src/frontend/hooks/server/maps.ts index 040c9bedb..d08574dd7 100644 --- a/src/frontend/hooks/server/maps.ts +++ b/src/frontend/hooks/server/maps.ts @@ -1,8 +1,8 @@ +import {useClientApi} from '@comapeo/core-react'; import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'; import * as FileSystem from 'expo-file-system'; import * as v from 'valibot'; -import {useApi} from '../../contexts/ApiContext'; import {DOCUMENT_DIRECTORY} from '../../lib/file-system'; import {createRefreshTokenStore} from '../refreshTokenStore'; @@ -28,7 +28,7 @@ export type CustomMapInfo = v.InferOutput; const {useRefreshToken, useRefreshTokenActions} = createRefreshTokenStore(); export function useMapStyleJsonUrl() { - const api = useApi(); + const api = useClientApi(); const refreshToken = useRefreshToken(); return useQuery({ @@ -42,7 +42,7 @@ export function useMapStyleJsonUrl() { export function useImportCustomMapFile() { const queryClient = useQueryClient(); - const api = useApi(); + const api = useClientApi(); const {refresh} = useRefreshTokenActions(); return useMutation({ @@ -96,7 +96,7 @@ export function useRemoveCustomMapFile() { * Returns `null` if no viable map is found. Throws an error if a detected map is invalid. */ export function useGetCustomMapInfo() { - const api = useApi(); + const api = useClientApi(); return useQuery({ queryKey: [MAPS_QUERY_KEY, 'custom', 'info'], diff --git a/src/frontend/hooks/server/mediaSync.ts b/src/frontend/hooks/server/mediaSync.ts index e665e07bd..1c7ec2531 100644 --- a/src/frontend/hooks/server/mediaSync.ts +++ b/src/frontend/hooks/server/mediaSync.ts @@ -1,9 +1,9 @@ +import {useClientApi} from '@comapeo/core-react'; import { useMutation, useQueryClient, useSuspenseQuery, } from '@tanstack/react-query'; -import {useApi} from '../../contexts/ApiContext'; import {MediaSyncSetting} from '../../sharedTypes'; export const MEDIA_SYNC_SETTING_KEY = 'media_sync_setting'; @@ -17,7 +17,7 @@ export function isArchiveDevice(value: MediaSyncSetting): boolean { } export function useGetMediaSyncSetting() { - const api = useApi(); + const api = useClientApi(); return useSuspenseQuery({ queryKey: [MEDIA_SYNC_SETTING_KEY], @@ -29,7 +29,7 @@ export function useGetMediaSyncSetting() { } export function useSetMediaSyncSetting() { - const api = useApi(); + const api = useClientApi(); const queryClient = useQueryClient(); return useMutation({ diff --git a/src/frontend/hooks/server/projects.ts b/src/frontend/hooks/server/projects.ts index e5e6d20f3..29e630e5f 100644 --- a/src/frontend/hooks/server/projects.ts +++ b/src/frontend/hooks/server/projects.ts @@ -1,6 +1,6 @@ +import {useClientApi} from '@comapeo/core-react'; import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'; -import {useApi} from '../../contexts/ApiContext'; import {useActiveProject} from '../../contexts/ActiveProjectContext'; import {PRESETS_KEY} from './presets'; import {ICONS_KEY} from './icons'; @@ -17,7 +17,7 @@ export const THIS_USERS_ROLE_KEY = 'my_role'; export const REMOTE_ARCHIVE = 'remote_archive'; export function useProject(projectId?: string) { - const api = useApi(); + const api = useClientApi(); return useQuery({ queryKey: [PROJECT_KEY, projectId], @@ -33,7 +33,7 @@ export function useProject(projectId?: string) { } export function useAllProjects() { - const api = useApi(); + const api = useClientApi(); return useQuery({ queryKey: [ALL_PROJECTS_KEY], @@ -44,7 +44,7 @@ export function useAllProjects() { } export function useCreateProject() { - const api = useApi(); + const api = useClientApi(); const queryClient = useQueryClient(); return useMutation({ @@ -107,7 +107,7 @@ export const useOriginalVersionIdToDeviceId = (originalVersionId: string) => { }; export function useLeaveProject() { - const mapeoApi = useApi(); + const mapeoApi = useClientApi(); const queryClient = useQueryClient(); diff --git a/src/frontend/hooks/useLocalPeers.ts b/src/frontend/hooks/useLocalPeers.ts index abd1bce9d..72838ad0c 100644 --- a/src/frontend/hooks/useLocalPeers.ts +++ b/src/frontend/hooks/useLocalPeers.ts @@ -1,6 +1,6 @@ -import {type MapeoClientApi} from '@comapeo/ipc'; import {useSyncExternalStore} from 'react'; -import {useApi} from '../contexts/ApiContext'; +import {useClientApi} from '@comapeo/core-react'; +import {type MapeoClientApi} from '@comapeo/ipc'; type LocalPeer = Awaited>[number]; @@ -10,7 +10,7 @@ let localPeerState: ReturnType | undefined; * @returns An array of local peers (includes peers that were previously connected but are no longer connected) */ export function useLocalPeers(): LocalPeer[] { - const api = useApi(); + const api = useClientApi(); if (!localPeerState) { localPeerState = createLocalPeerState(api); } diff --git a/src/frontend/hooks/useProjectInvitesListener.ts b/src/frontend/hooks/useProjectInvitesListener.ts index 8a8facf71..d795279a2 100644 --- a/src/frontend/hooks/useProjectInvitesListener.ts +++ b/src/frontend/hooks/useProjectInvitesListener.ts @@ -1,8 +1,8 @@ import {useCallback, useEffect, useState} from 'react'; -import {useApi} from '../contexts/ApiContext'; import {useQueryClient} from '@tanstack/react-query'; import {INVITE_KEY} from './server/invites'; import {Invite, InviteRemovalReason} from '@comapeo/core/dist/invite-api'; +import {useClientApi} from '@comapeo/core-react'; export const useProjectInvitesListener = ({ inviteId, @@ -11,7 +11,7 @@ export const useProjectInvitesListener = ({ inviteId?: string; bottomSheetIsOpen: boolean; }) => { - const mapeoApi = useApi(); + const mapeoApi = useClientApi(); const queryClient = useQueryClient(); const [currentInviteCanceled, setCurrentInviteCancelled] = useState(false); diff --git a/src/frontend/screens/Settings/CreateTestData.tsx b/src/frontend/screens/Settings/CreateTestData.tsx index fe53f369b..1f9b7c983 100644 --- a/src/frontend/screens/Settings/CreateTestData.tsx +++ b/src/frontend/screens/Settings/CreateTestData.tsx @@ -1,3 +1,4 @@ +import {useClientApi} from '@comapeo/core-react'; import {useMutation, useQueryClient} from '@tanstack/react-query'; import {lengthToDegrees} from '@turf/helpers'; import {randomPosition} from '@turf/random'; @@ -9,7 +10,6 @@ import {StyleSheet, TextInput, ToastAndroid, View} from 'react-native'; import {UIActivityIndicator} from 'react-native-indicators'; import {useActiveProject} from '../../contexts/ActiveProjectContext'; -import {useApi} from '../../contexts/ApiContext'; import {OBSERVATION_KEY} from '../../hooks/server/observations'; import {useLocation} from '../../hooks/useLocation'; import {LIGHT_GREY, RED, WHITE} from '../../lib/styles'; @@ -225,7 +225,7 @@ const styles = StyleSheet.create({ function useCreateFakeObservationsMutation() { const queryClient = useQueryClient(); - const mapeoApi = useApi(); + const mapeoApi = useClientApi(); const {projectApi, projectId} = useActiveProject(); return useMutation({ diff --git a/src/frontend/sharedComponents/ProjectInviteBottomSheet/index.tsx b/src/frontend/sharedComponents/ProjectInviteBottomSheet/index.tsx index e4cbf1791..1af750bf7 100644 --- a/src/frontend/sharedComponents/ProjectInviteBottomSheet/index.tsx +++ b/src/frontend/sharedComponents/ProjectInviteBottomSheet/index.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import {Invite, InviteRemovalReason} from '@comapeo/core/dist/invite-api'; +import {useClientApi} from '@comapeo/core-react'; import {BottomSheetModal, useBottomSheetModal} from '../BottomSheetModal'; import { @@ -13,7 +14,6 @@ import {InviteSuccessBottomSheetContent} from './InviteSuccessBottomSheetContent import {InviteCanceledBottomSheetContent} from './InviteCanceledBottomSheetContent'; import {useAllProjects} from '../../hooks/server/projects'; import {LeaveProjectModalContent} from '../LeaveProjectModalContent'; -import {useApi} from '../../contexts/ApiContext'; export type LeaveProjectModalState = 'AlreadyOnProj' | 'LeaveProj'; @@ -169,7 +169,7 @@ export const ProjectInviteBottomSheet = ({ }; function useAcceptedInvite() { - const api = useApi(); + const api = useClientApi(); const [acceptedInvite, setAcceptedInvite] = React.useState( null, );