Skip to content

Commit 737bf0f

Browse files
authored
feat: configure max session length (#203)
* feat: add max session duration config * fix: don’t use hardcoded max value
1 parent f747dfc commit 737bf0f

File tree

7 files changed

+103
-31
lines changed

7 files changed

+103
-31
lines changed

main.css

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,27 @@ input[type="range"]::-webkit-slider-thumb {
565565
text-align: left;
566566
}
567567

568+
.setting-time-input-wrapper {
569+
appearance: none;
570+
-webkit-appearance: none;
571+
background: var(--color-background-light);
572+
padding: 0.3em;
573+
border: 0.56vw solid var(--color-background-lightest);
574+
border-radius: 1.1vw;
575+
}
576+
577+
.setting-time-input {
578+
background-color: var(--color-background-light);
579+
display: inline-block;
580+
font-family: "RobotoMono", monospace;
581+
font-size: 3.3vw;
582+
color: var(--color-foreground);
583+
border: none;
584+
}
585+
.setting-time-input:focus {
586+
outline: none;
587+
}
588+
568589
#about h2 {
569590
color: var(--color-short-round);
570591
font-weight: 400;

src-elm/Json.elm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ configEncoder config =
119119
)
120120
, ( "longBreakDuration", Encode.int config.longBreakDuration )
121121
, ( "maxRoundNumber", Encode.int config.maxRoundNumber )
122+
, ( "maxSessionDuration", Encode.int config.maxSessionDuration )
122123
, ( "minimizeToTray", Encode.bool config.minimizeToTray )
123124
, ( "minimizeToTrayOnClose", Encode.bool config.minimizeToTrayOnClose )
124125
, ( "muted", Encode.bool config.muted )
@@ -153,6 +154,7 @@ configDecoder =
153154
|> Pipe.optional "long_break_audio" (Decode.maybe Decode.string) Nothing
154155
|> Pipe.required "long_break_duration" Decode.int
155156
|> Pipe.required "max_round_number" Decode.int
157+
|> Pipe.required "max_session_duration" Decode.int
156158
|> Pipe.required "minimize_to_tray" Decode.bool
157159
|> Pipe.required "minimize_to_tray_on_close" Decode.bool
158160
|> Pipe.required "muted" Decode.bool

src-elm/Main.elm

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ type alias Flags =
5656
, desktopNotifications : Bool
5757
, longBreakDuration : Seconds
5858
, maxRoundNumber : Int
59+
, maxSessionDuration : Seconds
5960
, minimizeToTray : Bool
6061
, minimizeToTrayOnClose : Bool
6162
, muted : Bool
@@ -113,6 +114,7 @@ init flags =
113114
, longBreakAudio = Nothing
114115
, longBreakDuration = flags.longBreakDuration
115116
, maxRoundNumber = flags.maxRoundNumber
117+
, maxSessionDuration = flags.maxSessionDuration
116118
, minimizeToTray = flags.minimizeToTray
117119
, minimizeToTrayOnClose = flags.minimizeToTrayOnClose
118120
, muted = flags.muted
@@ -630,7 +632,7 @@ update msg ({ config } as model) =
630632
{ config | autoQuit = sessionTypeFromString sessionTypeString }
631633

632634
FocusTime value ->
633-
{ config | focusDuration = min (90 * 60) (toInt value * 60) }
635+
{ config | focusDuration = min (config.maxSessionDuration * 60) (toInt value * 60) }
634636

635637
Label sessionType label ->
636638
case sessionType of
@@ -644,13 +646,23 @@ update msg ({ config } as model) =
644646
{ config | defaultLongBreakLabel = label }
645647

646648
LongBreakTime value ->
647-
{ config | longBreakDuration = min (90 * 60) (toInt value * 60) }
649+
{ config | longBreakDuration = min (config.maxSessionDuration * 60) (toInt value * 60) }
650+
651+
MaxSessionDuration value ->
652+
{ config | maxSessionDuration = toInt value * 60 }
648653

649654
Rounds value ->
650655
{ config | maxRoundNumber = min 12 (toInt value) }
651656

