Compare commits
16
Commits
14052
...
issue-14267
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f46269f5e | ||
|
|
ab1dd7e794 | ||
|
|
0967582639 | ||
|
|
91b237d3d3 | ||
|
|
69303cd7d1 | ||
|
|
b774ecbcb4 | ||
|
|
b492ed2969 | ||
|
|
6d557d735a | ||
|
|
f29ed58445 | ||
|
|
1941591110 | ||
|
|
a91e9dfb38 | ||
|
|
c423aa7970 | ||
|
|
910bafc5f4 | ||
|
|
2ae57f7b21 | ||
|
|
14a5edb24b | ||
|
|
0ff6fb25f4 |
@@ -272,7 +272,7 @@ run-android: export TARGET := android
|
||||
run-android: ##@run Build Android APK and start it on the device
|
||||
npx react-native run-android --appIdSuffix debug
|
||||
|
||||
SIMULATOR=
|
||||
SIMULATOR=iPhone 13
|
||||
run-ios: export TARGET := ios
|
||||
run-ios: ##@run Build iOS app and start it in a simulator/device
|
||||
ifneq ("$(SIMULATOR)", "")
|
||||
|
||||
@@ -62,7 +62,7 @@ pipeline {
|
||||
--rerun_count=2 \
|
||||
--testrail_report=True \
|
||||
-m testrail_id \
|
||||
-m \"not upgrade\" \
|
||||
-m \"new_ui_critical\" \
|
||||
-k \"${params.KEYWORD_EXPRESSION}\" \
|
||||
--apk=${params.APK_NAME}
|
||||
"""
|
||||
|
||||
@@ -14,7 +14,7 @@ pipeline {
|
||||
string(
|
||||
name: 'TEST_MARKERS',
|
||||
description: 'Marker expression for matching tests to run.',
|
||||
defaultValue: 'critical',
|
||||
defaultValue: 'new_ui_critical',
|
||||
) */
|
||||
string(
|
||||
name: 'APK_NAME',
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 286 B |
Binary file not shown.
|
After Width: | Height: | Size: 396 B |
@@ -1,4 +1,4 @@
|
||||
import { useDerivedValue } from 'react-native-reanimated';
|
||||
import { useDerivedValue, withTiming, withSequence, withDelay, Easing } from 'react-native-reanimated';
|
||||
|
||||
// Generic Worklets
|
||||
|
||||
@@ -32,38 +32,99 @@ export function applyAnimationsToStyle(animations, style) {
|
||||
|
||||
// Switcher Worklets
|
||||
|
||||
export function switcherCloseButtonOpacity (switcherButtonOpacity) {
|
||||
export function stackOpacity (stackId, selectedStackId) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return 1 - switcherButtonOpacity.value;
|
||||
return selectedStackId.value == stackId ? 1 : 0;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function switcherScreenRadius (switcherScreenSize) {
|
||||
export function stackPointer (stackId, selectedStackId) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return switcherScreenSize.value/2;
|
||||
return selectedStackId.value == stackId ? "auto" : "none";
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function switcherScreenBottomPosition (switcherScreenRadius, switcherPressedRadius, initalPosition) {
|
||||
export function bottomTabIconColor (stackId, selectedStackId, passThrough, selectedTabColor, defaultColor, passThroughColor) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return initalPosition + switcherPressedRadius - switcherScreenRadius.value;
|
||||
if (selectedStackId.value == stackId){
|
||||
return selectedTabColor;
|
||||
}
|
||||
else if (passThrough.value){
|
||||
return passThroughColor;
|
||||
}
|
||||
else {
|
||||
return defaultColor;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function switcherContainerBottomPosition (switcherScreenBottom, heightOffset) {
|
||||
|
||||
// Home Stack
|
||||
|
||||
const defaultDurationAndEasing = {
|
||||
duration: 300,
|
||||
easing: Easing.bezier(0, 0, 1, 1),
|
||||
}
|
||||
|
||||
export function homeStackOpacity (homeStackOpen) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return - (switcherScreenBottom.value + heightOffset);
|
||||
return withTiming(homeStackOpen.value ? 1 : 0, defaultDurationAndEasing);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function homeStackTop (homeStackOpen, top) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return withTiming(homeStackOpen.value ? 0 : top, defaultDurationAndEasing);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function homeStackLeft (selectedStackId, animateHomeStackLeft, homeStackOpen, left) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
if (animateHomeStackLeft.value) {
|
||||
var leftValue = left[selectedStackId.value];
|
||||
if (homeStackOpen.value) {
|
||||
return withSequence(withTiming(leftValue, {duration: 0}), withTiming(0, defaultDurationAndEasing))
|
||||
} else {
|
||||
return withTiming(leftValue, defaultDurationAndEasing);
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function homeStackPointer (homeStackOpen) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return homeStackOpen.value ? "auto" : "none";
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function homeStackScale (homeStackOpen, minimizeScale) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return withTiming(homeStackOpen.value ? 1 : minimizeScale, defaultDurationAndEasing);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
(+ 50 keyboard-height-android-delta) ;; TODO : remove 50 when react-native-navigation v8 will be implemented https://github.com/wix/react-native-navigation/issues/7225
|
||||
0))
|
||||
min-height (+ (* styles/vertical-padding 2) (:bottom safe-area))
|
||||
max-height (- window-height (:top safe-area) styles/margin-top)
|
||||
max-height (- window-height (:top safe-area))
|
||||
visible (react/state false)
|
||||
|
||||
master-translation-y (animated/use-value 0)
|
||||
@@ -202,17 +202,15 @@
|
||||
:wait-for master-ref
|
||||
:enabled (and (not disable-drag?)
|
||||
(not= sheet-height max-height))})
|
||||
[animated/view {:height sheet-height}
|
||||
[animated/scroll-view {:bounces false
|
||||
:flex 1
|
||||
:scroll-enabled (= sheet-height max-height)}
|
||||
[animated/view {:style {:padding-top styles/vertical-padding
|
||||
:padding-bottom (+ styles/vertical-padding
|
||||
(if (and platform/ios? keyboard-shown)
|
||||
keyboard-height
|
||||
(:bottom safe-area)))}
|
||||
:on-layout #(reset! height (.-nativeEvent.layout.height ^js %))}
|
||||
(into [:<>]
|
||||
(react/get-children children))]]]]]])))
|
||||
[animated/view {:height sheet-height
|
||||
:flex 1}
|
||||
[animated/view {:style {:padding-top styles/vertical-padding
|
||||
:padding-bottom (+ styles/vertical-padding
|
||||
(if (and platform/ios? keyboard-shown)
|
||||
keyboard-height
|
||||
(:bottom safe-area)))}
|
||||
:on-layout #(reset! height (.-nativeEvent.layout.height ^js %))}
|
||||
(into [:<>]
|
||||
(react/get-children children))]]]]])))
|
||||
|
||||
(def bottom-sheet (reagent/adapt-react-class (react/memo bottom-sheet-hooks)))
|
||||
(def bottom-sheet (reagent/adapt-react-class (react/memo bottom-sheet-hooks)))
|
||||
|
||||
@@ -56,6 +56,8 @@
|
||||
{:keyboardVerticalOffset (+ 44 (:status-bar-height @navigation-const))})]
|
||||
(reagent/children this))))
|
||||
|
||||
(def status-bar (.-StatusBar ^js rn))
|
||||
|
||||
(def keyboard (.-Keyboard ^js rn))
|
||||
|
||||
(def dismiss-keyboard! #(.dismiss ^js keyboard))
|
||||
|
||||
@@ -44,6 +44,11 @@
|
||||
:background-color {:default colors/danger-50
|
||||
:pressed colors/danger-60
|
||||
:disabled colors/danger-50}}
|
||||
:positive {:icon-color colors/white
|
||||
:label {:style {:color colors/white}}
|
||||
:background-color {:default colors/success-50
|
||||
:pressed colors/success-60
|
||||
:disabled colors/success-50-opa-30}}
|
||||
:photo-bg {:icon-color colors/neutral-100
|
||||
:icon-secondary-color colors/neutral-80-opa-40
|
||||
:label {:style {:color colors/neutral-100}}
|
||||
@@ -104,6 +109,11 @@
|
||||
:background-color {:default colors/danger-60
|
||||
:pressed colors/danger-50
|
||||
:disabled colors/danger-60}}
|
||||
:positive {:icon-color colors/white
|
||||
:label {:style {:color colors/white}}
|
||||
:background-color {:default colors/success-60
|
||||
:pressed colors/success-50
|
||||
:disabled colors/success-60-opa-30}}
|
||||
:photo-bg {:icon-color colors/white
|
||||
:icon-secondary-color colors/neutral-30
|
||||
:label {:style {:color colors/white}}
|
||||
|
||||
@@ -62,9 +62,10 @@
|
||||
(let [pressed? (reagent/atom false)]
|
||||
(fn [{:keys [type on-press count customization-color style]}]
|
||||
[rn/touchable-without-feedback
|
||||
{:on-press-in #(reset! pressed? true)
|
||||
:on-press-out #(reset! pressed? false)
|
||||
:on-press on-press}
|
||||
{:on-press-in #(reset! pressed? true)
|
||||
:on-press-out #(reset! pressed? false)
|
||||
:on-press on-press
|
||||
:accessibility-label type}
|
||||
[rn/view {:style (merge
|
||||
{:flex-direction :row
|
||||
:height 24
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
(ns quo2.components.community.discover-card
|
||||
(:require [quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.ui.screens.communities.styles :as styles]
|
||||
[status-im.ui.components.react :as react]))
|
||||
|
||||
;; Discover card placeholders images.
|
||||
;; TODO replaced when real data is available
|
||||
(def images
|
||||
{:images [{:id 1 :column-images [{:id 1 :image ""}]}
|
||||
{:id 2 :column-images [{}]}]})
|
||||
|
||||
(defn card-title-and-description [title description]
|
||||
[react/view
|
||||
{:flex 1
|
||||
:padding-top 8
|
||||
:padding-bottom 8
|
||||
:border-radius 12}
|
||||
[react/view {:flex 1
|
||||
:padding-horizontal 12}
|
||||
[text/text {:accessibility-label :community-name-text
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1
|
||||
:weight :semi-bold
|
||||
:size :paragraph-1}
|
||||
title]
|
||||
[text/text {:accessibility-label :community-name-text
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1
|
||||
:color (colors/theme-colors
|
||||
colors/neutral-50
|
||||
colors/neutral-40)
|
||||
:weight :regular
|
||||
:size :paragraph-2}
|
||||
description]]])
|
||||
|
||||
(defn placeholder-list-images [{:keys [images width height border-radius]}]
|
||||
[react/view
|
||||
[react/view {:justify-content :center}
|
||||
(for [{:keys [id]} images]
|
||||
^{:key id}
|
||||
[react/view {:border-radius border-radius
|
||||
:margin-top 4
|
||||
:margin-right 4
|
||||
:width width
|
||||
:height height
|
||||
:background-color colors/neutral-10}])]])
|
||||
|
||||
(defn placeholder-row-images [{:keys [first-image last-image images width height
|
||||
border-radius]}]
|
||||
[react/view
|
||||
(when first-image
|
||||
[react/view {:border-bottom-right-radius 6
|
||||
:border-bottom-left-radius 6
|
||||
:margin-right 4
|
||||
:width width
|
||||
:height height
|
||||
:background-color colors/neutral-10}])
|
||||
(when images
|
||||
[placeholder-list-images {:images images
|
||||
:width 32
|
||||
:height 32
|
||||
:border-radius 6}])
|
||||
(when last-image
|
||||
[react/view {:border-top-left-radius border-radius
|
||||
:border-top-right-radius 6
|
||||
:margin-top 4
|
||||
:width width
|
||||
:height height
|
||||
:background-color colors/neutral-10}])])
|
||||
|
||||
(defn discover-card [{:keys [title description on-press]}]
|
||||
(let [on-joined-images (get images :images)]
|
||||
[react/touchable-without-feedback
|
||||
{:on-press on-press}
|
||||
[react/view (merge (styles/community-card 16)
|
||||
{:background-color (colors/theme-colors
|
||||
colors/white
|
||||
colors/neutral-90)}
|
||||
{:flex-direction :row
|
||||
:margin-horizontal 20
|
||||
:height 56
|
||||
:padding-right 12})
|
||||
[card-title-and-description title description]
|
||||
(for [{:keys [id column-images]} on-joined-images]
|
||||
^{:key id}
|
||||
[placeholder-row-images {:images (when (= id 1)
|
||||
column-images)
|
||||
:width 32
|
||||
:height (if (= id 1) 8 26)
|
||||
:border-radius 6
|
||||
:first-image "" ; TODO replace with real data
|
||||
:last-image ""}]) ; TODO replace with real data
|
||||
]]))
|
||||
@@ -5,64 +5,70 @@
|
||||
[quo2.components.icon :as icon]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn- get-icon-color [section]
|
||||
(if (= section :bottom)
|
||||
(defn- get-icon-color [danger?]
|
||||
(if danger?
|
||||
(colors/theme-colors colors/danger-50 colors/danger-60)
|
||||
(colors/theme-colors colors/neutral-50 colors/neutral-40)))
|
||||
|
||||
(defn action [section]
|
||||
(fn [{:keys [icon label sub-label right-icon on-press]}]
|
||||
[rn/touchable-opacity {:on-press on-press}
|
||||
[react/view {:style
|
||||
{:flex 1
|
||||
:height (if sub-label 56 47)
|
||||
:margin-horizontal 20
|
||||
:flex-direction :row}}
|
||||
[react/view {:style
|
||||
{:height 20
|
||||
:margin-top :auto
|
||||
:margin-bottom :auto
|
||||
:margin-right 12
|
||||
:width 20}}
|
||||
[icon/icon icon
|
||||
{:color (get-icon-color section)
|
||||
:size 20}]]
|
||||
[react/view
|
||||
{:style
|
||||
{:flex 1
|
||||
:justify-content :center}}
|
||||
(defn action [{:keys [icon
|
||||
label
|
||||
sub-label
|
||||
right-icon
|
||||
danger?
|
||||
on-press]}]
|
||||
[rn/touchable-opacity {:on-press on-press}
|
||||
[react/view {:style
|
||||
{:height (if sub-label 56 47)
|
||||
:margin-horizontal 20
|
||||
:flex-direction :row}}
|
||||
[react/view {:style
|
||||
{:height 20
|
||||
:margin-top :auto
|
||||
:margin-bottom :auto
|
||||
:margin-right 12
|
||||
:width 20}}
|
||||
[icon/icon icon
|
||||
{:color (get-icon-color danger?)
|
||||
:size 20}]]
|
||||
[react/view
|
||||
{:style
|
||||
{:flex 1
|
||||
:justify-content :center}}
|
||||
[text/text
|
||||
{:size :paragraph-1
|
||||
:weight :medium
|
||||
:style {:color
|
||||
(when danger?
|
||||
(colors/theme-colors colors/danger-50 colors/danger-60))}}
|
||||
label]
|
||||
(when sub-label
|
||||
[text/text
|
||||
{:size :paragraph-1
|
||||
:weight :medium
|
||||
:style
|
||||
{:color (when (= section :bottom)
|
||||
(colors/theme-colors colors/danger-50 colors/danger-60))}}
|
||||
label]
|
||||
(when sub-label [text/text
|
||||
{:size :paragraph-2
|
||||
:style
|
||||
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}}
|
||||
sub-label])]
|
||||
(when right-icon
|
||||
[react/view {:style
|
||||
{:height 20
|
||||
:margin-top :auto
|
||||
:margin-bottom :auto
|
||||
:width 20}}
|
||||
[icon/icon right-icon
|
||||
{:color (get-icon-color section)
|
||||
:size 20}]])]]))
|
||||
{:size :paragraph-2
|
||||
:style {:color
|
||||
(colors/theme-colors colors/neutral-50 colors/neutral-40)}}
|
||||
sub-label])]
|
||||
(when right-icon
|
||||
[react/view {:style
|
||||
{:height 20
|
||||
:margin-top :auto
|
||||
:margin-bottom :auto
|
||||
:width 20}}
|
||||
[icon/icon right-icon
|
||||
{:color (get-icon-color danger?)
|
||||
:size 20}]])]])
|
||||
|
||||
(defn action-drawer [{:keys [actions actions-with-consequence]}]
|
||||
(defn divider []
|
||||
[rn/view {:style {:border-top-width 1
|
||||
:border-top-color (colors/theme-colors colors/neutral-10 colors/neutral-80)
|
||||
:margin-top 8
|
||||
:margin-bottom 7
|
||||
:align-items :center
|
||||
:flex-direction :row}}])
|
||||
|
||||
(defn action-drawer [sections]
|
||||
[:<> {:style
|
||||
{:flex 1}}
|
||||
(map (action :top) actions)
|
||||
(when actions-with-consequence
|
||||
[:<>
|
||||
[rn/view {:style {:border-top-width 1
|
||||
:border-top-color (colors/theme-colors colors/neutral-10 colors/neutral-80)
|
||||
:margin-top 8
|
||||
:margin-bottom 7
|
||||
:align-items :center
|
||||
:flex-direction :row}}]
|
||||
(map (action :bottom) actions-with-consequence)])])
|
||||
(interpose
|
||||
[divider]
|
||||
(for [actions sections]
|
||||
(map action actions)))])
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
(ns quo2.components.list-items.received-contact-request
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.utils.handlers :refer [<sub >evt]]
|
||||
[quo2.components.avatars.user-avatar :as user-avatar]
|
||||
[quo2.foundations.typography :as typography]
|
||||
[clojure.string :as str]
|
||||
[status-im.utils.utils :as utils.utils]
|
||||
[status-im.utils.datetime :as time]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[quo2.components.notifications.notification-dot :refer [notification-dot]]))
|
||||
|
||||
(defn get-display-name [chat-id no-ens-name no-nickname]
|
||||
(let [name (first (<sub [:contacts/contact-two-names-by-identity chat-id]))]
|
||||
(if (and no-ens-name no-nickname)
|
||||
(let [[word1 word2] (str/split name " ")]
|
||||
(str word1 " " word2))
|
||||
name)))
|
||||
|
||||
(defn list-item [{:keys [chat-id image contact message timestamp read]}]
|
||||
(let [no-ens-name (str/blank? (get-in message [:content :ens-name]))
|
||||
no-nickname (nil? (get-in contact [:names :nickname]))
|
||||
display-name (get-display-name chat-id no-ens-name no-nickname)]
|
||||
[rn/view {:style {:flex-direction :row
|
||||
:padding-top 8
|
||||
:margin-top 4
|
||||
:padding-bottom 12
|
||||
:flex 1}}
|
||||
(when-not read
|
||||
[notification-dot {:right 32 :top 16}])
|
||||
[user-avatar/user-avatar {:full-name display-name
|
||||
:status-indicator? true
|
||||
:online? true
|
||||
:size :small
|
||||
:profile-picture image
|
||||
:ring? false}]
|
||||
[rn/view {:style {:margin-horizontal 8}}
|
||||
[rn/view {:style {:flex-direction :row}}
|
||||
[rn/text {:style (merge typography/font-semi-bold typography/paragraph-1
|
||||
{:color (colors/theme-colors colors/neutral-100 colors/white)
|
||||
:margin-right 8})} display-name]
|
||||
(when no-ens-name [rn/text {:style (merge typography/font-regular typography/label
|
||||
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40)
|
||||
:margin-top 4})}
|
||||
(str (utils.utils/get-shortened-address chat-id) " · ")])
|
||||
[rn/text {:style (merge typography/font-regular typography/label
|
||||
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40)
|
||||
:margin-top 4})}
|
||||
(time/to-short-str timestamp)]]
|
||||
[rn/view {:style {:border-radius 12
|
||||
:margin-top 10
|
||||
:padding-horizontal 12
|
||||
:padding-vertical 8
|
||||
:border-width 1
|
||||
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-70)}}
|
||||
[rn/text {:style (merge typography/font-regular
|
||||
typography/paragraph-1
|
||||
{:color (colors/theme-colors colors/neutral-100 colors/white)})}
|
||||
(:text (:content message))]]
|
||||
[rn/view {:style {:margin-top 12
|
||||
:flex-direction :row}}
|
||||
[rn/touchable-opacity {:accessibility-label :decline-cr
|
||||
:on-press #(>evt [:contact-requests.ui/decline-request (:message-id message)])
|
||||
:active-opacity 1
|
||||
:style {:background-color (colors/theme-colors colors/danger-50 colors/danger-60)
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:align-self :flex-start
|
||||
:border-radius 8
|
||||
:padding-vertical 4
|
||||
:padding-horizontal 8}}
|
||||
[rn/text {:style (merge typography/font-medium typography/paragraph-2 {:color colors/white})} (i18n/label :t/decline)]]
|
||||
[rn/touchable-opacity {:accessibility-label :accept-cr
|
||||
:on-press #(>evt [:contact-requests.ui/accept-request (:message-id message)])
|
||||
:active-opacity 1
|
||||
:style {:background-color (colors/theme-colors colors/success-50 colors/success-60)
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:align-self :flex-start
|
||||
:border-radius 8
|
||||
:padding-vertical 4
|
||||
:padding-horizontal 8
|
||||
:margin-left 8}}
|
||||
[rn/text {:style (merge typography/font-medium typography/paragraph-2 {:color colors/white})} (i18n/label :t/accept)]]]]]))
|
||||
@@ -1,71 +1,77 @@
|
||||
(ns quo2.components.navigation.bottom-nav-tab
|
||||
(:require [quo.react-native :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[quo2.reanimated :as reanimated]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.icon :as icon]
|
||||
[quo2.components.icons.icons :as icons]
|
||||
[quo2.components.counter.counter :as counter]))
|
||||
|
||||
(defn toggle-background-color [background-color press-out? pass-through?]
|
||||
(let [color (cond
|
||||
press-out? nil
|
||||
pass-through? colors/white-opa-5
|
||||
:else colors/neutral-70)]
|
||||
(reset! background-color color)))
|
||||
(reanimated/set-shared-value
|
||||
background-color
|
||||
(cond
|
||||
press-out? "transparent"
|
||||
pass-through? colors/white-opa-5
|
||||
:else colors/neutral-70)))
|
||||
|
||||
(defn bottom-nav-tab
|
||||
"[bottom-nav-tab opts]
|
||||
opts
|
||||
{:icon :main-icons2/communities
|
||||
:selected? true/false
|
||||
:new-notifications? true/false
|
||||
:notification-indicator :unread-dot/:counter
|
||||
:counter-label number
|
||||
:on-press bottom-tab on-press function
|
||||
:pass-through? true/false
|
||||
:icon-color-anim reanimated shared value
|
||||
"
|
||||
[_]
|
||||
(let [background-color (reagent/atom nil)]
|
||||
(fn [{:keys [icon selected? new-notifications? notification-indicator counter-label on-press pass-through?]}]
|
||||
[rn/touchable-without-feedback
|
||||
{:on-press on-press
|
||||
:on-press-in #(toggle-background-color background-color false pass-through?)
|
||||
:on-press-out #(toggle-background-color background-color true pass-through?)}
|
||||
[rn/view {:style {:width 90
|
||||
:height 40
|
||||
:background-color @background-color
|
||||
:border-radius 10}}
|
||||
[rn/hole-view {:style {:padding-left 33
|
||||
:padding-top 8}
|
||||
:key new-notifications? ;; Key is required to force removal of holes
|
||||
:holes (cond
|
||||
(not new-notifications?) ;; No new notifications, remove holes
|
||||
[]
|
||||
[{:keys [icon new-notifications? notification-indicator counter-label
|
||||
on-press pass-through? icon-color-anim accessibility-label]}]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [icon-animated-style (reanimated/apply-animations-to-style
|
||||
{:tint-color icon-color-anim}
|
||||
{:width 24
|
||||
:height 24})
|
||||
background-color (reanimated/use-shared-value "transparent")
|
||||
background-animated-style (reanimated/apply-animations-to-style
|
||||
{:background-color background-color}
|
||||
{:width 90
|
||||
:height 40
|
||||
:border-radius 10})]
|
||||
[rn/touchable-without-feedback
|
||||
{:on-press on-press
|
||||
:on-press-in #(toggle-background-color background-color false pass-through?)
|
||||
:on-press-out #(toggle-background-color background-color true pass-through?)
|
||||
:accessibility-label accessibility-label}
|
||||
[reanimated/view {:style background-animated-style}
|
||||
[rn/hole-view {:style {:padding-left 33
|
||||
:padding-top 8}
|
||||
:key new-notifications? ;; Key is required to force removal of holes
|
||||
:holes (cond
|
||||
(not new-notifications?) ;; No new notifications, remove holes
|
||||
[]
|
||||
|
||||
(= notification-indicator :unread-dot)
|
||||
[{:x 50 :y 5 :width 10 :height 10 :borderRadius 5}]
|
||||
(= notification-indicator :unread-dot)
|
||||
[{:x 50 :y 5 :width 10 :height 10 :borderRadius 5}]
|
||||
|
||||
:else
|
||||
[{:x 47 :y 1 :width 18 :height 18 :borderRadius 7}])}
|
||||
[icon/icon
|
||||
icon
|
||||
{:size 24
|
||||
:color (cond
|
||||
selected? colors/white
|
||||
pass-through? colors/white-opa-40
|
||||
:else colors/neutral-50)}]]
|
||||
(when new-notifications?
|
||||
(if (= notification-indicator :counter)
|
||||
[counter/counter {:outline false
|
||||
:override-text-color colors/white
|
||||
:override-bg-color colors/primary-50
|
||||
:style {:position :absolute
|
||||
:left 48
|
||||
:top 2}}
|
||||
counter-label]
|
||||
[rn/view {:style {:width 8
|
||||
:height 8
|
||||
:border-radius 4
|
||||
:top 6
|
||||
:left 51
|
||||
:position :absolute
|
||||
:background-color colors/primary-50}}]))]])))
|
||||
:else
|
||||
[{:x 47 :y 1 :width 18 :height 18 :borderRadius 7}])}
|
||||
[reanimated/image
|
||||
{:style icon-animated-style
|
||||
:source (icons/icon-source (keyword (str icon 24)))}]]
|
||||
(when new-notifications?
|
||||
(if (= notification-indicator :counter)
|
||||
[counter/counter {:outline false
|
||||
:override-text-color colors/white
|
||||
:override-bg-color colors/primary-50
|
||||
:style {:position :absolute
|
||||
:left 48
|
||||
:top 2}}
|
||||
counter-label]
|
||||
[rn/view {:style {:width 8
|
||||
:height 8
|
||||
:border-radius 4
|
||||
:top 6
|
||||
:left 51
|
||||
:position :absolute
|
||||
:background-color colors/primary-50}}]))]]))])
|
||||
|
||||
@@ -44,8 +44,7 @@
|
||||
open-scanner show-qr open-activity-center style avatar counter-label]}]
|
||||
(let [button-common-props (get-button-common-props type)]
|
||||
[rn/view {:style (merge
|
||||
{:height 56
|
||||
:flex 1}
|
||||
{:height 56}
|
||||
style)}
|
||||
;; Left Section
|
||||
[rn/touchable-without-feedback {:on-press open-profile}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
(ns quo2.components.notifications.notification-dot
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn notification-dot [style]
|
||||
[rn/view {:style (merge {:background-color (colors/theme-colors colors/primary-50 colors/primary-60)
|
||||
:width 8
|
||||
:height 8
|
||||
:border-radius 4
|
||||
:position :absolute
|
||||
:z-index 1}
|
||||
style)}])
|
||||
@@ -16,14 +16,16 @@
|
||||
(defn open-reactions-menu
|
||||
[{:keys [on-press]}]
|
||||
(let [dark? (theme/dark?)]
|
||||
[rn/touchable-opacity {:on-press on-press
|
||||
:style (merge reaction-styling
|
||||
[rn/touchable-opacity
|
||||
{:on-press on-press
|
||||
:accessibility-label :emoji-reaction-add
|
||||
:style (merge reaction-styling
|
||||
{:padding-horizontal 9
|
||||
:border-width 1
|
||||
:margin-top 5
|
||||
:border-color (if dark?
|
||||
colors/neutral-70
|
||||
colors/neutral-30)})}
|
||||
:border-width 1
|
||||
:margin-top 5
|
||||
:border-color (if dark?
|
||||
colors/neutral-70
|
||||
colors/neutral-30)})}
|
||||
[icons/icon :main-icons2/add
|
||||
{:size 20
|
||||
:color (if dark?
|
||||
@@ -32,26 +34,30 @@
|
||||
|
||||
(defn reaction
|
||||
"Add your emoji as a param here"
|
||||
[{:keys [emoji clicks neutral? on-press]}]
|
||||
[{:keys [emoji clicks neutral? on-press accessibility-label]}]
|
||||
(let [dark? (theme/dark?)
|
||||
text-color (if dark? colors/white
|
||||
colors/neutral-100)
|
||||
numeric-value (int clicks)
|
||||
clicks-positive? (pos? numeric-value)]
|
||||
[rn/touchable-opacity {:on-press on-press
|
||||
:style (merge reaction-styling
|
||||
(cond-> {:background-color
|
||||
(if dark?
|
||||
(if neutral?
|
||||
colors/neutral-70
|
||||
:transparent)
|
||||
(if neutral?
|
||||
colors/neutral-30
|
||||
:transparent))}
|
||||
(and dark? (not neutral?)) (assoc :border-color colors/neutral-70
|
||||
:border-width 1)
|
||||
(and (not dark?) (not neutral?)) (assoc :border-color colors/neutral-30
|
||||
:border-width 1)))}
|
||||
[rn/touchable-opacity
|
||||
{:on-press on-press
|
||||
:accessibility-label accessibility-label
|
||||
:style (merge reaction-styling
|
||||
(cond-> {:background-color
|
||||
(if dark?
|
||||
(if neutral?
|
||||
colors/neutral-70
|
||||
:transparent)
|
||||
(if neutral?
|
||||
colors/neutral-30
|
||||
:transparent))}
|
||||
(and dark? (not neutral?))
|
||||
(assoc :border-color colors/neutral-70
|
||||
:border-width 1)
|
||||
(and (not dark?) (not neutral?))
|
||||
(assoc :border-color colors/neutral-30
|
||||
:border-width 1)))}
|
||||
[icons/icon emoji {:no-color true
|
||||
:size 16}]
|
||||
[quo2.text/text {:size :paragraph-2
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
[rn/view {:style
|
||||
{:height 20
|
||||
:width 20}}
|
||||
[icons/icon :main-icons2/check
|
||||
[icons/icon :main-icons2/check-small
|
||||
{:size 20
|
||||
:color (colors/theme-colors
|
||||
(colors/alpha colors/neutral-100 (if disabled? 0.3 1))
|
||||
@@ -53,8 +53,8 @@
|
||||
[rn/view
|
||||
{:style (merge
|
||||
container-style
|
||||
{:height 21
|
||||
:width 21})}
|
||||
{:height 20
|
||||
:width 20})}
|
||||
[rn/view
|
||||
{:style {:flex 1
|
||||
:border-radius 6
|
||||
@@ -72,9 +72,9 @@
|
||||
[rn/view {:style
|
||||
{:height 20
|
||||
:width 20}}
|
||||
[icons/icon :main-icons2/check
|
||||
[icons/icon :main-icons2/check-small
|
||||
{:size 20
|
||||
:color (colors/alpha colors/white (if disabled? 0.3 1))}]])]]])))
|
||||
:color colors/white}]])]]])))
|
||||
|
||||
(defn radio [{:keys [default-checked?]}]
|
||||
(let [checked? (reagent/atom (or default-checked? false))]
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
[reagent.core :as reagent]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.core :as utils]
|
||||
[status-im.utils.number :as number-utils]))
|
||||
[status-im.utils.number :as number-utils]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.notifications.notification-dot :refer [notification-dot]]))
|
||||
|
||||
(def default-tab-size 32)
|
||||
|
||||
@@ -14,9 +16,21 @@
|
||||
(fn [{:keys [data size] :or {size default-tab-size}}]
|
||||
[rn/view (merge {:flex-direction :row} style)
|
||||
(doall
|
||||
(for [{:keys [label id]} data]
|
||||
(for [{:keys [label id new-info]} data]
|
||||
^{:key id}
|
||||
[rn/view {:style {:margin-right (if (= size default-tab-size) 12 8)}}
|
||||
(when new-info
|
||||
[rn/view {:position :absolute
|
||||
:z-index 1
|
||||
:right -2
|
||||
:top -2
|
||||
:width 10
|
||||
:height 10
|
||||
:border-radius 5
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:background-color (colors/theme-colors colors/neutral-5 colors/neutral-95)}
|
||||
[notification-dot]])
|
||||
[tab/tab
|
||||
{:id id
|
||||
:size size
|
||||
@@ -132,15 +146,15 @@
|
||||
[tab/tab {:id id
|
||||
:size size
|
||||
:override-theme override-theme
|
||||
:blur? blur?
|
||||
:active (= id @active-tab-id)
|
||||
:on-press (fn [id]
|
||||
(reset! active-tab-id id)
|
||||
(when scroll-on-press?
|
||||
(.scrollToIndex @flat-list-ref
|
||||
#js {:animated true
|
||||
:index index
|
||||
:viewPosition 0.5}))
|
||||
(when on-change
|
||||
(on-change id)))}
|
||||
:blur? blur?
|
||||
:active (= id @active-tab-id)
|
||||
:on-press (fn [id]
|
||||
(reset! active-tab-id id)
|
||||
(when scroll-on-press?
|
||||
(.scrollToIndex @flat-list-ref
|
||||
#js {:animated true
|
||||
:index index
|
||||
:viewPosition 0.5}))
|
||||
(when on-change
|
||||
(on-change id)))}
|
||||
label]])})])))))
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
;;Blur
|
||||
(def neutral-5-opa-70 (alpha neutral-5 0.7))
|
||||
(def neutral-90-opa-70 (alpha neutral-90 0.7))
|
||||
(def neutral-95-opa-70 (alpha neutral-95 0.7))
|
||||
|
||||
;;80 with transparency
|
||||
(def neutral-80-opa-5 (alpha neutral-80 0.05))
|
||||
@@ -60,6 +59,20 @@
|
||||
(def neutral-80-opa-90 (alpha neutral-80 0.9))
|
||||
(def neutral-80-opa-95 (alpha neutral-80 0.95))
|
||||
|
||||
;;95 with transparency
|
||||
(def neutral-95-opa-60 (alpha neutral-95 0.6))
|
||||
(def neutral-95-opa-70 (alpha neutral-95 0.7))
|
||||
(def neutral-95-opa-80 (alpha neutral-95 0.8))
|
||||
(def neutral-95-opa-90 (alpha neutral-95 0.9))
|
||||
(def neutral-95-opa-95 (alpha neutral-95 0.95))
|
||||
|
||||
;;100 with transparency
|
||||
(def neutral-100-opa-60 (alpha neutral-100 0.6))
|
||||
(def neutral-100-opa-70 (alpha neutral-100 0.7))
|
||||
(def neutral-100-opa-80 (alpha neutral-100 0.8))
|
||||
(def neutral-100-opa-90 (alpha neutral-100 0.9))
|
||||
(def neutral-100-opa-95 (alpha neutral-100 0.95))
|
||||
|
||||
;;;;White
|
||||
|
||||
;;Solid
|
||||
@@ -104,6 +117,12 @@
|
||||
(def success-50-opa-30 (alpha success-50 0.3))
|
||||
(def success-50-opa-40 (alpha success-50 0.4))
|
||||
|
||||
(def success-60-opa-5 (alpha success-60 0.05))
|
||||
(def success-60-opa-10 (alpha success-60 0.1))
|
||||
(def success-60-opa-20 (alpha success-60 0.2))
|
||||
(def success-60-opa-30 (alpha success-60 0.3))
|
||||
(def success-60-opa-40 (alpha success-60 0.4))
|
||||
|
||||
;;;;Danger
|
||||
|
||||
;;Solid
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
{:key :ghost
|
||||
:value "Ghost"}
|
||||
{:key :danger
|
||||
:value "Danger"}]}
|
||||
:value "Danger"}
|
||||
{:key :positive
|
||||
:value "Positive"}]}
|
||||
{:label "Size:"
|
||||
:key :size
|
||||
:type :select
|
||||
|
||||
@@ -14,24 +14,27 @@
|
||||
:key :show-red-options?
|
||||
:type :boolean}])
|
||||
|
||||
(def options-with-consequences [{:icon :main-icons2/share-context
|
||||
:label "Clear history"}])
|
||||
(def options-with-consequences [{:icon :main-icons2/delete
|
||||
:danger? true
|
||||
:label "Clear history"}])
|
||||
|
||||
(defn render-action-sheet [state]
|
||||
[quo2/action-drawer {:actions-with-consequence (when (:show-red-options? @state) options-with-consequences)
|
||||
:actions [{:icon :main-icons2/share-context
|
||||
:label "View channel members and details"}
|
||||
{:icon :main-icons2/communities
|
||||
:label "Mark as read"}
|
||||
{:icon :main-icons2/muted
|
||||
:label (if (:muted? @state) "Unmute channel" "Mute channel")
|
||||
:right-icon :main-icons2/chevron-right
|
||||
:sub-label (when (:muted? @state) "Muted for 15 min")}
|
||||
{:icon :main-icons2/scan
|
||||
:right-icon :main-icons2/chevron-right
|
||||
:label "Fetch messages"}
|
||||
{:icon :main-icons2/add-user
|
||||
:label "Share link to the channel"}]}])
|
||||
[quo2/action-drawer (cond-> [[{:icon :main-icons2/friend
|
||||
:label "View channel members and details"}
|
||||
{:icon :main-icons2/communities
|
||||
:label "Mark as read"}
|
||||
{:icon :main-icons2/muted
|
||||
:label (if (:muted? @state) "Unmute channel" "Mute channel")
|
||||
:right-icon :main-icons2/chevron-right
|
||||
:sub-label (when (:muted? @state) "Muted for 15 min")}
|
||||
{:icon :main-icons2/scan
|
||||
:right-icon :main-icons2/chevron-right
|
||||
:label "Fetch messages"}
|
||||
{:icon :main-icons2/add-user
|
||||
:label "Share link to the channel"}]]
|
||||
|
||||
(:show-red-options? @state)
|
||||
(conj options-with-consequences))])
|
||||
|
||||
(defn cool-preview []
|
||||
(let [state (reagent/atom {:muted? true
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo.previews.preview :as preview]
|
||||
[reagent.core :as reagent]
|
||||
[quo2.reanimated :as reanimated]
|
||||
[quo2.components.navigation.bottom-nav-tab :as quo2]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
@@ -36,21 +37,34 @@
|
||||
:key :counter-label
|
||||
:type :text}])
|
||||
|
||||
(defn get-icon-color [selected? pass-through?]
|
||||
(cond
|
||||
selected? colors/white
|
||||
pass-through? colors/white-opa-40
|
||||
:else colors/neutral-50))
|
||||
|
||||
(defn cool-preview []
|
||||
(let [state (reagent/atom {:icon :main-icons2/communities
|
||||
:selected? true
|
||||
:pass-through? true
|
||||
:new-notifications? true
|
||||
:notification-indicator :counter
|
||||
:counter-label 8
|
||||
:preview-label-color colors/white})]
|
||||
(fn []
|
||||
[rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!}
|
||||
[rn/view {:padding-bottom 150}
|
||||
[preview/customizer state descriptor]
|
||||
[rn/view {:padding-vertical 60
|
||||
:align-items :center}
|
||||
[quo2/bottom-nav-tab @state (:value @state)]]]])))
|
||||
(let [state (reagent/atom {:icon :main-icons2/communities
|
||||
:new-notifications? true
|
||||
:notification-indicator :counter
|
||||
:counter-label 8
|
||||
:preview-label-color colors/white})
|
||||
selected? (reagent/cursor state [:selected?])
|
||||
pass-through? (reagent/cursor state [:pass-through?])]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [icon-color-anim (reanimated/use-shared-value colors/white)]
|
||||
(reanimated/set-shared-value
|
||||
icon-color-anim
|
||||
(get-icon-color @selected? @pass-through?))
|
||||
[rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!}
|
||||
[rn/view {:padding-bottom 150}
|
||||
[preview/customizer state descriptor]
|
||||
[rn/view {:padding-vertical 60
|
||||
:align-items :center}
|
||||
[quo2/bottom-nav-tab
|
||||
(merge @state {:icon-color-anim icon-color-anim})
|
||||
(:value @state)]]]]))]))
|
||||
|
||||
(defn preview-bottom-nav-tab []
|
||||
[rn/view {:background-color colors/neutral-100
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(ns status-im.activity-center.core
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.constants :as constants]
|
||||
(:require [re-frame.core :as rf]
|
||||
[status-im.constants :as const]
|
||||
[status-im.data-store.activities :as data-store.activities]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.utils.fx :as fx]
|
||||
@@ -18,21 +18,23 @@
|
||||
~excessively~ big, this implementation will probably need to be revisited."
|
||||
[db-notifications new-notifications]
|
||||
(reduce (fn [acc {:keys [id type read] :as notification}]
|
||||
(let [filter-status (if read :read :unread)]
|
||||
(cond-> (-> acc
|
||||
(update-in [type :read :data]
|
||||
(fn [data]
|
||||
(remove #(= id (:id %)) data)))
|
||||
(update-in [type :unread :data]
|
||||
(fn [data]
|
||||
(remove #(= id (:id %)) data))))
|
||||
(not (or (:dismissed notification) (:accepted notification)))
|
||||
(update-in [type filter-status :data]
|
||||
(fn [data]
|
||||
(->> notification
|
||||
(conj data)
|
||||
(sort-by (juxt :timestamp :id))
|
||||
reverse))))))
|
||||
(let [filter-status (if read :read :unread)
|
||||
remove-notification (fn [data]
|
||||
(remove #(= id (:id %)) data))
|
||||
insert-and-sort (fn [data]
|
||||
(->> notification
|
||||
(conj data)
|
||||
(sort-by (juxt :timestamp :id))
|
||||
reverse))]
|
||||
(as-> acc $
|
||||
(update-in $ [type :read :data] remove-notification)
|
||||
(update-in $ [type :unread :data] remove-notification)
|
||||
(update-in $ [const/activity-center-notification-type-no-type :read :data] remove-notification)
|
||||
(update-in $ [const/activity-center-notification-type-no-type :unread :data] remove-notification)
|
||||
(if (or (:dismissed notification) (:accepted notification))
|
||||
$
|
||||
(-> $ (update-in [type filter-status :data] insert-and-sort)
|
||||
(update-in [const/activity-center-notification-type-no-type filter-status :data] insert-and-sort))))))
|
||||
db-notifications
|
||||
new-notifications))
|
||||
|
||||
@@ -43,11 +45,37 @@
|
||||
{:db (update-in db [:activity-center :notifications]
|
||||
update-notifications new-notifications)}))
|
||||
|
||||
;;;; Contact verification
|
||||
|
||||
(fx/defn contact-verification-decline
|
||||
{:events [:activity-center.contact-verification/decline]}
|
||||
[_ contact-verification-id]
|
||||
{::json-rpc/call [{:method "wakuext_declineContactVerificationRequest"
|
||||
:params [contact-verification-id]
|
||||
:on-success #(rf/dispatch [:activity-center.contact-verification/decline-success %])
|
||||
:on-error #(rf/dispatch [:activity-center.contact-verification/decline-error contact-verification-id %])}]})
|
||||
|
||||
(fx/defn contact-verification-decline-success
|
||||
{:events [:activity-center.contact-verification/decline-success]}
|
||||
[cofx response]
|
||||
(->> response
|
||||
:activityCenterNotifications
|
||||
(map data-store.activities/<-rpc)
|
||||
(notifications-reconcile cofx)))
|
||||
|
||||
(fx/defn contact-verification-decline-error
|
||||
{:events [:activity-center.contact-verification/decline-error]}
|
||||
[_ contact-verification-id error]
|
||||
(log/warn "Failed to decline contact verification"
|
||||
{:contact-verification-id contact-verification-id
|
||||
:error error})
|
||||
nil)
|
||||
|
||||
;;;; Notifications fetching and pagination
|
||||
|
||||
(def defaults
|
||||
{:filter-status :unread
|
||||
:filter-type constants/activity-center-notification-type-no-type
|
||||
:filter-type const/activity-center-notification-type-no-type
|
||||
:notifications-per-page 10})
|
||||
|
||||
(def start-or-end-cursor
|
||||
@@ -70,8 +98,8 @@
|
||||
{:db (assoc-in db [:activity-center :notifications filter-type filter-status :loading?] true)
|
||||
::json-rpc/call [{:method (filter-status->rpc-method filter-status)
|
||||
:params [cursor (defaults :notifications-per-page) filter-type]
|
||||
:on-success #(re-frame/dispatch [:activity-center.notifications/fetch-success filter-type filter-status reset-data? %])
|
||||
:on-error #(re-frame/dispatch [:activity-center.notifications/fetch-error filter-type filter-status %])}]}))
|
||||
:on-success #(rf/dispatch [:activity-center.notifications/fetch-success filter-type filter-status reset-data? %])
|
||||
:on-error #(rf/dispatch [:activity-center.notifications/fetch-error filter-type filter-status %])}]}))
|
||||
|
||||
(fx/defn notifications-fetch-first-page
|
||||
{:events [:activity-center.notifications/fetch-first-page]}
|
||||
|
||||
@@ -2,366 +2,491 @@
|
||||
(:require [cljs.test :refer [deftest is testing]]
|
||||
[day8.re-frame.test :as rf-test]
|
||||
[re-frame.core :as rf]
|
||||
[status-im.constants :as c]
|
||||
[status-im.constants :as const]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
status-im.events
|
||||
[status-im.test-helpers :as h]))
|
||||
[status-im.test-helpers :as h]
|
||||
[status-im.utils.config :as config]))
|
||||
|
||||
(defn setup []
|
||||
(h/register-helper-events)
|
||||
(rf/dispatch [:init/app-started]))
|
||||
|
||||
(defn remove-color-key
|
||||
"Remove `:color` key from notifications because they have random values that we
|
||||
can't assert against."
|
||||
[grouped-notifications {:keys [type status]}]
|
||||
(update-in grouped-notifications
|
||||
[type status :data]
|
||||
(fn [old _]
|
||||
(map #(dissoc % :color) old))
|
||||
nil))
|
||||
;;;; Contact verification
|
||||
|
||||
(deftest contact-verification-decline-test
|
||||
(with-redefs [config/new-activity-center-enabled? true]
|
||||
(testing "successfully declines and reconciles returned notification"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])
|
||||
contact-verification-id 24
|
||||
expected-notification {:accepted false
|
||||
:author "0x04d03f"
|
||||
:chat-id "0x04d03f"
|
||||
:contact-verification-status 3
|
||||
:dismissed false
|
||||
:id 24
|
||||
:last-message nil
|
||||
:message {:command-parameters nil
|
||||
:content {:chat-id nil
|
||||
:ens-name nil
|
||||
:image nil
|
||||
:line-count nil
|
||||
:links nil
|
||||
:parsed-text nil
|
||||
:response-to nil
|
||||
:rtl? nil
|
||||
:sticker nil
|
||||
:text nil}
|
||||
:outgoing false
|
||||
:outgoing-status nil
|
||||
:quoted-message nil}
|
||||
:name "0x04d03f"
|
||||
:read true
|
||||
:reply-message nil
|
||||
:timestamp 1666647286000
|
||||
:type const/activity-center-notification-type-contact-verification}]
|
||||
(h/stub-fx-with-callbacks
|
||||
::json-rpc/call
|
||||
:on-success (constantly {:activityCenterNotifications
|
||||
[{:accepted false
|
||||
:author "0x04d03f"
|
||||
:chatId "0x04d03f"
|
||||
:contactVerificationStatus 3
|
||||
:dismissed false
|
||||
:id contact-verification-id
|
||||
:message {}
|
||||
:name "0x04d03f"
|
||||
:read true
|
||||
:timestamp 1666647286000
|
||||
:type const/activity-center-notification-type-contact-verification}]}))
|
||||
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
|
||||
(rf/dispatch [:activity-center.contact-verification/decline contact-verification-id])
|
||||
|
||||
(is (= {:method "wakuext_declineContactVerificationRequest"
|
||||
:params [contact-verification-id]}
|
||||
(-> @spy-queue
|
||||
(get-in [0 :args 0])
|
||||
(select-keys [:method :params]))))
|
||||
|
||||
(is (= {const/activity-center-notification-type-no-type
|
||||
{:read {:data [expected-notification]}
|
||||
:unread {:data []}}
|
||||
const/activity-center-notification-type-contact-verification
|
||||
{:read {:data [expected-notification]}
|
||||
:unread {:data []}}}
|
||||
(get-in (h/db) [:activity-center :notifications]))))))
|
||||
|
||||
(testing "logs failure"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [contact-verification-id 666]
|
||||
(h/using-log-test-appender
|
||||
(fn [logs]
|
||||
(h/stub-fx-with-callbacks ::json-rpc/call :on-error (constantly :fake-error))
|
||||
|
||||
(rf/dispatch [:activity-center.contact-verification/decline contact-verification-id])
|
||||
|
||||
(is (= {:args ["Failed to decline contact verification"
|
||||
{:contact-verification-id contact-verification-id
|
||||
:error :fake-error}]
|
||||
:level :warn}
|
||||
(last @logs))))))))))
|
||||
|
||||
;;;; Notification reconciliation
|
||||
|
||||
(deftest notifications-reconcile-test
|
||||
(testing "does nothing when there are no new notifications"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [notifications {c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat}
|
||||
{:id "0x2"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x3"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}}
|
||||
c/activity-center-notification-type-private-group-chat
|
||||
{:unread {:cursor ""
|
||||
:data [{:id "0x4"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat}]}}}]
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications] notifications])
|
||||
(with-redefs [config/new-activity-center-enabled? true]
|
||||
(testing "does nothing when there are no new notifications"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [notifications {const/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type const/activity-center-notification-type-one-to-one-chat}
|
||||
{:id "0x2"
|
||||
:read true
|
||||
:type const/activity-center-notification-type-one-to-one-chat}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x3"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-one-to-one-chat}]}}
|
||||
const/activity-center-notification-type-private-group-chat
|
||||
{:unread {:cursor ""
|
||||
:data [{:id "0x4"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-private-group-chat}]}}}]
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications] notifications])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile nil])
|
||||
(rf/dispatch [:activity-center.notifications/reconcile nil])
|
||||
|
||||
(is (= notifications (get-in (h/db) [:activity-center :notifications]))))))
|
||||
(is (= notifications (get-in (h/db) [:activity-center :notifications]))))))
|
||||
|
||||
(testing "removes dismissed or accepted notifications"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1" :read true :type c/activity-center-notification-type-one-to-one-chat}
|
||||
{:id "0x2" :read true :type c/activity-center-notification-type-one-to-one-chat}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x3" :read false :type c/activity-center-notification-type-one-to-one-chat}]}}
|
||||
2 {:unread {:cursor ""
|
||||
:data [{:id "0x4" :read false :type 2}
|
||||
{:id "0x6" :read false :type 2}]}}}])
|
||||
(testing "removes dismissed or accepted notifications"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{const/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1" :read true :type const/activity-center-notification-type-one-to-one-chat}
|
||||
{:id "0x2" :read true :type const/activity-center-notification-type-one-to-one-chat}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x3" :read false :type const/activity-center-notification-type-one-to-one-chat}]}}
|
||||
const/activity-center-notification-type-private-group-chat
|
||||
{:unread {:cursor ""
|
||||
:data [{:id "0x4" :read false :type const/activity-center-notification-type-private-group-chat}
|
||||
{:id "0x6" :read false :type const/activity-center-notification-type-private-group-chat}]}}}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:dismissed true}
|
||||
{:id "0x3"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:accepted true}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat
|
||||
:dismissed true}
|
||||
{:id "0x5"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat
|
||||
:accepted true}]])
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1"
|
||||
:read true
|
||||
:type const/activity-center-notification-type-one-to-one-chat
|
||||
:dismissed true}
|
||||
{:id "0x3"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-one-to-one-chat
|
||||
:accepted true}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-private-group-chat
|
||||
:dismissed true}
|
||||
{:id "0x5"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-private-group-chat
|
||||
:accepted true}]])
|
||||
|
||||
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x2"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}
|
||||
:unread {:cursor ""
|
||||
:data []}}
|
||||
c/activity-center-notification-type-private-group-chat
|
||||
{:read {:data []}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x6"
|
||||
(is (= {const/activity-center-notification-type-no-type
|
||||
{:read {:data []}
|
||||
:unread {:data []}}
|
||||
|
||||
const/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x2"
|
||||
:read true
|
||||
:type const/activity-center-notification-type-one-to-one-chat}]}
|
||||
:unread {:cursor ""
|
||||
:data []}}
|
||||
const/activity-center-notification-type-private-group-chat
|
||||
{:read {:data []}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x6"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-private-group-chat}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications])))))
|
||||
|
||||
(testing "replaces old notifications with newly arrived ones"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{const/activity-center-notification-type-no-type
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type const/activity-center-notification-type-one-to-one-chat}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x4"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-private-group-chat}
|
||||
{:id "0x6"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-private-group-chat}]}}
|
||||
const/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type const/activity-center-notification-type-one-to-one-chat}]}}
|
||||
const/activity-center-notification-type-private-group-chat
|
||||
{:unread {:cursor ""
|
||||
:data [{:id "0x4"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-private-group-chat}
|
||||
{:id "0x6"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-private-group-chat}]}}}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1"
|
||||
:read true
|
||||
:type const/activity-center-notification-type-one-to-one-chat
|
||||
:last-message {}}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-private-group-chat
|
||||
:author "0xabc"}
|
||||
{:id "0x6"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-private-group-chat}]])
|
||||
|
||||
(is (= {const/activity-center-notification-type-no-type
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type const/activity-center-notification-type-one-to-one-chat
|
||||
:last-message {}}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x6"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-private-group-chat}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-private-group-chat
|
||||
:author "0xabc"}]}}
|
||||
const/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type const/activity-center-notification-type-one-to-one-chat
|
||||
:last-message {}}]}
|
||||
:unread {:data []}}
|
||||
const/activity-center-notification-type-private-group-chat
|
||||
{:read {:data []}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x6"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-private-group-chat}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-private-group-chat
|
||||
:author "0xabc"}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications])))))
|
||||
|
||||
(testing "reconciles notifications that switched their read/unread status"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{const/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type const/activity-center-notification-type-one-to-one-chat}]}}}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-one-to-one-chat}]])
|
||||
|
||||
(is (= {const/activity-center-notification-type-no-type
|
||||
{:read {:data []}
|
||||
:unread {:data [{:id "0x1"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications])))))
|
||||
:type const/activity-center-notification-type-one-to-one-chat}]}}
|
||||
|
||||
(testing "replaces old notifications with newly arrived ones"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}}
|
||||
c/activity-center-notification-type-private-group-chat
|
||||
{:unread {:cursor ""
|
||||
:data [{:id "0x4"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat}
|
||||
{:id "0x6"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat}]}}}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:last-message {}}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat
|
||||
:author "0xabc"}
|
||||
{:id "0x6"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat}]])
|
||||
|
||||
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:last-message {}}]}
|
||||
:unread {:data []}}
|
||||
c/activity-center-notification-type-private-group-chat
|
||||
{:read {:data []}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x6"
|
||||
const/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data []}
|
||||
:unread {:data [{:id "0x1"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat
|
||||
:author "0xabc"}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications])))))
|
||||
:type const/activity-center-notification-type-one-to-one-chat}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications])))))
|
||||
|
||||
(testing "reconciles notifications that switched their read/unread status"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}}}])
|
||||
;; Sorting by timestamp and ID is compatible with what the backend does when
|
||||
;; returning paginated results.
|
||||
(testing "sorts notifications by timestamp and id in descending order"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{const/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1" :read true :type const/activity-center-notification-type-one-to-one-chat :timestamp 1}
|
||||
{:id "0x2" :read true :type const/activity-center-notification-type-one-to-one-chat :timestamp 1}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x3" :read false :type const/activity-center-notification-type-one-to-one-chat :timestamp 50}
|
||||
{:id "0x4" :read false :type const/activity-center-notification-type-one-to-one-chat :timestamp 100}
|
||||
{:id "0x5" :read false :type const/activity-center-notification-type-one-to-one-chat :timestamp 100}]}}}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]])
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1" :read true :type const/activity-center-notification-type-one-to-one-chat :timestamp 1 :last-message {}}
|
||||
{:id "0x4" :read false :type const/activity-center-notification-type-one-to-one-chat :timestamp 100 :last-message {}}]])
|
||||
|
||||
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data []}
|
||||
:unread {:data [{:id "0x1"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications])))))
|
||||
|
||||
;; Sorting by timestamp and ID is compatible with what the backend does when
|
||||
;; returning paginated results.
|
||||
(testing "sorts notifications by timestamp and id in descending order"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1" :read true :type c/activity-center-notification-type-one-to-one-chat :timestamp 1}
|
||||
{:id "0x2" :read true :type c/activity-center-notification-type-one-to-one-chat :timestamp 1}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x3" :read false :type c/activity-center-notification-type-one-to-one-chat :timestamp 50}
|
||||
{:id "0x4" :read false :type c/activity-center-notification-type-one-to-one-chat :timestamp 100}
|
||||
{:id "0x5" :read false :type c/activity-center-notification-type-one-to-one-chat :timestamp 100}]}}}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1" :read true :type c/activity-center-notification-type-one-to-one-chat :timestamp 1 :last-message {}}
|
||||
{:id "0x4" :read false :type c/activity-center-notification-type-one-to-one-chat :timestamp 100 :last-message {}}]])
|
||||
|
||||
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x2"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:timestamp 1}
|
||||
{:id "0x1"
|
||||
(is (= {const/activity-center-notification-type-no-type
|
||||
{:read {:data [{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:type const/activity-center-notification-type-one-to-one-chat
|
||||
:timestamp 1
|
||||
:last-message {}}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x5"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:timestamp 100}
|
||||
{:id "0x4"
|
||||
:unread {:data [{:id "0x4"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:type const/activity-center-notification-type-one-to-one-chat
|
||||
:timestamp 100
|
||||
:last-message {}}
|
||||
{:id "0x3"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:timestamp 50}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications]))))))
|
||||
:last-message {}}]}}
|
||||
const/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x2"
|
||||
:read true
|
||||
:type const/activity-center-notification-type-one-to-one-chat
|
||||
:timestamp 1}
|
||||
{:id "0x1"
|
||||
:read true
|
||||
:type const/activity-center-notification-type-one-to-one-chat
|
||||
:timestamp 1
|
||||
:last-message {}}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x5"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-one-to-one-chat
|
||||
:timestamp 100}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-one-to-one-chat
|
||||
:timestamp 100
|
||||
:last-message {}}
|
||||
{:id "0x3"
|
||||
:read false
|
||||
:type const/activity-center-notification-type-one-to-one-chat
|
||||
:timestamp 50}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications])))))))
|
||||
|
||||
;;;; Notifications fetching and pagination
|
||||
|
||||
(deftest notifications-fetch-test
|
||||
(testing "fetches first page"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/stub-fx-with-callbacks
|
||||
::json-rpc/call
|
||||
:on-success (constantly {:cursor "10"
|
||||
:notifications [{:id "0x1"
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:read false
|
||||
:chatId "0x9"}]}))
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(with-redefs [config/new-activity-center-enabled? true]
|
||||
(testing "fetches first page"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/stub-fx-with-callbacks
|
||||
::json-rpc/call
|
||||
:on-success (constantly {:cursor "10"
|
||||
:notifications [{:id "0x1"
|
||||
:type const/activity-center-notification-type-one-to-one-chat
|
||||
:read false
|
||||
:chatId "0x9"}]}))
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/fetch-first-page
|
||||
{:filter-type c/activity-center-notification-type-one-to-one-chat}])
|
||||
(rf/dispatch [:activity-center.notifications/fetch-first-page
|
||||
{:filter-type const/activity-center-notification-type-one-to-one-chat}])
|
||||
|
||||
(is (= :unread (get-in (h/db) [:activity-center :filter :status])))
|
||||
(is (= "" (get-in @spy-queue [0 :args 0 :params 0]))
|
||||
"Should be called with empty cursor when fetching first page")
|
||||
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||
{:unread {:cursor "10"
|
||||
:data [{:chat-id "0x9"
|
||||
:chat-name nil
|
||||
:chat-type c/activity-center-notification-type-one-to-one-chat
|
||||
:group-chat false
|
||||
:id "0x1"
|
||||
:public? false
|
||||
:last-message nil
|
||||
:message nil
|
||||
:read false
|
||||
:reply-message nil
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}}}
|
||||
(remove-color-key (get-in (h/db) [:activity-center :notifications])
|
||||
{:status :unread
|
||||
:type c/activity-center-notification-type-one-to-one-chat}))))))
|
||||
(is (= :unread (get-in (h/db) [:activity-center :filter :status])))
|
||||
(is (= "" (get-in @spy-queue [0 :args 0 :params 0]))
|
||||
"Should be called with empty cursor when fetching first page")
|
||||
(is (= {const/activity-center-notification-type-one-to-one-chat
|
||||
{:unread {:cursor "10"
|
||||
:data [{:chat-id "0x9"
|
||||
:chat-name nil
|
||||
:chat-type const/activity-center-notification-type-one-to-one-chat
|
||||
:group-chat false
|
||||
:id "0x1"
|
||||
:public? false
|
||||
:last-message nil
|
||||
:message nil
|
||||
:read false
|
||||
:reply-message nil
|
||||
:type const/activity-center-notification-type-one-to-one-chat}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications]))))))
|
||||
|
||||
(testing "does not fetch next page when pagination cursor reached the end"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
c/activity-center-notification-type-one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :unread :cursor]
|
||||
""])
|
||||
(testing "does not fetch next page when pagination cursor reached the end"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
const/activity-center-notification-type-one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications const/activity-center-notification-type-one-to-one-chat :unread :cursor]
|
||||
""])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
|
||||
(is (= [] @spy-queue)))))
|
||||
(is (= [] @spy-queue)))))
|
||||
|
||||
;; The cursor can be nil sometimes because the reconciliation doesn't care
|
||||
;; about updating the cursor value, but we have to make sure the next page is
|
||||
;; only fetched if the current cursor is valid.
|
||||
(testing "does not fetch next page when cursor is nil"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
c/activity-center-notification-type-one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :unread :cursor]
|
||||
nil])
|
||||
;; The cursor can be nil sometimes because the reconciliation doesn't care
|
||||
;; about updating the cursor value, but we have to make sure the next page is
|
||||
;; only fetched if the current cursor is valid.
|
||||
(testing "does not fetch next page when cursor is nil"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
const/activity-center-notification-type-one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications const/activity-center-notification-type-one-to-one-chat :unread :cursor]
|
||||
nil])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
|
||||
(is (= [] @spy-queue)))))
|
||||
(is (= [] @spy-queue)))))
|
||||
|
||||
(testing "fetches next page when pagination cursor is not empty"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/stub-fx-with-callbacks
|
||||
::json-rpc/call
|
||||
:on-success (constantly {:cursor ""
|
||||
:notifications [{:id "0x1"
|
||||
:type c/activity-center-notification-type-mention
|
||||
:read false
|
||||
:chatId "0x9"}]}))
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
c/activity-center-notification-type-mention])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-mention :unread :cursor]
|
||||
"10"])
|
||||
(testing "fetches next page when pagination cursor is not empty"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/stub-fx-with-callbacks
|
||||
::json-rpc/call
|
||||
:on-success (constantly {:cursor ""
|
||||
:notifications [{:id "0x1"
|
||||
:type const/activity-center-notification-type-mention
|
||||
:read false
|
||||
:chatId "0x9"}]}))
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
const/activity-center-notification-type-mention])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications const/activity-center-notification-type-mention :unread :cursor]
|
||||
"10"])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
|
||||
(is (= "wakuext_unreadActivityCenterNotifications" (get-in @spy-queue [0 :args 0 :method])))
|
||||
(is (= "10" (get-in @spy-queue [0 :args 0 :params 0]))
|
||||
"Should be called with current cursor")
|
||||
(is (= {c/activity-center-notification-type-mention
|
||||
{:unread {:cursor ""
|
||||
:data [{:chat-id "0x9"
|
||||
:chat-name nil
|
||||
:chat-type 3
|
||||
:id "0x1"
|
||||
:last-message nil
|
||||
:message nil
|
||||
:read false
|
||||
:reply-message nil
|
||||
:type c/activity-center-notification-type-mention}]}}}
|
||||
(remove-color-key (get-in (h/db) [:activity-center :notifications])
|
||||
{:status :unread
|
||||
:type c/activity-center-notification-type-mention}))))))
|
||||
(is (= "wakuext_unreadActivityCenterNotifications" (get-in @spy-queue [0 :args 0 :method])))
|
||||
(is (= "10" (get-in @spy-queue [0 :args 0 :params 0]))
|
||||
"Should be called with current cursor")
|
||||
(is (= {const/activity-center-notification-type-mention
|
||||
{:unread {:cursor ""
|
||||
:data [{:chat-id "0x9"
|
||||
:chat-name nil
|
||||
:chat-type 3
|
||||
:id "0x1"
|
||||
:last-message nil
|
||||
:message nil
|
||||
:read false
|
||||
:reply-message nil
|
||||
:type const/activity-center-notification-type-mention}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications]))))))
|
||||
|
||||
(testing "does not fetch next page while it is still loading"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:read])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
c/activity-center-notification-type-one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :read :cursor]
|
||||
"10"])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :read :loading?]
|
||||
true])
|
||||
(testing "does not fetch next page while it is still loading"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:read])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
const/activity-center-notification-type-one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications const/activity-center-notification-type-one-to-one-chat :read :cursor]
|
||||
"10"])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications const/activity-center-notification-type-one-to-one-chat :read :loading?]
|
||||
true])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
|
||||
(is (= [] @spy-queue)))))
|
||||
(is (= [] @spy-queue)))))
|
||||
|
||||
(testing "resets loading flag after an error"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/stub-fx-with-callbacks ::json-rpc/call :on-error (constantly :fake-error))
|
||||
(h/spy-event-fx spy-queue :activity-center.notifications/fetch-error)
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
c/activity-center-notification-type-one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :unread :cursor]
|
||||
""])
|
||||
(testing "resets loading flag after an error"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/stub-fx-with-callbacks ::json-rpc/call :on-error (constantly :fake-error))
|
||||
(h/spy-event-fx spy-queue :activity-center.notifications/fetch-error)
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
const/activity-center-notification-type-one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications const/activity-center-notification-type-one-to-one-chat :unread :cursor]
|
||||
""])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/fetch-first-page])
|
||||
(rf/dispatch [:activity-center.notifications/fetch-first-page])
|
||||
|
||||
(is (nil? (get-in (h/db) [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :unread :loading?])))
|
||||
(is (= [:activity-center.notifications/fetch-error
|
||||
c/activity-center-notification-type-one-to-one-chat
|
||||
:unread
|
||||
:fake-error]
|
||||
(:args (last @spy-queue))))))))
|
||||
(is (nil? (get-in (h/db) [:activity-center :notifications const/activity-center-notification-type-one-to-one-chat :unread :loading?])))
|
||||
(is (= [:activity-center.notifications/fetch-error
|
||||
const/activity-center-notification-type-one-to-one-chat
|
||||
:unread
|
||||
:fake-error]
|
||||
(:args (last @spy-queue)))))))))
|
||||
|
||||
@@ -176,6 +176,11 @@
|
||||
:on-error #(log/error "failed to clear history " chat-id %)}]}
|
||||
(clear-history chat-id remove-chat?)))
|
||||
|
||||
(fx/defn chat-deactivated
|
||||
{:events [::chat-deactivated]}
|
||||
[_ chat-id]
|
||||
(log/debug "chat deactivated" chat-id))
|
||||
|
||||
(fx/defn deactivate-chat
|
||||
"Deactivate chat in db, no side effects"
|
||||
[{:keys [db now] :as cofx} chat-id]
|
||||
@@ -185,10 +190,10 @@
|
||||
(assoc-in db [:chats chat-id :active] false)
|
||||
(update db :chats dissoc chat-id))
|
||||
(update :chats-home-list disj chat-id)
|
||||
(assoc-in [:current-chat-id] nil))
|
||||
(assoc :current-chat-id nil))
|
||||
::json-rpc/call [{:method "wakuext_deactivateChat"
|
||||
:params [{:id chat-id}]
|
||||
:on-success #(log/debug "chat deactivated" chat-id)
|
||||
:on-success #(re-frame/dispatch [::chat-deactivated chat-id])
|
||||
:on-error #(log/error "failed to create public chat" chat-id %)}]}
|
||||
(clear-history chat-id true)))
|
||||
|
||||
@@ -227,9 +232,7 @@
|
||||
{:clear-message-notifications
|
||||
[[chat-id] (get-in db [:multiaccount :remote-push-notifications-enabled?])]}
|
||||
(deactivate-chat chat-id)
|
||||
(offload-messages chat-id)
|
||||
(when (not (= (:view-id db) :home))
|
||||
(navigation/pop-to-root-tab :chat-stack))))
|
||||
(offload-messages chat-id)))
|
||||
|
||||
(fx/defn show-more-chats
|
||||
{:events [:chat.ui/show-more-chats]}
|
||||
@@ -265,6 +268,7 @@
|
||||
[{db :db :as cofx} chat-id from-switcher?]
|
||||
(fx/merge cofx
|
||||
{:db (assoc db :current-chat-id chat-id)}
|
||||
(offload-messages chat-id)
|
||||
(preload-chat-data chat-id)
|
||||
(navigation2/navigate-to-nav2 :chat chat-id nil from-switcher?)))
|
||||
|
||||
@@ -384,6 +388,11 @@
|
||||
(log/error "mute chat failed" chat-id error)
|
||||
{:db (assoc-in db [:chats chat-id :muted] (not muted?))})
|
||||
|
||||
(fx/defn mute-chat-toggled-successfully
|
||||
{:events [::mute-chat-toggled-successfully]}
|
||||
[_ chat-id]
|
||||
(log/debug "muted chat successfully" chat-id))
|
||||
|
||||
(fx/defn mute-chat
|
||||
{:events [::mute-chat-toggled]}
|
||||
[{:keys [db] :as cofx} chat-id muted?]
|
||||
@@ -392,7 +401,7 @@
|
||||
::json-rpc/call [{:method method
|
||||
:params [chat-id]
|
||||
:on-error #(re-frame/dispatch [::mute-chat-failed chat-id muted? %])
|
||||
:on-success #(log/debug "muted chat successfully")}]}))
|
||||
:on-success #(re-frame/dispatch [::mute-chat-toggled-successfully chat-id])}]}))
|
||||
|
||||
(fx/defn show-profile
|
||||
{:events [:chat.ui/show-profile]}
|
||||
@@ -496,4 +505,4 @@
|
||||
(update-in [:chats chat-id :unviewed-messages-count]
|
||||
#(max (- % count) 0))
|
||||
(update-in [:chats chat-id :unviewed-mentions-count]
|
||||
#(max (- % countWithMentions) 0)))})
|
||||
#(max (- % countWithMentions) 0)))})
|
||||
|
||||
@@ -91,7 +91,8 @@
|
||||
:pinned (pin-message :pinned)})
|
||||
(when pinned
|
||||
(protocol/send-chat-messages [{:chat-id (pin-message :chat-id)
|
||||
:content-type constants/content-type-pin
|
||||
:content-type constants/content-type-system-text
|
||||
:text "pinned a message"
|
||||
:response-to (pin-message :message-id)
|
||||
:ens-name preferred-name}])))))
|
||||
|
||||
@@ -99,3 +100,16 @@
|
||||
{:events [::load-pin-messages]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
(load-more-pin-messages cofx chat-id true))
|
||||
|
||||
(fx/defn show-pin-limit-modal
|
||||
{:events [::show-pin-limit-modal]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
(fx/merge
|
||||
{:db (assoc-in db [:pin-modal chat-id] true)}))
|
||||
|
||||
(fx/defn hide-pin-limit-modal
|
||||
{:events [::hide-pin-limit-modal]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
(fx/merge
|
||||
{:db (assoc-in db [:pin-modal chat-id] false)}))
|
||||
|
||||
|
||||
@@ -84,3 +84,10 @@
|
||||
:group-chat true}
|
||||
"opened" {}
|
||||
"1-1" {}}})
|
||||
|
||||
(deftest navigate-to-chat-nav2
|
||||
(let [chat-id "test_chat"
|
||||
db {:pagination-info {chat-id {:all-loaded? true}}}]
|
||||
(testing "Pagination info should be reset on navigation"
|
||||
(let [res (chat/navigate-to-chat-nav2 {:db db} chat-id false)]
|
||||
(is (nil? (get-in res [:db :pagination-info chat-id :all-loaded?])))))))
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
(def ^:const content-type-community 9)
|
||||
(def ^:const content-type-gap 10)
|
||||
(def ^:const content-type-contact-request 11) ;; TODO: temp, will be removed
|
||||
(def ^:const content-type-pin 13)
|
||||
|
||||
(def ^:const contact-request-state-none 0)
|
||||
(def ^:const contact-request-state-mutual 1)
|
||||
@@ -22,6 +21,13 @@
|
||||
(def ^:const contact-request-state-received 3)
|
||||
(def ^:const contact-request-state-dismissed 4)
|
||||
|
||||
(def ^:const contact-verification-state-unknown 0)
|
||||
(def ^:const contact-verification-state-pending 1)
|
||||
(def ^:const contact-verification-state-accepted 2)
|
||||
(def ^:const contact-verification-state-declined 3)
|
||||
(def ^:const contact-verification-state-cancelled 4)
|
||||
(def ^:const contact-verification-state-trusted 5)
|
||||
|
||||
(def ^:const emoji-reaction-love 1)
|
||||
(def ^:const emoji-reaction-thumbs-up 2)
|
||||
(def ^:const emoji-reaction-thumbs-down 3)
|
||||
@@ -183,11 +189,15 @@
|
||||
(def ^:const activity-center-notification-type-mention 3)
|
||||
(def ^:const activity-center-notification-type-reply 4)
|
||||
(def ^:const activity-center-notification-type-contact-request 5)
|
||||
(def ^:const activity-center-notification-type-contact-verification 6)
|
||||
|
||||
;; TODO: Remove this constant once the old Notification Center code is removed.
|
||||
;; Its value clashes with the new constant `activity-center-notification-type-contact-verification`
|
||||
;; used in status-go.
|
||||
(def ^:const activity-center-notification-type-contact-request-retracted 6)
|
||||
|
||||
;; TODO: Replace with correct enum values once status-go implements them.
|
||||
(def ^:const activity-center-notification-type-admin 66610)
|
||||
(def ^:const activity-center-notification-type-identity-verification 66611)
|
||||
(def ^:const activity-center-notification-type-tx 66612)
|
||||
(def ^:const activity-center-notification-type-membership 66613)
|
||||
(def ^:const activity-center-notification-type-system 66614)
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
[status-im.utils.logging.core :as utils.logs]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.utils.snoopy :as snoopy]
|
||||
[status-im.switcher.animation :as animation]
|
||||
[status-im.async-storage.core :as async-storage]
|
||||
[status-im.utils.universal-links.core :as utils.universal-links]))
|
||||
|
||||
(set! interop/next-tick js/setTimeout)
|
||||
@@ -39,6 +41,9 @@
|
||||
|
||||
(utils.universal-links/initialize)
|
||||
|
||||
;; TODO(parvesh) - Remove while moving functionality to status-go
|
||||
(async-storage/get-item :selected-stack-id #(animation/selected-stack-id-loaded %))
|
||||
|
||||
;;DEV
|
||||
(snoopy/subscribe!)
|
||||
(when (and js/goog.DEBUG platform/ios? DevSettings)
|
||||
|
||||
@@ -1,46 +1,48 @@
|
||||
(ns status-im.data-store.activities
|
||||
(:require [status-im.data-store.messages :as messages]
|
||||
[status-im.constants :as constants]
|
||||
(:require [clojure.set :as set]
|
||||
[quo.design-system.colors :as colors]
|
||||
clojure.set))
|
||||
[status-im.constants :as constants]
|
||||
[status-im.data-store.messages :as messages]
|
||||
[status-im.utils.config :as config]))
|
||||
|
||||
(defn rpc->type [{:keys [type name] :as chat}]
|
||||
(cond
|
||||
(= constants/activity-center-notification-type-reply type)
|
||||
(defn- rpc->type [{:keys [type name] :as chat}]
|
||||
(case type
|
||||
constants/activity-center-notification-type-reply
|
||||
(assoc chat
|
||||
:chat-name name
|
||||
:chat-type constants/private-group-chat-type)
|
||||
|
||||
(= constants/activity-center-notification-type-mention type)
|
||||
constants/activity-center-notification-type-mention
|
||||
(assoc chat
|
||||
:chat-type constants/private-group-chat-type
|
||||
:chat-name name)
|
||||
|
||||
(= constants/activity-center-notification-type-private-group-chat type)
|
||||
constants/activity-center-notification-type-private-group-chat
|
||||
(assoc chat
|
||||
:chat-type constants/private-group-chat-type
|
||||
:chat-name name
|
||||
:public? false
|
||||
:group-chat true)
|
||||
|
||||
(= constants/activity-center-notification-type-one-to-one-chat type)
|
||||
constants/activity-center-notification-type-one-to-one-chat
|
||||
(assoc chat
|
||||
:chat-type constants/one-to-one-chat-type
|
||||
:chat-name name
|
||||
:public? false
|
||||
:group-chat false)
|
||||
|
||||
:else
|
||||
chat))
|
||||
|
||||
(defn <-rpc [item]
|
||||
(-> item
|
||||
rpc->type
|
||||
(clojure.set/rename-keys {:lastMessage :last-message
|
||||
:replyMessage :reply-message
|
||||
:chatId :chat-id})
|
||||
(assoc :color (rand-nth colors/chat-colors))
|
||||
(update :last-message #(when % (messages/<-rpc %)))
|
||||
(update :message #(when % (messages/<-rpc %)))
|
||||
(update :reply-message #(when % (messages/<-rpc %)))
|
||||
(dissoc :chatId)))
|
||||
(cond-> (-> item
|
||||
rpc->type
|
||||
(set/rename-keys {:lastMessage :last-message
|
||||
:replyMessage :reply-message
|
||||
:chatId :chat-id
|
||||
:contactVerificationStatus :contact-verification-status})
|
||||
(update :last-message #(when % (messages/<-rpc %)))
|
||||
(update :message #(when % (messages/<-rpc %)))
|
||||
(update :reply-message #(when % (messages/<-rpc %)))
|
||||
(dissoc :chatId))
|
||||
(not config/new-activity-center-enabled?)
|
||||
(assoc :color (rand-nth colors/chat-colors))))
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
(ns status-im.data-store.activities-test
|
||||
(:require [cljs.test :refer [deftest is testing]]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.data-store.activities :as store]
|
||||
[status-im.utils.config :as config]))
|
||||
|
||||
(def chat-id
|
||||
"0x04c66155")
|
||||
|
||||
(def chat-name
|
||||
"0x04c661")
|
||||
|
||||
(def raw-notification
|
||||
{:chatId chat-id
|
||||
:contactVerificationStatus constants/contact-verification-state-pending
|
||||
:lastMessage {}
|
||||
:name chat-name
|
||||
:replyMessage {}})
|
||||
|
||||
(deftest <-rpc-test
|
||||
(with-redefs [config/new-activity-center-enabled? true]
|
||||
(testing "renames keys"
|
||||
(is (= {:name chat-name
|
||||
:chat-id chat-id
|
||||
:contact-verification-status constants/contact-verification-state-pending}
|
||||
(-> raw-notification
|
||||
store/<-rpc
|
||||
(dissoc :last-message :message :reply-message)))))
|
||||
|
||||
(testing "transforms messages from RPC response"
|
||||
(is (= {:last-message {:quoted-message nil
|
||||
:outgoing-status nil
|
||||
:command-parameters nil
|
||||
:content {:sticker nil
|
||||
:rtl? nil
|
||||
:ens-name nil
|
||||
:parsed-text nil
|
||||
:response-to nil
|
||||
:chat-id nil
|
||||
:image nil
|
||||
:line-count nil
|
||||
:links nil
|
||||
:text nil}
|
||||
:outgoing false}
|
||||
:message nil
|
||||
:reply-message {:quoted-message nil
|
||||
:outgoing-status nil
|
||||
:command-parameters nil
|
||||
:content {:sticker nil
|
||||
:rtl? nil
|
||||
:ens-name nil
|
||||
:parsed-text nil
|
||||
:response-to nil
|
||||
:chat-id nil
|
||||
:image nil
|
||||
:line-count nil
|
||||
:links nil
|
||||
:text nil}
|
||||
:outgoing false}}
|
||||
(-> raw-notification
|
||||
store/<-rpc
|
||||
(select-keys [:last-message :message :reply-message])))))
|
||||
|
||||
(testing "augments notification based on its type"
|
||||
(is (= {:chat-name chat-name
|
||||
:chat-type constants/private-group-chat-type
|
||||
:name chat-name}
|
||||
(-> raw-notification
|
||||
(assoc :type constants/activity-center-notification-type-reply)
|
||||
store/<-rpc
|
||||
(select-keys [:name :chat-type :chat-name :public? :group-chat]))))
|
||||
|
||||
(is (= {:chat-name chat-name
|
||||
:chat-type constants/private-group-chat-type
|
||||
:name chat-name}
|
||||
(-> raw-notification
|
||||
(assoc :type constants/activity-center-notification-type-mention)
|
||||
store/<-rpc
|
||||
(select-keys [:name :chat-type :chat-name :public? :group-chat]))))
|
||||
|
||||
(is (= {:chat-name chat-name
|
||||
:chat-type constants/private-group-chat-type
|
||||
:group-chat true
|
||||
:name chat-name
|
||||
:public? false}
|
||||
(-> raw-notification
|
||||
(assoc :type constants/activity-center-notification-type-private-group-chat)
|
||||
store/<-rpc
|
||||
(select-keys [:name :chat-type :chat-name :public? :group-chat]))))
|
||||
|
||||
(is (= {:chat-name chat-name
|
||||
:chat-type constants/one-to-one-chat-type
|
||||
:group-chat false
|
||||
:name chat-name
|
||||
:public? false}
|
||||
(-> raw-notification
|
||||
(assoc :type constants/activity-center-notification-type-one-to-one-chat)
|
||||
store/<-rpc
|
||||
(select-keys [:name :chat-type :chat-name :public? :group-chat])))))))
|
||||
@@ -20,6 +20,7 @@
|
||||
(clojure.set/rename-keys {:id :message-id
|
||||
:whisperTimestamp :whisper-timestamp
|
||||
:editedAt :edited-at
|
||||
:contactVerificationState :contact-verification-state
|
||||
:contactRequestState :contact-request-state
|
||||
:commandParameters :command-parameters
|
||||
:gapParameters :gap-parameters
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
:response-to "a"
|
||||
:links nil}
|
||||
:whisper-timestamp 1
|
||||
:contact-verification-state 1
|
||||
:contact-request-state 2
|
||||
:outgoing-status :sending
|
||||
:command-parameters nil
|
||||
:outgoing true
|
||||
@@ -35,6 +37,8 @@
|
||||
:whisperTimestamp 1
|
||||
:parsedText "parsed-text"
|
||||
:ensName "ens-name"
|
||||
:contactVerificationState 1
|
||||
:contactRequestState 2
|
||||
:localChatId chat-id
|
||||
:from from
|
||||
:text "hta"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
[clojure.string :as string]
|
||||
[re-frame.core :as rf]
|
||||
status-im.events
|
||||
[status-im.chat.models :as chat.models]
|
||||
[status-im.utils.security :as security]
|
||||
[status-im.multiaccounts.logout.core :as logout]
|
||||
[status-im.transport.core :as transport]
|
||||
@@ -231,5 +232,65 @@
|
||||
(logout!) (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in an inconsistent state between tests
|
||||
(assert-logout))))))))
|
||||
|
||||
(deftest delete-chat-test
|
||||
(log/info "========= delete-chat-test ==================")
|
||||
(rf-test/run-test-async
|
||||
(initialize-app!)
|
||||
(rf-test/wait-for
|
||||
[:status-im.init.core/initialize-view]
|
||||
(generate-and-derive-addresses!)
|
||||
(rf-test/wait-for
|
||||
[:multiaccount-generate-and-derive-addresses-success] ; wait for the keys
|
||||
(assert-multiaccount-loaded)
|
||||
(create-multiaccount!)
|
||||
(rf-test/wait-for
|
||||
[::transport/messenger-started]
|
||||
(assert-messenger-started)
|
||||
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
|
||||
(rf-test/wait-for
|
||||
[:status-im.chat.models/one-to-one-chat-created]
|
||||
(rf/dispatch-sync [:chat.ui/navigate-to-chat chat-id])
|
||||
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
|
||||
(is @(rf/subscribe [:chats/chat chat-id]))
|
||||
(rf/dispatch-sync [:chat.ui/remove-chat-pressed chat-id])
|
||||
(rf/dispatch-sync [:chat.ui/remove-chat chat-id])
|
||||
(rf-test/wait-for
|
||||
[::chat.models/chat-deactivated]
|
||||
(is (not @(rf/subscribe [:chats/chat chat-id])))
|
||||
(logout!) (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in an inconsistent state between tests
|
||||
(assert-logout)))))))))
|
||||
|
||||
(deftest mute-chat-test
|
||||
(log/info "========= mute-chat-test ==================")
|
||||
(rf-test/run-test-async
|
||||
(initialize-app!)
|
||||
(rf-test/wait-for
|
||||
[:status-im.init.core/initialize-view]
|
||||
(generate-and-derive-addresses!)
|
||||
(rf-test/wait-for
|
||||
[:multiaccount-generate-and-derive-addresses-success] ; wait for the keys
|
||||
(assert-multiaccount-loaded)
|
||||
(create-multiaccount!)
|
||||
(rf-test/wait-for
|
||||
[::transport/messenger-started]
|
||||
(assert-messenger-started)
|
||||
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
|
||||
(rf-test/wait-for
|
||||
[:status-im.chat.models/one-to-one-chat-created]
|
||||
(rf/dispatch-sync [:chat.ui/navigate-to-chat chat-id])
|
||||
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
|
||||
(is @(rf/subscribe [:chats/chat chat-id]))
|
||||
(rf/dispatch-sync [::chat.models/mute-chat-toggled chat-id true])
|
||||
(rf-test/wait-for
|
||||
[::chat.models/mute-chat-toggled-successfully]
|
||||
(is @(rf/subscribe [:chats/muted chat-id]))
|
||||
(rf/dispatch-sync [::chat.models/mute-chat-toggled chat-id false])
|
||||
(rf-test/wait-for
|
||||
[::chat.models/mute-chat-toggled-successfully]
|
||||
|
||||
(is (not @(rf/subscribe [:chats/muted chat-id])))
|
||||
(logout!) (rf-test/wait-for [::logout/logout-method] ; we need to logout to make sure the node is not in an inconsistent state between tests
|
||||
(assert-logout))))))))))
|
||||
|
||||
(comment
|
||||
(run-tests))
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
name)]
|
||||
(cond-> {:nickname nickname
|
||||
:three-words-name (or alias (gfycat/generate-gfy public-key))}
|
||||
;; Preferred name is our own otherwise we make sure it's verified
|
||||
;; Preferred name is our own otherwise we make sure it's verified
|
||||
(or preferred-name (and ens-verified name))
|
||||
(assoc :ens-name (str "@" (or (stateofus/username ens-name) ens-name))))))
|
||||
|
||||
|
||||
@@ -468,7 +468,7 @@
|
||||
"Decides which root should be initialised depending on user and app state"
|
||||
[db]
|
||||
(if (get db :tos/accepted?)
|
||||
(re-frame/dispatch [:init-root (if config/new-ui-enabled? :home-stack :chat-stack)])
|
||||
(re-frame/dispatch [:init-root (if config/new-ui-enabled? :shell-stack :chat-stack)])
|
||||
(re-frame/dispatch [:init-root :tos])))
|
||||
|
||||
(fx/defn login-only-events
|
||||
@@ -517,7 +517,7 @@
|
||||
(logging/set-log-level (:log-level multiaccount))
|
||||
|
||||
(if config/new-ui-enabled?
|
||||
(navigation/init-root :home-stack)
|
||||
(navigation/init-root :shell-stack)
|
||||
;; if it's a first account, the ToS will be accepted at welcome carousel
|
||||
;; if not a first account, the ToS might have been accepted by other account logins
|
||||
(if (or first-account? tos-accepted?)
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
[status-im.reloader :as reloader]
|
||||
[status-im.utils.datetime :as datetime]))
|
||||
|
||||
(def parent-stack (atom :home-stack))
|
||||
(def parent-stack (atom :shell-stack))
|
||||
|
||||
(fx/defn reload-new-ui
|
||||
{:events [:reload-new-ui]}
|
||||
[_]
|
||||
(reloader/reload)
|
||||
{:new-ui/reset-bottom-tabs nil
|
||||
:dispatch [:init-root :home-stack]})
|
||||
:dispatch [:init-root :shell-stack]})
|
||||
|
||||
(fx/defn init-root-nav2
|
||||
{:events [:init-root-nav2]}
|
||||
@@ -27,7 +27,7 @@
|
||||
[_ modal]
|
||||
{:close-modal-fx-nav2 modal})
|
||||
|
||||
(defn navigate-from-home-stack [go-to-view-id id db]
|
||||
(defn navigate-from-shell-stack [go-to-view-id id db]
|
||||
(reset! parent-stack go-to-view-id)
|
||||
{:navigate-to-fx-nav2 [go-to-view-id id]
|
||||
:db (assoc-in db [:navigation2/navigation2-stacks id] {:type go-to-view-id
|
||||
@@ -49,8 +49,12 @@
|
||||
(if from-switcher?
|
||||
(navigate-from-switcher go-to-view-id id db from-home?)
|
||||
(if from-home?
|
||||
(navigate-from-home-stack go-to-view-id id db)
|
||||
(navigate-from-shell-stack go-to-view-id id db)
|
||||
;; TODO(parvesh) - new stacks created from other screens should be stacked on current stack, instead of creating new entry
|
||||
(navigate-from-home-stack go-to-view-id id db)))))
|
||||
(navigate-from-shell-stack go-to-view-id id db)))))
|
||||
|
||||
(fx/defn change-root-status-bar-style
|
||||
{:events [:change-root-status-bar-style]}
|
||||
[_ style]
|
||||
{:change-root-status-bar-style-fx style})
|
||||
|
||||
|
||||
@@ -13,8 +13,10 @@
|
||||
:wallet 2
|
||||
:browser 3})
|
||||
|
||||
;; (defonce set-navigation-default-options
|
||||
;; (.setDefaultOptions Navigation (clj->js {:options {:topBar {:visible false}}})))
|
||||
(defn change-root-status-bar-style [style]
|
||||
(.mergeOptions Navigation
|
||||
"shell-stack"
|
||||
(clj->js {:statusBar {:style style}})))
|
||||
|
||||
;; TODO (parvesh) - improve open-modal and close-modal
|
||||
(defn open-modal [comp]
|
||||
@@ -55,7 +57,7 @@
|
||||
(let [{:keys [options]} (get views/screens comp)]
|
||||
(reset! nav2-utils/container-stack-view-id comp)
|
||||
(.push Navigation
|
||||
(name :home-stack)
|
||||
"shell-stack"
|
||||
(clj->js {:stack {:id comp
|
||||
:children [{:component {:id comp
|
||||
:name comp
|
||||
@@ -79,3 +81,5 @@
|
||||
(re-frame/reg-fx :navigate-to-fx-nav2 navigate)
|
||||
|
||||
(re-frame/reg-fx :navigate-from-switcher-fx navigate-from-switcher)
|
||||
|
||||
(re-frame/reg-fx :change-root-status-bar-style-fx change-root-status-bar-style)
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
(ns status-im.navigation2.roots
|
||||
(:require [quo.theme :as theme]
|
||||
[quo2.foundations.colors :as colors]
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[status-im.utils.platform :as platform]))
|
||||
|
||||
(defn status-bar-options []
|
||||
(if platform/android?
|
||||
{:navigationBar {:backgroundColor colors/neutral-80}
|
||||
{:navigationBar {:backgroundColor colors/neutral-100}
|
||||
:statusBar {:backgroundColor :transparent
|
||||
:style (if (theme/dark?) :light :dark)
|
||||
:style :light
|
||||
:drawBehind true}}
|
||||
{:statusBar {:style (if (theme/dark?) :light :dark)}}))
|
||||
{:statusBar {:style :light}}))
|
||||
|
||||
(defn roots []
|
||||
{:home-stack
|
||||
{:shell-stack
|
||||
{:root
|
||||
{:stack {:id :home-stack
|
||||
{:stack {:id :shell-stack
|
||||
:children [{:component {:name :chat-stack
|
||||
:id :chat-stack
|
||||
:options (merge (status-bar-options)
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
(ns status-im.navigation2.screens
|
||||
(:require [status-im.ui2.screens.chat.view :as chat]
|
||||
[status-im.switcher.home-stack :as home-stack]
|
||||
[status-im.navigation2.stack-with-switcher :as stack-with-switcher]))
|
||||
[status-im.switcher.shell-stack :as shell-stack]))
|
||||
|
||||
;; We have to use the home screen name :chat-stack for now, for compatibility with navigation.cljs
|
||||
(def screens [{:name :chat-stack ;; TODO(parvesh) - rename to home-stack
|
||||
(def screens [{:name :chat-stack ;; TODO(parvesh) - rename to shell-stack
|
||||
:insets {:top false}
|
||||
:component home-stack/home}])
|
||||
:component shell-stack/shell-stack}])
|
||||
|
||||
;; These screens will overwrite navigation/screens.cljs screens on enabling new UI toggle
|
||||
(def screen-overwrites
|
||||
[{:name :chat
|
||||
:options {:topBar {:visible false}}
|
||||
:component #(stack-with-switcher/overlap-stack chat/chat :chat)}])
|
||||
:component chat/chat}])
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
(ns status-im.navigation2.stack-with-switcher
|
||||
(:require [quo.react-native :as rn]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.switcher.switcher :as switcher]))
|
||||
|
||||
(defn overlap-stack [comp view-id]
|
||||
[rn/view {:style {:flex 1
|
||||
:margin-bottom (if platform/ios? 30 0)}}
|
||||
[comp]
|
||||
[switcher/switcher view-id]])
|
||||
@@ -27,6 +27,12 @@
|
||||
vals
|
||||
(sort-by :pinned-at <))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/pin-modal
|
||||
:<- [:messages/pin-modal]
|
||||
(fn [pin-modal [_ chat-id]]
|
||||
(get pin-modal chat-id)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/message-reactions
|
||||
:<- [:multiaccount/public-key]
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
(reg-root-key-sub :messages/pagination-info :pagination-info)
|
||||
(reg-root-key-sub :messages/pin-message-lists :pin-message-lists)
|
||||
(reg-root-key-sub :messages/pin-messages :pin-messages)
|
||||
(reg-root-key-sub :messages/pin-modal :pin-modal)
|
||||
|
||||
;;browser
|
||||
(reg-root-key-sub :browsers :browser/browsers)
|
||||
@@ -252,4 +253,4 @@
|
||||
|
||||
(reg-root-key-sub :messenger/started? :messenger/started?)
|
||||
|
||||
(reg-root-key-sub :information-box-states :information-box-states)
|
||||
(reg-root-key-sub :information-box-states :information-box-states)
|
||||
|
||||
@@ -1,65 +1,108 @@
|
||||
(ns status-im.switcher.animation
|
||||
(:require [quo2.reanimated :as reanimated]
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[quo2.reanimated :as reanimated]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.async-storage.core :as async-storage]
|
||||
[status-im.switcher.constants :as constants]))
|
||||
|
||||
;;;; Switcher Animations
|
||||
|
||||
;; Component Animations
|
||||
(defn switcher-touchable-on-press-in
|
||||
[touchable-scale]
|
||||
(reanimated/animate-shared-value-with-timing touchable-scale constants/switcher-pressed-scale 300 :easing1))
|
||||
|
||||
(defn switcher-touchable-on-press-out [switcher-opened? view-id shared-values]
|
||||
(let [{:keys [width height]} (constants/dimensions)
|
||||
switcher-bottom-position (constants/switcher-pressed-bottom-position view-id)
|
||||
switcher-target-radius (Math/hypot
|
||||
(/ width 2)
|
||||
(- height constants/switcher-pressed-radius switcher-bottom-position))
|
||||
switcher-size (* 2 switcher-target-radius)]
|
||||
(reanimated/animate-shared-value-with-timing (:button-touchable-scale shared-values) 1 300 :easing1)
|
||||
(if @switcher-opened?
|
||||
(do
|
||||
(reanimated/animate-shared-value-with-timing (:switcher-button-opacity shared-values) 1 300 :easing1)
|
||||
(reanimated/animate-shared-value-with-timing (:switcher-screen-size shared-values) constants/switcher-pressed-size 300 :linear)
|
||||
(reanimated/animate-shared-value-with-timing (:switcher-container-scale shared-values) 0.9 300 :linear))
|
||||
(do
|
||||
(reanimated/animate-shared-value-with-timing (:switcher-button-opacity shared-values) 0 300 :easing1)
|
||||
(reanimated/animate-shared-value-with-timing (:switcher-screen-size shared-values) switcher-size 300 :linear)
|
||||
(reanimated/animate-shared-value-with-timing (:switcher-container-scale shared-values) 1 300 :linear)))
|
||||
(swap! switcher-opened? not)))
|
||||
|
||||
;; Derived Values
|
||||
|
||||
(defn switcher-close-button-opacity [switcher-button-opacity]
|
||||
(.switcherCloseButtonOpacity ^js reanimated/worklet-factory switcher-button-opacity))
|
||||
|
||||
(defn switcher-screen-radius [switcher-screen-size]
|
||||
(.switcherScreenRadius ^js reanimated/worklet-factory switcher-screen-size))
|
||||
|
||||
(defn switcher-screen-bottom-position [switcher-screen-radius view-id]
|
||||
(.switcherScreenBottomPosition ^js reanimated/worklet-factory
|
||||
switcher-screen-radius
|
||||
constants/switcher-pressed-radius
|
||||
(constants/switcher-pressed-bottom-position view-id)))
|
||||
|
||||
(defn switcher-container-bottom-position [switcher-screen-bottom]
|
||||
(.switcherContainerBottomPosition ^js reanimated/worklet-factory
|
||||
switcher-screen-bottom
|
||||
(+ constants/switcher-container-height-padding
|
||||
constants/switcher-height-offset)))
|
||||
|
||||
|
||||
;;;; Bottom Tabs & Home Stack Animations
|
||||
|
||||
(def selected-stack-id (atom nil))
|
||||
(def home-stack-open? (atom false))
|
||||
(def pass-through? (atom false))
|
||||
|
||||
(def bottom-nav-tab-width 90)
|
||||
|
||||
(defn selected-stack-id-loaded [stack-id]
|
||||
(reset! selected-stack-id stack-id)
|
||||
(reset! home-stack-open? (some? stack-id)))
|
||||
|
||||
(defn calculate-home-stack-position []
|
||||
(let [{:keys [width height]} (constants/dimensions)
|
||||
minimize-scale (/ bottom-nav-tab-width width)
|
||||
empty-space-half-scale (/ (- 1 minimize-scale) 2)
|
||||
left-margin (/ (- width (* 4 bottom-nav-tab-width)) 2)
|
||||
left-empty-space (* empty-space-half-scale width)
|
||||
top-empty-space (* empty-space-half-scale
|
||||
(- height (constants/bottom-tabs-container-height)))]
|
||||
{:left (reduce
|
||||
(fn [acc stack-id]
|
||||
(assoc acc stack-id (+ (- left-margin left-empty-space)
|
||||
(* (.indexOf constants/stacks-ids stack-id)
|
||||
bottom-nav-tab-width))))
|
||||
{:none 0} constants/stacks-ids)
|
||||
:top (+ top-empty-space (constants/bottom-tabs-container-height))
|
||||
:scale minimize-scale}))
|
||||
|
||||
(defn get-shared-values []
|
||||
(let [selected-stack-id-sv (reanimated/use-shared-value
|
||||
;; passing keywords or nil is not working with reanimated
|
||||
(name (if @selected-stack-id @selected-stack-id :none)))
|
||||
;; Second shared value of selected-stack-id required to make sure stack is still visible while minimizing
|
||||
selected-stack-id-sv2 (reanimated/use-shared-value
|
||||
(name (if @selected-stack-id @selected-stack-id :none)))
|
||||
pass-through-sv (reanimated/use-shared-value @pass-through?)
|
||||
home-stack-open-sv (reanimated/use-shared-value @home-stack-open?)
|
||||
animate-home-stack-left (reanimated/use-shared-value (not @home-stack-open?))
|
||||
home-stack-position (calculate-home-stack-position)]
|
||||
(reduce
|
||||
(fn [acc id]
|
||||
(let [tabs-icon-color-keyword (get constants/tabs-icon-color-keywords id)
|
||||
stack-opacity-keyword (get constants/stacks-opacity-keywords id)
|
||||
stack-pointer-keyword (get constants/stacks-pointer-keywords id)]
|
||||
(assoc
|
||||
acc
|
||||
stack-opacity-keyword (.stackOpacity
|
||||
^js reanimated/worklet-factory
|
||||
(name id) selected-stack-id-sv2)
|
||||
stack-pointer-keyword (.stackPointer
|
||||
^js reanimated/worklet-factory
|
||||
(name id) selected-stack-id-sv2)
|
||||
tabs-icon-color-keyword (.bottomTabIconColor
|
||||
^js reanimated/worklet-factory
|
||||
(name id) selected-stack-id-sv pass-through-sv
|
||||
colors/white colors/neutral-50 colors/white-opa-40))))
|
||||
{:selected-stack-id selected-stack-id-sv
|
||||
:selected-stack-id2 selected-stack-id-sv2
|
||||
:pass-through? pass-through-sv
|
||||
:home-stack-open? home-stack-open-sv
|
||||
:animate-home-stack-left animate-home-stack-left
|
||||
:home-stack-left (.homeStackLeft
|
||||
^js reanimated/worklet-factory
|
||||
selected-stack-id-sv2 animate-home-stack-left home-stack-open-sv
|
||||
(clj->js (:left home-stack-position)))
|
||||
:home-stack-top (.homeStackTop
|
||||
^js reanimated/worklet-factory
|
||||
home-stack-open-sv (:top home-stack-position))
|
||||
:home-stack-opacity (.homeStackOpacity
|
||||
^js reanimated/worklet-factory home-stack-open-sv)
|
||||
:home-stack-pointer (.homeStackPointer
|
||||
^js reanimated/worklet-factory home-stack-open-sv)
|
||||
:home-stack-scale (.homeStackScale
|
||||
^js reanimated/worklet-factory home-stack-open-sv
|
||||
(:scale home-stack-position))}
|
||||
constants/stacks-ids)))
|
||||
|
||||
;; Animation
|
||||
|
||||
(defn change-tab [shared-values stack-id]
|
||||
(when-not (colors/dark?)
|
||||
(js/setTimeout #(re-frame/dispatch [:change-root-status-bar-style :dark]) 300))
|
||||
(if @home-stack-open?
|
||||
(reanimated/set-shared-value (:animate-home-stack-left shared-values) false)
|
||||
(reset! home-stack-open? true))
|
||||
(reset! selected-stack-id stack-id)
|
||||
(reanimated/set-shared-value (:selected-stack-id2 shared-values) (name stack-id))
|
||||
(reanimated/set-shared-value (:selected-stack-id shared-values) (name stack-id))
|
||||
(reanimated/set-shared-value (:home-stack-open? shared-values) true)
|
||||
(async-storage/set-item! :selected-stack-id stack-id))
|
||||
|
||||
(defn close-home-stack [shared-values]
|
||||
(re-frame/dispatch [:change-root-status-bar-style :light])
|
||||
(reanimated/set-shared-value (:animate-home-stack-left shared-values) true)
|
||||
(reset! home-stack-open? false)
|
||||
(reset! selected-stack-id nil)
|
||||
(reanimated/set-shared-value (:home-stack-open? shared-values) false)
|
||||
(reanimated/set-shared-value (:selected-stack-id shared-values) "none")
|
||||
(async-storage/set-item! :selected-stack-id nil))
|
||||
|
||||
(defn bottom-tab-on-press [shared-values selected-stack-id]
|
||||
(doseq [id constants/stacks-ids]
|
||||
(let [selected-tab? (= id selected-stack-id)
|
||||
tab-opacity-shared-value (get shared-values (get constants/tabs-opacity-keywords id))
|
||||
stack-opacity-shared-value (get shared-values (get constants/stacks-opacity-keywords id))
|
||||
stack-pointer-shared-value (get shared-values (get constants/stacks-pointer-keywords id))]
|
||||
(reanimated/animate-shared-value-with-timing tab-opacity-shared-value (if selected-tab? 1 0) 300 :easing3)
|
||||
(reanimated/set-shared-value stack-pointer-shared-value (if selected-tab? "auto" "none"))
|
||||
(if selected-tab?
|
||||
(reanimated/animate-shared-value-with-delay stack-opacity-shared-value 1 300 :easing3 150)
|
||||
(reanimated/animate-shared-value-with-timing stack-opacity-shared-value 0 300 :easing3)))))
|
||||
|
||||
@@ -2,66 +2,58 @@
|
||||
(:require [quo.react-native :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[re-frame.core :as re-frame]
|
||||
[quo2.reanimated :as reanimated]
|
||||
[status-im.switcher.styles :as styles]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.switcher.constants :as constants]
|
||||
[status-im.switcher.animation :as animation]
|
||||
[quo2.components.icon :as icons]))
|
||||
|
||||
(def selected-stack-id (atom :communities-stack))
|
||||
[quo2.components.navigation.bottom-nav-tab :as bottom-nav-tab]))
|
||||
|
||||
;; Reagent atoms used for lazily loading home screen tabs
|
||||
(def load-communities-tab? (reagent/atom true))
|
||||
(def load-communities-tab? (reagent/atom false))
|
||||
(def load-chats-tab? (reagent/atom false))
|
||||
(def load-wallet-tab? (reagent/atom false))
|
||||
(def load-browser-tab? (reagent/atom false))
|
||||
|
||||
(defn load-selected-stack [stack-id]
|
||||
(case stack-id
|
||||
:communities-stack (reset! load-communities-tab? true)
|
||||
:chats-stack (reset! load-chats-tab? true)
|
||||
:wallet-stack (reset! load-wallet-tab? true)
|
||||
:browser-stack (reset! load-browser-tab? true)
|
||||
""))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:new-ui/reset-bottom-tabs
|
||||
(fn []
|
||||
(reset! selected-stack-id :communities-stack)
|
||||
(reset! load-communities-tab? true)
|
||||
(reset! load-chats-tab? false)
|
||||
(reset! load-wallet-tab? false)
|
||||
(reset! load-browser-tab? false)))
|
||||
(let [selected-stack-id @animation/selected-stack-id]
|
||||
(reset! load-communities-tab? (= selected-stack-id :communities-stack))
|
||||
(reset! load-chats-tab? (= selected-stack-id :chats-stack))
|
||||
(reset! load-wallet-tab? (= selected-stack-id :wallet-stack))
|
||||
(reset! load-browser-tab? (= selected-stack-id :browser-stack)))))
|
||||
|
||||
(defn bottom-tab-on-press [shared-values stack-id]
|
||||
(when-not (= stack-id @selected-stack-id)
|
||||
(reset! selected-stack-id stack-id)
|
||||
(animation/bottom-tab-on-press shared-values stack-id)
|
||||
(case stack-id
|
||||
:communities-stack (reset! load-communities-tab? true)
|
||||
:chats-stack (reset! load-chats-tab? true)
|
||||
:wallet-stack (reset! load-wallet-tab? true)
|
||||
:browser-stack (reset! load-browser-tab? true))))
|
||||
(when-not (= stack-id @animation/selected-stack-id)
|
||||
(let [stack-load-delay (cond
|
||||
@animation/home-stack-open? 0
|
||||
platform/android? 250
|
||||
:else 300)]
|
||||
(animation/change-tab shared-values stack-id)
|
||||
(js/setTimeout #(load-selected-stack stack-id) stack-load-delay))))
|
||||
|
||||
;; TODO(parvesh) - reimplement tab with counter, once design is complete
|
||||
(defn bottom-tab [icon stack-id icons-only? shared-values]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [bottom-tab-original-style {:padding 16}]
|
||||
(if icons-only?
|
||||
[rn/touchable-opacity {:active-opacity 1
|
||||
:style bottom-tab-original-style
|
||||
:on-press #(bottom-tab-on-press shared-values stack-id)}
|
||||
[reanimated/view {:style (reanimated/apply-animations-to-style
|
||||
{:opacity (get
|
||||
shared-values
|
||||
(get constants/tabs-opacity-keywords stack-id))}
|
||||
{})}
|
||||
[icons/icon icon (styles/bottom-tab-icon :bottom-tabs-selected-tab)]]]
|
||||
[rn/view {:style bottom-tab-original-style}
|
||||
[icons/icon icon (styles/bottom-tab-icon :bottom-tabs-non-selected-tab)]])))])
|
||||
|
||||
(defn tabs [shared-values icons-only?]
|
||||
[rn/view {:style (styles/bottom-tabs icons-only?)}
|
||||
[bottom-tab :main-icons2/communities :communities-stack icons-only? shared-values]
|
||||
[bottom-tab :main-icons2/messages :chats-stack icons-only? shared-values]
|
||||
[rn/view {:width 50}]
|
||||
[bottom-tab :main-icons2/wallet :wallet-stack icons-only? shared-values]
|
||||
[bottom-tab :main-icons2/browser :browser-stack icons-only? shared-values]])
|
||||
(defn bottom-tab [icon stack-id shared-values]
|
||||
[bottom-nav-tab/bottom-nav-tab
|
||||
{:icon icon
|
||||
:icon-color-anim (get
|
||||
shared-values
|
||||
(get constants/tabs-icon-color-keywords stack-id))
|
||||
:on-press #(bottom-tab-on-press shared-values stack-id)
|
||||
:accessibility-label (str (name stack-id) "-tab")}])
|
||||
|
||||
(defn bottom-tabs [shared-values]
|
||||
[:<>
|
||||
[tabs shared-values false]
|
||||
[tabs shared-values true]])
|
||||
(load-selected-stack @animation/selected-stack-id)
|
||||
[rn/view {:style (styles/bottom-tabs-container false)}
|
||||
[rn/view {:style (styles/bottom-tabs)}
|
||||
[bottom-tab :main-icons2/communities :communities-stack shared-values]
|
||||
[bottom-tab :main-icons2/messages :chats-stack shared-values]
|
||||
[bottom-tab :main-icons2/wallet :wallet-stack shared-values]
|
||||
[bottom-tab :main-icons2/browser :browser-stack shared-values]]])
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
(ns status-im.switcher.cards.messaging-card
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[status-im.constants :as constants]
|
||||
[quo2.components.buttons.button :as button]
|
||||
[status-im.utils.handlers :refer [>evt <sub]]
|
||||
[status-im.switcher.cards.styles :as styles]))
|
||||
|
||||
;; TODO - Add switcher close animation (fade) while opening screen from cards
|
||||
;; currently dealy is added to avoid default circular animation
|
||||
(defn on-press [id toggle-switcher-screen]
|
||||
(js/setTimeout toggle-switcher-screen 100)
|
||||
(>evt [:chat.ui/navigate-to-chat-nav2 id true]))
|
||||
|
||||
;; TODO - add last message for other content types
|
||||
(defn last-message [{:keys [content content-type]}]
|
||||
(cond
|
||||
(= constants/content-type-text content-type)
|
||||
[text/text (styles/messaging-card-last-message-text-props) (:text content)]))
|
||||
|
||||
(defn card [{:keys [id toggle-switcher-screen]}]
|
||||
(let [chat (<sub [:chats/chat id])]
|
||||
[rn/touchable-without-feedback {:on-press #(on-press id toggle-switcher-screen)}
|
||||
[rn/view {:style (styles/messaging-card-main-container)}
|
||||
[rn/view {:style (styles/messaging-card-secondary-container)}
|
||||
[text/text (styles/messaging-card-title-props) (:alias chat)]
|
||||
[text/text (styles/messaging-card-subtitle-props) "Message"]
|
||||
[rn/view {:style (styles/messaging-card-details-container)}
|
||||
[last-message (:last-message chat)]]]
|
||||
[rn/view {:style (styles/messaging-card-avatar-container)}]
|
||||
[button/button (styles/messaging-card-close-button-props) :main-icons/close]]]))
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
(ns status-im.switcher.cards.styles
|
||||
(:require [quo.theme :as theme]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(def themes
|
||||
{:light {:messaging-card-container-background-color "#26A69A"
|
||||
:messaging-card-secondary-container-background-color colors/white
|
||||
:messaging-card-title-color colors/neutral-100
|
||||
:messaging-card-subtitle-color colors/neutral-50
|
||||
:messaging-card-last-message-text-color colors/neutral-100
|
||||
:messaging-card-close-button-bg-color colors/white-opa-50
|
||||
:messaging-card-close-button-icon-color colors/neutral-100}
|
||||
:dark {:messaging-card-container-background-color "#26A69A"
|
||||
:messaging-card-secondary-container-background-color colors/neutral-90
|
||||
:messaging-card-title-color colors/white
|
||||
:messaging-card-subtitle-color colors/neutral-40
|
||||
:messaging-card-last-message-text-color colors/white
|
||||
:messaging-card-close-button-bg-color colors/neutral-80-opa-60
|
||||
:messaging-card-close-button-icon-color colors/white}})
|
||||
|
||||
(defn get-color [key]
|
||||
(get-in themes [(theme/get-theme) key]))
|
||||
|
||||
;; Messaging Card
|
||||
|
||||
(defn messaging-card-main-container []
|
||||
{:width 160
|
||||
:height 172
|
||||
:border-radius 16
|
||||
:margin 8
|
||||
:background-color (get-color :messaging-card-container-background-color)})
|
||||
|
||||
(defn messaging-card-secondary-container []
|
||||
{:width 160
|
||||
:height 132
|
||||
:background-color (get-color :messaging-card-secondary-container-background-color)
|
||||
:border-radius 16
|
||||
:position :absolute
|
||||
:bottom 0})
|
||||
|
||||
(defn messaging-card-title []
|
||||
{:position :absolute
|
||||
:top 32
|
||||
:margin-horizontal 12
|
||||
:color (get-color :messaging-card-title-color)})
|
||||
|
||||
(defn messaging-card-title-props []
|
||||
{:size :paragraph-1
|
||||
:weight :semi-bold
|
||||
:number-of-lines 1
|
||||
:ellipsize-mode :tail
|
||||
:style (messaging-card-title)})
|
||||
|
||||
(defn messaging-card-subtitle []
|
||||
{:position :absolute
|
||||
:top 54
|
||||
:margin-horizontal 12
|
||||
:color (get-color :messaging-card-subtitle-color)})
|
||||
|
||||
(defn messaging-card-subtitle-props []
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style (messaging-card-subtitle)})
|
||||
|
||||
(defn messaging-card-details-container []
|
||||
{:position :absolute
|
||||
:bottom 12
|
||||
:margin-horizontal 12
|
||||
:width 136
|
||||
:height 36})
|
||||
|
||||
(defn messaging-card-last-message-text []
|
||||
{:color (get-color :messaging-card-last-message-text-color)})
|
||||
|
||||
(defn messaging-card-last-message-text-props []
|
||||
{:size :paragraph-2
|
||||
:weight :regular
|
||||
:number-of-lines 2
|
||||
:ellipsize-mode :tail
|
||||
:style (messaging-card-last-message-text)})
|
||||
|
||||
(defn messaging-card-close-button []
|
||||
{:position :absolute
|
||||
:right 8
|
||||
:top 8
|
||||
:background-color (get-color :messaging-card-close-button-bg-color)
|
||||
:icon-color (get-color :messaging-card-icon-color)})
|
||||
|
||||
(defn messaging-card-close-button-props []
|
||||
{:size 24
|
||||
:type :grey
|
||||
:icon true
|
||||
:on-press #(print "close pressed")
|
||||
:style (messaging-card-close-button)})
|
||||
|
||||
(defn messaging-card-avatar-container []
|
||||
{:width 48
|
||||
:height 48
|
||||
:border-radius 24
|
||||
:position :absolute
|
||||
:left 12
|
||||
:top 16
|
||||
:background-color :pink})
|
||||
@@ -1,57 +1,26 @@
|
||||
(ns status-im.switcher.constants
|
||||
(:require [quo.react-native :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.utils.handlers :refer [<sub]]
|
||||
[status-im.utils.platform :as platform]))
|
||||
|
||||
;; For translucent status bar(android), dimensions/window also includes status bar's height,
|
||||
;; this offset is used for correctly calculating switcher position
|
||||
(def switcher-height-offset
|
||||
(if platform/android? (:status-bar-height @rn/navigation-const) 0))
|
||||
(defn bottom-tabs-container-height []
|
||||
(if platform/android? 57 82))
|
||||
|
||||
;; extra height of switcher container for show/peek hidden cards while opening animation
|
||||
(def switcher-container-height-padding 100)
|
||||
(defn bottom-tabs-extended-container-height []
|
||||
(if platform/android? 90 120))
|
||||
|
||||
(def switcher-button-radius 24)
|
||||
|
||||
(def switcher-button-size
|
||||
(* switcher-button-radius 2))
|
||||
|
||||
(def switcher-pressed-scale 0.9)
|
||||
|
||||
(def switcher-pressed-radius
|
||||
(* switcher-pressed-scale switcher-button-radius))
|
||||
|
||||
(def switcher-pressed-size
|
||||
(* 2 switcher-pressed-radius))
|
||||
|
||||
(def switcher-bottom-positions
|
||||
{:android
|
||||
{:home-stack 15
|
||||
:chat 140}
|
||||
:ios
|
||||
{:home-stack 40
|
||||
:chat 140}})
|
||||
|
||||
(defn switcher-bottom-position [view-id]
|
||||
(get-in
|
||||
switcher-bottom-positions
|
||||
[(keyword platform/os) view-id]))
|
||||
|
||||
(defn switcher-pressed-bottom-position [view-id]
|
||||
(+
|
||||
(get-in
|
||||
switcher-bottom-positions
|
||||
[(keyword platform/os) view-id])
|
||||
(- switcher-button-radius switcher-pressed-radius)))
|
||||
|
||||
;; TODO(parvesh) - use different height for android and ios(Confirm from Design)
|
||||
(defn bottom-tabs-height []
|
||||
(if platform/android? 55 80))
|
||||
(def status-bar-offset
|
||||
(if platform/android? (.-currentHeight ^js rn/status-bar) 0))
|
||||
|
||||
;; status bar height is not included in : the dimensions/window for devices with a notch
|
||||
;; https://github.com/facebook/react-native/issues/23693#issuecomment-662860819
|
||||
(defn dimensions []
|
||||
(let [{:keys [width height]} (<sub [:dimensions/window])]
|
||||
{:width width
|
||||
:height (+ height switcher-height-offset)}))
|
||||
:height (if (> status-bar-offset 28)
|
||||
(+ height status-bar-offset)
|
||||
height)}))
|
||||
|
||||
(def stacks-ids [:communities-stack :chats-stack :wallet-stack :browser-stack])
|
||||
|
||||
@@ -67,8 +36,10 @@
|
||||
:wallet-stack :wallet-stack-pointer
|
||||
:browser-stack :browser-stack-pointer})
|
||||
|
||||
(def tabs-opacity-keywords
|
||||
{:communities-stack :communities-tab-opacity
|
||||
:chats-stack :chats-tab-opacity
|
||||
:wallet-stack :wallet-tab-opacity
|
||||
:browser-stack :browser-tab-opacity})
|
||||
(def tabs-icon-color-keywords
|
||||
{:communities-stack :communities-tab-icon-color
|
||||
:chats-stack :chats-tab-icon-opacity
|
||||
:wallet-stack :wallet-tab-icon-opacity
|
||||
:browser-stack :browser-tab-icon-opacity})
|
||||
|
||||
(def pass-through? (reagent/atom false))
|
||||
|
||||
@@ -1,33 +1,36 @@
|
||||
(ns status-im.switcher.home-stack
|
||||
(:require [quo2.reanimated :as reanimated]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.switcher.switcher :as switcher]
|
||||
[status-im.ui2.screens.chat.home :as chat.home]
|
||||
[status-im.switcher.styles :as styles]
|
||||
[status-im.switcher.animation :as animation]
|
||||
[status-im.switcher.constants :as constants]
|
||||
[status-im.ui2.screens.chat.home :as chat.home]
|
||||
[status-im.switcher.bottom-tabs :as bottom-tabs]
|
||||
[status-im.ui.screens.profile.user.views :as profile.user]
|
||||
[status-im.ui.screens.communities.communities-list-redesign :as communities]
|
||||
[status-im.ui.screens.wallet.accounts.views :as wallet.accounts]))
|
||||
[status-im.ui.screens.wallet.accounts.views :as wallet.accounts]
|
||||
[quo2.components.navigation.floating-shell-button :as floating-shell-button]
|
||||
[status-im.ui.screens.communities.communities-list-redesign :as communities]))
|
||||
|
||||
(defn load-stack? [stack-id]
|
||||
(case stack-id
|
||||
:communities-stack @bottom-tabs/load-communities-tab?
|
||||
:chats-stack @bottom-tabs/load-chats-tab?
|
||||
:browser-stack @bottom-tabs/load-browser-tab?
|
||||
:wallet-stack @bottom-tabs/load-wallet-tab?))
|
||||
:chats-stack @bottom-tabs/load-chats-tab?
|
||||
:browser-stack @bottom-tabs/load-browser-tab?
|
||||
:wallet-stack @bottom-tabs/load-wallet-tab?))
|
||||
|
||||
(defn stack-view [stack-id shared-values]
|
||||
(when (load-stack? stack-id)
|
||||
[:f>
|
||||
(fn []
|
||||
[reanimated/view {:style (reanimated/apply-animations-to-style
|
||||
{:opacity (get shared-values (get constants/stacks-opacity-keywords stack-id))
|
||||
:pointer-events (get shared-values (get constants/stacks-pointer-keywords stack-id))}
|
||||
{:top 0
|
||||
:bottom (if platform/ios? 79 54)
|
||||
:left 0
|
||||
:right 0
|
||||
:position :absolute})}
|
||||
[reanimated/view
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:opacity (get shared-values (get constants/stacks-opacity-keywords stack-id))
|
||||
:pointer-events (get shared-values (get constants/stacks-pointer-keywords stack-id))}
|
||||
{:position :absolute
|
||||
:top 0
|
||||
:bottom 0
|
||||
:left 0
|
||||
:right 0
|
||||
:accessibility-label stack-id})}
|
||||
(case stack-id
|
||||
:communities-stack [communities/communities-list]
|
||||
:chats-stack [chat.home/home]
|
||||
@@ -35,29 +38,22 @@
|
||||
:browser-stack [profile.user/my-profile])])]))
|
||||
|
||||
(defn home-stack [shared-values]
|
||||
[:<>
|
||||
[stack-view :communities-stack shared-values]
|
||||
[stack-view :chats-stack shared-values]
|
||||
[stack-view :browser-stack shared-values]
|
||||
[stack-view :wallet-stack shared-values]])
|
||||
|
||||
(defn home []
|
||||
[:f>
|
||||
(fn []
|
||||
(let [selected-stack-id @bottom-tabs/selected-stack-id
|
||||
shared-values (reduce (fn [acc id]
|
||||
(let [selected-tab? (= id selected-stack-id)
|
||||
tab-opacity-keyword (get constants/tabs-opacity-keywords id)
|
||||
stack-opacity-keyword (get constants/stacks-opacity-keywords id)
|
||||
stack-pointer-keyword (get constants/stacks-pointer-keywords id)]
|
||||
(assoc
|
||||
acc
|
||||
tab-opacity-keyword (reanimated/use-shared-value (if selected-tab? 1 0))
|
||||
stack-opacity-keyword (reanimated/use-shared-value (if selected-tab? 1 0))
|
||||
stack-pointer-keyword (reanimated/use-shared-value (if selected-tab? "auto" "none")))))
|
||||
{}
|
||||
constants/stacks-ids)]
|
||||
[:<>
|
||||
[home-stack shared-values]
|
||||
[bottom-tabs/bottom-tabs shared-values]
|
||||
[switcher/switcher :home-stack]]))])
|
||||
(let [home-stack-original-style (styles/home-stack)
|
||||
home-stack-animated-style (reanimated/apply-animations-to-style
|
||||
{:top (:home-stack-top shared-values)
|
||||
:left (:home-stack-left shared-values)
|
||||
:opacity (:home-stack-opacity shared-values)
|
||||
:pointer-events (:home-stack-pointer shared-values)
|
||||
:transform [{:scale (:home-stack-scale shared-values)}]}
|
||||
home-stack-original-style)]
|
||||
[reanimated/view {:style home-stack-animated-style}
|
||||
[stack-view :communities-stack shared-values]
|
||||
[stack-view :chats-stack shared-values]
|
||||
[stack-view :browser-stack shared-values]
|
||||
[stack-view :wallet-stack shared-values]
|
||||
[floating-shell-button/floating-shell-button
|
||||
{:jump-to {:on-press #(animation/close-home-stack shared-values)}}
|
||||
{:position :absolute
|
||||
:bottom 12}]]))])
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
(ns status-im.switcher.shell
|
||||
(:require [quo.react-native :as rn]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo.components.safe-area :as safe-area]
|
||||
[quo2.components.navigation.top-nav :as top-nav]))
|
||||
|
||||
(defn placeholder []
|
||||
[rn/view {:style {:position :absolute
|
||||
:top 0
|
||||
:left 0
|
||||
:right 0
|
||||
:bottom -1
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:accessibility-label :shell-placeholder-view}}
|
||||
[rn/view {:style {:margin-top 12
|
||||
:width 80
|
||||
:height 80
|
||||
:border-radius 16
|
||||
:background-color colors/neutral-90}}]
|
||||
[text/text {:size :heading-2
|
||||
:weight :semi-bold
|
||||
:style {:margin-top 20
|
||||
:color colors/white}}
|
||||
(i18n/label :t/shell-placeholder-title)]
|
||||
[text/text {:size :paragraph-1
|
||||
:weight :regular
|
||||
:align :center
|
||||
:style {:margin-top 8
|
||||
:color colors/white}}
|
||||
(i18n/label :t/shell-placeholder-subtitle)]])
|
||||
|
||||
(defn shell []
|
||||
[safe-area/consumer
|
||||
(fn [insets]
|
||||
[rn/view {:style {:top 0
|
||||
:left 0
|
||||
:right 0
|
||||
:bottom -1
|
||||
:position :absolute
|
||||
:background-color colors/neutral-100}}
|
||||
[top-nav/top-nav {:type :shell
|
||||
:style {:margin-top (:top insets)}}]
|
||||
[placeholder]
|
||||
[rn/scroll-view {:style {:padding-horizontal 20
|
||||
:flex-direction :row}}
|
||||
[text/text {:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style {:color colors/white
|
||||
:margin-top 12}}
|
||||
(i18n/label :t/jump-to)]]])])
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
(ns status-im.switcher.shell-stack
|
||||
(:require [status-im.switcher.shell :as shell]
|
||||
[status-im.switcher.animation :as animation]
|
||||
[status-im.switcher.home-stack :as home-stack]
|
||||
[status-im.switcher.bottom-tabs :as bottom-tabs]))
|
||||
|
||||
(defn shell-stack []
|
||||
[:f>
|
||||
(fn []
|
||||
(let [shared-values (animation/get-shared-values)]
|
||||
[:<>
|
||||
[shell/shell]
|
||||
[bottom-tabs/bottom-tabs shared-values]
|
||||
[home-stack/home-stack shared-values]]))])
|
||||
@@ -1,89 +1,35 @@
|
||||
(ns status-im.switcher.styles
|
||||
(:require [quo.theme :as theme]
|
||||
[quo2.foundations.colors :as colors]
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.switcher.constants :as constants]))
|
||||
|
||||
(def themes
|
||||
{:light {:bottom-tabs-bg-color colors/neutral-80
|
||||
:bottom-tabs-on-scroll-bg-color colors/neutral-80-opa-80
|
||||
:bottom-tabs-non-selected-tab colors/neutral-50
|
||||
:bottom-tabs-selected-tab colors/white
|
||||
:switcher-close-button-bg-color colors/white}
|
||||
:dark {:bottom-tabs-bg-color colors/neutral-80
|
||||
:bottom-tabs-on-scroll-bg-color colors/neutral-80-opa-80
|
||||
:bottom-tabs-non-selected-tab colors/neutral-50
|
||||
:bottom-tabs-selected-tab colors/white
|
||||
:switcher-close-button-bg-color colors/white}})
|
||||
|
||||
(defn get-color [key]
|
||||
(get-in themes [(theme/get-theme) key]))
|
||||
|
||||
;; Bottom Tabs
|
||||
(defn bottom-tab-icon [tab-state]
|
||||
{:size 24
|
||||
:color (get-color tab-state)})
|
||||
(defn bottom-tabs-container [pass-through?]
|
||||
{:background-color (if pass-through? colors/neutral-100-opa-70 colors/neutral-100)
|
||||
:flex 1
|
||||
:align-items :center
|
||||
:flex-direction :column
|
||||
:height (constants/bottom-tabs-container-height)
|
||||
:position :absolute
|
||||
:bottom -1
|
||||
:right 0
|
||||
:left 0
|
||||
:accessibility-label :bottom-tabs-container})
|
||||
|
||||
(defn bottom-tabs [icons-only?]
|
||||
{:background-color (if icons-only? nil (get-color :bottom-tabs-bg-color))
|
||||
:flex-direction :row
|
||||
:flex 1
|
||||
:justify-content :space-between
|
||||
:height (constants/bottom-tabs-height)
|
||||
:position :absolute
|
||||
:bottom -1
|
||||
:right 0
|
||||
:left 0
|
||||
:padding-horizontal 16})
|
||||
(defn bottom-tabs []
|
||||
{:flex-direction :row
|
||||
:position :absolute
|
||||
:bottom (if platform/android? 8 34)
|
||||
:flex 1
|
||||
:accessibility-label :bottom-tabs})
|
||||
|
||||
;; Switcher
|
||||
(defn switcher-button []
|
||||
{:width constants/switcher-button-size
|
||||
:height constants/switcher-button-size
|
||||
:z-index 2})
|
||||
|
||||
(defn merge-switcher-button-common-styles [style]
|
||||
(merge
|
||||
{:width constants/switcher-button-size
|
||||
:height constants/switcher-button-size
|
||||
:border-radius constants/switcher-button-radius
|
||||
:position :absolute
|
||||
:z-index 2
|
||||
:align-items :center
|
||||
:align-self :center
|
||||
:justify-content :center}
|
||||
style))
|
||||
|
||||
(defn switcher-button-touchable [view-id]
|
||||
(merge-switcher-button-common-styles
|
||||
{:bottom (constants/switcher-bottom-position view-id)}))
|
||||
|
||||
(defn switcher-close-button []
|
||||
(merge-switcher-button-common-styles
|
||||
{:backgroundColor (get-color :switcher-close-button-bg-color)}))
|
||||
|
||||
(defn switcher-screen []
|
||||
(cond-> (merge-switcher-button-common-styles
|
||||
{:background-color colors/neutral-80-opa-80
|
||||
:z-index 1
|
||||
:overflow :hidden})
|
||||
platform/android? (dissoc :background-color)
|
||||
true (dissoc :justify-content)))
|
||||
|
||||
(defn switcher-blur-background []
|
||||
;; Home Stack
|
||||
(defn home-stack []
|
||||
(let [{:keys [width height]} (constants/dimensions)]
|
||||
{:style {:width width
|
||||
:height (+ height constants/switcher-container-height-padding)}
|
||||
:blur-amount 17
|
||||
:overlay-color colors/neutral-80-opa-80}))
|
||||
|
||||
(defn switcher-screen-container []
|
||||
(let [{:keys [width height]} (constants/dimensions)]
|
||||
{:width width
|
||||
:height (+ height constants/switcher-container-height-padding)
|
||||
:align-items :center
|
||||
:position :absolute}))
|
||||
|
||||
(defn switcher-switch-screen []
|
||||
{:margin-top 40
|
||||
:align-items :center})
|
||||
{:border-bottom-left-radius 20
|
||||
:border-bottom-right-radius 20
|
||||
:background-color (colors/theme-colors colors/neutral-5 colors/neutral-95)
|
||||
:overflow :hidden
|
||||
:position :absolute
|
||||
:width width
|
||||
:height (- height (constants/bottom-tabs-container-height))}))
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
(ns status-im.switcher.switcher
|
||||
(:require [reagent.core :as reagent]
|
||||
[quo2.reanimated :as reanimated]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.switcher.styles :as styles]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.switcher.constants :as constants]
|
||||
[status-im.switcher.animation :as animation]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.react-native.resources :as resources]
|
||||
[status-im.switcher.switcher-container :as switcher-container]
|
||||
[quo.react-native :as rn]))
|
||||
|
||||
(defn switcher-button [view-id toggle-switcher-screen-fn shared-values]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [touchable-original-style (styles/switcher-button-touchable view-id)
|
||||
close-button-original-style (styles/switcher-close-button)
|
||||
switcher-button-original-style (styles/switcher-button)
|
||||
touchable-animated-style (reanimated/apply-animations-to-style
|
||||
{:transform [{:scale (:button-touchable-scale shared-values)}]}
|
||||
touchable-original-style)
|
||||
close-button-animated-style (reanimated/apply-animations-to-style
|
||||
{:opacity (:close-button-opacity shared-values)}
|
||||
close-button-original-style)
|
||||
switcher-button-animated-style (reanimated/apply-animations-to-style
|
||||
{:opacity (:switcher-button-opacity shared-values)}
|
||||
switcher-button-original-style)]
|
||||
[reanimated/touchable-opacity {:active-opacity 1
|
||||
:on-press-in #(animation/switcher-touchable-on-press-in
|
||||
(:button-touchable-scale shared-values))
|
||||
:on-press-out toggle-switcher-screen-fn
|
||||
:style touchable-animated-style}
|
||||
[reanimated/view {:style close-button-animated-style}
|
||||
[icons/icon :main-icons/close {:color colors/neutral-100}]]
|
||||
[reanimated/image {:source (resources/get-image :switcher)
|
||||
:style switcher-button-animated-style}]]))])
|
||||
|
||||
(defn switcher-screen [toggle-switcher-screen-fn shared-values]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [switcher-screen-original-style (styles/switcher-screen)
|
||||
switcher-container-original-style (styles/switcher-screen-container)
|
||||
switcher-screen-animated-style (reanimated/apply-animations-to-style
|
||||
{:width (:switcher-screen-size shared-values)
|
||||
:height (:switcher-screen-size shared-values)
|
||||
:bottom (:switcher-screen-bottom shared-values)
|
||||
:border-radius (:switcher-screen-radius shared-values)}
|
||||
switcher-screen-original-style)
|
||||
switcher-container-animated-style (reanimated/apply-animations-to-style
|
||||
{:bottom (:switcher-container-bottom shared-values)
|
||||
:transform [{:scale (:switcher-container-scale shared-values)}]}
|
||||
switcher-container-original-style)]
|
||||
[reanimated/view {:style switcher-screen-animated-style}
|
||||
[react/blur-view (styles/switcher-blur-background)]
|
||||
[reanimated/view {:style switcher-container-animated-style}
|
||||
[switcher-container/tabs toggle-switcher-screen-fn]]]))])
|
||||
|
||||
(defn switcher [view-id]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [switcher-opened? (reagent/atom false)
|
||||
switcher-button-opacity (reanimated/use-shared-value 1)
|
||||
switcher-screen-size (reanimated/use-shared-value constants/switcher-pressed-size)
|
||||
switcher-screen-radius (animation/switcher-screen-radius switcher-screen-size)
|
||||
switcher-screen-bottom (animation/switcher-screen-bottom-position switcher-screen-radius view-id)
|
||||
shared-values {:switcher-button-opacity switcher-button-opacity
|
||||
:switcher-screen-size switcher-screen-size
|
||||
:switcher-screen-radius switcher-screen-radius
|
||||
:switcher-screen-bottom switcher-screen-bottom
|
||||
:button-touchable-scale (reanimated/use-shared-value 1)
|
||||
:switcher-container-scale (reanimated/use-shared-value 0.9)
|
||||
:close-button-opacity (animation/switcher-close-button-opacity switcher-button-opacity)
|
||||
:switcher-container-bottom (animation/switcher-container-bottom-position switcher-screen-bottom)}
|
||||
toggle-switcher-screen-fn #(animation/switcher-touchable-on-press-out switcher-opened? view-id shared-values)
|
||||
{:keys [keyboard-shown]} (rn/use-keyboard)]
|
||||
(when-not keyboard-shown
|
||||
[:<>
|
||||
[switcher-screen toggle-switcher-screen-fn shared-values]
|
||||
[switcher-button view-id toggle-switcher-screen-fn shared-values]])))])
|
||||
@@ -1,31 +0,0 @@
|
||||
(ns status-im.switcher.switcher-container
|
||||
(:require [quo.react-native :as rn]
|
||||
[status-im.switcher.cards.messaging-card :as messaging-card]
|
||||
[status-im.switcher.styles :as styles]
|
||||
[status-im.utils.handlers :refer [<sub]]))
|
||||
|
||||
;; TODO - use something like this to avoid multiple renders etc.
|
||||
;; (defn switch-screen [toggle-switcher-screen]
|
||||
;; (let [cards (<sub [:navigation2/switcher-cards])
|
||||
;; new-cards (reduce (fn [acc card]
|
||||
;; (conj acc (assoc card :toggle-switcher-screen toggle-switcher-screen)))
|
||||
;; () cards)]
|
||||
;; (fn []
|
||||
;; [rn/view {:style (styles/switcher-switch-screen)}
|
||||
;; [rn/flat-list {:width 352
|
||||
;; :data new-cards
|
||||
;; :render-fn messaging-card/card
|
||||
;; :num-columns 2
|
||||
;; :key-fn str}]])))
|
||||
|
||||
(defn switch-screen [toggle-switcher-screen]
|
||||
(let [cards (<sub [:navigation2/switcher-cards toggle-switcher-screen])]
|
||||
[rn/view {:style (styles/switcher-switch-screen)}
|
||||
[rn/flat-list {:width 352
|
||||
:data cards
|
||||
:render-fn messaging-card/card
|
||||
:num-columns 2
|
||||
:key-fn str}]]))
|
||||
|
||||
(defn tabs [toggle-switcher-screen]
|
||||
[switch-screen toggle-switcher-screen])
|
||||
@@ -1,6 +0,0 @@
|
||||
(ns status-im.switcher.utils
|
||||
(:require [re-frame.core :as re-frame]))
|
||||
|
||||
(def switcher-container-view-id (atom nil))
|
||||
|
||||
(re-frame/reg-fx :switcher-container-view-id #(reset! switcher-container-view-id %))
|
||||
@@ -6,9 +6,10 @@
|
||||
prefer to use it for more general purpose concepts, such as the re-frame event
|
||||
layer."
|
||||
(:require [re-frame.core :as rf]
|
||||
[re-frame.registrar :as rf-registrar]
|
||||
[re-frame.db :as rf-db]
|
||||
[re-frame.events :as rf-events]
|
||||
[re-frame.db :as rf-db]))
|
||||
[re-frame.registrar :as rf-registrar]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
(defn db
|
||||
"A simple wrapper to get the latest value from the app db."
|
||||
@@ -85,3 +86,17 @@
|
||||
(original-on-error (on-error fx-map))
|
||||
(and original-on-success on-success)
|
||||
(original-on-success (on-success fx-map)))))))
|
||||
|
||||
(defn using-log-test-appender
|
||||
"Rebinds `taoensso.timbre/*config*` to use a custom test appender that persists
|
||||
all `taoensso.timbre/log` call arguments. `f` is called with the atom
|
||||
reference so that tests can de-reference it and verify log messages and their
|
||||
respective levels."
|
||||
[f]
|
||||
(let [logs (atom [])]
|
||||
(binding [log/*config* (assoc-in log/*config*
|
||||
[:appenders :test]
|
||||
{:enabled? true
|
||||
:fn (fn [{:keys [vargs level]}]
|
||||
(swap! logs conj {:args vargs :level level}))})]
|
||||
(f logs))))
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
(re-frame/dispatch [:mark-all-activity-center-notifications-as-read])
|
||||
(if config/new-activity-center-enabled?
|
||||
(re-frame/dispatch [:show-popover {:view :activity-center
|
||||
:style {:margin 0}
|
||||
:disable-touchable-overlay? true
|
||||
:blur-view? true
|
||||
:blur-view-props {:blur-amount 20
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
(ns status-im.ui.screens.activity-center.views
|
||||
(:require [quo.components.animated.pressable :as animation]
|
||||
[quo.react :as react]
|
||||
[quo.react-native :as rn]
|
||||
[quo2.components.buttons.button :as button]
|
||||
[quo2.components.markdown.text :as text]
|
||||
@@ -7,7 +8,6 @@
|
||||
[quo2.components.tabs.tabs :as tabs]
|
||||
[quo2.components.tags.context-tags :as context-tags]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
@@ -15,98 +15,114 @@
|
||||
[status-im.utils.handlers :refer [<sub >evt]]
|
||||
[quo.components.safe-area :as safe-area]))
|
||||
|
||||
(defn activity-title
|
||||
[{:keys [type]}]
|
||||
(case type
|
||||
constants/activity-center-notification-type-contact-request
|
||||
(i18n/label :t/contact-request)
|
||||
;;;; Misc
|
||||
|
||||
constants/activity-center-notification-type-one-to-one-chat
|
||||
"Dummy 1:1 chat title"
|
||||
(defn sender-name
|
||||
[contact]
|
||||
(or (get-in contact [:names :nickname])
|
||||
(get-in contact [:names :three-words-name])))
|
||||
|
||||
"Dummy default title"))
|
||||
(defmulti notification-component :type)
|
||||
|
||||
(defn activity-icon
|
||||
[{:keys [type]}]
|
||||
(case type
|
||||
constants/activity-center-notification-type-contact-request
|
||||
:main-icons2/add-user
|
||||
:main-icons2/placeholder))
|
||||
;;;; Contact request notifications
|
||||
|
||||
(defn activity-context
|
||||
[{:keys [message last-message type]}]
|
||||
(case type
|
||||
constants/activity-center-notification-type-contact-request
|
||||
(let [message (or message last-message)
|
||||
contact (<sub [:contacts/contact-by-identity (:from message)])
|
||||
sender-name (or (get-in contact [:names :nickname])
|
||||
(get-in contact [:names :three-words-name]))]
|
||||
[[context-tags/user-avatar-tag
|
||||
{:color :purple
|
||||
:override-theme :dark
|
||||
:size :small
|
||||
:style {:background-color colors/white-opa-10}
|
||||
:text-style {:color colors/white}}
|
||||
sender-name
|
||||
(multiaccounts/displayed-photo contact)]
|
||||
[rn/text {:style {:color colors/white}}
|
||||
(i18n/label :t/contact-request-sent)]])
|
||||
nil))
|
||||
(defmethod notification-component constants/activity-center-notification-type-contact-request
|
||||
[{:keys [id] :as notification}]
|
||||
(let [message (or (:message notification) (:last-message notification))
|
||||
contact (<sub [:contacts/contact-by-identity (:author notification)])
|
||||
pressable (case (:contact-request-state message)
|
||||
constants/contact-request-message-state-accepted
|
||||
;; NOTE [2022-09-21]: We need to dispatch to
|
||||
;; `:contact.ui/send-message-pressed` instead of
|
||||
;; `:chat.ui/navigate-to-chat`, otherwise the chat screen looks completely
|
||||
;; broken if it has never been opened before for the accepted contact.
|
||||
[animation/pressable {:on-press (fn []
|
||||
(>evt [:hide-popover])
|
||||
(>evt [:contact.ui/send-message-pressed {:public-key (:author notification)}]))}]
|
||||
[:<>])]
|
||||
(conj pressable
|
||||
[activity-logs/activity-log
|
||||
(merge {:title (i18n/label :t/contact-request)
|
||||
:icon :main-icons2/add-user
|
||||
:timestamp (datetime/timestamp->relative (:timestamp notification))
|
||||
:unread? (not (:read notification))
|
||||
:context [[context-tags/user-avatar-tag
|
||||
{:color :purple
|
||||
:override-theme :dark
|
||||
:size :small
|
||||
:style {:background-color colors/white-opa-10}
|
||||
:text-style {:color colors/white}}
|
||||
(sender-name contact)
|
||||
(multiaccounts/displayed-photo contact)]
|
||||
[rn/text {:style {:color colors/white}}
|
||||
(i18n/label :t/contact-request-sent)]]
|
||||
:message {:body (get-in message [:content :text])}
|
||||
:status (case (:contact-request-state message)
|
||||
constants/contact-request-message-state-accepted
|
||||
{:type :positive :label (i18n/label :t/accepted)}
|
||||
constants/contact-request-message-state-declined
|
||||
{:type :negative :label (i18n/label :t/declined)}
|
||||
nil)}
|
||||
(case (:contact-request-state message)
|
||||
constants/contact-request-state-mutual
|
||||
{:button-1 {:label (i18n/label :t/decline)
|
||||
:type :danger
|
||||
:on-press #(>evt [:contact-requests.ui/decline-request id])}
|
||||
:button-2 {:label (i18n/label :t/message-reply)
|
||||
:type :success
|
||||
:override-background-color colors/success-60
|
||||
:on-press #(>evt [:contact-requests.ui/accept-request id])}}
|
||||
nil))])))
|
||||
|
||||
(defn activity-message
|
||||
[{:keys [message last-message]}]
|
||||
{:body (get-in (or message last-message) [:content :text])})
|
||||
;;;; Contact verification notifications
|
||||
|
||||
(defn activity-status
|
||||
[notification]
|
||||
(case (get-in notification [:message :contact-request-state])
|
||||
constants/contact-request-message-state-accepted
|
||||
{:type :positive :label (i18n/label :t/accepted)}
|
||||
constants/contact-request-message-state-declined
|
||||
{:type :negative :label (i18n/label :t/declined)}
|
||||
nil))
|
||||
(defmethod notification-component constants/activity-center-notification-type-contact-verification
|
||||
[{:keys [id contact-verification-status] :as notification}]
|
||||
(let [message (or (:message notification) (:last-notification notification))
|
||||
contact (<sub [:contacts/contact-by-identity (:author notification)])]
|
||||
[activity-logs/activity-log
|
||||
(merge {:title (i18n/label :t/identity-verification-request)
|
||||
:icon :main-icons2/friend
|
||||
:timestamp (datetime/timestamp->relative (:timestamp notification))
|
||||
:unread? (not (:read notification))
|
||||
:context [[context-tags/user-avatar-tag
|
||||
{:color :purple
|
||||
:override-theme :dark
|
||||
:size :small
|
||||
:style {:background-color colors/white-opa-10}
|
||||
:text-style {:color colors/white}}
|
||||
(sender-name contact)
|
||||
(multiaccounts/displayed-photo contact)]
|
||||
[rn/text {:style {:color colors/white}}
|
||||
(str (i18n/label :t/identity-verification-request-sent)
|
||||
":")]]
|
||||
:message (case contact-verification-status
|
||||
(constants/contact-verification-state-pending
|
||||
constants/contact-verification-state-declined)
|
||||
{:body (get-in message [:content :text])}
|
||||
nil)
|
||||
:status (case contact-verification-status
|
||||
constants/contact-verification-state-declined
|
||||
{:type :negative :label (i18n/label :t/declined)}
|
||||
nil)}
|
||||
(case contact-verification-status
|
||||
constants/contact-verification-state-pending
|
||||
{:button-1 {:label (i18n/label :t/decline)
|
||||
:type :danger
|
||||
:on-press #(>evt [:activity-center.contact-verification/decline id])}
|
||||
:button-2 {:label (i18n/label :t/accept)
|
||||
:type :primary
|
||||
;; TODO: The acceptance flow will be implemented in follow-up PRs.
|
||||
:on-press identity}}
|
||||
nil))]))
|
||||
|
||||
(defn activity-buttons
|
||||
[{:keys [id type]}]
|
||||
(case type
|
||||
constants/activity-center-notification-type-contact-request
|
||||
{:button-1 {:label (i18n/label :t/decline)
|
||||
:type :danger
|
||||
:on-press #(>evt [:contact-requests.ui/decline-request id])}
|
||||
:button-2 {:label (i18n/label :t/accept)
|
||||
:type :success
|
||||
:override-background-color colors/success-60
|
||||
:on-press #(>evt [:contact-requests.ui/accept-request id])}}
|
||||
nil))
|
||||
|
||||
(defn activity-pressable
|
||||
[notification activity]
|
||||
(case (get-in notification [:message :contact-request-state])
|
||||
constants/contact-request-message-state-accepted
|
||||
;; NOTE [2022-09-21]: We need to dispatch to
|
||||
;; `:contact.ui/send-message-pressed` instead of
|
||||
;; `:chat.ui/navigate-to-chat`, otherwise the chat screen looks completely
|
||||
;; broken if it has never been opened before for the accepted contact.
|
||||
[animation/pressable {:on-press (fn []
|
||||
(>evt [:hide-popover])
|
||||
(>evt [:contact.ui/send-message-pressed {:public-key (:author notification)}]))}
|
||||
activity]
|
||||
activity))
|
||||
;;;; Type-independent components
|
||||
|
||||
(defn render-notification
|
||||
[notification index]
|
||||
[rn/view {:margin-top (if (= 0 index) 0 4)
|
||||
:padding-horizontal 20}
|
||||
[activity-pressable notification
|
||||
[activity-logs/activity-log
|
||||
(merge {:context (activity-context notification)
|
||||
:icon (activity-icon notification)
|
||||
:message (activity-message notification)
|
||||
:status (activity-status notification)
|
||||
:timestamp (datetime/timestamp->relative (:timestamp notification))
|
||||
:title (activity-title notification)
|
||||
:unread? (not (:read notification))}
|
||||
(activity-buttons notification))]]])
|
||||
[notification-component notification]])
|
||||
|
||||
(defn filter-selector-read-toggle
|
||||
[]
|
||||
@@ -164,7 +180,7 @@
|
||||
:label (i18n/label :t/replies)}
|
||||
{:id constants/activity-center-notification-type-contact-request
|
||||
:label (i18n/label :t/contact-requests)}
|
||||
{:id constants/activity-center-notification-type-identity-verification
|
||||
{:id constants/activity-center-notification-type-contact-verification
|
||||
:label (i18n/label :t/identity-verification)}
|
||||
{:id constants/activity-center-notification-type-tx
|
||||
:label (i18n/label :t/transactions)}
|
||||
@@ -181,7 +197,7 @@
|
||||
:type :blur-bg
|
||||
:size 32
|
||||
:override-theme :dark
|
||||
:style {:margin-vertical 12
|
||||
:style {:margin-bottom 12
|
||||
:margin-left screen-padding}
|
||||
:on-press #(>evt [:hide-popover])}
|
||||
:main-icons2/close]
|
||||
@@ -203,19 +219,20 @@
|
||||
|
||||
(defn activity-center
|
||||
[]
|
||||
(reagent/create-class
|
||||
{:component-did-mount #(>evt [:activity-center.notifications/fetch-first-page])
|
||||
:reagent-render
|
||||
(fn []
|
||||
(let [notifications (<sub [:activity-center/filtered-notifications])
|
||||
window-width (<sub [:dimensions/window-width])]
|
||||
[safe-area/view {:style {:flex 1}}
|
||||
[rn/view {:style {:width window-width
|
||||
:flex 1}}
|
||||
[header]
|
||||
[rn/flat-list {:content-container-style {:flex-grow 1}
|
||||
:data notifications
|
||||
:empty-component [empty-tab]
|
||||
:key-fn :id
|
||||
:on-end-reached #(>evt [:activity-center.notifications/fetch-next-page])
|
||||
:render-fn render-notification}]]]))}))
|
||||
[:f>
|
||||
(fn []
|
||||
(let [notifications (<sub [:activity-center/filtered-notifications])
|
||||
window-width (<sub [:dimensions/window-width])
|
||||
{:keys [top bottom]} (safe-area/use-safe-area)]
|
||||
(react/effect! #(>evt [:activity-center.notifications/fetch-first-page]))
|
||||
[rn/view {:style {:flex 1
|
||||
:width window-width
|
||||
:padding-top (if (pos? top) (+ top 12) 12)
|
||||
:padding-bottom bottom}}
|
||||
[header]
|
||||
[rn/flat-list {:content-container-style {:flex-grow 1}
|
||||
:data notifications
|
||||
:empty-component [empty-tab]
|
||||
:key-fn :id
|
||||
:on-end-reached #(>evt [:activity-center.notifications/fetch-next-page])
|
||||
:render-fn render-notification}]]))])
|
||||
@@ -46,4 +46,4 @@
|
||||
(let [edit @(re-frame/subscribe [:chats/edit-message])]
|
||||
(focus-input-on-edit edit had-edit text-input-ref)
|
||||
(when edit
|
||||
[edit-message-wrapper])))))
|
||||
[edit-message-wrapper])))))
|
||||
|
||||
@@ -130,4 +130,4 @@
|
||||
:background-color (colors/get-color :ui-background)
|
||||
:border-top-width 1
|
||||
:border-top-color (colors/get-color :ui-01)
|
||||
:z-index 3})
|
||||
:z-index 3})
|
||||
|
||||
@@ -428,9 +428,7 @@
|
||||
(if (and (not pinned) (> (count pinned-messages) 2))
|
||||
(do
|
||||
(js/setTimeout (fn [] (re-frame/dispatch [:dismiss-keyboard])) 500)
|
||||
(re-frame/dispatch [:show-popover {:view :pin-limit
|
||||
:message message
|
||||
:prevent-closing? true}]))
|
||||
(re-frame/dispatch [::models.pin-message/show-pin-limit-modal chat-id]))
|
||||
(re-frame/dispatch [::models.pin-message/send-pin-message (assoc message :pinned (not pinned))]))))
|
||||
|
||||
(defn on-long-press-fn [on-long-press {:keys [pinned message-pin-enabled outgoing edit-enabled show-input?] :as message} content]
|
||||
@@ -540,12 +538,11 @@
|
||||
(fn [{:keys [content current-public-key outgoing edit-enabled public? pinned in-popover? message-pin-enabled content-type edited-at] :as message}
|
||||
{:keys [on-long-press modal]
|
||||
:as reaction-picker}]
|
||||
;; Makes sure to render a text-messsage and not an emoji-message if it has been edited with text
|
||||
;; Makes sure to render a text-message and not an emoji-message if it has been edited with text
|
||||
(if (= content-type constants/content-type-text)
|
||||
[message-content-wrapper message
|
||||
[collapsible-text-message message on-long-press modal] reaction-picker]
|
||||
(let [response-to (:response-to content)]
|
||||
(println "outgoing" outgoing edit-enabled)
|
||||
[message-content-wrapper message
|
||||
[react/touchable-highlight (when-not modal
|
||||
{:disabled in-popover?
|
||||
|
||||
@@ -10,13 +10,17 @@
|
||||
|
||||
(defn message-reactions [{:keys [content-type]} reactions timeline on-emoji-press on-open]
|
||||
(when (seq reactions)
|
||||
[rn/view {:style (styles/reactions-row timeline (if (= content-type constants/content-type-text) text-reaction-margin-top default-reaction-margin-top))}
|
||||
[rn/view {:style (styles/reactions-row
|
||||
timeline
|
||||
(if (= content-type constants/content-type-text)
|
||||
text-reaction-margin-top default-reaction-margin-top))}
|
||||
(for [{:keys [own emoji-id quantity] :as emoji-reaction} reactions]
|
||||
^{:key (str emoji-reaction)}
|
||||
[rn/view {:style {:margin-right 6 :margin-top 5}}
|
||||
[quo2.reaction/reaction {:emoji (get constants/reactions emoji-id)
|
||||
:neutral? own
|
||||
:clicks quantity
|
||||
:on-press #(on-emoji-press emoji-id)}]])
|
||||
[quo2.reaction/reaction {:emoji (get constants/reactions emoji-id)
|
||||
:neutral? own
|
||||
:clicks quantity
|
||||
:on-press #(on-emoji-press emoji-id)
|
||||
:accessibility-label (str "emoji-reaction-" emoji-id)}]])
|
||||
;; on-press won't work until we integrate Message Context Drawer
|
||||
[quo2.reaction/open-reactions-menu (when @on-open {:on-press @on-open})]]))
|
||||
|
||||
@@ -153,18 +153,9 @@
|
||||
:flex-direction :row-reverse})
|
||||
|
||||
(defn message-view
|
||||
[{:keys [content-type mentioned]}]
|
||||
[{:keys [content-type]}]
|
||||
(merge
|
||||
{:border-radius 10}
|
||||
(cond
|
||||
(= content-type constants/content-type-system-text) nil
|
||||
mentioned {:background-color colors/mentioned-background
|
||||
:border-color colors/mentioned-border
|
||||
:border-width 1}
|
||||
(= content-type constants/content-type-audio) {:background-color colors/blue
|
||||
:padding-horizontal 12
|
||||
:padding-top 6})
|
||||
|
||||
(when (= content-type constants/content-type-emoji)
|
||||
{:flex-direction :row})))
|
||||
|
||||
|
||||
@@ -32,17 +32,17 @@
|
||||
first-name]))))
|
||||
|
||||
(defn format-author
|
||||
([contact] (format-author contact nil))
|
||||
([{:keys [names] :as contact} {:keys [modal profile? you?]}]
|
||||
([contact] (format-author contact nil nil))
|
||||
([{:keys [names] :as contact} {:keys [modal profile? you?]} max-length]
|
||||
(let [{:keys [nickname ens-name]} names
|
||||
[first-name second-name] (multiaccounts/contact-two-names contact false)]
|
||||
(if (or nickname ens-name)
|
||||
[react/nested-text {:number-of-lines 2
|
||||
:style {:color (if modal colors/white-persist colors/black)
|
||||
:font-size (if profile? 15 13)
|
||||
:line-height (if profile? 22 18)
|
||||
:style {:color (if modal colors/white-persist colors/black)
|
||||
:font-size (if profile? 15 13)
|
||||
:line-height (if profile? 22 18)
|
||||
:letter-spacing -0.2
|
||||
:font-weight "600"}}
|
||||
:font-weight "600"}}
|
||||
(subs first-name 0 81)
|
||||
(when you?
|
||||
[{:style {:color colors/black-light :font-weight "500" :font-size 13}}
|
||||
@@ -50,12 +50,15 @@
|
||||
(when nickname
|
||||
[{:style {:color colors/black-light :font-weight "500"}}
|
||||
(str " " (subs second-name 0 81))])]
|
||||
[react/text {:style {:color (if modal colors/white-persist colors/black)
|
||||
:font-size (if profile? 15 13)
|
||||
:line-height (if profile? 22 18)
|
||||
:font-weight "600"
|
||||
:letter-spacing -0.2}}
|
||||
first-name]))))
|
||||
[react/text {:style {:color (if modal colors/white-persist colors/black)
|
||||
:font-size (if profile? 15 13)
|
||||
:line-height (if profile? 22 18)
|
||||
:font-weight "600"
|
||||
:letter-spacing -0.2}
|
||||
:number-of-lines 1}
|
||||
(if (and max-length (> (count first-name) max-length))
|
||||
(str (subs first-name 0 max-length) "...")
|
||||
first-name)]))))
|
||||
|
||||
(defn format-reply-author [from username current-public-key style outgoing]
|
||||
(let [contact-name (str reply-symbol username)]
|
||||
@@ -63,7 +66,7 @@
|
||||
[react/text {:style (style true)}
|
||||
(str reply-symbol (i18n/label :t/You))])
|
||||
(if (or (= (aget contact-name 0) "@")
|
||||
;; in case of replies
|
||||
;; in case of replies
|
||||
(= (aget contact-name 1) "@"))
|
||||
(let [trimmed-name (subs contact-name 0 81)]
|
||||
[react/text {:number-of-lines 2
|
||||
|
||||
@@ -258,6 +258,7 @@
|
||||
(re-frame/dispatch [:mark-all-activity-center-notifications-as-read])
|
||||
(if config/new-activity-center-enabled?
|
||||
(re-frame/dispatch [:show-popover {:view :activity-center
|
||||
:style {:margin 0}
|
||||
:disable-touchable-overlay? true
|
||||
:blur-view? true
|
||||
:blur-view-props {:blur-amount 20
|
||||
|
||||
@@ -204,7 +204,8 @@
|
||||
(first @(re-frame/subscribe [:contacts/contact-two-names-by-identity chat-id])))])
|
||||
|
||||
(defn home-list-item [home-item opts]
|
||||
(let [{:keys [chat-id chat-name color group-chat muted emoji highlight edit? public? unviewed-messages-count contacts]} home-item
|
||||
(let [{:keys [chat-id chat-name color group-chat muted emoji highlight edit? public? unviewed-messages-count contacts last-message]} home-item
|
||||
last-message-content (get-in last-message [:content :text])
|
||||
background-color (when highlight (colors/get-color :interactive-02))]
|
||||
[react/touchable-opacity (merge {:style {:height 64 :background-color background-color}} opts)
|
||||
[:<>
|
||||
@@ -230,7 +231,7 @@
|
||||
:ellipsize-mode :middle
|
||||
:weight :medium
|
||||
:style {:color (quo2.colors/theme-colors quo2.colors/neutral-50 quo2.colors/neutral-40)}}
|
||||
(i18n/label :t/public)]
|
||||
(or last-message-content (i18n/label :t/public))]
|
||||
(if group-chat
|
||||
[react/view {:flex-direction :row
|
||||
:flex 1
|
||||
@@ -251,7 +252,7 @@
|
||||
:style {:color (quo2.colors/theme-colors quo2.colors/neutral-50 quo2.colors/neutral-40)}
|
||||
:number-of-lines 1
|
||||
:ellipsize-mode :middle}
|
||||
(utils.utils/get-shortened-address chat-id)]))]]]))
|
||||
(or last-message-content (utils.utils/get-shortened-address chat-id))]))]]]))
|
||||
|
||||
(defn home-list-item-old [home-item opts]
|
||||
(let [{:keys [chat-id chat-name color group-chat public? timestamp last-message muted emoji highlight edit?]} home-item
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
(ns status-im.ui2.screens.chat.actions
|
||||
(:require
|
||||
[status-im.chat.models :as chat.models]
|
||||
[status-im.chat.models.pin-message :as models.pin-message]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.utils.handlers :refer [<sub >evt]]
|
||||
[quo2.components.drawers.action-drawers :as drawer]))
|
||||
|
||||
(defn- entry [icon label on-press danger?]
|
||||
{:pre [(keyword? icon)
|
||||
(string? label)
|
||||
(fn? on-press)
|
||||
(boolean? danger?)]}
|
||||
{:icon icon
|
||||
:label label
|
||||
:on-press on-press
|
||||
:danger? danger?})
|
||||
|
||||
(defn hide-sheet-and-dispatch [event]
|
||||
(>evt [:bottom-sheet/hide])
|
||||
(>evt event))
|
||||
|
||||
(defn show-profile-action [chat-id]
|
||||
(hide-sheet-and-dispatch [:chat.ui/show-profile chat-id])
|
||||
(>evt [::models.pin-message/load-pin-messages chat-id]))
|
||||
|
||||
(defn mark-all-read-action [chat-id]
|
||||
(hide-sheet-and-dispatch [:chat/mark-all-as-read chat-id]))
|
||||
|
||||
(defn edit-nickname-action [chat-id]
|
||||
(hide-sheet-and-dispatch [:chat.ui/edit-nickname chat-id]))
|
||||
|
||||
(defn mute-chat-action [chat-id]
|
||||
(hide-sheet-and-dispatch [::chat.models/mute-chat-toggled chat-id true]))
|
||||
|
||||
(defn unmute-chat-action [chat-id]
|
||||
(hide-sheet-and-dispatch [::chat.models/mute-chat-toggled chat-id false]))
|
||||
|
||||
(defn clear-history-action [chat-id]
|
||||
(hide-sheet-and-dispatch [:chat.ui/clear-history-pressed chat-id]))
|
||||
|
||||
(defn delete-chat-action [chat-id]
|
||||
(hide-sheet-and-dispatch [:chat.ui/remove-chat-pressed chat-id]))
|
||||
|
||||
(defn mute-chat-entry [muted? chat-id]
|
||||
(entry :main-icons2/muted
|
||||
(i18n/label
|
||||
(if muted?
|
||||
:unmute-chat
|
||||
:mute-chat))
|
||||
(if muted?
|
||||
#(unmute-chat-action chat-id)
|
||||
#(mute-chat-action chat-id))
|
||||
false))
|
||||
|
||||
(defn mark-as-read-entry [chat-id]
|
||||
(entry :main-icons2/check
|
||||
(i18n/label :mark-as-read)
|
||||
#(mark-all-read-action chat-id)
|
||||
false))
|
||||
|
||||
(defn clear-history-entry [chat-id]
|
||||
(entry :main-icons2/delete
|
||||
(i18n/label :clear-history)
|
||||
#(clear-history-action chat-id)
|
||||
true))
|
||||
|
||||
(defn delete-chat-entry [chat-id]
|
||||
(entry :main-icons2/delete
|
||||
(i18n/label :delete-chat)
|
||||
#(delete-chat-action chat-id)
|
||||
true))
|
||||
|
||||
(defn view-profile-entry [chat-id]
|
||||
(entry :main-icons2/friend
|
||||
(i18n/label :view-profile)
|
||||
#(show-profile-action chat-id)
|
||||
false))
|
||||
|
||||
(defn edit-nickname-entry [chat-id]
|
||||
(entry :main-icons2/edit
|
||||
(i18n/label :edit-nickname)
|
||||
#(edit-nickname-action chat-id)
|
||||
false))
|
||||
|
||||
(defn destructive-actions [chat-id]
|
||||
[(clear-history-entry chat-id)
|
||||
(delete-chat-entry chat-id)])
|
||||
|
||||
(defn notification-actions [muted? chat-id]
|
||||
[(mute-chat-entry muted? chat-id)
|
||||
(mark-as-read-entry chat-id)])
|
||||
|
||||
(defn one-to-one-actions [muted? chat-id]
|
||||
[drawer/action-drawer [[(view-profile-entry chat-id)
|
||||
(edit-nickname-entry chat-id)]
|
||||
(notification-actions muted? chat-id)
|
||||
(destructive-actions chat-id)]])
|
||||
|
||||
(defn public-chat-actions [muted? chat-id]
|
||||
[drawer/action-drawer [(notification-actions muted? chat-id)
|
||||
(destructive-actions chat-id)]])
|
||||
|
||||
(defn private-group-chat-actions [muted? chat-id]
|
||||
[drawer/action-drawer [(notification-actions muted? chat-id)
|
||||
(destructive-actions chat-id)]])
|
||||
|
||||
(defn actions [chat-type chat-id]
|
||||
(let [muted? (<sub [:chats/muted chat-id])]
|
||||
(case chat-type
|
||||
constants/one-to-one-chat-type
|
||||
[one-to-one-actions muted? chat-id]
|
||||
constants/public-chat-type
|
||||
[public-chat-actions muted? chat-id]
|
||||
constants/private-group-chat-type
|
||||
[private-group-chat-actions muted? chat-id])))
|
||||
@@ -3,7 +3,7 @@
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.constants :as constants]
|
||||
[quo2.components.buttons.button :as quo2.button]
|
||||
[quo2.foundations.colors :as quo2.colors]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.list-items.menu-item :as quo2.menu-item]
|
||||
[quo2.components.separator :as quo2.separator]))
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
(let [main-actions (filter #(= (:type %) :main) actions)
|
||||
danger-actions (filter #(= (:type %) :danger) actions)
|
||||
admin-actions (filter #(= (:type %) :admin) actions)]
|
||||
[rn/view {:flex 1}
|
||||
[rn/view
|
||||
[rn/view {:style {:width "100%"
|
||||
:flex-direction :row
|
||||
:justify-content :space-between
|
||||
@@ -30,11 +30,11 @@
|
||||
:type :grey
|
||||
:icon true
|
||||
:icon-no-color true
|
||||
:accessibility-label :reply-cancel-button
|
||||
:accessibility-label (str "emoji-picker-" id)
|
||||
:on-press #(do
|
||||
(send-emoji id)
|
||||
(re-frame/dispatch [:bottom-sheet/hide]))}
|
||||
(when active {:style {:background-color quo2.colors/neutral-10}}))
|
||||
(when active {:style {:background-color colors/neutral-10}}))
|
||||
icon]))]
|
||||
[rn/view {:style {:padding-horizontal 8}}
|
||||
(for [action main-actions]
|
||||
@@ -73,4 +73,4 @@
|
||||
:icon (:icon action)
|
||||
:on-press #(do
|
||||
(when on-press (on-press))
|
||||
(re-frame/dispatch [:bottom-sheet/hide]))}]))]])))
|
||||
(re-frame/dispatch [:bottom-sheet/hide]))}]))]])))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns status-im.ui2.screens.chat.components.reply
|
||||
(:require [quo2.foundations.colors :as quo2.colors]
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[quo.react-native :as rn]
|
||||
[status-im.constants :as constants]
|
||||
@@ -48,11 +48,13 @@
|
||||
(let [contact-name (<sub [:contacts/contact-name-by-identity from])
|
||||
current-public-key (<sub [:multiaccount/public-key])
|
||||
content-type (or content-type contentType)]
|
||||
[rn/view {:style {:flex-direction :row :height (when-not pin? 24)}}
|
||||
[rn/view {:style {:flex-direction :row
|
||||
:height (when-not pin? 24)
|
||||
:accessibility-label :reply-message}}
|
||||
[rn/view {:style (styles/reply-content pin?)}
|
||||
(when-not pin?
|
||||
;;TODO quo2 icon should be used
|
||||
[icons/icon :main-icons/connector {:color (quo2.colors/theme-colors quo2.colors/neutral-40 quo2.colors/neutral-60)
|
||||
[icons/icon :main-icons/connector {:color (colors/theme-colors colors/neutral-40 colors/neutral-60)
|
||||
:container-style {:position :absolute :left 10 :bottom -4 :width 16 :height 16}}])
|
||||
[rn/view {:style (styles/quoted-message pin?)}
|
||||
[photos/member-photo from identicon 16]
|
||||
@@ -71,7 +73,7 @@
|
||||
(when (or (= constants/content-type-image content-type)
|
||||
(= constants/content-type-sticker content-type)
|
||||
(= constants/content-type-audio content-type))
|
||||
{:color (quo2.colors/theme-colors quo2.colors/neutral-50 quo2.colors/neutral-40)}))}
|
||||
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}))}
|
||||
(case (or content-type contentType)
|
||||
constants/content-type-image "Image"
|
||||
constants/content-type-sticker "Sticker"
|
||||
@@ -86,4 +88,4 @@
|
||||
;;TODO quo2 icon should be used
|
||||
[icons/icon :main-icons/close {:width 16
|
||||
:height 16
|
||||
:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/neutral-40)}]])]))
|
||||
:color (colors/theme-colors colors/neutral-100 colors/neutral-40)}]])]))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
(:require [quo.react-native :as rn]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.utils.handlers :refer [<sub >evt]]
|
||||
[quo.design-system.colors :as colors]
|
||||
[quo.design-system.colors :as quo.colors]
|
||||
[status-im.utils.utils :as utils.utils]
|
||||
[status-im.utils.platform :as platform]
|
||||
[clojure.string :as string]
|
||||
@@ -11,7 +11,7 @@
|
||||
[status-im.ui2.screens.chat.composer.style :as style]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.chat.models.mentions :as mentions]
|
||||
[quo2.foundations.colors :as quo2.colors]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo.react]))
|
||||
|
||||
(defonce input-texts (atom {}))
|
||||
@@ -156,7 +156,7 @@
|
||||
:auto-focus false
|
||||
:on-focus #(set-active-panel nil)
|
||||
:max-length chat.constants/max-text-size
|
||||
:placeholder-text-color (:text-02 @colors/theme)
|
||||
:placeholder-text-color (:text-02 @quo.colors/theme)
|
||||
:placeholder (if cooldown-enabled?
|
||||
(i18n/label :cooldown/text-input-disabled)
|
||||
(i18n/label :t/type-a-message))
|
||||
@@ -174,6 +174,6 @@
|
||||
[idx item])
|
||||
(<sub [:chat/input-with-mentions]))]
|
||||
^{:key (str idx "_" type "_" text)}
|
||||
[rn/text (when (= type :mention) {:style {:color quo2.colors/primary-50}})
|
||||
[rn/text (when (= type :mention) {:style {:color colors/primary-50}})
|
||||
text])
|
||||
(get @input-texts chat-id))]))
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
(ns status-im.ui2.screens.chat.composer.style
|
||||
(:require [quo2.foundations.typography :as quo2.typography]
|
||||
[quo.design-system.colors :as colors]
|
||||
[quo.design-system.colors :as quo.colors]
|
||||
[status-im.utils.platform :as platform]
|
||||
[quo2.foundations.colors :as quo2.colors]))
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn text-input []
|
||||
(merge quo2.typography/font-regular
|
||||
@@ -11,7 +11,7 @@
|
||||
:min-height 34
|
||||
:margin 0
|
||||
:flex-shrink 1
|
||||
:color (:text-01 @colors/theme)
|
||||
:color (:text-01 @quo.colors/theme)
|
||||
:margin-horizontal 20}
|
||||
(if platform/android?
|
||||
{:padding-vertical 8
|
||||
@@ -28,7 +28,7 @@
|
||||
:bottom (- window-height)
|
||||
:height window-height
|
||||
:flex 1
|
||||
:background-color (quo2.colors/theme-colors quo2.colors/white quo2.colors/neutral-90)
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-90)
|
||||
:z-index 2}
|
||||
(if platform/ios?
|
||||
{:shadow-radius 16
|
||||
@@ -40,7 +40,7 @@
|
||||
(defn bottom-sheet-handle []
|
||||
{:width 32
|
||||
:height 4
|
||||
:background-color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)
|
||||
:background-color (colors/theme-colors colors/neutral-100 colors/white)
|
||||
:opacity 0.05
|
||||
:border-radius 100
|
||||
:align-self :center
|
||||
@@ -52,7 +52,7 @@
|
||||
:elevation 2
|
||||
:z-index 3
|
||||
:position :absolute
|
||||
:background-color (quo2.colors/theme-colors quo2.colors/white quo2.colors/neutral-90)
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-90)
|
||||
;these 3 props play together, we need this magic to hide message text in the safe area
|
||||
:padding-top 10
|
||||
:padding-bottom (+ 12 (:bottom insets))
|
||||
@@ -65,7 +65,7 @@
|
||||
:right 0
|
||||
:bottom 0
|
||||
:height window-height
|
||||
:background-color quo2.colors/neutral-95-opa-70
|
||||
:background-color colors/neutral-95-opa-70
|
||||
:z-index 1})
|
||||
|
||||
(defn reply-content [pin?]
|
||||
@@ -79,4 +79,4 @@
|
||||
:width "45%"}
|
||||
(when-not pin? {:position :absolute
|
||||
:left 34
|
||||
:top 3})))
|
||||
:top 3})))
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
{window-height :height} (rn/use-window-dimensions)
|
||||
{:keys [keyboard-shown keyboard-height]} (rn/use-keyboard)
|
||||
max-y (- window-height (if (> keyboard-height 0) keyboard-height 360) (:top insets)) ; 360 - default height
|
||||
max-height (- max-y 56 (:bottom insets)) ; 56 - top-bar height
|
||||
max-height (Math/abs (- max-y 56 (:bottom insets))) ; 56 - top-bar height
|
||||
added-value (if (and (not (seq suggestions)) reply) 38 0) ; increased height of input box needed when reply
|
||||
min-y (+ min-y (when reply 38))
|
||||
y (get-y-value context keyboard-shown min-y max-y added-value max-height chat-id suggestions reply)
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.screens.home.styles :as styles]
|
||||
[status-im.ui2.screens.chat.actions :as actions]
|
||||
[status-im.ui.screens.home.views.inner-item :refer [home-list-item]]
|
||||
[quo.design-system.colors :as colors]
|
||||
[quo.design-system.colors :as quo.colors]
|
||||
[quo.core :as quo]
|
||||
[quo.platform :as platform]
|
||||
[status-im.add-new.core :as new-chat]
|
||||
@@ -19,35 +20,39 @@
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.components.plus-button :as components.plus-button]
|
||||
[status-im.ui.screens.chat.sheets :as sheets]
|
||||
[status-im.ui.components.tabbar.core :as tabbar]
|
||||
[status-im.ui.components.invite.views :as invite]
|
||||
[status-im.utils.handlers :refer [<sub]]
|
||||
[status-im.utils.handlers :refer [<sub >evt]]
|
||||
[status-im.utils.config :as config]
|
||||
[quo2.components.markdown.text :as quo2.text]
|
||||
[status-im.qr-scanner.core :as qr-scanner]
|
||||
[status-im.ui.components.chat-icon.styles :as chat-icon.styles]
|
||||
[quo.react-native :as rn]
|
||||
[quo2.foundations.colors :as quo2.colors]
|
||||
[quo.react]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.foundations.typography :as typography]
|
||||
[quo2.components.buttons.button :as quo2.button]
|
||||
[quo2.components.tabs.tabs :as quo2.tabs]
|
||||
[quo2.components.community.discover-card :as discover-card]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon])
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon]
|
||||
[quo2.components.icon :as quo2.icons]
|
||||
[quo.components.safe-area :as safe-area]
|
||||
[quo2.components.list-items.received-contact-request :as received-contact-request])
|
||||
(:require-macros [status-im.utils.views :as views]))
|
||||
|
||||
(defn home-tooltip-view []
|
||||
[rn/view (styles/chat-tooltip)
|
||||
[rn/view {:style {:width 66 :position :absolute :top -6 :background-color colors/white
|
||||
[rn/view {:style {:width 66 :position :absolute :top -6 :background-color quo.colors/white
|
||||
:align-items :center}}
|
||||
[rn/image {:source (resources/get-image :empty-chats-header)
|
||||
:style {:width 50 :height 50}}]]
|
||||
[rn/touchable-highlight
|
||||
{:style {:position :absolute :right 0 :top 0
|
||||
:width 44 :height 44 :align-items :center :justify-content :center}
|
||||
{:style {:position :absolute :right 0 :top 0
|
||||
:width 44 :height 44 :align-items :center :justify-content :center}
|
||||
:on-press #(re-frame/dispatch [:multiaccounts.ui/hide-home-tooltip])
|
||||
:accessibility-label :hide-home-button}
|
||||
[icons/icon :main-icons/close-circle {:color colors/gray}]]
|
||||
[icons/icon :main-icons/close-circle {:color quo.colors/gray}]]
|
||||
[react/i18n-text {:style styles/no-chats-text :key :chat-and-transact}]
|
||||
[rn/view {:align-items :center
|
||||
:margin-top 8
|
||||
@@ -112,7 +117,7 @@
|
||||
(re-frame/dispatch [:set :public-group-topic nil])
|
||||
(re-frame/dispatch [:search/home-filter-changed nil]))}])])))
|
||||
|
||||
(defn render-fn [{:keys [chat-id] :as home-item}]
|
||||
(defn render-fn [{:keys [chat-id chat-type] :as home-item}]
|
||||
[home-list-item
|
||||
home-item
|
||||
{:on-press (fn []
|
||||
@@ -124,31 +129,33 @@
|
||||
(re-frame/dispatch [:accept-all-activity-center-notifications-from-chat chat-id]))
|
||||
:on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[sheets/actions home-item])}])}])
|
||||
[actions/actions
|
||||
chat-type
|
||||
chat-id])}])}])
|
||||
|
||||
(defn- render-contact [{:keys [public-key] :as row}]
|
||||
(let [[first-name second-name] (multiaccounts/contact-two-names row true)
|
||||
row (assoc row :chat-id public-key)]
|
||||
|
||||
[quo/list-item
|
||||
{:title first-name
|
||||
:subtitle second-name
|
||||
:background-color quo2.colors/neutral-5
|
||||
:on-press (fn []
|
||||
(re-frame/dispatch [:dismiss-keyboard])
|
||||
(if platform/android?
|
||||
(re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 public-key])
|
||||
(re-frame/dispatch [:chat.ui/navigate-to-chat public-key]))
|
||||
(re-frame/dispatch [:search/home-filter-changed nil])
|
||||
(re-frame/dispatch [:accept-all-activity-center-notifications-from-chat public-key]))
|
||||
:on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[sheets/actions row])}])
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo row)]}]))
|
||||
{:title first-name
|
||||
:subtitle second-name
|
||||
:background-color colors/neutral-5
|
||||
:on-press (fn []
|
||||
(re-frame/dispatch [:dismiss-keyboard])
|
||||
(if platform/android?
|
||||
(re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 public-key])
|
||||
(re-frame/dispatch [:chat.ui/navigate-to-chat public-key]))
|
||||
(re-frame/dispatch [:search/home-filter-changed nil])
|
||||
(re-frame/dispatch [:accept-all-activity-center-notifications-from-chat public-key]))
|
||||
;:on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet TODO: new UI yet to be implemented
|
||||
; {:content (fn []
|
||||
; [sheets/actions row])}])
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo row)]}]))
|
||||
|
||||
(defn chat-list-key-fn [item]
|
||||
(or (:chat-id item) (:public-key item)))
|
||||
(or (:chat-id item) (:public-key item) (:id item)))
|
||||
|
||||
(defn get-item-layout [_ index]
|
||||
#js {:length 64 :offset (* 64 index) :index index})
|
||||
@@ -171,17 +178,110 @@
|
||||
(vals @data)))
|
||||
|
||||
(defn contacts-section-header [{:keys [title]}]
|
||||
[rn/view {:style {:border-top-width 1 :border-top-color quo2.colors/neutral-20 :padding-vertical 8 :padding-horizontal 20 :margin-top 8}}
|
||||
[rn/text {:style (merge typography/font-medium typography/paragraph-2 {:color quo2.colors/neutral-50})} title]])
|
||||
[rn/view {:style {:border-top-width 1 :border-top-color colors/neutral-20 :padding-vertical 8 :padding-horizontal 20 :margin-top 8}}
|
||||
[rn/text {:style (merge typography/font-medium typography/paragraph-2 {:color colors/neutral-50})} title]])
|
||||
|
||||
(defn find-contact-requests [notifications]
|
||||
(let [received-requests (atom [])
|
||||
has-unread (atom false)]
|
||||
(doseq [i (range (count notifications))]
|
||||
(doseq [j (range (count (:data (nth notifications i))))]
|
||||
(when (= 1 (get-in (nth (:data (nth notifications i)) j) [:message :contact-request-state]))
|
||||
(swap! received-requests conj (nth (:data (nth notifications i)) j)))
|
||||
(when (= false (get-in (nth (:data (nth notifications i)) j) [:read]))
|
||||
(reset! has-unread true))))
|
||||
{:received-requests @received-requests :has-unread @has-unread}))
|
||||
|
||||
(def selected-requests-tab (reagent/atom :received))
|
||||
|
||||
(defn contact-requests-sheet []
|
||||
[:f>
|
||||
(fn []
|
||||
(let [{window-height :height} (rn/use-window-dimensions)
|
||||
safe-area (safe-area/use-safe-area)
|
||||
notifications (<sub [:activity.center/notifications-grouped-by-date])
|
||||
{received-requests :received-requests} (find-contact-requests notifications)
|
||||
sent-requests []]
|
||||
[rn/view {:style {:margin-left 20
|
||||
:height (- window-height (:top safe-area))}}
|
||||
[rn/touchable-opacity
|
||||
{:on-press #(>evt [:bottom-sheet/hide])
|
||||
:style
|
||||
{:background-color (colors/theme-colors colors/neutral-10 colors/neutral-80)
|
||||
:width 32
|
||||
:height 32
|
||||
:border-radius 10
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:margin-bottom 24}}
|
||||
[quo2.icons/icon :main-icons2/close {:color (colors/theme-colors "#000000" "#ffffff")}]]
|
||||
[rn/text {:style (merge
|
||||
typography/heading-1
|
||||
typography/font-semi-bold
|
||||
{:color (colors/theme-colors "#000000" "#ffffff")})}
|
||||
(i18n/label :t/pending-requests)]
|
||||
[quo2.tabs/tabs
|
||||
{:style {:margin-top 12 :margin-bottom 20}
|
||||
:size 32
|
||||
:on-change #(reset! selected-requests-tab %)
|
||||
:default-active :received
|
||||
:data [{:id :received
|
||||
:label (i18n/label :t/received)}
|
||||
{:id :sent
|
||||
:label (i18n/label :t/sent)}]}]
|
||||
[list/flat-list
|
||||
{:key-fn :first
|
||||
:data (if (= @selected-requests-tab :received) received-requests sent-requests)
|
||||
:render-fn received-contact-request/list-item}]]))])
|
||||
|
||||
(defn contact-requests [count]
|
||||
[rn/touchable-opacity
|
||||
{:active-opacity 1
|
||||
:on-press #(do
|
||||
(>evt
|
||||
[:bottom-sheet/show-sheet
|
||||
{:content (fn [] [contact-requests-sheet])}])
|
||||
(>evt [:mark-all-activity-center-notifications-as-read]))
|
||||
:style {:flex-direction :row
|
||||
:margin 8
|
||||
:padding-horizontal 12
|
||||
:padding-vertical 8
|
||||
:align-items :center}}
|
||||
[rn/view {:style {:justify-content :center
|
||||
:align-items :center
|
||||
:width 32
|
||||
:height 32
|
||||
:border-radius 16
|
||||
:border-width 1
|
||||
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-80)}}
|
||||
[quo2.icons/icon :main-icons2/pending-user {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}]]
|
||||
[rn/view {:style {:margin-left 8}}
|
||||
[rn/text {:style
|
||||
(merge typography/paragraph-1 typography/font-semi-bold {:color (colors/theme-colors "#000000" "#ffffff")})} (i18n/label :t/pending-requests)]
|
||||
[rn/text {:style (merge typography/paragraph-2 typography/font-regular {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)})} "Alice, Pedro and 3 others"]]
|
||||
[rn/view {:style {:width 16
|
||||
:height 16
|
||||
:position :absolute
|
||||
:right 22
|
||||
:border-radius 6
|
||||
:background-color (colors/theme-colors colors/primary-50 colors/primary-60)}}
|
||||
[rn/text {:style (merge typography/font-medium typography/label {:color "#ffffff" :text-align :center})} count]]])
|
||||
|
||||
(defn chats []
|
||||
(let [{:keys [items search-filter]} (<sub [:home-items])
|
||||
current-active-tab @selected-tab
|
||||
items (prepare-items current-active-tab items)
|
||||
contacts (<sub [:contacts/active])
|
||||
contacts (prepare-contacts contacts)]
|
||||
items (prepare-items current-active-tab items)
|
||||
contacts (<sub [:contacts/active])
|
||||
contacts (prepare-contacts contacts)
|
||||
notifications (<sub [:activity.center/notifications-grouped-by-date])
|
||||
{requests :received-requests new-info :has-unread} (find-contact-requests notifications)]
|
||||
[rn/view {:style {:flex 1}}
|
||||
[quo2.tabs/tabs {:style {:margin-left 20 :margin-bottom 20} :size 32
|
||||
[discover-card/discover-card {:title (i18n/label :t/invite-friends-to-status)
|
||||
:description (i18n/label :t/share-invite-link)}]
|
||||
[quo2.tabs/tabs {:style {:margin-left 20
|
||||
:margin-bottom 20
|
||||
:margin-top 24}
|
||||
:size 32
|
||||
:on-change #(reset! selected-tab %)
|
||||
:default-active selected-tab
|
||||
:data [{:id :recent
|
||||
@@ -189,7 +289,8 @@
|
||||
{:id :groups
|
||||
:label (i18n/label :t/groups)}
|
||||
{:id :contacts
|
||||
:label (i18n/label :t/contacts)}]}]
|
||||
:label (i18n/label :t/contacts)
|
||||
:new-info new-info}]}]
|
||||
(if (and (empty? items)
|
||||
(empty? search-filter)
|
||||
(not @search-active?))
|
||||
@@ -202,12 +303,14 @@
|
||||
:keyboard-should-persist-taps :always
|
||||
:data items
|
||||
:render-fn render-fn}]
|
||||
[list/section-list
|
||||
{:key-fn :title
|
||||
:sticky-section-headers-enabled false
|
||||
:sections contacts
|
||||
:render-section-header-fn contacts-section-header
|
||||
:render-fn render-contact}]))]))
|
||||
[rn/view {:style {:flex 1}} (when (> (count requests) 0)
|
||||
[contact-requests (count requests)])
|
||||
[list/section-list
|
||||
{:key-fn :title
|
||||
:sticky-section-headers-enabled false
|
||||
:sections contacts
|
||||
:render-section-header-fn contacts-section-header
|
||||
:render-fn render-contact}]]))]))
|
||||
|
||||
(views/defview chats-list []
|
||||
(views/letsubs [loading? [:chats/loading?]]
|
||||
@@ -221,81 +324,88 @@
|
||||
(views/defview plus-button []
|
||||
(views/letsubs [logging-in? [:multiaccounts/login]]
|
||||
[components.plus-button/plus-button
|
||||
{:on-press (when-not logging-in?
|
||||
#(re-frame/dispatch [:bottom-sheet/show-sheet :add-new {}]))
|
||||
:loading logging-in?
|
||||
{:on-press (when-not logging-in?
|
||||
#(re-frame/dispatch [:bottom-sheet/show-sheet :add-new {}]))
|
||||
:loading logging-in?
|
||||
:accessibility-label :new-chat-button}]))
|
||||
|
||||
(views/defview notifications-button []
|
||||
(views/letsubs [notif-count [:activity.center/notifications-count]]
|
||||
[rn/view
|
||||
[quo2.button/button {:type :grey
|
||||
:size 32
|
||||
:width 32
|
||||
:style {:margin-left 12}
|
||||
[quo2.button/button {:type :grey
|
||||
:size 32
|
||||
:width 32
|
||||
:style {:margin-left 12}
|
||||
:accessibility-label :notifications-button
|
||||
:on-press #(do
|
||||
(re-frame/dispatch [:mark-all-activity-center-notifications-as-read])
|
||||
(if config/new-activity-center-enabled?
|
||||
(re-frame/dispatch [:navigate-to :activity-center])
|
||||
(re-frame/dispatch [:navigate-to :notifications-center])))}
|
||||
[icons/icon :main-icons/notification2 {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}]]
|
||||
:on-press #(do
|
||||
(re-frame/dispatch [:mark-all-activity-center-notifications-as-read])
|
||||
(if config/new-activity-center-enabled?
|
||||
(re-frame/dispatch [:show-popover {:view :activity-center
|
||||
:style {:margin 0}
|
||||
:disable-touchable-overlay? true
|
||||
:blur-view? true
|
||||
:blur-view-props {:blur-amount 20
|
||||
:blur-type :dark}}])
|
||||
(re-frame/dispatch [:navigate-to :notifications-center])))}
|
||||
[icons/icon :main-icons/notification2 {:color (colors/theme-colors colors/neutral-100 colors/white)}]]
|
||||
(when (pos? notif-count)
|
||||
[rn/view {:style (merge (styles/counter-public-container) {:top 5 :right 5})
|
||||
[rn/view {:style (merge (styles/counter-public-container) {:top 5 :right 5})
|
||||
:pointer-events :none}
|
||||
[rn/view {:style styles/counter-public
|
||||
:accessibility-label :notifications-unread-badge}]])]))
|
||||
|
||||
(defn qr-button []
|
||||
[quo2.button/button {:type :grey
|
||||
[quo2.button/button {:type :grey
|
||||
:accessibility-label "qr-button"
|
||||
:size 32
|
||||
:width 32
|
||||
:style {:margin-left 12}
|
||||
:on-press #(do
|
||||
(re-frame/dispatch [::qr-scanner/scan-code
|
||||
{:handler ::qr-scanner/on-scan-success}]))}
|
||||
[icons/icon :main-icons/qr2 {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}]])
|
||||
:size 32
|
||||
:width 32
|
||||
:style {:margin-left 12}
|
||||
:on-press #(do
|
||||
(re-frame/dispatch [::qr-scanner/scan-code
|
||||
{:handler ::qr-scanner/on-scan-success}]))}
|
||||
[icons/icon :main-icons/qr2 {:color (colors/theme-colors colors/neutral-100 colors/white)}]])
|
||||
|
||||
(defn scan-button []
|
||||
[quo2.button/button {:type :grey
|
||||
:size 32
|
||||
:width 32
|
||||
[quo2.button/button {:type :grey
|
||||
:size 32
|
||||
:width 32
|
||||
:accessibility-label "scan-button"
|
||||
:on-press #(do
|
||||
(re-frame/dispatch [::qr-scanner/scan-code
|
||||
{:handler ::qr-scanner/on-scan-success}]))}
|
||||
[icons/icon :main-icons/scan2 {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}]])
|
||||
:on-press #(do
|
||||
(re-frame/dispatch [::qr-scanner/scan-code
|
||||
{:handler ::qr-scanner/on-scan-success}]))}
|
||||
[icons/icon :main-icons/scan2 {:color (colors/theme-colors colors/neutral-100 colors/white)}]])
|
||||
|
||||
(views/defview profile-button []
|
||||
(views/letsubs [{:keys [public-key preferred-name emoji]} [:multiaccount]]
|
||||
[rn/view
|
||||
[chat-icon/emoji-chat-icon-view public-key false preferred-name emoji
|
||||
{:size 28
|
||||
:chat-icon chat-icon.styles/chat-icon-chat-list}]]))
|
||||
{:size 28
|
||||
:chat-icon chat-icon.styles/chat-icon-chat-list}]]))
|
||||
|
||||
(defn home []
|
||||
[rn/keyboard-avoiding-view {:style {:flex 1
|
||||
:background-color (quo2.colors/theme-colors quo2.colors/neutral-5 quo2.colors/neutral-95)}
|
||||
:ignore-offset true}
|
||||
[topbar/topbar {:navigation :none
|
||||
:use-insets true
|
||||
:background (quo2.colors/theme-colors quo2.colors/neutral-5 quo2.colors/neutral-95)
|
||||
:left-component [rn/view {:flex-direction :row :margin-left 20}
|
||||
[profile-button]]
|
||||
:right-component [rn/view {:flex-direction :row :margin-right 20}
|
||||
[scan-button]
|
||||
[qr-button]
|
||||
[notifications-button]]
|
||||
:border-bottom false}]
|
||||
[rn/view {:flex-direction :row
|
||||
:justify-content :space-between
|
||||
:align-items :center
|
||||
:margin-horizontal 20
|
||||
:margin-top 15
|
||||
:margin-bottom 8}
|
||||
[quo2.text/text {:size :heading-1 :weight :semi-bold} (i18n/label :t/messages)]
|
||||
[plus-button]]
|
||||
[chats-list]
|
||||
[tabbar/tabs-counts-subscriptions]])
|
||||
|
||||
[:f>
|
||||
(fn []
|
||||
(quo.react/effect! #(re-frame/dispatch [:get-activity-center-notifications]))
|
||||
[rn/keyboard-avoiding-view {:style {:flex 1
|
||||
:background-color (colors/theme-colors colors/neutral-5 colors/neutral-95)}
|
||||
:ignore-offset true}
|
||||
[topbar/topbar {:navigation :none
|
||||
:use-insets true
|
||||
:background (colors/theme-colors colors/neutral-5 colors/neutral-95)
|
||||
:left-component [rn/view {:flex-direction :row :margin-left 20}
|
||||
[profile-button]]
|
||||
:right-component [rn/view {:flex-direction :row :margin-right 20}
|
||||
[scan-button]
|
||||
[qr-button]
|
||||
[notifications-button]]
|
||||
:border-bottom false}]
|
||||
[rn/view {:flex-direction :row
|
||||
:justify-content :space-between
|
||||
:align-items :center
|
||||
:margin-horizontal 20
|
||||
:margin-top 15
|
||||
:margin-bottom 20}
|
||||
[quo2.text/text {:size :heading-1 :weight :semi-bold} (i18n/label :t/messages)]
|
||||
[plus-button]]
|
||||
[chats-list]
|
||||
[tabbar/tabs-counts-subscriptions]])])
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
(ns status-im.ui2.screens.chat.messages.message
|
||||
(:require [quo.core :as quo]
|
||||
[quo.design-system.colors :as colors]
|
||||
[quo.design-system.colors :as quo.colors]
|
||||
[quo.react-native :as rn]
|
||||
[quo2.components.messages.system-message :as system-message]
|
||||
[quo2.foundations.colors :as quo2.colors]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.foundations.typography :as typography]
|
||||
[re-frame.core :as re-frame]
|
||||
[reagent.core :as reagent]
|
||||
@@ -17,7 +17,6 @@
|
||||
[status-im.ui.components.animation :as animation]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon]
|
||||
[status-im.ui.components.fast-image :as fast-image]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui2.screens.chat.components.reaction-drawer :as reaction-drawer]
|
||||
@@ -37,27 +36,11 @@
|
||||
[status-im.ui2.screens.chat.components.reply :as components.reply]
|
||||
[status-im.utils.config :as config]
|
||||
[status-im.utils.handlers :refer [<sub >evt]]
|
||||
[status-im.utils.security :as security])
|
||||
[status-im.utils.security :as security]
|
||||
[quo2.components.icon :as icons]
|
||||
[status-im.utils.datetime :as time])
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
||||
|
||||
(defn message-timestamp-anim
|
||||
[anim-opacity show-timestamp?]
|
||||
(animation/start
|
||||
(animation/anim-sequence
|
||||
[(animation/timing
|
||||
anim-opacity
|
||||
{:toValue 1
|
||||
:duration 100
|
||||
:easing (.-ease ^js animation/easing)
|
||||
:useNativeDriver true})
|
||||
(animation/timing
|
||||
anim-opacity
|
||||
{:toValue 0
|
||||
:delay 2000
|
||||
:duration 100
|
||||
:easing (.-ease ^js animation/easing)
|
||||
:useNativeDriver true})]) #(reset! show-timestamp? false)))
|
||||
|
||||
(defview mention-element [from]
|
||||
(letsubs [contact-name [:contacts/contact-name-by-identity from]]
|
||||
contact-name))
|
||||
@@ -82,16 +65,15 @@
|
||||
:tiny-icons/tiny-pending)
|
||||
{:width 16
|
||||
:height 12
|
||||
:color (if pinned colors/gray colors/white)
|
||||
:color (if pinned quo.colors/gray quo.colors/white)
|
||||
:accessibility-label (name outgoing-status)}])
|
||||
(when edited-at [rn/text {:style (style/message-status-text)} edited-at-text])]))
|
||||
|
||||
(defn message-timestamp
|
||||
[{:keys [timestamp-str in-popover?]} show-timestamp?]
|
||||
[{:keys [timestamp-str in-popover?]}]
|
||||
(when-not in-popover? ;; We keep track if showing this message in a list in pin-limit-popover
|
||||
(let [anim-opacity (animation/create-value 0)]
|
||||
[rn/animated-view {:style (style/message-timestamp-wrapper) :opacity anim-opacity}
|
||||
(when @show-timestamp? (message-timestamp-anim anim-opacity show-timestamp?))
|
||||
[rn/text
|
||||
{:style (style/message-timestamp-text)
|
||||
:accessibility-label :message-timestamp}
|
||||
@@ -132,7 +114,7 @@
|
||||
(conj acc
|
||||
[rn/text
|
||||
{:style
|
||||
{:color colors/blue
|
||||
{:color quo.colors/blue
|
||||
:text-decoration-line :underline}
|
||||
:on-press
|
||||
#(when (and (security/safe-link? destination)
|
||||
@@ -143,16 +125,16 @@
|
||||
|
||||
"mention"
|
||||
(conj acc
|
||||
[rn/view {:style {:background-color quo2.colors/primary-50-opa-10 :border-radius 6 :padding-horizontal 3}}
|
||||
[rn/view {:style {:background-color colors/primary-50-opa-10 :border-radius 6 :padding-horizontal 3}}
|
||||
[rn/text
|
||||
{:style (merge {:color (if (system-text? content-type) colors/black quo2.colors/primary-50)}
|
||||
{:style (merge {:color (if (system-text? content-type) quo.colors/black colors/primary-50)}
|
||||
(if (system-text? content-type) typography/font-regular typography/font-medium))
|
||||
:on-press (when-not (system-text? content-type)
|
||||
#(>evt [:chat.ui/show-profile literal]))}
|
||||
[mention-element literal]]])
|
||||
"status-tag"
|
||||
(conj acc [rn/text
|
||||
{:style {:color colors/blue
|
||||
{:style {:color quo.colors/blue
|
||||
:text-decoration-line :underline}
|
||||
:on-press
|
||||
#(re-frame/dispatch
|
||||
@@ -207,14 +189,14 @@
|
||||
[{:keys [content-type content] :as message}]
|
||||
[rn/view (style/message-view message)
|
||||
[rn/text
|
||||
{:style {:color colors/white-persist}}
|
||||
{:style {:color quo.colors/white-persist}}
|
||||
(if (seq (:text content))
|
||||
(:text content)
|
||||
(str "Unhandled content-type " content-type))]])
|
||||
|
||||
(defn message-not-sent-text
|
||||
[chat-id message-id]
|
||||
[rn/touchable-highlight
|
||||
[rn/touchable-opacity
|
||||
{:on-press
|
||||
(fn []
|
||||
(re-frame/dispatch
|
||||
@@ -226,7 +208,7 @@
|
||||
[rn/text {:style style/not-sent-text}
|
||||
(i18n/label :t/status-not-sent-tap)]
|
||||
[rn/view style/not-sent-icon
|
||||
[icons/icon :main-icons/warning {:color colors/red}]]]])
|
||||
[icons/icon :main-icons2/warning {:color quo.colors/red}]]]])
|
||||
|
||||
(defn pin-author-name [pinned-by]
|
||||
(let [user-contact @(re-frame/subscribe [:multiaccount/contact])
|
||||
@@ -235,14 +217,14 @@
|
||||
(str " " (if (= pinned-by (user-contact :public-key)) (i18n/label :t/You) (first contact-names)))))
|
||||
|
||||
(defn pin-icon [color size]
|
||||
[icons/icon :main-icons/pin16 {:color color
|
||||
:height size
|
||||
:width size}])
|
||||
[icons/icon :main-icons2/pin {:color color
|
||||
:height size
|
||||
:width size}])
|
||||
|
||||
(defn pinned-by-indicator [pinned-by]
|
||||
[rn/view {:style (style/pin-indicator)
|
||||
:accessibility-label :pinned-by}
|
||||
[pin-icon quo2.colors/primary-50 16]
|
||||
[pin-icon colors/primary-50 16]
|
||||
[quo/text {:size :small
|
||||
:color :main
|
||||
:style (style/pin-author-text)}
|
||||
@@ -254,17 +236,17 @@
|
||||
(= outgoing-status :not-sent))
|
||||
[message-not-sent-text chat-id message-id]))
|
||||
|
||||
(defview message-author-name [from opts]
|
||||
(defview message-author-name [from opts max-length]
|
||||
(letsubs [contact-with-names [:contacts/contact-by-identity from]]
|
||||
(chat.utils/format-author contact-with-names opts)))
|
||||
(chat.utils/format-author contact-with-names opts max-length)))
|
||||
|
||||
(defview message-my-name [opts]
|
||||
(letsubs [contact-with-names [:multiaccount/contact]]
|
||||
(chat.utils/format-author contact-with-names opts)))
|
||||
(chat.utils/format-author contact-with-names opts nil)))
|
||||
|
||||
(defview community-content [{:keys [community-id] :as message}]
|
||||
(letsubs [{:keys [name description verified] :as community} [:communities/community community-id]
|
||||
communities-enabled? [:communities/enabled?]]
|
||||
communities-enabled? [:communities/enabled?]]
|
||||
(when (and communities-enabled? community)
|
||||
[rn/view {:style (assoc (style/message-wrapper message)
|
||||
:margin-vertical 10
|
||||
@@ -273,7 +255,7 @@
|
||||
(when verified
|
||||
[rn/view (style/community-verified)
|
||||
[rn/text {:style {:font-size 13
|
||||
:color colors/blue}} (i18n/label :t/communities-verified)]])
|
||||
:color quo.colors/blue}} (i18n/label :t/communities-verified)]])
|
||||
[rn/view (style/community-message verified)
|
||||
[rn/view {:width 62
|
||||
:padding-left 14}
|
||||
@@ -287,11 +269,11 @@
|
||||
name]
|
||||
[rn/text description]]]
|
||||
[rn/view (style/community-view-button)
|
||||
[rn/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to
|
||||
:community
|
||||
{:community-id (:id community)}])}
|
||||
[rn/touchable-opacity {:on-press #(re-frame/dispatch [:navigate-to
|
||||
:community
|
||||
{:community-id (:id community)}])}
|
||||
[rn/text {:style {:text-align :center
|
||||
:color colors/blue}} (i18n/label :t/view)]]]])))
|
||||
:color quo.colors/blue}} (i18n/label :t/view)]]]])))
|
||||
|
||||
(defn message-content-wrapper
|
||||
"Author, userpic and delivery wrapper"
|
||||
@@ -315,8 +297,10 @@
|
||||
:pointer-events :box-none}
|
||||
[rn/view (style/message-author-userpic)
|
||||
(when (or (and (seq response-to) (:quoted-message message)) last-in-group? pinned)
|
||||
[rn/touchable-highlight {:on-press #(do (when modal (close-modal))
|
||||
(re-frame/dispatch [:chat.ui/show-profile from]))}
|
||||
[rn/touchable-opacity {:active-opacity 1
|
||||
:on-press #(do (when modal (close-modal))
|
||||
(>evt [:bottom-sheet/hide])
|
||||
(re-frame/dispatch [:chat.ui/show-profile from]))}
|
||||
[photos/member-photo from identicon]])]
|
||||
|
||||
[rn/view {:style (style/message-author-wrapper)}
|
||||
@@ -325,6 +309,7 @@
|
||||
[rn/touchable-opacity {:style style/message-author-touchable
|
||||
:disabled in-popover?
|
||||
:on-press #(do (when modal (close-modal))
|
||||
(>evt [:bottom-sheet/hide])
|
||||
(re-frame/dispatch [:chat.ui/show-profile from]))}
|
||||
[message-author-name from {:modal modal}]]
|
||||
[rn/text
|
||||
@@ -371,11 +356,11 @@
|
||||
:visible @visible
|
||||
:on-close #(do (reset! visible false)
|
||||
(reagent/flush))}]
|
||||
[rn/touchable-highlight {:on-press (fn []
|
||||
(reset! visible true)
|
||||
(rn/dismiss-keyboard!))
|
||||
:on-long-press @on-long-press
|
||||
:disabled in-popover?}
|
||||
[rn/touchable-opacity {:on-press (fn []
|
||||
(reset! visible true)
|
||||
(rn/dismiss-keyboard!))
|
||||
:on-long-press @on-long-press
|
||||
:disabled in-popover?}
|
||||
[rn/view {:style (style/image-message style-opts)
|
||||
:accessibility-label :image-message}
|
||||
(when (or (:error @dimensions) (not (:loaded @dimensions)))
|
||||
@@ -383,7 +368,7 @@
|
||||
(merge (dissoc style-opts :opacity)
|
||||
{:flex 1 :align-items :center :justify-content :center :position :absolute})
|
||||
(if (:error @dimensions)
|
||||
[icons/icon :main-icons/cancel]
|
||||
[icons/icon :main-icons2/cancel]
|
||||
[rn/activity-indicator {:animating true}])])
|
||||
[fast-image/fast-image {:style (dissoc style-opts :outgoing)
|
||||
:on-load (image-set-size dimensions)
|
||||
@@ -401,21 +386,12 @@
|
||||
[message]
|
||||
[message.gap/gap message])
|
||||
|
||||
(defmethod ->message constants/content-type-system-text [{:keys [content] :as message}]
|
||||
[rn/view {:accessibility-label :chat-item}
|
||||
[rn/view (style/system-message-body message)
|
||||
[rn/view (style/message-view message)
|
||||
[rn/view (style/message-view-content)
|
||||
[render-parsed-text message (:parsed-text content)]]]]])
|
||||
|
||||
(defn pin-message [{:keys [chat-id pinned] :as message}]
|
||||
(let [pinned-messages @(re-frame/subscribe [:chats/pinned chat-id])]
|
||||
(if (and (not pinned) (> (count pinned-messages) 2))
|
||||
(do
|
||||
(js/setTimeout (fn [] (re-frame/dispatch [:dismiss-keyboard])) 500)
|
||||
(re-frame/dispatch [:show-popover {:view :pin-limit
|
||||
:message message
|
||||
:prevent-closing? true}]))
|
||||
(re-frame/dispatch [::models.pin-message/show-pin-limit-modal chat-id]))
|
||||
(re-frame/dispatch [::models.pin-message/send-pin-message (assoc message :pinned (not pinned))]))))
|
||||
|
||||
(defn on-long-press-fn [on-long-press {:keys [pinned message-pin-enabled outgoing edit-enabled show-input? community?] :as message} content]
|
||||
@@ -447,15 +423,19 @@
|
||||
:icon :main-icons2/pin
|
||||
:id (if pinned :unpin :pin)}])
|
||||
[{:type :danger
|
||||
:on-press #(re-frame/dispatch
|
||||
[:chat.ui/delete-message-for-me message
|
||||
config/delete-message-for-me-undo-time-limit-ms])
|
||||
:on-press (fn []
|
||||
(when pinned (pin-message message))
|
||||
(re-frame/dispatch
|
||||
[:chat.ui/delete-message-for-me message
|
||||
config/delete-message-for-me-undo-time-limit-ms]))
|
||||
:label (i18n/label :t/delete-for-me)
|
||||
:icon :main-icons2/delete
|
||||
:id :delete-for-me}]
|
||||
(when (and outgoing config/delete-message-enabled?)
|
||||
[{:type :danger
|
||||
:on-press #(re-frame/dispatch [:chat.ui/soft-delete-message message])
|
||||
:on-press (fn []
|
||||
(when pinned (pin-message message))
|
||||
(re-frame/dispatch [:chat.ui/soft-delete-message message]))
|
||||
:label (i18n/label :t/delete-for-everyone)
|
||||
:icon :main-icons2/delete
|
||||
:id :delete-for-all}]))))
|
||||
@@ -470,7 +450,7 @@
|
||||
(js/setTimeout #(on-long-press-fn on-long-press message content) 200))
|
||||
(on-long-press-fn on-long-press message content)))]
|
||||
(reset! ref on-long-press)
|
||||
[rn/touchable-highlight
|
||||
[rn/touchable-opacity
|
||||
(when-not modal
|
||||
{:delay-long-press 100
|
||||
:on-long-press on-long-press
|
||||
@@ -513,27 +493,27 @@
|
||||
[{:type :main
|
||||
:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message])
|
||||
:id :reply
|
||||
:icon :main-icons/reply-context20
|
||||
:icon :main-icons2/reply-context20
|
||||
:label (i18n/label :t/message-reply)}
|
||||
{:type :main
|
||||
:on-press #(react/copy-to-clipboard (get content :text))
|
||||
:id :copy
|
||||
:icon :main-icons/copy-context20
|
||||
:icon :main-icons2/copy-context20
|
||||
:label (i18n/label :t/copy-text)}]
|
||||
(when message-pin-enabled [{:type :main
|
||||
:on-press #(pin-message message)
|
||||
:id :pin
|
||||
:icon :main-icons/pin-context20
|
||||
:icon :main-icons2/pin-context20
|
||||
:label (if pinned (i18n/label :t/unpin) (i18n/label :t/pin))}]))))]
|
||||
(reset! ref on-long-press)
|
||||
[message-content-wrapper message
|
||||
[rn/touchable-highlight (when-not modal
|
||||
{:disabled in-popover?
|
||||
:on-press (fn []
|
||||
(rn/dismiss-keyboard!)
|
||||
(reset! show-timestamp? true))
|
||||
:delay-long-press 100
|
||||
:on-long-press on-long-press})
|
||||
[rn/touchable-opacity (when-not modal
|
||||
{:disabled in-popover?
|
||||
:on-press (fn []
|
||||
(rn/dismiss-keyboard!)
|
||||
(reset! show-timestamp? true))
|
||||
:delay-long-press 100
|
||||
:on-long-press on-long-press})
|
||||
[rn/view style/message-view-wrapper
|
||||
[message-timestamp message show-timestamp?]
|
||||
[rn/view (style/message-view message)
|
||||
@@ -554,21 +534,21 @@
|
||||
(on-long-press
|
||||
(when-not outgoing
|
||||
[{:type :main
|
||||
:icon :main-icons/stickers-context20
|
||||
:icon :main-icons2/stickers-context20
|
||||
:on-press #(when pack
|
||||
(re-frame/dispatch [:chat.ui/show-profile from]))
|
||||
:label (i18n/label :t/see-sticker-set)}])))]
|
||||
(reset! ref on-long-press)
|
||||
[message-content-wrapper message
|
||||
[rn/touchable-highlight (when-not modal
|
||||
{:disabled in-popover?
|
||||
:accessibility-label :sticker-message
|
||||
:on-press (fn [_]
|
||||
(when pack
|
||||
(re-frame/dispatch [:stickers/open-sticker-pack (str pack)]))
|
||||
(rn/dismiss-keyboard!))
|
||||
:delay-long-press 100
|
||||
:on-long-press on-long-press})
|
||||
[rn/touchable-opacity (when-not modal
|
||||
{:disabled in-popover?
|
||||
:accessibility-label :sticker-message
|
||||
:on-press (fn [_]
|
||||
(when pack
|
||||
(re-frame/dispatch [:stickers/open-sticker-pack (str pack)]))
|
||||
(rn/dismiss-keyboard!))
|
||||
:delay-long-press 100
|
||||
:on-long-press on-long-press})
|
||||
[fast-image/fast-image {:style {:margin 10 :width 140 :height 140}
|
||||
:source {:uri (str (-> content :sticker :url) "&download=true")}}]]
|
||||
reaction-picker]))
|
||||
@@ -582,31 +562,31 @@
|
||||
(concat [{:type :main
|
||||
:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message])
|
||||
:id :reply
|
||||
:icon :main-icons/reply-context20
|
||||
:icon :main-icons2/reply-context20
|
||||
:label (i18n/label :t/message-reply)}
|
||||
{:type :main
|
||||
:on-press #(re-frame/dispatch [:chat.ui/save-image-to-gallery (:image content)])
|
||||
:id :save
|
||||
:icon :main-icons/save-context20
|
||||
:icon :main-icons2/save-context20
|
||||
:label (i18n/label :t/save-image-library)}
|
||||
{:type :main
|
||||
:on-press #(images/download-image-http
|
||||
(get-in message [:content :image]) preview/share)
|
||||
:id :share
|
||||
:icon :main-icons/share-context20
|
||||
:icon :main-icons2/share-context20
|
||||
:label (i18n/label :t/share-image)}]
|
||||
[{:type :danger
|
||||
:on-press #(re-frame/dispatch
|
||||
[:chat.ui/delete-message-for-me message
|
||||
config/delete-message-for-me-undo-time-limit-ms])
|
||||
:label (i18n/label :t/delete-for-me)
|
||||
:icon :main-icons/delete-context20
|
||||
:icon :main-icons2/delete-context20
|
||||
:id :delete-for-me}]
|
||||
(when (and outgoing config/delete-message-enabled?)
|
||||
[{:type :danger
|
||||
:on-press #(re-frame/dispatch [:chat.ui/soft-delete-message message])
|
||||
:label (i18n/label :t/delete-for-everyone)
|
||||
:icon :main-icons/delete-context20
|
||||
:icon :main-icons2/delete-context20
|
||||
:id :delete}]))))]
|
||||
(reset! ref on-long-press)
|
||||
[message-content-wrapper message
|
||||
@@ -625,29 +605,29 @@
|
||||
(let [on-long-press (fn [] (on-long-press [{:type :main
|
||||
:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message])
|
||||
:label (i18n/label :t/message-reply)
|
||||
:icon :main-icons/reply-context20
|
||||
:icon :main-icons2/reply
|
||||
:id :reply}
|
||||
{:type :main
|
||||
:on-press #(pin-message message)
|
||||
:label (i18n/label (if pinned :t/unpin-from-chat :t/pin-to-chat))
|
||||
:icon :main-icons/pin-context20
|
||||
:icon :main-icons2/pin-context20
|
||||
:id (if pinned :unpin :pin)}
|
||||
{:type :danger
|
||||
:on-press #(re-frame/dispatch
|
||||
[:chat.ui/delete-message-for-me message
|
||||
config/delete-message-for-me-undo-time-limit-ms])
|
||||
:label (i18n/label :t/delete-for-me)
|
||||
:icon :main-icons/delete-context20
|
||||
:icon :main-icons2/delete-context20
|
||||
:id :delete-for-me}
|
||||
(when (and outgoing config/delete-message-enabled?)
|
||||
{:type :danger
|
||||
:on-press #(re-frame/dispatch [:chat.ui/soft-delete-message message])
|
||||
:label (i18n/label :t/delete-for-everyone)
|
||||
:icon :main-icons/delete-context20
|
||||
:icon :main-icons2/delete-context20
|
||||
:id :delete})]))]
|
||||
(reset! ref on-long-press)
|
||||
[message-content-wrapper message
|
||||
[rn/touchable-highlight
|
||||
[rn/touchable-opacity
|
||||
(when-not modal
|
||||
{:on-long-press on-long-press
|
||||
:on-press (fn []
|
||||
@@ -667,15 +647,15 @@
|
||||
(i18n/label :t/contact-request-pending)]
|
||||
[rn/activity-indicator {:animating true
|
||||
:size :small
|
||||
:color colors/gray}]])
|
||||
:color quo.colors/gray}]])
|
||||
|
||||
(defn contact-request-status-accepted []
|
||||
[quo/text {:style {:color colors/green}
|
||||
[quo/text {:style {:color quo.colors/green}
|
||||
:weight :medium}
|
||||
(i18n/label :t/contact-request-accepted)])
|
||||
|
||||
(defn contact-request-status-declined []
|
||||
[quo/text {:style {:color colors/red}
|
||||
[quo/text {:style {:color quo.colors/red}
|
||||
:weight :medium}
|
||||
(i18n/label :t/contact-request-declined)])
|
||||
|
||||
@@ -734,11 +714,12 @@
|
||||
#(on-emoji-press %))}]))
|
||||
on-long-press (atom nil)]
|
||||
[rn/view
|
||||
{:style (merge (when (and (not in-pinned-view?) (or mentioned pinned)) {:background-color quo2.colors/primary-50-opa-5
|
||||
{:style (merge (when (and (not in-pinned-view?) (or mentioned pinned)) {:background-color colors/primary-50-opa-5
|
||||
:border-radius 16
|
||||
:margin-bottom 4})
|
||||
(when (or mentioned pinned last-in-group?) {:margin-top 8})
|
||||
{:margin-horizontal 8})}
|
||||
|
||||
(when pinned
|
||||
[rn/view {:style (style/pin-indicator-container)}
|
||||
[pinned-by-indicator pinned-by]])
|
||||
@@ -748,9 +729,9 @@
|
||||
[reaction-row/message-reactions message reactions nil on-emoji-press on-long-press]])) ;; TODO: pass on-open-drawer function
|
||||
|
||||
(defn message-render-fn
|
||||
[{:keys [outgoing] :as message}
|
||||
[{:keys [outgoing whisper-timestamp] :as message}
|
||||
_
|
||||
{:keys [group-chat public? community? current-public-key show-input? message-pin-enabled edit-enabled]}]
|
||||
{:keys [group-chat public? community? current-public-key show-input? edit-enabled]}]
|
||||
[chat-message
|
||||
(assoc message
|
||||
:incoming-group (and group-chat (not outgoing))
|
||||
@@ -759,9 +740,10 @@
|
||||
:community? community?
|
||||
:current-public-key current-public-key
|
||||
:show-input? show-input?
|
||||
:message-pin-enabled message-pin-enabled
|
||||
:message-pin-enabled true
|
||||
:in-pinned-view? true
|
||||
:pinned true
|
||||
:timestamp-str (time/timestamp->time whisper-timestamp)
|
||||
:edit-enabled edit-enabled)])
|
||||
|
||||
(def list-key-fn #(or (:message-id %) (:value %)))
|
||||
@@ -770,13 +752,13 @@
|
||||
(let [pinned-messages (vec (vals (<sub [:chats/pinned chat-id])))
|
||||
current-chat (<sub [:chats/current-chat])
|
||||
community (<sub [:communities/community (:community-id current-chat)])]
|
||||
[rn/view
|
||||
[rn/view {:accessibility-label :pinned-messages-list}
|
||||
[rn/text {:style (merge typography/heading-1 typography/font-semi-bold {:margin-horizontal 20
|
||||
:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)})}
|
||||
:color (colors/theme-colors colors/neutral-100 colors/white)})}
|
||||
(i18n/label :t/pinned-messages)]
|
||||
(when community
|
||||
[rn/view {:style {:flex-direction :row
|
||||
:background-color (quo2.colors/theme-colors quo2.colors/neutral-10 quo2.colors/neutral-80)
|
||||
:background-color (colors/theme-colors colors/neutral-10 colors/neutral-80)
|
||||
:border-radius 20
|
||||
:align-items :center
|
||||
:align-self :flex-start
|
||||
@@ -784,44 +766,58 @@
|
||||
:padding 4
|
||||
:margin-top 8}}
|
||||
[chat-icon/chat-icon-view-toolbar chat-id (:group-chat current-chat) (:chat-name current-chat) (:color current-chat) (:emoji current-chat) 22]
|
||||
[rn/text {:style {:margin-left 6 :margin-right 4 :color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}} (:name community)]
|
||||
[rn/text {:style {:margin-left 6 :margin-right 4 :color (colors/theme-colors colors/neutral-100 colors/white)}} (:name community)]
|
||||
[icons/icon
|
||||
:main-icons/chevron-right
|
||||
{:color (quo2.colors/theme-colors quo2.colors/neutral-50 quo2.colors/neutral-40)
|
||||
:main-icons2/chevron-right
|
||||
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40)
|
||||
:width 12
|
||||
:height 12}]
|
||||
[rn/text {:style {:margin-left 4
|
||||
:margin-right 8
|
||||
:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}} (str "# " (:chat-name current-chat))]])
|
||||
[list/flat-list
|
||||
{:data pinned-messages
|
||||
:render-fn message-render-fn
|
||||
:key-fn list-key-fn
|
||||
:separator [rn/view {:background-color (quo2.colors/theme-colors quo2.colors/neutral-10 quo2.colors/neutral-80) :height 1 :margin-top 8}]}]]))
|
||||
:color (colors/theme-colors colors/neutral-100 colors/white)}} (str "# " (:chat-name current-chat))]])
|
||||
(if (> (count pinned-messages) 0)
|
||||
[list/flat-list
|
||||
{:data pinned-messages
|
||||
:render-fn message-render-fn
|
||||
:key-fn list-key-fn
|
||||
:separator [rn/view {:background-color (colors/theme-colors colors/neutral-10 colors/neutral-80) :height 1 :margin-top 8}]}]
|
||||
[rn/view {:style {:justify-content :center
|
||||
:align-items :center
|
||||
:flex 1
|
||||
:margin-top 20}}
|
||||
[rn/view {:style {:width 120
|
||||
:height 120
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:border-width 1}} [icons/icon :main-icons2/placeholder]]
|
||||
[rn/text {:style (merge typography/paragraph-1 typography/font-semi-bold {:margin-top 20})} (i18n/label :t/no-pinned-messages)]
|
||||
[rn/text {:style (merge typography/paragraph-2 typography/font-regular)}
|
||||
(i18n/label (if community :t/no-pinned-messages-community-desc :t/no-pinned-messages-desc))]])]))
|
||||
|
||||
(defmethod ->message constants/content-type-pin [{:keys [from in-popover? timestamp-str chat-id] :as message} {:keys [modal close-modal]}]
|
||||
(defn pin-system-message [{:keys [from in-popover? timestamp-str chat-id] :as message} {:keys [modal close-modal]}]
|
||||
(let [response-to (:response-to (:content message))]
|
||||
[rn/touchable-opacity {:on-press (fn []
|
||||
(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
{:content #(pinned-messages-list chat-id)}]))
|
||||
(>evt [:bottom-sheet/show-sheet
|
||||
{:content #(pinned-messages-list chat-id)}]))
|
||||
:active-opacity 1
|
||||
:style (merge {:flex-direction :row :margin-vertical 8} (style/message-wrapper message))}
|
||||
[rn/view {:style {:width photos.style/default-size
|
||||
:height photos.style/default-size
|
||||
:margin-right 16
|
||||
:border-radius photos.style/default-size
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:background-color quo2.colors/primary-50-opa-10}}
|
||||
[pin-icon quo2.colors/primary-50 16]]
|
||||
[rn/view {:style {:width photos.style/default-size
|
||||
:height photos.style/default-size
|
||||
:margin-right 16
|
||||
:border-radius photos.style/default-size
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:background-color colors/primary-50-opa-10}
|
||||
:accessibility-label :content-type-pin-icon}
|
||||
[pin-icon colors/primary-50 16]]
|
||||
[rn/view
|
||||
[rn/view {:style {:flex-direction :row :align-items :center}}
|
||||
[rn/touchable-opacity {:style style/message-author-touchable
|
||||
:disabled in-popover?
|
||||
:on-press #(do (when modal (close-modal))
|
||||
(re-frame/dispatch [:chat.ui/show-profile from]))}
|
||||
[message-author-name from {:modal modal}]]
|
||||
[rn/text {:style {:font-size 13}} (str " " (i18n/label :pinned-a-message))]
|
||||
[message-author-name from {:modal modal} 20]]
|
||||
[rn/text {:style {:font-size 13}} (str " " (i18n/label :t/pinned-a-message))]
|
||||
[rn/text
|
||||
{:style (merge
|
||||
{:padding-left 5
|
||||
@@ -831,33 +827,45 @@
|
||||
timestamp-str]]
|
||||
[quoted-message response-to (:quoted-message message) true]]]))
|
||||
|
||||
(defmethod ->message constants/content-type-system-text [{:keys [content quoted-message] :as message}]
|
||||
(if quoted-message
|
||||
[pin-system-message message]
|
||||
[rn/view {:accessibility-label :chat-item}
|
||||
[rn/view (style/system-message-body message)
|
||||
[rn/view (style/message-view message)
|
||||
[rn/view (style/message-view-content)
|
||||
[render-parsed-text message (:parsed-text content)]]]]]))
|
||||
|
||||
(defn pinned-banner [chat-id]
|
||||
(let [pinned-messages (<sub [:chats/pinned chat-id])
|
||||
latest-pin-text (get-in (last (vals pinned-messages)) [:content :text])
|
||||
pins-count (count (seq pinned-messages))]
|
||||
(when (> pins-count 0)
|
||||
[rn/touchable-opacity
|
||||
{:style {:height 50
|
||||
:background-color quo2.colors/primary-50-opa-20
|
||||
:flex-direction :row
|
||||
:align-items :center
|
||||
:padding-horizontal 20
|
||||
:padding-vertical 10}
|
||||
:active-opacity 1
|
||||
:on-press (fn []
|
||||
(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
{:content #(pinned-messages-list chat-id)}]))}
|
||||
[pin-icon (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white) 20]
|
||||
{:accessibility-label :pinned-banner
|
||||
:style {:height 50
|
||||
:background-color colors/primary-50-opa-20
|
||||
:flex-direction :row
|
||||
:align-items :center
|
||||
:padding-horizontal 20
|
||||
:padding-vertical 10}
|
||||
:active-opacity 1
|
||||
:on-press (fn []
|
||||
(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
{:content #(pinned-messages-list chat-id)}]))}
|
||||
[pin-icon (colors/theme-colors colors/neutral-100 colors/white) 20]
|
||||
[rn/text {:number-of-lines 1
|
||||
:style (merge typography/paragraph-2 {:margin-left 10
|
||||
:margin-right 50
|
||||
:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)})} latest-pin-text]
|
||||
[rn/view {:style {:position :absolute
|
||||
:right 22
|
||||
:height 20
|
||||
:width 20
|
||||
:border-radius 8
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:background-color quo2.colors/neutral-80-opa-5}}
|
||||
[rn/text {:style (merge typography/label typography/font-medium {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)})} pins-count]]])))
|
||||
:color (colors/theme-colors colors/neutral-100 colors/white)})} latest-pin-text]
|
||||
[rn/view {:accessibility-label :pins-count
|
||||
:style {:position :absolute
|
||||
:right 22
|
||||
:height 20
|
||||
:width 20
|
||||
:border-radius 8
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:background-color colors/neutral-80-opa-5}}
|
||||
[rn/text {:style (merge typography/label typography/font-medium {:color (colors/theme-colors colors/neutral-100 colors/white)})} pins-count]]])))
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
(ns status-im.ui2.screens.chat.messages.pinned-message
|
||||
(:require [status-im.i18n.i18n :as i18n]
|
||||
[quo.react :as react]
|
||||
[quo2.reanimated :as reanimated]
|
||||
[quo.react-native :as rn]
|
||||
[quo2.foundations.typography :as typography]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.switcher.constants :as constants]
|
||||
[status-im.chat.models.pin-message :as models.pin-message]
|
||||
[status-im.utils.handlers :refer [<sub >evt]]
|
||||
[status-im.ui2.screens.chat.messages.style :as style]
|
||||
[quo2.components.icon :as icons]))
|
||||
|
||||
(defn pin-limit-popover [chat-id pinned-messages-list]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [{:keys [width]} (constants/dimensions)
|
||||
show-pin-limit-modal? (<sub [:chats/pin-modal chat-id])
|
||||
opacity-animation (reanimated/use-shared-value 0)
|
||||
z-index-animation (reanimated/use-shared-value -1)]
|
||||
(react/effect! #(do
|
||||
(reanimated/set-shared-value opacity-animation (reanimated/with-timing (if show-pin-limit-modal? 1 0)))
|
||||
(reanimated/set-shared-value z-index-animation (reanimated/with-timing (if show-pin-limit-modal? 10 -1)))))
|
||||
[reanimated/view {:style (reanimated/apply-animations-to-style
|
||||
{:opacity opacity-animation
|
||||
:z-index z-index-animation}
|
||||
(style/pin-popover width))
|
||||
:accessibility-label :pin-limit-popover}
|
||||
[rn/view {:style (style/pin-alert-container)}
|
||||
[rn/view {:style (style/pin-alert-circle)}
|
||||
[rn/text {:style {:color colors/danger-50}} "!"]]]
|
||||
[rn/view {:style {:margin-left 8}}
|
||||
[rn/text {:style (merge typography/paragraph-1 typography/font-semi-bold {:color (colors/theme-colors colors/white colors/neutral-100)})} (i18n/label :t/cannot-pin-title)]
|
||||
[rn/text {:style (merge typography/paragraph-2 typography/font-regular {:color (colors/theme-colors colors/white colors/neutral-100)})} (i18n/label :t/cannot-pin-desc)]
|
||||
[rn/touchable-opacity
|
||||
{:accessibility-label :view-pinned-messages
|
||||
:active-opacity 1
|
||||
:on-press (fn []
|
||||
(>evt [::models.pin-message/hide-pin-limit-modal chat-id])
|
||||
(>evt [:bottom-sheet/show-sheet
|
||||
{:content #(pinned-messages-list chat-id)}]))
|
||||
:style (style/view-pinned-messages)}
|
||||
[rn/text {:style (merge typography/paragraph-2 typography/font-medium {:color colors/white})} (i18n/label :t/view-pinned-messages)]]]
|
||||
[rn/touchable-opacity {:accessibility-label :close-pin-limit-popover
|
||||
:active-opacity 1
|
||||
:on-press #(>evt [::models.pin-message/hide-pin-limit-modal chat-id])
|
||||
:style {:position :absolute
|
||||
:top 16
|
||||
:right 16}}
|
||||
[icons/icon :main-icons2/close {:color (colors/theme-colors colors/white colors/neutral-100)
|
||||
:height 8
|
||||
:width 8}]]]))])
|
||||
@@ -0,0 +1,38 @@
|
||||
(ns status-im.ui2.screens.chat.messages.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn pin-popover [width]
|
||||
{:position :absolute
|
||||
:width (- width 16)
|
||||
:left 8
|
||||
:background-color (colors/theme-colors colors/neutral-80-opa-90 colors/white-opa-90)
|
||||
:flex-direction :row
|
||||
:border-radius 16
|
||||
:padding 12})
|
||||
|
||||
(defn pin-alert-container []
|
||||
{:background-color (colors/theme-colors colors/neutral-80-opa-20 colors/white-opa-40)
|
||||
:width 36
|
||||
:height 36
|
||||
:border-radius 18
|
||||
:justify-content :center
|
||||
:align-items :center})
|
||||
|
||||
(defn pin-alert-circle []
|
||||
{:width 18
|
||||
:height 18
|
||||
:border-radius 9
|
||||
:border-color colors/danger-50-opa-40
|
||||
:border-width 1
|
||||
:justify-content :center
|
||||
:align-items :center})
|
||||
|
||||
(defn view-pinned-messages []
|
||||
{:background-color colors/primary-60
|
||||
:border-radius 8
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:padding-horizontal 8
|
||||
:padding-vertical 4
|
||||
:align-self :flex-start
|
||||
:margin-top 10})
|
||||
@@ -1,7 +1,7 @@
|
||||
(ns status-im.ui2.screens.chat.messages.view
|
||||
(:require [reagent.core :as reagent]
|
||||
[quo.react-native :as rn]
|
||||
[quo2.foundations.colors :as quo2.colors]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.utils.handlers :refer [<sub >evt]]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
@@ -36,10 +36,10 @@
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:border-radius (/ 24 2)
|
||||
:background-color (quo2.colors/theme-colors quo2.colors/neutral-80-opa-70 quo2.colors/white-opa-70)}}
|
||||
:background-color (colors/theme-colors colors/neutral-80-opa-70 colors/white-opa-70)}}
|
||||
;;TODO icon from quo2 should be used instead!
|
||||
[icons/icon
|
||||
:main-icons/arrow-down {:color (quo2.colors/theme-colors quo2.colors/white quo2.colors/neutral-100)
|
||||
:main-icons/arrow-down {:color (colors/theme-colors colors/white colors/neutral-100)
|
||||
:width 12
|
||||
:height 12}]]])
|
||||
|
||||
@@ -188,4 +188,4 @@
|
||||
:inverted (when platform/ios? true)
|
||||
:style (when platform/android? {:scaleY -1})})]
|
||||
(when @show-floating-scroll-down-button
|
||||
[floating-scroll-down-button show-input?])]))
|
||||
[floating-scroll-down-button show-input?])]))
|
||||
|
||||
@@ -6,12 +6,13 @@
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[quo.react-native :as rn]
|
||||
[quo2.components.buttons.button :as quo2.button]
|
||||
[quo2.foundations.colors :as quo2.colors]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.navigation.state :as navigation.state]
|
||||
[status-im.ui2.screens.chat.messages.view :as messages]
|
||||
[status-im.utils.handlers :refer [<sub >evt]]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui2.screens.chat.messages.pinned-message :as pinned-message]
|
||||
[re-frame.db]
|
||||
[status-im.ui2.screens.chat.messages.message :as message]))
|
||||
|
||||
@@ -30,15 +31,17 @@
|
||||
:size 32
|
||||
:width 32
|
||||
:accessibility-label "back-button"
|
||||
:on-press #(>evt [:navigate-back])}
|
||||
[icons/icon :main-icons/arrow-left {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}]])
|
||||
:on-press #(do
|
||||
(>evt [:close-chat])
|
||||
(>evt [:navigate-back]))}
|
||||
[icons/icon :main-icons/arrow-left {:color (colors/theme-colors colors/neutral-100 colors/white)}]])
|
||||
|
||||
(defn search-button []
|
||||
[quo2.button/button {:type :grey
|
||||
:size 32
|
||||
:width 32
|
||||
:accessibility-label "search-button"}
|
||||
[icons/icon :main-icons/search {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}]])
|
||||
[icons/icon :main-icons/search {:color (colors/theme-colors colors/neutral-100 colors/white)}]])
|
||||
|
||||
(defn navigate-back-handler []
|
||||
(when (and (not @navigation.state/curr-modal) (= (get @re-frame.db/app-db :view-id) :chat))
|
||||
@@ -68,6 +71,7 @@
|
||||
(if group-chat
|
||||
[invitation-requests chat-id admins]
|
||||
(when-not mutual-contact-requests-enabled? [add-contact-bar chat-id])))
|
||||
[pinned-message/pin-limit-popover chat-id message/pinned-messages-list]
|
||||
[message/pinned-banner chat-id]
|
||||
;;MESSAGES LIST
|
||||
[messages/messages-view
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
and adds unicode ellipsis in between"
|
||||
[address]
|
||||
(when address
|
||||
(str (subs address 0 6) "\u2026" (subs address (- (count address) 4) (count address)))))
|
||||
(str (subs address 0 6) "\u2026" (subs address (- (count address) 3) (count address)))))
|
||||
|
||||
(defn get-shortened-checksum-address [address]
|
||||
(when address
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
|
||||
"owner": "status-im",
|
||||
"repo": "status-go",
|
||||
"version": "v0.111.6",
|
||||
"commit-sha1": "5c3435c12fe430740e2b5d2a05066d423c61acec",
|
||||
"src-sha256": "0v1scizr5lsbm4w3bysc2cl1v9wsd9b27bzjn80p4rw6gca7s2l1"
|
||||
"version": "659ab120b66dca9521c93cbd8516582634059074",
|
||||
"commit-sha1": "659ab120b66dca9521c93cbd8516582634059074",
|
||||
"src-sha256": "1x5pasyf4ivrw92pil4ljl6cfw3g6wn4drgxxszcdzih0pwzggx4"
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ class TestrailReport(BaseTestReport):
|
||||
self.user = environ.get('TESTRAIL_USER')
|
||||
|
||||
self.run_id = None
|
||||
self.suite_id = 48
|
||||
#self.suite_id = 48
|
||||
self.suite_id = 5274
|
||||
self.project_id = 14
|
||||
|
||||
self.outcomes = {
|
||||
@@ -104,43 +105,47 @@ class TestrailReport(BaseTestReport):
|
||||
test_cases['pr'] = dict()
|
||||
test_cases['nightly'] = dict()
|
||||
test_cases['upgrade'] = dict()
|
||||
## PR e2e
|
||||
test_cases['pr']['critical'] = 730
|
||||
test_cases['pr']['contacts'] = 50831
|
||||
test_cases['pr']['public_chat'] = 50654
|
||||
test_cases['pr']['one_to_one_chat'] = 50655
|
||||
test_cases['pr']['group_chat'] = 50656
|
||||
test_cases['pr']['onboarding'] = 50659
|
||||
test_cases['pr']['recovery'] = 50660
|
||||
test_cases['pr']['wallet'] = 50661
|
||||
test_cases['pr']['send_tx'] = 50662
|
||||
test_cases['pr']['keycard_tx'] = 50663
|
||||
test_cases['pr']['1_1_chat_commands'] = 50825
|
||||
test_cases['pr']['ens'] = 50827
|
||||
test_cases['pr']['sync'] = 50834
|
||||
test_cases['pr']['browser'] = 50812
|
||||
## PR e2e old UI
|
||||
# test_cases['pr']['critical'] = 730
|
||||
# test_cases['pr']['contacts'] = 50831
|
||||
# test_cases['pr']['public_chat'] = 50654
|
||||
# test_cases['pr']['one_to_one_chat'] = 50655
|
||||
# test_cases['pr']['group_chat'] = 50656
|
||||
# test_cases['pr']['onboarding'] = 50659
|
||||
# test_cases['pr']['recovery'] = 50660
|
||||
# test_cases['pr']['wallet'] = 50661
|
||||
# test_cases['pr']['send_tx'] = 50662
|
||||
# test_cases['pr']['keycard_tx'] = 50663
|
||||
# test_cases['pr']['1_1_chat_commands'] = 50825
|
||||
# test_cases['pr']['ens'] = 50827
|
||||
# test_cases['pr']['sync'] = 50834
|
||||
# test_cases['pr']['browser'] = 50812
|
||||
|
||||
test_cases['pr']['critical'] = 50955
|
||||
test_cases['pr']['one_to_one_chat'] = 50956
|
||||
|
||||
|
||||
## Nightly e2e
|
||||
test_cases['nightly']['medium'] = 736
|
||||
test_cases['nightly']['chat'] = 50811
|
||||
test_cases['nightly']['browser'] = 50826
|
||||
test_cases['nightly']['profile'] = 50828
|
||||
test_cases['nightly']['deep_link'] = 50836
|
||||
test_cases['nightly']['share_profile'] = 50837
|
||||
test_cases['nightly']['chat_2'] = 50838
|
||||
test_cases['nightly']['group_chat'] = 50839
|
||||
test_cases['nightly']['pairing'] = 50840
|
||||
test_cases['nightly']['activity_center'] = 50833
|
||||
test_cases['nightly']['timeline'] = 50842
|
||||
test_cases['nightly']['community'] = 50841
|
||||
test_cases['nightly']['permissions'] = 50843
|
||||
test_cases['nightly']['scan qr'] = 50844
|
||||
test_cases['nightly']['mentions'] = 50845
|
||||
test_cases['nightly']['mutual_contact_requests'] = 50857
|
||||
test_cases['nightly']['keycard'] = 50850
|
||||
test_cases['nightly']['wallet'] = 50851
|
||||
# test_cases['nightly']['medium'] = 736
|
||||
# test_cases['nightly']['chat'] = 50811
|
||||
# test_cases['nightly']['browser'] = 50826
|
||||
# test_cases['nightly']['profile'] = 50828
|
||||
# test_cases['nightly']['deep_link'] = 50836
|
||||
# test_cases['nightly']['share_profile'] = 50837
|
||||
# test_cases['nightly']['chat_2'] = 50838
|
||||
# test_cases['nightly']['group_chat'] = 50839
|
||||
# test_cases['nightly']['pairing'] = 50840
|
||||
# test_cases['nightly']['activity_center'] = 50833
|
||||
# test_cases['nightly']['timeline'] = 50842
|
||||
# test_cases['nightly']['community'] = 50841
|
||||
# test_cases['nightly']['permissions'] = 50843
|
||||
# test_cases['nightly']['scan qr'] = 50844
|
||||
# test_cases['nightly']['mentions'] = 50845
|
||||
# test_cases['nightly']['mutual_contact_requests'] = 50857
|
||||
# test_cases['nightly']['keycard'] = 50850
|
||||
# test_cases['nightly']['wallet'] = 50851
|
||||
## Upgrade e2e
|
||||
test_cases['upgrade']['general'] = 881
|
||||
# test_cases['upgrade']['general'] = 881
|
||||
|
||||
case_ids = list()
|
||||
for arg in argv:
|
||||
@@ -148,7 +153,8 @@ class TestrailReport(BaseTestReport):
|
||||
key, value = arg.split('=')
|
||||
case_ids = value.split(',')
|
||||
if len(case_ids) == 0:
|
||||
if 'critical' in argv:
|
||||
# if 'critical' in argv:
|
||||
if 'new_ui_critical' in argv:
|
||||
for category in test_cases['pr']:
|
||||
for case in self.get_cases([test_cases['pr'][category]]):
|
||||
case_ids.append(case['id'])
|
||||
|
||||
@@ -60,7 +60,7 @@ test_dapp_web_url = "status-im.github.io/dapp"
|
||||
test_dapp_url = 'https://simpledapp.status.im/'
|
||||
test_dapp_name = 'simpledapp.status.im'
|
||||
|
||||
emojis = {'thumbs-up': 2, 'thumbs-down': 3, 'love': 1, 'laugh': 4, 'angry': 6, 'sad': 5}
|
||||
emojis = {'thumbs-up': 5, 'thumbs-down': 6, 'love': 1, 'laugh': 4, 'angry': 2, 'sad': 3}
|
||||
|
||||
|
||||
with open(os.sep.join(__file__.split(os.sep)[:-1]) + '/../../../translations/en.json') as json_file:
|
||||
|
||||
@@ -227,6 +227,7 @@ class TestOneToOneChatMultipleSharedDevices(MultipleSharedDeviceTestCase):
|
||||
self.chat_2 = self.home_2.get_chat(self.default_username_1).click()
|
||||
|
||||
@marks.testrail_id(6315)
|
||||
# moved
|
||||
def test_1_1_chat_message_reaction(self):
|
||||
message_from_sender = "Message sender"
|
||||
self.device_1.just_fyi("Sender start 1-1 chat, set emoji and check counter")
|
||||
@@ -378,6 +379,7 @@ class TestOneToOneChatMultipleSharedDevices(MultipleSharedDeviceTestCase):
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@marks.testrail_id(5315)
|
||||
# moved
|
||||
def test_1_1_chat_non_latin_message_to_newly_added_contact_with_profile_picture_on_different_networks(self):
|
||||
self.home_1.get_app_from_background()
|
||||
self.home_2.get_app_from_background()
|
||||
@@ -1212,3 +1214,103 @@ class TestEnsStickersMultipleDevicesMerged(MultipleSharedDeviceTestCase):
|
||||
and account.qr_code_image.is_element_displayed()):
|
||||
self.errors.append('No self profile pop-up data displayed after My_profile button tap')
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
|
||||
@pytest.mark.xdist_group(name="one_2")
|
||||
@marks.new_ui_critical
|
||||
class TestOneToOneChatMultipleSharedDevices(MultipleSharedDeviceTestCase):
|
||||
|
||||
def prepare_devices(self):
|
||||
self.drivers, self.loop = create_shared_drivers(2)
|
||||
self.device_1, self.device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
|
||||
self.home_1 = self.device_1.create_user(enable_notifications=True)
|
||||
self.home_2 = self.device_2.create_user(enable_notifications=True)
|
||||
self.profile_1 = self.home_1.get_profile_view()
|
||||
self.public_key_1, self.default_username_1 = self.home_1.get_public_key_and_username(return_username=True)
|
||||
self.public_key_2, self.default_username_2 = self.home_2.get_public_key_and_username(return_username=True)
|
||||
self.profile_1.chats_tab.click()
|
||||
self.chat_1 = self.home_1.add_contact(self.public_key_2)
|
||||
self.chat_1.send_message('hey')
|
||||
self.home_2.click_system_back_button_until_element_is_shown()
|
||||
self.home_2.chats_tab.click()
|
||||
self.chat_2 = self.home_2.get_chat(self.default_username_1).click()
|
||||
|
||||
@marks.testrail_id(702730)
|
||||
def test_1_1_chat_message_reaction(self):
|
||||
message_from_sender = "Message sender"
|
||||
self.device_1.just_fyi("Sender start 1-1 chat, set emoji and check counter")
|
||||
self.chat_1.send_message(message_from_sender)
|
||||
self.chat_1.set_reaction(message_from_sender)
|
||||
|
||||
message_sender = self.chat_1.chat_element_by_text(message_from_sender)
|
||||
if message_sender.emojis_below_message() != 1:
|
||||
self.errors.append("Counter of reaction is not updated on your own message!")
|
||||
|
||||
self.device_2.just_fyi("Receiver sets own emoji and verifies counter on received message in 1-1 chat")
|
||||
message_receiver = self.chat_2.chat_element_by_text(message_from_sender)
|
||||
if message_receiver.emojis_below_message() != 1:
|
||||
self.errors.append("Counter of reaction is not updated on received message!")
|
||||
self.chat_2.set_reaction(message_from_sender)
|
||||
for counter in message_sender.emojis_below_message(), message_receiver.emojis_below_message():
|
||||
if counter != 2:
|
||||
self.errors.append('Counter is not updated after setting emoji from receiver!')
|
||||
|
||||
self.device_2.just_fyi("Receiver pick the same emoji and verify that counter will decrease for both users")
|
||||
self.chat_2.set_reaction(message_from_sender)
|
||||
for counter in message_sender.emojis_below_message(), message_receiver.emojis_below_message():
|
||||
if counter != 1:
|
||||
self.errors.append('Counter is not decreased after re-tapping emoji from receiver!')
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@marks.testrail_id(702745)
|
||||
def test_1_1_chat_non_latin_messages_stack_update_profile_photo(self):
|
||||
self.home_1.click_system_back_button_until_element_is_shown()
|
||||
self.home_1.browser_tab.click() #temp, until profile is on browser tab
|
||||
self.profile_1.edit_profile_picture('sauce_logo.png')
|
||||
self.profile_1.chats_tab.click()
|
||||
|
||||
self.chat_2.just_fyi("Send messages with non-latin symbols")
|
||||
messages = ['hello', '¿Cómo estás tu año?', 'ё, доброго вечерочка', '® æ ç ♥']
|
||||
|
||||
for message in messages:
|
||||
self.chat_2.send_message(message)
|
||||
if not self.chat_1.chat_message_input.is_element_displayed():
|
||||
self.chat_1.click_system_back_button_until_element_is_shown()
|
||||
self.home_1.get_chat(self.default_username_2).click()
|
||||
|
||||
for message in messages:
|
||||
if not self.chat_1.chat_element_by_text(message).is_element_displayed():
|
||||
self.errors.append("Message with test '%s' was not received" % message)
|
||||
|
||||
self.chat_2.just_fyi("Checking updated member photo, timestamp and username on message")
|
||||
timestamp = self.chat_2.chat_element_by_text(messages[0]).timestamp
|
||||
sent_time_variants = self.chat_2.convert_device_time_to_chat_timestamp()
|
||||
if timestamp not in sent_time_variants:
|
||||
self.errors.append('Timestamp on message %s does not correspond expected [%s]' % (timestamp, *sent_time_variants))
|
||||
|
||||
for message in [messages[1], messages[2]]:
|
||||
if self.chat_2.chat_element_by_text(message).member_photo.is_element_displayed():
|
||||
self.errors.append('%s is not stack to 1st(they are sent in less than 5 minutes)!' % message)
|
||||
|
||||
self.chat_1.just_fyi("Sending message while user is still not in contacts")
|
||||
message = 'profile_photo'
|
||||
self.chat_1.send_message(message)
|
||||
self.chat_2.chat_element_by_text(message).wait_for_visibility_of_element(30)
|
||||
if not self.chat_2.chat_element_by_text(message).member_photo.is_element_differs_from_template("member.png",
|
||||
diff=5):
|
||||
self.errors.append("Image of user in 1-1 chat is updated even when user is not added to contacts!")
|
||||
|
||||
self.chat_1.just_fyi("Users add to contacts each other")
|
||||
[home.click_system_back_button_until_element_is_shown() for home in (self.home_1, self.home_2)]
|
||||
[home.browser_tab.click() for home in (self.home_1, self.home_2)]
|
||||
self.profile_1.add_contact_via_contacts_list(self.public_key_2)
|
||||
self.profile_2 = self.home_2.get_profile_view()
|
||||
self.profile_2.add_contact_via_contacts_list(self.public_key_1)
|
||||
|
||||
self.chat_1.just_fyi("Go back to chat view and checking that profile photo is updated")
|
||||
[home.chats_tab.click() for home in (self.home_1, self.home_2)]
|
||||
if not self.chat_2.chat_message_input.is_element_displayed():
|
||||
self.home_2.get_chat(self.default_username_1).click()
|
||||
if self.chat_2.chat_element_by_text(message).member_photo.is_element_differs_from_template("member.png", diff=5):
|
||||
self.errors.append("Image of user in 1-1 chat is too different from template!")
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@@ -4,6 +4,8 @@ testrail_case_id = pytest.mark.testrail_case_id
|
||||
testrail_id = pytest.mark.testrail_id # atomic tests
|
||||
critical = pytest.mark.critical
|
||||
medium = pytest.mark.medium
|
||||
# new ui
|
||||
new_ui_critical = pytest.mark.new_ui_critical
|
||||
|
||||
flaky = pytest.mark.flaky
|
||||
upgrade = pytest.mark.upgrade
|
||||
|
||||
@@ -79,8 +79,28 @@ class HomeButton(TabButton):
|
||||
self.click_until_presence_of_element(element)
|
||||
return self.navigate()
|
||||
|
||||
class CommunitiesTab(TabButton):
|
||||
def __init__(self, driver):
|
||||
super().__init__(driver, accessibility_id="communities-stack-tab")
|
||||
|
||||
|
||||
class ChatsTab(TabButton):
|
||||
def __init__(self, driver):
|
||||
super().__init__(driver, accessibility_id="chats-stack-tab")
|
||||
|
||||
def navigate(self):
|
||||
from views.home_view import HomeView
|
||||
return HomeView(self.driver)
|
||||
|
||||
|
||||
class WalletTab(TabButton):
|
||||
def __init__(self, driver):
|
||||
super().__init__(driver, accessibility_id="wallet-stack-tab")
|
||||
|
||||
class BrowserTab(TabButton):
|
||||
def __init__(self, driver):
|
||||
super().__init__(driver, accessibility_id="browser-stack-tab")
|
||||
|
||||
class DappTabButton(TabButton):
|
||||
def __init__(self, driver):
|
||||
super().__init__(driver, xpath="//*[contains(@content-desc,'tab, 2 out of 5')]")
|
||||
@@ -224,13 +244,19 @@ class BaseView(object):
|
||||
self.send_message_button = SendMessageButton(self.driver)
|
||||
self.send_contact_request_button = Button(self.driver, translation_id="send-request")
|
||||
|
||||
# Tabs
|
||||
# Old UI Tabs
|
||||
self.home_button = HomeButton(self.driver)
|
||||
self.wallet_button = WalletButton(self.driver)
|
||||
self.profile_button = ProfileButton(self.driver)
|
||||
self.dapp_tab_button = DappTabButton(self.driver)
|
||||
self.status_button = StatusButton(self.driver)
|
||||
|
||||
# New UI Tabs
|
||||
self.communities_tab = CommunitiesTab(self.driver)
|
||||
self.chats_tab = ChatsTab(self.driver)
|
||||
self.browser_tab = BrowserTab(self.driver)
|
||||
self.wallet_tab = WalletTab(self.driver)
|
||||
|
||||
self.yes_button = Button(self.driver, xpath="//*[@text='YES' or @text='GOT IT']")
|
||||
self.no_button = Button(self.driver, translation_id="no")
|
||||
self.back_button = BackButton(self.driver)
|
||||
@@ -351,7 +377,9 @@ class BaseView(object):
|
||||
def click_system_back_button_until_element_is_shown(self, attempts=3, element='home'):
|
||||
counter = 0
|
||||
if element == 'home':
|
||||
element = self.home_button
|
||||
element = self.chats_tab
|
||||
# Old UI
|
||||
# element = self.home_button
|
||||
while not element.is_element_displayed(1) and counter <= attempts:
|
||||
self.driver.press_keycode(4)
|
||||
try:
|
||||
@@ -573,7 +601,9 @@ class BaseView(object):
|
||||
|
||||
def get_public_key_and_username(self, return_username=False):
|
||||
self.driver.info("Get public key and username")
|
||||
profile_view = self.profile_button.click()
|
||||
# profile_view = self.profile_button.click()
|
||||
self.browser_tab.click() # temp, until profile is on browser tab
|
||||
profile_view = self.get_profile_view()
|
||||
default_username = profile_view.default_username_text.text
|
||||
profile_view.share_my_profile_button.click()
|
||||
profile_view.public_key_text.wait_for_visibility_of_element(20)
|
||||
|
||||
@@ -151,14 +151,11 @@ class ChatElementByText(Text):
|
||||
return TimeStampText(self.driver, self.locator)
|
||||
|
||||
@property
|
||||
def timestamp_on_tap(self):
|
||||
timestamp_element = Text(self.driver, xpath="//*[@content-desc='message-timestamp']")
|
||||
try:
|
||||
self.sent_status_checkmark.wait_for_element(30)
|
||||
self.sent_status_checkmark.click_until_presence_of_element(timestamp_element)
|
||||
return timestamp_element.text
|
||||
except (NoSuchElementException, TimeoutException):
|
||||
return None
|
||||
def timestamp(self):
|
||||
class TimeStampText(Button):
|
||||
def __init__(self, driver, parent_locator: str):
|
||||
super().__init__(driver, xpath="%s//*[@content-desc='message-timestamp']" % parent_locator)
|
||||
return TimeStampText(self.driver, self.locator).text
|
||||
|
||||
@property
|
||||
def member_photo(self):
|
||||
@@ -228,21 +225,39 @@ class ChatElementByText(Text):
|
||||
return RepliedToUsernameText(self.driver, self.message_locator).text
|
||||
except NoSuchElementException:
|
||||
return ''
|
||||
# Old UI
|
||||
# def emojis_below_message(self, emoji: str = 'thumbs-up', own=True):
|
||||
# class EmojisNumber(Text):
|
||||
# def __init__(self, driver, parent_locator: str):
|
||||
# self.own = own
|
||||
# self.emoji = emoji
|
||||
# self.emojis_id = 'emoji-' + str(emojis[self.emoji]) + '-is-own-' + str(self.own).lower()
|
||||
# super().__init__(driver, prefix=parent_locator, xpath="/../..//*[@content-desc='%s']" % self.emojis_id)
|
||||
#
|
||||
# @property
|
||||
# def text(self):
|
||||
# try:
|
||||
# text = self.find_element().text
|
||||
# self.driver.info("%s is '%s' for '%s' where my reaction is set on message is '%s'" % (self.name, text, self.emoji, str(self.own)))
|
||||
# return text
|
||||
# except NoSuchElementException:
|
||||
# return 0
|
||||
#
|
||||
# return int(EmojisNumber(self.driver, self.locator).text)
|
||||
|
||||
def emojis_below_message(self, emoji: str = 'thumbs-up', own=True):
|
||||
def emojis_below_message(self, emoji: str = 'thumbs-up'):
|
||||
class EmojisNumber(Text):
|
||||
def __init__(self, driver, parent_locator: str):
|
||||
self.own = own
|
||||
self.emoji = emoji
|
||||
self.emojis_id = 'emoji-' + str(emojis[self.emoji]) + '-is-own-' + str(self.own).lower()
|
||||
super().__init__(driver, prefix=parent_locator, xpath="/../..//*[@content-desc='%s']" % self.emojis_id)
|
||||
self.emojis_id = 'emoji-reaction-%s' % str(emojis[self.emoji])
|
||||
super().__init__(driver, prefix=parent_locator, xpath="/../..//*[@content-desc='%s']/android.widget.TextView" % self.emojis_id)
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
try:
|
||||
text = self.find_element().text
|
||||
self.driver.info("%s is '%s' for '%s' where my reaction is set on message is '%s'" % (self.name, text, self.emoji, str(self.own)))
|
||||
return text
|
||||
self.driver.info("%s is '%s' for '%s'" % (self.name, text, self.emoji))
|
||||
return int(text.strip())
|
||||
except NoSuchElementException:
|
||||
return 0
|
||||
|
||||
@@ -858,7 +873,9 @@ class ChatView(BaseView):
|
||||
self.chat_element_by_text(message).long_press_element()
|
||||
else:
|
||||
self.element_by_text_part(message).long_press_element()
|
||||
element = Button(self.driver, accessibility_id='pick-emoji-%s' % key)
|
||||
# old UI
|
||||
# element = Button(self.driver, accessibility_id='pick-emoji-%s' % key)
|
||||
element = Button(self.driver, accessibility_id='emoji-picker-%s' % key)
|
||||
element.click()
|
||||
element.wait_for_invisibility_of_element()
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 7.9 KiB |
@@ -290,7 +290,8 @@ class HomeView(BaseView):
|
||||
chat.public_key_edit_box.click()
|
||||
chat.public_key_edit_box.send_keys(public_key)
|
||||
one_to_one_chat = self.get_chat_view()
|
||||
chat.confirm_until_presence_of_element(one_to_one_chat.chat_message_input)
|
||||
chat.confirm()
|
||||
# chat.confirm_until_presence_of_element(one_to_one_chat.chat_message_input)
|
||||
if add_in_contacts and one_to_one_chat.add_to_contacts.is_element_displayed():
|
||||
one_to_one_chat.add_to_contacts.click()
|
||||
if nickname:
|
||||
|
||||
@@ -362,6 +362,17 @@ class ProfileView(BaseView):
|
||||
from views.chat_view import ChatView
|
||||
return ChatView(self.driver)
|
||||
|
||||
def add_contact_via_contacts_list(self, public_key):
|
||||
self.driver.info("Adding user to Contacts via Profile > Contacts")
|
||||
self.contacts_button.wait_and_click(30)
|
||||
self.add_new_contact_button.wait_and_click()
|
||||
chat = self.get_chat_view()
|
||||
chat.public_key_edit_box.click()
|
||||
chat.public_key_edit_box.send_keys(public_key)
|
||||
chat.confirm_until_presence_of_element(self.add_new_contact_button)
|
||||
self.click_system_back_button_until_element_is_shown()
|
||||
|
||||
|
||||
def add_custom_network(self, rpc_url: str, name: str, symbol: str, netwrok_id:str):
|
||||
self.driver.info("## Add custom network", device=False)
|
||||
self.advanced_button.click()
|
||||
|
||||
@@ -191,13 +191,18 @@ class SignInView(BaseView):
|
||||
self.create_password_input.set_value(password)
|
||||
self.confirm_your_password_input.set_value(password)
|
||||
self.next_button.click()
|
||||
self.maybe_later_button.wait_for_visibility_of_element(30)
|
||||
if enable_notifications:
|
||||
self.enable_notifications_button.click()
|
||||
else:
|
||||
self.maybe_later_button.click_until_presence_of_element(self.lets_go_button)
|
||||
self.lets_go_button.click_until_absense_of_element(self.lets_go_button)
|
||||
self.profile_button.wait_for_visibility_of_element(30)
|
||||
|
||||
# Old UI
|
||||
# self.maybe_later_button.wait_for_visibility_of_element(30)
|
||||
# if enable_notifications:
|
||||
# self.enable_notifications_button.click()
|
||||
# else:
|
||||
# self.maybe_later_button.click_until_presence_of_element(self.lets_go_button)
|
||||
# self.lets_go_button.click_until_absense_of_element(self.lets_go_button)
|
||||
# self.profile_button.wait_for_visibility_of_element(30)
|
||||
|
||||
self.chats_tab.wait_for_visibility_of_element(30)
|
||||
|
||||
self.driver.info("## New multiaccount is created successfully!", device=False)
|
||||
return self.get_home_view()
|
||||
|
||||
|
||||
+20
-3
@@ -1480,6 +1480,7 @@
|
||||
"page-camera-request-blocked": "camera requests blocked. To enable camera requests go to Settings",
|
||||
"nickname": "Nickname",
|
||||
"add-nickname": "Add a nickname (optional)",
|
||||
"edit-nickname": "Edit nickname",
|
||||
"nickname-description": "Nicknames help you identify others in Status.\nOnly you can see the nicknames you’ve added",
|
||||
"accept": "Accept",
|
||||
"group-invite": "Group invite",
|
||||
@@ -1510,6 +1511,8 @@
|
||||
"name-optional": "Name (optional)",
|
||||
"mute": "Mute",
|
||||
"unmute": "Unmute",
|
||||
"mute-chat": "Mute chat",
|
||||
"unmute-chat": "Unmute chat",
|
||||
"mute-community": "Mute community",
|
||||
"unmute-community": "Unmute community",
|
||||
"scan-tokens": "Scan tokens",
|
||||
@@ -1573,7 +1576,7 @@
|
||||
"master-account": "Master account",
|
||||
"back-up": "Back up",
|
||||
"key-on-device": "Private key is saved on this device",
|
||||
"whats-trending": "See what`s trending",
|
||||
"whats-trending": "See what's trending",
|
||||
"seed-key-uid-mismatch": "Seed doesn't match",
|
||||
"seed-key-uid-mismatch-desc-1": "The seed phrase you entered does not match {{multiaccount-name}}",
|
||||
"seed-key-uid-mismatch-desc-2": "To manage keys for this account verify your seed phrase and try again.",
|
||||
@@ -1775,7 +1778,7 @@
|
||||
"new-ui": "New UI",
|
||||
"send-contact-request-message": "To start a chat you need to become contacts",
|
||||
"contact-request": "Contact request",
|
||||
"contact-requests": "Contact Requests",
|
||||
"contact-requests": "Contact requests",
|
||||
"say-hi": "Say hi",
|
||||
"opened": "Opened",
|
||||
"accepted": "Accepted",
|
||||
@@ -1810,6 +1813,9 @@
|
||||
"pin-to-channel": "Pin to the channel",
|
||||
"unpin-from-chat": "Unpin from the chat",
|
||||
"unpin-from-channel": "Unpin from the channel",
|
||||
"cannot-pin-title": "You can't pin this message!",
|
||||
"cannot-pin-desc": "You can only pin a max of 3 messages.\nUnpin at least one to pin a new one.",
|
||||
"view-pinned-messages": "View pinned messages",
|
||||
"copy-text": "Copy text",
|
||||
"edit-message": "Edit message",
|
||||
"save-image-library": "Save image to library",
|
||||
@@ -1819,8 +1825,19 @@
|
||||
"admin": "Admin",
|
||||
"replies": "Replies",
|
||||
"identity-verification": "Identity verification",
|
||||
"identity-verification-request": "Identity verification request",
|
||||
"identity-verification-request-sent": "asked you",
|
||||
"membership": "Membership",
|
||||
"jump-to": "Jump to",
|
||||
"blank-messages-text": "Your messages will be here",
|
||||
"groups": "Groups"
|
||||
"groups": "Groups",
|
||||
"shell-placeholder-title": "Your apps will run here",
|
||||
"shell-placeholder-subtitle": "Open tabs of your communities, messages,\nwallet account and browser windows",
|
||||
"invite-friends-to-status": "Invite friends to status",
|
||||
"share-invite-link": "Share an invite link",
|
||||
"pending-requests": "Pending requests",
|
||||
"received": "Received",
|
||||
"sent": "Sent",
|
||||
"no-pinned-messages-desc": "This chat doesn't have any pinned messages.",
|
||||
"no-pinned-messages-community-desc": "This channel doesn't have any pinned messages."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user