parent
6ab6e3f041
commit
51f6a173df
|
@ -165,12 +165,15 @@
|
||||||
(utils/show-popup (i18n/label :t/error)
|
(utils/show-popup (i18n/label :t/error)
|
||||||
(i18n/label :t/photos-access-error))))
|
(i18n/label :t/photos-access-error))))
|
||||||
|
|
||||||
(defn show-image-picker [images-fn]
|
(defn show-image-picker
|
||||||
|
([images-fn]
|
||||||
|
(show-image-picker images-fn nil))
|
||||||
|
([images-fn media-type]
|
||||||
(let [image-picker (.-default image-picker-class)]
|
(let [image-picker (.-default image-picker-class)]
|
||||||
(-> image-picker
|
(-> image-picker
|
||||||
(.openPicker (clj->js {:multiple false}))
|
(.openPicker (clj->js {:multiple false :mediaType (or media-type "any")}))
|
||||||
(.then images-fn)
|
(.then images-fn)
|
||||||
(.catch show-access-error))))
|
(.catch show-access-error)))))
|
||||||
|
|
||||||
;; Clipboard
|
;; Clipboard
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
(ns status-im.extensions.camera
|
||||||
|
(:require [re-frame.core :as re-frame]
|
||||||
|
[reagent.core :as reagent]
|
||||||
|
[status-im.utils.fx :as fx]
|
||||||
|
[status-im.extensions.views :as ext-views]
|
||||||
|
[status-im.ui.components.list-selection :as list-selection]
|
||||||
|
[status-im.qr-scanner.core :as qr-scanner]
|
||||||
|
[status-im.ui.screens.navigation :as navigation]
|
||||||
|
[status-im.utils.handlers :as handlers]))
|
||||||
|
|
||||||
|
;; Common
|
||||||
|
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:extensions/camera-cancel
|
||||||
|
(fn [_ [_ _ {{:keys [on-failure]} :data}]]
|
||||||
|
(when on-failure
|
||||||
|
(re-frame/dispatch (on-failure {:result "user cancelled"})))))
|
||||||
|
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:extensions/camera-denied
|
||||||
|
(fn [_ [_ {{:keys [on-failure]} :data}]]
|
||||||
|
(when on-failure
|
||||||
|
(re-frame/dispatch (on-failure {:result "user denied access to camera"})))))
|
||||||
|
|
||||||
|
;; Photo taker\picker
|
||||||
|
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:extensions/camera-error
|
||||||
|
(fn [cofx [_ error {:keys [on-failure]}]]
|
||||||
|
(when on-failure
|
||||||
|
{:dispatch (on-failure {:result error})})))
|
||||||
|
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:extensions/camera-picture-taken
|
||||||
|
(fn [cofx [_ base64 {{:keys [on-success]} :data}]]
|
||||||
|
(fx/merge cofx
|
||||||
|
{:dispatch (on-success {:result base64})}
|
||||||
|
(navigation/navigate-back))))
|
||||||
|
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:extensions/camera-picture
|
||||||
|
(fn [_ [_ _ params]]
|
||||||
|
(list-selection/show (ext-views/pick-or-take-picture-list-selection {:data params}))
|
||||||
|
{}))
|
||||||
|
|
||||||
|
;; QR code scanner
|
||||||
|
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:extensions/camera-qr-code-scanned
|
||||||
|
(fn [cofx [_ _ qr-code {{:keys [on-success]} :data}]]
|
||||||
|
(fx/merge cofx
|
||||||
|
{:dispatch (on-success {:result qr-code})}
|
||||||
|
(navigation/navigate-back))))
|
||||||
|
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:extensions/camera-qr-code
|
||||||
|
(fn [{:keys [db] :as cofx} [_ _ {:keys [on-success on-failure]}]]
|
||||||
|
(qr-scanner/scan-qr-code cofx {:modal? false
|
||||||
|
:deny-handler :extensions/camera-denied}
|
||||||
|
{:handler :extensions/camera-qr-code-scanned
|
||||||
|
:cancel-handler :extensions/camera-cancel
|
||||||
|
:data {:on-success on-success
|
||||||
|
:on-failure on-failure}})))
|
|
@ -20,6 +20,7 @@
|
||||||
[status-im.utils.handlers :as handlers]
|
[status-im.utils.handlers :as handlers]
|
||||||
[status-im.utils.fx :as fx]
|
[status-im.utils.fx :as fx]
|
||||||
status-im.extensions.ethereum
|
status-im.extensions.ethereum
|
||||||
|
status-im.extensions.camera
|
||||||
[status-im.utils.ethereum.tokens :as tokens]
|
[status-im.utils.ethereum.tokens :as tokens]
|
||||||
[status-im.utils.ethereum.core :as ethereum]
|
[status-im.utils.ethereum.core :as ethereum]
|
||||||
[status-im.chat.commands.sending :as commands-sending]))
|
[status-im.chat.commands.sending :as commands-sending]))
|
||||||
|
@ -464,6 +465,16 @@
|
||||||
:arguments {:values :vector
|
:arguments {:values :vector
|
||||||
:operation {:one-of #{:plus :minus :times :divide}}
|
:operation {:one-of #{:plus :minus :times :divide}}
|
||||||
:on-result :event}}
|
:on-result :event}}
|
||||||
|
'camera/picture
|
||||||
|
{:permissions [:read]
|
||||||
|
:value :extensions/camera-picture
|
||||||
|
:arguments {:on-success :event
|
||||||
|
:on-failure? :event}}
|
||||||
|
'camera/qr-code
|
||||||
|
{:permissions [:read]
|
||||||
|
:value :extensions/camera-qr-code
|
||||||
|
:arguments {:on-success :event
|
||||||
|
:on-failure? :event}}
|
||||||
'schedule/start
|
'schedule/start
|
||||||
{:permissions [:read]
|
{:permissions [:read]
|
||||||
:value :extensions/schedule-start
|
:value :extensions/schedule-start
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
(ns status-im.extensions.views
|
||||||
|
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||||
|
(:require [re-frame.core :as re-frame]
|
||||||
|
[reagent.core :as reagent]
|
||||||
|
[status-im.ui.components.camera :as camera]
|
||||||
|
[status-im.ui.components.react :as react]
|
||||||
|
[status-im.ui.components.status-bar.view :as status-bar]
|
||||||
|
[status-im.ui.components.toolbar.view :as toolbar]
|
||||||
|
[status-im.ui.components.toolbar.actions :as actions]
|
||||||
|
[status-im.ui.components.icons.vector-icons :as icons]
|
||||||
|
[status-im.i18n :as i18n]
|
||||||
|
[status-im.ui.screens.profile.photo-capture.styles :as styles]
|
||||||
|
[status-im.utils.image-processing :as image-processing]))
|
||||||
|
|
||||||
|
;; ensure photo taken or picked are not too big
|
||||||
|
(def max-width 1024)
|
||||||
|
(def max-height 1024)
|
||||||
|
|
||||||
|
(defn- process-image-and-finish [path context]
|
||||||
|
(let [on-success (fn [base64]
|
||||||
|
(re-frame/dispatch [:extensions/camera-picture-taken base64 context]))
|
||||||
|
on-error (fn [type error]
|
||||||
|
(re-frame/dispatch [:extensions/camera-error error context]))]
|
||||||
|
(image-processing/img->base64 path on-success on-error max-width max-height)))
|
||||||
|
|
||||||
|
(defview take-picture []
|
||||||
|
(letsubs [context [:get-screen-params]
|
||||||
|
camera-ref (reagent/atom false)]
|
||||||
|
[react/view styles/container
|
||||||
|
[status-bar/status-bar]
|
||||||
|
[toolbar/toolbar nil
|
||||||
|
[toolbar/nav-button (actions/back
|
||||||
|
#(do
|
||||||
|
(re-frame/dispatch [:extensions/camera-cancel context])
|
||||||
|
(re-frame/dispatch [:navigate-back])))]
|
||||||
|
[toolbar/content-title (i18n/label :t/extensions-camera-send-picture)]]
|
||||||
|
[camera/camera {:style {:flex 1}
|
||||||
|
:aspect (:fill camera/aspects)
|
||||||
|
:captureTarget (:disk camera/capture-targets)
|
||||||
|
:ref #(reset! camera-ref %)}]
|
||||||
|
[react/view styles/button-container
|
||||||
|
[react/view styles/button
|
||||||
|
[react/touchable-highlight {:on-press (fn []
|
||||||
|
(let [camera @camera-ref]
|
||||||
|
(-> (.capture camera)
|
||||||
|
(.then #(process-image-and-finish (.-path %) context))
|
||||||
|
(.catch #(re-frame/dispatch [:extensions/camera-error % context])))))}
|
||||||
|
[react/view
|
||||||
|
[icons/icon :main-icons/camera {:color :white}]]]]]]))
|
||||||
|
|
||||||
|
;TODO image picker doesn't notify when the user cancel image selection
|
||||||
|
; in this case we cannot notify cancel to extension
|
||||||
|
(defn- open-image-picker [context]
|
||||||
|
(react/show-image-picker
|
||||||
|
(fn [image]
|
||||||
|
(let [path (get (js->clj image) "path")]
|
||||||
|
(process-image-and-finish path context)))
|
||||||
|
"photo"))
|
||||||
|
|
||||||
|
(defn pick-or-take-picture-list-selection [context]
|
||||||
|
{:title (i18n/label :t/extensions-camera-send-picture)
|
||||||
|
:options [{:label (i18n/label :t/image-source-gallery)
|
||||||
|
:action #(open-image-picker context)}
|
||||||
|
{:label (i18n/label :t/image-source-make-photo)
|
||||||
|
:action (fn []
|
||||||
|
(re-frame/dispatch [:request-permissions {:permissions [:camera :write-external-storage]
|
||||||
|
:on-allowed #(re-frame/dispatch [:navigate-to :take-picture context])
|
||||||
|
:on-denied #(re-frame/dispatch [:extensions/camera-denied context])}]))}]
|
||||||
|
:on-cancel #(re-frame/dispatch [:extensions/camera-cancel context])})
|
|
@ -5,16 +5,18 @@
|
||||||
[status-im.utils.fx :as fx]))
|
[status-im.utils.fx :as fx]))
|
||||||
|
|
||||||
(fx/defn scan-qr-code
|
(fx/defn scan-qr-code
|
||||||
[{:keys [db]} {:keys [modal?] :as identifier} qr-codes]
|
[{:keys [db]} {:keys [modal? deny-handler] :as identifier} qr-codes]
|
||||||
{:db (assoc db :qr-codes qr-codes)
|
{:db (assoc db :qr-codes qr-codes)
|
||||||
:request-permissions-fx {:permissions [:camera]
|
:request-permissions-fx {:permissions [:camera]
|
||||||
:on-allowed #(re-frame/dispatch [(if modal? :navigate-to-modal :navigate-to)
|
:on-allowed #(re-frame/dispatch [(if modal? :navigate-to-modal :navigate-to)
|
||||||
:qr-scanner {:current-qr-context identifier}])
|
:qr-scanner {:current-qr-context identifier}])
|
||||||
:on-denied (fn []
|
:on-denied (if (nil? deny-handler)
|
||||||
|
(fn []
|
||||||
(utils/set-timeout
|
(utils/set-timeout
|
||||||
#(utils/show-popup (i18n/label :t/error)
|
#(utils/show-popup (i18n/label :t/error)
|
||||||
(i18n/label :t/camera-access-error))
|
(i18n/label :t/camera-access-error))
|
||||||
50))}})
|
50))
|
||||||
|
#(re-frame/dispatch [deny-handler qr-codes]))}})
|
||||||
|
|
||||||
(fx/defn set-qr-code
|
(fx/defn set-qr-code
|
||||||
[{:keys [db]} context data]
|
[{:keys [db]} context data]
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
[status-im.utils.core :as utils]
|
[status-im.utils.core :as utils]
|
||||||
[status-im.react-native.js-dependencies :as js-dependencies]))
|
[status-im.react-native.js-dependencies :as js-dependencies]))
|
||||||
|
|
||||||
(defn- callback [options]
|
(defn- callback [options on-cancel]
|
||||||
(fn [index]
|
(fn [index]
|
||||||
(when (< index (count options))
|
(if (< index (count options))
|
||||||
(when-let [handler (:action (nth options index))]
|
(when-let [handler (:action (nth options index))]
|
||||||
(handler)))))
|
(handler))
|
||||||
|
(when on-cancel
|
||||||
|
(on-cancel)))))
|
||||||
|
|
||||||
(defn- prepare-options [title message options]
|
(defn- prepare-options [title message options]
|
||||||
(let [destructive-opt-index (utils/first-index :destructive? options)] ;; TODO Can only be a single destructive?
|
(let [destructive-opt-index (utils/first-index :destructive? options)] ;; TODO Can only be a single destructive?
|
||||||
|
@ -18,7 +20,7 @@
|
||||||
(when title {:title title})
|
(when title {:title title})
|
||||||
(when message {:message message})))))
|
(when message {:message message})))))
|
||||||
|
|
||||||
(defn show [{:keys [title message options]}]
|
(defn show [{:keys [title message options on-cancel]}]
|
||||||
(.showActionSheetWithOptions (.-ActionSheetIOS js-dependencies/react-native)
|
(.showActionSheetWithOptions (.-ActionSheetIOS js-dependencies/react-native)
|
||||||
(prepare-options title message options)
|
(prepare-options title message options)
|
||||||
(callback options)))
|
(callback options on-cancel)))
|
||||||
|
|
|
@ -3,17 +3,21 @@
|
||||||
|
|
||||||
(def dialogs (.-default rn-dependencies/dialogs))
|
(def dialogs (.-default rn-dependencies/dialogs))
|
||||||
|
|
||||||
(defn show [{:keys [title options cancel-text]}]
|
(defn show [{:keys [title options cancel-text on-cancel]}]
|
||||||
(.. dialogs
|
(.. dialogs
|
||||||
(showPicker title nil (clj->js {:items (mapv #(select-keys % [:label])
|
(showPicker title nil (clj->js {:items (mapv #(select-keys % [:label])
|
||||||
options)
|
options)
|
||||||
:negativeText cancel-text
|
:negativeText cancel-text
|
||||||
:positiveText nil}))
|
:positiveText nil}))
|
||||||
(then (fn [selected]
|
(then (fn [selected]
|
||||||
(when-let [label (get-in (js->clj selected :keywordize-keys true)
|
(let [result (js->clj selected :keywordize-keys true)]
|
||||||
|
(if (not= (get result :action) "actionSelect")
|
||||||
|
(when on-cancel
|
||||||
|
(on-cancel))
|
||||||
|
(when-let [label (get-in result
|
||||||
[:selectedItem :label])]
|
[:selectedItem :label])]
|
||||||
(when-let [action (->> options
|
(when-let [action (->> options
|
||||||
(filter #(= label (:label %)))
|
(filter #(= label (:label %)))
|
||||||
first
|
first
|
||||||
:action)]
|
:action)]
|
||||||
(action)))))))
|
(action)))))))))
|
|
@ -5,7 +5,6 @@
|
||||||
[status-im.ui.screens.profile.models :as profile.models]
|
[status-im.ui.screens.profile.models :as profile.models]
|
||||||
[status-im.ui.screens.profile.navigation]
|
[status-im.ui.screens.profile.navigation]
|
||||||
[status-im.ui.components.list-selection :as list-selection]
|
[status-im.ui.components.list-selection :as list-selection]
|
||||||
[status-im.ui.screens.profile.models :as profile.models]
|
|
||||||
[status-im.utils.handlers :as handlers]
|
[status-im.utils.handlers :as handlers]
|
||||||
[status-im.utils.identicon :as identicon]
|
[status-im.utils.identicon :as identicon]
|
||||||
[status-im.utils.universal-links.core :as universal-links]))
|
[status-im.utils.universal-links.core :as universal-links]))
|
||||||
|
|
|
@ -19,7 +19,8 @@
|
||||||
(re-frame/dispatch [callback-event base64]))
|
(re-frame/dispatch [callback-event base64]))
|
||||||
on-error (fn [type error]
|
on-error (fn [type error]
|
||||||
(.log js/console type error))]
|
(.log js/console type error))]
|
||||||
(image-processing/img->base64 path on-success on-error)))))
|
(image-processing/img->base64 path on-success on-error 150 150)))
|
||||||
|
"photo"))
|
||||||
|
|
||||||
(defn send-transaction [chat-id {:keys [db] :as cofx}]
|
(defn send-transaction [chat-id {:keys [db] :as cofx}]
|
||||||
(let [send-command (get-in db [:id->command ["send" #{:personal-chats}]])]
|
(let [send-command (get-in db [:id->command ["send" #{:personal-chats}]])]
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
(re-frame/dispatch [:navigate-back]))
|
(re-frame/dispatch [:navigate-back]))
|
||||||
on-error (fn [type error]
|
on-error (fn [type error]
|
||||||
(log/debug type error))]
|
(log/debug type error))]
|
||||||
(image-processing/img->base64 path on-success on-error)))
|
(image-processing/img->base64 path on-success on-error 150 150)))
|
||||||
|
|
||||||
(defn profile-photo-capture []
|
(defn profile-photo-capture []
|
||||||
(let [camera-ref (reagent/atom nil)]
|
(let [camera-ref (reagent/atom nil)]
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
[status-im.ui.screens.profile.contact.views :as profile.contact]
|
[status-im.ui.screens.profile.contact.views :as profile.contact]
|
||||||
[status-im.ui.screens.profile.group-chat.views :as profile.group-chat]
|
[status-im.ui.screens.profile.group-chat.views :as profile.group-chat]
|
||||||
[status-im.ui.screens.profile.photo-capture.views :refer [profile-photo-capture]]
|
[status-im.ui.screens.profile.photo-capture.views :refer [profile-photo-capture]]
|
||||||
|
[status-im.extensions.views :refer [take-picture]]
|
||||||
[status-im.ui.screens.wallet.main.views :as wallet.main]
|
[status-im.ui.screens.wallet.main.views :as wallet.main]
|
||||||
[status-im.ui.screens.wallet.collectibles.views :refer [collectibles-list]]
|
[status-im.ui.screens.wallet.collectibles.views :refer [collectibles-list]]
|
||||||
[status-im.ui.screens.wallet.send.views :refer [send-transaction send-transaction-modal sign-message-modal]]
|
[status-im.ui.screens.wallet.send.views :refer [send-transaction send-transaction-modal sign-message-modal]]
|
||||||
|
@ -169,6 +170,7 @@
|
||||||
:new add-new
|
:new add-new
|
||||||
:new-chat new-chat
|
:new-chat new-chat
|
||||||
:qr-scanner qr-scanner
|
:qr-scanner qr-scanner
|
||||||
|
:take-picture take-picture
|
||||||
:new-group new-group
|
:new-group new-group
|
||||||
:add-participants-toggle-list add-participants-toggle-list
|
:add-participants-toggle-list add-participants-toggle-list
|
||||||
:contact-toggle-list contact-toggle-list
|
:contact-toggle-list contact-toggle-list
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
(on-error :base64 error))]
|
(on-error :base64 error))]
|
||||||
(read-file path "base64" on-success on-error)))
|
(read-file path "base64" on-success on-error)))
|
||||||
|
|
||||||
(defn img->base64 [path on-success on-error]
|
(defn img->base64 [path on-success on-error max-width max-height]
|
||||||
(let [on-resized (fn [image]
|
(let [on-resized (fn [image]
|
||||||
(let [path (object/get image "path")]
|
(let [path (object/get image "path")]
|
||||||
(log/debug "Resized: " path)
|
(log/debug "Resized: " path)
|
||||||
|
@ -26,4 +26,4 @@
|
||||||
on-error (fn [error]
|
on-error (fn [error]
|
||||||
(log/debug "Resized error: " error)
|
(log/debug "Resized error: " error)
|
||||||
(on-error :resize error))]
|
(on-error :resize error))]
|
||||||
(resize path 150 150 on-resized on-error)))
|
(resize path max-width max-height on-resized on-error)))
|
||||||
|
|
|
@ -920,6 +920,7 @@
|
||||||
"mailserver-error-content": "The mailserver you selected couldn't be reached.",
|
"mailserver-error-content": "The mailserver you selected couldn't be reached.",
|
||||||
"dapps-permissions": "Dapps permissions",
|
"dapps-permissions": "Dapps permissions",
|
||||||
"revoke-access": "Revoke access",
|
"revoke-access": "Revoke access",
|
||||||
|
"extensions-camera-send-picture": "Send picture",
|
||||||
"mobile-syncing-sheet-title": "Sync using the mobile network",
|
"mobile-syncing-sheet-title": "Sync using the mobile network",
|
||||||
"mobile-syncing-sheet-details": "Status tends to use a lot of data when syncing chats. You can choose not to sync when on mobile network",
|
"mobile-syncing-sheet-details": "Status tends to use a lot of data when syncing chats. You can choose not to sync when on mobile network",
|
||||||
"mobile-network-continue-syncing": "Continue syncing",
|
"mobile-network-continue-syncing": "Continue syncing",
|
||||||
|
|
Loading…
Reference in New Issue