Bookmark queries scoped to user or collective

This commit is contained in:
eikek
2022-01-08 19:31:26 +01:00
parent f914aa723e
commit a50a0a9a1a
14 changed files with 767 additions and 7 deletions

View File

@ -0,0 +1,179 @@
{-
Copyright 2020 Eike K. & Contributors
SPDX-License-Identifier: AGPL-3.0-or-later
-}
module Comp.BookmarkQueryForm exposing (Model, Msg, get, init, initQuery, update, view)
import Comp.Basic as B
import Comp.PowerSearchInput
import Data.BookmarkedQuery exposing (BookmarkedQueryDef, Location(..))
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onCheck, onInput)
import Messages.Comp.BookmarkQueryForm exposing (Texts)
import Styles as S
import Util.Maybe
type alias Model =
{ name : Maybe String
, queryModel : Comp.PowerSearchInput.Model
, location : Location
}
initQuery : String -> ( Model, Cmd Msg )
initQuery q =
let
res =
Comp.PowerSearchInput.update
(Comp.PowerSearchInput.setSearchString q)
Comp.PowerSearchInput.init
in
( { name = Nothing
, queryModel = res.model
, location = User
}
, Cmd.batch
[ Cmd.map QueryMsg res.cmd
]
)
init : ( Model, Cmd Msg )
init =
initQuery ""
isValid : Model -> Bool
isValid model =
Comp.PowerSearchInput.isValid model.queryModel
&& model.name
/= Nothing
get : Model -> Maybe BookmarkedQueryDef
get model =
let
qStr =
Maybe.withDefault "" model.queryModel.input
in
if isValid model then
Just
{ query =
{ query = qStr
, name = Maybe.withDefault "" model.name
}
, location = model.location
}
else
Nothing
type Msg
= SetName String
| QueryMsg Comp.PowerSearchInput.Msg
| SetLocation Location
update : Flags -> Msg -> Model -> ( Model, Cmd Msg, Sub Msg )
update _ msg model =
case msg of
SetName n ->
( { model | name = Util.Maybe.fromString n }, Cmd.none, Sub.none )
SetLocation loc ->
( { model | location = loc }, Cmd.none, Sub.none )
QueryMsg lm ->
let
res =
Comp.PowerSearchInput.update lm model.queryModel
in
( { model | queryModel = res.model }
, Cmd.map QueryMsg res.cmd
, Sub.map QueryMsg res.subs
)
--- View
view : Texts -> Model -> Html Msg
view texts model =
let
queryInput =
div
[ class "relative flex flex-grow flex-row" ]
[ Html.map QueryMsg
(Comp.PowerSearchInput.viewInput
{ placeholder = texts.queryLabel
, extraAttrs = []
}
model.queryModel
)
, Html.map QueryMsg
(Comp.PowerSearchInput.viewResult [] model.queryModel)
]
in
div
[ class "flex flex-col" ]
[ div [ class "mb-4" ]
[ label
[ for "sharename"
, class S.inputLabel
]
[ text texts.basics.name
, B.inputRequired
]
, input
[ type_ "text"
, onInput SetName
, placeholder texts.basics.name
, value <| Maybe.withDefault "" model.name
, id "sharename"
, class S.textInput
]
[]
]
, div [ class "flex flex-col mb-4 " ]
[ label [ class "inline-flex items-center" ]
[ input
[ type_ "radio"
, checked (model.location == User)
, onCheck (\_ -> SetLocation User)
, class S.radioInput
]
[]
, span [ class "ml-2" ] [ text texts.userLocation ]
, span [ class "ml-3 opacity-75 text-sm" ] [ text texts.userLocationText ]
]
, label [ class "inline-flex items-center" ]
[ input
[ type_ "radio"
, checked (model.location == Collective)
, class S.radioInput
, onCheck (\_ -> SetLocation Collective)
]
[]
, span [ class "ml-2" ] [ text texts.collectiveLocation ]
, span [ class "ml-3 opacity-75 text-sm" ] [ text texts.collectiveLocationText ]
]
]
, div [ class "mb-4" ]
[ label
[ for "sharequery"
, class S.inputLabel
]
[ text texts.queryLabel
, B.inputRequired
]
, queryInput
]
]

View File

@ -0,0 +1,173 @@
module Comp.BookmarkQueryManage exposing (..)
import Api
import Api.Model.BasicResult exposing (BasicResult)
import Comp.Basic as B
import Comp.BookmarkQueryForm
import Data.BookmarkedQuery exposing (BookmarkedQueryDef)
import Data.Flags exposing (Flags)
import Html exposing (Html, div, text)
import Html.Attributes exposing (class, classList, href)
import Html.Events exposing (onClick)
import Http
import Messages.Comp.BookmarkQueryManage exposing (Texts)
import Styles as S
type alias Model =
{ formModel : Comp.BookmarkQueryForm.Model
, loading : Bool
, formState : FormState
}
type FormState
= FormStateNone
| FormStateError Http.Error
| FormStateSaveError String
| FormStateInvalid
| FormStateSaved
init : String -> ( Model, Cmd Msg )
init query =
let
( fm, fc ) =
Comp.BookmarkQueryForm.initQuery query
in
( { formModel = fm
, loading = False
, formState = FormStateNone
}
, Cmd.map FormMsg fc
)
type Msg
= Submit
| Cancel
| FormMsg Comp.BookmarkQueryForm.Msg
| SaveResp (Result Http.Error BasicResult)
--- Update
type FormResult
= Submitted BookmarkedQueryDef
| Cancelled
| Done
| None
type alias UpdateResult =
{ model : Model
, cmd : Cmd Msg
, sub : Sub Msg
, outcome : FormResult
}
update : Flags -> Msg -> Model -> UpdateResult
update flags msg model =
let
empty =
{ model = model
, cmd = Cmd.none
, sub = Sub.none
, outcome = None
}
in
case msg of
FormMsg lm ->
let
( fm, fc, fs ) =
Comp.BookmarkQueryForm.update flags lm model.formModel
in
{ model = { model | formModel = fm }
, cmd = Cmd.map FormMsg fc
, sub = Sub.map FormMsg fs
, outcome = None
}
Submit ->
case Comp.BookmarkQueryForm.get model.formModel of
Just data ->
{ empty | cmd = save flags data, outcome = Submitted data, model = { model | loading = True } }
Nothing ->
{ empty | model = { model | formState = FormStateInvalid } }
Cancel ->
{ model = model
, cmd = Cmd.none
, sub = Sub.none
, outcome = Cancelled
}
SaveResp (Ok res) ->
if res.success then
{ empty | model = { model | loading = False, formState = FormStateSaved }, outcome = Done }
else
{ empty | model = { model | loading = False, formState = FormStateSaveError res.message } }
SaveResp (Err err) ->
{ empty | model = { model | loading = False, formState = FormStateError err } }
save : Flags -> BookmarkedQueryDef -> Cmd Msg
save flags model =
Api.addBookmark flags model SaveResp
--- View
view : Texts -> Model -> Html Msg
view texts model =
div [ class "relative" ]
[ B.loadingDimmer { label = "", active = model.loading }
, Html.map FormMsg (Comp.BookmarkQueryForm.view texts.form model.formModel)
, case model.formState of
FormStateNone ->
div [ class "hidden" ] []
FormStateError err ->
div [ class S.errorMessage ]
[ text <| texts.httpError err
]
FormStateInvalid ->
div [ class S.errorMessage ]
[ text texts.formInvalid
]
FormStateSaveError m ->
div [ class S.errorMessage ]
[ text m
]
FormStateSaved ->
div [ class S.successMessage ]
[ text texts.saved
]
, div [ class "flex flex-row space-x-2 py-2" ]
[ B.primaryButton
{ label = texts.basics.submit
, icon = "fa fa-save"
, disabled = False
, handler = onClick Submit
, attrs = [ href "#" ]
}
, B.secondaryButton
{ label = texts.basics.cancel
, icon = "fa fa-times"
, disabled = False
, handler = onClick Cancel
, attrs = [ href "#" ]
}
]
]