657+
SaveMaxSessionDuration ->
658+
{ config
659+
| focusDuration = min config.focusDuration config.maxSessionDuration
660+
, longBreakDuration = min config.longBreakDuration config.maxSessionDuration
661+
, shortBreakDuration = min config.shortBreakDuration config.maxSessionDuration
662+
}
663+
652664
ShortBreakTime value ->
653-
{ config | shortBreakDuration = min (90 * 60) (toInt value * 60) }
665+
{ config | shortBreakDuration = min (config.maxSessionDuration * 60) (toInt value * 60) }
654666

655667
Toggle value ->
656668
case value of

src-elm/Types.elm

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ type alias Config =
102102
, longBreakAudio : Maybe String
103103
, longBreakDuration : Seconds
104104
, maxRoundNumber : Int
105+
, maxSessionDuration : Seconds
105106
, minimizeToTray : Bool
106107
, minimizeToTrayOnClose : Bool
107108
, muted : Bool
@@ -188,7 +189,9 @@ type SettingType
188189
| FocusTime String
189190
| Label SessionType String
190191
| LongBreakTime String
192+
| MaxSessionDuration String
191193
| Rounds String
194+
| SaveMaxSessionDuration
192195
| ShortBreakTime String
193196
| Toggle Setting
194197

src-elm/View/Drawer.elm

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ module View.Drawer exposing (drawerView)
22

33
import Html exposing (Html, a, div, h2, input, option, p, section, select, span, text)
44
import Html.Attributes exposing (class, href, id, selected, style, target, title, type_, value)
5-
import Html.Events exposing (onClick, onInput)
5+
import Html.Events exposing (onBlur, onClick, onInput)
66
import ListWithCurrent
77
import Svg exposing (path, svg)
88
import Svg.Attributes as SvgAttr
99
import Types exposing (Model, Msg(..), SessionType(..), Setting(..), SettingTab(..), SettingType(..))
1010

1111

