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