Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/1011 infra feeds #1015

Merged
merged 58 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
b5e9375
feat: fetch from devhub-cache-api-rs.fly.dev
Tguntenaar Nov 6, 2024
48d8365
@Megha-Dev-19 WIP
Tguntenaar Nov 13, 2024
c6fde60
wip
Tguntenaar Nov 13, 2024
97d10d5
fmt
Tguntenaar Nov 13, 2024
c4d856e
wip
Tguntenaar Nov 15, 2024
32965d6
events and devhub are ready to be reviewed
Tguntenaar Nov 15, 2024
bc05626
feat: infra proposals
Tguntenaar Nov 15, 2024
f126fc8
fmt
Tguntenaar Nov 15, 2024
a4980a5
feat: rfps infra
Tguntenaar Nov 15, 2024
d9a8b6a
remove comments
Tguntenaar Nov 15, 2024
e796489
fix: spelling
Tguntenaar Nov 19, 2024
ec7c610
fix: spelling
Tguntenaar Nov 20, 2024
96ab80c
replace all nearqueryapi in devhub related to proposals and rfps
Tguntenaar Nov 20, 2024
6d9bbdf
devhub: simplemde, acceptedTerms, passing instance
Tguntenaar Nov 21, 2024
e721ee6
fix: devhub
Tguntenaar Nov 21, 2024
e17e72c
refactor events: deleted SimpleMDE and LinkedProposalsDropdown for both
Tguntenaar Nov 21, 2024
0d25bac
test: replace all references of queryapi in tests
Tguntenaar Nov 21, 2024
f63612a
test: fix linkedProposals and simpleMDE test :)
Tguntenaar Nov 21, 2024
0484689
test: skip discussions test for now
Tguntenaar Nov 21, 2024
7546c0f
clean up SimpleMDE
Tguntenaar Nov 22, 2024
330476a
infra: SimpleMDE, LinkedDropdown rfp + proposal, Proposal + Rfp.jsx, …
Tguntenaar Nov 22, 2024
a6a28a3
test: fix events test, 1. had to deploy events with new cors policy, …
Tguntenaar Nov 22, 2024
f744909
test: infra -- fix: should show correct linked RFP to a proposal in f…
Tguntenaar Nov 22, 2024
7f6a2f4
test: infra -- fix: should create proposal and link an RFP
Tguntenaar Nov 22, 2024
f6fa221
remove comments
Tguntenaar Nov 22, 2024
dff2af3
test: @petersalomonsen fixed!
Tguntenaar Nov 22, 2024
d904a6b
fmt
Tguntenaar Nov 22, 2024
99bf15a
test: discussions test back in
Tguntenaar Nov 22, 2024
b08601f
test: skip discussions test
Tguntenaar Nov 23, 2024
4805d33
revert: changes to rfp comment test
Tguntenaar Nov 24, 2024
83785a6
initial commit 1002
Tguntenaar Dec 3, 2024
14a1341
fmt
Tguntenaar Dec 3, 2024
5061189
Merge branch 'main' into feature/1002-feed
petersalomonsen Dec 3, 2024
7edd4ad
test for comparing local feed with production
petersalomonsen Dec 3, 2024
4a8f7b3
add events committee feed components + by-sort component
Tguntenaar Dec 4, 2024
9f4b5f0
fmt
Tguntenaar Dec 4, 2024
a938540
compare links in prod and local
petersalomonsen Dec 4, 2024
d8f882e
test: update events test
Tguntenaar Dec 5, 2024
3d95784
add events committee feed components + by-sort component
Tguntenaar Dec 4, 2024
be199e6
fmt
Tguntenaar Dec 4, 2024
3211999
test: update events test
Tguntenaar Dec 5, 2024
bf4ac5f
Merge branch 'feature/1002-feed' of github.com:Tguntenaar/neardevhub-…
Tguntenaar Dec 5, 2024
ea1d5bb
test: comment spec
Tguntenaar Dec 5, 2024
29ab16a
test: included some test from pr 982
Tguntenaar Dec 5, 2024
98d752a
revert commit
Tguntenaar Dec 6, 2024
444dee5
feat: simpleMDE to new api
Tguntenaar Dec 6, 2024
6fc1d60
fmt
Tguntenaar Dec 6, 2024
c768b10
feat: linkedproposaldropdown to new api
Tguntenaar Dec 6, 2024
8984298
fmt
Tguntenaar Dec 6, 2024
ca5f1e0
test: proposal autolink
Tguntenaar Dec 6, 2024
0292094
Merge branch 'main' of github.com:NEAR-DevHub/neardevhub-bos into fea…
Tguntenaar Dec 7, 2024
dba0972
Merge branch 'feature/replace-indexer-with-api' of github.com:Tgunten…
Tguntenaar Dec 7, 2024
6e310da
fix: simplemde + test
Tguntenaar Dec 8, 2024
2a55cfc
linked dropdowns
Tguntenaar Dec 8, 2024
96dc94c
feature: update feeds with new api
Tguntenaar Dec 8, 2024
7b49c4a
fmt
Tguntenaar Dec 8, 2024
f2a18a8
weird merge conflict
Tguntenaar Dec 8, 2024
0ae2963
Merge branch 'main' of github.com:NEAR-DevHub/neardevhub-bos into fea…
Tguntenaar Dec 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading