Skip to content

feat: add a settings menu to control Pomodoro, Heatmap, Themes - #71

Open
Punnawit9285 wants to merge 23 commits into
masterfrom
settings
Open

Punnawit9285 wants to merge 23 commits into
masterfrom
settings

Conversation

@Punnawit9285

@Punnawit9285 Punnawit9285 commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Adds a Settings sheet for choosing which widgets show, the typeface and the Pomodoro options on each device, and reworks the Pomodoro timer.

Screen.Recording.2569-10-08.at.11.05.39.mov
  • Pomodoro options: (Supersedes fix: Pomodoro timer no longer advances while the page is closed #66.)
    • The hour the session count resets each day (4 AM by default).
    • Whether leaving the tab pauses the timer ("Flick only") or not ("Studying elsewhere").
    • Turning the timer off also stops it.
    • It's now on the home page too, with one shared state across pages, and it has a new "Long Break After" setting (1–10 sessions).

All concerns raised by @ittipatken in #66 have been completely resolved by this. Thanks for the suggestions kub! I think it would be much nicer for users to use.

image
  • Settings sheet: opens from a toolbar button on the home, year and course pages. Settings are saved locally and applied before the app starts, so the defaults never flash first.

  • Show or hide widgets: switches for the Pomodoro timer, the study heatmap, the activity heatmap on the home page and the theme button. A switch only appears when its widget is in the build, so the sheet works with or without the themes and heatmap PRs. The Pomodoro timer and the study heatmap start switched off; the activity heatmap from master stays on until the user hides it.

  • Activity heatmap period: the heatmap on the home page can cover the last year, 6 months, month or 7 days. The 7 days stay the default and are still drawn by the hour; a longer period is drawn a day a square, a column a week, and the card's totals follow it.

  • Typeface: a dropdown of 10 fonts for user to choose

  • Restore default settings: resets every feature that has preferences, through RestoreDefaultsService.

Merging: this can be merged alone or with the themes PR, in either order. The one file they share, restore-defaults.service.ts, is identical on both branches, and where they edit the same page they change different lines.

Settings stay on the device that set them: a phone and a desktop are
allowed to differ, so the storage key carries no account id and nothing
is sent anywhere.

Which widgets exist is discovered rather than declared. Each entry in the
registry names the elements that would prove its feature is in this build,
and only the ones actually found are offered, so the list can name a
feature that has not been merged yet and start working on its own once it
is. Switched-off widgets are stored as the keys to hide, so a widget added
by a later release is visible by default and needs no migration.

Fonts are applied through Ionic's own --ion-font-family, and the
downloadable faces are fetched only once chosen.
The hiding rules name elements this stylesheet does not own. One that has
not shipped yet simply matches nothing, so the file is safe to carry ahead
of the features it controls. Hiding with CSS rather than dropping the
element also keeps a switched-off widget in the page, which is what lets
it go on being discovered and so be switched back on.

The same file restates the font family after Ionic's iOS rule, which sets
the `font` shorthand from -apple-system-body and would otherwise discard a
chosen face on iPhone, and carries the size correction for families that
render much smaller than the rest.

The boot script applies both before Angular starts, so neither a hidden
widget nor the default font flashes on the way in.
Each section appears only once it has something to control, so the sheet
grows by itself as features land rather than showing switches that do
nothing.

The font picker draws every name in its own face. That needs the
downloadable ones present, so opening the sheet asks for them in a single
request cut down to the handful of characters the picker actually draws;
the full family follows only when one is chosen.
The count was kept for the life of the install, so a morning began wherever
the last one left off. It now belongs to a day that starts at an hour the
reader picks, four in the morning by default, late enough that a night's
work counts as one sitting.

The day is checked once per tick against a precomputed boundary, so
nothing new is scheduled, and crossing it mid-session leaves the running
phase alone. The day itself is kept with the settings rather than with the
timer state, so resetting the timer does not persuade the app that a new
day has begun.

Switching the widget off now stops the timer. It runs from the root
injector whether or not it is on screen, so hiding it alone would have
left it counting.
Only the switch itself answered a tap, so aiming at the row — the obvious
target, and the whole width of it — did nothing at all. The label now sits
inside the toggle, which is how Ionic 8 expects it and which makes the
entire row the control.

The switch also sat flush against the card's edge, because the item's end
padding had been zeroed. It is left alone now, so the switch keeps its
margin.

The font was a column of eleven sample buttons, which ran to 647px and
pushed everything else off the screen. It is a dropdown now, with a single
line underneath drawn in the chosen face so the fit can still be judged.
The section is 127px.
The button lived only on the home page, while the one widget it can switch
off lives on a course page. Turning the timer off therefore changed nothing
the reader could see, which made the whole sheet look inert.

It now sits on the course and year toolbars as well, so a switch takes
effect in front of whoever threw it.
Ionic's popover copies an option's classes onto its row but not its
styles, so every row now takes its face from a rule keyed by that class,
built from the font list. Default shows Ionic's own font rather than the
one currently chosen.

Also rename "Start over" to "Restore default settings", and stop the
iOS toolbar title running under the toolbar buttons on phones.
The timer can sit on more than one page, so its switch in the settings
sheet says what it is rather than where it is.
The settings sheet now announces the restore through
RestoreDefaultsService, and each feature that keeps preferences puts its
own back. The sheet's settings listen like any other, so the button
needs no knowledge of which features a build contains.

The service is shared byte for byte with the themes branch, where the
theme listens too, so the two merge cleanly.
The sheet stops at 90% of the screen, but its content was laid out for
the full height, so on a short screen the Restore default settings
button could never be scrolled into view. With expandToScroll off the
content scrolls within the sheet at any height.
Titles, hints, rows and the font sample each started at a different
distance from the edge, and the rows floated with no card behind them.
Everything now starts at one inset: titles and hints line up with the
rows on a card drawn from the theme's step colour, the font sample sits
inside its card, and the note about the day's reset sits under the
"Day starts at" row. The Typeface and reset-hour menus open from the
row's right edge towards the sheet instead of hanging over its edge.
Session type (Watching lectures / Studying elsewhere) joins the Pomodoro
section, with a line saying what the chosen one does, and Restore
default settings puts it back to Watching lectures.

The choice lives in PomodoroSessionModeService, shared byte for byte
with the Pomodoro branch, which is where the timer that acts on it is.
Until a timer that can pause has run on the device, the sheet leaves
the control out rather than offer a setting that does nothing.
Each widget arrives in its own pull request, and the switches only work
while settings.scss still has a rule for every element the sheet
offers. The test build now loads that stylesheet, and a test switches
each widget off and on and checks its element really hides and comes
back. Another checks that switching the Pomodoro off pauses a running
timer.
…age playback

- show Pomodoro timer on the homepage with synchronized state across pages
- keep video playback in-page (playsinline, preferFullWindow) so notifications overlay fullscreen
- add fullscreen toast notifications on phase transitions
- split Pomodoro timer and info popover into template, style and component files
- configure study sessions before long break
- accurately track background tab countdown and pause timer when closed
- change 'Watching lectures' label to 'Flick only'
- update hint text to clarify that only time spent on Flick counts
- save study, break and long-break minutes and sessions before a long break to FlickMan's v1/user_settings
- on sign-in take on what another device saved, or share this device's when nothing is saved yet
- send a change about 2 seconds after it is made; the reset hour, session mode and a running timer stay per device
- wait for the ID token before the first read, so sync is not switched off while sign-in finishes
@ittipatken

Copy link
Copy Markdown
Member

I suggest the features should be opt-in (turn off by default) to avoid cluttering the interface.

- hide both widgets on a device with no saved settings, and again after Restore default settings
- apply the same default in the boot script so neither flashes in before Angular starts
- keep the theme button on by default; a saved choice still wins
Ionic 9 (#74) dropped @ionic/angular/standalone, so the settings
menu and sheet now import from @ionic/angular like the rest of
the app.
FlickMan won't store user settings (docchula/FlickMan#23 closed), so drop the sync:
- remove UserSyncService and PomodoroSyncService with their specs
- remove ManService's v1/user_settings calls and the REST post() they needed
- remove the preference events and applyPreferences() that only sync used
@Punnawit9285

Copy link
Copy Markdown
Member Author
  • All settings now stay on each device, including the Pomodoro lengths and the sessions before a long break.
  • Removed UserSyncService, PomodoroSyncService and the v1/user_settings calls. This PR no longer changes main.ts or man.service.ts.

Everything is now on local and doesn't require FlickMan anymore

Master's activity card on the home page (`ion-card.play-stats-card`, the
last 7 days of viewing) gets its own switch, Activity heatmap. It stays on
until the reader hides it, since it is part of master rather than of this
pull request. The widget tests now build elements from selectors with
classes as well as tag names.
Settings gets an Activity heatmap section with a Period: last year, last 6
months, last month or the last 7 days. The 7 days stay the default and are
still drawn by the hour; a longer period is drawn a day a square, a column a
week under the month it starts, from the same daily data as the activity
window. The card's heading and totals follow the period.
@Punnawit9285

Copy link
Copy Markdown
Member Author
image

This will make user able to choose between year / 6 month / 1 month / 1 week heatmap to be displayed too

@ittipatken

ittipatken commented Oct 11, 2026 •

Copy link
Copy Markdown
Member

This pull request has become too complex to review with over 2000 lines of code and 39 files changed. I suggest breaking this pull request into separate pull requests for each feature, starting with ability to just turn on/off.

@keenthekeen

keenthekeen commented Oct 11, 2026 •

Copy link
Copy Markdown
Member

I think this adds too much complexity for this app. If someone wants advanced timer options, a dedicated Pomodoro app will serve them better. The heatmap and the app font also doesn't need to be customizable. I'll accept a bug fix for the timer though.

The Pomodoro timer has 11 users this month (10 days).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants