Compare commits

...
Author SHA1 Message Date
Richard Ramos 98b0c218b4 status-go: extract node configuration to individual tables 2022-01-07 11:05:31 -04:00
Shivek Khurana eeb8db00f7 🔁 SWAP UI
CLJ Refactor x Hydra are cool. Add modal

Swap UI

Swap token input and gas slider in place

Asset selector sheets

WIP

Selector events

Tabs are back

Hide Swaps behind ff, add cards and modals for advanced settings

Fix lint issues

Signed-off-by: Shivek Khurana <shivek@status.im>
2022-01-05 23:57:51 +05:30
Churikova Tetiana 3499cdc123 e2e: update ci and made first group 2021-12-29 18:02:58 +01:00
Parvesh Monu 44b8a4c0f4 fix community invite link is not resolved (#12956) 2021-12-28 16:38:42 +05:30
Michele Balistreri e45f959c27 support PUK and seed recovery during onboarding
Signed-off-by: Michele Balistreri <michele@bitgamma.com>
2021-12-27 20:35:33 +03:00
Roman Volosovskyi cd86a1065b [#12936] Add accessibility-id to message timestamp 2021-12-27 13:44:21 +02:00
Parvesh Monu ffba396e4d fix distortion of app theme due to change in system theme (#12934) 2021-12-27 15:24:45 +05:30
Du Cheng 961692251c [#12930] Remove public chat references 2021-12-24 21:10:10 +02:00
Parvesh Monu 94fde74b88 Reorder community chats and categories using drag and drop (#12854) 2021-12-24 21:04:44 +05:30
Roman Volosovskyi 5402c217df [#12913] Properly handle syncing of pubchat delition/installation after offline 2021-12-24 17:03:34 +02:00
Parvesh Monu 4588dafe02 Open user profile from membership requests (#12950) 2021-12-24 20:27:30 +05:30
Parvesh Monu edbb06a7c3 Add community members to mentionable list (#12926) 2021-12-24 20:25:00 +05:30
Roman Volosovskyi 29a7e4dc1c [#12918] Show history gaps only in public chats and communities 2021-12-24 13:40:01 +02:00
Roman Volosovskyi de3c08ba18 Add sync history for 2 mins option for testing purposes 2021-12-21 21:42:42 +02:00
Churikova Tetiana e5ff46fdd5 e2e: fixes
Signed-off-by: Churikova Tetiana <churikova.tm@gmail.com>
2021-12-21 14:58:41 +01:00
Churikova Tetiana be79a8e54c e2e: fix
Signed-off-by: Churikova Tetiana <churikova.tm@gmail.com>
2021-12-17 14:48:24 +01:00
Roman Volosovskyi 36d96c6264 [#12912] Fix endless loading after removing messages in group chat 2021-12-17 12:44:08 +02:00
jinhojang6 340213ece5 Update translations
Signed-off-by: Shivek Khurana <shivek@status.im>
2021-12-17 15:54:06 +05:30
Churikova Tetiana 032a8786ff e2e: fixes
Signed-off-by: Churikova Tetiana <churikova.tm@gmail.com>
2021-12-16 17:46:48 +01:00
Michele Balistreri 15d671275f avoid closing/opening NFC dialog in rapid sequence
Signed-off-by: Michele Balistreri <michele@bitgamma.com>
2021-12-16 18:32:08 +03:00
Ajay Sivan f3533c5f99 Show message timestamps on tap, remove from bubbles
Signed-off-by: andrey <motor4ik@gmail.com>
2021-12-16 13:15:35 +01:00
yevh-berdnyk b18d7b6a2a e2e: setup and teardown with shared drivers 2021-12-15 21:14:16 +02:00
Roman Volosovskyi 2c23ab4c5d [#12867] Add accessibility IDs to toggles for e2e 2021-12-15 18:57:25 +02:00
Roman Volosovskyi 646add59b8 [#12782] Re-open app on the last open chat 2021-12-15 13:45:42 +02:00
dependabot[bot] cd111aeeec Bump lxml from 4.6.2 to 4.6.5 in /test/appium
Bumps [lxml](https://github.com/lxml/lxml) from 4.6.2 to 4.6.5.
- [Release notes](https://github.com/lxml/lxml/releases)
- [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt)
- [Commits](https://github.com/lxml/lxml/compare/lxml-4.6.2...lxml-4.6.5)

---
updated-dependencies:
- dependency-name: lxml
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-13 19:45:32 +00:00
Roman Volosovskyi 81b60ce86c [#12907] Fix error on 'Accept and join' in referral chat 2021-12-13 16:42:08 +02:00
Roman Volosovskyi 356dd89147 [#12898] Fix endless loading in pubchats 2021-12-13 13:53:09 +02:00
Roman Volosovskyi a6102c1133 Fix unique :key warning on add pubchat screen 2021-12-13 12:59:03 +02:00
Andrea Maria Piana 7dfa93f2a2 Fix syncing public chats 2021-12-10 17:55:12 +00:00
Michele Balistreri c6271557b1 fix pairing for new cards
Signed-off-by: Michele Balistreri <michele@bitgamma.com>
2021-12-10 17:45:27 +03:00
Shivek Khurana d1f3a33194 Add 1.18 to RELEASES.MD
Signed-off-by: Shivek Khurana <shivek@status.im>
2021-12-09 15:46:55 +05:30
Shivek Khurana c6371bf3b3 Bump release to 1.18.0
Signed-off-by: Shivek Khurana <shivek@status.im>
2021-12-08 16:39:24 +05:30
jinhojang6 59bc7af00c Update translations 2021-12-07 17:14:40 +01:00
Michele Balistreri a5737856c0 sync leftovers
Signed-off-by: Michele Balistreri <michele@bitgamma.com>
2021-12-07 17:39:42 +03:00
Roman Volosovskyi 7d0944cfd1 [#12813] Remove POA network from the list 2021-12-07 13:08:31 +02:00
Roman Volosovskyi 099bcf8a17 Allow sending messages offline (non-datasync messages) 2021-12-07 12:25:26 +02:00
Churikova Tetiana 4504f07a2a e2e: small fixes
Signed-off-by: Churikova Tetiana <churikova.tm@gmail.com>
2021-12-06 17:58:19 +01:00
Brian Sztamfater b5d8a641c6 Improve mention suggestions list
Signed-off-by: Brian Sztamfater <brian@status.im>
2021-12-06 10:45:49 -03:00
Parvesh Monu 11d3bf094a Decline pending group invites from user, when user is blocked (#12878) 2021-12-06 18:34:13 +05:30
Andrea Maria Piana 32c0f9719c [Fixes: #12869] Save contact on unblock 2021-12-06 11:46:29 +00:00
andrey b587a8f85e [#11895] No last message when opening chat but it was visible on chat-preview
Signed-off-by: andrey <motor4ik@gmail.com>
2021-12-03 12:15:19 +01:00
andrey 53d7d719f0 [#12858] [ANDROID] Changing screen orientation results in breaking images / layout
Signed-off-by: andrey <motor4ik@gmail.com>
2021-12-03 12:13:51 +01:00
Michele Balistreri 46c023920c sync activity center
Signed-off-by: Michele Balistreri <michele@bitgamma.com>
2021-12-02 17:27:57 +03:00
137 changed files with 2694 additions and 1127 deletions
+2
View File
@@ -32,3 +32,5 @@ METRICS_ENABLED=0
DELETE_MESSAGE_ENABLED=1
COLLECTIBLES_ENABLED=1
COMMANDS_ENABLED=1
TWO_MINUTES_SYNCING=1
SWAP_ENABLED=1
+1
View File
@@ -33,3 +33,4 @@ COMMUNITIES_ENABLED=1
COMMUNITIES_MANAGEMENT_ENABLED=1
METRICS_ENABLED=0
DELETE_MESSAGE_ENABLED=1
TWO_MINUTES_SYNCING=1
+1
View File
@@ -35,3 +35,4 @@ COMMUNITIES_ENABLED=1
COMMUNITIES_MANAGEMENT_ENABLED=1
METRICS_ENABLED=0
DELETE_MESSAGE_ENABLED=1
TWO_MINUTES_SYNCING=1
+35
View File
@@ -1,3 +1,38 @@
## 1.18.0 Release notes :package:
### Features
* 🚦Online status indicator <https://github.com/status-im/status-react/issues/12547>
* 👛 Add possibility to select wallet for ENS names <https://github.com/status-im/status-react/pull/12761>
* 🐞 Bug reporting and logs sharing improvements <https://github.com/status-im/status-react/pull/12773>
* 🍳 Simplify Keycard onboarding by using defaults for pairing password <https://github.com/status-im/status-react/issues/12685>
* 💾 Backup contacts via Waku <https://github.com/status-im/status-react/issues/12550>
* 🪙 Added ENS token <https://github.com/status-im/status-react/issues/12807>
* 🌠 Using react-native-fast-image for stickers <https://github.com/status-im/status-react/pull/12769>
#### Message reliability improvements
* 𝌗 Show loading until all messages are processed <https://github.com/status-im/status-react/pull/12834>
* 💪🏼 More robust message confirmations handling <https://github.com/status-im/status-react/pull/12824>
* 📶 Allow sending messages when offline <https://github.com/status-im/status-react/issues/11889>
#### Improvements on device pairing
* 🔔💻 Notification in activity centre now sync across devices <https://github.com/status-im/status-react/issues/12571>
* ✅💻 Sync read messages across devices <https://github.com/status-im/status-react/pull/12698>
#### Network settings improvements
* ✨ Add Binance Smart chain <https://github.com/status-im/status-react/pull/12809>
* ♏︎ Added custom network symbol <https://github.com/status-im/status-react/commit/c16b0860ec921a61c119857de27c6f29e301e4f6>
* 💰 Network fee on xDai chain <https://github.com/status-im/status-react/pull/12845>
* 📇 Support tx history (in a way) when on BSC chains <https://github.com/status-im/status-react/pull/12843>
* 🙅🏻 Remove POA network from the list <https://github.com/status-im/status-react/pull/12887>
### Bug Fixes
* Fix activity center community invite preview <https://github.com/status-im/status-react/pull/12803>
* Missing notifications for some contacts <https://github.com/status-im/status-react/issues/12776>
* Fixes scrolling issues on the 'Max priority fee' bottom sheet <https://github.com/status-im/status-react/issues/12530>
* Handle Keycard timeout on iOS 15 <https://github.com/status-im/status-react/pull/12826>
* Allow user to be reinvited to the same group after leaving <https://github.com/status-im/status-react/pull/12814>
* Fix for "TypeError: Network request failed" when switching to Goerli network <https://github.com/status-im/status-react/issues/11964>
* Fix for Spin of death on adding custom NFT token contract <https://github.com/status-im/status-react/issues/12365>
* Changing screen orientation results in breaking images / layout <https://github.com/status-im/status-react/issues/12858>
## 1.17
### For iOS and Android
+1 -1
View File
@@ -1 +1 @@
1.17.0
1.18.0
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="alert_text">#ffffff</color>
</resources>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="alert_text">#000000</color>
</resources>
@@ -8,6 +8,7 @@
<item name="android:forceDarkAllowed">true</item>
<item name="android:windowTranslucentStatus">false</item>
<item name="android:statusBarColor">?attr/backgroundColor</item>
<item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
</style>
<style name="LightTheme" parent="AppTheme">
@@ -21,4 +22,9 @@
<item name="backgroundColor">#000000</item>
<item name="android:windowLightStatusBar">false</item>
</style>
<style name="AlertDialogTheme" parent="Base.Theme.AppCompat.Light.Dialog.Alert">
<item name="android:textColor">@color/alert_text</item>
<item name="android:textColorPrimary">@color/alert_text</item>
</style>
</resources>
+7 -7
View File
@@ -3,15 +3,15 @@ pipeline {
agent { label 'linux' }
parameters {
string(
name: 'NETWORK',
description: 'Name of test network to use.',
defaultValue: 'ropsten',
)
string(
name: 'APK_NAME',
description: 'Filename of APK uploaded to SauceLabs.',
)
string(
name: 'KEYWORD_EXPRESSION',
description: 'This will run tests which contain names that match the given string expression (Optional)',
defaultValue: '',
)
}
options {
@@ -56,8 +56,8 @@ pipeline {
--rerun_count=2 \
--testrail_report=True \
-m testrail_id \
-m "not upgrade" \
--network=${params.NETWORK} \
-m \"not upgrade\" \
-k \"${params.KEYWORD_EXPRESSION}\" \
--apk=${params.APK_NAME}
"""
}
+9 -9
View File
@@ -8,15 +8,10 @@ pipeline {
description: 'Name of the branch to checkout and build.',
defaultValue: 'develop',
)
string(
name: 'NETWORK',
description: 'Name of test network to use.',
defaultValue: 'ropsten',
)
string(
name: 'TEST_MARKERS',
description: 'Marker expression for matching tests to run.',
defaultValue: 'critical or high',
defaultValue: '',
)
string(
name: 'APK_NAME',
@@ -26,9 +21,14 @@ pipeline {
name: 'PR_ID',
description: 'ID of the Pull Request triggering this build.',
)
string(
name: 'KEYWORD_EXPRESSION',
description: 'This will run tests which contain names that match the given string expression (Optional)',
defaultValue: '',
)
string(
name: 'TR_CASE_IDS',
description: 'IDs of the TestRail case, separated by a comma. (Optional)',
description: 'IDs of the TestRail case, separated by a comma (Optional)',
defaultValue: '',
)
}
@@ -91,8 +91,8 @@ pipeline {
--numprocesses 15 \
--rerun_count=2 \
--testrail_report=True \
-m "${params.TEST_MARKERS}" \
--network=${params.NETWORK} \
-m \"${params.TEST_MARKERS}\" \
-k \"${params.KEYWORD_EXPRESSION}\" \
--apk=${params.APK_NAME} \
--build=PR-${params.PR_ID} \
--pr_number=${params.PR_ID} \
+21 -10
View File
@@ -3,11 +3,6 @@ pipeline {
agent { label 'linux' }
parameters {
string(
name: 'NETWORK',
description: 'Name of test network to use',
defaultValue: 'ropsten',
)
string(
name: 'APK_NAME',
description: 'Filename of APK uploaded to SauceLabs (base for upgrade, usually release build)',
@@ -16,6 +11,16 @@ pipeline {
name: 'APK_NAME_UPGRADE',
description: 'Filename of APK of upgraded application (installed on top of base)',
)
string(
name: 'KEYWORD_EXPRESSION',
description: 'This will run tests which contain names that match the given string expression (Optional)',
defaultValue: '',
)
string(
name: 'TR_CASE_IDS',
description: 'IDs of the TestRail case, separated by a comma (Optional)',
defaultValue: '',
)
}
options {
@@ -32,7 +37,12 @@ pipeline {
} }
}
stage('Test') {
steps {
steps { script {
/* for managing optional arguments */
def extraPytestOpts = ''
if (params.TR_CASE_IDS != '') {
extraPytestOpts = "--run_testrail_ids='${params.TR_CASE_IDS}'"
}
withCredentials([
usernamePassword(
credentialsId: 'test-rail-api',
@@ -56,18 +66,19 @@ pipeline {
dir('test/appium/tests') {
sh """
python3 -m pytest \
-m upgrade \
-m "upgrade" \
-k \"${params.KEYWORD_EXPRESSION}\" \
--numprocesses 15 \
--rerun_count=2 \
--testrail_report=True \
--network=${params.NETWORK} \
--apk=${params.APK_NAME} \
--apk_upgrade=${params.APK_NAME_UPGRADE}
--apk_upgrade=${params.APK_NAME_UPGRADE} \
${extraPytestOpts}
"""
}
}
}
}
} }
}
post {
+5 -1
View File
@@ -163,4 +163,8 @@ used for some abi encoding primitives
## "rn-emoji-keyboard": "https://github.com/status-im/rn-emoji-keyboard"
Used for taking emoji input, for custom emoji thumbnails for community channels
Used for taking emoji input, for custom emoji thumbnails for community channels
## "react-native-draggable-flatlist": "https://github.com/computerjazz/react-native-draggable-flatlist"
A drag-and-drop-enabled FlatList component for React Native
+3 -3
View File
@@ -230,7 +230,7 @@ PODS:
- React
- react-native-status (1.0.0):
- React
- react-native-status-keycard (2.5.36):
- react-native-status-keycard (2.5.37):
- Keycard
- React
- react-native-webview (11.3.0):
@@ -626,7 +626,7 @@ SPEC CHECKSUMS:
FBLazyVector: 3bb422f41b18121b71783a905c10e58606f7dc3e
FBReactNativeSpec: f2c97f2529dd79c083355182cc158c9f98f4bd6e
Folly: b73c3869541e86821df3c387eb0af5f65addfab4
glog: 5bc68409594b19a3e5c5cbced7b1ecf61053b709
glog: 61334f8bdb4deea07543d4fbac3fb5948e78a7a5
Keycard: dd96182888da0aacf4de821b641103143bbb26cc
libwebp: 98a37e597e40bfdb4c911fc98f2c53d0b12d05fc
Permission-Camera: afad27bf90337684d4a86f3825112d648c8c4d3b
@@ -653,7 +653,7 @@ SPEC CHECKSUMS:
react-native-slider: 12bd76d3d568c9c5500825db54123d44b48e4ad4
react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865
react-native-status: 45dbf1302ce3c258b459dfab137cd1c2c68c295d
react-native-status-keycard: eb84554a23315510948613a9467e7e3481be340c
react-native-status-keycard: 961d01ca190889ddf220206822fd752f8f4f3f7a
react-native-webview: af9990b21a9aeafa8e8347746eb4116c0de086af
React-RCTActionSheet: 89a0ca9f4a06c1f93c26067af074ccdce0f40336
React-RCTAnimation: 1bde3ecc0c104c55df246eda516e0deb03c4e49b
+1
View File
@@ -39,6 +39,7 @@
"react-native-dark-mode": "^0.2.2",
"react-native-device-info": "^7.4.0",
"react-native-dialogs": "^1.0.4",
"react-native-draggable-flatlist": "^3.0.3",
"react-native-fast-image": "8.5.11",
"react-native-fetch-polyfill": "^1.1.2",
"react-native-fs": "^2.14.1",
Binary file not shown.

After

Width:  |  Height:  |  Size: 527 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 899 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

+4
View File
@@ -206,6 +206,9 @@
(def rn-emoji-keyboard
#js {:EmojiKeyboard #js {}})
(def react-native-draggable-flatlist
#js {:default #js {}})
;; Update i18n_resources.cljs
(defn mock [module]
(case module
@@ -239,6 +242,7 @@
"@react-native-community/push-notification-ios" push-notification-ios
"react-native-camera-kit" react-native-camera-kit
"rn-emoji-keyboard" rn-emoji-keyboard
"react-native-draggable-flatlist" react-native-draggable-flatlist
"./fleets.js" default-fleets
"./chats.js" default-chats
"../translations/ar.json" (js/JSON.parse (slurp "./translations/ar.json"))
+7 -7
View File
@@ -49,15 +49,15 @@
:hold hold
:disabled disabled}]]]))))
(defn switch-view [{:keys [transition hold disabled]}]
(defn switch-view [{:keys [transition hold disabled value]}]
[animated/view {:style (styles/switch-style transition disabled)
:accessibility-label :switch
:accessibility-label (str "switch-" (if value "on" "off"))
:accessibility-role :switch}
[animated/view {:style (styles/switch-bullet-style transition hold)}]])
(defn radio-view [{:keys [transition hold disabled]}]
(defn radio-view [{:keys [transition hold disabled value]}]
[animated/view {:style (styles/radio-style transition disabled)
:accessibility-label :radio
:accessibility-label (str "radio-" (if value "on" "off"))
:accessibility-role :radio}
[animated/view {:style (styles/radio-bullet-style transition hold)}]])
@@ -67,15 +67,15 @@
[rn/touchable-without-feedback
{:on-press (when (and onChange (not disabled)) onChange)}
[rn/view {:style (styles/checkbox-style value disabled)
:accessibility-label :checkbox
:accessibility-label (str "checkbox-" (if value "on" "off"))
:accessibility-role :checkbox}
[rn/view {:style (styles/check-icon-style value)}
[icons/tiny-icon :tiny-icons/tiny-check {:color colors/white}]]]])))
(defn animated-checkbox-view [{:keys [transition hold disabled]}]
(defn animated-checkbox-view [{:keys [transition hold disabled value]}]
[animated/view
{:style (styles/animated-checkbox-style transition disabled)
:accessibility-label :checkbox
:accessibility-label (str "checkbox-" (if value "on" "off"))
:accessibility-role :checkbox}
[animated/view {:style (styles/animated-check-icon-style transition hold)}
[icons/tiny-icon :tiny-icons/tiny-check {:color colors/white}]]])
+14 -12
View File
@@ -55,13 +55,13 @@
:small :base
:large))
(defn container [{:keys [size]} & children]
(defn container [{:keys [size container-style]} & children]
(into [rn/view {:style (merge (:tiny spacing/padding-horizontal)
{:min-height (size->container-size size)
:padding-vertical 8
:flex-direction :row
:align-items :center
:justify-content :space-between})}]
:justify-content :space-between} container-style)}]
children))
(defn icon-column
@@ -197,17 +197,19 @@
left-side-alignment icon-color icon-bg-color
title subtitle subtitle-secondary active on-press on-long-press chevron size text-size
accessory-text accessibility-label title-accessibility-label accessory-style
haptic-feedback haptic-type error animated animated-accessory? title-text-weight]
:or {subtitle-max-lines 1
theme :main
haptic-feedback true
animated platform/ios?
haptic-type :selection}}]
haptic-feedback haptic-type error animated animated-accessory? title-text-weight container-style
active-background-enabled]
:or {subtitle-max-lines 1
theme :main
haptic-feedback true
animated platform/ios?
active-background-enabled true
haptic-type :selection}}]
(let [theme (if disabled :disabled theme)
{:keys [text-color active-background passive-background]}
(themes theme)
icon-color (or icon-color (:icon-color (themes theme)))
icon-bg-color (or icon-bg-color (:icon-bg-color (themes theme)))
icon-color (or icon-color (:icon-color (themes theme)))
icon-bg-color (or icon-bg-color (:icon-bg-color (themes theme)))
optional-haptic (fn []
(when haptic-feedback
(haptic/trigger haptic-type)))
@@ -223,7 +225,7 @@
[component
(merge {:type :list-item
:disabled disabled
:bg-color active-background
:bg-color (when active-background-enabled active-background)
:accessibility-label accessibility-label}
(when on-press
{:on-press (fn []
@@ -233,7 +235,7 @@
{:on-long-press (fn []
(optional-haptic)
(on-long-press))}))
[container {:size size}
[container {:size size :container-style container-style}
[left-side {:icon-color icon-color
:text-color (if on-press
text-color
+23 -10
View File
@@ -5,7 +5,10 @@
["react-native" :as rn]
["@react-native-community/hooks" :as hooks]
["react-native-navigation" :refer (Navigation)]
["rn-emoji-keyboard" :refer (EmojiKeyboard)]))
["rn-emoji-keyboard" :refer (EmojiKeyboard)]
["react-native-draggable-flatlist" :default DraggableFlatList]))
(def rn-draggable-flatlist (reagent/adapt-react-class DraggableFlatList))
(def emoji-keyboard (reagent/adapt-react-class EmojiKeyboard))
@@ -107,7 +110,13 @@
(defn- wrap-render-fn [f render-data]
(fn [data]
(reagent/as-element [f (.-item ^js data) (.-index ^js data) (.-separators ^js data) render-data])))
(reagent/as-element [f (.-item ^js data) (.-index ^js data)
(.-separators ^js data) render-data
(.-isActive ^js data) (.-drag ^js data)])))
(defn- wrap-on-drag-end-fn [f]
(fn [data]
(f (.-from ^js data) (.-to ^js data) (.-data ^js data))))
(defn- wrap-key-fn [f]
(fn [data index]
@@ -115,19 +124,23 @@
(f data index)))
(defn base-list-props
[{:keys [key-fn render-fn empty-component header footer separator data render-data] :as props}]
[{:keys [key-fn render-fn empty-component header footer separator data render-data on-drag-end-fn] :as props}]
(merge {:data (to-array data)}
(when key-fn {:keyExtractor (wrap-key-fn key-fn)})
(when render-fn {:renderItem (wrap-render-fn render-fn render-data)})
(when separator {:ItemSeparatorComponent (fn [] (reagent/as-element separator))})
(when empty-component {:ListEmptyComponent (fn [] (reagent/as-element empty-component))})
(when header {:ListHeaderComponent (reagent/as-element header)})
(when footer {:ListFooterComponent (reagent/as-element footer)})
(dissoc props :data :header :footer :empty-component :separator :render-fn :key-fn)))
(when key-fn {:keyExtractor (wrap-key-fn key-fn)})
(when render-fn {:renderItem (wrap-render-fn render-fn render-data)})
(when separator {:ItemSeparatorComponent (fn [] (reagent/as-element separator))})
(when empty-component {:ListEmptyComponent (fn [] (reagent/as-element empty-component))})
(when header {:ListHeaderComponent (reagent/as-element header)})
(when footer {:ListFooterComponent (reagent/as-element footer)})
(when on-drag-end-fn {:onDragEnd (wrap-on-drag-end-fn on-drag-end-fn)})
(dissoc props :data :header :footer :empty-component :separator :render-fn :key-fn :on-drag-end-fn)))
(defn flat-list [props]
[rn-flat-list (base-list-props props)])
(defn draggable-flat-list [props]
[rn-draggable-flatlist (base-list-props props)])
(defn animated-flat-list [props]
[animated-flat-list-class (base-list-props props)])
;; Hooks
+1 -1
View File
@@ -37,7 +37,7 @@
(fx/defn join-public-chat
[cofx chat-name]
(chat/start-public-chat cofx chat-name {:navigation-reset? true}))
(chat/start-public-chat cofx chat-name nil))
(fx/defn accept-pack
{:events [::accept-pack]}
+1 -1
View File
@@ -9,7 +9,7 @@
(def key->string str)
(defn- set-item! [key value]
(defn set-item! [key value]
(-> ^js async-storage
(.setItem (key->string key)
(clj->transit value))
+39 -33
View File
@@ -199,15 +199,23 @@
(fx/defn close-chat
{:events [:close-chat]}
[{:keys [db] :as cofx} target-chat-id]
(let [chat-id (:current-chat-id db)]
(if (:ignore-close-chat db)
{:db (dissoc db :ignore-close-chat)}
(when (= target-chat-id chat-id)
(chat.state/reset-visible-item)
(fx/merge cofx
{:db (dissoc db :current-chat-id)}
(offload-messages chat-id))))))
[{:keys [db] :as cofx}]
(let [chat-id (:current-chat-id db)
navigate-after-home-to-chat (:navigate-after-home-to-chat db)]
(chat.state/reset-visible-item)
(if navigate-after-home-to-chat
{:db (dissoc db :navigate-after-home-to-chat)}
(fx/merge cofx
{:db (dissoc db :current-chat-id)}
(offload-messages chat-id)))))
(fx/defn force-close-chat
[{:keys [db] :as cofx} chat-id]
(do
(chat.state/reset-visible-item)
(fx/merge cofx
{:db (dissoc db :current-chat-id)}
(offload-messages chat-id))))
(fx/defn remove-chat
"Removes chat completely from app, producing all necessary effects for that"
@@ -234,19 +242,21 @@
(fx/defn navigate-to-chat
"Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data"
{:events [:chat.ui/navigate-to-chat]}
[{db :db :as cofx} chat-id dont-reset?]
(fx/merge cofx
(close-chat (:current-chat-id db))
(fn [{:keys [db]}]
{:db (assoc db :current-chat-id chat-id :ignore-close-chat true)})
(preload-chat-data chat-id)
#(when (group-chat? cofx chat-id)
(loading/load-chat % chat-id))
#(when-not dont-reset?
(navigation/change-tab % :chat))
#(when-not dont-reset?
(navigation/pop-to-root-tab % :chat-stack))
(navigation/navigate-to-cofx :chat nil)))
[{db :db :as cofx} chat-id]
(let [home-view? (= (get db :view-id) :home)]
(fx/merge cofx
(close-chat)
(force-close-chat chat-id)
(fn [{:keys [db]}]
{:db (assoc db :current-chat-id chat-id :navigate-after-home-to-chat (not home-view?))})
(preload-chat-data chat-id)
#(when (group-chat? cofx chat-id)
(loading/load-chat % chat-id))
#(when-not home-view?
(navigation/change-tab % :chat))
#(when-not home-view?
(navigation/pop-to-root-tab % :chat-stack))
(navigation/navigate-to-cofx :chat nil))))
(fx/defn handle-clear-history-response
{:events [::history-cleared]}
@@ -294,14 +304,11 @@
(fx/defn handle-public-chat-created
{:events [::public-chat-created]}
[{:keys [db]} chat-id {:keys [dont-navigate?]} response]
(let [chat (chats-store/<-rpc (first (:chats response)))
db-with-chat {:db (-> db
(assoc-in [:chats chat-id] chat)
(update :chats-home-list conj chat-id))}]
(if dont-navigate?
db-with-chat
(assoc db-with-chat :dispatch [:chat.ui/navigate-to-chat chat-id]))))
[{:keys [db]} chat-id _ response]
{:db (-> db
(assoc-in [:chats chat-id] (chats-store/<-rpc (first (:chats response))))
(update :chats-home-list conj chat-id))
:dispatch [:chat.ui/navigate-to-chat chat-id]})
(fx/defn create-public-chat-go [_ chat-id opts]
{::json-rpc/call [{:method "wakuext_createPublicChat"
@@ -312,11 +319,10 @@
(fx/defn start-public-chat
"Starts a new public chat"
{:events [:chat.ui/start-public-chat]}
[cofx topic {:keys [dont-navigate? profile-public-key] :as opts}]
[cofx topic {:keys [profile-public-key] :as opts}]
(if (or (new-public-chat.db/valid-topic? topic) profile-public-key)
(if (active-chat? cofx topic)
(when-not dont-navigate?
(navigate-to-chat cofx topic false))
(navigate-to-chat cofx topic)
(create-public-chat-go
cofx
topic
+5 -4
View File
@@ -42,11 +42,15 @@
[{:keys [db]} community-id]
{:db (community-failed-to-resolve db community-id)})
(defn community-link [id]
(str "https://join.status.im/c/" id))
(fx/defn handle-community-resolved
{:events [::community-resolved]}
[{:keys [db] :as cofx} community-id community]
(fx/merge cofx
{:db (community-resolved db community-id)}
{:db (community-resolved db community-id)
:dispatch [::cache-link-preview-data (community-link community-id) community]}
(models.communities/handle-community community)))
(fx/defn resolve-community-info
@@ -78,9 +82,6 @@
:link-previews-cache
(assoc (get multiaccount :link-previews-cache {}) site data)))
(defn community-link [id]
(str "https://join.status.im/c/" id))
(defn cache-community-preview-data
[{:keys [id] :as community}]
(re-frame/dispatch [::cache-link-preview-data
+54 -19
View File
@@ -9,7 +9,8 @@
[status-im.utils.utils :as utils]
[status-im.native-module.core :as status]
[quo.react-native :as rn]
[quo.react :as react]))
[quo.react :as react]
[status-im.multiaccounts.core :as multiaccounts]))
(def at-sign "@")
@@ -170,11 +171,55 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn add-searchable-phrases
[{:keys [alias name nickname] :as user}]
(reduce
(fn [user s]
(if (nil? s)
user
(let [new-words (concat
[s]
(rest (string/split s " ")))]
(update user :searchable-phrases (fnil concat []) new-words))))
user
[alias name nickname]))
(defn add-searchable-phrases-to-contact
[{:keys [alias name added? blocked? identicon public-key nickname]} community-chat?]
(when (and alias
(not (string/blank? alias))
(or name
nickname
added?
community-chat?)
(not blocked?))
(add-searchable-phrases
{:alias alias
:name (or (utils/safe-replace name ".stateofus.eth" "") alias)
:identicon identicon
:nickname nickname
:public-key public-key})))
(defn mentionable-commmunity-members [contacts my-public-key community-members]
(reduce (fn [acc identity]
(let [contact (multiaccounts/contact-by-identity
contacts identity)
contact (if (string/blank? (:alias contact))
(assoc contact :alias
(get-in contact [:names :three-words-name]))
contact)
mentionable-contact (add-searchable-phrases-to-contact
contact true)]
(if (nil? mentionable-contact) acc
(assoc acc identity mentionable-contact))))
{}
(keys (dissoc community-members my-public-key))))
(defn get-mentionable-users
[{{:keys [current-chat-id]
:contacts/keys [contacts] :as db} :db}]
(let [{:keys [chat-type users] :as chat}
(get-in db [:chats current-chat-id])
(let [{:keys [chat-type community-id users] :as chat} (get-in db [:chats current-chat-id])
{:keys [name preferred-name public-key]} (:multiaccount db)
chat-specific-suggestions
(cond
(= chat-type constants/private-group-chat-type)
@@ -200,9 +245,12 @@
current-chat-id
(contact.db/public-key->contact contacts current-chat-id))
:else users)
{:keys [name preferred-name public-key]}
(:multiaccount db)]
(= chat-type constants/community-chat-type)
(merge users (mentionable-commmunity-members
contacts public-key
(get-in db [:communities community-id :members])))
:else users)]
(reduce
(fn [acc [key {:keys [alias name identicon]}]]
(let [name (utils/safe-replace name ".stateofus.eth" "")]
@@ -634,19 +682,6 @@
[_ ref cursor]
{::reset-text-input-cursor [ref cursor]})
(defn add-searchable-phrases
[{:keys [alias name nickname] :as user}]
(reduce
(fn [user s]
(if (nil? s)
user
(let [new-words (concat
[s]
(rest (string/split s " ")))]
(update user :searchable-phrases (fnil concat []) new-words))))
user
[alias name nickname]))
(defn is-valid-terminating-character? [c]
(case c
"\t" true ; tab
+26 -3
View File
@@ -288,7 +288,7 @@
(fx/defn edit-channel
{:events [::edit-channel-confirmation-pressed]}
[{:keys [db] :as cofx}]
(let [{:keys [name description color community-id emoji edit-channel-id category-id]}
(let [{:keys [name description color community-id emoji edit-channel-id category-id position]}
(get db :communities/create-channel)]
{::json-rpc/call [{:method "wakuext_editCommunityChat"
:params [community-id
@@ -298,6 +298,7 @@
:color color
:emoji emoji}
:category_id category-id
:position position
:permissions {:access constants/community-channel-access-no-membership}}]
:js-response true
:on-success #(re-frame/dispatch [::community-channel-edited %])
@@ -345,7 +346,7 @@
(fx/defn edit-channel-pressed
{:events [::edit-channel-pressed]}
[{:keys [db] :as cofx} community-id chat-name description color emoji chat-id category-id]
[{:keys [db] :as cofx} community-id chat-name description color emoji chat-id category-id position]
(let [{:keys [color emoji]} (if (string/blank? emoji)
(rand-nth emoji-thumbnail-styles/emoji-picker-default-thumbnails)
{:color color :emoji emoji})]
@@ -356,7 +357,8 @@
:community-id community-id
:emoji emoji
:edit-channel-id chat-id
:category-id category-id})}
:category-id category-id
:position position})}
(navigation/open-modal :edit-community-channel nil))))
(fx/defn community-created
@@ -564,6 +566,15 @@
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
:on-error #(log/error "failed to remove chat from community" %)}]}))
(fx/defn delete-community-chat
{:events [:delete-community-chat]}
[_ community-id chat-id]
{::json-rpc/call [{:method "wakuext_deleteCommunityChat"
:params [community-id chat-id]
:js-response true
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
:on-error #(log/error "failed to delete community chat" %)}]})
(fx/defn delete-category
{:events [:delete-community-category]}
[_ community-id category-id]
@@ -592,6 +603,18 @@
(navigation/navigate-back)
(remove-chat-from-category community-id id categoryID))))
(fx/defn reorder-category-chat
{:events [::reorder-community-category-chat]}
[_ community-id category-id chat-id new-position]
{::json-rpc/call [{:method "wakuext_reorderCommunityChat"
:params [{:communityId community-id
:categoryId category-id
:chatId chat-id
:position new-position}]
:js-response true
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
:on-error #(log/error "failed to reorder community category chat" %)}]})
(fx/defn reorder-category
{:events [::reorder-community-category]}
[_ community-id category-id new-position]
+1
View File
@@ -146,6 +146,7 @@
(def ^:const timeline-chat-id "@timeline70bd746ddcc12beb96b2c9d572d0784ab137ffc774f5383e50585a932080b57cca0484b259e61cecbaa33a4c98a300a")
(def ^:const two-mins (* 2 60))
(def ^:const one-day (* 60 60 24))
(def ^:const three-days (* one-day 3))
(def ^:const one-week (* one-day 7))
+10 -3
View File
@@ -5,6 +5,8 @@
[status-im.data-store.chats :as chats-store]
[status-im.data-store.contacts :as contacts-store]
[status-im.navigation :as navigation]
[status-im.utils.types :as types]
[status-im.notifications-center.core :as notification-center]
[status-im.utils.fx :as fx]))
(fx/defn clean-up-chat
@@ -34,13 +36,16 @@
(fx/defn contact-blocked
{:events [::contact-blocked]}
[{:keys [db] :as cofx} {:keys [public-key]} chats]
(let [fxs (map #(clean-up-chat public-key %) chats)]
(let [fxs (when chats
(map #(->> (chats-store/<-rpc %)
(clean-up-chat public-key)) (types/js->clj chats)))]
(apply fx/merge
cofx
{:db (-> db
(update :chats dissoc public-key)
(update :chats-home-list disj public-key)
(assoc-in [:contacts/contacts public-key :added] false))}
(notification-center/get-activity-center-notifications-count)
fxs)))
(fx/defn block-contact
@@ -58,8 +63,10 @@
(update :contacts/blocked (fnil conj #{}) public-key)
;; update the contact in contacts list
(assoc-in [:contacts/contacts public-key] contact))}
(contacts-store/block public-key #(do (re-frame/dispatch [::contact-blocked contact (map chats-store/<-rpc %)])
(re-frame/dispatch [:hide-popover])))
(contacts-store/block
public-key #(do (re-frame/dispatch [::contact-blocked contact (.-chats %)])
(re-frame/dispatch [:sanitize-messages-and-process-response %])
(re-frame/dispatch [:hide-popover])))
;; reset navigation to avoid going back to non existing one to one chat
(if from-one-to-one-chat?
(navigation/pop-to-root-tab :chat-stack)
+4 -1
View File
@@ -28,7 +28,10 @@
:chat-type constants/one-to-one-chat-type
:chat-name name
:public? false
:group-chat false)))
:group-chat false)
:else
chat))
(defn <-rpc [item]
(-> item
+1
View File
@@ -45,6 +45,7 @@
(fx/defn block [_ contact-id on-success]
{::json-rpc/call [{:method (json-rpc/call-ext-method "blockContact")
:params [contact-id]
:js-response true
:on-success on-success
:on-failure #(log/error "failed to block contact" % contact-id)}]})
-2
View File
@@ -26,7 +26,6 @@
:testnet {:id 3 :name "Ropsten"}
:rinkeby {:id 4 :name "Rinkeby"}
:xdai {:id 100 :name "xDai"}
:poa {:id 99 :name "POA"}
:goerli {:id 5 :name "Goerli"}
:bsc {:id BSC-mainnet-chain-id
:name "BSC"}
@@ -57,7 +56,6 @@
(defn sidechain? [id]
(contains? #{(chain-keyword->chain-id :xdai)
(chain-keyword->chain-id :poa)
(chain-keyword->chain-id :bsc)} id))
(defn network-with-upstream-rpc? [network]
+1
View File
@@ -165,6 +165,7 @@
"wakuext_reorderCommunityChat" {}
"wakuext_editCommunityCategory" {}
"wakuext_deleteCommunityCategory" {}
"wakuext_deleteCommunityChat" {}
"wakuext_ensVerified" {}
"wakuext_dismissActivityCenterNotifications" {}
"wakuext_acceptActivityCenterNotifications" {}
-4
View File
@@ -28,10 +28,6 @@
:symbol :ETH
:symbol-display :ETHri
:decimals 18}
:poa {:name "POA"
:symbol :ETH
:symbol-display :POA
:decimals 18}
:xdai {:name "xDAI"
:symbol :ETH
:symbol-display :xDAI
+98 -56
View File
@@ -1,65 +1,66 @@
(ns status-im.events
(:require [re-frame.core :as re-frame]
[status-im.chat.models :as chat]
[status-im.i18n.i18n :as i18n]
[status-im.mailserver.core :as mailserver]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.ui.components.react :as react]
[status-im.utils.fx :as fx]
status-im.utils.logging.core
status-im.backup.core
[status-im.wallet.core :as wallet]
[status-im.keycard.core :as keycard]
[status-im.utils.dimensions :as dimensions]
[status-im.multiaccounts.biometric.core :as biometric]
[status-im.constants :as constants]
[status-im.native-module.core :as status]
[status-im.ui.components.permissions :as permissions]
[status-im.utils.utils :as utils]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.anon-metrics.core :as anon-metrics]
[status-im.utils.universal-links.core :as universal-links]
clojure.set
status-im.currency.core
status-im.navigation
status-im.utils.universal-links.core
status-im.wallet.custom-tokens.core
status-im.waku.core
status-im.wallet.choose-recipient.core
status-im.wallet.accounts.core
status-im.popover.core
status-im.visibility-status-popover.core
status-im.visibility-status-updates.core
status-im.bottom-sheet.core
(:require clojure.set
[re-frame.core :as re-frame]
status-im.add-new.core
status-im.search.core
status-im.http.core
status-im.profile.core
[status-im.anon-metrics.core :as anon-metrics]
[status-im.async-storage.core :as async-storage]
status-im.backup.core
status-im.bootnodes.core
[status-im.bottom-sheet.core :as bottom-sheet]
status-im.browser.core
status-im.browser.permissions
[status-im.chat.models :as chat]
status-im.chat.models.images
status-im.ui.screens.privacy-and-security-settings.events
status-im.chat.models.input
status-im.chat.models.loading
status-im.chat.models.transport
[status-im.constants :as constants]
status-im.contact.block
status-im.contact.chat
status-im.contact.core
status-im.currency.core
[status-im.ethereum.json-rpc :as json-rpc]
status-im.ethereum.subscriptions
status-im.fleet.core
status-im.http.core
[status-im.i18n.i18n :as i18n]
status-im.init.core
[status-im.keycard.core :as keycard]
status-im.log-level.core
status-im.mailserver.constants
[status-im.mailserver.core :as mailserver]
[status-im.multiaccounts.biometric.core :as biometric]
[status-im.multiaccounts.core :as multiaccounts]
status-im.multiaccounts.login.core
status-im.multiaccounts.logout.core
status-im.multiaccounts.update.core
[status-im.native-module.core :as status]
[status-im.navigation :as navigation]
status-im.notifications-center.core
status-im.pairing.core
status-im.popover.core
status-im.profile.core
status-im.search.core
status-im.signals.core
status-im.stickers.core
status-im.transport.core
status-im.init.core
status-im.log-level.core
status-im.mailserver.constants
status-im.ethereum.subscriptions
status-im.fleet.core
status-im.contact.block
status-im.contact.core
status-im.contact.chat
status-im.chat.models.input
status-im.chat.models.loading
status-im.bootnodes.core
status-im.browser.core
status-im.browser.permissions
status-im.chat.models.transport
status-im.notifications-center.core
[status-im.navigation :as navigation]))
[status-im.ui.components.permissions :as permissions]
[status-im.ui.components.react :as react]
status-im.ui.screens.privacy-and-security-settings.events
[status-im.utils.dimensions :as dimensions]
[status-im.utils.fx :as fx]
status-im.utils.logging.core
[status-im.utils.universal-links.core :as universal-links]
[status-im.utils.utils :as utils]
status-im.visibility-status-popover.core
status-im.visibility-status-updates.core
status-im.waku.core
status-im.wallet.accounts.core
status-im.wallet.choose-recipient.core
[status-im.wallet.core :as wallet]
status-im.wallet.custom-tokens.core
[status-im.navigation.core :as navigation.core]
[status-im.multiaccounts.login.core :as login.core]))
(re-frame/reg-fx
:dismiss-keyboard
@@ -113,10 +114,35 @@
(fx/defn system-theme-mode-changed
{:events [:system-theme-mode-changed]}
[{:keys [db]} theme]
(let [cur-theme (get-in db [:multiaccount :appearance])]
[{:keys [db] :as cofx} theme]
(let [cur-theme (get-in db [:multiaccount :appearance])
current-tab (get db :current-tab :chat)
view-id (:view-id db)
screen-params (get-in db [:navigation/screen-params view-id])
root-id @navigation.core/root-id]
(navigation.core/dismiss-all-modals)
(when (or (nil? cur-theme) (zero? cur-theme))
{::multiaccounts/switch-theme (if (= :dark theme) 2 1)})))
(fx/merge cofx
{::multiaccounts/switch-theme (if (= :dark theme) 2 1)
:utils/dispatch-later
(cond-> [{:ms 2000 :dispatch
(if (= view-id :chat)
[:chat.ui/navigate-to-chat (:current-chat-id db)]
[:navigate-to view-id screen-params])}]
(some #(= view-id %) navigation.core/community-screens)
(conj {:ms 1000 :dispatch
[:navigate-to :community
(get-in db [:navigation/screen-params :community])]})
(= view-id :community-emoji-thumbnail-picker)
(conj {:ms 1500 :dispatch
[:navigate-to :create-community-channel
(get-in db [:navigation/screen-params :create-community-channel])]}))}
(bottom-sheet/hide-bottom-sheet)
(navigation/init-root root-id)
(when (= root-id :chat-stack)
(navigation/change-tab current-tab))))))
(def authentication-options
{:reason (i18n/label :t/biometric-auth-reason-login)})
@@ -193,8 +219,24 @@
(fx/defn on-will-focus
{:events [:screens/on-will-focus]
:interceptors [anon-metrics/interceptor]}
[cofx view-id]
[{:keys [db] :as cofx} view-id]
(fx/merge cofx
(cond
(= :chat view-id)
{::async-storage/set! {:chat-id (get-in cofx [:db :current-chat-id])
:key-uid (get-in cofx [:db :multiaccount :key-uid])}
:db (assoc db :screens/was-focused-once? true)}
(= :login view-id)
{}
(not (get db :screens/was-focused-once?))
{:db (assoc db :screens/was-focused-once? true)}
:else
{::async-storage/set! {:chat-id nil
:key-uid nil}
:db (assoc db :screens/was-focused-once? true)})
#(case view-id
:keycard-settings (keycard/settings-screen-did-load %)
:reset-card (keycard/reset-card-screen-did-load %)
+1 -1
View File
@@ -61,7 +61,7 @@
[cofx {:keys [chat-id invitation-admin chat-name]}]
(if (get-in cofx [:db :chats chat-id])
{:dispatch-n [[:accept-all-activity-center-notifications-from-chat chat-id]
[:chat.ui/navigate-to-chat chat-id false]]}
[:chat.ui/navigate-to-chat chat-id]]}
{::json-rpc/call [{:method (json-rpc/call-ext-method "createGroupChatFromInvitation")
:params [chat-name chat-id invitation-admin]
:js-response true
+1 -1
View File
@@ -16,7 +16,7 @@
(fx/merge cofx
{:db (-> db
(assoc-in [:keycard :creating-backup?] backup-type))}
(when-not (:multiaccounts/login db)
(when (:multiaccount db)
(navigation/change-tab :profile))
(navigation/navigate-to-cofx :seed-phrase nil)))
+7 -5
View File
@@ -70,7 +70,9 @@
(mnemonic/set-mnemonic))
(when (= flow :recovery)
(onboarding/proceed-with-generating-key)))
(recovery/load-pair-screen cofx))))
(if (get-in db [:keycard :secrets :password])
(onboarding/load-pairing-screen cofx)
(recovery/load-pair-screen cofx)))))
(fx/defn navigate-to-keycard-settings
{:events [:profile.ui/keycard-settings-button-pressed]}
@@ -523,14 +525,14 @@
(fx/merge cofx
{:db (assoc-in db [:keycard :card-state] card-state)}
(set-setup-step card-state)
(common/hide-connection-sheet)
(when paired?
(load-pairing))
(when (and flow
(= card-state :init))
(proceed-setup-with-initialized-card flow instance-uid paired?))
(if (and flow
(= card-state :init))
(proceed-setup-with-initialized-card flow instance-uid paired?)
(common/hide-connection-sheet))
(when (= card-state :pre-init)
(if (= flow :import)
+1 -1
View File
@@ -66,7 +66,7 @@
:error nil
:status nil))
:hide-popover nil})
(when-not (:multiaccounts/login db)
(when (:multiaccount db)
(navigation/change-tab :profile))
(when-not (:multiaccounts/login db)
(if (:popover/popover db)
+1
View File
@@ -21,6 +21,7 @@
(assoc-in [:keycard :setup-step] :recovery-phrase)
(assoc-in [:keycard :secrets :mnemonic] mnemonic))}
(common/clear-on-card-connected)
(common/hide-connection-sheet)
(navigation/navigate-replace :keycard-onboarding-recovery-phrase nil))))
(fx/defn load-loading-keys-screen
+4 -1
View File
@@ -236,7 +236,10 @@
(assoc-in [:keycard :card-state] :init)
(assoc-in [:keycard :setup-step] :secret-keys)
(update-in [:keycard :secrets] merge secrets'))}
(load-pairing-screen))))
(common/show-connection-sheet
{:on-card-connected :keycard/get-application-info
:on-card-read :keycard/check-card-state
:handler (common/get-application-info :keycard/check-card-state)}))))
(fx/defn on-install-applet-and-init-card-error
{:events [:keycard.callback/on-install-applet-and-init-card-error
+8 -2
View File
@@ -217,9 +217,15 @@
:t/keycard-access-reset :t/keycard-backup-success-title))
:content (i18n/label (if (= backup-type :recovery-card)
:t/keycard-can-use-with-new-passcode :t/keycard-backup-success-body))}}
(if (multiaccounts.model/logged-in? cofx)
(cond
(multiaccounts.model/logged-in? cofx)
(navigation/set-stack-root :profile-stack [:my-profile :keycard-settings])
(return-to-keycard-login))))
(:multiaccounts/login db)
(return-to-keycard-login)
:else
(navigation/set-stack-root :onboarding [:get-your-keys]))))
(re-frame/reg-fx
::finish-migration
+3 -2
View File
@@ -184,8 +184,9 @@
(fx/defn handle-successful-request
{:events [::request-success]}
[{:keys [db]} response-js]
{:db (dissoc db :mailserver/current-request)})
[{:keys [db] :as cofx} response-js]
{:db (dissoc db :mailserver/current-request)
:dispatch [:sanitize-messages-and-process-response response-js]})
(fx/defn process-next-messages-request
{:events [::request-messages]}
@@ -109,12 +109,15 @@
types/json->clj
:error
string/blank?
not)]
not)
onboarding? (not (or (:multiaccounts/login db) (:multiaccount db)))]
(if error?
(popover/show-popover cofx {:view :custom-seed-phrase})
{::validate-seed-against-key-uid {:seed-phrase (-> db :multiaccounts/key-storage :seed-phrase)
;; Unique key-uid of the account for which we are going to move keys
:key-uid (or (-> db :multiaccounts/login :key-uid) (-> db :multiaccount :key-uid))}})))
:key-uid (or (-> db :multiaccounts/login :key-uid)
(-> db :multiaccount :key-uid)
(and onboarding? (-> db :keycard :application-info :key-uid)))}})))
(fx/defn choose-storage-pressed
{:events [::choose-storage-pressed]}
+16 -2
View File
@@ -325,6 +325,19 @@
(logging/set-log-level (:log-level multiaccount))
(notifications-center/get-activity-center-notifications-count))))
(re-frame/reg-fx
::open-last-chat
(fn [key-uid]
(async-storage/get-item
:chat-id
(fn [chat-id]
(when chat-id
(async-storage/get-item
:key-uid
(fn [stored-key-uid]
(when (= stored-key-uid key-uid)
(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id])))))))))
(fx/defn get-chats-callback
{:events [::get-chats-callback]}
[{:keys [db] :as cofx}]
@@ -339,7 +352,8 @@
::initialize-wallet
(fn [accounts custom-tokens favourites]
(re-frame/dispatch [::initialize-wallet
accounts custom-tokens favourites]))}
accounts custom-tokens favourites]))
::open-last-chat (get-in db [:multiaccount :key-uid])}
notifications-enabled?
(assoc ::notifications/enable nil))
(transport/start-messenger)
@@ -381,7 +395,7 @@
config/metrics-enabled?)
(navigation/navigate-to :anon-metrics-opt-in {})
:else (re-frame/dispatch [:init-root :chat-stack]))))
:else (re-frame/dispatch [:init-root :chat-stack]))))
(fx/defn login-only-events
[{:keys [db] :as cofx} key-uid password save-password?]
@@ -89,8 +89,7 @@
:mainnet #{:SNT}
:rinkeby #{:MOKSHA
:KDO}
:xdai #{}
:poa #{}}
:xdai #{}}
:preview-privacy? true
:fleet :eth.prod
:identicon "data:image/png;base64iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAADAFBMVEX////YsYwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPGFwxAAABAHRSTlP//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKmfXxgAABnNJREFUeNoBaAaX+QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEAAAAAAQEBAQAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAABAQEBAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAQEAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAAAAAAEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEAAAAAAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQAAAAABAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAAAAAAEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAloYA4a9rBHIAAAAASUVORK5CYII="
+2 -2
View File
@@ -1,6 +1,6 @@
(ns status-im.navigation
(:require [status-im.utils.fx :as fx]
[status-im.anon-metrics.core :as anon-metrics]))
(:require [status-im.anon-metrics.core :as anon-metrics]
[status-im.utils.fx :as fx]))
(defn- all-screens-params [db view screen-params]
(cond-> db
+68 -18
View File
@@ -1,17 +1,19 @@
(ns status-im.navigation.core
(:require
[re-frame.core :as re-frame]
[status-im.ui.screens.views :as views]
[status-im.utils.platform :as platform]
[status-im.navigation.roots :as roots]
[status-im.ui.components.react :as react]
[quo.components.text-input :as quo.text-input]
[status-im.ui.components.icons.icons :as icons]
[quo.design-system.colors :as quo.colors]
[status-im.utils.fx :as fx]
["react-native-navigation" :refer (Navigation)]
["react-native" :as rn]
[clojure.set :as clojure.set]
["react-native-gesture-handler" :refer (gestureHandlerRootHOC)]
["react-native" :as rn]))
["react-native-navigation" :refer (Navigation)]
[quo.components.text-input :as quo.text-input]
[quo.design-system.colors :as quo.colors]
[re-frame.core :as re-frame]
[status-im.navigation.roots :as roots]
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.components.react :as react]
[status-im.ui.screens.views :as views]
[status-im.utils.fx :as fx]
[status-im.utils.platform :as platform]
[taoensso.timbre :as log]))
(def debug? ^boolean js/goog.DEBUG)
@@ -26,6 +28,7 @@
;; REGISTER COMPONENT (LAZY)
(defn reg-comp [key]
(log/debug "reg-comp" key)
(if-let [comp (get views/components (keyword key))]
(.registerComponent Navigation key (fn [] (views/component comp)))
(let [screen (views/screen key)]
@@ -35,6 +38,7 @@
(.setLazyComponentRegistrator Navigation reg-comp))
(defn dismiss-all-modals []
(log/debug "dissmiss-all-modals")
(when @curr-modal
(reset! curr-modal false)
(reset! dissmissing true)
@@ -44,6 +48,7 @@
;; PUSH SCREEN
(defn navigate [comp]
(log/debug "NAVIGATE" comp)
(let [{:keys [options]} (get views/screens comp)]
(.push Navigation
(name @root-comp-id)
@@ -57,6 +62,7 @@
;; OPEN MODAL
(defn update-modal-topbar-options [options]
(log/debug "update-modal-topbar-options" options)
(merge options
(roots/merge-top-bar {:elevation 0
:noBorder true
@@ -68,6 +74,7 @@
options)))
(defn open-modal [comp]
(log/debug "open-modal" comp)
(let [{:keys [options]} (get views/screens comp)]
(if @dissmissing
(reset! dissmissing comp)
@@ -88,6 +95,7 @@
;; DISSMISS MODAL
(defn dissmissModal []
(log/debug "dissmissModal")
(reset! dissmissing true)
(.dismissModal Navigation (name (last @modals))))
@@ -105,6 +113,7 @@
(handler)))))))
(defn set-view-id [view-id]
(log/debug "set-view-id" view-id)
(when-let [{:keys [on-focus]} (get views/screens view-id)]
(re-frame/dispatch [:set :view-id view-id])
(re-frame/dispatch [:screens/on-will-focus view-id])
@@ -135,6 +144,7 @@
(.events Navigation)
(fn [^js evn]
(let [view-id (keyword (.-componentName evn))]
(log/debug "screen-appear-reg" view-id)
(when (get views/screens view-id)
(when (and (not= view-id :bottom-sheet)
(not= view-id :popover)
@@ -148,17 +158,20 @@
(.registerComponentDidDisappearListener
(.events Navigation)
(fn [^js evn]
(when-not (#{"popover" "bottom-sheet" "signing-sheet" "visibility-status-popover"}
(.-componentName evn))
(doseq [[_ {:keys [ref value]}] @quo.text-input/text-input-refs]
(.setNativeProps ^js ref (clj->js {:text value})))
(doseq [[^js text-input default-value] @react/text-input-refs]
(.setNativeProps text-input (clj->js {:text default-value})))))))
(let [view-id (keyword (.-componentName evn))]
(when-not (#{"popover" "bottom-sheet" "signing-sheet" "visibility-status-popover"}
(.-componentName evn))
(re-frame/dispatch [::view-disappeared view-id])
(doseq [[_ {:keys [ref value]}] @quo.text-input/text-input-refs]
(.setNativeProps ^js ref (clj->js {:text value})))
(doseq [[^js text-input default-value] @react/text-input-refs]
(.setNativeProps text-input (clj->js {:text default-value}))))))))
;; SET ROOT
(re-frame/reg-fx
:init-root-fx
(fn [new-root-id]
(log/debug :init-root-fx new-root-id)
(reset! root-comp-id new-root-id)
(reset! root-id @root-comp-id)
(.setRoot Navigation (clj->js (get (roots/roots) new-root-id)))))
@@ -166,6 +179,7 @@
(re-frame/reg-fx
:init-root-with-component-fx
(fn [[new-root-id new-root-comp-id]]
(log/debug :init-root-with-component-fx new-root-id new-root-comp-id)
(reset! root-comp-id new-root-comp-id)
(reset! root-id @root-comp-id)
(.setRoot Navigation (clj->js (get (roots/roots) new-root-id)))))
@@ -173,6 +187,7 @@
(fx/defn set-multiaccount-root
{:events [::set-multiaccount-root]}
[{:keys [db]}]
(log/debug :set-multiaccounts-root)
(let [key-uid (get-in db [:multiaccounts/login :key-uid])
keycard-account? (boolean (get-in db [:multiaccounts/multiaccounts
key-uid
@@ -192,6 +207,7 @@
(.hide ^js splash-screen))))
(defn get-screen-component [comp]
(log/debug :get-screen-component comp)
(let [{:keys [options]} (get views/screens comp)]
{:component {:id comp
:name comp
@@ -203,6 +219,7 @@
(re-frame/reg-fx
:set-stack-root-fx
(fn [[stack comp]]
(log/debug :set-stack-root-fx stack comp)
(.setStackRoot Navigation
(name stack)
(clj->js (if (vector? comp)
@@ -225,6 +242,7 @@
(re-frame/reg-fx
:change-tab-fx
(fn [tab]
(log/debug :change-tab-fx)
(reset! root-comp-id (get tab-root-ids (get tab-key-idx tab)))
(.mergeOptions Navigation "tabs-stack" (clj->js {:bottomTabs {:currentTabIndex (get tab-key-idx tab)}}))
;;when we change tab we want to dismiss all modals
@@ -234,6 +252,7 @@
(re-frame/reg-fx
:change-tab-count-fx
(fn [[tab cnt]]
(log/debug :change-tab-count-fx tab cnt)
(.mergeOptions Navigation
(name (get tab-root-ids (get tab-key-idx tab)))
(clj->js {:bottomTab (cond
@@ -253,6 +272,7 @@
(re-frame/reg-fx
:pop-to-root-tab-fx
(fn [comp]
(log/debug :pop-to-root-tab-fx comp)
(dismiss-all-modals)
(.popToRoot Navigation (name comp))))
@@ -260,7 +280,10 @@
(.registerBottomTabSelectedListener
(.events Navigation)
(fn [^js evn]
(let [comp (get tab-root-ids (.-selectedTabIndex evn))]
(let [selected-tab-index (.-selectedTabIndex evn)
comp (get tab-root-ids selected-tab-index)
tab-key (get (clojure.set/map-invert tab-key-idx) selected-tab-index)]
(re-frame/dispatch [:set :current-tab tab-key])
(when (and platform/android? (= @root-comp-id comp))
(.popToRoot Navigation (name comp)))
(reset! root-comp-id comp)))))
@@ -343,11 +366,13 @@
(re-frame/reg-fx
:navigate-to-fx
(fn [key]
(log/debug :navigate-to-fx key)
(navigate key)))
(re-frame/reg-fx
:navigate-back-fx
(fn []
(log/debug :navigate-back-fx)
(if @curr-modal
(dissmissModal)
(.pop Navigation (name @root-comp-id)))))
@@ -355,5 +380,30 @@
(re-frame/reg-fx
:navigate-replace-fx
(fn [view-id]
(log/debug :navigate-replace-fx view-id)
(.pop Navigation (name @root-comp-id))
(navigate view-id)))
(def community-screens '(:community-management
:community-members
:community-requests-to-join
:create-community-channel
:community-emoji-thumbnail-picker
:create-community-category
:community-edit-chats))
;; change view-id if it is still same after component is disappeared
;; https://github.com/wix/react-native-navigation/issues/5744#issuecomment-563226820
(fx/defn view-disappeared
{:events [::view-disappeared]}
[{:keys [db]} view-id]
(when (= view-id (:view-id db))
{:db (assoc db :view-id (cond
(= view-id :community-emoji-thumbnail-picker)
:create-community-channel
(some #(= view-id %) community-screens)
:community
:else
:home))}))
+1
View File
@@ -35,6 +35,7 @@
(defn default-root []
{:layout {:componentBackgroundColor colors/white
:orientation "portrait"
:backgroundColor colors/white}})
(defn merge-top-bar [root-options options]
+3 -1
View File
@@ -1,5 +1,6 @@
(ns status-im.notifications.local
(:require [status-im.utils.fx :as fx]
(:require [status-im.async-storage.core :as async-storage]
[status-im.utils.fx :as fx]
[status-im.ethereum.decode :as decode]
["@react-native-community/push-notification-ios" :default pn-ios]
[status-im.notifications.android :as pn-android]
@@ -40,6 +41,7 @@
(defn handle-notification-press [{{deep-link :deepLink} :userInfo
interaction :userInteraction}]
(async-storage/set-item! (str :chat-id) nil)
(when (and deep-link
(or platform/ios?
(and platform/android? interaction)))
+22 -8
View File
@@ -6,15 +6,29 @@
[status-im.data-store.activities :as data-store.activities]))
(fx/defn handle-activities [{:keys [db]} activities]
{:db (-> db
(update-in [:activity.center/notifications :notifications] #(concat activities %))
(update :activity.center/notifications-count + (count activities)))
:dispatch (cond
(= (:view-id db) :notifications-center)
[:mark-all-activity-center-notifications-as-read]
(let [{:keys [unread-count notifications]}
(reduce (fn [acc {:keys [read dismissed accepted] :as notification}]
(as-> acc a
(if read
(update a :unread-count dec)
(update a :unread-count inc))
(= (:view-id db) :chat)
[:accept-all-activity-center-notifications-from-chat (:current-chat-id db)])})
(if (or dismissed accepted)
(update a :notifications (fn [items] (remove #(= (:id notification) (:id %)) items)))
(update a :notifications conj notification))))
{:unread-count (get db :activity.center/notifications-count 0)
:notifications (get-in db [:activity.center/notifications :notifications])}
activities)]
(merge
{:db (-> db
(assoc-in [:activity.center/notifications :notifications] notifications)
(assoc :activity.center/notifications-count (max 0 unread-count)))}
(cond
(= (:view-id db) :notifications-center)
{:dispatch [:mark-all-activity-center-notifications-as-read]}
(= (:view-id db) :chat)
{:dispatch [:accept-all-activity-center-notifications-from-chat (:current-chat-id db)]}))))
(fx/defn get-activity-center-notifications-count
{:events [:get-activity-center-notifications-count]}
+49 -43
View File
@@ -34,7 +34,6 @@
[status-im.utils.security :as security]
[status-im.wallet.db :as wallet.db]
[status-im.wallet.utils :as wallet.utils]
[status-im.utils.utils :as utils]
status-im.ui.screens.keycard.subs
[status-im.chat.models.mentions :as mentions]
[status-im.notifications.core :as notifications]
@@ -181,6 +180,12 @@
(reg-root-key-sub :wallet/fetching-collection-assets :wallet/fetching-collection-assets)
(reg-root-key-sub :wallet/collectible-assets :wallet/collectible-assets)
(reg-root-key-sub :wallet/selected-collectible :wallet/selected-collectible)
(reg-root-key-sub :wallet/modal-selecting-source-token? :wallet/modal-selecting-source-token?)
(reg-root-key-sub :wallet/swap-from-token :wallet/swap-from-token)
(reg-root-key-sub :wallet/swap-to-token :wallet/swap-to-token)
(reg-root-key-sub :wallet/swap-from-token-amount :wallet/swap-from-token-amount)
(reg-root-key-sub :wallet/swap-to-token-amount :wallet/swap-to-token-amount)
(reg-root-key-sub :wallet/swap-advanced-mode? :wallet/swap-advanced-mode?)
;;commands
(reg-root-key-sub :commands/select-account :commands/select-account)
@@ -877,6 +882,22 @@
(assoc :color colors/blue))
chats)))))
(re-frame/reg-sub
:chats/sorted-categories-by-community-id
(fn [[_ community-id]]
[(re-frame/subscribe [:chats/by-community-id community-id])
(re-frame/subscribe [:communities/community-chats community-id])])
(fn [[chats comm-chats] [_ community-id]]
(let [chat-cat (into {} (map (fn [{:keys [id categoryID position]}]
{(str community-id id) {:categoryID categoryID
:position position}})
(vals comm-chats)))]
(group-by :categoryID (sort-by :position
(map #(cond-> (merge % (chat-cat (:chat-id %)))
(= community-id constants/status-community-id)
(assoc :color colors/blue))
chats))))))
(re-frame/reg-sub
:chats/category-by-chat-id
(fn [[_ community-id _]]
@@ -903,7 +924,9 @@
:<- [:communities]
(fn [communities [_ id]]
(->> (get-in communities [id :categories])
(sort-by #(:position (get % 1))))))
(map #(assoc (get % 1) :community-id id))
(sort-by :position)
(into []))))
(re-frame/reg-sub
:chats/current-chat-ui-props
@@ -1197,19 +1220,6 @@
(hydrate-messages messages))))))
;;we want to keep data unchanged so react doesn't change component when we leave screen
(def memo-chat-messages-stream (atom nil))
(re-frame/reg-sub
:chats/chat-messages-stream
(fn [[_ chat-id] _]
[(re-frame/subscribe [:chats/raw-chat-messages-stream chat-id])
(re-frame/subscribe [:chats/chat-no-messages? chat-id])
(re-frame/subscribe [:view-id])])
(fn [[messages empty view-id]]
(when (or (= view-id :chat) empty)
(reset! memo-chat-messages-stream messages))
@memo-chat-messages-stream))
(def memo-profile-messages-stream (atom nil))
(re-frame/reg-sub
@@ -1413,22 +1423,11 @@
:<- [:contacts/contacts]
(fn [contacts]
(reduce
(fn [acc [key {:keys [alias name added? blocked? identicon public-key nickname]}]]
(if (and alias
(not= alias "")
(or name
nickname
added?)
(not blocked?))
(let [name (utils/safe-replace name ".stateofus.eth" "")]
(assoc acc public-key
(mentions/add-searchable-phrases
{:alias alias
:name (or name alias)
:identicon identicon
:nickname nickname
:public-key key})))
acc))
(fn [acc [key contact]]
(let [mentionable-contact (mentions/add-searchable-phrases-to-contact
contact false)]
(if (nil? mentionable-contact) acc
(assoc acc key mentionable-contact))))
{}
contacts)))
@@ -1438,20 +1437,27 @@
:<- [:chats/mentionable-contacts]
:<- [:contacts/blocked-set]
:<- [:multiaccount]
(fn [[{:keys [chat-id users chat-type]} contacts blocked {:keys [name preferred-name public-key]}]]
(let [contacts-with-one-to-one (if (= chat-type constants/one-to-one-chat-type)
(assoc contacts chat-id (get contacts chat-id (-> chat-id
contact.db/public-key->new-contact
contact.db/enrich-contact)))
contacts)]
:<- [:contacts/contacts]
(fn [[{:keys [chat-id users contacts community-id chat-type] :as chat}
mentionable-contacts blocked {:keys [public-key]} my-contacts]]
(let [community-members @(re-frame/subscribe [:communities/community-members
community-id])
contacts-with-one-to-one (if (= chat-type constants/one-to-one-chat-type)
(assoc mentionable-contacts chat-id (get mentionable-contacts chat-id (-> chat-id
contact.db/public-key->new-contact
contact.db/enrich-contact)))
mentionable-contacts)
members-left (into #{} (filter #(group-chat/member-removed? chat %) (keys users)))
filtered-contacts (select-keys contacts-with-one-to-one (if (nil? community-id)
(distinct (concat (seq contacts) (keys users) [chat-id]))
(keys community-members)))
mentionable-commmunity-members (mentions/mentionable-commmunity-members
my-contacts public-key community-members)]
(apply dissoc
(-> users
(merge contacts-with-one-to-one)
(assoc public-key (mentions/add-searchable-phrases
{:alias name
:name (or preferred-name name)
:public-key public-key})))
(conj blocked public-key)))))
(merge filtered-contacts)
(merge mentionable-commmunity-members))
(conj (concat blocked members-left) public-key)))))
(re-frame/reg-sub
:chat/mention-suggestions
+4 -4
View File
@@ -81,9 +81,7 @@
(fx/merge cofx
(process-next response-js sync-handler)
(models.contact/ensure-contacts
(map data-store.contacts/<-rpc contacts-clj)
(when chats
(map data-store.chats/<-rpc (types/js->clj chats))))))
(map data-store.contacts/<-rpc contacts-clj) chats)))
(seq communities)
(let [communities-clj (types/js->clj communities)]
@@ -257,7 +255,9 @@
(fx/defn update-envelopes-status
[{:keys [db] :as cofx} message-id status]
(apply fx/merge cofx (map #(update-envelope-status % status) message-id)))
(when (or (not= status :not-sent)
(= :online (:network db)))
(apply fx/merge cofx (map #(update-envelope-status % status) message-id))))
(fx/defn set-message-envelope-hash
"message-type is used for tracking"
@@ -13,7 +13,9 @@
(merge {:style (merge
styles/wrapper
style)
:accessibility-label accessibility-label}
:accessibility-label (str accessibility-label
"-"
(if checked? "on" "off"))}
(when on-value-change
{:on-press #(on-value-change (not checked?))}))
(if checked?
@@ -6,7 +6,8 @@
(defn emoji-thumbnail [emoji color size]
(when-not (string/blank? emoji)
[react/view (styles/emoji-thumbnail-icon color size)
[react/text {:style (styles/emoji-thumbnail-icon-text size)} emoji]]))
[react/text {:style (styles/emoji-thumbnail-icon-text size)
:accessibility-label :thumbnail-emoji} emoji]]))
(defn emoji-thumbnail-touchable [emoji color size func]
(when-not (string/blank? emoji)
@@ -17,7 +17,6 @@
(defn emoji-thumbnail-icon-text [size]
{:font-size (emoji-utils/emoji-font-size size)
:line-height size
:accessibility-label :thumbnail-emoji
:margin-top (emoji-utils/emoji-top-margin-for-vertical-alignment size)}) ;; Required for vertical alignment bug - Check function defination for more info
@@ -86,21 +86,4 @@
:style {:width 160 :height 160}}]]
[react/text {:style {:text-align :center :margin-bottom 16 :line-height 22}}
(i18n/label :t/public-chat-description)]
[chat-name-input topic error]]
[react/view {:margin-top 32}
(for [[section chats] (chat.models/chats)]
^{:key section}
[react/view
[react/view {:margin-right 16 :padding-left 16 :padding-vertical 3
:border-bottom-width 1 :border-bottom-color colors/gray-lighter
:border-top-width 1 :border-top-color colors/gray-lighter
:border-right-width 1 :border-right-color colors/gray-lighter
:border-bottom-right-radius 14 :border-top-right-radius 14}
[quo/text {:weight :medium} section]]
[react/view {:flex-direction :row :flex-wrap :wrap :margin-vertical 8 :padding-horizontal 16}
(let [lang-topic (get-language-topic)
chats (if (and (= section-featured section) lang-topic)
(conj chats lang-topic)
chats)]
(for [chat chats]
(render-topic chat)))]])]]))
[chat-name-input topic error]]]))
@@ -181,7 +181,7 @@
:message-id @current-player-message-id}))
nil)
(defview message-content [{:keys [audio audio-duration-ms message-id outgoing]} timestamp-view]
(defview message-content [{:keys [audio audio-duration-ms message-id outgoing]}]
(letsubs [state (reagent/atom nil)
progress (reagent/atom 0)
progress-anim (anim/create-value 0)
@@ -223,5 +223,4 @@
(#{:playing :paused :seeking} (:general @state)) @progress
:else (:duration @state))
s (quot time 1000)]
(gstring/format "%02d:%02d" (quot s 60) (mod s 60)))]
timestamp-view]]))))
(gstring/format "%02d:%02d" (quot s 60) (mod s 60)))]]]))))
+21 -20
View File
@@ -12,28 +12,29 @@
(re-frame/dispatch [:chat.ui/fill-gaps chat-id gap-ids])))
(views/defview gap
[{:keys [gap-ids chat-id gap-parameters]}]
[{:keys [gap-ids chat-id gap-parameters public? community?]}]
(views/letsubs [in-progress? [:chats/fetching-gap-in-progress?
gap-ids
chat-id]
connected? [:mailserver/connected?]
first-gap? (= gap-ids #{:first-gap})]
[react/view {:style (style/gap-container)}
[react/touchable-highlight
{:on-press (when (and connected? (not in-progress?))
(on-press chat-id gap-ids))
:style style/touchable}
[react/view {:style style/label-container}
(if in-progress?
[react/activity-indicator]
[react/nested-text
{:style (style/gap-text connected?)}
(i18n/label (if first-gap? :t/load-more-messages :t/fetch-messages))
(when first-gap?
[{:style style/date}
(let [date (datetime/timestamp->long-date
(* 1000 (:from gap-parameters)))]
(str
"\n"
(i18n/label :t/load-messages-before
{:date date})))])])]]]))
(when (or (not first-gap?) public? community?)
[react/view {:style (style/gap-container)}
[react/touchable-highlight
{:on-press (when (and connected? (not in-progress?))
(on-press chat-id gap-ids))
:style style/touchable}
[react/view {:style style/label-container}
(if in-progress?
[react/activity-indicator]
[react/nested-text
{:style (style/gap-text connected?)}
(i18n/label (if first-gap? :t/load-more-messages :t/fetch-messages))
(when first-gap?
[{:style style/date}
(let [date (datetime/timestamp->long-date
(* 1000 (:from gap-parameters)))]
(str
"\n"
(i18n/label :t/load-messages-before
{:date date})))])])]]])))
+140 -104
View File
@@ -24,42 +24,66 @@
[status-im.ui.screens.chat.components.reply :as components.reply]
[status-im.ui.screens.chat.message.link-preview :as link-preview]
[status-im.ui.screens.communities.icon :as communities.icon]
[status-im.ui.components.animation :as animation]
[status-im.chat.models.pin-message :as models.pin-message])
(: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))
(def edited-at-text (str " ⌫ " (i18n/label :t/edited)))
(defn message-status [{:keys [outgoing content outgoing-status pinned edited-at in-popover?]}]
(when-not in-popover? ;; We keep track if showing this message in a list in pin-limit-popover
[react/view
{:align-self :flex-end
:position :absolute
:bottom 9 ; 6 Bubble bottom, 3 message baseline
(if (:rtl? content) :left :right) 0
:flex-direction :row
:align-items :flex-end}
(when outgoing
[icons/icon (case outgoing-status
:sending :tiny-icons/tiny-pending
:sent :tiny-icons/tiny-sent
:not-sent :tiny-icons/tiny-warning
:delivered :tiny-icons/tiny-delivered
:tiny-icons/tiny-pending)
{:width 16
:height 12
:color (if pinned colors/gray colors/white)
:accessibility-label (name outgoing-status)}])
(when edited-at [react/text {:style (style/message-status-text (and outgoing (not pinned)))} edited-at-text])]))
(defn message-timestamp
([message]
[message-timestamp message false])
([{:keys [timestamp-str outgoing content outgoing-status pinned edited-at in-popover?]} justify-timestamp?]
(when-not in-popover? ;; We keep track if showing this message in a list in pin-limit-popover
[react/view (when justify-timestamp?
{:align-self :flex-end
:position :absolute
:bottom 9 ; 6 Bubble bottom, 3 message baseline
(if (:rtl? content) :left :right) 0
:flex-direction :row
:align-items :flex-end})
(when (and outgoing justify-timestamp?)
[icons/icon (case outgoing-status
:sending :tiny-icons/tiny-pending
:sent :tiny-icons/tiny-sent
:not-sent :tiny-icons/tiny-warning
:delivered :tiny-icons/tiny-delivered
:tiny-icons/tiny-pending)
{:width 16
:height 12
:color (if pinned colors/gray colors/white)
:accessibility-label (name outgoing-status)}])
[react/text {:style (style/message-timestamp-text (and outgoing (not pinned)))}
(str
timestamp-str
(when edited-at edited-at-text))]])))
[{:keys [timestamp-str in-popover?] :as message} show-timestamp?]
(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)]
[react/animated-view {:style (style/message-timestamp-wrapper message) :opacity anim-opacity}
(when @show-timestamp? (message-timestamp-anim anim-opacity show-timestamp?))
[react/text
{:style (style/message-timestamp-text)
:accessibility-label :message-timestamp}
timestamp-str]])))
(defview quoted-message
[_ {:keys [from parsed-text image]} outgoing current-public-key public? pinned]
@@ -168,19 +192,19 @@
(defn render-parsed-text [message tree]
(reduce (fn [acc e] (render-block message acc e)) [:<>] tree))
(defn render-parsed-text-with-timestamp [{:keys [timestamp-str outgoing edited-at in-popover?] :as message} tree]
(defn render-parsed-text-with-message-status [{:keys [outgoing edited-at in-popover?] :as message} tree]
(let [elements (render-parsed-text message tree)
timestamp [react/text {:style (style/message-timestamp-placeholder)}
(str (if (and outgoing (not in-popover?)) " " " ") (when-not in-popover? (str timestamp-str (when edited-at edited-at-text))))]
message-status [react/text {:style (style/message-status-placeholder)}
(str (if (and outgoing (not in-popover?)) " " " ") (when (and (not in-popover?) edited-at) edited-at-text))]
last-element (peek elements)]
;; Using `nth` here as slightly faster than `first`, roughly 30%
;; It's worth considering pure js structures for this code path as
;; it's perfomance critical
(if (= react/text-class (nth last-element 0))
;; Append timestamp to last text
(conj (pop elements) (conj last-element timestamp))
;; Append timestamp to new block
(conj elements timestamp))))
;; Append message status to last text
(conj (pop elements) (conj last-element message-status))
;; Append message status to new block
(conj elements message-status))))
(defn unknown-content-type
[{:keys [outgoing content-type content] :as message}]
@@ -409,7 +433,8 @@
(defn collapsible-text-message [{:keys [mentioned]} _]
(let [collapsed? (reagent/atom false)
collapsible? (reagent/atom false)]
collapsible? (reagent/atom false)
show-timestamp? (reagent/atom false)]
(fn [{:keys [content outgoing current-public-key public? pinned in-popover?] :as message} on-long-press modal]
(let [max-height (when-not (or outgoing modal)
(if @collapsible?
@@ -418,7 +443,8 @@
[react/touchable-highlight
(when-not modal
{:on-press (fn [_]
(react/dismiss-keyboard!))
(react/dismiss-keyboard!)
(reset! show-timestamp? true))
:delay-long-press 100
:on-long-press (fn []
(if @collapsed?
@@ -426,40 +452,41 @@
(js/setTimeout #(on-long-press-fn on-long-press message content) 200))
(on-long-press-fn on-long-press message content)))
:disabled in-popover?})
[react/view {:style (style/message-view message)}
[react/view {:style (style/message-view-content)
:max-height max-height}
(let [response-to (:response-to content)]
[react/view {:on-layout
#(when (and (> (.-nativeEvent.layout.height ^js %) max-message-height-px)
(not @collapsible?)
(not outgoing)
(not modal))
(reset! collapsed? true)
(reset! collapsible? true))}
(when (and (seq response-to) (:quoted-message message))
[quoted-message response-to (:quoted-message message) outgoing current-public-key public? pinned])
[render-parsed-text-with-timestamp message (:parsed-text content)]])
(when-not @collapsed?
[message-timestamp message true])
(when (and @collapsible? (not modal))
(if @collapsed?
(let [color (if pinned colors/pin-background (if mentioned colors/mentioned-background colors/blue-light))]
[react/touchable-highlight
{:on-press #(swap! collapsed? not)
:style {:position :absolute :bottom 0 :left 0 :right 0 :height 72}}
[react/linear-gradient {:colors [(str color "00") color]
:start {:x 0 :y 0} :end {:x 0 :y 0.9}}
[react/view {:height 72 :align-self :center :justify-content :flex-end
:padding-bottom 10}
[react/view (style/collapse-button)
[icons/icon :main-icons/dropdown
{:color colors/white}]]]]])
[react/touchable-highlight {:on-press #(swap! collapsed? not)
:style {:align-self :center :margin 5}}
[react/view (style/collapse-button)
[icons/icon :main-icons/dropdown-up
{:color colors/white}]]]))]]]))))
[react/view (style/message-view-wrapper outgoing)
[message-timestamp message show-timestamp?]
[react/view {:style (style/message-view message)}
[react/view {:style (style/message-view-content)
:max-height max-height}
(let [response-to (:response-to content)]
[react/view {:on-layout
#(when (and (> (.-nativeEvent.layout.height ^js %) max-message-height-px)
(not @collapsible?)
(not outgoing)
(not modal))
(reset! collapsed? true)
(reset! collapsible? true))}
(when (and (seq response-to) (:quoted-message message))
[quoted-message response-to (:quoted-message message) outgoing current-public-key public? pinned])
[render-parsed-text-with-message-status message (:parsed-text content)]])
(when-not @collapsible? [message-status message])
(when (and @collapsible? (not modal))
(if @collapsed?
(let [color (if pinned colors/pin-background (if mentioned colors/mentioned-background colors/blue-light))]
[react/touchable-highlight
{:on-press #(swap! collapsed? not)
:style {:position :absolute :bottom 0 :left 0 :right 0 :height 72}}
[react/linear-gradient {:colors [(str color "00") color]
:start {:x 0 :y 0} :end {:x 0 :y 0.9}}
[react/view {:height 72 :align-self :center :justify-content :flex-end
:padding-bottom 10}
[react/view (style/collapse-button)
[icons/icon :main-icons/dropdown
{:color colors/white}]]]]])
[react/touchable-highlight {:on-press #(swap! collapsed? not)
:style {:align-self :center :margin 5}}
[react/view (style/collapse-button)
[icons/icon :main-icons/dropdown-up
{:color colors/white}]]]))]]]]))))
(defmethod ->message constants/content-type-text
[message {:keys [on-long-press modal] :as reaction-picker}]
@@ -484,33 +511,37 @@
(defmethod ->message constants/content-type-emoji
[{:keys [content current-public-key outgoing public? pinned in-popover? message-pin-enabled] :as message} {:keys [on-long-press modal]
:as reaction-picker}]
(let [response-to (:response-to content)]
[message-content-wrapper message
[react/touchable-highlight (when-not modal
{:disabled in-popover?
:on-press (fn []
(react/dismiss-keyboard!))
:delay-long-press 100
:on-long-press (fn []
(on-long-press
(concat
[{:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message])
:id :reply
:label (i18n/label :t/message-reply)}
{:on-press #(react/copy-to-clipboard (get content :text))
:id :copy
:label (i18n/label :t/sharing-copy-to-clipboard)}]
(when message-pin-enabled [{:on-press #(pin-message message)
:label (if pinned (i18n/label :t/unpin) (i18n/label :t/pin))}]))))})
[react/view (style/message-view message)
[react/view {:style (style/message-view-content)}
[react/view {:style (style/style-message-text outgoing)}
(when (and (seq response-to) (:quoted-message message))
[quoted-message response-to (:quoted-message message) outgoing current-public-key public? pinned])
[react/text {:style (style/emoji-message message)}
(:text content)]]
[message-timestamp message]]]]
reaction-picker]))
(let [response-to (:response-to content)
show-timestamp? (reagent/atom false)]
(fn [] [message-content-wrapper message
[react/touchable-highlight (when-not modal
{:disabled in-popover?
:on-press (fn []
(react/dismiss-keyboard!)
(reset! show-timestamp? true))
:delay-long-press 100
:on-long-press (fn []
(on-long-press
(concat
[{:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message])
:id :reply
:label (i18n/label :t/message-reply)}
{:on-press #(react/copy-to-clipboard (get content :text))
:id :copy
:label (i18n/label :t/sharing-copy-to-clipboard)}]
(when message-pin-enabled [{:on-press #(pin-message message)
:label (if pinned (i18n/label :t/unpin) (i18n/label :t/pin))}]))))})
[react/view (style/message-view-wrapper outgoing)
[message-timestamp message show-timestamp?]
[react/view (style/message-view message)
[react/view {:style (style/message-view-content)}
[react/view {:style (style/style-message-text outgoing)}
(when (and (seq response-to) (:quoted-message message))
[quoted-message response-to (:quoted-message message) outgoing current-public-key public? pinned])
[react/text {:style (style/emoji-message message)}
(:text content)]]
[message-status message]]]]]
reaction-picker])))
(defmethod ->message constants/content-type-sticker
[{:keys [content from outgoing in-popover?]
@@ -556,14 +587,19 @@
(defmethod ->message constants/content-type-audio [message {:keys [on-long-press modal]
:as reaction-picker}]
[message-content-wrapper message
[react/touchable-highlight (when-not modal
{:on-long-press
(fn [] (on-long-press []))})
[react/view {:style (style/message-view message) :accessibility-label :audio-message}
[react/view {:style (style/message-view-content)}
[message.audio/message-content message [message-timestamp message false]]]]]
reaction-picker])
(let [show-timestamp? (reagent/atom false)]
(fn [] [message-content-wrapper message
[react/touchable-highlight (when-not modal
{:on-long-press
(fn [] (on-long-press []))
:on-press (fn []
(reset! show-timestamp? true))})
[react/view (style/message-view-wrapper (:outgoing message))
[message-timestamp message show-timestamp?]
[react/view {:style (style/message-view message) :accessibility-label :audio-message}
[react/view {:style (style/message-view-content)}
[message.audio/message-content message] [message-status message]]]]]
reaction-picker])))
(defmethod ->message :default [message]
[message-content-wrapper message
+5 -3
View File
@@ -4,11 +4,12 @@
[status-im.ui.screens.chat.styles.photos :as style]
[status-im.profile.db :as profile.db]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.utils.image :as utils.image]))
[status-im.utils.image :as utils.image]
[quo.design-system.colors :as colors]))
(def memo-photo-rend
(memoize
(fn [photo-path size accessibility-label]
(fn [photo-path size accessibility-label _]
(let [identicon? (when photo-path (profile.db/base64-png? photo-path))]
[react/view {:style (style/photo-container size)}
[react/image {:source (utils.image/source photo-path)
@@ -18,8 +19,9 @@
(when identicon?
[react/view {:style (style/photo-border size)}])]))))
;; "(colors/dark?)" is passed to memoized function to avoid previous themes cache
(defn photo [photo-path {:keys [size accessibility-label]}]
[memo-photo-rend photo-path size accessibility-label])
[memo-photo-rend photo-path size accessibility-label (colors/dark?)])
;; We optionally pass identicon for perfomance reason, so it does not have to be calculated for each message
(defn member-photo [pub-key identicon]
@@ -43,5 +43,5 @@
:justify-content :space-between})
(defn timestamp [outgoing]
(merge (message.style/message-timestamp-text outgoing)
(merge (message.style/audio-message-timestamp-text outgoing)
{:margin-left 40}))
@@ -29,11 +29,33 @@
(def message-timestamp
{:font-size 10})
(defn message-timestamp-placeholder
(defn message-status-placeholder
[]
(merge message-timestamp {:opacity 0 :color "rgba(0,0,0,0)"}))
(defn message-timestamp-text
(defn message-timestamp-wrapper [{:keys [last-in-group? outgoing group-chat]}]
{:justify-content :center
(if outgoing :margin-right :margin-left) 12 ;; horizontal margin is only required at the adjust side of the message.
:margin-top (if (and last-in-group?
(or outgoing
(not group-chat)))
16
0) ;; Add gap between message groups
:opacity 0})
(defn message-timestamp-text []
(merge message-timestamp
{:color colors/gray
:text-align :center}))
(defn message-status-text [outgoing]
{:font-size 10
:line-height 10
:color (if outgoing
colors/white-transparent-70-persist
colors/gray)})
(defn audio-message-timestamp-text
[outgoing]
(merge message-timestamp
{:line-height 10
@@ -44,7 +66,7 @@
(defn message-wrapper [{:keys [outgoing in-popover?]}]
(if (and outgoing (not in-popover?))
{:margin-left 96}
{:margin-right 52}))
{:margin-right 96}))
(defn message-author-wrapper
[outgoing display-photo? in-popover?]
@@ -156,10 +178,10 @@
:padding-left 3})
(defn emoji-message
[{:keys [incoming-group]}]
[{:keys [incoming-group outgoing]}]
{:font-size 28
:line-height 34 ;TODO: Smaller crops the icon on the top
:margin-right 12
:margin-right (if outgoing 18 0) ;; Margin to display outgoing message status
:margin-top (if incoming-group 4 0)})
(defn collapse-button []
@@ -171,6 +193,10 @@
:shadow-color (:shadow-01 @colors/theme)
:shadow-offset {:width 0 :height 4}})
(defn message-view-wrapper [outgoing]
{:align-self :flex-end
:flex-direction (if outgoing :row :row-reverse)})
(defn message-view
[{:keys [content-type outgoing group-chat last-in-group? mentioned pinned]}]
(merge
+47 -49
View File
@@ -243,7 +243,8 @@
(defn render-fn [{:keys [outgoing type] :as message}
idx
_
{:keys [group-chat public? current-public-key space-keeper chat-id show-input? message-pin-enabled edit-enabled in-pinned-view?]}]
{:keys [group-chat public? community? current-public-key space-keeper
chat-id show-input? message-pin-enabled edit-enabled in-pinned-view?]}]
[react/view {:style (when (and platform/android? (not in-pinned-view?)) {:scaleY -1})}
(if (= type :datemark)
[message-datemark/chat-datemark (:value message)]
@@ -255,6 +256,7 @@
:incoming-group (and group-chat (not outgoing))
:group-chat group-chat
:public? public?
:community? community?
:current-public-key current-public-key
:show-input? show-input?
:message-pin-enabled message-pin-enabled
@@ -285,6 +287,7 @@
community-admin?))))]
{:group-chat group-chat
:public? public?
:community? (not (nil? community-id))
:current-public-key current-public-key
:space-keeper space-keeper
:chat-id chat-id
@@ -295,7 +298,7 @@
(defn messages-view [{:keys [chat bottom-space pan-responder space-keeper show-input?]}]
(let [{:keys [group-chat chat-id public? community-id admins]} chat
messages @(re-frame/subscribe [:chats/chat-messages-stream chat-id])]
messages @(re-frame/subscribe [:chats/raw-chat-messages-stream chat-id])]
;;do not use anonymous functions for handlers
[list/flat-list
(merge
@@ -343,8 +346,7 @@
[toolbar-content/toolbar-content-view-inner chat-info]]))
(defn chat []
(let [curr-chat-id (:chat-id @(re-frame/subscribe [:chats/current-chat-chat-view]))
bottom-space (reagent/atom 0)
(let [bottom-space (reagent/atom 0)
panel-space (reagent/atom 52)
active-panel (reagent/atom nil)
position-y (animated/value 0)
@@ -355,48 +357,44 @@
space-keeper (get-space-keeper-ios bottom-space panel-space active-panel text-input-ref)
set-active-panel (get-set-active-panel active-panel)
on-close #(set-active-panel nil)]
(reagent/create-class
{:component-will-unmount #(re-frame/dispatch-sync [:close-chat curr-chat-id])
:component-did-mount (fn [] (js/setTimeout #(re-frame/dispatch [:set :ignore-close-chat false]) 500))
:reagent-render
(fn []
(let [{:keys [chat-id show-input? group-chat admins invitation-admin] :as chat}
;;we want to react only on these fields, do not use full chat map here
@(re-frame/subscribe [:chats/current-chat-chat-view])
max-bottom-space (max @bottom-space @panel-space)]
[:<>
[connectivity/loading-indicator]
(when chat-id
(if group-chat
[invitation-requests chat-id admins]
[add-contact-bar chat-id]))
;;MESSAGES LIST
[messages-view {:chat chat
:bottom-space max-bottom-space
:pan-responder pan-responder
:space-keeper space-keeper
:show-input? show-input?}]
(when (and group-chat invitation-admin)
[accessory/view {:y position-y
:on-update-inset on-update}
[invitation-bar chat-id]])
[components/autocomplete-mentions text-input-ref max-bottom-space]
(when show-input?
;; NOTE: this only accepts two children
[accessory/view {:y position-y
:pan-state pan-state
:has-panel (boolean @active-panel)
:on-close on-close
:on-update-inset on-update}
[react/view
[edit/edit-message-auto-focus-wrapper text-input-ref]
[reply/reply-message-auto-focus-wrapper text-input-ref]
;; We set the key so we can force a re-render as
;; it does not rely on ratom but just atoms
^{:key (str @components/chat-input-key "chat-input")}
[components/chat-toolbar
{:chat-id chat-id
:active-panel @active-panel
:set-active-panel set-active-panel
:text-input-ref text-input-ref}]]
[bottom-sheet @active-panel]])]))})))
(fn []
(let [{:keys [chat-id show-input? group-chat admins invitation-admin] :as chat}
;;we want to react only on these fields, do not use full chat map here
@(re-frame/subscribe [:chats/current-chat-chat-view])
max-bottom-space (max @bottom-space @panel-space)]
[:<>
[connectivity/loading-indicator]
(when chat-id
(if group-chat
[invitation-requests chat-id admins]
[add-contact-bar chat-id]))
;;MESSAGES LIST
[messages-view {:chat chat
:bottom-space max-bottom-space
:pan-responder pan-responder
:space-keeper space-keeper
:show-input? show-input?}]
(when (and group-chat invitation-admin)
[accessory/view {:y position-y
:on-update-inset on-update}
[invitation-bar chat-id]])
[components/autocomplete-mentions text-input-ref max-bottom-space]
(when show-input?
;; NOTE: this only accepts two children
[accessory/view {:y position-y
:pan-state pan-state
:has-panel (boolean @active-panel)
:on-close on-close
:on-update-inset on-update}
[react/view
[edit/edit-message-auto-focus-wrapper text-input-ref]
[reply/reply-message-auto-focus-wrapper text-input-ref]
;; We set the key so we can force a re-render as
;; it does not rely on ratom but just atoms
^{:key (str @components/chat-input-key "chat-input")}
[components/chat-toolbar
{:chat-id chat-id
:active-panel @active-panel
:set-active-panel set-active-panel
:text-input-ref text-input-ref}]]
[bottom-sheet @active-panel]])]))))
@@ -12,6 +12,7 @@
(fn []
(let [current-chat (<sub [:chat-by-id chat-id])
{:keys [chat-name color description community-id emoji]} current-chat
{:keys [position]} (<sub [:chats/community-chat-by-id community-id chat-id])
category (<sub [:chats/category-by-chat-id community-id chat-id])
{:keys [admin]} (<sub [:communities/community community-id])
pinned-messages (<sub [:chats/pinned chat-id])]
@@ -27,7 +28,8 @@
color
emoji
chat-id
(:id category)])}])
(:id category)
position])}])
:extended-header (profile-header/extended-header
{:title chat-name
:color color
@@ -22,7 +22,6 @@
[re-frame.core :as re-frame]
[status-im.ui.screens.chat.sheets :as sheets]
[status-im.ui.components.accordion :as accordion]
[clojure.string :as string]
[status-im.ui.screens.communities.styles :as styles]))
(def request-cooldown-ms (* 60 1000))
@@ -139,12 +138,6 @@
:title (i18n/label :t/edit-chats)
:accessibility-label :community-edit-chats
:icon :main-icons/edit
:on-press #(hide-sheet-and-dispatch [:open-modal :community-edit-chats {:community-id id}])}]
[quo/list-item
{:theme :accent
:title (i18n/label :t/reorder-categories)
:accessibility-label :community-reorder-categories
:icon :main-icons/channel-category
:on-press #(hide-sheet-and-dispatch
[:open-modal :community-reorder-categories {:community-id id}])}]])]))
@@ -154,60 +147,42 @@
:color :secondary}
text]])
(defn community-chat-item [{:keys [chat-id] :as home-item} _ _ {:keys [from-chat]}]
(defn community-chat-item [{:keys [chat-id] :as home-item} _ _ _]
[inner-item/home-list-item
;; We want communities to behave as public chats when it comes to
;; unread indicator
(assoc home-item :public? true)
{:on-press (fn []
(re-frame/dispatch [:dismiss-keyboard])
(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id (not from-chat)])
(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id])
(re-frame/dispatch [:search/home-filter-changed nil])
(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])}])}])
(defn categories-accordion [community-id chats categories edit data]
(defn categories-accordion [community-id chats categories data]
[:<>
(for [{:keys [name id state]} (vals categories)]
(for [{:keys [name id state]} categories]
^{:key (str "cat" name id)}
[:<>
[accordion/section
{:on-open #(>evt [::communities/store-category-state community-id id true])
:on-close #(>evt [::communities/store-category-state community-id id false])
:default state
:opened edit
:disabled edit
:title [rn/view styles/category-item
(if edit
[rn/touchable-opacity {:on-press #(>evt [:delete-community-category community-id id])}
[icons/icon :main-icons/delete-circle {:no-color true}]]
[icons/icon :main-icons/channel-category {:color colors/gray}])
[icons/icon :main-icons/channel-category {:color colors/gray}]
[rn/text {:style {:font-size 17 :margin-left 10 :color colors/black}} name]]
:content [rn/view
:content [:<>
(for [chat (get chats id)]
(if edit
^{:key (str "chat" chat id)}
[rn/view styles/category-item
[rn/touchable-opacity {:on-press #(>evt [:remove-chat-from-community-category
community-id
(string/replace (:chat-id chat) community-id "")
(:categoryID chat)])}
[icons/icon :main-icons/delete-circle {:no-color true}]]
[rn/view {:flex 1}
[inner-item/home-list-item
(assoc chat :public? true)]]]
^{:key (str "chat" chat id)}
[community-chat-item chat nil nil data]))]}]
^{:key (str "chat" chat id)}
[community-chat-item chat nil nil data])]}]
[quo/separator]])])
(defn community-chat-list [community-id categories edit from-chat]
(let [chats (<sub [:chats/categories-by-community-id community-id])]
(defn community-chat-list [community-id categories from-chat]
(let [chats (<sub [:chats/sorted-categories-by-community-id community-id])]
(if (and (empty? categories) (empty? chats))
(if edit
[blank-page (i18n/label :t/welcome-community-blank-message-edit-chats)]
[blank-page (i18n/label :t/welcome-community-blank-message)])
[blank-page (i18n/label :t/welcome-community-blank-message)]
[list/flat-list
{:key-fn :chat-id
:content-container-style {:padding-bottom 8}
@@ -215,7 +190,7 @@
:data (get chats "")
:render-data {:from-chat from-chat}
:render-fn community-chat-item
:header [categories-accordion community-id chats categories edit {:from-chat from-chat}]
:header [categories-accordion community-id chats categories {:from-chat from-chat}]
:footer [rn/view {:height 68}]}])))
(defn channel-preview-item [{:keys [id name]}]
@@ -267,23 +242,6 @@
[components.react/small-loading-indicator]
(i18n/label :t/fetch-community))]]]))
(defn community-edit []
(let [{:keys [community-id]} (<sub [:get-screen-params])]
(fn []
(let [{:keys [id name images members permissions color]} (<sub [:communities/community community-id])
categories (<sub [:communities/sorted-categories community-id])]
[:<>
[topbar/topbar
{:modal? true
:content [toolbar-content
id
name
color
images
(not= (:access permissions) constants/community-no-membership-access)
(count members)]}]
[community-chat-list id categories true false]]))))
(defn community []
(let [{:keys [community-id from-chat]} (<sub [:get-screen-params])]
(fn []
@@ -65,4 +65,4 @@
@category-name
(vec @selected-items)]
3000)}
(i18n/label :t/create)]}]]))))
(i18n/label :t/create)]}]]))))
@@ -1,60 +1,176 @@
(ns status-im.ui.screens.communities.reorder-categories
(:require [quo.core :as quo]
[clojure.walk :as walk]
[quo.react-native :as rn]
[reagent.core :as reagent]
[clojure.string :as string]
[clojure.set :as clojure.set]
[status-im.i18n.i18n :as i18n]
[status-im.utils.utils :as utils]
[status-im.constants :as constants]
[quo.design-system.colors :as colors]
[status-im.ui.components.react :as react]
[status-im.utils.platform :as platform]
[status-im.ui.components.topbar :as topbar]
[status-im.communities.core :as communities]
[status-im.utils.handlers :refer [>evt <sub]]
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.screens.communities.styles :as styles]
[status-im.ui.screens.communities.community :as community]))
[status-im.ui.screens.communities.community :as community]
[status-im.ui.screens.home.views.inner-item :as inner-item]))
(defn category-change-positon [community-id key up? position]
(let [new-position (if up? (dec position) (inc position))]
(>evt [::communities/reorder-community-category community-id key new-position])))
(def collapse-chats? (reagent/atom false))
(def data (reagent/atom []))
(defn category-item-button [community-id up? disabled? key position]
(let [[margin-right icon] (if up?
[10 :main-icons/dropdown-up]
[25 :main-icons/dropdown])]
[react/touchable-opacity {:disabled disabled?
:on-press #(category-change-positon
community-id key up? position)}
[rn/view {:style (styles/reorder-categories-button margin-right)}
[icons/icon icon {:color colors/black}]]]))
(defn show-delete-chat-confirmation [community-id chat-id]
(utils/show-confirmation
{:title (i18n/label :t/delete-confirmation)
:content (i18n/label :t/delete-chat-confirmation)
:confirm-button-text (i18n/label :t/delete)
:on-accept #(>evt [:delete-community-chat community-id chat-id])}))
(defn category-item [community-id count {:keys [key name position]}]
(let [up-disabled? (= position 0)
down-disabled? (= position (dec count))]
(defn show-delete-catgory-confirmation [community-id category-id]
(utils/show-confirmation
{:title (i18n/label :t/delete-confirmation)
:content (i18n/label :t/delete-category-confirmation)
:confirm-button-text (i18n/label :t/delete)
:on-accept #(>evt [:delete-community-category community-id category-id])}))
(defn chat-item
[{:keys [id community-id] :as home-item} is-active? drag]
(let [chat-id (string/replace id community-id "")
background-color (if is-active? colors/gray-lighter colors/white)
home-item (clojure.set/rename-keys home-item {:id :chat-id})]
[rn/view {:accessibility-label :chat-item
:style (merge styles/category-item
{:background-color background-color})}
[rn/touchable-opacity
{:accessibility-label :delete-community-chat
:on-press #(show-delete-chat-confirmation community-id chat-id)}
[icons/icon :main-icons/delete-circle {:no-color true}]]
[rn/view {:flex 1}
[inner-item/home-list-item (assoc home-item :edit? true) {:active-opacity 1}]]
[rn/touchable-opacity {:on-long-press drag
:delay-long-press 100
:accessibility-label :chat-drag-handle
:style {:padding 20}}
[icons/icon :main-icons/reorder-handle {:no-color true :width 18 :height 12}]]]))
(defn category-item
[{:keys [id name community-id]} is-active? drag]
(let [background-color (if is-active? colors/gray-lighter colors/white)
category-none? (string/blank? id)]
[:<>
[rn/view {:style styles/reorder-categories-item}
[icons/icon :main-icons/channel-category {:color colors/gray}]
[rn/text {:style (styles/reorder-categories-text)} name]
[category-item-button community-id true up-disabled? key position]
[category-item-button community-id false down-disabled? key position]]
[quo/separator]]))
[quo/separator]
[rn/view {:accessibility-label :category-item
:style (merge styles/category-item
{:background-color background-color})}
(if category-none?
[icons/icon :main-icons/channel-category {:color colors/gray}]
[rn/touchable-opacity
{:accessibility-label :delete-category-button
:on-press #(show-delete-catgory-confirmation community-id id)}
[icons/icon :main-icons/delete-circle {:no-color true}]])
[rn/view {:flex 1}
[rn/text {:style {:font-size 17 :margin-left 10 :color colors/black}} name]]
(when (and (not category-none?) @collapse-chats?)
[rn/touchable-opacity {:accessibility-label :category-drag-handle
:on-long-press drag
:delay-long-press 100
:style {:padding 20}}
[icons/icon :main-icons/reorder-handle {:no-color true :width 18 :height 12}]])]]))
(defn categories-view [community-id categories count]
[rn/view {:accessibility-label :reorder-categories-list}
(for [category-vector categories]
(let [category (clojure.set/rename-keys (get category-vector 1) {:id :key})]
[category-item community-id count category]))])
(defn render-fn
[{:keys [chat-type] :as item} _ _ _ is-active? drag]
(if (= chat-type constants/community-chat-type)
[chat-item item is-active? drag]
[category-item item is-active? drag]))
(defn calculate-chat-new-position-and-category
[to second-call? old-category going-up?]
(let [{:keys [id chat-type categoryID position]} (get @data to)
[new-category new-position]
(if going-up?
(if (= chat-type constants/community-chat-type)
[categoryID (if second-call? (inc position) position)]
(if second-call? [id 0]
(calculate-chat-new-position-and-category (dec to) true old-category true)))
(if (= chat-type constants/community-chat-type)
(if (= categoryID old-category)
[categoryID position]
[categoryID (inc position)]) [id 0]))]
[new-category new-position]))
(defn update-local-atom [data-js]
(reset! data data-js)
(reagent/flush))
(defn on-drag-end-chat [from to data-js]
(let [{:keys [id community-id categoryID position]} (get @data from)
[new-category new-position] (calculate-chat-new-position-and-category
to false categoryID (> from to))
chat-id (string/replace id community-id "")]
(when-not (and (= new-position position) (= new-category categoryID))
(update-local-atom data-js)
(>evt [::communities/reorder-community-category-chat
community-id new-category chat-id new-position]))))
(defn on-drag-end-category [from to data-js]
(let [{:keys [id community-id position]} (get @data from)]
(when (and (< to (count @data)) (not= position to) (not= id ""))
(update-local-atom data-js)
(>evt [::communities/reorder-community-category community-id id to]))))
(defn on-drag-end-fn [from to data-js]
(if @collapse-chats?
(on-drag-end-category from to data-js)
(when-not (= to 0) (on-drag-end-chat from to data-js))))
(defn reset-data [categories chats]
(reset! data (if @collapse-chats? categories ;; If chats are collapsed then only show categories
(walk/postwalk-replace ;; else, categories and chats
{:chat-id :id}
(reduce (fn [acc category]
(-> acc
(conj category)
(into (get chats (:id category))))) [] categories)))))
(defn draggable-list []
[rn/draggable-flat-list
{:key-fn :id
:data @data
:render-fn render-fn
:autoscroll-threshold (if platform/android? 150 250)
:autoscroll-speed (if platform/android? 10 150) ;; TODO - Use same speed for both ios and android
:container-style {:margin-bottom 108} ;; after bumping react native version to > 0.64
:on-drag-end-fn on-drag-end-fn}])
(defn view []
(let [{:keys [community-id]} (<sub [:get-screen-params])
{:keys [id name images members permissions color]}
(<sub [:communities/community community-id])
categories (<sub [:communities/sorted-categories community-id])]
sorted-categories (<sub [:communities/sorted-categories community-id])
categories (if @collapse-chats? sorted-categories
(conj sorted-categories
{:id ""
:position (count sorted-categories)
:name (i18n/label :t/none)
:community-id community-id}))
chats (<sub [:chats/sorted-categories-by-community-id community-id])]
(reset-data categories chats)
[:<>
[topbar/topbar
{:modal? true
:content [community/toolbar-content id name color images
(not= (:access permissions) constants/community-no-membership-access)
(count members)]}]
(if (empty? categories)
[community/blank-page (i18n/label :t/welcome-community-blank-message-categories)]
[categories-view community-id categories (count categories)])]))
(if (and (empty? sorted-categories) (empty? chats))
[community/blank-page (i18n/label :t/welcome-community-blank-message-edit-chats)]
[:<>
[quo/list-item {:size :small
:title (i18n/label :t/rearrange-categories)
:active @collapse-chats?
:accessory :switch
:container-style {:padding-left 10}
:on-press #(reset! collapse-chats? (not @collapse-chats?))}]
[quo/separator]
[draggable-list]])]))
@@ -11,7 +11,8 @@
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.components.topbar :as topbar]
[status-im.i18n.i18n :as i18n]
[status-im.communities.core :as communities]))
[status-im.communities.core :as communities]
[quo.components.animated.pressable :as animation]))
(defn hide-sheet-and-dispatch [event]
(>evt [:bottom-sheet/hide])
@@ -19,11 +20,11 @@
(defn request-actions [community-id request-id]
[react/view {:flex-direction :row}
[react/touchable-highlight {:on-press #(re-frame/dispatch [:communities.ui/accept-request-to-join-pressed community-id request-id])}
[animation/pressable {:on-press #(re-frame/dispatch [:communities.ui/accept-request-to-join-pressed community-id request-id])}
[icons/icon :main-icons/checkmark-circle {:width 35
:height 35
:color colors/green}]]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:communities.ui/decline-request-to-join-pressed community-id request-id])}
[animation/pressable {:on-press #(re-frame/dispatch [:communities.ui/decline-request-to-join-pressed community-id request-id])}
[icons/icon :main-icons/cancel {:width 35
:height 35
:container-style {:margin-left 16}
@@ -41,7 +42,8 @@
:accessory (when can-manage-users?
[request-actions community-id id])
:icon [chat-icon/contact-icon-contacts-tab
(multiaccounts/displayed-photo member)]}]))
(multiaccounts/displayed-photo member)]
:on-press #(re-frame/dispatch [:chat.ui/show-profile public-key])}]))
(defn requests-to-join []
(let [{:keys [community-id]} (<sub [:get-screen-params])]
@@ -25,6 +25,7 @@
(let [{:keys [community-id chat]} (<sub [:get-screen-params])]
(fn []
(let [categories (<sub [:communities/sorted-categories community-id])
chats (<sub [:chats/sorted-categories-by-community-id community-id])
comm-chat (<sub [:chats/community-chat-by-id community-id (:chat-id chat)])
_ (reset! selected-item (:categoryID comm-chat))]
[:<>
@@ -44,7 +45,7 @@
:create-community-category
{:community-id community-id}])
:title (i18n/label :t/create-category)}]
:data (conj (vec (vals categories)) {:name (i18n/label :t/none) :id ""})
:data (conj categories {:name (i18n/label :t/none) :id ""})
:render-fn render-fn}]]
[toolbar/toolbar
{:show-border? true
@@ -54,6 +55,7 @@
[::communities/change-category-confirmation-pressed
community-id
@selected-item
comm-chat]
(assoc comm-chat :position
(count (get chats @selected-item)))] ;; Add as last item in new category
3000)}
(i18n/label :t/done)]}]]))))
@@ -1,34 +1,8 @@
(ns status-im.ui.screens.communities.styles
(:require [quo.design-system.colors :as colors]))
(ns status-im.ui.screens.communities.styles)
(def category-item
{:flex 1
:flex-direction :row
:align-items :center
:height 52
:margin-left 18})
(def reorder-categories-item
{:accessibility-label :reorder-categories-item
:flex-direction :row
:height 60
:align-items :center
:margin-left 18})
(defn reorder-categories-text []
{:font-size 17
:margin-left 10
:color (colors/get-color :text-01)
:flex 1})
(defn reorder-categories-button [margin-right]
{:accessibility-label :reorder-categories-button
:width 30
:height 30
:border-radius 15
:margin 10
:margin-right margin-right
:border-width 1
:justify-content :center
:align-items :center
:border-color colors/black})
:padding-left 18})
@@ -3,10 +3,12 @@
(:require [status-im.ui.components.react :as react]
[status-im.i18n.i18n :as i18n]
[status-im.constants :as constants]
[status-im.utils.config :as config]
[quo.core :as quo]
[re-frame.core :as re-frame]))
(def titles {constants/one-day (i18n/label :t/one-day)
(def titles {constants/two-mins (i18n/label :t/two-minutes)
constants/one-day (i18n/label :t/one-day)
constants/three-days (i18n/label :t/three-days)
constants/one-week (i18n/label :t/one-week)
constants/one-month (i18n/label :t/one-month)})
@@ -21,6 +23,8 @@
(views/defview default-sync-period-settings []
(views/letsubs [{:keys [default-sync-period]} [:multiaccount]]
[react/view {:margin-top 8}
(when config/two-minutes-syncing?
[radio-item constants/two-mins default-sync-period])
[radio-item constants/one-day default-sync-period]
[radio-item constants/three-days default-sync-period]
[radio-item constants/one-week default-sync-period]
+22 -37
View File
@@ -3,7 +3,6 @@
[reagent.core :as reagent]
[status-im.i18n.i18n :as i18n]
[status-im.react-native.resources :as resources]
[status-im.communities.core :as communities]
[status-im.ui.components.connectivity.view :as connectivity]
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.components.list.views :as list]
@@ -13,7 +12,6 @@
[status-im.ui.screens.home.views.inner-item :as inner-item]
[status-im.ui.screens.referrals.home-item :as referral-item]
[quo.design-system.colors :as colors]
[status-im.ui.screens.add-new.new-public-chat.view :as new-public-chat]
[quo.core :as quo]
[status-im.add-new.core :as new-chat]
[status-im.ui.components.search-input.view :as search-input]
@@ -24,7 +22,8 @@
[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.tabbar.core :as tabbar]
["react-native-navigation" :refer (Navigation)])
(:require-macros [status-im.utils.views :as views]))
(defn home-tooltip-view []
@@ -40,30 +39,10 @@
:accessibility-label :hide-home-button}
[icons/icon :main-icons/close-circle {:color colors/gray}]]
[react/i18n-text {:style styles/no-chats-text :key :chat-and-transact}]
[invite/button]
[react/view {:align-items :center :margin-bottom 16}
[react/view {:style (styles/hr-wrapper)}]
[react/i18n-text {:style (styles/or-text) :key :or}]]
[react/i18n-text {:style {:margin-horizontal 16
:text-align :center}
:key :follow-your-interests}]
[react/view {:flex-direction :row :flex-wrap :wrap :justify-content :center
:margin-top 10
:margin-bottom 18}
(for [chat (new-public-chat/featured-public-chats)]
^{:key chat}
[new-public-chat/render-topic chat])]
(when @(re-frame/subscribe [:communities/enabled?])
[:<>
[react/i18n-text {:style {:margin-horizontal 16
:text-align :center}
:key :join-a-community}]
[react/view {:flex-direction :row :flex-wrap :wrap :justify-content :center
:margin-top 10
:margin-bottom 18}
(for [{:keys [id] :as community} communities/featured]
^{:key id}
[communities.views/render-featured-community community])]])])
[react/view {:align-items :center
:margin-top 8
:margin-bottom 12}
[invite/button]]])
(defn welcome-blank-page []
[react/view {:style {:flex 1 :flex-direction :row :align-items :center :justify-content :center}}
@@ -199,13 +178,19 @@
:accessibility-label :notifications-unread-badge}]])]))
(defn home []
[react/keyboard-avoiding-view {:style {:flex 1}
:ignore-offset true}
[topbar/topbar {:title (i18n/label :t/chat)
:navigation :none
:right-component [react/view {:flex-direction :row :margin-right 16}
[connectivity/connectivity-button]
[notifications-button]]}]
[chats-list]
[plus-button]
[tabbar/tabs-counts-subscriptions]])
(reagent/create-class
{:component-did-mount #(set! (.-navigationEventListener %) (.bindComponent (.events Navigation) % "home"))
:componentWillAppear #(do (re-frame/dispatch-sync [:set :view-id :home])
(re-frame/dispatch [:close-chat]))
:reagent-render
(fn []
[react/keyboard-avoiding-view {:style {:flex 1}
:ignore-offset true}
[topbar/topbar {:title (i18n/label :t/chat)
:navigation :none
:right-component [react/view {:flex-direction :row :margin-right 16}
[connectivity/connectivity-button]
[notifications-button]]}]
[chats-list]
[plus-button]
[tabbar/tabs-counts-subscriptions]])}))
@@ -149,13 +149,16 @@
:else
[icons/icon :main-icons/tiny-new-contact (icon-style)]))
(defn chat-item-title [chat-id muted group-chat chat-name]
(defn chat-item-title [chat-id muted group-chat chat-name edit?]
[quo/text {:weight :medium
:color (when muted :secondary)
:accessibility-label :chat-name-text
:ellipsize-mode :tail
:number-of-lines 1
:style {:position :absolute :left 92 :top 10 :right 90}}
:style {:position :absolute
:left 92
:top 10
:right (if edit? 50 90)}}
(if group-chat
(utils/truncate-str chat-name 30)
;; This looks a bit odd, but I would like only to subscribe
@@ -164,7 +167,7 @@
(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 public? timestamp last-message muted emoji highlight]} home-item
(let [{:keys [chat-id chat-name color group-chat public? timestamp last-message muted emoji highlight edit?]} home-item
background-color (when highlight (colors/get-color :interactive-02))]
[react/touchable-opacity (merge {:style {:height 64 :background-color background-color}} opts)
[:<>
@@ -178,13 +181,15 @@
:default-chat-icon-text (if (string/blank? emoji)
(chat-icon.styles/default-chat-icon-text 40)
(chat-icon.styles/emoji-chat-icon-text 40))}]
[chat-item-title chat-id muted group-chat chat-name]
[react/text {:style styles/datetime-text
:number-of-lines 1
:accessibility-label :last-message-time-text}
;;TODO (perf) move to event
(memo-timestamp (if (pos? (:whisper-timestamp last-message))
(:whisper-timestamp last-message)
timestamp))]
[message-content-text (select-keys last-message [:content :content-type :community-id]) true]
[unviewed-indicator home-item]]]))
[chat-item-title chat-id muted group-chat chat-name edit?]
(when-not edit?
[:<>
[react/text {:style styles/datetime-text
:number-of-lines 1
:accessibility-label :last-message-time-text}
;;TODO (perf) move to event
(memo-timestamp (if (pos? (:whisper-timestamp last-message))
(:whisper-timestamp last-message)
timestamp))]
[unviewed-indicator home-item]])
[message-content-text (select-keys last-message [:content :content-type :community-id]) true]]]))
+44 -21
View File
@@ -44,6 +44,17 @@
[status-im.ui.screens.currency-settings.views :as currency-settings]
[status-im.ui.screens.dapps-permissions.views :as dapps-permissions]
[status-im.ui.screens.default-sync-period-settings.view :as default-sync-period-settings]
[status-im.ui.screens.wallet.settings.views :as wallet-settings]
[status-im.ui.screens.wallet.transactions.views :as wallet-transactions]
[status-im.ui.screens.wallet.custom-tokens.views :as custom-tokens]
[status-im.ui.screens.wallet.accounts.views :as wallet.accounts]
[status-im.ui.screens.wallet.collectibles.views :as wallet.collectibles]
[status-im.ui.screens.wallet.account.views :as wallet.account]
[status-im.ui.screens.wallet.add-new.views :as add-account]
[status-im.ui.screens.wallet.account-settings.views :as account-settings]
[status-im.ui.screens.wallet.swap.views :as wallet.swap]
[status-im.ui.screens.status.views :as status.views]
[status-im.ui.screens.profile.user.views :as profile.user]
[status-im.ui.screens.ens.views :as ens]
[status-im.ui.screens.fleet-settings.views :as fleet-settings]
[status-im.ui.screens.glossary.view :as glossary]
@@ -91,7 +102,6 @@
[status-im.ui.screens.profile.contact.views :as contact]
[status-im.ui.screens.profile.group-chat.views :as profile.group-chat]
[status-im.ui.screens.profile.seed.views :as profile.seed]
[status-im.ui.screens.profile.user.views :as profile.user]
[status-im.ui.screens.progress.views :as progress]
[status-im.ui.screens.qr-scanner.views :as qr-scanner]
[status-im.ui.screens.referrals.public-chat :as referrals.public-chat]
@@ -99,7 +109,6 @@
[status-im.ui.screens.reset-password.views :as reset-password]
[status-im.ui.screens.rpc-usage-info :as rpc-usage-info]
[status-im.ui.screens.status.new.views :as status.new]
[status-im.ui.screens.status.views :as status.views]
[status-im.ui.screens.stickers.views :as stickers]
[status-im.ui.screens.sync-settings.views :as sync-settings]
[status-im.ui.screens.terms-of-service.views :as terms-of-service]
@@ -107,18 +116,10 @@
:as
edit-wakuv2-node]
[status-im.ui.screens.wakuv2-settings.views :as wakuv2-settings]
[status-im.ui.screens.wallet.account-settings.views :as account-settings]
[status-im.ui.screens.wallet.account.views :as wallet.account]
[status-im.ui.screens.wallet.accounts-manage.views :as accounts-manage]
[status-im.ui.screens.wallet.accounts.views :as wallet.accounts]
[status-im.ui.screens.wallet.add-new.views :as add-account]
[status-im.ui.screens.wallet.buy-crypto.views :as wallet.buy-crypto]
[status-im.ui.screens.wallet.collectibles.views :as wallet.collectibles]
[status-im.ui.screens.wallet.custom-tokens.views :as custom-tokens]
[status-im.ui.screens.wallet.recipient.views :as recipient]
[status-im.ui.screens.wallet.send.views :as wallet.send]
[status-im.ui.screens.wallet.settings.views :as wallet-settings]
[status-im.ui.screens.wallet.transactions.views :as wallet-transactions]))
[status-im.ui.screens.wallet.send.views :as wallet.send]))
(def components
[{:name :chat-toolbar
@@ -219,9 +220,10 @@
;Chat
{:name :chat
:options {:topBar {:title {:component {:name :chat-toolbar :id :chat-toolbar}
:alignment :fill}
:rightButtons (right-button-options :chat :more)}}
:options {:popGesture false
:topBar {:title {:component {:name :chat-toolbar :id :chat-toolbar}
:alignment :fill}
:rightButtons (right-button-options :chat :more)}}
:right-handler chat/topbar-button
:component chat/chat}
@@ -287,11 +289,6 @@
;;TODO custom
:options {:topBar {:visible false}}
:component select-category/view}
{:name :community-edit-chats
;;TODO custom
:insets {:bottom true}
:options {:topBar {:visible false}}
:component community/community-edit}
{:name :community-reorder-categories
:insets {:top false}
:options {:topBar {:visible false}}
@@ -399,6 +396,32 @@
:options {:topBar {:title {:text (i18n/label :t/wallet-manage-accounts)}}}
:component accounts-manage/manage}
{:name :token-swap
;;TODO dynamic title
:options {:topBar {:visible false}}
:component wallet.swap/swap}
{:name :token-swap-advanced-nonce
:options {:topBar {:title {:text (i18n/label :t/nonce)}}}
:component wallet.swap/nonce-modal}
{:name :token-swap-advanced-approve-token
:options {:topBar {:title {:text (i18n/label :t/approve-token)}}}
:component wallet.swap/approve-token-modal}
{:name :token-swap-advanced-transaction-fee
:options {:topBar {:title {:text (i18n/label :t/transaction-fee)}}}
:component wallet.swap/transaction-fee-modal}
{:name :token-swap-advanced-swap-details
:options {:topBar {:title {:text (i18n/label :t/swap-details)}}}
:component wallet.swap/swap-details-modal}
{:name :swap-asset-selector
;;TODO dynamic title
:options {:topBar {:visible false}}
:component wallet.swap/asset-selector}
;;MY STATUS
{:name :status
@@ -707,8 +730,8 @@
:options {:topBar {:visible false}}
:component wallet.buy-crypto/website}
{:name :nft-details
:insets {:bottom true}
{:name :nft-details
:insets {:bottom true}
;;TODO dynamic title
:options {:topBar {:visible false}}
:component wallet.collectibles/nft-details-modal}
@@ -40,6 +40,8 @@
:chevron true
:accessory :text
:accessory-text (cond
(= default-sync-period constants/two-mins)
(i18n/label :t/two-minutes)
(= default-sync-period constants/one-day)
(i18n/label :t/one-day)
(= default-sync-period constants/three-days)
@@ -42,3 +42,19 @@
:duration 200
:easing (.-ease ^js animation/easing)
:useNativeDriver true}))
(def round-action-button
{:background-color colors/blue
:height 44
:flex 1
:justify-content :center
:align-items :center
:width 44
:border-radius 44})
(def top-actions
{:flex 1
:flex-direction :row
:justify-content :space-between
:width "60%"
:align-self :center})
@@ -7,16 +7,17 @@
[quo.design-system.colors :as colors]
[status-im.ui.components.icons.icons :as icons]
[quo.core :as quo]
[quo.design-system.spacing :as spacing]
[status-im.ui.components.react :as react]
[status-im.ui.components.topbar :as topbar]
[status-im.utils.config :as config]
[status-im.ui.screens.wallet.account.styles :as styles]
[status-im.ui.screens.wallet.accounts.sheets :as sheets]
[status-im.ui.screens.wallet.accounts.views :as accounts]
[status-im.ui.screens.wallet.buy-crypto.views :as buy-crypto]
[status-im.ui.screens.wallet.transactions.views :as history]
[status-im.ui.components.tabs :as tabs]
[status-im.ui.screens.wallet.collectibles.views :as collectibles.views])
[status-im.ui.screens.wallet.collectibles.views :as collectibles.views]
[status-im.ui.screens.wallet.buy-crypto.views :as buy-crypto])
(:require-macros [status-im.utils.views :as views]))
(def state (reagent/atom {:tab :assets}))
@@ -102,7 +103,6 @@
currency [:wallet/currency]
opensea-enabled? [:opensea-enabled?]
collectible-collection [:wallet/collectible-collection address]
mainnet? [:mainnet?]
ethereum-network? [:ethereum-network?]]
(let [{:keys [tab]} @state]
[react/view {:flex 1}
@@ -111,11 +111,10 @@
(when ethereum-network?
[tabs/tab-title state :nft (i18n/label :t/wallet-collectibles) (= tab :nft)])
[tabs/tab-title state :history (i18n/label :t/history) (= tab :history)]]
[quo/separator {:style {:margin-top -8}}]
(cond
(= tab :assets)
[:<>
(when mainnet?
[buy-crypto/banner])
(for [item tokens]
^{:key (:name item)}
[accounts/render-asset item nil nil (:code currency)])]
@@ -181,10 +180,31 @@
(styles/bottom-send-recv-buttons-lower anim-y button-group-height)
#(reset! to-show false))))))))
(defn round-action-button [{:keys [icon title on-press]}]
[react/view {:style {:flex 1
:align-items :center
:margin-vertical (:large spacing/spacing)}}
[react/touchable-opacity {:style styles/round-action-button
:on-press on-press}
(icons/icon icon {:color colors/white})]
[quo/text {:color :secondary
:size :small
:style {:margin-top (:tiny spacing/spacing)}}
title]])
(defn top-actions []
[react/view {:style styles/top-actions}
[round-action-button {:icon :main-icons/add
:title (i18n/label :t/buy-crypto)
:on-press #(re-frame/dispatch [:buy-crypto.ui/open-screen])}]
[round-action-button {:icon :main-icons/change
:title (i18n/label :t/swap)
:on-press #(re-frame/dispatch [:open-modal :token-swap])}]])
(views/defview account []
(views/letsubs [{:keys [name address] :as account} [:multiaccount/current-account]
fetching-error [:wallet/fetching-error]]
(let [anim-y (animation/create-value button-group-height)
(let [anim-y (animation/create-value button-group-height)
scroll-y (animation/create-value 0)]
(anim-listener anim-y scroll-y)
[:<>
@@ -206,9 +226,9 @@
@accounts/updates-counter
@(re-frame/subscribe [:wallet/refreshing-history?])))}
(when fetching-error
[react/view {:style {:flex 1
[react/view {:style {:flex 1
:align-items :center
:margin 8}}
:margin 8}}
[icons/icon
:main-icons/warning
{:color :red
@@ -233,5 +253,8 @@
[react/scroll-view {:horizontal true}
[react/view {:flex-direction :row :padding-top 8 :padding-bottom 12}
[account-card account]]]]
(if config/swap-enabled?
[top-actions]
[buy-crypto/banner])
[assets-and-collections address]]
[bottom-send-recv-buttons account anim-y]])))
@@ -0,0 +1,433 @@
(ns status-im.ui.screens.wallet.swap.views
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
[re-frame.core :as re-frame]
[status-im.ethereum.tokens :as tokens]
[status-im.i18n.i18n :as i18n]
[status-im.ui.components.chat-icon.screen :as chat-icon]
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.components.keyboard-avoid-presentation
:as
kb-presentation]
[status-im.ui.components.react :as react]
[status-im.ui.components.search-input.view :as search-input]
[status-im.ui.components.slider :as slider]
[status-im.wallet.swap.core :as wallet.swap]
[status-im.ui.components.toolbar :as toolbar]
[status-im.ui.components.topbar :as topbar]
[status-im.ui.screens.wallet.components.views :as wallet.components]
[status-im.utils.handlers :refer [<sub]]
[status-im.wallet.utils :as wallet.utils]
[clojure.string :as str]))
(defn render-asset [{{:keys
[icon decimals amount color value]
:as token} :token
currency :currency
on-press :on-press}]
[quo/list-item
{:title [quo/text {:weight :medium}
[quo/text {:weight :inherit}
(str (if amount
(wallet.utils/format-amount amount decimals)
"...")
" ")]
[quo/text {:color :secondary
:weight :inherit}
(wallet.utils/display-symbol token)]]
:on-press on-press
:subtitle (str (if value value "...") " " currency)
:accessibility-label
(str (:symbol token) "-asset-value")
:icon (if icon
[wallet.components/token-icon icon]
[chat-icon/custom-icon-view-list (:name token) color])}])
(defn asset-selector []
(let [{:keys [address]} (<sub [:multiaccount/current-account])
{:keys [tokens]} (<sub [:wallet/visible-assets-with-values address])
source? (<sub [:wallet/modal-selecting-source-token?])
currency (<sub [:wallet/currency])]
[:<>
[topbar/topbar
{:title (if source?
(i18n/label :t/select-token-to-swap)
(i18n/label :t/select-token-to-receive))
:modal? true}]
[search-input/search-input
{:search-active? true}]
[react/scroll-view
(for [token tokens]
^{:key (:name token)}
[render-asset {:token token
:on-press #(re-frame/dispatch
[(if source?
::wallet.swap/set-from-token
::wallet.swap/set-to-token)
(:symbol token)])
:currency (:code currency)}])]]))
(defn pill-button [{:keys [on-press label margin-left]}]
[react/touchable-opacity {:on-press on-press
:style {:background-color colors/blue-light
:padding-horizontal 12
:padding-vertical 2
:border-radius 24
:margin-left (or margin-left 8)}}
[quo/text {:color :link
:weight :medium} label]])
(defn token-display
"Show token and act as an anchor to open selector."
[{:keys [token source?]}]
(let [token-icon-source (-> token :icon :source)]
[react/touchable-highlight
{:on-press #(re-frame/dispatch [::wallet.swap/open-asset-selector-modal source?])}
[react/view {:style {:flex-direction :row
:align-items :center
:border-width 1
:border-color colors/gray-lighter
:border-radius 8
:margin-left 16
:padding-horizontal 8
:padding-vertical 2}
:accessibility-label
:choose-asset-button}
[quo/text {:style {:margin-right 8}}
(-> token :symbol name)]
[react/image {:source (if (fn? token-icon-source)
(token-icon-source)
token-icon-source)}]]]))
(defn token-input
"Component to get the amount and type of tokens"
[{:keys [amount error label token max-from source?]}]
(let [window-width (<sub [:dimensions/window-width])]
[react/view {:style {:justify-content :space-between
:flex-direction :row
:align-items :center}}
[react/view {:flex 2}
[react/view {:flex-direction :row
:align-items :center}
[quo/text label]
(when max-from [pill-button {:on-press #()
:label "Max 0.043"}])]
[react/text-input
{:style {:font-size 38
:max-width (- (* (/ window-width 4) 3) 106)
:color (if error colors/red colors/black)}
:keyboard-type :decimal-pad
:auto-capitalize :words
:accessibility-label :amount-input
:default-value amount
:editable true
:auto-focus true
:on-change-text #(re-frame/dispatch [(when source?
::wallet.swap/set-from-token-amount)
%])
:placeholder "0.0"}]]
[token-display {:token token
:source? source?}]]))
(defn separator-with-icon []
[react/view
{:margin-vertical 8}
[quo/separator]
[react/touchable-opacity
{:on-press #(re-frame/dispatch [::wallet.swap/switch-from-token-with-to])}
[react/view {:style {:background-color colors/gray-lighter
:width 40
:height 40
:border-radius 40
:border-width 4
:border-color colors/white
:margin-top -20
:margin-bottom -20
:align-self :center
:align-items :center
:justify-content :center}}
[react/image {:source (icons/icon-source :main-icons/change)
:style {:tint-color colors/gray
:transform [{:rotate "90deg"}]}}]]]])
(defn floating-card [{:keys [icon title body on-press]}]
[react/view {:style {:border-width 1
:padding 2 ;; need a padding because border breaks otherwise
:border-radius 12
:margin-top 12
:border-color colors/gray-lighter}}
[quo/list-item {:title title
:subtitle body
:active-background-enabled
false
:on-press on-press
:theme :main
:chevron true
:icon [react/view {:style {:background-color colors/blue-light
:padding 8
:border-radius 4}}
(icons/icon icon {:color :dark})]}]])
(defn card-body-row [key value primary?]
[react/view {:flex-direction :row}
[quo/text {:color (when-not primary? :secondary)} key]
[quo/text {:style {:margin-right 4}
:color (when-not primary? :secondary)} ":"]
[quo/text {:ellipsize-mode :middle
:number-of-lines 1
:style {:width "50%"}
:color (when-not primary? :secondary)
:weight :semi-bold} value]])
(defn transaction-fee-card [{:keys [gas-amount price-limit tip-limit gas-in-eth gas-in-usd]}]
[floating-card {:title (i18n/label :t/transaction-fee)
:icon :main-icons/gas
:on-press #(re-frame/dispatch [:open-modal :token-swap-advanced-transaction-fee])
:body [react/view
[card-body-row (i18n/label :t/gas-amount-limit) gas-amount]
[card-body-row (i18n/label :t/per-gas-price-limit) price-limit]
[card-body-row (i18n/label :t/per-gas-tip-limit) tip-limit]
[card-body-row (i18n/label :t/total-gas)
(str gas-in-eth " • $" gas-in-usd)
true]]}])
(defn swap-details-card [{:keys [slippage price-impact]}]
[floating-card {:title (i18n/label :t/swap-details)
:icon :main-icons/change
:on-press #(re-frame/dispatch [:open-modal :token-swap-advanced-swap-details])
:body [react/view
[card-body-row (i18n/label :t/slippage) (str slippage " %")]
[card-body-row (i18n/label :t/price-impact) (str price-impact " %")]]}])
(defn nonce-card [{:keys [nonce]}]
[floating-card {:title (i18n/label :t/nonce)
:icon :main-icons/channel
:on-press #(re-frame/dispatch [:open-modal :token-swap-advanced-nonce])
:body [react/view
[card-body-row (i18n/label :t/nonce) nonce]]}])
(defn approve-token-card [{:keys [token contract-address approve-limit]}]
[floating-card {:title (i18n/label :t/approve)
:icon :main-icons/check
:on-press #(re-frame/dispatch [:open-modal :token-swap-advanced-approve-token])
:body [react/view
[card-body-row (i18n/label :t/token) token]
[card-body-row (i18n/label :t/contract-address) contract-address]
[card-body-row (i18n/label :t/approve-limit) approve-limit]]}])
(defn advanced-input [{:keys [label label-help value on-change after]}]
[react/view {:style {:padding-horizontal 16
:margin-bottom 16}}
[react/view {:style {:flex-direction :row
:justify-content :space-between}}
[quo/text {} label]
label-help]
[quo/text-input {:default-value (str value)
:show-cancel false
:style {:margin-top 12
:border-radius 8}
:after {:component after}
:on-change-text on-change}]])
(defn help-label-kv [{:keys [key value]}]
[react/view {:style {:flex-direction :row}}
[quo/text {:color :secondary} key]
[quo/text {:color :secondary
:style {:margin-right 4}} ":"]
[quo/text {:color :secondary} value]])
(defn nonce-modal []
(let [last-txn-nonce 22
nonce 23]
[kb-presentation/keyboard-avoiding-view {:style {:flex 1
:margin-top 16}
:ignore-offset true}
[advanced-input {:label (i18n/label :t/nonce)
:label-help [help-label-kv {:key (i18n/label :t/last-transaction)
:value last-txn-nonce}]
:value nonce
:on-change #()
:on-save #()}]
[toolbar/toolbar
{:show-border? true
:left [quo/button {:type :secondary}
(i18n/label :t/cancel)]
:right [quo/button {:theme :accent}
(i18n/label :t/save)]}]]))
(defn approve-token-modal []
[quo/text "modal"])
(defn transaction-fee-modal []
(let [gas-amount-limit 21000
gas-amount 21000
per-gas-price-limit 7.3
current-avg-per-gas-price-limit 7.3
per-gas-tip-limit 150
current-avg-per-gas-tip-limit 150]
[kb-presentation/keyboard-avoiding-view {:style {:flex 1
:margin-top 16}
:ignore-offset true}
[react/view {:flex 1}
[advanced-input {:label (i18n/label :t/gas-amount-limit)
:label-help [help-label-kv {:key (i18n/label :t/limit)
:value gas-amount-limit}]
:value gas-amount
:after [quo/text {:color :secondary} (i18n/label :t/gwei)]
:on-change #()
:on-save #()}]
[advanced-input {:label (i18n/label :t/per-gas-price-limit)
:label-help [help-label-kv {:key (i18n/label :t/current-average)
:value current-avg-per-gas-price-limit}]
:value per-gas-price-limit
:after [quo/text {:color :secondary} (i18n/label :t/gwei)]
:on-change #()
:on-save #()}]
[advanced-input {:label (i18n/label :t/per-gas-price-limit)
:label-help [help-label-kv {:key (i18n/label :t/current-average)
:value current-avg-per-gas-tip-limit}]
:value per-gas-tip-limit
:after [quo/text {:color :secondary} (i18n/label :t/gwei)]
:on-change #()
:on-save #()}]
[quo/list-item {:title (i18n/label :t/maximum-fee)
:text-size :base
:title-text-weight :regular
:accessory :text
:accessory-text [quo/text "0.3 ETH"]
:container-style {:margin-top 16}}]
[quo/text {:color :secondary
:style {:padding-horizontal 16}}
(i18n/label :t/maximum-fee-desc)]]
[toolbar/toolbar
{:show-border? true
:left [quo/button {:type :secondary}
(i18n/label :t/cancel)]
:right [quo/button {:theme :accent}
(i18n/label :t/save)]}]]))
(defn swap-details-modal []
(let [slippage-limit 20
slippage 0.5
price-impact 4]
[kb-presentation/keyboard-avoiding-view {:style {:flex 1
:margin-top 16}
:ignore-offset true}
[react/view {:flex 1}
[advanced-input {:label (str (i18n/label :t/slippage) " %")
:label-help [help-label-kv {:key (i18n/label :t/limit)
:value (str slippage-limit "%")}]
:value slippage
:on-change #()
:on-save #()}]
[quo/list-item {:title (i18n/label :t/price-impact)
:text-size :base
:title-text-weight :regular
:accessory :text
:accessory-text (str price-impact "%")
:container-style {:margin-top 16}}]
[quo/text {:color :secondary
:style {:padding-horizontal 16}}
(i18n/label :t/price-impact-desc)]]
[toolbar/toolbar
{:show-border? true
:left [quo/button {:type :secondary}
(i18n/label :t/cancel)]
:right [quo/button {:theme :accent}
(i18n/label :t/save)]}]]))
(defn advanced-settings []
[react/view {:style {:flex 1
:padding-horizontal 16}}
[react/view {:align-self :flex-start}
[pill-button {:label (i18n/label :t/switch-to-simple-interface)
:margin-left 0
:on-press #(re-frame/dispatch [::wallet.swap/set-advanced-mode false])}]]
[transaction-fee-card {:gas-amount 21000
:price-limit 74
:tip-limit 21
:gas-in-eth 0.0031
:gas-in-usd 34.28}]
[swap-details-card {:slippage 0.5
:price-impact 4}]
[approve-token-card {:token "USDC"
:contract-address "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2"
:approve-limit "unlimited"}]
[nonce-card {:nonce 22}]])
(defn swap []
(let [{:keys [name]}
(<sub [:multiaccount/current-account])
all-tokens (<sub [:wallet/all-tokens])
from-symbol (<sub [:wallet/swap-from-token])
to-symbol (<sub [:wallet/swap-to-token])
advanced-mode? (<sub [:wallet/swap-advanced-mode?])
amount "0.02"
from-token (tokens/symbol->token all-tokens (or from-symbol :DGX))
to-token (tokens/symbol->token all-tokens (or to-symbol :SNT))]
[kb-presentation/keyboard-avoiding-view {:style (merge
{:flex 1})
:ignore-offset true}
[topbar/topbar
{:title name
:subtitle (str/upper-case (i18n/label :t/powered-by-paraswap))
:modal? true}]
[react/view (merge {:padding-horizontal 16
:margin-vertical 32}
(when-not advanced-mode?
{:flex 1}))
[token-input {:amount amount
:error nil
:label (i18n/label :t/amount)
:token from-token
:source? true
:max-from 67.28}]
[separator-with-icon]
[token-input {:amount "0.01"
:error nil
:label (i18n/label :t/minimum-received)
:source? false
:token to-token}]]
(when-not advanced-mode?
[react/view {:style {:flex-direction :row
:justify-content :space-between
:padding-horizontal 16
:align-items :center}}
[react/view {:style {:flex-direction :row}}
[quo/text {} (i18n/label :t/priority)]
[pill-button {:label (i18n/label :t/advanced)
:on-press #(re-frame/dispatch [::wallet.swap/set-advanced-mode true])}]]
[quo/text {:color :secondary} "0.0034 ETH/ $ 8.09"]])
(comment
(re-frame/dispatch [::wallet.swap/set-advanced-mode false]))
(when-not advanced-mode?
[react/view {:style {:padding-horizontal 16}}
[slider/animated-slider
{:minimum-value 0
:maximum-value 100
:style {:margin-vertical 8}}]])
(when advanced-mode?
[quo/text "here"]
[advanced-settings])
[toolbar/toolbar
{:show-border? true
:right [quo/button {:theme :accent}
(i18n/label :t/swap)]}]]))
+2 -6
View File
@@ -52,6 +52,8 @@
(def delete-message-enabled? (enabled? (get-config :DELETE_MESSAGE_ENABLED "0")))
(def collectibles-enabled? (enabled? (get-config :COLLECTIBLES_ENABLED "1")))
(def test-stateofus? (enabled? (get-config :TEST_STATEOFUS "0")))
(def two-minutes-syncing? (enabled? (get-config :TWO_MINUTES_SYNCING "0")))
(def swap-enabled? (enabled? (get-config :SWAP_ENABLED "0")))
;; CONFIG VALUES
(def log-level
@@ -110,12 +112,6 @@
:DataDir "/ethereum/xdai_rpc"
:UpstreamConfig {:Enabled true
:URL "https://dai.poa.network"}}}
{:id "poa_rpc",
:name "POA Network",
:config {:NetworkId (ethereum/chain-keyword->chain-id :poa)
:DataDir "/ethereum/poa_rpc"
:UpstreamConfig {:Enabled true
:URL "https://core.poa.network"}}}
{:id "bsc_rpc",
:chain-explorer-link "https://bscscan.com/address/",
:name "BSC Network",
+11 -8
View File
@@ -51,11 +51,14 @@
(swap! handlers/handler-nesting-level inc))
(let [[first-arg & rest-args] args
initial-fxs? (map? first-arg)
fx-fns (if initial-fxs? rest-args args)]
(clojure.core/reduce (fn [fxs fx-fn]
(let [updated-cofx (update-db cofx fxs)]
(if fx-fn
(safe-merge fxs (fx-fn updated-cofx))
fxs)))
(if initial-fxs? first-arg {:db db})
fx-fns)))
fx-fns (if initial-fxs? rest-args args)
res
(clojure.core/reduce (fn [fxs fx-fn]
(let [updated-cofx (update-db cofx fxs)]
(if fx-fn
(safe-merge fxs (fx-fn updated-cofx))
fxs)))
(if initial-fxs? first-arg {:db db})
fx-fns)]
(swap! handlers/handler-nesting-level dec)
res))
+1 -1
View File
@@ -22,7 +22,7 @@
(defn- format-price-resp [resp mainnet?]
;;NOTE(this check is to allow value conversion for sidechains with native currencies listed on cryptocompare
;; under a symbol different than display symbol. Specific use case xDAI and POA.
;; under a symbol different than display symbol. Specific use case xDAI.
(if mainnet?
(when-let [RAW (:RAW (types/json->clj resp))]
(into {} (for [[from entries] RAW]
@@ -72,7 +72,7 @@
(fx/defn handle-community-chat [cofx {:keys [chat-id]}]
(log/info "universal-links: handling community chat" chat-id)
{:dispatch-n [[:accept-all-activity-center-notifications-from-chat chat-id]
[:chat.ui/navigate-to-chat chat-id true]]})
[:chat.ui/navigate-to-chat chat-id]]})
(fx/defn handle-public-chat [cofx {:keys [topic]}]
(log/info "universal-links: handling public chat" topic)
+51
View File
@@ -0,0 +1,51 @@
(ns status-im.wallet.swap.core
(:require [status-im.utils.fx :as fx]
[re-frame.db :as re-frame.db]
[status-im.navigation :as navigation]))
(fx/defn open-asset-selector-modal
"source? true signinfies we are selecting the source asset. false implies selection of sink asset"
{:events [::open-asset-selector-modal]}
[{:keys [db]} source?]
(fx/merge {:db (assoc db :wallet/modal-selecting-source-token? source?)}
(navigation/open-modal :swap-asset-selector {})))
(fx/defn set-from-token
{:events [::set-from-token]}
[{:keys [db]} from-symbol]
(fx/merge {:db (assoc db :wallet/swap-from-token from-symbol)}
(navigation/navigate-back)))
(fx/defn set-to-token
{:events [::set-to-token]}
[{:keys [db]} to-symbol]
(fx/merge {:db (assoc db :wallet/swap-to-token to-symbol)}
(navigation/navigate-back)))
(fx/defn set-from-token-amount
[{:keys [db]} from-amount]
{:db (assoc db :wallet/swap-from-token-amount from-amount)})
(fx/defn set-max-from-token-amount
[{:keys [db]} _]
{:db (assoc db :wallet/swap-from-token-amount 0)})
(fx/defn switch-from-token-with-to
{:events [::switch-from-token-with-to]}
[{:keys [db]}]
{:db (assoc db
:wallet/swap-from-token (:wallet/swap-to-token db)
:wallet/swap-to-token (:wallet/swap-from-token db))})
(fx/defn set-advanced-mode
{:events [::set-advanced-mode]}
[{:keys [db]} mode]
{:db (assoc db :wallet/swap-advanced-mode? mode)})
(comment
(->> re-frame.db/app-db
deref
:wallet/all-tokens
vals
(map #(str (:name %) "-" (:symbol %)))))
+3 -3
View File
@@ -3,7 +3,7 @@
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
"owner": "status-im",
"repo": "status-go",
"version": "v0.91.12",
"commit-sha1": "f60cf5cd47c9f50f1bfb8e01aa5f49802d5220c5",
"src-sha256": "10dwxrav77v7xvbzy9camacdflsvpyjnwry638jhgrd4i32x745f"
"version": "feat/node-config",
"commit-sha1": "3daf1ea073fe1aa636a9d301a8e2a04acd7adbca",
"src-sha256": "1vc70av6jvsch12hyr32hsnh5yqsaavmhy32d20zx3nfb3kxgv12"
}
+1 -1
View File
@@ -1,6 +1,6 @@
[pytest]
norecursedirs = .git views
addopts = -s -v --tb=line --junitxml=result.xml
addopts = -s -v --tb=line --junitxml=result.xml --dist loadgroup -n 5
junit_family=legacy
markers =
critical: TCs by priority
+6 -6
View File
@@ -17,7 +17,7 @@ future==0.18.2
flaky==3.7.0
idna==2.10
kiwisolver==1.3.1
lxml==4.6.2
lxml==4.6.5
matplotlib==3.3.3
multidict==5.0.2
namedlist==1.8
@@ -30,9 +30,9 @@ pycryptodome==3.9.9
pyethash==0.1.27
pyparsing==2.4.7
pysha3==1.0.2
pytest==6.1.2
pytest==6.2.0
pytest-forked==1.3.0
pytest-xdist==2.2.0
pytest-xdist==2.5.0
python-dateutil==2.8.1
pytz==2020.4
PyYAML==5.4
@@ -42,11 +42,11 @@ rlp==1.2.0
sauceclient==1.0.0
scrypt==0.8.17
selenium==3.14.1
six==1.10.0
six==1.11.0
urllib3==1.26.3
yarl==1.6.3
zbarlight==3.0
docker==4.4.0
influxdb==5.3.1
web3
imagehash
web3==5.25.0
imagehash
+4 -3
View File
@@ -97,10 +97,11 @@ class TestrailReport(BaseTestReport):
def get_regression_cases(self):
test_cases = dict()
test_cases['critical'] = 734
test_cases['high'] = 735
test_cases['critical'] = 730
test_cases['medium'] = 736
test_cases['upgrade'] = 881
test_cases['public_chat'] = 50654
test_cases['one_to_one_chat'] = 50655
case_ids = list()
for arg in argv:
if "run_testrail_ids" in arg:
@@ -108,7 +109,7 @@ class TestrailReport(BaseTestReport):
case_ids = value.split(',')
if len(case_ids) == 0:
if 'critical or high' in argv:
for case in self.get_cases([test_cases['critical'], test_cases['high']]):
for case in self.get_cases([test_cases['critical'], test_cases['public_chat'], test_cases['one_to_one_chat']]):
case_ids.append(case['id'])
elif 'upgrade' in argv and 'not upgrade' not in argv:
for case in self.get_cases([test_cases['upgrade']]):
@@ -96,7 +96,7 @@ class TestCreateAccount(SingleDeviceTestCase):
sign_in.get_started_button.click()
if sign_in.generate_key_button.is_element_displayed():
self.errors.append("Agree with ToS is not mandatory to proceed onboarding!")
sign_in.accept_tos_checkbox.click()
sign_in.accept_tos_checkbox.enable()
sign_in.get_started_button.click_until_presence_of_element(sign_in.generate_key_button)
sign_in.generate_key_button.click()
from views.sign_in_view import MultiAccountButton
@@ -141,7 +141,7 @@ class TestCreateAccount(SingleDeviceTestCase):
@marks.high
def test_pass_phrase_validation(self):
sign_in = SignInView(self.driver)
sign_in.accept_tos_checkbox.click()
sign_in.accept_tos_checkbox.enable()
sign_in.get_started_button.click_until_presence_of_element(sign_in.access_key_button)
sign_in.access_key_button.click()
validations = [
@@ -219,7 +219,7 @@ class TestCreateAccount(SingleDeviceTestCase):
@marks.medium
def test_create_account_short_and_mismatch_password(self):
sign_in = SignInView(self.driver)
sign_in.accept_tos_checkbox.click()
sign_in.accept_tos_checkbox.enable()
sign_in.get_started_button.click()
sign_in.generate_key_button.click()
sign_in.next_button.click()
@@ -61,7 +61,7 @@ class TestCreateAccount(SingleDeviceTestCase):
sign_in.confirm_button.click()
keycard = sign_in.move_and_reset_button.click()
keycard.begin_setup_button.click()
keycard.connect_card_button.click()
keycard.connect_card_button.wait_and_click()
keycard.enter_default_pin()
keycard.enter_default_pin()
if not sign_in.element_by_translation_id("migration-successful").is_element_displayed(30):
@@ -126,7 +126,7 @@ class TestCreateAccount(SingleDeviceTestCase):
sign_in.migration_password_input.set_value(common_password)
sign_in.confirm_button.click()
keycard.begin_setup_button.click()
keycard.connect_card_button.click()
keycard.connect_card_button.wait_and_click()
keycard.enter_default_pin()
keycard.enter_default_pin()
if not sign_in.element_by_translation_id("migration-successful").is_element_displayed(30):
@@ -179,7 +179,7 @@ class TestCreateAccount(SingleDeviceTestCase):
sign_in = SignInView(self.driver)
sign_in.just_fyi('Cancel on PIN code setup stage')
sign_in.accept_tos_checkbox.click()
sign_in.accept_tos_checkbox.enable()
sign_in.get_started_button.click()
sign_in.generate_key_button.click()
username = sign_in.first_username_on_choose_chat_name.text
@@ -187,7 +187,7 @@ class TestCreateAccount(SingleDeviceTestCase):
keycard_flow = sign_in.keycard_storage_button.click()
keycard_flow.next_button.click()
keycard_flow.begin_setup_button.click()
keycard_flow.connect_card_button.click()
keycard_flow.connect_card_button.wait_and_click()
keycard_flow.enter_another_pin()
keycard_flow.cancel_button.click()
@@ -232,7 +232,7 @@ class TestCreateAccount(SingleDeviceTestCase):
@marks.flaky
def test_keycard_interruption_access_key_onboarding_flow(self):
sign_in = SignInView(self.driver)
sign_in.accept_tos_checkbox.click()
sign_in.accept_tos_checkbox.enable()
sign_in.get_started_button.click()
sign_in.access_key_button.click()
@@ -246,7 +246,7 @@ class TestCreateAccount(SingleDeviceTestCase):
sign_in.just_fyi('Cancel on PIN code setup stage')
keycard_flow.next_button.click()
keycard_flow.begin_setup_button.click()
keycard_flow.connect_card_button.click()
keycard_flow.connect_card_button.wait_and_click()
keycard_flow.enter_another_pin()
keycard_flow.cancel_button.click()
@@ -277,7 +277,7 @@ class TestCreateAccount(SingleDeviceTestCase):
recovered_user = transaction_senders['A']
sign_in.just_fyi('Recover multiaccount')
sign_in.accept_tos_checkbox.click()
sign_in.accept_tos_checkbox.enable()
sign_in.get_started_button.click_until_presence_of_element(sign_in.access_key_button)
sign_in.access_key_button.click()
sign_in.recover_with_keycard_button.click()
@@ -324,7 +324,7 @@ class TestCreateAccount(SingleDeviceTestCase):
sign_in.toggle_airplane_mode()
sign_in.just_fyi('Recover multiaccount offline')
sign_in.accept_tos_checkbox.click()
sign_in.accept_tos_checkbox.enable()
sign_in.get_started_button.click_until_presence_of_element(sign_in.access_key_button)
sign_in.access_key_button.click()
sign_in.recover_with_keycard_button.click()
@@ -673,7 +673,7 @@ class TestKeycardCreateMultiaccountMultipleDevice(MultipleDeviceTestCase):
device_1, device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
device_1.just_fyi("Create keycard account and save seed phrase")
device_1.accept_tos_checkbox.click()
device_1.accept_tos_checkbox.enable()
device_1.get_started_button.click()
device_1.generate_key_button.click_until_presence_of_element(device_1.next_button)
device_1.next_button.click_until_absense_of_element(device_1.element_by_translation_id("intro-wizard-title2"))
@@ -754,9 +754,9 @@ class TestProfileMultipleDevice(MultipleDeviceTestCase):
profile_1.just_fyi('Check profile image updated in group chat invite')
home_1.get_back_to_home_view()
new_group_chat = 'new_gr'
home_1.create_group_chat(user_names_to_add=[default_username_2], group_chat_name=new_group_chat)
home_2.click_system_back_button()
home_2.open_notification_bar()
home_1.create_group_chat(user_names_to_add=[default_username_2], group_chat_name=new_group_chat)
invite = group_chat_2.pn_invited_to_group_chat(default_username_1, new_group_chat)
home_2.get_pn(invite).wait_for_visibility_of_element(30)
@@ -1036,7 +1036,7 @@ class TestProfileMultipleDevice(MultipleDeviceTestCase):
profile_1.mail_server_button.click()
profile_1.use_history_node_button.click()
profile_1.home_button.click(desired_view='chat')
if not public_chat_1.chat_element_by_text(message).is_element_displayed(30):
if not public_chat_1.chat_element_by_text(message).is_element_displayed(60):
self.errors.append('History was not fetched after enabling use_history_node')
self.errors.verify_no_errors()
@@ -1219,7 +1219,7 @@ class TestProfileMultipleDevice(MultipleDeviceTestCase):
@marks.testrail_id(695856)
@marks.medium
@marks.flaky
#@marks.flaky
def test_pair_devices_sync_photo_community_group_chats(self):
self.create_drivers(2)
device_1, device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
@@ -1287,6 +1287,7 @@ class TestProfileMultipleDevice(MultipleDeviceTestCase):
if not channel_2.chat_element_by_text(message_after_sync).is_element_displayed(30):
self.errors.append("Message sent to community channel after sync is not shown!")
[channel.back_button.click() for channel in (channel_1, channel_2)]
[home.get_chat(comm_before_sync_name, community=True).click() for home in (home_1, home_2)]
comm_before_1.add_channel(channel_after_sync)
if not comm_before_2.get_chat(channel_after_sync).is_element_displayed(30):
self.errors.append("New added channel after sync is not shown!")
@@ -1295,12 +1296,12 @@ class TestProfileMultipleDevice(MultipleDeviceTestCase):
[home.home_button.double_click() for home in (home_1, home_2)]
home_1.get_chat(comm_before_sync_name, community=True).click()
comm_before_1.leave_community()
if not home_2.get_chat(comm_before_sync_name).is_element_disappeared(30):
if not home_2.element_by_text_part(comm_before_sync_name).is_element_disappeared(30):
self.errors.append("Leaving community was not synced!")
device_1.just_fyi("Adding new community and check it will be synced")
home_1.create_community(comm_after_sync_name)
if not home_2.get_chat(comm_after_sync_name, community=True).is_element_displayed(30):
if not home_2.element_by_text(comm_after_sync_name).is_element_displayed(30):
self.errors.append('Added community was not appeared after initial sync')
# TODO: skip until #11558 (rechecked 23.11.21, valid)
@@ -168,7 +168,10 @@ class TestWalletManagement(SingleDeviceTestCase):
self.errors.append('%s asset is not shown in wallet after relogin' % asset)
sign_in.just_fyi("Deselecting asset")
wallet.select_asset(asset)
wallet.multiaccount_more_options.click()
wallet.manage_assets_button.click()
wallet.asset_checkbox_by_name(asset).click()
wallet.cross_icon.click()
if wallet.asset_by_name(asset).is_element_displayed():
self.errors.append('%s asset is shown in wallet but was deselected' % asset)
self.errors.verify_no_errors()
@@ -616,7 +616,6 @@ class TestChatManagementMultipleDevice(MultipleDeviceTestCase):
device_1.just_fyi('check that new messages from blocked user are not delivered')
self.drivers[0].launch_app()
device_1.sign_in()
home_1.join_public_chat(chat_name)
for message in message_before_block_2, message_after_block_2:
if chat_public_1.chat_element_by_text(message).is_element_displayed():
self.errors.append(
@@ -697,7 +696,6 @@ class TestChatManagementMultipleDevice(MultipleDeviceTestCase):
if public_chat_after_block_1.chat_element_by_text(message_after_block_2).is_element_displayed():
self.errors.append("Message from blocked user '%s' is received" % device_2.driver.number)
public_chat_after_block_1.get_back_to_home_view()
if home_1.notifications_unread_badge.is_element_displayed():
device_1.driver.fail("Unread badge is shown after receiving new message from blocked user")
if blocked_chat_user.is_element_displayed():
@@ -719,6 +717,7 @@ class TestChatManagementMultipleDevice(MultipleDeviceTestCase):
device_1.click_system_home_button()
self.drivers[0].launch_app()
device_1.sign_in()
public_chat_after_block_1.home_button.double_click()
if home_1.notifications_unread_badge.is_element_displayed():
device_1.driver.fail("Unread badge is shown after after fetching new messages from offline")
if blocked_chat_user.is_element_displayed():
@@ -1019,7 +1018,7 @@ class TestChatManagementMultipleDevice(MultipleDeviceTestCase):
public_1.chat_message_input.send_keys('@')
if (chat_1.search_user_in_mention_suggestion_list(ens_nickname_value).is_element_displayed() or
chat_1.search_user_in_mention_suggestion_list(sender['username']).is_element_displayed()):
self.errors.append('Blcoked user is available in mention suggestion list')
self.errors.append('Blocked user is available in mention suggestion list')
self.errors.verify_no_errors()
@@ -11,7 +11,7 @@ class TestCommandsMultipleDevices(MultipleDeviceTestCase):
@marks.critical
@marks.transaction
def test_send_eth_in_1_1_chat_transaction_push(self):
sender = transaction_senders['A']
sender = transaction_senders['B']
self.create_drivers(2)
device_1, device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
home_1 = device_1.recover_access(passphrase=sender['passphrase'], enable_notifications=True)
@@ -45,7 +45,7 @@ class TestCommandsMultipleDevices(MultipleDeviceTestCase):
chat_2 = home_2.get_chat(sender['username']).click()
receiver_message = chat_2.get_incoming_transaction(account_name)
timestamp_sender = sender_message.timestamp_message.text
timestamp_sender = sender_message.timestamp_command_message.text
if not receiver_message.is_element_displayed():
self.drivers[0].fail('No message about incoming transaction in 1-1 chat is shown for receiver')
receiver_message.transaction_status.wait_for_element_text(receiver_message.address_requested)
@@ -68,7 +68,7 @@ class TestCommandsMultipleDevices(MultipleDeviceTestCase):
send_bottom_sheet = sender_message.sign_and_send.click()
send_bottom_sheet.next_button.click()
send_bottom_sheet.sign_transaction()
updated_timestamp_sender = sender_message.timestamp_message.text
updated_timestamp_sender = sender_message.timestamp_command_message.text
if updated_timestamp_sender == timestamp_sender:
self.errors.append("Timestamp of message is not updated after signing transaction")
chat_1.wallet_button.click()
@@ -35,17 +35,16 @@ class TestGroupChatMultipleDevice(MultipleDeviceTestCase):
home_1.add_contact(key_2)
home_1.get_back_to_home_view()
home_2.put_app_to_background()
home_2.open_notification_bar()
chat_1 = home_1.create_group_chat([username_2], chat_name)
home_2.just_fyi('check that PN invite to group chat is received and after tap you are redirected to group chat')
home_2.open_notification_bar()
pns = [chat_1.pn_invited_to_group_chat(username_1, chat_name),
chat_1.pn_wants_you_to_join_to_group_chat(username_1, chat_name)]
for pn in pns:
if not home_2.get_pn(pn).is_element_displayed(30):
self.errors.append('%s is not shown after invite to group chat' % pn)
group_invite_pn = home_2.get_pn(pns[1])
group_invite_pn = home_2.get_pn(pns[0])
if not group_invite_pn.group_chat_icon.is_element_displayed(30):
self.errors.append('No icon is shown for PN for group invite')
@@ -255,9 +254,9 @@ class TestGroupChatMultipleDevice(MultipleDeviceTestCase):
chat.changed_group_name_system_message(device_1_username, new_chat_name)).is_element_displayed():
self.errors.append('Message about changing chat name is not shown')
device_2.just_fyi('Check that you can navigate to renamed chat')
device_2.just_fyi('Check that you can see renamed chat')
device_2_chat.back_button.click()
home_2.get_chat(new_chat_name).click()
home_2.get_chat(new_chat_name).wait_for_visibility_of_element(60)
self.errors.verify_no_errors()
@@ -40,7 +40,7 @@ class TestCommandsMultipleDevices(MultipleDeviceTestCase):
chat_2 = home_2.get_chat(sender['username']).click()
receiver_message = chat_2.get_incoming_transaction()
timestamp_sender = sender_message.timestamp_message.text
timestamp_sender = sender_message.timestamp_command_message.text
if not receiver_message.is_element_displayed():
self.drivers[0].fail('No message about incoming transaction in 1-1 chat is shown for receiver')
receiver_message.transaction_status.wait_for_element_text(receiver_message.address_requested)
@@ -63,7 +63,7 @@ class TestCommandsMultipleDevices(MultipleDeviceTestCase):
send_message = sender_message.sign_and_send.click()
send_message.next_button.click()
send_message.sign_transaction(keycard=True)
updated_timestamp_sender = sender_message.timestamp_message.text
updated_timestamp_sender = sender_message.timestamp_command_message.text
if updated_timestamp_sender == timestamp_sender:
self.errors.append("Timestamp of message is not updated after signing transaction")
@@ -109,8 +109,7 @@ class TestMessagesOneToOneChatMultiple(MultipleDeviceTestCase):
home_1.just_fyi('go back online and check that 1-1 chat will be fetched')
home_1.toggle_airplane_mode()
chat_element = home_1.get_chat(username_2)
chat_element.wait_for_visibility_of_element(30)
chat_element = home_1.get_chat(username_2, wait_time=60)
chat_1 = chat_element.click()
chat_1.chat_element_by_text(message_1).wait_for_visibility_of_element(2)
@@ -162,13 +161,14 @@ class TestMessagesOneToOneChatMultiple(MultipleDeviceTestCase):
if device_2_chat.user_name_text.text != default_username_1:
self.errors.append("Default username '%s' is not shown in one-to-one chat" % default_username_1)
profile_1.just_fyi("Check timestamps for sender and receiver")
for chat in device_1_chat, device_2_chat:
chat.verify_message_is_under_today_text(timestamp_message, self.errors)
timestamp = chat.chat_element_by_text(timestamp_message).timestamp_message.text
if timestamp not in sent_time_variants:
self.errors.append(
"Timestamp is not shown, expected '%s', in fact '%s'" % (sent_time_variants.join(","), timestamp))
# TODO: disabled until https://github.com/status-im/status-react/issues/12936 fix
# profile_1.just_fyi("Check timestamps for sender and receiver")
# for chat in device_1_chat, device_2_chat:
# chat.verify_message_is_under_today_text(timestamp_message, self.errors)
# timestamp = chat.chat_element_by_text(timestamp_message).timestamp_message.text
# if timestamp not in sent_time_variants:
# self.errors.append(
# "Timestamp is not shown, expected '%s', in fact '%s'" % (sent_time_variants.join(","), timestamp))
device_2_home.just_fyi("Add user to contact and verify his default username")
device_2_chat.add_to_contacts.click()
@@ -208,7 +208,7 @@ class TestMessagesOneToOneChatMultiple(MultipleDeviceTestCase):
message_before_edit, message_after_edit = "Message BEFORE edit 1", "Message AFTER edit 2"
public_chat_1.send_message(message_before_edit)
public_chat_1.edit_message_in_chat(message_before_edit, message_after_edit)
if not public_chat_1.element_by_text_part("⌫ Edited").is_element_displayed():
if not public_chat_1.element_by_text_part("⌫ Edited").is_element_displayed(60):
self.errors.append('No mark in message bubble about this message was edited')
device_2.just_fyi(
@@ -223,9 +223,9 @@ class TestMessagesOneToOneChatMultiple(MultipleDeviceTestCase):
if not home_1.element_by_text_part(message_after_edit_1_1).is_element_present():
self.errors.append('UNedited message version displayed on preview')
chat_private_1 = device_1_one_to_one_chat_element.click()
if not home_1.element_by_text_part(message_after_edit_1_1).is_element_present():
if not home_1.element_by_text_part(message_after_edit_1_1).is_element_present(30):
self.errors.append('No edited message in 1-1 chat displayed')
if not home_1.element_by_text_part("⌫ Edited").is_element_present():
if not home_1.element_by_text_part("⌫ Edited").is_element_present(30):
self.errors.append('No mark in message bubble about this message was edited on receiver side')
device_2.just_fyi("Verify Device1 can not edit and delete received message from Device2")
@@ -238,10 +238,10 @@ class TestMessagesOneToOneChatMultiple(MultipleDeviceTestCase):
device_2.just_fyi("Delete message and check it is not shown in chat preview on home")
chat_private_2.delete_message_in_chat(message_after_edit_1_1)
for chat in (chat_private_2, chat_private_1):
if chat.chat_element_by_text(message_after_edit_1_1).is_element_displayed():
if chat.chat_element_by_text(message_after_edit_1_1).is_element_displayed(30):
self.errors.append("Deleted message is shown in chat view for 1-1 chat")
chat_private_1.home_button.double_click()
if home_1.element_by_text(message_after_edit_1_1).is_element_displayed():
if home_1.element_by_text(message_after_edit_1_1).is_element_displayed(30):
self.errors.append("Deleted message is shown on chat element on home screen")
device_2.just_fyi("Send one more message and check that PN will be deleted with message deletion")
@@ -258,13 +258,13 @@ class TestMessagesOneToOneChatMultiple(MultipleDeviceTestCase):
chat_private_2.just_fyi("Check for that edited message is shown for Device 2 and delete message in public chat")
[home.home_button.double_click() for home in (home_1, home_2)]
public_chat_1, public_chat_2 = home_1.get_chat('#%s' % chat_name).click(), home_2.join_public_chat(chat_name)
if not public_chat_2.element_by_text_part("⌫ Edited").is_element_displayed():
if not public_chat_2.element_by_text_part("⌫ Edited").is_element_displayed(60):
self.errors.append('No mark in message bubble about this message was edited')
if not public_chat_2.element_by_text_part(message_after_edit).is_element_displayed():
if not public_chat_2.element_by_text_part(message_after_edit).is_element_displayed(60):
self.errors.append('Message is not edited.')
public_chat_1.delete_message_in_chat(message_after_edit)
for chat in (public_chat_1, public_chat_2):
if chat.chat_element_by_text(message_after_edit).is_element_displayed():
if chat.chat_element_by_text(message_after_edit).is_element_displayed(30):
self.errors.append("Deleted message is shown in chat view for public chat")
self.errors.verify_no_errors()
@@ -883,7 +883,7 @@ class TestMessagesOneToOneChatSingle(SingleDeviceTestCase):
chat.send_message_button.click()
message_bubble = chat.chat_element_by_text(formatted_message[message])
message_bubble.timestamp_message.long_press_element()
message_bubble.sent_status_checkmark.long_press_element()
chat.element_by_text('Copy').click()
message_input.paste_text_from_clipboard()
@@ -964,13 +964,10 @@ class TestMessagesOneToOneChatSingle(SingleDeviceTestCase):
self.errors.append('"%s" is not show in chat preview on home screen!' % message)
home.get_chat('@%s' % ens).click()
home.just_fyi('Check redirect to user profile on mention by ENS tap')
chat.chat_element_by_text(message).click()
if not chat.profile_block_contact.is_element_displayed():
self.errors.append('No redirect to user profile after tapping on message with mention (ENS) in 1-1 chat')
home.just_fyi('Set nickname and mention user by nickname in 1-1 chat')
russian_nickname = 'МОЙ дорогой ДРУх'
chat.chat_options.click()
chat.view_profile_button.click()
chat.set_nickname(russian_nickname)
chat.select_mention_from_suggestion_list(russian_nickname + ' @' + ens)
+88 -119
View File
@@ -1,117 +1,125 @@
import emoji
import random
from dateutil import parser
from tests import marks
from tests.base_test_case import MultipleDeviceTestCase, SingleDeviceTestCase
from views.sign_in_view import SignInView
from datetime import timedelta
from time import sleep
import emoji
import pytest
from dateutil import parser
class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
from tests import marks
from tests.base_test_case import MultipleDeviceTestCase, SingleDeviceTestCase, create_shared_drivers, \
MultipleSharedDeviceTestCase
from views.sign_in_view import SignInView
@pytest.mark.xdist_group(name="public_chat")
class TestPublicChatMultipleDeviceMerged(MultipleSharedDeviceTestCase):
@classmethod
def setup_class(cls):
cls.drivers, cls.loop = create_shared_drivers(2)
device_1, device_2 = SignInView(cls.drivers[0]), SignInView(cls.drivers[1])
cls.home_1, cls.home_2 = device_1.create_user(), device_2.create_user()
profile_1 = cls.home_1.profile_button.click()
cls.username_1 = profile_1.default_username_text.text
profile_1.home_button.click()
cls.home_2.home_button.click()
cls.public_chat_name = cls.home_1.get_random_chat_name()
cls.chat_1, cls.chat_2 = cls.home_1.join_public_chat(cls.public_chat_name), cls.home_2.join_public_chat(cls.public_chat_name)
@marks.testrail_id(5313)
@marks.critical
def test_public_chat_messaging_emojis_timestamps(self):
self.create_drivers(2)
device_1, device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
home_1, home_2 = device_1.create_user(), device_2.create_user()
profile_1 = home_1.profile_button.click()
default_username_1 = profile_1.default_username_text.text
profile_1.home_button.click()
home_2.home_button.click()
def test_public_chat_text_timestamps_while_on_different_tab(self):
message = 'hello'
self.chat_2.dapp_tab_button.click()
self.chat_1.send_message(message)
sent_time_variants = self.chat_1.convert_device_time_to_chat_timestamp()
timestamp = self.chat_1.chat_element_by_text(message).timestamp_on_tap
if timestamp not in sent_time_variants:
self.errors.append("Timestamp is not shown, expected: '%s', in fact: '%s'" % (sent_time_variants.join(','), timestamp))
self.chat_2.home_button.click(desired_view='chat')
for chat in self.chat_1, self.chat_2:
chat.verify_message_is_under_today_text(message, self.errors)
if self.chat_2.chat_element_by_text(message).username.text != self.username_1:
self.errors.append("Default username '%s' is not shown next to the received message" % self.username_1)
self.errors.verify_no_errors()
home_1.just_fyi("Check preselected chats, redirect to status chat")
home_1.plus_button.click_until_presence_of_element(home_1.join_public_chat_button)
home_1.join_public_chat_button.click()
preselected_chats = ['#status', '#chitchat', '#defi', '#crypto', '#markets', '#dap-ps']
for chat in preselected_chats:
if not home_1.element_by_text(chat).is_element_displayed():
self.errors.append("'%s' text is not in the list of preselected chats" % chat)
home_1.element_by_text(preselected_chats[0]).click()
status_chat = home_1.get_chat_view()
if not status_chat.user_name_text != preselected_chats[0]:
self.errors.append('No redirect to chat if tap on #status chat')
status_chat.home_button.click()
public_chat_name = home_1.get_random_chat_name()
chat_1, chat_2 = home_1.join_public_chat(public_chat_name), home_2.join_public_chat(public_chat_name)
home_1.just_fyi("Check sending text messages, emojis, usernames and timestamps on messages")
@marks.testrail_id(700719)
@marks.critical
def test_public_emoji(self):
emoji_name = random.choice(list(emoji.EMOJI_UNICODE))
emoji_unicode = emoji.EMOJI_UNICODE[emoji_name]
message, emoji_message = 'hello', emoji.emojize(emoji_name)
chat_1.send_message(message)
sent_time_variants = chat_1.convert_device_time_to_chat_timestamp()
for chat in chat_1, chat_2:
chat.verify_message_is_under_today_text(message, self.errors)
timestamp = chat.chat_element_by_text(message).timestamp_message.text
if timestamp not in sent_time_variants:
self.errors.append(
"Timestamp is not shown, expected '%s', in fact '%s'" % (sent_time_variants.join(','), timestamp))
if chat_2.chat_element_by_text(message).username.text != default_username_1:
self.errors.append("Default username '%s' is not shown next to the received message" % default_username_1)
chat_1.send_message(emoji_message)
for chat in chat_1, chat_2:
if not chat.chat_element_by_text(emoji_unicode).is_element_displayed():
emoji_message = emoji.emojize(emoji_name)
self.chat_1.send_message(emoji_message)
for chat in self.chat_1, self.chat_2:
if not chat.chat_element_by_text(emoji_unicode).is_element_displayed(30):
self.errors.append('Message with emoji was not sent or received in public chat')
self.chat_1.just_fyi("Can copy and paste emojis")
self.chat_1.element_by_text_part(emoji_unicode).long_press_element()
self.chat_1.element_by_text('Copy').click()
self.chat_1.chat_message_input.paste_text_from_clipboard()
if self.chat_1.chat_message_input.text != emoji_unicode:
self.errors.append('Emoji message was not copied')
self.chat_1.just_fyi("Can reply to emojis")
self.chat_2.quote_message(emoji_unicode)
message_text = 'test message'
self.chat_2.chat_message_input.send_keys(message_text)
self.chat_2.send_message_button.click()
chat_element_1 = self.chat_1.chat_element_by_text(message_text)
if not chat_element_1.is_element_displayed(sec=10) or chat_element_1.replied_message_text != emoji_unicode:
self.errors.append('Reply message was not received by the sender')
self.errors.verify_no_errors()
@marks.testrail_id(5360)
@marks.critical
def test_unread_messages_counter_public_chat(self):
self.create_drivers(2)
driver_2 = self.drivers[1]
home_1, home_2 = SignInView(self.drivers[0]).create_user(), SignInView(self.drivers[1]).create_user()
profile_1 = home_1.profile_button.click()
username_1 = profile_1.default_username_text.text
profile_1.home_button.click()
chat_name = home_1.get_random_chat_name()
chat_1, chat_2 = home_1.join_public_chat(chat_name), home_2.join_public_chat(chat_name)
chat_1.send_message('пиу')
chat_1.home_button.click()
message, message_2 = 'test message', 'test message2'
chat_2.send_message(message)
home_1.just_fyi(
"Check unread message indicator on home, on chat element and that it is not shown after reading messages")
if not home_1.home_button.public_unread_messages.is_element_displayed():
def test_public_unread_messages_counter(self):
self.chat_1.send_message('пиу')
home_1 = self.chat_1.home_button.click()
message = 'test message'
self.chat_2.send_message(message)
if not self.chat_1.home_button.public_unread_messages.is_element_displayed():
self.errors.append('New messages public chat badge is not shown on Home button')
chat_element = home_1.get_chat('#' + chat_name)
chat_element = home_1.get_chat('#' + self.public_chat_name)
if not chat_element.new_messages_public_chat.is_element_displayed():
self.errors.append('New messages counter is not shown in public chat')
self.errors.verify_no_errors()
home_1.just_fyi("Check unread message counter when mentioned in public chat")
chat_2 = home_2.get_chat_view()
chat_2.select_mention_from_suggestion_list(username_1, username_1[:2])
chat_2.send_message_button.click()
@marks.testrail_id(700718)
@marks.critical
def test_public_unread_messages_counter_for_mentions_relogin(self):
message = 'test message2'
[chat.home_button.double_click() for chat in (self.chat_1, self.chat_2)]
chat_element = self.home_1.get_chat('#' + self.public_chat_name)
self.home_2.get_chat('#' + self.public_chat_name).click()
self.chat_2.select_mention_from_suggestion_list(self.username_1, self.username_1[:2])
self.chat_2.send_message_button.click()
chat_element.new_messages_counter.wait_for_element(30)
chat_element.new_messages_counter.wait_for_element_text("1", 60)
chat_element.click()
home_1.home_button.double_click()
if home_1.home_button.public_unread_messages.is_element_displayed():
self.home_1.home_button.double_click()
if self.home_1.home_button.public_unread_messages.is_element_displayed():
self.errors.append('New messages public chat badge is shown on Home button')
if chat_element.new_messages_public_chat.is_element_displayed():
self.errors.append('Unread messages badge is shown in public chat while there are no unread messages')
[home.get_chat('#' + chat_name).click() for home in (home_1, home_2)]
chat_1.send_message(message_2)
chat_2.chat_element_by_text(message_2).wait_for_element(20)
[home.get_chat('#' + self.public_chat_name).click() for home in (self.home_1, self.home_2)]
self.chat_1.send_message(message)
self.chat_2.chat_element_by_text(message).wait_for_element(20)
home_2.just_fyi("Check that unread message indicator is not reappeared after relogin")
self.chat_2.just_fyi("Check that unread messages counter doesn't reappear after relogin")
driver_2 = self.drivers[1]
driver_2.close_app()
driver_2.launch_app()
SignInView(driver_2).sign_in()
chat_element = home_2.get_chat('#' + chat_name)
chat_element = self.home_2.get_chat('#' + self.public_chat_name)
if chat_element.new_messages_public_chat.is_element_displayed():
self.errors.append('New messages counter is shown after relogin')
self.drivers[0].fail('New messages counter is shown after relogin')
self.errors.verify_no_errors()
class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
@marks.testrail_id(6270)
@marks.medium
def test_mark_all_messages_as_read_public_chat(self):
@@ -141,45 +149,6 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
self.errors.verify_no_errors()
@marks.testrail_id(6275)
@marks.medium
def test_receive_message_while_in_different_tab_and_emoji_messages_long_press(self):
self.create_drivers(2)
device_1, device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
home_1, home_2 = device_1.create_user(), device_2.create_user()
public_chat_name = home_1.get_random_chat_name()
chat_1, chat_2 = home_1.join_public_chat(public_chat_name), home_2.join_public_chat(public_chat_name)
device_2.just_fyi("Switch to different tab out from chat")
device_2.dapp_tab_button.click()
emoji_name = random.choice(list(emoji.EMOJI_UNICODE))
emoji_unicode = emoji.EMOJI_UNICODE[emoji_name]
chat_1.chat_message_input.send_keys(emoji.emojize(emoji_name))
chat_1.send_message_button.click()
device_1.just_fyi("Long press emoji message actions")
chat_1.element_by_text_part(emoji_unicode).long_press_element()
chat_1.element_by_text('Copy').click()
chat_1.chat_message_input.paste_text_from_clipboard()
if chat_1.chat_message_input.text != emoji_unicode:
self.errors.append('Emoji message was not copied')
home_2.home_button.click(desired_view='chat')
chat_element_2 = chat_2.element_by_text_part(emoji_unicode)
if not chat_element_2.is_element_displayed(sec=10):
self.errors.append('Message with emoji was not received in public chat by the recipient')
chat_2.quote_message(emoji_unicode)
message_text = 'test message'
chat_2.chat_message_input.send_keys(message_text)
chat_2.send_message_button.click()
chat_element_1 = chat_1.chat_element_by_text(message_text)
if not chat_element_1.is_element_displayed(sec=10) or chat_element_1.replied_message_text != emoji_unicode:
self.errors.append('Reply message was not received by the sender')
self.errors.verify_no_errors()
@marks.testrail_id(6342)
@marks.medium
def test_different_status_in_timeline(self):
@@ -290,8 +290,6 @@ class TestBrowsing(SingleDeviceTestCase):
urls = {
'https://cdn.dribbble.com/users/45534/screenshots/3142450/logo_dribbble.png':
'url1.png',
'https://thebitcoinpub-91d3.kxcdn.com/uploads/default/original/2X/d/db97611b41a96cb7642b06636b82c0800678b140.jpg':
'url2.png',
'https://steemitimages.com/DQmYEjeBuAKVRa3b3ZqwLicSHaPUm7WFtQqohGaZdA9ghjx/images%20(4).jpeg':
'url3.png'
}

Some files were not shown because too many files have changed in this diff Show More