feat: camera screen (1/2) (#16569)

* feat: camera screen (1/2)
This commit is contained in:
Omar Basem 2023-07-24 16:46:43 +04:00 committed by GitHub
parent c5a486622d
commit e33c877989
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 277 additions and 35 deletions

1
.env
View File

@ -22,7 +22,6 @@ KEYCARD_TEST_MENU=0
QR_READ_TEST_MENU=1 QR_READ_TEST_MENU=1
ENABLE_ROOT_ALERT=1 ENABLE_ROOT_ALERT=1
ENABLE_QUO_PREVIEW=1 ENABLE_QUO_PREVIEW=1
MAX_IMAGES_BATCH=5
APN_TOPIC=im.status.ethereum.pr APN_TOPIC=im.status.ethereum.pr
COMMUNITIES_ENABLED=1 COMMUNITIES_ENABLED=1
DATABASE_MANAGEMENT_ENABLED=1 DATABASE_MANAGEMENT_ENABLED=1

View File

@ -21,7 +21,6 @@ COMMANDS_ENABLED=1
KEYCARD_TEST_MENU=1 KEYCARD_TEST_MENU=1
QR_READ_TEST_MENU=1 QR_READ_TEST_MENU=1
ENABLE_ROOT_ALERT=0 ENABLE_ROOT_ALERT=0
MAX_IMAGES_BATCH=5
APN_TOPIC=im.status.ethereum.pr APN_TOPIC=im.status.ethereum.pr
VERIFY_TRANSACTION_CHAIN_ID=5 VERIFY_TRANSACTION_CHAIN_ID=5
VERIFY_ENS_CHAIN_ID=5 VERIFY_ENS_CHAIN_ID=5

View File

@ -26,7 +26,6 @@ VERIFY_TRANSACTION_CHAIN_ID=5
VERIFY_ENS_CHAIN_ID=5 VERIFY_ENS_CHAIN_ID=5
TEST_STATEOFUS=1 TEST_STATEOFUS=1
BLANK_PREVIEW=0 BLANK_PREVIEW=0
MAX_IMAGES_BATCH=5
DATABASE_MANAGEMENT_ENABLED=1 DATABASE_MANAGEMENT_ENABLED=1
COMMUNITIES_ENABLED=1 COMMUNITIES_ENABLED=1
COMMUNITIES_MANAGEMENT_ENABLED=1 COMMUNITIES_MANAGEMENT_ENABLED=1

View File

@ -17,7 +17,6 @@ RPC_NETWORKS_ONLY=0
PARTITIONED_TOPIC=0 PARTITIONED_TOPIC=0
CONTRACT_NODES=1 CONTRACT_NODES=1
ENABLE_ROOT_ALERT=1 ENABLE_ROOT_ALERT=1
MAX_IMAGES_BATCH=5
BLANK_PREVIEW=0 BLANK_PREVIEW=0
COMMUNITIES_ENABLED=1 COMMUNITIES_ENABLED=1
DATABASE_MANAGEMENT_ENABLED=1 DATABASE_MANAGEMENT_ENABLED=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -35,4 +35,5 @@
(get-icons 16) (get-icons 16)
(get-icons 20) (get-icons 20)
(get-icons 24) (get-icons 24)
(get-icons 32))) (get-icons 32)
(get-icons 48)))

View File

@ -136,6 +136,8 @@
;;Solid ;;Solid
(def black "#000000") (def black "#000000")
(def black-opa-0 (alpha black 0)) (def black-opa-0 (alpha black 0))
(def black-opa-30 (alpha black 0.3))
(def black-opa-60 (alpha black 0.6))
(def onboarding-header-black "#000716") (def onboarding-header-black "#000716")
;;;;Primary ;;;;Primary
@ -180,6 +182,8 @@
(def danger-50 "#E95460") (def danger-50 "#E95460")
(def danger-60 "#BA434D") (def danger-60 "#BA434D")
(def system-yellow "#FFD60A")
;;50 with transparency ;;50 with transparency
(def danger-50-opa-5 (alpha danger-50 0.05)) (def danger-50-opa-5 (alpha danger-50 0.05))
(def danger-50-opa-10 (alpha danger-50 0.1)) (def danger-50-opa-10 (alpha danger-50 0.1))

View File

@ -1,8 +1,16 @@
(ns react-native.camera-kit (ns react-native.camera-kit
(:require ["react-native-camera-kit" :refer (Camera CameraType)] (:require ["react-native-camera-kit" :refer (Camera CameraType)]
[reagent.core :as reagent])) [reagent.core :as reagent]
[oops.core :as oops]
[taoensso.timbre :as log]))
(def camera (reagent/adapt-react-class Camera)) (def camera (reagent/adapt-react-class Camera))
(def camera-type-front (.-Front CameraType)) (def camera-type-front (.-Front CameraType))
(def camera-type-back (.-Back CameraType)) (def camera-type-back (.-Back CameraType))
(defn capture
[^js camera-ref on-success]
(-> (.capture camera-ref)
(.then #(on-success (oops/oget % :uri)))
(.catch #(log/warn "couldn't capture photo" {:error %}))))

View File

@ -4,8 +4,8 @@
[react-native.share :as share] [react-native.share :as share]
[react-native.cameraroll :as cameraroll] [react-native.cameraroll :as cameraroll]
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react]
[status-im2.config :as config]
[react-native.fs :as fs] [react-native.fs :as fs]
[status-im2.constants :as constants]
[utils.re-frame :as rf] [utils.re-frame :as rf]
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[taoensso.timbre :as log])) [taoensso.timbre :as log]))
@ -48,7 +48,7 @@
[{:keys [db]} chat-id uri] [{:keys [db]} chat-id uri]
(let [current-chat-id (or chat-id (:current-chat-id db)) (let [current-chat-id (or chat-id (:current-chat-id db))
images (get-in db [:chat/inputs current-chat-id :metadata :sending-image])] images (get-in db [:chat/inputs current-chat-id :metadata :sending-image])]
(when (and (< (count images) config/max-images-batch) (when (and (< (count images) constants/max-album-photos)
(not (get images uri))) (not (get images uri)))
{::image-selected [uri current-chat-id]}))) {::image-selected [uri current-chat-id]})))
@ -68,5 +68,5 @@
[{:keys [db]} chat-id] [{:keys [db]} chat-id]
(let [current-chat-id (or chat-id (:current-chat-id db)) (let [current-chat-id (or chat-id (:current-chat-id db))
images (get-in db [:chat/inputs current-chat-id :metadata :sending-image])] images (get-in db [:chat/inputs current-chat-id :metadata :sending-image])]
(when (< (count images) config/max-images-batch) (when (< (count images) constants/max-album-photos)
{::chat-open-image-picker-camera current-chat-id}))) {::chat-open-image-picker-camera current-chat-id})))

View File

@ -0,0 +1,18 @@
(ns status-im2.common.device-permissions
(:require
[quo2.foundations.colors :as colors]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn camera
[on-allowed]
(rf/dispatch
[:request-permissions
{:permissions [:camera]
:on-allowed on-allowed
:on-denied #(rf/dispatch
[:toasts/upsert
{:icon :i/info
:icon-color colors/danger-50
:override-theme :light
:text (i18n/label :t/camera-permission-denied)}])}]))

View File

@ -26,7 +26,6 @@
(def snoopy-enabled? (enabled? (get-config :SNOOPY 0))) (def snoopy-enabled? (enabled? (get-config :SNOOPY 0)))
(def dev-build? (enabled? (get-config :DEV_BUILD 0))) (def dev-build? (enabled? (get-config :DEV_BUILD 0)))
(def max-message-delivery-attempts (js/parseInt (get-config :MAX_MESSAGE_DELIVERY_ATTEMPTS "6"))) (def max-message-delivery-attempts (js/parseInt (get-config :MAX_MESSAGE_DELIVERY_ATTEMPTS "6")))
(def max-images-batch (js/parseInt (get-config :MAX_IMAGES_BATCH "1")))
;; NOTE: only disabled in releases ;; NOTE: only disabled in releases
(def local-notifications? (enabled? (get-config :LOCAL_NOTIFICATIONS "1"))) (def local-notifications? (enabled? (get-config :LOCAL_NOTIFICATIONS "1")))
(def blank-preview? (enabled? (get-config :BLANK_PREVIEW "1"))) (def blank-preview? (enabled? (get-config :BLANK_PREVIEW "1")))

View File

@ -0,0 +1,97 @@
(ns status-im2.contexts.chat.camera.style
(:require [quo2.foundations.colors :as colors]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]))
(def screen-container
{:flex 1
:background-color colors/black})
(def flash-container
{:position :absolute
:top 50
:left 25})
(defn camera-window
[width height top]
{:width width
:height height
:top top})
(def zoom-button-container
{:width 37
:height 37
:justify-content :center
:align-items :center})
(defn zoom-container
[top insets]
{:width 157
:height 43
:border-radius 100
:position :absolute
:background-color colors/black-opa-60
:align-self :center
:justify-content :space-around
:align-items :center
:flex-direction :row
:bottom (+ top (:bottom insets) (when platform/android? (:top insets)) 18)})
(defn zoom-button
[size]
(reanimated/apply-animations-to-style
{:width size
:height size}
{:background-color colors/black-opa-30
:justify-content :center
:align-items :center
:border-radius 50}))
(defn bottom-area
[top insets]
{:left 20
:right 20
:position :absolute
:height (+ top (when platform/android? (:top insets)))
:bottom (:bottom insets)})
(def photo-text
{:color colors/system-yellow
:margin-top 18
:font-size 14
:align-self :center})
(def actions-container
{:flex-direction :row
:margin-top 20
:align-items :center
:justify-content :space-between})
(def outer-circle
{:width 69
:height 69
:background-color colors/black
:border-radius 69
:border-width 6
:border-color colors/white
:justify-content :center
:align-items :center})
(def inner-circle
{:width 53
:height 53
:border-radius 53
:background-color colors/white})
(defn confirmation-container
[insets]
{:position :absolute
:bottom 0
:left 0
:right 0
:background-color "#131313"
:height (+ 69 (:bottom insets))
:flex-direction :row
:padding-horizontal 20
:justify-content :space-between
:padding-top 18})

View File

