Skip to content

fix(ui): sidebar org links don't show active proposals count #2205

Description

@wa0x6e

Since #2099, sidebar follow-list items belonging to an organization link to the org page instead of the space. resolveSpaceItem (apps/ui/src/helpers/organizations/config.ts) hardcodes active_proposals: 0 in the org branch:

if (org) {
  return {
    link: { name: 'org', params: { org: org.id } },
    title: org.name,
    avatarSpace: { ...org.spaceIds[0], avatar: '', active_proposals: 0 }
  };
}

SpaceAvatar only renders the badge when active_proposals is truthy, so org sidebar items never show the active proposals indicator, even when org spaces have live proposals. Non-org spaces are unaffected.

Expected: org sidebar item shows the active proposals count, summed across all of the org's spaces (for consistency — the badge should reflect the whole org, not just the followed space).


Investigation findings

Data availability

  • Offchain hub: activeProposals is returned by space queries (apps/ui/src/networks/offchain/api/queries.ts) — real counts available today.
  • Onchain networks (eth, arb1, sn): active_proposals is currently hardcoded to null in formatSpace (apps/ui/src/networks/common/graphqlApi/index.ts:360). The branch feat/active-proposals-explore-ui fixes this: it adds active_proposal_count to SPACE_FRAGMENT and maps active_proposals: space.active_proposal_count. Once merged, batched loadSpaces returns true counts for all networks.
  • The fix should sum all org spaces without skipping onchain ones: today they contribute null (treated as 0), and it becomes fully correct once the branch above merges — no further changes needed.

Org configs (ORGANIZATIONS in apps/ui/src/helpers/organizations/config.ts)

Org Spaces Networks
starknet 2 sn, s
snapshot 1 s
arbitrum 3 arb1 ×2, s
ens 2 eth, s

Every org has exactly one offchain (s:) space.

Query cost analysis

Approaches considered, extra HTTP requests to populate org-wide counts:

Approach Extra requests Notes
A. Per-org loadSpace for each org space (like useOrganization) up to 8 wasteful
B. Fold org sibling ids into the existing followed-spaces batch (getSpaces id_in in useFollowedSpacesQuery) 0–1 muddies followed-query semantics
C. One shared batched query for all followed orgs' sibling ids, split per network via getSpaces ≤4 (one per network: s, arb1, eth, sn) clean, cached by vue-query
D. Like C, but split per endpoint instead of per network (recommended) 2 flat see below

Recommended approach (D)

All mainnet onchain networks (eth, arb1, sn) share one sx-api endpoint (API_URL in apps/ui/src/networks/evm/metadata.ts and starknet/metadata.ts). loadSpaces (apps/ui/src/networks/common/graphqlApi/index.ts:775) only sets the indexer variable when filter.network is provided — omitting it queries across all indexers in a single request. id_in passes straight into where, and returned spaces carry _indexer so the network field resolves correctly.

So one shared query fn, splitting all followed orgs' sibling space ids into two buckets:

  1. onchain ids → single loadSpaces({ id_in }, no network filter) against the shared sx-api endpoint → 1 request covers arb1 + eth + sn
  2. offchain ids → hub loadSpaces({ id_in }) → 1 request

2 requests total, regardless of how many orgs are followed. The existing getSpaces helper (apps/ui/src/queries/spaces.ts) splits per network, so this needs a small custom fetch (~15 lines) instead of reusing it. Result should be a single vue-query entry (keyed on the set of followed org ids) shared by all sidebar items; each org item sums active_proposals over its spaces (null → 0).

Caveat: testnet networks use API_TESTNET_URL (different endpoint). No org config uses testnets today; group by endpoint if that ever changes.

Implementation sketch

  • Add a shared query (e.g. useOrgsSpacesQuery in apps/ui/src/queries/spaces.ts) fetching all sibling spaces for a given set of org configs, using the two-bucket fetch above.
  • In AppSidebarItem (apps/ui/src/components/App/SidebarItem.vue), when resolveSpaceItem resolves to an org, override avatarSpace.active_proposals with the summed count from the shared query.
  • resolveSpaceItem itself stays pure/unchanged (existing unit tests in apps/ui/src/helpers/organizations.test.ts keep passing); only the component layer supplies the real count.
  • Note: SpacesListItem.vue also uses resolveSpaceItem; check whether the explore list should get the same treatment for consistency.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions