diff --git a/instances/devhub.near/aliases.mainnet.json b/instances/devhub.near/aliases.mainnet.json index e153b8a83..a877fe047 100644 --- a/instances/devhub.near/aliases.mainnet.json +++ b/instances/devhub.near/aliases.mainnet.json @@ -10,5 +10,6 @@ "REPL_RPC_URL": "https://rpc.mainnet.near.org", "REPL_PROPOSAL_FEED_INDEXER_QUERY_NAME": "polyprogrammist_near_devhub_prod_v1_proposals_with_latest_snapshot", "REPL_INDEXER_HASURA_ROLE": "polyprogrammist_near", - "REPL_POSTHOG_API_KEY": "01234567890123456789012345678901234567890123456" + "REPL_POSTHOG_API_KEY": "01234567890123456789012345678901234567890123456", + "REPL_CACHE_URL": "https://devhub-cache-api-rs.fly.dev" } diff --git a/instances/devhub.near/widget/app.jsx b/instances/devhub.near/widget/app.jsx index 221dce7de..42f9044c6 100644 --- a/instances/devhub.near/widget/app.jsx +++ b/instances/devhub.near/widget/app.jsx @@ -154,7 +154,7 @@ function Page() { return ( ); } @@ -163,7 +163,7 @@ function Page() { return ( ); } @@ -171,7 +171,7 @@ function Page() { return ( ); } diff --git a/instances/devhub.near/widget/config/data.jsx b/instances/devhub.near/widget/config/data.jsx index ce40e902a..b6abb2da2 100644 --- a/instances/devhub.near/widget/config/data.jsx +++ b/instances/devhub.near/widget/config/data.jsx @@ -90,6 +90,7 @@ return { contract: "devhub.near", proposalFeedIndexerQueryName: "polyprogrammist_near_devhub_prod_v1_proposals_with_latest_snapshot", + cacheUrl: "${REPL_CACHE_URL}", indexerHasuraRole: "polyprogrammist_near", isDevhub: true, proposalFeedAnnouncement, diff --git a/instances/devhub.near/widget/devhub/components/molecule/Compose.jsx b/instances/devhub.near/widget/devhub/components/molecule/Compose.jsx index 0ae8c0b62..2333a8762 100644 --- a/instances/devhub.near/widget/devhub/components/molecule/Compose.jsx +++ b/instances/devhub.near/widget/devhub/components/molecule/Compose.jsx @@ -95,6 +95,7 @@ const Compose = ({ "${REPL_DEVHUB}/widget/devhub.components.molecule.SimpleMDE" } props={{ + instance: props.instance, data: { handler: state.handler, content: state.data }, onChange: (content) => { State.update({ data: content, handler: "update" }); @@ -163,6 +164,7 @@ const Compose = ({ "${REPL_DEVHUB}/widget/devhub.components.molecule.SimpleMDE" } props={{ + ...props, data: { handler: state.handler, content: state.data }, onChange: (content) => { State.update({ data: content, handler: "update" }); diff --git a/instances/devhub.near/widget/devhub/components/molecule/SimpleMDE.jsx b/instances/devhub.near/widget/devhub/components/molecule/SimpleMDE.jsx index aa4a9a04e..283e87206 100644 --- a/instances/devhub.near/widget/devhub/components/molecule/SimpleMDE.jsx +++ b/instances/devhub.near/widget/devhub/components/molecule/SimpleMDE.jsx @@ -5,6 +5,10 @@ const { getLinkUsingCurrentGateway } = VM.require( "${REPL_DEVHUB}/widget/core.lib.url" ) || { getLinkUsingCurrentGateway: () => {} }; + +const instance = props.instance ?? ""; +const { cacheUrl, contract } = VM.require(`${instance}/widget/config.data`); + const data = props.data; const onChange = props.onChange ?? (() => {}); const onChangeKeyup = props.onChangeKeyup ?? (() => {}); // in case where we want immediate action @@ -30,21 +34,8 @@ const showAccountAutoComplete = props.showAutoComplete ?? false; const showProposalIdAutoComplete = props.showProposalIdAutoComplete ?? false; const autoFocus = props.autoFocus ?? false; -const queryName = "${REPL_PROPOSAL_FEED_INDEXER_QUERY_NAME}"; -const query = `query GetLatestSnapshot($offset: Int = 0, $limit: Int = 10, $where: ${queryName}_bool_exp = {}) { -${queryName}( - offset: $offset - limit: $limit - order_by: {proposal_id: desc} - where: $where -) { - name - proposal_id -} -}`; - const proposalLink = getLinkUsingCurrentGateway( - `${REPL_DEVHUB}/widget/app?page=proposal&id=` + `${contract}/widget/app?page=proposal&id=` ); const code = ` @@ -88,11 +79,11 @@ const code = ` } .CodeMirror { - min-height:200px !important; // for autocomplete to be visble + min-height:200px !important; // for autocomplete to be visible } .CodeMirror-scroll { - min-height:200px !important; // for autocomplete to be visble + min-height:200px !important; // for autocomplete to be visible } ${embeddCSS} @@ -129,7 +120,6 @@ let isEditorInitialized = false; let followingData = {}; let profilesData = {}; let proposalLink = ''; -let query = ''; let showAccountAutoComplete = ${showAccountAutoComplete}; let showProposalIdAutoComplete = ${showProposalIdAutoComplete}; @@ -183,12 +173,11 @@ function getSuggestedAccounts(term) { return results; } -async function asyncFetch(endpoint, { method, headers, body }) { +async function asyncFetch(endpoint, { method, headers }) { try { const response = await fetch(endpoint, { method: method, headers: headers, - body: body }); if (!response.ok) { @@ -212,46 +201,28 @@ function extractNumbers(str) { return numbers; }; +function searchCacheApi(searchProposalId) { + let searchInput = encodeURI(searchProposalId); + + let searchUrl = "${cacheUrl}/proposals/search/" + searchInput; + + return asyncFetch(searchUrl, { + method: "GET", + headers: { + accept: "application/json", + }, + }).catch((error) => { + console.log("Error searching cache api", error); + }); +} + async function getSuggestedProposals(id) { let results = []; - const variables = { - limit: 5, - offset: 0, - where: {}, - }; + if (id) { - const proposalId = extractNumbers(id); - if (proposalId) { - variables["where"] = { proposal_id: { _eq: id } }; - } else { - variables["where"] = { - _or: [ - { name: { _iregex: id } }, - { summary: { _iregex: id } }, - { description: { _iregex: id } }, - ], - }; - } + const searchResults = await searchCacheApi(id); + results = searchResults?.records || []; } - await asyncFetch("https://near-queryapi.api.pagoda.co/v1/graphql", { - method: "POST", - headers: { "x-hasura-role": "${REPL_INDEXER_HASURA_ROLE}" }, - body: JSON.stringify({ - query: query, - variables: variables, - operationName: "GetLatestSnapshot", - }), - }) - .then((res) => { - const proposals = - res?.data?.[ - "${REPL_PROPOSAL_FEED_INDEXER_QUERY_NAME}" - ]; - results = proposals; - }) - .catch((error) => { - console.error(error); - }); return results; }; @@ -402,7 +373,7 @@ if (showAccountAutoComplete) { }); }); } - // show dropwdown only when @ is at first place or when there is a space before @ + // show dropdown only when @ is at first place or when there is a space before @ if (!mentionToken && (token.string === "@" && cursor.ch === 1 || token.string === "@" && cm.getTokenAt({line:cursor.line, ch: cursor.ch - 1}).string == ' ')) { mentionToken = token; mentionCursorStart = cursor; @@ -439,6 +410,7 @@ if (showProposalIdAutoComplete) { let proposalId; let referenceCursorStart; const dropdown = document.getElementById("referencedropdown"); + // Create loader element once and store it const loader = document.createElement('div'); loader.className = 'loader'; loader.textContent = 'Loading...'; @@ -460,8 +432,10 @@ if (showProposalIdAutoComplete) { const proposalIdInput = cm.getRange(referenceCursorStart, cursor); dropdown.innerHTML = ''; // Clear previous content dropdown.appendChild(loader); // Show loader - + const suggestedProposals = await getSuggestedProposals(proposalIdInput); + + // Clear dropdown including loader dropdown.innerHTML = suggestedProposals .map( (item) => @@ -496,7 +470,7 @@ if (showProposalIdAutoComplete) { } } - // show dropwdown only when there is space before # or it's first char + // show dropdown only when there is space before # or it's first char if (!proposalId && (token.string === "#" && cursor.ch === 1 || token.string === "#" && cm.getTokenAt({line:cursor.line, ch: cursor.ch - 1}).string == ' ')) { proposalId = token; referenceCursorStart = cursor; @@ -545,9 +519,6 @@ window.addEventListener("message", (event) => { if (event.data.profilesData) { profilesData = JSON.parse(event.data.profilesData); } - if (event.data.query) { - query = event.data.query; - } if (event.data.proposalLink) { proposalLink = event.data.proposalLink; } @@ -570,7 +541,6 @@ return ( content: props.data?.content ?? "", followingData, profilesData: JSON.stringify(profilesData), - query: query, handler: props.data.handler, proposalLink: proposalLink, }} diff --git a/instances/devhub.near/widget/devhub/entity/addon/blogv2/editor/form.jsx b/instances/devhub.near/widget/devhub/entity/addon/blogv2/editor/form.jsx index f5cbcf8d3..3ddfe60d4 100644 --- a/instances/devhub.near/widget/devhub/entity/addon/blogv2/editor/form.jsx +++ b/instances/devhub.near/widget/devhub/entity/addon/blogv2/editor/form.jsx @@ -172,6 +172,7 @@ const ContentEditor = useMemo(() => { { - return asyncFetch(QUERYAPI_ENDPOINT, { - method: "POST", - headers: { "x-hasura-role": "${REPL_INDEXER_HASURA_ROLE}" }, - body: JSON.stringify({ - query: operationsDoc, - variables: variables, - operationName: operationName, - }), - }); -}; +const { cacheUrl } = VM.require(`${instance}/widget/config.data`); -const queryName = "${REPL_PROPOSAL_FEED_INDEXER_QUERY_NAME}"; -const query = `query GetLatestSnapshot($offset: Int = 0, $limit: Int = 10, $where: ${queryName}_bool_exp = {}) { - ${queryName}( - offset: $offset - limit: $limit - order_by: {proposal_id: desc} - where: $where - ) { - block_height - } - }`; +const fetchAndSetProposalSnapshot = () => { + if (!props.proposalId) { + return; + } + asyncFetch(`${cacheUrl}/proposal/${props.proposalId}/snapshots`, { + method: "GET", + headers: { accept: "application/json" }, + }) + .then((response) => { + if (!response.ok) { + console.error(`Failed to fetch snapshots: ${response.status}`); + } + return response.body; + }) + .then((snapshots) => { + if (!Array.isArray(snapshots) || snapshots.length === 0) { + console.error("No snapshots found"); + } -const variables = { - limit: 10, - offset, - where: { proposal_id: { _eq: proposalId } }, + // Get the most recent snapshot + const latestSnapshot = snapshots.reduce((latest, current) => + current.block_height > latest.block_height ? current : latest + ); + + State.update({ proposalBlockHeight: latestSnapshot.block_height }); + }) + .catch((error) => { + console.error("Failed to fetch proposal snapshot:", error); + }); }; -fetchGraphQL(query, "GetLatestSnapshot", variables).then(async (result) => { - if (result.status === 200) { - if (result.body.data) { - const data = result.body.data?.[queryName]; - State.update({ proposalBlockHeight: data[0].block_height }); - } - } -}); +// Fetch snapshot data on component mount +fetchAndSetProposalSnapshot(); let acceptedTermsVersion = Near.block().header.height; diff --git a/instances/devhub.near/widget/devhub/entity/proposal/CommentsAndLogs.jsx b/instances/devhub.near/widget/devhub/entity/proposal/CommentsAndLogs.jsx index 8da752e75..f3236305e 100644 --- a/instances/devhub.near/widget/devhub/entity/proposal/CommentsAndLogs.jsx +++ b/instances/devhub.near/widget/devhub/entity/proposal/CommentsAndLogs.jsx @@ -331,7 +331,7 @@ const parseProposalKeyAndValue = (key, modifiedValue, originalValue) => { accepted ); diff --git a/instances/devhub.near/widget/devhub/entity/proposal/ComposeComment.jsx b/instances/devhub.near/widget/devhub/entity/proposal/ComposeComment.jsx index 7e8209392..fadf6240a 100644 --- a/instances/devhub.near/widget/devhub/entity/proposal/ComposeComment.jsx +++ b/instances/devhub.near/widget/devhub/entity/proposal/ComposeComment.jsx @@ -199,6 +199,7 @@ const Compose = useMemo(() => { { { src={ "${REPL_DEVHUB}/widget/devhub.entity.proposal.AcceptedTerms" } - props={{ proposalId: proposalId, portal: "DevHub" }} + props={{ ...props, proposalId: proposalId, portal: "DevHub" }} /> and commit to honoring it diff --git a/instances/devhub.near/widget/devhub/entity/proposal/Feed.jsx b/instances/devhub.near/widget/devhub/entity/proposal/Feed.jsx index a10e52241..a609d8983 100644 --- a/instances/devhub.near/widget/devhub/entity/proposal/Feed.jsx +++ b/instances/devhub.near/widget/devhub/entity/proposal/Feed.jsx @@ -10,7 +10,7 @@ const { proposalFeedAnnouncement, availableCategoryOptions, proposalFeedIndexerQueryName, - indexerHasuraRole, + cacheUrl, isDevhub, isInfra, isEvents, @@ -193,8 +193,7 @@ const FeedItem = ({ proposal, index }) => {
@@ -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, @@ -286,120 +289,76 @@ const FeedPage = () => { currentlyDisplaying: 0, }); - const query = `query GetLatestSnapshot($offset: Int = 0, $limit: Int = 10, $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 = 10, $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, - }), + const makeMoreItems = () => { + State.update({ makeMoreLoader: true }); + fetchProposals(state.data.length); + }; + + function searchCacheApi() { + let searchTerm = state.input; + 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 separateNumberAndText(str) { - const numberRegex = /\d+/; + function searchProposals() { + if (state.loading) return; + State.update({ loading: true }); - 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() }; - } + searchCacheApi().then((result) => { + let body = result.body; + + const promises = body.records.map((proposal) => { + if (isNumber(proposal.linked_rfp)) { + 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); + }); + }); } - const buildWhereClause = () => { - let where = {}; - if (state.author) { - where = { author_id: { _eq: state.author }, ...where }; - } + function fetchCacheApi(variables) { + let fetchUrl = `${cacheUrl}/proposals?order=${variables.order}&limit=${variables.limit}&offset=${variables.offset}`; - if (state.category) { - if (isInfra || isEvents) { - where = { labels: { _contains: state.category }, ...where }; - } else { - where = { category: { _eq: state.category }, ...where }; - } + if (variables.author_id) { + fetchUrl += `&filters.author_id=${variables.author_id}`; } - - if (state.stage) { - // timeline is stored as jsonb - where = { - timeline: { _cast: { String: { _regex: `${state.stage}` } } }, - ...where, - }; + if (variables.stage) { + fetchUrl += `&filters.stage=${variables.stage}`; } - 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, - }; + 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); + }); + } - return where; - }; - - const makeMoreItems = () => { - State.update({ makeMoreLoader: true }); - fetchProposals(state.data.length); - }; - - const fetchProposals = (offset) => { + function fetchProposals(offset) { if (!offset) { offset = 0; } @@ -407,36 +366,30 @@ const FeedPage = () => { State.update({ loading: true }); const FETCH_LIMIT = 10; 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)) { + 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 }); @@ -448,48 +401,45 @@ 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); } return items; }; 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 { - State.update({ - data, - 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 { + State.update({ + data, + currentlyDisplaying: data.length, + loading: false, + searchLoader: false, + makeMoreLoader: false, + }); + } }; useEffect(() => { const handler = setTimeout(() => { - fetchProposals(); + if (state.input) { + searchProposals(); + } else { + fetchProposals(); + } }, 1000); return () => { diff --git a/instances/devhub.near/widget/devhub/entity/proposal/LinkedProposalsDropdown.jsx b/instances/devhub.near/widget/devhub/entity/proposal/LinkedProposalsDropdown.jsx index 53d6eba9e..995a38a0a 100644 --- a/instances/devhub.near/widget/devhub/entity/proposal/LinkedProposalsDropdown.jsx +++ b/instances/devhub.near/widget/devhub/entity/proposal/LinkedProposalsDropdown.jsx @@ -1,24 +1,15 @@ const { href } = VM.require("${REPL_DEVHUB}/widget/core.lib.url"); href || (href = () => {}); +const instance = props.instance ?? ""; + +const { cacheUrl, contract } = VM.require(`${instance}/widget/config.data`); + const linkedProposals = props.linkedProposals; const onChange = props.onChange; const [selectedProposals, setSelectedProposals] = useState(linkedProposals); const [proposalsOptions, setProposalsOptions] = useState([]); -const [searchProposalId, setSearchProposalId] = useState(""); -const QUERYAPI_ENDPOINT = `https://near-queryapi.api.pagoda.co/v1/graphql`; -const queryName = "${REPL_PROPOSAL_FEED_INDEXER_QUERY_NAME}"; -const query = `query GetLatestSnapshot($offset: Int = 0, $limit: Int = 10, $where: ${queryName}_bool_exp = {}) { -${queryName}( - offset: $offset - limit: $limit - order_by: {proposal_id: desc} - where: $where -) { - name - proposal_id -} -}`; +const [textAfterHash, setTextAfterHash] = useState(""); useEffect(() => { if (JSON.stringify(linkedProposals) !== JSON.stringify(selectedProposals)) { @@ -32,73 +23,43 @@ useEffect(() => { } }, [selectedProposals]); -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() }; - } -} +function searchCacheApi(input) { + let searchInput = encodeURI(input); + let searchUrl = `${cacheUrl}/proposals/search/${searchInput}`; -const buildWhereClause = () => { - let where = {}; - const { number, text } = separateNumberAndText(searchProposalId); - - if (number) { - where = { proposal_id: { _eq: number }, ...where }; - } - - if (text) { - where = { name: { _ilike: `%${text}%` }, ...where }; - } - - return where; -}; - -function fetchGraphQL(operationsDoc, operationName, variables) { - return asyncFetch(QUERYAPI_ENDPOINT, { - method: "POST", - headers: { "x-hasura-role": "${REPL_INDEXER_HASURA_ROLE}" }, - body: JSON.stringify({ - query: operationsDoc, - variables: variables, - operationName: operationName, - }), + return asyncFetch(searchUrl, { + method: "GET", + headers: { + accept: "application/json", + }, + }).catch((error) => { + console.log("Error searching cache api", error); }); } -const fetchProposals = () => { - const FETCH_LIMIT = 30; - const variables = { - limit: FETCH_LIMIT, - offset: 0, - where: buildWhereClause(), - }; - fetchGraphQL(query, "GetLatestSnapshot", variables).then(async (result) => { - if (result.status === 200) { - if (result.body.data) { - const proposalsData = result.body.data[queryName]; +function searchProposals(input) { + if (state.loading) return; + State.update({ loading: true }); - const data = []; - for (const prop of proposalsData) { - data.push({ - label: "# " + prop.proposal_id + " : " + prop.name, - value: prop.proposal_id, - }); - } - setProposalsOptions(data); - } + searchCacheApi(input).then((result) => { + let proposalsData = result.body.records; + + const data = []; + for (const prop of proposalsData) { + data.push({ + label: "# " + prop.proposal_id + " : " + prop.name, + value: prop.proposal_id, + }); } + setProposalsOptions(data); }); -}; +} useEffect(() => { - fetchProposals(); -}, [searchProposalId]); + if (textAfterHash.trim()) { + searchProposals(textAfterHash); + } +}, [textAfterHash]); return ( <> @@ -108,7 +69,7 @@ return ( - +
); @@ -137,7 +98,7 @@ return ( { if (!selectedProposals.some((item) => item.value === v.value)) { setSelectedProposals([...selectedProposals, v]); @@ -149,7 +110,7 @@ return ( defaultLabel: "Search proposals", searchByValue: true, onSearch: (value) => { - setSearchProposalId(value); + setTextAfterHash(value); }, }} /> diff --git a/instances/devhub.near/widget/devhub/entity/proposal/Proposal.jsx b/instances/devhub.near/widget/devhub/entity/proposal/Proposal.jsx index 8c2182381..8c92be1b0 100644 --- a/instances/devhub.near/widget/devhub/entity/proposal/Proposal.jsx +++ b/instances/devhub.near/widget/devhub/entity/proposal/Proposal.jsx @@ -939,6 +939,7 @@ return ( } props={{ ...props, + instance: props.instance, item: item, notifyAccountId: authorId, id: proposal.id, diff --git a/instances/devhub.near/widget/devhub/feature/proposal-search/by-sort.jsx b/instances/devhub.near/widget/devhub/feature/proposal-search/by-sort.jsx index 7fa65fca0..192361033 100644 --- a/instances/devhub.near/widget/devhub/feature/proposal-search/by-sort.jsx +++ b/instances/devhub.near/widget/devhub/feature/proposal-search/by-sort.jsx @@ -1,11 +1,9 @@ const options = [ { label: "All", value: "" }, - { label: "Most recent", value: "proposal_id" }, // proposal_id desc - { label: "Most viewed", value: "views" }, // views desc - // TODO add track_comments function to devhub to track it in the indexer - // { label: "Most commented", value: "" }, // comments desc - // { label: "Unanswered", value: "" }, // where comments = 0 - // where comments = 0 + { label: "Most recent", value: "id_desc" }, // proposal_id desc + { label: "Oldest", value: "id_asc" }, // proposal_id desc + { label: "Recently updated", value: "ts_desc" }, // timestamp desc + { label: "Least recently updated", value: "ts_asc" }, // timestamp asc ]; const setSelected = props.onStateChange ?? (() => {}); diff --git a/instances/events-committee.near/aliases.mainnet.json b/instances/events-committee.near/aliases.mainnet.json index efd479a11..39dc1ec40 100644 --- a/instances/events-committee.near/aliases.mainnet.json +++ b/instances/events-committee.near/aliases.mainnet.json @@ -11,5 +11,6 @@ "REPL_SOCIAL_CONTRACT": "social.near", "REPL_RPC_URL": "https://rpc.mainnet.near.org", "REPL_PROPOSAL_FEED_INDEXER_QUERY_NAME": "thomasguntenaar_near_event_committee_prod_v1_proposals_with_latest_snapshot", - "REPL_INDEXER_HASURA_ROLE": "thomasguntenaar_near" + "REPL_INDEXER_HASURA_ROLE": "thomasguntenaar_near", + "REPL_CACHE_URL": "https://events-cache-api-rs.fly.dev" } diff --git a/instances/events-committee.near/widget/app.jsx b/instances/events-committee.near/widget/app.jsx index e8969533e..63d2bcdb6 100644 --- a/instances/events-committee.near/widget/app.jsx +++ b/instances/events-committee.near/widget/app.jsx @@ -34,7 +34,7 @@ function Page() { return ( ); } @@ -42,7 +42,7 @@ function Page() { return ( ); } @@ -50,7 +50,7 @@ function Page() { return ( ); } diff --git a/instances/events-committee.near/widget/config/data.jsx b/instances/events-committee.near/widget/config/data.jsx index b16e70e46..f9445cfb1 100644 --- a/instances/events-committee.near/widget/config/data.jsx +++ b/instances/events-committee.near/widget/config/data.jsx @@ -46,6 +46,7 @@ return { proposalFeedIndexerQueryName: "thomasguntenaar_near_event_committee_prod_v1_proposals_with_latest_snapshot", indexerHasuraRole: "thomasguntenaar_near", + cacheUrl: "${REPL_CACHE_URL}", isEvents: true, proposalFeedAnnouncement, availableCategoryOptions: categoryOptions, diff --git a/instances/events-committee.near/widget/devhub/components/molecule/Compose.jsx b/instances/events-committee.near/widget/devhub/components/molecule/Compose.jsx index 637f4c8dc..6b4621125 100644 --- a/instances/events-committee.near/widget/devhub/components/molecule/Compose.jsx +++ b/instances/events-committee.near/widget/devhub/components/molecule/Compose.jsx @@ -85,8 +85,9 @@ const Compose = ({ {state.selectedTab === "editor" ? ( <> { State.update({ data: content, handler: "update" }); diff --git a/instances/events-committee.near/widget/devhub/components/molecule/SimpleMDE.jsx b/instances/events-committee.near/widget/devhub/components/molecule/SimpleMDE.jsx deleted file mode 100644 index d5de9fecd..000000000 --- a/instances/events-committee.near/widget/devhub/components/molecule/SimpleMDE.jsx +++ /dev/null @@ -1,598 +0,0 @@ -/** - * iframe embedding a SimpleMDE component - * https://github.com/sparksuite/simplemde-markdown-editor - */ -const { getLinkUsingCurrentGateway } = VM.require( - "${REPL_DEVHUB}/widget/core.lib.url" -) || { getLinkUsingCurrentGateway: () => {} }; -const data = props.data; -const onChange = props.onChange ?? (() => {}); -const onChangeKeyup = props.onChangeKeyup ?? (() => {}); // in case where we want immediate action -const height = props.height ?? "390"; -const className = props.className ?? "w-100"; -const embeddCSS = props.embeddCSS; -const sortedRelevantUsers = props.sortedRelevantUsers || []; - -State.init({ - iframeHeight: height, - message: props.data, -}); - -const profilesData = Social.get("*/profile/name", "final") ?? {}; -const followingData = - Social.get(`${context.accountId}/graph/follow/**`, "final") ?? {}; - -// SIMPLEMDE CONFIG // -const fontFamily = props.fontFamily ?? "sans-serif"; -const alignToolItems = props.alignToolItems ?? "right"; -const placeholder = props.placeholder ?? ""; -const showAccountAutoComplete = props.showAutoComplete ?? false; -const showProposalIdAutoComplete = props.showProposalIdAutoComplete ?? false; -const autoFocus = props.autoFocus ?? false; - -const queryName = "${REPL_PROPOSAL_FEED_INDEXER_QUERY_NAME}"; -const query = `query GetLatestSnapshot($offset: Int = 0, $limit: Int = 10, $where: ${queryName}_bool_exp = {}) { -${queryName}( - offset: $offset - limit: $limit - order_by: {proposal_id: desc} - where: $where -) { - name - proposal_id -} -}`; - -const proposalLink = getLinkUsingCurrentGateway( - `${REPL_EVENTS}/widget/app?page=proposal&id=` -); - -const code = ` - - - - - - - - - - - - - - - - - - - - - - - -`; - -return ( -