Community channels page#5770
Conversation
💀 |
b1bdaf4 to
b22272c
Compare
rtibblesbot
left a comment
There was a problem hiding this comment.
Solid new feature adding a Community Library browsing page with filters, search, pagination, and channel details. The architecture is clean — good use of provide/inject for filter state, backward-compatible pagination, and proper deduplication for M2M joins.
CI passing (relevant checks skipped due to path matching).
Suggestions:
- Stray
>after</script>in AboutCommunityLibraryModal.vue - Unused
nResultsFoundi18n string (duplicate ofresultsText) - Debug
printleft in test - Country span renders with dot separator even when countries array is empty
- Duplicate
mapResponseChannelacross two files
See inline comments for details.
Comments on lines not in diff:
contentcuration/kolibri_public/views.py:209 — praise: The existing deduplication via OrderedDict neatly handles potential duplicates from the M2M included_languages filter join. Good that this was already in place.
@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly
How was this generated?
Reviewed the pull request diff checking for:
- Correctness: bugs, edge cases, undocumented behavior, resource leaks, hardcoded values
- Design: unnecessary complexity, naming, readability, comment accuracy, redundant state
- Architecture: duplicated concerns, minimal interfaces, composition over inheritance
- Testing: behavior-based assertions, mocks only at hard boundaries, accurate coverage
- Completeness: missing dependencies, unupdated usages, i18n, accessibility, security
- Principles: DRY (same reason to change), SRP, Rule of Three (no premature abstraction)
- Checked CI status and linked issue acceptance criteria
- For UI changes: inspected screenshots for layout, visual completeness, and consistency
| } = communityChannelsStrings; | ||
|
|
||
| </script> | ||
| > |
There was a problem hiding this comment.
suggestion: There's a stray > character after the closing </script> tag. Vue SFC parsers will likely ignore it, but it should be removed to keep the file clean.
| searchLabel: { | ||
| message: 'Search', | ||
| context: 'Label for the search input', | ||
| }, |
There was a problem hiding this comment.
suggestion: nResultsFound appears to be an unused duplicate of resultsText (defined a few lines above). Only resultsText is referenced in the template. Consider removing nResultsFound to avoid confusion. They also use different ICU plural syntax (=1 vs one).
There was a problem hiding this comment.
Great catch, thanks.
| response = self._list({"languages": "en,fr", "public": "false"}) | ||
| self.assertEqual(response.status_code, 200, response.content) | ||
| ids = [UUID(item["id"]) for item in response.data] | ||
| print("ids", ids) |
There was a problem hiding this comment.
nitpick: Debug print("ids", ids) left in the test. Should be removed before merge.
| <span> | ||
| {{ language }} | ||
| </span> | ||
| <span v-if="channel.countries"> |
There was a problem hiding this comment.
suggestion: v-if="channel.countries" is truthy for an empty array []. When the API returns countries: [] (which it does by default in consolidate), this span renders with empty text but still gets the • dot separator from the CSS span:not(:last-child)::after rule. Consider v-if="channel.countries && channel.countries.length" or v-if="country" to avoid the visual artifact.
| import SidePanelModal from 'shared/views/SidePanelModal'; | ||
| import { commonStrings } from 'shared/strings/commonStrings'; | ||
|
|
||
| function mapResponseChannel(channel) { |
There was a problem hiding this comment.
suggestion: This mapResponseChannel function is nearly identical to the one in CommunityChannelDetailsModal.vue (which has additional fields like resource_count, resource_size, languages, licenses). Consider extracting a shared utility to avoid divergence over time. The details modal version is a superset — the list version could potentially reuse it.
There was a problem hiding this comment.
I don't want to mess with it just yet, until we have a clear direction on whether we want to introduce info from the public models on more places.
| @@ -0,0 +1,156 @@ | |||
| import { computed, inject, provide } from 'vue'; | |||
There was a problem hiding this comment.
praise: Clean use of provide/inject with Symbol keys to share filter state between the parent list component and the filters component without prop drilling. The composable pattern with both useCommunityChannelsFilters (provider) and injectCommunityChannelsFilters (consumer) is well-structured.
|
|
||
|
|
||
| class ChannelMetadataListPagination(CachedListPagination): | ||
| page_size = None |
There was a problem hiding this comment.
praise: Good choice setting page_size = None — this makes pagination opt-in via query param, preserving backward compatibility for existing callers that expect a flat list response.
| * @returns {UseFilterReturn} | ||
| */ | ||
| export function useFilter({ name, filterMap, defaultValue = null }) { | ||
| export function useFilter({ name, filterMap, multi, defaultValue = null }) { |
There was a problem hiding this comment.
praise: The multi-select extension to useFilter is well-designed — comma-separated URL params, proper unifyMultiParams merging, and clean integration with existing single-select behavior.
rtibblesbot
left a comment
There was a problem hiding this comment.
Prior findings
Resolved:
- Stray
>in AboutCommunityLibraryModal.vue (suggestion) — removed in2720b83 - Unused
nResultsFoundstring (suggestion) — removed in2720b83 - Debug
printin test (nitpick) — removed in2720b83 - Empty-array truthy check on
channel.countriesin StudioChannelCard (suggestion) — changed tov-if="country"in2720b83
Acknowledged (not re-raised):
- Duplicate
mapResponseChannel(suggestion) — author replied: "I don't want to mess with it just yet"
5/5 prior findings resolved or acknowledged. 0 re-raised.
Clean delta — all prior findings addressed. One new suggestion on StudioDetailsPanel. CI passing.
@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly
How was this generated?
Compared the current PR state against findings from a prior review:
- Retrieved prior bot reviews deterministically via the GitHub API
- Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
- Only raised NEW findings for newly introduced code
- Reviewed the pull request diff checking for correctness, design, architecture, testing, completeness, and adherence to DRY/SRP principles
- Checked CI status and linked issue acceptance criteria
| </StudioDetailsRow> | ||
|
|
||
| <StudioDetailsRow | ||
| v-if="_details.countries" |
There was a problem hiding this comment.
suggestion: Same empty-array pattern that was just fixed in StudioChannelCard: _details.countries is [] (truthy) for community channels with no countries, so this row renders and ExpandableList receives items=null from the countries computed. Consider v-if="countries" (the computed property) to stay consistent with the StudioChannelCard fix.
| self.assertEqual(response.status_code, 200, response.content) | ||
| ids = [UUID(item["id"]) for item in response.data] | ||
| self.assertIn(UUID(self.channel_en.id), ids) | ||
| self.assertNotIn(UUID(self.channel_multi.id), ids) |
There was a problem hiding this comment.
praise: Thorough test coverage — search, case insensitivity, combined filters, and the language M2M filter are all well-covered with clear setup and assertions.
Summary
useFiltercomposable to support multi-value select filters.Grabacion.de.pantalla.2026-03-20.a.la.s.8.03.42.a.m.mov
Reviewer guidance
AI usage