Daily audio: subscription-driven Today view; stop client auto-regeneration#1152
Merged
Conversation
…ation
Drive the Today view off the new per-(user,language) daily-audio subscription
state (subscription_status / awaiting_engagement / next_lesson_date) returned by
get_todays_lesson, instead of localStorage guesswork.
- Remove the auto-generate effect + localStorage flags that could silently
regenerate a lesson when landing on an empty Today (e.g. after finishing an
older/paused lesson). The backend cron is now the source of truth for whether
a lesson should exist; the client only generates on first-ever setup.
- Label the in-flight progress screen from the saved subscription type/subject
(fixes cron-generated lessons being mislabeled "Three of Your Study Words").
- Status box shows real state: "next lesson <date>", awaiting-engagement
("finish this to get the next"), off, and a turn-off control.
- Reconfiguring no longer regenerates today's lesson; it applies from the next
scheduled lesson.
Requires the matching API PR (new response fields + set_daily_subscription_enabled).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
✅ Deploy Preview for voluble-nougat-015dd1 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Use the API's #643 `paused` (waiting lesson the learner hasn't engaged with) for the badge + "listen to this one and they'll start again" copy, alongside the new subscription_status/next_lesson_date. A paused lesson arrives as lessonData, so the empty-Today path no longer needs an awaiting-engagement branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tasks
…tate When subscribed but no lesson exists for today (cron miss / first day / timezone), offer an explicit generate button instead of a possibly-wrong "next lesson <date>" — the API now returns next_lesson_date=null for this case. Replaces the silent auto-generation that caused live regeneration. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Migrate useDailyLessonPreference off /save_user_preferences for daily-audio config — now uses /daily_subscription (GET) + /configure_daily_subscription (POST), which talk to DailyAudioSubscription directly. Removes the dependency on the legacy daily_audio_lesson_type_<lang> / _suggestion_<lang> preference keys; those are still mirrored for one release for any client that hasn't updated yet (legacy compat block in the API, marked for ~2026-06-05 removal). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pair with the API's new `language` parameter on the daily-subscription and get_todays_lesson endpoints. Send the lang the UI is currently displaying so a fast language switch can't write the new topic / turn off / fetch today's lesson against the wrong language while the server's user.learned_language is still catching up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the user doesn't have enough study words yet, the Vocabulary pill greys out silently — leaving people guessing. Add a small inline note under the pill row when autoDisabled is true, plus a title attr on the pill itself. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… the gap The pill no longer greys out when the user is short on study words — they can still select it. The inline note explains the prerequisite, and if they save anyway the existing "not enough words" error path takes over (first-setup gen fails with an actionable message; the cron silently skips until words appear, matching the empty-Today recovery flow). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ontrast White on the brand amber was ~1.9:1 (well below WCAG AA's 4.5:1) and unreadable in dark mode. Near-black on amber is ~11:1. Brand orange is the same in light and dark mode, so a single text color works for both. Bumped font-weight to 600 while there. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ulary tab It was showing whenever Vocabulary was disabled, even when Topic/Situation was the active pill — confusing context. Gate it on suggestionType === "auto" so it only appears alongside the pill it explains. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… a notice
Reorder: description first ("what it is"), then the note ("why it's not ready
yet") — they read as a small contextual unit. Drop the floating italic and
replace with a left amber accent so it reads as a notice tied to the pill,
visually consistent with the brand color used on the Save button.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pairs with API change that adds language_code to the progress record. The mount adopt path skips the in-flight progress when it's for another language and falls through to the normal getTodaysLesson. The poll path stops polling if it sees a non-matching progress (we're watching the wrong generation). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- #1 Polling no longer dies when dailyType resolves mid-generation. Read dailyType/dailySuggestion via refs in pollForProgress and drop them from the effect's deps; stopPolling's setIsGenerating(false) was being called by the deps-change cleanup, which then made the re-run return early. - #2 turn-on/turn-off failures use toast.error so the user actually sees them (setError was invisible from the episode-card render). Add a toast.success for confirmation too. - #3 alreadySubscribed also treats subscription_status === "active" as subscribed, closing the race where the dialog opened before the hook had loaded dailyType for an existing active subscriber. - #4 Document the getTodaysLesson callback contract (always an object; check lesson_id) so the next caller can't write `if (!data)`. - #5 formatNextLessonDate returns null for past dates instead of "today" — a backend regression emitting yesterday no longer reads as "today". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
setDailySubscriptionEnabled + configureDailySubscription had a near-identical fetch / response.ok / .json() / .then(callback) / .catch(Sentry+onError) block. Pulled into a module-local postForm(api, path, formData, callback, onError, tag) so each endpoint client is its FormData + a single helper call, and the next endpoint (or a change to the error-handling shape) is one place to touch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Drives the Today view off the new first-class daily-audio subscription state instead of localStorage guesswork, and fixes two bugs along the way.
useEffect+localStorageflags that silently regenerated a lesson whenever Today was shown empty (e.g. after finishing an older/paused lesson, or navigating back). The backend cron is now the source of truth for whether a lesson should exist; the client only generates on first-ever setup.TodayEpisodeCard): "next lesson<date>", awaiting-engagement ("finish this to get the next"), turned-off state, and a turn-off control — replacing the phantomlessonData.pausedflag and the hardcoded "tomorrow" copy.Dependency
Requires the matching API PR (adds
subscription_status/awaiting_engagement/next_lesson_datetoget_todays_lesson, and theset_daily_subscription_enabledendpoint). Merge/deploy API first.Test plan
zeeguu_*localStorage keys are written (DevTools → Application).🤖 Generated with Claude Code