   (e.g. showing a dialog that has user-driven recovery choices).
 
 
-  ### Use case - Lazy loading
-
-  The other major case is wanting to load data in response to a user interaction. Interestingly, the query that you might
-  have used in the initial load use case might have included UI queries for data you didn't want to fetch yet. So, we want
-  to note that the initial load use-case supports eliding part of the query. For example, you can load an item without,
-  say, comments. Later, when the user wants to see comments you can supply a button that can load the comments on demand.
-
-  This is directly supported by `load-field`, which derives the query to send to the server from the component itself!
-
-  ```
-  (load-field this :comments)
-  ```
-
-  The only requirements are that the component has an Ident and the query for the component includes a join or property
-  named `:comments` in the query.
-
-  For example, say you had:
-
-  ```
-  (defui Item
-     static om/IQuery
-     (query [this] [:id :value {:comments (om/get-query Comment)}])
-     static om/Ident
-     (ident [this props] [:item/by-id (:id props)])
-     Object
-     (render [this]
-        ...
-        (dom/button #js { :onClick #(df/load-field this :comments) } \"Load comments\")
-        ...)
-  ```
-
-  then clicking the button will result in the following query to the server:
-
-  ```
-  [{[:item/by-id 32] [{:comments [:other :props]]}]
-  ```
-
-  and the code to write for the server is now trivial. The dispatch key is :item/by-id, the 32 is accessible on the AST,
-  and the query is a pull fragment that will work relative to an item in your (assuming Datomic) database!
-
-  Furthermore, the underlying code can easily put a marker in place of that data in the app state so you can show the
-  'load in progress' marker of your choice.
-
-  Untangled has supplied all of the Om plumbing for you.
-
-  #### How reads work : `untangled/load`
-
-  The helper functions described above simply trigger a built-in Untangled mutation called `untangled/load`, which you are
-  allowed (and sometimes encouraged) to use directly. It is the Untangled method of doing follow-on reads after a remote
-  mutation:
-
-  ```
-  (om/transact! this '[(app/do-some-thing) (untangled/load {:query [:a]})])
-  ```
-
-  The normal form of follow-on keywords (for re-rendering the UI) works fine, it will just never trigger remote
-  reads.
-
-  The `untangled/load` mutation does a very simple thing: It puts a state marker in a well-known location in your app state
-  to indicate that you're wanting to load something (and returns `:remote true`). This causes the network
-  plumbing to be triggered. The network plumbing only receives mutations that are marked remote, so it does the following:
-
-  - It looks for the special mutations `untangled/load` and `tx/fallback`. The latter is part of the unhappy path handling.
-     - For each load, it places a state marker in the app state at the target destination for the query data
-     - All loads that are present are combined together into a single Om query
-  - It looks for other mutations
-  - It puts the 'other mutations' on the send queue
-  - It puts the derived query from the `untangled/load` onto the send queue
-
-  A separate \"thread\" (core async go block) watches the send queue, and sends things one-at-a-time (e.g. each entry
-  in the queue is processed in a sequence, ensuring you can reason about things sequentially). The one-at-a-time
-  semantics are very important for handling tempid reassignment, rational optimistic updates, and unhappy path handling.
-
-  The send processing block (uses core async to make a thread-like infinite loop):
-
-  - Pulls an item from the queue (or \"blocks\" when empty)
-  - Sends it over the network
-  - Updates the marker in the app state to `loading` (which causes a re-render, so you can render loading UI)
-  - \"waits\" for the response
-      - On success: merges the data
-      - On error: updates the state marker to an error state (which re-renders allowing the UI to show error UI)
-  - Repeats in an infinite loop
-
-  #### Using `untangled/load` directly
-
-  TODO: See the helper functions `load-data` and `load-field`. We might add more specific versions of `untangled/load`
-  that provide a clearer end-user API.
-
-  ### Remote reads after a mutation
-
-  In Om, you can list properties after your mutation to indicate re-renders. You can force them to be remote reads by
-  quoting them. All of this requires complex logic in your parser to compare flags on the AST, process the resulting
-  query in send (e.g. via process-roots), etc. It is more flexible, but the very common case can be made a lot more direct.
-
-  ```
-  (om/transact! this '[(app/f) ':thing]) ; run mutation and re-read thing on the server...
-  ; BUT YOU implement the logic to make sure it is understood that way!
-  ```
-
-  In Untangled, follow-on keywords are always local re-render reads, and nothing more:
-
-  ```
-  (om/transact! this '[(app/f) :thing]) ; Om and Untangled: Do mutation, and re-render anything that has :thing in a query
-  ```
-
-  Instead, we supply access to the internal mutation we use to queue loads, so that remote reads are simple and explicit:
-
-  ```
-  ; Do mutation, then run a remote read of the given query, along with a post-mutation to alter app state when the load is complete
-  (om/transact! this `[(app/f) (untangled/load {:query ~(om/get-query Thing) :post-mutation after-load-sym}])
-  ```
-
-  Of course, you can (and *should) use syntax quoting to embed a query from (om/get-query) so that normalization works,
-  and the post-mutation can be used to deal with the fact that other parts of the UI may want to, for example, point
-  to this newly-loaded thing. The `after-load-sym` is a symbol (e.g. dispatch key to the mutate multimethod). The multi-method
-  is guaranteed to be called with the app state in the environment, but no other Om environment items are ensured at
-  the moment.
-
-
   ### Global network activity marker
 
-  TODO: Document how to use the network activity marker to show a general purpose loading marker in the UI. Basically query
-  to the top-level (via an Om query link).
+  Untangled will automatically maintain a global network activity marker at the top level of the app state under the
+  keyword `:ui/loading-data`. This key will have a `true` value when there are network requests awaiting a response from
+  the server, and will have a `false` value when there are no network requests in process.
+
+  You can access this marker from any component that composes to root by including a link in your component's query:
+
+  ```
+  (defui Item ... )
+  (def ui-item (om/factory Item {:keyfn :id}))
+
+  (defui List
+    static om/IQuery (query [this] [:id :title {:items (om/get-query Item)} [:ui/loading-data '_]]
+    ...
+    (render [this]
+      (let [{:keys [title items ui/loading-data]} (om/props this)]
+        (if (and loading-data (empty? items))
+          (dom/div nil \"Loading...\")
+          (dom/div nil
+            (dom/h1 nil title)
+            (map ui-item items))))
+  ```
+  Because the global loading marker is at the top level of the application state, do not use the keyword as a follow-on
+  read to mutations because it may unnecessarily trigger a re-render of the entire application.
 ")

  - However, if you do it, Untangled merges with the following rules:
      - If the query *asks* for an attribute, and the *response does not include it*, then it is always removed from the app state since the
      server has clearly indicated it is gone.
      - The the query *does not ask* for an attribute (which means the response cannot possibly contain it), then Untangled
      will avoid removing it, even if other attributes come back (e.g. it will be a merge leaving the property that was
      not asked for alone). This does indicate that your UI is possibly in a state inconsistent with the server, which
      is the reason for the \"avoid this case\" advice.


  ## Reads

  Remote reads in Untangled are explicit calls to the load functions in the `untangled.client.data-fetch` namespace, of
  which there are two pairs:

  1. `load-data` and `load-data-action`
  2. `load-field` and `load-field-action`

  ### Load Data vs. Load Field

  Both of these function families are calls to the built-in `untangled/load` mutation, so requests made by either function
  go through the same networking layer and have the same customizations through their named parameters (see the doc strings
  in the [data-fetch namespace](https://github.com/untangled-web/untangled-client/blob/master/src/untangled/client/data_fetch.cljs)).
  The only difference is that `load-data` must be passed a complete query while `load-field` uses the passed-in component
  to create its query.

  So, `load-field` focuses the component's query to the specified field, associates the component's ident with the query,
  and asks the UI to re-render all components with the calling component's ident after the load is complete. Since `load-field`
  requires a component to build the query sent to the server, it cannot be used with the reconciler. If you want to load data
  from your server when the app initially loads, you must use `load-data` with the reconciler passed to the
  `:started-callback` function when creating a new untangled client application.

  #### Use case - Lazy loading

  One common operation is to load data in response to a user interaction. Interestingly, the query that you might
  have used in the initial load use case might have included UI queries for data you didn't want to fetch yet. So, we want
  to note that the initial load use-case supports eliding part of the query. For example, you can load an item without,
  say, comments. Later, when the user wants to see comments you can supply a button that can load the comments on demand.

  This is directly supported by `load-field`, which derives the query to send to the server from the component itself!

  ```
  (load-field this :comments)
  ```

  The only requirements are that the component has an Ident and the query for the component includes a join or property
  named `:comments` in the query.

  For example, say you had:

  ```
  (defui Item
     static om/IQuery
     (query [this] [:id :value {:comments (om/get-query Comment)}])
     static om/Ident
     (ident [this props] [:item/by-id (:id props)])
     Object
     (render [this]
        ...
        (dom/button #js { :onClick #(df/load-field this :comments) } \"Load comments\")
        ...)

  ;; Note that the call to load field above is the same as:
      (df/load-data this (om/full-query this [:comments])
        :ident (om/get-ident this)
        :refresh [(om/get-ident this)])
  ```

  then clicking the button will result in the following query to the server:

  ```
  [{[:item/by-id 32] [{:comments [:other :props]]}]
  ```

  and the code to write for the server is now trivial. The dispatch key is :item/by-id, the 32 is accessible on the AST,
  and the query is a pull fragment that will work relative to an item in your (assuming Datomic) database!

  Furthermore, the underlying code can easily put a marker in place of that data in the app state so you can show the
  'load in progress' marker of your choice.

  Untangled has supplied all of the Om plumbing for you.

  ### Load vs. Load-Action

  Both `load-field` and `load-data` will call `om/transact!` under the hood, targeting untangled's built-in `untangled/load`
  mutation, which is responsible for sending your request to the server. By contrast, `load-field-action` and `load-data-action`,
  **do not** call `om/transact!`, and are used to initialize a load inside of one of your custom client-side mutations.

  Let's look at an example of a standard load. Say you want to load a list of people from the server:
  ```
  (require [untangled.client.data-fetch :as df])

  (defui Person
    static om/IQuery (query [this] [:id :name :ui/fetch-state])
    ... )
  (def ui-person (om/factory Person))

  (defui PeopleList
    static om/IQuery (query [this] [:db/id :list-title {:people (om/get-query Person}]
    static om/Ident (ident [this props] [:people-list/by-id (:db/id props)])
    Object
    (render [this]
      (let [{:keys [people]} (om/props this)]
        ;; people starts out as nil
        (dom/div nil
          (df/lazily-loaded #(map ui-person %) people
            :not-present-render #(dom/button #js {:onClick #(df/load-field this :people)}
                                   \"Load People\"))))))
  ```

  Since we are in the UI and not inside of a mutation's action thunk, we want to use `load-field` to initialize the
  call to `om/transact!`. The use of `lazily-loaded` above will show a button to load people when `people` is `nil`
  (for example, when the app initially loads), and will render each person in the list of people once the button is
  clicked and the data has been loaded. By including `:ui/fetch-state` in the subcomponent's query, `lazily-loaded`
  is able to render different UIs for ready, loading, and failure states as well. See the
  [lazy loading cookbook recipe](https://github.com/untangled-web/untangled-cookbook/tree/master/recipes/lazy-loading-visual-indicators)
  for a running example.

  The action-suffixed load functions are useful when performing an action in the user interface that must *both* modify
  the client-side database *and* load data from the server. They must be used with `untangled.client.data-fetch/remote-load`,
  which converts the remote mutation key for your mutation to the `untangled/load` key:

  ```
  (require [untangled.client.data-fetch :as df]
           [untangled.client.mutations :refer [mutate]]
           [app.ui :as ui] ;; namespace with defuis
           )

  (defmethod mutate 'app/change-view [{:keys [state] as env} _ {:keys [new-view]}]
    {:remote (df/remote-load env)
     :action (fn []
                (let [new-view-query (cond
                                       (= new-view :main) (om/get-query ui/Main)
                                       (= new-view :settings) (om/get-query ui/Settings)]
                (df/load-data-action state new-view-query)
                (swap! state update :app/current-view new-view))})
  ```
  This snippet defines a mutation that (1) modifies the app state to display the view passed in via the mutation parameters,
  and (2) loads the data for that view. A few important points:

  1. If an action thunk calls an action-suffixed load, then it MUST call `remote-load` for the remote keyword.
  2. The `remote-load` function *changes* the mutation's dispatch key to `untangled/load` when sending it to the remote.
  So, a mutation that uses an action-suffixed load cannot have a server-side implementation.
  3. If you find yourself wanting to put a call to `load-data` or `load-field` in a React Lifecycle method, try reworking
  the code to use the action-suffixed load instead. To learn more about the dangers of loads and lifecycle methods, see
  the [reference on loading data]().

  ### How reads work : `untangled/load`

  The helper functions described above simply trigger a built-in Untangled mutation called `untangled/load`, which you are
  allowed (and sometimes encouraged) to use directly. It is the Untangled method of doing follow-on reads after a remote
  mutation:

  ```
  (om/transact! this '[(app/do-some-thing) (untangled/load {:query [:a]})])
  ```

  The normal form of follow-on keywords (for re-rendering the UI) works fine, it will just never trigger remote
  reads.

  The `untangled/load` mutation does a very simple thing: It puts a state marker in a well-known location in your app state
  to indicate that you're wanting to load something (and returns `:remote true`). This causes the network
  plumbing to be triggered. The network plumbing only receives mutations that are marked remote, so it does the following:

  - It looks for the special mutations `untangled/load` and `tx/fallback`. The latter is part of the unhappy path handling.
     - For each load, it places a state marker in the app state at the target destination for the query data
     - All loads that are present are combined together into a single Om query
  - It looks for other mutations
  - It puts the 'other mutations' on the send queue
  - It puts the derived query from the `untangled/load` onto the send queue

  A separate \"thread\" (core async go block) watches the send queue, and sends things one-at-a-time (e.g. each entry
  in the queue is processed in a sequence, ensuring you can reason about things sequentially). The one-at-a-time
  semantics are very important for handling tempid reassignment, rational optimistic updates, and unhappy path handling.

  The send processing block (uses core async to make a thread-like infinite loop):

  - Pulls an item from the queue (or \"blocks\" when empty)
  - Sends it over the network
  - Updates the marker in the app state to `loading` (which causes a re-render, so you can render loading UI)
  - \"waits\" for the response
      - On success: merges the data
      - On error: updates the state marker to an error state (which re-renders allowing the UI to show error UI)
  - Repeats in an infinite loop

  ### UI attributes

