From fdbf53c442379df479cfd9e0282b333cba2e61b7 Mon Sep 17 00:00:00 2001 From: Konstantinos Kaloutas Date: Sun, 24 Nov 2024 20:03:23 +0200 Subject: [PATCH] move some side-effecting functions to effects --- src/renderer/app/effects.cljs | 58 ++++++++++++++++++++++++++++++--- src/renderer/app/events.cljs | 10 ++++++ src/renderer/core.cljs | 9 ++++-- src/renderer/reepl/views.cljs | 9 ++---- src/renderer/tree/views.cljs | 3 +- src/renderer/utils/dom.cljs | 32 ++----------------- src/renderer/utils/file.cljs | 60 ----------------------------------- 7 files changed, 77 insertions(+), 104 deletions(-) delete mode 100644 src/renderer/utils/file.cljs diff --git a/src/renderer/app/effects.cljs b/src/renderer/app/effects.cljs index b20d4aae..bfd71c26 100644 --- a/src/renderer/app/effects.cljs +++ b/src/renderer/app/effects.cljs @@ -7,8 +7,7 @@ [renderer.app.events :as-alias e] [renderer.history.handlers :as history.h] [renderer.notification.events :as-alias notification.e] - [renderer.utils.dom :as dom] - [renderer.utils.file :as file])) + [renderer.utils.dom :as dom])) (rf.storage/reg-co-fx! config/app-key {:cofx :store}) @@ -65,15 +64,54 @@ (rf/reg-fx ::file-save - file/save!) + (fn [{:keys [options data on-success on-error formatter]}] + (if (.-showSaveFilePicker js/window) + (-> (.showSaveFilePicker js/window (clj->js options)) + (.then (fn [^js/FileSystemFileHandle file-handle] + (.then (.createWritable file-handle) + (fn [^js/FileSystemWritableFileStream writable-stream] + (.then (.write writable-stream data) + (fn [] + (.close writable-stream) + (when on-success + (rf/dispatch (conj on-success (cond-> file-handle + formatter + formatter)))))))))) + (.catch #(when on-error (rf/dispatch (conj on-error %))))) + (rf/dispatch + [::notification.e/unavailable-feature + "Save File Picker" + "https://developer.mozilla.org/en-US/docs/Web/API/Window/showSaveFilePicker#browser_compatibility"])))) + +(defn legacy-file-open! + [cb] + (let [el (js/document.createElement "input")] + (set! (.-type el) "file") + (.addEventListener el "change" (fn [e] (.remove el) + (cb (first (.. e -target -files))))) + (.click el))) (rf/reg-fx ::file-open - file/open!) + (fn [{:keys [options on-error on-success]}] + (let [success-cb #(rf/dispatch (conj on-success %))] + (if (.-showOpenFilePicker js/window) + (-> (.showOpenFilePicker js/window (clj->js options)) + (.then (fn [[^js/FileSystemFileHandle file-handle]] + (.then (.getFile file-handle) success-cb))) + (.catch #(when on-error (rf/dispatch (conj on-error %))))) + (legacy-file-open! success-cb))))) (rf/reg-fx ::download - file/download!) + (fn [{:keys [data title]}] + (let [blob (js/Blob. [data]) + url (js/URL.createObjectURL blob) + a (js/document.createElement "a")] + (.setAttribute a "href" url) + (.setAttribute a "download" title) + (.click a) + (js/window.URL.revokeObjectURL url)))) (rf/reg-fx ::set-document-attr @@ -86,3 +124,13 @@ (when (not (db/valid? db)) (js/console.error (str "Event: " (first event))) (throw (js/Error. (str "Spec check failed: " (db/explain db))))))) + +(rf/reg-fx + ::scroll-into-view + (fn [el] + (.scrollIntoView el #js {:block "nearest"}))) + +(rf/reg-fx + ::scroll-to-bottom + (fn [el] + (set! (.-scrollTop el) (.-scrollHeight el)))) diff --git a/src/renderer/app/events.cljs b/src/renderer/app/events.cljs index 9ea4dbed..e85ddc34 100644 --- a/src/renderer/app/events.cljs +++ b/src/renderer/app/events.cljs @@ -122,3 +122,13 @@ (cond-> context db (rf/assoc-effect :fx (conj (or fx []) [::fx/validate-db [db event]]))))))) + +(rf/reg-event-fx + ::scroll-into-view + (fn [_ [_ el]] + {::fx/scroll-into-view el})) + +(rf/reg-event-fx + ::scroll-to-bottom + (fn [_ [_ el]] + {::fx/scroll-to-bottom el})) diff --git a/src/renderer/core.cljs b/src/renderer/core.cljs index e97824c6..37f54745 100644 --- a/src/renderer/core.cljs +++ b/src/renderer/core.cljs @@ -74,7 +74,7 @@ (defn ^:dev/after-load mount-root! [] (rf/clear-subscription-cache!) - (let [root-el (dom/root-element!)] + (let [root-el (.getElementById js/document "app")] (ra.dom/unmount-component-at-node root-el) (ra.dom/render [error/boundary [app.v/root]] root-el))) @@ -88,11 +88,14 @@ (defn add-listeners! [] - (.addEventListener js/window "focus" (rf/dispatch [::window.e/set-focused true])) - (.addEventListener js/window "blur" (rf/dispatch [::window.e/set-focused (dom/focused!?)])) (.addEventListener js/document "keydown" keyb/event-handler!) (.addEventListener js/document "keyup" keyb/event-handler!) (.addEventListener js/document "fullscreenchange" #(rf/dispatch [::window.e/set-fullscreen (boolean (.-fullscreenElement js/document))])) + (.addEventListener js/window "focus" (rf/dispatch [::window.e/set-focused true])) + (.addEventListener js/window "blur" (rf/dispatch [::window.e/set-focused + (or (.hasFocus js/document) + (and (dom/frame-document!) + (.hasFocus (dom/frame-document!))))])) (rf/dispatch [::document.e/center])) diff --git a/src/renderer/reepl/views.cljs b/src/renderer/reepl/views.cljs index f165f6b0..69b53d04 100644 --- a/src/renderer/reepl/views.cljs +++ b/src/renderer/reepl/views.cljs @@ -15,7 +15,6 @@ [renderer.reepl.show-value :refer [show-value]] [renderer.reepl.subs :as s] [renderer.ui :as ui] - [renderer.utils.dom :as dom] [replumb.core :as replumb.core]) (:require-macros [reagent.ratom :refer [reaction]])) @@ -89,12 +88,10 @@ (ra/create-class {:component-did-mount (fn [_this] - (let [el (.-current ref)] - (dom/scroll-to-bottom! el))) + (rf/dispatch [::app.e/scroll-to-bottom (.-current ref)])) :component-did-update (fn [_this] - (let [el (.-current ref)] - (dom/scroll-to-bottom! el))) + (rf/dispatch [::app.e/scroll-to-bottom (.-current ref)])) :reagent-render (fn [items opts] [:div.flex-1.border-b.border-default.h-full.overflow-hidden.flex @@ -126,7 +123,7 @@ (let [[_ _ selected] (ra/argv this)] (when (and (not old-selected) selected) - (dom/scroll-into-view! (.-current ref))))) + (rf/dispatch [::app.e/scroll-into-view (.-current ref)])))) :reagent-render (fn [text selected active set-active] [:div.p-1.bg-secondary.text-nowrap diff --git a/src/renderer/tree/views.cljs b/src/renderer/tree/views.cljs index 99a15bc0..7695c246 100644 --- a/src/renderer/tree/views.cljs +++ b/src/renderer/tree/views.cljs @@ -4,6 +4,7 @@ [clojure.string :as str] [re-frame.core :as rf] [reagent.core :as ra] + [renderer.app.events :as-alias app.e] [renderer.document.events :as-alias document.e] [renderer.document.subs :as-alias document.s] [renderer.element.events :as-alias element.e] @@ -139,7 +140,7 @@ :on-pointer-enter #(rf/dispatch [::document.e/set-hovered-id id]) :ref (fn [this] (when (and this selected) - (dom/scroll-into-view! this) + (rf/dispatch [::app.e/scroll-into-view this]) (set-last-focused-id! (.getAttribute this "data-id")))) :draggable true :on-key-down #(key-down-handler! % id) diff --git a/src/renderer/utils/dom.cljs b/src/renderer/utils/dom.cljs index 06a979bc..efc02092 100644 --- a/src/renderer/utils/dom.cljs +++ b/src/renderer/utils/dom.cljs @@ -11,29 +11,11 @@ [e] (.stopPropagation e)) -(defn root-element! - [] - (.getElementById js/document "app")) - -(defn frame-element! - [] - (.getElementById js/document "frame")) - -(defn frame-window! - [] - (when-let [frame (frame-element!)] - (.-contentWindow frame))) - (defn frame-document! [] - (when-let [window (frame-window!)] - (.-document window))) - -(defn focused!? - [] - (or (.hasFocus js/document) - (and (frame-document!) - (.hasFocus (frame-document!))))) + (when-let [frame (.getElementById js/document "frame")] + (when-let [window (.-contentWindow frame)] + (.-document window)))) (defn svg-elements! [] @@ -44,11 +26,3 @@ [] (when-let [document (frame-document!)] (.getElementById document "canvas"))) - -(defn scroll-into-view! - [el] - (.scrollIntoView el #js {:block "nearest"})) - -(defn scroll-to-bottom! - [el] - (set! (.-scrollTop el) (.-scrollHeight el))) diff --git a/src/renderer/utils/file.cljs b/src/renderer/utils/file.cljs deleted file mode 100644 index d07b4fb9..00000000 --- a/src/renderer/utils/file.cljs +++ /dev/null @@ -1,60 +0,0 @@ -(ns renderer.utils.file - (:require - [re-frame.core :as rf] - [renderer.notification.events :as-alias notification.e])) - -#_(def JSFile - [:fn (fn [x] - (if-not (nil? x) - (identical? (.-constructor x) js/File) - false))]) - -(defn download! - [{:keys [data title]}] - (let [blob (js/Blob. [data]) - url (js/URL.createObjectURL blob) - a (js/document.createElement "a")] - (.setAttribute a "href" url) - (.setAttribute a "download" title) - (.click a) - (js/window.URL.revokeObjectURL url))) - -(defn legacy-open! - [cb] - (let [el (js/document.createElement "input")] - (set! (.-type el) "file") - (.addEventListener el "change" (fn [e] (.remove el) - (cb (first (.. e -target -files))))) - (.click el))) - -(defn open! - "https://developer.mozilla.org/en-US/docs/Web/API/Window/showOpenFilePicker" - [{:keys [options on-error on-success]}] - (let [success-cb #(rf/dispatch (conj on-success %))] - (if (.-showOpenFilePicker js/window) - (-> (.showOpenFilePicker js/window (clj->js options)) - (.then (fn [[^js/FileSystemFileHandle file-handle]] - (.then (.getFile file-handle) success-cb))) - (.catch #(when on-error (rf/dispatch (conj on-error %))))) - (legacy-open! success-cb)))) - -(defn save! - "https://developer.mozilla.org/en-US/docs/Web/API/Window/showSaveFilePicker" - [{:keys [options data on-success on-error formatter]}] - (if (.-showSaveFilePicker js/window) - (-> (.showSaveFilePicker js/window (clj->js options)) - (.then (fn [^js/FileSystemFileHandle file-handle] - (.then (.createWritable file-handle) - (fn [^js/FileSystemWritableFileStream writable-stream] - (.then (.write writable-stream data) - (fn [] - (.close writable-stream) - (when on-success - (rf/dispatch (conj on-success (cond-> file-handle - formatter - formatter)))))))))) - (.catch #(when on-error (rf/dispatch (conj on-error %))))) - (rf/dispatch - [::notification.e/unavailable-feature - "Save File Picker" - "https://developer.mozilla.org/en-US/docs/Web/API/Window/showSaveFilePicker#browser_compatibility"])))