Add support for dark mode
This commit is contained in:
parent
28f6c05acb
commit
df821e07af
45
index.html
45
index.html
|
@ -13,14 +13,34 @@
|
||||||
<script src="js/vd.js"></script>
|
<script src="js/vd.js"></script>
|
||||||
<script src="js/main.js"></script>
|
<script src="js/main.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
function isDarkMode(e) {
|
||||||
|
var darkMode = JSON.parse(localStorage.getItem('darkMode'));
|
||||||
|
|
||||||
|
if (darkMode === null) {
|
||||||
|
if (e === undefined) {
|
||||||
|
e = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
|
}
|
||||||
|
|
||||||
|
darkMode = e.matches === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return darkMode;
|
||||||
|
}
|
||||||
|
|
||||||
var app = Elm.Main.init({
|
var app = Elm.Main.init({
|
||||||
node: document.getElementById('container'),
|
node: document.getElementById('container'),
|
||||||
flags: { width: window.innerWidth, height: window.innerHeight }
|
flags: {
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight,
|
||||||
|
darkMode: isDarkMode(),
|
||||||
|
darkSetting: JSON.parse(localStorage.getItem('darkMode'))
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var lastId, player;
|
var lastId, player;
|
||||||
|
|
||||||
if (app.ports !== undefined && app.ports.registerVideo !== undefined) {
|
if (app.ports !== undefined) {
|
||||||
|
if (app.ports.registerVideo !== undefined) {
|
||||||
app.ports.registerVideo.subscribe(function(args) {
|
app.ports.registerVideo.subscribe(function(args) {
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
var time = vd.parseTime(args[2]) || undefined;
|
var time = vd.parseTime(args[2]) || undefined;
|
||||||
|
@ -41,12 +61,31 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app.ports !== undefined && app.ports.eraseVideo !== undefined) {
|
if (app.ports.setDarkMode !== undefined) {
|
||||||
|
app.ports.setDarkMode.subscribe(function(arg) {
|
||||||
|
if (arg === null) {
|
||||||
|
localStorage.removeItem('darkMode');
|
||||||
|
} else {
|
||||||
|
localStorage.setItem('darkMode', arg);
|
||||||
|
}
|
||||||
|
app.ports.darkMode.send(isDarkMode());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.ports !== undefined) {
|
||||||
|
if (app.ports.eraseVideo !== undefined) {
|
||||||
app.ports.eraseVideo.subscribe(function() {
|
app.ports.eraseVideo.subscribe(function() {
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
lastId = undefined;
|
lastId = undefined;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) {
|
||||||
|
app.ports.darkMode.send(isDarkMode(e));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
module Colors exposing (blackFont, greyBackground, greyFont, primary, primaryOver, red, selected, white)
|
module Colors exposing (..)
|
||||||
|
|
||||||
import Element
|
import Element
|
||||||
|
|
||||||
|
@ -28,16 +28,37 @@ greyBackground =
|
||||||
Element.rgba255 0 0 0 0.7
|
Element.rgba255 0 0 0 0.7
|
||||||
|
|
||||||
|
|
||||||
blackFont : Element.Color
|
background : Bool -> Element.Color
|
||||||
blackFont =
|
background darkMode =
|
||||||
Element.rgb255 54 54 54
|
if darkMode then
|
||||||
|
Element.rgb255 47 49 54
|
||||||
|
|
||||||
|
else
|
||||||
|
Element.rgb255 245 245 245
|
||||||
|
|
||||||
|
|
||||||
greyFont : Element.Color
|
font : Bool -> Element.Color
|
||||||
greyFont =
|
font darkMode =
|
||||||
|
if darkMode then
|
||||||
|
Element.rgb255 245 245 245
|
||||||
|
|
||||||
|
else
|
||||||
|
Element.rgb255 54 57 63
|
||||||
|
|
||||||
|
|
||||||
|
detailFont : Bool -> Element.Color
|
||||||
|
detailFont darkMode =
|
||||||
|
if darkMode then
|
||||||
|
Element.rgb255 160 160 160
|
||||||
|
|
||||||
|
else
|
||||||
Element.rgb255 128 128 128
|
Element.rgb255 128 128 128
|
||||||
|
|
||||||
|
|
||||||
selected : Element.Color
|
selected : Bool -> Element.Color
|
||||||
selected =
|
selected darkMode =
|
||||||
|
if darkMode then
|
||||||
|
Element.rgb255 64 68 75
|
||||||
|
|
||||||
|
else
|
||||||
Element.rgb255 223 233 250
|
Element.rgb255 223 233 250
|
||||||
|
|
34
src/Core.elm
34
src/Core.elm
|
@ -24,6 +24,8 @@ type alias Model =
|
||||||
, time : Time.Posix
|
, time : Time.Posix
|
||||||
, currentDate : Time.Posix
|
, currentDate : Time.Posix
|
||||||
, url : Url.Url
|
, url : Url.Url
|
||||||
|
, darkMode : Bool
|
||||||
|
, darkSetting : Maybe Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,10 +51,12 @@ type Msg
|
||||||
| Unhover
|
| Unhover
|
||||||
| TimeReceived Time.Posix
|
| TimeReceived Time.Posix
|
||||||
| CurrentDateReceived Time.Posix
|
| CurrentDateReceived Time.Posix
|
||||||
|
| DarkMode Bool
|
||||||
|
| DarkModeClicked
|
||||||
|
|
||||||
|
|
||||||
init : { width : Int, height : Int } -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
|
init : { width : Int, height : Int, darkMode : Bool, darkSetting : Maybe Bool } -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
|
||||||
init { width, height } url key =
|
init { width, height, darkMode, darkSetting } url key =
|
||||||
( Model
|
( Model
|
||||||
[]
|
[]
|
||||||
Time.utc
|
Time.utc
|
||||||
|
@ -62,6 +66,8 @@ init { width, height } url key =
|
||||||
(Time.millisToPosix 0)
|
(Time.millisToPosix 0)
|
||||||
(Time.millisToPosix 0)
|
(Time.millisToPosix 0)
|
||||||
url
|
url
|
||||||
|
darkMode
|
||||||
|
darkSetting
|
||||||
, Cmd.batch
|
, Cmd.batch
|
||||||
[ Task.attempt TimeZoneReceivedResult TimeZone.getZone
|
[ Task.attempt TimeZoneReceivedResult TimeZone.getZone
|
||||||
, Task.perform CurrentDateReceived Time.now
|
, Task.perform CurrentDateReceived Time.now
|
||||||
|
@ -85,6 +91,7 @@ subscriptions _ =
|
||||||
Sub.batch
|
Sub.batch
|
||||||
[ Events.onResize (\w h -> SizeReceived w h)
|
[ Events.onResize (\w h -> SizeReceived w h)
|
||||||
, Time.every 200 TimeReceived
|
, Time.every 200 TimeReceived
|
||||||
|
, Ports.darkMode DarkMode
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -154,6 +161,13 @@ update msg model =
|
||||||
, Nav.pushUrl model.key "#"
|
, Nav.pushUrl model.key "#"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
DarkModeClicked ->
|
||||||
|
let
|
||||||
|
next =
|
||||||
|
nextDarkSetting model.darkSetting
|
||||||
|
in
|
||||||
|
( { model | darkSetting = next }, Ports.setDarkMode next )
|
||||||
|
|
||||||
PlaylistClicked playlist ->
|
PlaylistClicked playlist ->
|
||||||
( model
|
( model
|
||||||
, Nav.pushUrl model.key ("#" ++ playlist.url)
|
, Nav.pushUrl model.key ("#" ++ playlist.url)
|
||||||
|
@ -248,6 +262,9 @@ update msg model =
|
||||||
Browser.External s ->
|
Browser.External s ->
|
||||||
( model, Nav.load s )
|
( model, Nav.load s )
|
||||||
|
|
||||||
|
DarkMode dark ->
|
||||||
|
( { model | darkMode = dark }, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
splitter : String -> Maybe ( String, String )
|
splitter : String -> Maybe ( String, String )
|
||||||
splitter input =
|
splitter input =
|
||||||
|
@ -262,3 +279,16 @@ splitter input =
|
||||||
parseQueryString : String -> Dict String String
|
parseQueryString : String -> Dict String String
|
||||||
parseQueryString input =
|
parseQueryString input =
|
||||||
Dict.fromList (List.filterMap splitter (String.split "&" input))
|
Dict.fromList (List.filterMap splitter (String.split "&" input))
|
||||||
|
|
||||||
|
|
||||||
|
nextDarkSetting : Maybe Bool -> Maybe Bool
|
||||||
|
nextDarkSetting current =
|
||||||
|
case current of
|
||||||
|
Nothing ->
|
||||||
|
Just True
|
||||||
|
|
||||||
|
Just True ->
|
||||||
|
Just False
|
||||||
|
|
||||||
|
Just False ->
|
||||||
|
Nothing
|
||||||
|
|
|
@ -5,7 +5,7 @@ import Core
|
||||||
import Views
|
import Views
|
||||||
|
|
||||||
|
|
||||||
main : Program { width : Int, height : Int } Core.Model Core.Msg
|
main : Program { width : Int, height : Int, darkMode : Bool, darkSetting : Maybe Bool } Core.Model Core.Msg
|
||||||
main =
|
main =
|
||||||
Browser.application
|
Browser.application
|
||||||
{ init = Core.init
|
{ init = Core.init
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
port module Ports exposing (eraseVideo, registerVideo)
|
port module Ports exposing (darkMode, eraseVideo, registerVideo, setDarkMode)
|
||||||
|
|
||||||
|
|
||||||
port registerVideo : ( String, String, Maybe String ) -> Cmd msg
|
port registerVideo : ( String, String, Maybe String ) -> Cmd msg
|
||||||
|
|
||||||
|
|
||||||
port eraseVideo : () -> Cmd msg
|
port eraseVideo : () -> Cmd msg
|
||||||
|
|
||||||
|
|
||||||
|
port setDarkMode : Maybe Bool -> Cmd msg
|
||||||
|
|
||||||
|
|
||||||
|
port darkMode : (Bool -> msg) -> Sub msg
|
||||||
|
|
|
@ -35,13 +35,14 @@ view model =
|
||||||
{ title = title model
|
{ title = title model
|
||||||
, body =
|
, body =
|
||||||
[ Element.layout
|
[ Element.layout
|
||||||
[ Font.color Colors.blackFont
|
[ Font.color (Colors.font model.darkMode)
|
||||||
|
, Background.color (Colors.background model.darkMode)
|
||||||
, Font.size Consts.normalFontSize
|
, Font.size Consts.normalFontSize
|
||||||
, Font.family [ Font.typeface "Cantarell" ]
|
, Font.family [ Font.typeface "Cantarell" ]
|
||||||
]
|
]
|
||||||
(Element.column
|
(Element.column
|
||||||
[ Element.width Element.fill, Element.height Element.fill ]
|
[ Element.width Element.fill, Element.height Element.fill ]
|
||||||
[ topBar, element ]
|
[ topBar model.darkSetting, element ]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -67,21 +68,46 @@ viewContent model =
|
||||||
playlistsView model.device model.playlists model.currentDate model.time hover
|
playlistsView model.device model.playlists model.currentDate model.time hover
|
||||||
|
|
||||||
Core.Playlist playlist hover ->
|
Core.Playlist playlist hover ->
|
||||||
videoMiniaturesView model.device model.zone model.currentDate model.time hover playlist
|
videoMiniaturesView model.darkMode model.device model.zone model.currentDate model.time hover playlist
|
||||||
|
|
||||||
Core.Video playlist video hover ->
|
Core.Video playlist video hover ->
|
||||||
videoView model.device model.zone model.currentDate model.time hover playlist video
|
videoView model.darkMode model.device model.zone model.currentDate model.time hover playlist video
|
||||||
|
|
||||||
|
|
||||||
topBar : Element Core.Msg
|
topBar : Maybe Bool -> Element Core.Msg
|
||||||
topBar =
|
topBar darkSetting =
|
||||||
Element.row
|
Element.row
|
||||||
[ Element.width Element.fill
|
[ Element.width Element.fill
|
||||||
, Background.color Colors.primary
|
, Background.color Colors.primary
|
||||||
, Font.color Colors.white
|
, Font.color Colors.white
|
||||||
, Font.size Consts.homeFontSize
|
, Font.size Consts.homeFontSize
|
||||||
]
|
]
|
||||||
[ homeButton ]
|
[ homeButton, mode darkSetting ]
|
||||||
|
|
||||||
|
|
||||||
|
mode : Maybe Bool -> Element Core.Msg
|
||||||
|
mode current =
|
||||||
|
let
|
||||||
|
( label, icon ) =
|
||||||
|
case current of
|
||||||
|
Nothing ->
|
||||||
|
( "Par défaut", "🌓" )
|
||||||
|
|
||||||
|
Just True ->
|
||||||
|
( "Mode nuit", "🌑" )
|
||||||
|
|
||||||
|
Just False ->
|
||||||
|
( "Mode jour", "🌕" )
|
||||||
|
in
|
||||||
|
Input.button
|
||||||
|
[ Element.height Element.fill, Element.alignRight, Element.padding 10 ]
|
||||||
|
{ label =
|
||||||
|
Element.row [ Element.spacing 5 ]
|
||||||
|
[ Element.el [ Font.size Consts.titleFontSize ] (Element.text label)
|
||||||
|
, Element.el [ Font.size Consts.homeFontSize ] (Element.text icon)
|
||||||
|
]
|
||||||
|
, onPress = Just Core.DarkModeClicked
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
homeButton : Element Core.Msg
|
homeButton : Element Core.Msg
|
||||||
|
@ -203,14 +229,14 @@ playlistView currentDate time hover playlist =
|
||||||
button
|
button
|
||||||
|
|
||||||
|
|
||||||
videoMiniaturesView : Element.Device -> Time.Zone -> Time.Posix -> Time.Posix -> Maybe (Hover Twitch.Video) -> Twitch.Playlist -> Element Core.Msg
|
videoMiniaturesView : Bool -> Element.Device -> Time.Zone -> Time.Posix -> Time.Posix -> Maybe (Hover Twitch.Video) -> Twitch.Playlist -> Element Core.Msg
|
||||||
videoMiniaturesView device zone currentDate time hover playlist =
|
videoMiniaturesView darkMode device zone currentDate time hover playlist =
|
||||||
let
|
let
|
||||||
empty =
|
empty =
|
||||||
Element.el [ Element.width Element.fill ] Element.none
|
Element.el [ Element.width Element.fill ] Element.none
|
||||||
|
|
||||||
views =
|
views =
|
||||||
List.map (videoMiniatureView zone currentDate time hover playlist) playlist.videos
|
List.map (videoMiniatureView darkMode zone currentDate time hover playlist) playlist.videos
|
||||||
|
|
||||||
grouped =
|
grouped =
|
||||||
group (numberOfVideosPerRow device) views
|
group (numberOfVideosPerRow device) views
|
||||||
|
@ -288,13 +314,13 @@ videoMiniature currentDate time hover playlist video =
|
||||||
image
|
image
|
||||||
|
|
||||||
|
|
||||||
videoMiniatureView : Time.Zone -> Time.Posix -> Time.Posix -> Maybe (Hover Twitch.Video) -> Twitch.Playlist -> Twitch.Video -> Element Core.Msg
|
videoMiniatureView : Bool -> Time.Zone -> Time.Posix -> Time.Posix -> Maybe (Hover Twitch.Video) -> Twitch.Playlist -> Twitch.Video -> Element Core.Msg
|
||||||
videoMiniatureView zone currentDate time hover playlist video =
|
videoMiniatureView darkMode zone currentDate time hover playlist video =
|
||||||
let
|
let
|
||||||
display =
|
display =
|
||||||
Element.column [ Element.width Element.fill, Element.spacing 10 ]
|
Element.column [ Element.width Element.fill, Element.spacing 10 ]
|
||||||
[ videoMiniature currentDate time hover playlist video
|
[ videoMiniature currentDate time hover playlist video
|
||||||
, videoDescription zone video
|
, videoDescription darkMode zone video
|
||||||
]
|
]
|
||||||
|
|
||||||
button =
|
button =
|
||||||
|
@ -306,21 +332,21 @@ videoMiniatureView zone currentDate time hover playlist video =
|
||||||
button
|
button
|
||||||
|
|
||||||
|
|
||||||
videoInList : Time.Zone -> Time.Posix -> Time.Posix -> Maybe (Hover Twitch.Video) -> Twitch.Playlist -> Twitch.Video -> Twitch.Video -> Element Core.Msg
|
videoInList : Bool -> Time.Zone -> Time.Posix -> Time.Posix -> Maybe (Hover Twitch.Video) -> Twitch.Playlist -> Twitch.Video -> Twitch.Video -> Element Core.Msg
|
||||||
videoInList zone currentDate time hover playlist activeVideo video =
|
videoInList darkMode zone currentDate time hover playlist activeVideo video =
|
||||||
let
|
let
|
||||||
label =
|
label =
|
||||||
Element.row [ Element.width Element.fill, Element.spacing 10 ]
|
Element.row [ Element.width Element.fill, Element.spacing 10 ]
|
||||||
[ Element.el [ Element.width (Element.fillPortion 2) ]
|
[ Element.el [ Element.width (Element.fillPortion 2) ]
|
||||||
(videoMiniature currentDate time hover playlist video)
|
(videoMiniature currentDate time hover playlist video)
|
||||||
, Element.el [ Element.width (Element.fillPortion 3), Element.paddingXY 0 10, Element.alignTop ]
|
, Element.el [ Element.width (Element.fillPortion 3), Element.paddingXY 0 10, Element.alignTop ]
|
||||||
(videoDescription zone video)
|
(videoDescription darkMode zone video)
|
||||||
]
|
]
|
||||||
in
|
in
|
||||||
if video == activeVideo then
|
if video == activeVideo then
|
||||||
Element.el
|
Element.el
|
||||||
[ Element.width Element.fill
|
[ Element.width Element.fill
|
||||||
, Background.color Colors.selected
|
, Background.color (Colors.selected darkMode)
|
||||||
, Border.color Colors.primary
|
, Border.color Colors.primary
|
||||||
, Border.width 2
|
, Border.width 2
|
||||||
]
|
]
|
||||||
|
@ -333,8 +359,8 @@ videoInList zone currentDate time hover playlist activeVideo video =
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
videoView : Element.Device -> Time.Zone -> Time.Posix -> Time.Posix -> Maybe (Hover Twitch.Video) -> Twitch.Playlist -> Twitch.Video -> Element Core.Msg
|
videoView : Bool -> Element.Device -> Time.Zone -> Time.Posix -> Time.Posix -> Maybe (Hover Twitch.Video) -> Twitch.Playlist -> Twitch.Video -> Element Core.Msg
|
||||||
videoView device zone currentDate time hover playlist video =
|
videoView darkMode device zone currentDate time hover playlist video =
|
||||||
let
|
let
|
||||||
( builder, contentPadding ) =
|
( builder, contentPadding ) =
|
||||||
case device.class of
|
case device.class of
|
||||||
|
@ -401,12 +427,12 @@ videoView device zone currentDate time hover playlist video =
|
||||||
, Element.height Element.fill
|
, Element.height Element.fill
|
||||||
, Element.scrollbarY
|
, Element.scrollbarY
|
||||||
]
|
]
|
||||||
(List.map (videoInList zone currentDate time hover playlist video) playlist.videos)
|
(List.map (videoInList darkMode zone currentDate time hover playlist video) playlist.videos)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
videoDescription : Time.Zone -> Twitch.Video -> Element Core.Msg
|
videoDescription : Bool -> Time.Zone -> Twitch.Video -> Element Core.Msg
|
||||||
videoDescription zone video =
|
videoDescription darkMode zone video =
|
||||||
Element.column [ Element.spacing 10 ]
|
Element.column [ Element.spacing 10 ]
|
||||||
[ Element.paragraph
|
[ Element.paragraph
|
||||||
[ Font.bold
|
[ Font.bold
|
||||||
|
@ -416,7 +442,7 @@ videoDescription zone video =
|
||||||
, case video.date of
|
, case video.date of
|
||||||
Just date ->
|
Just date ->
|
||||||
Element.paragraph
|
Element.paragraph
|
||||||
[ Font.color Colors.greyFont
|
[ Font.color (Colors.detailFont darkMode)
|
||||||
]
|
]
|
||||||
[ Element.text ("Diffusé le " ++ formatDate zone date) ]
|
[ Element.text ("Diffusé le " ++ formatDate zone date) ]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue