Skip to content

Commit

Permalink
feat: Profile Caching
Browse files Browse the repository at this point in the history
Added initial data to profile queries
Cleaned up hydration handler to improve time to responsive
Added prefetch to V3 DMs
Updated useMessageIsUnread to handle empty/loading state
Added additional performance logs
  • Loading branch information
alexrisch committed Dec 30, 2024
1 parent 187d108 commit 5783ea8
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 14 deletions.
4 changes: 2 additions & 2 deletions components/StateHandlers/HydrationStateHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ export default function HydrationStateHandler() {
);

const results = await Promise.allSettled([
getXmtpClient(account),
// This will handle creating the client and setting the conversation list from persistence
fetchPersistedConversationListQuery({ account }),
prefetchInboxIdQuery({ account }),
]);
prefetchInboxIdQuery({ account });

const errors = results.filter(
(result) => result.status === "rejected"
Expand Down
4 changes: 3 additions & 1 deletion components/V3DMListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { useToggleReadStatus } from "../features/conversation-list/hooks/useTogg
import Avatar from "./Avatar";
import { ContextMenuIcon, ContextMenuItem } from "./ContextMenuItems";
import { ConversationListItemDumb } from "./ConversationListItem/ConversationListItemDumb";
import { prefetchConversationMessages } from "@/queries/useConversationMessages";

type V3DMListItemProps = {
conversation: DmWithCodecsType;
Expand Down Expand Up @@ -162,10 +163,11 @@ export const V3DMListItem = ({ conversation }: V3DMListItemProps) => {
}, [avatarUri, preferredName]);

const onPress = useCallback(() => {
prefetchConversationMessages(currentAccount, topic);
navigate("Conversation", {
topic: topic,
});
}, [topic]);
}, [topic, currentAccount]);

