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
ENABLE_ROOT_ALERT=1
ENABLE_QUO_PREVIEW=1
MAX_IMAGES_BATCH=5
APN_TOPIC=im.status.ethereum.pr
COMMUNITIES_ENABLED=1
DATABASE_MANAGEMENT_ENABLED=1

View File

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

View File

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

View File

@ -17,7 +17,6 @@ RPC_NETWORKS_ONLY=0
PARTITIONED_TOPIC=0
CONTRACT_NODES=1
ENABLE_ROOT_ALERT=1
MAX_IMAGES_BATCH=5
BLANK_PREVIEW=0
COMMUNITIES_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 20)
(get-icons 24)
(get-icons 32)))
(get-icons 32)
(get-icons 48)))

View File

@ -136,6 +136,8 @@
;;Solid
(def black "#000000")
(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")
;;;;Primary
@ -180,6 +182,8 @@
(def danger-50 "#E95460")
(def danger-60 "#BA434D")
(def system-yellow "#FFD60A")
;;50 with transparency
(def danger-50-opa-5 (alpha danger-50 0.05))
(def danger-50-opa-10 (alpha danger-50 0.1))

View File

@ -1,8 +1,16 @@
(ns react-native.camera-kit
(: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-type-front (.-Front 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.cameraroll :as cameraroll]
[status-im.ui.components.react :as react]
[status-im2.config :as config]
[react-native.fs :as fs]
[status-im2.constants :as constants]
[utils.re-frame :as rf]
[status-im.utils.platform :as platform]
[taoensso.timbre :as log]))
@ -48,7 +48,7 @@
[{:keys [db]} chat-id uri]
(let [current-chat-id (or chat-id (:current-chat-id db))
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)))
{::image-selected [uri current-chat-id]})))
@ -68,5 +68,5 @@
[{:keys [db]} chat-id]
(let [current-chat-id (or chat-id (:current-chat-id db))
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})))

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 dev-build? (enabled? (get-config :DEV_BUILD 0)))
(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
(def local-notifications? (enabled? (get-config :LOCAL_NOTIFICATIONS "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
(:require
[quo2.core :as quo]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.permissions :as permissions]
[react-native.platform :as platform]
@ -9,6 +10,7 @@
[status-im2.common.alert.events :as alert]
[status-im2.contexts.chat.composer.constants :as comp-constants]
[status-im2.contexts.chat.messages.list.view :as messages.list]
[status-im2.common.device-permissions :as device-permissions]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[status-im2.contexts.chat.composer.actions.style :as style]
@ -148,16 +150,32 @@
50)}]))
: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
[]
(let [images-count (count (vals (rf/sub [:chats/sending-image])))]
[quo/button
{:on-press #(js/alert "to be implemented")
{:on-press #(go-to-camera images-count)
:icon true
:type :outline
:size 32
:style {:margin-right 12}}
:i/camera])
:i/camera]))
(defn open-photo-selector
[{:keys [input-ref]}

View File

@ -2,9 +2,9 @@
(:require [react-native.cameraroll :as cameraroll]
[clojure.string :as string]
[re-frame.core :as re-frame]
[status-im2.constants :as constants]
[utils.i18n :as i18n]
[react-native.permissions :as permissions]
[status-im2.config :as config]
[utils.re-frame :as rf]
[status-im.utils.image-processing :as image-processing]
[taoensso.timbre :as log]
@ -141,6 +141,6 @@
[{:keys [db]} image chat-id]
(let [current-chat-id (or chat-id (:current-chat-id db))
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)))
{:camera-roll-image-selected [image current-chat-id]})))

View File

@ -12,6 +12,7 @@
[react-native.reanimated :as reanimated]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im2.common.device-permissions :as device-permissions]
[status-im2.constants :as constants]
[status-im2.contexts.syncing.scan-sync-code.style :as style]
[status-im2.contexts.syncing.utils :as sync-utils]
@ -27,19 +28,6 @@
(defonce dismiss-animations (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
"Performing the check for the first time
will trigger local network access permission in iOS.
@ -140,7 +128,8 @@
:button-icon :i/camera
:button-label :t/enable-camera
: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
[]

View File

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

View File

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

View File

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