From a60235abf3a4499fae24880b6ba302a8764b14ac Mon Sep 17 00:00:00 2001 From: Siddarth Kumar Date: Tue, 16 May 2023 21:57:34 +0530 Subject: [PATCH] (feat) : add share capabilities in shell (#15782) fixes: #13439 Summary - Update `quo2/qr-code` component to use `fast-image` instead of `rn/image` - Adds profile tab in share section, wallet tab is marked as WIP - Shows profile QR code along with link to user's profile and the user's emoji hash. - profile link is shareable and copyable on tap. - emoji hash is shareable and copyable on tap. - fixed weird android issues --- src/quo2/components/share/qr_code/view.cljs | 5 +- src/status_im2/common/home/view.cljs | 4 +- src/status_im2/contexts/share/events.cljs | 15 ++ src/status_im2/contexts/share/style.cljs | 102 ++++++++++++ src/status_im2/contexts/share/view.cljs | 162 ++++++++++++++++++++ src/status_im2/events.cljs | 1 + src/status_im2/navigation/screens.cljs | 7 +- src/utils/image_server.cljs | 1 + translations/en.json | 6 +- 9 files changed, 298 insertions(+), 5 deletions(-) create mode 100644 src/status_im2/contexts/share/events.cljs create mode 100644 src/status_im2/contexts/share/style.cljs create mode 100644 src/status_im2/contexts/share/view.cljs diff --git a/src/quo2/components/share/qr_code/view.cljs b/src/quo2/components/share/qr_code/view.cljs index efd3b740ed..804ea4702c 100644 --- a/src/quo2/components/share/qr_code/view.cljs +++ b/src/quo2/components/share/qr_code/view.cljs @@ -1,11 +1,12 @@ (ns quo2.components.share.qr-code.view (:require [quo2.components.share.qr-code.style :as style] - [react-native.core :as rn])) + [react-native.core :as rn] + [react-native.fast-image :as fast-image])) (defn qr-code [{:keys [source width height]}] [rn/view {:style style/container} - [rn/image + [fast-image/fast-image {:source source :style (style/image width height)}]]) diff --git a/src/status_im2/common/home/view.cljs b/src/status_im2/common/home/view.cljs index 56e4307897..facb22e782 100644 --- a/src/status_im2/common/home/view.cljs +++ b/src/status_im2/common/home/view.cljs @@ -95,7 +95,9 @@ (assoc button-common-props :accessibility-label :open-scanner-button) :i/scan] [quo/button - (assoc button-common-props :accessibility-label :show-qr-button) + (merge button-common-props + {:accessibility-label :show-qr-button + :on-press #(rf/dispatch [:open-modal :share-shell])}) :i/qr-code] [rn/view [unread-indicator] diff --git a/src/status_im2/contexts/share/events.cljs b/src/status_im2/contexts/share/events.cljs new file mode 100644 index 0000000000..fb287b3b6a --- /dev/null +++ b/src/status_im2/contexts/share/events.cljs @@ -0,0 +1,15 @@ +(ns status-im2.contexts.share.events + (:require [utils.re-frame :as rf] + [status-im2.common.toasts.events :as toasts] + [quo2.foundations.colors :as colors])) + +(rf/defn copy-text-and-show-toast + {:events [:share/copy-text-and-show-toast]} + [cofx text-to-copy post-copy-message] + (rf/merge cofx + {:copy-to-clipboard text-to-copy} + (toasts/upsert + {:icon :correct + :icon-color colors/success-50 + :override-theme :dark + :text post-copy-message}))) diff --git a/src/status_im2/contexts/share/style.cljs b/src/status_im2/contexts/share/style.cljs new file mode 100644 index 0000000000..0f0333a91c --- /dev/null +++ b/src/status_im2/contexts/share/style.cljs @@ -0,0 +1,102 @@ +(ns status-im2.contexts.share.style + (:require [quo2.foundations.colors :as colors] + [react-native.platform :as platform])) + +(def screen-padding 20) + +(def blur + {:style {:position :absolute + :top 0 + :left 0 + :right 0 + :bottom 0} + :overlay-color colors/neutral-80-opa-80 + :blur-amount 20}) + +(def header-button + {:margin-bottom 12 + :margin-left screen-padding}) + +(def header-heading + {:padding-horizontal screen-padding + :padding-vertical 12 + :color colors/white}) + +(def qr-code-container + {:padding 12 + :border-radius 16 + :margin-top 12 + :margin-bottom 4 + :margin-horizontal screen-padding + :background-color colors/white-opa-5 + :flex-direction :column + :justify-content :center}) + +(def emoji-hash-container + {:border-radius 16 + :margin-top 12 + :margin-horizontal screen-padding + :background-color colors/white-opa-5 + :flex-direction :row + :justify-content :flex-start + :align-items :flex-start}) + +(def profile-address-column + {:flex-direction :column}) + +(def emoji-address-column + {:flex-direction :column}) + +(def profile-address-label + {:color colors/white-opa-40 + :padding-top 10}) + +(def profile-address-content + {:color colors/white + :align-self :flex-start + :padding-top 2}) + +(def profile-address-container + {:flex-direction :row + :justify-content :flex-start + :margin-top 6 + :margin-horizontal 4}) + +(def emoji-address-container + {:flex-direction :row + :justify-content :flex-start + :margin-top 6 + :margin-horizontal 4}) + +(def emoji-hash-label + {:color colors/white-opa-40 + :margin-top 8 + :padding-bottom (if platform/ios? 2 0) + :padding-left 12}) + + +(def share-button-container + {:position :absolute + :right 0 + :top 16}) + +(def emoji-share-button-container + {:position :absolute + :right 4 + :top 16}) + +(def emoji-hash-content + {:color colors/white + :align-self :flex-start + :padding-top 4 + :padding-bottom 12 + :padding-left 12 + :font-size 14}) + +(def tabs-container + {:padding-horizontal screen-padding + :margin-vertical 8}) + +(def wip-style + {:color colors/white + :text-align :center}) diff --git a/src/status_im2/contexts/share/view.cljs b/src/status_im2/contexts/share/view.cljs new file mode 100644 index 0000000000..314925bacf --- /dev/null +++ b/src/status_im2/contexts/share/view.cljs @@ -0,0 +1,162 @@ +(ns status-im2.contexts.share.view + (:require [utils.i18n :as i18n] + [quo2.core :as quo] + [react-native.core :as rn] + [status-im2.contexts.share.style :as style] + [utils.re-frame :as rf] + [reagent.core :as reagent] + [quo2.foundations.colors :as colors] + [clojure.string :as string] + [react-native.blur :as blur] + [status-im.ui.components.list-selection :as list-selection] + [utils.image-server :as image-server] + [react-native.navigation :as navigation])) + +(defn header + [] + [:<> + [quo/button + {:icon true + :type :blur-bg + :size 32 + :accessibility-label :close-shell-share-tab + :override-theme :dark + :style style/header-button + :on-press #(rf/dispatch [:navigate-back])} + :i/close] + [quo/text + {:size :heading-1 + :weight :semi-bold + :style style/header-heading} + (i18n/label :t/share)]]) + +(defn abbreviated-url + "The goal here is to generate a string that begins with + join.status.im/u/ joined with the 1st 5 characters + of the compressed public key followed by an ellipsis followed by + the last 12 characters of the compressed public key" + [base-url public-pk] + (let [first-part-of-public-pk (subs public-pk 0 5) + ellipsis "..." + public-pk-size (count public-pk) + last-part-of-public-pk (subs public-pk (- public-pk-size 12) (- public-pk-size 1)) + abbreviated-url (str base-url first-part-of-public-pk ellipsis last-part-of-public-pk)] + abbreviated-url)) + +(defn profile-tab + [window-width] + (let [multiaccount (rf/sub [:multiaccount]) + emoji-hash (string/join (get multiaccount :emoji-hash)) + qr-size (int (- window-width 64)) + public-pk (get multiaccount :compressed-key) + abbreviated-url (abbreviated-url image-server/status-profile-base-url-without-https public-pk) + profile-url (str image-server/status-profile-base-url public-pk) + port (rf/sub [:mediaserver/port]) + key-uid (get multiaccount :key-uid) + source-uri (image-server/get-account-qr-image-uri {:key-uid key-uid + :public-key public-pk + :port port + :qr-size qr-size})] + [:<> + [rn/view {:style style/qr-code-container} + [quo/qr-code + {:source {:uri source-uri} + :width qr-size + :height qr-size}] + [rn/view {:style style/profile-address-container} + [rn/view {:style style/profile-address-column} + [quo/text + {:size :paragraph-2 + :weight :medium + :style style/profile-address-label} + (i18n/label :t/link-to-profile)] + [rn/touchable-highlight + {:active-opacity 1 + :underlay-color colors/neutral-80-opa-1-blur + :background-color :transparent + :on-press #(rf/dispatch [:share/copy-text-and-show-toast profile-url + (i18n/label :t/link-to-profile-copied)]) + :on-long-press #(rf/dispatch [:share/copy-text-and-show-toast profile-url + (i18n/label :t/link-to-profile-copied)])} + [quo/text + {:style style/profile-address-content + :size :paragraph-1 + :weight :medium + :ellipsize-mode :middle + :number-of-lines 1} + abbreviated-url]]] + [rn/view {:style style/share-button-container} + [quo/button + {:icon true + :type :blur-bg + :size 32 + :accessibility-label :link-to-profile + :override-theme :dark + :on-press #(list-selection/open-share {:message profile-url})} + :i/share]]]] + + [rn/view {:style style/emoji-hash-container} + [rn/view {:style style/emoji-address-container} + [rn/view {:style style/emoji-address-column} + [quo/text + {:size :paragraph-2 + :weight :medium + :style style/emoji-hash-label} + (i18n/label :t/emoji-hash)] + [rn/touchable-highlight + {:active-opacity 1 + :underlay-color colors/neutral-80-opa-1-blur + :background-color :transparent + :on-press #(rf/dispatch [:share/copy-text-and-show-toast emoji-hash + (i18n/label :t/emoji-hash-copied)]) + :on-long-press #(rf/dispatch [:share/copy-text-and-show-toast emoji-hash + (i18n/label :t/emoji-hash-copied)])} + [rn/text {:style style/emoji-hash-content} emoji-hash]]]] + [rn/view {:style style/emoji-share-button-container} + [quo/button + {:icon true + :type :blur-bg + :size 32 + :accessibility-label :link-to-profile + :override-theme :dark + :style {:margin-right 12} + :on-press #(rf/dispatch [:share/copy-text-and-show-toast emoji-hash + (i18n/label :t/emoji-hash-copied)]) + :on-long-press #(rf/dispatch [:share/copy-text-and-show-toast emoji-hash + (i18n/label :t/emoji-hash-copied)])} + :i/copy]]]])) + +(defn wallet-tab + [] + [rn/text {:style style/wip-style} "not implemented"]) + +(defn tab-content + [window-width] + (let [selected-tab (reagent/atom :profile)] + (fn [] + [:<> + [header] + [rn/view {:style style/tabs-container} + [quo/segmented-control + {:size 28 + :blur? true + :override-theme :dark + :on-change #(reset! selected-tab %) + :default-active :profile + :data [{:id :profile + :label (i18n/label :t/profile)} + {:id :wallet + :label (i18n/label :t/wallet)}]}]] + (if (= @selected-tab :profile) + [profile-tab window-width] + [wallet-tab])]))) + +(defn view + [] + (let [window-width (rf/sub [:dimensions/window-width])] + (fn [] + [rn/view + {:flex 1 + :padding-top (navigation/status-bar-height)} + [blur/view style/blur] + [tab-content window-width]]))) diff --git a/src/status_im2/events.cljs b/src/status_im2/events.cljs index a86d2b9ed1..41d9b07eae 100644 --- a/src/status_im2/events.cljs +++ b/src/status_im2/events.cljs @@ -14,6 +14,7 @@ [status-im2.db :as db] [utils.re-frame :as rf] [utils.datetime :as datetime] + status-im2.contexts.share.events status-im2.contexts.syncing.events status-im2.contexts.chat.events)) diff --git a/src/status_im2/navigation/screens.cljs b/src/status_im2/navigation/screens.cljs index b21a73a58f..230d6c1450 100644 --- a/src/status_im2/navigation/screens.cljs +++ b/src/status_im2/navigation/screens.cljs @@ -31,7 +31,8 @@ [status-im2.contexts.chat.group-details.view :as group-details] [status-im.ui.screens.screens :as old-screens] [status-im2.contexts.communities.actions.request-to-join.view :as join-menu] - [status-im2.contexts.syncing.setup-syncing.view :as settings-setup-syncing])) + [status-im2.contexts.syncing.setup-syncing.view :as settings-setup-syncing] + [status-im2.contexts.share.view :as share])) (defn screens [] @@ -42,6 +43,10 @@ :options options/transparent-screen-options :component activity-center/view} + {:name :share-shell + :options options/transparent-screen-options + :component share/view} + {:name :shell-stack :component shell/shell-stack} diff --git a/src/utils/image_server.cljs b/src/utils/image_server.cljs index 22b9e40d4c..06eff40bfb 100644 --- a/src/utils/image_server.cljs +++ b/src/utils/image_server.cljs @@ -6,6 +6,7 @@ (def ^:const contact-images-action "/contactImages") (def ^:const generate-qr-action "/GenerateQRCode") (def ^:const status-profile-base-url "https://join.status.im/u/") +(def ^:const status-profile-base-url-without-https "join.status.im/u/") (defn timestamp [] (datetime/timestamp)) diff --git a/translations/en.json b/translations/en.json index c334b7a667..016b2f80ad 100644 --- a/translations/en.json +++ b/translations/en.json @@ -2152,5 +2152,9 @@ "user-shared-a-community": "{{user}} shared a community", "you-deleted-a-message": "You deleted a message", "this-message-was-deleted": "This message was deleted", - "user-deleted-a-message": "{{user}} deleted a message" + "user-deleted-a-message": "{{user}} deleted a message", + "link-to-profile": "Link to profile", + "emoji-hash": "Emoji Hash", + "emoji-hash-copied":"Emojihash copied to clipboard", + "link-to-profile-copied":"Link to Profile copied to clipboard" }