feat: eXIP-7.3.0.18 Spaces list portlet EXO-89465 - #446
Conversation
boubaker
left a comment
There was a problem hiding this comment.
AI review — Round #1
Part of the cross-repo review of eXIP 7.3.0.18 "User Spaces List" (Tribe note 50524, revision 6) across Meeds-io/social#6085, Meeds-io/analytics#446, Meeds-io/meeds#4281, Meeds-io/kudos#628 and Meeds-io/gamification#2002, reviewed as one delivery. First round, so there is no status table. Findings are inline; the 🔴s of this delivery are in Meeds-io/social#6085, not here.
The decision this PR implements — a mode of the existing widget rather than a second portlet — is carried out faithfully, and the two things most likely to go wrong in it did not: the empty states are genuinely two distinct behaviours, and nothing new leaked into social/webapp. Three of the four findings below are about blast radius: styling and behaviour changes that reach the widget's existing analytics-mode instances on every page that hosts one.
Verified conform
- Both empty states, exactly per the board. Own profile with no space → the whole block disappears (
hiddenWidget, plus$updateApplicationVisibilityto drop the layout wrapper and its column gap — the same hookUserSettingNotificationsand other self-hiding widgets use, so this is the platform idiom and not a local trick). Visited profile with no common space → layer-group icon, tertiary, "No common spaces" (US02). The existing empty state with the create-space button appears in neither, in the widget and in the drawer header. A failed listing also hides the block rather than painting "no common spaces" over an error — that is a distinction the spec did not ask for and the right call. - Tab count.
singleTab = ownProfile || $root.isExternal || $root.profileOwnerExternal— one tab on one's own profile, for an external viewer, and on an external owner's profile; two only for an internal viewer on an internal profile. Initial tab and thescopeit maps to are consistent, and the drawer paginates 20 per page as the spec states, with a per-tab in-flight guard and id-based de-duplication. - Profile-mode wiring.
URLUtils.getStreamOwnerId()rather thangetCurrentUser(), for the reason the JSP comment gives —getCurrentUser()also returns null for an external viewer on a profile it may not access, which would silently drop the widget back to its analytics-mode behaviour on a profile page. The owner is escaped withescapeEcmaScriptbefore reaching the inline script.SpacesListWidgetList.itemId/itemSpacekeeps the analytics-mode id lists working alongside the profile-mode objects, and handing the resolved objects over avoids a per-space read that would be refused for a listed space the viewer is not a member of. - The REST contract matches what social ships: path,
offset/limit/scope/returnSize, and every field the UI reads (id,displayName,avatarUrl,visibility,member) is a component of theUserSpacerecord.getProfileSpacescaps the page size at the endpoint's own bound, so an oversized instance setting degrades instead of returning 400. - i18n is complete: all six new keys present in both
Analytics_en.propertiesandAnalytics_fr.properties, no orphans. - No reinvented shared component: the widget reuses its own catalogued drawer / item / list components;
social'sspacesListComponentsis a spaces card list and is not the right primitive for a widget row.
Classification
N1 for the delivery as a whole — max-severity aggregation from Meeds-io/social#6085, which carries the ACL decision code, the ACL-bearing cache key and the REST surface. This PR's own diff is UI-only, but it must not be merged ahead of, or independently of, that classification: it needs validation by an Architect/Senior Developer who knows the delivery is N1, not auto-merge on AI review alone, and author != approver.
Process: the title carries no EXO- id (dev-lifecycle.md §4) — the id exists, EXO-89465; and the body carries no Knowledge: line, which dev-lifecycle.md §3b step 5 gates at the feature/mips integration PR.
🤖 Generated with Claude Code
MayTekayaa
left a comment
There was a problem hiding this comment.
AI review — Round #2 (follow-up)
Part of the cross-repo review of eXIP 7.3.0.18 "User Spaces List" (Tribe note 50524, revision 6; board project 8368) across Meeds-io/social#6085, Meeds-io/analytics#446, Meeds-io/meeds#4281, Meeds-io/kudos#628 and Meeds-io/gamification#2002, reviewed as one delivery. Head reviewed: 2478194e0; the commit pushed since round 1 was re-reviewed in full.
Status of round #1
| # | Finding | Status |
|---|---|---|
| 1 | 🟡 Per-portlet CSS in analytics.less |
✅ Fixed — both rule blocks removed; the look now comes from platform-ui helpers through an emphasized prop set only in profile mode. Every class used exists: text-header (helpers.less:125), text-sub-title (:203), text-font-size (:194), text-color (:149); primary--text / font-weight-bold are Vuetify 2 core. |
| 2 | 🟡 headerTranslations hidden in profile mode |
✅ Fixed — SpacesListWidgetSettingsDrawer.vue:34-44 carries no v-if; v-if="!profileMode" now guards only the analytics-mode rows. US04 restored; PO item 5 (the block's title) has a control to land on again. |
| 3 | 🟡 Non-member spaces lost their link | ✅ Fixed — SpacesListWidgetItem.vue:84 links every listed space; the PO decision is recorded in the comment above it. |
| 4 | 🟢 allow-expand removed for external viewers in analytics mode; See-all restyle |
✅ Fixed (:allow-expand="!profileMode", SpacesListWidgetDrawer.vue:28) / ➖ the See-all button restyle still lands on every widget instance — accepted as a deliberate alignment with the Documents and Task widgets, worth one line in the body. |
| 5 | Process — no EXO- id, no Knowledge: line |
✅ Fixed — title carries EXO-89465; body carries Knowledge: none — … pointing at Meeds-io/social#6085 for the delivery's knowledge update. |
Verified conform
- Both empty states, per the board:
emptyWidget = !spacesCount && initialized && applicationMountedstarts false, sohiddenWidgetflips only after the first load — no flicker; own profile empty or failed → block hidden through$root.$updateApplicationVisibility(false)(helper at socialcommon/initComponents.js:142); visited profile →fa-layer-grouptertiary + "No common spaces"; the create-space state appears in neither the widget nor the drawer header. - Query budget: the widget calls
getUserSpaces(owner, 0, min(userSpacesLimit || 4, 100))with noscopeand noreturnSize— one list query, no count; the drawer paginates 20 with a one-row lookahead instead of a count query, per-tab in-flight guard, id de-duplication. - Tabs:
singleTab = ownProfile || $root.isExternal || $root.profileOwnerExternal; own profile opens onallwith scopeALL, everyone else oncommon; thescopestrings matchUserSpacesScope. Placeholder centred in afill-heightflex column, as the PO asked on US02. - Contract with social: every field the UI reads (
id,displayName,avatarUrl) is a component ofrecord UserSpacewith that JSON name; the URL and query parameters matchUserSpacesRest.URLUtils.getStreamOwnerId()is imported from the same package as the already-usedUtils; the owner isescapeEcmaScript-escaped inside quotes. - i18n: the five new keys exist in both
Analytics_en.propertiesandAnalytics_fr.properties; the pre-existing keys the new code reuses (hiddenSpace,noSpaces,header) are present in both. - No reinvented shared component; the widget reuses its own catalogued drawer, list and item.
What this PR does well
The round-1 CSS finding was fixed the right way — by moving the styling decision into a prop that only profile mode sets, so nothing leaks into the analytics-mode instances — and the empty-state gating on initialized && applicationMounted avoids the hide-then-show flicker a naive !length check would have produced.
Classification
N1 for the delivery as a whole — max-severity aggregation from Meeds-io/social#6085 (ACL decision code, ACL-bearing cache key, REST surface). This PR's own diff is UI-only, but it must not merge ahead of, or independently of, that classification: validation by an Architect/Senior Developer who knows the delivery is N1, author != approver, no auto-merge on AI review alone. Release order 2 of 4, after social.
🤖 Generated with Claude Code
Self-review close-out — #446 (eXIP 7.3.0.18, head
|
| # | Finding | Status |
|---|---|---|
| 1 | 🟡 Per-portlet CSS in analytics.less, first rule leaking into analytics mode |
✅ Fixed 2478194e0 — rules removed; profile-mode look through platform-ui helpers via an emphasized prop set only in that mode |
| 2 | 🟡 headerTranslations hidden in profile mode (US04) |
✅ Fixed 2478194e0 |
| 3 | 🟡 Every non-member space lost its link | ✅ Fixed 2478194e0 — every listed space is a link (PO decision recorded in SpacesListWidgetItem.vue) |
| 4 | 🟢 Drawer expansion removed for external viewers in analytics mode; See-all restyle on every instance | ✅ Fixed (:allow-expand="!profileMode") / ➖ restyle accepted as deliberate alignment, stated in the body |
| 5 | 🟢 Trailing blank line left in analytics.less |
✅ Fixed 08d99ec7a — file out of the diff |
| 6 | Process — EXO- id, Knowledge: line |
✅ |
Verified conform (closing round): both empty states per the board (own profile empty → block hidden via $updateApplicationVisibility; visited profile → layer-group placeholder "No common spaces"); tab count (singleTab when either side is external, US03/US06/US07); settings drawer exposes header translations and the limit only, default 4, min 1 (US04); drawer paginates 20 with an extra-row lookahead, per-tab pending guard, id de-duplication; owner resolved through URLUtils.getStreamOwnerId() and escaped in the JSP; REST path and parameters match UserSpacesRest; Math.min(limit, 100) matches the endpoint bound; i18n complete in en and fr; no reinvented shared component.
Classification: N1 for the delivery as a whole (max-severity aggregation from Meeds-io/social#6085); UI-only diff here, but it must not merge ahead of or independently of that classification — Architect/Senior Developer validation, author ≠ approver, no auto-merge on AI review alone. Release wave 2 of 4.
🤖 Generated with Claude Code
boubaker
left a comment
There was a problem hiding this comment.
AI review — Round #2 (follow-up)
Re-review of Meeds-io/analytics#446 at 08d99ec7a (two commits since round 1, five files). Everything verified in the source at this head.
Status of round-1 findings
| # | Finding | Status |
|---|---|---|
| 1 | 🟡 Per-portlet CSS in analytics.less, first rule leaking into analytics mode |
✅ Fixed — analytics.less is out of the diff entirely; the look comes from platform-ui helpers through an emphasized prop passed only from the profile-mode lists. text-header, text-sub-title verified in platform-ui-skin/…/core/helpers.less (:125, :203); primary--text / font-weight-bold are Vuetify utilities |
| 2 | 🟡 headerTranslations hidden in profile mode |
✅ Fixed — the v-if="!profileMode" is gone from the title wrapper (SpacesListWidgetSettingsDrawer.vue:34); the analytics-only rows keep theirs. One correction to my round-1 wording: I said board US04 "deliberately re-confirmed" the two settings. Read directly this round, US04 (89467) speaks only of the number of spaces (default 4); the two-settings rule comes from the spec's Configurability table, not the board. The fix is right either way — it is what gives PO item 5 (the block's title, still pending) a place to land |
| 3 | 🟡 Every non-member space lost its link | ✅ code / |
| 4 | 🟢 Drawer expansion removed for external viewers in analytics mode; "See all" restyle on every instance | ✅ Fixed (:allow-expand="!profileMode", :28) / ➖ restyle accepted as deliberate alignment, now stated in the PR body |
| — | Process: EXO- id, Knowledge: line |
✅ / ✅ (none — …, carried by social#6085) |
New this round
Two inline comments, both small: the provenance of the link decision (🟡, because it changes screens that were already "Tested & validated"), and a comment that describes a colour the skin does not actually produce (🟢).
Verified conform
Board stories re-read from the board itself this round (89465 US01, 89466 US02, 89467 US04, 89468 US05, 89469 US06, 89470 US07 — all labelled Tested & validated): both empty states, the alphabetical order, the external-owner single tab, the two-tab drawer for an internal owner, and the settings default of 4 all match the code. The emphasized prop is passed only from the three profile-mode <spaces-list-widget-list> usages, so the analytics-mode drawer keeps text-color — the leak is closed. text-sub-title (colour only) and text-header (size/weight/colour) both carry !important at equal specificity, and text-sub-title is declared later in helpers.less, so the inactive tab is grey as intended.
Classification: N1 for the delivery as a whole (max-severity aggregation from Meeds-io/social#6085); UI-only here, must not merge ahead of or independently of that classification — Architect/Senior Developer validation, author != approver, no auto-merge on AI review alone.
🤖 Generated with Claude Code
MayTekayaa
left a comment
There was a problem hiding this comment.
AI review — Round #3 (final)
Part of the cross-repo review of eXIP 7.3.0.18 "User Spaces List" (Tribe note 50524, revision 6; board project 8368) across Meeds-io/social#6085, Meeds-io/analytics#446, Meeds-io/meeds#4281, Meeds-io/kudos#628 and Meeds-io/gamification#2002, reviewed as one delivery. Head reviewed: 08d99ec7a; the single commit since round 2 removes the trailing blank line and nothing else.
Status of every finding
| Round | # | Finding | Status |
|---|---|---|---|
| 1 | 1 | 🟡 Per-portlet CSS in analytics.less |
✅ Fixed (2478194e0) — platform-ui helpers through the emphasized prop |
| 1 | 2 | 🟡 headerTranslations hidden in profile mode (US04) |
✅ Fixed |
| 1 | 3 | 🟡 Non-member spaces lost their link | ✅ Fixed — every listed space is a link, PO decision recorded |
| 1 | 4 | 🟢 Drawer expansion removed for external viewers in analytics mode; See-all restyle | ✅ Fixed / ➖ restyle accepted and now stated in the body |
| 1 | 5 | Process — EXO- id, Knowledge: line |
✅ Fixed |
| 2 | 1 | 🟢 Trailing blank line in analytics.less |
✅ Fixed (08d99ec7a) — the file is out of the diff (9 files, was 10) |
All findings from previous rounds are resolved; nothing outstanding from the AI review side.
Verified conform (unchanged at this head)
Both empty states per the board, with the block hidden through $updateApplicationVisibility on an empty or failed own profile and the layer-group placeholder on a visited one; no count query from the widget, lookahead pagination of 20 in the drawer; tab count on the viewer and owner externality; REST contract and field names matching UserSpace; i18n parity; no reinvented shared component.
Classification
N1 for the delivery as a whole — max-severity aggregation from Meeds-io/social#6085. UI-only diff, but it must not merge ahead of or independently of that classification: Architect/Senior Developer validation, author != approver, no auto-merge on AI review alone. Release order 2 of 5, after social.
🤖 Generated with Claude Code
AI review — correction to Round #3The Round #3 (final) comment above (15:52 UTC) was drafted from a check made at 15:27 UTC and did not see the Round #2 (follow-up) posted by the other reviewing session at 15:38 UTC. Its "nothing outstanding from the AI review side" statement is therefore withdrawn for this PR: the review reopens on the two findings of that round — the provenance of the "PO decision" cited in Everything else in Round #3 stands: the round-1 and round-2 items are fixed at 🤖 Generated with Claude Code |
…our the skin overrides - EXO-89465 Prior to this change, SpacesListWidgetItem.vue attributed the "every listed space is a link" rule to a PO decision that is recorded neither on the board nor on the eXIP thread, and SpacesListWidgetDrawer.vue described the profile-mode tabs as tertiary while platform-ui's shared skin forces the secondary colour with !important on .v-tab--active and .v-tabs-slider, making the color prop inert. This change will cite the actual source of the link rule (review round 1 of #446, PO confirmation pending on board story US02) and correct the tabs comment, dropping the inert color prop so the code does not promise what the skin overrides. No behaviour change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
AI review — Round #3 (follow-up)Re-review of Status of round-2 findings
Nothing new this round. No finding is open on this PR from the AI review side; the one thing pending is the PO's answer on US02, which is a board item, not a PR item — if the PO prefers plain text for non-member spaces, one line in Classification: N1 for the delivery as a whole (max-severity aggregation from 🤖 Generated with Claude Code |
The merge-base changed after approval.
47726f0 to
02eccf9
Compare
…awer- EXO-89465 (#441) Prior to this change, the spaces list widget always showed the viewer's analytics-driven spaces, even on a user's profile page. This change will make the widget, its See-all drawer and its settings list the profile owner's spaces when placed on a profile page. --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This change will add some UI adjustments to the spaces list portlet.
…ry space - EXO-89465 - Drop the portlet-specific Less for the See-all drawer (bold primary space names, profile tabs font size and inactive grey); the same look now comes from the platform-ui helpers (primary--text, font-weight-bold, text-header, text-sub-title) through a new `emphasized` prop on the list and item components, set only in profile mode. - Every listed space is a link, member or not (PO decision): a non-member lands on the space access page, which handles join / request / invite-only. - Allow expanding the drawer for external users too in widget mode. - Show the header title field in the settings drawer in profile mode as well. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…our the skin overrides - EXO-89465 Prior to this change, SpacesListWidgetItem.vue attributed the "every listed space is a link" rule to a PO decision that is recorded neither on the board nor on the eXIP thread, and SpacesListWidgetDrawer.vue described the profile-mode tabs as tertiary while platform-ui's shared skin forces the secondary colour with !important on .v-tab--active and .v-tabs-slider, making the color prop inert. This change will cite the actual source of the link rule (review round 1 of #446, PO confirmation pending on board story US02) and correct the tabs comment, dropping the inert color prop so the code does not promise what the skin overrides. No behaviour change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
34ff519 to
4a21ea2
Compare
|



Integration of the "User Spaces List" eXIP (eXIP 7.3.0.18, Tribe note 50524) from feature/devx into feature/mips. Part of a cross-repo delivery with Meeds-io/social#6085, Meeds-io/meeds#4281, Meeds-io/kudos#628 and Meeds-io/gamification#2002; release wave 2 of 4, after social.
The listing is delivered as a profile-owner mode of the existing
SpacesListWidget, not a second portlet (spec decision D1): the JSP passes the profile owner (URLUtils.getStreamOwnerId()), the widget callsGET /social/rest/users/{username}/spacesand hands the resolved spaces to its own list, item and "See all" drawer components. Profile-mode styling comes from platform-ui helpers through anemphasizedprop set only in that mode — no per-portlet CSS, nothing leaks into the analytics-mode instances.One deliberate change to every existing
SpacesListWidgetinstance: the "See all" button is restyled (text,color="primary",small,link, no height/min-width/padding overrides) to align with the Documents and Task widgets. Drawer expansion is suppressed in profile mode only; analytics mode is unchanged.Knowledge: none — UI-only mode of an existing widget; the delivery's knowledge update (profile listing endpoint, ACL matrix, cache key) is carried by Meeds-io/social#6085.
Classification: N1 for the delivery as a whole (max-severity aggregation from Meeds-io/social#6085) — must not merge ahead of or independently of that classification; Architect/Senior Developer validation, author ≠ approver, no auto-merge on AI review alone.