diff --git a/Makefile b/Makefile index 10afe60..974101e 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,11 @@ -.PHONY = tests +.PHONY: dev compile: clojure -m cljs.main -O advanced -d target -o target/pluto.js -c pluto.js +dev: + clojure -A:dev + install: lein install diff --git a/deps.edn b/deps.edn index d916569..4837866 100644 --- a/deps.edn +++ b/deps.edn @@ -3,15 +3,12 @@ reagent {:mvn/version "0.8.1"} re-frame {:mvn/version "0.10.6"}} :paths ["src"] - :aliases {:examples {:extra-paths ["pluto-web/src" "target" "test" "examples/src" "examples/resources"] - :extra-deps {com.bhauman/figwheel-main {:mvn/version "0.2.0"} - com.bhauman/rebel-readline-cljs {:mvn/version "0.1.4"} - binaryage/devtools {:mvn/version "0.9.10"} - cljsjs/codemirror {:mvn/version "5.40.2-1"} - cljsjs/parinfer {:mvn/version "3.11.0-0"} - cljsjs/parinfer-codemirror {:mvn/version "1.4.1-2"} - cljsjs/material-ui {:mvn/version "3.9.1-0"}} - :main-opts ["-m" "figwheel.main" "-b" "examples/dev" "-r"]} + :aliases {:dev {:extra-paths ["dev/src" "dev/resources" "target"] + :extra-deps {status-im/pluto-web {:mvn/version "0.1.0"} + com.bhauman/figwheel-main {:mvn/version "0.2.0"} + com.bhauman/rebel-readline-cljs {:mvn/version "0.1.4"} + binaryage/devtools {:mvn/version "0.9.10"}} + :main-opts ["-m" "figwheel.main" "-b" "dev/core" "-r"]} :compile {:extra-paths ["test"] :main-opts ["-e" "(require,'[eftest.runner,:refer,[find-tests,run-tests]]),(run-tests,(find-tests,\"test\"))"]} :test-clj {:extra-paths ["test"] diff --git a/dev/core.cljs.edn b/dev/core.cljs.edn new file mode 100644 index 0000000..2e5d6a7 --- /dev/null +++ b/dev/core.cljs.edn @@ -0,0 +1,4 @@ +^{:watch-dirs ["src" "dev/src"] + :log-level :info} +{:main pluto.dev} + diff --git a/dev/resources/public/assets/extensions/demo/extension.edn b/dev/resources/public/assets/extensions/demo/extension.edn new file mode 100644 index 0000000..3b20e10 --- /dev/null +++ b/dev/resources/public/assets/extensions/demo/extension.edn @@ -0,0 +1,64 @@ +{meta + {:name "Simple Demo" + :description "A simple demo of extension" + :documentation "Nothing. Just see a text with dynamic random color."} + + lifecycle + {:on-activation [alert {:value "Activation !!"}]} + + hooks/main.demo + {:view [main]} + + events/my-alert + (let [{you :arg [value :as all] :value} properties] + [alert {:value "Eh! ${you} ${value}"}]) + + events/cb + (let [{value :arg v :value} properties + {cond2? :cond?} [random-boolean]] + (if cond? + [alert {:value "Eh bis! ${cond2?}"}] + [alert {:value "Eh ter! ${cond2?}"}]) + [identity {:cb [my-alert {:arg value :value ["%% ${v}"]}]}]) + + views/local-view + {:component-did-mount [alert {:value "Mount!!"}] + :view + (let [{name :name color :color level :level} properties] + (case level + :polite [text {:style {:color color}} "Hello!! ${name}"] + :neutral [text {:style {:color color}} "Hey!! ${name}"] + [text "?? ${name}"]))} + + views/local-view2 + (let [{name :name color :color level :level} properties] + (case level + :polite [text {:style {:color color}} "Hey!! ${name}"] + "Hello ${name}")) + + views/main + (let [{name :name users :users} properties + {cond? :cond?} [random-boolean]] + [view + [text "1"] + [text "2"] + ;[local-view {:name "Hey!! ${name}" :color :red :level :polite}] + [button {:on-click [cb {:arg name :value "AA"}]} + "Click!"] + ;[button {:on-click [alert {}]} + ; "Click2 !"] + + [button {:on-click [my-alert {:arg cond? :value ["%% ${name}"]}]} + "Click2 !"] + [button {:on-click [alert {:value "AA"}]} + "Click3 !"] + (if cond? + [text {:style {:color "green"}} + name] + [text {:style {:color "red"}} + name]) + [view "Nested for block"] + (for [{nm :nm} users] + [view + (for [{nm2 :nm} users] + [text nm " and " nm2])])])} diff --git a/dev/resources/public/index.html b/dev/resources/public/index.html new file mode 100644 index 0000000..0a3ca4d --- /dev/null +++ b/dev/resources/public/index.html @@ -0,0 +1,43 @@ + + + + + +
+
+ +
+
+
+ + + + + diff --git a/dev/src/pluto/dev.cljs b/dev/src/pluto/dev.cljs new file mode 100644 index 0000000..8da896d --- /dev/null +++ b/dev/src/pluto/dev.cljs @@ -0,0 +1,105 @@ +(ns ^:figwheel-hooks pluto.dev + (:require pluto.reader.events + pluto.reader.views + pluto.web.events + pluto.web.queries + [pluto.core :as pluto] + [pluto.log :as log] + [pluto.storages :as storages] + [pluto.web.components :as components] + [devtools.core :as devtools] + [reagent.core :as reagent] + [re-frame.core :as re-frame] + [re-frame.registrar :as registrar] + [re-frame.loggers :as re-frame.loggers])) + +(enable-console-print!) +(devtools/install!) + +(defn ^:before-load before-reload [] + (re-frame/clear-subscription-cache!) + (println "Reloading ...")) + +(defn ^:after-load after-reload [] + (println "Reloading done.")) + +(def warn (js/console.warn.bind js/console)) +(re-frame.loggers/set-loggers! + {:warn (fn [& args] + (cond + (= "re-frame: overwriting" (first args)) nil + :else (apply warn args)))}) + +(defn- dispatch-events [ctx events] + (doseq [event events] + (if (vector? event) + (re-frame/dispatch event) + (log/fire! ctx ::log/error :event/dispatch event)))) + +(defn- resolve-query [ctx [id :as data]] + (if (registrar/get-handler :sub id) + (re-frame/subscribe data) + (log/fire! ctx ::log/error :query/resolve data))) + +(def ctx + {:env {:id "Extension ID"} + :capacities {:components components/all + :queries {'random-boolean + {:data :random-boolean} + 'identity + {:data :extensions/identity + :arguments {:value :map}}} + :hooks {:main + {:properties {:view :view}}} + :events {'identity + {:permissions [:read] + :data :identity + :arguments {:cb :event}} + 'alert + {:permissions [:read] + :data :alert + :arguments {:value :string}}}} + :event-fn dispatch-events + :query-fn resolve-query}) + +(def payload + {:name "Test Extension" + :users [{:nm "Jane"} + {:nm "Sue"}]}) + +(defn render [h el] + (reagent/render (h {:name "Test Extension" + :users [{:nm "Jane"} + {:nm "Sue"}]}) el)) + +(defn errors-list [v] + (fn [] + [:div + [:div "Errors"] + (into [:ul] + (for [[_ {type :type :as m}] v] + [:li + [:span [:b (str type)] (pr-str (dissoc m :type))]]))])) + +(defn render-extension [m el el-errors] + (let [{:keys [data errors]} (pluto/parse ctx m)] + (when errors + (render (errors-list errors) el-errors)) + (when-let [f (get-in data [:lifecycle :on-activation])] + (f)) + (if-let [view (get-in data [:hooks :main.demo :view])] + (render view el) + (render (fn [] [:div "Oups"]) el)))) + +(defn read-extension [o el el-errors] + (let [{:keys [data errors]} (pluto/read (:content o))] + (render-extension data el el-errors))) + +(defn render-result [{:keys [type value]} el el-errors] + (case type + :error (set! (.-innerHTML el-errors) value) + (read-extension value el el-errors))) + +(defn ^:export bootstrap + [s el el-errors] + (storages/fetch s #(render-result % el el-errors))) diff --git a/scripts/merge-pr.sh b/scripts/merge-pr.sh deleted file mode 100644 index f833407..0000000 --- a/scripts/merge-pr.sh +++ /dev/null @@ -1,148 +0,0 @@ -#!/bin/bash - -set -eof pipefail - -trap cleanup EXIT - -fatal() { - echo "FATAL: $@" >&2 - exit 1 -} - -warn() { - echo "$@" -} - -confirm() { - read -p "$1 (type 'yes' to continue) " r - if [[ $r != yes ]]; then - exit 3 - fi -} - -load_config() { - [[ -f merge-pr.conf ]] && . merge-pr.conf - : ${OWNER:=status-im} - : ${REPO:=pluto} - : ${REMOTE:=origin} - : ${BRANCH:=master} -} - -check_pr_prereq() { - if ! command -v jq >/dev/null; then - fatal "jq(1) is not found, PR cannot be queried." - fi - if ! command -v curl >/dev/null; then - fatal "curl(1) is not found, PR cannot be queried." - fi -} - -GH_URL_BASE="https://api.github.com" - -get_pr_info() { - echo '[ Reading PR info ]' - local pr=$1 - local pr_info_url="$GH_URL_BASE/repos/${OWNER}/${REPO}/pulls/$pr" - set +e - local pr_info - pr_info=$(curl -fsS "$pr_info_url") - if [ $? -ne 0 ]; then - fatal "Unable to get PR info from $pr_info_url" - fi - set -e - if [[ $(echo "$pr_info" | jq -r .state) == closed ]]; then - fatal "PR $pr is closed, will not merge" - fi - if [[ ($(echo "$pr_info" | jq -r .maintainer_can_modify) == true) ||\ - ($(echo "$pr_info" | jq -r .author_association) == MEMBER) ||\ - ($(echo "$pr_info" | jq -r .author_association) == OWNER)]]; then - RW_PR_REPO=1 - else - warn "PR does not allow 'edits from maintainers', so it will be kept open" - fi - PR_URL=$(echo "$pr_info" | jq -r .head.repo.ssh_url) - PR_REMOTE_NAME=pr-$pr - PR_BRANCH=$(echo "$pr_info" | jq -r .head.ref) - PR_LOCAL_BRANCH=pr-$pr -} - -fetch_pr() { - echo '[ Fetching PR ]' - git remote add $PR_REMOTE_NAME $PR_URL - git fetch $PR_REMOTE_NAME $PR_BRANCH -} - -refresh_base_branch() { - git fetch $REMOTE $BRANCH -} - -rebase_pr() { - git checkout -B $PR_LOCAL_BRANCH $PR_REMOTE_NAME/$PR_BRANCH - git rebase $BRANCH -} - -check_is_pr_single_commit() { - if [[ $(git rev-list $BRANCH..$PR_LOCAL_BRANCH | wc -l) -ne 1 ]] ;then - confirm "PR has multiple commits, continue merging without squashing them?" - fi -} - -confirm_pr() { - git log -p $BRANCH..$PR_LOCAL_BRANCH - confirm "Do you like this PR?" -} - -sign_pr() { - git commit --amend --gpg-sign --signoff -} - -verify_pr() { - git show --show-signature $PR_LOCAL_BRANCH - confirm "Is the signature on the commit correct?" -} - -merge_pr() { - # If PR is specified and can be pushed into, do it to mark PR as closed - if [[ -n $RW_PR_REPO ]]; then - git push -f $PR_REMOTE_NAME $PR_LOCAL_BRANCH:$PR_BRANCH - fi - git checkout $BRANCH - git merge --ff-only $PR_LOCAL_BRANCH - git push $REMOTE $BRANCH -} - -cleanup() { - if [[ -z $DEBUG ]]; then - git checkout -q $BRANCH - git branch -q -D $PR_LOCAL_BRANCH 2>/dev/null || : - git remote remove $PR_REMOTE_NAME 2>/dev/null || : - fi -} - -run() { - if [[ $# -ne 1 ]] ; then - cat <&2 -Requirements: - jq - curl -Usage: - ./merge-pr.sh - -EOF - exit 2 - fi - load_config - check_pr_prereq - get_pr_info "$@" - cleanup - fetch_pr - refresh_base_branch - rebase_pr - check_is_pr_single_commit - confirm_pr - sign_pr - verify_pr - merge_pr -} - -run "$@" diff --git a/scripts/publish.sh b/scripts/publish.sh deleted file mode 100644 index 703585f..0000000 --- a/scripts/publish.sh +++ /dev/null @@ -1,7 +0,0 @@ -clojure -m cljs.main -O advanced -d website/static/js -o website/static/js/pluto.js -c pluto.js -cd website -GIT_USER=jeluard\ -CURRENT_BRANCH=#2 \ - USE_SSH=true \ - yarn run publish-gh-pages -cd .. diff --git a/src/pluto/js.cljs b/src/pluto/js.cljs deleted file mode 100644 index 2874272..0000000 --- a/src/pluto/js.cljs +++ /dev/null @@ -1,344 +0,0 @@ -(ns pluto.js - "Exports reader function to JavaScript hosts. - Type conversion is properly handled." - (:require [pluto.core :as pluto])) - -(defn ^:export to-clj [o] - (js->clj o)) - -(defn ^:export from-clj [o] - (clj->js o)) - -(defn component []) - -;; TODO find a syntax so that :event can define associated types they will be injected - -(def ctx - {:capacities {:components {'view {:data component} - 'scroll-view {:data component :properties {:keyboard-should-persist-taps :keyword :content-container-style :map}} - 'keyboard-avoiding-view {:data component} - 'text {:data component} - 'touchable-opacity {:data component :properties {:on-press :event}} - 'icon {:data component :properties {:key :keyword :color :any}} - 'image {:data component :properties {:uri :string :source :string}} - 'input {:data component :properties {:on-change :event :placeholder :string :keyboard-type :keyword :change-delay? :number :placeholder-text-color :any :selection-color :any}} - 'button {:data component :properties {:enabled :boolean :disabled :boolean :on-click :event}} - 'link {:data component :properties {:uri :string}} - 'list {:data component :properties {:data :vector :item-view :view :key? :keyword}} - 'checkbox {:data component :properties {:on-change :event :checked :boolean}} - 'activity-indicator {:data component :properties {:animating :boolean :color :string :size :keyword :hides-when-stopped :boolean}} - 'picker {:data component :properties {:on-change :event :selected :string :enabled :boolean :data :vector}} - 'nft-token-viewer {:data component :properties {:token :string}} - 'transaction-status {:data component :properties {:outgoing :string :tx-hash :string}} - 'map {:data component - :properties {:marker {:lng :number - :lat :number - :boundingbox {:lng1 :number - :lat1 :number - :lng2 :number - :lat2 :number}} - :fly? :boolean - :interactive? :boolean - :on-change :event}} - 'map-link {:data component :properties {:text :string :lng :any :lat :any}}} - :queries {'identity {:data :extensions/identity :arguments {:value :map}} - 'store/get {:data :store/get :arguments {:key :string}} - 'contacts/all {:data :extensions.contacts/all} - 'wallet/collectibles {:data :get-collectible-token :arguments {:token :string :symbol :string}} - 'wallet/balance {:data :extensions.wallet/balance :arguments {:token :string}} - 'wallet/token {:data :extensions.wallet/token :arguments {:token :string :amount? :number :amount-in-wei? :number}} - 'wallet/tokens {:data :extensions.wallet/tokens :arguments {:filter? :vector :visible? :boolean}}} - :events {'identity - {:permissions [:read] - :data :extensions/identity-event - :arguments {:cb :event}} - 'alert - {:permissions [:read] - :data :alert - :arguments {:value :string}} - 'selection-screen - {:permissions [:read] - :data :extensions/show-selection-screen - :arguments {:items :vector :on-select :event :render :view :title :string :extractor-key :keyword}} - 'chat.command/set-parameter - {:permissions [:read] - :data :extensions.chat.command/set-parameter - :arguments {:value :any}} - 'chat.command/set-custom-parameter - {:permissions [:read] - :data :extensions.chat.command/set-custom-parameter - :arguments {:key :keyword :value :any}} - 'chat.command/set-parameter-with-custom-params - {:permissions [:read] - :data :extensions.chat.command/set-parameter-with-custom-params - :arguments {:value :string :params :map}} - 'chat.command/send-plain-text-message - {:permissions [:read] - :data :extensions.chat.command/send-plain-text-message - :arguments {:value :string}} - 'chat.command/send-message - {:permissions [:read] - :data :extensions.chat.command/send-message - :arguments {:params :map}} - 'log - {:permissions [:read] - :data :log - :arguments {:value :string}} - 'arithmetic - {:permissions [:read] - :data :extensions/arithmetic - :arguments {:values :vector - :operation {:one-of #{:plus :minus :times :divide}} - :on-result :event}} - 'schedule/start - {:permissions [:read] - :data :extensions/schedule-start - :arguments {:interval :number - :on-created :event - :on-result :event}} - 'schedule/cancel - {:permissions [:read] - :data :extensions/schedule-cancel - :arguments {:value :number}} - 'json/parse - {:permissions [:read] - :data :extensions/json-parse - :arguments {:value :string - :on-result :event}} - 'json/stringify - {:permissions [:read] - :data :extensions/json-stringify - :arguments {:value :string - :on-result :event}} - 'store/put - {:permissions [:read] - :data :store/put - :arguments {:key :string :value :any}} - 'store/puts - {:permissions [:read] - :data :store/puts - :arguments {:value :vector}} - 'store/append - {:permissions [:read] - :data :store/append - :arguments {:key :string :value :any}} - 'store/clear - {:permissions [:read] - :data :store/clear - :arguments {:key :string}} - 'store/clear-all - {:permissions [:read] - :data :store/clear-all} - 'http/get - {:permissions [:read] - :data :http/get - :arguments {:url :string - :timeout? :string - :on-success :event - :on-failure? :event}} - 'http/post - {:permissions [:read] - :data :http/post - :arguments {:url :string - :body :string - :timeout? :string - :on-success :event - :on-failure? :event}} - 'ipfs/cat - {:permissions [:read] - :data :ipfs/cat - :arguments {:hash :string - :on-success :event - :on-failure? :event}} - 'ipfs/add - {:permissions [:read] - :data :ipfs/add - :arguments {:value :string - :on-success :event - :on-failure? :event}} - 'ethereum/transaction-receipt - {:permissions [:read] - :data :extensions/ethereum-transaction-receipt - :arguments {:value :string - :on-success :event - :on-failure? :event}} - 'ethereum/await-transaction-receipt - {:permissions [:read] - :data :extensions/ethereum-await-transaction-receipt - :arguments {:value :string - :interval :number - :on-success :event - :on-failure? :event}} - 'ethereum/sign - {:permissions [:read] - :data :extensions/ethereum-sign - :arguments {:message? :string - :data? :string - :on-success :event - :on-failure? :event}} - 'ethereum/create-address - {:permissions [:read] - :data :extensions/ethereum-create-address - :arguments {:on-result :event}} - 'ethereum/send-transaction - {:permissions [:read] - :data :extensions/ethereum-send-transaction - :arguments {:to :string - :gas? :string - :gas-price? :string - :value? :string - :method? :string - :params? :vector - :nonce? :string - :on-success :event - :on-failure? :event}} - 'ethereum/logs - {:permissions [:read] - :data :extensions/ethereum-logs - :arguments {:from? :string - :to? :string - :address? :vector - :topics? :vector - :block-hash? :string - :on-success :event - :on-failure? :event}} - 'ethereum/create-filter - {:permissions [:read] - :data :extensions/ethereum-create-filter - :arguments {:type {:one-of #{:filter :block :pending-transaction}} - :from? :string - :to? :string - :address? :vector - :topics? :vector - :block-hash? :string - :on-success :event - :on-failure? :event}} - 'ethereum/logs-changes - {:permissions [:read] - :data :extensions/ethereum-logs-changes - :arguments {:id :string}} - 'ethereum/cancel-filter - {:permissions [:read] - :data :extensions/ethereum-cancel-filter - :arguments {:id :string}} - 'ethereum.ens/resolve - {:permissions [:read] - :data :extensions/ethereum-resolve-ens - :arguments {:name :string - :on-success :event - :on-failure? :event}} - 'ethereum.erc20/total-supply - {:permissions [:read] - :data :extensions/ethereum-erc20-total-supply - :arguments {:contract :string - :on-success :event - :on-failure? :event}} - 'ethereum.erc20/balance-of - {:permissions [:read] - :data :extensions/ethereum-erc20-balance-of - :arguments {:contract :string - :token-owner :string - :on-success :event - :on-failure? :event}} - 'ethereum.erc20/transfer - {:permissions [:read] - :data :extensions/ethereum-erc20-transfer - :arguments {:contract :string - :to :string - :value :number - :on-success :event - :on-failure? :event}} - 'ethereum.erc20/transfer-from - {:permissions [:read] - :data :extensions/ethereum-erc20-transfer-from - :arguments {:contract :string - :from :string - :to :string - :value :number - :on-success :event - :on-failure? :event}} - 'ethereum.erc20/approve - {:permissions [:read] - :data :extensions/ethereum-erc20-approve - :arguments {:contract :string - :spender :string - :value :number - :on-success :event - :on-failure? :event}} - 'ethereum.erc20/allowance - {:permissions [:read] - :data :extensions/ethereum-erc20-allowance - :arguments {:contract :string - :token-owner :string - :spender :string - :on-success :event - :on-failure? :event}} - 'ethereum.erc721/owner-of - {:permissions [:read] - :data :extensions/ethereum-erc721-owner-of - :arguments {:contract :string - :token-id :string - :on-success :event - :on-failure? :event}} - 'ethereum.erc721/is-approved-for-all - {:permissions [:read] - :data :extensions/ethereum-erc721-is-approved-for-all - :arguments {:contract :string - :owner :string - :operator :string - :on-success :event - :on-failure? :event}} - 'ethereum.erc721/get-approved - {:permissions [:read] - :data :extensions/ethereum-erc721-get-approved - :arguments {:contract :string - :token-id :string - :on-success :event - :on-failure? :event}} - 'ethereum.erc721/set-approval-for-all - {:permissions [:read] - :data :extensions/ethereum-erc721-set-approval-for-all - :arguments {:contract :string - :operator :string - :approved :boolean - :on-success :event - :on-failure? :event}} - 'ethereum.erc721/safe-transfer-from - {:permissions [:read] - :data :extensions/ethereum-erc721-safe-transfer-from - :arguments {:contract :string - :from :string - :to :string - :token-id :string - :data? :string - :on-success :event - :on-failure? :event}} - 'ethereum/call - {:permissions [:read] - :data :extensions/ethereum-call - :arguments {:to :string - :method :string - :params? :vector - :outputs? :vector - :on-success :event - :on-failure? :event}}} - :hooks {:wallet.settings {:properties {:label :string - :view :view - :on-click? :event}} - :chat.command {:properties {:description? :string - :scope #{:personal-chats :public-chats :group-chats} - :short-preview :view - :preview :view - :parameters? [{:id :keyword - :type {:one-of #{:text :phone :password :number}} - :placeholder :string - :suggestions? :view}] - :on-send? :event - :on-send-sync? :event - :on-receive? :event}}}}}) - -(defn ^:export read [s] - (pluto/read s)) - -(defn ^:export parse [m] - (pluto/parse ctx (:data m)))