A whole lot of filtering. URL now reflects filters.
This commit is contained in:
parent
1d366295ad
commit
84b0919cd6
|
@ -15,6 +15,7 @@ import Date exposing (Date, Month(..))
|
||||||
-- External modules
|
-- External modules
|
||||||
|
|
||||||
import Date.Extra
|
import Date.Extra
|
||||||
|
import Navigation exposing (Location)
|
||||||
|
|
||||||
|
|
||||||
-- DECODERS
|
-- DECODERS
|
||||||
|
@ -107,7 +108,7 @@ eventTypeDecoder =
|
||||||
|> required "light_text" bool
|
|> required "light_text" bool
|
||||||
|
|
||||||
|
|
||||||
initDataDecoder : Decoder (Flags -> Filter -> Route -> Model)
|
initDataDecoder : Decoder (Flags -> Filter -> Location -> Route -> Model)
|
||||||
initDataDecoder =
|
initDataDecoder =
|
||||||
decode Model
|
decode Model
|
||||||
|> required "days" (list dayDecoder)
|
|> required "days" (list dayDecoder)
|
||||||
|
|
|
@ -32,15 +32,15 @@ init : Flags -> Location -> ( Model, Cmd Msg )
|
||||||
init flags location =
|
init flags location =
|
||||||
let
|
let
|
||||||
currentRoute =
|
currentRoute =
|
||||||
parseLocation location
|
parseLocation (Debug.log "location" location)
|
||||||
|
|
||||||
emptyFilter =
|
emptyFilter =
|
||||||
Filter [] [] []
|
Filter [] [] []
|
||||||
|
|
||||||
initModel =
|
model =
|
||||||
Model [] [] [] [] [] flags emptyFilter currentRoute
|
Model [] [] [] [] [] flags emptyFilter location currentRoute
|
||||||
in
|
in
|
||||||
initModel ! [ sendInitMessage flags.camp_slug flags.websocket_server ]
|
model ! [ sendInitMessage flags.camp_slug flags.websocket_server ]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,6 @@ type Msg
|
||||||
| WebSocketPayload String
|
| WebSocketPayload String
|
||||||
| ToggleEventTypeFilter EventType
|
| ToggleEventTypeFilter EventType
|
||||||
| ToggleEventLocationFilter EventLocation
|
| ToggleEventLocationFilter EventLocation
|
||||||
| ToggleVideoRecordingFilter { name : String, filter : EventInstance -> Bool }
|
| ToggleVideoRecordingFilter { name : String, slug : String, filter : EventInstance -> Bool }
|
||||||
| OnLocationChange Location
|
| OnLocationChange Location
|
||||||
| BackInHistory
|
| BackInHistory
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
module Models exposing (..)
|
module Models exposing (..)
|
||||||
|
|
||||||
|
-- Core modules
|
||||||
|
|
||||||
import Date exposing (Date, now)
|
import Date exposing (Date, now)
|
||||||
|
|
||||||
|
|
||||||
|
-- External modules
|
||||||
|
|
||||||
|
import Navigation exposing (Location)
|
||||||
|
|
||||||
|
|
||||||
type Route
|
type Route
|
||||||
= OverviewRoute
|
= OverviewRoute
|
||||||
|
| OverviewFilteredRoute String
|
||||||
| DayRoute String
|
| DayRoute String
|
||||||
| EventRoute EventSlug
|
| EventRoute EventSlug
|
||||||
| NotFoundRoute
|
| NotFoundRoute
|
||||||
|
@ -18,6 +26,7 @@ type alias Model =
|
||||||
, eventTypes : List EventType
|
, eventTypes : List EventType
|
||||||
, flags : Flags
|
, flags : Flags
|
||||||
, filter : Filter
|
, filter : Filter
|
||||||
|
, location : Location
|
||||||
, route : Route
|
, route : Route
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +34,7 @@ type alias Model =
|
||||||
type alias Filter =
|
type alias Filter =
|
||||||
{ eventTypes : List EventType
|
{ eventTypes : List EventType
|
||||||
, eventLocations : List EventLocation
|
, eventLocations : List EventLocation
|
||||||
, videoRecording : List { name : String, filter : EventInstance -> Bool }
|
, videoRecording : List { name : String, slug : String, filter : EventInstance -> Bool }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,29 @@ import Navigation exposing (Location)
|
||||||
import UrlParser exposing (Parser, (</>), oneOf, map, top, s, string, parseHash)
|
import UrlParser exposing (Parser, (</>), oneOf, map, top, s, string, parseHash)
|
||||||
|
|
||||||
|
|
||||||
|
{--
|
||||||
|
URLs to support:
|
||||||
|
|
||||||
|
- #
|
||||||
|
This show the overview of the schedule
|
||||||
|
|
||||||
|
- #?type={types},location={locations},video={no,yes,link}
|
||||||
|
This is the overview, just with filters enable
|
||||||
|
|
||||||
|
- #day/{year}-{month}-{day}
|
||||||
|
Show a particular day
|
||||||
|
|
||||||
|
- #event/{slug}
|
||||||
|
Show a particular event
|
||||||
|
|
||||||
|
--}
|
||||||
|
|
||||||
|
|
||||||
matchers : Parser (Route -> a) a
|
matchers : Parser (Route -> a) a
|
||||||
matchers =
|
matchers =
|
||||||
oneOf
|
oneOf
|
||||||
[ map OverviewRoute top
|
[ map OverviewRoute top
|
||||||
|
, map OverviewFilteredRoute (top </> string)
|
||||||
, map DayRoute (s "day" </> string)
|
, map DayRoute (s "day" </> string)
|
||||||
, map EventRoute (s "event" </> string)
|
, map EventRoute (s "event" </> string)
|
||||||
]
|
]
|
||||||
|
|
|
@ -2,10 +2,11 @@ module Update exposing (update)
|
||||||
|
|
||||||
-- Local modules
|
-- Local modules
|
||||||
|
|
||||||
import Models exposing (Model, Route(OverviewRoute, EventRoute), Filter)
|
import Models exposing (Model, Route(..), Filter)
|
||||||
import Messages exposing (Msg(..))
|
import Messages exposing (Msg(..))
|
||||||
import Decoders exposing (webSocketActionDecoder, initDataDecoder, eventDecoder)
|
import Decoders exposing (webSocketActionDecoder, initDataDecoder, eventDecoder)
|
||||||
import Routing exposing (parseLocation)
|
import Routing exposing (parseLocation)
|
||||||
|
import Views.FilterView exposing (parseFilterFromQuery, filterToQuery)
|
||||||
|
|
||||||
|
|
||||||
-- Core modules
|
-- Core modules
|
||||||
|
@ -33,7 +34,7 @@ update msg model =
|
||||||
"init" ->
|
"init" ->
|
||||||
case Json.Decode.decodeString initDataDecoder str of
|
case Json.Decode.decodeString initDataDecoder str of
|
||||||
Ok m ->
|
Ok m ->
|
||||||
m model.flags (Filter [] [] []) model.route
|
m model.flags model.filter model.location model.route
|
||||||
|
|
||||||
Err error ->
|
Err error ->
|
||||||
model
|
model
|
||||||
|
@ -43,8 +44,11 @@ update msg model =
|
||||||
|
|
||||||
Err error ->
|
Err error ->
|
||||||
model
|
model
|
||||||
|
|
||||||
|
( newModel_, _ ) =
|
||||||
|
update (OnLocationChange model.location) newModel
|
||||||
in
|
in
|
||||||
newModel ! []
|
newModel_ ! []
|
||||||
|
|
||||||
ToggleEventTypeFilter eventType ->
|
ToggleEventTypeFilter eventType ->
|
||||||
let
|
let
|
||||||
|
@ -59,8 +63,14 @@ update msg model =
|
||||||
|
|
||||||
newFilter =
|
newFilter =
|
||||||
{ currentFilter | eventTypes = eventTypesFilter }
|
{ currentFilter | eventTypes = eventTypesFilter }
|
||||||
|
|
||||||
|
query =
|
||||||
|
filterToQuery newFilter
|
||||||
|
|
||||||
|
cmd =
|
||||||
|
Navigation.newUrl query
|
||||||
in
|
in
|
||||||
{ model | filter = newFilter } ! []
|
{ model | filter = newFilter } ! [ cmd ]
|
||||||
|
|
||||||
ToggleEventLocationFilter eventLocation ->
|
ToggleEventLocationFilter eventLocation ->
|
||||||
let
|
let
|
||||||
|
@ -75,8 +85,14 @@ update msg model =
|
||||||
|
|
||||||
newFilter =
|
newFilter =
|
||||||
{ currentFilter | eventLocations = eventLocationsFilter }
|
{ currentFilter | eventLocations = eventLocationsFilter }
|
||||||
|
|
||||||
|
query =
|
||||||
|
filterToQuery newFilter
|
||||||
|
|
||||||
|
cmd =
|
||||||
|
Navigation.newUrl query
|
||||||
in
|
in
|
||||||
{ model | filter = newFilter } ! []
|
{ model | filter = newFilter } ! [ cmd ]
|
||||||
|
|
||||||
ToggleVideoRecordingFilter videoRecording ->
|
ToggleVideoRecordingFilter videoRecording ->
|
||||||
let
|
let
|
||||||
|
@ -91,15 +107,29 @@ update msg model =
|
||||||
|
|
||||||
newFilter =
|
newFilter =
|
||||||
{ currentFilter | videoRecording = videoRecordingFilter }
|
{ currentFilter | videoRecording = videoRecordingFilter }
|
||||||
|
|
||||||
|
query =
|
||||||
|
filterToQuery newFilter
|
||||||
|
|
||||||
|
cmd =
|
||||||
|
Navigation.newUrl query
|
||||||
in
|
in
|
||||||
{ model | filter = newFilter } ! []
|
{ model | filter = newFilter } ! [ cmd ]
|
||||||
|
|
||||||
OnLocationChange location ->
|
OnLocationChange location ->
|
||||||
let
|
let
|
||||||
newRoute =
|
newRoute =
|
||||||
parseLocation location
|
parseLocation location
|
||||||
|
|
||||||
|
newFilter =
|
||||||
|
case newRoute of
|
||||||
|
OverviewFilteredRoute query ->
|
||||||
|
parseFilterFromQuery query model
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
model.filter
|
||||||
in
|
in
|
||||||
{ model | route = newRoute } ! []
|
{ model | filter = newFilter, route = newRoute, location = location } ! []
|
||||||
|
|
||||||
BackInHistory ->
|
BackInHistory ->
|
||||||
model ! [ Navigation.back 1 ]
|
model ! [ Navigation.back 1 ]
|
||||||
|
|
|
@ -30,6 +30,9 @@ view model =
|
||||||
OverviewRoute ->
|
OverviewRoute ->
|
||||||
scheduleOverviewView model
|
scheduleOverviewView model
|
||||||
|
|
||||||
|
OverviewFilteredRoute _ ->
|
||||||
|
scheduleOverviewView model
|
||||||
|
|
||||||
DayRoute dayIso ->
|
DayRoute dayIso ->
|
||||||
let
|
let
|
||||||
day =
|
day =
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
module Views.FilterView exposing (filterSidebar, applyFilters)
|
module Views.FilterView exposing (filterSidebar, applyFilters, parseFilterFromQuery, filterToQuery)
|
||||||
|
|
||||||
-- Local modules
|
-- Local modules
|
||||||
|
|
||||||
import Messages exposing (Msg(..))
|
import Messages exposing (Msg(..))
|
||||||
import Models exposing (Model, EventInstance)
|
import Models exposing (Model, EventInstance, Filter, Day)
|
||||||
|
|
||||||
|
|
||||||
|
-- Core modules
|
||||||
|
|
||||||
|
import Regex
|
||||||
|
|
||||||
|
|
||||||
-- External modules
|
-- External modules
|
||||||
|
@ -14,6 +19,7 @@ import Html.Events exposing (onClick)
|
||||||
import Date.Extra exposing (Interval(..), equalBy)
|
import Date.Extra exposing (Interval(..), equalBy)
|
||||||
|
|
||||||
|
|
||||||
|
applyFilters : Day -> Model -> List EventInstance
|
||||||
applyFilters day model =
|
applyFilters day model =
|
||||||
let
|
let
|
||||||
types =
|
types =
|
||||||
|
@ -89,11 +95,11 @@ hasRecordingFilter eventInstance =
|
||||||
eventInstance.videoUrl /= ""
|
eventInstance.videoUrl /= ""
|
||||||
|
|
||||||
|
|
||||||
videoRecordingFilters : List { name : String, filter : EventInstance -> Bool }
|
videoRecordingFilters : List { name : String, slug : String, filter : EventInstance -> Bool }
|
||||||
videoRecordingFilters =
|
videoRecordingFilters =
|
||||||
[ { name = "Will not be recorded", filter = notRecordedFilter }
|
[ { name = "Will not be recorded", slug = "not-to-be-recorded", filter = notRecordedFilter }
|
||||||
, { name = "Will recorded", filter = recordedFilter }
|
, { name = "Will recorded", slug = "to-be-recorded", filter = recordedFilter }
|
||||||
, { name = "Has recording", filter = hasRecordingFilter }
|
, { name = "Has recording", slug = "has-recording", filter = hasRecordingFilter }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -147,3 +153,76 @@ filterChoiceView filter currentFilters action =
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
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 : String -> 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 -> String
|
||||||
|
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
|
||||||
|
"#" ++ result
|
||||||
|
|
|
@ -3,8 +3,8 @@ module Views.ScheduleOverview exposing (scheduleOverviewView)
|
||||||
-- Local modules
|
-- Local modules
|
||||||
|
|
||||||
import Messages exposing (Msg(..))
|
import Messages exposing (Msg(..))
|
||||||
import Models exposing (Model, Day, EventInstance)
|
import Models exposing (Model, Day, EventInstance, Filter)
|
||||||
import Views.FilterView exposing (filterSidebar, applyFilters)
|
import Views.FilterView exposing (filterSidebar, applyFilters, parseFilterFromQuery)
|
||||||
|
|
||||||
|
|
||||||
-- External modules
|
-- External modules
|
||||||
|
|
Loading…
Reference in a new issue