diff --git a/CLAUDE.md b/CLAUDE.md index 5192660ea4..a15b5bad88 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -431,16 +431,30 @@ yarn intl:compile # Compile translations for runtime // src/state/queries/profile.ts import {useQuery, useMutation, useQueryClient} from '@tanstack/react-query' -// Query key pattern -const RQKEY_ROOT = 'profile' -export const RQKEY = (did: string) => [RQKEY_ROOT, did] +import {createQueryKey} from '#/state/queries/util' -// Query hook +/* + * Query key name should match the query hook name for consistency + */ +const profileQueryKeyRoot = 'profile' + +/* + * Use object params and createQueryKey helper for better readability and to + * avoid bugs with parameter order or types. + */ +export const createProfileQueryKey = (args: {did: string}) => + createQueryKey(profileQueryKeyRoot, args) + +/* + * Query hook should be named use[Name]Query, where [Name] describes the data + * being fetched. This is not a strict requirement, but it's a helpful + * convention for discoverability + */ export function useProfileQuery({did}: {did: string}) { const agent = useAgent() return useQuery({ - queryKey: RQKEY(did), + queryKey: createProfileQueryKey({did}), queryFn: async () => { const res = await agent.getProfile({actor: did}) return res.data @@ -450,8 +464,12 @@ export function useProfileQuery({did}: {did: string}) { }) } -// Mutation hook -export function useUpdateProfile() { +/* + * Mutation hook should match the name of the query hook, but with "Mutation" + * suffix. This is not a strict requirement, but it's a helpful convention for + * discoverability and consistency. + */ +export function useProfileMutation() { const queryClient = useQueryClient() return useMutation({ @@ -459,7 +477,9 @@ export function useUpdateProfile() { // Update logic }, onSuccess: (_, variables) => { - queryClient.invalidateQueries({queryKey: RQKEY(variables.did)}) + queryClient.invalidateQueries({ + queryKey: createProfileQueryKey({did: variables.did}), + }) }, onError: (error) => { if (isNetworkError(error)) { @@ -473,6 +493,24 @@ export function useUpdateProfile() { } }) } + +/* + * If cache mutation is needed, include specific interfaces for the specific + * mutations you require adjacent to the source queries. Naming should be + * descriptive of the mutation's purpose, e.g. use[Name]CacheMutation. This is + * not a strict requirement, but it's a helpful convention for discoverability + * and consistency. + */ +export function useProfileCacheMutation() { + const queryClient = useQueryClient() + + return (data: Partial) => { + queryClient.setQueryData(createProfileQueryKey({did: data.did}), oldData => { + if (!oldData) return oldData + return {...oldData, ...data} + }) + } +} ``` **Stale Time Constants** (from `src/state/queries/index.ts`): @@ -491,7 +529,7 @@ export function useDraftsQuery() { const agent = useAgent() return useInfiniteQuery({ - queryKey: ['drafts'], + queryKey: createQueryKey('drafts'), queryFn: async ({pageParam}) => { const res = await agent.app.bsky.draft.getDrafts({cursor: pageParam}) return res.data @@ -504,6 +542,19 @@ export function useDraftsQuery() { To get all items from pages: `data?.pages.flatMap(page => page.items) ?? []` +**Persisted Queries** + +To persist query data across app restarts, `createQueryKey` supports a third +parameter called `options`, which has a `persistedVersion` property. When this +property is set to a number, the query will be persisted. + +When this property is updated (e.g. incremented), the persisted data will be cleared and replaced with the new data from the query function. This is useful for cases where the shape of the data has changed and old persisted data would no longer be valid. + +```tsx +export const createProfileQueryKey = (args: {did: string}) => + createQueryKey(profileQueryKeyRoot, args, {persistedVersion: 1}) +``` + ### Preferences (React Context) ```tsx diff --git a/package.json b/package.json index 81f9d5e4c0..b2b6b7000f 100644 --- a/package.json +++ b/package.json @@ -167,7 +167,7 @@ "expo-location": "~19.0.8", "expo-media-library": "~18.2.1", "expo-notifications": "~0.32.16", - "expo-paste-input": "^0.1.10", + "expo-paste-input": "^0.1.12", "expo-privacy-sensitive": "^0.1.0", "expo-screen-orientation": "~9.0.8", "expo-sharing": "~14.0.8", diff --git a/src/components/StarterPack/Main/PostsList.tsx b/src/components/StarterPack/Main/PostsList.tsx index e9d7a9a9a2..ebd0dcf59d 100644 --- a/src/components/StarterPack/Main/PostsList.tsx +++ b/src/components/StarterPack/Main/PostsList.tsx @@ -46,6 +46,7 @@ export const PostsList = forwardRef( return ( ) { - return <>{children} +export function Provider({children}: {children: React.ReactNode}) { + return children } export function useHotkeysContext() { diff --git a/src/lib/hotkeys/index.tsx b/src/lib/hotkeys/index.tsx index 6cddf3b13b..17e2c9c7bd 100644 --- a/src/lib/hotkeys/index.tsx +++ b/src/lib/hotkeys/index.tsx @@ -1,4 +1,3 @@ -import React from 'react' import {useLingui} from '@lingui/react/macro' import { HotkeysProvider, diff --git a/src/lib/react-query.tsx b/src/lib/react-query.tsx index b69e715056..aa8ee8ce47 100644 --- a/src/lib/react-query.tsx +++ b/src/lib/react-query.tsx @@ -10,7 +10,7 @@ import { import {createPersistedQueryStorage} from '#/lib/persisted-query-storage' import {listenNetworkConfirmed, listenNetworkLost} from '#/state/events' -import {PERSISTED_QUERY_ROOT} from '#/state/queries' +import {isQueryPersisted} from '#/state/queries/util' import * as env from '#/env' import {IS_NATIVE, IS_WEB} from '#/env' @@ -137,8 +137,7 @@ const dehydrateOptions: PersistQueryClientProviderProps['persistOptions']['dehyd { shouldDehydrateMutation: (_: any) => false, shouldDehydrateQuery: query => { - const root = String(query.queryKey[0]) - return root === PERSISTED_QUERY_ROOT + return isQueryPersisted(query.queryKey) }, } diff --git a/src/locale/locales/en/messages.po b/src/locale/locales/en/messages.po index a26cb20b0b..ea308c4249 100644 --- a/src/locale/locales/en/messages.po +++ b/src/locale/locales/en/messages.po @@ -910,8 +910,8 @@ msgstr "" msgid "Add media to post" msgstr "" -#: src/components/moderation/ReportDialog/index.tsx:522 -#: src/components/moderation/ReportDialog/index.tsx:526 +#: src/components/moderation/ReportDialog/index.tsx:532 +#: src/components/moderation/ReportDialog/index.tsx:536 msgid "Add more details (optional)" msgstr "" @@ -1002,7 +1002,7 @@ msgstr "" msgid "Additional details (limit 1000 characters)" msgstr "" -#: src/components/moderation/ReportDialog/index.tsx:540 +#: src/components/moderation/ReportDialog/index.tsx:550 msgid "Additional details (limit 300 characters)" msgstr "" @@ -1887,7 +1887,7 @@ msgstr "" #. placeholder {0}: sanitizeHandle(item.feed.creator.handle, '@') #. placeholder {0}: sanitizeHandle(labeler.creator.handle, '@') #: src/components/LabelingServiceCard/index.tsx:62 -#: src/components/moderation/ReportDialog/index.tsx:843 +#: src/components/moderation/ReportDialog/index.tsx:853 #: src/screens/Search/components/StarterPackCard.tsx:107 #: src/screens/Search/Explore.tsx:969 msgid "By {0}" @@ -2028,7 +2028,7 @@ msgstr "" msgid "Change Handle" msgstr "" -#: src/components/moderation/ReportDialog/index.tsx:439 +#: src/components/moderation/ReportDialog/index.tsx:449 msgid "Change moderation service" msgstr "" @@ -2041,11 +2041,11 @@ msgstr "" msgid "Change password dialog" msgstr "" -#: src/components/moderation/ReportDialog/index.tsx:304 +#: src/components/moderation/ReportDialog/index.tsx:314 msgid "Change report category" msgstr "" -#: src/components/moderation/ReportDialog/index.tsx:384 +#: src/components/moderation/ReportDialog/index.tsx:394 msgid "Change report reason" msgstr "" @@ -2430,7 +2430,7 @@ msgstr "" msgid "Complete the challenge" msgstr "" -#: src/lib/hotkeys/index.tsx:67 +#: src/lib/hotkeys/index.tsx:66 #: src/view/com/feeds/ComposerPrompt.tsx:147 #: src/view/shell/desktop/LeftNav.tsx:575 msgid "Compose new post" @@ -2919,8 +2919,8 @@ msgstr "" #. Accessibility label for button to create a moderation report for the selected option #. placeholder {0}: option.title -#: src/components/moderation/ReportDialog/index.tsx:703 -#: src/components/moderation/ReportDialog/index.tsx:749 +#: src/components/moderation/ReportDialog/index.tsx:713 +#: src/components/moderation/ReportDialog/index.tsx:759 msgid "Create report for {0}" msgstr "" @@ -4207,7 +4207,7 @@ msgstr "" #. placeholder {0}: sanitizeHandle(feed.creatorHandle, '@') #. placeholder {0}: sanitizeHandle(view.creator.handle, '@') #: src/components/FeedCard.tsx:170 -#: src/state/queries/feed.ts:121 +#: src/state/queries/feed.ts:118 #: src/view/com/feeds/FeedSourceCard.tsx:151 msgid "Feed by {0}" msgstr "" @@ -4394,7 +4394,7 @@ msgstr "" msgid "Focus code input" msgstr "" -#: src/lib/hotkeys/index.tsx:74 +#: src/lib/hotkeys/index.tsx:73 msgid "Focus the search field" msgstr "Focus the search field" @@ -5943,7 +5943,7 @@ msgstr "" msgid "Load new notifications" msgstr "" -#: src/screens/Profile/ProfileFeed/index.tsx:203 +#: src/screens/Profile/ProfileFeed/index.tsx:204 #: src/screens/Profile/Sections/Feed.tsx:117 #: src/screens/ProfileList/FeedSection.tsx:113 #: src/view/com/feeds/FeedPage.tsx:170 @@ -6393,8 +6393,8 @@ msgstr "" msgid "Navigates to your profile" msgstr "" -#: src/components/moderation/ReportDialog/index.tsx:335 -#: src/components/moderation/ReportDialog/index.tsx:352 +#: src/components/moderation/ReportDialog/index.tsx:345 +#: src/components/moderation/ReportDialog/index.tsx:362 msgid "Need to report a copyright violation, legal request, or regulatory compliance issue?" msgstr "" @@ -6480,7 +6480,7 @@ msgstr "" msgid "New password" msgstr "" -#: src/screens/Profile/ProfileFeed/index.tsx:220 +#: src/screens/Profile/ProfileFeed/index.tsx:221 #: src/screens/ProfileList/index.tsx:251 #: src/screens/ProfileList/index.tsx:300 #: src/view/screens/Feeds.tsx:553 @@ -8339,7 +8339,7 @@ msgid "Report conversation" msgstr "" #: src/components/moderation/ReportDialog/index.tsx:98 -#: src/components/moderation/ReportDialog/index.tsx:255 +#: src/components/moderation/ReportDialog/index.tsx:265 msgid "Report dialog" msgstr "" @@ -8554,7 +8554,7 @@ msgstr "" #: src/components/dms/MessageItem.tsx:322 #: src/components/Error.tsx:66 #: src/components/Lists.tsx:115 -#: src/components/moderation/ReportDialog/index.tsx:289 +#: src/components/moderation/ReportDialog/index.tsx:299 #: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx:56 #: src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoFallback.tsx:59 #: src/components/StarterPack/ProfileStarterPacks.tsx:377 @@ -8576,7 +8576,7 @@ msgstr "" msgid "Retry" msgstr "" -#: src/components/moderation/ReportDialog/index.tsx:286 +#: src/components/moderation/ReportDialog/index.tsx:296 #: src/view/screens/Storybook/Admonitions.tsx:61 msgid "Retry loading report options" msgstr "" @@ -8916,7 +8916,7 @@ msgstr "" msgid "Select a color" msgstr "" -#: src/components/moderation/ReportDialog/index.tsx:374 +#: src/components/moderation/ReportDialog/index.tsx:384 msgid "Select a reason" msgstr "" @@ -9001,7 +9001,7 @@ msgstr "" msgid "Select languages" msgstr "" -#: src/components/moderation/ReportDialog/index.tsx:424 +#: src/components/moderation/ReportDialog/index.tsx:434 msgid "Select moderation service" msgstr "" @@ -9100,7 +9100,7 @@ msgstr "" msgid "Send post to..." msgstr "" -#: src/components/moderation/ReportDialog/index.tsx:814 +#: src/components/moderation/ReportDialog/index.tsx:824 msgid "Send report to {title}" msgstr "" @@ -9603,7 +9603,7 @@ msgid "Something went wrong" msgstr "" #: src/components/ageAssurance/AgeAssuranceInitDialog.tsx:139 -#: src/components/moderation/ReportDialog/index.tsx:281 +#: src/components/moderation/ReportDialog/index.tsx:291 #: src/screens/Deactivated.tsx:86 #: src/screens/Settings/components/DeactivateAccountDialog.tsx:59 #: src/view/screens/Storybook/Admonitions.tsx:56 @@ -9788,9 +9788,9 @@ msgstr "" msgid "Submit Appeal" msgstr "" -#: src/components/moderation/ReportDialog/index.tsx:502 -#: src/components/moderation/ReportDialog/index.tsx:563 -#: src/components/moderation/ReportDialog/index.tsx:570 +#: src/components/moderation/ReportDialog/index.tsx:512 +#: src/components/moderation/ReportDialog/index.tsx:573 +#: src/components/moderation/ReportDialog/index.tsx:580 msgid "Submit report" msgstr "" @@ -10185,7 +10185,7 @@ msgid "There was an issue fetching notifications. Tap here to try again." msgstr "" #: src/screens/Search/Explore.tsx:1025 -#: src/view/com/posts/PostFeed.tsx:763 +#: src/view/com/posts/PostFeed.tsx:773 msgid "There was an issue fetching posts. Tap here to try again." msgstr "" @@ -10857,7 +10857,7 @@ msgstr "" msgid "Unfollows the user" msgstr "" -#: src/components/moderation/ReportDialog/index.tsx:486 +#: src/components/moderation/ReportDialog/index.tsx:496 msgid "Unfortunately, none of your subscribed labelers supports this report type." msgstr "" @@ -11154,7 +11154,7 @@ msgid "User list by {0}" msgstr "" #. placeholder {0}: sanitizeHandle(view.creator.handle, '@') -#: src/state/queries/feed.ts:162 +#: src/state/queries/feed.ts:159 msgid "User List by {0}" msgstr "" @@ -12540,7 +12540,7 @@ msgid "Your reply was sent" msgstr "" #. placeholder {0}: state.selectedLabeler?.creator.displayName -#: src/components/moderation/ReportDialog/index.tsx:513 +#: src/components/moderation/ReportDialog/index.tsx:523 msgid "Your report will be sent to <0>{0}." msgstr "" diff --git a/src/screens/Profile/ProfileFeed/index.tsx b/src/screens/Profile/ProfileFeed/index.tsx index 03fae0207a..634779f9db 100644 --- a/src/screens/Profile/ProfileFeed/index.tsx +++ b/src/screens/Profile/ProfileFeed/index.tsx @@ -185,6 +185,7 @@ export function ProfileFeedScreenInner({ [PERSISTED_QUERY_ROOT, 'feed-info', kind, feedUris] +) => + createQueryKey( + 'feed-info', + { + kind, + feedUris, + }, + { + persistedVersion: 1, + }, + ) export function usePinnedFeedsInfos() { const {hasSession} = useSession() @@ -428,11 +435,11 @@ export function usePinnedFeedsInfos() { const pinnedItems = preferences?.savedFeeds.filter(feed => feed.pinned) ?? [] return useQuery({ - queryKey: createPinnedFeedInfosQueryKeyRoot( + queryKey: createPinnedFeedInfosQueryKey( 'pinned', pinnedItems.map(f => f.value), ), - gcTime: PERSISTED_QUERY_GCTIME, + gcTime: GCTIME.INFINITY, staleTime: STALE.INFINITY, enabled: !isLoadingPrefs, queryFn: async () => { @@ -536,11 +543,11 @@ export function useSavedFeeds() { const queryClient = useQueryClient() return useQuery({ - queryKey: createPinnedFeedInfosQueryKeyRoot( + queryKey: createPinnedFeedInfosQueryKey( 'saved', savedItems.map(f => f.value), ), - gcTime: PERSISTED_QUERY_GCTIME, + gcTime: GCTIME.INFINITY, staleTime: STALE.INFINITY, enabled: !isLoadingPrefs, placeholderData: previousData => { diff --git a/src/state/queries/index.ts b/src/state/queries/index.ts index bac6931fd5..183d8c883a 100644 --- a/src/state/queries/index.ts +++ b/src/state/queries/index.ts @@ -19,22 +19,6 @@ export const STALE = { INFINITY: Infinity, } -/** - * Root key for persisted queries. - * - * If the `querykey` of your query uses this at index 0, it will be - * persisted automatically by the `PersistQueryClientProvider` in - * `#/lib/react-query.tsx`. - * - * Be careful when using this, since it will change the query key and may - * break any cases where we call `invalidateQueries` or `refetchQueries` - * with the old key. - * - * Also, only use this for queries that are safe to persist between - * app launches (like user preferences). - * - * Note that for queries that are persisted, it is recommended to extend - * the `gcTime` to a longer duration, otherwise it'll get busted - */ -export const PERSISTED_QUERY_ROOT = 'PERSISTED' -export const PERSISTED_QUERY_GCTIME = Infinity +export const GCTIME = { + INFINITY: Infinity, +} diff --git a/src/state/queries/labeler.ts b/src/state/queries/labeler.ts index 9bf4372739..e7251866fe 100644 --- a/src/state/queries/labeler.ts +++ b/src/state/queries/labeler.ts @@ -3,15 +3,12 @@ import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' import {z} from 'zod' import {MAX_LABELERS} from '#/lib/constants' -import { - PERSISTED_QUERY_GCTIME, - PERSISTED_QUERY_ROOT, - STALE, -} from '#/state/queries' +import {GCTIME, STALE} from '#/state/queries' import { preferencesQueryKey, usePreferencesQuery, } from '#/state/queries/preferences' +import {createQueryKey} from '#/state/queries/util' import {useAgent} from '#/state/session' const labelerInfoQueryKeyRoot = 'labeler-info' @@ -26,11 +23,8 @@ export const labelersInfoQueryKey = (dids: string[]) => [ dids.slice().sort(), ] -const persistedLabelersDetailedInfoQueryKey = (dids: string[]) => [ - PERSISTED_QUERY_ROOT, - 'labelers-detailed-info', - dids, -] +const createLabelersDetailedInfoQueryKey = (dids: string[]) => + createQueryKey('labelers-detailed-info', {dids}, {persistedVersion: 1}) export function useLabelerInfoQuery({ did, @@ -69,8 +63,8 @@ export function useLabelersDetailedInfoQuery({dids}: {dids: string[]}) { const agent = useAgent() return useQuery({ enabled: !!dids.length, - queryKey: persistedLabelersDetailedInfoQueryKey(dids), - gcTime: PERSISTED_QUERY_GCTIME, + queryKey: createLabelersDetailedInfoQueryKey(dids), + gcTime: GCTIME.INFINITY, staleTime: STALE.MINUTES.ONE, queryFn: async () => { const res = await agent.app.bsky.labeler.getServices({ diff --git a/src/state/queries/preferences/index.ts b/src/state/queries/preferences/index.ts index e78a5d73f1..b57e9ffe85 100644 --- a/src/state/queries/preferences/index.ts +++ b/src/state/queries/preferences/index.ts @@ -9,11 +9,7 @@ import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' import {PROD_DEFAULT_FEED} from '#/lib/constants' import {replaceEqualDeep} from '#/lib/functions' import {getAge} from '#/lib/strings/time' -import { - PERSISTED_QUERY_GCTIME, - PERSISTED_QUERY_ROOT, - STALE, -} from '#/state/queries' +import {GCTIME, STALE} from '#/state/queries' import { DEFAULT_HOME_FEED_PREFS, DEFAULT_LOGGED_OUT_PREFERENCES, @@ -23,6 +19,7 @@ import { type ThreadViewPreferences, type UsePreferencesQueryResponse, } from '#/state/queries/preferences/types' +import {createQueryKey} from '#/state/queries/util' import {useAgent} from '#/state/session' import {saveLabelers} from '#/state/session/agent-config' import {useAgeAssurance} from '#/ageAssurance' @@ -33,7 +30,11 @@ export * from '#/state/queries/preferences/const' export * from '#/state/queries/preferences/moderation' export * from '#/state/queries/preferences/types' -export const preferencesQueryKey = [PERSISTED_QUERY_ROOT, 'getPreferences'] +export const preferencesQueryKey = createQueryKey( + 'getPreferences', + {}, + {persistedVersion: 1}, +) export function usePreferencesQuery() { const agent = useAgent() @@ -44,7 +45,7 @@ export function usePreferencesQuery() { structuralSharing: replaceEqualDeep, refetchOnWindowFocus: true, queryKey: preferencesQueryKey, - gcTime: PERSISTED_QUERY_GCTIME, + gcTime: GCTIME.INFINITY, queryFn: async () => { if (!agent.did) { return DEFAULT_LOGGED_OUT_PREFERENCES diff --git a/src/state/queries/util.ts b/src/state/queries/util.ts index 363b6b6e02..7ea54745c0 100644 --- a/src/state/queries/util.ts +++ b/src/state/queries/util.ts @@ -14,6 +14,61 @@ import { import * as bsky from '#/types/bsky' +export type StructuredQueryKey> = readonly [ + string, + T, + { + persistedVersion?: number + }, +] + +/** + * Helper method to ensure consistent query keys and key ordering + */ +export function createQueryKey>( + /** + * The query key root. All queries must have a root. + */ + root: string, + /** + * Any arguments the query depends on, and if changed, should result in the query being refetched. + */ + args: T, + options: { + /** + * If provided, this indicates that the query is persisted and the version + * of the persisted query format. + * + * This is used to ensure that when we make breaking changes to the + * persisted query format, we can increment the version and avoid trying to + * read old persisted queries with the new format. + * + * If you're persisting your queries, you probably want to set `gcTime: + * GCTIME.INFINITY` for this query, otherwise it'll get busted immediately + * after being persisted. + */ + persistedVersion?: number + } = {}, +): StructuredQueryKey { + return [root, args, options] as const +} + +export function isQueryPersisted( + queryKey: QueryKey, +): queryKey is StructuredQueryKey> { + return ( + Array.isArray(queryKey) && + queryKey.length === 3 && + typeof queryKey[0] === 'string' && + typeof queryKey[1] === 'object' && + queryKey[1] !== null && + typeof queryKey[2] === 'object' && + queryKey[2] !== null && + 'persistedVersion' in queryKey[2] && + typeof queryKey[2].persistedVersion === 'number' + ) +} + export async function truncateAndInvalidate( queryClient: QueryClient, queryKey: QueryKey, diff --git a/src/view/com/posts/PostFeed.tsx b/src/view/com/posts/PostFeed.tsx index f813dc9358..5c06a99125 100644 --- a/src/view/com/posts/PostFeed.tsx +++ b/src/view/com/posts/PostFeed.tsx @@ -38,6 +38,7 @@ import { RQKEY, usePostFeedQuery, } from '#/state/queries/post-feed' +import {truncateAndInvalidate} from '#/state/queries/util' import {useSession} from '#/state/session' import {useProgressGuide} from '#/state/shell/progress-guide' import {useSelectedFeed} from '#/state/shell/selected-feed' @@ -700,13 +701,22 @@ let PostFeed = ({ }) setIsPTRing(true) try { - await refetch() + await truncateAndInvalidate(queryClient, RQKEY(feed, feedParams)) onHasNew?.(false) } catch (err) { logger.error('Failed to refresh posts feed', {message: err}) } setIsPTRing(false) - }, [ax, refetch, setIsPTRing, onHasNew, feed, feedType, enabled]) + }, [ + ax, + queryClient, + setIsPTRing, + onHasNew, + feed, + feedParams, + feedType, + enabled, + ]) const onEndReached = useCallback(async () => { if (isFetching || !hasNextPage || isError) return diff --git a/yarn.lock b/yarn.lock index 6812b62896..e6c5c596ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9086,10 +9086,10 @@ expo-notifications@~0.32.16: expo-application "~7.0.8" expo-constants "~18.0.13" -expo-paste-input@^0.1.10: - version "0.1.10" - resolved "https://registry.yarnpkg.com/expo-paste-input/-/expo-paste-input-0.1.10.tgz#7df0a07ae6ecd381004624dbe88762997d962780" - integrity sha512-TkAauK1eIq7+vk2SA39trmDO6dLfFLQL+09KIVZh//3XWs8gHC5ezH3vjfjr7xj9xi67amyeiDCErP5caTDIKg== +expo-paste-input@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/expo-paste-input/-/expo-paste-input-0.1.12.tgz#0295eb26caf738fc39019f4ecae48c22b126b67e" + integrity sha512-iaOCgygmCYVbSj+gJOxr8P28y8Tf0X4UHtVVXRjEsARKf5gQUoVaRX63uzB+8h+g6vN710fSmu5vdxNP28bw8g== expo-privacy-sensitive@^0.1.0: version "0.1.0"