|
|
@@ -1,18 +1,19 @@
|
|
|
(ns frontend.extensions.zotero
|
|
|
(:require [cljs.core.async :refer [<! >! go chan] :as a]
|
|
|
[clojure.string :as str]
|
|
|
+ [frontend.components.svg :as svg]
|
|
|
[frontend.extensions.zotero.api :as api]
|
|
|
- [frontend.extensions.zotero.handler :as zotero-handler]
|
|
|
[frontend.extensions.zotero.extractor :as extractor]
|
|
|
- [frontend.components.svg :as svg]
|
|
|
+ [frontend.extensions.zotero.handler :as zotero-handler]
|
|
|
+ [frontend.extensions.zotero.setting :as setting]
|
|
|
[frontend.state :as state]
|
|
|
+ [frontend.ui :as ui]
|
|
|
[frontend.util :as util]
|
|
|
[rum.core :as rum]))
|
|
|
|
|
|
(defonce term-chan (chan))
|
|
|
(defonce debounce-chan (api/debounce term-chan 200))
|
|
|
|
|
|
-
|
|
|
(rum/defc zotero-search-item [{:keys [data] :as item} handle-command-zotero]
|
|
|
(let [type (:item-type data)
|
|
|
title (:title data)
|
|
|
@@ -35,62 +36,150 @@
|
|
|
|
|
|
(rum/defc zotero-search [handle-command-zotero]
|
|
|
|
|
|
- (let [cache-api-key (js/localStorage.getItem "zotero-api-key")
|
|
|
- cache-user-id (js/localStorage.getItem "zotero-user-id")
|
|
|
- cache-api-key-empty (str/blank? cache-api-key)
|
|
|
- cache-user-id-empty (str/blank? cache-user-id)
|
|
|
- api-key
|
|
|
- (if cache-api-key-empty
|
|
|
- (js/prompt "Please enter your Zotero API key (https://www.zotero.org/settings/keys/new)")
|
|
|
- cache-api-key)
|
|
|
-
|
|
|
- user-id (if cache-user-id-empty (js/prompt "Please enter your Zotero user id (https://www.zotero.org/settings/keys)") cache-user-id)]
|
|
|
-
|
|
|
- (when cache-api-key-empty (js/localStorage.setItem "zotero-api-key" api-key))
|
|
|
-
|
|
|
- (when cache-user-id-empty (js/localStorage.setItem "zotero-user-id" user-id))
|
|
|
-
|
|
|
- (let [[term set-term!] (rum/use-state "")
|
|
|
- [search-result set-search-result!] (rum/use-state [])
|
|
|
- [search-error set-search-error!] (rum/use-state nil)
|
|
|
- [is-searching set-is-searching!] (rum/use-state false)]
|
|
|
-
|
|
|
-
|
|
|
- (go
|
|
|
- (let [d-term (<! debounce-chan)]
|
|
|
- (when-not (str/blank? d-term)
|
|
|
- (set-is-searching! true)
|
|
|
-
|
|
|
-
|
|
|
- (let [result (<! (api/query-items "journalArticle" d-term))]
|
|
|
- (if (false? (:success result))
|
|
|
- (set-search-error! (:body result))
|
|
|
- (set-search-result! result)))
|
|
|
-
|
|
|
- (set-is-searching! false))))
|
|
|
-
|
|
|
- [:div.zotero-search.p-4
|
|
|
- {:style {:width 600}}
|
|
|
-
|
|
|
- [:div.flex.items-center.mb-2
|
|
|
- [[:input.p-2.border.mr-2.flex-1
|
|
|
- {:autoFocus true
|
|
|
- :placeholder "Search for your Zotero journal article (title, author, text, anything)"
|
|
|
- :value term :on-change (fn [e]
|
|
|
- (go
|
|
|
- (js/console.log "sending term-chan!!" (util/evalue e))
|
|
|
- (>! term-chan (util/evalue e)))
|
|
|
- (set-term! (util/evalue e)))}]
|
|
|
-
|
|
|
- (when is-searching [:span.loader-reverse svg/refresh])]]
|
|
|
-
|
|
|
- [:div.h-2.text-sm.text-red-400.mb-2 (if search-error (str "Search error: " search-error) "")]
|
|
|
-
|
|
|
- [:div
|
|
|
- (map
|
|
|
- (fn [item] (rum/with-key (zotero-search-item item handle-command-zotero) (:key item)))
|
|
|
- search-result)]])))
|
|
|
-
|
|
|
-(rum/defc setting []
|
|
|
- [:div
|
|
|
- [:h1.title "Zotero setting"]])
|
|
|
+ (let [[term set-term!] (rum/use-state "")
|
|
|
+ [search-result set-search-result!] (rum/use-state [])
|
|
|
+ [search-error set-search-error!] (rum/use-state nil)
|
|
|
+ [is-searching set-is-searching!] (rum/use-state false)]
|
|
|
+
|
|
|
+
|
|
|
+ (go
|
|
|
+ (let [d-term (<! debounce-chan)]
|
|
|
+ (when-not (str/blank? d-term)
|
|
|
+ (set-is-searching! true)
|
|
|
+
|
|
|
+
|
|
|
+ (let [result (<! (api/query-items "journalArticle" d-term))]
|
|
|
+ (if (false? (:success result))
|
|
|
+ (set-search-error! (:body result))
|
|
|
+ (set-search-result! result)))
|
|
|
+
|
|
|
+ (set-is-searching! false))))
|
|
|
+
|
|
|
+ [:div.zotero-search.p-4
|
|
|
+ {:style {:width 600}}
|
|
|
+
|
|
|
+ [:div.flex.items-center.mb-2
|
|
|
+ [[:input.p-2.border.mr-2.flex-1
|
|
|
+ {:autoFocus true
|
|
|
+ :placeholder "Search for your Zotero journal article (title, author, text, anything)"
|
|
|
+ :value term :on-change (fn [e]
|
|
|
+ (go
|
|
|
+ (js/console.log "sending term-chan!!" (util/evalue e))
|
|
|
+ (>! term-chan (util/evalue e)))
|
|
|
+ (set-term! (util/evalue e)))}]
|
|
|
+
|
|
|
+ (when is-searching [:span.loader-reverse svg/refresh])]]
|
|
|
+
|
|
|
+ [:div.h-2.text-sm.text-red-400.mb-2 (if search-error (str "Search error: " search-error) "")]
|
|
|
+
|
|
|
+ [:div
|
|
|
+ (map
|
|
|
+ (fn [item] (rum/with-key (zotero-search-item item handle-command-zotero) (:key item)))
|
|
|
+ search-result)]]))
|
|
|
+
|
|
|
+
|
|
|
+(rum/defcs settings
|
|
|
+ <
|
|
|
+ (rum/local (setting/api-key) ::api-key)
|
|
|
+ rum/reactive
|
|
|
+ [state]
|
|
|
+ (let [api-key (::api-key state)]
|
|
|
+ [:div#zotero-settings
|
|
|
+ [:h1.title "Zotero settings"]
|
|
|
+
|
|
|
+ [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
|
|
|
+ [:label.block.text-bg.font-medium.leading-5.opacity-70
|
|
|
+ {:for "zotero_api_key"}
|
|
|
+ "Zotero API key"]
|
|
|
+ [:div.mt-1.sm:mt-0.sm:col-span-2
|
|
|
+ [:div.max-w-lg.rounded-md
|
|
|
+ [:input.form-input.block
|
|
|
+ {:value @api-key
|
|
|
+ :placeholder "Please enter your Zotero API key"
|
|
|
+ :on-change (fn [e]
|
|
|
+ (reset! api-key (util/evalue e))
|
|
|
+ (setting/set-api-key (util/evalue e)))}]]]]
|
|
|
+
|
|
|
+ [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
|
|
|
+ [:label.block.text-sm.font-medium.leading-5.opacity-70
|
|
|
+ {:for "zotero_type"}
|
|
|
+ "Zotero user or group?"]
|
|
|
+ [:div.mt-1.sm:mt-0.sm:col-span-2
|
|
|
+ [:div.max-w-lg.rounded-md
|
|
|
+ [:select.form-select.is-small
|
|
|
+ {:value (-> (setting/setting :type) name)
|
|
|
+ :on-change (fn [e]
|
|
|
+ (let [type (-> (util/evalue e)
|
|
|
+ (str/lower-case)
|
|
|
+ keyword)]
|
|
|
+ (setting/set-setting! :type type)))}
|
|
|
+ (for [type (map name [:user :group])]
|
|
|
+ [:option {:key type :value type} (str/capitalize type)])]]]]
|
|
|
+
|
|
|
+ [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
|
|
|
+ [:label.block.text-bg.font-medium.leading-5.opacity-70
|
|
|
+ {:for "zotero_type_id"}
|
|
|
+ "User or Group id"]
|
|
|
+ [:div.mt-1.sm:mt-0.sm:col-span-2
|
|
|
+ [:div.max-w-lg.rounded-md
|
|
|
+ [:input.form-input.block
|
|
|
+ {:value (setting/setting :type-id)
|
|
|
+ :placeholder "User/Group id"
|
|
|
+ :on-change (fn [e]
|
|
|
+ (setting/set-setting! :type-id (util/evalue e)))}]]]]
|
|
|
+
|
|
|
+ [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
|
|
|
+ [:label.block.text-sm.font-medium.leading-5.opacity-70
|
|
|
+ {:for "zotero_include_attachment_links"}
|
|
|
+ "Include attachment links?"]
|
|
|
+ [:div
|
|
|
+ [:div.rounded-md.sm:max-w-xs
|
|
|
+ (ui/toggle (setting/setting :include-attachments?)
|
|
|
+ (fn [] (setting/set-setting! :include-attachments? (not (setting/setting :include-attachments?))))
|
|
|
+ true)]]]
|
|
|
+
|
|
|
+ (when (setting/setting :include-attachments?)
|
|
|
+ [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
|
|
|
+ [:label.block.text-bg.font-medium.leading-5.opacity-70
|
|
|
+ {:for "zotero_attachments_block_text"}
|
|
|
+ "Attachtment under block of:"]
|
|
|
+ [:div.mt-1.sm:mt-0.sm:col-span-2
|
|
|
+ [:div.max-w-lg.rounded-md
|
|
|
+ [:input.form-input.block
|
|
|
+ {:value (setting/setting :attachments-block-text)
|
|
|
+ :on-change (fn [e]
|
|
|
+ (setting/set-setting! :attachments-block-text (util/evalue e)))}]]]])
|
|
|
+
|
|
|
+ [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
|
|
|
+ [:label.block.text-sm.font-medium.leading-5.opacity-70
|
|
|
+ {:for "zotero_include_notes"}
|
|
|
+ "Include notes?"]
|
|
|
+ [:div
|
|
|
+ [:div.rounded-md.sm:max-w-xs
|
|
|
+ (ui/toggle (setting/setting :include-notes?)
|
|
|
+ (fn [] (setting/set-setting! :include-notes?
|
|
|
+ (not (setting/setting :include-notes?))))
|
|
|
+ true)]]]
|
|
|
+
|
|
|
+ (when (setting/setting :include-notes?)
|
|
|
+ [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
|
|
|
+ [:label.block.text-bg.font-medium.leading-5.opacity-70
|
|
|
+ {:for "zotero_notes_block_text"}
|
|
|
+ "Notes under block of:"]
|
|
|
+ [:div.mt-1.sm:mt-0.sm:col-span-2
|
|
|
+ [:div.max-w-lg.rounded-md
|
|
|
+ [:input.form-input.block
|
|
|
+ {:value (setting/setting :notes-block-text)
|
|
|
+ :on-change (fn [e]
|
|
|
+ (setting/set-setting! :notes-block-text (util/evalue e)))}]]]])
|
|
|
+
|
|
|
+ [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
|
|
|
+ [:label.block.text-bg.font-medium.leading-5.opacity-70
|
|
|
+ {:for "zotero_page_prefix"}
|
|
|
+ "Insert page name with prefix:"]
|
|
|
+ [:div.mt-1.sm:mt-0.sm:col-span-2
|
|
|
+ [:div.max-w-lg.rounded-md
|
|
|
+ [:input.form-input.block
|
|
|
+ {:value (setting/setting :page-insert-prefix)
|
|
|
+ :on-change (fn [e]
|
|
|
+ (setting/set-setting! :page-insert-prefix (util/evalue e)))}]]]]]))
|