diff --git a/apps/u3/src/components/social/farcaster/FCast.tsx b/apps/u3/src/components/social/farcaster/FCast.tsx index 0c6a27d9..131e03cf 100644 --- a/apps/u3/src/components/social/farcaster/FCast.tsx +++ b/apps/u3/src/components/social/farcaster/FCast.tsx @@ -170,7 +170,7 @@ export default function FCast({ type: 'link', title: cast.text.slice(0, 200), // todo: expand this limit at model }); - }, [castId.hash]); + }, [castId.hash, isDetail]); const [updatedCast, setUpdatedCast] = useState(cast); const changeCastLikesWithCurrFid = (liked: boolean) => { @@ -208,7 +208,8 @@ export default function FCast({ }); }; const formatLinkParam = formatLink(linkParam); - const { getLinkId, linkId, setLinkId } = useLinkId(formatLinkParam); + const [linkId, setLinkId] = useState(''); + // const { getLinkId, linkId, setLinkId } = useLinkId(formatLinkParam); /** * 注:这里是区分v2版本布局,在这里兼容v2是为了保证功能一致改动时方便 @@ -341,18 +342,21 @@ export default function FCast({ e.stopPropagation(); }} > - { - if (!linkId && linkParam?.url && linkParam?.type) { - getLinkId(linkParam).then((id) => { - if (id) setLinkId(id); - else setLinkId(''); - }); - } - }} - /> + {isDetail && ( + { + // if (!linkId && linkParam?.url && linkParam?.type) { + // getLinkId(linkParam).then((id) => { + // if (id) setLinkId(id); + // else setLinkId(''); + // }); + // } + // }} + /> + )} + { + // onSaveSuccess?.(newLinkId); + // toast.success('Save successful'); + // }, + // onFailedFavor: (err: string) => { + // toast.error(`Save failed: ${err}`); + // }, + // }); + + const { isFavored, isFavoring, onFavor } = useFavorActionWithLink(link, { onSuccessfullyFavor: (done: boolean, newLinkId: string) => { onSaveSuccess?.(newLinkId); toast.success('Save successful'); @@ -75,8 +86,7 @@ export default function FCastSuperLike({ onFavor(); } }; - const superLiked = liked && recasted && isFavored; - console.log('superLiked', { superLiked, liked, recasted, isFavored }); + const superLiked = liked && recasted; return (
void; + onFailedFavor?: (errMsg: string) => void; + } +) { + const s3LinkModel = getS3LinkModel(); + const { signIn } = useAuthentication(); + const session = useSession(); + const { s3LinkModalAuthed } = useLinkState(); + const { fetchLinkIdWithLink } = useFetchLinkIdWithLink(); + const [isFavored, setIsFavored] = useState(false); + const [isFavoring, setIsFavoring] = useState(false); + + const onFavor = useCallback(async () => { + if (!session || !s3LinkModalAuthed) { + signIn(); + return; + } + if (!s3LinkModel) return; + try { + setIsFavoring(true); + // create link if not exist + let linkId = await fetchLinkIdWithLink(link); + + if (!linkId && link && link.url && link.type) { + const unknownLink = await s3LinkModel?.fetchLink(link); + if (unknownLink && unknownLink?.id) { + linkId = unknownLink?.id; + } else { + throw new Error('fail to fetch linkId'); + } + } + // find curr user favor + const favorsRes = await s3LinkModel.queryLinkFavors({ linkId }); + if (favorsRes?.errors && favorsRes?.errors.length > 0) { + throw new Error(favorsRes?.errors[0]?.message); + } + const data = favorsRes.data?.node; + const favors = + data?.favors?.edges + ?.map((edge) => edge?.node) + ?.filter((node) => !!node) || []; + const findCurrUserFavor = favors.find( + (node) => node?.creator?.id === session?.id + ); + if (findCurrUserFavor) { + // update favor + const { id } = findCurrUserFavor; + // const revoke = !findCurrUserFavor.revoke; + const revoke = false; + const updateFavorRes = await s3LinkModel?.updateFavor(id, { revoke }); + + if (updateFavorRes?.errors && updateFavorRes?.errors.length > 0) { + throw new Error(updateFavorRes?.errors[0]?.message); + } + setIsFavored(!revoke); + if (opts?.onSuccessfullyFavor) + opts.onSuccessfullyFavor(!revoke, linkId); + } else { + // create favor + // console.log('start favor', linkId) + const res = await s3LinkModel?.createFavor({ + linkID: linkId, + revoke: false, + }); + if (res?.errors && res?.errors.length > 0) { + throw new Error(res?.errors[0]?.message); + } + setIsFavored(true); + if (opts?.onSuccessfullyFavor) opts.onSuccessfullyFavor(true, linkId); + } + } catch (error) { + const errMsg = error?.message; + if (opts?.onFailedFavor) opts.onFailedFavor(errMsg); + } finally { + setIsFavoring(false); + } + }, [ + session, + link?.url, + link?.type, + fetchLinkIdWithLink, + s3LinkModalAuthed, + signIn, + opts?.onSuccessfullyFavor, + opts?.onFailedFavor, + ]); + + return { isFavored, isFavoring, onFavor }; +} diff --git a/apps/u3/src/hooks/shared/useFetchLinkId.ts b/apps/u3/src/hooks/shared/useFetchLinkId.ts new file mode 100644 index 00000000..ee9f7c98 --- /dev/null +++ b/apps/u3/src/hooks/shared/useFetchLinkId.ts @@ -0,0 +1,45 @@ +/* eslint-disable @typescript-eslint/no-shadow */ +import { useState } from 'react'; +import { Link } from '@us3r-network/data-model'; +import { getS3LinkModel, useLinkState } from '@us3r-network/link'; + +export const useFetchLinkIdWithLink = () => { + const s3LinkModel = getS3LinkModel(); + const { s3LinkModalInitialed } = useLinkState(); + const [linkId, setLinkId] = useState(''); + const [loading, setLoading] = useState(false); + + const fetchLinkIdWithLink = async (link: Link) => { + if (!s3LinkModel || !s3LinkModalInitialed) return ''; + try { + setLoading(true); + const filters = { + where: { + url: { + equalTo: link.url, + }, + type: { + equalTo: link.type, + }, + }, + }; + const resp = await s3LinkModel?.queryLinks({ + filters, + }); + const links = resp?.data?.linkIndex?.edges; + if (links && links.length > 0) { + const { id } = links[0].node; + setLinkId(id); + return id; + } + throw new Error('No link found'); + } catch (error) { + setLinkId(''); + return ''; + } finally { + setLoading(false); + } + }; + + return { fetchLinkIdWithLink, loading, linkId }; +};