diff --git a/apps/u3/.env.development b/apps/u3/.env.development index 6a2753a9..03d46a31 100644 --- a/apps/u3/.env.development +++ b/apps/u3/.env.development @@ -16,7 +16,7 @@ REACT_APP_WALLET_CONNECT_PROJECT_ID = c652d0148879353d7e965d7f6f361e59 REACT_APP_AIRSTACK_API_KEY = 135391e4df1d47fa898d43c9f4b27329 # social api is prod only for now -REACT_APP_API_SOCIAL_URL = https://api-dev.u3.xyz +REACT_APP_API_SOCIAL_URL = https://api.u3.xyz # REACT_APP_API_SOCIAL_URL = http://localhost:3001 # 'local'| 'dev' | 'production' REACT_APP_XMTP_ENV = dev diff --git a/apps/u3/package.json b/apps/u3/package.json index 32e0924a..964c1743 100644 --- a/apps/u3/package.json +++ b/apps/u3/package.json @@ -33,6 +33,7 @@ "@tiptap/react": "^2.1.13", "@types/validator": "^13.11.7", "@us3r-network/auth-with-rainbowkit": "^0.2.1", + "@us3r-network/data-model": "^0.7.0", "@us3r-network/link": "0.7.1-alpha.0", "@us3r-network/profile": "0.7.0", "@wagmi/core": "^1.4.12", diff --git a/apps/u3/src/components/notification/NotificationModal.tsx b/apps/u3/src/components/notification/NotificationModal.tsx index 661606f3..f88877f9 100644 --- a/apps/u3/src/components/notification/NotificationModal.tsx +++ b/apps/u3/src/components/notification/NotificationModal.tsx @@ -56,15 +56,16 @@ export default function NotificationModal() { }} > - {notifications.map((notification) => { + {notifications.map((notification, index) => { if ('message_hash' in notification) { return ( ); } @@ -117,7 +118,7 @@ export function FarcasterNotificationItem({ `/social/post-detail/fcast/${Buffer.from( notification.replies_parent_hash ).toString('hex')}#${Buffer.from( - notification.casts_hash + notification.replies_hash ).toString('hex')}` ); setOpenNotificationModal(false); diff --git a/apps/u3/src/components/save/SaveExploreListItem.tsx b/apps/u3/src/components/save/SaveExploreListItem.tsx index 064489a8..408457ae 100644 --- a/apps/u3/src/components/save/SaveExploreListItem.tsx +++ b/apps/u3/src/components/save/SaveExploreListItem.tsx @@ -12,7 +12,7 @@ export default function SaveExploreListItem({ data, ...props }: SaveExploreListItemProps) { - console.log('save item', data); + // console.log('save item', data); return ( diff --git a/apps/u3/src/components/save/SyncingBotSaves.tsx b/apps/u3/src/components/save/SyncingBotSaves.tsx new file mode 100644 index 00000000..db537759 --- /dev/null +++ b/apps/u3/src/components/save/SyncingBotSaves.tsx @@ -0,0 +1,117 @@ +import { useFavorAction } from '@us3r-network/link'; +import { useSession } from '@us3r-network/auth-with-rainbowkit'; +import { useCallback, useEffect, useState } from 'react'; +import { Link } from '@us3r-network/data-model'; +import { + getSavedCasts, + setSavedCastsSynced, +} from '@/services/social/api/farcaster'; +import { getAddressWithDidPkh } from '../../utils/shared/did'; + +export default function SyncingBotSaves({ + onComplete, +}: { + onComplete?: () => void; +}) { + const session = useSession(); + const [saves, setSaves] = useState([]); + const [link, setLink] = useState(null); + + useEffect(() => { + const walletAddress = getAddressWithDidPkh(session.id); + getSavedCasts(walletAddress).then((res) => { + setSaves(res); + console.log('getSavedCasts', res); + }); + }, [session]); + + useEffect(() => { + if (!saves || saves.length === 0) return; + syncNext(); + }, [saves.length]); + + const syncNext = useCallback(() => { + if (!saves || saves.length === 0) return; + const save = saves[0]; + const nextLink = { + url: `https://u3.xyz/social/post-detail/fcast/${Buffer.from( + save.castHash.data + ).toString('hex')}`, + title: save.text || 'Saved Farcaster Cast using U3 Bot', + type: 'link', + data: JSON.stringify(save), + }; + console.log('cast', nextLink); + setLink(nextLink); + }, [saves]); + + return link ? ( + { + console.log('onSuccessfullyFavor', isFavored, linkId); + if (isFavored) { + const linkData = JSON.parse(link.data); + const { id, walletAddress } = linkData; + console.log(id, walletAddress); + setSavedCastsSynced(Buffer.from(walletAddress).toString('hex'), id); + const newSaves = saves.slice(1); + setSaves(newSaves); + if (newSaves.length === 0) onComplete(); + } + }} + onFailedFavor={(errMsg: string) => { + console.log('onFailedFavor', errMsg); + }} + /> + ) : null; +} + +function FavoringLink({ + link, + onSuccessfullyFavor, + onFailedFavor, +}: { + link?: Link | undefined; + onSuccessfullyFavor?: (isFavored: boolean, linkId: string) => void; + onFailedFavor?: (errMsg: string) => void; +}) { + // console.log('FavoringLink', link); + const { isFavored, isFavoring, onFavor } = useFavorAction('', link, { + onSuccessfullyFavor: (done: boolean, newLinkId: string) => { + // console.log('onSuccessfullyFavor in FavoringLink', done, newLinkId); + onSuccessfullyFavor(done, newLinkId); + }, + onFailedFavor: (err: string) => { + // console.log('onFailedFavor in FavoringLink', err); + onFailedFavor(err); + }, + }); + useEffect(() => { + console.log('link: ', link, isFavored); + if (link) + setTimeout(() => { + console.log('onFavor fire!'); + if (!isFavored && !isFavoring) onFavor(); + }, 500); + }, [link]); + + if (isFavoring) + return ( +
{`Favoring Bot Saved Link '${link.title}' ......`}
+ ); + if (isFavored) + return ( +
{`Favored Bot Saved Link '${link.title}' ......`}
+ ); + // return ( + //
+ //

