Notechondria

Version: 0.1.71 Build Date: 2026-04-26T16:00

What's Changed

Bug — local courses pulled public notes that didn't belong to them

  • User-reported: "when user in offline mode and create offline categories, the public notes will still pop. Public notes only fit for online category and should never appear on local user-created category". Root cause: _loadLearnerNotes in editor_app/lib/core/note_loading.dart always called widget.client.listNotes(...) regardless of which category was selected. When the active course had a negative id (locally-created, not yet synced) the call still went out — with courseId=null because the backend can't filter by a negative id — and the server returned a generic public-notes feed that got rendered under the user's offline category.

  • Fix: short-circuit _loadLearnerNotes when the selected category id is negative or when the explicit filter scope is local. Both paths clear _learnerNotes, set _hasMoreLearnerNotes=false, and return without hitting the network.

Note-state filter — 4-option dropdown replaces the old checkbox

  • Spec: "create a drop down (local notes, personal notes, public notes, private notes) allow user to filter their own notes, default set to show personal notes (include private and public)". The previous "Include public notes from other users" checkbox is gone. New DropdownButtonFormField lives at the top of the learner page with four options: Personal (default, private + public own notes), Private (own private), Public (own public), Local drafts only.

  • Backend notes/api.py extended to honor scope=private and scope=public (own + visibility filter). scope=all and the default scope=personal are unchanged for back-compat.

  • When the user opens a locally-created (negative-id) category the dropdown is force-pinned to "Local" and disabled, with an info-icon tooltip explaining why ("Local categories only contain local drafts. Switch to a synced category to filter cloud notes.").

Local-note search — entirely client-side

  • Spec: "Implement search function for local notes over frontend." _visibleLocalDrafts already used _localSearchScore; this round adds course-scoped local drafts: when a category is selected, build_helpers.dart passes _localNotesForCourse(_selectedCourse) to _LearnerPage instead of all _localDrafts. The local search now operates on the right list — drafts that belong to the current category — and respects the new "Local drafts only" filter.

Visual state distinction for notes

  • Spec: "Make clear distinction between the states of the notes." New _NoteStateBadge pill widget renders inline next to each note title, with icon + colored background pulled from the colorScheme:
    • Local draft: cloud_off_outlined icon, tertiaryContainer background.
    • Public: public icon, primaryContainer background.
    • Private: lock_outline icon, surfaceContainerHighest background. Replaces the previous single-line text concatenation (Local draft | course | timestamp) with a clearer at-a-glance signal.

Live-markdown editor — padding fix

  • Spec: "Remove the extra container border for live markdown editor, rendering have too much padding on left and right make the horizontal view too narrow." The wrapping Card around _buildInlineLiveMarkdownBody is gone; the inner SingleChildScrollView's horizontal padding drops from 20 to 4. The dialog already provides a 20px outer gutter, so the old total of 40+px on each side made the rendered view feel cramped on phones.

Files Changed

  • frontend/editor_app/lib/core/note_loading.dart_loadLearnerNotes short-circuit for local-course / scope=local.
  • frontend/editor_app/lib/core/build_helpers.dart — pass _localNotesForCourse(_selectedCourse) to _LearnerPage; pass new isLocalCourseSelected prop.
  • frontend/editor_app/lib/modules/learner.dart — replace checkbox with 4-option dropdown; _NoteStateBadge widget; _searchHint / _cloudSectionLabel / _emptyCloudCopy / _buildScopeItems helpers; visibility logic for the cloud section keyed off effectiveScope.
  • frontend/editor_app/lib/modules/note_editor.dart_buildLiveMarkdownEditor returns the body directly (no Card wrapper); inner padding 20 → 4.
  • backend/notes/api.pyscope=private and scope=public branches added to the list endpoint, with comments explaining the new semantics.

Notes

  • Backend scope=all is preserved for back-compat. The frontend doesn't surface it in the new dropdown, but third-party clients (and an older frontend mid-rollout) can still request it.
  • Round-trip behavior: switching from a local category to a synced one re-issues _loadLearnerNotes with the previous scope; switching back to a local category resets the cloud list to empty as documented above.