diff --git a/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.Comment.jsx b/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.Comment.jsx index b0b0f9d7e..96429ca0d 100644 --- a/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.Comment.jsx +++ b/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.Comment.jsx @@ -26,7 +26,7 @@ const commentUrl = `https://alpha.near.org/#/${APP_OWNER}/widget/QueryApi.Exampl if (!state.content && accountId && blockHeight !== "now") { const commentQuery = ` query CommentQuery { - dataplatform_near_feed_indexer_comments( + dataplatform_near_social_feed_comments( where: {_and: {account_id: {_eq: "${accountId}"}, block_height: {_eq: ${blockHeight}}}} ) { content @@ -57,7 +57,7 @@ query CommentQuery { fetchGraphQL(commentQuery, "CommentQuery", {}).then((result) => { if (result.status === 200) { if (result.body.data) { - const comments = result.body.data.dataplatform_near_feed_indexer_comments; + const comments = result.body.data.dataplatform_near_social_feed_comments; if (comments.length > 0) { const comment = comments[0]; let content = JSON.parse(comment.content); diff --git a/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.Post.jsx b/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.Post.jsx index 2138f9751..4e7673a31 100644 --- a/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.Post.jsx +++ b/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.Post.jsx @@ -23,10 +23,9 @@ const item = { // Load post if not contents and comments are not passed in if (!state.content || !state.comments || !state.likes) { - console.log("making call again"); const postsQuery = ` query IndexerQuery { - dataplatform_near_feed_indexer_posts( + dataplatform_near_social_feed_posts( order_by: {block_height: desc} where: {_and: {block_height: {_eq: ${blockHeight}}, account_id: {_eq: "${accountId}"}}} ) { @@ -64,7 +63,7 @@ query IndexerQuery { fetchGraphQL(postsQuery, "IndexerQuery", {}).then((result) => { if (result.status === 200) { if (result.body.data) { - const posts = result.body.data.dataplatform_near_feed_indexer_posts; + const posts = result.body.data.dataplatform_near_social_feed_posts; if (posts.length > 0) { const post = posts[0]; let content = JSON.parse(post.content); diff --git a/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.PostPage.jsx b/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.PostPage.jsx index b400f2166..27afc38bb 100644 --- a/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.PostPage.jsx +++ b/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.PostPage.jsx @@ -13,7 +13,7 @@ State.init({ }); const parentPostByComment = `query ParentPostByComment { - dataplatform_near_feed_indexer_comments( + dataplatform_near_social_feed_comments( where: {_and: {account_id: {_eq: "${accountId}"}, block_height: {_eq: ${commentBlockHeight}}}} ) { post { @@ -64,7 +64,7 @@ if (commentBlockHeight) { (result) => { if (result.status === 200) { if (result.body.data) { - const posts = result.body.data.dataplatform_near_feed_indexer_comments; + const posts = result.body.data.dataplatform_near_social_feed_comments; if (posts.length > 0) { const post = posts[0].post; let content = JSON.parse(post.content); diff --git a/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.Posts.jsx b/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.Posts.jsx new file mode 100644 index 000000000..43753e27f --- /dev/null +++ b/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.Posts.jsx @@ -0,0 +1,303 @@ +const APP_OWNER = props.APP_OWNER || "dataplatform.near"; +const GRAPHQL_ENDPOINT = + props.GRAPHQL_ENDPOINT || "https://near-queryapi.api.pagoda.co"; +const sortOption = props.postsOrderOption || "blockHeight"; // following, blockHeight +const LIMIT = 25; +let accountsFollowing = props.accountsFollowing + +if (context.accountId && !accountsFollowing) { + const graph = Social.keys(`${context.accountId}/graph/follow/*`, "final"); + if (graph !== null) { + accountsFollowing = Object.keys(graph[context.accountId].graph.follow || {}); + } +} + +State.init({ + selectedTab: Storage.privateGet("selectedTab") || "all", + posts: [], + postsCountLeft: 0, + initLoadPosts: false, + initLoadPostsAll: false +}); + +function fetchGraphQL(operationsDoc, operationName, variables) { + return asyncFetch( + `${GRAPHQL_ENDPOINT}/v1/graphql`, + { + method: "POST", + headers: { "x-hasura-role": "dataplatform_near" }, + body: JSON.stringify({ + query: operationsDoc, + variables: variables, + operationName: operationName, + }), + } + ); +} + +const createQuery = (sortOption, type) => { +let querySortOption = ""; +switch (sortOption) { + case "recentComments": + querySortOption = `{ last_comment_timestamp: desc_nulls_last },`; + break; + // More options... + default: + querySortOption = ""; +} + +let queryFilter = ""; +switch (type) { + case "following": + let queryAccountsString = accountsFollowing.map(account => `"${account}"`).join(", "); + queryFilter = `account_id: { _in: [${queryAccountsString}]}`; + break; + // More options... + default: + queryFilter = ""; +} + +const indexerQueries = ` + query GetPostsQuery($offset: Int) { + dataplatform_near_social_feed_posts(order_by: [${querySortOption} { block_height: desc }], offset: $offset, limit: ${LIMIT}) { + account_id + block_height + block_timestamp + content + receipt_id + accounts_liked + last_comment_timestamp + comments(order_by: {block_height: asc}) { + account_id + block_height + block_timestamp + content + } + } + dataplatform_near_social_feed_posts_aggregate(order_by: [${querySortOption} { block_height: desc }], offset: $offset){ + aggregate { + count + } + } +} +query GetFollowingPosts($offset: Int) { + dataplatform_near_social_feed_posts(where: {${queryFilter}}, order_by: [${querySortOption} { block_height: desc }], offset: $offset) { + account_id + block_height + block_timestamp + content + receipt_id + accounts_liked + last_comment_timestamp + comments(order_by: {block_height: asc}) { + account_id + block_height + block_timestamp + content + } + } + dataplatform_near_social_feed_posts_aggregate(where: {${queryFilter}}, order_by: [${querySortOption} { block_height: desc }], offset: $offset) { + aggregate { + count + } + } +} +`; +return indexerQueries +} + +const loadMorePosts = () => { + const queryName = state.selectedTab == "following" && accountsFollowing ? "GetFollowingPosts" : "GetPostsQuery" + const type = state.selectedTab == "following" && accountsFollowing ? "following" : "all" + + if(state.selectedTab == "following" && accountsSelected && accountsSelected.length == 0) { + console.log("user has no followers") + return + } + fetchGraphQL(createQuery(sortOption, type), queryName, { + offset: state.posts.length, + }).then((result) => { + if (result.status === 200 && result.body) { + if(result.body.errors) { + console.log('error:', result.body.errors) + return + } + let data = result.body.data; + if (data) { + const newPosts = data.dataplatform_near_social_feed_posts; + const postsCountLeft = + data.dataplatform_near_social_feed_posts_aggregate.aggregate.count; + if (newPosts.length > 0) { + State.update({ + posts: [...state.posts, ...newPosts], + postsCountLeft, + }); + } + } + } + }); +}; + +const previousSelectedTab = Storage.privateGet("selectedTab"); +if (previousSelectedTab && previousSelectedTab !== state.selectedTab) { + State.update({ + selectedTab: previousSelectedTab, + }); +} + +function selectTab(selectedTab) { + Storage.privateSet("selectedTab", selectedTab); + State.update({ + posts: [], + postsCountLeft: 0, + selectedTab + }); + loadMorePosts() +} + +const H2 = styled.h2` + font-size: 19px; + line-height: 22px; + color: #11181C; + margin: 0 0 24px; + padding: 0 24px; + + @media (max-width: 1200px) { + display: none; + } +`; + +const Content = styled.div` + @media (max-width: 1200px) { + > div:first-child { + border-top: none; + } + } +`; + +const ComposeWrapper = styled.div` + border-top: 1px solid #ECEEF0; +`; + +const FilterWrapper = styled.div` + border-top: 1px solid #ECEEF0; + padding: 24px 24px 0; + + @media (max-width: 1200px) { + padding: 12px; + } +`; + +const PillSelect = styled.div` + display: inline-flex; + align-items: center; + + @media (max-width: 600px) { + width: 100%; + + button { + flex: 1; + } + } +`; + +const PillSelectButton = styled.button` + display: block; + position: relative; + border: 1px solid #E6E8EB; + border-right: none; + padding: 3px 24px; + border-radius: 0; + font-size: 12px; + line-height: 18px; + color: ${(p) => (p.selected ? "#fff" : "#687076")}; + background: ${(p) => (p.selected ? "#006ADC !important" : "#FBFCFD")}; + font-weight: 600; + transition: all 200ms; + + &:hover { + background: #ECEDEE; + text-decoration: none; + } + + &:focus { + outline: none; + border-color: #006ADC !important; + box-shadow: 0 0 0 1px #006ADC; + z-index: 5; + } + + &:first-child { + border-radius: 6px 0 0 6px; + } + &:last-child { + border-radius: 0 6px 6px 0; + border-right: 1px solid #E6E8EB; + } +`; + +const FeedWrapper = styled.div` + .post { + padding-left: 24px; + padding-right: 24px; + + @media (max-width: 1200px) { + padding-left: 12px; + padding-right: 12px; + } + } +`; + +const hasMore = state.postsCountLeft != state.posts.length + +if(!state.initLoadPostsAll) { + loadMorePosts() + State.update({initLoadPostsAll: true}) +} + +if(state.initLoadPostsAll == true && !state.initLoadPosts && accountsFollowing) { + if (accountsFollowing.length > 0 && state.selectedTab == "following") { + selectTab("following") + } + State.update({initLoadPosts: true}) +} + +return ( + <> +

Posts

+ + + {context.accountId && ( + <> + + + + + + + selectTab("all")} + selected={state.selectedTab === "all"} + > + All + + + selectTab("following")} + selected={state.selectedTab === "following"} + > + Following + + + + + )} + + + + + + +); diff --git a/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.jsx b/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.jsx index b1a3515b3..f8855e248 100644 --- a/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.jsx +++ b/frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.jsx @@ -1,13 +1,9 @@ const GRAPHQL_ENDPOINT = props.GRAPHQL_ENDPOINT || "https://near-queryapi.api.pagoda.co"; const APP_OWNER = props.APP_OWNER || "dataplatform.near"; -const LIMIT = 10; -const option = props.postsOrderOption ?? "blockHeight"; - -State.init({ - posts: [], - postsCount: 0, -}); +const loadMorePosts = props.loadMorePosts; +const hasMore = props.hasMore || false; +const posts = props.posts || []; const Subheading = styled.h2` display: block; @@ -23,55 +19,6 @@ const Subheading = styled.h2` outline: none; `; -let querySortFilter = ""; -switch (option) { - case "recentComments": - querySortFilter = `{ last_comment_timestamp: desc_nulls_last },`; - break; - // More options... - default: - querySortFilter = ""; -} - -const indexerQueries = ` - query GetPostsQuery($offset: Int) { - dataplatform_near_feed_indexer_posts(order_by: [${querySortFilter} { block_height: desc }], offset: $offset, limit: ${LIMIT}) { - account_id - block_height - block_timestamp - content - receipt_id - accounts_liked - last_comment_timestamp - comments(order_by: {block_height: asc}) { - account_id - block_height - block_timestamp - content - } - } - dataplatform_near_feed_indexer_posts_aggregate { - aggregate { - count - } - } -} -`; - -function fetchGraphQL(operationsDoc, operationName, variables) { - return asyncFetch( - `${GRAPHQL_ENDPOINT}/v1/graphql`, - { - method: "POST", - headers: { "x-hasura-role": "dataplatform_near" }, - body: JSON.stringify({ - query: operationsDoc, - variables: variables, - operationName: operationName, - }), - } - ); -} const Post = styled.div` border-bottom: 1px solid #ECEEF0; @@ -104,34 +51,27 @@ const renderItem = (item, i) => { ); }; -const loadMorePosts = () => { - fetchGraphQL(indexerQueries, "GetPostsQuery", { - offset: state.posts.length, - }).then((result) => { - if (result.status === 200) { - let data = result.body.data; - if (data) { - const newPosts = data.dataplatform_near_feed_indexer_posts; - console.log(newPosts); - const postsCount = - data.dataplatform_near_feed_indexer_posts_aggregate.aggregate.count; - if (newPosts.length > 0) { - State.update({ - posts: [...state.posts, ...newPosts], - postsCount: postsCount, - }); - } - } - } - }); -}; +const renderedItems = posts.map(renderItem); + +const Loader = () => { +return( +
+
) +} + +if (!posts) return() -const renderedItems = state.posts.map(renderItem); return ( (p.primary ? "1px solid #ECEEF0" : "none")}; + border-right: ${(p) => (p.primary ? "1px solid #ECEEF0" : "none")}; + + > div { + padding-bottom: 24px; + margin-bottom: 24px; + border-bottom: 1px solid #ECEEF0; + + &:last-child { + padding-bottom: 0; + margin-bottom: 0; + border-bottom: none; + } + } + + @media (max-width: 1200px) { + padding-top: 0px; + border-left: none; + border-right: none; + display: ${(p) => (p.active ? "block" : "none")}; + margin: ${(p) => (p.negativeMargin ? "0 -12px" : "0")}; + } +`; + +const Tabs = styled.div` + display: none; + height: 48px; + background: #F8F9FA; + border-bottom: 1px solid #ECEEF0; + margin-bottom: ${(p) => (p.noMargin ? "0" : p.halfMargin ? "24px" : "24px")}; + overflow: auto; + scroll-behavior: smooth; + + @media (max-width: 1200px) { + display: flex; + margin-left: -12px; + margin-right: -12px; + + > * { + flex: 1; + } + } +`; + +const TabsButton = styled.a` + display: inline-flex; + align-items: center; + justify-content: center; + height: 100%; + font-weight: 600; + font-size: 12px; + padding: 0 12px; + position: relative; + color: ${(p) => (p.selected ? "#11181C" : "#687076")}; + background: none; + border: none; + outline: none; + text-align: center; + text-decoration: none !important; + + &:hover { + color: #11181C; + } + + &::after { + content: ''; + display: ${(p) => (p.selected ? "block" : "none")}; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 3px; + background: #59E692; + } +`; + return ( - + + + + Posts + + + + Components + + + + Explore + + + +
+
+ + +
+
+ +
+
+ +
+
+
); + diff --git a/frontend/widgets/examples/feed/src/QueryApi.dev.Feed.jsx b/frontend/widgets/examples/feed/src/QueryApi.dev.Feed.jsx index 507acabba..a3b0e3d18 100644 --- a/frontend/widgets/examples/feed/src/QueryApi.dev.Feed.jsx +++ b/frontend/widgets/examples/feed/src/QueryApi.dev.Feed.jsx @@ -2,12 +2,166 @@ const GRAPHQL_ENDPOINT = "https://near-queryapi.dev.api.pagoda.co"; const APP_OWNER = "dev-queryapi.dataplatform.near"; +let accountsFollowing = undefined; + +if (context.accountId) { + const graph = Social.keys(`${context.accountId}/graph/follow/*`, "final"); + if (graph !== null) { + accountsFollowing = Object.keys(graph[context.accountId].graph.follow || {}); + } +} + +State.init({ + selectedTab: props.tab || "posts", +}); + +if (props.tab && props.tab !== state.selectedTab) { + State.update({ + selectedTab: props.tab, + }); +} + +const activityUrl = `/#/${APP_OWNER}/widget/QueryApi.dev.Feed`; + +const Wrapper = styled.div` + margin-top: calc(var(--body-top-padding) * -1); + padding-bottom: 48px; +`; + +const Main = styled.div` + display: grid; + grid-template-columns: 290px minmax(0, 1fr) 290px; + grid-gap: 16px; + + @media (max-width: 1200px) { + display: block; + } +`; + +const Section = styled.div` + padding-top: 24px; + border-left: ${(p) => (p.primary ? "1px solid #ECEEF0" : "none")}; + border-right: ${(p) => (p.primary ? "1px solid #ECEEF0" : "none")}; + + > div { + padding-bottom: 24px; + margin-bottom: 24px; + border-bottom: 1px solid #ECEEF0; + + &:last-child { + padding-bottom: 0; + margin-bottom: 0; + border-bottom: none; + } + } + + @media (max-width: 1200px) { + padding-top: 0px; + border-left: none; + border-right: none; + display: ${(p) => (p.active ? "block" : "none")}; + margin: ${(p) => (p.negativeMargin ? "0 -12px" : "0")}; + } +`; + +const Tabs = styled.div` + display: none; + height: 48px; + background: #F8F9FA; + border-bottom: 1px solid #ECEEF0; + margin-bottom: ${(p) => (p.noMargin ? "0" : p.halfMargin ? "24px" : "24px")}; + overflow: auto; + scroll-behavior: smooth; + + @media (max-width: 1200px) { + display: flex; + margin-left: -12px; + margin-right: -12px; + + > * { + flex: 1; + } + } +`; + +const TabsButton = styled.a` + display: inline-flex; + align-items: center; + justify-content: center; + height: 100%; + font-weight: 600; + font-size: 12px; + padding: 0 12px; + position: relative; + color: ${(p) => (p.selected ? "#11181C" : "#687076")}; + background: none; + border: none; + outline: none; + text-align: center; + text-decoration: none !important; + + &:hover { + color: #11181C; + } + + &::after { + content: ''; + display: ${(p) => (p.selected ? "block" : "none")}; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 3px; + background: #59E692; + } +`; + return ( - + + + + Posts + + + + Components + + + + Explore + + + +
+
+ + +
+
+ +
+
+ +
+
+
); +