2016-11-20 20:03:17 +01:00

148 lines
3.8 KiB
Clojure

;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.main.data.undo
(:require #_[cljs.pprint :as pp]
[beicon.core :as rx]
[uxbox.util.rstore :as rs]
[uxbox.main.data.pages :as udp]
[uxbox.main.state :as st]))
;; --- Watch Page Changes
(declare save-undo-entry)
(declare save-undo-entry?)
(declare undo?)
(declare redo?)
(declare initialize-undo-for-page)
(defn watch-page-changes
"A function that starts watching for `IPageUpdate`
events emited to the global event stream and just
reacts on them emiting an other event that just
persists the state of the page in an undo stack."
[id]
(rs/emit! (initialize-undo-for-page id))
(as-> rs/stream $
(rx/filter #(satisfies? udp/IPageUpdate %) $)
(rx/filter #(not (undo? %)) $)
(rx/filter #(not (redo? %)) $)
(rx/debounce 500 $)
(rx/on-next $ #(rs/emit! (save-undo-entry id)))))
;; -- Save Undo Entry
(defrecord SaveUndoEntry [id]
rs/UpdateEvent
(-apply-update [_ state]
(let [page (udp/pack-page state id)]
(-> state
(update-in [:undo id :stack] #(cons (:data page) %))
(assoc-in [:undo id :selected] 0)))))
;; rs/EffectEvent
;; (-apply-effect [_ state]
;; (let [undo (get-in state [:undo id])]
;; (println (pr-str undo)))))
(defn save-undo-entry
[id]
(SaveUndoEntry. id))
(defn save-undo-entry?
[v]
(instance? SaveUndoEntry v))
;; --- Initialize Undo (For page)
(defrecord InitializeUndoForPage [id]
rs/WatchEvent
(-apply-watch [_ state stream]
(let [initialized? (get-in state [:undo id])
page-loaded? (get-in state [:pages id])]
(cond
(and page-loaded? initialized?)
(rx/empty)
page-loaded?
(rx/of (save-undo-entry id))
:else
(->> stream
(rx/filter udp/pages-fetched?)
(rx/take 1)
(rx/map #(initialize-undo-for-page id)))))))
(defn- initialize-undo-for-page
[id]
(InitializeUndoForPage. id))
;; --- Select Previous Entry
(defrecord Undo []
udp/IPageUpdate
rs/UpdateEvent
(-apply-update [_ state]
(let [page-id (get-in state [:workspace :page])
undo (get-in state [:undo page-id])
stack (:stack undo)
selected (:selected undo 0)]
(if (>= selected (dec (count stack)))
state
(let [pointer (inc selected)
page (get-in state [:pages page-id])
data (nth stack pointer)
packed (assoc page :data data)]
;; (println "Undo: pointer=" pointer)
;; (println "Undo: packed=")
;; (pp/pprint packed)
(-> state
(udp/assoc-page packed)
(assoc-in [:undo page-id :selected] pointer)))))))
(defn undo
[]
(Undo.))
(defn undo?
[v]
(instance? Undo v))
;; --- Select Next Entry
(defrecord Redo []
udp/IPageUpdate
rs/UpdateEvent
(-apply-update [_ state]
(let [page-id (get-in state [:workspace :page])
undo (get-in state [:undo page-id])
stack (:stack undo)
selected (:selected undo)]
(if (or (nil? selected) (zero? selected))
state
(let [pointer (dec selected)
data (nth stack pointer)
page (get-in state [:pages page-id])
packed (assoc page :data data)]
;; (println "Redo: pointer=" pointer)
;; (println "Redo: packed=")
;; (pp/pprint packed)
(-> state
(udp/assoc-page packed)
(assoc-in [:undo page-id :selected] pointer)))))))
(defn redo
[]
(Redo.))
(defn redo?
[v]
(instance? Redo v))