From 29a589488401bbf013c47621a9ba4b3429f1b890 Mon Sep 17 00:00:00 2001 From: eikek Date: Wed, 18 May 2022 00:23:46 +0200 Subject: [PATCH] Refresh item after addon is done --- .../scala/docspell/backend/ops/OJob.scala | 2 +- .../docspell/restserver/Subscriptions.scala | 11 ++++- .../docspell/restserver/ws/OutputEvent.scala | 12 +++++- .../docspell/scheduler/msg/JobDone.scala | 5 ++- .../scheduler/impl/SchedulerImpl.scala | 2 +- modules/webapp/src/main/elm/App/Update.elm | 12 ++++-- .../main/elm/Comp/ItemDetail/AddFilesForm.elm | 2 +- .../src/main/elm/Comp/ItemDetail/Model.elm | 2 +- .../src/main/elm/Comp/ItemDetail/Update.elm | 16 ++++++-- .../webapp/src/main/elm/Data/ServerEvent.elm | 41 +++++++++++++++++-- modules/webapp/src/main/elm/Page.elm | 11 +++++ .../src/main/elm/Page/ItemDetail/Data.elm | 1 + .../src/main/elm/Page/ItemDetail/Update.elm | 7 ++++ modules/webapp/src/main/elm/Ports.elm | 6 +++ modules/webapp/src/main/webjar/docspell.js | 12 ++++++ 15 files changed, 123 insertions(+), 19 deletions(-) diff --git a/modules/backend/src/main/scala/docspell/backend/ops/OJob.scala b/modules/backend/src/main/scala/docspell/backend/ops/OJob.scala index a5d88d35..ba848c7f 100644 --- a/modules/backend/src/main/scala/docspell/backend/ops/OJob.scala +++ b/modules/backend/src/main/scala/docspell/backend/ops/OJob.scala @@ -83,7 +83,7 @@ object OJob { else pubsub.publish1IgnoreErrors( 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 diff --git a/modules/restserver/src/main/scala/docspell/restserver/Subscriptions.scala b/modules/restserver/src/main/scala/docspell/restserver/Subscriptions.scala index 8ffa7045..ced60b68 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/Subscriptions.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/Subscriptions.scala @@ -14,6 +14,8 @@ import docspell.pubsub.api.PubSubT import docspell.restserver.ws.OutputEvent import docspell.scheduler.msg.{JobDone, JobSubmitted} +import io.circe.parser + /** Subscribes to those events from docspell that are forwarded to the websocket endpoints */ object Subscriptions { @@ -27,7 +29,14 @@ object Subscriptions { def jobDone[F[_]](pubSub: PubSubT[F]): Stream[F, OutputEvent] = pubSub .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] = pubSub diff --git a/modules/restserver/src/main/scala/docspell/restserver/ws/OutputEvent.scala b/modules/restserver/src/main/scala/docspell/restserver/ws/OutputEvent.scala index 5ac68c48..f41dd5de 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/ws/OutputEvent.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/ws/OutputEvent.scala @@ -40,12 +40,20 @@ object OutputEvent { 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 = token.account.collective == group 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 { diff --git a/modules/scheduler/api/src/main/scala/docspell/scheduler/msg/JobDone.scala b/modules/scheduler/api/src/main/scala/docspell/scheduler/msg/JobDone.scala index 61eff3f9..3bd79832 100644 --- a/modules/scheduler/api/src/main/scala/docspell/scheduler/msg/JobDone.scala +++ b/modules/scheduler/api/src/main/scala/docspell/scheduler/msg/JobDone.scala @@ -10,7 +10,7 @@ import docspell.common._ import docspell.pubsub.api.{Topic, TypedTopic} 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. */ final case class JobDone( @@ -18,7 +18,8 @@ final case class JobDone( group: Ident, task: Ident, args: String, - state: JobState + state: JobState, + result: Option[Json] ) object JobDone { implicit val jsonDecoder: Decoder[JobDone] = diff --git a/modules/scheduler/impl/src/main/scala/docspell/scheduler/impl/SchedulerImpl.scala b/modules/scheduler/impl/src/main/scala/docspell/scheduler/impl/SchedulerImpl.scala index c114e2bc..db6fc5f1 100644 --- a/modules/scheduler/impl/src/main/scala/docspell/scheduler/impl/SchedulerImpl.scala +++ b/modules/scheduler/impl/src/main/scala/docspell/scheduler/impl/SchedulerImpl.scala @@ -231,7 +231,7 @@ final class SchedulerImpl[F[_]: Async]( _ <- Sync[F].whenA(JobState.isDone(finishState))( pubSub.publish1IgnoreErrors( 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))( diff --git a/modules/webapp/src/main/elm/App/Update.elm b/modules/webapp/src/main/elm/App/Update.elm index 67b9f416..9ca103ed 100644 --- a/modules/webapp/src/main/elm/App/Update.elm +++ b/modules/webapp/src/main/elm/App/Update.elm @@ -314,13 +314,16 @@ updateWithSub msg model = ReceiveWsMessage data -> case data of - Ok (JobDone task) -> + Ok (JobDone details) -> let isProcessItem = - task == "process-item" + details.task == "process-item" isDownloadZip = - task == "download-query-zip" + details.task == "download-query-zip" + + isAddonExistingItem = + Data.ServerEvent.isAddonExistingItem model.itemDetailModel.detail.item.id details newModel = { model @@ -337,6 +340,9 @@ updateWithSub msg model = else if Page.isDashboardPage model.page && isProcessItem then updateDashboard texts Page.Dashboard.Data.reloadDashboardData newModel + else if Page.isDetailPage model.page && isAddonExistingItem then + updateItemDetail texts (Page.ItemDetail.Data.ReloadItem True) newModel + else ( newModel, Cmd.none, Sub.none ) diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/AddFilesForm.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/AddFilesForm.elm index 8f6b3baf..074b6a31 100644 --- a/modules/webapp/src/main/elm/Comp/ItemDetail/AddFilesForm.elm +++ b/modules/webapp/src/main/elm/Comp/ItemDetail/AddFilesForm.elm @@ -76,7 +76,7 @@ view texts model = , a [ class S.successMessageLink , href "#" - , onClick ReloadItem + , onClick (ReloadItem False) ] [ text texts.refreshNow ] diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/Model.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/Model.elm index e01db9d7..8f33119e 100644 --- a/modules/webapp/src/main/elm/Comp/ItemDetail/Model.elm +++ b/modules/webapp/src/main/elm/Comp/ItemDetail/Model.elm @@ -281,7 +281,7 @@ initSelectViewModel = type Msg = ToggleMenu - | ReloadItem + | ReloadItem Bool | Init | SetItem ItemDetail | SetActiveAttachment Int diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm index 48871ecb..5128c5c9 100644 --- a/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm +++ b/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm @@ -387,12 +387,22 @@ update inav env msg model = resultModel { model | menuOpen = not model.menuOpen } - ReloadItem -> + ReloadItem withFile -> if model.item.id == "" then resultModel model 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 -> let @@ -1002,7 +1012,7 @@ update inav env msg model = DeleteAttachResp (Ok res) -> if res.success then - update inav env ReloadItem model + update inav env (ReloadItem False) model else resultModel model diff --git a/modules/webapp/src/main/elm/Data/ServerEvent.elm b/modules/webapp/src/main/elm/Data/ServerEvent.elm index 57320996..b7d68d75 100644 --- a/modules/webapp/src/main/elm/Data/ServerEvent.elm +++ b/modules/webapp/src/main/elm/Data/ServerEvent.elm @@ -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.Pipeline as P @@ -13,7 +13,7 @@ import Json.Decode.Pipeline as P type ServerEvent = JobSubmitted String - | JobDone String + | JobDone JobDoneDetails | JobsWaiting Int | 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.succeed AddonInfo @@ -51,8 +77,7 @@ decodeTag : String -> D.Decoder ServerEvent decodeTag tag = case tag of "job-done" -> - D.field "content" D.string - |> D.map JobDone + D.field "content" (D.map JobDone decodeJobDoneDetails) "job-submitted" -> D.field "content" D.string @@ -68,3 +93,11 @@ decodeTag 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)) diff --git a/modules/webapp/src/main/elm/Page.elm b/modules/webapp/src/main/elm/Page.elm index 5799fdac..69adaa4a 100644 --- a/modules/webapp/src/main/elm/Page.elm +++ b/modules/webapp/src/main/elm/Page.elm @@ -14,6 +14,7 @@ module Page exposing , hasSidebar , href , isDashboardPage + , isDetailPage , isOpen , isSearchPage , isSecured @@ -175,6 +176,16 @@ isDashboardPage page = False +isDetailPage : Page -> Bool +isDetailPage page = + case page of + ItemDetailPage _ -> + True + + _ -> + False + + pageName : Page -> String pageName page = case page of diff --git a/modules/webapp/src/main/elm/Page/ItemDetail/Data.elm b/modules/webapp/src/main/elm/Page/ItemDetail/Data.elm index 3d0111a4..2133f697 100644 --- a/modules/webapp/src/main/elm/Page/ItemDetail/Data.elm +++ b/modules/webapp/src/main/elm/Page/ItemDetail/Data.elm @@ -38,6 +38,7 @@ type Msg | ItemResp (Result Http.Error ItemDetail) | ScrollResult (Result Dom.Error ()) | UiSettingsUpdated + | ReloadItem Bool type alias UpdateResult = diff --git a/modules/webapp/src/main/elm/Page/ItemDetail/Update.elm b/modules/webapp/src/main/elm/Page/ItemDetail/Update.elm index f8ef0541..5547a169 100644 --- a/modules/webapp/src/main/elm/Page/ItemDetail/Update.elm +++ b/modules/webapp/src/main/elm/Page/ItemDetail/Update.elm @@ -47,6 +47,13 @@ update inav env msg model = , selectedItems = env.selectedItems } + ReloadItem withFiles -> + let + m = + ItemDetailMsg (Comp.ItemDetail.Model.ReloadItem withFiles) + in + update inav env m model + ItemDetailMsg lmsg -> let result = diff --git a/modules/webapp/src/main/elm/Ports.elm b/modules/webapp/src/main/elm/Ports.elm index 09ff2b44..61c5d99e 100644 --- a/modules/webapp/src/main/elm/Ports.elm +++ b/modules/webapp/src/main/elm/Ports.elm @@ -11,6 +11,7 @@ port module Ports exposing , printElement , receiveCheckQueryResult , receiveServerEvent + , refreshFileView , removeAccount , setAccount , setUiTheme @@ -54,6 +55,11 @@ port printElement : String -> Cmd 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 diff --git a/modules/webapp/src/main/webjar/docspell.js b/modules/webapp/src/main/webjar/docspell.js index 3176e162..8a9d24f6 100644 --- a/modules/webapp/src/main/webjar/docspell.js +++ b/modules/webapp/src/main/webjar/docspell.js @@ -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; function closeWS() { @@ -146,6 +156,8 @@ function initWS() { } }); } + +// Websockets are not used yet for communicating to the server // elmApp.ports.sendWsMessage.subscribe(function(msg) { // socket.send(msg); // });