(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
This commit is contained in:
Siddarth Kumar 2023-05-16 21:57:34 +05:30 committed by GitHub
parent b5a8f0a127
commit a60235abf3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 298 additions and 5 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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