refactor: create useReaction hook, used in PureStatusReactionWrapper componet
This commit is contained in:
parent
d2126f60f2
commit
afd1f49e59
|
@ -47,6 +47,7 @@ export { useUpdateGroupTag } from './groups/useUpdateGroupTag.ts';
|
|||
export { useBookmarks } from './statuses/useBookmarks.ts';
|
||||
export { useBookmark } from './statuses/useBookmark.ts';
|
||||
export { useFavourite } from './statuses/useFavourite.ts';
|
||||
export { useReaction } from './statuses/useReaction.ts';
|
||||
|
||||
// Streaming
|
||||
export { useUserStream } from './streaming/useUserStream.ts';
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
import { useFavourite } from 'soapbox/api/hooks/index.ts';
|
||||
import { importEntities } from 'soapbox/entity-store/actions.ts';
|
||||
import { Entities } from 'soapbox/entity-store/entities.ts';
|
||||
import { useTransaction } from 'soapbox/entity-store/hooks/index.ts';
|
||||
import { useApi } from 'soapbox/hooks/useApi.ts';
|
||||
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
||||
import { useGetState } from 'soapbox/hooks/useGetState.ts';
|
||||
import { EmojiReaction, Status as StatusEntity, statusSchema } from 'soapbox/schemas/index.ts';
|
||||
import { isLoggedIn } from 'soapbox/utils/auth.ts';
|
||||
|
||||
export function useReaction() {
|
||||
const api = useApi();
|
||||
const getState = useGetState();
|
||||
const dispatch = useAppDispatch();
|
||||
const { transaction } = useTransaction();
|
||||
const { favourite, unfavourite } = useFavourite();
|
||||
|
||||
function emojiReactEffect(statusId: string, emoji: string) {
|
||||
transaction({
|
||||
Statuses: {
|
||||
[statusId]: (status) => {
|
||||
// Get the emoji already present in the status reactions, if it exists.
|
||||
const currentEmoji = status.reactions.find((value) => value.name === emoji);
|
||||
// If the emoji doesn't exist, append it to the array and return.
|
||||
if (!currentEmoji) {
|
||||
return ({
|
||||
...status,
|
||||
reactions: [...status.reactions, { me: true, name: emoji, count: 1 }],
|
||||
});
|
||||
}
|
||||
// if the emoji exists in the status reactions, then just update the array and return.
|
||||
return ({
|
||||
...status,
|
||||
reactions: status.reactions.map((val) => {
|
||||
if (val.name === emoji) {
|
||||
return { ...val, me: true, count: (val.count ?? 0) + 1 };
|
||||
}
|
||||
return val;
|
||||
}),
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function unemojiReactEffect(statusId: string, emoji: string) {
|
||||
transaction({
|
||||
Statuses: {
|
||||
[statusId]: (status) => {
|
||||
return ({
|
||||
...status,
|
||||
reactions: status.reactions.map((val) => {
|
||||
if (val.name === emoji && val.me === true) {
|
||||
return { ...val, me: false, count: (val.count ?? 1) - 1 };
|
||||
}
|
||||
return val;
|
||||
}),
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const emojiReact = async (status: StatusEntity, emoji: string) => { // TODO: add custom emoji support
|
||||
if (!isLoggedIn(getState)) return;
|
||||
emojiReactEffect(status.id, emoji);
|
||||
|
||||
try {
|
||||
const response = await api.put(`/api/v1/pleroma/statuses/${status.id}/reactions/${emoji}`);
|
||||
const result = statusSchema.parse(await response.json());
|
||||
if (result) {
|
||||
dispatch(importEntities([result], Entities.STATUSES));
|
||||
}
|
||||
} catch (e) {
|
||||
unemojiReactEffect(status.id, emoji);
|
||||
}
|
||||
};
|
||||
|
||||
const unEmojiReact = async (status: StatusEntity, emoji: string) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
unemojiReactEffect(status.id, emoji);
|
||||
|
||||
try {
|
||||
const response = await api.delete(`/api/v1/pleroma/statuses/${status.id}/reactions/${emoji}`);
|
||||
const result = statusSchema.parse(await response.json());
|
||||
if (result) {
|
||||
dispatch(importEntities([result], Entities.STATUSES));
|
||||
}
|
||||
} catch (e) {
|
||||
emojiReactEffect(status.id, emoji);
|
||||
}
|
||||
};
|
||||
|
||||
const simpleEmojiReact = async (status: StatusEntity, emoji: string) => {
|
||||
const emojiReacts: readonly EmojiReaction[] = status.reactions;
|
||||
|
||||
// Undo a standard favourite
|
||||
if (emoji === '👍' && status.favourited) return unfavourite(status.id);
|
||||
|
||||
// Undo an emoji reaction
|
||||
const undo = emojiReacts.filter(e => e.me === true && e.name === emoji).length > 0;
|
||||
if (undo) return unEmojiReact(status, emoji);
|
||||
|
||||
try {
|
||||
await Promise.all([
|
||||
...emojiReacts
|
||||
.filter((emojiReact) => emojiReact.me === true)
|
||||
// Remove all existing emoji reactions by the user before adding a new one. If 'emoji' is an 'apple' and the status already has 'banana' as an emoji, then remove 'banana'
|
||||
.map(emojiReact => unEmojiReact(status, emojiReact.name)),
|
||||
// Remove existing standard like, if it exists
|
||||
status.favourited && unfavourite(status.id),
|
||||
]);
|
||||
|
||||
if (emoji === '👍') {
|
||||
favourite(status.id);
|
||||
} else {
|
||||
emojiReact(status, emoji);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
return { emojiReact, unEmojiReact, simpleEmojiReact };
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import { useState, useEffect, useRef, cloneElement } from 'react';
|
||||
|
||||
import { simpleEmojiReact } from 'soapbox/actions/emoji-reacts.ts';
|
||||
import { openModal } from 'soapbox/actions/modals.ts';
|
||||
import { useReaction } from 'soapbox/api/hooks/index.ts';
|
||||
import EmojiSelector from 'soapbox/components/ui/emoji-selector.tsx';
|
||||
import Portal from 'soapbox/components/ui/portal.tsx';
|
||||
import { Entities } from 'soapbox/entity-store/entities.ts';
|
||||
|
@ -10,11 +10,8 @@ import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
|||
import { useGetState } from 'soapbox/hooks/useGetState.ts';
|
||||
import { useOwnAccount } from 'soapbox/hooks/useOwnAccount.ts';
|
||||
import { userTouching } from 'soapbox/is-mobile.ts';
|
||||
import { normalizeStatus } from 'soapbox/normalizers/index.ts';
|
||||
import { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
|
||||
import type { Status as LegacyStatus } from 'soapbox/types/entities.ts';
|
||||
|
||||
interface IPureStatusReactionWrapper {
|
||||
statusId: string;
|
||||
children: JSX.Element;
|
||||
|
@ -27,6 +24,7 @@ const PureStatusReactionWrapper: React.FC<IPureStatusReactionWrapper> = ({ statu
|
|||
const getState = useGetState();
|
||||
|
||||
const status = selectEntity<StatusEntity>(getState(), Entities.STATUSES, statusId);
|
||||
const { simpleEmojiReact } = useReaction();
|
||||
|
||||
const timeout = useRef<NodeJS.Timeout>();
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
@ -71,7 +69,7 @@ const PureStatusReactionWrapper: React.FC<IPureStatusReactionWrapper> = ({ statu
|
|||
|
||||
const handleReact = (emoji: string, custom?: string): void => {
|
||||
if (ownAccount) {
|
||||
dispatch(simpleEmojiReact(normalizeStatus(status) as LegacyStatus, emoji, custom));
|
||||
simpleEmojiReact(status, emoji);
|
||||
} else {
|
||||
handleUnauthorized();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue