Add support for dark mode

This commit is contained in:
Thomas Forgione 2021-04-03 23:33:01 +02:00
parent 28f6c05acb
commit df821e07af
6 changed files with 181 additions and 59 deletions

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) ]