Add Notifications page#5610
Conversation
c7df896 to
9a59b96
Compare
There was a problem hiding this comment.
These tests were moved to be their own test files
| grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); | ||
| gap: 6px 12px; | ||
|
|
||
| /* TODO: Open a KDS follow up to fix KTextbox feedback message alignment */ |
There was a problem hiding this comment.
Follow up: learningequality/kolibri-design-system#1187
| }, | ||
| UPDATE_SESSION_FROM_INDEXEDDB(state, { id, ...mods }) { | ||
| if (id === state.currentUser.id) { | ||
| UPDATE_SESSION_FROM_INDEXEDDB(state, { CURRENT_USER, ...mods }) { |
There was a problem hiding this comment.
This was a weird error, the key field in the session indexedDB table is CURRENT_USER and its only value could be CURRENT_USER so it doesnt make sense to look at the id and if id === state.currentUser.id for this table. Therefore, until now, this function wasn't doing anything.
|
Hi @AlexVelezLl, I didn't review, just noticed the note about If there are any other places like that, please let me know and we can chat. This tracker should be quite up-to-date so it may too help. |
|
And also |
|
Thanks a lot @MisRob! I did look for an alternative on Studio, but didn't find it. Will use the StudioImmersiveModal instead. Thanks! |
|
Yeah the naming doesn't help - when possible I try to rename in line with Kolibri conventions :) Glad it helps. |
9a59b96 to
61f2f65
Compare
61f2f65 to
4fef186
Compare
4fef186 to
eee90ba
Compare
rtibblesbot
left a comment
There was a problem hiding this comment.
Great work on this notifications feature! This is a well-structured PR with clean composable architecture, good test coverage, and thoughtful design decisions (like using query params for modal persistence and the extensible notification type renderer pattern). The video demo looks polished.
There are a few issues to address before merging:
Blocking issues:
- Variable shadowing bug in
useFetch.js— thecatch (error)parameter shadows the outererrorref, so error state is never written to the ref notify_update_to_channel_editors()is called beforesuper().save()for new submissions, butdate_updatedisauto_now=Trueand hasn't been set yet — the notification date will beNonenotify_users()readsuser.newest_notification_datefrom stale in-memory objects after.update()— the change events will push the old values- Approval/rejection notification components pass
notification.date_createdinstead ofnotification.datetoNotificationBase, showing the wrong date NotificationFiltersreceives alastReadFilterprop from the parent but never declares or uses it
Visual inspection: The UI video shows proper layout, working tabs, filters, and notification list rendering. The immersive modal and responsive design look good.
CI: All checks pass.
Looking forward to the updates — this is a great addition to Studio!
contentcuration/contentcuration/frontend/shared/composables/useFetch.js
Outdated
Show resolved
Hide resolved
...end/shared/views/NotificationsModal/notificationTypes/CommunityLibrarySubmissionApproval.vue
Outdated
Show resolved
Hide resolved
contentcuration/contentcuration/frontend/shared/views/NotificationsModal/index.vue
Outdated
Show resolved
Hide resolved
...tcuration/frontend/shared/views/NotificationsModal/composables/useCommunityLibraryUpdates.js
Show resolved
Hide resolved
contentcuration/contentcuration/frontend/shared/views/NotificationsModal/NotificationList.vue
Outdated
Show resolved
Hide resolved
...entcuration/contentcuration/frontend/shared/views/NotificationsModal/NotificationFilters.vue
Show resolved
Hide resolved
contentcuration/contentcuration/frontend/administration/pages/Channels/ChannelTable.vue
Outdated
Show resolved
Hide resolved
contentcuration/contentcuration/frontend/shared/views/NotificationsModal/index.vue
Outdated
Show resolved
Hide resolved
rtibblesbot
left a comment
There was a problem hiding this comment.
Solid feature implementation — the notifications page, composables migration, and red dot indicator are well-architected.
CI passing. Video demo verified: layout, filters, chips, and tab switching look correct.
Blocking:
- Variable shadowing bug in
useFetch.js—catch (error)shadows the outererrorref (see inline) notify_update_to_channel_editors()called beforesuper().save()on creation —date_updatedisNoneforauto_nowfields until saved (see inline)- Approval/rejection notification components pass
notification.date_createdinstead ofnotification.datetoNotificationBase(see inline)
Suggestions:
lastReadFilterprop is passed toNotificationFiltersbut never declared or used in that component (see inline)
contentcuration/contentcuration/frontend/shared/composables/useFetch.js
Outdated
Show resolved
Hide resolved
...end/shared/views/NotificationsModal/notificationTypes/CommunityLibrarySubmissionApproval.vue
Outdated
Show resolved
Hide resolved
contentcuration/contentcuration/frontend/shared/views/NotificationsModal/index.vue
Outdated
Show resolved
Hide resolved
rtibblesbot
left a comment
There was a problem hiding this comment.
Solid implementation of the notifications page and red dot indicator. Architecture is well-considered — the composable-based approach, extensible notification type rendering, and timestamp-based unread tracking are good design choices. CI passing. Video demo verified.
- blocking: Wrong date displayed for approval/rejection notifications (shows creation date instead of status-change date)
- blocking: Variable shadowing bug in
useFetch— theerrorref is never set on catch - suggestion: Unused prop
lastReadFilterpassed toNotificationFilters - nitpick: Using array index as
v-forkey in notification list
...end/shared/views/NotificationsModal/notificationTypes/CommunityLibrarySubmissionApproval.vue
Outdated
Show resolved
Hide resolved
...nd/shared/views/NotificationsModal/notificationTypes/CommunityLibrarySubmissionRejection.vue
Outdated
Show resolved
Hide resolved
contentcuration/contentcuration/frontend/shared/composables/useFetch.js
Outdated
Show resolved
Hide resolved
contentcuration/contentcuration/frontend/shared/views/NotificationsModal/index.vue
Outdated
Show resolved
Hide resolved
contentcuration/contentcuration/frontend/shared/views/NotificationsModal/NotificationList.vue
Outdated
Show resolved
Hide resolved
rtibblesbot
left a comment
There was a problem hiding this comment.
Solid implementation of the notifications page and red-dot indicator. The architecture — timestamp-based unread tracking, composable-driven filtering, per-type notification renderers — is well thought out and extensible.
CI passing. Video demo reviewed; UI looks clean with proper layout, filter behavior, and empty states.
Findings:
- blocking (1): Race condition in
notify_users— queryset is read after a bulkupdate(), sonewest_notification_datein the change events may be stale. See inline comment onmodels.py:706. - suggestion (3):
useFetch.jscatch block shadows outererrorvariable;mark_notifications_readusesGreateston an in-memory assignment instead of DB-level update;notify_update_to_channel_editorsis called beforesuper().save()sodate_updatedmay beNonefor new submissions. - nitpick (2): Mutating input argument in
getSubmissionsUpdates; minor typo in comment. - strings: This PR adds new i18n strings and targets the
unstablebranch (which is the default branch), so that's appropriate.
contentcuration/contentcuration/frontend/shared/composables/useFetch.js
Outdated
Show resolved
Hide resolved
...tcuration/frontend/shared/views/NotificationsModal/composables/useCommunityLibraryUpdates.js
Outdated
Show resolved
Hide resolved
contentcuration/contentcuration/frontend/shared/views/NotificationsModal/index.vue
Outdated
Show resolved
Hide resolved
eee90ba to
4431baa
Compare
rtibblesbot
left a comment
There was a problem hiding this comment.
Well-structured PR that adds a solid notifications system. The composable architecture is extensible and the UI is clean.
CI passing. Video demo reviewed — layout, filters, tabs, and red dot all look correct.
Findings:
- suggestion (x3): Date filter eagerness, self-notification on submission creation,
View morebuttons not wired up - nitpick (x1):
getStartOfWeeklocale assumption
See inline comments for details.
...entcuration/contentcuration/frontend/shared/views/NotificationsModal/NotificationFilters.vue
Show resolved
Hide resolved
...entcuration/contentcuration/frontend/shared/views/NotificationsModal/NotificationFilters.vue
Outdated
Show resolved
Hide resolved
...end/shared/views/NotificationsModal/notificationTypes/CommunityLibrarySubmissionApproval.vue
Show resolved
Hide resolved
4431baa to
acd221f
Compare
rtibblesbot
left a comment
There was a problem hiding this comment.
Solid implementation of the notifications page and red dot indicator. The architecture — composable-based data fetching, extensible notification type renderers, and timestamp-based unread tracking — is well-considered.
CI passing. Video demo verified: notifications page layout, tabs, filters, chips, and empty states all look correct.
Findings:
- suggestion: Date filter values are computed at component setup time, not at selection time (NotificationFilters.vue)
- suggestion:
mark_notifications_readleaves the in-memory model field as aGreatestexpression after save - nitpick:
flaggedNotificationstring uses{ variable }(with spaces) inconsistent with existing{variable}pattern - nitpick:
getStartOfWeek()treats Sunday as week start (US convention) — may not match expectations for an international platform - nitpick: New strings are added targeting
unstable(the default branch), which is appropriate
...entcuration/contentcuration/frontend/shared/views/NotificationsModal/NotificationFilters.vue
Show resolved
Hide resolved
contentcuration/contentcuration/frontend/shared/strings/communityChannelsStrings.js
Outdated
Show resolved
Hide resolved
...entcuration/contentcuration/frontend/shared/views/NotificationsModal/NotificationFilters.vue
Outdated
Show resolved
Hide resolved
acd221f to
0b5acd8
Compare
rtibblesbot
left a comment
There was a problem hiding this comment.
Good work on the notifications page. The architecture is clean: notification types map to dedicated renderer components, the composable handles data transformation well, and the Vuex session bug fix is correct.
CI: Python tests in progress. Video demo verified — layout, filters, tabs, and chips all look correct.
Suggestions:
useCommunityLibraryUpdates.js:75-76: Direct mutation of submission objects from API responseMainNavigationDrawer.vue:238: Drawer not closed when opening notifications modal (unlike language modal)CommunityLibrarySubmissionRejection.vue:54:resolved_by_namemay be null, could render literal "null" in titleindex.vue:25,32: Redundant@clickhandlers on VTabs whenv-modelalready manages selection
Nitpicks:
useCommunityLibraryUpdates.js:125: Redundantnew Date()wrapping on values already of type Dateindex.vue:167: Typo "precisision" → "precision" (now on line 167 after latest commit)
...tcuration/frontend/shared/views/NotificationsModal/composables/useCommunityLibraryUpdates.js
Outdated
Show resolved
Hide resolved
contentcuration/contentcuration/frontend/shared/views/MainNavigationDrawer.vue
Show resolved
Hide resolved
...nd/shared/views/NotificationsModal/notificationTypes/CommunityLibrarySubmissionRejection.vue
Outdated
Show resolved
Hide resolved
contentcuration/contentcuration/frontend/shared/views/NotificationsModal/index.vue
Outdated
Show resolved
Hide resolved
...tcuration/frontend/shared/views/NotificationsModal/composables/useCommunityLibraryUpdates.js
Outdated
Show resolved
Hide resolved
contentcuration/contentcuration/frontend/shared/views/NotificationsModal/index.vue
Show resolved
Hide resolved
rtibblesbot
left a comment
There was a problem hiding this comment.
Thorough implementation of the notifications page and red dot indicator for community library submissions.
CI passing. Video demo verified — layout, filters, tabs, and status chips all look correct.
Suggestions:
useFetch.js: thedata.value = nullmove changes behavior for all consumers (see inline)mark_notifications_read: unusedpkparameteruseCommunityLibraryUpdates.js:isLoadingMorestate could be stale on error pathNotificationFilters.vue: date filter values computed once at component creation
Nitpicks:
CommunityLibraryStatusChip.vue:propsdeclared aftersetup(non-standard order)useStore.js: no guard for missing instance
...tcuration/frontend/shared/views/NotificationsModal/composables/useCommunityLibraryUpdates.js
Outdated
Show resolved
Hide resolved
...entcuration/contentcuration/frontend/shared/views/NotificationsModal/NotificationFilters.vue
Show resolved
Hide resolved
...ration/contentcuration/frontend/shared/views/communityLibrary/CommunityLibraryStatusChip.vue
Outdated
Show resolved
Hide resolved
contentcuration/contentcuration/frontend/shared/views/NotificationsModal/index.vue
Outdated
Show resolved
Hide resolved
0b5acd8 to
78c22f1
Compare
rtibblesbot
left a comment
There was a problem hiding this comment.
Well-structured notifications feature. The architecture — composable-based data fetching, extensible notification type renderers, timestamp-based unread tracking — is solid. CI passing. Video demo verified.
Delta from prior reviews
- [blocking] Variable shadowing bug in
useFetch.js— RESOLVED (renamed tocaughtError) - [blocking]
notify_update_to_channel_editors()called beforesuper().save()for new submissions — RESOLVED (line 2935 callssuper().save()before line 2939 callsnotify_update_to_channel_editors) - [blocking]
notify_users()reads stale in-memory objects after.update()— RESOLVED (line 703-705 refreshes queryset from DB before creating change events) - [blocking] Approval/rejection components pass
notification.date_createdinstead ofnotification.date— RESOLVED (both now usenotification.date) - [blocking]
Greatest(NULL, value)returns NULL on PostgreSQL — RESOLVED (CoalescewrapsF()to handle NULL) - [blocking]
NotificationFiltersreceives unusedlastReadFilterprop — RESOLVED (prop removed;lastReadis now passed viaqueryParamsto the composable) - [suggestion]
useFetch.jsdata.value = nullbehavior change — RESOLVED (moved inside catch block with clear comment) - [suggestion]
mark_notifications_readleaves in-memory field asGreatestexpression — UNRESOLVED (aftersave(),self.last_read_notification_dateis aGreatestexpression, not a datetime; low risk since the callsite discards the instance, but fragile if reused) - [suggestion]
WithNotificationIndicatorscreen reader support — RESOLVED (addedvisuallyhiddenspan withnewNotificationsNotice$()text) - [suggestion]
useStoremissing guard forgetCurrentInstance— RESOLVED (throws descriptive error) - [suggestion]
notify_update_to_channel_editorson resolve doesn't exclude the resolving admin — UNRESOLVED (admin self-notification if they're also an editor; design choice, not a bug) - [suggestion] Date filter values computed once at component creation — UNRESOLVED (see inline)
- [suggestion]
handleNotificationsReadtimeout path leaves stale data — RESOLVED (timeout resolves the promise, allowing the flow to continue) - [suggestion]
resolved_by_namemay be null rendering literal "null" — RESOLVED (get_resolved_by_namereturnsNone, and rejection template falls back to empty string) - [suggestion] Drawer not closed when opening notifications modal — UNRESOLVED (see inline)
- [nitpick] Array index as
v-forkey — RESOLVED (key is now${notification.id}-${notification.type}) - [nitpick]
CommunityLibraryStatusChipmixes Options API with Composition API /propsaftersetup— UNRESOLVED (minor, not worth blocking) - [nitpick]
getStartOfWeeklocale assumption — RESOLVED (comment updated: "Setting first day of the week to Monday") - [nitpick] Typo "precisision" — unable to locate in current code (likely resolved)
- [nitpick] Test description mismatch — unable to verify (not re-raised)
New findings
- suggestion (x1): "View more" buttons are non-functional (see inline)
- suggestion (x1): Drawer not closed when opening notifications modal (carried from prior, see inline)
- suggestion (x1): Date filters computed eagerly at setup time (carried from prior, see inline)
@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, UNRESOLVED, PARTIALLY_ADDRESSED, 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
| <KButton | ||
| :text="viewMoreAction$()" | ||
| appearance="basic-link" | ||
| /> |
There was a problem hiding this comment.
suggestion: The "View more" KButton has no @click handler — it renders but does nothing when clicked. Same in the rejection and creation notification components. If this is intentionally deferred, consider hiding the button until it's wired up, to avoid a confusing dead click for users.
| this.drawer = false; | ||
| this.showLanguageModal = true; | ||
| }, | ||
| showNotificationsModal() { |
There was a problem hiding this comment.
suggestion: Unlike openLanguageModal() (line 234) which sets this.drawer = false before opening the modal, showNotificationsModal leaves the drawer open. The notifications modal renders as a full-screen immersive page, so the drawer would be visible underneath or require the user to dismiss it separately.
| today: { | ||
| label: todayLabel$(), | ||
| params: { | ||
| date_updated__gte: new Date().toISOString().split('T')[0], |
There was a problem hiding this comment.
suggestion: The date filter values (DateFilterMap) are computed once at component setup time. If the notifications modal stays open across a day boundary, "Today" will still use the date from when the component was first created. Consider computing the date strings lazily when the filter is selected, or recomputing when the modal opens.
There was a problem hiding this comment.
I know it's an edge case, but might be worth filing a follow up issue for these values to get refreshed (similar to how we do for the ElapsedTime component in Kolibri). Exact cadence doesn't have to be hugely frequent - I just know some people keep their Studio page open in the browser for long periods (because of specific issues we've seen with the vuex and data syncing functionality that only happens in long lived sessions).
There was a problem hiding this comment.
Hey @rtibblesbot, could you please create a follow-up issue to remind us to fix it later?
3551b54 to
58a0557
Compare
rtibblesbot
left a comment
There was a problem hiding this comment.
Well-architected notifications feature. The composable-based data fetching, extensible notification type renderers, and timestamp-based unread tracking are solid design choices.
CI: Frontend tests, linting, Python unit tests, and build still in progress. Build message files and path checks passing.
Delta from prior reviews
- [blocking] Variable shadowing bug in
useFetch.js— RESOLVED (renamed tocaughtError) - [blocking]
notify_update_to_channel_editors()called beforesuper().save()for new submissions — RESOLVED (line 2935 callssuper().save()before line 2939 callsnotify_update_to_channel_editors) - [blocking]
notify_users()reads stale in-memory objects after.update()— RESOLVED (lines 703-705 refresh queryset from DB before creating change events) - [blocking] Approval/rejection components pass
notification.date_createdinstead ofnotification.date— RESOLVED (all three components now usenotification.date) - [blocking]
Greatest(NULL, value)returns NULL on PostgreSQL — RESOLVED (CoalescewrapsF()to handle NULL in bothmark_notifications_readandnotify_users) - [blocking]
NotificationFiltersreceives unusedlastReadFilterprop — RESOLVED (prop removed;lastReadis now passed viaqueryParamsto the composable) - [blocking] "View more" buttons have no click handler — RESOLVED as intentional deferral (developer confirmed: "Will implement this page later")
- [suggestion]
useFetch.jsdata.value = nullbehavior change — RESOLVED (moved inside catch block with explanatory comment) - [suggestion]
mark_notifications_readleaves in-memory field asGreatestexpression — UNRESOLVED (low risk since callsite returns HTTP 204 immediately, but fragile if method is reused) - [suggestion]
WithNotificationIndicatorscreen reader support — RESOLVED (addedvisuallyhiddenspan withnewNotificationsNotice$()text) - [suggestion]
useStoremissing guard forgetCurrentInstance— RESOLVED (throws descriptive error) - [suggestion]
notify_update_to_channel_editorson resolve doesn't exclude the resolving admin — CONTESTED (developer explained: "In the rare case that the resolver is an editor of the channel being resolved, it's okay if they get the notification, too") - [suggestion] Date filter values computed once at component creation — UNRESOLVED (developer acknowledged: "I think we can live with that"; very minor edge case)
- [suggestion]
handleNotificationsReadtimeout path leaves stale data — RESOLVED (waitForLastReadUpdateremoved entirely; flow now awaitsmarkNotificationsReaddispatch directly) - [suggestion]
resolved_by_namemay be null rendering literal "null" — RESOLVED (falls back to empty string:props.notification.resolved_by_name || '') - [suggestion] Drawer not closed when opening notifications modal — UNRESOLVED (see inline)
- [suggestion] Redundant
@clickhandlers on VTabs — RESOLVED (removed;v-modelhandles selection) - [suggestion] AppBar
showNotificationsModalmissingtrackClickanalytics — RESOLVED (line 237:this.$analytics.trackClick('general', 'Notifications')) - [suggestion]
NotificationListhas no fallback for unknown notification types — RESOLVED (line 28:|| 'div'fallback) - [suggestion] Direct mutation of submission objects from API response — RESOLVED (lines 75-79: creates new
subobject via spread before mutating) - [suggestion]
isLoadingMorestate could be stale on error path — RESOLVED (line 155:wasLoadingMorecaptured before try block) - [suggestion] Unused
pkparameter inmark_notifications_read— UNRESOLVED (minor;pk=Nonestill present but unused sincedetail=False) - [nitpick] Array index as
v-forkey — RESOLVED (key is now${notification.id}-${notification.type}) - [nitpick]
CommunityLibraryStatusChipmixes Options API with Composition API — UNRESOLVED (minor style inconsistency) - [nitpick]
getStartOfWeeklocale assumption — RESOLVED (comment updated and logic changed: "Setting first day of the week to Monday") - [nitpick] Typo "precisision" — RESOLVED (line 146 now reads "precision")
- [nitpick] Redundant
new Date()wrapping in sort — RESOLVED (line 125:b.date - a.datewithout wrapping) - [nitpick]
flaggedNotificationstring uses{ variable }with spaces — UNRESOLVED (minor i18n inconsistency) - [nitpick] Test description mismatch — unable to verify (not re-raised)
No new blocking issues found. The majority of prior findings have been addressed. The remaining unresolved items are low-risk suggestions and nitpicks.
@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, UNRESOLVED, PARTIALLY_ADDRESSED, 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
| this.drawer = false; | ||
| this.showLanguageModal = true; | ||
| }, | ||
| showNotificationsModal() { |
There was a problem hiding this comment.
suggestion: Carried from prior review — openLanguageModal() (line 234) sets this.drawer = false before opening the language modal, but showNotificationsModal does not close the drawer. Since the notifications modal is a full-screen immersive page, the drawer may remain visible underneath. Consider adding this.drawer = false; for consistency:
showNotificationsModal() {
this.drawer = false;
this.$router.push({
query: {
...this.$route.query,
modal: Modals.NOTIFICATIONS,
},
});
this.trackClick('Notifications');
},
rtibblesbot
left a comment
There was a problem hiding this comment.
Well-implemented notifications feature. The majority of prior findings have been addressed.
CI: Python tests in progress. Video demo previously verified.
Delta from prior reviews
- [blocking] Variable shadowing bug in
useFetch.js— RESOLVED (renamed tocaughtError) - [blocking]
notify_update_to_channel_editors()called beforesuper().save()for new submissions — RESOLVED (super().save()on line 2935 precedesnotify_update_to_channel_editorson line 2939) - [blocking]
notify_users()reads stale in-memory objects after.update()— RESOLVED (lines 703-705 refresh queryset from DB) - [blocking] Approval/rejection components pass
notification.date_createdinstead ofnotification.date— RESOLVED (all three components now usenotification.date) - [blocking]
Greatest(NULL, value)returns NULL on PostgreSQL — RESOLVED (CoalescewrapsF()to handle NULL) - [blocking]
NotificationFiltersreceives unusedlastReadFilterprop — RESOLVED (prop removed;lastReadpassed viaqueryParams) - [blocking] "View more" buttons have no click handler — RESOLVED as intentional deferral
- [suggestion]
useFetch.jsdata.value = nullbehavior change — RESOLVED (moved inside catch block) - [suggestion]
mark_notifications_readleaves in-memory field asGreatestexpression — UNRESOLVED (low risk since viewset returns HTTP 204 immediately, but fragile if method is reused) - [suggestion]
WithNotificationIndicatorscreen reader support — RESOLVED (addedvisuallyhiddenspan) - [suggestion]
useStoremissing guard forgetCurrentInstance— RESOLVED (throws descriptive error) - [suggestion]
notify_update_to_channel_editorson resolve doesn't exclude the resolving admin — CONTESTED (developer explained acceptable for rare edge case) - [suggestion] Date filter values computed once at component creation — UNRESOLVED (developer acknowledged as acceptable edge case)
- [suggestion]
handleNotificationsReadtimeout path leaves stale data — RESOLVED (timeout approach removed entirely) - [suggestion]
resolved_by_namemay be null rendering literal "null" — RESOLVED (falls back to empty string) - [suggestion] Drawer not closed when opening notifications modal — UNRESOLVED (see inline)
- [suggestion] Redundant
@clickhandlers on VTabs — RESOLVED - [suggestion] AppBar
showNotificationsModalmissingtrackClickanalytics — RESOLVED (line 237) - [suggestion]
NotificationListno fallback for unknown notification types — RESOLVED (|| 'div'fallback) - [suggestion] Direct mutation of submission objects from API response — RESOLVED (spread copy)
- [suggestion]
isLoadingMorestate stale on error path — RESOLVED (wasLoadingMorecaptured) - [suggestion] Unused
pkparameter inmark_notifications_read— RESOLVED (not present in method signature) - [nitpick] Array index as
v-forkey — RESOLVED (key is${notification.id}-${notification.type}) - [nitpick]
CommunityLibraryStatusChipmixes Options API with Composition API — RESOLVED (now fully<script setup>) - [nitpick]
getStartOfWeeklocale assumption — RESOLVED (Monday-based, with comment) - [nitpick] Typo "precisision" — RESOLVED
- [nitpick] Redundant
new Date()wrapping in sort — RESOLVED - [nitpick]
flaggedNotificationstring uses{ variable }with spaces — RESOLVED (uses{variable}) - [nitpick] Test description mismatch — not re-raised
No new blocking issues. The remaining unresolved items are low-risk suggestions carried from prior reviews.
@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, UNRESOLVED, PARTIALLY_ADDRESSED, 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
| query: { | ||
| ...this.$route.query, | ||
| modal: Modals.NOTIFICATIONS, | ||
| }, |
There was a problem hiding this comment.
suggestion: (Carried from prior review) The drawer isn't closed when opening the notifications modal. openLanguageModal() on line 234 sets this.drawer = false before showing its modal, but showNotificationsModal() does not. The drawer will remain visible behind the immersive modal.
showNotificationsModal() {
this.drawer = false;
this.$router.push({58a0557 to
5f5d5b0
Compare
rtibblesbot
left a comment
There was a problem hiding this comment.
Well-implemented notifications feature. The architecture — composable-based data fetching, extensible notification type renderers, timestamp-based unread tracking — remains solid. The vast majority of prior findings have been addressed.
CI: Frontend tests passing. Build, linting, and Python unit tests in progress.
Delta from prior reviews
- [blocking] Variable shadowing bug in
useFetch.js— RESOLVED (renamed tocaughtError) - [blocking]
notify_update_to_channel_editors()called beforesuper().save()for new submissions — RESOLVED (super().save()precedesnotify_update_to_channel_editors) - [blocking]
notify_users()reads stale in-memory objects after.update()— RESOLVED (refreshed queryset from DB before creating change events) - [blocking] Approval/rejection components pass
notification.date_createdinstead ofnotification.date— RESOLVED (all usenotification.date) - [blocking]
Greatest(NULL, value)returns NULL on PostgreSQL — RESOLVED (CoalescewrapsF()to handle NULL) - [blocking]
NotificationFiltersreceives unusedlastReadFilterprop — RESOLVED (prop removed) - [blocking] "View more" buttons have no click handler — RESOLVED as intentional deferral
- [suggestion]
useFetch.jsdata.value = nullbehavior change — RESOLVED (moved inside catch block) - [suggestion]
mark_notifications_readleaves in-memory field asGreatestexpression — UNRESOLVED (low risk: viewset returns HTTP 204 immediately and frontend optimistically updates session state, so the stale expression is never read) - [suggestion]
WithNotificationIndicatorscreen reader support — RESOLVED (addedvisuallyhiddenspan) - [suggestion]
useStoremissing guard forgetCurrentInstance— RESOLVED (throws descriptive error) - [suggestion]
notify_update_to_channel_editorson resolve doesn't exclude the resolving admin — CONTESTED (developer explained acceptable for rare edge case) - [suggestion] Date filter values computed once at component creation — UNRESOLVED (developer acknowledged as acceptable; see inline)
- [suggestion]
handleNotificationsReadtimeout path leaves stale data — RESOLVED (timeout approach removed entirely) - [suggestion]
resolved_by_namemay be null rendering literal "null" — RESOLVED (falls back to empty string) - [suggestion] Drawer not closed when opening notifications modal — RESOLVED (
MainNavigationDrawersetsthis.drawer = false) - [suggestion] Redundant
@clickhandlers on VTabs — RESOLVED - [suggestion] AppBar
showNotificationsModalmissingtrackClickanalytics — RESOLVED - [suggestion]
NotificationListno fallback for unknown notification types — RESOLVED (|| 'div'fallback) - [suggestion] Direct mutation of submission objects from API response — RESOLVED (spread copy)
- [suggestion]
isLoadingMorestate stale on error path — RESOLVED (wasLoadingMorecaptured) - [suggestion] Unused
pkparameter inmark_notifications_read— RESOLVED - [nitpick] Array index as
v-forkey — RESOLVED - [nitpick]
CommunityLibraryStatusChipmixes Options API with Composition API — RESOLVED (now fully<script setup>) - [nitpick]
getStartOfWeeklocale assumption — RESOLVED (Monday-based, with comment) - [nitpick] Typo "precisision" — RESOLVED
- [nitpick] Redundant
new Date()wrapping in sort — RESOLVED - [nitpick]
flaggedNotificationstring uses{ variable }with spaces — RESOLVED - [nitpick] Test description mismatch — not re-raised
New findings
- suggestion (x1):
useFetchisFinishednever set totrueon error — affects existing consumers (see inline) - suggestion (x1):
useFetchcomment contradicts code in catch block (see inline)
No new blocking issues. The remaining unresolved items are low-risk suggestions from prior reviews.
@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, UNRESOLVED, PARTIALLY_ADDRESSED, 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
5f5d5b0 to
4f7569e
Compare
rtibblesbot
left a comment
There was a problem hiding this comment.
Well-implemented notifications feature. All prior blocking issues have been addressed.
CI: Python tests pending. Video demo previously verified across multiple reviews.
~28 prior finding(s) resolved.
Carried-forward unresolved:
- [suggestion]
mark_notifications_readleaves in-memory field asGreatestexpression — UNRESOLVED (low risk: viewset returns HTTP 204 immediately; fragile only if method is reused elsewhere) - [suggestion] Date filter values computed once at component setup — UNRESOLVED (developer acknowledged as acceptable edge case)
- [suggestion]
notify_update_to_channel_editorson resolve doesn't exclude resolving admin — CONTESTED (developer provided rationale: acceptable for rare edge case where resolver is also a channel editor)
No new blocking issues found.
@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, UNRESOLVED, PARTIALLY_ADDRESSED, 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
rtibbles
left a comment
There was a problem hiding this comment.
Some questions and comments, nothing blocking.
|
|
||
| /** | ||
| * @typedef {Object} UseFilterReturn | ||
| * @property {import('vue').ComputedRef<string|undefined>} filter Reactive settable filter value. |
There was a problem hiding this comment.
Nice that the effort of turning this into a composable from the previous mixin now means that it is also more broadly useful across the app!
| /** | ||
| * @typedef {Object} UseFilterReturn | ||
| * @property {import('vue').ComputedRef<string|undefined>} filter Reactive settable filter value. | ||
| * @property {import('vue').ComputedRef<{ |
There was a problem hiding this comment.
Nice that the effort of turning this into a composable from a mixin, now means that we can reuse this in other parts of the app too!
| </template> | ||
|
|
||
|
|
||
| <script setup> |
|
|
||
| <script setup> | ||
|
|
||
| import Chip from './Chip.vue'; |
There was a problem hiding this comment.
How does registering components work with script setup? You just import it and it knows?
There was a problem hiding this comment.
Yes, it's beautiful!
| import { CommunityLibraryStatus } from 'shared/constants'; | ||
|
|
||
| export default { | ||
| name: 'CommunityLibraryStatusChip', |
There was a problem hiding this comment.
Presumably the name is now just auto derived from the filename?
There was a problem hiding this comment.
Yes. Although it had some issues in Vue test utils if we wanted to do something like wrapper.findComponent({ name: 'NotificationsFilter' }), but the name appears correctly in the Vue browser plugin.
There was a problem hiding this comment.
Good job we're switching over to Vue Testing Library then!
| <KButton | ||
| :text="viewMoreAction$()" | ||
| appearance="basic-link" | ||
| /> |
| today: { | ||
| label: todayLabel$(), | ||
| params: { | ||
| date_updated__gte: new Date().toISOString().split('T')[0], |
There was a problem hiding this comment.
I know it's an edge case, but might be worth filing a follow up issue for these values to get refreshed (similar to how we do for the ElapsedTime component in Kolibri). Exact cadence doesn't have to be hugely frequent - I just know some people keep their Studio page open in the browser for long periods (because of specific issues we've seen with the vuex and data syncing functionality that only happens in long lived sessions).
| return json_for_parse_from_data(None) | ||
|
|
||
| user_data = {field: getattr(user, field) for field in user_fields} | ||
| user_data = {} |
There was a problem hiding this comment.
Not a blocker, and it was kind of like this when you got here, but using the DjangoJSONEncoder class might clean this up a little:
https://docs.djangoproject.com/en/3.2/topics/serialization/#djangojsonencoder
There was a problem hiding this comment.
Something like this? f0b08e3. Just tried it, and it seems to be working fine
| FILE, | ||
| INVITATION, | ||
| USER, | ||
| SESSION, |
There was a problem hiding this comment.
What viewset does this map to? Not sure I'm completely understanding the data flow here.
rtibblesbot
left a comment
There was a problem hiding this comment.
Well-implemented notifications feature. All prior blocking issues resolved.
CI: JS tests, linting, build passing. Python tests in progress.
~30 prior finding(s) resolved.
Carried-forward:
- [suggestion]
mark_notifications_readleaves in-memory field asGreatestexpression — UNRESOLVED (low risk: viewset returns HTTP 204 immediately and the instance is discarded) - [suggestion] Date filter values computed once at component setup — CONTESTED (maintainer acknowledged as edge case, requested follow-up issue be filed)
- [suggestion]
notify_update_to_channel_editorson resolve doesn't exclude resolving admin — CONTESTED (developer explained, maintainer did not re-raise) - [suggestion]
useFetchisFinishednever set totrueon error — CONTESTED (developer confirmed intentional behavior)
No new blocking issues. Recent commits (DjangoJSONEncoder in _json_dumps, SESSION comment in ALL_TABLES) are maintainer-requested changes and look correct.
@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, UNRESOLVED, PARTIALLY_ADDRESSED, 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
rtibblesbot
left a comment
There was a problem hiding this comment.
Well-implemented notifications feature. All prior blocking issues resolved; recent commits address @rtibbles' feedback (DjangoJSONEncoder, SESSION comment).
CI passing.
~30 prior finding(s) resolved.
Carried-forward unresolved:
- [suggestion]
mark_notifications_readleaves in-memory field asGreatestexpression — UNRESOLVED (low risk: viewset returns HTTP 204 immediately and the instance is discarded; fragile only if method is reused elsewhere) - [suggestion] Date filter values computed once at component setup — UNRESOLVED (developer acknowledged as acceptable; @rtibbles asked for a follow-up issue to refresh values in long-lived sessions — developer requested bot file it)
Carried-forward contested:
- [suggestion]
notify_update_to_channel_editorson resolve doesn't exclude resolving admin — CONTESTED (developer provided rationale, accepted by team)
No new blocking issues found.
@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, UNRESOLVED, PARTIALLY_ADDRESSED, 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
Summary
Modal=NOTIFICATIONSquery param is set in the route. Since this modal will be shown across the entire application, I chose this option rather than creating a new route for this page on all app routers.Usermodel:last_read_notification_dateandnewest_notification_date, the first representing the date of the last notification read by the user, the second the date of the most recent notification. If newest_notification_date > last_read_notification_date, the user has a new notification :).useFilter,useFetch,useKeywordSearch, anduseQueryParamsto the shared folder.useFiltercomposable to comply with the current KSelect API.useSnackbaras a wrapper of the current Vuex Snackbar module, until this gets migrated.useStoreas sugar syntax to get the store from the currentInstance.useCommunityLibraryUpdatescomposable to manage the data fetching and transformation of submissions into updates/notifications.Grabacion.de.pantalla.2025-12-17.a.la.s.10.41.42.a.m.mov
References
Closes #5457
Closes #5458
Reviewer guidance
notificationspage on the profile menu in the Appbar or by opening the main menu.admin_communitylibrary_submission/{id}/resolveendpoint.Tech debt introduced
Right now, notification filter selections don't close automatically when clicking outside the select input. This is a bug due to some weird interaction of. There is now aVDialogandPopper, I did not want to spend too much time trying to solve this because we will end up replacing Vuetify in Studio anyways, but noting that this bug will be present until then. We can coordinate later if we can prioritize migrating theImmersivePageinto Studio before releasing the Community Library, or if we prefer spending more time trying to fix this bugStudioImmersivePage! (Thanks @MisRob 😄 )