Skip to content

Commit

Permalink
Feature/1011 infra feeds (#1015)
Browse files Browse the repository at this point in the history
* feat: fetch from devhub-cache-api-rs.fly.dev

* @Megha-Dev-19 WIP

* wip

* fmt

* wip

* events and devhub are ready to be reviewed

* feat: infra proposals

* fmt

* feat: rfps infra

* remove comments

* fix: spelling

* fix: spelling

* replace all nearqueryapi in devhub related to proposals and rfps

* devhub: simplemde, acceptedTerms, passing instance

* fix: devhub

* refactor events: deleted SimpleMDE and LinkedProposalsDropdown for both

* test: replace all references of queryapi in tests

* test: fix linkedProposals and simpleMDE test :)

* test: skip discussions test for now

* clean up SimpleMDE

* infra: SimpleMDE, LinkedDropdown rfp + proposal, Proposal + Rfp.jsx, remove fetchgraphql from common

* test: fix events test, 1. had to deploy events with new cors policy, 2. passing instance down to simplemde, 3. mock the test on the right api path.

* test: infra -- fix: should show correct linked RFP to a proposal in feed page

* test: infra -- fix: should create proposal and link an RFP

* remove comments

* test: @petersalomonsen fixed!

* fmt

* test: discussions test back in

* test: skip discussions test

* revert: changes to rfp comment test

* initial commit 1002

* fmt

* test for comparing local feed with production

* add events committee feed components + by-sort component

* fmt

* compare links in prod and local

* test: update events test

* add events committee feed components + by-sort component

* fmt

* test: update events test

* test: comment spec

* test: included some test from pr 982

* revert commit

* feat: simpleMDE to new api

* fmt

* feat: linkedproposaldropdown to new api

* fmt

* test: proposal autolink

* fix: simplemde + test

* linked dropdowns

* feature: update feeds with new api

* fmt

---------

Co-authored-by: Peter Salomonsen <[email protected]>
  • Loading branch information
Tguntenaar and petersalomonsen authored Dec 9, 2024
1 parent 2846387 commit 6b3a866
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 286 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const {
proposalFeedAnnouncement,
availableCategoryOptions,
proposalFeedIndexerQueryName,
indexerHasuraRole,
cacheUrl,
isDevhub,
isInfra,
isEvents,
Expand Down Expand Up @@ -193,8 +193,7 @@ const FeedItem = ({ proposal, index }) => {
<div
className="text-sm text-muted d-flex gap-1 align-items-center"
data-testid={
`proposalId_${proposal.proposal_id}` +
`_rfpId_${rfpData.rfp_id}`
`proposalId_${proposal.proposal_id}` + `_rfpId_${rfpData.id}`
}
>
<i class="bi bi-link-45deg"></i>
Expand Down Expand Up @@ -269,14 +268,18 @@ const getProposal = (proposal_id) => {
});
};

