mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-22 02:18:26 +00:00
Refresh item after addon is done
This commit is contained in:
@ -83,7 +83,7 @@ object OJob {
|
|||||||
else
|
else
|
||||||
pubsub.publish1IgnoreErrors(
|
pubsub.publish1IgnoreErrors(
|
||||||
JobDone.topic,
|
JobDone.topic,
|
||||||
JobDone(job.id, job.group, job.task, job.args, JobState.Cancelled)
|
JobDone(job.id, job.group, job.task, job.args, JobState.Cancelled, None)
|
||||||
)
|
)
|
||||||
} yield JobCancelResult.removed
|
} yield JobCancelResult.removed
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@ import docspell.pubsub.api.PubSubT
|
|||||||
import docspell.restserver.ws.OutputEvent
|
import docspell.restserver.ws.OutputEvent
|
||||||
import docspell.scheduler.msg.{JobDone, JobSubmitted}
|
import docspell.scheduler.msg.{JobDone, JobSubmitted}
|
||||||
|
|
||||||
|
import io.circe.parser
|
||||||
|
|
||||||
/** Subscribes to those events from docspell that are forwarded to the websocket endpoints
|
/** Subscribes to those events from docspell that are forwarded to the websocket endpoints
|
||||||
*/
|
*/
|
||||||
object Subscriptions {
|
object Subscriptions {
|
||||||
@ -27,7 +29,14 @@ object Subscriptions {
|
|||||||
def jobDone[F[_]](pubSub: PubSubT[F]): Stream[F, OutputEvent] =
|
def jobDone[F[_]](pubSub: PubSubT[F]): Stream[F, OutputEvent] =
|
||||||
pubSub
|
pubSub
|
||||||
.subscribe(JobDone.topic)
|
.subscribe(JobDone.topic)
|
||||||
.map(m => OutputEvent.JobDone(m.body.group, m.body.task))
|
.map(m =>
|
||||||
|
OutputEvent.JobDone(
|
||||||
|
m.body.group,
|
||||||
|
m.body.task,
|
||||||
|
parser.parse(m.body.args).toOption,
|
||||||
|
m.body.result
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def jobSubmitted[F[_]](pubSub: PubSubT[F]): Stream[F, OutputEvent] =
|
def jobSubmitted[F[_]](pubSub: PubSubT[F]): Stream[F, OutputEvent] =
|
||||||
pubSub
|
pubSub
|
||||||
|
@ -40,12 +40,20 @@ object OutputEvent {
|
|||||||
Msg("job-submitted", task).asJson
|
Msg("job-submitted", task).asJson
|
||||||
}
|
}
|
||||||
|
|
||||||
final case class JobDone(group: Ident, task: Ident) extends OutputEvent {
|
final case class JobDone(
|
||||||
|
group: Ident,
|
||||||
|
task: Ident,
|
||||||
|
args: Option[Json],
|
||||||
|
result: Option[Json]
|
||||||
|
) extends OutputEvent {
|
||||||
def forCollective(token: AuthToken): Boolean =
|
def forCollective(token: AuthToken): Boolean =
|
||||||
token.account.collective == group
|
token.account.collective == group
|
||||||
|
|
||||||
def asJson: Json =
|
def asJson: Json =
|
||||||
Msg("job-done", task).asJson
|
Msg(
|
||||||
|
"job-done",
|
||||||
|
Map("task" -> task.asJson, "args" -> args.asJson, "result" -> result.asJson)
|
||||||
|
).asJson
|
||||||
}
|
}
|
||||||
|
|
||||||
final case class JobsWaiting(collective: Ident, count: Int) extends OutputEvent {
|
final case class JobsWaiting(collective: Ident, count: Int) extends OutputEvent {
|
||||||
|
@ -10,7 +10,7 @@ import docspell.common._
|
|||||||
import docspell.pubsub.api.{Topic, TypedTopic}
|
import docspell.pubsub.api.{Topic, TypedTopic}
|
||||||
|
|
||||||
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
|
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
|
||||||
import io.circe.{Decoder, Encoder}
|
import io.circe.{Decoder, Encoder, Json}
|
||||||
|
|
||||||
/** Message to notify about finished jobs. They have a final state. */
|
/** Message to notify about finished jobs. They have a final state. */
|
||||||
final case class JobDone(
|
final case class JobDone(
|
||||||
@ -18,7 +18,8 @@ final case class JobDone(
|
|||||||
group: Ident,
|
group: Ident,
|
||||||
task: Ident,
|
task: Ident,
|
||||||
args: String,
|
args: String,
|
||||||
state: JobState
|
state: JobState,
|
||||||
|
result: Option[Json]
|
||||||
)
|
)
|
||||||
object JobDone {
|
object JobDone {
|
||||||
implicit val jsonDecoder: Decoder[JobDone] =
|
implicit val jsonDecoder: Decoder[JobDone] =
|
||||||
|
@ -231,7 +231,7 @@ final class SchedulerImpl[F[_]: Async](
|
|||||||
_ <- Sync[F].whenA(JobState.isDone(finishState))(
|
_ <- Sync[F].whenA(JobState.isDone(finishState))(
|
||||||
pubSub.publish1IgnoreErrors(
|
pubSub.publish1IgnoreErrors(
|
||||||
JobDone.topic,
|
JobDone.topic,
|
||||||
JobDone(job.id, job.group, job.task, job.args, finishState)
|
JobDone(job.id, job.group, job.task, job.args, finishState, result.json)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
_ <- Sync[F].whenA(JobState.isDone(finishState))(
|
_ <- Sync[F].whenA(JobState.isDone(finishState))(
|
||||||
|
@ -314,13 +314,16 @@ updateWithSub msg model =
|
|||||||
|
|
||||||
ReceiveWsMessage data ->
|
ReceiveWsMessage data ->
|
||||||
case data of
|
case data of
|
||||||
Ok (JobDone task) ->
|
Ok (JobDone details) ->
|
||||||
let
|
let
|
||||||
isProcessItem =
|
isProcessItem =
|
||||||
task == "process-item"
|
details.task == "process-item"
|
||||||
|
|
||||||
isDownloadZip =
|
isDownloadZip =
|
||||||
task == "download-query-zip"
|
details.task == "download-query-zip"
|
||||||
|
|
||||||
|
isAddonExistingItem =
|
||||||
|
Data.ServerEvent.isAddonExistingItem model.itemDetailModel.detail.item.id details
|
||||||
|
|
||||||
newModel =
|
newModel =
|
||||||
{ model
|
{ model
|
||||||
@ -337,6 +340,9 @@ updateWithSub msg model =
|
|||||||
else if Page.isDashboardPage model.page && isProcessItem then
|
else if Page.isDashboardPage model.page && isProcessItem then
|
||||||
updateDashboard texts Page.Dashboard.Data.reloadDashboardData newModel
|
updateDashboard texts Page.Dashboard.Data.reloadDashboardData newModel
|
||||||
|
|
||||||
|
else if Page.isDetailPage model.page && isAddonExistingItem then
|
||||||
|
updateItemDetail texts (Page.ItemDetail.Data.ReloadItem True) newModel
|
||||||
|
|
||||||
else
|
else
|
||||||
( newModel, Cmd.none, Sub.none )
|
( newModel, Cmd.none, Sub.none )
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ view texts model =
|
|||||||
, a
|
, a
|
||||||
[ class S.successMessageLink
|
[ class S.successMessageLink
|
||||||
, href "#"
|
, href "#"
|
||||||
, onClick ReloadItem
|
, onClick (ReloadItem False)
|
||||||
]
|
]
|
||||||
[ text texts.refreshNow
|
[ text texts.refreshNow
|
||||||
]
|
]
|
||||||
|
@ -281,7 +281,7 @@ initSelectViewModel =
|
|||||||
|
|
||||||
type Msg
|
type Msg
|
||||||
= ToggleMenu
|
= ToggleMenu
|
||||||
| ReloadItem
|
| ReloadItem Bool
|
||||||
| Init
|
| Init
|
||||||
| SetItem ItemDetail
|
| SetItem ItemDetail
|
||||||
| SetActiveAttachment Int
|
| SetActiveAttachment Int
|
||||||
|
@ -387,12 +387,22 @@ update inav env msg model =
|
|||||||
resultModel
|
resultModel
|
||||||
{ model | menuOpen = not model.menuOpen }
|
{ model | menuOpen = not model.menuOpen }
|
||||||
|
|
||||||
ReloadItem ->
|
ReloadItem withFile ->
|
||||||
if model.item.id == "" then
|
if model.item.id == "" then
|
||||||
resultModel model
|
resultModel model
|
||||||
|
|
||||||
else
|
else
|
||||||
resultModelCmd ( model, Api.itemDetail env.flags model.item.id GetItemResp )
|
resultModelCmd
|
||||||
|
( model
|
||||||
|
, Cmd.batch
|
||||||
|
[ Api.itemDetail env.flags model.item.id GetItemResp
|
||||||
|
, if withFile then
|
||||||
|
Ports.refreshFileView "ds-pdf-view-iframe"
|
||||||
|
|
||||||
|
else
|
||||||
|
Cmd.none
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
FolderDropdownMsg m ->
|
FolderDropdownMsg m ->
|
||||||
let
|
let
|
||||||
@ -1002,7 +1012,7 @@ update inav env msg model =
|
|||||||
|
|
||||||
DeleteAttachResp (Ok res) ->
|
DeleteAttachResp (Ok res) ->
|
||||||
if res.success then
|
if res.success then
|
||||||
update inav env ReloadItem model
|
update inav env (ReloadItem False) model
|
||||||
|
|
||||||
else
|
else
|
||||||
resultModel model
|
resultModel model
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
-}
|
-}
|
||||||
|
|
||||||
|
|
||||||
module Data.ServerEvent exposing (AddonInfo, ServerEvent(..), decode)
|
module Data.ServerEvent exposing (AddonInfo, JobDoneDetails, ServerEvent(..), decode, isAddonExistingItem)
|
||||||
|
|
||||||
import Json.Decode as D
|
import Json.Decode as D
|
||||||
import Json.Decode.Pipeline as P
|
import Json.Decode.Pipeline as P
|
||||||
@ -13,7 +13,7 @@ import Json.Decode.Pipeline as P
|
|||||||
|
|
||||||
type ServerEvent
|
type ServerEvent
|
||||||
= JobSubmitted String
|
= JobSubmitted String
|
||||||
| JobDone String
|
| JobDone JobDoneDetails
|
||||||
| JobsWaiting Int
|
| JobsWaiting Int
|
||||||
| AddonInstalled AddonInfo
|
| AddonInstalled AddonInfo
|
||||||
|
|
||||||
@ -26,6 +26,32 @@ type alias AddonInfo =
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type alias JobDoneDetails =
|
||||||
|
{ task : String
|
||||||
|
, args : Maybe D.Value
|
||||||
|
, result : Maybe D.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| Return wether the job done details belong to running an addon of
|
||||||
|
that item with the given id.
|
||||||
|
-}
|
||||||
|
isAddonExistingItem : String -> JobDoneDetails -> Bool
|
||||||
|
isAddonExistingItem itemId details =
|
||||||
|
let
|
||||||
|
itemIdDecoder =
|
||||||
|
D.field "itemId" D.string
|
||||||
|
|
||||||
|
-- This decodes the structure from scalas ItemAddonTaskArgs (only itemId)
|
||||||
|
decodedId =
|
||||||
|
Maybe.map (D.decodeValue itemIdDecoder) details.args
|
||||||
|
|> Maybe.andThen Result.toMaybe
|
||||||
|
in
|
||||||
|
details.task
|
||||||
|
== "addon-existing-item"
|
||||||
|
&& (itemId /= "" && decodedId == Just itemId)
|
||||||
|
|
||||||
|
|
||||||
addonInfoDecoder : D.Decoder AddonInfo
|
addonInfoDecoder : D.Decoder AddonInfo
|
||||||
addonInfoDecoder =
|
addonInfoDecoder =
|
||||||
D.succeed AddonInfo
|
D.succeed AddonInfo
|
||||||
@ -51,8 +77,7 @@ decodeTag : String -> D.Decoder ServerEvent
|
|||||||
decodeTag tag =
|
decodeTag tag =
|
||||||
case tag of
|
case tag of
|
||||||
"job-done" ->
|
"job-done" ->
|
||||||
D.field "content" D.string
|
D.field "content" (D.map JobDone decodeJobDoneDetails)
|
||||||
|> D.map JobDone
|
|
||||||
|
|
||||||
"job-submitted" ->
|
"job-submitted" ->
|
||||||
D.field "content" D.string
|
D.field "content" D.string
|
||||||
@ -68,3 +93,11 @@ decodeTag tag =
|
|||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
D.fail ("Unknown tag: " ++ tag)
|
D.fail ("Unknown tag: " ++ tag)
|
||||||
|
|
||||||
|
|
||||||
|
decodeJobDoneDetails : D.Decoder JobDoneDetails
|
||||||
|
decodeJobDoneDetails =
|
||||||
|
D.map3 JobDoneDetails
|
||||||
|
(D.field "task" D.string)
|
||||||
|
(D.field "args" (D.maybe D.value))
|
||||||
|
(D.field "result" (D.maybe D.value))
|
||||||
|
@ -14,6 +14,7 @@ module Page exposing
|
|||||||
, hasSidebar
|
, hasSidebar
|
||||||
, href
|
, href
|
||||||
, isDashboardPage
|
, isDashboardPage
|
||||||
|
, isDetailPage
|
||||||
, isOpen
|
, isOpen
|
||||||
, isSearchPage
|
, isSearchPage
|
||||||
, isSecured
|
, isSecured
|
||||||
@ -175,6 +176,16 @@ isDashboardPage page =
|
|||||||
False
|
False
|
||||||
|
|
||||||
|
|
||||||
|
isDetailPage : Page -> Bool
|
||||||
|
isDetailPage page =
|
||||||
|
case page of
|
||||||
|
ItemDetailPage _ ->
|
||||||
|
True
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
False
|
||||||
|
|
||||||
|
|
||||||
pageName : Page -> String
|
pageName : Page -> String
|
||||||
pageName page =
|
pageName page =
|
||||||
case page of
|
case page of
|
||||||
|
@ -38,6 +38,7 @@ type Msg
|
|||||||
| ItemResp (Result Http.Error ItemDetail)
|
| ItemResp (Result Http.Error ItemDetail)
|
||||||
| ScrollResult (Result Dom.Error ())
|
| ScrollResult (Result Dom.Error ())
|
||||||
| UiSettingsUpdated
|
| UiSettingsUpdated
|
||||||
|
| ReloadItem Bool
|
||||||
|
|
||||||
|
|
||||||
type alias UpdateResult =
|
type alias UpdateResult =
|
||||||
|
@ -47,6 +47,13 @@ update inav env msg model =
|
|||||||
, selectedItems = env.selectedItems
|
, selectedItems = env.selectedItems
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReloadItem withFiles ->
|
||||||
|
let
|
||||||
|
m =
|
||||||
|
ItemDetailMsg (Comp.ItemDetail.Model.ReloadItem withFiles)
|
||||||
|
in
|
||||||
|
update inav env m model
|
||||||
|
|
||||||
ItemDetailMsg lmsg ->
|
ItemDetailMsg lmsg ->
|
||||||
let
|
let
|
||||||
result =
|
result =
|
||||||
|
@ -11,6 +11,7 @@ port module Ports exposing
|
|||||||
, printElement
|
, printElement
|
||||||
, receiveCheckQueryResult
|
, receiveCheckQueryResult
|
||||||
, receiveServerEvent
|
, receiveServerEvent
|
||||||
|
, refreshFileView
|
||||||
, removeAccount
|
, removeAccount
|
||||||
, setAccount
|
, setAccount
|
||||||
, setUiTheme
|
, setUiTheme
|
||||||
@ -54,6 +55,11 @@ port printElement : String -> Cmd msg
|
|||||||
port receiveWsMessage : (D.Value -> msg) -> Sub msg
|
port receiveWsMessage : (D.Value -> msg) -> Sub msg
|
||||||
|
|
||||||
|
|
||||||
|
{-| Given an ID of an element that is either EMBED or IFRAME the js will reload its src
|
||||||
|
-}
|
||||||
|
port refreshFileView : String -> Cmd msg
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Higher level functions based on ports
|
--- Higher level functions based on ports
|
||||||
|
|
||||||
|
@ -121,6 +121,16 @@ elmApp.ports.printElement.subscribe(function(id) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
elmApp.ports.refreshFileView.subscribe(function(id) {
|
||||||
|
var el = document.getElementById(id);
|
||||||
|
if (el) {
|
||||||
|
var tag = el.tagName;
|
||||||
|
if (tag === "EMBED" || tag === "IFRAME") {
|
||||||
|
var url = el.src;
|
||||||
|
el.src = url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var dsWebSocket = null;
|
var dsWebSocket = null;
|
||||||
function closeWS() {
|
function closeWS() {
|
||||||
@ -146,6 +156,8 @@ function initWS() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Websockets are not used yet for communicating to the server
|
||||||
// elmApp.ports.sendWsMessage.subscribe(function(msg) {
|
// elmApp.ports.sendWsMessage.subscribe(function(msg) {
|
||||||
// socket.send(msg);
|
// socket.send(msg);
|
||||||
// });
|
// });
|
||||||
|
Reference in New Issue
Block a user