Working on event detail. Also small visual help in DayView. Also some favorite stuff.
This commit is contained in:
parent
974694bd5f
commit
a447ca476f
|
@ -7,7 +7,7 @@ import Models exposing (Day, Speaker, Event, EventInstance, EventLocation, Event
|
||||||
|
|
||||||
-- Core modules
|
-- Core modules
|
||||||
|
|
||||||
import Json.Decode exposing (int, string, float, list, bool, dict, Decoder)
|
import Json.Decode exposing (int, string, float, list, bool, dict, Decoder, nullable)
|
||||||
import Json.Decode.Pipeline exposing (decode, required, optional, hardcoded)
|
import Json.Decode.Pipeline exposing (decode, required, optional, hardcoded)
|
||||||
import Date exposing (Date, Month(..))
|
import Date exposing (Date, Month(..))
|
||||||
|
|
||||||
|
@ -55,6 +55,7 @@ eventDecoder =
|
||||||
|> required "speakers" (list speakerDecoder)
|
|> required "speakers" (list speakerDecoder)
|
||||||
|> required "video_recording" bool
|
|> required "video_recording" bool
|
||||||
|> optional "video_url" string ""
|
|> optional "video_url" string ""
|
||||||
|
|> required "event_type" string
|
||||||
|
|
||||||
|
|
||||||
dateDecoder : Decoder Date
|
dateDecoder : Decoder Date
|
||||||
|
@ -86,6 +87,7 @@ eventInstanceDecoder =
|
||||||
|> required "location_icon" string
|
|> required "location_icon" string
|
||||||
|> required "video_recording" bool
|
|> required "video_recording" bool
|
||||||
|> optional "video_url" string ""
|
|> optional "video_url" string ""
|
||||||
|
|> optional "is_favorited" (nullable bool) Nothing
|
||||||
|
|
||||||
|
|
||||||
eventLocationDecoder : Decoder EventLocation
|
eventLocationDecoder : Decoder EventLocation
|
||||||
|
|
|
@ -75,6 +75,7 @@ type alias EventInstance =
|
||||||
, locationIcon : String
|
, locationIcon : String
|
||||||
, videoRecording : Bool
|
, videoRecording : Bool
|
||||||
, videoUrl : String
|
, videoUrl : String
|
||||||
|
, isFavorited : Maybe Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,6 +86,7 @@ type alias Event =
|
||||||
, speakers : List Speaker
|
, speakers : List Speaker
|
||||||
, videoRecording : Bool
|
, videoRecording : Bool
|
||||||
, videoUrl : String
|
, videoUrl : String
|
||||||
|
, eventType : String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -52,12 +52,12 @@ dayView day model =
|
||||||
div
|
div
|
||||||
[ classList [ ( "row", True ) ] ]
|
[ classList [ ( "row", True ) ] ]
|
||||||
[ gutter minutes
|
[ gutter minutes
|
||||||
, locationColumns filteredEventInstances model.eventLocations model.flags.schedule_midnight_offset_hours
|
, locationColumns filteredEventInstances model.eventLocations model.flags.schedule_midnight_offset_hours minutes
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
locationColumns : List EventInstance -> List EventLocation -> Int -> Html Msg
|
locationColumns : List EventInstance -> List EventLocation -> Int -> List Date -> Html Msg
|
||||||
locationColumns eventInstances eventLocations offset =
|
locationColumns eventInstances eventLocations offset minutes =
|
||||||
let
|
let
|
||||||
columnWidth =
|
columnWidth =
|
||||||
100.0 / toFloat (List.length eventLocations)
|
100.0 / toFloat (List.length eventLocations)
|
||||||
|
@ -71,11 +71,11 @@ locationColumns eventInstances eventLocations offset =
|
||||||
[ ( "col-sm-11", True )
|
[ ( "col-sm-11", True )
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
(List.map (\location -> locationColumn columnWidth eventInstances offset location) eventLocations)
|
(List.map (\location -> locationColumn columnWidth eventInstances offset minutes location) eventLocations)
|
||||||
|
|
||||||
|
|
||||||
locationColumn : Float -> List EventInstance -> Int -> EventLocation -> Html Msg
|
locationColumn : Float -> List EventInstance -> Int -> List Date -> EventLocation -> Html Msg
|
||||||
locationColumn columnWidth eventInstances offset location =
|
locationColumn columnWidth eventInstances offset minutes location =
|
||||||
let
|
let
|
||||||
locationInstances =
|
locationInstances =
|
||||||
List.filter (\instance -> instance.location == location.slug) eventInstances
|
List.filter (\instance -> instance.location == location.slug) eventInstances
|
||||||
|
@ -102,6 +102,23 @@ locationColumn columnWidth eventInstances offset location =
|
||||||
]
|
]
|
||||||
[ text location.name ]
|
[ text location.name ]
|
||||||
]
|
]
|
||||||
|
++ (List.map
|
||||||
|
(\x ->
|
||||||
|
div
|
||||||
|
[ style
|
||||||
|
[ ( "backgroundColor"
|
||||||
|
, if Date.minute x == 30 || Date.minute x == 45 then
|
||||||
|
"#f8f8f8"
|
||||||
|
else
|
||||||
|
"#fff"
|
||||||
|
)
|
||||||
|
, ( "height", px blockHeight )
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
minutes
|
||||||
|
)
|
||||||
++ (List.map (\group -> renderGroup offset group) overlappingGroups)
|
++ (List.map (\group -> renderGroup offset group) overlappingGroups)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,14 @@ import Messages exposing (Msg(..))
|
||||||
import Models exposing (..)
|
import Models exposing (..)
|
||||||
|
|
||||||
|
|
||||||
|
-- Core modules
|
||||||
|
|
||||||
|
import Date
|
||||||
|
|
||||||
|
|
||||||
-- External modules
|
-- External modules
|
||||||
|
|
||||||
import Html exposing (Html, text, div, ul, li, span, i, h4, a, p, hr)
|
import Html exposing (Html, text, div, ul, li, span, i, h3, h4, a, p, hr, strong)
|
||||||
import Html.Attributes exposing (class, classList, href)
|
import Html.Attributes exposing (class, classList, href)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
import Markdown
|
import Markdown
|
||||||
|
@ -22,58 +27,84 @@ eventDetailView eventSlug model =
|
||||||
model.events
|
model.events
|
||||||
|> List.filter (\e -> e.slug == eventSlug)
|
|> List.filter (\e -> e.slug == eventSlug)
|
||||||
|> List.head
|
|> List.head
|
||||||
|
|
||||||
|
eventInstances =
|
||||||
|
List.filter (\instance -> instance.eventSlug == eventSlug) model.eventInstances
|
||||||
in
|
in
|
||||||
case event of
|
case event of
|
||||||
Just event ->
|
Just event ->
|
||||||
div [ class "row" ]
|
div [ class "row" ]
|
||||||
[ div [ class "col-sm-9" ]
|
[ eventDetailContent event
|
||||||
[ a [ onClick BackInHistory, classList [ ( "btn", True ), ( "btn-default", True ) ] ]
|
, eventDetailSidebar event eventInstances
|
||||||
[ i [ classList [ ( "fa", True ), ( "fa-chevron-left", True ) ] ] []
|
|
||||||
, text " Back"
|
|
||||||
]
|
|
||||||
, h4 [] [ text event.title ]
|
|
||||||
, p [] [ Markdown.toHtml [] event.abstract ]
|
|
||||||
, hr [] []
|
|
||||||
, eventInstancesList eventSlug model.eventInstances
|
|
||||||
]
|
|
||||||
, div
|
|
||||||
[ classList
|
|
||||||
[ ( "col-sm-3", True )
|
|
||||||
, ( "schedule-sidebar", True )
|
|
||||||
, ( "sticky", True )
|
|
||||||
]
|
|
||||||
]
|
|
||||||
[ videoRecordingSidebar event
|
|
||||||
, speakerSidebar event.speakers
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
|
|
||||||
Nothing ->
|
Nothing ->
|
||||||
div [ class "row" ]
|
div [ class "row" ]
|
||||||
[ text
|
[ h4 [] [ text "Event not found." ]
|
||||||
(case model.dataLoaded of
|
, a [ href "#" ] [ text "Click here to go the schedule overview." ]
|
||||||
True ->
|
|
||||||
"Event not found."
|
|
||||||
|
|
||||||
False ->
|
|
||||||
"Loading..."
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
videoRecordingSidebar : Event -> Html Msg
|
eventDetailContent : Event -> Html Msg
|
||||||
videoRecordingSidebar event =
|
eventDetailContent event =
|
||||||
|
div [ class "col-sm-9" ]
|
||||||
|
[ a [ onClick BackInHistory, classList [ ( "btn", True ), ( "btn-default", True ) ] ]
|
||||||
|
[ i [ classList [ ( "fa", True ), ( "fa-chevron-left", True ) ] ] []
|
||||||
|
, text " Back"
|
||||||
|
]
|
||||||
|
, h3 [] [ text event.title ]
|
||||||
|
, p [] [ Markdown.toHtml [] event.abstract ]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
eventDetailSidebar : Event -> List EventInstance -> Html Msg
|
||||||
|
eventDetailSidebar event eventInstances =
|
||||||
let
|
let
|
||||||
( video, willBeRecorded ) =
|
videoRecordingLink =
|
||||||
if event.videoUrl /= "" then
|
case event.videoUrl of
|
||||||
( h4 [] [ text "Watch the video here!" ], True )
|
"" ->
|
||||||
else if event.videoRecording == True then
|
[]
|
||||||
( h4 [] [ text "This event will be recorded!" ], True )
|
|
||||||
else
|
_ ->
|
||||||
( h4 [] [ text "This event will NOT be recorded!" ], False )
|
[ a [ href event.videoUrl, classList [ ( "btn", True ), ( "btn-success", True ) ] ]
|
||||||
|
[ i [ classList [ ( "fa", True ), ( "fa-film", True ) ] ] []
|
||||||
|
, text " Watch recording here!"
|
||||||
|
]
|
||||||
|
]
|
||||||
in
|
in
|
||||||
div [ classList [ ( "alert", True ), ( "alert-danger", not willBeRecorded ), ( "alert-info", willBeRecorded ) ] ]
|
div
|
||||||
[ video ]
|
[ classList
|
||||||
|
[ ( "col-sm-3", True )
|
||||||
|
, ( "schedule-sidebar", True )
|
||||||
|
, ( "sticky", True )
|
||||||
|
]
|
||||||
|
]
|
||||||
|
(videoRecordingLink
|
||||||
|
++ [ speakerSidebar event.speakers
|
||||||
|
, eventMetaDataSidebar event
|
||||||
|
, eventInstancesSidebar eventInstances
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
eventMetaDataSidebar : Event -> Html Msg
|
||||||
|
eventMetaDataSidebar event =
|
||||||
|
let
|
||||||
|
videoRecording =
|
||||||
|
case event.videoRecording of
|
||||||
|
True ->
|
||||||
|
"Yes"
|
||||||
|
|
||||||
|
False ->
|
||||||
|
"No"
|
||||||
|
in
|
||||||
|
div []
|
||||||
|
[ h4 [] [ text "Metadata" ]
|
||||||
|
, ul []
|
||||||
|
[ li [] [ strong [] [ text "Type: " ], text event.eventType ]
|
||||||
|
, li [] [ strong [] [ text "Recording: " ], text videoRecording ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
speakerSidebar : List Speaker -> Html Msg
|
speakerSidebar : List Speaker -> Html Msg
|
||||||
|
@ -94,27 +125,30 @@ speakerDetail speaker =
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
eventInstancesList : String -> List EventInstance -> Html Msg
|
eventInstancesSidebar : List EventInstance -> Html Msg
|
||||||
eventInstancesList eventSlug eventInstances =
|
eventInstancesSidebar eventInstances =
|
||||||
let
|
div []
|
||||||
instances =
|
[ h4 []
|
||||||
List.filter (\instance -> instance.eventSlug == eventSlug) eventInstances
|
[ text "This event will occur at:" ]
|
||||||
in
|
, ul
|
||||||
div []
|
[]
|
||||||
[ h4 []
|
(List.map eventInstanceItem eventInstances)
|
||||||
[ text "This event will occur at:" ]
|
]
|
||||||
, ul
|
|
||||||
[]
|
|
||||||
(List.map eventInstanceItem instances)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
eventInstanceItem : EventInstance -> Html Msg
|
eventInstanceItem : EventInstance -> Html Msg
|
||||||
eventInstanceItem eventInstance =
|
eventInstanceItem eventInstance =
|
||||||
li []
|
let
|
||||||
[ text
|
toFormat =
|
||||||
((Date.Extra.toFormattedString "y-MM-dd HH:mm" eventInstance.from)
|
if Date.day eventInstance.from == Date.day eventInstance.to then
|
||||||
++ " to "
|
"HH:mm"
|
||||||
++ (Date.Extra.toFormattedString "y-MM-d HH:mm" eventInstance.to)
|
else
|
||||||
)
|
"E HH:mm"
|
||||||
]
|
in
|
||||||
|
li []
|
||||||
|
[ text
|
||||||
|
((Date.Extra.toFormattedString "E HH:mm" eventInstance.from)
|
||||||
|
++ " to "
|
||||||
|
++ (Date.Extra.toFormattedString toFormat eventInstance.to)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
|
@ -438,6 +438,7 @@ class Event(CampRelatedModel):
|
||||||
for speaker in self.speakers.all()
|
for speaker in self.speakers.all()
|
||||||
],
|
],
|
||||||
'video_recording': self.video_recording,
|
'video_recording': self.video_recording,
|
||||||
|
'event_type': self.event_type.name,
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.video_url:
|
if self.video_url:
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
{% block extra_head %}
|
{% block extra_head %}
|
||||||
<noscript>
|
<noscript>
|
||||||
<meta http-equiv="refresh" content="3; url={% url "noscript_schedule_index" camp_slug=camp.slug %}" />
|
<meta http-equiv="refresh" content="0; url={% url "noscript_schedule_index" camp_slug=camp.slug %}" />
|
||||||
</noscript>
|
</noscript>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -220,10 +220,16 @@ footer {
|
||||||
.schedule-filter ul {
|
.schedule-filter ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schedule-filter ul li {
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.schedule-filter .btn {
|
.schedule-filter .btn {
|
||||||
min-width: 200px;
|
width: 100%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue