Allow bookmarks in periodic query notification

This commit is contained in:
eikek 2022-01-10 14:25:20 +01:00
parent ccb4df5bd7
commit 699cf091e6
19 changed files with 497 additions and 82 deletions

View File

@ -20,6 +20,8 @@ trait OQueryBookmarks[F[_]] {
def getAll(account: AccountId): F[Vector[OQueryBookmarks.Bookmark]]
def findOne(account: AccountId, nameOrId: String): F[Option[OQueryBookmarks.Bookmark]]
def create(account: AccountId, bookmark: OQueryBookmarks.NewBookmark): F[AddResult]
def update(
@ -53,9 +55,15 @@ object OQueryBookmarks {
def getAll(account: AccountId): F[Vector[Bookmark]] =
store
.transact(RQueryBookmark.allForUser(account))
.map(
_.map(r => Bookmark(r.id, r.name, r.label, r.query, r.isPersonal, r.created))
)
.map(_.map(convert.toModel))
def findOne(
account: AccountId,
nameOrId: String
): F[Option[OQueryBookmarks.Bookmark]] =
store
.transact(RQueryBookmark.findByNameOrId(account, nameOrId))
.map(_.map(convert.toModel))
def create(account: AccountId, b: NewBookmark): F[AddResult] = {
val record =
@ -65,23 +73,28 @@ object OQueryBookmarks {
def update(account: AccountId, id: Ident, b: NewBookmark): F[UpdateResult] =
UpdateResult.fromUpdate(
store.transact(
RQueryBookmark.update(
RQueryBookmark(
id,
b.name,
b.label,
None, // userId and some other values are not used
account.collective,
b.query,
Timestamp.Epoch
)
)
)
store.transact(RQueryBookmark.update(convert.toRecord(account, id, b)))
)
def delete(account: AccountId, bookmark: Ident): F[Unit] =
store.transact(RQueryBookmark.deleteById(account.collective, bookmark)).as(())
})
private object convert {
def toModel(r: RQueryBookmark): Bookmark =
Bookmark(r.id, r.name, r.label, r.query, r.isPersonal, r.created)
def toRecord(account: AccountId, id: Ident, b: NewBookmark): RQueryBookmark =
RQueryBookmark(
id,
b.name,
b.label,
None, // userId and some other values are not used
account.collective,
b.query,
Timestamp.Epoch
)
}
}

View File

@ -7,6 +7,7 @@
package docspell.joex.notify
import cats.data.OptionT
import cats.data.{NonEmptyList => Nel}
import cats.effect._
import cats.implicits._
@ -17,10 +18,14 @@ import docspell.joex.scheduler.Task
import docspell.notification.api.EventContext
import docspell.notification.api.NotificationChannel
import docspell.notification.api.PeriodicQueryArgs
import docspell.query.ItemQuery
import docspell.query.ItemQuery.Expr.AndExpr
import docspell.query.ItemQueryParser
import docspell.store.qb.Batch
import docspell.store.queries.ListItem
import docspell.store.queries.{QItem, Query}
import docspell.store.records.RQueryBookmark
import docspell.store.records.RShare
import docspell.store.records.RUser
object PeriodicQueryTask {
@ -54,22 +59,77 @@ object PeriodicQueryTask {
)
.getOrElse(())
private def queryString(q: ItemQuery.Expr) =
ItemQueryParser.asString(q)
def makeQuery[F[_]: Sync](ctx: Context[F, Args])(cont: Query => F[Unit]): F[Unit] = {
def fromBookmark(id: String) =
ctx.store
.transact(RQueryBookmark.findByNameOrId(ctx.args.account, id))
.map(_.map(_.query))
.flatTap(q =>
ctx.logger.debug(s"Loaded bookmark '$id': ${q.map(_.expr).map(queryString)}")
)
def fromShare(id: String) =
ctx.store
.transact(RShare.findOneByCollective(ctx.args.account.collective, Some(true), id))
.map(_.map(_.query))
.flatTap(q =>
ctx.logger.debug(s"Loaded share '$id': ${q.map(_.expr).map(queryString)}")
)
def fromBookmarkOrShare(id: String) =
OptionT(fromBookmark(id)).orElse(OptionT(fromShare(id))).value
def withQuery(bm: Option[ItemQuery], str: String): F[Unit] =
ItemQueryParser.parse(str) match {
case Right(q) =>
val expr = bm.map(b => AndExpr(Nel.of(b.expr, q.expr))).getOrElse(q.expr)
val query = Query(Query.Fix(ctx.args.account, Some(expr), None))
ctx.logger.debug(s"Running query: ${queryString(expr)}") *> cont(query)
case Left(err) =>
ctx.logger.error(
s"Item query is invalid, stopping: ${ctx.args.query.map(_.query)} - ${err.render}"
)
}
(ctx.args.bookmark, ctx.args.query) match {
case (Some(bm), Some(qstr)) =>
ctx.logger.debug(s"Using bookmark $bm and query $qstr") *>
fromBookmarkOrShare(bm).flatMap(bq => withQuery(bq, qstr.query))
case (Some(bm), None) =>
fromBookmarkOrShare(bm).flatMap {
case Some(bq) =>
val query = Query(Query.Fix(ctx.args.account, Some(bq.expr), None))
ctx.logger.debug(s"Using bookmark: ${queryString(bq.expr)}") *> cont(query)
case None =>
ctx.logger.error(
s"No bookmark found for id: $bm. Can't continue. Please fix the task query."
)
}
case (None, Some(qstr)) =>
ctx.logger.debug(s"Using query: ${qstr.query}") *> withQuery(None, qstr.query)
case (None, None) =>
ctx.logger.error(s"No query provided for task $taskName!")
}
}
def withItems[F[_]: Sync](ctx: Context[F, Args], limit: Int, now: Timestamp)(
cont: Vector[ListItem] => F[Unit]
): F[Unit] =
ItemQueryParser.parse(ctx.args.query.query) match {
case Right(q) =>
val query = Query(Query.Fix(ctx.args.account, Some(q.expr), None))
val items = ctx.store
.transact(QItem.findItems(query, now.toUtcDate, 0, Batch.limit(limit)))
.compile
.to(Vector)
makeQuery(ctx) { query =>
val items = ctx.store
.transact(QItem.findItems(query, now.toUtcDate, 0, Batch.limit(limit)))
.compile
.to(Vector)
items.flatMap(cont)
case Left(err) =>
ctx.logger.error(
s"Item query is invalid, stopping: ${ctx.args.query} - ${err.render}"
)
items.flatMap(cont)
}
def withEventContext[F[_]](

View File

@ -15,7 +15,8 @@ import io.circe.{Decoder, Encoder}
final case class PeriodicQueryArgs(
account: AccountId,
channel: ChannelOrRef,
query: ItemQueryString,
query: Option[ItemQueryString],
bookmark: Option[String],
baseUrl: Option[LenientUri]
)

View File

@ -8037,12 +8037,12 @@ extraSchemas:
PeriodicQuerySettings:
description: |
Settings for the periodc-query task.
Settings for the periodc-query task. At least one of `query` and
`bookmark` is required!
required:
- id
- enabled
- channel
- query
- schedule
properties:
id:
@ -8065,6 +8065,10 @@ extraSchemas:
query:
type: string
format: itemquery
bookmark:
type: string
description: |
Name or ID of bookmark to use.
PeriodicDueItemsSettings:
description: |

View File

@ -21,7 +21,8 @@ final case class PeriodicQuerySettings(
summary: Option[String],
enabled: Boolean,
channel: NotificationChannel,
query: ItemQuery,
query: Option[ItemQuery],
bookmark: Option[String],
schedule: CalEvent
) {}

View File

@ -117,11 +117,20 @@ object PeriodicQueryRoutes extends MailAddressCodec {
Sync[F]
.pure(for {
ch <- NotificationChannel.convert(settings.channel)
qstr <- ItemQueryParser
.asString(settings.query.expr)
.left
.map(err => new IllegalArgumentException(s"Query not renderable: $err"))
} yield (ch, ItemQueryString(qstr)))
qstr <- settings.query match {
case Some(q) =>
ItemQueryParser
.asString(q.expr)
.left
.map(err => new IllegalArgumentException(s"Query not renderable: $err"))
.map(Option.apply)
case None =>
Right(None)
}
_ <-
if (qstr.nonEmpty || settings.bookmark.nonEmpty) Right(())
else Left(new IllegalArgumentException("No query or bookmark provided"))
} yield (ch, qstr.map(ItemQueryString.apply)))
.rethrow
.map { case (channel, qstr) =>
UserTask(
@ -134,6 +143,7 @@ object PeriodicQueryRoutes extends MailAddressCodec {
user,
Right(channel),
qstr,
settings.bookmark,
Some(baseUrl / "app" / "item")
)
)
@ -155,7 +165,8 @@ object PeriodicQueryRoutes extends MailAddressCodec {
task.summary,
task.enabled,
ch,
ItemQueryParser.parseUnsafe(task.args.query.query),
task.args.query.map(_.query).map(ItemQueryParser.parseUnsafe),
task.args.bookmark,
task.timer
)
}

View File

@ -11,12 +11,12 @@ import cats.syntax.all._
import docspell.common._
import docspell.query.ItemQuery
import docspell.store.AddResult
import docspell.store.qb.DSL._
import docspell.store.qb._
import doobie._
import doobie.implicits._
import docspell.store.AddResult
final case class RQueryBookmark(
id: Ident,
@ -153,4 +153,25 @@ object RQueryBookmark {
bm.cid === account.collective && (bm.userId.isNull || bm.userId.in(users))
).build.query[RQueryBookmark].to[Vector]
}
def findByNameOrId(
account: AccountId,
nameOrId: String
): ConnectionIO[Option[RQueryBookmark]] = {
val user = RUser.as("u")
val bm = RQueryBookmark.as("bm")
val users = Select(
user.uid.s,
from(user),
user.cid === account.collective && user.login === account.user
)
Select(
select(bm.all),
from(bm),
bm.cid === account.collective &&
(bm.userId.isNull || bm.userId.in(users)) &&
(bm.name === nameOrId || bm.id ==== nameOrId)
).build.query[RQueryBookmark].option
}
}

View File

@ -138,6 +138,23 @@ object RShare {
.option
})
def findOneByCollective(
cid: Ident,
enabled: Option[Boolean],
nameOrId: String
): ConnectionIO[Option[RShare]] = {
val s = RShare.as("s")
val u = RUser.as("u")
Select(
select(s.all),
from(s).innerJoin(u, u.uid === s.userId),
u.cid === cid &&
(s.name === nameOrId || s.id ==== nameOrId) &&?
enabled.map(e => s.enabled === e)
).build.query[RShare].option
}
def findAllByCollective(
cid: Ident,
ownerLogin: Option[Ident],

View File

@ -0,0 +1,177 @@
{-
Copyright 2020 Eike K. & Contributors
SPDX-License-Identifier: AGPL-3.0-or-later
-}
module Comp.BookmarkDropdown exposing (Item(..), Model, Msg, getSelected, getSelectedId, init, initWith, update, view)
import Api
import Api.Model.BookmarkedQuery exposing (BookmarkedQuery)
import Api.Model.ShareDetail exposing (ShareDetail)
import Comp.Dropdown exposing (Option)
import Data.Bookmarks exposing (AllBookmarks)
import Data.DropdownStyle
import Data.Flags exposing (Flags)
import Data.UiSettings exposing (UiSettings)
import Html exposing (Html)
import Http
import Messages.Comp.BookmarkDropdown exposing (Texts)
import Util.List
type Model
= Model (Comp.Dropdown.Model Item)
type Msg
= DropdownMsg (Comp.Dropdown.Msg Item)
| GetBookmarksResp (Maybe String) (Result Http.Error AllBookmarks)
initCmd : Flags -> Maybe String -> Cmd Msg
initCmd flags selected =
Api.getBookmarks flags (GetBookmarksResp selected)
type Item
= BM BookmarkedQuery
| Share ShareDetail
toItems : AllBookmarks -> List Item
toItems all =
List.map BM all.bookmarks
++ List.map Share all.shares
initWith : AllBookmarks -> Maybe String -> Model
initWith bms selected =
let
items =
toItems bms
findSel id =
Util.List.find
(\b ->
case b of
BM m ->
m.id == id
Share s ->
s.id == id
)
items
in
Model <|
Comp.Dropdown.makeSingleList
{ options = items, selected = Maybe.andThen findSel selected }
init : Flags -> Maybe String -> ( Model, Cmd Msg )
init flags selected =
( Model Comp.Dropdown.makeSingle, initCmd flags selected )
getSelected : Model -> Maybe Item
getSelected model =
case model of
Model dm ->
Comp.Dropdown.getSelected dm
|> List.head
getSelectedId : Model -> Maybe String
getSelectedId model =
let
id item =
case item of
BM b ->
b.id
Share s ->
s.id
in
getSelected model |> Maybe.map id
--- Update
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
let
dmodel =
case model of
Model a ->
a
in
case msg of
GetBookmarksResp sel (Ok all) ->
( initWith all sel, Cmd.none )
GetBookmarksResp _ (Err err) ->
( model, Cmd.none )
DropdownMsg lm ->
let
( dm, dc ) =
Comp.Dropdown.update lm dmodel
in
( Model dm, Cmd.map DropdownMsg dc )
--- View
itemOption : Texts -> Item -> Option
itemOption texts item =
case item of
BM b ->
{ text = b.name
, additional =
if b.personal then
texts.personal
else
texts.collective
}
Share s ->
{ text = Maybe.withDefault "-" s.name, additional = texts.share }
itemColor : Item -> String
itemColor item =
case item of
BM b ->
if b.personal then
"text-cyan-600 dark:text-indigo-300"
else
"text-sky-600 dark:text-violet-300"
Share _ ->
"text-blue-600 dark:text-purple-300"
view : Texts -> UiSettings -> Model -> Html Msg
view texts settings model =
let
viewSettings =
{ makeOption = itemOption texts
, placeholder = texts.placeholder
, labelColor = \a -> \_ -> itemColor a
, style = Data.DropdownStyle.mainStyle
}
dm =
case model of
Model a ->
a
in
Html.map DropdownMsg
(Comp.Dropdown.viewSingle2 viewSettings settings dm)

View File

@ -834,7 +834,7 @@ viewIntern2 texts settings withButtons model =
]
, case model.form of
TM tm ->
Html.map TagMsg (Comp.TagForm.view2 texts.tagForm tm)
Html.map TagMsg (Comp.TagForm.view2 texts.tagForm settings tm)
PMR pm ->
Html.map PersonMsg (Comp.PersonForm.view2 texts.personForm True settings pm)

View File

@ -462,16 +462,17 @@ view2 cfg settings model =
viewMultiple2 cfg settings model
else
viewSingle2 cfg model
viewSingle2 cfg settings model
viewSingle2 : ViewSettings a -> Model a -> Html (Msg a)
viewSingle2 cfg model =
viewSingle2 : ViewSettings a -> UiSettings -> Model a -> Html (Msg a)
viewSingle2 cfg settings model =
let
renderItem item =
a
[ href "#"
, class cfg.style.item
, class (cfg.labelColor item.value settings)
, classList
[ ( cfg.style.itemActive, item.active )
, ( "font-semibold", item.selected )
@ -480,7 +481,7 @@ viewSingle2 cfg model =
, onKeyUp KeyPress
]
[ text <| (.value >> cfg.makeOption >> .text) item
, span [ class "text-gray-400 float-right" ]
, span [ class "text-gray-400 opacity-75 float-right" ]
[ text <| (.value >> cfg.makeOption >> .additional) item
]
]

View File

@ -17,6 +17,7 @@ module Comp.PeriodicQueryTaskForm exposing
)
import Comp.Basic as B
import Comp.BookmarkDropdown
import Comp.CalEventInput
import Comp.ChannelForm
import Comp.MenuBar as MB
@ -44,6 +45,7 @@ type alias Model =
, scheduleModel : Comp.CalEventInput.Model
, queryModel : Comp.PowerSearchInput.Model
, channelModel : Comp.ChannelForm.Model
, bookmarkDropdown : Comp.BookmarkDropdown.Model
, formState : FormState
, loading : Int
}
@ -75,6 +77,7 @@ type Msg
| CalEventMsg Comp.CalEventInput.Msg
| QueryMsg Comp.PowerSearchInput.Msg
| ChannelMsg Comp.ChannelForm.Msg
| BookmarkMsg Comp.BookmarkDropdown.Msg
| StartOnce
| Cancel
| RequestDelete
@ -93,11 +96,14 @@ initWith flags s =
res =
Comp.PowerSearchInput.update
(Comp.PowerSearchInput.setSearchString s.query)
(Comp.PowerSearchInput.setSearchString (Maybe.withDefault "" s.query))
Comp.PowerSearchInput.init
( cfm, cfc ) =
Comp.ChannelForm.initWith flags s.channel
( bm, bc ) =
Comp.BookmarkDropdown.init flags s.bookmark
in
( { settings = s
, enabled = s.enabled
@ -105,6 +111,7 @@ initWith flags s =
, scheduleModel = sm
, queryModel = res.model
, channelModel = cfm
, bookmarkDropdown = bm
, formState = FormStateInitial
, loading = 0
, summary = s.summary
@ -113,6 +120,7 @@ initWith flags s =
[ Cmd.map CalEventMsg sc
, Cmd.map QueryMsg res.cmd
, Cmd.map ChannelMsg cfc
, Cmd.map BookmarkMsg bc
]
)
@ -128,6 +136,9 @@ init flags ct =
( cfm, cfc ) =
Comp.ChannelForm.init flags ct
( bm, bc ) =
Comp.BookmarkDropdown.init flags Nothing
in
( { settings = Data.PeriodicQuerySettings.empty ct
, enabled = False
@ -135,6 +146,7 @@ init flags ct =
, scheduleModel = sm
, queryModel = Comp.PowerSearchInput.init
, channelModel = cfm
, bookmarkDropdown = bm
, formState = FormStateInitial
, loading = 0
, summary = Nothing
@ -142,6 +154,7 @@ init flags ct =
, Cmd.batch
[ Cmd.map CalEventMsg scmd
, Cmd.map ChannelMsg cfc
, Cmd.map BookmarkMsg bc
]
)
@ -172,27 +185,46 @@ makeSettings model =
Nothing ->
Err ValidateCalEventInvalid
queryString =
Result.fromMaybe ValidateQueryStringRequired model.queryModel.input
query =
let
qstr =
model.queryModel.input
bm =
Comp.BookmarkDropdown.getSelectedId model.bookmarkDropdown
in
case ( qstr, bm ) of
( Just _, Just _ ) ->
Result.Ok ( qstr, bm )
( Just _, Nothing ) ->
Result.Ok ( qstr, bm )
( Nothing, Just _ ) ->
Result.Ok ( qstr, bm )
( Nothing, Nothing ) ->
Result.Err ValidateQueryStringRequired
channelM =
Result.fromMaybe
ValidateChannelRequired
(Comp.ChannelForm.getChannel model.channelModel)
make timer channel query =
make timer channel q =
{ prev
| enabled = model.enabled
, schedule = Data.CalEvent.makeEvent timer
, summary = model.summary
, channel = channel
, query = query
, query = Tuple.first q
, bookmark = Tuple.second q
}
in
Result.map3 make
schedule_
channelM
queryString
query
withValidSettings : (PeriodicQuerySettings -> Action) -> Model -> UpdateResult
@ -257,6 +289,17 @@ update flags msg model =
, sub = Sub.none
}
BookmarkMsg lm ->
let
( bm, bc ) =
Comp.BookmarkDropdown.update lm model.bookmarkDropdown
in
{ model = { model | bookmarkDropdown = bm }
, action = NoAction
, cmd = Cmd.map BookmarkMsg bc
, sub = Sub.none
}
ToggleEnabled ->
{ model =
{ model
@ -344,9 +387,14 @@ view texts extraClasses settings model =
(Comp.PowerSearchInput.viewResult [] model.queryModel)
]
formHeader txt =
formHeader txt req =
h2 [ class S.formHeader, class "mt-2" ]
[ text txt
, if req then
B.inputRequired
else
span [] []
]
in
div
@ -438,23 +486,29 @@ view texts extraClasses settings model =
]
]
, div [ class "mb-4" ]
[ formHeader (texts.channelHeader (Comp.ChannelForm.channelType model.channelModel))
[ formHeader (texts.channelHeader (Comp.ChannelForm.channelType model.channelModel)) False
, Html.map ChannelMsg
(Comp.ChannelForm.view texts.channelForm settings model.channelModel)
]
, div [ class "mb-4" ]
[ formHeader texts.queryLabel
, label
[ for "sharequery"
, class S.inputLabel
[ formHeader texts.queryLabel True
, div [ class "mb-3" ]
[ label [ class S.inputLabel ]
[ text "Bookmark" ]
, Html.map BookmarkMsg (Comp.BookmarkDropdown.view texts.bookmarkDropdown settings model.bookmarkDropdown)
]
[ text texts.queryLabel
, B.inputRequired
, div [ class "mb-3" ]
[ label
[ for "sharequery"
, class S.inputLabel
]
[ text texts.queryLabel
]
, queryInput
]
, queryInput
]
, div [ class "mb-4" ]
[ formHeader texts.schedule
[ formHeader texts.schedule False
, label [ class S.inputLabel ]
[ text texts.schedule
, B.inputRequired

View File

@ -20,6 +20,7 @@ import Comp.Basic as B
import Comp.Dropdown
import Data.DropdownStyle as DS
import Data.Flags exposing (Flags)
import Data.UiSettings exposing (UiSettings)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput)
@ -126,8 +127,8 @@ update _ msg model =
--- View2
view2 : Texts -> Model -> Html Msg
view2 texts model =
view2 : Texts -> UiSettings -> Model -> Html Msg
view2 texts settings model =
let
categoryCfg =
{ makeOption = \s -> Comp.Dropdown.mkOption s
@ -170,6 +171,7 @@ view2 texts model =
, Html.map CatMsg
(Comp.Dropdown.viewSingle2
categoryCfg
settings
model.catDropdown
)
]

View File

@ -24,6 +24,7 @@ import Comp.TagTable
import Comp.YesNoDimmer
import Data.Flags exposing (Flags)
import Data.TagOrder exposing (TagOrder)
import Data.UiSettings exposing (UiSettings)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onSubmit)
@ -247,13 +248,13 @@ update flags msg model =
--- View2
view2 : Texts -> Model -> Html Msg
view2 texts model =
view2 : Texts -> UiSettings -> Model -> Html Msg
view2 texts settings model =
if model.viewMode == Table then
viewTable2 texts model
else
viewForm2 texts model
viewForm2 texts settings model
viewTable2 : Texts -> Model -> Html Msg
@ -290,8 +291,8 @@ viewTable2 texts model =
]
viewForm2 : Texts -> Model -> Html Msg
viewForm2 texts model =
viewForm2 : Texts -> UiSettings -> Model -> Html Msg
viewForm2 texts settings model =
let
newTag =
model.tagFormModel.tag.id == ""
@ -373,7 +374,7 @@ viewForm2 texts model =
FormErrorSubmit m ->
text m
]
, Html.map FormMsg (Comp.TagForm.view2 texts.tagForm model.tagFormModel)
, Html.map FormMsg (Comp.TagForm.view2 texts.tagForm settings model.tagFormModel)
, B.loadingDimmer
{ active = model.loading
, label = texts.basics.loading

View File

@ -18,7 +18,8 @@ type alias PeriodicQuerySettings =
, enabled : Bool
, summary : Maybe String
, channel : NotificationChannel
, query : String
, query : Maybe String
, bookmark : Maybe String
, schedule : String
}
@ -29,19 +30,21 @@ empty ct =
, enabled = False
, summary = Nothing
, channel = Data.NotificationChannel.empty ct
, query = ""
, query = Nothing
, bookmark = Nothing
, schedule = ""
}
decoder : D.Decoder PeriodicQuerySettings
decoder =
D.map6 PeriodicQuerySettings
D.map7 PeriodicQuerySettings
(D.field "id" D.string)
(D.field "enabled" D.bool)
(D.field "summary" (D.maybe D.string))
(D.maybe (D.field "summary" D.string))
(D.field "channel" Data.NotificationChannel.decoder)
(D.field "query" D.string)
(D.maybe (D.field "query" D.string))
(D.maybe (D.field "bookmark" D.string))
(D.field "schedule" D.string)
@ -52,6 +55,7 @@ encode s =
, ( "enabled", E.bool s.enabled )
, ( "summary", Maybe.map E.string s.summary |> Maybe.withDefault E.null )
, ( "channel", Data.NotificationChannel.encode s.channel )
, ( "query", E.string s.query )
, ( "query", Maybe.map E.string s.query |> Maybe.withDefault E.null )
, ( "bookmark", Maybe.map E.string s.bookmark |> Maybe.withDefault E.null )
, ( "schedule", E.string s.schedule )
]

View File

@ -0,0 +1,43 @@
{-
Copyright 2020 Eike K. & Contributors
SPDX-License-Identifier: AGPL-3.0-or-later
-}
module Messages.Comp.BookmarkDropdown exposing
( Texts
, de
, gb
)
import Messages.Basics
type alias Texts =
{ basics : Messages.Basics.Texts
, placeholder : String
, personal : String
, collective : String
, share : String
}
gb : Texts
gb =
{ basics = Messages.Basics.gb
, placeholder = "Bookmark"
, personal = "Personal"
, collective = "Collective"
, share = "Share"
}
de : Texts
de =
{ basics = Messages.Basics.de
, placeholder = "Bookmark"
, personal = "Persönlich"
, collective = "Kollektiv"
, share = "Freigabe"
}

View File

@ -33,7 +33,7 @@ gb =
, userLocationText = "The bookmarked query is just for you"
, collectiveLocation = "Collective scope"
, collectiveLocationText = "The bookmarked query can be used and edited by all users"
, nameExistsWarning = "A bookmark with this name exists!"
, nameExistsWarning = "A bookmark with this name exists! Choose another name."
}
@ -45,5 +45,5 @@ de =
, userLocationText = "Der Bookmark ist nur für dich"
, collectiveLocation = "Kollektiv-Bookmark"
, collectiveLocationText = "Der Bookmark kann von allen Benutzer verwendet werden"
, nameExistsWarning = "Der Bookmark existiert bereits!"
, nameExistsWarning = "Der Bookmark existiert bereits! Verwende einen anderen Namen."
}

