bornhack-website/schedule/src/Views/FilterView.elm

238 lines
7.0 KiB
Elm

module Views.FilterView exposing (filterSidebar, applyFilters, parseFilterFromQuery, filterToQuery)
-- Local modules
import Messages exposing (Msg(..))
import Models exposing (Model, EventInstance, Filter, Day, FilterQuery, Route(OverviewFilteredRoute), VideoRecordingFilter, EventType, EventLocation)
import Routing exposing (routeToString)
-- Core modules
import Regex
-- External modules
import Html exposing (Html, text, div, ul, li, span, i, h4, small)
import Html.Attributes exposing (class, classList)
import Html.Events exposing (onClick)
import Date.Extra exposing (Interval(..), equalBy)
applyFilters : Day -> Model -> List EventInstance
applyFilters day model =
let
slugs default filters =
List.map .slug
(if List.isEmpty filters then
default
else
filters
)
types =
slugs model.eventTypes model.filter.eventTypes
locations =
slugs model.eventLocations model.filter.eventLocations
videoFilters =
slugs videoRecordingFilters model.filter.videoRecording
filteredEventInstances =
List.filter
(\eventInstance ->
(Date.Extra.equalBy Month eventInstance.from day.date)
&& (Date.Extra.equalBy Date.Extra.Day eventInstance.from day.date)
&& List.member eventInstance.location locations
&& List.member eventInstance.eventType types
&& List.member eventInstance.videoState videoFilters
)
model.eventInstances
in
filteredEventInstances
filterSidebar : Model -> Html Msg
filterSidebar model =
div
[ classList
[ ( "col-sm-3", True )
, ( "col-sm-push-9", True )
, ( "schedule-sidebar", True )
, ( "schedule-filter", True )
, ( "sticky", True )
]
]
[ h4 [] [ text "Filter" ]
, div [ class "form-group" ]
[ filterView
"Type"
model.eventTypes
model.filter.eventTypes
ToggleEventTypeFilter
model.eventInstances
.eventType
, filterView
"Location"
model.eventLocations
model.filter.eventLocations
ToggleEventLocationFilter
model.eventInstances
.location
, filterView
"Video"
videoRecordingFilters
model.filter.videoRecording
ToggleVideoRecordingFilter
model.eventInstances
.videoState
]
]
videoRecordingFilters : List VideoRecordingFilter
videoRecordingFilters =
[ { name = "Will not be recorded", slug = "not-to-be-recorded" }
, { name = "Will recorded", slug = "to-be-recorded" }
, { name = "Has recording", slug = "has-recording" }
]
filterView :
String
-> List { a | name : String, slug : String }
-> List { a | name : String, slug : String }
-> ({ a | name : String, slug : String } -> Msg)
-> List EventInstance
-> (EventInstance -> String)
-> Html Msg
filterView name possibleFilters currentFilters action eventInstances slugLike =
div []
[ text (name ++ ":")
, ul []
(possibleFilters
|> List.map
(\filter ->
filterChoiceView
filter
currentFilters
action
eventInstances
slugLike
)
)
]
filterChoiceView :
{ a | name : String, slug : String }
-> List { a | name : String, slug : String }
-> ({ a | name : String, slug : String } -> Msg)
-> List EventInstance
-> (EventInstance -> String)
-> Html Msg
filterChoiceView filter currentFilters action eventInstances slugLike =
let
active =
List.member filter currentFilters
notActive =
not active
eventInstanceCount =
eventInstances
|> List.filter (\eventInstance -> slugLike eventInstance == filter.slug)
|> List.length
in
li []
[ div
[ classList
[ ( "btn", True )
, ( "btn-default", True )
, ( "filter-choice-active", active )
]
, onClick (action filter)
]
[ span []
[ i [ classList [ ( "fa", True ), ( "fa-minus", active ), ( "fa-plus", notActive ) ] ] []
, text (" " ++ filter.name)
, small [] [ text <| " (" ++ (toString eventInstanceCount) ++ ")" ]
]
]
]
findFilter : List { a | slug : String } -> String -> Maybe { a | slug : String }
findFilter modelItems filterSlug =
List.head (List.filter (\x -> x.slug == filterSlug) modelItems)
getFilter : String -> List { a | slug : String } -> String -> List { a | slug : String }
getFilter filterType modelItems query =
let
filterMatch =
query
|> Regex.find (Regex.AtMost 1) (Regex.regex (filterType ++ "=([\\w,_-]+)&*"))
|> List.concatMap .submatches
|> List.head
|> Maybe.withDefault Nothing
|> Maybe.withDefault ""
filterSlugs =
String.split "," filterMatch
in
List.filterMap (\x -> findFilter modelItems x) filterSlugs
parseFilterFromQuery : FilterQuery -> Model -> Filter
parseFilterFromQuery query model =
let
types =
getFilter "type" model.eventTypes query
locations =
getFilter "location" model.eventLocations query
videoFilters =
getFilter "video" videoRecordingFilters query
in
{ eventTypes = types
, eventLocations = locations
, videoRecording = videoFilters
}
filterToQuery : Filter -> FilterQuery
filterToQuery filter =
let
typePart =
case String.join "," (List.map .slug filter.eventTypes) of
"" ->
""
types ->
"type=" ++ types
locationPart =
case String.join "," (List.map .slug filter.eventLocations) of
"" ->
""
locations ->
"location=" ++ locations
videoPart =
case String.join "," (List.map .slug filter.videoRecording) of
"" ->
""
video ->
"video=" ++ video
result =
String.join "&" (List.filter (\x -> x /= "") [ typePart, locationPart, videoPart ])
in
routeToString <| OverviewFilteredRoute result