Skip to content

Commit

Permalink
DPLT-1053 Feat: implement Following feed (#138)
Browse files Browse the repository at this point in the history
Co-authored-by: Morgan McCauley <[email protected]>
  • Loading branch information
roshaans and morgsmccauley authored Jul 22, 2023
1 parent 0742afe commit 20d2aaa
Show file tree
Hide file tree
Showing 7 changed files with 650 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}"}}}
) {
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down
303 changes: 303 additions & 0 deletions frontend/widgets/examples/feed/src/QueryApi.Examples.Feed.Posts.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<>
<H2>Posts</H2>

<Content>
{context.accountId && (
<>
<ComposeWrapper>
<Widget src="calebjacob.near/widget/Posts.Compose" />
</ComposeWrapper>

<FilterWrapper>
<PillSelect>
<PillSelectButton
type="button"
onClick={() => selectTab("all")}
selected={state.selectedTab === "all"}
>
All
</PillSelectButton>

<PillSelectButton
type="button"
onClick={() => selectTab("following")}
selected={state.selectedTab === "following"}
>
Following
</PillSelectButton>
</PillSelect>
</FilterWrapper>
</>
)}

<FeedWrapper>
<Widget src={`${APP_OWNER}/widget/QueryApi.Examples.Feed`} props={{ hasMore, loadMorePosts, posts: state.posts}} />
</FeedWrapper>
</Content>
</>
);
Loading

0 comments on commit 20d2aaa

Please sign in to comment.