const onLeftSwipe = useCallback(() => {
const translation = ref.current?.state.rowTranslation;
Expand Down
2 changes: 1 addition & 1 deletion components/V3GroupConversationListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { saveTopicsData } from "@utils/api";
import { getMinimalDate } from "@utils/date";
import { Haptics } from "@utils/haptics";
import { navigate } from "@utils/navigation";
import { RefObject, useCallback, useEffect, useMemo, useRef } from "react";
import { RefObject, useCallback, useMemo, useRef } from "react";
import { useColorScheme } from "react-native";
import { Swipeable } from "react-native-gesture-handler";
import { runOnJS } from "react-native-reanimated";
Expand Down
3 changes: 2 additions & 1 deletion features/conversation-list/hooks/useMessageIsUnread.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useMemo } from "react";
import { ChatStoreType } from "@data/store/chatStore";
import { DecodedMessageWithCodecsType } from "@/utils/xmtpRN/client.types";
import { useChatStore, useCurrentAccount } from "@data/store/accountsStore";
import { useChatStore } from "@data/store/accountsStore";
import { useSelect } from "@data/store/storeHelpers";
import { normalizeTimestamp } from "@/utils/date";
import { getCurrentUserAccountInboxId } from "@/hooks/use-current-account-inbox-id";
Expand All @@ -25,6 +25,7 @@ export const useConversationIsUnread = ({
return useMemo(() => {
// No message means no unread status
if (!lastMessage) return false;
if (!topicsData[topic]) return false;

// Check if status is unread
if (topicsData[topic]?.status === "unread") return true;
Expand Down
12 changes: 10 additions & 2 deletions queries/useConversationMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,23 @@ export const conversationMessagesQueryFn = async (
conversation: ConversationWithCodecsType,
options?: MessagesOptions
) => {
const start = performance.now();
logger.info("[useConversationMessages] queryFn fetching messages...");
if (!conversation) {
throw new Error("Conversation not found in conversationMessagesQueryFn");
}
const messages = await conversation.messages(options);
const end = performance.now();
logger.info(
`[useConversationMessages] queryFn fetched ${messages.length} messages`
`[useConversationMessages] queryFn fetched ${messages.length} messages in ${end - start}ms`
);
return processMessages({ messages });
const processingStart = performance.now();
const processedMessages = processMessages({ messages });
const processingEnd = performance.now();
logger.info(
`[useConversationMessages] queryFn processed ${messages.length} messages in ${processingEnd - processingStart}ms`
);
return processedMessages;
};

const conversationMessagesByTopicQueryFn = async (
Expand Down
25 changes: 21 additions & 4 deletions queries/useInboxProfileSocialsQuery.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IProfileSocials } from "@/features/profiles/profile-types";
import { useQueries, useQuery } from "@tanstack/react-query";
import { QueryKey, useQueries, useQuery } from "@tanstack/react-query";
import { getProfilesForInboxIds } from "@utils/api";
import {
create,
Expand All @@ -11,7 +11,10 @@ import { queryClient } from "./queryClient";
import { InboxId } from "@xmtp/react-native-sdk";
import mmkv from "@/utils/mmkv";

const profileSocialsQueryKey = (account: string, peerAddress: string) => [
const profileSocialsQueryKey = (
account: string,
peerAddress: string
): QueryKey => [
"inboxProfileSocials",
account?.toLowerCase(),
peerAddress?.toLowerCase(),
Expand All @@ -34,7 +37,10 @@ const profileSocials = create({
}),
});

const fetchInboxProfileSocials = async (account: string, inboxId: InboxId) => {
const fetchInboxProfileSocials = async (
account: string,
inboxId: InboxId
): Promise<IProfileSocials[] | null> => {
const data = await profileSocials.fetch(inboxId);

const key = inboxProfileSocialsQueryStorageKey(account, inboxId);
Expand Down Expand Up @@ -63,7 +69,18 @@ const inboxProfileSocialsQueryConfig = (
// And automatic retries if there was an error fetching
refetchOnMount: false,
staleTime: 1000 * 60 * 60 * 24,
// persister: reactQueryPersister,
initialData: (): IProfileSocials[] | null | undefined => {
if (!account || !inboxId) {
return undefined;
}
if (mmkv.contains(inboxProfileSocialsQueryStorageKey(account, inboxId))) {
const data = JSON.parse(
mmkv.getString(inboxProfileSocialsQueryStorageKey(account, inboxId))!
) as IProfileSocials[];
return data;
}
},
initialDataUpdatedAt: 0,
});

export const useInboxProfileSocialsQuery = (
Expand Down
18 changes: 15 additions & 3 deletions queries/useProfileSocialsQuery.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IProfileSocials } from "@/features/profiles/profile-types";
import { useQueries, useQuery } from "@tanstack/react-query";
import { QueryKey, useQueries, useQuery } from "@tanstack/react-query";
import { getProfilesForAddresses } from "@utils/api";
import {
create,
Expand All @@ -12,7 +12,10 @@ import mmkv from "@/utils/mmkv";

type ProfileSocialsData = IProfileSocials | null | undefined;

const profileSocialsQueryKey = (account: string, peerAddress: string) => [
const profileSocialsQueryKey = (
account: string,
peerAddress: string
): QueryKey => [
"profileSocials",
account?.toLowerCase(),
// Typesafe because there's a lot of account! usage
Expand Down Expand Up @@ -62,6 +65,15 @@ const profileSocialsQueryConfig = (account: string, peerAddress: string) => ({
// And automatic retries if there was an error fetching
refetchOnMount: false,
staleTime: 1000 * 60 * 60 * 24,
initialData: (): ProfileSocialsData => {
if (mmkv.contains(profileSocialsQueryStorageKey(account, peerAddress))) {
const data = JSON.parse(
mmkv.getString(profileSocialsQueryStorageKey(account, peerAddress))!
) as ProfileSocialsData;
return data;
}
},
initialDataUpdatedAt: 0,
// persister: reactQueryPersister,
});

Expand Down Expand Up @@ -96,7 +108,7 @@ export const fetchProfileSocialsQuery = (
account: string,
peerAddress: string
) => {
return queryClient.fetchQuery<ProfileSocialsData>(
return queryClient.fetchQuery<IProfileSocials | null>(
profileSocialsQueryConfig(account, peerAddress)
);
};
Expand Down

0 comments on commit 5783ea8

Please sign in to comment.