Compare commits

..
Author SHA1 Message Date
yevh-berdnyk ba74308139 Merge remote-tracking branch 'origin/tests/removing-rerun-errors' into tests/removing-rerun-errors 2021-12-17 17:03:21 +02:00
yevh-berdnyk a8fd6fdd60 2 rerun errors removed 2021-12-17 17:01:45 +02:00
yevh-berdnyk fe9dd7c549 Merge branch 'tests/removing-rerun-errors' of github.com:status-im/status-react into tests/removing-rerun-errors 2021-12-17 16:59:54 +02: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
yevh-berdnyk 71651601ad Removed rerun errors 2021-12-15 03:55:27 +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
Churikova Tetiana 816f15f976 e2e: dependencies
Signed-off-by: Churikova Tetiana <churikova.tm@gmail.com>
2021-12-01 18:41:05 +01:00
snyk-bot 853af40167 fix: test/appium/requirements.txt to reduce vulnerabilities
The following vulnerabilities are fixed by pinning transitive dependencies:
- https://snyk.io/vuln/SNYK-PYTHON-AIOHTTP-1584144

Signed-off-by: Churikova Tetiana <churikova.tm@gmail.com>
2021-12-01 16:10:39 +01:00
Andrea Maria Piana 153204ecb1 Add confirmation of p2p messages
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2021-12-01 13:00:56 +00:00
Roman Volosovskyi 62162c0f2a [#12848] Add an option to submit error report to GH 2021-11-30 16:13:36 +02:00
Roman Volosovskyi c7e66fb1aa [#12818] Support tx history (in a way) when on BSC chains 2021-11-27 11:03:01 +02:00
jakub 89d42b9ef4 nix: fix gomobile builds requiring Android SDK
This works fine on `x86_64`, but will fail when used on `aarch64`.

Issue: https://github.com/status-im/status-react/issues/12794

Signed-off-by: Jakub Sokołowski <jakub@status.im>
2021-11-26 16:22:57 +01:00
jakub d0f76c5412 nix: upgrade nixpkgs to current master
Notable package upgrades:

| Software  | Old          | New           |
|-----------|--------------|---------------|
| Glibc     | `2.32`       | `2.33`        |
| BinUtils  | `2.35.1`     | `2.35.2`      |
| CoreUtils | `8.32`       | `9.0`         |
| OpenSSL   | `1.1.1k`     | `1.1.1l`      |
| Git       | `2.31.1`     | `2.33.1`      |
| GCC       | `8.4.0`      | `8.5.0`       |
| Clojure   | `1.10.3.855` | `1.10.3.1029` |
| Node.js   | `12.22.1`    | `12.22.7`     |
| Yarn      | `1.22.10`    | `1.22.17`     |

Signed-off-by: Jakub Sokołowski <jakub@status.im>
2021-11-26 16:22:45 +01:00
Churikova Tetiana 3dc943c89e update release umbrella issue 2021-11-26 16:09:22 +01:00
Churikova Tetiana f5589c368c e2e: new tests + todos
Signed-off-by: Churikova Tetiana <churikova.tm@gmail.com>
2021-11-26 15:18:29 +01:00
Roman Volosovskyi 469e082de1 [#12819] Fix network fee calculation on xDai chain 2021-11-26 13:25:09 +02:00
jakub a0430607d3 ci: specify macos host architecture in labels
Since now we have a 5th Gen Mac Mini with `arm64` M1 CPU.

Signed-off-by: Jakub Sokołowski <jakub@status.im>
2021-11-26 12:07:36 +01:00
Parvesh Monu 751b30688d Decline pending group invitations from user, when user is banned (#12835) 2021-11-25 21:09:10 +05:30
Parvesh Monu b4b39e5088 Change accessibility label as per online status (#12846) 2021-11-25 18:07:47 +05:30
andrey ae26024890 [#12837] Cannot remove value from the 'Custom nonce' field
Signed-off-by: andrey <motor4ik@gmail.com>
2021-11-25 08:04:56 +01:00
jakub 8b055cd3c3 ci: fix macos host label for pod updates
Job: https://ci.status.im/job/status-react/job/tools/job/run-pod-repo-update

Signed-off-by: Jakub Sokołowski <jakub@status.im>
2021-11-24 20:54:46 +01:00
andrey c9c8263c9e [#12365] Spin of death on adding custom NFT token contract
Signed-off-by: andrey <motor4ik@gmail.com>
2021-11-24 12:36:47 +01:00
andrey 4000abfd0a [#11964] "TypeError: Network request failed" when switching to Goerli network
Signed-off-by: andrey <motor4ik@gmail.com>
2021-11-23 17:44:03 +01:00
andrey 995a647a34 [#12827] Can't send transaction with custom nonce as it is overlapped by keyboard
Signed-off-by: andrey <motor4ik@gmail.com>
2021-11-23 11:46:45 +01:00
Andrea Maria Piana fdadee6cef update statusgo
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2021-11-22 11:20:56 +00:00
Churikova Tetiana 27f1feeaf6 e2e: catch testrail error
Signed-off-by: Churikova Tetiana <churikova.tm@gmail.com>
2021-11-19 18:28:29 +01:00
Parvesh Monu debf3df61c Allow user to be reinvited to the same group after leaving (#12814) 2021-11-19 20:28:42 +05:30
122 changed files with 1994 additions and 984 deletions
+1 -1
View File
@@ -68,7 +68,7 @@ Upgrade (e2e):
- [ ] 3. Upload builds for DO store, https://dev.status.im/ (Jakub)
#### 5) At time of publishing
- [ ] 1. Notify marketing to push comms and blog post (Jonny)
- [ ] 1. Notify marketing to push comms and blog post (Jonny + get review on blog from QA team)
- [ ] 2. Publish release to app store and playstore
- [ ] 3. Final push to update prod site https://status.im/, [instructions](https://github.com/status-im/infra-docs/blob/master/articles/status_release_upload.md) (Jakub - requires credentials to upload)
- [ ] 4. Add the link to the post about the release and publish the release: https://github.com/status-im/status-react/releases (Jakub)
+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
+1 -1
View File
@@ -1,7 +1,7 @@
library 'status-jenkins-lib@v1.3.3'
pipeline {
agent { label 'macos-xcode-12.5' }
agent { label 'macos && x86_64 && xcode-12.5' }
parameters {
string(
+1 -1
View File
@@ -29,7 +29,7 @@ pipeline {
name: 'HOST_LABEL',
description: 'Label of host to run on',
/* Using startTimeInMillis to randomize which host gets the update. */
defaultValue: "macos-0${(currentBuild.startTimeInMillis % 3) + 1}",
defaultValue: "maci7-0${(currentBuild.startTimeInMillis % 3) + 1}",
)
}
+2
View File
@@ -42,6 +42,8 @@ in {
allowHigher = true;
};
gomobile = super.gomobile.override {
# FIXME: No Android SDK packages for aarch64-darwin.
withAndroidPkgs = stdenv.system != "aarch64-darwin";
androidPkgs = self.androidEnvCustom.compose;
};
+3 -3
View File
@@ -11,10 +11,10 @@ let
# We follow the master branch of official nixpkgs.
nixpkgsSrc = fetchFromGitHub {
name = "nixpkgs-source";
owner = "NixOS";
owner = "status-im";
repo = "nixpkgs";
rev = "65135081f63ca2f5eaa9b7b54bfc7bf31502cfd6";
sha256 = "074lyh19x8xs8xqf4wwwl64870rpml96g7dhx1y09i81w5k29aga";
rev = "15111f8a9ad423d300886b537647691c2faa28cd";
sha256 = "05ny644x3dpxigljnb4rmams5vrs5gkbcyqjfamvlqm8rdmsi0kn";
# To get the compressed Nix sha256, use:
# nix-prefetch-url --unpack https://github.com/${ORG}/nixpkgs/archive/${REV}.tar.gz
};
+39 -54
View File
@@ -19,20 +19,11 @@
:restSpeedThreshold 0.1
:restDisplacementThreshold 0.1})
;; NOTE(Ferossgp): RNGH does not work in modal react native
(defn modal [{:keys [visible] :as props} & children]
(if platform/android?
(into [rn/view {:style styles/container
:pointer-events (if visible :box-none :none)}]
(when visible children))
(into [rn/modal props] children)))
(defn bottom-sheet-hooks [props]
(let [{on-cancel :onCancel
disable-drag? :disableDrag?
show-handle? :showHandle?
visible? :visible?
transparent :transparent
backdrop-dismiss? :backdropDismiss?
back-button-cancel :backButtonCancel
children :children
@@ -50,7 +41,7 @@
keyboard-height-android-delta (if (and platform/android? keyboard-shown) (+ keyboard-height 20) 0)
safe-area (safe-area/use-safe-area)
window-height (- window-height (if platform/android?
(+ 50 keyboard-height-android-delta)
(+ 50 keyboard-height-android-delta) ;; TODO : remove 50 when react-native-navigation v8 will be implemented https://github.com/wix/react-native-navigation/issues/7225
0))
min-height (+ (* styles/vertical-padding 2) (:bottom safe-area))
max-height (- window-height (:top safe-area) styles/margin-top)
@@ -186,48 +177,42 @@
(close-sheet)))
[visible?])
(reagent/as-element
[modal {:visible @visible
:transparent true
:presentation-style :overFullScreen
:hardware-accelerated true
:on-request-close (fn []
(when back-button-cancel
(close-sheet)))}
[rn/view {:style styles/container
:opacity (if transparent 0 1)
:pointer-events :box-none}
[gesture-handler/tap-gesture-handler (merge {:enabled backdrop-dismiss?}
tap-gesture-handler)
[animated/view {:style (merge (styles/backdrop)
(when platform/ios?
{:opacity opacity
:background-color (:backdrop @colors/theme)}))}]]
[animated/view {:style (merge (styles/content-container window-height)
{:transform [{:translateY (animated/add translate-y keyboard-height-android-delta)}
{:translateY (* window-height 2)}]})}
[gesture-handler/pan-gesture-handler (merge on-master-event
{:ref master-ref
:wait-for body-ref
:enabled (not disable-drag?)})
[animated/view {:style styles/content-header}
(when show-handle?
[rn/view {:style styles/handle}])]]
[gesture-handler/pan-gesture-handler (merge on-body-event
{:ref body-ref
:wait-for master-ref
:enabled (and (not disable-drag?)
(not= sheet-height max-height))})
[animated/view {:height sheet-height}
[animated/scroll-view {:bounces false
:flex 1
:scroll-enabled (= sheet-height max-height)}
[animated/view {:style {:padding-top styles/vertical-padding
:padding-bottom (+ styles/vertical-padding
(if (and platform/ios? keyboard-shown)
keyboard-height
(:bottom safe-area)))}
:on-layout #(reset! height (.-nativeEvent.layout.height ^js %))}
(into [:<>]
(react/get-children children))]]]]]]])))
[rn/view {:style styles/container
:pointer-events :box-none}
[gesture-handler/tap-gesture-handler (merge {:enabled backdrop-dismiss?}
tap-gesture-handler)
[animated/view {:style (merge (styles/backdrop)
(when platform/ios?
{:opacity opacity
:background-color (:backdrop @colors/theme)}))}]]
[animated/view {:style (merge (styles/content-container window-height)
{:transform [{:translateY (if (= sheet-height max-height)
(animated/add translate-y keyboard-height-android-delta)
translate-y)}
{:translateY (* window-height 2)}]})}
[gesture-handler/pan-gesture-handler (merge on-master-event
{:ref master-ref
:wait-for body-ref
:enabled (not disable-drag?)})
[animated/view {:style styles/content-header}
(when show-handle?
[rn/view {:style styles/handle}])]]
[gesture-handler/pan-gesture-handler (merge on-body-event
{:ref body-ref
:wait-for master-ref
:enabled (and (not disable-drag?)
(not= sheet-height max-height))})
[animated/view {:height sheet-height}
[animated/scroll-view {:bounces false
:flex 1
:scroll-enabled (= sheet-height max-height)}
[animated/view {:style {:padding-top styles/vertical-padding
:padding-bottom (+ styles/vertical-padding
(if (and platform/ios? keyboard-shown)
keyboard-height
(:bottom safe-area)))}
:on-layout #(reset! height (.-nativeEvent.layout.height ^js %))}
(into [:<>]
(react/get-children children))]]]]]])))
(def bottom-sheet (reagent/adapt-react-class (react/memo bottom-sheet-hooks)))
(def bottom-sheet (reagent/adapt-react-class (react/memo bottom-sheet-hooks)))
+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}]]])
+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))
+46 -38
View File
@@ -129,11 +129,13 @@
(map (map-chats cofx) chats))]
(fx/merge
cofx
{:db (-> db
(update :chats merge all-chats)
(update :chats-home-list set/union chats-home-list)
(update :chats #(apply dissoc % removed-chats))
(update :chats-home-list set/difference removed-chats))}
(merge {:db (-> db
(update :chats merge all-chats)
(update :chats-home-list set/union chats-home-list)
(update :chats #(apply dissoc % removed-chats))
(update :chats-home-list set/difference removed-chats))}
(when (not-empty removed-chats)
{:clear-multiple-message-notifications removed-chats}))
leave-removed-chat)))
(fx/defn clear-history
@@ -197,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"
@@ -232,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]}
@@ -292,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"
@@ -310,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
+4 -2
View File
@@ -14,7 +14,8 @@
[quo.design-system.colors :as colors]
[status-im.navigation :as navigation]
[status-im.utils.handlers :refer [>evt]]
[status-im.ui.components.emoji-thumbnail.styles :as emoji-thumbnail-styles]))
[status-im.ui.components.emoji-thumbnail.styles :as emoji-thumbnail-styles]
[status-im.notifications-center.core :as notification-center]))
(def crop-size 1000)
@@ -447,7 +448,8 @@
[cofx response-js]
(fx/merge cofx
(bottom-sheet/hide-bottom-sheet)
(handle-response response-js)))
(handle-response response-js)
(notification-center/get-activity-center-notifications-count)))
(fx/defn member-ban
{:events [::member-ban]}
+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)}]})
+24 -10
View File
@@ -17,16 +17,20 @@
(.hexToUtf8 utils s)
(catch :default _ nil)))
;; IDs standardized in https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md#list-of-chain-ids
(def BSC-mainnet-chain-id 56)
(def BSC-testnet-chain-id 97)
;; IDs standardized in https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md#list-of-chain-ids
(def chains
{:mainnet {:id 1 :name "Mainnet"}
: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 56 :name "BSC"}})
{:mainnet {:id 1 :name "Mainnet"}
:testnet {:id 3 :name "Ropsten"}
:rinkeby {:id 4 :name "Rinkeby"}
:xdai {:id 100 :name "xDai"}
:goerli {:id 5 :name "Goerli"}
:bsc {:id BSC-mainnet-chain-id
:name "BSC"}
:bsc-testnet {:id BSC-testnet-chain-id
:name "BSC tetnet"}})
(defn chain-id->chain-keyword [i]
(or (some #(when (= i (:id (val %))) (key %)) chains)
@@ -47,11 +51,11 @@
(defn testnet? [id]
(contains? #{(chain-keyword->chain-id :testnet)
(chain-keyword->chain-id :rinkeby)
(chain-keyword->chain-id :goerli)} id))
(chain-keyword->chain-id :goerli)
(chain-keyword->chain-id :bsc-testnet)} id))
(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]
@@ -109,6 +113,16 @@
network-id (get db :networks/current-network)]
(get networks network-id)))
(defn binance-chain-id? [chain-id]
(or (= BSC-mainnet-chain-id chain-id)
(= BSC-testnet-chain-id chain-id)))
(defn binance-chain? [db]
(-> db
current-network
network->chain-id
binance-chain-id?))
(def custom-rpc-node-id-len 45)
(defn custom-rpc-node? [{:keys [id]}]
+1
View File
@@ -194,6 +194,7 @@
"wallet_getCryptoOnRamps" {}
"wallet_getOpenseaCollectionsByOwner" {}
"wallet_getOpenseaAssetsByOwnerAndCollection" {}
"wallet_loadTransferByHash" {}
"browsers_getBrowsers" {}
"browsers_addBrowser" {}
"browsers_deleteBrowser" {}
+24 -24
View File
@@ -17,30 +17,30 @@
(def all-native-currencies
(ethereum.macros/resolve-native-currency-icons
{:mainnet {:name "Ether"
:symbol :ETH
:decimals 18}
:testnet {:name "Ropsten Ether"
:symbol :ETH
:symbol-display :ETHro
:decimals 18}
:rinkeby {:name "Rinkeby Ether"
: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
:symbol-exchange :DAI
:decimals 18}
:bsc {:name "BSC"
:symbol :ETH
:symbol-display :BNB
:decimals 18}}))
{:mainnet {:name "Ether"
:symbol :ETH
:decimals 18}
:testnet {:name "Ropsten Ether"
:symbol :ETH
:symbol-display :ETHro
:decimals 18}
:rinkeby {:name "Rinkeby Ether"
:symbol :ETH
:symbol-display :ETHri
:decimals 18}
:xdai {:name "xDAI"
:symbol :ETH
:symbol-display :xDAI
:symbol-exchange :DAI
:decimals 18}
:bsc {:name "BSC"
:symbol :ETH
:symbol-display :BNB
:decimals 18}
:bsc-testnet {:name "BSC test"
:symbol :ETH
:symbol-display :BNBtest
:decimals 18}}))
(def native-currency-symbols
(set (map #(-> % val :symbol) all-native-currencies)))
+26 -9
View File
@@ -2,6 +2,7 @@
(:require [cljs.spec.alpha :as spec]
[re-frame.core :as re-frame]
[status-im.ens.core :as ens]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.decode :as decode]
[status-im.ethereum.eip55 :as eip55]
[status-im.ethereum.encode :as encode]
@@ -13,16 +14,31 @@
(def confirmations-count-threshold 12)
(def etherscan-supported? #{:testnet :mainnet :rinkeby})
(def etherscan-supported?
#{(ethereum/chain-keyword->chain-id :mainnet)
(ethereum/chain-keyword->chain-id :testnet)
(ethereum/chain-keyword->chain-id :rinkeby)})
(let [network->subdomain {:testnet "ropsten" :rinkeby "rinkeby"}]
(defn get-transaction-details-url [chain hash]
{:pre [(keyword? chain) (string? hash)]
:post [(or (nil? %) (string? %))]}
(when (etherscan-supported? chain)
(let [network-subdomain (when-let [subdomain (network->subdomain chain)]
(str subdomain "."))]
(str "https://" network-subdomain "etherscan.io/tx/" hash)))))
(def binance-mainnet-chain-id (ethereum/chain-keyword->chain-id :bsc))
(def binance-testnet-chain-id (ethereum/chain-keyword->chain-id :bsc-testnet))
(def network->subdomain {3 "ropsten"
4 "rinkeby"})
(defn get-transaction-details-url [chain-id hash]
{:pre [(number? chain-id) (string? hash)]
:post [(or (nil? %) (string? %))]}
(cond
(etherscan-supported? chain-id)
(let [network-subdomain (when-let [subdomain (network->subdomain chain-id)]
(str subdomain "."))]
(str "https://" network-subdomain "etherscan.io/tx/" hash))
(= chain-id binance-mainnet-chain-id)
(str "https://bscscan.com/tx/" hash)
(= chain-id binance-testnet-chain-id)
(str "https://testnet.bscscan.com/tx/" hash)))
(def default-erc20-token
{:symbol :ERC20
@@ -358,6 +374,7 @@
(tx-fetching-in-progress [address]))))
(fx/defn get-fetched-transfers
{:events [:transaction/get-fetched-transfers]}
[{:keys [db]}]
{:transactions/get-transfers
{:chain-tokens (:wallet/all-tokens db)
+68 -53
View File
@@ -1,65 +1,64 @@
(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
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))
(re-frame/reg-fx
:dismiss-keyboard
@@ -193,8 +192,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
+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
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
+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]}
+20 -3
View File
@@ -122,8 +122,11 @@
(wallet/get-cached-balances scan-all-tokens?))
(when-not (get db :wallet/new-account)
(wallet/restart-wallet-service nil))
(when-not (utils.mobile-sync/syncing-allowed? cofx)
(when (or (not (utils.mobile-sync/syncing-allowed? cofx))
(ethereum/binance-chain? db))
(transactions/get-fetched-transfers))
(when (ethereum/binance-chain? db)
(wallet/request-current-block-update))
(prices/update-prices)))
(fx/defn login
@@ -322,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}]
@@ -336,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)
@@ -378,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
+31 -11
View File
@@ -1,17 +1,18 @@
(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]
["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 +27,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 +37,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 +47,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 +61,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 +73,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 +94,7 @@
;; DISSMISS MODAL
(defn dissmissModal []
(log/debug "dissmissModal")
(reset! dissmissing true)
(.dismissModal Navigation (name (last @modals))))
@@ -105,6 +112,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 +143,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)
@@ -159,6 +168,7 @@
(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 +176,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 +184,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 +204,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 +216,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 +239,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 +249,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 +269,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))))
@@ -343,11 +360,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 +374,6 @@
(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)))
+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]
+7
View File
@@ -92,6 +92,13 @@
(when platform/android?
(pn-android/clear-message-notifications chat-id))))
(re-frame/reg-fx
:clear-multiple-message-notifications
(fn [chat-ids]
(when platform/android?
(doseq [chat-id chat-ids]
(pn-android/clear-message-notifications chat-id)))))
(fx/defn handle-enable-notifications-event
{:events [::registered-for-push-notifications]}
[cofx token]
+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)))
+21 -8
View File
@@ -6,15 +6,28 @@
[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)]
{:db (-> db
(assoc-in [:activity.center/notifications :notifications] notifications)
(assoc :activity.center/notifications-count (max 0 unread-count)))
:dispatch (cond
(= (:view-id db) :notifications-center)
[:mark-all-activity-center-notifications-as-read]
(= (:view-id db) :chat)
[:accept-all-activity-center-notifications-from-chat (:current-chat-id db)])}))
(fx/defn get-activity-center-notifications-count
{:events [:get-activity-center-notifications-count]}
+5 -5
View File
@@ -234,7 +234,7 @@
:keycard-step (when pinless? :connect)})
:show-signing-sheet nil}
#(when-not wallet-set-up-passed?
{:dispatch-n [[:show-popover {:view :signing-phrase}]]})
{:dispatch-later [{:dispatch [:show-popover {:view :signing-phrase}] :ms 200}]})
(when pinless?
(keycard.card/start-nfc {:on-success #(re-frame/dispatch [:keycard.callback/start-nfc-success])
:on-failure #(re-frame/dispatch [:keycard.callback/start-nfc-failure])})
@@ -249,7 +249,7 @@
:show-signing-sheet nil
:dismiss-keyboard nil}
#(when-not wallet-set-up-passed?
{:dispatch-n [[:show-popover {:view :signing-phrase}]]})
{:dispatch-later [{:dispatch [:show-popover {:view :signing-phrase}] :ms 200}]})
(prices/update-prices)
#(when-not gas
{:db (assoc-in (:db %) [:signing/edit-fee :gas-loading?] true)
@@ -304,13 +304,13 @@
(fx/defn transaction-result
[{:keys [db] :as cofx} result tx-obj]
(let [{:keys [on-result symbol amount]} (get db :signing/tx)]
(let [{:keys [on-result symbol amount from]} (get db :signing/tx)]
(fx/merge cofx
{:db (dissoc db :signing/tx :signing/sign)
:signing/show-transaction-result nil}
(prepare-unconfirmed-transaction result tx-obj symbol amount)
(check-queue)
(wallet/watch-tx result)
(wallet/watch-tx (get from :address) result)
#(when on-result
{:dispatch (conj on-result result)}))))
@@ -323,7 +323,7 @@
(fx/merge
cofx
{:db (dissoc db :signing/tx :signing/sign)}
(wallet/watch-tx transaction-hash)
(wallet/watch-tx (get from :address) transaction-hash)
(if (keycard.common/keycard-multiaccount? db)
(signing.keycard/hash-message
{:data data
+27 -13
View File
@@ -7,7 +7,8 @@
[status-im.utils.money :as money]
[status-im.signing.eip1559 :as eip1559]
[taoensso.timbre :as log]
[status-im.popover.core :as popover]))
[status-im.popover.core :as popover]
[clojure.string :as string]))
(def min-gas-price-wei ^js (money/bignumber 1))
@@ -211,9 +212,7 @@
[{db :db} price]
(if (eip1559/sync-enabled?)
(let [{:keys [normal-base-fee max-priority-fee]} price
max-priority-fee-bn (money/with-precision
(get-suggested-tip max-priority-fee)
0)]
max-priority-fee-bn (money/with-precision (get-suggested-tip max-priority-fee) 0)]
{:db (-> db
(assoc-in [:signing/tx :maxFeePerGas]
(money/to-hex (money/add max-priority-fee-bn
@@ -279,7 +278,7 @@
{:events [:signing.nonce/submit]}
[{db :db :as cofx} nonce]
(fx/merge cofx
{:db (assoc-in db [:signing/tx :nonce] nonce)}
{:db (assoc-in db [:signing/tx :nonce] (if (string/blank? nonce) nil nonce))}
(bottom-sheet/hide-bottom-sheet)))
(re-frame/reg-fx
@@ -290,7 +289,9 @@
(fn []
(json-rpc/call
{:method "eth_feeHistory"
:params [101 "latest" nil]
;; NOTE(rasom): We don't need `reward` atm but if the last parameter is
;; `nil` request fails on some chains (particularly on xDai).
:params [101 "latest" [100]]
:on-success #(re-frame/dispatch [::header-fetched
(assoc params :fee-history %)])}))
(fn []
@@ -342,31 +343,44 @@
(defn check-base-fee [{:keys [baseFeePerGas]}]
(let [all-base-fees (mapv money/bignumber baseFeePerGas)
current-base-fee (last all-base-fees)
next-base-fee (peek all-base-fees)
previous-fees (subvec all-base-fees 0 101)
current-base-fee (peek previous-fees)
percentiles (calc-percentiles previous-fees [10 20 80])]
{:normal-base-fee (money/to-hex
(recommended-base-fee
current-base-fee
next-base-fee
(get percentiles 20)
(get percentiles 80)))
:slow-base-fee (money/to-hex
(slow-base-fee
current-base-fee
next-base-fee
(get percentiles 10)))
:fast-base-fee (money/to-hex
(fast-base-fee
current-base-fee
next-base-fee
(get percentiles 80)))
:current-base-fee (money/to-hex current-base-fee)}))
(defn max-priority-fee-hex [gas-price base-fee]
(money/to-hex
(money/sub (money/bignumber gas-price)
(money/bignumber base-fee))))
(fx/defn header-fetched
{:events [::header-fetched]}
[_ {:keys [error-callback success-callback fee-history] :as params}]
{::json-rpc/call
[{:method "eth_maxPriorityFeePerGas"
:on-success #(success-callback (merge {:max-priority-fee %}
(check-base-fee fee-history)))
[;; NOTE(rasom): eth_maxPriorityFeePerGas is not supported by some networks
;; so it is more reliable to calculate maxPriorityFeePerGas using the value
;; returned by eth_gasPrice and current base fee.
{:method "eth_gasPrice"
:on-success #(success-callback
(let [{:keys [current-base-fee] :as base-fees}
(check-base-fee fee-history)]
(merge {:max-priority-fee
(max-priority-fee-hex % current-base-fee)}
base-fees)))
:on-error (if error-callback
#(error-callback %)
#(log/error "Can't fetch header" %))}]})
+25 -26
View File
@@ -1197,19 +1197,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
@@ -1438,20 +1425,26 @@
:<- [: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)]
(fn [[{:keys [chat-id users contacts community-id chat-type] :as chat} mentionable-contacts blocked {:keys [name preferred-name public-key]}]]
(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)))]
(apply dissoc
(-> users
(merge contacts-with-one-to-one)
(merge filtered-contacts)
(assoc public-key (mentions/add-searchable-phrases
{:alias name
:name (or preferred-name name)
:public-key public-key})))
(conj blocked public-key)))))
(conj (concat blocked members-left) public-key)))))
(re-frame/reg-sub
:chat/mention-suggestions
@@ -1776,11 +1769,11 @@
(get-in wallet [:fetching address :all-fetched?])))
(re-frame/reg-sub
:wallet/etherscan-link
:wallet/chain-explorer-link
(fn [db [_ address]]
(let [network (:networks/current-network db)
link (get-in config/default-networks-by-id
[network :etherscan-link])]
[network :chain-explorer-link])]
(when link
(str link address)))))
@@ -2108,8 +2101,8 @@
(fn [[_ _ address] _]
[(re-frame/subscribe [:wallet.transactions/transactions address])
(re-frame/subscribe [:ethereum/native-currency])
(re-frame/subscribe [:ethereum/chain-keyword])])
(fn [[transactions native-currency chain-keyword] [_ hash _]]
(re-frame/subscribe [:chain-id])])
(fn [[transactions native-currency chain-id] [_ hash _]]
(let [{:keys [gas-used gas-price fee-cap tip-cap hash timestamp type]
:as transaction}
(get transactions hash)
@@ -2147,7 +2140,7 @@
(money/fee-value gas-used gas-price)
native-currency-text))
:url (transactions/get-transaction-details-url
chain-keyword
chain-id
hash)}))))))
(re-frame/reg-sub
@@ -2196,6 +2189,12 @@
:<- [:wallet]
:request-transaction)
(re-frame/reg-sub
:wallet/binance-chain?
:<- [:current-network]
(fn [network]
(ethereum/binance-chain-id? (get-in network [:config :NetworkId]))))
;;UI ==============================================================================================================
(re-frame/reg-sub
+5 -5
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)]
@@ -216,7 +214,7 @@
[{:ms 100 :dispatch [:process-statuses statuses]}])
(when (seq transactions)
(for [transaction-hash transactions]
{:ms 100 :dispatch [:watch-tx transaction-hash]})))}
{:ms 100 :dispatch [:watch-tx nil transaction-hash]})))}
(process-response response-js process-async)))))
(fx/defn remove-hash
@@ -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"
@@ -46,21 +46,22 @@
(defn profile-photo-plus-dot-view
[{:keys [public-key photo-container photo-path community?]}]
(let [photo-path (if (nil? photo-path)
@(re-frame.core/subscribe [:chats/photo-path public-key])
photo-path)
photo-container (if (nil? photo-container)
styles/container-chat-list photo-container)
size (:width photo-container)
identicon? (when photo-path (profile.db/base64-png? photo-path))
dot-styles (visibility-status-utils/icon-visibility-status-dot
public-key size identicon?)]
(let [photo-path (if (nil? photo-path)
@(re-frame.core/subscribe [:chats/photo-path public-key])
photo-path)
photo-container (if (nil? photo-container)
styles/container-chat-list photo-container)
size (:width photo-container)
identicon? (when photo-path (profile.db/base64-png? photo-path))
dot-styles (visibility-status-utils/icon-visibility-status-dot
public-key size identicon?)
dot-accessibility-label (:accessibility-label dot-styles)]
[react/view {:style photo-container
:accessibility-label :profile-photo}
[photos/photo photo-path {:size size}]
(when-not community?
[react/view {:style dot-styles
:accessibility-label :profile-photo-dot}])]))
:accessibility-label dot-accessibility-label}])]))
(defn emoji-chat-icon-view
[chat-id group-chat name emoji styles]
@@ -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?
@@ -103,4 +103,5 @@
(conj chats lang-topic)
chats)]
(for [chat chats]
(render-topic chat)))]])]]))
^{:key chat}
[render-topic chat]))]])]]))
+12 -3
View File
@@ -35,6 +35,15 @@
{:type :primary
:accessibility-label :bug-report-submit
:theme :accent
:on-press #(re-frame/dispatch
[:logging/submit-report])}
(i18n/label :t/submit)]]]))
:on-press #(re-frame/dispatch [:logging/submit-report])}
(i18n/label :t/bug-report-submit-email)]
[react-native/view
{:style {:margin-vertical 16
:align-items :center}}
[quo/text (i18n/label :t/or)]]
[quo/button
{:type :primary
:accessibility-label :bug-report-submit-gh-issue
:theme :accent
:on-press #(re-frame/dispatch [:logging/submit-gh-issue])}
(i18n/label :t/bug-report-submit-gh-issue)]]]))
@@ -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)))]]]))))
+138 -104
View File
@@ -24,42 +24,64 @@
[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)}
timestamp-str]])))
(defview quoted-message
[_ {:keys [from parsed-text image]} outgoing current-public-key public? pinned]
@@ -168,19 +190,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 +431,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 +441,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 +450,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 +509,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 +585,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
@@ -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
+43 -48
View File
@@ -295,7 +295,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 +343,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 +354,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]])]))))
@@ -154,14 +154,14 @@
: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
+18 -11
View File
@@ -24,7 +24,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 []
@@ -199,13 +200,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]])}))
@@ -82,11 +82,15 @@
visibility-status-update (visibility-status-update public-key my-icon?)
size (/ container-size 4)
margin (if identicon? (/ size 6) (/ size 7))
dot-color (dot-color visibility-status-update my-icon?)]
dot-color (dot-color visibility-status-update my-icon?)
accessibility-label (if (= dot-color colors/color-online)
:online-profile-photo-dot
:offline-profile-photo-dot)]
(merge (styles/visibility-status-dot dot-color size)
{:bottom margin
:right margin
:position :absolute})))
{:bottom margin
:right margin
:position :absolute
:accessibility-label accessibility-label})))
(defn visibility-status-order [public-key]
(let [my-icon? (my-icon? public-key)
+2 -1
View File
@@ -219,7 +219,8 @@
;Chat
{:name :chat
:options {:topBar {:title {:component {:name :chat-toolbar :id :chat-toolbar}
: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
+1 -1
View File
@@ -392,12 +392,12 @@
:default-value default-nonce
:on-change-text #(reset! nonce %)
:show-cancel false
:auto-focus true
:container-style {:margin-bottom 20}}]
[react/view {:align-items :flex-end}
[quo/button
{:type :primary
:accessibility-label :save-nonce
:theme :accent
:disabled (clojure.string/blank? @nonce)
:on-press #(re-frame/dispatch [:signing.nonce/submit @nonce])}
(i18n/label :t/save)]]])))
@@ -110,8 +110,7 @@
[tabs/tab-title state :assets (i18n/label :t/wallet-assets) (= tab :assets)]
(when ethereum-network?
[tabs/tab-title state :nft (i18n/label :t/wallet-collectibles) (= tab :nft)])
(when ethereum-network?
[tabs/tab-title state :history (i18n/label :t/history) (= tab :history)])]
[tabs/tab-title state :history (i18n/label :t/history) (= tab :history)]]
(cond
(= tab :assets)
[:<>
@@ -73,8 +73,8 @@
{:on-press #(re-frame/dispatch [:signing.ui/cancel-transaction-pressed hash])}
(i18n/label :t/cancel)]])])
(defn etherscan-link [address]
(let [link @(re-frame/subscribe [:wallet/etherscan-link address])]
(defn chain-explorer-link [address]
(let [link @(re-frame/subscribe [:wallet/chain-explorer-link address])]
[react/touchable-highlight
{:on-press #(when link
(.openURL ^js react/linking link))}
@@ -124,11 +124,12 @@
keycard-account? @(re-frame/subscribe [:multiaccounts/keycard-account?])
custom-rpc-node? @(re-frame/subscribe [:custom-rpc-node])
non-archival-rpc-node? @(re-frame/subscribe [:wallet/non-archival-node])
binance-chain? @(re-frame/subscribe [:wallet/binance-chain?])
all-fetched? @(re-frame/subscribe [:wallet/tx-history-fetched? address])
syncing-allowed? @(re-frame/subscribe [:mobile-network/syncing-allowed?])]
[react/view {:flex 1}
[etherscan-link address]
(cond non-archival-rpc-node?
[chain-explorer-link address]
(cond (or non-archival-rpc-node? binance-chain?)
[non-archival-node]
custom-rpc-node?
[custom-node])
+52 -49
View File
@@ -1,8 +1,8 @@
(ns status-im.utils.config
(:require [clojure.string :as string]
(:require ["react-native-config" :default react-native-config]
[clojure.string :as string]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.ens :as ens]
["react-native-config" :default react-native-config]))
[status-im.ethereum.ens :as ens]))
(def config
(memoize
@@ -94,56 +94,59 @@
(get-in default-multiaccount [:wallet/visible-tokens chain]))
(def mainnet-networks
[{:id "mainnet_rpc",
:etherscan-link "https://etherscan.io/address/",
:name "Mainnet with upstream RPC",
:config {:NetworkId (ethereum/chain-keyword->chain-id :mainnet)
:DataDir "/ethereum/mainnet_rpc"
:UpstreamConfig {:Enabled true
:URL mainnet-rpc-url}}}])
[{:id "mainnet_rpc",
:chain-explorer-link "https://etherscan.io/address/",
:name "Mainnet with upstream RPC",
:config {:NetworkId (ethereum/chain-keyword->chain-id :mainnet)
:DataDir "/ethereum/mainnet_rpc"
:UpstreamConfig {:Enabled true
:URL mainnet-rpc-url}}}])
(def sidechain-networks
[{:id "xdai_rpc",
:name "xDai Chain",
:config {:NetworkId (ethereum/chain-keyword->chain-id :xdai)
: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",
:name "BSC Network",
:config {:NetworkId (ethereum/chain-keyword->chain-id :bsc)
:DataDir "/ethereum/bsc_rpc"
:UpstreamConfig {:Enabled true
:URL "https://bsc-dataseed.binance.org"}}}])
[{:id "xdai_rpc",
:name "xDai Chain",
:chain-explorer-link "https://blockscout.com/xdai/mainnet/address/",
:config {:NetworkId (ethereum/chain-keyword->chain-id :xdai)
:DataDir "/ethereum/xdai_rpc"
:UpstreamConfig {:Enabled true
:URL "https://dai.poa.network"}}}
{:id "bsc_rpc",
:chain-explorer-link "https://bscscan.com/address/",
:name "BSC Network",
:config {:NetworkId (ethereum/chain-keyword->chain-id :bsc)
:DataDir "/ethereum/bsc_rpc"
:UpstreamConfig {:Enabled true
:URL "https://bsc-dataseed.binance.org"}}}])
(def testnet-networks
[{:id "testnet_rpc",
:etherscan-link "https://ropsten.etherscan.io/address/",
:name "Ropsten with upstream RPC",
:config {:NetworkId (ethereum/chain-keyword->chain-id :testnet)
:DataDir "/ethereum/testnet_rpc"
:UpstreamConfig {:Enabled true
:URL testnet-rpc-url}}}
{:id "rinkeby_rpc",
:etherscan-link "https://rinkeby.etherscan.io/address/",
:name "Rinkeby with upstream RPC",
:config {:NetworkId (ethereum/chain-keyword->chain-id :rinkeby)
:DataDir "/ethereum/rinkeby_rpc"
:UpstreamConfig {:Enabled true
:URL (str "https://rinkeby.infura.io/v3/" INFURA_TOKEN)}}}
{:id "goerli_rpc",
:etherscan-link "https://goerli.etherscan.io/address/",
:name "Goerli with upstream RPC",
:config {:NetworkId (ethereum/chain-keyword->chain-id :goerli)
:DataDir "/ethereum/goerli_rpc"
:UpstreamConfig {:Enabled true
:URL "https://goerli.blockscout.com/"}}}])
[{:id "testnet_rpc",
:chain-explorer-link "https://ropsten.etherscan.io/address/",
:name "Ropsten with upstream RPC",
:config {:NetworkId (ethereum/chain-keyword->chain-id :testnet)
:DataDir "/ethereum/testnet_rpc"
:UpstreamConfig {:Enabled true
:URL testnet-rpc-url}}}
{:id "rinkeby_rpc",
:chain-explorer-link "https://rinkeby.etherscan.io/address/",
:name "Rinkeby with upstream RPC",
:config {:NetworkId (ethereum/chain-keyword->chain-id :rinkeby)
:DataDir "/ethereum/rinkeby_rpc"
:UpstreamConfig {:Enabled true
:URL (str "https://rinkeby.infura.io/v3/" INFURA_TOKEN)}}}
{:id "goerli_rpc",
:chain-explorer-link "https://goerli.etherscan.io/address/",
:name "Goerli with upstream RPC",
:config {:NetworkId (ethereum/chain-keyword->chain-id :goerli)
:DataDir "/ethereum/goerli_rpc"
:UpstreamConfig {:Enabled true
:URL (str "https://goerli.infura.io/v3/" INFURA_TOKEN)}}}
{:id "bsc_testnet_rpc",
:chain-explorer-link "https://testnet.bscscan.com/address/",
:name "BSC testnet",
:config {:NetworkId (ethereum/chain-keyword->chain-id :bsc-testnet)
:DataDir "/ethereum/bsc_testnet_rpc"
:UpstreamConfig {:Enabled true
:URL "https://data-seed-prebsc-1-s1.binance.org:8545/"}}}])
(def default-networks
(concat testnet-networks mainnet-networks sidechain-networks))
+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))
+29
View File
@@ -1,6 +1,7 @@
(ns status-im.utils.logging.core
(:require ["react-native-mail" :default react-native-mail]
[clojure.string :as string]
[goog.string :as gstring]
[re-frame.core :as re-frame]
[status-im.bottom-sheet.core :as bottom-sheet]
[status-im.i18n.i18n :as i18n]
@@ -227,3 +228,31 @@
cofx
(bottom-sheet/hide-bottom-sheet)
(send-logs :email))))
(re-frame/reg-fx
::open-url
(fn [url]
(.openURL ^js react/linking url)))
(def gh-issue-url "https://github.com/status-im/status-react/issues/new?labels=bug&title=%s&body=%s")
(fx/defn submit-issue
[{:keys [db]}]
(let [{:keys [steps description]}
(get db :bug-report/details)
title (or description (i18n/label :t/bug-report-description-placeholder))
body (str title
"\n\n"
(or steps (i18n/label :t/bug-report-steps-placeholder)))
url (gstring/format gh-issue-url (js/escape title) (js/escape body))]
{::open-url url}))
(fx/defn submit-gh-issue
{:events [:logging/submit-gh-issue]}
[{:keys [db] :as cofx} details steps]
(fx/merge
cofx
{:db (dissoc db :bug-report/details)}
(bottom-sheet/hide-bottom-sheet)
(submit-issue)))
+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 -12
View File
@@ -439,9 +439,7 @@
(let [{:keys [slow-base-fee normal-base-fee fast-base-fee
current-base-fee max-priority-fee]}
price
max-priority-fee-bn (money/with-precision
(signing.gas/get-suggested-tip max-priority-fee)
0)
max-priority-fee-bn (money/with-precision (signing.gas/get-suggested-tip max-priority-fee) 0)
fee-cap (-> normal-base-fee
money/bignumber
(money/add max-priority-fee-bn)
@@ -769,12 +767,15 @@
{:keys [force-restart? on-recent-history-fetching]
:as params}]
(when (:multiaccount db)
(let [syncing-allowed? (mobile-network-utils/syncing-allowed? cofx)]
(let [syncing-allowed? (mobile-network-utils/syncing-allowed? cofx)
binance-chain? (ethereum/binance-chain? db)]
(log/info "restart-wallet-service"
"force-restart?" force-restart?
"syncing-allowed?" syncing-allowed?)
(if (or syncing-allowed?
force-restart?)
"syncing-allowed?" syncing-allowed?
"binance-chain?" binance-chain?)
(if (and (or syncing-allowed?
force-restart?)
(not binance-chain?))
(check-recent-history cofx params)
(after-checking-history cofx)))))
@@ -794,6 +795,27 @@
cofx
{:force-restart? force-restart?}))
(re-frame/reg-fx
:load-transaction-by-hash
(fn [[address hash]]
(log/info "calling wallet_loadTransferByHash" address hash)
(json-rpc/call
{:method "wallet_loadTransferByHash"
:params [address hash]
:on-success #(re-frame/dispatch [:transaction/get-fetched-transfers])
:on-error #(log/warn "Transfer loading failed" %)})))
(fx/defn load-transaction-by-hash
[_ address hash]
{:load-transaction-by-hash [address hash]})
(fx/defn transaction-included
{:events [::transaction-included]}
[{:keys [db] :as cofx} address hash]
(if (ethereum/binance-chain? db)
(load-transaction-by-hash cofx address hash)
(restart cofx true)))
(def pull-to-refresh-cooldown-period (* 1 60 1000))
(fx/defn restart-on-pull
@@ -815,17 +837,17 @@
::start-watching
(fn [hashes]
(log/info "[wallet] watch transactions" hashes)
(doseq [hash hashes]
(doseq [[address hash] hashes]
(json-rpc/call
{:method "wallet_watchTransaction"
:params [hash]
:on-success #(re-frame.core/dispatch [::restart true])
:on-success #(re-frame.core/dispatch [::transaction-included address hash])
:on-error #(log/info "[wallet] watch transaction error" % "hash" hash)}))))
(fx/defn watch-tx
{:events [:watch-tx]}
[{:keys [db] :as cofx} tx-id]
{::start-watching [tx-id]})
[{:keys [db] :as cofx} address tx-id]
{::start-watching [[address tx-id]]})
(fx/defn watch-transsactions
[_ hashes]
@@ -1022,7 +1044,7 @@
db)))
db
(map (partial normalize-transaction db) raw-transactions))
::start-watching (map :hash raw-transactions)})
::start-watching (map (juxt :from :hash) raw-transactions)})
(re-frame/reg-fx
:wallet/delete-pending-transactions
@@ -1039,3 +1061,20 @@
[{:keys [db]} enabled?]
{::async-storage/set! {:transactions-management-enabled? enabled?}
:db (assoc db :wallet/transactions-management-enabled? enabled?)})
(fx/defn update-curent-block
{:events [::update-current-block]}
[{:keys [db]} block]
{:db (assoc db :ethereum/current-block block)})
(re-frame/reg-fx
::request-current-block-update
(fn []
(json-rpc/call
{:method "eth_getBlockByNumber"
:params ["latest" false]
:on-success #(re-frame/dispatch [::update-current-block (get % :number)])})))
(fx/defn request-current-block-update
[_]
{::request-current-block-update nil})
@@ -19,6 +19,7 @@
{:contract contract
:method "decimals()"
:outputs ["uint256"]
:on-error #(re-frame/dispatch [:wallet.custom-token/not-supported])
:on-success
(fn [[contract-decimals]]
(re-frame/dispatch [:wallet.custom-token/decimals-result
@@ -31,6 +32,7 @@
{:contract contract
:method "symbol()"
:outputs ["string"]
:on-error #(re-frame/dispatch [:wallet.custom-token/not-supported])
:on-success
(fn [[contract-symbol]]
(re-frame/dispatch [:wallet.custom-token/symbol-result
@@ -58,6 +60,7 @@
{:contract contract
:method "name()"
:outputs ["string"]
:on-error #(re-frame/dispatch [:wallet.custom-token/not-supported])
:on-success
(fn [[contract-name]]
(re-frame/dispatch [:wallet.custom-token/name-result
@@ -149,6 +152,12 @@
(update db :wallet/custom-token-screen merge {:name token-name})
:wallet.custom-token/get-symbol contract})
(fx/defn not-supported
{:events [:wallet.custom-token/not-supported]}
[{:keys [db]}]
{:db (assoc-in db [:wallet/custom-token-screen :in-progress?] nil)
:utils/show-popup {:content (i18n/label :t/contract-isnt-supported)}})
(fx/defn balance-result
{:events [:wallet.custom-token/balance-result]}
[{:keys [db]} contract balance]
+5 -5
View File
@@ -4,11 +4,11 @@
(deftest get-transaction-details-url
(is (= "https://etherscan.io/tx/asdfasdf"
(transactions/get-transaction-details-url :mainnet "asdfasdf")))
(transactions/get-transaction-details-url 1 "asdfasdf")))
(is (= "https://rinkeby.etherscan.io/tx/asdfasdfg"
(transactions/get-transaction-details-url :rinkeby "asdfasdfg")))
(transactions/get-transaction-details-url 4 "asdfasdfg")))
(is (= "https://ropsten.etherscan.io/tx/asdfasdfgg"
(transactions/get-transaction-details-url :testnet "asdfasdfgg")))
(is (nil? (transactions/get-transaction-details-url :not-a-net "asdfasdfg")))
(transactions/get-transaction-details-url 3 "asdfasdfgg")))
(is (nil? (transactions/get-transaction-details-url 7787878 "asdfasdfg")))
(is (thrown? js/Error (transactions/get-transaction-details-url nil "asdfasdfg")))
(is (thrown? js/Error (transactions/get-transaction-details-url :asdf 1))))
(is (thrown? js/Error (transactions/get-transaction-details-url 676868 1))))
+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.2",
"commit-sha1": "b4c4cf62418e485905feafd44f038422c1862ebb",
"src-sha256": "0d6ihvgd99rmxs21g1bdhxs6yxlgvpnkvja2kx3vr0zwcynay6jh"
"version": "v0.92.5",
"commit-sha1": "64589fbd1f81022c7f995a7d993f0c026ecc6923",
"src-sha256": "0d4zh9kmgi0vwgn1wsbjfnp7hiv2m0l1472wkjg23snskz2g5g14"
}
+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
+7 -7
View File
@@ -1,23 +1,23 @@
aiohttp==3.7.4
aiohttp==3.8.0
allpairspy==2.5.0
apipkg==1.5
Appium-Python-Client==1.0.2
async-timeout==3.0.1
async-timeout==4.0.1
certifi==2020.11.8
chardet==3.0.4
cycler==0.10.0
cytoolz==0.11.0
emoji==0.5.0
eth-hash==0.2.0
eth-hash==0.3.2
eth-keys==0.3.3
eth-utils==1.9.5
eth-utils==1.10.0
ethereum==2.3.2
execnet==1.7.1
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
+2 -2
View File
@@ -12,14 +12,14 @@ RERUN_ERRORS = [
'failed to start the browser or device',
'ERROR The test with session id',
"503 Service Unavailable",
"object has no attribute",
# "object has no attribute",
"[Errno 104] Connection reset by peer",
"Sauce could not start your job",
"HTTP Error 303",
"http.client.RemoteDisconnected: Remote end closed connection without response",
"[Errno 110] Connection timed out",
"replacement transaction underpriced",
"StaleElementReferenceException",
# "StaleElementReferenceException",
"'GetStartedButton' is not found on the screen",
"'AccessKeyButton' is not found on the screen",
"'SignInPhraseText' is not found on the screen"
+9 -2
View File
@@ -7,6 +7,7 @@ import base64
from os import environ
from support.base_test_report import BaseTestReport
from sys import argv
from json import JSONDecodeError
class TestrailReport(BaseTestReport):
@@ -49,7 +50,10 @@ class TestrailReport(BaseTestReport):
result = requests.post(self.api_url + method, headers={'Authorization': self.headers['Authorization']},
files=files)
files['attachment'].close()
return result.json()
try:
return result.json()
except JSONDecodeError:
pass
def get_suites(self):
return self.get('get_suites/%s' % self.project_id)
@@ -130,7 +134,10 @@ class TestrailReport(BaseTestReport):
'comment': '%s' % ('# Error: \n %s \n' % emoji.demojize(
last_testrun.error)) + devices + test_steps if last_testrun.error
else devices + test_steps}
result_id = self.post(method, data=data)['id']
try:
result_id = self.post(method, data=data)['id']
except KeyError:
result_id = ''
if last_testrun.error:
for geth in test.geth_paths.keys():
self.add_attachment(method='add_attachment_to_result/%s' % str(result_id),
@@ -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):
@@ -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()
@@ -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()
@@ -61,7 +61,7 @@ class TestProfileSingleDevice(SingleDeviceTestCase):
@marks.testrail_id(695890)
@marks.medium
def test_can_use_another_fleets_and_networks_advanced(self):
def test_can_use_another_fleets_and_networks_advanced_set_nonce(self):
user = user_mainnet
sign_in = SignInView(self.driver)
home = sign_in.recover_access(user['passphrase'])
@@ -74,18 +74,21 @@ class TestProfileSingleDevice(SingleDeviceTestCase):
profile.waku_bloom_toggle.click()
sign_in.sign_in()
# home.just_fyi("Check tx management")
# TODO: blocked due to 12827
# wallet = home.wallet_button.click()
# send_tx = wallet.send_transaction_from_main_screen.click()
# from views.send_transaction_view import SendTransactionView
# send_tx = SendTransactionView(self.driver)
# send_tx.amount_edit_box.set_value('0')
# send_tx.set_recipient_address(ens_user['address'])
# send_tx.next_button.click()
# send_tx.advanced_button.click()
# send_tx.nonce_input.set_value('4')
# send_tx.nonce_save_button.click()
home.just_fyi("Check tx management")
wallet = home.wallet_button.click()
send_tx = wallet.send_transaction_from_main_screen.click()
from views.send_transaction_view import SendTransactionView
send_tx = SendTransactionView(self.driver)
send_tx.amount_edit_box.set_value('0')
send_tx.set_recipient_address(ens_user['address'])
send_tx.next_button.click()
send_tx.set_up_wallet_when_sending_tx()
send_tx.advanced_button.click()
send_tx.nonce_input.set_value('4')
send_tx.nonce_save_button.click()
error_text = send_tx.sign_transaction(error=True)
if error_text != 'nonce too low':
self.errors.append("%s is not expected error when signing tx with custom nonce" % error_text)
home.just_fyi("Check balance on mainnet")
profile = home.profile_button.click()
@@ -751,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)
@@ -814,7 +817,7 @@ class TestProfileMultipleDevice(MultipleDeviceTestCase):
profile_1.bootnodes_button.click()
profile_1.add_bootnode_button.click()
profile_1.specify_name_input.set_value('test')
# TODO: blocked as validation is missing for bootnodes
# TODO: blocked as validation is missing for bootnodes (rechecked 23.11.21, valid)
# profile_1.bootnode_address_input.set_value('invalid_bootnode_address')
# if not profile_1.element_by_text_part('Invalid format').is_element_displayed():
# self.errors.append('Validation message about invalid format of bootnode is not shown')
@@ -972,7 +975,7 @@ class TestProfileMultipleDevice(MultipleDeviceTestCase):
public_chat_1 = home_1.join_public_chat(public_chat_name)
public_chat_1.relogin()
# TODO: blocked due to 11786
# TODO: blocked due to 11786 (rechecked 23.11.21, valid)
# profile_1.just_fyi('check that still connected to custom mailserver after relogin')
# home_1.profile_button.click()
# profile_1.sync_settings_button.click()
@@ -992,7 +995,7 @@ class TestProfileMultipleDevice(MultipleDeviceTestCase):
# profile_1.home_button.click()
# home_1.get_chat('#%s' % public_chat_name).click()
# if not public_chat_1.chat_element_by_text(message).is_element_displayed(60):
# self.errors.append("Chat history wasn't fetched")
# self.errors.append("Chat history wasn't fetched")
self.errors.verify_no_errors()
@@ -1033,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()
@@ -1174,24 +1177,21 @@ class TestProfileMultipleDevice(MultipleDeviceTestCase):
home_2.just_fyi('set mobile data to "ON"')
home_2.home_button.click()
home_2.mobile_connection_off_icon.click()
home_2.use_mobile_data_switch.click()
home_2.use_mobile_data_switch.wait_and_click(30)
if not home_2.connected_to_node_text.is_element_displayed(10):
self.errors.append("Not connected to history node after enabling fetching on mobile data")
home_2.click_system_back_button()
home_2.mobile_connection_on_icon.wait_for_visibility_of_element(10)
if not home_2.mobile_connection_on_icon.is_element_displayed():
self.errors.append('No mobile connection ON icon is shown')
home_2.get_chat('#%s' % public_chat_name).click()
if not public_2.chat_element_by_text(public_chat_message).is_element_displayed(180):
self.errors.append("Chat history was not fetched with mobile data fetching ON")
home_2.just_fyi('check redirect to sync settings by tappin "Sync" in connection status bottom sheet')
home_2.just_fyi('check redirect to sync settings by tapping on "Sync" in connection status bottom sheet')
home_2.home_button.click()
home_2.mobile_connection_on_icon.click()
home_2.connection_settings_button.click()
if not home_2.element_by_translation_id("mobile-network-use-mobile").is_element_displayed():
self.errors.append(
"Was not redirected to sync settings after tapping on Settings in connection bottom sheet")
self.errors.append("Was not redirected to sync settings after tapping on Settings in connection bottom sheet")
home_1.just_fyi("Check default preferences in Sync settings")
profile_1 = home_1.profile_button.click()
@@ -1217,78 +1217,106 @@ class TestProfileMultipleDevice(MultipleDeviceTestCase):
self.errors.verify_no_errors()
@marks.testrail_id(5680)
@marks.high
@marks.skip
# TODO: skip until edit userpic is enabled back
def test_pair_devices_sync_name_photo_public_group_chats(self):
@marks.testrail_id(695856)
@marks.medium
@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])
home_1 = device_1.create_user()
home_1.profile_button.click()
profile_1 = home_1.get_profile_view()
profile_1 = home_1.profile_button.click()
profile_1.privacy_and_security_button.click()
profile_1.backup_recovery_phrase_button.click()
recovery_phrase = profile_1.backup_recovery_phrase()
profile_1.back_button.click()
profile_1.get_back_to_home_view()
device_1_name = 'device_%s' % device_1.driver.number
device_2_name = 'device_%s' % device_2.driver.number
public_chat_before_sync_name = 'b-public-%s' % home_1.get_random_chat_name()
public_chat_after_sync_name = 'a-public-%s' % home_1.get_random_chat_name()
profile_1.home_button.double_click()
name_1, name_2 = 'device_%s' % device_1.driver.number, 'device_%s' % device_2.driver.number
comm_before_sync_name, channel, message = 'b-%s' % home_1.get_random_chat_name(), 'some-rand-chann', 'comm_message'
comm_joined_name = 'Status'
comm_after_sync_name = 'a-public-%s' % home_1.get_random_chat_name()
group_chat_name = 'group-%s' % home_1.get_random_chat_name()
message_after_sync = 'sent after sync'
channel_after_sync, message_after_sync = 'chann-after-sync', 'sent after sync'
device_1.just_fyi('join public chat, create group chat, edit user picture')
public_1 = home_1.join_public_chat(public_chat_before_sync_name)
public_1.back_button.click()
device_1.just_fyi('join Status community, create community, create group chat, edit user picture')
# Follow Status community
home_1.element_by_text(comm_joined_name).scroll_and_click()
from views.chat_view import CommunityView
comm_to_join_1 = CommunityView(self.drivers[0])
comm_to_join_1.follow_button.wait_and_click()
comm_to_join_1.home_button.double_click()
# Create community as admin, add channel, send message
comm_before_1 = home_1.create_community(comm_before_sync_name)
channel_before_1 = comm_before_1.add_channel(channel)
channel_before_1.send_message(message)
home_1.home_button.double_click()
# Starting group chat
one_to_one_1 = home_1.add_contact(basic_user['public_key'])
one_to_one_1.back_button.click()
one_to_one_1.home_button.click()
group_chat_1 = home_1.create_group_chat([basic_user['username']], group_chat_name)
group_chat_1.back_button.click()
group_chat_1.home_button.click()
# Editing profile picture
home_1.profile_button.click()
profile_1 = home_1.get_profile_view()
profile_1.edit_profile_picture('sauce_logo.png')
device_2.just_fyi('go to profile > Devices, set device name, discover device 2 to device 1')
home_2 = device_2.recover_access(passphrase=' '.join(recovery_phrase.values()))
profile_2 = home_2.get_profile_view()
profile_2.discover_and_advertise_device(device_2_name)
profile_2 = home_2.profile_button.click()
device_1.just_fyi('enable pairing of device 2 and sync')
profile_1.discover_and_advertise_device(device_1_name)
profile_1.get_toggle_device_by_name(device_2_name).click()
device_2.just_fyi('Pair main and secondary devices')
profile_2.discover_and_advertise_device(name_2)
profile_1.discover_and_advertise_device(name_1)
profile_1.get_toggle_device_by_name(name_2).wait_and_click()
profile_1.sync_all_button.click()
profile_1.sync_all_button.wait_for_visibility_of_element(15)
[device.profile_button.click() for device in (profile_1, profile_2)]
device_2.just_fyi('check that public chat and profile details are updated')
device_2.just_fyi('check that created/joined community and profile details are updated')
home_2 = profile_2.home_button.click()
if not home_2.element_by_text('#%s' % public_chat_before_sync_name).is_element_displayed():
self.errors.append('Public chat "%s" doesn\'t appear after initial sync'
% public_chat_before_sync_name)
home_2.home_button.click()
home_2.profile_button.click()
if not profile_2.profile_picture.is_element_image_equals_template('sauce_logo_profile.png'):
self.errors.append('Profile picture was not updated after initial sync')
profile_2.home_button.click()
for community in (comm_before_sync_name, comm_joined_name):
if not home_2.get_chat(community, community=True).is_element_displayed():
self.errors.append('Community %s was not appeared after initial sync' % community)
comm_before_2 = home_2.get_chat(comm_before_sync_name, community=True).click()
channel_2 = comm_before_2.get_chat(channel).click()
if not channel_2.chat_element_by_text(message).is_element_displayed(30):
self.errors.append("Message sent to community channel before sync is not shown!")
device_1.just_fyi('send message to group chat, and join to new public chat')
device_1.just_fyi("Send message, add new channel and check it will be synced")
home_1.home_button.click()
home_1.get_chat(comm_before_sync_name, community=True).click()
channel_1 = comm_before_1.get_chat(channel).click()
channel_1.send_message(message_after_sync)
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!")
device_1.just_fyi("Leave community and check it will be synced")
[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):
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):
self.errors.append('Added community was not appeared after initial sync')
# TODO: skip until #11558 (rechecked 23.11.21, valid)
# home_2.profile_button.click()
# if not profile_2.profile_picture.is_element_image_equals_template('sauce_logo_profile.png'):
# self.errors.append('Profile picture was not updated after initial sync')
# profile_2.home_button.click()
#
device_1.just_fyi('send message to group chat, check that message in group chat is shown')
home_1 = profile_1.home_button.click()
public_1 = home_1.join_public_chat(public_chat_after_sync_name)
public_1.back_button.click()
home_1.element_by_text(group_chat_name).click()
home_1.get_chat(group_chat_name).click()
group_chat_1.send_message(message_after_sync)
group_chat_1.back_button.click()
device_2.just_fyi('check that message in group chat is shown, public chats are synced')
if not home_2.element_by_text('#%s' % public_chat_after_sync_name).is_element_displayed():
self.errors.append('Public chat "%s" doesn\'t appear on other device when devices are paired'
% public_chat_before_sync_name)
home_2.element_by_text(group_chat_name).click()
device_2_group_chat = home_2.get_chat_view()
if not device_2_group_chat.chat_element_by_text(message_after_sync).is_element_displayed():
group_chat_2 = home_2.get_chat(group_chat_name).click()
if not group_chat_2.chat_element_by_text(message_after_sync).is_element_displayed():
self.errors.append('"%s" message in group chat is not synced' % message_after_sync)
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(
@@ -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)
@@ -92,7 +92,7 @@ class TestCommandsMultipleDevices(MultipleDeviceTestCase):
[message.transaction_status.wait_for_element_text(message.confirmed, 60) for message in
(sender_message, receiver_message)]
# TODO: should be added PNs for receiver after getting more stable feature
# TODO: should be added PNs for receiver after getting more stable feature (rechecked 23.11.21, valid)
self.errors.verify_no_errors()
@marks.testrail_id(6263)
@@ -33,8 +33,7 @@ class TestCommunitiesMultipleDevices(MultipleDeviceTestCase):
home_2.just_fyi("Tapping on community link and request membership")
pub_2 = home_2.join_public_chat(pub_chat_name)
# TODO: due to #12271
pub_2.element_by_text(community_name).wait_for_element(330)
pub_2.element_by_text(community_name).wait_for_element(100)
community_message_2 = pub_2.get_community_link_preview_by_text(community_link_text)
if community_message_2.community_description != community_description:
self.errors.append(
@@ -64,9 +63,8 @@ class TestCommunitiesMultipleDevices(MultipleDeviceTestCase):
if not community_2.community_info_picture.is_element_image_similar_to_template(community_pic):
self.errors.append("Community image is different!")
channel_2 = community_2.get_chat(channel_name).click()
# TODO: due to 12281
# if not channel_2.chat_element_by_text(message).is_element_displayed():
# self.errors.append("Message was not received in community channel!")
if not channel_2.chat_element_by_text(message).is_element_displayed(30):
self.errors.append("Message was not received in community channel!")
channel_2.send_message(message_member)
community_1.home_button.double_click()
home_1.get_chat(community_name, community=True).click()
@@ -113,11 +111,9 @@ class TestCommunitiesMultipleDevices(MultipleDeviceTestCase):
pub_1.get_back_to_home_view()
home_2.just_fyi("Tapping on community link and request membership")
# TODO: due to #12271
pub_2.element_by_text(community_name).wait_for_element(330)
pub_2.element_by_text(community_name).wait_for_element(60)
community_message_2 = pub_2.get_community_link_preview_by_text(community_link_text)
community_message_2.view()
community_2 = CommunityView(self.drivers[1])
community_2 = community_message_2.view()
community_2.request_access_button.click()
if not community_2.membership_request_pending_text.is_element_displayed():
self.errors.append("Membership request is not pending")
@@ -35,20 +35,19 @@ 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.drivers[0].fail('No icon is shown for PN for group invite')
if not group_invite_pn.group_chat_icon.is_element_displayed(30):
self.errors.append('No icon is shown for PN for group invite')
group_invite_pn.click()
create_system_message = chat_1.create_system_message(username_1, chat_name)
invite_system_message = chat_1.invite_system_message(username_1, username_2)
@@ -99,8 +98,7 @@ class TestGroupChatMultipleDevice(MultipleDeviceTestCase):
@marks.testrail_id(3997)
@marks.medium
def test_leave_group_chat_via_group_info(self):
def test_leave_group_chat_highligted_via_group_info(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()
@@ -108,12 +106,23 @@ class TestGroupChatMultipleDevice(MultipleDeviceTestCase):
device_2.just_fyi('Create and join group chat')
public_key_2, username_2 = device_2.get_public_key_and_username(True)
device_2.home_button.click()
public_key_1 = home_1.get_public_key_and_username()
[home.home_button.click() for home in (home_1, home_2)]
home_2.add_contact(public_key_1)
home_1.add_contact(public_key_2)
home_1.get_back_to_home_view()
[home.home_button.click() for home in (home_1, home_2)]
chat_1 = home_1.create_group_chat([username_2], chat_name)
home_2.just_fyi("Check that new group chat from contact is highlited")
chat_2_element = home_2.get_chat(chat_name)
if chat_2_element.no_message_preview.is_element_differs_from_template('highligted_preview_group.png', 0):
self.errors.append("Preview message is not hightligted or text is not shown! ")
left_system_message = chat_1.leave_system_message(username_2)
chat_2 = home_2.get_chat(chat_name).click()
chat_2.home_button.click()
if not chat_2_element.no_message_preview.is_element_differs_from_template('highligted_preview_group.png', 0):
self.errors.append("Preview message is still hightligted after opening! ")
home_2.get_chat(chat_name).click()
chat_2.join_chat_button.click()
device_2.just_fyi('Send several message and leave chat')
@@ -245,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()
@@ -140,10 +140,9 @@ class TestMessagesOneToOneChatMultiple(MultipleDeviceTestCase):
device_1_home.profile_button.click()
default_username_1 = profile_1.default_username_text.text
profile_1.home_button.double_click()
# TODO: Skip until edit-profile feature returned
# profile_1 = device_1_home.profile_button.click()
# profile_1.edit_profile_picture('sauce_logo.png')
# profile_1.home_button.click()
profile_1 = device_1_home.profile_button.click()
profile_1.edit_profile_picture('sauce_logo.png')
profile_1.home_button.click()
device_2_public_key, default_username_2 = device_2_home.get_public_key_and_username(return_username=True)
device_2_home.home_button.click()
@@ -188,9 +187,8 @@ class TestMessagesOneToOneChatMultiple(MultipleDeviceTestCase):
device_1_chat.chat_options.click()
device_1_chat.view_profile_button.click()
# TODO: skip until edit-profile feature returned
# if not device_2_chat.contact_profile_picture.is_element_image_equals_template('sauce_logo.png'):
# self.errors.append("Updated profile picture is not shown in one-to-one chat")
if not device_2_chat.contact_profile_picture.is_element_image_equals_template('sauce_logo_profile_2.png'):
self.errors.append("Updated profile picture is not shown in one-to-one chat")
self.errors.verify_no_errors()
@marks.testrail_id(695843)
@@ -210,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(
@@ -225,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")
@@ -240,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")
@@ -260,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()
@@ -661,7 +659,7 @@ class TestMessagesOneToOneChatMultiple(MultipleDeviceTestCase):
@marks.testrail_id(5362)
@marks.medium
def test_unread_messages_counter_1_1_chat(self):
def test_unread_messages_counter_preview_highlited_1_1_chat(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()
@@ -672,7 +670,7 @@ class TestMessagesOneToOneChatMultiple(MultipleDeviceTestCase):
home_1.home_button.click()
chat_2 = home_2.add_contact(public_key_1)
message, message_2 = 'test message', 'test message2'
message, message_2, message_3 = 'test message', 'test message2', 'test'
chat_2.send_message(message)
chat_element = home_1.get_chat(default_username_2)
home_1.dapp_tab_button.click()
@@ -683,13 +681,26 @@ class TestMessagesOneToOneChatMultiple(MultipleDeviceTestCase):
device_1.home_button.click()
if chat_element.new_messages_counter.text != '1':
self.errors.append('New messages counter is not shown on chat element')
chat_element.click()
chat_1 = chat_element.click()
chat_1.add_to_contacts.click()
home_1.home_button.double_click()
if home_1.home_button.counter.is_element_displayed():
self.errors.append('New messages counter is shown on Home button for already seen message')
if chat_element.new_messages_counter.text == '1':
self.errors.append('New messages counter is shown on chat element for already seen message')
home_1.delete_chat_long_press(default_username_2)
home_1.just_fyi("Checking preview of message and chat highlighting")
chat_2.send_message(message_3)
chat_1_element = home_1.get_chat(default_username_2)
if chat_1_element.chat_preview.is_element_differs_from_template('highligted_preview.png', 0):
self.errors.append("Preview message is not hightligted or text is not shown! ")
home_1.get_chat(default_username_2).click()
home_1.home_button.double_click()
if not home_1.get_chat(default_username_2).chat_preview.is_element_differs_from_template('highligted_preview.png', 0):
self.errors.append("Preview message is still highlighted after opening ")
self.errors.verify_no_errors()
@marks.testrail_id(6321)
@@ -757,7 +768,7 @@ class TestMessagesOneToOneChatMultiple(MultipleDeviceTestCase):
self.errors.append("Counter of reaction is not re-set on %s for message receiver!" % key)
chat_2.just_fyi("Sending Emoji/Tag/Links in chat")
# TODO: add link and tag messages after #11168 is fixed
# TODO: add link and tag messages after #11168 is fixed(rechecked 23.11.21, valid)
navigate_to_start_state_of_both_devices()
emoji_name = random.choice(list(emoji.EMOJI_UNICODE))
@@ -786,7 +797,7 @@ class TestMessagesOneToOneChatMultiple(MultipleDeviceTestCase):
@marks.testrail_id(5425)
@marks.medium
# TODO: should be completed with quoting after fix 9480
# TODO: should be completed with quoting after fix 9480 (rechecked 23.11.21, valid)
def test_markdown_support_in_messages(self):
self.create_drivers(2)
sign_in_1, sign_in_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
@@ -853,7 +864,7 @@ class TestMessagesOneToOneChatSingle(SingleDeviceTestCase):
chat = sign_in.get_chat_view()
message_text = {'text_message': 'mmmeowesage_text'}
formatted_message = {'message_with_link': 'https://status.im',
# TODO: blocked with 11161
# TODO: blocked with 11161 (rechecked 23.11.21, valid)
# 'message_with_tag': '#successishere'
}
message_input = chat.chat_message_input
@@ -1020,11 +1031,6 @@ class TestMessagesOneToOneChatSingle(SingleDeviceTestCase):
'url': 'https://join.status.im/u/%sinvalid' % ens_user['public_key'],
'error': 'Please enter or scan a valid chat key'
},
# TODO: comment until clarification case with scanning QR with ENS names only
# 'ens_another_domain':{
# 'url': ens_user['ens_another'],
# 'username': ens_user['username']
# },
'own_profile_key': {
'url': basic_user['public_key'],
'error': "That's you"
+45 -36
View File
@@ -1,26 +1,35 @@
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.home_view import HomeView
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])
home_1, home_2 = device_1.create_user(), device_2.create_user()
profile_1 = home_1.profile_button.click()
cls.username_1 = profile_1.default_username_text.text
profile_1.home_button.click()
home_2.home_button.click()
@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()
home_1, home_2 = HomeView(self.drivers[0]), HomeView(self.drivers[1])
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()
@@ -50,8 +59,8 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
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)
if 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)
chat_1.send_message(emoji_message)
for chat in chat_1, chat_2:
@@ -63,13 +72,11 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
@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()
home_1, home_2 = HomeView(self.drivers[0]), HomeView(self.drivers[1])
home_1.get_back_to_home_view()
home_2.get_back_to_home_view()
home_1.home_button.click()
home_2.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('пиу')
@@ -87,7 +94,7 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
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.select_mention_from_suggestion_list(self.username_1, self.username_1[:2])
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)
@@ -104,6 +111,7 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
chat_2.chat_element_by_text(message_2).wait_for_element(20)
home_2.just_fyi("Check that unread message indicator is not reappeared after relogin")
driver_2 = self.drivers[1]
driver_2.close_app()
driver_2.launch_app()
SignInView(driver_2).sign_in()
@@ -112,6 +120,9 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
self.errors.append('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):
@@ -243,17 +254,15 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
"Expected value %s is not shown in timeline_1 after adding user to contacts" % expected_value)
profile_1.just_fyi('Checking message tag and reactions on statuses')
# TODO: no way to tap into tag message from timeline_1
# tag_status = '#public-chat-to-redirect-long-name'
# timeline_1.set_new_status(tag_status)
# #timeline_2 = profile_1.get_chat_view()
# public_chat_2 = home_2.get_chat_view()
#
# public_chat_2.element_by_text(tag_status).wait_and_click()
# public_chat_2.user_name_text.wait_for_element(30)
# if not public_chat_2.user_name_text.text == tag_status[1:]:
# self.errors.append('Could not redirect a user to a public chat tapping the tag message from timeline_1')
# public_chat_2.back_button.click()
tag_status = '#public-chat-to-redirect-long-name'
timeline_1.set_new_status(tag_status)
public_chat_2 = home_2.get_chat_view()
public_chat_2.element_by_text(tag_status).wait_and_click()
public_chat_2.user_name_text.wait_for_element(30)
if not public_chat_2.user_name_text.text == tag_status:
self.errors.append('Could not redirect a user to a public chat tapping the tag message from timeline_1')
public_chat_2.back_button.click()
timeline_1.set_reaction(text_status)
status_with_reaction_1 = timeline_1.chat_element_by_text(text_status)
@@ -281,7 +290,7 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
chat_2.remove_from_contacts.click()
chat_2.close_button.click()
chat_2.status_button.click()
if timeline_2.chat_element_by_text(text_status).is_element_displayed(10):
if public_chat_2.chat_element_by_text(text_status).is_element_displayed(10):
self.errors.append("Statuses of removed user are still shown in profile")
self.errors.verify_no_errors()
@@ -244,7 +244,7 @@ class TestBrowsing(SingleDeviceTestCase):
self.errors.verify_no_errors()
# TODO: waiting mode
# TODO: waiting mode (rechecked 23.11.21, valid)
@marks.testrail_id(6300)
@marks.skip
@marks.medium
@@ -286,26 +286,25 @@ class TestBrowsing(SingleDeviceTestCase):
@marks.testrail_id(5456)
@marks.medium
@marks.skip
# Decicded to leave for manual testing, as there is no simple way of comparing images depending on phone brightness
def test_can_access_images_by_link(self):
urls = {
'https://cdn.dribbble.com/users/45534/screenshots/3142450/logo_dribbble.png':
'url_1.png',
'https://thebitcoinpub-91d3.kxcdn.com/uploads/default/original/2X/d/db97611b41a96cb7642b06636b82c0800678b140.jpg':
'url_2.png',
'url1.png',
'https://steemitimages.com/DQmYEjeBuAKVRa3b3ZqwLicSHaPUm7WFtQqohGaZdA9ghjx/images%20(4).jpeg':
'url_3.png'
'url3.png'
}
sign_in_view = SignInView(self.driver)
home_view = sign_in_view.create_user()
dapp_view = home_view.dapp_tab_button.click()
from views.web_views.base_web_view import BaseWebView
base_web = BaseWebView(self.driver)
for url in urls:
self.driver.set_clipboard_text(url)
dapp_view.enter_url_editbox.click()
dapp_view.paste_text()
dapp_view.confirm()
if not dapp_view.web_page.is_element_image_equals_template(urls[url]):
base_web.wait_for_d_aap_to_load(20)
if dapp_view.web_page.is_element_differs_from_template(urls[url], 5):
self.errors.append('Web page does not match expected template %s' % urls[url])
dapp_view.cross_icon.click()
base_web.browser_previous_page_button.click_until_presence_of_element(dapp_view.enter_url_editbox)
self.errors.verify_no_errors()
+6 -3
View File
@@ -13,6 +13,7 @@ import views.upgrade_dbs.group.data as group
class TestUpgradeApplication(SingleDeviceTestCase):
@marks.testrail_id(6284)
@marks.flaky
def test_unread_previews_public_chat_version_upgrade(self):
sign_in = SignInView(self.driver)
unread_one_to_one_name, unread_public_name = 'All Whopping Dassierat', '#before-upgrade'
@@ -38,7 +39,7 @@ class TestUpgradeApplication(SingleDeviceTestCase):
home.just_fyi("Check chat previews")
for chat in chats.keys():
if 'preview' in chats.keys():
actual_chat_preview = home.get_chat(chat).chat_preview
actual_chat_preview = home.get_chat(chat).chat_preview.text
expected_chat_preview = chats[chat]['preview']
if actual_chat_preview != expected_chat_preview:
self.errors.append('Expected preview for %s is "%s", in fact "%s"' % (chat, expected_chat_preview, actual_chat_preview))
@@ -117,8 +118,8 @@ class TestUpgradeApplication(SingleDeviceTestCase):
public_chat = home.get_chat(mention).click()
public_chat.scroll_to_start_of_history()
pub_chat_data = chats[mention]
public_chat.element_starts_with_text(pub_chat_data['reply']).scroll_to_element()
public_replied_message = public_chat.chat_element_by_text(pub_chat_data['reply'])
public_replied_message.scroll_to_element()
if pub_chat_data['long'] not in public_replied_message.replied_message_text:
self.errors.append("Reply is not present in message received in public chat %s after upgrade" % mention)
public_chat.element_starts_with_text(pub_chat_data['mention']).scroll_to_element()
@@ -477,6 +478,7 @@ class TestUpgradeMultipleApplication(MultipleDeviceTestCase):
self.errors.verify_no_errors()
@marks.testrail_id(695812)
@marks.flaky
def test_devices_activity_centre_profile_settings_upgrade(self):
self.create_drivers(2)
user = ens_user
@@ -524,7 +526,8 @@ class TestUpgradeMultipleApplication(MultipleDeviceTestCase):
if not profile_1.accept_new_chats_from_contacts_only.is_element_displayed():
self.errors.append("Accept contacts from setting is not preserved after upgrade!")
profile_1.profile_button.click()
profile_1.appearance_button.click()
profile_1.privacy_and_security_button.click()
profile_1.show_profile_pictures_of.scroll_to_element()
if not profile_1.show_profile_pictures_of.is_element_image_similar_to_template('block_dark.png'):
self.errors.append('Dark mode is not applied!')
if not profile_1.element_by_translation_id("everyone").is_element_displayed():
@@ -443,7 +443,7 @@ class TestTransactionWalletSingleDevice(SingleDeviceTestCase):
recipient = "0x" + basic_user['address']
amount = '0.000%s' % str(random.randint(10000, 99999)) + '1'
wallet.send_transaction(asset_name=symbol, amount=amount, recipient=recipient)
# TODO: disabled due to 10838
# TODO: disabled due to 10838 (rechecked 23.11.21, valid)
# transactions_view = wallet.transaction_history_button.click()
# transactions_view.transactions_table.find_transaction(amount=amount, asset=symbol)
@@ -324,7 +324,7 @@ class TestGroupChatMultipleDevice(MultipleDeviceTestCase):
message = 'after removing member'
chats[0].send_message(message)
for chat in (chats[0], chats[2]):
if not chat.chat_element_by_text(message).is_element_displayed():
if not chat.chat_element_by_text(message).is_element_displayed(30):
self.errors.append("Message '%s' was not received after removing member" % message)
if chats[1].chat_element_by_text(message).is_element_displayed():
self.errors.append("Message '%s' was received by removed member" % message)
+167 -89
View File
@@ -19,47 +19,89 @@ from tests import test_suite_data, start_threads, appium_container, pytest_confi
import base64
from re import findall
sauce_username = environ.get('SAUCE_USERNAME')
sauce_access_key = environ.get('SAUCE_ACCESS_KEY')
executor_sauce_lab = 'http://%s:%s@ondemand.saucelabs.com:80/wd/hub' % (sauce_username, sauce_access_key)
executor_local = 'http://localhost:4723/wd/hub'
implicit_wait = 5
def get_capabilities_local():
desired_caps = dict()
if pytest_config_global['docker']:
# apk is in shared volume directory
apk = '/root/shared_volume/%s' % pytest_config_global['apk']
else:
apk = pytest_config_global['apk']
desired_caps['app'] = apk
desired_caps['deviceName'] = 'nexus_5'
desired_caps['platformName'] = 'Android'
desired_caps['appiumVersion'] = '1.9.1'
desired_caps['platformVersion'] = '10.0'
desired_caps['newCommandTimeout'] = 600
desired_caps['fullReset'] = False
desired_caps['unicodeKeyboard'] = True
desired_caps['automationName'] = 'UiAutomator2'
desired_caps['setWebContentDebuggingEnabled'] = True
return desired_caps
def add_local_devices_to_capabilities():
updated_capabilities = list()
raw_out = re.split(r'[\r\\n]+', str(subprocess.check_output(['adb', 'devices'])).rstrip())
for line in raw_out[1:]:
serial = re.findall(r"(([\d.\d:]*\d+)|\bemulator-\d+)", line)
if serial:
capabilities = get_capabilities_local()
capabilities['udid'] = serial[0][0]
updated_capabilities.append(capabilities)
return updated_capabilities
def get_capabilities_sauce_lab():
desired_caps = dict()
desired_caps['app'] = 'sauce-storage:' + test_suite_data.apk_name
desired_caps['build'] = pytest_config_global['build']
desired_caps['name'] = test_suite_data.current_test.name
desired_caps['platformName'] = 'Android'
desired_caps['appiumVersion'] = '1.18.1'
desired_caps['platformVersion'] = '10.0'
desired_caps['deviceName'] = 'Android GoogleAPI Emulator'
desired_caps['deviceOrientation'] = "portrait"
desired_caps['commandTimeout'] = 600
desired_caps['idleTimeout'] = 600
desired_caps['unicodeKeyboard'] = True
desired_caps['automationName'] = 'UiAutomator2'
desired_caps['setWebContentDebuggingEnabled'] = True
desired_caps['ignoreUnimportantViews'] = False
desired_caps['enableNotificationListener'] = True
desired_caps['maxDuration'] = 1800
return desired_caps
def update_capabilities_sauce_lab(new_capabilities: dict):
caps = get_capabilities_sauce_lab().copy()
caps.update(new_capabilities)
return caps
class AbstractTestCase:
__metaclass__ = ABCMeta
@property
def sauce_username(self):
return environ.get('SAUCE_USERNAME')
@property
def sauce_access_key(self):
return environ.get('SAUCE_ACCESS_KEY')
@property
def executor_sauce_lab(self):
return 'http://%s:%s@ondemand.saucelabs.com:80/wd/hub' % (self.sauce_username, self.sauce_access_key)
@property
def executor_local(self):
return 'http://localhost:4723/wd/hub'
@staticmethod
def print_sauce_lab_info(driver):
def print_sauce_lab_info(self, driver):
sys.stdout = sys.stderr
print("SauceOnDemandSessionID=%s job-name=%s" % (driver.session_id,
pytest_config_global['build']))
@staticmethod
def get_translation_by_key(key):
def get_translation_by_key(self, key):
return transl[key]
def add_local_devices_to_capabilities(self):
updated_capabilities = list()
raw_out = re.split(r'[\r\\n]+', str(subprocess.check_output(['adb', 'devices'])).rstrip())
for line in raw_out[1:]:
serial = re.findall(r"(([\d.\d:]*\d+)|\bemulator-\d+)", line)
if serial:
capabilities = self.capabilities_local
capabilities['udid'] = serial[0][0]
updated_capabilities.append(capabilities)
return updated_capabilities
@property
def app_path(self):
app_path = '/storage/emulated/0/Android/data/im.status.ethereum.pr/files/Download/' if findall(r'pr\d\d\d\d\d',
@@ -71,53 +113,6 @@ class AbstractTestCase:
def geth_path(self):
return self.app_path + 'geth.log'
@property
def capabilities_sauce_lab(self):
desired_caps = dict()
desired_caps['app'] = 'sauce-storage:' + test_suite_data.apk_name
desired_caps['build'] = pytest_config_global['build']
desired_caps['name'] = test_suite_data.current_test.name
desired_caps['platformName'] = 'Android'
desired_caps['appiumVersion'] = '1.18.1'
desired_caps['platformVersion'] = '10.0'
desired_caps['deviceName'] = 'Android GoogleAPI Emulator'
desired_caps['deviceOrientation'] = "portrait"
desired_caps['commandTimeout'] = 600
desired_caps['idleTimeout'] = 600
desired_caps['unicodeKeyboard'] = True
desired_caps['automationName'] = 'UiAutomator2'
desired_caps['setWebContentDebuggingEnabled'] = True
desired_caps['ignoreUnimportantViews'] = False
desired_caps['enableNotificationListener'] = True
desired_caps['maxDuration'] = 1800
return desired_caps
def update_capabilities_sauce_lab(self, new_capabilities: dict):
caps = self.capabilities_sauce_lab.copy()
caps.update(new_capabilities)
return caps
@property
def capabilities_local(self):
desired_caps = dict()
if pytest_config_global['docker']:
# apk is in shared volume directory
apk = '/root/shared_volume/%s' % pytest_config_global['apk']
else:
apk = pytest_config_global['apk']
desired_caps['app'] = apk
desired_caps['deviceName'] = 'nexus_5'
desired_caps['platformName'] = 'Android'
desired_caps['appiumVersion'] = '1.9.1'
desired_caps['platformVersion'] = pytest_config_global['platform_version']
desired_caps['newCommandTimeout'] = 600
desired_caps['fullReset'] = False
desired_caps['unicodeKeyboard'] = True
desired_caps['automationName'] = 'UiAutomator2'
desired_caps['setWebContentDebuggingEnabled'] = True
return desired_caps
@abstractmethod
def setup_method(self, method):
raise NotImplementedError('Should be overridden from a child class')
@@ -130,10 +125,6 @@ class AbstractTestCase:
def environment(self):
return pytest_config_global['env']
@property
def implicitly_wait(self):
return 5
network_api = NetworkApi()
github_report = GithubHtmlReport()
@@ -197,13 +188,13 @@ class SingleDeviceTestCase(AbstractTestCase):
appium_container.start_appium_container(pytest_config_global['docker_shared_volume'])
appium_container.connect_device(pytest_config_global['device_ip'])
(executor, capabilities) = (self.executor_sauce_lab, self.capabilities_sauce_lab) if \
self.environment == 'sauce' else (self.executor_local, self.capabilities_local)
(executor, capabilities) = (executor_sauce_lab, get_capabilities_sauce_lab()) if \
self.environment == 'sauce' else (executor_local, get_capabilities_local())
for key, value in kwargs.items():
capabilities[key] = value
self.driver = Driver(executor, capabilities)
test_suite_data.current_test.testruns[-1].jobs[self.driver.session_id] = 1
self.driver.implicitly_wait(self.implicitly_wait)
self.driver.implicitly_wait(implicit_wait)
self.errors = Errors()
if pytest_config_global['docker']:
@@ -241,7 +232,7 @@ class LocalMultipleDeviceTestCase(AbstractTestCase):
def teardown_method(self, method):
for driver in self.drivers:
try:
self.add_alert_text_to_report(driver)
self.add_alert_text_to_report(self.drivers[driver])
self.drivers[driver].quit()
except WebDriverException:
pass
@@ -263,12 +254,12 @@ class SauceMultipleDeviceTestCase(AbstractTestCase):
self.drivers = self.loop.run_until_complete(start_threads(quantity,
Driver,
self.drivers,
self.executor_sauce_lab,
self.update_capabilities_sauce_lab(capabilities)))
executor_sauce_lab,
update_capabilities_sauce_lab(capabilities)))
for driver in range(quantity):
test_suite_data.current_test.testruns[-1].jobs[self.drivers[driver].session_id] = driver + 1
self.drivers[driver].implicitly_wait(
custom_implicitly_wait if custom_implicitly_wait else self.implicitly_wait)
custom_implicitly_wait if custom_implicitly_wait else implicit_wait)
def teardown_method(self, method):
geth_names, geth_contents = [], []
@@ -290,10 +281,97 @@ class SauceMultipleDeviceTestCase(AbstractTestCase):
cls.loop.close()
def create_shared_drivers(quantity):
drivers = dict()
if pytest_config_global['env'] == 'local':
capabilities = add_local_devices_to_capabilities()
for i in range(quantity):
driver = Driver(executor_local, capabilities[i])
test_suite_data.current_test.testruns[-1].jobs[driver.session_id] = i + 1
driver.implicitly_wait(implicit_wait)
drivers[i] = driver
loop = None
else:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
capabilities = {'maxDuration': 1800}
drivers = loop.run_until_complete(start_threads(quantity,
Driver,
drivers,
executor_sauce_lab,
update_capabilities_sauce_lab(capabilities)))
for i in range(quantity):
test_suite_data.current_test.testruns[-1].jobs[drivers[i].session_id] = i + 1
drivers[i].implicitly_wait(implicit_wait)
return drivers, loop
class LocalSharedMultipleDeviceTestCase(AbstractTestCase):
def setup_method(self, method):
jobs = test_suite_data.current_test.testruns[-1].jobs
if not jobs:
for index, driver in self.drivers.items():
jobs[driver.session_id] = index + 1
self.errors = Errors()
def teardown_method(self, method):
for driver in self.drivers:
try:
self.add_alert_text_to_report(self.drivers[driver])
except WebDriverException:
pass
@classmethod
def teardown_class(cls):
for driver in cls.drivers:
try:
cls.drivers[driver].quit()
except WebDriverException:
pass
class SauceSharedMultipleDeviceTestCase(AbstractTestCase):
def setup_method(self, method):
jobs = test_suite_data.current_test.testruns[-1].jobs
if not jobs:
for index, driver in self.drivers.items():
jobs[driver.session_id] = index + 1
self.errors = Errors()
def teardown_method(self, method):
geth_names, geth_contents = [], []
for driver in self.drivers:
try:
self.print_sauce_lab_info(self.drivers[driver])
self.add_alert_text_to_report(self.drivers[driver])
geth_names.append(
'%s_geth%s.log' % (test_suite_data.current_test.name, str(self.drivers[driver].number)))
geth_contents.append(self.pull_geth(self.drivers[driver]))
except (WebDriverException, AttributeError):
pass
finally:
geth = {geth_names[i]: geth_contents[i] for i in range(len(geth_names))}
self.github_report.save_test(test_suite_data.current_test, geth)
@classmethod
def teardown_class(cls):
for driver in cls.drivers:
try:
cls.drivers[driver].quit()
except WebDriverException:
pass
cls.loop.close()
if pytest_config_global['env'] == 'local':
MultipleDeviceTestCase = LocalMultipleDeviceTestCase
MultipleSharedDeviceTestCase = LocalSharedMultipleDeviceTestCase
else:
MultipleDeviceTestCase = SauceMultipleDeviceTestCase
MultipleSharedDeviceTestCase = SauceSharedMultipleDeviceTestCase
class NoDeviceTestCase(AbstractTestCase):
+2 -1
View File
@@ -282,7 +282,8 @@ def pytest_runtest_protocol(item, nextitem):
for i in range(rerun_count):
reports = runtestprotocol(item, nextitem=nextitem)
for report in reports:
if report.failed and should_rerun_test(report.longreprtext):
is_in_group = [i for i in item.iter_markers(name='xdist_group')]
if report.failed and should_rerun_test(report.longreprtext) and not is_in_group:
break # rerun
else:
return True # no need to rerun
+7 -1
View File
@@ -229,8 +229,8 @@ class BaseElement(object):
difference = ImageChops.difference(self.image, self.template)
stat = ImageStat.Stat(difference)
diff_ratio = sum(stat.mean) / (len(stat.mean) * 255)
self.driver.info('Image differs from template to %s percents' % str(diff_ratio * 100))
if diff_ratio * 100 > diff:
self.driver.info('Image differs from template to %s percents' % str(diff_ratio * 100))
result = True
return result
@@ -399,3 +399,9 @@ class SilentButton(Button):
def text(self):
text = self.find_element().text
return text
class CheckBox(Button):
def click(self):
super(CheckBox, self).click_until_presence_of_element(Button(self.driver, accessibility_id="checkbox-on"))
return self.navigate()
+18 -2
View File
@@ -13,7 +13,7 @@ from selenium.common.exceptions import NoSuchElementException, TimeoutException
from support.device_apps import start_web_browser
from tests import common_password, pytest_config_global, transl
from views.base_element import Button, BaseElement, EditBox, Text
from views.base_element import Button, BaseElement, EditBox, Text, CheckBox
class BackButton(Button):
@@ -26,6 +26,7 @@ class BackButton(Button):
return self.navigate()
class AllowButton(Button):
def __init__(self, driver):
super().__init__(driver, translation_id="allow", uppercase=True)
@@ -191,9 +192,21 @@ class AirplaneModeButton(Button):
def __init__(self, driver):
super().__init__(driver, accessibility_id="Airplane mode")
def click(self):
def open_quick_action_menu(self):
action = TouchAction(self.driver)
action.press(None, 200, 0).move_to(None, 200, 300).perform()
def click(self):
counter = 0
desired_element = AirplaneModeButton(self.driver)
while not desired_element.is_element_present() and counter <= 3:
try:
self.open_quick_action_menu()
desired_element.wait_for_element(5)
except (NoSuchElementException, TimeoutException):
counter += 1
else:
self.driver.info("%s element not found" % desired_element.name)
super(AirplaneModeButton, self).click()
self.driver.press_keycode(4)
@@ -252,6 +265,9 @@ class BaseView(object):
self.qr_code_image = Button(self.driver, accessibility_id="qr-code-image")
self.sign_in_phrase = SignInPhraseText(self.driver)
# checkboxes and toggles
self.checkbox_button = CheckBox(self.driver, accessibility_id="checkbox-off")
# external browser
self.open_in_status_button = OpenInStatusButton(self.driver)
+12 -2
View File
@@ -306,17 +306,21 @@ class CommunityView(HomeView):
suffix='/following-sibling::android.view.ViewGroup/android.widget.TextView')
self.members_button = Button(self.driver, translation_id="members-label")
self.community_info_picture = Button(self.driver, accessibility_id="chat-icon")
self.leave_community_button = Button(self.driver, translation_id="leave-community")
self.edit_community_button = Button(self.driver, translation_id="edit-community")
# Members
self.invite_people_button = Button(self.driver, accessibility_id="community-invite-people")
self.membership_requests_button = Button(self.driver, translation_id="membership-requests")
# Requesting access to commmunity
# Requesting access to community / joining community
self.request_access_button = Button(self.driver, translation_id="request-access")
self.membership_request_pending_text = Text(self.driver, translation_id="membership-request-pending")
self.join_button = Button(self.driver, translation_id="join")
self.follow_button = Button(self.driver, translation_id="follow")
def add_channel(self, name: str, description="Some new channel"):
self.driver.info("Adding channel")
self.driver.info("Adding channel in community")
self.plus_button.click()
self.community_create_a_channel_button.wait_and_click()
self.channel_name_edit_box.set_value(name)
@@ -326,6 +330,12 @@ class CommunityView(HomeView):
self.get_chat(name).click()
return chat_view
def leave_community(self):
self.driver.info("Leaving community")
self.community_options_button.wait_and_click()
self.community_info_button.wait_and_click()
self.leave_community_button.scroll_and_click()
def copy_community_link(self):
self.driver.info("Copy community link")
self.community_options_button.click()
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

+12 -5
View File
@@ -1,6 +1,6 @@
import time
from selenium.common.exceptions import TimeoutException, NoSuchElementException
from views.base_element import Button, Text, BaseElement, SilentButton
from views.base_element import Button, Text, BaseElement, SilentButton, CheckBox
from views.base_view import BaseView
from tests import test_dapp_url
@@ -65,7 +65,14 @@ class ChatElement(SilentButton):
def __init__(self, driver, parent_locator: str):
super().__init__(driver, xpath="%s//*[@content-desc='chat-message-text']" % parent_locator)
return PreveiewMessageText(self.driver, self.locator).text
return PreveiewMessageText(self.driver, self.locator)
@property
def no_message_preview(self):
class NoMessageText(Text):
def __init__(self, driver, parent_locator: str):
super().__init__(driver, xpath="%s//*[@content-desc='no-messages-text']" % parent_locator)
return NoMessageText(self.driver, self.locator)
@property
def new_messages_public_chat(self):
@@ -158,7 +165,7 @@ class PushNotificationElement(SilentButton):
class GroupChatIconElement(BaseElement):
def __init__(self, driver, parent_locator):
super().__init__(driver,
xpath="%s/../../../*[@resource-id='android:id/right_icon_container']" % parent_locator)
xpath="%s/../../../../*[@resource-id='android:id/right_icon_container']" % parent_locator)
return GroupChatIconElement(self.driver, self.locator)
@@ -204,8 +211,7 @@ class HomeView(BaseView):
# Sync using mobile data bottom sheet
self.continue_syncing_button = Button(self.driver, accessibility_id="mobile-network-continue-syncing")
self.stop_syncing_button = Button(self.driver, accessibility_id="mobile-network-stop-syncing")
self.remember_my_choice_checkbox = Button(self.driver, xpath="//*[@content-desc='remember-choice']"
"//*[@content-desc='checkbox']")
self.remember_my_choice_checkbox = CheckBox(self.driver, accessibility_id=":checkbox-on")
# Connection status bottom sheet
self.connected_to_n_peers_text = Text(self.driver, accessibility_id="connected-to-n-peers")
@@ -309,6 +315,7 @@ class HomeView(BaseView):
self.plus_button.click_until_presence_of_element(self.join_public_chat_button, attempts=5)
self.join_public_chat_button.wait_for_visibility_of_element(5)
chat_view = self.join_public_chat_button.click()
chat_view.chat_name_editbox.wait_for_visibility_of_element(20)
chat_view.chat_name_editbox.click()
chat_view.chat_name_editbox.send_keys(chat_name)
time.sleep(2)
+1 -1
View File
@@ -67,6 +67,6 @@ class KeycardView(BaseView):
def confirm_pin_and_proceed(self):
self.next_button.click()
self.begin_setup_button.click()
self.connect_card_button.click()
self.connect_card_button.wait_and_click()
self.enter_default_pin()
self.enter_default_pin()
+1 -1
View File
@@ -339,7 +339,7 @@ class ProfileView(BaseView):
self.advanced_button.click()
self.network_settings_button.click()
network_button = Button(self.driver, xpath="//*[@text='%s']" % network)
network_button.click()
network_button.scroll_and_click()
self.connect_button.click_until_presence_of_element(self.confirm_button)
self.confirm_button.click_until_absense_of_element(self.confirm_button)
from views.sign_in_view import SignInView
+8 -3
View File
@@ -134,7 +134,7 @@ class SendTransactionView(BaseView):
self.enter_recipient_address_input.click()
self.done_button.click_until_absense_of_element(self.done_button)
def sign_transaction(self, sender_password: str = common_password, keycard=False):
def sign_transaction(self, sender_password: str = common_password, keycard=False, error=False):
self.driver.info("Signing transaction, (keycard: %s)" % str(keycard), device=False)
if self.sign_in_phrase.is_element_displayed(30):
self.set_up_wallet_when_sending_tx()
@@ -146,10 +146,15 @@ class SendTransactionView(BaseView):
self.enter_password_input.send_keys(sender_password)
self.sign_button.click_until_absense_of_element(self.sign_button)
self.ok_button.wait_for_element(120)
if self.element_by_text_part('Transaction failed').is_element_displayed():
self.driver.fail('Transaction failed')
error_text = ''
if error:
error_text = Text(self.driver, id='android:id/message').text
else:
if self.element_by_text_part('Transaction failed').is_element_displayed():
self.driver.fail('Transaction failed')
self.driver.info("## Transaction is signed!", device=False)
self.ok_button.click()
return error_text
@staticmethod
def get_formatted_recipient_address(address):
+1 -7
View File
@@ -144,7 +144,7 @@ class SignInView(BaseView):
self.first_username_on_choose_chat_name = Text(self.driver,
xpath="//*[@content-desc='select-account-button-0']//android.widget.TextView[1]")
self.get_keycard_banner = Button(self.driver, translation_id="get-a-keycard")
self.accept_tos_checkbox = Button(self.driver, xpath="//android.widget.CheckBox[@content-desc='checkbox']")
self.accept_tos_checkbox = self.checkbox_button
# keycard recovery
self.recover_with_keycard_button = Button(self.driver, accessibility_id="recover-with-keycard-button")
@@ -178,9 +178,6 @@ class SignInView(BaseView):
if not second_user:
self.accept_tos_checkbox.click()
self.get_started_button.click_until_presence_of_element(self.generate_key_button)
if not self.generate_key_button.is_element_displayed():
[button.click() for button in
[self.accept_tos_checkbox, self.get_started_button, self.generate_key_button]]
self.generate_key_button.click()
self.next_button.click_until_absense_of_element(self.element_by_translation_id("intro-wizard-title2"))
@@ -209,9 +206,6 @@ class SignInView(BaseView):
if not second_user:
self.accept_tos_checkbox.click()
self.get_started_button.click_until_presence_of_element(self.access_key_button)
if not self.access_key_button.is_element_displayed():
self.accept_tos_checkbox.click()
self.get_started_button.click()
self.access_key_button.click()
self.enter_seed_phrase_button.click()
self.seedphrase_input.click()
+6 -6
View File
@@ -55,21 +55,21 @@ chats = {
'preview':'Request address for transaction accepted',
'messages': {
'audio': {
'length':'00:10',
'timestamp' : '1:02 PM'
'length': '00:10',
'timestamp': '1:02 PM'
},
},
'commands':{
'incoming_ETH_shared':{
'value':'0.01 ETH',
'incoming_ETH_shared': {
'value': '0.01 ETHro',
'status': "Shared 'Ethereum account'"
},
'incoming_ETH_confirmed': {
'value': '0.1 ETH',
'value': '0.1 ETHro',
'status': transl["status-confirmed"]
},
'incoming_ETH_declined': {
'value': '20 ETH',
'value': '20 ETHro',
'status': transl["transaction-declined"]
},
'incoming_STT_confirmed': {
+12 -9
View File
@@ -1,7 +1,7 @@
import time
from tests import common_password
from views.base_element import Button, Text, EditBox, SilentButton
from views.base_element import Button, Text, EditBox, SilentButton, CheckBox
from views.base_view import BaseView
@@ -14,12 +14,13 @@ class TransactionHistoryButton(Button):
return TransactionsView(self.driver)
class AssetCheckBox(SilentButton):
class AssetCheckBox(CheckBox):
def __init__(self, driver, asset_name):
super().__init__(driver, xpath="//*[@text='%s']" % asset_name)
def click(self):
self.scroll_to_element(12).click()
self.scroll_to_element(12)
super().click()
class BackupRecoveryPhrase(Button):
@@ -101,12 +102,12 @@ class WalletView(BaseView):
self.manage_assets_button = Button(self.driver, accessibility_id="wallet-manage-assets")
self.manage_accounts_button = Button(self.driver, accessibility_id="wallet-manage-accounts")
self.scan_tokens_button = Button(self.driver, accessibility_id="wallet-scan-token")
self.stt_check_box = Button(self.driver,
xpath="//*[@text='STT']/../android.view.ViewGroup[@content-desc='checkbox']")
self.stt_check_box = CheckBox(self.driver,
xpath="//*[@text='STT']/../android.view.ViewGroup[@content-desc='checkbox-off']")
self.all_assets_full_names = Text(self.driver,
xpath="//*[@content-desc='checkbox']/../android.widget.TextView[1]")
xpath="//*[@content-desc='checkbox-off']/../android.widget.TextView[1]")
self.all_assets_symbols = Button(self.driver,
xpath="//*[@content-desc='checkbox']/../android.widget.TextView[2]")
xpath="//*[@content-desc='checkbox-off']/../android.widget.TextView[2]")
self.currency_item_text = Text(self.driver, xpath="//*[@content-desc='currency-item']//android.widget.TextView")
self.address_text = Text(self.driver, accessibility_id="address-text")
@@ -170,7 +171,7 @@ class WalletView(BaseView):
self.driver.info('Waiting %s seconds for %s balance update to be equal to %s' % (
counter, asset, expected_balance))
else:
self.driver.info('**Balance for %s is equal to %s**' % (asset, expected_balance))
self.driver.info('Balance for %s is equal to %s' % (asset, expected_balance))
return
def wait_balance_is_changed(self, asset='ETH', initial_balance=0, wait_time=400, scan_tokens=False):
@@ -229,7 +230,9 @@ class WalletView(BaseView):
self.driver.info("Getting %s amount" % asset)
asset_value = SilentButton(self.driver, xpath="//android.view.ViewGroup[@content-desc=':%s-asset-value']"
"//android.widget.TextView[1]" % asset)
asset_value.scroll_to_element()
for _ in range(2):
if not asset_value.is_element_displayed():
self.element = asset_value.scroll_to_element()
try:
return float(asset_value.text.split()[0])
except ValueError:
+6 -6
View File
@@ -41,7 +41,7 @@ class BaseWebView(BaseView):
self.save_bookmark_button = Button(self.driver, accessibility_id="save-bookmark")
def wait_for_d_aap_to_load(self, wait_time=35):
self.driver.info("**Waiting %ss for dapp to load**" % wait_time)
self.driver.info("Waiting %ss for dapp to load" % wait_time)
counter = 0
while self.progress_bar_icon.is_element_present(5):
time.sleep(1)
@@ -50,7 +50,7 @@ class BaseWebView(BaseView):
self.driver.fail("Page is not loaded during %s seconds" % wait_time)
def open_in_webview(self):
self.driver.info("**Opening in webview**")
self.driver.info("Opening in webview")
if self.web_view_browser.is_element_displayed():
self.web_view_browser.click()
if self.always_button.is_element_displayed():
@@ -59,23 +59,23 @@ class BaseWebView(BaseView):
def remove_tab(self, name='', clear_all=False):
self.open_tabs_button.click()
if clear_all:
self.driver.info("**Closing all tabs**")
self.driver.info("Closing all tabs")
self.close_all_button.click()
else:
self.driver.info("**Removing '%s' from recent websites**")
self.driver.info("Removing '%s' from recent websites")
close_button = Button(self.driver,
xpath="//*[contains(@text, '%s')]/../../../../*[@content-desc='empty-tab']" % name)
close_button.scroll_to_element()
close_button.click()
def edit_bookmark_name(self, name):
self.driver.info("**Editing bookmark name to '%s'**" % name)
self.driver.info("Editing bookmark name to '%s'" % name)
self.bookmark_name_input.clear()
self.bookmark_name_input.send_keys(name)
self.save_bookmark_button.click()
def add_to_bookmarks(self, name=''):
self.driver.info("**Adding '%s' to bookmarks**" % name)
self.driver.info("Adding '%s' to bookmarks" % name)
self.options_button.click()
self.add_remove_favorites_button.click()
if name:
+31 -3
View File
@@ -111,7 +111,12 @@
"back-up": "دعم",
"back-up-seed-phrase": "النسخ الاحتياطي لعبارة \"Seed \"",
"back-up-your-seed-phrase": "النسخ الاحتياطي للعبارة الأولية الخاصة بك",
"backing-up": "النسخة الإحتياطية...",
"backup-disabled": "معطل",
"backup-enabled": "تمكين",
"backup-recovery-phrase": "النسخ الاحتياطي للعبارة الأولية",
"backup-settings": "إعدادات النسخ الاحتياطي",
"backup-through-waku": "النسخ الاحتياطي من خلال واكو",
"bad-fees-description": "رسوم الأولوية الخاصة بك أقل من المعايير المقترحة لدينا.",
"balance": "توازن",
"begin-set-up": "ابدأ الإعداد",
@@ -165,6 +170,14 @@
"browsing-site-blocked-go-back": "الرجوع للخلف",
"browsing-site-blocked-title": "هذا الموقع محظور",
"browsing-title": "تصفح",
"bug-report": "الإبلاغ عن خطأ",
"bug-report-description": "* وصف",
"bug-report-description-placeholder": "مطلوب ، لا يمكن أن يكون فارغًا",
"bug-report-steps": "خطوات لإعادة الإنتاج",
"bug-report-steps-placeholder": "- افتح التطبيق\n - قم بعمل ما\n - وبعد ذلك شيء آخر ...",
"bug-report-submit-email": "إرسال عن طريق البريد الإلكتروني مع سجلات الأرشيف",
"bug-report-submit-gh-issue": "إرسال مشكلة GitHub بدون سجلات",
"bug-report-too-short-description": "الوصف قصير جدًا",
"build-yourself": "لاستخدام التطبيق بدون شروط الاستخدام هذه ، يمكنك إنشاء الإصدار الخاص بك",
"buy-crypto": "شراء العملات المشفرة",
"buy-crypto-choose-a-service": "اختر الخدمة التي ترغب في استخدامها لشراء العملات المشفرة",
@@ -246,7 +259,6 @@
"community-color-placeholder": "اختر لونًا",
"community-edit-title": "تعديل المجتمع",
"community-emoji-thumbnail-title": "المجتمع-الرموز التعبيرية-الصورة المصغرة-العنوان",
"community-image-delete": "مجتمع-صورة-حذف",
"community-image-pick": "اختر صورة",
"community-image-remove": "ازاله",
"community-image-take": "التقط صورة",
@@ -308,6 +320,7 @@
"continue-anyway": "استمر على أي حال",
"contract-address": "عنوان العقد",
"contract-interaction": "عقد التفاعل",
"contract-isnt-supported": "العقد غير مدعوم",
"copy-info": "نسخ المعلومات",
"copy-qr": "نسخ الكود",
"copy-to-clipboard": "نسخ",
@@ -943,6 +956,7 @@
"keycard-unauthorized-operation": "أنت غير مصرح له بإجراء هذه العملية. \n يرجى الضغط على بطاقة صالحة والمحاولة مرة أخرى.",
"keycard-upsell-subtitle": "تعزيز الأمن والراحة",
"language": "لغة",
"last-backup-performed": "آخر عملية نسخ احتياطي تم إجراؤها:",
"learn-more": "أعرف أكثر",
"learn-more-about-keycard": "تعرف علي المزيد حول بطاقة المفاتيح",
"leave": "غادر",
@@ -1105,6 +1119,7 @@
"network-invalid-status-code": "رمز status غير صالح: {{code}}",
"network-invalid-url": "عنوان URL للشبكة غير صالح",
"network-settings": "اعدادات الشبكة",
"never": "أبدا",
"new": "الجديد",
"new-category": "فئة جديدة",
"new-chat": "دردشة جديدة",
@@ -1237,6 +1252,7 @@
"pending-invitations": "طلبات العضوية المعلقة",
"per-gas-price-limit": "حد سعر الغاز",
"per-gas-tip-limit": "حد الغاز",
"perform-backup": "إجراء النسخ الاحتياطي",
"permissions": "أذونات",
"phone-e164": "الدولية 1",
"photos": "الصور",
@@ -1348,10 +1364,11 @@
"remove-network": "أزاله شبكه الاتصال",
"remove-token": "إزالة عملة رمزية",
"removed": "إزالة",
"reorder-categories": "إعادة ترتيب الفئات",
"repeat-pin": "أدخل رمز المرور المكون من 6 أرقام",
"repeat-puk": "كرر رمز مرور الجديد المكون من 12 رقمًا",
"replying-to": "الرد على{{author}}",
"report-bug-email-template": "1. وصف المشكلة\n{{description}}\n\n2-الخطوات اللازمة لأعاده الإنتاج\n{{steps}}\n\n3. إرفاق لقطات الشاشة التي يمكن ان العرض المشكلة ، يرجى\n",
"report-bug-email-template": "1. وصف المشكلة\n {{description}}\n \n\n 2. خطوات التكاثر\n {{steps}}\n \n\n 3. أرفق لقطات الشاشة التي يمكن أن تعرض المشكلة، من فضلك \n",
"request-access": "طلب الدخول",
"request-feature": "طلب ميزة",
"request-membership": "طلب عضوية",
@@ -1430,6 +1447,7 @@
"share-contact-code": "مشاركة مفتاح الدردشة الخاص بي",
"share-dapp-text": "تحقق من هذا التطبيق الذي أستخدمه في Status: {{link}}",
"share-link": "مشاركة الرابط",
"share-logs": "مشاركة السجلات",
"share-my-profile": "تعديل الملف الشخصي",
"share-profile": "مشاركة الملف الشخصي",
"share-profile-link": "مشاركة رابط الملف الشخصي",
@@ -1472,6 +1490,7 @@
"specify-network-id": "تحديد معرف شبكه الاتصال",
"specify-rpc-url": "حدد عنوان URL لـ RPC",
"specify-server-public-key": "أدخل المفتاح العام للخادم",
"specify-symbol": "حدد رمزًا",
"start-chat": "بدء الدردشة",
"start-conversation": "بدء المحادثة",
"start-group-chat": "بدء الدردشة الجماعية",
@@ -1481,8 +1500,15 @@
"starter-pack-received": "تلقى حزمة المبتدئين",
"starter-pack-received-description": "إليك بعض من العملة الرقمية لتبدأ! استخدمها للحصول على ملصقات ، واسم ENS و تجربت dapps",
"status": "Status",
"status-always-online": "دائما متصل",
"status-automatic": "تلقائي",
"status-automatic-subtitle": "تعيين الحالة تلقائيا",
"status-confirmed": "تم تأكيد",
"status-dnd": "عدم الإزعاج",
"status-dnd-subtitle": "كتم صوت كل الإشعارات",
"status-hardwallet": "Status المحفظة الصلبة",
"status-inactive": "غير نشط",
"status-inactive-subtitle": "يخفي حالتك على الإنترنت",
"status-is-open-source": "Status مفتوحة المصدر",
"status-keycard": "بطاقة مفتاح Status",
"status-mobile-descr": "Status تميل إلى استخدام الكثير من البيانات عند مزامنة الدردشات. يمكنك اختيار عدم المزامنة عند شبكة المحمول",
@@ -1685,7 +1711,9 @@
"webview-camera-permission-requests-subtitle": "عند التمكين ، يمكن أن تطلب مواقع الويب و التطبيقات الامركزية استخدام الكاميرا",
"welcome-blank-community-message": "ستظهر مجتمعاتك هنا.",
"welcome-blank-message": "ستظهر دردشاتك هنا. لبدء دردشات جديده اضغط علي زر ⊕",
"welcome-community-blank-message": "ستظهر دردشاتك هنا. لبدء محادثات جديدة انقر على النقاط الثلاث أعلاه واختر \"إنشاء قناة\"",
"welcome-community-blank-message": "ستظهر قنواتك هنا. لإنشاء قناة جديدة، انقر فوق الزر ⊕ وحدد \"إنشاء قناة\"",
"welcome-community-blank-message-categories": "ستظهر الفئات هنا. لإنشاء فئة جديدة، عد إلى شاشة المنتدى، وانقر على زر ⊕ وحدد \"إنشاء فئة\"",
"welcome-community-blank-message-edit-chats": "ستظهر قنواتك هنا. لإنشاء قناة جديدة، ارجع إلى شاشة المجتمع، وانقر على الزر ⊕ وحدد \"إنشاء قناة\"",
"welcome-screen-text": "انشاء محفظتك ، دعوة الأصدقاء للدردشة\nو استعرض شعبية dapps!",
"welcome-to-status": "مرحبًا بك في Status!",
"welcome-to-status-description": "قم بإعداد محفظة التشفير الخاصة بك ، ودعوة الأصدقاء للدردشة وتصفح التطبيقات اللامركزية",
+3 -1
View File
@@ -107,6 +107,7 @@
"back-up": "ব্যাক আপ",
"back-up-seed-phrase": "বীজ বাক্যাংশ ব্যাক আপ করুন",
"back-up-your-seed-phrase": "আপনার বীজ বাক্যাংশ ব্যাক আপ করুন",
"backup-disabled": "অক্ষম",
"backup-recovery-phrase": "বীজ বাক্যাংশ ব্যাক আপ করুন",
"bad-fees-description": "আপনার অগ্রাধিকার ফি আমাদের প্রস্তাবিত প্যারামিটারের নিচে।",
"balance": "ভারসাম্য",
@@ -161,6 +162,7 @@
"browsing-site-blocked-go-back": "ফিরে যাও",
"browsing-site-blocked-title": "এই সাইটটি অবরুদ্ধ করা হয়েছে",
"browsing-title": "ব্রাউজ করুন",
"bug-report": "একটি বাগ রিপোর্ট করুন",
"build-yourself": "এই ব্যবহারের শর্তাবলী ছাড়া অ্যাপটি ব্যবহার করতে, আপনি আপনার নিজস্ব সংস্করণ তৈরি করতে পারেন",
"buy-crypto": "ক্রিপ্টো কিনুন",
"buy-crypto-choose-a-service": "ক্রিপ্টো কেনার জন্য আপনি যে পরিষেবাটি ব্যবহার করতে চান তা চয়ন করুন",
@@ -1285,7 +1287,7 @@
"remove-token": "টোকেন অপসারণ করুন",
"removed": "অপসারণ",
"repeat-pin": "নতুন ৬-সংখ্যার পাসকোড পুনরাবৃত্তি করুন",
"report-bug-email-template": "1. ইস্যু বিবরণ \n {{description}} \n\n\n 2. প্রজননের পদক্ষেপ \n {{steps}} \n\n\n 3. সমস্যা ডেমো করতে পারে দয়া করে এমন স্ক্রিনশট সংযুক্ত করুন।",
"report-bug-email-template": "1. ইস্যু বিবরণ (আপনি যে বৈশিষ্ট্যটি চান তা বর্ণনা করুন, অথবা সংক্ষেপে বাগ এবং আপনি কি করেছেন, আপনি কি আশা করেছিলেন, এবং আসলে কি ঘটবে তা বর্ণনা করুন। নিচের বিভাগসমূহ) 2. প্রজননের পদক্ষেপ (বর্ণনা করুন কিভাবে আমরা ধাপে ধাপে বাগ প্রতিলিপি করতে পারি। - Status খুলুন -... - ধাপ ৩, ইত্যাদি। 3. প্রত্যাশিত আচরণ (আপনি যা আশা করেছেন তা বর্ণনা করুন। 4. প্রকৃত আচরণ (আসলে কি ঘটেছে তা বর্ণনা করুন। 5. সমস্যা ডেমো করতে পারে দয়া করে এমন স্ক্রিনশট সংযুক্ত করুন।",
"request-feature": "একটি বৈশিষ্ট্য অনুরোধ",
"request-membership": "সদস্যতার জন্য অনুরোধ",
"request-pending": "মুলতুবি অনুরোধ…",

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