const { fetchGraphQL, GRAPHQL_ENDPOINT } = VM.require( "near/widget/Entities.QueryApi.Client" ); if (!fetchGraphQL || !GRAPHQL_ENDPOINT) return <></>; const LIMIT = 10; const ACCOUNT_ID = "crans.near"; State.init({ shouldFallback: false, posts: [], }); const postsQuery = ` query PostsQuery { dataplatform_near_social_feed_posts( limit: ${LIMIT}, order_by: { block_height: desc }, where: { account_id: { _eq: "${ACCOUNT_ID}" } } ) { account_id block_height content } } `; const fetchPosts = () => { fetchGraphQL(postsQuery, "PostsQuery", {}) .then((response) => { if ( response && response.body.data.dataplatform_near_social_feed_posts.length > 0 ) { State.update({ posts: response.body.data.dataplatform_near_social_feed_posts, shouldFallback: false, }); } else { console.log( "Falling back to Social index feed. No QueryApi data received." ); State.update({ shouldFallback: true }); } }) .catch((error) => { console.log( "Error while fetching QueryApi feed (falling back to index feed): ", error ); State.update({ shouldFallback: true }); }); }; fetchPosts(); const FallbackFeed = () => ( <Widget src={`near/widget/v1.Feed`} props={{ accounts: [ACCOUNT_ID], limit: LIMIT, }} /> ); const Markdown = (props) => <Widget src="near/widget/Markdown" props={props} />; // Function to calculate time ago const getTimeAgo = (blockHeight) => { const currentBlockHeight = Near.block("optimistic").header.height; const blocksPassed = currentBlockHeight - blockHeight; const millisecondsPassed = blocksPassed * 1000; // Assuming 1 block per second const secondsPassed = Math.floor(millisecondsPassed / 1000); const minutesPassed = Math.floor(secondsPassed / 60); const hoursPassed = Math.floor(minutesPassed / 60); const daysPassed = Math.floor(hoursPassed / 24); if (daysPassed > 0) return `${daysPassed}d ago`; if (hoursPassed > 0) return `${hoursPassed}h ago`; if (minutesPassed > 0) return `${minutesPassed}m ago`; return `${secondsPassed}s ago`; }; const PostItem = ({ post }) => { const content = JSON.parse(post.content); const timeAgo = getTimeAgo(post.block_height); return ( <div className="post-item" style={{ marginBottom: "20px", borderBottom: "1px solid #eee", paddingBottom: "20px", }} > <div style={{ display: "flex", alignItems: "center", marginBottom: "10px" }} > <Widget src="mob.near/widget/ProfileImage" props={{ accountId: post.account_id, style: { width: "40px", height: "40px", borderRadius: "50%", marginRight: "10px", }, }} /> <div> <strong>{post.account_id}</strong> <div style={{ fontSize: "0.8em", color: "#888" }}>{timeAgo}</div> </div> </div> <Markdown text={content.text} /> {content.image && ( <Widget src="mob.near/widget/Image" props={{ image: content.image, style: { marginTop: "10px", maxWidth: "100%" }, }} /> )} <div style={{ marginTop: "10px", fontSize: "0.8em", color: "#888" }}> Block Height: {post.block_height} </div> </div> ); }; return ( <div className="activity-feed" style={{ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif', maxWidth: "600px", margin: "0 auto", }} > {state.shouldFallback ? ( <FallbackFeed /> ) : ( <> {state.posts.map((post, index) => ( <PostItem key={index} post={post} /> ))} </> )} </div> );