Skip to content

Commit

Permalink
support save/load on browsers
Browse files Browse the repository at this point in the history
  • Loading branch information
sprocketc committed Mar 3, 2024
1 parent 852ccdd commit 49d8d04
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 47 deletions.
39 changes: 20 additions & 19 deletions src/file.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,10 @@
["electron" :refer [app dialog]]
["fs" :as fs]
["path" :as path]
[clojure.edn :as edn]
#_[cognitect.transit :as tr]))
[clojure.edn :as edn]))

(def main-window (atom nil))

#_(defn roundtrip
[data]
(let [writer (tr/writer :json)]
(tr/write writer data)))

(def default-path (.getPath app "documents"))

(def dialog-options
Expand All @@ -21,37 +15,44 @@
:filters [{:name "rso"
:extensions ["rso"]}]})

(defn serialize-document
[data file-path]
(pr-str (assoc data
:path file-path
:title (.basename path file-path))))

(defn write-to-file
[file-path data f]
(.writeFile fs file-path data #js {:encoding "utf-8"}
(fn [_err] (f {:path file-path
:title (.basename path file-path)
:data data}))))
(.writeFile fs file-path (pr-str data) #js {:encoding "utf-8"}
(fn [_err] (f (serialize-document data file-path)))))

(defn save
"Saves the provided data.
https://www.electronjs.org/docs/api/dialog#dialogshowsavedialogsyncbrowserwindow-options"
If there is no path defined, pick a new file.
https://www.electronjs.org/docs/api/dialog#dialogshowsavedialogbrowserwindow-options"
[data f]
(let [file-path (-> data edn/read-string :path)]
(let [document (edn/read-string data)
file-path (:path document)]
(if (and file-path (.existsSync fs file-path))
(write-to-file file-path data f)
(write-to-file file-path document f)
(.then (.showSaveDialog dialog ^js @main-window (clj->js dialog-options))
(fn [^js/Promise file]
(when-not (.-canceled file)
(write-to-file (.-filePath file) data f)))))))
(write-to-file (.-filePath file) document f)))))))

(defn open
"Opens a file.
https://www.electronjs.org/docs/api/dialog#dialogshowopendialogsyncbrowserwindow-options"
https://www.electronjs.org/docs/api/dialog#dialogshowopendialogbrowserwindow-options"
[f]
(.then (.showOpenDialog dialog ^js @main-window (clj->js dialog-options))
(fn [^js/Promise file]
(when-not (.-canceled file)
(let [file-path (first (js->clj (.-filePaths file)))]
(.readFile fs file-path #js {:encoding "utf-8"}
(fn [_err data] (f {:path file-path
:title (.basename path file-path)
:data data}))))))))
(fn [_err data]
(let [document (edn/read-string data)]
(f (serialize-document document file-path))))))))))

(def export-options
{:defaultPath default-path
Expand Down
7 changes: 4 additions & 3 deletions src/renderer/core.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#_["@sentry/react" :as sentry-react]
["electron-log/renderer"]
["paper" :refer [paper]]
[cljs.reader :as edn]
[config]
[devtools.core :as devtools]
[platform]
Expand All @@ -28,9 +29,9 @@
[renderer.theme.core]
[renderer.timeline.core]
[renderer.tools.core]
[renderer.utils.dom :as dom]
[renderer.utils.error :as error]
[renderer.utils.keyboard :as keyb]
[renderer.utils.dom :as dom]
[renderer.views :as v]
[renderer.window.core]
[replumb.repl :as repl]
Expand Down Expand Up @@ -79,8 +80,8 @@
"windowLeavedFullscreen" (rf/dispatch [:window/set-fullscreen? false])
"windowMinimized" (rf/dispatch [:window/set-minimized? true])
"windowRestored" (rf/dispatch [:window/set-minimized? false])
"fileLoaded" (rf/dispatch [:document/load (.-data data)])
"fileSaved" (rf/dispatch [:document/saved (.-data data)])))))
"fileLoaded" (rf/dispatch [:document/load (edn/read-string (.-data data))])
"fileSaved" (rf/dispatch [:document/saved (edn/read-string (.-data data))])))))

