I use "@amityco/ts-sdk: "^6.35.0"
. To handle the live updates I use a couple of hooks based on the useLiveCollection hook. At the moment, I’ve added a trick that allows me to refetch the data associated with posts, but it doesn’t look like the best approach because I need to flush all subscriptions associated with the post.
import { subscribeTopic } from '@amityco/ts-sdk';
import { useRef, useMemo, useEffect, useCallback } from 'react';
import { hashKey, useQuery, useQueryClient } from '@tanstack/react-query';
import { useSubscriptionsStore } from '../store/subscriptions-store';
export function useLiveCollection<TCallback, TParams>({
fetcher,
params,
queryKey,
callback = () => {},
shouldCall = () => true,
getSubscribedTopic,
}: {
fetcher: (
params: Amity.LiveCollectionParams<TParams>,
callback: Amity.LiveCollectionCallback<TCallback>
) => Amity.Unsubscriber;
params: Amity.LiveCollectionParams<TParams>;
queryKey: string[];
callback?: Amity.LiveCollectionCallback<TCallback>;
shouldCall?: () => boolean;
queryFn?: () => Promise<TCallback[]>;
getSubscribedTopic?: () => string;
}): {
items: TCallback[];
isLoading: boolean;
hasMore: boolean;
loadMore: () => void;
error: Error | null;
} {
const queryClient = useQueryClient();
const { subscribe, getSubscriptionsByKey } = useSubscriptionsStore();
const loadMoreFnRef = useRef<(() => void) | null>(null);
const isSubscribed = useMemo(
() => getSubscriptionsByKey(hashKey(queryKey)).length > 0,
[getSubscriptionsByKey, queryKey]
);
const loadMore = useCallback(() => {
loadMoreFnRef.current?.();
}, []);
const callbackFn: Amity.LiveCollectionCallback<TCallback> = useCallback(
(response) => {
if (!shouldCall()) return;
const responseData = Array.isArray(response.data) ? response.data : [response.data];
const oldData = queryClient.getQueryData<{ items: TCallback[] }>(queryKey);
queryClient.setQueryData(queryKey, {
items: responseData.length > 0 ? responseData : oldData?.items || [],
isLoading: false,
hasMore: response.hasNextPage,
error: response.error,
});
loadMoreFnRef.current = response.onNextPage || null;
callback(response);
},
[queryClient, queryKey, shouldCall, callback]
);
useEffect(() => {
if (!shouldCall() || isSubscribed) return;
const unsubscriber = fetcher(params, callbackFn);
subscribe(hashKey(queryKey), unsubscriber);
if (getSubscribedTopic) {
try {
const topicUnsubscriber = subscribeTopic(getSubscribedTopic());
subscribe(hashKey(queryKey), topicUnsubscriber);
} catch (error) {
console.error(error);
}
}
}, [
params,
shouldCall,
fetcher,
callbackFn,
subscribe,
queryKey,
getSubscribedTopic,
isSubscribed,
]);
const { data } = useQuery({
queryKey,
initialData: {
items: [],
isLoading: true,
hasMore: false,
error: null,
},
staleTime: 0,
gcTime: 1000 * 60 * 60 * 24 * 5,
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
refetchInterval: false,
refetchIntervalInBackground: false,
});
return {
items: data.items,
isLoading: data.isLoading,
hasMore: data.hasMore,
loadMore,
error: data.error,
};
}
import { getUserTopic, PostRepository, SubscriptionLevels } from '@amityco/ts-sdk';
import { useLiveCollection } from 'src/socials/shared/hooks/use-live-collection';
import { useGetUserQuery } from '../../users/api/get-user';
export type UseGetPostsQueryProps = {
userId: string;
targetId: string;
};
export const useGetPostsQuery = ({ userId, targetId }: UseGetPostsQueryProps) => {
const { user } = useGetUserQuery({ userId });
const {
items: posts,
isLoading,
error,
hasMore,
loadMore,
} = useLiveCollection<Amity.Post, any>({
fetcher: PostRepository.getPosts,
params: {
targetId,
targetType: 'user',
sortBy: 'lastCreated',
limit: 3,
},
queryKey: ['posts', targetId],
shouldCall: () => !!targetId && !!user,
getSubscribedTopic: () => {
if (!user) throw new Error('User not found');
return getUserTopic(user, SubscriptionLevels.POST);
},
});
return {
posts,
loading: isLoading,
error,
hasMore,
loadMore,
};
};
import { hashKey } from '@tanstack/react-query';
import { getUserTopic, CommentRepository, SubscriptionLevels } from '@amityco/ts-sdk';
import { useLiveCollection } from 'src/socials/shared/hooks/use-live-collection';
import { useSubscriptionsStore } from 'src/socials/shared/store/subscriptions-store';
import { useGetUserQuery } from '../../users/api/get-user';
export type UseGetCommentsQueryProps = {
userId: string;
referenceId: string;
referenceType: Amity.CommentReferenceType;
};
export const useGetCommentsQuery = ({
userId,
referenceId,
referenceType,
}: UseGetCommentsQueryProps) => {
const { user } = useGetUserQuery({ userId });
const { unsubscribe, getSubscriptionsByKey } = useSubscriptionsStore();
const {
items: comments,
isLoading,
error,
hasMore,
loadMore,
} = useLiveCollection<Amity.Comment, any>({
fetcher: CommentRepository.getComments,
params: {
referenceId,
referenceType,
sortBy: 'lastCreated',
limit: 3,
},
queryKey: ['comment', referenceId, referenceType],
shouldCall: () => !!referenceId && !!user,
callback: (response) => {
if (response.data) {
const postKey = hashKey(['posts', userId]);
const hasSubscriptions = getSubscriptionsByKey(postKey).length > 0;
if (hasSubscriptions) {
unsubscribe(postKey);
}
}
},
getSubscribedTopic: () => {
if (!user) throw new Error('User not found');
return getUserTopic(user, SubscriptionLevels.COMMENT);
},
});
return {
comments,
loading: isLoading,
error,
hasMore,
loadMore,
};
};