(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:
parent
b5a8f0a127
commit
a60235abf3
|
@ -1,11 +1,12 @@
|
||||||
(ns quo2.components.share.qr-code.view
|
(ns quo2.components.share.qr-code.view
|
||||||
(:require [quo2.components.share.qr-code.style :as style]
|
(: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
|
(defn qr-code
|
||||||
[{:keys [source width height]}]
|
[{:keys [source width height]}]
|
||||||
[rn/view
|
[rn/view
|
||||||
{:style style/container}
|
{:style style/container}
|
||||||
[rn/image
|
[fast-image/fast-image
|
||||||
{:source source
|
{:source source
|
||||||
:style (style/image width height)}]])
|
:style (style/image width height)}]])
|
||||||
|
|
|
@ -95,7 +95,9 @@
|
||||||
(assoc button-common-props :accessibility-label :open-scanner-button)
|
(assoc button-common-props :accessibility-label :open-scanner-button)
|
||||||
:i/scan]
|
:i/scan]
|
||||||
[quo/button
|
[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]
|
:i/qr-code]
|
||||||
[rn/view
|
[rn/view
|
||||||
[unread-indicator]
|
[unread-indicator]
|
||||||
|
|
|
@ -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})))
|
|
@ -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})
|
|
@ -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]])))
|
|
@ -14,6 +14,7 @@
|
||||||
[status-im2.db :as db]
|
[status-im2.db :as db]
|
||||||
[utils.re-frame :as rf]
|
[utils.re-frame :as rf]
|
||||||
[utils.datetime :as datetime]
|
[utils.datetime :as datetime]
|
||||||
|
status-im2.contexts.share.events
|
||||||
status-im2.contexts.syncing.events
|
status-im2.contexts.syncing.events
|
||||||
status-im2.contexts.chat.events))
|
status-im2.contexts.chat.events))
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,8 @@
|
||||||
[status-im2.contexts.chat.group-details.view :as group-details]
|
[status-im2.contexts.chat.group-details.view :as group-details]
|
||||||
[status-im.ui.screens.screens :as old-screens]
|
[status-im.ui.screens.screens :as old-screens]
|
||||||
[status-im2.contexts.communities.actions.request-to-join.view :as join-menu]
|
[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
|
(defn screens
|
||||||
[]
|
[]
|
||||||
|
@ -42,6 +43,10 @@
|
||||||
:options options/transparent-screen-options
|
:options options/transparent-screen-options
|
||||||
:component activity-center/view}
|
:component activity-center/view}
|
||||||
|
|
||||||
|
{:name :share-shell
|
||||||
|
:options options/transparent-screen-options
|
||||||
|
:component share/view}
|
||||||
|
|
||||||
{:name :shell-stack
|
{:name :shell-stack
|
||||||
:component shell/shell-stack}
|
:component shell/shell-stack}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
(def ^:const contact-images-action "/contactImages")
|
(def ^:const contact-images-action "/contactImages")
|
||||||
(def ^:const generate-qr-action "/GenerateQRCode")
|
(def ^:const generate-qr-action "/GenerateQRCode")
|
||||||
(def ^:const status-profile-base-url "https://join.status.im/u/")
|
(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))
|
(defn timestamp [] (datetime/timestamp))
|
||||||
|
|
||||||
|
|
|
@ -2152,5 +2152,9 @@
|
||||||
"user-shared-a-community": "{{user}} shared a community",
|
"user-shared-a-community": "{{user}} shared a community",
|
||||||
"you-deleted-a-message": "You deleted a message",
|
"you-deleted-a-message": "You deleted a message",
|
||||||
"this-message-was-deleted": "This message was deleted",
|
"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"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue