Signed-off-by: Julien Eluard <julien.eluard@gmail.com>
This commit is contained in:
tbenr 2019-02-08 18:14:02 +01:00 committed by Julien Eluard
parent 6ab6e3f041
commit 51f6a173df
No known key found for this signature in database
GPG Key ID: 6FD7DB5437FCBEF6
13 changed files with 187 additions and 30 deletions

View File

@ -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

View File

@ -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}})))

View File

@ -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

View File

@ -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])})

View File

@ -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]

View File

@ -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)))

View File

@ -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)))))))))

View File

@ -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]))

View File

@ -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}]])]

View File

@ -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)]

View File

@ -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

View File

@ -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)))

View File

@ -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",