From 0ee0137c72319d914048b7facdf45c7f28935d32 Mon Sep 17 00:00:00 2001 From: Omar Basem Date: Mon, 31 Jul 2023 09:01:30 +0400 Subject: [PATCH] Camera extra features (#16781) feat: camera extra features --- .../contexts/chat/camera/style.cljs | 42 +++- src/status_im2/contexts/chat/camera/view.cljs | 195 ++++++++++++------ translations/en.json | 2 +- 3 files changed, 168 insertions(+), 71 deletions(-) diff --git a/src/status_im2/contexts/chat/camera/style.cljs b/src/status_im2/contexts/chat/camera/style.cljs index 4e340a0209..f6a8ea30fd 100644 --- a/src/status_im2/contexts/chat/camera/style.cljs +++ b/src/status_im2/contexts/chat/camera/style.cljs @@ -7,10 +7,23 @@ {:flex 1 :background-color colors/black}) -(def flash-container - {:position :absolute - :top 50 - :left 25}) +(defn flash-container + [rotate uri] + (reanimated/apply-animations-to-style + {:transform [{:rotate rotate}]} + {:position :absolute + :top 50 + :left (if uri -25 25)})) + +(def cancel-dash + {:width 1 + :height 32 + :top -4 + :left 12 + :z-index 1 + :transform [{:rotate "-45deg"}] + :background-color colors/white + :position :absolute}) (defn camera-window [width height top] @@ -18,6 +31,12 @@ :height height :top top}) +(defn image + [width height top portrait?] + {:width width + :height (if portrait? height (* width 0.75)) + :top top}) + (def zoom-button-container {:width 37 :height 37 @@ -38,19 +57,22 @@ :bottom (+ top (:bottom insets) (when platform/android? (:top insets)) 18)}) (defn zoom-button - [size] + [size rotate] (reanimated/apply-animations-to-style - {:width size - :height size} + {:width size + :height size + :transform [{:rotate rotate}]} {:background-color colors/black-opa-30 :justify-content :center :align-items :center :border-radius 50})) (defn bottom-area - [top insets] + [top insets uri] {:left 20 :right 20 + :opacity (if uri 0 1) + :z-index (if uri 0 1) :position :absolute :height (+ top (when platform/android? (:top insets))) :bottom (:bottom insets)}) @@ -84,8 +106,10 @@ :background-color colors/white}) (defn confirmation-container - [insets] + [insets uri] {:position :absolute + :opacity (if uri 1 0) + :z-index (if uri 1 0) :bottom 0 :left 0 :right 0 diff --git a/src/status_im2/contexts/chat/camera/view.cljs b/src/status_im2/contexts/chat/camera/view.cljs index 68f1558db3..b770463b71 100644 --- a/src/status_im2/contexts/chat/camera/view.cljs +++ b/src/status_im2/contexts/chat/camera/view.cljs @@ -4,6 +4,9 @@ [quo2.foundations.colors :as colors] [react-native.camera-kit :as camera-kit] [react-native.core :as rn] + [react-native.fast-image :as fast-image] + [react-native.orientation :as orientation] + [react-native.platform :as platform] [react-native.reanimated :as reanimated] [react-native.safe-area :as safe-area] [reagent.core :as reagent] @@ -11,8 +14,28 @@ [status-im2.contexts.chat.camera.style :as style] [utils.re-frame :as rf])) +(defn retake + [flash uri] + (let [current-flash @flash] + (when platform/android? + (reset! flash false) ; On Android, setting flash needs to be delayed until camera has initialized + (js/setTimeout #(reset! flash current-flash) 300)) + (reset! uri nil))) + +(defn handle-orientation + [current-orientation rotate] + (orientation/use-device-orientation-change (fn [result] + (reset! current-orientation result) + (cond + (= result orientation/landscape-left) + (reanimated/animate rotate "90deg") + (= result orientation/landscape-right) + (reanimated/animate rotate "-90deg") + :else + (reanimated/animate rotate "0deg"))))) + (defn- f-zoom-button - [{:keys [value current-zoom]}] + [{:keys [value current-zoom rotate]}] (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]) @@ -20,13 +43,14 @@ {:on-press #(reset! current-zoom value) :style style/zoom-button-container :accessibility-label (str "zoom-" value)} - [reanimated/view {:style (style/zoom-button size)} + [reanimated/view {:style (style/zoom-button size rotate)} [quo/text - {:size (if selected? :paragraph-2 :label) - :weight :semi-bold - :style {:color (if selected? - colors/system-yellow - colors/white)}} + {:size (if selected? :paragraph-2 :label) + :weight :semi-bold + :number-of-lines 1 + :style {:color (if selected? + colors/system-yellow + colors/white)}} (str value (when selected? "x"))]]])) (defn zoom-button @@ -43,59 +67,108 @@ (camera-kit/capture @camera-ref #(reset! uri %))) :style style/inner-circle}]]) +(defn zoom-buttons + [] + (let [current-zoom (reagent/atom 1)] + (fn [top insets rotate] + [rn/view {:style (style/zoom-container top insets)} + [zoom-button {:value 0.5 :current-zoom current-zoom :rotate rotate}] + [zoom-button {:value 1 :current-zoom current-zoom :rotate rotate}] + [zoom-button {:value 2 :current-zoom current-zoom :rotate rotate}] + [zoom-button {:value 3 :current-zoom current-zoom :rotate rotate}]]))) + + +(defn- f-bottom-area + [{:keys [top insets uri camera-ref rotate]} back flip-camera] + [rn/view {:style (style/bottom-area top insets @uri)} + [quo/text {:style style/photo-text} (i18n/label :t/photo-caps)] + [rn/view {:style style/actions-container} + [quo/text + {:on-press back + :style {:font-size 17 + :color colors/white} + :accessibility-label :cancel} + (i18n/label :t/cancel)] + [snap-button camera-ref uri] + [reanimated/touchable-opacity + {:style (reanimated/apply-animations-to-style {:transform [{:rotate rotate}]} {}) + :on-press flip-camera} + [quo/icon :i/rotate-camera + {:size 48 :color colors/white :accessibility-label :flip-camera}]]]]) + +(defn bottom-area + [{:keys [flash camera-type] :as args}] + (let [back #(rf/dispatch [:navigate-back]) + flip-camera (fn [] + (reset! flash false) + (reset! camera-type (if (= @camera-type camera-kit/camera-type-back) + camera-kit/camera-type-front + camera-kit/camera-type-back)))] + [:f> f-bottom-area args back flip-camera])) + +(defn- f-camera-screen + [{:keys [camera-ref uri camera-type current-orientation flash toggle-flash]}] + (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) + top-landscape (/ (- height (* width 0.75) (:bottom insets)) 2) + portrait? (= @current-orientation orientation/portrait) + rotate (reanimated/use-shared-value "0deg") + retake #(retake flash uri) + use-photo (fn [] + (rf/dispatch [:photo-selector/camera-roll-pick {:uri @uri}]) + (rf/dispatch [:navigate-back]))] + (handle-orientation current-orientation rotate) + [rn/view {:style style/screen-container} + [reanimated/touchable-opacity + {:active-opacity 1 + :on-press toggle-flash + :style (style/flash-container rotate @uri)} + (when-not @flash + [rn/view {:style style/cancel-dash}]) + [quo/icon :i/flash-camera + {:color colors/white + :size 24}]] + (if @uri + [fast-image/fast-image + {:style (style/image width camera-window-height (if portrait? top top-landscape) portrait?) + :source {:uri @uri}}] + [camera-kit/camera + {:ref #(reset! camera-ref %) + :style (style/camera-window width camera-window-height top) + :flash-mode (if @flash :on :off) + :camera-type @camera-type}]) + (when-not @uri + [zoom-buttons top insets rotate]) + [rn/view {:style (style/confirmation-container insets @uri)} + [quo/text + {:on-press retake + :style {:font-size 17 + :color colors/white}} + (i18n/label :t/retake)] + [quo/text + {:on-press use-photo + :style {:font-size 17 + :color colors/white}} + (i18n/label :t/use-photo)]] + [bottom-area + {:top top + :insets insets + :uri uri + :camera-type camera-type + :camera-ref camera-ref + :flash flash + :rotate rotate}]])) + (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}]]])])))) + (let [flash (reagent/atom false) + args {:camera-ref (atom nil) + :uri (reagent/atom nil) + :camera-type (reagent/atom camera-kit/camera-type-back) + :current-orientation (atom orientation/portrait) + :flash flash + :toggle-flash #(swap! flash not)}] + [:f> f-camera-screen args])) diff --git a/translations/en.json b/translations/en.json index 9aefc8b1ea..6205386ed4 100644 --- a/translations/en.json +++ b/translations/en.json @@ -2264,5 +2264,5 @@ "community-unmuted": "Community unmuted", "retake": "Retake", "use-photo": "Use Photo", - "PHOTO": "PHOTO" + "photo-caps": "PHOTO" }