@ -0,0 +1,101 @@
(ns status-im2.contexts.chat.camera.view
(:require
[quo2.core :as quo]
[quo2.foundations.colors :as colors]
[react-native.camera-kit :as camera-kit]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[utils.i18n :as i18n]
[status-im2.contexts.chat.camera.style :as style]
[utils.re-frame :as rf]))
(defn- f-zoom-button
[{:keys [value current-zoom]}]
(let [selected? (= @current-zoom value)
size (reanimated/use-shared-value (if selected? 37 25))]
(rn/use-effect #(reanimated/animate size (if selected? 37 25)) [@current-zoom])
[rn/touchable-opacity
{:on-press #(reset! current-zoom value)
:style style/zoom-button-container
:accessibility-label (str "zoom-" value)}
[reanimated/view {:style (style/zoom-button size)}
[quo/text
{:size (if selected? :paragraph-2 :label)
:weight :semi-bold
:style {:color (if selected?
colors/system-yellow
colors/white)}}
(str value (when selected? "x"))]]]))
(defn zoom-button
[args]
[:f> f-zoom-button args])
(defn snap-button
[camera-ref uri]
[rn/view
{:style style/outer-circle
:accessibility-label :snap}
[rn/touchable-opacity
{:on-press (fn []
(camera-kit/capture @camera-ref #(reset! uri %)))
:style style/inner-circle}]])
(defn camera-screen
[]
(let [camera-ref (atom nil)
uri (reagent/atom nil)
current-zoom (reagent/atom "1")]
(fn []
(let [window (rn/get-window)
{:keys [width height]} window
camera-window-height (* width 1.33)
insets (safe-area/get-insets)
top (/ (- height camera-window-height (:bottom insets)) 2)]
[rn/view {:style style/screen-container}
(when-not @uri
[rn/view {:style style/flash-container}
[quo/icon :i/flash-camera
{:color colors/white
:size 24}]])
(if @uri
[rn/image
{:style (style/camera-window width camera-window-height top)
:source {:uri @uri}}]
[camera-kit/camera
{:ref #(reset! camera-ref %)
:style (style/camera-window width camera-window-height top)}])
(when-not @uri
[rn/view {:style (style/zoom-container top insets)}
[zoom-button {:value "0.5" :current-zoom current-zoom}]
[zoom-button {:value "1" :current-zoom current-zoom}]
[zoom-button {:value "2" :current-zoom current-zoom}]
[zoom-button {:value "3" :current-zoom current-zoom}]])
(if @uri
[rn/view {:style (style/confirmation-container insets)}
[quo/text
{:on-press #(reset! uri nil)
:style {:font-size 17
:color colors/white}}
(i18n/label :t/retake)]
[quo/text
{:on-press (fn []
(rf/dispatch [:photo-selector/camera-roll-pick {:uri @uri}])
(rf/dispatch [:navigate-back]))
:style {:font-size 17
:color colors/white}}
(i18n/label :t/use-photo)]]
[rn/view {:style (style/bottom-area top insets)}
[quo/text {:style style/photo-text} (i18n/label :t/PHOTO)]
[rn/view {:style style/actions-container}
[quo/text
{:on-press #(rf/dispatch [:navigate-back])
:style {:font-size 17
:color colors/white}
:accessibility-label :cancel}
(i18n/label :t/cancel)]
[snap-button camera-ref uri]
[quo/icon :i/rotate-camera
{:size 48 :color colors/white :accessibility-label :flip-camera}]]])]))))

View File

@ -1,6 +1,7 @@
(ns status-im2.contexts.chat.composer.actions.view (ns status-im2.contexts.chat.composer.actions.view
(:require (:require
[quo2.core :as quo] [quo2.core :as quo]
[quo2.foundations.colors :as colors]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.permissions :as permissions] [react-native.permissions :as permissions]
[react-native.platform :as platform] [react-native.platform :as platform]
@ -9,6 +10,7 @@
[status-im2.common.alert.events :as alert] [status-im2.common.alert.events :as alert]
[status-im2.contexts.chat.composer.constants :as comp-constants] [status-im2.contexts.chat.composer.constants :as comp-constants]
[status-im2.contexts.chat.messages.list.view :as messages.list] [status-im2.contexts.chat.messages.list.view :as messages.list]
[status-im2.common.device-permissions :as device-permissions]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.re-frame :as rf] [utils.re-frame :as rf]
[status-im2.contexts.chat.composer.actions.style :as style] [status-im2.contexts.chat.composer.actions.style :as style]
@ -148,16 +150,32 @@
50)}])) 50)}]))
:max-duration-ms constants/audio-max-duration-ms}]])) :max-duration-ms constants/audio-max-duration-ms}]]))
(defn images-limit-toast
[]
(rf/dispatch [:toasts/upsert
{:id :random-id
:icon :info
:icon-color colors/danger-50-opa-40
:container-style {:top (when platform/ios? 20)}
:text (i18n/label :t/only-6-images)}]))
(defn go-to-camera
[images-count]
(device-permissions/camera #(if (>= images-count constants/max-album-photos)
(images-limit-toast)
(rf/dispatch [:navigate-to :camera-screen]))))
(defn camera-button (defn camera-button
[] []
[quo/button (let [images-count (count (vals (rf/sub [:chats/sending-image])))]
{:on-press #(js/alert "to be implemented") [quo/button
:icon true {:on-press #(go-to-camera images-count)
:type :outline :icon true
:size 32 :type :outline
:style {:margin-right 12}} :size 32
:i/camera]) :style {:margin-right 12}}
:i/camera]))
(defn open-photo-selector (defn open-photo-selector
[{:keys [input-ref]} [{:keys [input-ref]}

View File

@ -2,9 +2,9 @@
(:require [react-native.cameraroll :as cameraroll] (:require [react-native.cameraroll :as cameraroll]
[clojure.string :as string] [clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im2.constants :as constants]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[react-native.permissions :as permissions] [react-native.permissions :as permissions]
[status-im2.config :as config]
[utils.re-frame :as rf] [utils.re-frame :as rf]
[status-im.utils.image-processing :as image-processing] [status-im.utils.image-processing :as image-processing]
[taoensso.timbre :as log] [taoensso.timbre :as log]
@ -141,6 +141,6 @@
[{:keys [db]} image chat-id] [{:keys [db]} image chat-id]
(let [current-chat-id (or chat-id (:current-chat-id db)) (let [current-chat-id (or chat-id (:current-chat-id db))
images (get-in db [:chat/inputs current-chat-id :metadata :sending-image])] images (get-in db [:chat/inputs current-chat-id :metadata :sending-image])]
(when (and (< (count images) config/max-images-batch) (when (and (< (count images) constants/max-album-photos)
(not (some #(= (:uri image) (:uri %)) images))) (not (some #(= (:uri image) (:uri %)) images)))
{:camera-roll-image-selected [image current-chat-id]}))) {:camera-roll-image-selected [image current-chat-id]})))

View File

@ -12,6 +12,7 @@
[react-native.reanimated :as reanimated] [react-native.reanimated :as reanimated]
[react-native.safe-area :as safe-area] [react-native.safe-area :as safe-area]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im2.common.device-permissions :as device-permissions]
[status-im2.constants :as constants] [status-im2.constants :as constants]
[status-im2.contexts.syncing.scan-sync-code.style :as style] [status-im2.contexts.syncing.scan-sync-code.style :as style]
[status-im2.contexts.syncing.utils :as sync-utils] [status-im2.contexts.syncing.utils :as sync-utils]
@ -27,19 +28,6 @@
(defonce dismiss-animations (atom nil)) (defonce dismiss-animations (atom nil))
(defonce navigate-back-fn (atom nil)) (defonce navigate-back-fn (atom nil))
(defn request-camera-permission
[]
(rf/dispatch
[:request-permissions
{:permissions [:camera]
:on-allowed #(reset! camera-permission-granted? true)
:on-denied #(rf/dispatch
[:toasts/upsert
{:icon :i/info
:icon-color colors/danger-50
:override-theme :light
:text (i18n/label :t/camera-permission-denied)}])}]))
(defn perform-preflight-check (defn perform-preflight-check
"Performing the check for the first time "Performing the check for the first time
will trigger local network access permission in iOS. will trigger local network access permission in iOS.
@ -140,7 +128,8 @@
:button-icon :i/camera :button-icon :i/camera
:button-label :t/enable-camera :button-label :t/enable-camera
:accessibility-label :request-camera-permission :accessibility-label :request-camera-permission
:on-press request-camera-permission})) :on-press (fn []
(device-permissions/camera #(reset! camera-permission-granted? true)))}))
(defn- camera-and-local-network-access-permission-view (defn- camera-and-local-network-access-permission-view
[] []

View File

@ -114,7 +114,7 @@
:animate true :animate true
:drawBehind true :drawBehind true
:translucent true} :translucent true}
:navigationBar {:backgroundColor colors/black} :navigationBar {:backgroundColor colors/neutral-100}
:layout {:componentBackgroundColor :transparent :layout {:componentBackgroundColor :transparent
:backgroundColor :transparent :backgroundColor :transparent
;; issue: https://github.com/wix/react-native-navigation/issues/7726 ;; issue: https://github.com/wix/react-native-navigation/issues/7726
@ -129,6 +129,9 @@
:decelerate :decelerate
:factor 1.5}}]}}}) :factor 1.5}}]}}})
(def camera-screen
{:navigationBar {:backgroundColor colors/black}})
(defn merge-top-bar (defn merge-top-bar
[root-options options] [root-options options]
(let [options (:topBar options)] (let [options (:topBar options)]

View File

@ -6,6 +6,7 @@
[status-im2.contexts.chat.lightbox.view :as lightbox] [status-im2.contexts.chat.lightbox.view :as lightbox]
[status-im2.contexts.chat.messages.view :as chat] [status-im2.contexts.chat.messages.view :as chat]
[status-im2.contexts.chat.photo-selector.view :as photo-selector] [status-im2.contexts.chat.photo-selector.view :as photo-selector]
[status-im2.contexts.chat.camera.view :as camera-screen]
[status-im2.contexts.communities.discover.view :as communities.discover] [status-im2.contexts.communities.discover.view :as communities.discover]
[status-im2.contexts.communities.overview.view :as communities.overview] [status-im2.contexts.communities.overview.view :as communities.overview]
[status-im2.contexts.onboarding.intro.view :as intro] [status-im2.contexts.onboarding.intro.view :as intro]
@ -107,6 +108,10 @@
:options {:sheet? true} :options {:sheet? true}
:component photo-selector/photo-selector} :component photo-selector/photo-selector}
{:name :camera-screen
:options options/camera-screen
:component camera-screen/camera-screen}
{:name :new-contact {:name :new-contact
:options {:sheet? true} :options {:sheet? true}
:component add-new-contact/new-contact} :component add-new-contact/new-contact}

View File

@ -2245,5 +2245,8 @@
"chat-unmuted-successfully": "Chat unmuted successfully! ", "chat-unmuted-successfully": "Chat unmuted successfully! ",
"channel-unmuted-successfully": "Channel unmuted successfully! ", "channel-unmuted-successfully": "Channel unmuted successfully! ",
"photo-saved": "Photo saved to your device", "photo-saved": "Photo saved to your device",
"community-unmuted": "Community unmuted" "community-unmuted": "Community unmuted",
"retake": "Retake",
"use-photo": "Use Photo",
"PHOTO": "PHOTO"
} }