mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-05 22:55:58 +00:00
Implement scan-mailbox form and routes
This commit is contained in:
parent
0d6677f90b
commit
6e8582ea80
@ -11,6 +11,18 @@ import docspell.common._
|
|||||||
|
|
||||||
trait OUserTask[F[_]] {
|
trait OUserTask[F[_]] {
|
||||||
|
|
||||||
|
/** Return the settings for the scan-mailbox task of the current user.
|
||||||
|
* There is at most one such task per user.
|
||||||
|
*/
|
||||||
|
def getScanMailbox(account: AccountId): F[UserTask[ScanMailboxArgs]]
|
||||||
|
|
||||||
|
/** Updates the scan-mailbox tasks and notifies the joex nodes.
|
||||||
|
*/
|
||||||
|
def submitScanMailbox(
|
||||||
|
account: AccountId,
|
||||||
|
task: UserTask[ScanMailboxArgs]
|
||||||
|
): F[Unit]
|
||||||
|
|
||||||
/** Return the settings for the notify-due-items task of the current
|
/** Return the settings for the notify-due-items task of the current
|
||||||
* user. There is at most one such task per user.
|
* user. There is at most one such task per user.
|
||||||
*/
|
*/
|
||||||
@ -51,6 +63,20 @@ object OUserTask {
|
|||||||
_ <- joex.notifyAllNodes
|
_ <- joex.notifyAllNodes
|
||||||
} yield ()
|
} yield ()
|
||||||
|
|
||||||
|
def getScanMailbox(account: AccountId): F[UserTask[ScanMailboxArgs]] =
|
||||||
|
store
|
||||||
|
.getOneByName[ScanMailboxArgs](account, ScanMailboxArgs.taskName)
|
||||||
|
.getOrElseF(scanMailboxDefault(account))
|
||||||
|
|
||||||
|
def submitScanMailbox(
|
||||||
|
account: AccountId,
|
||||||
|
task: UserTask[ScanMailboxArgs]
|
||||||
|
): F[Unit] =
|
||||||
|
for {
|
||||||
|
_ <- store.updateOneTask[ScanMailboxArgs](account, task)
|
||||||
|
_ <- joex.notifyAllNodes
|
||||||
|
} yield ()
|
||||||
|
|
||||||
def getNotifyDueItems(account: AccountId): F[UserTask[NotifyDueItemsArgs]] =
|
def getNotifyDueItems(account: AccountId): F[UserTask[NotifyDueItemsArgs]] =
|
||||||
store
|
store
|
||||||
.getOneByName[NotifyDueItemsArgs](account, NotifyDueItemsArgs.taskName)
|
.getOneByName[NotifyDueItemsArgs](account, NotifyDueItemsArgs.taskName)
|
||||||
@ -86,6 +112,27 @@ object OUserTask {
|
|||||||
Nil
|
Nil
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private def scanMailboxDefault(
|
||||||
|
account: AccountId
|
||||||
|
): F[UserTask[ScanMailboxArgs]] =
|
||||||
|
for {
|
||||||
|
id <- Ident.randomId[F]
|
||||||
|
} yield UserTask(
|
||||||
|
id,
|
||||||
|
ScanMailboxArgs.taskName,
|
||||||
|
false,
|
||||||
|
CalEvent.unsafe("*-*-* 0,12:00"),
|
||||||
|
ScanMailboxArgs(
|
||||||
|
account,
|
||||||
|
Ident.unsafe(""),
|
||||||
|
Nil,
|
||||||
|
Some(Duration.hours(12)),
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,10 @@ case class Duration(nanos: Long) {
|
|||||||
|
|
||||||
def seconds: Long = millis / 1000
|
def seconds: Long = millis / 1000
|
||||||
|
|
||||||
|
def minutes: Long = seconds / 60
|
||||||
|
|
||||||
|
def hours: Long = minutes / 60
|
||||||
|
|
||||||
def toScala: FiniteDuration =
|
def toScala: FiniteDuration =
|
||||||
FiniteDuration(nanos, TimeUnit.NANOSECONDS)
|
FiniteDuration(nanos, TimeUnit.NANOSECONDS)
|
||||||
|
|
||||||
@ -55,7 +59,6 @@ object Duration {
|
|||||||
end = Timestamp.current[F]
|
end = Timestamp.current[F]
|
||||||
} yield end.map(e => Duration.millis(e.toMillis - now.toMillis))
|
} yield end.map(e => Duration.millis(e.toMillis - now.toMillis))
|
||||||
|
|
||||||
|
|
||||||
implicit val jsonEncoder: Encoder[Duration] =
|
implicit val jsonEncoder: Encoder[Duration] =
|
||||||
Encoder.encodeLong.contramap(_.millis)
|
Encoder.encodeLong.contramap(_.millis)
|
||||||
|
|
||||||
|
@ -1825,6 +1825,7 @@ components:
|
|||||||
- imapConnection
|
- imapConnection
|
||||||
- schedule
|
- schedule
|
||||||
- folders
|
- folders
|
||||||
|
- deleteMail
|
||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
@ -1843,6 +1844,7 @@ components:
|
|||||||
format: calevent
|
format: calevent
|
||||||
receivedSinceHours:
|
receivedSinceHours:
|
||||||
type: integer
|
type: integer
|
||||||
|
format: int32
|
||||||
description: |
|
description: |
|
||||||
Look only for mails newer than `receivedSinceHours' hours.
|
Look only for mails newer than `receivedSinceHours' hours.
|
||||||
targetFolder:
|
targetFolder:
|
||||||
|
@ -76,6 +76,7 @@ object RestServer {
|
|||||||
"email/settings" -> MailSettingsRoutes(restApp.backend, token),
|
"email/settings" -> MailSettingsRoutes(restApp.backend, token),
|
||||||
"email/sent" -> SentMailRoutes(restApp.backend, token),
|
"email/sent" -> SentMailRoutes(restApp.backend, token),
|
||||||
"usertask/notifydueitems" -> NotifyDueItemsRoutes(cfg, restApp.backend, token),
|
"usertask/notifydueitems" -> NotifyDueItemsRoutes(cfg, restApp.backend, token),
|
||||||
|
"usertask/scanmailbox" -> ScanMailboxRoutes(restApp.backend, token),
|
||||||
"calevent/check" -> CalEventCheckRoutes()
|
"calevent/check" -> CalEventCheckRoutes()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -0,0 +1,103 @@
|
|||||||
|
package docspell.restserver.routes
|
||||||
|
|
||||||
|
import cats.effect._
|
||||||
|
import cats.implicits._
|
||||||
|
import org.http4s._
|
||||||
|
import org.http4s.dsl.Http4sDsl
|
||||||
|
import org.http4s.circe.CirceEntityEncoder._
|
||||||
|
import org.http4s.circe.CirceEntityDecoder._
|
||||||
|
|
||||||
|
import docspell.backend.BackendApp
|
||||||
|
import docspell.backend.auth.AuthToken
|
||||||
|
import docspell.common._
|
||||||
|
import docspell.restapi.model._
|
||||||
|
import docspell.store.usertask._
|
||||||
|
import docspell.restserver.conv.Conversions
|
||||||
|
|
||||||
|
object ScanMailboxRoutes {
|
||||||
|
|
||||||
|
def apply[F[_]: Effect](
|
||||||
|
backend: BackendApp[F],
|
||||||
|
user: AuthToken
|
||||||
|
): HttpRoutes[F] = {
|
||||||
|
val dsl = new Http4sDsl[F] {}
|
||||||
|
val ut = backend.userTask
|
||||||
|
import dsl._
|
||||||
|
|
||||||
|
HttpRoutes.of {
|
||||||
|
case req @ POST -> Root / "startonce" =>
|
||||||
|
for {
|
||||||
|
data <- req.as[ScanMailboxSettings]
|
||||||
|
task = makeTask(user.account, data)
|
||||||
|
res <-
|
||||||
|
ut.executeNow(user.account, task)
|
||||||
|
.attempt
|
||||||
|
.map(Conversions.basicResult(_, "Submitted successfully."))
|
||||||
|
resp <- Ok(res)
|
||||||
|
} yield resp
|
||||||
|
|
||||||
|
case GET -> Root =>
|
||||||
|
for {
|
||||||
|
task <- ut.getScanMailbox(user.account)
|
||||||
|
res <- taskToSettings(user.account, backend, task)
|
||||||
|
resp <- Ok(res)
|
||||||
|
} yield resp
|
||||||
|
|
||||||
|
case req @ POST -> Root =>
|
||||||
|
for {
|
||||||
|
data <- req.as[ScanMailboxSettings]
|
||||||
|
task = makeTask(user.account, data)
|
||||||
|
res <-
|
||||||
|
ut.submitScanMailbox(user.account, task)
|
||||||
|
.attempt
|
||||||
|
.map(Conversions.basicResult(_, "Saved successfully."))
|
||||||
|
resp <- Ok(res)
|
||||||
|
} yield resp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def makeTask(
|
||||||
|
user: AccountId,
|
||||||
|
settings: ScanMailboxSettings
|
||||||
|
): UserTask[ScanMailboxArgs] =
|
||||||
|
UserTask(
|
||||||
|
settings.id,
|
||||||
|
ScanMailboxArgs.taskName,
|
||||||
|
settings.enabled,
|
||||||
|
settings.schedule,
|
||||||
|
ScanMailboxArgs(
|
||||||
|
user,
|
||||||
|
settings.imapConnection,
|
||||||
|
settings.folders,
|
||||||
|
settings.receivedSinceHours.map(_.toLong).map(Duration.hours),
|
||||||
|
settings.targetFolder,
|
||||||
|
settings.deleteMail,
|
||||||
|
settings.direction
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def taskToSettings[F[_]: Sync](
|
||||||
|
account: AccountId,
|
||||||
|
backend: BackendApp[F],
|
||||||
|
task: UserTask[ScanMailboxArgs]
|
||||||
|
): F[ScanMailboxSettings] =
|
||||||
|
for {
|
||||||
|
conn <-
|
||||||
|
backend.mail
|
||||||
|
.getImapSettings(account, None)
|
||||||
|
.map(
|
||||||
|
_.find(_.name == task.args.imapConnection)
|
||||||
|
.map(_.name)
|
||||||
|
)
|
||||||
|
} yield ScanMailboxSettings(
|
||||||
|
task.id,
|
||||||
|
task.enabled,
|
||||||
|
conn.getOrElse(Ident.unsafe("")),
|
||||||
|
task.args.folders, //folders
|
||||||
|
task.timer,
|
||||||
|
task.args.receivedSince.map(_.hours.toInt),
|
||||||
|
task.args.targetFolder,
|
||||||
|
task.args.deleteMail,
|
||||||
|
task.args.direction
|
||||||
|
)
|
||||||
|
}
|
@ -14,16 +14,18 @@ import Api.Model.Tag exposing (Tag)
|
|||||||
import Api.Model.TagList exposing (TagList)
|
import Api.Model.TagList exposing (TagList)
|
||||||
import Comp.CalEventInput
|
import Comp.CalEventInput
|
||||||
import Comp.Dropdown
|
import Comp.Dropdown
|
||||||
import Comp.EmailInput
|
|
||||||
import Comp.IntField
|
import Comp.IntField
|
||||||
|
import Comp.StringListInput
|
||||||
import Data.CalEvent exposing (CalEvent)
|
import Data.CalEvent exposing (CalEvent)
|
||||||
|
import Data.Direction exposing (Direction(..))
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.Validated exposing (Validated(..))
|
import Data.Validated exposing (Validated(..))
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onCheck, onClick)
|
import Html.Events exposing (onCheck, onClick, onInput)
|
||||||
import Http
|
import Http
|
||||||
import Util.Http
|
import Util.Http
|
||||||
|
import Util.List
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
import Util.Update
|
import Util.Update
|
||||||
|
|
||||||
@ -32,6 +34,13 @@ type alias Model =
|
|||||||
{ settings : ScanMailboxSettings
|
{ settings : ScanMailboxSettings
|
||||||
, connectionModel : Comp.Dropdown.Model String
|
, connectionModel : Comp.Dropdown.Model String
|
||||||
, enabled : Bool
|
, enabled : Bool
|
||||||
|
, deleteMail : Bool
|
||||||
|
, receivedHours : Maybe Int
|
||||||
|
, receivedHoursModel : Comp.IntField.Model
|
||||||
|
, targetFolder : Maybe String
|
||||||
|
, foldersModel : Comp.StringListInput.Model
|
||||||
|
, folders : List String
|
||||||
|
, direction : Maybe Direction
|
||||||
, schedule : Validated CalEvent
|
, schedule : Validated CalEvent
|
||||||
, scheduleModel : Comp.CalEventInput.Model
|
, scheduleModel : Comp.CalEventInput.Model
|
||||||
, formMsg : Maybe BasicResult
|
, formMsg : Maybe BasicResult
|
||||||
@ -44,10 +53,15 @@ type Msg
|
|||||||
| ConnMsg (Comp.Dropdown.Msg String)
|
| ConnMsg (Comp.Dropdown.Msg String)
|
||||||
| ConnResp (Result Http.Error ImapSettingsList)
|
| ConnResp (Result Http.Error ImapSettingsList)
|
||||||
| ToggleEnabled
|
| ToggleEnabled
|
||||||
|
| ToggleDeleteMail
|
||||||
| CalEventMsg Comp.CalEventInput.Msg
|
| CalEventMsg Comp.CalEventInput.Msg
|
||||||
| SetScanMailboxSettings (Result Http.Error ScanMailboxSettings)
|
| SetScanMailboxSettings (Result Http.Error ScanMailboxSettings)
|
||||||
| SubmitResp (Result Http.Error BasicResult)
|
| SubmitResp (Result Http.Error BasicResult)
|
||||||
| StartOnce
|
| StartOnce
|
||||||
|
| ReceivedHoursMsg Comp.IntField.Msg
|
||||||
|
| SetTargetFolder String
|
||||||
|
| FoldersMsg Comp.StringListInput.Msg
|
||||||
|
| DirectionMsg (Maybe Direction)
|
||||||
|
|
||||||
|
|
||||||
initCmd : Flags -> Cmd Msg
|
initCmd : Flags -> Cmd Msg
|
||||||
@ -74,10 +88,17 @@ init flags =
|
|||||||
, placeholder = "Select connection..."
|
, placeholder = "Select connection..."
|
||||||
}
|
}
|
||||||
, enabled = False
|
, enabled = False
|
||||||
|
, deleteMail = False
|
||||||
|
, receivedHours = Nothing
|
||||||
|
, receivedHoursModel = Comp.IntField.init (Just 1) Nothing True "Received Since Hours"
|
||||||
|
, foldersModel = Comp.StringListInput.init
|
||||||
|
, folders = []
|
||||||
|
, targetFolder = Nothing
|
||||||
|
, direction = Nothing
|
||||||
, schedule = initialSchedule
|
, schedule = initialSchedule
|
||||||
, scheduleModel = sm
|
, scheduleModel = sm
|
||||||
, formMsg = Nothing
|
, formMsg = Nothing
|
||||||
, loading = 3
|
, loading = 2
|
||||||
}
|
}
|
||||||
, Cmd.batch
|
, Cmd.batch
|
||||||
[ initCmd flags
|
[ initCmd flags
|
||||||
@ -102,16 +123,29 @@ makeSettings model =
|
|||||||
|> Maybe.map Valid
|
|> Maybe.map Valid
|
||||||
|> Maybe.withDefault (Invalid [ "Connection missing" ] "")
|
|> Maybe.withDefault (Invalid [ "Connection missing" ] "")
|
||||||
|
|
||||||
make smtp timer =
|
infolders =
|
||||||
|
if model.folders == [] then
|
||||||
|
Invalid [ "No folders given" ] []
|
||||||
|
|
||||||
|
else
|
||||||
|
Valid model.folders
|
||||||
|
|
||||||
|
make smtp timer folders =
|
||||||
{ prev
|
{ prev
|
||||||
| imapConnection = smtp
|
| imapConnection = smtp
|
||||||
, enabled = model.enabled
|
, enabled = model.enabled
|
||||||
|
, receivedSinceHours = model.receivedHours
|
||||||
|
, deleteMail = model.deleteMail
|
||||||
|
, targetFolder = model.targetFolder
|
||||||
|
, folders = folders
|
||||||
|
, direction = Maybe.map Data.Direction.toString model.direction
|
||||||
, schedule = Data.CalEvent.makeEvent timer
|
, schedule = Data.CalEvent.makeEvent timer
|
||||||
}
|
}
|
||||||
in
|
in
|
||||||
Data.Validated.map2 make
|
Data.Validated.map3 make
|
||||||
conn
|
conn
|
||||||
model.schedule
|
model.schedule
|
||||||
|
infolders
|
||||||
|
|
||||||
|
|
||||||
withValidSettings : (ScanMailboxSettings -> Cmd Msg) -> Model -> ( Model, Cmd Msg )
|
withValidSettings : (ScanMailboxSettings -> Cmd Msg) -> Model -> ( Model, Cmd Msg )
|
||||||
@ -211,6 +245,60 @@ update flags msg model =
|
|||||||
, Cmd.none
|
, Cmd.none
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ToggleDeleteMail ->
|
||||||
|
( { model
|
||||||
|
| deleteMail = not model.deleteMail
|
||||||
|
, formMsg = Nothing
|
||||||
|
}
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
ReceivedHoursMsg m ->
|
||||||
|
let
|
||||||
|
( pm, val ) =
|
||||||
|
Comp.IntField.update m model.receivedHoursModel
|
||||||
|
in
|
||||||
|
( { model
|
||||||
|
| receivedHoursModel = pm
|
||||||
|
, receivedHours = val
|
||||||
|
, formMsg = Nothing
|
||||||
|
}
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
SetTargetFolder str ->
|
||||||
|
( { model | targetFolder = Util.Maybe.fromString str }
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
FoldersMsg lm ->
|
||||||
|
let
|
||||||
|
( fm, itemAction ) =
|
||||||
|
Comp.StringListInput.update lm model.foldersModel
|
||||||
|
|
||||||
|
newList =
|
||||||
|
case itemAction of
|
||||||
|
Comp.StringListInput.AddAction s ->
|
||||||
|
Util.List.distinct (s :: model.folders)
|
||||||
|
|
||||||
|
Comp.StringListInput.RemoveAction s ->
|
||||||
|
List.filter (\e -> e /= s) model.folders
|
||||||
|
|
||||||
|
Comp.StringListInput.NoAction ->
|
||||||
|
model.folders
|
||||||
|
in
|
||||||
|
( { model
|
||||||
|
| foldersModel = fm
|
||||||
|
, folders = newList
|
||||||
|
}
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
DirectionMsg md ->
|
||||||
|
( { model | direction = md }
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
SetScanMailboxSettings (Ok s) ->
|
SetScanMailboxSettings (Ok s) ->
|
||||||
let
|
let
|
||||||
imap =
|
imap =
|
||||||
@ -234,7 +322,12 @@ update flags msg model =
|
|||||||
( { nm
|
( { nm
|
||||||
| settings = s
|
| settings = s
|
||||||
, enabled = s.enabled
|
, enabled = s.enabled
|
||||||
|
, deleteMail = s.deleteMail
|
||||||
|
, receivedHours = s.receivedSinceHours
|
||||||
|
, targetFolder = s.targetFolder
|
||||||
|
, folders = s.folders
|
||||||
, schedule = Data.Validated.Unknown newSchedule
|
, schedule = Data.Validated.Unknown newSchedule
|
||||||
|
, direction = Maybe.andThen Data.Direction.fromString s.direction
|
||||||
, scheduleModel = sm
|
, scheduleModel = sm
|
||||||
, formMsg = Nothing
|
, formMsg = Nothing
|
||||||
, loading = model.loading - 1
|
, loading = model.loading - 1
|
||||||
@ -313,13 +406,110 @@ view extraClasses model =
|
|||||||
[ text "Loading..."
|
[ text "Loading..."
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
, div [ class "inline field" ]
|
||||||
|
[ div [ class "ui checkbox" ]
|
||||||
|
[ input
|
||||||
|
[ type_ "checkbox"
|
||||||
|
, onCheck (\_ -> ToggleEnabled)
|
||||||
|
, checked model.enabled
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, label [] [ text "Enabled" ]
|
||||||
|
]
|
||||||
|
, span [ class "small-info" ]
|
||||||
|
[ text "Enable or disable this task."
|
||||||
|
]
|
||||||
|
]
|
||||||
, div [ class "required field" ]
|
, div [ class "required field" ]
|
||||||
[ label [] [ text "Send via" ]
|
[ label [] [ text "Mailbox" ]
|
||||||
, Html.map ConnMsg (Comp.Dropdown.view model.connectionModel)
|
, Html.map ConnMsg (Comp.Dropdown.view model.connectionModel)
|
||||||
, span [ class "small-info" ]
|
, span [ class "small-info" ]
|
||||||
[ text "The IMAP connection to use when sending notification mails."
|
[ text "The IMAP connection to use when sending notification mails."
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
, div [ class "required field" ]
|
||||||
|
[ label [] [ text "Folders" ]
|
||||||
|
, Html.map FoldersMsg (Comp.StringListInput.view model.folders model.foldersModel)
|
||||||
|
, span [ class "small-info" ]
|
||||||
|
[ text "The folders to go through"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, Html.map ReceivedHoursMsg
|
||||||
|
(Comp.IntField.viewWithInfo
|
||||||
|
"Select mails newer than `now - receivedHours`"
|
||||||
|
model.receivedHours
|
||||||
|
"field"
|
||||||
|
model.receivedHoursModel
|
||||||
|
)
|
||||||
|
, div [ class "field" ]
|
||||||
|
[ label [] [ text "Target folder" ]
|
||||||
|
, input
|
||||||
|
[ type_ "text"
|
||||||
|
, onInput SetTargetFolder
|
||||||
|
, Maybe.withDefault "" model.targetFolder |> value
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, span [ class "small-info" ]
|
||||||
|
[ text "Move all mails successfully submitted into this folder."
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, div [ class "inline field" ]
|
||||||
|
[ div [ class "ui checkbox" ]
|
||||||
|
[ input
|
||||||
|
[ type_ "checkbox"
|
||||||
|
, onCheck (\_ -> ToggleDeleteMail)
|
||||||
|
, checked model.deleteMail
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, label [] [ text "Delete imported mails" ]
|
||||||
|
]
|
||||||
|
, span [ class "small-info" ]
|
||||||
|
[ text "Whether to delete all mails successfully imported into docspell."
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, div [ class "required field" ]
|
||||||
|
[ label [] [ text "Item direction" ]
|
||||||
|
, div [ class "grouped fields" ]
|
||||||
|
[ div [ class "field" ]
|
||||||
|
[ div [ class "ui radio checkbox" ]
|
||||||
|
[ input
|
||||||
|
[ type_ "radio"
|
||||||
|
, checked (model.direction == Nothing)
|
||||||
|
, onCheck (\_ -> DirectionMsg Nothing)
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, label [] [ text "Automatic" ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, div [ class "field" ]
|
||||||
|
[ div [ class "ui radio checkbox" ]
|
||||||
|
[ input
|
||||||
|
[ type_ "radio"
|
||||||
|
, checked (model.direction == Just Incoming)
|
||||||
|
, onCheck (\_ -> DirectionMsg (Just Incoming))
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, label [] [ text "Incoming" ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, div [ class "field" ]
|
||||||
|
[ div [ class "ui radio checkbox" ]
|
||||||
|
[ input
|
||||||
|
[ type_ "radio"
|
||||||
|
, checked (model.direction == Just Outgoing)
|
||||||
|
, onCheck (\_ -> DirectionMsg (Just Outgoing))
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, label [] [ text "Outgoing" ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, span [ class "small-info" ]
|
||||||
|
[ text "Sets the direction for an item. If you know all mails are incoming or "
|
||||||
|
, text "outgoing, you can set it here. Otherwise it will be guessed from looking "
|
||||||
|
, text "at sender and receiver."
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
, div [ class "required field" ]
|
, div [ class "required field" ]
|
||||||
[ label []
|
[ label []
|
||||||
[ text "Schedule"
|
[ text "Schedule"
|
||||||
|
98
modules/webapp/src/main/elm/Comp/StringListInput.elm
Normal file
98
modules/webapp/src/main/elm/Comp/StringListInput.elm
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
module Comp.StringListInput exposing
|
||||||
|
( ItemAction(..)
|
||||||
|
, Model
|
||||||
|
, Msg
|
||||||
|
, init
|
||||||
|
, update
|
||||||
|
, view
|
||||||
|
)
|
||||||
|
|
||||||
|
import Html exposing (..)
|
||||||
|
import Html.Attributes exposing (..)
|
||||||
|
import Html.Events exposing (onClick, onInput)
|
||||||
|
import Util.Maybe
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ currentInput : String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= AddString
|
||||||
|
| RemoveString String
|
||||||
|
| SetString String
|
||||||
|
|
||||||
|
|
||||||
|
init : Model
|
||||||
|
init =
|
||||||
|
{ currentInput = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- Update
|
||||||
|
|
||||||
|
|
||||||
|
type ItemAction
|
||||||
|
= AddAction String
|
||||||
|
| RemoveAction String
|
||||||
|
| NoAction
|
||||||
|
|
||||||
|
|
||||||
|
update : Msg -> Model -> ( Model, ItemAction )
|
||||||
|
update msg model =
|
||||||
|
case msg of
|
||||||
|
SetString str ->
|
||||||
|
( { model | currentInput = str }
|
||||||
|
, NoAction
|
||||||
|
)
|
||||||
|
|
||||||
|
AddString ->
|
||||||
|
( { model | currentInput = "" }
|
||||||
|
, Util.Maybe.fromString model.currentInput
|
||||||
|
|> Maybe.map AddAction
|
||||||
|
|> Maybe.withDefault NoAction
|
||||||
|
)
|
||||||
|
|
||||||
|
RemoveString s ->
|
||||||
|
( model, RemoveAction s )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
|
view : List String -> Model -> Html Msg
|
||||||
|
view values model =
|
||||||
|
let
|
||||||
|
valueItem s =
|
||||||
|
div [ class "item" ]
|
||||||
|
[ a
|
||||||
|
[ class "ui icon link"
|
||||||
|
, onClick (RemoveString s)
|
||||||
|
, href "#"
|
||||||
|
]
|
||||||
|
[ i [ class "delete icon" ] []
|
||||||
|
]
|
||||||
|
, text s
|
||||||
|
]
|
||||||
|
in
|
||||||
|
div [ class "string-list-input" ]
|
||||||
|
[ div [ class "ui list" ]
|
||||||
|
(List.map valueItem values)
|
||||||
|
, div [ class "ui icon input" ]
|
||||||
|
[ input
|
||||||
|
[ placeholder ""
|
||||||
|
, type_ "text"
|
||||||
|
, onInput SetString
|
||||||
|
, value model.currentInput
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, i
|
||||||
|
[ class "circular add link icon"
|
||||||
|
, onClick AddString
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
]
|
@ -128,7 +128,7 @@ viewNotificationForm model =
|
|||||||
viewScanMailboxForm : Model -> List (Html Msg)
|
viewScanMailboxForm : Model -> List (Html Msg)
|
||||||
viewScanMailboxForm model =
|
viewScanMailboxForm model =
|
||||||
[ h2 [ class "ui header" ]
|
[ h2 [ class "ui header" ]
|
||||||
[ i [ class "ui bullhorn icon" ] []
|
[ i [ class "ui envelope open outline icon" ] []
|
||||||
, div [ class "content" ]
|
, div [ class "content" ]
|
||||||
[ text "Scan Mailbox"
|
[ text "Scan Mailbox"
|
||||||
]
|
]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user