feat(editor): support paste an image in block

pull/1037/head
charlie 2020-12-31 15:03:16 +08:00 committed by Tienson Qin
parent cb3848ebab
commit eee8fb6bb2
3 changed files with 72 additions and 36 deletions

View File

@ -20,6 +20,7 @@
[goog.dom :as gdom]
[clojure.string :as string]
[clojure.set :as set]
[cljs.core.match :refer-macros [match]]
[frontend.commands :as commands
:refer [*show-commands
*matched-commands
@ -360,6 +361,15 @@
(absolute-modal cp set-default-width? pos)))))
(rum/defc image-uploader < rum/reactive
{:did-mount (fn [state]
(let [[id format] (:rum/args state)]
(add-watch editor-handler/*image-pending-file ::pending-image
(fn [_ _ f0 f]
(reset! *slash-caret-pos (util/get-caret-pos (gdom/getElement id)))
(editor-handler/upload-image id #js[f] format editor-handler/*image-uploading? true))))
state)
:will-unmount (fn [state]
(remove-watch editor-handler/*image-pending-file ::pending-image))}
[id format]
[:div.image-uploader
[:input
@ -693,40 +703,59 @@
[:div.editor-inner {:class (if block "block-editor" "non-block-editor")}
(when config/mobile? (mobile-bar state id))
(ui/ls-textarea
{:id id
{:id id
:cacheMeasurements true
:default-value (or content "")
:minRows (if (state/enable-grammarly?) 2 1)
:on-click (fn [_e]
(let [input (gdom/getElement id)
current-pos (:pos (util/get-caret-pos input))]
(state/set-edit-pos! current-pos)
(editor-handler/close-autocomplete-if-outside input)))
:on-change (fn [e]
(let [value (util/evalue e)
current-pos (:pos (util/get-caret-pos (gdom/getElement id)))]
(state/set-edit-content! id value false)
(state/set-edit-pos! current-pos)
(when-let [repo (or (:block/repo block)
(state/get-current-repo))]
(state/set-editor-last-input-time! repo (util/time-ms))
(db/clear-repo-persistent-job! repo))
(let [input (gdom/getElement id)
native-e (gobj/get e "nativeEvent")
last-input-char (util/nth-safe value (dec current-pos))]
(case last-input-char
"/"
;; TODO: is it cross-browser compatible?
(when (not= (gobj/get native-e "inputType") "insertFromPaste")
(when-let [matched-commands (seq (editor-handler/get-matched-commands input))]
(reset! *slash-caret-pos (util/get-caret-pos input))
(reset! *show-commands true)))
"<"
(when-let [matched-commands (seq (editor-handler/get-matched-block-commands input))]
(reset! *angle-bracket-caret-pos (util/get-caret-pos input))
(reset! *show-block-commands true))
nil))))
:auto-focus false})
:default-value (or content "")
:minRows (if (state/enable-grammarly?) 2 1)
:on-click (fn [_e]
(let [input (gdom/getElement id)
current-pos (:pos (util/get-caret-pos input))]
(state/set-edit-pos! current-pos)
(editor-handler/close-autocomplete-if-outside input)))
:on-change (fn [e]
(let [value (util/evalue e)
current-pos (:pos (util/get-caret-pos (gdom/getElement id)))]
(state/set-edit-content! id value false)
(state/set-edit-pos! current-pos)
(when-let [repo (or (:block/repo block)
(state/get-current-repo))]
(state/set-editor-last-input-time! repo (util/time-ms))
(db/clear-repo-persistent-job! repo))
(let [input (gdom/getElement id)
native-e (gobj/get e "nativeEvent")
last-input-char (util/nth-safe value (dec current-pos))]
(case last-input-char
"/"
;; TODO: is it cross-browser compatible?
(when (not= (gobj/get native-e "inputType") "insertFromPaste")
(when-let [matched-commands (seq (editor-handler/get-matched-commands input))]
(reset! *slash-caret-pos (util/get-caret-pos input))
(reset! *show-commands true)))
"<"
(when-let [matched-commands (seq (editor-handler/get-matched-block-commands input))]
(reset! *angle-bracket-caret-pos (util/get-caret-pos input))
(reset! *show-block-commands true))
nil))))
:on-paste (fn [e]
(when-let [handled
(let [pick-one-allowed-item
(fn [items]
(when (and items (.-length items))
(let [files (. (js/Array.from items) (filter #(= (.-kind %) "file")))
it (gobj/get files 0) ;;; TODO: support multiple files
mime (and it (.-type it))]
(cond
(contains? #{"image/jpeg" "image/png" "image/jpg" "image/gif"} mime) [:image (. it getAsFile)]))))
clipboard-data (gobj/get e "clipboardData")
items (or (.-items clipboard-data)
(.-files clipboard-data))
picked (pick-one-allowed-item items)]
(when (and picked (get picked 1))
(match picked
[:image file] (editor-handler/set-image-pending-file file))
true))]
(util/stop e)))
:auto-focus false})
;; TODO: how to render the transitions asynchronously?
(transition-cp

View File

@ -54,7 +54,7 @@
[:div.mt-2.mb-4.relative.rounded-md.shadow-sm.max-w-xs
[:input#branch.form-input.block.w-full.sm:text-sm.sm:leading-5
{:value @branch
:placeholder "master"
:placeholder "e.g. master"
:on-change (fn [e]
(reset! branch (util/evalue e)))}]]]]

View File

@ -44,6 +44,7 @@
[lambdaisland.glogi :as log]))
;; FIXME: should support multiple images concurrently uploading
(defonce *image-pending-file (atom nil))
(defonce *image-uploading? (atom false))
(defonce *image-uploading-process (atom 0))
(defonce *selected-text (atom nil))
@ -1495,7 +1496,7 @@
nil))
(defn upload-image
[id files format uploading? drop?]
[id files format uploading? drop-or-paste?]
(image/upload
files
(fn [file file-name file-type]
@ -1506,16 +1507,21 @@
(insert-command! id
(get-image-link format signed-url file-name)
format
{:last-pattern (if drop? "" commands/slash)
{:last-pattern (if drop-or-paste? "" commands/slash)
:restore? true})
(reset! *image-uploading? false)
(reset! *image-uploading-process 0))
(fn [e]
(let [process (* (/ (gobj/get e "loaded")
(gobj/get e "total"))
100)]
(reset! *image-uploading? false)
(reset! *image-uploading-process process)))))))
(defn set-image-pending-file [file]
(reset! *image-pending-file file))
;; Editor should track some useful information, like editor modes.
;; For example:
;; 1. Which file format is it, markdown or org mode?
@ -1662,6 +1668,7 @@
[input]
(or @*show-commands
@*show-block-commands
@*image-uploading?
(state/get-editor-show-input)
(state/get-editor-show-page-search?)
(state/get-editor-show-block-search?)