12-
settingWrapper : String -> Msg -> Bool -> Html Msg
13-
settingWrapper title msg settingActive =
12+
settingWrapperToggle : String -> Msg -> Bool -> Html Msg
13+
settingWrapperToggle title msg settingActive =
1414
div
1515
[ class "setting-wrapper"
1616
, onClick msg
@@ -366,7 +366,8 @@ timerSettingView model =
366366
[ type_ "text"
367367
, class "setting-input"
368368
, Html.Attributes.min "1"
369-
, Html.Attributes.max "90"
369+
, Html.Attributes.step "1"
370+
, Html.Attributes.max <| String.fromInt (model.config.maxSessionDuration // 60)
370371
, value <| String.fromFloat (toFloat model.config.focusDuration / 60)
371372
, onInput <| FocusTime >> UpdateSetting
372373
, style "width" <| String.fromInt (String.length <| String.fromFloat (toFloat model.config.focusDuration / 60)) ++ "ch"
@@ -380,7 +381,7 @@ timerSettingView model =
380381
[ input
381382
[ type_ "range"
382383
, Html.Attributes.min "1"
383-
, Html.Attributes.max "90"
384+
, Html.Attributes.max <| String.fromInt (model.config.maxSessionDuration // 60)
384385
, Html.Attributes.step "1"
385386
, class "slider slider--red"
386387
, value <| String.fromFloat (toFloat model.config.focusDuration / 60)
@@ -389,7 +390,7 @@ timerSettingView model =
389390
[]
390391
, div
391392
[ class "slider-bar slider-bar--red"
392-
, style "width" (String.fromFloat ((100 * (toFloat model.config.focusDuration / 60) / 90) - 0.5) ++ "%")
393+
, style "width" (String.fromFloat ((100 * (toFloat model.config.focusDuration / 60) / (toFloat model.config.maxSessionDuration / 60)) - 0.5) ++ "%")
393394
]
394395
[]
395396
]
@@ -408,7 +409,7 @@ timerSettingView model =
408409
[ type_ "text"
409410
, class "setting-input"
410411
, Html.Attributes.min "1"
411-
, Html.Attributes.max "90"
412+
, Html.Attributes.max <| String.fromInt (model.config.maxSessionDuration // 60)
412413
, value <| String.fromFloat (toFloat model.config.shortBreakDuration / 60)
413414
, onInput <| ShortBreakTime >> UpdateSetting
414415
, style "width" <| String.fromInt (String.length <| String.fromFloat (toFloat model.config.shortBreakDuration / 60)) ++ "ch"
@@ -422,7 +423,7 @@ timerSettingView model =
422423
[ input
423424
[ type_ "range"
424425
, Html.Attributes.min "1"
425-
, Html.Attributes.max "90"
426+
, Html.Attributes.max <| String.fromInt (model.config.maxSessionDuration // 60)
426427
, Html.Attributes.step "1"
427428
, class "slider slider--green"
428429
, value <| String.fromFloat (toFloat model.config.shortBreakDuration / 60)
@@ -431,7 +432,7 @@ timerSettingView model =
431432
[]
432433
, div
433434
[ class "slider-bar slider-bar--green"
434-
, style "width" (String.fromFloat ((100 * (toFloat model.config.shortBreakDuration / 60) / 90) - 0.5) ++ "%")
435+
, style "width" (String.fromFloat ((100 * (toFloat model.config.shortBreakDuration / 60) / (toFloat model.config.maxSessionDuration / 60)) - 0.5) ++ "%")
435436
]
436437
[]
437438
]
@@ -450,7 +451,7 @@ timerSettingView model =
450451
[ type_ "text"
451452
, class "setting-input"
452453
, Html.Attributes.min "1"
453-
, Html.Attributes.max "90"
454+
, Html.Attributes.max <| String.fromInt (model.config.maxSessionDuration // 60)
454455
, value <| String.fromFloat (toFloat model.config.longBreakDuration / 60)
455456
, onInput <| LongBreakTime >> UpdateSetting
456457
, style "width" <| String.fromInt (String.length <| String.fromFloat (toFloat model.config.longBreakDuration / 60)) ++ "ch"
@@ -464,7 +465,7 @@ timerSettingView model =
464465
[ input
465466
[ type_ "range"
466467
, Html.Attributes.min "1"
467-
, Html.Attributes.max "90"
468+
, Html.Attributes.max <| String.fromInt (model.config.maxSessionDuration // 60)
468469
, Html.Attributes.step "1"
469470
, class "slider slider--blue"
470471
, value <| String.fromFloat (toFloat model.config.longBreakDuration / 60)
@@ -473,7 +474,7 @@ timerSettingView model =
473474
[]
474475
, div
475476
[ class "slider-bar slider-bar--blue"
476-
, style "width" (String.fromFloat ((100 * (toFloat model.config.longBreakDuration / 60) / 90) - 0.5) ++ "%")
477+
, style "width" (String.fromFloat ((100 * (toFloat model.config.longBreakDuration / 60) / (toFloat model.config.maxSessionDuration / 60)) - 0.5) ++ "%")
477478
]
478479
[]
479480
]
@@ -539,15 +540,15 @@ settingsSettingView { config } =
539540
[ class "drawer-heading"
540541
]
541542
[ text "General Settings" ]
542-
, settingWrapper "Always On Top" (UpdateSetting <| Toggle AlwaysOnTop) config.alwaysOnTop
543-
, settingWrapper "Auto-start Work Timer after Break" (UpdateSetting <| Toggle AutoStartWorkTimer) config.autoStartWorkTimer
544-
, settingWrapper "Auto-start Work Timer at app startup" (UpdateSetting <| Toggle AutoStartOnAppStartup) config.autoStartOnAppStartup
545-
, settingWrapper "Auto-start Break Timer after Work" (UpdateSetting <| Toggle AutoStartBreakTimer) config.autoStartBreakTimer
546-
, settingWrapper "Auto-start the app on system startup" (UpdateSetting <| Toggle SystemStartupAutoStart) config.systemStartupAutoStart
543+
, settingWrapperToggle "Always On Top" (UpdateSetting <| Toggle AlwaysOnTop) config.alwaysOnTop
544+
, settingWrapperToggle "Auto-start Work Timer after Break" (UpdateSetting <| Toggle AutoStartWorkTimer) config.autoStartWorkTimer
545+
, settingWrapperToggle "Auto-start Work Timer at app startup" (UpdateSetting <| Toggle AutoStartOnAppStartup) config.autoStartOnAppStartup
546+
, settingWrapperToggle "Auto-start Break Timer after Work" (UpdateSetting <| Toggle AutoStartBreakTimer) config.autoStartBreakTimer
547+
, settingWrapperToggle "Auto-start the app on system startup" (UpdateSetting <| Toggle SystemStartupAutoStart) config.systemStartupAutoStart
547548
, div
548549
[ class "setting-wrapper"
549550
]
550-
[ p [ class "setting-title", style "margin-top" "0.3rem" ] [ text "Auto-quit app" ]
551+
[ p [ class "setting-title", style "margin-top" "0.3rem" ] [ text "Auto-quit App" ]
551552
, div
552553
[]
553554
[ select [ onInput (AutoQuit >> UpdateSetting) ]
@@ -558,10 +559,31 @@ settingsSettingView { config } =
558559
]
559560
]
560561
]
561-
, settingWrapper "Desktop Notifications" (UpdateSetting <| Toggle DesktopNotifications) config.desktopNotifications
562-
, settingWrapper "Minimize to Tray" (UpdateSetting <| Toggle MinimizeToTray) config.minimizeToTray
563-
, settingWrapper "Minimize to Tray on Close" (UpdateSetting <| Toggle MinimizeToTrayOnClose) config.minimizeToTrayOnClose
564-
, settingWrapper "Start minimized to Tray" (UpdateSetting <| Toggle StartMinimized) config.startMinimized
562+
, settingWrapperToggle "Desktop Notifications" (UpdateSetting <| Toggle DesktopNotifications) config.desktopNotifications
563+
, div
564+
[ class "setting-wrapper"
565+
]
566+
[ p [ class "setting-title", style "margin-top" "0.3rem" ] [ text "Max Session Duration" ]
567+
, div
568+
[ class "setting-value setting-time-input-wrapper"
569+
]
570+
[ input
571+
[ type_ "text"
572+
, class "setting-time-input"
573+
, Html.Attributes.min "1"
574+
, Html.Attributes.step "1"
575+
, value <| String.fromFloat (toFloat config.maxSessionDuration / 60)
576+
, onInput <| MaxSessionDuration >> UpdateSetting
577+
, onBlur <| UpdateSetting SaveMaxSessionDuration
578+
, style "width" <| String.fromInt (String.length <| String.fromFloat (toFloat config.maxSessionDuration / 60)) ++ "ch"
579+
]
580+
[]
581+
, text ":00"
582+
]
583+
]
584+
, settingWrapperToggle "Minimize to Tray" (UpdateSetting <| Toggle MinimizeToTray) config.minimizeToTray
585+
, settingWrapperToggle "Minimize to Tray on Close" (UpdateSetting <| Toggle MinimizeToTrayOnClose) config.minimizeToTrayOnClose
586+
, settingWrapperToggle "Start minimized to Tray" (UpdateSetting <| Toggle StartMinimized) config.startMinimized
565587
]
566588

567589

@@ -572,8 +594,8 @@ soundsSettingView model =
572594
[ class "drawer-heading"
573595
]
574596
[ text "Sound Settings" ]
575-
, settingWrapper "Tick Sounds - Break" (UpdateSetting <| Toggle TickSoundsDuringBreak) model.config.tickSoundsDuringBreak
576-
, settingWrapper "Tick Sounds - Work" (UpdateSetting <| Toggle TickSoundsDuringWork) model.config.tickSoundsDuringWork
597+
, settingWrapperToggle "Tick Sounds - Break" (UpdateSetting <| Toggle TickSoundsDuringBreak) model.config.tickSoundsDuringBreak
598+
, settingWrapperToggle "Tick Sounds - Work" (UpdateSetting <| Toggle TickSoundsDuringWork) model.config.tickSoundsDuringWork
577599
, audioFileWrapper model.config.shortBreakAudio ShortBreak "Short break"
578600
, audioFileWrapper model.config.longBreakAudio LongBreak "Long break"
579601
, audioFileWrapper model.config.focusAudio Focus "Work session"

src-tauri/src/config.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ pub struct Config {
2626
pub long_break_audio: Option<String>,
2727
pub long_break_duration: u16,
2828
pub max_round_number: u16,
29+
#[serde(default = "default_max_session_duration")]
30+
pub max_session_duration: u16,
2931
pub minimize_to_tray: bool,
3032
pub minimize_to_tray_on_close: bool,
3133
#[serde(default)]
@@ -55,6 +57,10 @@ fn default_theme() -> String {
5557
"pomotroid".to_string()
5658
}
5759

60+
fn default_max_session_duration() -> u16 {
61+
90 * 60
62+
}
63+
5864
impl Config {
5965
pub fn get_config_file_path(config_dir: &Path, config_file_name: Option<String>) -> PathBuf {
6066
config_dir.join(config_file_name.unwrap_or("config.toml".to_string()))
@@ -101,23 +107,24 @@ impl Default for Config {
101107
auto_start_break_timer: true,
102108
auto_start_on_app_startup: false,
103109
auto_start_work_timer: true,
104-
default_focus_label: "Focus".to_string(),
105-
default_long_break_label: "Long break".to_string(),
106-
default_short_break_label: "Short break".to_string(),
110+
default_focus_label: default_focus_label(),
111+
default_long_break_label: default_long_break_label(),
112+
default_short_break_label: default_short_break_label(),
107113
desktop_notifications: true,
108114
focus_audio: None,
109115
focus_duration: 25 * 60,
110116
long_break_audio: None,
111117
long_break_duration: 20 * 60,
112118
max_round_number: 4u16,
119+
max_session_duration: default_max_session_duration(),
113120
minimize_to_tray: true,
114121
minimize_to_tray_on_close: true,
115122
muted: false,
116123
short_break_audio: None,
117124
short_break_duration: 5 * 60,
118125
start_minimized: false,
119126
system_startup_auto_start: false,
120-
theme: "pomotroid".to_string(),
127+
theme: default_theme(),
121128
tick_sounds_during_work: true,
122129
tick_sounds_during_break: true,
123130
}

src-ts/main.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ type ElmConfig = {
8787
longBreakAudio: string | null;
8888
longBreakDuration: number;
8989
maxRoundNumber: number;
90+
maxSessionDuration: number;
9091
minimizeToTray: boolean;
9192
minimizeToTrayOnClose: boolean;
9293
muted: boolean;
@@ -114,6 +115,7 @@ type RustConfig = {
114115
long_break_audio: string | null;
115116
long_break_duration: number;
116117
max_round_number: number;
118+
max_session_duration: number;
117119
minimize_to_tray: boolean;
118120
minimize_to_tray_on_close: boolean;
119121
muted: boolean;
@@ -143,6 +145,7 @@ let rustConfig: RustConfig = {
143145
long_break_audio: null,
144146
long_break_duration: 1200,
145147
max_round_number: 4,
148+
max_session_duration: 90,
146149
minimize_to_tray: true,
147150
minimize_to_tray_on_close: true,
148151
muted: false,
@@ -174,6 +177,7 @@ app = Elm.Main.init({
174177
focusDuration: rustConfig.focus_duration,
175178
longBreakDuration: rustConfig.long_break_duration,
176179
maxRoundNumber: rustConfig.max_round_number,
180+
maxSessionDuration: rustConfig.max_session_duration,
177181
minimizeToTray: rustConfig.minimize_to_tray,
178182
minimizeToTrayOnClose: rustConfig.minimize_to_tray_on_close,
179183
muted: rustConfig.muted,
@@ -297,6 +301,7 @@ app.ports.sendMessageFromElm.subscribe(async function (message: Message) {
297301
long_break_audio: config.longBreakAudio,
298302
long_break_duration: config.longBreakDuration,
299303
max_round_number: config.maxRoundNumber,
304+
max_session_duration: config.maxSessionDuration,
300305
minimize_to_tray: config.minimizeToTray,
301306
minimize_to_tray_on_close: config.minimizeToTrayOnClose,
302307
muted: config.muted,

0 commit comments

Comments
 (0)