| 6 | "": "const getFollowedDAOs = (accountId) => {\\\\n let following = Social.keys(`${accountId}/graph/follow/*`, \\\\\\\"final\\\\\\\", {\\\\n return_type: \\\\\\\"BlockHeight\\\\\\\",\\\\n });\\\\n\\\\n if (following === null) return null;\\\\n\\\\n following = Object.keys(following[accountId].graph.follow || {}).filter(\\\\n (account) => account.endsWith(\\\\\\\".sputnik-dao.near\\\\\\\"),\\\\n );\\\\n return following;\\\\n};\\\\n\\\\nconst baseApi = \\\\\\\"https://api.pikespeak.ai/daos/proposals\\\\\\\";\\\\nconst publicApiKey = \\\\\\\"36f2b87a-7ee6-40d8-80b9-5e68e587a5b5\\\\\\\";\\\\nconst resPerPage = 20;\\\\nconst accountId = context.accountId;\\\\n\\\\nState.init({\\\\n page: 0,\\\\n filters: {\\\\n proposal_status: [],\\\\n time_start: \\\\\\\"\\\\\\\",\\\\n time_end: \\\\\\\"\\\\\\\",\\\\n dao_type: \\\\\\\"all\\\\\\\"\\\\n },\\\\n filtersOpen: false\\\\n});\\\\n\\\\nlet followedDAOs = null;\\\\nif (state.filters.dao_type === \\\\\\\"followedDAOs\\\\\\\") {\\\\n followedDAOs = getFollowedDAOs(accountId);\\\\n if (followedDAOs === null) return \\\\\\\"\\\\\\\";\\\\n}\\\\n\\\\nconst GDAOs = [\\\\n \\\\\\\"gaming-dao.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"she-is-near.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"build.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"marketing.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"research-collective.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"ndc-degens.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"onboarddao.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"freelancerdao.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"nearnftwg.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"nearglobedao.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"service-dao.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"ndc-ops.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"creativesdao.sputnik-dao.near\\\\\\\"\\\\n];\\\\n\\\\nconst forgeUrl = (apiUrl, params) =>\\\\n apiUrl +\\\\n Object.keys(params)\\\\n .sort()\\\\n .reduce((paramString, p) => paramString + `${p}=${params[p]}&`, \\\\\\\"?\\\\\\\");\\\\n\\\\nlet daos = [\\\\n \\\\\\\"ndctrust.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"shitzu.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"orderly-ops.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"ref-finance.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"human.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"marmaj.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"cheddar.sputnik-dao.near\\\\\\\",\\\\n \\\\\\\"orderly-ops.sputnik-dao.near\\\\\\\"\\\\n];\\\\n\\\\nfunction manageDaosFilter() {\\\\n switch (state.filters.dao_type) {\\\\n case \\\\\\\"followedDAOs\\\\\\\": {\\\\n daos = followedDAOs;\\\\n break;\\\\n }\\\\n case \\\\\\\"myDAOs\\\\\\\": {\\\\n daos = useCache(\\\\n () =>\\\\n // TODO: need better API for this, fetching all members daos is not efficient\\\\n asyncFetch(forgeUrl(`https://api.pikespeak.ai/daos/members`, {}), {\\\\n mode: \\\\\\\"cors\\\\\\\",\\\\n headers: {\\\\n \\\\\\\"x-api-key\\\\\\\": publicApiKey\\\\n }\\\\n }).then((res) => {\\\\n return res.body[accountId][\\\\\\\"daos\\\\\\\"] ?? [];\\\\n }),\\\\n \\\\\\\"my-daos-feed\\\\\\\" + accountId,\\\\n { subscribe: false }\\\\n );\\\\n break;\\\\n }\\\\n case \\\\\\\"GDAOs\\\\\\\": {\\\\n daos = GDAOs;\\\\n break;\\\\n }\\\\n default: {\\\\n daos = daos.concat(GDAOs);\\\\n break;\\\\n }\\\\n }\\\\n}\\\\n\\\\nmanageDaosFilter();\\\\nconst Content = () => {\\\\n if (!(daos ?? [])?.length) {\\\\n if (state.filters.dao_type === \\\\\\\"followedDAOs\\\\\\\") {\\\\n return (\\\\n <div class=\\\\\\\"alert alert-info mt-4\\\\\\\" role=\\\\\\\"alert\\\\\\\">\\\\n You haven\\\\\\'t followed any DAOs.\\\\n </div>\\\\n );\\\\n }\\\\n if (state.filters.dao_type === \\\\\\\"myDAOs\\\\\\\") {\\\\n return (\\\\n <div class=\\\\\\\"alert alert-info mt-4\\\\\\\" role=\\\\\\\"alert\\\\\\\">\\\\n You are not a member of any DAOs.\\\\n </div>\\\\n );\\\\n }\\\\n } else {\\\\n return (\\\\n <div>\\\\n {res !== null && !res.body ? (\\\\n <div className=\\\\\\\"alert alert-danger mt-2\\\\\\\" role=\\\\\\\"alert\\\\\\\">\\\\n Network issue. Please try again later.\\\\n </div>\\\\n ) : (\\\\n <div>\\\\n <Widget\\\\n src=\\\\\\\"astraplusplus.ndctools.near/widget/DAO.Proposals.CardsList\\\\\\\"\\\\n props={{\\\\n state,\\\\n resPerPage,\\\\n proposals: res === null ? null : res.body,\\\\n showNavButton: true\\\\n }}\\\\n />\\\\n <div className=\\\\\\\"d-flex justify-content-center my-4\\\\\\\">\\\\n <Widget\\\\n src=\\\\\\\"nearui.near/widget/Navigation.PrevNext\\\\\\\"\\\\n props={{\\\\n hasPrev: state.page > 0,\\\\n hasNext: hasNextHandler(),\\\\n onPrev: () => {\\\\n update({\\\\n page: state.page - 1\\\\n });\\\\n },\\\\n onNext: () => {\\\\n update({\\\\n page: state.page + 1\\\\n });\\\\n },\\\\n nextHref: `#proposals-top`\\\\n }}\\\\n />\\\\n </div>\\\\n </div>\\\\n )}\\\\n </div>\\\\n );\\\\n }\\\\n};\\\\n\\\\nfunction fetchProposals() {\\\\n const resp = fetch(\\\\n forgeUrl(baseApi, {\\\\n daos: daos,\\\\n offset: state.page * resPerPage,\\\\n limit: resPerPage,\\\\n status: state.filters.proposal_status,\\\\n proposal_types: state.filters.proposal_types,\\\\n time_start: state.filters.time_start,\\\\n time_end: state.filters.time_end\\\\n }),\\\\n {\\\\n mode: \\\\\\\"cors\\\\\\\",\\\\n headers: {\\\\n \\\\\\\"x-api-key\\\\\\\": publicApiKey\\\\n }\\\\n }\\\\n );\\\\n\\\\n return resp;\\\\n}\\\\n\\\\nconst update = (newState) => State.update(newState);\\\\n\\\\nlet res = null;\\\\n\\\\nif ((daos ?? [])?.length > 0) {\\\\n res = fetchProposals();\\\\n}\\\\n\\\\nfunction hasNextHandler() {\\\\n const hasNext = false;\\\\n hasNext = resPerPage === res.body.length;\\\\n return hasNext;\\\\n}\\\\n\\\\nreturn (\\\\n <>\\\\n <div>\\\\n <h3>Global Proposals Feed</h3>\\\\n </div>\\\\n <div\\\\n className=\\\\\\\"d-flex align-items-center gap-2 flex-wrap-reverse justify-content-end\\\\\\\"\\\\n id=\\\\\\\"proposals-top\\\\\\\"\\\\n >\\\\n <Widget\\\\n src=\\\\\\\"nearui.near/widget/Input.Text\\\\\\\"\\\\n props={{\\\\n placeholder: \\\\\\\"Search by proposal ID or name\\\\\\\",\\\\n disabled: true,\\\\n type,\\\\n size,\\\\n icon: (\\\\n <i\\\\n className=\\\\\\\"bi bi-search\\\\\\\"\\\\n style={{\\\\n color: \\\\\\\"#4F46E5\\\\\\\"\\\\n }}\\\\n />\\\\n ),\\\\n inputProps: {\\\\n title: \\\\\\\"Disabled because no API for searching yet\\\\\\\"\\\\n }\\\\n }}\\\\n />\\\\n <Widget\\\\n src=\\\\\\\"astraplusplus.ndctools.near/widget/Layout.Modal\\\\\\\"\\\\n props={{\\\\n open: state.filtersOpen,\\\\n onOpenChange: (open) => {\\\\n State.update({\\\\n ...state,\\\\n filtersOpen: open\\\\n });\\\\n },\\\\n modalWidth: \\\\\\\"1000px\\\\\\\",\\\\n toggle: (\\\\n <Widget\\\\n src=\\\\\\\"nearui.near/widget/Input.Button\\\\\\\"\\\\n props={{\\\\n children: (\\\\n <>\\\\n Filter\\\\n <i className=\\\\\\\"bi bi-funnel\\\\\\\"></i>\\\\n </>\\\\n ),\\\\n variant: \\\\\\\"info outline\\\\\\\",\\\\n size: \\\\\\\"md\\\\\\\"\\\\n }}\\\\n />\\\\n ),\\\\n content: (\\\\n <Widget\\\\n src=\\\\\\\"astraplusplus.ndctools.near/widget/ProposalsFeed.FilterModal\\\\\\\"\\\\n props={{\\\\n filters: state.filters,\\\\n cancel: () => {\\\\n State.update({\\\\n ...state,\\\\n filtersOpen: false\\\\n });\\\\n },\\\\n applyFilters: (filters) => {\\\\n State.update({\\\\n ...state,\\\\n filters,\\\\n filtersOpen: false\\\\n });\\\\n },\\\\n daoId\\\\n }}\\\\n />\\\\n )\\\\n }}\\\\n />\\\\n </div>\\\\n <Content />\\\\n </>\\\\n);\\\\n" |