(defn load-system-fonts
[]
Expand Down
92 changes: 67 additions & 25 deletions src/renderer/document/events.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
(:require
[clojure.edn :as edn]
#_[de-dupe.core :as dd]
[platform]
[re-frame.core :as rf]
[re-frame.interceptor :refer [->interceptor get-effect get-coeffect assoc-coeffect assoc-effect]]
[renderer.document.db :as db]
Expand Down Expand Up @@ -137,56 +138,97 @@
(history.h/finalize "Create document"))
:dispatch [:center]}))

(def repath-types
[{:accept {"application/repath.studio" [".rso"]}}])

(def file-picker-options
(clj->js {:startIn "documents"
:types repath-types}))

(rf/reg-fx
::open
(fn []
(.then (.showOpenFilePicker js/window file-picker-options)
(fn [[file-handle]]
(.then (.getFile file-handle)
(fn [file]
(let [reader (js/FileReader.)]
(.addEventListener
reader
"load"
#(let [document (.. % -target -result)]
(rf/dispatch [:document/load (edn/read-string document)])))
(.readAsText reader file))))))))

(rf/reg-event-fx
:document/open
(fn [_ [_]]
{:send-to-main {:action "openDocument"}}))
(if platform/electron?
{:send-to-main {:action "openDocument"}}
{::open nil})))

(rf/reg-event-fx
:document/load
(fn [{:keys [db]} [_ data]]
(let [document (-> (.-data data)
edn/read-string
(assoc :path (.-path data)
:title (.-title data))
(fn [{:keys [db]} [_ document]]
(let [document (-> document
;; FIXME: Still contains cached values after expand.
#_(update-in [:history :states] dd/expand))]
#_(update-in document [:history :states] dd/expand))]
{:db (-> db
(h/create-tab document)
(history.h/finalize "Load document")
(update-in [:documents (:key document)] #(assoc % :save (-> % :history :position))))
(update-in [:documents (:key document)]
#(assoc % :save (-> % :history :position))))
:dispatch [:center]})))

(rf/reg-fx
::save
(fn [data]
(.then (.showSaveFilePicker js/window file-picker-options)
(fn [file-handle]
(.then (.createWritable file-handle)
(fn [writable]
(.then (.write writable (pr-str data))
(let [document (assoc data
:title (.-name file-handle)
:path (.-name file-handle))]
(.close writable)
(rf/dispatch [:document/saved document])))))))))

(defn save-format
[db]
(-> db
(get-in [:documents (:active-document db)])
(assoc :save (history.h/current-position db))
(dissoc :history)))

(rf/reg-event-fx
:document/save
(fn [{:keys [db]} [_]]
(let [document (-> db
(get-in [:documents (:active-document db)])
(assoc :save (history.h/current-position db))
(dissoc :history)
save-format
#_(update-in [:history :states] dd/de-dupe-eq))]
{:send-to-main {:action "saveDocument" :data (pr-str document)}})))
(if platform/electron?
{:send-to-main {:action "saveDocument" :data (pr-str document)}}
{::save document}))))

(rf/reg-event-fx
:document/save-as
(fn [{:keys [db]} [_]]
(let [document (-> db
(get-in [:documents (:active-document db)])
(assoc :save (history.h/current-position db))
(dissoc :history :path))]
{:send-to-main {:action "saveDocument" :data (pr-str document)}})))
save-format
;; Remove the path to trigger a file selection dialog.
(dissoc :path))]
(if platform/electron?
{:send-to-main {:action "saveDocument" :data (pr-str document)}}
{::save document}))))

(rf/reg-event-db
:document/saved
(fn [db [_ data]]
(let [document (-> (.-data data) edn/read-string)
key (:key document)]
;; Update the path/name and the saved position of the document.
;; Any other changes that could happen while saving should be preserved.
(-> db
(assoc-in [:documents key :path] (.-path data))
(assoc-in [:documents key :title] (.-title data))
(assoc-in [:documents key :save] (:save document))))))
(fn [db [_ document]]
;; Update only the path, teh title and the saved position of the document.
;; Any other changes that could happen while saving should be preserved.
(let [updates (select-keys document [:path :title :save])]
(update-in db [:documents (:key document)] merge updates))))

(rf/reg-event-fx
:document/save-all
Expand Down

0 comments on commit 49d8d04

Please sign in to comment.