{link?.title}

+ //

{link?.url}

+ //

{link?.type}

+ // + //
+ // ); +} diff --git a/apps/u3/src/container/Save.tsx b/apps/u3/src/container/Save.tsx index ddbddd34..23e7ae54 100644 --- a/apps/u3/src/container/Save.tsx +++ b/apps/u3/src/container/Save.tsx @@ -2,7 +2,7 @@ import { useMemo } from 'react'; import styled from 'styled-components'; import { usePersonalFavors } from '@us3r-network/link'; import { isMobile } from 'react-device-detect'; - +import { uniqBy } from 'lodash'; import { MainWrapper } from '../components/layout/Index'; import Loading from '../components/common/loading/Loading'; import PageTitle from '../components/layout/PageTitle'; @@ -14,6 +14,7 @@ import { } from '../utils/news/content'; import { getDappLinkDataWithJsonValue } from '../utils/dapp/dapp'; import { getEventLinkDataWithJsonValue } from '../utils/news/event'; +import SyncingBotSaves from '@/components/save/SyncingBotSaves'; // import { DappLinkData } from '../services/dapp/types/dapp'; // import { ContentLinkData } from '../services/news/types/contents'; // import { EventLinkData } from '../services/news/types/event'; @@ -51,43 +52,57 @@ const EmptyDesc = styled.span` export default function Save() { const { isFetching, personalFavors } = usePersonalFavors(); - - const list = personalFavors - .filter((item) => !!item?.link) - .map((item) => { - const { link, createAt } = item; - let linkData; - let title = ''; - let logo = ''; - switch (link.type) { - case 'dapp': - linkData = getDappLinkDataWithJsonValue(link?.data); - title = linkData?.name || link.title; - logo = linkData?.image || ''; - break; - case 'content': - linkData = getContentLinkDataWithJsonValue(link?.data); - title = linkData?.title || link.title; - logo = - getContentPlatformLogoWithJsonValue(linkData?.value) || - linkData?.platform?.logo || - ''; - break; - case 'event': - linkData = getEventLinkDataWithJsonValue(link?.data); - title = linkData?.name || link.title; - logo = linkData?.image || linkData?.platform?.logo || ''; - break; - default: - break; - } - return { ...link, id: link.id, title, logo, createAt }; - }); + // console.log('personalFavors', personalFavors); + const list = useMemo( + () => + uniqBy( + personalFavors + .filter((item) => !!item?.link && item.link.type !== 'test') + .map((item) => { + const { link, createAt } = item; + let linkData; + let title = ''; + let logo = ''; + switch (link.type) { + case 'dapp': + linkData = getDappLinkDataWithJsonValue(link?.data); + title = linkData?.name || link.title; + logo = linkData?.image || ''; + break; + case 'content': + linkData = getContentLinkDataWithJsonValue(link?.data); + title = linkData?.title || link.title; + logo = + getContentPlatformLogoWithJsonValue(linkData?.value) || + linkData?.platform?.logo || + ''; + break; + case 'event': + linkData = getEventLinkDataWithJsonValue(link?.data); + title = linkData?.name || link.title; + logo = linkData?.image || linkData?.platform?.logo || ''; + break; + default: + linkData = JSON.parse(link?.data); + title = linkData?.title || link.title; + logo = linkData?.image || ''; + break; + } + return { ...link, id: link.id, title, logo, createAt }; + }), + 'id' + ), + [personalFavors] + ); const isEmpty = useMemo(() => list.length === 0, [list]); - return ( {isMobile ? null : Saves} + { + console.log('onComplete SyncingBotSaves'); + }} + /> {isFetching ? ( diff --git a/apps/u3/src/services/social/api/farcaster.ts b/apps/u3/src/services/social/api/farcaster.ts index 70261052..7ba2d556 100644 --- a/apps/u3/src/services/social/api/farcaster.ts +++ b/apps/u3/src/services/social/api/farcaster.ts @@ -17,6 +17,7 @@ export type FarcasterNotification = { casts_hash?: Buffer; casts_text?: string; replies_text?: string; + replies_hash?: Buffer; replies_parent_hash?: Buffer; casts_mentions?: number[]; }; @@ -474,11 +475,11 @@ export async function getMetadataWithMod( return metadata; } -export function getSavedCasts(walletAddress: string, pageSize?: number) { +export async function getSavedCasts(walletAddress: string, pageSize?: number) { if (walletAddress.startsWith('0x')) { walletAddress = walletAddress.slice(2); } - return request({ + const resp = await request({ url: `${REACT_APP_API_SOCIAL_URL}/3r-bot/saved-casts`, method: 'get', params: { @@ -486,6 +487,7 @@ export function getSavedCasts(walletAddress: string, pageSize?: number) { pageSize, }, }); + return resp?.data?.data?.saves; } export function setSavedCastsSynced(walletAddress: string, lastedId: number) {