From f71e0aa343df0943a4f0c24706457d44e546b7a7 Mon Sep 17 00:00:00 2001 From: Stef Coenen Date: Thu, 24 Oct 2024 11:31:24 +0200 Subject: [PATCH] Refactored ReactionFile to EmojiReaction and CommentReaction; Made transitReactions post agnostic; --- .../Chat/Composer/ChatReactionComposer.tsx | 5 +- .../src/hooks/chat/useChatReaction.ts | 32 ++-- .../hooks/reactions/comments/useComments.tsx | 6 +- .../reactions/emojis/useEmojiReactions.tsx | 18 ++- .../src/hooks/reactions/useReaction.tsx | 17 ++- .../Blocks/Interacts/Comments/Comment.tsx | 18 ++- .../Interacts/Comments/Parts/CommentBody.tsx | 4 +- .../ReactionDetailsDialog.tsx | 6 +- .../reactions/CommunityReactionComposer.tsx | 4 +- .../reactions/useCommunityReaction.ts | 65 ++++----- .../src/hooks/useHighlightFeedItem.ts | 6 +- .../DriveData/File/DriveFileReactionTypes.ts | 7 +- .../ReactionData/GroupReactionsProvider.ts | 6 +- .../core/ReactionData/ReactionsProvider.ts | 132 +++++++++++++++++ packages/js-lib/src/core/core.ts | 1 + .../public/posts/PostEmojiReactionProvider.ts | 138 ++---------------- .../src/public/posts/PostReactionProvider.ts | 22 +-- packages/js-lib/src/public/posts/PostTypes.ts | 4 +- 18 files changed, 263 insertions(+), 228 deletions(-) create mode 100644 packages/js-lib/src/core/ReactionData/ReactionsProvider.ts diff --git a/packages/chat-app/src/components/Chat/Composer/ChatReactionComposer.tsx b/packages/chat-app/src/components/Chat/Composer/ChatReactionComposer.tsx index 73fa5a6d..77c6d704 100644 --- a/packages/chat-app/src/components/Chat/Composer/ChatReactionComposer.tsx +++ b/packages/chat-app/src/components/Chat/Composer/ChatReactionComposer.tsx @@ -64,9 +64,8 @@ export const ChatReactionComposer = ({ setIsReact(false); }} doUnlike={(emoji) => { - const reactionFile = myReactions?.find((reaction) => reaction.body === emoji); - if (reactionFile) - removeReaction({ conversation, message: msg, reaction: reactionFile }); + const reaction = myReactions?.find((reaction) => reaction.body === emoji); + if (reaction) removeReaction({ conversation, message: msg, reaction: reaction }); setIsReact(false); }} /> diff --git a/packages/chat-app/src/hooks/chat/useChatReaction.ts b/packages/chat-app/src/hooks/chat/useChatReaction.ts index b0b2ec08..b38e4828 100644 --- a/packages/chat-app/src/hooks/chat/useChatReaction.ts +++ b/packages/chat-app/src/hooks/chat/useChatReaction.ts @@ -6,7 +6,7 @@ import { getGroupReactions, GroupEmojiReaction, HomebaseFile, - ReactionFile, + EmojiReaction, ReactionPreview, uploadGroupReaction, } from '@homebase-id/js-lib/core'; @@ -71,7 +71,7 @@ export const useChatReaction = (props?: { }: { conversation: HomebaseFile; message: HomebaseFile; - reaction: ReactionFile; + reaction: EmojiReaction; }) => { const conversationContent = conversation.fileMetadata.appData.content; const identity = dotYouClient.getIdentity(); @@ -99,9 +99,9 @@ export const useChatReaction = (props?: { onMutate: async ({ message, reaction }) => { // Update the reaction overview const previousReactions = - queryClient.getQueryData(['chat-reaction', message.fileId]) || []; + queryClient.getQueryData(['chat-reaction', message.fileId]) || []; - const newReaction: ReactionFile = { + const newReaction: EmojiReaction = { authorOdinId: dotYouClient.getIdentity(), body: reaction, }; @@ -141,7 +141,7 @@ export const useChatReaction = (props?: { onMutate: async ({ message, reaction }) => { // Update the reaction overview - const previousReactions = queryClient.getQueryData([ + const previousReactions = queryClient.getQueryData([ 'chat-reaction', message.fileId, ]); @@ -199,7 +199,7 @@ export const insertNewReaction = ( messageLocalFileId: string, newReaction: GroupEmojiReaction ) => { - const currentReactions = queryClient.getQueryData([ + const currentReactions = queryClient.getQueryData([ 'chat-reaction', messageLocalFileId, ]); @@ -209,20 +209,20 @@ export const insertNewReaction = ( return; } - const reactionAsReactionFile: ReactionFile = { + const reactionAsEmojiReaction: EmojiReaction = { authorOdinId: newReaction.odinId, body: tryJsonParse<{ emoji: string }>(newReaction.reactionContent).emoji, }; - queryClient.setQueryData( + queryClient.setQueryData( ['chat-reaction', messageLocalFileId], [ ...currentReactions.filter( (reaction) => - reaction.authorOdinId !== reactionAsReactionFile.authorOdinId || - reaction.body !== reactionAsReactionFile.body + reaction.authorOdinId !== reactionAsEmojiReaction.authorOdinId || + reaction.body !== reactionAsEmojiReaction.body ), - reactionAsReactionFile, + reactionAsEmojiReaction, ] ); }; @@ -232,7 +232,7 @@ export const removeReaction = ( messageLocalFileId: string, removedReaction: GroupEmojiReaction ) => { - const currentReactions = queryClient.getQueryData([ + const currentReactions = queryClient.getQueryData([ 'chat-reaction', messageLocalFileId, ]); @@ -242,17 +242,17 @@ export const removeReaction = ( return; } - const reactionAsReactionFile: ReactionFile = { + const reactionAsEmojiReaction: EmojiReaction = { authorOdinId: removedReaction.odinId, body: tryJsonParse<{ emoji: string }>(removedReaction.reactionContent).emoji, }; - queryClient.setQueryData( + queryClient.setQueryData( ['chat-reaction', messageLocalFileId], currentReactions.filter( (reaction) => - reaction.authorOdinId !== reactionAsReactionFile.authorOdinId || - reaction.body !== reactionAsReactionFile.body + reaction.authorOdinId !== reactionAsEmojiReaction.authorOdinId || + reaction.body !== reactionAsEmojiReaction.body ) ); }; diff --git a/packages/common-app/src/hooks/reactions/comments/useComments.tsx b/packages/common-app/src/hooks/reactions/comments/useComments.tsx index 41ea9b8d..b02e2140 100644 --- a/packages/common-app/src/hooks/reactions/comments/useComments.tsx +++ b/packages/common-app/src/hooks/reactions/comments/useComments.tsx @@ -1,13 +1,13 @@ import { useInfiniteQuery, useQueryClient } from '@tanstack/react-query'; import { getComments, ReactionContext } from '@homebase-id/js-lib/public'; -import { HomebaseFile, ReactionFile } from '@homebase-id/js-lib/core'; +import { HomebaseFile, CommentReaction } from '@homebase-id/js-lib/core'; import { useDotYouClient } from '../../auth/useDotYouClient'; const PAGE_SIZE = 30; export interface UseCommentsVal { - comments: HomebaseFile[]; + comments: HomebaseFile[]; cursorState: string | undefined; } @@ -24,7 +24,7 @@ export const useComments = ({ context }: { context: ReactionContext }) => { pageParam?: string; }): Promise => { if (!context.odinId || !context.channelId || !context.target.globalTransitId) - return { comments: [] as HomebaseFile[], cursorState: undefined }; + return { comments: [] as HomebaseFile[], cursorState: undefined }; const response = await getComments(dotYouClient, context, PAGE_SIZE, pageParam); setTimeout(() => { diff --git a/packages/common-app/src/hooks/reactions/emojis/useEmojiReactions.tsx b/packages/common-app/src/hooks/reactions/emojis/useEmojiReactions.tsx index e6189a1e..4ec77c83 100644 --- a/packages/common-app/src/hooks/reactions/emojis/useEmojiReactions.tsx +++ b/packages/common-app/src/hooks/reactions/emojis/useEmojiReactions.tsx @@ -1,7 +1,7 @@ import { useInfiniteQuery } from '@tanstack/react-query'; -import { getReactions, ReactionContext } from '@homebase-id/js-lib/public'; +import { GetTargetDriveFromChannelId, ReactionContext } from '@homebase-id/js-lib/public'; -import { ReactionFile } from '@homebase-id/js-lib/core'; +import { getReactions, EmojiReaction } from '@homebase-id/js-lib/core'; import { useDotYouClient } from '../../auth/useDotYouClient'; const PAGE_SIZE = 15; @@ -21,9 +21,19 @@ export const useEmojiReactions = (context?: ReactionContext) => { !context?.channelId || (!context?.target?.fileId && !context?.target?.globalTransitId) ) { - return { reactions: [] as ReactionFile[], cursor: undefined }; + return { reactions: [] as EmojiReaction[], cursor: undefined }; } - return await getReactions(dotYouClient, context, PAGE_SIZE, pageParam); + return await getReactions( + dotYouClient, + context.odinId, + { + fileId: context.target.fileId, + globalTransitId: context.target.globalTransitId, + targetDrive: GetTargetDriveFromChannelId(context.channelId), + }, + PAGE_SIZE, + pageParam + ); }; return { diff --git a/packages/common-app/src/hooks/reactions/useReaction.tsx b/packages/common-app/src/hooks/reactions/useReaction.tsx index b9ff5af6..e64a2e05 100644 --- a/packages/common-app/src/hooks/reactions/useReaction.tsx +++ b/packages/common-app/src/hooks/reactions/useReaction.tsx @@ -12,7 +12,7 @@ import { import { getRichTextFromString } from '../../helpers/richTextHelper'; import { UseCommentsVal } from './comments/useComments'; -import { HomebaseFile, NewHomebaseFile, ReactionFile } from '@homebase-id/js-lib/core'; +import { CommentReaction, HomebaseFile, NewHomebaseFile } from '@homebase-id/js-lib/core'; import { useDotYouClient } from '../auth/useDotYouClient'; export const useReaction = () => { @@ -25,7 +25,7 @@ export const useReaction = () => { }: { context: ReactionContext; commentData: - | Omit, 'serverMetadata'> + | Omit, 'serverMetadata'> | Omit, 'serverMetadata'>; }) => { return await saveComment(dotYouClient, context, { @@ -50,7 +50,7 @@ export const useReaction = () => { commentFile, }: { context: ReactionContext; - commentFile: HomebaseFile; + commentFile: HomebaseFile; }) => { return await removeComment(dotYouClient, context, commentFile); }; @@ -91,7 +91,7 @@ export const useReaction = () => { let newInfinite: InfiniteData; if (prevInfinite) { if ( - (toSaveCommentData.commentData as HomebaseFile).fileMetadata + (toSaveCommentData.commentData as HomebaseFile).fileMetadata .globalTransitId ) { newInfinite = { @@ -101,11 +101,11 @@ export const useReaction = () => { ...page, comments: page.comments.map((comment) => comment.fileMetadata.globalTransitId === - (toSaveCommentData.commentData as HomebaseFile).fileMetadata + (toSaveCommentData.commentData as HomebaseFile).fileMetadata .globalTransitId ? toSaveCommentData.commentData : comment - ) as HomebaseFile[], + ) as HomebaseFile[], }; }), }; @@ -116,7 +116,7 @@ export const useReaction = () => { comments: [ toSaveCommentData.commentData, ...firstPage.comments, - ] as HomebaseFile[], + ] as HomebaseFile[], }; const newPages = [newFirtPage, ...prevInfinite.pages.slice(1)]; @@ -133,7 +133,8 @@ export const useReaction = () => { }, onSuccess: (savedGlobalId, savedCommentData) => { if ( - (savedCommentData.commentData as HomebaseFile).fileMetadata.globalTransitId + (savedCommentData.commentData as HomebaseFile).fileMetadata + .globalTransitId ) { // it was a normal update, already covered on the onMutate; return; diff --git a/packages/common-app/src/socialFeed/Blocks/Interacts/Comments/Comment.tsx b/packages/common-app/src/socialFeed/Blocks/Interacts/Comments/Comment.tsx index bf917e51..dcdb35ee 100644 --- a/packages/common-app/src/socialFeed/Blocks/Interacts/Comments/Comment.tsx +++ b/packages/common-app/src/socialFeed/Blocks/Interacts/Comments/Comment.tsx @@ -9,7 +9,7 @@ import { CommentHead } from './Parts/CommentHead'; import { CommentBody } from './Parts/CommentBody'; import { CommentMeta } from './Parts/CommentMeta'; import { CommentThread } from './Parts/CommentThread'; -import { HomebaseFile, NewHomebaseFile, ReactionFile } from '@homebase-id/js-lib/core'; +import { HomebaseFile, NewHomebaseFile, CommentReaction } from '@homebase-id/js-lib/core'; import { CanReactInfo } from '../../../../hooks/reactions/useCanReact'; import { useReaction } from '../../../../hooks/reactions/useReaction'; import { ErrorNotification } from '../../../../ui/Alert/ErrorNotification'; @@ -21,7 +21,7 @@ import { t } from '../../../../helpers/i18n/dictionary'; export interface CommentProps { context: ReactionContext; canReact?: CanReactInfo; - commentData: HomebaseFile | NewHomebaseFile; + commentData: HomebaseFile | NewHomebaseFile; isThread: boolean; onReply?: () => void; } @@ -51,8 +51,8 @@ export const Comment = ({ context, canReact, commentData, onReply, isThread }: C ...context, target: { fileId: commentData.fileId, - globalTransitId: (commentData as HomebaseFile).fileMetadata.globalTransitId, - isEncrypted: (commentData as HomebaseFile).fileMetadata.isEncrypted || false, + globalTransitId: (commentData as HomebaseFile).fileMetadata.globalTransitId, + isEncrypted: (commentData as HomebaseFile).fileMetadata.isEncrypted || false, }, }; @@ -103,7 +103,7 @@ export const Comment = ({ context, canReact, commentData, onReply, isThread }: C ? () => removeComment({ context, - commentFile: commentData as HomebaseFile, + commentFile: commentData as HomebaseFile, }) : undefined } @@ -113,7 +113,9 @@ export const Comment = ({ context, canReact, commentData, onReply, isThread }: C content={commentContent} previewThumbnail={commentData.fileMetadata.appData.previewThumbnail} commentFileId={fileId} - commentLastModifed={(commentData as HomebaseFile).fileMetadata.updated} + commentLastModifed={ + (commentData as HomebaseFile).fileMetadata.updated + } isEdit={isEdit} onCancel={() => setIsEdit(false)} onUpdate={doUpdate} @@ -125,8 +127,8 @@ export const Comment = ({ context, canReact, commentData, onReply, isThread }: C ).fileMetadata.created} - updated={(commentData as HomebaseFile).fileMetadata.updated} + created={(commentData as HomebaseFile).fileMetadata.created} + updated={(commentData as HomebaseFile).fileMetadata.updated} onReply={isThread ? undefined : () => (onReply ? onReply() : setIsReply(!isReply))} /> ) : null} diff --git a/packages/common-app/src/socialFeed/Blocks/Interacts/Comments/Parts/CommentBody.tsx b/packages/common-app/src/socialFeed/Blocks/Interacts/Comments/Parts/CommentBody.tsx index a4b38237..d6c96113 100644 --- a/packages/common-app/src/socialFeed/Blocks/Interacts/Comments/Parts/CommentBody.tsx +++ b/packages/common-app/src/socialFeed/Blocks/Interacts/Comments/Parts/CommentBody.tsx @@ -7,7 +7,7 @@ import { RichTextRenderer } from '../../../../../richText'; import { CommentEditor } from '../CommentComposer'; import { CommentMedia, CommentMediaPreview } from './CommentMedia'; import { ActionButtonState } from '../../../../../ui'; -import { EmbeddedThumb, ReactionFile } from '@homebase-id/js-lib/core'; +import { CommentReaction, EmbeddedThumb } from '@homebase-id/js-lib/core'; export const CommentBody = ({ context, @@ -23,7 +23,7 @@ export const CommentBody = ({ context?: ReactionContext; commentFileId?: string; commentLastModifed?: number; - content: RawReactionContent | ReactionFile; + content: RawReactionContent | CommentReaction; isEdit?: boolean; onUpdate?: (commentBody: string, attachment?: File) => void; onCancel?: () => void; diff --git a/packages/common-app/src/socialFeed/Blocks/Interacts/ReactionDetailsDialog/ReactionDetailsDialog.tsx b/packages/common-app/src/socialFeed/Blocks/Interacts/ReactionDetailsDialog/ReactionDetailsDialog.tsx index 121428f7..526f4547 100644 --- a/packages/common-app/src/socialFeed/Blocks/Interacts/ReactionDetailsDialog/ReactionDetailsDialog.tsx +++ b/packages/common-app/src/socialFeed/Blocks/Interacts/ReactionDetailsDialog/ReactionDetailsDialog.tsx @@ -1,7 +1,7 @@ import { ReactionContext } from '@homebase-id/js-lib/public'; import { useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; -import { ReactionFile } from '@homebase-id/js-lib/core'; +import { EmojiReaction } from '@homebase-id/js-lib/core'; import { t } from '../../../../helpers'; import { usePortal, useEmojiReactions, useEmojiSummary } from '../../../../hooks'; import { DialogWrapper, ActionButton } from '../../../../ui'; @@ -34,10 +34,10 @@ export const ReactionDetailsDialog = ({ const flattenedReactions = reactionDetails?.pages .flatMap((page) => page?.reactions) - .filter(Boolean) as ReactionFile[]; + .filter(Boolean) as EmojiReaction[]; const filteredEmojis = reactionSummary?.reactions?.filter((reaction) => - flattenedReactions?.some((reactionFile) => reactionFile.body === reaction.emoji) + flattenedReactions?.some((rct) => rct.body === reaction.emoji) ); useEffect(() => { diff --git a/packages/community-app/src/components/Community/Message/reactions/CommunityReactionComposer.tsx b/packages/community-app/src/components/Community/Message/reactions/CommunityReactionComposer.tsx index 097665d6..9c2d412a 100644 --- a/packages/community-app/src/components/Community/Message/reactions/CommunityReactionComposer.tsx +++ b/packages/community-app/src/components/Community/Message/reactions/CommunityReactionComposer.tsx @@ -36,8 +36,8 @@ export const CommunityReactionComposer = ({ defaultValue={[]} doLike={(emoji) => addReaction({ community, message: msg, reaction: emoji })} doUnlike={(emoji) => { - const reactionFile = myReactions?.find((reaction) => reaction.body === emoji); - if (reactionFile) removeReaction({ community, message: msg, reaction: reactionFile }); + const reaction = myReactions?.find((reaction) => reaction.body === emoji); + if (reaction) removeReaction({ community, message: msg, reaction: reaction }); }} /> ); diff --git a/packages/community-app/src/hooks/community/reactions/useCommunityReaction.ts b/packages/community-app/src/hooks/community/reactions/useCommunityReaction.ts index ce0eb732..2a91f657 100644 --- a/packages/community-app/src/hooks/community/reactions/useCommunityReaction.ts +++ b/packages/community-app/src/hooks/community/reactions/useCommunityReaction.ts @@ -1,13 +1,13 @@ import { QueryClient, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { - deleteGroupReaction, - getGroupReactions, + deleteReaction, + getReactions, GroupEmojiReaction, HomebaseFile, - ReactionFile, + EmojiReaction, ReactionPreview, - uploadGroupReaction, + uploadReaction, } from '@homebase-id/js-lib/core'; import { getNewId, tryJsonParse } from '@homebase-id/js-lib/helpers'; import { useDotYouClientContext } from '@homebase-id/common-app'; @@ -32,12 +32,16 @@ export const useCommunityReaction = (props?: { (communityId: string, messageGlobalTransitId: string) => async () => { const reactions = ( - await getGroupReactions(dotYouClient, { - target: { + await getReactions( + dotYouClient, + community?.fileMetadata.senderOdinId, + { + fileId: messageFileId, globalTransitId: messageGlobalTransitId, targetDrive: getTargetDriveFromCommunityId(communityId), }, - }) + 15 + ) )?.reactions || []; return reactions; @@ -60,13 +64,11 @@ export const useCommunityReaction = (props?: { if (!message.fileMetadata.globalTransitId) throw new Error('Message does not have a global transit id'); - return await uploadGroupReaction( - dotYouClient, + return await uploadReaction(dotYouClient, reaction, community.fileMetadata.senderOdinId, { + fileId: message.fileId, + globalTransitId: message.fileMetadata.globalTransitId, targetDrive, - message.fileMetadata.globalTransitId, - reaction, - [community.fileMetadata.senderOdinId] - ); + }); }; const removeReaction = async ({ @@ -76,12 +78,9 @@ export const useCommunityReaction = (props?: { }: { community: HomebaseFile; message: HomebaseFile; - reaction: ReactionFile; + reaction: EmojiReaction; }) => { if (!community || !message) return; - const communityContent = community.fileMetadata.appData.content; - const identity = dotYouClient.getIdentity(); - const members = communityContent.members.filter((recipient) => recipient !== identity); const targetDrive = getTargetDriveFromCommunityId( community.fileMetadata.appData.uniqueId as string @@ -90,10 +89,10 @@ export const useCommunityReaction = (props?: { if (!message.fileMetadata.globalTransitId) throw new Error('Message does not have a global transit id'); - return await deleteGroupReaction(dotYouClient, targetDrive, members, reaction, { + return await deleteReaction(dotYouClient, reaction, community.fileMetadata.senderOdinId, { + targetDrive, fileId: message.fileId, globalTransitId: message.fileMetadata.globalTransitId, - targetDrive: targetDrive, }); }; @@ -112,9 +111,9 @@ export const useCommunityReaction = (props?: { onMutate: async ({ community, message, reaction }) => { // Update the reaction overview const previousReactions = - queryClient.getQueryData(['community-reaction', message.fileId]) || []; + queryClient.getQueryData(['community-reaction', message.fileId]) || []; - const newReaction: ReactionFile = { + const newReaction: EmojiReaction = { authorOdinId: dotYouClient.getIdentity(), body: reaction, }; @@ -158,7 +157,7 @@ export const useCommunityReaction = (props?: { onMutate: async ({ community, message, reaction }) => { // Update the reaction overview - const previousReactions = queryClient.getQueryData([ + const previousReactions = queryClient.getQueryData([ 'community-reaction', message.fileId, ]); @@ -220,7 +219,7 @@ export const insertNewReaction = ( messageLocalFileId: string, newReaction: GroupEmojiReaction ) => { - const currentReactions = queryClient.getQueryData([ + const currentReactions = queryClient.getQueryData([ 'community-reaction', messageLocalFileId, ]); @@ -230,20 +229,20 @@ export const insertNewReaction = ( return; } - const reactionAsReactionFile: ReactionFile = { + const reactionAsEmojiReaction: EmojiReaction = { authorOdinId: newReaction.odinId, body: tryJsonParse<{ emoji: string }>(newReaction.reactionContent).emoji, }; - queryClient.setQueryData( + queryClient.setQueryData( ['community-reaction', messageLocalFileId], [ ...currentReactions.filter( (reaction) => - reaction.authorOdinId !== reactionAsReactionFile.authorOdinId || - reaction.body !== reactionAsReactionFile.body + reaction.authorOdinId !== reactionAsEmojiReaction.authorOdinId || + reaction.body !== reactionAsEmojiReaction.body ), - reactionAsReactionFile, + reactionAsEmojiReaction, ] ); }; @@ -253,7 +252,7 @@ export const removeReaction = ( messageLocalFileId: string, removedReaction: GroupEmojiReaction ) => { - const currentReactions = queryClient.getQueryData([ + const currentReactions = queryClient.getQueryData([ 'community-reaction', messageLocalFileId, ]); @@ -263,17 +262,17 @@ export const removeReaction = ( return; } - const reactionAsReactionFile: ReactionFile = { + const reactionAsEmojiReaction: EmojiReaction = { authorOdinId: removedReaction.odinId, body: tryJsonParse<{ emoji: string }>(removedReaction.reactionContent).emoji, }; - queryClient.setQueryData( + queryClient.setQueryData( ['community-reaction', messageLocalFileId], currentReactions.filter( (reaction) => - reaction.authorOdinId !== reactionAsReactionFile.authorOdinId || - reaction.body !== reactionAsReactionFile.body + reaction.authorOdinId !== reactionAsEmojiReaction.authorOdinId || + reaction.body !== reactionAsEmojiReaction.body ) ); }; diff --git a/packages/feed-app/src/hooks/useHighlightFeedItem.ts b/packages/feed-app/src/hooks/useHighlightFeedItem.ts index 422f4003..27f98d26 100644 --- a/packages/feed-app/src/hooks/useHighlightFeedItem.ts +++ b/packages/feed-app/src/hooks/useHighlightFeedItem.ts @@ -1,11 +1,9 @@ -import { HomebaseFile, ReactionFile } from '@homebase-id/js-lib/core'; +import { HomebaseFile } from '@homebase-id/js-lib/core'; import { PostContent } from '@homebase-id/js-lib/public'; import { useEffect, useMemo } from 'react'; import { useSearchParams } from 'react-router-dom'; -export const useHighlightFeedItem = ( - feedItem: HomebaseFile | HomebaseFile -) => { +export const useHighlightFeedItem = (feedItem: HomebaseFile) => { const [searchParams] = useSearchParams(); const isHighlighted = useMemo(() => { diff --git a/packages/js-lib/src/core/DriveData/File/DriveFileReactionTypes.ts b/packages/js-lib/src/core/DriveData/File/DriveFileReactionTypes.ts index fcc07180..78e547fb 100644 --- a/packages/js-lib/src/core/DriveData/File/DriveFileReactionTypes.ts +++ b/packages/js-lib/src/core/DriveData/File/DriveFileReactionTypes.ts @@ -12,11 +12,16 @@ export interface ReactionPreview { totalCommentCount: number; } -export interface ReactionFile { +interface ReactionBase { authorOdinId?: string; body: string; +} + +export interface CommentReaction extends ReactionBase { bodyAsRichText?: RichText; mediaPayloadKey?: string; } +export type EmojiReaction = ReactionBase; + export type RichText = Record[]; diff --git a/packages/js-lib/src/core/ReactionData/GroupReactionsProvider.ts b/packages/js-lib/src/core/ReactionData/GroupReactionsProvider.ts index a3d086e3..97351c2f 100644 --- a/packages/js-lib/src/core/ReactionData/GroupReactionsProvider.ts +++ b/packages/js-lib/src/core/ReactionData/GroupReactionsProvider.ts @@ -1,6 +1,6 @@ import { stringifyToQueryParams, tryJsonParse } from '../../helpers/DataUtil'; import { DotYouClient } from '../DotYouClient'; -import { ReactionFile } from '../DriveData/File/DriveFileReactionTypes'; +import { EmojiReaction } from '../DriveData/File/DriveFileReactionTypes'; import { TargetDrive } from '../DriveData/File/DriveFileTypes'; export interface GroupEmojiReaction { @@ -29,7 +29,7 @@ export const getGroupReactions = async ( }, pageSize = 15, cursor?: string -): Promise<{ reactions: ReactionFile[]; cursor: string } | undefined> => { +): Promise<{ reactions: EmojiReaction[]; cursor: string } | undefined> => { const client = dotYouClient.createAxiosClient(); const data = { @@ -81,7 +81,7 @@ export const deleteGroupReaction = async ( dotYouClient: DotYouClient, targetDrive: TargetDrive, recipients: string[], - emoji: ReactionFile, + emoji: EmojiReaction, target: { fileId: string; globalTransitId: string; diff --git a/packages/js-lib/src/core/ReactionData/ReactionsProvider.ts b/packages/js-lib/src/core/ReactionData/ReactionsProvider.ts new file mode 100644 index 00000000..f9f34c5b --- /dev/null +++ b/packages/js-lib/src/core/ReactionData/ReactionsProvider.ts @@ -0,0 +1,132 @@ +import { FileIdFileIdentifier, GlobalTransitIdFileIdentifier } from '../../../core'; +import { tryJsonParse } from '../../helpers/DataUtil'; +import { EmojiReaction } from '../core'; + +import { DotYouClient } from '../DotYouClient'; + +const emojiRootTransit = '/transit/reactions'; +const emojiRoot = '/drive/files/reactions'; + +export interface ServerReactionsListWithCursor { + reactions: { + odinId: string; + reactionContent: string; + }[]; + cursor: string; +} + +export const uploadReaction = async ( + dotYouClient: DotYouClient, + emoji: string, + odinId: string | undefined, + file: FileIdFileIdentifier | GlobalTransitIdFileIdentifier +): Promise => { + const isLocal = odinId === dotYouClient.getIdentity(); + const client = dotYouClient.createAxiosClient(); + + const data = { + reaction: JSON.stringify({ emoji: emoji }), + file: file, + }; + + if (isLocal) { + const url = emojiRoot + '/add'; + return client + .post(url, data) + .then((response) => { + return { ...response.data, status: response.data?.status?.toLowerCase() }; + }) + .catch(dotYouClient.handleErrorResponse); + } else { + const url = emojiRootTransit + '/add'; + return client + .post(url, { odinId: odinId, request: data }) + .then((response) => ({ ...response.data, status: response.data?.status?.toLowerCase() })) + .catch(dotYouClient.handleErrorResponse); + } +}; + +export const deleteReaction = async ( + dotYouClient: DotYouClient, + emoji: EmojiReaction, + odinId: string | undefined, + file: FileIdFileIdentifier | GlobalTransitIdFileIdentifier +): Promise => { + const isLocal = odinId === dotYouClient.getIdentity(); + const client = dotYouClient.createAxiosClient(); + + const data = { + odinId: emoji.authorOdinId, + reaction: JSON.stringify({ emoji: emoji.body }), + file: file, + }; + + if (isLocal) { + const url = emojiRoot + '/delete'; + return client + .post(url, data) + .then((response) => { + return { ...response.data, status: response.data?.status?.toLowerCase() }; + }) + .catch(dotYouClient.handleErrorResponse); + } else { + const url = emojiRootTransit + '/delete'; + return client + .post(url, { odinId: odinId, request: data }) + .then((response) => { + return { ...response.data, status: response.data?.status?.toLowerCase() }; + }) + .catch(dotYouClient.handleErrorResponse); + } +}; + +export const getReactions = async ( + dotYouClient: DotYouClient, + odinId: string | undefined, + file: FileIdFileIdentifier | GlobalTransitIdFileIdentifier, + pageSize = 15, + cursor?: string +): Promise<{ reactions: EmojiReaction[]; cursor: string } | undefined> => { + const isLocal = odinId === dotYouClient.getIdentity(); + const client = dotYouClient.createAxiosClient(); + + const data = { + file: file, + cursor: cursor, + maxRecords: pageSize, + }; + + if (isLocal) { + const url = emojiRoot + '/list'; + return client + .post(url, data) + .then((response) => { + return { + reactions: response.data.reactions.map((reaction) => { + return { + authorOdinId: reaction.odinId, + body: tryJsonParse<{ emoji: string }>(reaction.reactionContent).emoji, + }; + }), + cursor: response.data.cursor, + }; + }) + .catch(dotYouClient.handleErrorResponse); + } else { + const url = emojiRootTransit + '/list'; + return client + .post(url, { odinId: odinId, request: data }) + .then((response) => { + return { + reactions: response.data.reactions.map((reaction) => { + return { + authorOdinId: reaction.odinId, + body: tryJsonParse<{ emoji: string }>(reaction.reactionContent).emoji, + }; + }), + cursor: response.data.cursor, + }; + }) + .catch(dotYouClient.handleErrorResponse); + } +}; diff --git a/packages/js-lib/src/core/core.ts b/packages/js-lib/src/core/core.ts index adb52f4c..17f8b12c 100644 --- a/packages/js-lib/src/core/core.ts +++ b/packages/js-lib/src/core/core.ts @@ -39,6 +39,7 @@ export * from './WebsocketData/WebsocketTypes'; // Reactions export * from './ReactionData/GroupReactionsProvider'; +export * from './ReactionData/ReactionsProvider'; // Notifications export * from './NotificationData/PushNotificationsProvider'; diff --git a/packages/js-lib/src/public/posts/PostEmojiReactionProvider.ts b/packages/js-lib/src/public/posts/PostEmojiReactionProvider.ts index ea32ab9a..37c0b1fb 100644 --- a/packages/js-lib/src/public/posts/PostEmojiReactionProvider.ts +++ b/packages/js-lib/src/public/posts/PostEmojiReactionProvider.ts @@ -1,95 +1,37 @@ import { DotYouClient } from '../../core/DotYouClient'; -import { ReactionFile } from '../../core/DriveData/File/DriveFileReactionTypes'; +import { deleteReaction, uploadReaction } from '../../core/ReactionData/ReactionsProvider'; import { tryJsonParse } from '../../helpers/DataUtil'; -import { GetTargetDriveFromChannelId } from './PostDefinitionProvider'; +import { getChannelDrive, GetTargetDriveFromChannelId } from './PostDefinitionProvider'; import { EmojiReactionSummary } from './PostReactionProvider'; -import { ReactionContext } from './PostTypes'; +import { RawReactionContent, ReactionContext } from './PostTypes'; interface ServerReactionsSummary { reactions: { reactionContent: string; count: number }[]; total: number; } -interface ServerReactionsListWithCursor { - reactions: { - odinId: string; - reactionContent: string; - }[]; - cursor: string; -} - -// TODO: Replace all of this with the new GroupReactionsProvider const emojiRootTransit = '/transit/reactions'; const emojiRoot = '/drive/files/reactions'; export const saveEmojiReaction = async ( dotYouClient: DotYouClient, - emoji: { body: string }, + emoji: RawReactionContent, context: ReactionContext ): Promise => { - const isLocal = context.odinId === dotYouClient.getIdentity(); - const client = dotYouClient.createAxiosClient(); - - const data = { - reaction: JSON.stringify({ emoji: emoji.body }), - file: { - targetDrive: GetTargetDriveFromChannelId(context.channelId), - fileId: context.target.fileId, - globalTransitId: context.target.globalTransitId, - }, - }; - - if (isLocal) { - const url = emojiRoot + '/add'; - return client - .post(url, data) - .then((response) => { - return { ...response.data, status: response.data?.status?.toLowerCase() }; - }) - .catch(dotYouClient.handleErrorResponse); - } else { - const url = emojiRootTransit + '/add'; - return client - .post(url, { odinId: context.odinId, request: data }) - .then((response) => ({ ...response.data, status: response.data?.status?.toLowerCase() })) - .catch(dotYouClient.handleErrorResponse); - } + return uploadReaction(dotYouClient, emoji.body, context.odinId, { + ...context.target, + targetDrive: getChannelDrive(context.channelId), + }); }; export const removeEmojiReaction = async ( dotYouClient: DotYouClient, - emoji: { body: string; authorOdinId?: string | undefined }, + emoji: RawReactionContent, context: ReactionContext ): Promise => { - const isLocal = context.odinId === dotYouClient.getIdentity(); - const client = dotYouClient.createAxiosClient(); - - const data = { - odinId: emoji.authorOdinId, - reaction: JSON.stringify({ emoji: emoji.body }), - file: { - targetDrive: GetTargetDriveFromChannelId(context.channelId), - fileId: context.target.fileId, - globalTransitId: context.target.globalTransitId, - }, - }; - - if (isLocal) { - const url = emojiRoot + '/delete'; - return client - .post(url, data) - .then((response) => { - return { ...response.data, status: response.data?.status?.toLowerCase() }; - }) - .catch(dotYouClient.handleErrorResponse); - } else { - const url = emojiRootTransit + '/delete'; - return client - .post(url, { odinId: context.odinId, request: data }) - .then((response) => { - return { ...response.data, status: response.data?.status?.toLowerCase() }; - }) - .catch(dotYouClient.handleErrorResponse); - } + return deleteReaction(dotYouClient, emoji, context.odinId, { + ...context.target, + targetDrive: getChannelDrive(context.channelId), + }); }; export const getReactionSummary = async ( @@ -165,60 +107,6 @@ export const getReactionSummary = async ( } }; -export const getReactions = async ( - dotYouClient: DotYouClient, - context: ReactionContext, - pageSize = 15, - cursor?: string -): Promise<{ reactions: ReactionFile[]; cursor: string } | undefined> => { - const isLocal = context.odinId === dotYouClient.getIdentity(); - const client = dotYouClient.createAxiosClient(); - - const data = { - file: { - targetDrive: GetTargetDriveFromChannelId(context.channelId), - fileId: context.target.fileId, - globalTransitId: context.target.globalTransitId, - }, - cursor: cursor, - maxRecords: pageSize, - }; - - if (isLocal) { - const url = emojiRoot + '/list'; - return client - .post(url, data) - .then((response) => { - return { - reactions: response.data.reactions.map((reaction) => { - return { - authorOdinId: reaction.odinId, - body: tryJsonParse<{ emoji: string }>(reaction.reactionContent).emoji, - }; - }), - cursor: response.data.cursor, - }; - }) - .catch(dotYouClient.handleErrorResponse); - } else { - const url = emojiRootTransit + '/list'; - return client - .post(url, { odinId: context.odinId, request: data }) - .then((response) => { - return { - reactions: response.data.reactions.map((reaction) => { - return { - authorOdinId: reaction.odinId, - body: tryJsonParse<{ emoji: string }>(reaction.reactionContent).emoji, - }; - }), - cursor: response.data.cursor, - }; - }) - .catch(dotYouClient.handleErrorResponse); - } -}; - export const getMyReactions = async ( dotYouClient: DotYouClient, odinId: string | undefined, diff --git a/packages/js-lib/src/public/posts/PostReactionProvider.ts b/packages/js-lib/src/public/posts/PostReactionProvider.ts index db7e8309..367cd433 100644 --- a/packages/js-lib/src/public/posts/PostReactionProvider.ts +++ b/packages/js-lib/src/public/posts/PostReactionProvider.ts @@ -21,7 +21,7 @@ import { EmbeddedThumb, NewHomebaseFile, PriorityOptions, - ReactionFile, + CommentReaction, } from '../../core/core'; import { jsonStringify64, @@ -148,7 +148,7 @@ export const saveComment = async ( const instructionSet: TransitInstructionSet = { transferIv: getRandom16ByteArray(), - overwriteGlobalTransitFileId: (comment as HomebaseFile).fileMetadata + overwriteGlobalTransitFileId: (comment as HomebaseFile).fileMetadata .globalTransitId, remoteTargetDrive: targetDrive, schedule: ScheduleOptions.SendLater, @@ -179,7 +179,7 @@ export const saveComment = async ( export const removeComment = async ( dotYouClient: DotYouClient, context: ReactionContext, - commentFile: HomebaseFile + commentFile: HomebaseFile ) => { const isLocal = context.odinId === dotYouClient.getIdentity(); const targetDrive = GetTargetDriveFromChannelId(context.channelId); @@ -206,7 +206,7 @@ export const getComments = async ( context: ReactionContext, pageSize = 25, cursorState?: string -): Promise<{ comments: HomebaseFile[]; cursorState: string }> => { +): Promise<{ comments: HomebaseFile[]; cursorState: string }> => { const isLocal = context.odinId === dotYouClient.getIdentity(); const targetDrive = GetTargetDriveFromChannelId(context.channelId); const qp: FileQueryParams = { @@ -225,13 +225,13 @@ export const getComments = async ( ? await queryBatch(dotYouClient, qp, ro) : await queryBatchOverPeer(dotYouClient, context.odinId, qp, ro); - const comments: HomebaseFile[] = ( + const comments: HomebaseFile[] = ( await Promise.all( result.searchResults.map(async (dsr) => dsrToComment(dotYouClient, context.odinId, dsr, targetDrive, result.includeMetadataHeader) ) ) - ).filter((attr) => !!attr) as HomebaseFile[]; + ).filter((attr) => !!attr) as HomebaseFile[]; return { comments, cursorState: result.cursorState }; }; @@ -242,14 +242,14 @@ const dsrToComment = async ( dsr: HomebaseFile, targetDrive: TargetDrive, includeMetadataHeader: boolean -): Promise | null> => { +): Promise | null> => { const isLocal = odinId === dotYouClient.getIdentity(); const params = [targetDrive, dsr, includeMetadataHeader] as const; const contentData = isLocal - ? await getContentFromHeaderOrPayload(dotYouClient, ...params) - : await getContentFromHeaderOrPayloadOverPeer(dotYouClient, odinId, ...params); + ? await getContentFromHeaderOrPayload(dotYouClient, ...params) + : await getContentFromHeaderOrPayloadOverPeer(dotYouClient, odinId, ...params); if (!contentData) return null; @@ -310,7 +310,7 @@ export interface CommentsReactionSummary { totalCount: number; } -export interface CommentReactionPreview extends ReactionFile { +export interface CommentReactionPreview extends CommentReaction { reactions: EmojiReactionSummary; isEncrypted: boolean; } @@ -332,7 +332,7 @@ export const parseReactionPreview = ( ...(commentPreview.isEncrypted && !commentPreview.content.length ? { body: '' } - : tryJsonParse(commentPreview.content)), + : tryJsonParse(commentPreview.content)), isEncrypted: commentPreview.isEncrypted, reactions: parseReactions(commentPreview.reactions), diff --git a/packages/js-lib/src/public/posts/PostTypes.ts b/packages/js-lib/src/public/posts/PostTypes.ts index a7a93b36..eb82286b 100644 --- a/packages/js-lib/src/public/posts/PostTypes.ts +++ b/packages/js-lib/src/public/posts/PostTypes.ts @@ -3,10 +3,10 @@ import { TargetDrive, EmbeddedThumb, RichText, - ReactionFile, NewHomebaseFile, SecurityGroupType, AccessControlList, + CommentReaction, } from '../../core/core'; import { toGuidId } from '../../helpers/helpers'; @@ -157,7 +157,7 @@ export interface ReactionContext { target: { fileId: string; globalTransitId: string; isEncrypted: boolean }; } -export interface RawReactionContent extends Omit { +export interface RawReactionContent extends Omit { attachment?: File; }