Created
September 15, 2022 19:59
-
-
Save realgenekim/32188389abc670a16bcd715e41245c62 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns com.example.state-machines.incrementally-loaded-report-gk | |
;(ns com.fulcrologic.rad.state-machines.incrementally-loaded-report | |
"Gene modifications to incrementally-loaded-report -- | |
This has the same interface as the 'incrementally-loaded-report' UISM included in RAD. | |
https://github.com/fulcrologic/fulcro-rad/blob/develop/src/main/com/fulcrologic/rad/state_machines/incrementally_loaded_report.cljc | |
However, instead of rendering only after the resolver resturns the entire data set, | |
it renders immediately after the first load completes, and use df/load! to asynchronously load | |
each chunk. | |
I use df/load! because it's asynchronous, versus uism/load which doesn't allow mutations | |
or changing the query-params while loading. | |
Personal note: I'm finding UISM to be a marvel. It's amazing that you can pull out | |
all the I/O stuff into UISMs, and I'm amazed that I could change the i/o behavior so easily. | |
END GENE | |
A Report state machine that will load the data in pages to prevent network timeouts for large result | |
sets. This requires a resolver that can accept :report/offset and :report/limit parameters and that returns | |
``` | |
{:report/next-offset n | |
:report/results data} | |
``` | |
where `n` is the next offset to use to get the next page of data, and `data` is a vector | |
of the results in the current fetch. | |
See incrementally-loaded-report-options for supported additional report options. | |
" | |
(:require | |
[com.fulcrologic.fulcro.algorithms.merge :as merge] | |
[com.fulcrologic.fulcro.raw.application :as app] | |
[com.fulcrologic.fulcro.raw.components :as comp] | |
[com.fulcrologic.fulcro.ui-state-machines :as uism :refer [defstatemachine]] | |
[com.fulcrologic.fulcro.data-fetch :as df] | |
[com.fulcrologic.fulcro.mutations :as m] | |
[com.fulcrologic.fulcro.raw.components :as rc] | |
[com.fulcrologic.rad.attributes :as attr] | |
[com.fulcrologic.rad.options-util :as opts :refer [?! debounce]] | |
[com.fulcrologic.rad.report :as report] | |
[com.fulcrologic.rad.routing :as rad-routing] | |
[com.fulcrologic.rad.routing.history :as history] | |
[com.fulcrologic.rad.type-support.date-time :as dt] | |
;[com.example.state-machines.tmp :as tmp] | |
[com.example.fulcro-utils :as futils] | |
[taoensso.timbre :as log])) | |
(defn start-load [env] | |
(let [Report (uism/actor-class env :actor/report) | |
report-ident (uism/actor->ident env :actor/report) | |
{::report/keys [source-attribute load-options] | |
::keys [chunk-size]} (comp/component-options Report) | |
load-options (?! load-options env) | |
current-params (-> | |
(report/current-control-parameters env) | |
(assoc | |
:report/offset 0 | |
; small chunk here to optimize for fast loading of first page | |
:report/limit 50) | |
(dissoc ::chunk-size)) | |
;(or chunk-size 50)) | |
page-path (uism/resolve-alias env :loaded-page)] | |
(log/warn :start-load "****** UISM/load!!!!") | |
(-> env | |
(uism/assoc-aliased :raw-rows []) | |
(uism/load source-attribute nil (merge | |
{:params current-params | |
::uism/ok-event :event/page-loaded | |
::uism/error-event :event/failed | |
:marker report-ident | |
:target page-path} | |
load-options)) | |
(uism/activate :state/loading)))) | |
(defn finalize-report [{::uism/keys [state-map] :as env}] | |
(let [Report (uism/actor-class env :actor/report) | |
{::report/keys [row-pk report-loaded]} (comp/component-options Report) | |
table-name (::attr/qualified-key row-pk)] | |
(-> env | |
(report/preprocess-raw-result) | |
(report/filter-rows) | |
(report/sort-rows) | |
(report/populate-current-page) | |
(uism/store :last-load-time (inst-ms (dt/now))) | |
(uism/store :raw-items-in-table (count (keys (get state-map table-name)))) | |
(uism/activate :state/gathering-parameters) | |
(cond-> report-loaded report-loaded)))) | |
(m/defmutation mutation-resume-load! | |
[params] | |
(action [{:keys [app state]}] | |
(log/warn ::mutation-resume-load! "mutation: mutation-resume-load!: ") | |
(let [; | |
{:keys [env current-params report-ident page-path load-options source-attribute | |
actor-name]} params | |
state-map @state] | |
#_(log/warn ::resume-load! :current-param current-params :load-options load-options :page-path page-path | |
:source-attribute source-attribute :load-options load-options :actor-name actor-name | |
:state-map state-map | |
:state state | |
:env env) | |
(uism/trigger! app report-ident :event/page-loaded)))) | |
(defn load-next! [{::uism/keys [state-map] :as env}] | |
(let [Report (uism/actor-class env :actor/report) | |
{::report/keys [row-pk report-loaded]} (comp/component-options Report) | |
{:report/keys [next-offset results]} (uism/alias-value env :loaded-page) | |
{::report/keys [BodyItem source-attribute load-options] | |
::keys [chunk-size]} (comp/component-options Report) | |
more? (and (number? next-offset) (pos? next-offset)) | |
current-params (assoc | |
(report/current-control-parameters env) | |
:report/offset next-offset | |
:report/limit (or chunk-size 100)) | |
report-ident (uism/actor->ident env :actor/report) | |
page-path (uism/resolve-alias env :loaded-page) | |
load-options (?! load-options env) | |
table-name (::attr/qualified-key row-pk)] | |
(log/warn :load-next! :next-offset next-offset) | |
(if more? | |
(do | |
; #object[cljs.core.Atom | |
; {:val {:load-args {:params {:api/time :year, | |
; :start-date #inst"2022-01-01T08:00:00.000-00:00", | |
; :end-date #inst"2023-01-01T08:00:00.000-00:00", | |
; :report/offset 100, | |
; :report/limit 100}, | |
; :com.fulcrologic.fulcro.ui-state-machines/ok-event :event/page-loaded, | |
; :com.fulcrologic.fulcro.ui-state-machines/error-event :event/failed, | |
(log/warn :load-next! "****** df/load!!!!") | |
(df/load! (resolve 'com.example.client/app) | |
source-attribute | |
(rc/nc ['*]) | |
;Report | |
(merge | |
{:params current-params | |
;::uism/ok-event :event/page-loaded | |
::uism/ok-event :event/ready-for-next-load | |
::uism/error-event :event/failed | |
:marker report-ident | |
:target page-path | |
:parallel true | |
:post-mutation `mutation-resume-load! | |
:post-mutation-params {:report-ident report-ident}} | |
load-options)))) | |
(-> env | |
(report/preprocess-raw-result) | |
(report/filter-rows) | |
(report/sort-rows) | |
(report/populate-current-page) | |
(uism/activate :state/gathering-parameters) | |
(cond-> report-loaded report-loaded)))) | |
(defn process-loaded-page [env] | |
(let [Report (uism/actor-class env :actor/report) | |
report-ident (uism/actor->ident env :actor/report) | |
{::report/keys [BodyItem source-attribute load-options] | |
::keys [chunk-size]} (comp/component-options Report) | |
load-options (?! load-options env) | |
{:report/keys [next-offset results]} (uism/alias-value env :loaded-page) | |
page-path (uism/resolve-alias env :loaded-page) | |
target-path (uism/resolve-alias env :raw-rows) | |
current-params (assoc | |
(report/current-control-parameters env) | |
:report/offset next-offset | |
:report/limit (or chunk-size 100)) | |
more? (and (number? next-offset) (pos? next-offset)) | |
append-results (fn [state-map] | |
(reduce | |
(fn [s item] (merge/merge-component s BodyItem item :append target-path)) | |
state-map | |
results))] | |
(log/warn :process-loaded-page "BEGIN!!! ****** uism/load") | |
#_(log/warn :process-loaded-page :current-param current-params :load-options load-options :page-path page-path | |
:source-attribute source-attribute :load-options load-options) | |
#_(log/warn :apply-action :append-results (-> env (uism/apply-action append-results))) | |
(-> env | |
(uism/apply-action append-results) | |
(uism/trigger report-ident :event/ready-for-next-load) | |
#_(cond-> | |
more? (uism/load source-attribute nil (merge | |
{:params current-params | |
;::uism/ok-event :event/page-loaded | |
::uism/ok-event :event/ready-for-next-load | |
::uism/error-event :event/failed | |
:marker report-ident | |
:target page-path} | |
load-options)) | |
(not more?) (uism/trigger report-ident :event/loaded))))) | |
(defn handle-resume-report | |
"Internal state machine implementation. Called on :event/resumt to do the steps to resume an already running report | |
that has just been re-mounted." | |
[{::uism/keys [state-map] :as env}] | |
(let [env (report/initialize-parameters env) | |
Report (uism/actor-class env :actor/report) | |
{::report/keys [load-cache-seconds | |
load-cache-expired? | |
row-pk]} (comp/component-options Report) | |
now-ms (inst-ms (dt/now)) | |
last-load-time (uism/retrieve env :last-load-time) | |
last-table-count (uism/retrieve env :raw-items-in-table) | |
cache-expiration-ms (* 1000 (or load-cache-seconds 0)) | |
table-name (::attr/qualified-key row-pk) | |
current-table-count (count (keys (get state-map table-name))) | |
cache-looks-stale? (or | |
(nil? last-load-time) | |
(not= current-table-count last-table-count) | |
(< last-load-time (- now-ms cache-expiration-ms))) | |
user-cache-expired? (?! load-cache-expired? env cache-looks-stale?) | |
cache-expired? (if (boolean user-cache-expired?) | |
user-cache-expired? | |
cache-looks-stale?)] | |
(if cache-expired? | |
(start-load env) | |
(report/handle-filter-event env)))) | |
(defn start [env] | |
(let [{::uism/keys [fulcro-app event-data]} env | |
{::report/keys [run-on-mount?]} (report/report-options env) | |
page-path (report/route-params-path env ::current-page) | |
desired-page (-> (history/current-route fulcro-app) | |
:params | |
(get-in page-path)) | |
run-now? (or desired-page run-on-mount?)] | |
(-> env | |
(uism/store :route-params (:route-params event-data)) | |
(cond-> | |
(nil? desired-page) (uism/assoc-aliased :current-page 1)) | |
(report/initialize-parameters) | |
(cond-> | |
run-now? (start-load) | |
(not run-now?) (uism/activate :state/gathering-parameters))))) | |
(defstatemachine incrementally-loaded-machine | |
(-> report/report-machine | |
(assoc-in [::uism/aliases :loaded-page] [:actor/report :ui/incremental-page]) | |
(assoc-in [::uism/states :initial ::uism/handler] start) | |
(assoc-in [::uism/states :state/loading ::uism/events :event/page-loaded ::uism/handler] process-loaded-page) | |
(assoc-in [::uism/states :state/loading ::uism/events :event/ready-for-next-load ::uism/handler] load-next!) | |
(assoc-in [::uism/states :state/loading ::uism/events :event/loaded ::uism/handler] finalize-report) | |
(assoc-in [::uism/states :state/gathering-parameters ::uism/events :event/run ::uism/handler] start-load) | |
(assoc-in [::uism/states :state/gathering-parameters ::uism/events :event/resume ::uism/handler] handle-resume-report) | |
(assoc-in [::uism/states :state/gathering-parameters ::uism/events :event/page-loaded ::uism/handler] process-loaded-page) | |
(assoc-in [::uism/states :state/gathering-parameters ::uism/events :event/loaded ::uism/handler] finalize-report) | |
(assoc-in [::uism/states :state/gathering-parameters ::uism/events :event/ready-for-next-load ::uism/handler] load-next!))) | |
(defn raw-loaded-item-count | |
"Returns the count of raw loaded items when given the props of the report. Can be used for progress reporting of | |
the load/refresh/" | |
[report-instance] | |
(let [state-map (app/current-state report-instance) | |
path (conj (comp/get-ident report-instance) :ui/loaded-data)] | |
(count (get-in state-map path)))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment