From 65c48eb97d1015d4ea9ba139f7f02b9173bc53d8 Mon Sep 17 00:00:00 2001 From: Vincent Jousse Date: Fri, 4 Oct 2024 16:55:32 +0200 Subject: [PATCH] refactor: split Elm code --- src-elm/Json.elm | 138 +++++++++++++++++++++++++ src-elm/Main.elm | 253 +--------------------------------------------- src-elm/Types.elm | 120 ++++++++++++++++++++++ 3 files changed, 261 insertions(+), 250 deletions(-) create mode 100644 src-elm/Json.elm create mode 100644 src-elm/Types.elm diff --git a/src-elm/Json.elm b/src-elm/Json.elm new file mode 100644 index 0000000..0c5f06b --- /dev/null +++ b/src-elm/Json.elm @@ -0,0 +1,138 @@ +module Json exposing (elmMessageEncoder, externalMessageDecoder) + +import Json.Decode as Decode +import Json.Decode.Pipeline as Pipe +import Json.Encode as Encode +import Themes exposing (Theme, ThemeColors) +import Types exposing (Config, ConfigAndThemes, ElmMessage, ExternalMessage(..), RustSession, RustState, SessionStatus(..), SessionType(..)) + + +elmMessageEncoder : ElmMessage -> Encode.Value +elmMessageEncoder elmMessage = + Encode.object + [ ( "name", Encode.string elmMessage.name ) + ] + + +themeColorsDecoder : Decode.Decoder ThemeColors +themeColorsDecoder = + Decode.succeed ThemeColors + |> Pipe.required "accent" Decode.string + |> Pipe.required "background" Decode.string + |> Pipe.required "background_light" Decode.string + |> Pipe.required "background_lightest" Decode.string + |> Pipe.required "focus_round" Decode.string + |> Pipe.required "focus_round_end" Decode.string + |> Pipe.required "focus_round_middle" Decode.string + |> Pipe.required "foreground" Decode.string + |> Pipe.required "foreground_darker" Decode.string + |> Pipe.required "foreground_darkest" Decode.string + |> Pipe.required "long_round" Decode.string + |> Pipe.required "short_round" Decode.string + + +themeDecoder : Decode.Decoder Theme +themeDecoder = + Decode.succeed Theme + |> Pipe.required "colors" themeColorsDecoder + |> Pipe.required "name" Decode.string + + +themesDecoder : Decode.Decoder (List Theme) +themesDecoder = + Decode.list themeDecoder + + +configDecoder : Decode.Decoder Config +configDecoder = + let + fieldSet0 = + Decode.map8 Config + (Decode.field "always_on_top" Decode.bool) + (Decode.field "auto_start_break_timer" Decode.bool) + (Decode.field "auto_start_work_timer" Decode.bool) + (Decode.field "desktop_notifications" Decode.bool) + (Decode.field "long_break_duration" Decode.int) + (Decode.field "max_round_number" Decode.int) + (Decode.field "minimize_to_tray" Decode.bool) + (Decode.field "minimize_to_tray_on_close" Decode.bool) + in + Decode.map6 (<|) + fieldSet0 + (Decode.field "pomodoro_duration" Decode.int) + (Decode.field "short_break_duration" Decode.int) + (Decode.field "theme" Decode.string) + (Decode.field "tick_sounds_during_break" Decode.bool) + (Decode.field "tick_sounds_during_work" Decode.bool) + + +configAndThemesDecoder : Decode.Decoder ConfigAndThemes +configAndThemesDecoder = + Decode.succeed ConfigAndThemes + |> Pipe.required "config" configDecoder + |> Pipe.required "themes" themesDecoder + + +rustStateDecoder : Decode.Decoder RustState +rustStateDecoder = + Decode.succeed RustState + |> Pipe.required "current_session" rustSessionDecoder + + +sessionTypeFromStringDecoder : String -> Decode.Decoder SessionType +sessionTypeFromStringDecoder string = + case String.toLower string of + "focus" -> + Decode.succeed Focus + + "shortbreak" -> + Decode.succeed ShortBreak + + "longbreak" -> + Decode.succeed LongBreak + + _ -> + Decode.fail ("Unknown sessionType: " ++ string) + + +sessionTypeDecoder : Decode.Decoder SessionType +sessionTypeDecoder = + Decode.string |> Decode.andThen sessionTypeFromStringDecoder + + +sessionStatusDecoder : Decode.Decoder SessionStatus +sessionStatusDecoder = + Decode.string |> Decode.andThen sessionStatusFromStringDecoder + + +sessionStatusFromStringDecoder : String -> Decode.Decoder SessionStatus +sessionStatusFromStringDecoder string = + case String.toLower string of + "paused" -> + Decode.succeed Paused + + "notstarted" -> + Decode.succeed NotStarted + + "running" -> + Decode.succeed Running + + _ -> + Decode.fail ("Unknown sessionStatus: " ++ string) + + +rustSessionDecoder : Decode.Decoder RustSession +rustSessionDecoder = + Decode.succeed RustSession + |> Pipe.required "current_time" Decode.int + |> Pipe.optional "label" (Decode.maybe Decode.string) Nothing + |> Pipe.required "session_type" sessionTypeDecoder + |> Pipe.required "state" sessionStatusDecoder + + +externalMessageDecoder : Decode.Decoder ExternalMessage +externalMessageDecoder = + Decode.oneOf + [ rustStateDecoder |> Decode.map RustStateMsg + , configAndThemesDecoder |> Decode.map RustConfigAndThemesMsg + ] diff --git a/src-elm/Main.elm b/src-elm/Main.elm index 7b6b11f..cd5dc58 100644 --- a/src-elm/Main.elm +++ b/src-elm/Main.elm @@ -1,17 +1,18 @@ -port module Main exposing (Config, ConfigAndThemes, CurrentState, Defaults, ExternalMessage(..), Flags, Model, Msg(..), NextRoundInfo, Notification, RustSession, RustState, Seconds, SessionStatus(..), SessionType(..), Setting(..), SettingTab(..), SettingType(..), main) +port module Main exposing (Flags, Model, Msg(..), main) import Browser import ColorHelper exposing (RGB(..), fromCSSHexToRGB, fromRGBToCSSHex) import Html exposing (Html, a, div, h1, h2, input, nav, p, section, text) import Html.Attributes exposing (attribute, class, href, id, style, target, title, type_, value) import Html.Events exposing (onClick, onInput, onMouseLeave) +import Json exposing (elmMessageEncoder, externalMessageDecoder) import Json.Decode as Decode -import Json.Decode.Pipeline as Pipe import Json.Encode as Encode import ListWithCurrent exposing (ListWithCurrent(..)) import Svg exposing (path, svg) import Svg.Attributes as SvgAttr import Themes exposing (Theme, ThemeColors, pomodorolmTheme) +import Types exposing (Config, CurrentState, Defaults, ExternalMessage(..), NextRoundInfo, Notification, RustState, Seconds, SessionStatus(..), SessionType(..), Setting(..), SettingTab(..), SettingType(..)) main : Program Flags Model Msg @@ -24,21 +25,6 @@ main = } -type alias Seconds = - Int - - -type alias ElmMessage = - { name : String } - - -elmMessageEncoder : ElmMessage -> Encode.Value -elmMessageEncoder elmMessage = - Encode.object - [ ( "name", Encode.string elmMessage.name ) - ] - - type alias Model = { appVersion : String , config : Config @@ -60,98 +46,6 @@ type alias Model = } -type alias Config = - { alwaysOnTop : Bool - , autoStartBreakTimer : Bool - , autoStartWorkTimer : Bool - , desktopNotifications : Bool - , longBreakDuration : Seconds - , maxRoundNumber : Int - , minimizeToTray : Bool - , minimizeToTrayOnClose : Bool - , pomodoroDuration : Seconds - , shortBreakDuration : Seconds - , theme : String - , tickSoundsDuringBreak : Bool - , tickSoundsDuringWork : Bool - } - - -type alias ConfigAndThemes = - { config : Config - , themes : List Theme - } - - -themeColorsDecoder : Decode.Decoder ThemeColors -themeColorsDecoder = - Decode.succeed ThemeColors - |> Pipe.required "accent" Decode.string - |> Pipe.required "background" Decode.string - |> Pipe.required "background_light" Decode.string - |> Pipe.required "background_lightest" Decode.string - |> Pipe.required "focus_round" Decode.string - |> Pipe.required "focus_round_end" Decode.string - |> Pipe.required "focus_round_middle" Decode.string - |> Pipe.required "foreground" Decode.string - |> Pipe.required "foreground_darker" Decode.string - |> Pipe.required "foreground_darkest" Decode.string - |> Pipe.required "long_round" Decode.string - |> Pipe.required "short_round" Decode.string - - -themeDecoder : Decode.Decoder Theme -themeDecoder = - Decode.succeed Theme - |> Pipe.required "colors" themeColorsDecoder - |> Pipe.required "name" Decode.string - - -themesDecoder : Decode.Decoder (List Theme) -themesDecoder = - Decode.list themeDecoder - - -configDecoder : Decode.Decoder Config -configDecoder = - let - fieldSet0 = - Decode.map8 Config - (Decode.field "always_on_top" Decode.bool) - (Decode.field "auto_start_break_timer" Decode.bool) - (Decode.field "auto_start_work_timer" Decode.bool) - (Decode.field "desktop_notifications" Decode.bool) - (Decode.field "long_break_duration" Decode.int) - (Decode.field "max_round_number" Decode.int) - (Decode.field "minimize_to_tray" Decode.bool) - (Decode.field "minimize_to_tray_on_close" Decode.bool) - in - Decode.map6 (<|) - fieldSet0 - (Decode.field "pomodoro_duration" Decode.int) - (Decode.field "short_break_duration" Decode.int) - (Decode.field "theme" Decode.string) - (Decode.field "tick_sounds_during_break" Decode.bool) - (Decode.field "tick_sounds_during_work" Decode.bool) - - -configAndThemesDecoder : Decode.Decoder ConfigAndThemes -configAndThemesDecoder = - Decode.succeed ConfigAndThemes - |> Pipe.required "config" configDecoder - |> Pipe.required "themes" themesDecoder - - -type alias CurrentState = - { color : String, percentage : Float, paused : Bool } - - -type SessionType - = Focus - | ShortBreak - | LongBreak - - sessionStatusToString : SessionStatus -> String sessionStatusToString sessionStatus = case sessionStatus of @@ -165,57 +59,6 @@ sessionStatusToString sessionStatus = "not_started" -type SessionStatus - = NotStarted - | Paused - | Running - - -type SettingTab - = TimerTab - | ThemeTab - | SettingsTab - | AboutTab - - -type Setting - = AlwaysOnTop - | AutoStartWorkTimer - | AutoStartBreakTimer - | TickSoundsDuringWork - | TickSoundsDuringBreak - | DesktopNotifications - | MinimizeToTray - | MinimizeToTrayOnClose - - -type alias Notification = - { body : String - , title : String - , name : String - , red : Int - , green : Int - , blue : Int - } - - -type alias NextRoundInfo = - { nextSessionType : SessionType - , htmlIdOfAudioToPlay : String - , nextRoundNumber : Int - , nextTime : Seconds - , notification : Notification - } - - -type alias Defaults = - { longBreakDuration : Seconds - , pomodoroDuration : Seconds - , shortBreakDuration : Seconds - , maxRoundNumber : Int - } - - type alias Flags = { alwaysOnTop : Bool , appVersion : String @@ -297,96 +140,6 @@ init flags = ) -type SettingType - = FocusTime - | LongBreakTime - | Rounds - | ShortBreakTime - - -type alias RustSession = - { currentTime : Int - , label : Maybe String - , sessionType : SessionType - , status : SessionStatus - } - - -type alias RustState = - { currentSession : RustSession - } - - -type ExternalMessage - = RustStateMsg RustState - | RustConfigAndThemesMsg ConfigAndThemes - - -rustStateDecoder : Decode.Decoder RustState -rustStateDecoder = - Decode.succeed RustState - |> Pipe.required "current_session" rustSessionDecoder - - -sessionTypeFromStringDecoder : String -> Decode.Decoder SessionType -sessionTypeFromStringDecoder string = - case String.toLower string of - "focus" -> - Decode.succeed Focus - - "shortbreak" -> - Decode.succeed ShortBreak - - "longbreak" -> - Decode.succeed LongBreak - - _ -> - Decode.fail ("Unknown sessionType: " ++ string) - - -sessionTypeDecoder : Decode.Decoder SessionType -sessionTypeDecoder = - Decode.string |> Decode.andThen sessionTypeFromStringDecoder - - -sessionStatusDecoder : Decode.Decoder SessionStatus -sessionStatusDecoder = - Decode.string |> Decode.andThen sessionStatusFromStringDecoder - - -sessionStatusFromStringDecoder : String -> Decode.Decoder SessionStatus -sessionStatusFromStringDecoder string = - case String.toLower string of - "paused" -> - Decode.succeed Paused - - "notstarted" -> - Decode.succeed NotStarted - - "running" -> - Decode.succeed Running - - _ -> - Decode.fail ("Unknown sessionStatus: " ++ string) - - -rustSessionDecoder : Decode.Decoder RustSession -rustSessionDecoder = - Decode.succeed RustSession - |> Pipe.required "current_time" Decode.int - |> Pipe.optional "label" (Decode.maybe Decode.string) Nothing - |> Pipe.required "session_type" sessionTypeDecoder - |> Pipe.required "state" sessionStatusDecoder - - -externalMessageDecoder : Decode.Decoder ExternalMessage -externalMessageDecoder = - Decode.oneOf - [ rustStateDecoder |> Decode.map RustStateMsg - , configAndThemesDecoder |> Decode.map RustConfigAndThemesMsg - ] - - type Msg = CloseWindow | ChangeSettingTab SettingTab diff --git a/src-elm/Types.elm b/src-elm/Types.elm new file mode 100644 index 0000000..04bab57 --- /dev/null +++ b/src-elm/Types.elm @@ -0,0 +1,120 @@ +module Types exposing (Config, ConfigAndThemes, CurrentState, Defaults, ElmMessage, ExternalMessage(..), NextRoundInfo, Notification, RustSession, RustState, Seconds, SessionStatus(..), SessionType(..), Setting(..), SettingTab(..), SettingType(..)) + +import Themes exposing (Theme) + + +type alias Seconds = + Int + + +type alias ElmMessage = + { name : String } + + +type alias Config = + { alwaysOnTop : Bool + , autoStartBreakTimer : Bool + , autoStartWorkTimer : Bool + , desktopNotifications : Bool + , longBreakDuration : Seconds + , maxRoundNumber : Int + , minimizeToTray : Bool + , minimizeToTrayOnClose : Bool + , pomodoroDuration : Seconds + , shortBreakDuration : Seconds + , theme : String + , tickSoundsDuringBreak : Bool + , tickSoundsDuringWork : Bool + } + + +type alias ConfigAndThemes = + { config : Config + , themes : List Theme + } + + +type alias CurrentState = + { color : String, percentage : Float, paused : Bool } + + +type SessionType + = Focus + | ShortBreak + | LongBreak + + +type SessionStatus + = NotStarted + | Paused + | Running + + +type SettingTab + = TimerTab + | ThemeTab + | SettingsTab + | AboutTab + + +type Setting + = AlwaysOnTop + | AutoStartWorkTimer + | AutoStartBreakTimer + | TickSoundsDuringWork + | TickSoundsDuringBreak + | DesktopNotifications + | MinimizeToTray + | MinimizeToTrayOnClose + + +type alias Notification = + { body : String + , title : String + , name : String + , red : Int + , green : Int + , blue : Int + } + + +type alias NextRoundInfo = + { nextSessionType : SessionType + , htmlIdOfAudioToPlay : String + , nextRoundNumber : Int + , nextTime : Seconds + , notification : Notification + } + + +type alias Defaults = + { longBreakDuration : Seconds + , pomodoroDuration : Seconds + , shortBreakDuration : Seconds + , maxRoundNumber : Int + } + + +type SettingType + = FocusTime + | LongBreakTime + | Rounds + | ShortBreakTime + + +type alias RustSession = + { currentTime : Int + , label : Maybe String + , sessionType : SessionType + , status : SessionStatus + } + + +type alias RustState = + { currentSession : RustSession + } + + +type ExternalMessage + = RustStateMsg RustState + | RustConfigAndThemesMsg ConfigAndThemes