View File

@ -14,6 +14,7 @@ module Messages.Comp.PeriodicQueryTaskForm exposing
import Data.ChannelType exposing (ChannelType)
import Http
import Messages.Basics
import Messages.Comp.BookmarkDropdown
import Messages.Comp.CalEventInput
import Messages.Comp.ChannelForm
import Messages.Comp.HttpError
@ -24,6 +25,7 @@ type alias Texts =
{ basics : Messages.Basics.Texts
, calEventInput : Messages.Comp.CalEventInput.Texts
, channelForm : Messages.Comp.ChannelForm.Texts
, bookmarkDropdown : Messages.Comp.BookmarkDropdown.Texts
, httpError : Http.Error -> String
, reallyDeleteTask : String
, startOnce : String
@ -49,6 +51,7 @@ gb =
, calEventInput = Messages.Comp.CalEventInput.gb
, channelForm = Messages.Comp.ChannelForm.gb
, httpError = Messages.Comp.HttpError.gb
, bookmarkDropdown = Messages.Comp.BookmarkDropdown.gb
, reallyDeleteTask = "Really delete this notification task?"
, startOnce = "Start Once"
, startTaskNow = "Start this task now"
@ -66,7 +69,7 @@ gb =
, invalidCalEvent = "The calendar event is not valid."
, queryLabel = "Query"
, channelRequired = "A valid channel must be given."
, queryStringRequired = "A query string must be supplied"
, queryStringRequired = "A query string and/or bookmark must be supplied"
, channelHeader = \ct -> "Connection details for " ++ Messages.Data.ChannelType.gb ct
}
@ -77,6 +80,7 @@ de =
, calEventInput = Messages.Comp.CalEventInput.de
, channelForm = Messages.Comp.ChannelForm.de
, httpError = Messages.Comp.HttpError.de
, bookmarkDropdown = Messages.Comp.BookmarkDropdown.de
, reallyDeleteTask = "Diesen Benachrichtigungsauftrag wirklich löschen?"
, startOnce = "Jetzt starten"
, startTaskNow = "Starte den Auftrag sofort"
@ -94,6 +98,6 @@ de =
, invalidCalEvent = "Das Kalenderereignis ist nicht gültig."
, queryLabel = "Abfrage"
, channelRequired = "Ein Versandkanal muss angegeben werden."
, queryStringRequired = "Eine Suchabfrage muss angegeben werden."
, queryStringRequired = "Eine Suchabfrage und/oder ein Bookmark muss angegeben werden."
, channelHeader = \ct -> "Details für " ++ Messages.Data.ChannelType.de ct
}

View File

@ -146,7 +146,7 @@ viewContent texts flags settings model =
]
(case model.currentTab of
Just TagTab ->
viewTags texts model
viewTags texts settings model
Just EquipTab ->
viewEquip texts model
@ -180,8 +180,8 @@ menuEntryActive model tab =
class ""
viewTags : Texts -> Model -> List (Html Msg)
viewTags texts model =
viewTags : Texts -> UiSettings -> Model -> List (Html Msg)
viewTags texts settings model =
[ h2
[ class S.header1
, class "inline-flex items-center"
@ -194,6 +194,7 @@ viewTags texts model =
, Html.map TagManageMsg
(Comp.TagManage.view2
texts.tagManage
settings
model.tagManageModel
)
]