const FeedPage = () => {
const QUERYAPI_ENDPOINT = `https://near-queryapi.api.pagoda.co/v1/graphql`;
const getRfp = (rfp_id) => {
return Near.asyncView(contract, "get_rfp", {
rfp_id,
});
};

const FeedPage = () => {
State.init({
data: [],
author: "",
stage: "",
sort: "",
sort: "id_desc",
category: "",
input: "",
loading: false,
Expand All @@ -286,114 +289,6 @@ const FeedPage = () => {
currentlyDisplaying: 0,
});

const query = `query GetLatestSnapshot($offset: Int = 0, $limit: Int = 20, $where: ${proposalFeedIndexerQueryName}_bool_exp = {}) {
${proposalFeedIndexerQueryName}(
offset: $offset
limit: $limit
order_by: {proposal_id: desc}
where: $where
) {
author_id
block_height
name
category
summary
editor_id
proposal_id
ts
timeline
views
labels
linked_rfp
}
${proposalFeedIndexerQueryName}_aggregate(
order_by: {proposal_id: desc}
where: $where
) {
aggregate {
count
}
}
}`;

const rfpQuery = `query GetLatestSnapshot($offset: Int = 0, $limit: Int = 20, $where: ${rfpFeedIndexerQueryName}_bool_exp = {}) {
${rfpFeedIndexerQueryName}(
offset: $offset
limit: $limit
order_by: {rfp_id: desc}
where: $where
) {
name
rfp_id
}
}`;

function fetchGraphQL(operationsDoc, operationName, variables) {
return asyncFetch(QUERYAPI_ENDPOINT, {
method: "POST",
headers: { "x-hasura-role": indexerHasuraRole },
body: JSON.stringify({
query: operationsDoc,
variables: variables,
operationName: operationName,
}),
});
}

function separateNumberAndText(str) {
const numberRegex = /\d+/;

if (numberRegex.test(str)) {
const number = str.match(numberRegex)[0];
const text = str.replace(numberRegex, "").trim();
return { number: parseInt(number), text };
} else {
return { number: null, text: str.trim() };
}
}

const buildWhereClause = () => {
let where = {};
if (state.author) {
where = { author_id: { _eq: state.author }, ...where };
}

if (state.category) {
if (isInfra || isEvents) {
where = { labels: { _contains: state.category }, ...where };
} else {
where = { category: { _eq: state.category }, ...where };
}
}

if (state.stage) {
// timeline is stored as jsonb
where = {
timeline: { _cast: { String: { _regex: `${state.stage}` } } },
...where,
};
}
if (state.input) {
const { number, text } = separateNumberAndText(state.input);
if (number) {
where = { proposal_id: { _eq: number }, ...where };
}

if (text) {
where = {
_or: [
{ name: { _iregex: `${text}` } },
{ summary: { _iregex: `${text}` } },
{ description: { _iregex: `${text}` } },
],
...where,
};
}
}

return where;
};

const makeMoreItems = () => {
State.update({ makeMoreLoader: true });
fetchProposals(state.data.length);
Expand All @@ -406,44 +301,100 @@ const FeedPage = () => {
REJECTED: 1,
};

const fetchProposals = (offset) => {
function searchCacheApi(searchTerm) {
let searchInput = encodeURI(searchTerm);
let searchUrl = `${cacheUrl}/proposals/search/${searchInput}`;

return asyncFetch(searchUrl, {
method: "GET",
headers: {
accept: "application/json",
},
}).catch((error) => {
console.log("Error searching cache api", error);
});
}

function searchProposals(input) {
if (state.loading) return;
State.update({ loading: true });

searchCacheApi(input).then((result) => {
const body = result.body;
const promises = body.records.map((proposal) => {
if (isNumber(proposal.linked_rfp)) {
return getRfp(proposal.linked_rfp).then((rfp) => {
return { ...proposal, rfpData: rfp };
});
} else {
return Promise.resolve(proposal);
}
});
Promise.all(promises).then((proposalsWithRfpData) => {
State.update({ aggregatedCount: body.total_records });
fetchBlockHeights(proposalsWithRfpData, 0);
});
});
}

function fetchCacheApi(variables) {
let fetchUrl = `${cacheUrl}/proposals?order=${variables.order}&limit=${variables.limit}&offset=${variables.offset}`;

if (variables.author_id) {
fetchUrl += `&filters.author_id=${variables.author_id}`;
}
if (variables.stage) {
fetchUrl += `&filters.stage=${variables.stage}`;
}
if (variables.category) {
if (isInfra || isEvents) {
fetchUrl += `&filters.labels=${variables.category}`;
} else {
fetchUrl += `&filters.category=${variables.category}`;
}
}
return asyncFetch(fetchUrl, {
method: "GET",
headers: {
accept: "application/json",
},
}).catch((error) => {
console.log("Error fetching cache api", error);
});
}

function fetchProposals(offset) {
if (!offset) {
offset = 0;
}
if (state.loading) return;
State.update({ loading: true });
const FETCH_LIMIT = 20;
const variables = {
order: state.sort,
limit: FETCH_LIMIT,
offset,
where: buildWhereClause(),
category: state.category ? encodeURIComponent(state.category) : "",
author_id: state.author ? encodeURIComponent(state.author) : "",
stage: state.stage ? encodeURIComponent(state.stage) : "",
};
fetchGraphQL(query, "GetLatestSnapshot", variables).then(async (result) => {
if (result.status === 200) {
if (result.body.data) {
const data = result.body.data[proposalFeedIndexerQueryName];
const totalResult =
result.body.data[`${proposalFeedIndexerQueryName}_aggregate`];
const promises = data.map((item) => {
if (isNumber(item.linked_rfp)) {
return fetchGraphQL(rfpQuery, "GetLatestSnapshot", {
where: { rfp_id: { _eq: item.linked_rfp } },
}).then((result) => {
const rfpData = result.body.data?.[rfpFeedIndexerQueryName];
return { ...item, rfpData: rfpData[0] };
});
} else {
return Promise.resolve(item);
}
});
Promise.all(promises).then((res) => {
State.update({ aggregatedCount: totalResult.aggregate.count });
fetchBlockHeights(res, offset);
fetchCacheApi(variables).then((result) => {
const body = result.body;
const promises = body.records.map((proposal) => {
if (isNumber(proposal.linked_rfp)) {
return getRfp(proposal.linked_rfp).then((rfp) => {
return { ...proposal, rfpData: rfp };
});
} else {
return Promise.resolve(proposal);
}
}
});
Promise.all(promises).then((proposalsWithRfpData) => {
State.update({ aggregatedCount: body.total_records });
fetchBlockHeights(proposalsWithRfpData, offset);
});
});
};
}

useEffect(() => {
State.update({ searchLoader: true });
Expand All @@ -455,12 +406,11 @@ const FeedPage = () => {
...new Set([...newItems, ...state.data].map((i) => JSON.stringify(i))),
].map((i) => JSON.parse(i));
// Sorting in the front end
if (state.sort === "proposal_id" || state.sort === "") {
if (state.sort === "id_desc" || state.sort === "") {
items.sort((a, b) => b.proposal_id - a.proposal_id);
} else if (state.sort === "views") {
items.sort((a, b) => b.views - a.views);
}

// Show the accepted once before showing rejected proposals
items.sort((a, b) => {
return statusOrder[a.timeline.status] - statusOrder[b.timeline.status];
});
Expand All @@ -469,43 +419,40 @@ const FeedPage = () => {
};

const fetchBlockHeights = (data, offset) => {
let promises = data.map((item) => getProposal(item.proposal_id));
Promise.all(promises).then((blockHeights) => {
data = data.map((item, index) => ({
...item,
timeline: JSON.parse(item.timeline),
social_db_post_block_height:
blockHeights[index].social_db_post_block_height,
}));
if (offset) {
let newData = mergeItems(data);
State.update({
data: newData,
currentlyDisplaying: newData.length,
loading: false,
searchLoader: false,
makeMoreLoader: false,
});
} else {
let sorted = [...data].sort((a, b) => {
return (
statusOrder[a.timeline.status] - statusOrder[b.timeline.status]
);
});
State.update({
data: sorted,
currentlyDisplaying: data.length,
loading: false,
searchLoader: false,
makeMoreLoader: false,
});
}
});
data = data.map((item, index) => ({
...item,
timeline: JSON.parse(item.timeline),
}));
if (offset) {
let newData = mergeItems(data);
State.update({
data: newData,
currentlyDisplaying: newData.length,
loading: false,
searchLoader: false,
makeMoreLoader: false,
});
} else {
let sorted = [...data].sort((a, b) => {
return statusOrder[a.timeline.status] - statusOrder[b.timeline.status];
});
State.update({
data: sorted,
currentlyDisplaying: data.length,
loading: false,
searchLoader: false,
makeMoreLoader: false,
});
}
};

useEffect(() => {
const handler = setTimeout(() => {
fetchProposals();
if (state.input) {
searchProposals(state.input);
} else {
fetchProposals();
}
}, 1000);

return () => {
Expand Down
Loading

0 comments on commit 6b3a866

Please sign in to comment.