Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9326fc5121 | ||
|
|
2eddeb0753 | ||
|
|
2e0a594d63 | ||
|
|
6991f213af | ||
|
|
35eae4587b | ||
|
|
46fd30597a | ||
|
|
af5f979443 | ||
|
|
adfb0ddb5d | ||
|
|
0a282b3a47 | ||
|
|
5f98e96d23 | ||
|
|
823fce2457 | ||
|
|
1d5e1479f7 | ||
|
|
c2c281d9c6 | ||
|
|
1f6ebbb5e8 | ||
|
|
73983b2abd | ||
|
|
390ac858dc | ||
|
|
69c49a5557 | ||
|
|
2ed3b50663 | ||
|
|
8dd75205f3 | ||
|
|
081f1a2115 |
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.5.7'
|
||||
library 'status-jenkins-lib@v1.6.0'
|
||||
|
||||
/* Options section can't access functions in objects. */
|
||||
def isPRBuild = utils.isPRBuild()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.5.7'
|
||||
library 'status-jenkins-lib@v1.6.0'
|
||||
|
||||
pipeline {
|
||||
agent { label 'linux' }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.5.7'
|
||||
library 'status-jenkins-lib@v1.6.0'
|
||||
|
||||
/* Options section can't access functions in objects. */
|
||||
def isPRBuild = utils.isPRBuild()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.5.7'
|
||||
library 'status-jenkins-lib@v1.6.0'
|
||||
|
||||
pipeline {
|
||||
agent { label params.AGENT_LABEL }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.5.7'
|
||||
library 'status-jenkins-lib@v1.6.0'
|
||||
|
||||
/* Options section can't access functions in objects. */
|
||||
def isPRBuild = utils.isPRBuild()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.5.7'
|
||||
library 'status-jenkins-lib@v1.6.0'
|
||||
|
||||
pipeline {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.5.7'
|
||||
library 'status-jenkins-lib@v1.6.0'
|
||||
|
||||
pipeline {
|
||||
agent { label 'macos' }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.5.7'
|
||||
library 'status-jenkins-lib@v1.6.0'
|
||||
|
||||
pipeline {
|
||||
agent { label 'linux' }
|
||||
|
||||
@@ -14,14 +14,25 @@ This step will take a while the first time as it will download all dependencies.
|
||||
|
||||
There are three steps necessary to start development, in this case for Android:
|
||||
|
||||
1. `make run-clojure` - Compiles Clojure into JavaScript, watches for changes on cljs files, and hot-reloads code in the app
|
||||
2. `make run-metro` - Starts metro bundler and watches JavaScript code
|
||||
3. `make run-android` - Builds the Android app and starts it on the device
|
||||
1. `make run-clojure` - Compiles Clojure into JavaScript, watches for changes on cljs files, and hot-reloads code in the app.
|
||||
2. `make run-metro` - Starts metro bundler and watches JavaScript code.
|
||||
3. `make run-android` or `make run-ios` - Builds the Android/iOS app and starts it on the device.
|
||||
|
||||
The first two will continue watching for changes and keep re-building the app. They need to be ready first.
|
||||
The last one will exit once the app is up and ready.
|
||||
|
||||
You need to have your emulator or real devices running and visible to adb, before you run `make run-android`.
|
||||
## Simulators and Devices
|
||||
### Android
|
||||
|
||||
You need to have an emulator like [AVD](https://developer.android.com/studio/run/emulator), or [Genymotion](#genymotion-virtualization), or a real device running and visible to [adb](https://developer.android.com/studio/command-line/adb), before you run `make run-android`.
|
||||
|
||||
### iOS
|
||||
|
||||
You can specify the simulator type by adding the `SIMULATOR` flag:
|
||||
```sh
|
||||
make run-ios SIMULATOR="iPhone 11 Pro"
|
||||
```
|
||||
Some manual steps are necesary for [developing on a physical iOS Device](#physical-ios-device).
|
||||
|
||||
# Build release
|
||||
|
||||
@@ -71,3 +82,17 @@ Steps:
|
||||
2. [Setup Git to use your GPG key](https://help.github.com/en/github/authenticating-to-github/telling-git-about-your-signing-key)
|
||||
3. [Setup Git to sign commits](https://help.github.com/en/github/authenticating-to-github/signing-commits)
|
||||
4. [Setup GitHub to validate commits](https://help.github.com/en/github/authenticating-to-github/adding-a-new-gpg-key-to-your-github-account)
|
||||
|
||||
## Physical iOS Device
|
||||
|
||||
To use a physical iPhone your device UDID must be added to provisioning profiles and your Apple account invited as Developer to Status team.
|
||||
|
||||
1. [Get your UDID of your iPhone.](https://www.extentia.com/post/finding-the-udid-of-an-ios-device)
|
||||
2. Request from someone with access like @cammellos or @jakubgs to
|
||||
- Add the UDID to development devices on Apple Developer Portal.
|
||||
- Invite your Apple account to be Developer in Status team.
|
||||
3. Run a build in XCode using the project from `status-mobile/ios` directory.
|
||||
- You might see error: `Select a development team in the Signing & Capabilities editor`
|
||||
- Select `STATUS HOLDINGS PTE. LTD.` as the development team and rebuild again.
|
||||
|
||||
Once build finishes Status should start on your iPhone with its logs in terminal running `make run-metro`.
|
||||
|
||||
@@ -238,7 +238,7 @@ PODS:
|
||||
- React
|
||||
- react-native-status (1.0.0):
|
||||
- React
|
||||
- react-native-status-keycard (2.5.37):
|
||||
- react-native-status-keycard (2.5.38):
|
||||
- Keycard
|
||||
- React
|
||||
- react-native-webview (11.16.0):
|
||||
@@ -640,7 +640,7 @@ SPEC CHECKSUMS:
|
||||
FBLazyVector: 3bb422f41b18121b71783a905c10e58606f7dc3e
|
||||
FBReactNativeSpec: f2c97f2529dd79c083355182cc158c9f98f4bd6e
|
||||
Folly: b73c3869541e86821df3c387eb0af5f65addfab4
|
||||
glog: 64a900d72fb14cd1b2cdf3b717a5555ceb889d49
|
||||
glog: 36ce0530c6d2c3a5a4326885ef4069564887a1db
|
||||
HMSegmentedControl: 34c1f54d822d8308e7b24f5d901ec674dfa31352
|
||||
Keycard: ac6df4d91525c3c82635ac24d4ddd9a80aca5fc8
|
||||
libwebp: 60305b2e989864154bd9be3d772730f08fc6a59c
|
||||
@@ -671,7 +671,7 @@ SPEC CHECKSUMS:
|
||||
react-native-shake: de052eaa3eadc4a326b8ddd7ac80c06e8d84528c
|
||||
react-native-slider: 12bd76d3d568c9c5500825db54123d44b48e4ad4
|
||||
react-native-status: 21f75d492fd311dc111303da38a7a2b23a8a8466
|
||||
react-native-status-keycard: 961d01ca190889ddf220206822fd752f8f4f3f7a
|
||||
react-native-status-keycard: f60ca57d789aad6875c64ece81ab06ef0609e0d3
|
||||
react-native-webview: 28a8636d97ee641f2ee8f20492d7a6c269c1d703
|
||||
React-RCTActionSheet: 89a0ca9f4a06c1f93c26067af074ccdce0f40336
|
||||
React-RCTAnimation: 1bde3ecc0c104c55df246eda516e0deb03c4e49b
|
||||
|
||||
@@ -164,9 +164,22 @@ RCT_EXPORT_METHOD(sendLogs:(NSString *)dbJson
|
||||
NSString *networkDirPath = @"ethereum/mainnet_rpc";
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
NSString *goerliNetworkDirPath = @"ethereum/goerli_rpc_dev";
|
||||
#else
|
||||
NSString *goerliNetworkDirPath = @"ethereum/goerli_rpc";
|
||||
#endif
|
||||
|
||||
NSURL *networkDir = [rootUrl URLByAppendingPathComponent:networkDirPath];
|
||||
NSURL *originalGethLogsFile = [networkDir URLByAppendingPathComponent:@"geth.log"];
|
||||
NSURL *gethLogsFile = [logsFolderName URLByAppendingPathComponent:@"geth.log"];
|
||||
NSURL *gethLogsFile = [logsFolderName URLByAppendingPathComponent:@"mainnet_geth.log"];
|
||||
|
||||
NSURL *goerliNetworkDir = [rootUrl URLByAppendingPathComponent:goerliNetworkDirPath];
|
||||
NSURL *goerliGethLogsFile = [goerliNetworkDir URLByAppendingPathComponent:@"geth.log"];
|
||||
NSURL *goerliLogsFile = [logsFolderName URLByAppendingPathComponent:@"goerli_geth.log"];
|
||||
|
||||
NSURL *mainGethLogsFile = [rootUrl URLByAppendingPathComponent:@"geth.log"];
|
||||
NSURL *mainLogsFile = [logsFolderName URLByAppendingPathComponent:@"geth.log"];
|
||||
|
||||
[dbJson writeToFile:dbFile.path atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
||||
[jsLogs writeToFile:jsLogsFile.path atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
||||
@@ -174,6 +187,8 @@ RCT_EXPORT_METHOD(sendLogs:(NSString *)dbJson
|
||||
//NSString* gethLogs = StatusgoExportNodeLogs();
|
||||
//[gethLogs writeToFile:gethLogsFile.path atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
||||
[fileManager copyItemAtPath:originalGethLogsFile.path toPath:gethLogsFile.path error:nil];
|
||||
[fileManager copyItemAtPath:goerliGethLogsFile.path toPath:goerliLogsFile.path error:nil];
|
||||
[fileManager copyItemAtPath:mainGethLogsFile.path toPath:mainLogsFile.path error:nil];
|
||||
|
||||
[SSZipArchive createZipFileAtPath:zipFile.path withContentsOfDirectory:logsFolderName.path];
|
||||
[fileManager removeItemAtPath:logsFolderName.path error:nil];
|
||||
@@ -411,12 +426,10 @@ RCT_EXPORT_METHOD(multiAccountDeriveAddresses:(NSString *)json
|
||||
NSString *relativeDataDir = [configJSON objectForKey:@"DataDir"];
|
||||
NSString *absDataDir = [rootUrl.path stringByAppendingString:relativeDataDir];
|
||||
NSURL *absDataDirUrl = [NSURL fileURLWithPath:absDataDir];
|
||||
NSURL *dataDirUrl = [NSURL fileURLWithPath:relativeDataDir];
|
||||
NSURL *logUrl = [dataDirUrl URLByAppendingPathComponent:@"geth.log"];
|
||||
NSString *keystoreDir = [@"/keystore/" stringByAppendingString:keyUID];
|
||||
[configJSON setValue:keystoreDir forKey:@"KeyStoreDir"];
|
||||
[configJSON setValue:@"" forKey:@"LogDir"];
|
||||
[configJSON setValue:logUrl.path forKey:@"LogFile"];
|
||||
[configJSON setValue:@"geth.log" forKey:@"LogFile"];
|
||||
|
||||
NSString *resultingConfig = [configJSON bv_jsonStringWithPrettyPrint:NO];
|
||||
NSLog(@"node config %@", resultingConfig);
|
||||
@@ -426,7 +439,7 @@ RCT_EXPORT_METHOD(multiAccountDeriveAddresses:(NSString *)json
|
||||
withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
NSLog(@"logUrlPath %@ rootDir %@", logUrl.path, rootUrl.path);
|
||||
NSLog(@"logUrlPath %@ rootDir %@", @"geth.log", rootUrl.path);
|
||||
NSURL *absLogUrl = [absDataDirUrl URLByAppendingPathComponent:@"geth.log"];
|
||||
if(![fileManager fileExistsAtPath:absLogUrl.path]) {
|
||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||
|
||||
@@ -27,13 +27,11 @@
|
||||
"@walletconnect/client": "^2.0.0-beta.23",
|
||||
"@walletconnect/client-legacy": "npm:@walletconnect/client@^1.7.1",
|
||||
"bignumber.js": "git+https://github.com/status-im/bignumber.js.git#refs/tags/v4.0.2-status",
|
||||
"buffer": "^5.4.2",
|
||||
"chance": "^1.1.0",
|
||||
"create-react-class": "^15.6.2",
|
||||
"emojilib": "^2.4.0",
|
||||
"eth-phishing-detect": "^1.1.13",
|
||||
"eth-phishing-detect": "^1.2.0",
|
||||
"functional-red-black-tree": "^1.0.1",
|
||||
"hi-base32": "^0.5.0",
|
||||
"i18n-js": "^3.3.0",
|
||||
"node-libs-react-native": "^1.2.1",
|
||||
"qrcode": "^1.4.1",
|
||||
@@ -73,7 +71,6 @@
|
||||
"react-native-touch-id": "^4.4.1",
|
||||
"react-native-webview": "git+https://github.com/status-im/react-native-webview.git#refs/tags/v11.16.0-status",
|
||||
"rn-emoji-keyboard": "git+https://github.com/status-im/rn-emoji-keyboard.git#refs/tags/v0.4.3",
|
||||
"tdigest": "^0.1.1",
|
||||
"web3-utils": "^1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
|
After Width: | Height: | Size: 321 B |
|
After Width: | Height: | Size: 373 B |
|
After Width: | Height: | Size: 307 B |
|
After Width: | Height: | Size: 387 B |
|
After Width: | Height: | Size: 454 B |
|
After Width: | Height: | Size: 673 B |
|
After Width: | Height: | Size: 681 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 667 B |
|
After Width: | Height: | Size: 940 B |
|
After Width: | Height: | Size: 371 B |
|
After Width: | Height: | Size: 509 B |
|
After Width: | Height: | Size: 814 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 441 B |
|
After Width: | Height: | Size: 585 B |
|
After Width: | Height: | Size: 580 B |
|
After Width: | Height: | Size: 892 B |
|
After Width: | Height: | Size: 327 B |
|
After Width: | Height: | Size: 440 B |
|
After Width: | Height: | Size: 249 B |
|
After Width: | Height: | Size: 282 B |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
@@ -248,6 +248,8 @@
|
||||
|
||||
(def react-native-gradien #js {:default #js {}})
|
||||
|
||||
(def masked-view #js {:default #js {}})
|
||||
|
||||
(def react-native-permissions #js {:default #js {}})
|
||||
|
||||
(def push-notification-ios #js {:default #js {:abandonPermissions identity}})
|
||||
@@ -308,6 +310,7 @@
|
||||
"react-native-device-info" react-native-device-info
|
||||
"react-native-push-notification" react-native-push-notification
|
||||
"react-native-linear-gradient" react-native-gradien
|
||||
"@react-native-community/masked-view" masked-view
|
||||
"react-native-blob-util" react-native-blob-util
|
||||
"react-native-navigation" react-native-navigation
|
||||
"@react-native-community/push-notification-ios" push-notification-ios
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
:small 20})
|
||||
|
||||
(defn icon-avatar
|
||||
[{:keys [size icon color]}]
|
||||
[{:keys [size icon color opacity]
|
||||
:or {opacity 20}}]
|
||||
(let [component-size (size sizes)
|
||||
circle-color (colors/custom-color color 50 20)
|
||||
circle-color (colors/custom-color color 50 opacity)
|
||||
icon-color (colors/custom-color-by-theme color 50 60)
|
||||
icon-size (case size
|
||||
:big 20
|
||||
@@ -25,4 +26,4 @@
|
||||
:align-items :center}}
|
||||
[icons/icon icon {:container-style {:width icon-size
|
||||
:height icon-size}
|
||||
:color icon-color}]]))
|
||||
:color icon-color}]]))
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.icon :as icons]
|
||||
[clojure.string :refer [upper-case split blank?]]
|
||||
[quo.theme :refer [dark?]]
|
||||
[status-im.ui.components.react :as react]))
|
||||
[quo.theme :refer [dark?]]))
|
||||
|
||||
(def sizes {:big {:outer 80
|
||||
:inner 72
|
||||
@@ -98,47 +97,46 @@
|
||||
status-indicator?
|
||||
profile-picture
|
||||
full-name]
|
||||
:or {full-name "empty name"
|
||||
status-indicator? true
|
||||
online? true
|
||||
size :big
|
||||
ring? true}}]
|
||||
(let [initials (if full-name
|
||||
(reduce str (map first (split full-name " ")))
|
||||
"")
|
||||
:or {full-name "empty name"
|
||||
status-indicator? true
|
||||
online? true
|
||||
size :big
|
||||
ring? true}}]
|
||||
(let [initials (if full-name
|
||||
(reduce str (map first (split full-name " ")))
|
||||
"")
|
||||
first-initial-letter (if full-name
|
||||
(or (first full-name) "")
|
||||
"")
|
||||
identicon? (contains? identicon-sizes size)
|
||||
small? (contains? small-sizes size)
|
||||
using-profile-picture? (-> profile-picture
|
||||
blank?
|
||||
false?)
|
||||
outer-dimensions (get-in sizes [size :outer])
|
||||
inner-dimensions (get-in sizes [size (if ring?
|
||||
:inner
|
||||
:outer)])
|
||||
font-size (get-in sizes [size :font-size])
|
||||
icon-text (if-not (or (blank? first-initial-letter)
|
||||
(blank? initials))
|
||||
(if small?
|
||||
first-initial-letter
|
||||
initials)
|
||||
"")]
|
||||
[rn/view {:style {:width outer-dimensions
|
||||
:height outer-dimensions
|
||||
identicon? (contains? identicon-sizes size)
|
||||
small? (contains? small-sizes size)
|
||||
outer-dimensions (get-in sizes [size :outer])
|
||||
inner-dimensions (get-in sizes [size (if ring?
|
||||
:inner
|
||||
:outer)])
|
||||
font-size (get-in sizes [size :font-size])
|
||||
icon-text (if-not (or (blank? first-initial-letter)
|
||||
(blank? initials))
|
||||
(if small?
|
||||
first-initial-letter
|
||||
initials)
|
||||
"")]
|
||||
[rn/view {:style {:width outer-dimensions
|
||||
:height outer-dimensions
|
||||
:border-radius outer-dimensions}}
|
||||
(when (and ring? identicon?)
|
||||
[icons/icon :main-icons/identicon-ring {:width outer-dimensions
|
||||
:height outer-dimensions
|
||||
:size outer-dimensions
|
||||
[icons/icon :main-icons/identicon-ring {:width outer-dimensions
|
||||
:height outer-dimensions
|
||||
:size outer-dimensions
|
||||
:no-color true}])
|
||||
(if using-profile-picture?
|
||||
[react/image {:style (container-styling inner-dimensions outer-dimensions)
|
||||
:source {:uri profile-picture}}]
|
||||
(if profile-picture
|
||||
;; display image
|
||||
[rn/image {:style (container-styling inner-dimensions outer-dimensions)
|
||||
:source profile-picture}]
|
||||
;; else display initials
|
||||
[container inner-dimensions outer-dimensions
|
||||
[text/text {:weight :semi-bold
|
||||
:size font-size
|
||||
:style {:color colors/white-opa-70}}
|
||||
:size font-size
|
||||
:style {:color colors/white-opa-70}}
|
||||
(upper-case icon-text)]])
|
||||
[dot-indicator size status-indicator? online? ring? (dark?)]]))
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
(ns quo2.components.buttons.dynamic-button
|
||||
(:require [quo.react-native :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[quo2.components.icon :as icon]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.markdown.text :as text]))
|
||||
|
||||
(defn- in?
|
||||
"true if collection contains element"
|
||||
[collection element]
|
||||
(some #(= element %) collection))
|
||||
|
||||
(defn- get-button-color [type pressed? customization-color]
|
||||
(if (in? '(:jump-to :mention) type)
|
||||
(if pressed?
|
||||
(colors/custom-color-by-theme customization-color 60 50)
|
||||
(colors/custom-color-by-theme customization-color 50 60))
|
||||
(if pressed?
|
||||
(colors/theme-colors colors/neutral-80-opa-80 colors/white-opa-80)
|
||||
(colors/theme-colors colors/neutral-80-opa-70 colors/white-opa-70))))
|
||||
|
||||
(defn- get-icon-and-text-color [type]
|
||||
(if (in? '(:jump-to :mention) type)
|
||||
colors/white
|
||||
(colors/theme-colors colors/white colors/neutral-100)))
|
||||
|
||||
(defn- icon-view [type]
|
||||
[icon/icon
|
||||
(case type
|
||||
:jump-to :main-icons2/jump-to
|
||||
:mention :main-icons2/mention
|
||||
:notification-down :main-icons2/arrow-down
|
||||
:notification-up :main-icons2/arrow-up
|
||||
:search-with-label :main-icons2/search
|
||||
:search :main-icons2/search
|
||||
:bottom :main-icons2/arrow-down)
|
||||
{:size 12
|
||||
:color (get-icon-and-text-color type)
|
||||
:container-style {:margin-top 6
|
||||
:margin-bottom 6
|
||||
:margin-left (case type
|
||||
:jump-to 0
|
||||
:mention 8
|
||||
:notification-down 2
|
||||
:notification-up 2
|
||||
:search-with-label 8
|
||||
:search 6
|
||||
:bottom 6)
|
||||
:margin-right (case type
|
||||
:jump-to 8
|
||||
:mention 2
|
||||
:notification-down 8
|
||||
:notification-up 8
|
||||
:search-with-label 4
|
||||
:search 6
|
||||
:bottom 6)}}])
|
||||
|
||||
(defn dynamic-button
|
||||
"[:dynamic-button opts]
|
||||
opts
|
||||
{:type :jump-to/:mention/:notification-down/:notification-up/:search/:search-with-label/:bottom
|
||||
:on-press fn
|
||||
:count mentions or notifications count
|
||||
:customization-color customize jump-to and mention button color}"
|
||||
[_]
|
||||
(let [pressed? (reagent/atom false)]
|
||||
(fn [{:keys [type on-press count customization-color]
|
||||
:or {customization-color :primary}}]
|
||||
[rn/touchable-without-feedback
|
||||
{:on-press-in #(reset! pressed? true)
|
||||
:on-press-out #(reset! pressed? false)
|
||||
:on-press on-press}
|
||||
[rn/view {:style {:flex-direction :row
|
||||
:height 24
|
||||
:border-radius 12
|
||||
:background-color (get-button-color type @pressed? customization-color)}}
|
||||
(when (in? '(:mention :search :search-with-label :bottom) type)
|
||||
[icon-view type])
|
||||
(when (in? '(:jump-to :mention :notification-down :notification-up :search-with-label) type)
|
||||
[text/text {:weight :medium
|
||||
:size :paragraph-2
|
||||
:style {:color (get-icon-and-text-color type)
|
||||
:margin-top 2.5
|
||||
:margin-bottom 3.5
|
||||
:margin-left (case type
|
||||
:jump-to 8
|
||||
:mention 0
|
||||
:notification-down 8
|
||||
:notification-up 8
|
||||
:search-with-label 0)
|
||||
:margin-right (case type
|
||||
:jump-to 0
|
||||
:mention 8
|
||||
:notification-down 0
|
||||
:notification-up 0
|
||||
:search-with-label 8)}}
|
||||
(case type
|
||||
:jump-to (i18n/label :t/jump-to)
|
||||
:search-with-label (i18n/label :t/back)
|
||||
(:mention :notification-down :notification-up) (str count))])
|
||||
(when (in? '(:jump-to :notification-down :notification-up) type)
|
||||
[icon-view type])]])))
|
||||
@@ -4,7 +4,7 @@
|
||||
[quo2.components.icon :as icons]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.tags.permission-tag :as permission]
|
||||
[quo2.components.tags.filter-tag :as filter-tag]
|
||||
[quo2.components.tags.tag :as tag]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.money :as money]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
@@ -49,7 +49,7 @@
|
||||
(for [{:keys [id tag-label resource]} tags]
|
||||
^{:key id}
|
||||
[react/view {:margin-right 8}
|
||||
[filter-tag/filter-tag
|
||||
[tag/tag
|
||||
{:id id
|
||||
:size 24
|
||||
:label tag-label
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
(ns quo2.components.info.nfc-prompt
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo.theme :as theme]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.ui.components.icons.icons :as icons]))
|
||||
|
||||
(defn nfc-prompt
|
||||
[{:keys [prompt-status on-press]}]
|
||||
(let [dark? (theme/dark?)
|
||||
ready? (= :ready prompt-status)
|
||||
connected? (= :connected prompt-status)
|
||||
success? (= :success prompt-status)
|
||||
ready-or-connected? (or ready? connected?)]
|
||||
[rn/view {:style {:height 400
|
||||
:flex-direction "column-reverse"}}
|
||||
[rn/view {:style {:background-color (if dark?
|
||||
colors/dark-prompt-bg
|
||||
colors/white)
|
||||
:width "100%"
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:height "100%"}}
|
||||
[rn/view {:style (cond-> {:flex-direction :row
|
||||
:justify-content :center
|
||||
:align-items :center}
|
||||
success? (assoc :display :none))}
|
||||
[rn/text {:style {:font-size 26
|
||||
:font-weight "400"
|
||||
:color (if dark?
|
||||
colors/dark-prompt-title
|
||||
colors/light-prompt-title)
|
||||
:line-height 31
|
||||
:text-align :center}}
|
||||
"Ready to Scan"]]
|
||||
[rn/view {:style {:justify-content :center
|
||||
:align-items :center
|
||||
:margin-top 29}}
|
||||
[icons/icon (if success?
|
||||
:main-icons/nfc-prompt-success
|
||||
(when ready-or-connected?
|
||||
:main-icons/nfc-prompt))
|
||||
{:width 114
|
||||
:color "nil"
|
||||
:height 114}]
|
||||
[rn/view {:style {:width 291
|
||||
:height 95
|
||||
:justify-content :center
|
||||
:align-items :center}}
|
||||
[rn/text {:style {:font-size 16
|
||||
:font-weight "400"
|
||||
:color (if dark?
|
||||
colors/white
|
||||
colors/neutral-95)
|
||||
:line-height 20}}
|
||||
(if success?
|
||||
"Success"
|
||||
(if (= :ready prompt-status)
|
||||
"Hold your iPhone near a Status Keycard"
|
||||
"Connected. Don’t move your card."))]]
|
||||
[rn/touchable-opacity {:style (cond-> {:padding-vertical 18
|
||||
:padding-horizontal 10
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:width 291
|
||||
:border-radius 10
|
||||
:background-color (if dark?
|
||||
colors/dark-prompt-button
|
||||
colors/light-prompt-button)
|
||||
:height 52}
|
||||
success? (assoc :display :none))
|
||||
:on-press (fn [] on-press)}
|
||||
[rn/text {:style {:color (if dark?
|
||||
colors/white
|
||||
colors/neutral-95)}}
|
||||
"Cancel"]]]]]))
|
||||
@@ -0,0 +1,162 @@
|
||||
(ns quo2.components.messages.system-message
|
||||
(:require [status-im.i18n.i18n :as i18n]
|
||||
[quo.react-native :as rn]
|
||||
[status-im.utils.core :as utils]
|
||||
[quo.theme :as theme]
|
||||
[quo2.components.buttons.button :as button]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.reanimated :as ra]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.avatars.user-avatar :as user-avatar]
|
||||
[quo2.components.avatars.icon-avatar :as icon-avatar]))
|
||||
|
||||
(def themes-landed {:pinned colors/primary-50-opa-5
|
||||
:added colors/primary-50-opa-5
|
||||
:deleted colors/danger-50-opa-5})
|
||||
|
||||
(def themes
|
||||
{:light {:text colors/neutral-100
|
||||
:time colors/neutral-50
|
||||
:bg {:default colors/white
|
||||
:pressed colors/neutral-5
|
||||
:landed themes-landed}}
|
||||
:dark {:text colors/white
|
||||
:time colors/neutral-40
|
||||
:bg {:default colors/neutral-90
|
||||
:pressed colors/neutral-80
|
||||
:landed themes-landed}}})
|
||||
|
||||
(defn get-color [& keys]
|
||||
(reduce (fn [acc k] (get acc k (reduced acc)))
|
||||
((theme/get-theme) themes) (vec keys)))
|
||||
|
||||
(defn sm-timestamp [timestamp-str]
|
||||
[rn/view {:margin-left 6}
|
||||
[text/text {:size :label
|
||||
:style {:color (get-color :time)
|
||||
:text-transform :none}}
|
||||
timestamp-str]])
|
||||
|
||||
(defn sm-icon [{:keys [icon color opacity]}]
|
||||
[rn/view {:align-items :center
|
||||
:margin-right 8}
|
||||
[icon-avatar/icon-avatar {:size :medium
|
||||
:icon icon
|
||||
:color color
|
||||
:opacity opacity}]])
|
||||
|
||||
(defn sm-user-avatar [image]
|
||||
[rn/view {:margin-right 4}
|
||||
[user-avatar/user-avatar {:status-indicator? false
|
||||
:online? false
|
||||
:size :xxxs
|
||||
:profile-picture image
|
||||
:ring? false}]])
|
||||
|
||||
(defmulti sm-render :type)
|
||||
|
||||
(defmethod sm-render :deleted [{:keys [state action timestamp-str]}]
|
||||
[rn/view {:align-items :center
|
||||
:justify-content :space-between
|
||||
:flex 1
|
||||
:flex-direction :row}
|
||||
[rn/view {:align-items :center
|
||||
:flex-direction :row}
|
||||
[sm-icon {:icon :main-icons/delete16
|
||||
:color :danger
|
||||
:opacity (if (= state :landed) 0 5)}]
|
||||
[text/text {:size :paragraph-2
|
||||
:style {:color (get-color :text)
|
||||
:margin-right 5}}
|
||||
(i18n/label (if action :message-deleted-for-you :message-deleted))]
|
||||
(when (nil? action) [sm-timestamp timestamp-str])]
|
||||
(when action [button/button {:size 24
|
||||
:before :main-icons/timeout
|
||||
:type :grey} (i18n/label :undo)])])
|
||||
|
||||
(defmethod sm-render :added [{:keys [state mentions timestamp-str]}]
|
||||
[rn/view {:align-items :center
|
||||
:flex-direction :row}
|
||||
[sm-icon {:icon :main-icons/add-user16
|
||||
:color :primary
|
||||
:opacity (if (= state :landed) 0 5)}]
|
||||
[sm-user-avatar (:image (first mentions))]
|
||||
[text/text {:weight :semi-bold
|
||||
:size :paragraph-2}
|
||||
(:name (first mentions))]
|
||||
[text/text {:size :paragraph-2
|
||||
:style {:color (get-color :text)
|
||||
:margin-left 3
|
||||
:margin-right 3}}
|
||||
(i18n/label :added)]
|
||||
[sm-user-avatar (:image (second mentions))]
|
||||
[text/text {:weight :semi-bold
|
||||
:size :paragraph-2}
|
||||
(:name (second mentions))]
|
||||
[sm-timestamp timestamp-str]])
|
||||
|
||||
(defmethod sm-render :pinned [{:keys [state pinned-by content timestamp-str]}]
|
||||
[rn/view {:flex-direction :row
|
||||
:flex 1
|
||||
:align-items :center}
|
||||
[sm-icon {:icon :main-icons/pin16
|
||||
:color :primary
|
||||
:opacity (if (= state :landed) 0 5)}]
|
||||
[rn/view {:flex-direction :column
|
||||
:flex 1}
|
||||
[rn/view {:align-items :baseline
|
||||
:flex-direction :row}
|
||||
[text/text {:size :paragraph-2
|
||||
:weight :semi-bold
|
||||
:style {:color (get-color :text)}}
|
||||
(utils/truncate-str pinned-by 18)]
|
||||
[rn/view {:margin-left 4
|
||||
:margin-right 2}
|
||||
[text/text {:size :paragraph-2
|
||||
:style {:color (get-color :text)}}
|
||||
(i18n/label :pinned-a-message)]]
|
||||
[sm-timestamp timestamp-str]]
|
||||
[rn/view {:flex-direction :row}
|
||||
[rn/view {:flex-direction :row
|
||||
:margin-right 4}
|
||||
[sm-user-avatar (:image (:mentions content))]
|
||||
[text/text {:weight :semi-bold
|
||||
:size :label}
|
||||
(:name (:mentions content))]]
|
||||
(when (seq (:text content))
|
||||
[rn/view {:margin-right 20
|
||||
:flex-direction :row
|
||||
:flex 1}
|
||||
[text/text {:size :label
|
||||
:style {:color (get-color :text)}
|
||||
:number-of-lines 1
|
||||
:ellipsize-mode :tail}
|
||||
(:text content)]])
|
||||
[rn/view {:justify-content :flex-end
|
||||
:flex-direction :row
|
||||
:min-width 10}
|
||||
(when (seq (:info content))
|
||||
[text/text {:size :label
|
||||
:style {:color (get-color :time)}}
|
||||
(utils/truncate-str (:info content) 24)])]]]])
|
||||
|
||||
(defn system-message [{:keys [type] :as message}]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [sv-color (ra/use-shared-value (get-color :bg :landed type))]
|
||||
(ra/animate-shared-value-with-delay
|
||||
sv-color (get-color :bg :default type) 0 :linear 1000)
|
||||
[ra/touchable-opacity
|
||||
{:on-press #(ra/set-shared-value
|
||||
sv-color (get-color :bg :pressed type))
|
||||
:style (ra/apply-animations-to-style
|
||||
{:background-color sv-color}
|
||||
{:flex-direction :row
|
||||
:flex 1
|
||||
:border-radius 16
|
||||
:padding-vertical 9
|
||||
:padding-horizontal 11
|
||||
:width 359
|
||||
:height 52
|
||||
:background-color sv-color})}
|
||||
[sm-render message]]))])
|
||||
@@ -0,0 +1,175 @@
|
||||
(ns quo2.components.navigation.page-nav
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.icon :as icons]
|
||||
[status-im.utils.dimensions :as dimensions]
|
||||
[clojure.string :as string]
|
||||
[quo2.components.markdown.text :as text]))
|
||||
|
||||
(def ^:private centrify-style
|
||||
{:display :flex
|
||||
:justify-content :center
|
||||
:align-items :center})
|
||||
|
||||
(def ^:private align-left (assoc centrify-style :align-items :flex-start))
|
||||
|
||||
(def ^:private icon-styles (assoc centrify-style :width 32 :height 32 :border-radius 10))
|
||||
|
||||
(defn- big? [size] (= size :big))
|
||||
|
||||
(defn- icon-props [color size]
|
||||
(merge {:size 20
|
||||
:container-style {:width (if (big? size)
|
||||
20
|
||||
16)
|
||||
:height (if (big? size)
|
||||
20
|
||||
16)}}
|
||||
(if-not (string/blank? color)
|
||||
{:color color}
|
||||
{:no-color true})))
|
||||
|
||||
(defn- mid-section-comp
|
||||
[{:keys [mid-section-description-user-icon horizontal-description? text-secondary-color align-mid? text-color mid-section-icon mid-section-main-text mid-section-type mid-section-description]}]
|
||||
[rn/view {:style (assoc
|
||||
centrify-style
|
||||
:flex-direction :row
|
||||
:margin-horizontal 2)}
|
||||
(when (or (and (not horizontal-description?)
|
||||
align-mid?
|
||||
(not= :text-with-description mid-section-type))
|
||||
(and mid-section-description-user-icon
|
||||
(not mid-section-icon)))
|
||||
[rn/image {:source {:uri mid-section-description-user-icon}
|
||||
:style {:width 32
|
||||
:height 32
|
||||
:border-radius 32
|
||||
:margin-right 8}}])
|
||||
[rn/view {:style {:flex-direction (if horizontal-description?
|
||||
:row
|
||||
:column)}}
|
||||
[text/text {:size :paragraph-1
|
||||
:weight :semi-bold
|
||||
:style {:color text-color
|
||||
:line-height 21}}
|
||||
mid-section-main-text]
|
||||
(when mid-section-description
|
||||
[text/text {:size :paragraph-2
|
||||
:weight :medium
|
||||
:style (cond-> {:padding-right 4
|
||||
:color text-secondary-color
|
||||
:line-height 18}
|
||||
horizontal-description? (assoc :margin-left 4 :margin-top 2))}
|
||||
mid-section-description])]])
|
||||
|
||||
(defn- mid-section
|
||||
[{:keys [horizontal-description? one-icon-align-left? mid-section-type left-align? mid-section-main-text mid-section-right-icon mid-section-main-text-icon-color mid-section-left-icon] :as mid-section-props}]
|
||||
(let [text-color (if (colors/dark?) colors/neutral-5 colors/neutral-95)
|
||||
text-secondary-color (if (colors/dark?) colors/neutral-40 colors/neutral-50)
|
||||
mid-section-comp-instance [mid-section-comp (assoc mid-section-props :text-secondary-color text-secondary-color)]]
|
||||
[rn/view {:style (merge
|
||||
(if left-align?
|
||||
align-left
|
||||
centrify-style)
|
||||
{:flex 1
|
||||
:margin-left 4
|
||||
:text-align-vertical :center})}
|
||||
(case mid-section-type
|
||||
:text-only [text/text {:size :paragraph-1
|
||||
:weight :semi-bold
|
||||
:style {:color text-color}}
|
||||
mid-section-main-text]
|
||||
:text-with-two-icons [rn/view {:style (assoc centrify-style :flex-direction :row)}
|
||||
[icons/icon mid-section-left-icon
|
||||
(icon-props mid-section-main-text-icon-color :big)]
|
||||
[text/text {:size :paragraph-1
|
||||
:weight :semi-bold
|
||||
:style {:padding-horizontal 4
|
||||
:color text-color}}
|
||||
mid-section-main-text]
|
||||
|
||||
[icons/icon mid-section-right-icon
|
||||
(icon-props mid-section-main-text-icon-color :big)]]
|
||||
:text-with-one-icon [rn/view {:style {:flex-direction :row}}
|
||||
(if one-icon-align-left?
|
||||
[rn/view {:style {:flex-direction :row
|
||||
:align-items :center}}
|
||||
(when horizontal-description?
|
||||
[icons/icon mid-section-left-icon
|
||||
(icon-props mid-section-main-text-icon-color :big)])
|
||||
mid-section-comp-instance]
|
||||
[rn/view {:style {:flex-direction :row
|
||||
:align-items :center}}
|
||||
mid-section-comp-instance
|
||||
(when horizontal-description?
|
||||
[icons/icon mid-section-left-icon
|
||||
(icon-props mid-section-main-text-icon-color :big)])])]
|
||||
:text-with-description mid-section-comp-instance)]))
|
||||
|
||||
(defn- right-section-icon
|
||||
[{:keys [background-color icon icon-color push-to-the-left?] :or {push-to-the-left? false}}]
|
||||
[rn/view {:style (assoc
|
||||
icon-styles
|
||||
:background-color background-color
|
||||
:width 32
|
||||
:height 32
|
||||
:margin-right (if push-to-the-left? 8 0))}
|
||||
[icons/icon icon (icon-props icon-color :big)]])
|
||||
|
||||
(defn page-nav
|
||||
[{:keys [one-icon-align-left? horizontal-description? align-mid? page-nav-color page-nav-background-uri mid-section-type mid-section-icon mid-section-main-text mid-section-left-icon mid-section-right-icon mid-section-description mid-section-description-color mid-section-description-icon mid-section-description-user-icon mid-section-main-text-icon-color left-section-icon left-section-icon-color left-section-icon-background-color right-section-icons]}]
|
||||
(let [{:keys [height width]} (dimensions/window)
|
||||
put-middle-section-on-left? (or align-mid?
|
||||
(> (count right-section-icons) 1))
|
||||
mid-section-props {:mid-section-type mid-section-type
|
||||
:horizontal-description? horizontal-description?
|
||||
:mid-section-main-text mid-section-main-text
|
||||
:one-icon-align-left? one-icon-align-left?
|
||||
:mid-section-right-icon mid-section-right-icon
|
||||
:mid-section-icon mid-section-icon
|
||||
:mid-section-main-text-icon-color mid-section-main-text-icon-color
|
||||
:mid-section-left-icon mid-section-left-icon}]
|
||||
[rn/view {:style (cond->
|
||||
{:display :flex
|
||||
:flex-direction :row
|
||||
:width width
|
||||
:height (* 0.0497 height)
|
||||
;; iPhone 11 Pro's height in Figma divided by Component height 56/1125
|
||||
:align-items :center
|
||||
:padding-horizontal 20
|
||||
:justify-content :space-between}
|
||||
page-nav-background-uri (assoc :background-color page-nav-color)
|
||||
page-nav-color (assoc :background page-nav-background-uri))}
|
||||
[rn/view {:style {:flex 1
|
||||
:flex-direction :row
|
||||
:align-items :center}}
|
||||
[rn/view {:style (merge
|
||||
icon-styles
|
||||
{:background-color left-section-icon-background-color
|
||||
:width 32
|
||||
:height 32}
|
||||
(when put-middle-section-on-left? {:margin-right 5}))}
|
||||
[icons/icon left-section-icon (icon-props left-section-icon-color :big)]]
|
||||
(when put-middle-section-on-left?
|
||||
[mid-section (assoc mid-section-props
|
||||
:left-align? true
|
||||
:mid-section-description mid-section-description
|
||||
:mid-section-description-color mid-section-description-color
|
||||
:mid-section-description-icon mid-section-description-icon
|
||||
:align-mid? align-mid?
|
||||
:mid-section-description-user-icon mid-section-description-user-icon)])]
|
||||
(when-not put-middle-section-on-left?
|
||||
[mid-section mid-section-props])
|
||||
[rn/view {:style (assoc
|
||||
centrify-style
|
||||
:flex-direction :row
|
||||
:flex 1
|
||||
:justify-content :flex-end)}
|
||||
(let [last-icon-index (- (count right-section-icons) 1)]
|
||||
(map-indexed (fn [index {:keys [icon background-color icon-color]}]
|
||||
^{:key index}
|
||||
[right-section-icon {:icon icon
|
||||
:background-color background-color
|
||||
:icon-color icon-color
|
||||
:push-to-the-left? (if (= index last-icon-index) false true)}])
|
||||
right-section-icons))]]))
|
||||
@@ -1,21 +1,142 @@
|
||||
(ns quo2.components.tabs.tabs
|
||||
(:require [reagent.core :as reagent]
|
||||
(:require [oops.core :refer [oget]]
|
||||
[quo.react-native :as rn]
|
||||
[quo2.components.tabs.tab :as tab]))
|
||||
[quo2.components.tabs.tab :as tab]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.core :as utils]
|
||||
[status-im.utils.number :as number-utils]))
|
||||
|
||||
(def default-tab-size 32)
|
||||
|
||||
(defn tabs [{:keys [default-active on-change]}]
|
||||
(let [active-tab-id (reagent/atom default-active)]
|
||||
(fn [{:keys [data size] :or {size 32}}]
|
||||
(fn [{:keys [data size] :or {size default-tab-size}}]
|
||||
(let [active-id @active-tab-id]
|
||||
[rn/view {:flex-direction :row}
|
||||
(for [{:keys [label id]} data]
|
||||
^{:key id}
|
||||
[rn/view {:margin-right (if (= size 32) 12 8)}
|
||||
[rn/view {:style {:margin-right (if (= size default-tab-size) 12 8)}}
|
||||
[tab/tab
|
||||
{:id id
|
||||
:size size
|
||||
:active (= id active-id)
|
||||
:on-press #(do (reset! active-tab-id %)
|
||||
(when on-change
|
||||
(on-change %)))}
|
||||
label]])]))))
|
||||
{:id id
|
||||
:size size
|
||||
:active (= id active-id)
|
||||
:on-press (fn [^js press-event id]
|
||||
(reset! active-tab-id id)
|
||||
(when on-change
|
||||
(on-change press-event id)))}
|
||||
label]])]))))
|
||||
|
||||
(defn- calculate-fade-end-percentage
|
||||
[{:keys [offset-x content-width layout-width max-fade-percentage]}]
|
||||
(let [fade-percentage (max max-fade-percentage
|
||||
(/ (+ layout-width offset-x)
|
||||
content-width))]
|
||||
;; Truncate to avoid unnecessary rendering.
|
||||
(if (> fade-percentage 0.99)
|
||||
0.99
|
||||
(number-utils/naive-round fade-percentage 2))))
|
||||
|
||||
(defn scrollable-tabs
|
||||
"Just like the component `tabs`, displays horizontally scrollable tabs with
|
||||
extra options to control if/how the end of the scroll view fades.
|
||||
|
||||
Tabs are rendered using ReactNative's FlatList, which offers the convenient
|
||||
`scrollToIndex` method. FlatList accepts VirtualizedList and ScrollView props,
|
||||
and so does this component.
|
||||
|
||||
Usage:
|
||||
[tabs/scrollable-tabs
|
||||
{:scroll-on-press? true
|
||||
:fade-end? true
|
||||
:on-change #(...)
|
||||
:default-active :tab-a
|
||||
:data [{:id :tab-a :label \"Tab A\"}
|
||||
{:id :tab-b :label \"Tab B\"}]}]]
|
||||
|
||||
Opts:
|
||||
- `size` number
|
||||
- `scroll-on-press?` When non-nil, clicking on a tab centers it the middle
|
||||
(with animation enabled).
|
||||
- `fade-end?` When non-nil, causes the end of the scrollable view to fade out.
|
||||
- `fade-end-percentage` Percentage where fading starts relative to the total
|
||||
layout width of the `flat-list` data."
|
||||
[{:keys [default-active fade-end-percentage]
|
||||
:or {fade-end-percentage 0.8}}]
|
||||
(let [active-tab-id (reagent/atom default-active)
|
||||
fading (reagent/atom {:fade-end-percentage fade-end-percentage})
|
||||
flat-list-ref (atom nil)]
|
||||
(fn [{:keys [data
|
||||
fade-end-percentage
|
||||
fade-end?
|
||||
on-change
|
||||
on-scroll
|
||||
scroll-event-throttle
|
||||
scroll-on-press?
|
||||
size]
|
||||
:or {fade-end-percentage fade-end-percentage
|
||||
fade-end? false
|
||||
scroll-event-throttle 64
|
||||
scroll-on-press? false
|
||||
size default-tab-size}
|
||||
:as props}]
|
||||
(let [maybe-mask-wrapper (if fade-end?
|
||||
[react/masked-view
|
||||
{:mask-element (reagent/as-element
|
||||
[react/linear-gradient {:colors [:black :transparent]
|
||||
:locations [(get @fading :fade-end-percentage) 1]
|
||||
:start {:x 0 :y 0}
|
||||
:end {:x 1 :y 0}
|
||||
:pointer-events :none
|
||||
:style {:width "100%"
|
||||
:height "100%"}}])}]
|
||||
[:<>])]
|
||||
(conj maybe-mask-wrapper
|
||||
[rn/flat-list
|
||||
(merge (dissoc props
|
||||
:default-active
|
||||
:fade-end-percentage
|
||||
:fade-end?
|
||||
:on-change
|
||||
:scroll-on-press?
|
||||
:size)
|
||||
(when scroll-on-press?
|
||||
{:initial-scroll-index (utils/first-index #(= @active-tab-id (:id %)) data)})
|
||||
{:ref #(reset! flat-list-ref %)
|
||||
:extra-data (str @active-tab-id)
|
||||
:horizontal true
|
||||
:scroll-event-throttle scroll-event-throttle
|
||||
:shows-horizontal-scroll-indicator false
|
||||
:data data
|
||||
:key-fn (comp str :id)
|
||||
:on-scroll (fn [^js e]
|
||||
(when fade-end?
|
||||
(let [offset-x (oget e "nativeEvent.contentOffset.x")
|
||||
content-width (oget e "nativeEvent.contentSize.width")
|
||||
layout-width (oget e "nativeEvent.layoutMeasurement.width")
|
||||
new-percentage (calculate-fade-end-percentage {:offset-x offset-x
|
||||
:content-width content-width
|
||||
:layout-width layout-width
|
||||
:max-fade-percentage fade-end-percentage})]
|
||||
;; Avoid unnecessary re-rendering.
|
||||
(when (not= new-percentage (get @fading :fade-end-percentage))
|
||||
(swap! fading assoc :fade-end-percentage new-percentage))))
|
||||
(when on-scroll
|
||||
(on-scroll e)))
|
||||
:render-fn (fn [{:keys [id label]} index]
|
||||
[rn/view {:style {:margin-right (if (= size default-tab-size) 12 8)
|
||||
:padding-right (when (= index (dec (count data)))
|
||||
(get-in props [:style :padding-left]))}}
|
||||
[tab/tab {:id id
|
||||
:size size
|
||||
:active (= id @active-tab-id)
|
||||
:on-press (fn [id]
|
||||
(reset! active-tab-id id)
|
||||
(when scroll-on-press?
|
||||
(.scrollToIndex @flat-list-ref
|
||||
#js {:animated true
|
||||
:index index
|
||||
:viewPosition 0.5}))
|
||||
(when on-change
|
||||
(on-change id)))}
|
||||
label]])})])))))
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
(ns quo2.components.tags.filter-tag
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[quo.theme :as theme]
|
||||
[quo.react-native :as rn]
|
||||
[quo2.components.icon :as icons]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.tags.base-tag :as base-tag]))
|
||||
|
||||
(def themes {:light {:default {:border-color colors/neutral-20
|
||||
:blurred-border-color colors/neutral-80-opa-5
|
||||
:text-color {:style {:color colors/neutral-100}}}
|
||||
:active {:border-color colors/neutral-30
|
||||
:blurred-border-color colors/neutral-80-opa-10
|
||||
:text-color {:style {:color colors/neutral-100}}}
|
||||
:disabled {:border-color colors/neutral-20
|
||||
:blurred-border-color colors/neutral-80-opa-5
|
||||
:text-color {:style {:color colors/neutral-100}}}}
|
||||
:dark {:default {:border-color colors/neutral-70
|
||||
:blurred-border-color colors/white-opa-10
|
||||
:text-color {:style {:color colors/white}}}
|
||||
:active {:border-color colors/neutral-60
|
||||
:blurred-border-color colors/white-opa-20
|
||||
:text-color {:style {:color colors/white}}}
|
||||
:disabled {:border-color colors/neutral-70
|
||||
:blurred-border-color colors/white-opa-10
|
||||
:text-color {:style {:color colors/white}}}}})
|
||||
|
||||
(defn tag-resources [size type resource icon-color label text-color labelled]
|
||||
[rn/view {:style (merge {:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :center}
|
||||
(when label
|
||||
{:padding-horizontal (case size 32 12 24 8)}))}
|
||||
(when (= type :icon)
|
||||
[icons/icon resource {:container-style (when label
|
||||
{:margin-right 4})
|
||||
:resize-mode :center
|
||||
:size (case size
|
||||
32 20
|
||||
24 12)
|
||||
:color icon-color}])
|
||||
(when (= type :emoji)
|
||||
[rn/image {:source resource
|
||||
:style (merge (case size
|
||||
32 {:height 20
|
||||
:width 20}
|
||||
24 {:height 12
|
||||
:width 12})
|
||||
(when label
|
||||
{:margin-right 4}))}])
|
||||
(when labelled
|
||||
[text/text (merge {:size (case size
|
||||
32 :paragraph-1
|
||||
24 :paragraph-2
|
||||
20 :label nil)
|
||||
:weight :medium
|
||||
:number-of-lines 1}
|
||||
text-color)
|
||||
label])])
|
||||
|
||||
(defn filter-tag
|
||||
[_ _]
|
||||
(fn [{:keys [id on-press disabled size resource active accessibility-label
|
||||
label type labelled blurred icon-color] :or {size 32}}]
|
||||
(let [state (cond disabled :disabled active :active :else :default)
|
||||
{:keys [border-color blurred-border-color text-color]}
|
||||
(get-in themes [(theme/get-theme) state])]
|
||||
[base-tag/base-tag {:id id
|
||||
:size size
|
||||
:border-width 1
|
||||
:border-color (if blurred
|
||||
blurred-border-color
|
||||
border-color)
|
||||
:on-press on-press
|
||||
:accessibility-label accessibility-label
|
||||
:disabled disabled
|
||||
:type type
|
||||
:label label}
|
||||
[tag-resources size type resource icon-color label text-color labelled]])))
|
||||
|
||||
@@ -1,51 +1,80 @@
|
||||
(ns quo2.components.tags.tag
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo2.foundations.colors :as colors]
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[quo.theme :as theme]
|
||||
[quo2.components.markdown.text :as text]))
|
||||
[quo.react-native :as rn]
|
||||
[quo2.components.icon :as icons]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.tags.base-tag :as base-tag]))
|
||||
|
||||
(def themes {:light {:background-color colors/neutral-20}
|
||||
:dark {:background-color colors/neutral-80}})
|
||||
(def themes {:light {:default {:border-color colors/neutral-20
|
||||
:blurred-border-color colors/neutral-80-opa-5
|
||||
:text-color {:style {:color colors/neutral-100}}}
|
||||
:active {:border-color colors/neutral-30
|
||||
:blurred-border-color colors/neutral-80-opa-10
|
||||
:text-color {:style {:color colors/neutral-100}}}
|
||||
:disabled {:border-color colors/neutral-20
|
||||
:blurred-border-color colors/neutral-80-opa-5
|
||||
:text-color {:style {:color colors/neutral-100}}}}
|
||||
:dark {:default {:border-color colors/neutral-70
|
||||
:blurred-border-color colors/white-opa-10
|
||||
:text-color {:style {:color colors/white}}}
|
||||
:active {:border-color colors/neutral-60
|
||||
:blurred-border-color colors/white-opa-20
|
||||
:text-color {:style {:color colors/white}}}
|
||||
:disabled {:border-color colors/neutral-70
|
||||
:blurred-border-color colors/white-opa-10
|
||||
:text-color {:style {:color colors/white}}}}})
|
||||
|
||||
(defn get-value-from-size [size big-option small-option]
|
||||
(if (= size :big) big-option small-option))
|
||||
(defn tag-resources [size type resource icon-color label text-color labelled]
|
||||
[rn/view {:style (merge {:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :center}
|
||||
(when label
|
||||
{:padding-horizontal (case size 32 12 24 8)}))}
|
||||
(when (= type :icon)
|
||||
[icons/icon resource {:container-style (when label
|
||||
{:margin-right 4})
|
||||
:resize-mode :center
|
||||
:size (case size
|
||||
32 20
|
||||
24 12)
|
||||
:color icon-color}])
|
||||
(when (= type :emoji)
|
||||
[rn/image {:source resource
|
||||
:style (merge (case size
|
||||
32 {:height 20
|
||||
:width 20}
|
||||
24 {:height 12
|
||||
:width 12})
|
||||
(when label
|
||||
{:margin-right 4}))}])
|
||||
(when labelled
|
||||
[text/text (merge {:size (case size
|
||||
32 :paragraph-1
|
||||
24 :paragraph-2
|
||||
20 :label nil)
|
||||
:weight :medium
|
||||
:number-of-lines 1}
|
||||
text-color)
|
||||
label])])
|
||||
|
||||
(defn tag-container [size]
|
||||
{:height (get-value-from-size size 32 24)
|
||||
:align-items :center
|
||||
:flex-direction :row
|
||||
:border-radius 20})
|
||||
|
||||
(defn tag "[tag opts \"label\"]]
|
||||
opts
|
||||
{
|
||||
:size :small/:big
|
||||
:token-img-src :token-img-src
|
||||
:token-img-style {}
|
||||
:border-color :color
|
||||
:overlay child-elements
|
||||
}"
|
||||
(defn tag
|
||||
[_ _]
|
||||
(fn [{:keys [size token-img-src token-img-style border-color overlay]
|
||||
:or {size :small}} label]
|
||||
[rn/view
|
||||
{:style (when border-color
|
||||
{:border-color border-color
|
||||
:border-radius 20
|
||||
:border-width 1})}
|
||||
[rn/view
|
||||
{:style (merge (tag-container size) (get themes (theme/get-theme)))}
|
||||
[rn/image
|
||||
{:source token-img-src
|
||||
:style (merge
|
||||
{:height (get-value-from-size size 28 20)
|
||||
:width (get-value-from-size size 28 20)
|
||||
:margin-left 2
|
||||
:margin-right (get-value-from-size size 8 6)} token-img-style)}]
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:number-of-lines 1
|
||||
:style
|
||||
{:margin-right (get-value-from-size size 12 11)}
|
||||
:size (get-value-from-size size :paragraph-2 :label)} label]
|
||||
overlay]]))
|
||||
(fn [{:keys [id on-press disabled size resource active accessibility-label
|
||||
label type labelled blurred icon-color] :or {size 32}}]
|
||||
(let [state (cond disabled :disabled active :active :else :default)
|
||||
{:keys [border-color blurred-border-color text-color]}
|
||||
(get-in themes [(theme/get-theme) state])]
|
||||
[base-tag/base-tag {:id id
|
||||
:size size
|
||||
:border-width 1
|
||||
:border-color (if blurred
|
||||
blurred-border-color
|
||||
border-color)
|
||||
:on-press on-press
|
||||
:accessibility-label accessibility-label
|
||||
:disabled disabled
|
||||
:type type
|
||||
:label label}
|
||||
[tag-resources size type resource icon-color label text-color labelled]])))
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
(ns quo2.components.tags.filter-tags
|
||||
(ns quo2.components.tags.tags
|
||||
(:require [reagent.core :as reagent]
|
||||
[quo.react-native :as rn]
|
||||
[quo2.components.tags.filter-tag :as tag]))
|
||||
[quo2.components.tags.tag :as tag]))
|
||||
|
||||
(defn tags [{:keys [default-active on-change]}]
|
||||
(let [active-tab-id (reagent/atom default-active)]
|
||||
@@ -11,7 +11,7 @@
|
||||
(for [{:keys [tag-label id resource]} data]
|
||||
^{:key id}
|
||||
[rn/view {:margin-right 8}
|
||||
[tag/filter-tag
|
||||
[tag/tag
|
||||
(merge {:id id
|
||||
:size size
|
||||
:type type
|
||||
@@ -2,9 +2,13 @@
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[quo.react-native :as rn]
|
||||
[quo.theme :as theme]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[quo2.components.tags.tag :as tag]))
|
||||
|
||||
(def themes {:light {:background-color colors/neutral-20}
|
||||
:dark {:background-color colors/neutral-80}})
|
||||
|
||||
(defn get-value-from-size [size big-option small-option]
|
||||
(if (= size :big) big-option small-option))
|
||||
|
||||
@@ -16,6 +20,46 @@
|
||||
:border-radius 20
|
||||
:margin-left 2})
|
||||
|
||||
(defn tag-container [size]
|
||||
{:height (get-value-from-size size 32 24)
|
||||
:align-items :center
|
||||
:flex-direction :row
|
||||
:border-radius 20})
|
||||
|
||||
(defn tag "[tag opts \"label\"]]
|
||||
opts
|
||||
{
|
||||
:size :small/:big
|
||||
:token-img-src :token-img-src
|
||||
:token-img-style {}
|
||||
:border-color :color
|
||||
:overlay child-elements
|
||||
}"
|
||||
[_ _]
|
||||
(fn [{:keys [size token-img-src token-img-style border-color overlay]
|
||||
:or {size :small}} label]
|
||||
[rn/view
|
||||
{:style (when border-color
|
||||
{:border-color border-color
|
||||
:border-radius 20
|
||||
:border-width 1})}
|
||||
[rn/view
|
||||
{:style (merge (tag-container size) (get themes (theme/get-theme)))}
|
||||
[rn/image
|
||||
{:source token-img-src
|
||||
:style (merge
|
||||
{:height (get-value-from-size size 28 20)
|
||||
:width (get-value-from-size size 28 20)
|
||||
:margin-left 2
|
||||
:margin-right (get-value-from-size size 8 6)} token-img-style)}]
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:number-of-lines 1
|
||||
:style
|
||||
{:margin-right (get-value-from-size size 12 11)}
|
||||
:size (get-value-from-size size :paragraph-2 :label)} label]
|
||||
overlay]]))
|
||||
|
||||
(defn token-tag
|
||||
"[token-tag opts]
|
||||
opts
|
||||
|
||||
@@ -119,6 +119,7 @@
|
||||
|
||||
;;;; Customization
|
||||
|
||||
;; Colors for customizing profiles and communities themes
|
||||
(def customization
|
||||
{:primary {50 primary-50 ;; User can also use primary color as customisation color
|
||||
60 primary-60}
|
||||
@@ -145,6 +146,11 @@
|
||||
:beige {50 "#CAAE93"
|
||||
60 "#AA927C"}})
|
||||
|
||||
(def colors-map (merge {:danger {50 danger-50
|
||||
60 danger-60}
|
||||
:success {50 success-50
|
||||
60 success-60}} customization))
|
||||
|
||||
(def custom-color
|
||||
"(custom-color color suffix opacity)
|
||||
color :primary/:purple/...
|
||||
@@ -155,7 +161,7 @@
|
||||
([color suffix]
|
||||
(custom-color color suffix nil))
|
||||
([color suffix opacity]
|
||||
(let [base-color (get-in customization [(keyword color) suffix])]
|
||||
(let [base-color (get-in colors-map [(keyword color) suffix])]
|
||||
(if opacity (alpha base-color (/ opacity 100)) base-color))))))
|
||||
|
||||
(defn custom-color-by-theme
|
||||
@@ -188,12 +194,6 @@
|
||||
(let [theme (or override-theme (theme/get-theme))]
|
||||
(if (= theme :light) light dark))))
|
||||
|
||||
(def dark-prompt-bg "#1C1C1E")
|
||||
(def dark-prompt-title "#9F9FA5")
|
||||
(def dark-prompt-button "#8E8E93")
|
||||
(def light-prompt-title "#8F8E94")
|
||||
(def light-prompt-button "#D5D4DB")
|
||||
|
||||
(defn dark?
|
||||
[]
|
||||
(theme/dark?))
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
:type :select
|
||||
:options [{:key :main-icons/placeholder20
|
||||
:value "Placeholder"}
|
||||
{:key :main-icons/walelt
|
||||
{:key :main-icons/wallet
|
||||
:value "Wallet"}
|
||||
{:key :main-icons/play
|
||||
:value "Play"}]}
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo.previews.preview :as preview]
|
||||
[quo2.components.avatars.user-avatar :as quo2]
|
||||
[reagent.core :as reagent]))
|
||||
[reagent.core :as reagent]
|
||||
[status-im.react-native.resources :as resources]))
|
||||
|
||||
(def descriptor [{:label "Size:"
|
||||
:key :size
|
||||
@@ -32,9 +33,15 @@
|
||||
{:label "Full name separated by space"
|
||||
:key :full-name
|
||||
:type :text}
|
||||
{:label "Profile Picture URL"
|
||||
:key :profile-picture
|
||||
:type :text}])
|
||||
{:label "Profile Picture"
|
||||
:key :profile-picture
|
||||
:type :select
|
||||
:options [{:value "None"
|
||||
:key nil}
|
||||
{:value "Alicia Keys"
|
||||
:key (resources/get-mock-image :user-picture-female2)}
|
||||
{:value "pedro.eth"
|
||||
:key (resources/get-mock-image :user-picture-male4)}]}])
|
||||
|
||||
(defn cool-preview []
|
||||
(let [state (reagent/atom {:full-name "A Y"
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
(ns quo2.screens.buttons.dynamic-button
|
||||
(:require [quo.react-native :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[quo.previews.preview :as preview]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.buttons.dynamic-button :as quo2]))
|
||||
|
||||
(def descriptor [{:label "Type:"
|
||||
:key :type
|
||||
:type :select
|
||||
:options [{:key :jump-to
|
||||
:value "Jump To"}
|
||||
{:key :mention
|
||||
:value "Mention"}
|
||||
{:key :notification-down
|
||||
:value "Notification Down"}
|
||||
{:key :notification-up
|
||||
:value "Notification Up"}
|
||||
{:key :search
|
||||
:value "Search"}
|
||||
{:key :search-with-label
|
||||
:value "Search With Label"}
|
||||
{:key :bottom
|
||||
:value "Bottom"}]}
|
||||
{:label "Count"
|
||||
:key :count
|
||||
:type :text}])
|
||||
|
||||
(defn cool-preview []
|
||||
(let [state (reagent/atom {:count 5 :type :jump-to})]
|
||||
(fn []
|
||||
[rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!}
|
||||
[rn/view {:padding-bottom 150}
|
||||
[preview/customizer state descriptor]
|
||||
[rn/view {:padding-vertical 60
|
||||
:align-items :center}
|
||||
[quo2/dynamic-button @state]]]])))
|
||||
|
||||
(defn preview-dynamic-button []
|
||||
[rn/view {:background-color (colors/theme-colors colors/white colors/neutral-90)
|
||||
:flex 1}
|
||||
[rn/flat-list {:flex 1
|
||||
:keyboardShouldPersistTaps :always
|
||||
:header [cool-preview]
|
||||
:key-fn str}]])
|
||||
@@ -1,33 +0,0 @@
|
||||
(ns quo2.screens.info.nfc-prompt
|
||||
(:require [quo.react-native :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[quo.previews.preview :as preview]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.info.nfc-prompt :as quo2]))
|
||||
|
||||
(def descriptor [{:label "Prompt status"
|
||||
:key :prompt-status
|
||||
:type :select
|
||||
:options [{:key :ready
|
||||
:value "Ready"}
|
||||
{:key :connected
|
||||
:value "Connected"}
|
||||
{:key :success
|
||||
:value "Success"}]}])
|
||||
|
||||
(defn cool-preview []
|
||||
(let [state (reagent/atom {:on-press identity
|
||||
:prompt-status :ready})]
|
||||
(fn []
|
||||
[rn/view {:margin-bottom 50
|
||||
:padding-vertical 16}
|
||||
[preview/customizer state descriptor]
|
||||
[quo2/nfc-prompt @state]])))
|
||||
|
||||
(defn preview-nfc-prompt []
|
||||
[rn/view {:background-color (colors/theme-colors colors/white colors/neutral-90)
|
||||
:flex 1}
|
||||
[rn/flat-list {:flex 1
|
||||
:keyboardShouldPersistTaps :always
|
||||
:header [cool-preview]
|
||||
:key-fn str}]])
|
||||
@@ -13,6 +13,7 @@
|
||||
[quo2.screens.avatars.user-avatar :as user-avatar]
|
||||
[quo2.screens.avatars.wallet-user-avatar :as wallet-user-avatar]
|
||||
[quo2.screens.buttons.button :as button]
|
||||
[quo2.screens.buttons.dynamic-button :as dynamic-button]
|
||||
[quo2.screens.counter.counter :as counter]
|
||||
[quo2.screens.community.community-card-view :as community-card]
|
||||
[quo2.screens.dividers.divider-label :as divider-label]
|
||||
@@ -25,6 +26,7 @@
|
||||
[quo2.screens.list-items.channel :as channel]
|
||||
[quo2.screens.markdown.text :as text]
|
||||
[quo2.screens.messages.gap :as messages-gap]
|
||||
[quo2.screens.messages.system-message :as system-message]
|
||||
[quo2.screens.notifications.activity-logs :as activity-logs]
|
||||
[quo2.screens.reactions.react :as react]
|
||||
[quo2.screens.selectors.disclaimer :as disclaimer]
|
||||
@@ -36,14 +38,14 @@
|
||||
[quo2.screens.tabs.segmented-tab :as segmented]
|
||||
[quo2.screens.tabs.tabs :as tabs]
|
||||
[quo2.screens.tags.context-tags :as context-tags]
|
||||
[quo2.screens.tags.filter-tags :as filter-tags]
|
||||
[quo2.screens.tags.tags :as tags]
|
||||
[quo2.screens.tags.permission-tag :as permission-tag]
|
||||
[quo2.screens.tags.status-tags :as status-tags]
|
||||
[quo2.screens.tags.token-tag :as token-tag]
|
||||
[quo2.screens.wallet.token-overview :as token-overview]
|
||||
[quo2.screens.wallet.network-breakdown :as network-breakdown]
|
||||
[quo2.screens.wallet.network-amount :as network-amount]
|
||||
[quo2.screens.info.nfc-prompt :as nfc-prompt]
|
||||
[quo2.screens.navigation.page-nav :as page-nav]
|
||||
[re-frame.core :as re-frame]))
|
||||
|
||||
(def screens-categories
|
||||
@@ -64,7 +66,10 @@
|
||||
:component channel-avatar/preview-channel-avatar}]
|
||||
:buttons [{:name :button
|
||||
:insets {:top false}
|
||||
:component button/preview-button}]
|
||||
:component button/preview-button}
|
||||
{:name :dynamic-button
|
||||
:insets {:top false}
|
||||
:component dynamic-button/preview-dynamic-button}]
|
||||
:community [{:name :community-cards
|
||||
:insets {:top false}
|
||||
:component community-card/preview-community-card}]
|
||||
@@ -85,10 +90,7 @@
|
||||
:component info-message/preview-info-message}
|
||||
{:name :information-box
|
||||
:insets {:top false}
|
||||
:component information-box/preview-information-box}
|
||||
{:name :nfc-prompt
|
||||
:insets {:top false}
|
||||
:component nfc-prompt/preview-nfc-prompt}]
|
||||
:component information-box/preview-information-box}]
|
||||
:list-items [{:name :channel
|
||||
:insets {:top false}
|
||||
:component channel/preview-channel}
|
||||
@@ -100,13 +102,19 @@
|
||||
:component text/preview-text}]
|
||||
:messages [{:name :gap
|
||||
:insets {:top false}
|
||||
:component messages-gap/preview-messages-gap}]
|
||||
:component messages-gap/preview-messages-gap}
|
||||
{:name :system-messages
|
||||
:insets {:top false}
|
||||
:component system-message/preview-system-message}]
|
||||
:navigation [{:name :bottom-nav-tab
|
||||
:insets {:top false}
|
||||
:component bottom-nav-tab/preview-bottom-nav-tab}
|
||||
{:name :top-nav
|
||||
:insets {:top false}
|
||||
:component top-nav/preview-top-nav}]
|
||||
:component top-nav/preview-top-nav}
|
||||
{:name :page-nav
|
||||
:insets {:top false}
|
||||
:component page-nav/preview-page-nav}]
|
||||
:notifications [{:name :activity-logs
|
||||
:insets {:top false}
|
||||
:component activity-logs/preview-activity-logs}]
|
||||
@@ -134,9 +142,9 @@
|
||||
:tags [{:name :context-tags
|
||||
:insets {:top false}
|
||||
:component context-tags/preview-context-tags}
|
||||
{:name :filter-tags
|
||||
{:name :tags
|
||||
:insets {:top false}
|
||||
:component filter-tags/preview-filter-tags}
|
||||
:component tags/preview-tags}
|
||||
{:name :permission-tag
|
||||
:insets {:top false}
|
||||
:component permission-tag/preview-permission-tag}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
(ns quo2.screens.messages.system-message
|
||||
(:require [reagent.core :as reagent]
|
||||
[status-im.react-native.resources :as resources]
|
||||
[quo.react-native :as rn]
|
||||
[quo.previews.preview :as preview]
|
||||
[quo2.components.messages.system-message :as system-message]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(def descriptor [{:label "Message Type"
|
||||
:key :type
|
||||
:type :select
|
||||
:options [{:value "Message pinned"
|
||||
:key :pinned}
|
||||
{:value "User added"
|
||||
:key :added}
|
||||
{:value "Message deleted"
|
||||
:key :deleted}]}
|
||||
{:label "Action"
|
||||
:key :action
|
||||
:type :select
|
||||
:options [{:value "none"
|
||||
:key nil}
|
||||
{:value "Undo"
|
||||
:key :undo}]}
|
||||
{:label "Pinned By"
|
||||
:key :pinned-by
|
||||
:type :text}
|
||||
{:label "Content Text"
|
||||
:key :content-text
|
||||
:type :text}
|
||||
{:label "Content Info"
|
||||
:key :content-info
|
||||
:type :text}
|
||||
{:label "Timestamp"
|
||||
:key :timestamp-str
|
||||
:type :text}])
|
||||
|
||||
(defn finalize-state [state]
|
||||
(merge @state
|
||||
{:mentions [{:name "Alicia Keys"
|
||||
:image (resources/get-mock-image :user-picture-female2)}
|
||||
{:name "pedro.eth"
|
||||
:image (resources/get-mock-image :user-picture-male4)}]
|
||||
:content {:text (:content-text @state)
|
||||
:info (:content-info @state)
|
||||
:mentions {:name "Alisher"
|
||||
:image (resources/get-mock-image :user-picture-male5)}}}))
|
||||
(defn preview []
|
||||
(let [state (reagent/atom {:type :deleted
|
||||
:pinned-by "Steve"
|
||||
:content-text "Hello! This is an example of a pinned message!"
|
||||
:content-info "3 photos"
|
||||
:timestamp-str "09:41"})]
|
||||
(fn []
|
||||
[rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!}
|
||||
[rn/view {:padding-bottom 150}
|
||||
[preview/customizer state descriptor]
|
||||
[rn/view {:padding-vertical 60
|
||||
:align-items :center}
|
||||
[system-message/system-message (finalize-state state)]]]])))
|
||||
|
||||
(defn preview-system-message []
|
||||
[rn/view {:background-color (colors/theme-colors colors/white colors/neutral-90)
|
||||
:flex 1}
|
||||
[rn/flat-list {:flex 1
|
||||
:header [preview]
|
||||
:key-fn str
|
||||
:keyboardShouldPersistTaps :always}]])
|
||||
@@ -0,0 +1,106 @@
|
||||
(ns quo2.screens.navigation.page-nav
|
||||
(:require [quo.previews.preview :as preview]
|
||||
[quo.react-native :as rn]
|
||||
[quo2.components.navigation.page-nav :as quo2]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(def ^:private descriptor
|
||||
[{:label "Page nav variation"
|
||||
:key :selected-variation
|
||||
:type :select
|
||||
:options [{:key :text-only?
|
||||
:value "Text only"}
|
||||
{:key :align-left?
|
||||
:value "Align Left"}
|
||||
{:key :align-left-top-down-text?
|
||||
:value "Align left top down text?"}
|
||||
{:key :align-left-with-icon?
|
||||
:value "Align Left with icon ?"}
|
||||
{:key :one-icon-align-left?
|
||||
:value "One icon on the left ?"}
|
||||
{:key :one-icon-align-right?
|
||||
:value "One icon on the right ?"}
|
||||
{:key :two-icons?
|
||||
:value "Two icons ?"}
|
||||
{:key :user-icon?
|
||||
:value "User icon ?"}
|
||||
{:key :empty?
|
||||
:value "Empty ?"}]}
|
||||
{:label "Number of right icons"
|
||||
:key :number-of-right-icons
|
||||
:type :select
|
||||
:options [{:key 1
|
||||
:value 1}
|
||||
{:key 2
|
||||
:value 2}
|
||||
{:key 3
|
||||
:value 3}]}])
|
||||
|
||||
(def ^:private selected-variation (reagent/atom {:selected-variation :text-only?
|
||||
:number-of-right-icons 1}))
|
||||
|
||||
(defn- cool-preview []
|
||||
(let [right-icon {:background-color (if (colors/dark?)
|
||||
colors/neutral-80
|
||||
colors/neutral-20)
|
||||
:icon :main-icons/placeholder
|
||||
:icon-color nil}
|
||||
base-props {:horizontal-description? true
|
||||
:one-icon-align-left? true
|
||||
:align-mid? false
|
||||
:page-nav-color :transparent
|
||||
:page-nav-background-uri ""
|
||||
:mid-section-type :text-with-description
|
||||
:mid-section-icon :main-icons/placeholder
|
||||
:mid-section-main-text "Status"
|
||||
:mid-section-left-icon :main-icons/placeholder
|
||||
:mid-section-right-icon :main-icons/placeholder
|
||||
:mid-section-description "SNT"
|
||||
:mid-section-description-color "black"
|
||||
:mid-section-description-icon :main-icons/placeholder
|
||||
:mid-section-description-user-icon "https://i.picsum.photos/id/810/200/300.jpg?hmac=HgwlXd-OaLOAqhGyCiZDUb_75EgUI4u0GtS7nfgxd8s"
|
||||
:left-section-icon :main-icons/unlocked
|
||||
:left-section-icon-background-color (if (colors/dark?)
|
||||
colors/neutral-80
|
||||
colors/neutral-20)}
|
||||
create-variation #(merge %1 %2)
|
||||
variations {:text-only? base-props
|
||||
:align-left? (create-variation base-props {:align-mid? true})
|
||||
:one-icon-align-left? (create-variation base-props {:one-icon-align-left? true
|
||||
:mid-section-type :text-with-one-icon})
|
||||
:one-icon-align-right? (create-variation base-props {:one-icon-align-left? false
|
||||
:mid-section-type :text-with-one-icon})
|
||||
:two-icons? (create-variation base-props {:mid-section-type :text-with-two-icons})
|
||||
:user-icon? (create-variation base-props {:align-mid? true
|
||||
:horizontal-description? false
|
||||
:mid-section-type :text-with-one-icon})
|
||||
:empty? (create-variation base-props {:mid-section-main-text ""
|
||||
:mid-section-description ""})
|
||||
:align-left-with-icon? (create-variation base-props {:align-mid? true
|
||||
:mid-section-type :text-with-one-icon})
|
||||
:align-left-top-down-text? (create-variation base-props {:align-mid? true
|
||||
:horizontal-description? false
|
||||
:mid-section-type :text-with-description})}
|
||||
state (reagent/atom (-> (get variations (:selected-variation @selected-variation))
|
||||
(assoc :right-section-icons (repeat (:number-of-right-icons @selected-variation) right-icon))))]
|
||||
(fn []
|
||||
[rn/view {:margin-bottom 50
|
||||
:padding 16}
|
||||
[rn/view {:flex 1}
|
||||
[preview/customizer selected-variation descriptor]]
|
||||
[rn/view {:padding-vertical 30
|
||||
:flex-direction :row
|
||||
:justify-content :center}
|
||||
[quo2/page-nav @state]]])))
|
||||
|
||||
(def ^:private trackable-cool-preview (reagent/track cool-preview selected-variation))
|
||||
|
||||
(defn preview-page-nav []
|
||||
[rn/view {:background-color (colors/theme-colors colors/white
|
||||
colors/neutral-90)
|
||||
:flex 1}
|
||||
[rn/flat-list {:flex 1
|
||||
:keyboardShouldPersistTaps :always
|
||||
:header [@trackable-cool-preview]
|
||||
:key-fn str}]])
|
||||
@@ -14,8 +14,7 @@
|
||||
[rn/view {:padding-vertical 60
|
||||
:align-items :center}
|
||||
[quo2/disclaimer
|
||||
{:checked? @checked?
|
||||
:container-style {:margin-bottom 40}
|
||||
{:container-style {:margin-bottom 40}
|
||||
:on-change #(swap! checked? not)}
|
||||
"I agree with the community rules"]
|
||||
[button/button
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
(defn preview-permission-tag []
|
||||
[rn/view {:background-color (colors/theme-colors
|
||||
colors/white
|
||||
colors/neutral-80)
|
||||
colors/neutral-90)
|
||||
:flex 1}
|
||||
[rn/flat-list {:flex 1
|
||||
:keyboardShouldPersistTaps :always
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
(ns quo2.screens.tags.filter-tags
|
||||
(ns quo2.screens.tags.tags
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo.previews.preview :as preview]
|
||||
[status-im.ui.components.react :as react]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.tags.filter-tags :as quo2.tags]
|
||||
[quo2.components.tags.tags :as tags]
|
||||
[status-im.react-native.resources :as resources]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
@@ -70,15 +70,15 @@
|
||||
:align-items :center
|
||||
:position :absolute
|
||||
:padding-horizontal 10}
|
||||
[quo2.tags/tags (merge @state
|
||||
{:default-active 1
|
||||
:data [{:id 1 :tag-label "Music" :resource (resources/get-image :music)}
|
||||
{:id 2 :tag-label "Lifestyle" :resource (resources/get-image :lifestyle)}
|
||||
{:id 3 :tag-label "Podcasts" :resource (resources/get-image :podcasts)}]})]]]]])))
|
||||
(defn preview-filter-tags []
|
||||
[tags/tags (merge @state
|
||||
{:default-active 1
|
||||
:data [{:id 1 :tag-label "Music" :resource (resources/get-image :music)}
|
||||
{:id 2 :tag-label "Lifestyle" :resource (resources/get-image :lifestyle)}
|
||||
{:id 3 :tag-label "Podcasts" :resource (resources/get-image :podcasts)}]})]]]]])))
|
||||
(defn preview-tags []
|
||||
[rn/view {:flex 1
|
||||
:background-color (colors/theme-colors
|
||||
colors/neutral-5
|
||||
colors/white
|
||||
colors/neutral-90)}
|
||||
[rn/flat-list {:flex 1
|
||||
:keyboardShouldPersistTaps :always
|
||||
@@ -1,97 +1,658 @@
|
||||
(ns status-im.activity-center.core
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.data-store.activities :as data-store.activities]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.datetime :as utils.datetime]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
;;;; Notification reconciliation
|
||||
|
||||
(def response-notifications
|
||||
[{:replyMessage nil,
|
||||
:read true,
|
||||
:dismissed false,
|
||||
:chatId
|
||||
"0x047c654a4b35e4c3605c0eacf68364dd43140616e4d96ae362c4bf7c5ae3f1a3c03ad5935ae1face369bf96edda4ec02459b33964f1a7781036824f1e528c21b2f",
|
||||
:name "0x047c65",
|
||||
:accepted false,
|
||||
:type 5,
|
||||
:author
|
||||
"0x047c654a4b35e4c3605c0eacf68364dd43140616e4d96ae362c4bf7c5ae3f1a3c03ad5935ae1face369bf96edda4ec02459b33964f1a7781036824f1e528c21b2f",
|
||||
:lastMessage nil,
|
||||
:id
|
||||
"0x047c654a4b35e4c3605c0eacf68364dd43140616e4d96ae362c4bf7c5ae3f1a3c03ad5935ae1face369bf96edda4ec02459b33964f1a7781036824f1e528c21b2f20",
|
||||
:timestamp 1665167287000,
|
||||
:message
|
||||
{:messageType 0,
|
||||
:identicon
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAeElEQVR4nOzR0QnCMBhGURV3cR53cQx3cR6naRfoQ6EtXH7OeQwh5PI9bkMIqRFSI6RGSI2QGiE1QmqE1AipEVIzJuS59+L3/1u2zj+v9/2Mjxx9f8wiQmqE1AipEVIjpEZIjZAaIVxkzCJCaoTUCKkRUrMGAAD////ICGUlBQ5nAAAAAElFTkSuQmCC",
|
||||
:replace "",
|
||||
:chatId "",
|
||||
:displayName "",
|
||||
:lineCount 0,
|
||||
:contactRequestState 2,
|
||||
:whisperTimestamp 1665167287000,
|
||||
:alias "Darkseagreen Colorless Honeybee",
|
||||
:seen true,
|
||||
:gapParameters {},
|
||||
:from
|
||||
"0x047c654a4b35e4c3605c0eacf68364dd43140616e4d96ae362c4bf7c5ae3f1a3c03ad5935ae1face369bf96edda4ec02459b33964f1a7781036824f1e528c21b2f",
|
||||
:id
|
||||
"0x047c654a4b35e4c3605c0eacf68364dd43140616e4d96ae362c4bf7c5ae3f1a3c03ad5935ae1face369bf96edda4ec02459b33964f1a7781036824f1e528c21b2f20",
|
||||
:parsedText
|
||||
[{:type "paragraph",
|
||||
:children [{:literal "Please add me to your contacts"}]}],
|
||||
:contentType 11,
|
||||
:clock 0,
|
||||
:localChatId "",
|
||||
:timestamp 0,
|
||||
:ensName "",
|
||||
:quotedMessage nil,
|
||||
:rtl false,
|
||||
:responseTo "",
|
||||
:text "Please add me to your contacts"}}
|
||||
{:replyMessage nil,
|
||||
:read true,
|
||||
:dismissed false,
|
||||
:chatId
|
||||
"0x04892d0884fb44a940853ba988ce14b4c7c18351ea617bf29a5ceaa9eca03a8e1770f66282d9af9f5644578f8fcb8f4ff8fd926d8b389db4384ec91492aee8406d",
|
||||
:name "0x04892d",
|
||||
:accepted false,
|
||||
:type 5,
|
||||
:author
|
||||
"0x04892d0884fb44a940853ba988ce14b4c7c18351ea617bf29a5ceaa9eca03a8e1770f66282d9af9f5644578f8fcb8f4ff8fd926d8b389db4384ec91492aee8406d",
|
||||
:lastMessage nil,
|
||||
:id
|
||||
"0x04892d0884fb44a940853ba988ce14b4c7c18351ea617bf29a5ceaa9eca03a8e1770f66282d9af9f5644578f8fcb8f4ff8fd926d8b389db4384ec91492aee8406d20",
|
||||
:timestamp 1665100538000,
|
||||
:message
|
||||
{:messageType 0,
|
||||
:identicon
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAiElEQVR4nOzWUQmAYAwHcRW7GMwKxrCCwUyjBRSUMTj+3O/Rh0+Owdg0hDCExhAaQ2gMoYkJmbse3o/zevq+rcvY8b+YiRhCYwjN5w3SvYWq78dMxBAaQ2i8tWgMoTGExhAaQ2gMoSnfPW831V/VGyxmIobQGKImMRMxhMYQGkNoDKG5AwAA//9P5BRl2i6T5QAAAABJRU5ErkJggg==",
|
||||
:replace "",
|
||||
:chatId "",
|
||||
:displayName "",
|
||||
:lineCount 0,
|
||||
:contactRequestState 2,
|
||||
:whisperTimestamp 1665100538000,
|
||||
:alias "Amused Neighboring Senegalpython",
|
||||
:seen true,
|
||||
:gapParameters {},
|
||||
:from
|
||||
"0x04892d0884fb44a940853ba988ce14b4c7c18351ea617bf29a5ceaa9eca03a8e1770f66282d9af9f5644578f8fcb8f4ff8fd926d8b389db4384ec91492aee8406d",
|
||||
:id
|
||||
"0x04892d0884fb44a940853ba988ce14b4c7c18351ea617bf29a5ceaa9eca03a8e1770f66282d9af9f5644578f8fcb8f4ff8fd926d8b389db4384ec91492aee8406d20",
|
||||
:parsedText
|
||||
[{:type "paragraph",
|
||||
:children [{:literal "Please add me to your contacts"}]}],
|
||||
:contentType 11,
|
||||
:clock 0,
|
||||
:localChatId "",
|
||||
:timestamp 0,
|
||||
:ensName "",
|
||||
:quotedMessage nil,
|
||||
:rtl false,
|
||||
:responseTo "",
|
||||
:text "Please add me to your contacts"}}
|
||||
{:replyMessage nil,
|
||||
:read true,
|
||||
:dismissed false,
|
||||
:chatId
|
||||
"0x04d7b7648d9ef3cacdb64755d2fed9a70e4eb1e287e5f59c58cb3eda2485bdc0d0fb0f88f87dcd568637f2213205defee3fbd864bfe279bf87e43301a32647b88f",
|
||||
:name "0x04d7b7",
|
||||
:accepted false,
|
||||
:type 5,
|
||||
:author
|
||||
"0x04d7b7648d9ef3cacdb64755d2fed9a70e4eb1e287e5f59c58cb3eda2485bdc0d0fb0f88f87dcd568637f2213205defee3fbd864bfe279bf87e43301a32647b88f",
|
||||
:lastMessage nil,
|
||||
:id
|
||||
"0x04d7b7648d9ef3cacdb64755d2fed9a70e4eb1e287e5f59c58cb3eda2485bdc0d0fb0f88f87dcd568637f2213205defee3fbd864bfe279bf87e43301a32647b88f20",
|
||||
:timestamp 1665097520000,
|
||||
:message
|
||||
{:messageType 0,
|
||||
:identicon
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAgUlEQVR4nOzWwQmAMBAFURV7sRI7sgw7shKr0XtASMCF4TPvHALDXv4yhTCExhAaQ2gMoYkJWXsfnvf1jHx8bPtc+U8r5iKG0BhCYwiNITSG0HRvrS+jm+qvDdaKuYghNIaoSMxFDKExhMYQGkNoDKExhMYQGkNoDKExhOYNAAD//0iXEGcISwhOAAAAAElFTkSuQmCC",
|
||||
:replace "",
|
||||
:chatId "",
|
||||
:displayName "",
|
||||
:lineCount 0,
|
||||
:contactRequestState 3,
|
||||
:whisperTimestamp 1665097520000,
|
||||
:alias "Honeydew Golden Amazondolphin",
|
||||
:seen true,
|
||||
:gapParameters {},
|
||||
:from
|
||||
"0x04d7b7648d9ef3cacdb64755d2fed9a70e4eb1e287e5f59c58cb3eda2485bdc0d0fb0f88f87dcd568637f2213205defee3fbd864bfe279bf87e43301a32647b88f",
|
||||
:id
|
||||
"0x04d7b7648d9ef3cacdb64755d2fed9a70e4eb1e287e5f59c58cb3eda2485bdc0d0fb0f88f87dcd568637f2213205defee3fbd864bfe279bf87e43301a32647b88f20",
|
||||
:parsedText
|
||||
[{:type "paragraph",
|
||||
:children [{:literal "Please add me to your contacts"}]}],
|
||||
:contentType 11,
|
||||
:clock 0,
|
||||
:localChatId "",
|
||||
:timestamp 0,
|
||||
:ensName "",
|
||||
:quotedMessage nil,
|
||||
:rtl false,
|
||||
:responseTo "",
|
||||
:text "Please add me to your contacts"}}
|
||||
{:replyMessage nil,
|
||||
:read true,
|
||||
:dismissed false,
|
||||
:chatId
|
||||
"0x040185b964f42f2f11a07bb3275478605cf9f5a2f79af8a15d30baeaa9fe47e19d636d6461b797cfd2edf57a51e7be912c81e60698758bf145eaa611ae30f921df",
|
||||
:name "0x040185",
|
||||
:accepted false,
|
||||
:type 5,
|
||||
:author
|
||||
"0x040185b964f42f2f11a07bb3275478605cf9f5a2f79af8a15d30baeaa9fe47e19d636d6461b797cfd2edf57a51e7be912c81e60698758bf145eaa611ae30f921df",
|
||||
:lastMessage nil,
|
||||
:id
|
||||
"0x040185b964f42f2f11a07bb3275478605cf9f5a2f79af8a15d30baeaa9fe47e19d636d6461b797cfd2edf57a51e7be912c81e60698758bf145eaa611ae30f921df20",
|
||||
:timestamp 1665097287000,
|
||||
:message
|
||||
{:messageType 0,
|
||||
:identicon
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAfklEQVR4nOzYsQmAMABEURV3sXI7x3A7K6fRBSyEcPA5/istIp+DFFmmEobQGEJjCI0hNDUha+rg876er+/Hts+J/9UsYgiNITS/b5D0LTR6fs0ihtAYQmMIjSE0hiikZhFDaAyh8V2LxhAaQxRSs4ghNIbQGEJjCM0bAAD//5fvEGQwrcNWAAAAAElFTkSuQmCC",
|
||||
:replace "",
|
||||
:chatId "",
|
||||
:displayName "",
|
||||
:lineCount 0,
|
||||
:contactRequestState 2,
|
||||
:whisperTimestamp 1665097287000,
|
||||
:alias "Quizzical Private Anteater",
|
||||
:seen true,
|
||||
:gapParameters {},
|
||||
:from
|
||||
"0x040185b964f42f2f11a07bb3275478605cf9f5a2f79af8a15d30baeaa9fe47e19d636d6461b797cfd2edf57a51e7be912c81e60698758bf145eaa611ae30f921df",
|
||||
:id
|
||||
"0x040185b964f42f2f11a07bb3275478605cf9f5a2f79af8a15d30baeaa9fe47e19d636d6461b797cfd2edf57a51e7be912c81e60698758bf145eaa611ae30f921df20",
|
||||
:parsedText
|
||||
[{:type "paragraph",
|
||||
:children [{:literal "Please add me to your contacts"}]}],
|
||||
:contentType 11,
|
||||
:clock 0,
|
||||
:localChatId "",
|
||||
:timestamp 0,
|
||||
:ensName "",
|
||||
:quotedMessage nil,
|
||||
:rtl false,
|
||||
:responseTo "",
|
||||
:text "Please add me to your contacts"}}
|
||||
{:replyMessage nil,
|
||||
:read true,
|
||||
:dismissed false,
|
||||
:chatId
|
||||
"0x046bf5a02fd3a28f82f656bbfbfcaf9bec021b3617c3dc043dc8353f4f6f009ac53df5eba2a2ae63b6f77413a1ad9a054f5c4509ce68b4bf0e688186ab95d2fca7",
|
||||
:name "0x046bf5",
|
||||
:accepted false,
|
||||
:type 5,
|
||||
:author
|
||||
"0x046bf5a02fd3a28f82f656bbfbfcaf9bec021b3617c3dc043dc8353f4f6f009ac53df5eba2a2ae63b6f77413a1ad9a054f5c4509ce68b4bf0e688186ab95d2fca7",
|
||||
:lastMessage nil,
|
||||
:id
|
||||
"0x046bf5a02fd3a28f82f656bbfbfcaf9bec021b3617c3dc043dc8353f4f6f009ac53df5eba2a2ae63b6f77413a1ad9a054f5c4509ce68b4bf0e688186ab95d2fca720",
|
||||
:timestamp 1665089696000,
|
||||
:message
|
||||
{:messageType 0,
|
||||
:identicon
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAgklEQVR4nOzWQQpAYABEYeQu9s7mGM5m7zTsZaE09Zret5R+vab+TEMJQ2gMoTGExhCampA5dfB+Htfb821Zx8T3ahYxhMYQms+3VvoW+nt+zSKG0BiikJpFDKExhCb2r5V+/6lmEUNoDFFIzSKG0BhCYwiNITSG0BhCYwjNHQAA///zHxhnUGfT1gAAAABJRU5ErkJggg==",
|
||||
:replace "",
|
||||
:chatId "",
|
||||
:displayName "",
|
||||
:lineCount 0,
|
||||
:contactRequestState 2,
|
||||
:whisperTimestamp 1665089696000,
|
||||
:alias "Growling Marvelous Parrot",
|
||||
:seen true,
|
||||
:gapParameters {},
|
||||
:from
|
||||
"0x046bf5a02fd3a28f82f656bbfbfcaf9bec021b3617c3dc043dc8353f4f6f009ac53df5eba2a2ae63b6f77413a1ad9a054f5c4509ce68b4bf0e688186ab95d2fca7",
|
||||
:id
|
||||
"0x046bf5a02fd3a28f82f656bbfbfcaf9bec021b3617c3dc043dc8353f4f6f009ac53df5eba2a2ae63b6f77413a1ad9a054f5c4509ce68b4bf0e688186ab95d2fca720",
|
||||
:parsedText
|
||||
[{:type "paragraph",
|
||||
:children [{:literal "Please add me to your contacts"}]}],
|
||||
:contentType 11,
|
||||
:clock 0,
|
||||
:localChatId "",
|
||||
:timestamp 0,
|
||||
:ensName "",
|
||||
:quotedMessage nil,
|
||||
:rtl false,
|
||||
:responseTo "",
|
||||
:text "Please add me to your contacts"}}
|
||||
{:replyMessage nil,
|
||||
:read true,
|
||||
:dismissed false,
|
||||
:chatId
|
||||
"0x044a2e346d30266b1b0a05def37b91a1578ab5e420c6971da1bde04d125c40b40a3ea1fe9519398b3fb354986096700f5887b67bb82a93f6e3dd151edc633b8f67",
|
||||
:name "0x044a2e",
|
||||
:accepted false,
|
||||
:type 5,
|
||||
:author
|
||||
"0x044a2e346d30266b1b0a05def37b91a1578ab5e420c6971da1bde04d125c40b40a3ea1fe9519398b3fb354986096700f5887b67bb82a93f6e3dd151edc633b8f67",
|
||||
:lastMessage nil,
|
||||
:id
|
||||
"0x044a2e346d30266b1b0a05def37b91a1578ab5e420c6971da1bde04d125c40b40a3ea1fe9519398b3fb354986096700f5887b67bb82a93f6e3dd151edc633b8f6720",
|
||||
:timestamp 1665075842000,
|
||||
:message
|
||||
{:messageType 0,
|
||||
:identicon
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAiUlEQVR4nOzWwQmEQBAF0V0xF2Pzbhjejc1oNIKRkR6h+NS7KxYNH6dfCENoDKExhMYQGkNoDKExhCYmZK6+4Dj3a8SHrMv2rzwfcxFDaAyhKS3Fk9aaVdepJeYihtAYQtO9IKP+qd7qXbmYixhCY4g+EnMRQ2gMoTGExhAaQ2gMoTGE5g4AAP//09cMY3Qm6ZsAAAAASUVORK5CYII=",
|
||||
:replace "",
|
||||
:chatId "",
|
||||
:displayName "",
|
||||
:lineCount 0,
|
||||
:contactRequestState 2,
|
||||
:whisperTimestamp 1665075842000,
|
||||
:alias "Stable Sniveling Baldeagle",
|
||||
:seen true,
|
||||
:gapParameters {},
|
||||
:from
|
||||
"0x044a2e346d30266b1b0a05def37b91a1578ab5e420c6971da1bde04d125c40b40a3ea1fe9519398b3fb354986096700f5887b67bb82a93f6e3dd151edc633b8f67",
|
||||
:id
|
||||
"0x044a2e346d30266b1b0a05def37b91a1578ab5e420c6971da1bde04d125c40b40a3ea1fe9519398b3fb354986096700f5887b67bb82a93f6e3dd151edc633b8f6720",
|
||||
:parsedText
|
||||
[{:type "paragraph",
|
||||
:children [{:literal "Please add me to your contacts"}]}],
|
||||
:contentType 11,
|
||||
:clock 0,
|
||||
:localChatId "",
|
||||
:timestamp 0,
|
||||
:ensName "",
|
||||
:quotedMessage nil,
|
||||
:rtl false,
|
||||
:responseTo "",
|
||||
:text "Please add me to your contacts"}}
|
||||
{:replyMessage nil,
|
||||
:read true,
|
||||
:dismissed false,
|
||||
:chatId
|
||||
"0x043e742bfa6dcd0c93689727ad02ac9e9d418f2a03a783b40d72bc8f0b3144707022ef84ae59a787977275c27ac9421ab094b8fb3908bb80f729f483eff38d84fc",
|
||||
:name "0x043e74",
|
||||
:accepted false,
|
||||
:type 5,
|
||||
:author
|
||||
"0x043e742bfa6dcd0c93689727ad02ac9e9d418f2a03a783b40d72bc8f0b3144707022ef84ae59a787977275c27ac9421ab094b8fb3908bb80f729f483eff38d84fc",
|
||||
:lastMessage nil,
|
||||
:id
|
||||
"0x043e742bfa6dcd0c93689727ad02ac9e9d418f2a03a783b40d72bc8f0b3144707022ef84ae59a787977275c27ac9421ab094b8fb3908bb80f729f483eff38d84fc20",
|
||||
:timestamp 1665075615000,
|
||||
:message
|
||||
{:messageType 0,
|
||||
:identicon
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAkklEQVR4nOzWwQnDMBAF0SSkl3SQylJGKksHqca+GwvWyGuGz7yzDho+CD1uIQyhMYTGEBpDaGJCntWD399/6b3Kvs/7da+ci1nEEBpD1CRmEUNoDKGZ/muN/kLd57diFjGExhCa8qt1VPW1OUvMIobQGEIz/bJc/acaiVnEEBpD1CRmEUNoDKExhMYQmjUAAP//o5AZOL4SLM8AAAAASUVORK5CYII=",
|
||||
:replace "",
|
||||
:chatId "",
|
||||
:displayName "",
|
||||
:lineCount 0,
|
||||
:contactRequestState 2,
|
||||
:whisperTimestamp 1665075615000,
|
||||
:alias "Trusty Nautical Zethusspinipes",
|
||||
:seen true,
|
||||
:gapParameters {},
|
||||
:from
|
||||
"0x043e742bfa6dcd0c93689727ad02ac9e9d418f2a03a783b40d72bc8f0b3144707022ef84ae59a787977275c27ac9421ab094b8fb3908bb80f729f483eff38d84fc",
|
||||
:id
|
||||
"0x043e742bfa6dcd0c93689727ad02ac9e9d418f2a03a783b40d72bc8f0b3144707022ef84ae59a787977275c27ac9421ab094b8fb3908bb80f729f483eff38d84fc20",
|
||||
:parsedText
|
||||
[{:type "paragraph",
|
||||
:children [{:literal "Please add me to your contacts"}]}],
|
||||
:contentType 11,
|
||||
:clock 0,
|
||||
:localChatId "",
|
||||
:timestamp 0,
|
||||
:ensName "",
|
||||
:quotedMessage nil,
|
||||
:rtl false,
|
||||
:responseTo "",
|
||||
:text "Please add me to your contacts"}}
|
||||
{:replyMessage nil,
|
||||
:read true,
|
||||
:dismissed false,
|
||||
:chatId
|
||||
"0x0435b461849c0af696b2f71ad95e9c57053722a2390c833b304b4284b00466f52f940004d45f0fee149cf6ed65b8ff856921d40059344a01e68b2fa9dae82e4900",
|
||||
:name "0x0435b4",
|
||||
:accepted false,
|
||||
:type 5,
|
||||
:author
|
||||
"0x0435b461849c0af696b2f71ad95e9c57053722a2390c833b304b4284b00466f52f940004d45f0fee149cf6ed65b8ff856921d40059344a01e68b2fa9dae82e4900",
|
||||
:lastMessage nil,
|
||||
:id
|
||||
"0x0435b461849c0af696b2f71ad95e9c57053722a2390c833b304b4284b00466f52f940004d45f0fee149cf6ed65b8ff856921d40059344a01e68b2fa9dae82e490020",
|
||||
:timestamp 1665074686000,
|
||||
:message
|
||||
{:messageType 0,
|
||||
:identicon
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAgUlEQVR4nOzWsQmAMBgFYRV3cTwbx7BxPKfRPigYJOR43FdKEI7X/NMQwhAaQ2gMoTGEJiZk/vrw2M/r6fu6LWOP96WYRQyhMUSNxCxiCI0hNL9vrTe1N1Xtf0oxixhCYwiNITSG0BiiRmIWMYTGEBpDaAyhMYTGEBpDaO4AAAD//5TbFGUr+nc/AAAAAElFTkSuQmCC",
|
||||
:replace "",
|
||||
:chatId "",
|
||||
:displayName "",
|
||||
:lineCount 0,
|
||||
:contactRequestState 3,
|
||||
:whisperTimestamp 1665074686000,
|
||||
:alias "Remote Vapid Honeyeater",
|
||||
:seen true,
|
||||
:gapParameters {},
|
||||
:from
|
||||
"0x0435b461849c0af696b2f71ad95e9c57053722a2390c833b304b4284b00466f52f940004d45f0fee149cf6ed65b8ff856921d40059344a01e68b2fa9dae82e4900",
|
||||
:id
|
||||
"0x0435b461849c0af696b2f71ad95e9c57053722a2390c833b304b4284b00466f52f940004d45f0fee149cf6ed65b8ff856921d40059344a01e68b2fa9dae82e490020",
|
||||
:parsedText
|
||||
[{:type "paragraph",
|
||||
:children [{:literal "Please add me to your contacts"}]}],
|
||||
:contentType 11,
|
||||
:clock 0,
|
||||
:localChatId "",
|
||||
:timestamp 0,
|
||||
:ensName "",
|
||||
:quotedMessage nil,
|
||||
:rtl false,
|
||||
:responseTo "",
|
||||
:text "Please add me to your contacts"}}
|
||||
{:replyMessage nil,
|
||||
:read true,
|
||||
:dismissed false,
|
||||
:chatId
|
||||
"0x04f18135a0d5e904d5a877c24ad286d227b1a91ee583d61f16ba71de801d9d92df330696c5788b0810999c4710271584ec84eac223bc5f7f9eaf483b0ed2bd2093",
|
||||
:name "0x04f181",
|
||||
:accepted false,
|
||||
:type 5,
|
||||
:author
|
||||
"0x04f18135a0d5e904d5a877c24ad286d227b1a91ee583d61f16ba71de801d9d92df330696c5788b0810999c4710271584ec84eac223bc5f7f9eaf483b0ed2bd2093",
|
||||
:lastMessage nil,
|
||||
:id
|
||||
"0x04f18135a0d5e904d5a877c24ad286d227b1a91ee583d61f16ba71de801d9d92df330696c5788b0810999c4710271584ec84eac223bc5f7f9eaf483b0ed2bd209320",
|
||||
:timestamp 1665073831000,
|
||||
:message
|
||||
{:messageType 0,
|
||||
:identicon
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAkElEQVR4nOzY0QmAMAwGYRV3cQI3cww3cwKn0QUUAm3h+LnvUYpwBELpMoUwhMYQGkNoDKGJCVlbf3Cd9/P1fT+2ucf5qpiJGEJjCE15U/xtm9Gq2yxmIobQGELTfNfqdadq3YoxEzGExhAaQ2gMoTGEZti7Vq/zVTETMYTGEA0SMxFDaAyhMYTGEJo3AAD//6aKGsYhz2MYAAAAAElFTkSuQmCC",
|
||||
:replace "",
|
||||
:chatId "",
|
||||
:displayName "",
|
||||
:lineCount 0,
|
||||
:contactRequestState 2,
|
||||
:whisperTimestamp 1665073831000,
|
||||
:alias "Nippy Idolized Armyworm",
|
||||
:seen true,
|
||||
:gapParameters {},
|
||||
:from
|
||||
"0x04f18135a0d5e904d5a877c24ad286d227b1a91ee583d61f16ba71de801d9d92df330696c5788b0810999c4710271584ec84eac223bc5f7f9eaf483b0ed2bd2093",
|
||||
:id
|
||||
"0x04f18135a0d5e904d5a877c24ad286d227b1a91ee583d61f16ba71de801d9d92df330696c5788b0810999c4710271584ec84eac223bc5f7f9eaf483b0ed2bd209320",
|
||||
:parsedText
|
||||
[{:type "paragraph",
|
||||
:children [{:literal "Please add me to your contacts"}]}],
|
||||
:contentType 11,
|
||||
:clock 0,
|
||||
:localChatId "",
|
||||
:timestamp 0,
|
||||
:ensName "",
|
||||
:quotedMessage nil,
|
||||
:rtl false,
|
||||
:responseTo "",
|
||||
:text "Please add me to your contacts"}}
|
||||
{:replyMessage nil,
|
||||
:read true,
|
||||
:dismissed false,
|
||||
:chatId
|
||||
"0x04b5aed12d7de0f72a7e3494b7d3ab20b316a9fde5732a3eb1d0f41a957900f764d725a3e725152c37bb615ab2251349ef84e92a145333d8ece9a598cae75681e7",
|
||||
:name "0x04b5ae",
|
||||
:accepted false,
|
||||
:type 5,
|
||||
:author
|
||||
"0x04b5aed12d7de0f72a7e3494b7d3ab20b316a9fde5732a3eb1d0f41a957900f764d725a3e725152c37bb615ab2251349ef84e92a145333d8ece9a598cae75681e7",
|
||||
:lastMessage nil,
|
||||
:id
|
||||
"0x04b5aed12d7de0f72a7e3494b7d3ab20b316a9fde5732a3eb1d0f41a957900f764d725a3e725152c37bb615ab2251349ef84e92a145333d8ece9a598cae75681e720",
|
||||
:timestamp 1665064413000,
|
||||
:message
|
||||
{:messageType 0,
|
||||
:identicon
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAm0lEQVR4nOzXsQnDMBBA0SRkl7QZLmNkOLeexm5cCcmcsIQ/x3+lUfM5OM6vRxKG0BhCYwiNITRpQt7Rh/912Wrff5/v8473pTQTMYTGEJrQRjjT2ja9otupJc1EDKExhGbardXLW+tgCI0hNOGt1TLqT/CqNBMxhMYQmiF3Us3s26yUZiKG0BiiSdJMxBAaQ2gMoTGEZg8AAP//9ecjm0YKWeEAAAAASUVORK5CYII=",
|
||||
:replace "",
|
||||
:chatId "",
|
||||
:displayName "",
|
||||
:lineCount 0,
|
||||
:contactRequestState 2,
|
||||
:whisperTimestamp 1665064413000,
|
||||
:alias "Scary Quickwitted Wrasse",
|
||||
:seen true,
|
||||
:gapParameters {},
|
||||
:from
|
||||
"0x04b5aed12d7de0f72a7e3494b7d3ab20b316a9fde5732a3eb1d0f41a957900f764d725a3e725152c37bb615ab2251349ef84e92a145333d8ece9a598cae75681e7",
|
||||
:id
|
||||
"0x04b5aed12d7de0f72a7e3494b7d3ab20b316a9fde5732a3eb1d0f41a957900f764d725a3e725152c37bb615ab2251349ef84e92a145333d8ece9a598cae75681e720",
|
||||
:parsedText
|
||||
[{:type "paragraph",
|
||||
:children [{:literal "Please add me to your contacts"}]}],
|
||||
:contentType 11,
|
||||
:clock 0,
|
||||
:localChatId "",
|
||||
:timestamp 0,
|
||||
:ensName "",
|
||||
:quotedMessage nil,
|
||||
:rtl false,
|
||||
:responseTo "",
|
||||
:text "Please add me to your contacts"}}])
|
||||
|
||||
(def response-unread-notifications
|
||||
[{:replyMessage nil,
|
||||
:read false,
|
||||
:dismissed false,
|
||||
:chatId
|
||||
"0x047c654a4b35e4c3605c0eacf68364dd43140616e4d96ae362c4bf7c5ae3f1a3c03ad5935ae1face369bf96edda4ec02459b33964f1a7781036824f1e528c21b2f",
|
||||
:name "0x047c65",
|
||||
:accepted false,
|
||||
:type 5,
|
||||
:author
|
||||
"0x047c654a4b35e4c3605c0eacf68364dd43140616e4d96ae362c4bf7c5ae3f1a3c03ad5935ae1face369bf96edda4ec02459b33964f1a7781036824f1e528c21b2f",
|
||||
:lastMessage nil,
|
||||
:id
|
||||
"0x047c654a4b35e4c3605c0eacf68364dd43140616e4d96ae362c4bf7c5ae3f1a3c03ad5935ae1face369bf96edda4ec02459b33964f1a7781036824f1e528c21b2f20",
|
||||
:timestamp 1665167287000,
|
||||
:message
|
||||
{:messageType 0,
|
||||
:identicon
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAeElEQVR4nOzR0QnCMBhGURV3cR53cQx3cR6naRfoQ6EtXH7OeQwh5PI9bkMIqRFSI6RGSI2QGiE1QmqE1AipEVIzJuS59+L3/1u2zj+v9/2Mjxx9f8wiQmqE1AipEVIjpEZIjZAaIVxkzCJCaoTUCKkRUrMGAAD////ICGUlBQ5nAAAAAElFTkSuQmCC",
|
||||
:replace "",
|
||||
:chatId "",
|
||||
:displayName "",
|
||||
:lineCount 0,
|
||||
:contactRequestState 2,
|
||||
:whisperTimestamp 1665167287000,
|
||||
:alias "Darkseagreen Colorless Honeybee",
|
||||
:seen true,
|
||||
:gapParameters {},
|
||||
:from
|
||||
"0x047c654a4b35e4c3605c0eacf68364dd43140616e4d96ae362c4bf7c5ae3f1a3c03ad5935ae1face369bf96edda4ec02459b33964f1a7781036824f1e528c21b2f",
|
||||
:id
|
||||
"0x047c654a4b35e4c3605c0eacf68364dd43140616e4d96ae362c4bf7c5ae3f1a3c03ad5935ae1face369bf96edda4ec02459b33964f1a7781036824f1e528c21b2f20",
|
||||
:parsedText
|
||||
[{:type "paragraph",
|
||||
:children [{:literal "Please add me to your contacts"}]}],
|
||||
:contentType 11,
|
||||
:clock 0,
|
||||
:localChatId "",
|
||||
:timestamp 0,
|
||||
:ensName "",
|
||||
:quotedMessage nil,
|
||||
:rtl false,
|
||||
:responseTo "",
|
||||
:text "Please add me to your contacts"}}])
|
||||
|
||||
(defn- update-notifications
|
||||
[old new]
|
||||
(let [ids-to-be-removed (->> new
|
||||
(filter #(or (:dismissed %) (:accepted %)))
|
||||
(map :id))
|
||||
grouped-new (apply dissoc (group-by :id new) ids-to-be-removed)
|
||||
grouped-old (apply dissoc (group-by :id old) ids-to-be-removed)]
|
||||
(->> (merge grouped-old grouped-new)
|
||||
vals
|
||||
(map first))))
|
||||
"Insert `new-notifications` in `db-notifications`.
|
||||
|
||||
Although correct, this is a naive implementation for reconciling notifications
|
||||
because for every notification in `new-notifications`, linear scans will be
|
||||
performed to remove it and sorting will be performed for every new insertion.
|
||||
If the number of existing notifications cached in the app db becomes
|
||||
~excessively~ big, this implementation will probably need to be revisited."
|
||||
[db-notifications new-notifications]
|
||||
(reduce (fn [acc {:keys [id type read] :as notification}]
|
||||
(let [filter-status (if read :read :unread)]
|
||||
(cond-> (-> acc
|
||||
(update-in [type :read :data]
|
||||
(fn [data]
|
||||
(remove #(= id (:id %)) data)))
|
||||
(update-in [type :unread :data]
|
||||
(fn [data]
|
||||
(remove #(= id (:id %)) data))))
|
||||
(not (or (:dismissed notification) (:accepted notification)))
|
||||
(update-in [type filter-status :data]
|
||||
(fn [data]
|
||||
(->> notification
|
||||
(conj data)
|
||||
(sort-by (juxt :timestamp :id))
|
||||
reverse))))))
|
||||
db-notifications
|
||||
new-notifications))
|
||||
|
||||
(fx/defn notifications-reconcile
|
||||
{:events [:activity-center.notifications/reconcile]}
|
||||
[{:keys [db]} new-notifications]
|
||||
(let [{read-new true
|
||||
unread-new false} (group-by :read new-notifications)
|
||||
read-old (get-in db [:activity-center :notifications-read :data])
|
||||
unread-old (get-in db [:activity-center :notifications-unread :data])]
|
||||
{:db (-> db
|
||||
(assoc-in [:activity-center :notifications-read :data]
|
||||
(update-notifications read-old read-new))
|
||||
(assoc-in [:activity-center :notifications-unread :data]
|
||||
(update-notifications unread-old unread-new)))}))
|
||||
(when (seq new-notifications)
|
||||
{:db (update-in db [:activity-center :notifications]
|
||||
update-notifications new-notifications)}))
|
||||
|
||||
;;;; Notifications fetching and pagination
|
||||
|
||||
(def notifications-per-page
|
||||
20)
|
||||
(def defaults
|
||||
{:filter-status :unread
|
||||
:filter-type constants/activity-center-notification-type-no-type
|
||||
:notifications-per-page 10})
|
||||
|
||||
(def start-or-end-cursor
|
||||
"")
|
||||
|
||||
(defn notifications-group->rpc-method
|
||||
[notifications-group]
|
||||
(if (= notifications-group :notifications-read)
|
||||
(defn- valid-cursor?
|
||||
[cursor]
|
||||
(and (some? cursor)
|
||||
(not= cursor start-or-end-cursor)))
|
||||
|
||||
(defn- filter-status->rpc-method
|
||||
[filter-status]
|
||||
(if (= filter-status :read)
|
||||
"wakuext_readActivityCenterNotifications"
|
||||
"wakuext_unreadActivityCenterNotifications"))
|
||||
|
||||
(defn notifications-read-status->group
|
||||
[status-filter]
|
||||
(if (= status-filter :read)
|
||||
:notifications-read
|
||||
:notifications-unread))
|
||||
|
||||
(fx/defn notifications-fetch
|
||||
[{:keys [db]} cursor notifications-group]
|
||||
(when-not (get-in db [:activity-center notifications-group :loading?])
|
||||
{:db (assoc-in db [:activity-center notifications-group :loading?] true)
|
||||
::json-rpc/call [{:method (notifications-group->rpc-method notifications-group)
|
||||
:params [cursor notifications-per-page]
|
||||
:on-success #(re-frame/dispatch [:activity-center.notifications/fetch-success notifications-group %])
|
||||
:on-error #(re-frame/dispatch [:activity-center.notifications/fetch-error notifications-group %])}]}))
|
||||
[{:keys [db]} {:keys [cursor filter-type filter-status reset-data?]}]
|
||||
(when-not (get-in db [:activity-center :notifications filter-type filter-status :loading?])
|
||||
{:db (assoc-in db [:activity-center :notifications filter-type filter-status :loading?] true)
|
||||
::json-rpc/call [{:method (filter-status->rpc-method filter-status)
|
||||
:params [cursor (defaults :notifications-per-page) filter-type]
|
||||
:on-success #(re-frame/dispatch [:activity-center.notifications/fetch-success filter-type filter-status reset-data? %])
|
||||
:on-error #(re-frame/dispatch [:activity-center.notifications/fetch-error filter-type filter-status %])}]}))
|
||||
|
||||
(fx/defn notifications-fetch-first-page
|
||||
{:events [:activity-center.notifications/fetch-first-page]}
|
||||
[{:keys [db] :as cofx} {:keys [status-filter] :or {status-filter :unread}}]
|
||||
(let [notifications-group (notifications-read-status->group status-filter)]
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:activity-center :current-status-filter] status-filter)
|
||||
(update-in [:activity-center notifications-group] dissoc :loading?)
|
||||
(update-in [:activity-center notifications-group] dissoc :data))}
|
||||
(notifications-fetch start-or-end-cursor notifications-group))))
|
||||
[{:keys [db] :as cofx} {:keys [filter-type filter-status]}]
|
||||
(tap> {:EVT-fetch-first-page (utils.datetime/now->iso8601)})
|
||||
(let [filter-type (or filter-type
|
||||
(get-in db [:activity-center :filter :type]
|
||||
(defaults :filter-type)))
|
||||
filter-status (or filter-status
|
||||
(get-in db [:activity-center :filter :status]
|
||||
(defaults :filter-status)))]
|
||||
(as-> {:db (-> db
|
||||
(assoc-in [:activity-center :filter :type] filter-type)
|
||||
(assoc-in [:activity-center :filter :status] filter-status)
|
||||
(assoc-in [:activity-center :notifications filter-type filter-status :loading?] true)
|
||||
;; Comment this assoc call and the UI becomes less
|
||||
;; responsive. If you use the fake orange rectangles in the
|
||||
;; UI you still notice the latency when this line is
|
||||
;; commented. This is worrisome news.
|
||||
(assoc-in [:activity-center :notifications filter-type filter-status :data] [])
|
||||
)}
|
||||
cofx'
|
||||
(if (and (= 0 filter-type) (= :read filter-status))
|
||||
(assoc cofx' :dispatch-later [;; Lower the delay and eventually the UI becomes less responsive.
|
||||
{:ms 50
|
||||
:dispatch [:activity-center.notifications/fetch-success
|
||||
filter-type
|
||||
filter-status
|
||||
true
|
||||
{:cursor "" :notifications response-notifications}]}])
|
||||
(if (and (= 0 filter-type) (= :unread filter-status))
|
||||
(assoc cofx' :dispatch-later [;; Lower the delay and eventually the UI becomes less responsive.
|
||||
{:ms 50
|
||||
:dispatch [:activity-center.notifications/fetch-success
|
||||
filter-type
|
||||
filter-status
|
||||
true
|
||||
{:cursor "" :notifications response-unread-notifications}]}])
|
||||
(assoc cofx' :dispatch [:activity-center.notifications/fetch-success
|
||||
filter-type
|
||||
filter-status
|
||||
true
|
||||
{:cursor "" :notifications []}]))))))
|
||||
|
||||
(fx/defn notifications-fetch-next-page
|
||||
{:events [:activity-center.notifications/fetch-next-page]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [status-filter (get-in db [:activity-center :current-status-filter])
|
||||
notifications-group (notifications-read-status->group status-filter)
|
||||
{:keys [cursor]} (get-in db [:activity-center notifications-group])]
|
||||
(when-not (= cursor start-or-end-cursor)
|
||||
(notifications-fetch cofx cursor notifications-group))))
|
||||
(let [{:keys [type status]} (get-in db [:activity-center :filter])
|
||||
{:keys [cursor]} (get-in db [:activity-center :notifications type status])]
|
||||
(when (valid-cursor? cursor)
|
||||
(tap> {:EVT-fetch-next-page (utils.datetime/now->iso8601)})
|
||||
(notifications-fetch cofx {:cursor cursor
|
||||
:filter-type type
|
||||
:filter-status status
|
||||
:reset-data? false}))))
|
||||
|
||||
(fx/defn notifications-fetch-success
|
||||
{:events [:activity-center.notifications/fetch-success]}
|
||||
[{:keys [db]} notifications-group {:keys [cursor notifications]}]
|
||||
{:db (-> db
|
||||
(update-in [:activity-center notifications-group] dissoc :loading?)
|
||||
(assoc-in [:activity-center notifications-group :cursor] cursor)
|
||||
(update-in [:activity-center notifications-group :data]
|
||||
concat
|
||||
(map data-store.activities/<-rpc notifications)))})
|
||||
[{:keys [db]}
|
||||
filter-type
|
||||
filter-status
|
||||
reset-data?
|
||||
{:keys [cursor notifications]}]
|
||||
(tap> {:EVT-fetch-success (utils.datetime/now->iso8601)})
|
||||
(let [processed (map data-store.activities/<-rpc notifications)]
|
||||
{:db (-> db
|
||||
(assoc-in [:activity-center :notifications filter-type filter-status :cursor] cursor)
|
||||
(update-in [:activity-center :notifications filter-type filter-status] dissoc :loading?)
|
||||
(update-in [:activity-center :notifications filter-type filter-status :data]
|
||||
(if reset-data?
|
||||
(constantly processed)
|
||||
#(concat %1 processed))))}))
|
||||
|
||||
(fx/defn notifications-fetch-error
|
||||
{:events [:activity-center.notifications/fetch-error]}
|
||||
[{:keys [db]} notifications-group error]
|
||||
[{:keys [db]} filter-type filter-status error]
|
||||
(log/warn "Failed to load Activity Center notifications" error)
|
||||
{:db (update-in db [:activity-center notifications-group] dissoc :loading?)})
|
||||
{:db (update-in db [:activity-center :notifications filter-type filter-status] dissoc :loading?)})
|
||||
|
||||
(fx/defn notifications-quick-toggle
|
||||
{:events [:activity-center.notifications/quick-toggle]}
|
||||
[{:keys [db]}]
|
||||
{:db (assoc db :activity-center-notifications-filter-status
|
||||
(if (= :read (:activity-center-notifications-filter-status db))
|
||||
:unread
|
||||
:read))})
|
||||
|
||||
(comment
|
||||
(fx/defn notifications-quick-toggle
|
||||
{:events [:activity-center.notifications/quick-toggle]}
|
||||
[{:keys [db]}]
|
||||
{:db (assoc-in db [:activity-center :filter :status]
|
||||
(if (= :read (get-in db [:activity-center :filter :status]))
|
||||
:unread
|
||||
:read))})
|
||||
|
||||
(re-frame/reg-event-db
|
||||
:dev/reset-activity-center
|
||||
(fn [db]
|
||||
(-> db
|
||||
(assoc-in [:activity-center]
|
||||
{:filter {:status :read
|
||||
:type 0}
|
||||
:notifications {}}))))
|
||||
|
||||
(re-frame/dispatch [:dev/reset-activity-center]))
|
||||
|
||||
@@ -2,99 +2,285 @@
|
||||
(:require [cljs.test :refer [deftest is testing]]
|
||||
[day8.re-frame.test :as rf-test]
|
||||
[re-frame.core :as rf]
|
||||
[status-im.constants :as c]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.test-helpers :as h]
|
||||
status-im.events))
|
||||
status-im.events
|
||||
[status-im.test-helpers :as h]))
|
||||
|
||||
(defn setup []
|
||||
(h/register-helper-events)
|
||||
(rf/dispatch [:init/app-started]))
|
||||
|
||||
(defn remove-color-key
|
||||
"Remove `:color` key from notifications because they have random values that we
|
||||
can't assert against."
|
||||
[grouped-notifications {:keys [type status]}]
|
||||
(update-in grouped-notifications
|
||||
[type status :data]
|
||||
(fn [old _]
|
||||
(map #(dissoc % :color) old))
|
||||
nil))
|
||||
|
||||
(deftest notifications-reconcile-test
|
||||
(testing "does nothing when there are no new notifications"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [read [{:id "0x1" :read true}]
|
||||
unread [{:id "0x4" :read false}]]
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-read :data] read])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-unread :data] unread])
|
||||
(let [notifications {c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat}
|
||||
{:id "0x2"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x3"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}}
|
||||
c/activity-center-notification-type-private-group-chat
|
||||
{:unread {:cursor ""
|
||||
:data [{:id "0x4"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat}]}}}]
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications] notifications])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile nil])
|
||||
|
||||
(is (= read (get-in (h/db) [:activity-center :notifications-read :data])))
|
||||
(is (= unread (get-in (h/db) [:activity-center :notifications-unread :data]))))))
|
||||
(is (= notifications (get-in (h/db) [:activity-center :notifications]))))))
|
||||
|
||||
(testing "removes dismissed or accepted notifications"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-read :data]
|
||||
[{:id "0x1" :read true}
|
||||
{:id "0x2" :read true}
|
||||
{:id "0x3" :read true}]])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-unread :data]
|
||||
[{:id "0x4" :read false}
|
||||
{:id "0x5" :read false}
|
||||
{:id "0x6" :read false}]])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1" :read true :type c/activity-center-notification-type-one-to-one-chat}
|
||||
{:id "0x2" :read true :type c/activity-center-notification-type-one-to-one-chat}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x3" :read false :type c/activity-center-notification-type-one-to-one-chat}]}}
|
||||
2 {:unread {:cursor ""
|
||||
:data [{:id "0x4" :read false :type 2}
|
||||
{:id "0x6" :read false :type 2}]}}}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1" :read true :dismissed true}
|
||||
{:id "0x3" :read true :accepted true}
|
||||
{:id "0x4" :read false :dismissed true}
|
||||
{:id "0x5" :read false :accepted true}]])
|
||||
[{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:dismissed true}
|
||||
{:id "0x3"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:accepted true}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat
|
||||
:dismissed true}
|
||||
{:id "0x5"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat
|
||||
:accepted true}]])
|
||||
|
||||
(is (= [{:id "0x2" :read true}]
|
||||
(get-in (h/db) [:activity-center :notifications-read :data])))
|
||||
(is (= [{:id "0x6" :read false}]
|
||||
(get-in (h/db) [:activity-center :notifications-unread :data])))))
|
||||
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x2"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}
|
||||
:unread {:cursor ""
|
||||
:data []}}
|
||||
c/activity-center-notification-type-private-group-chat
|
||||
{:read {:data []}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x6"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications])))))
|
||||
|
||||
(testing "replaces old notifications with newly arrived ones"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-read :data]
|
||||
[{:id "0x1" :read true}
|
||||
{:id "0x2" :read true}]])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-unread :data]
|
||||
[{:id "0x3" :read false}
|
||||
{:id "0x4" :read false}]])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}}
|
||||
c/activity-center-notification-type-private-group-chat
|
||||
{:unread {:cursor ""
|
||||
:data [{:id "0x4"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat}
|
||||
{:id "0x6"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat}]}}}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1" :read true :name "ABC"}
|
||||
{:id "0x3" :read false :name "XYZ"}]])
|
||||
[{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:last-message {}}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat
|
||||
:author "0xabc"}
|
||||
{:id "0x6"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat}]])
|
||||
|
||||
(is (= [{:id "0x1" :read true :name "ABC"}
|
||||
{:id "0x2" :read true}]
|
||||
(get-in (h/db) [:activity-center :notifications-read :data])))
|
||||
(is (= [{:id "0x3" :read false :name "XYZ"}
|
||||
{:id "0x4" :read false}]
|
||||
(get-in (h/db) [:activity-center :notifications-unread :data]))))))
|
||||
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:last-message {}}]}
|
||||
:unread {:data []}}
|
||||
c/activity-center-notification-type-private-group-chat
|
||||
{:read {:data []}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x6"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat
|
||||
:author "0xabc"}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications])))))
|
||||
|
||||
(testing "reconciles notifications that switched their read/unread status"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}}}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]])
|
||||
|
||||
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data []}
|
||||
:unread {:data [{:id "0x1"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications])))))
|
||||
|
||||
;; Sorting by timestamp and ID is compatible with what the backend does when
|
||||
;; returning paginated results.
|
||||
(testing "sorts notifications by timestamp and id in descending order"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1" :read true :type c/activity-center-notification-type-one-to-one-chat :timestamp 1}
|
||||
{:id "0x2" :read true :type c/activity-center-notification-type-one-to-one-chat :timestamp 1}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x3" :read false :type c/activity-center-notification-type-one-to-one-chat :timestamp 50}
|
||||
{:id "0x4" :read false :type c/activity-center-notification-type-one-to-one-chat :timestamp 100}
|
||||
{:id "0x5" :read false :type c/activity-center-notification-type-one-to-one-chat :timestamp 100}]}}}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1" :read true :type c/activity-center-notification-type-one-to-one-chat :timestamp 1 :last-message {}}
|
||||
{:id "0x4" :read false :type c/activity-center-notification-type-one-to-one-chat :timestamp 100 :last-message {}}]])
|
||||
|
||||
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x2"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:timestamp 1}
|
||||
{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:timestamp 1
|
||||
:last-message {}}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x5"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:timestamp 100}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:timestamp 100
|
||||
:last-message {}}
|
||||
{:id "0x3"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:timestamp 50}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications]))))))
|
||||
|
||||
(deftest notifications-fetch-test
|
||||
(testing "fetches first page"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/stub-fx-with-callbacks ::json-rpc/call
|
||||
:on-success (constantly {:cursor "10"
|
||||
:notifications [{:chatId "0x1"}]}))
|
||||
(h/stub-fx-with-callbacks
|
||||
::json-rpc/call
|
||||
:on-success (constantly {:cursor "10"
|
||||
:notifications [{:id "0x1"
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:read false
|
||||
:chatId "0x9"}]}))
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/fetch-first-page])
|
||||
(rf/dispatch [:activity-center.notifications/fetch-first-page
|
||||
{:filter-type c/activity-center-notification-type-one-to-one-chat}])
|
||||
|
||||
(is (= :unread (get-in (h/db) [:activity-center :current-status-filter])))
|
||||
(is (nil? (get-in (h/db) [:activity-center :notifications-unread :loading?])))
|
||||
(is (= "10" (get-in (h/db) [:activity-center :notifications-unread :cursor])))
|
||||
(is (= "" (get-in @spy-queue [0 :args 0 :params 0])))
|
||||
(is (= [{:chat-id "0x1"}]
|
||||
(->> (get-in (h/db) [:activity-center :notifications-unread :data])
|
||||
(map #(select-keys % [:chat-id]))))))))
|
||||
(is (= :unread (get-in (h/db) [:activity-center :filter :status])))
|
||||
(is (= "" (get-in @spy-queue [0 :args 0 :params 0]))
|
||||
"Should be called with empty cursor when fetching first page")
|
||||
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||
{:unread {:cursor "10"
|
||||
:data [{:chat-id "0x9"
|
||||
:chat-name nil
|
||||
:chat-type c/activity-center-notification-type-one-to-one-chat
|
||||
:group-chat false
|
||||
:id "0x1"
|
||||
:public? false
|
||||
:last-message nil
|
||||
:message nil
|
||||
:read false
|
||||
:reply-message nil
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}}}
|
||||
(remove-color-key (get-in (h/db) [:activity-center :notifications])
|
||||
{:status :unread
|
||||
:type c/activity-center-notification-type-one-to-one-chat}))))))
|
||||
|
||||
(testing "does not fetch next page when pagination cursor reached the end"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :current-status-filter] :unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-unread :cursor] ""])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
c/activity-center-notification-type-one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :unread :cursor]
|
||||
""])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
|
||||
(is (= [] @spy-queue)))))
|
||||
|
||||
;; The cursor can be nil sometimes because the reconciliation doesn't care
|
||||
;; about updating the cursor value, but we have to make sure the next page is
|
||||
;; only fetched if the current cursor is valid.
|
||||
(testing "does not fetch next page when cursor is nil"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
c/activity-center-notification-type-one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :unread :cursor]
|
||||
nil])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
|
||||
@@ -104,30 +290,54 @@
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/stub-fx-with-callbacks ::json-rpc/call
|
||||
:on-success (constantly {:cursor ""
|
||||
:notifications [{:chatId "0x1"}]}))
|
||||
(h/stub-fx-with-callbacks
|
||||
::json-rpc/call
|
||||
:on-success (constantly {:cursor ""
|
||||
:notifications [{:id "0x1"
|
||||
:type c/activity-center-notification-type-mention
|
||||
:read false
|
||||
:chatId "0x9"}]}))
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :current-status-filter] :unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-unread :cursor] "10"])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
c/activity-center-notification-type-mention])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-mention :unread :cursor]
|
||||
"10"])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
|
||||
(is (= "wakuext_unreadActivityCenterNotifications" (get-in @spy-queue [0 :args 0 :method])))
|
||||
(is (= "10" (get-in @spy-queue [0 :args 0 :params 0])))
|
||||
(is (= "" (get-in (h/db) [:activity-center :notifications-unread :cursor])))
|
||||
(is (= [{:chat-id "0x1"}]
|
||||
(->> (get-in (h/db) [:activity-center :notifications-unread :data])
|
||||
(map #(select-keys % [:chat-id]))))))))
|
||||
(is (= "10" (get-in @spy-queue [0 :args 0 :params 0]))
|
||||
"Should be called with current cursor")
|
||||
(is (= {c/activity-center-notification-type-mention
|
||||
{:unread {:cursor ""
|
||||
:data [{:chat-id "0x9"
|
||||
:chat-name nil
|
||||
:chat-type 3
|
||||
:id "0x1"
|
||||
:last-message nil
|
||||
:message nil
|
||||
:read false
|
||||
:reply-message nil
|
||||
:type c/activity-center-notification-type-mention}]}}}
|
||||
(remove-color-key (get-in (h/db) [:activity-center :notifications])
|
||||
{:status :unread
|
||||
:type c/activity-center-notification-type-mention}))))))
|
||||
|
||||
(testing "does not fetch next page while it is still loading"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :current-status-filter] :read])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-read :cursor] "10"])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-read :loading?] true])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:read])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
c/activity-center-notification-type-one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :read :cursor]
|
||||
"10"])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :read :loading?]
|
||||
true])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
|
||||
@@ -140,13 +350,18 @@
|
||||
(h/stub-fx-with-callbacks ::json-rpc/call :on-error (constantly :fake-error))
|
||||
(h/spy-event-fx spy-queue :activity-center.notifications/fetch-error)
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :current-status-filter] :unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications-unread :cursor] ""])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
c/activity-center-notification-type-one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :unread :cursor]
|
||||
""])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/fetch-first-page])
|
||||
|
||||
(is (nil? (get-in (h/db) [:activity-center :notifications-unread :loading?])))
|
||||
(is (nil? (get-in (h/db) [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :unread :loading?])))
|
||||
(is (= [:activity-center.notifications/fetch-error
|
||||
:notifications-unread
|
||||
c/activity-center-notification-type-one-to-one-chat
|
||||
:unread
|
||||
:fake-error]
|
||||
(:args (last @spy-queue))))))))
|
||||
|
||||
@@ -176,6 +176,7 @@
|
||||
(def ^:const docs-link "https://status.im/docs/")
|
||||
(def ^:const principles-link "https://our.status.im/our-principles/")
|
||||
|
||||
(def ^:const activity-center-notification-type-no-type 0)
|
||||
(def ^:const activity-center-notification-type-one-to-one-chat 1)
|
||||
(def ^:const activity-center-notification-type-private-group-chat 2)
|
||||
(def ^:const activity-center-notification-type-mention 3)
|
||||
@@ -183,6 +184,13 @@
|
||||
(def ^:const activity-center-notification-type-contact-request 5)
|
||||
(def ^:const activity-center-notification-type-contact-request-retracted 6)
|
||||
|
||||
;; TODO: Replace with correct enum values once status-go implements them.
|
||||
(def ^:const activity-center-notification-type-admin 66610)
|
||||
(def ^:const activity-center-notification-type-identity-verification 66611)
|
||||
(def ^:const activity-center-notification-type-tx 66612)
|
||||
(def ^:const activity-center-notification-type-membership 66613)
|
||||
(def ^:const activity-center-notification-type-system 66614)
|
||||
|
||||
(def ^:const visibility-status-unknown 0)
|
||||
(def ^:const visibility-status-automatic 1)
|
||||
(def ^:const visibility-status-dnd 2)
|
||||
|
||||
@@ -328,8 +328,9 @@
|
||||
;; Information Box
|
||||
|
||||
(def closable-information-boxes
|
||||
[{:id :ens-banner
|
||||
:global? true}]) ;; global? - close information box across all profiles
|
||||
"[{:id information box id
|
||||
:global? true/false (close information box across all profiles)}]"
|
||||
[])
|
||||
|
||||
(defn information-box-id-hash [id public-key global?]
|
||||
(if global?
|
||||
|
||||
@@ -55,13 +55,16 @@
|
||||
:podcasts (js/require "../resources/images/ui/podcasts.png")})
|
||||
|
||||
(def mock-images
|
||||
{:photo1 (js/require "../resources/images/mock/photo1.png")
|
||||
:photo2 (js/require "../resources/images/mock/photo2.png")
|
||||
:photo3 (js/require "../resources/images/mock/photo3.png")
|
||||
:community-banner (js/require "../resources/images/mock/community-banner.png")
|
||||
:community-logo (js/require "../resources/images/mock/community-logo.png")
|
||||
:gif (js/require "../resources/images/mock/gif.png")
|
||||
:sticker (js/require "../resources/images/mock/sticker.png")})
|
||||
{:photo1 (js/require "../resources/images/mock/photo1.png")
|
||||
:photo2 (js/require "../resources/images/mock/photo2.png")
|
||||
:photo3 (js/require "../resources/images/mock/photo3.png")
|
||||
:community-banner (js/require "../resources/images/mock/community-banner.png")
|
||||
:community-logo (js/require "../resources/images/mock/community-logo.png")
|
||||
:gif (js/require "../resources/images/mock/gif.png")
|
||||
:sticker (js/require "../resources/images/mock/sticker.png")
|
||||
:user-picture-female2 (js/require "../resources/images/mock/user_picture_female2.png")
|
||||
:user-picture-male4 (js/require "../resources/images/mock/user_picture_male4.png")
|
||||
:user-picture-male5 (js/require "../resources/images/mock/user_picture_male5.png")})
|
||||
|
||||
(defn get-theme-image [k]
|
||||
(get ui (when (colors/dark?) (keyword (str (name k) "-dark"))) (get ui k)))
|
||||
|
||||
@@ -3,32 +3,53 @@
|
||||
[status-im.utils.datetime :as datetime]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.utils.datetime :as utils.datetime]
|
||||
[clojure.string :as string]))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:activity-center/notifications-read
|
||||
(fn [db]
|
||||
(get-in db [:activity-center :notifications-read :data])))
|
||||
:activity-center/notifications
|
||||
:<- [:activity-center]
|
||||
(fn [activity-center]
|
||||
(:notifications activity-center)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:activity-center/notifications-unread
|
||||
(fn [db]
|
||||
(get-in db [:activity-center :notifications-unread :data])))
|
||||
:activity-center/filter-status
|
||||
:<- [:activity-center]
|
||||
(fn [activity-center]
|
||||
(get-in activity-center [:filter :status])))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:activity-center/current-status-filter
|
||||
(fn [db]
|
||||
(get-in db [:activity-center :current-status-filter])))
|
||||
:activity-center/filter-type
|
||||
:<- [:activity-center]
|
||||
(fn [activity-center]
|
||||
(get-in activity-center [:filter :type] constants/activity-center-notification-type-no-type)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:activity-center/notifications-per-read-status
|
||||
:<- [:activity-center/notifications-read]
|
||||
:<- [:activity-center/notifications-unread]
|
||||
:<- [:activity-center/current-status-filter]
|
||||
(fn [[notifications-read notifications-unread status-filter]]
|
||||
(if (= status-filter :unread)
|
||||
notifications-unread
|
||||
notifications-read)))
|
||||
:activity-center/loading?
|
||||
:<- [:activity-center/filter-type]
|
||||
:<- [:activity-center/filter-status]
|
||||
:<- [:activity-center/notifications]
|
||||
(fn [[filter-type filter-status notifications]]
|
||||
(get-in notifications [filter-type filter-status :loading?])))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:activity-center/filtered-notifications
|
||||
:<- [:activity-center/filter-type]
|
||||
:<- [:activity-center/filter-status]
|
||||
:<- [:activity-center/notifications]
|
||||
(fn [[filter-type filter-status notifications]]
|
||||
(tap> {:SUB-filtered-notifications (utils.datetime/now->iso8601)})
|
||||
(get-in notifications [filter-type filter-status :data])))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:activity-center/filter-status-unread-enabled?
|
||||
:<- [:activity-center/filter-status]
|
||||
(fn [filter-status]
|
||||
(= :unread filter-status))
|
||||
;; (fn [db]
|
||||
;; (tap> {:SUB-filter-status-unread-enabled? (utils.datetime/now->iso8601)})
|
||||
;; (= :read (:activity-center-notifications-filter-status db)))
|
||||
)
|
||||
|
||||
(defn- group-notifications-by-date
|
||||
[notifications]
|
||||
@@ -64,4 +85,4 @@
|
||||
(map #(assoc %
|
||||
:timestamp (or (:timestamp %) (:timestamp (or (:message %) (:last-message %))))
|
||||
:contact (multiaccounts/contact-by-identity contacts (get-in % [:message :from])))
|
||||
supported-notifications)))))
|
||||
supported-notifications)))))
|
||||
|
||||
@@ -228,6 +228,7 @@
|
||||
|
||||
(reg-root-key-sub :activity.center/notifications :activity.center/notifications)
|
||||
(reg-root-key-sub :activity.center/notifications-count :activity.center/notifications-count)
|
||||
(reg-root-key-sub :activity-center :activity-center)
|
||||
|
||||
(reg-root-key-sub :bug-report/description-error :bug-report/description-error)
|
||||
(reg-root-key-sub :bug-report/details :bug-report/details)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
:refer (SafeAreaProvider SafeAreaInsetsContext)]
|
||||
["@react-native-community/clipboard" :default Clipboard]
|
||||
["react-native-linear-gradient" :default LinearGradient]
|
||||
["@react-native-community/masked-view" :default MaskedView]
|
||||
["react-native-navigation" :refer (Navigation)]
|
||||
["react-native-fast-image" :as FastImage]
|
||||
["@react-native-community/blur" :as blur])
|
||||
@@ -40,6 +41,8 @@
|
||||
|
||||
(def linear-gradient (reagent/adapt-react-class LinearGradient))
|
||||
|
||||
(def masked-view (reagent/adapt-react-class MaskedView))
|
||||
|
||||
(def blur-view (reagent/adapt-react-class (.-BlurView blur)))
|
||||
|
||||
(defn valid-source? [source]
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
(ns status-im.ui.screens.activity-center.views
|
||||
(:require [quo.components.animated.pressable :as animation]
|
||||
[quo.design-system.colors :as quo.colors]
|
||||
[quo.react-native :as rn]
|
||||
[quo2.components.buttons.button :as button]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.notifications.activity-logs :as activity-logs]
|
||||
[quo2.components.tabs.tabs :as tabs]
|
||||
[quo2.components.tags.context-tags :as context-tags]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.utils.datetime :as datetime]
|
||||
[status-im.utils.handlers :refer [<sub >evt]]))
|
||||
|
||||
@@ -28,8 +30,8 @@
|
||||
[{:keys [type]}]
|
||||
(case type
|
||||
constants/activity-center-notification-type-contact-request
|
||||
:add-user
|
||||
:placeholder))
|
||||
:main-icons2/add-user
|
||||
:main-icons2/placeholder))
|
||||
|
||||
(defn activity-context
|
||||
[{:keys [message last-message type]}]
|
||||
@@ -77,7 +79,7 @@
|
||||
nil))
|
||||
|
||||
(defn activity-pressable
|
||||
[notification & children]
|
||||
[notification activity]
|
||||
(case (get-in notification [:message :contact-request-state])
|
||||
constants/contact-request-message-state-accepted
|
||||
;; NOTE [2022-09-21]: We need to dispatch to
|
||||
@@ -85,46 +87,154 @@
|
||||
;; `:chat.ui/navigate-to-chat`, otherwise the chat screen looks completely
|
||||
;; broken if it has never been opened before for the accepted contact.
|
||||
[animation/pressable {:on-press #(>evt [:contact.ui/send-message-pressed {:public-key (:author notification)}])}
|
||||
children]
|
||||
[:<> children]))
|
||||
activity]
|
||||
activity))
|
||||
|
||||
(defn render-notification
|
||||
[notification index]
|
||||
[rn/view {:flex 1
|
||||
:flex-direction :column
|
||||
:margin-top (if (= 0 index) 0 4)}
|
||||
[activity-pressable notification
|
||||
[activity-logs/activity-log
|
||||
(merge {:context (activity-context notification)
|
||||
:icon (activity-icon notification)
|
||||
:message (activity-message notification)
|
||||
:status (activity-status notification)
|
||||
:timestamp (datetime/timestamp->relative (:timestamp notification))
|
||||
:title (activity-title notification)
|
||||
:unread? (not (:read notification))}
|
||||
(activity-buttons notification))]]])
|
||||
(tap> {:render-notification (datetime/now->iso8601)})
|
||||
[rn/view {:margin-top (if (= 0 index) 0 4)
|
||||
:padding-horizontal 20}
|
||||
;; Use this fake notification rectangle to see a massive rendering
|
||||
;; performance difference.
|
||||
(if true
|
||||
[rn/view {:style {:width 100
|
||||
:height 100
|
||||
:background-color :orange
|
||||
:padding-vertical 10}}]
|
||||
[activity-pressable notification
|
||||
[activity-logs/activity-log
|
||||
(merge {:context (activity-context notification)
|
||||
:icon (activity-icon notification)
|
||||
:message (activity-message notification)
|
||||
:status (activity-status notification)
|
||||
:timestamp (datetime/timestamp->relative (:timestamp notification))
|
||||
:title (activity-title notification)
|
||||
:unread? (not (:read notification))}
|
||||
(activity-buttons notification))]])])
|
||||
|
||||
(defn notifications-list
|
||||
(defn filter-selector-read-toggle
|
||||
[]
|
||||
(let [notifications (<sub [:activity-center/notifications-per-read-status])]
|
||||
[rn/flat-list {:style {:padding-horizontal 8}
|
||||
:data notifications
|
||||
:key-fn :id
|
||||
:on-end-reached #(>evt [:activity-center.notifications/fetch-next-page])
|
||||
:render-fn render-notification}]))
|
||||
(tap> {:filter-selector-read-toggle (datetime/now->iso8601)})
|
||||
(let [unread-filter-enabled? (<sub [:activity-center/filter-status-unread-enabled?])]
|
||||
;; TODO: Replace the button by a Filter Selector component once available for use.
|
||||
[button/button {:icon true
|
||||
:type (if unread-filter-enabled? :primary :outline)
|
||||
:size 32
|
||||
:on-press #(do
|
||||
(tap> {:filter-selector-read-toggle-ON-PRESS (datetime/now->iso8601)})
|
||||
(>evt [:activity-center.notifications/fetch-first-page
|
||||
{:filter-status (if unread-filter-enabled?
|
||||
:read
|
||||
:unread)}]))}
|
||||
:main-icons2/unread]))
|
||||
|
||||
(defn activity-center []
|
||||
;; TODO(2022-10-07): The empty state is still under design analysis, so we
|
||||
;; shouldn't even care about translations at this point. A placeholder box is
|
||||
;; used instead of an image.
|
||||
(defn empty-tab
|
||||
[]
|
||||
[rn/view {:style {:align-items :center
|
||||
:flex 1
|
||||
:justify-content :center
|
||||
:padding-vertical 12}}
|
||||
[rn/view {:style {:background-color colors/neutral-80
|
||||
:height 120
|
||||
:margin-bottom 20
|
||||
:width 120}}]
|
||||
[text/text {:size :paragraph-1
|
||||
:style {:padding-bottom 2}
|
||||
:weight :semi-bold}
|
||||
"No notifications"]
|
||||
[text/text {:size :paragraph-2}
|
||||
"Your notifications will be here"]])
|
||||
|
||||
(defn tabs
|
||||
[]
|
||||
(tap> {:tabs (datetime/now->iso8601)})
|
||||
(let [filter-type (<sub [:activity-center/filter-type])]
|
||||
[tabs/scrollable-tabs {:size 32
|
||||
:style {:padding-left 20}
|
||||
:fade-end-percentage 0.79
|
||||
:scroll-on-press? true
|
||||
:fade-end? true
|
||||
:on-change #(>evt [:activity-center.notifications/fetch-first-page {:filter-type %}])
|
||||
:default-active filter-type
|
||||
:data [{:id constants/activity-center-notification-type-no-type
|
||||
:label (i18n/label :t/all)}
|
||||
{:id constants/activity-center-notification-type-admin
|
||||
:label (i18n/label :t/admin)}
|
||||
{:id constants/activity-center-notification-type-mention
|
||||
:label (i18n/label :t/mentions)}
|
||||
{:id constants/activity-center-notification-type-reply
|
||||
:label (i18n/label :t/replies)}
|
||||
{:id constants/activity-center-notification-type-contact-request
|
||||
:label (i18n/label :t/contact-requests)}
|
||||
{:id constants/activity-center-notification-type-identity-verification
|
||||
:label (i18n/label :t/identity-verification)}
|
||||
{:id constants/activity-center-notification-type-tx
|
||||
:label (i18n/label :t/transactions)}
|
||||
{:id constants/activity-center-notification-type-membership
|
||||
:label (i18n/label :t/membership)}
|
||||
{:id constants/activity-center-notification-type-system
|
||||
:label (i18n/label :t/system)}]}]))
|
||||
|
||||
(defn header
|
||||
[]
|
||||
(tap> {:header (datetime/now->iso8601)})
|
||||
(let [screen-padding 20]
|
||||
;; TODO: Remove temporary (and old) background color when the screen and
|
||||
;; header are properly blurred.
|
||||
[rn/view {:background-color (:ui-background @quo.colors/theme)}
|
||||
[button/button {:icon true
|
||||
:type :grey
|
||||
:size 32
|
||||
:style {:margin-vertical 12
|
||||
:margin-left screen-padding}
|
||||
:on-press #(>evt [:navigate-back])}
|
||||
:main-icons2/close]
|
||||
[text/text {:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style {:padding-horizontal screen-padding
|
||||
:padding-vertical 12}}
|
||||
(i18n/label :t/notifications)]
|
||||
[rn/view {:flex-direction :row
|
||||
:padding-vertical 12}
|
||||
[rn/view {:flex 1
|
||||
:align-self :stretch}
|
||||
[tabs]]
|
||||
[rn/view {:flex-grow 0
|
||||
:margin-left 16
|
||||
:padding-right screen-padding}
|
||||
[filter-selector-read-toggle]]]]))
|
||||
|
||||
(defn loading-indicator
|
||||
[]
|
||||
(tap> {:loading-indicator (datetime/now->iso8601)})
|
||||
(when (<sub [:activity-center/loading?])
|
||||
[rn/view {:background-color :orange
|
||||
:position :absolute
|
||||
:top 0
|
||||
:left 0
|
||||
:width 100
|
||||
:height 50}]))
|
||||
|
||||
(defn activity-center
|
||||
[]
|
||||
(reagent/create-class
|
||||
{:component-did-mount #(>evt [:activity-center.notifications/fetch-first-page {:status-filter :unread}])
|
||||
{:component-did-mount #(>evt [:activity-center.notifications/fetch-first-page])
|
||||
:reagent-render
|
||||
(fn []
|
||||
[:<>
|
||||
[topbar/topbar {:navigation {:on-press #(>evt [:navigate-back])}
|
||||
:title (i18n/label :t/activity)}]
|
||||
;; TODO(ilmotta): Temporary solution to switch between read/unread
|
||||
;; notifications while the Design team works on the mockups.
|
||||
[button/button {:on-press #(>evt [:activity-center.notifications/fetch-first-page {:status-filter :unread}])}
|
||||
"Unread"]
|
||||
[button/button {:on-press #(>evt [:activity-center.notifications/fetch-first-page {:status-filter :read}])}
|
||||
"Read"]
|
||||
[notifications-list]])}))
|
||||
(tap> {:activity-center (datetime/now->iso8601)})
|
||||
(let [notifications (<sub [:activity-center/filtered-notifications])]
|
||||
[:<>
|
||||
[loading-indicator]
|
||||
[rn/flat-list {:content-container-style {:flex-grow 1}
|
||||
:data notifications
|
||||
:empty-component [empty-tab]
|
||||
:header [header]
|
||||
:key-fn :id
|
||||
;; Commented to simplify analysis.
|
||||
;; :on-end-reached #(>evt [:activity-center.notifications/fetch-next-page])
|
||||
:render-fn render-notification
|
||||
:sticky-header-indices [0]}]]))}))
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
(:require [status-im.ui.components.icons.icons :as icons]
|
||||
[quo.react-native :as rn]
|
||||
[oops.core :refer [oget]]
|
||||
[quo.react :as react]
|
||||
[quo.react :as quo.react]
|
||||
[quo.platform :as platform]
|
||||
[quo.components.text :as text]
|
||||
[quo.design-system.colors :as colors]
|
||||
[status-im.ui.screens.chat.components.style :as styles]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.handlers :refer [<sub]]
|
||||
[status-im.ui.screens.chat.components.reply :as reply]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.chat.constants :as chat.constants]
|
||||
@@ -27,7 +28,7 @@
|
||||
[quo.components.safe-area :as safe-area]))
|
||||
|
||||
(defn input-focus [text-input-ref]
|
||||
(some-> ^js (react/current-ref text-input-ref) .focus))
|
||||
(some-> ^js (quo.react/current-ref text-input-ref) .focus))
|
||||
|
||||
(def panel->icons {:extensions :main-icons/commands
|
||||
:images :main-icons/photo})
|
||||
@@ -157,7 +158,7 @@
|
||||
(quo.react/set-native-props sticker-ref #js {:width nil :right nil})))
|
||||
|
||||
(defn reset-input [refs chat-id]
|
||||
(some-> ^js (react/current-ref (:text-input-ref refs)) .clear)
|
||||
(some-> ^js (quo.react/current-ref (:text-input-ref refs)) .clear)
|
||||
(swap! mentions-enabled update :render not)
|
||||
(swap! input-texts dissoc chat-id))
|
||||
|
||||
@@ -204,11 +205,11 @@
|
||||
(re-frame/reg-fx
|
||||
:set-input-text
|
||||
(fn [[chat-id text]]
|
||||
;; We enable mentions
|
||||
;; We enable mentions
|
||||
(swap! mentions-enabled assoc chat-id true)
|
||||
(on-text-change text chat-id)
|
||||
;; We update the key so that we force a refresh of the text input, as those
|
||||
;; are not ratoms
|
||||
;; We update the key so that we force a refresh of the text input, as those
|
||||
;; are not ratoms
|
||||
(force-text-input-update!)))
|
||||
|
||||
(fx/defn set-input-text
|
||||
@@ -461,12 +462,12 @@
|
||||
:input-focus #(input-focus text-input-ref)
|
||||
:set-active set-active-panel}])])]]]))))
|
||||
|
||||
(defn calculate-y [context keyboard-shown min-y max-y]
|
||||
(defn calculate-y [context keyboard-shown min-y max-y added-value]
|
||||
(if keyboard-shown
|
||||
(if (= (:state @context) :max)
|
||||
max-y
|
||||
(if (< (:y @context) max-y)
|
||||
(:y @context)
|
||||
(+ (:y @context) added-value)
|
||||
(do
|
||||
(swap! context assoc :state :max)
|
||||
max-y)))
|
||||
@@ -554,30 +555,31 @@
|
||||
:pdy 0 ;used for gesture
|
||||
:state :min ;:min, :custom-chat-available, :custom-chat-unavailable, :max
|
||||
:clear false})
|
||||
keyboard-was-shown (atom false)]
|
||||
keyboard-was-shown (atom false)
|
||||
text-input-ref (quo.react/create-ref)
|
||||
send-ref (quo.react/create-ref)
|
||||
refs {:send-ref send-ref
|
||||
:text-input-ref text-input-ref}]
|
||||
(fn []
|
||||
[:f>
|
||||
(fn []
|
||||
(let [text-input-ref (quo.react/create-ref)
|
||||
send-ref (quo.react/create-ref)
|
||||
refs {:send-ref send-ref
|
||||
:text-input-ref text-input-ref}
|
||||
|
||||
(let [reply (<sub [:chats/reply-message])
|
||||
{window-height :height} (rn/use-window-dimensions)
|
||||
{:keys [keyboard-shown keyboard-height]} (rn/use-keyboard)
|
||||
|
||||
max-y (- window-height (if (> keyboard-height 0) keyboard-height 360) (:top insets)) ; 360 - defaul height
|
||||
max-height (- max-y 56 (:bottom insets)) ; 56 - topbar height
|
||||
y (calculate-y context keyboard-shown min-y max-y)
|
||||
|
||||
max-y (- window-height (if (> keyboard-height 0) keyboard-height 360) (:top insets)) ; 360 - default height
|
||||
max-height (- max-y 56 (:bottom insets)) ; 56 - top-bar height
|
||||
added-value (if reply 38 0) ; increased height of input box needed when reply
|
||||
min-y (+ min-y added-value)
|
||||
y (calculate-y context keyboard-shown min-y max-y added-value)
|
||||
translate-y (reanimated/use-shared-value 0)
|
||||
shared-height (reanimated/use-shared-value min-y)
|
||||
bg-opacity (reanimated/use-shared-value 0)
|
||||
|
||||
bottom-sheet-gesture (get-bottom-sheet-gesture context translate-y text-input-ref keyboard-shown
|
||||
min-y max-y shared-height max-height bg-opacity)
|
||||
input-content-change (get-input-content-change context translate-y shared-height max-height
|
||||
bg-opacity keyboard-shown min-y max-y)]
|
||||
bg-opacity keyboard-shown min-y max-y)
|
||||
bottom-sheet-gesture (get-bottom-sheet-gesture context translate-y (:text-input-ref refs) keyboard-shown
|
||||
min-y max-y shared-height max-height bg-opacity)]
|
||||
(quo.react/effect! #(do
|
||||
(when (and @keyboard-was-shown (not keyboard-shown))
|
||||
(swap! context assoc :state :min))
|
||||
@@ -597,7 +599,8 @@
|
||||
(styles/new-input-bottom-sheet window-height))}
|
||||
;handle
|
||||
[rn/view {:style (styles/new-bottom-sheet-handle)}]
|
||||
[rn/view {:style {:height (- max-y 80)}}
|
||||
[reply/reply-message-auto-focus-wrapper (:text-input-ref refs) reply]
|
||||
[rn/view {:style {:height (- max-y 80 added-value)}}
|
||||
[text-input {:chat-id chat-id
|
||||
:on-content-size-change input-content-change
|
||||
:sending-image false
|
||||
@@ -619,4 +622,4 @@
|
||||
;black background
|
||||
[reanimated/view {:style (reanimated/apply-animations-to-style
|
||||
{:opacity bg-opacity}
|
||||
(styles/new-bottom-sheet-background window-height))}]]))])))])
|
||||
(styles/new-bottom-sheet-background window-height))}]]))])))])
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
[rn/view {:style (styles/reply-content)}
|
||||
[icons/icon :main-icons/connector {:color (theme-colors quo2.colors/neutral-40 quo2.colors/neutral-60)
|
||||
:container-style {:position :absolute :left 10 :bottom -4 :width 16 :height 16}}]
|
||||
[rn/view {:style {:position :absolute :left 34 :right 54 :top 3 :flex-direction :row :align-items :center}}
|
||||
[rn/view {:style {:position :absolute :left 34 :top 3 :flex-direction :row :align-items :center :width "45%"}}
|
||||
[photos/member-photo from identicon 16]
|
||||
[quo2.text/text {:weight :semi-bold
|
||||
:size :paragraph-2
|
||||
@@ -144,7 +144,8 @@
|
||||
(when-not (= reply @had-reply)
|
||||
(reset! had-reply reply)
|
||||
(when reply
|
||||
(js/setTimeout #(input-focus text-input-ref) 250))))
|
||||
;; A setTimeout of 0 is necessary to ensure the statement is enqueued and will get executed ASAP.
|
||||
(js/setTimeout #(input-focus text-input-ref) 0))))
|
||||
|
||||
(defn reply-message-wrapper-old [reply]
|
||||
[rn/view {:style {:padding-horizontal 15
|
||||
@@ -155,8 +156,6 @@
|
||||
|
||||
(defn reply-message-wrapper [reply]
|
||||
[rn/view {:style {:padding-horizontal 15
|
||||
:border-top-width 1
|
||||
:border-top-color (:ui-01 @quo.colors/theme)
|
||||
:padding-vertical 8}}
|
||||
[reply-message reply true]])
|
||||
|
||||
@@ -168,10 +167,9 @@
|
||||
(when reply
|
||||
[reply-message-wrapper-old reply])))))
|
||||
|
||||
(defn reply-message-auto-focus-wrapper [text-input-ref]
|
||||
(defn reply-message-auto-focus-wrapper [text-input-ref _]
|
||||
(let [had-reply (atom nil)]
|
||||
(fn []
|
||||
(let [reply @(re-frame/subscribe [:chats/reply-message])]
|
||||
(focus-input-on-reply reply had-reply text-input-ref)
|
||||
(when reply
|
||||
[reply-message-wrapper reply])))))
|
||||
(fn [_ reply]
|
||||
(focus-input-on-reply reply had-reply text-input-ref)
|
||||
(when reply
|
||||
[reply-message-wrapper reply]))))
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
[reagent.core :as reagent]
|
||||
[goog.string :as gstring]
|
||||
[status-im.audio.core :as audio]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.ui.screens.chat.styles.message.audio :as style]
|
||||
[status-im.ui.components.animation :as anim]
|
||||
[quo.design-system.colors :as colors]
|
||||
@@ -184,15 +183,6 @@
|
||||
:accessibility-label :play-pause-audio-message-button
|
||||
:color color}]])))
|
||||
|
||||
(fx/defn on-background
|
||||
{:events [:audio-message/on-background]}
|
||||
[_]
|
||||
(when (and @current-active-state-ref-ref
|
||||
@@current-active-state-ref-ref)
|
||||
(update-state {:state-ref @current-active-state-ref-ref
|
||||
:message-id @current-player-message-id}))
|
||||
nil)
|
||||
|
||||
(defview message-content [{:keys [audio audio-duration-ms message-id]}]
|
||||
(letsubs [state (reagent/atom nil)
|
||||
progress (reagent/atom 0)
|
||||
|
||||
@@ -423,6 +423,38 @@
|
||||
:inverted (when platform/ios? true)
|
||||
:style (when platform/android? {:scaleY -1})})]))
|
||||
|
||||
(def show-floating-scroll-down-button (reagent/atom false))
|
||||
(def threshold-percentage-to-show-floating-scroll-down-button 75)
|
||||
|
||||
(defn scroll-to-bottom []
|
||||
(some-> ^js @messages-list-ref (.scrollToOffset #js {:y 0 :animated true})))
|
||||
|
||||
(defn floating-scroll-down-button [show-input?]
|
||||
[rn/touchable-without-feedback
|
||||
{:on-press scroll-to-bottom}
|
||||
[rn/view {:style {:position :absolute
|
||||
:bottom (if show-input? 126 12)
|
||||
:right 12
|
||||
:height 24
|
||||
:width 24
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:border-radius (/ 24 2)
|
||||
:background-color (quo2.colors/theme-colors quo2.colors/neutral-80-opa-70 quo2.colors/white-opa-70)}}
|
||||
[icons/icon
|
||||
:main-icons/arrow-down {:color (quo2.colors/theme-colors quo2.colors/white quo2.colors/neutral-100)
|
||||
:width 12
|
||||
:height 12}]]])
|
||||
|
||||
(defn on-scroll [^js ev]
|
||||
(let [y (-> ev .-nativeEvent .-contentOffset .-y)
|
||||
layout-height (-> ev .-nativeEvent .-layoutMeasurement .-height)
|
||||
threshold-height (* (/ layout-height 100) threshold-percentage-to-show-floating-scroll-down-button)
|
||||
reached-threshold? (> y threshold-height)]
|
||||
(when (not= reached-threshold? @show-floating-scroll-down-button)
|
||||
(rn/configure-next (:ease-in-ease-out rn/layout-animation-presets))
|
||||
(reset! show-floating-scroll-down-button reached-threshold?))))
|
||||
|
||||
(defn messages-view [{:keys [chat
|
||||
bottom-space
|
||||
pan-responder
|
||||
@@ -438,39 +470,43 @@
|
||||
mutual-contact-requests-enabled?
|
||||
one-to-one?
|
||||
(not contact-added?))]
|
||||
|
||||
;;do not use anonymous functions for handlers
|
||||
[list/flat-list
|
||||
(merge
|
||||
pan-responder
|
||||
{:key-fn list-key-fn
|
||||
:ref list-ref
|
||||
:header [list-header chat]
|
||||
:footer [list-footer chat]
|
||||
:data (when-not should-send-contact-request?
|
||||
messages)
|
||||
:render-data (get-render-data {:group-chat group-chat
|
||||
:chat-id chat-id
|
||||
:public? public?
|
||||
:community-id community-id
|
||||
:admins admins
|
||||
:show-input? show-input?
|
||||
:edit-enabled true
|
||||
:in-pinned-view? false})
|
||||
:render-fn render-fn
|
||||
:on-viewable-items-changed on-viewable-items-changed
|
||||
:on-end-reached list-on-end-reached
|
||||
:on-scroll-to-index-failed identity ;;don't remove this
|
||||
:content-container-style {:padding-top (+ bottom-space 16)
|
||||
:padding-bottom 16}
|
||||
:scroll-indicator-insets {:top bottom-space} ;;ios only
|
||||
:keyboard-dismiss-mode :interactive
|
||||
:keyboard-should-persist-taps :handled
|
||||
:onMomentumScrollBegin state/start-scrolling
|
||||
:onMomentumScrollEnd state/stop-scrolling
|
||||
[:<>
|
||||
;;do not use anonymous functions for handlers
|
||||
[list/flat-list
|
||||
(merge
|
||||
pan-responder
|
||||
{:key-fn list-key-fn
|
||||
:ref list-ref
|
||||
:header [list-header chat]
|
||||
:footer [list-footer chat]
|
||||
:data (when-not should-send-contact-request?
|
||||
messages)
|
||||
:render-data (get-render-data {:group-chat group-chat
|
||||
:chat-id chat-id
|
||||
:public? public?
|
||||
:community-id community-id
|
||||
:admins admins
|
||||
:show-input? show-input?
|
||||
:edit-enabled true
|
||||
:in-pinned-view? false})
|
||||
:render-fn render-fn
|
||||
:on-viewable-items-changed on-viewable-items-changed
|
||||
:on-end-reached list-on-end-reached
|
||||
:on-scroll-to-index-failed identity ;;don't remove this
|
||||
:content-container-style {:padding-top (+ bottom-space 16)
|
||||
:padding-bottom 16}
|
||||
:scroll-indicator-insets {:top bottom-space} ;;ios only
|
||||
:keyboard-dismiss-mode :interactive
|
||||
:keyboard-should-persist-taps :handled
|
||||
:onMomentumScrollBegin state/start-scrolling
|
||||
:onMomentumScrollEnd state/stop-scrolling
|
||||
:scrollEventThrottle 16
|
||||
:on-scroll on-scroll
|
||||
;;TODO https://github.com/facebook/react-native/issues/30034
|
||||
:inverted (when platform/ios? true)
|
||||
:style (when platform/android? {:scaleY -1})})]))
|
||||
:inverted (when platform/ios? true)
|
||||
:style (when platform/android? {:scaleY -1})})]
|
||||
(when @show-floating-scroll-down-button
|
||||
[floating-scroll-down-button show-input?])]))
|
||||
|
||||
(defn back-button []
|
||||
[quo2.button/button {:type :grey
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
[quo2.components.markdown.text :as quo2.text]
|
||||
[quo2.components.buttons.button :as quo2.button]
|
||||
[quo2.components.counter.counter :as quo2.counter]
|
||||
[quo2.components.tags.filter-tags :as filter-tags]
|
||||
[quo2.components.tags.filter-tag :as filter-tag]
|
||||
[quo2.components.tags.tags :as tags]
|
||||
[quo2.foundations.colors :as quo2.colors]
|
||||
[quo.components.safe-area :as safe-area]
|
||||
[quo2.components.tabs.tabs :as quo2.tabs]
|
||||
@@ -29,14 +28,21 @@
|
||||
(def sort-list-by (reagent/atom :name))
|
||||
|
||||
(def mock-community-item-data ;; TODO: remove once communities are loaded with this data.
|
||||
{:data {:status :gated
|
||||
:locked true
|
||||
;; :images {:thumbnail {:uri []}} ;; TODO address issue with context tag image
|
||||
:cover (resources/get-image :community-cover)
|
||||
:tokens [{:id 1 :group [{:id 1 :token-icon (resources/get-image :status-logo)}]}]
|
||||
:tags [{:id 1 :tag-label (i18n/label :t/music) :resource (resources/get-image :music)}
|
||||
{:id 2 :tag-label (i18n/label :t/lifestyle) :resource (resources/get-image :lifestyle)}
|
||||
{:id 3 :tag-label (i18n/label :t/podcasts) :resource (resources/get-image :podcasts)}]}})
|
||||
{:data {:status :gated
|
||||
:locked true
|
||||
:cover (resources/get-image :community-cover)
|
||||
:tokens [{:id 1
|
||||
:group [{:id 1
|
||||
:token-icon (resources/get-image :status-logo)}]}]
|
||||
:tags [{:id 1
|
||||
:tag-label (i18n/label :t/music)
|
||||
:resource (resources/get-image :music)}
|
||||
{:id 2
|
||||
:tag-label (i18n/label :t/lifestyle)
|
||||
:resource (resources/get-image :lifestyle)}
|
||||
{:id 3
|
||||
:tag-label (i18n/label :t/podcasts)
|
||||
:resource (resources/get-image :podcasts)}]}})
|
||||
|
||||
(defn plus-button []
|
||||
(let [logging-in? (<sub [:multiaccounts/login])]
|
||||
@@ -180,7 +186,7 @@
|
||||
(let [filters [{:id 1 :tag-label (i18n/label :t/music) :resource (resources/get-image :music)}
|
||||
{:id 2 :tag-label (i18n/label :t/lifestyle) :resource (resources/get-image :lifestyle)}
|
||||
{:id 3 :tag-label (i18n/label :t/podcasts) :resource (resources/get-image :podcasts)}
|
||||
{:id 3 :tag-label (i18n/label :t/podcasts) :resource (resources/get-image :podcasts)}]]
|
||||
{:id 4 :tag-label (i18n/label :t/podcasts) :resource (resources/get-image :podcasts)}]]
|
||||
[react/scroll-view {:horizontal true
|
||||
:height 48
|
||||
:shows-horizontal-scroll-indicator false
|
||||
@@ -188,20 +194,12 @@
|
||||
:padding-top 4
|
||||
:padding-bottom 12
|
||||
:padding-horizontal 20}
|
||||
[react/view {:flex-direction :row}
|
||||
[react/view {:margin-right 8}
|
||||
[filter-tag/filter-tag {:resource :main-icons2/search
|
||||
:labelled false
|
||||
:type :icon
|
||||
:icon-color (quo2.colors/theme-colors
|
||||
quo2.colors/neutral-100
|
||||
quo2.colors/white)}]]
|
||||
[filter-tags/tags {:data filters
|
||||
:labelled true
|
||||
:type :emoji
|
||||
:icon-color (quo2.colors/theme-colors
|
||||
quo2.colors/neutral-50
|
||||
quo2.colors/neutral-40)}]]]))
|
||||
[tags/tags {:data filters
|
||||
:labelled true
|
||||
:type :emoji
|
||||
:icon-color (quo2.colors/theme-colors
|
||||
quo2.colors/neutral-50
|
||||
quo2.colors/neutral-40)}]]))
|
||||
|
||||
(defn communities-list []
|
||||
(let [multiaccount (<sub [:multiaccount])
|
||||
|
||||
@@ -1,72 +1,141 @@
|
||||
(ns status-im.ui.screens.communities.community-overview-redesign
|
||||
(:require [status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.components.react :as react]
|
||||
[quo2.components.markdown.text :as quo2.text]
|
||||
[quo2.components.buttons.button :as quo2.button]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.buttons.button :as button]
|
||||
[quo2.components.list-items.preview-list :as preview-list]
|
||||
[quo2.components.list-items.channel :as channel]
|
||||
[quo2.components.dividers.divider-label :as divider-label]
|
||||
[quo2.components.community.community-view :as community-view]
|
||||
[quo2.components.tags.status-tags :as status-tags]
|
||||
[status-im.ui.screens.communities.request-to-join-bottom-sheet-redesign :as request-to-join]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.utils.handlers :refer [<sub >evt]]
|
||||
[status-im.ui.screens.communities.styles :as styles]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.ui.screens.communities.icon :as communities.icon]))
|
||||
|
||||
(defn preview-list-component []
|
||||
[react/view {:style {:height 20 :margin-top 20}}
|
||||
[quo2.text/text {:accessibility-label :communities-screen-title
|
||||
:weight :semi-bold
|
||||
:size :label}
|
||||
"placeholder for preview list component"]])
|
||||
;; Mocked list items
|
||||
(def user-list
|
||||
[{:full-name "Alicia K"}
|
||||
{:full-name "Marcus C"}
|
||||
{:full-name "MNO PQR"}
|
||||
{:full-name "STU VWX"}])
|
||||
|
||||
(defn preview-user-list []
|
||||
[react/view {:style {:flex-direction :row
|
||||
:align-items :center
|
||||
:margin-top 20}}
|
||||
[preview-list/preview-list {:type :user
|
||||
:user user-list :list-size 4 :size 24}]
|
||||
[text/text {:accessibility-label :communities-screen-title
|
||||
:style {:margin-left 8}
|
||||
:size :label}
|
||||
"Join Alicia, Marcus and 2 more"]]) ;; TODO remove mocked data and use from contacts list/communities members
|
||||
|
||||
(def list-of-channels {:Welcome [{:name "welcome"
|
||||
:emoji "🤝"}
|
||||
{:name "onboarding"
|
||||
:emoji "🍑"}
|
||||
{:name "intro"
|
||||
:emoji "🦄"}]
|
||||
:General [{:name "general"
|
||||
:emoji "🐷"}
|
||||
{:name "people-ops"
|
||||
:emoji "🌏"}
|
||||
{:name "announcements"
|
||||
:emoji "🎺"}]
|
||||
:Mobile [{:name "mobile"
|
||||
:emoji "👽"}]})
|
||||
|
||||
(defn channel-list-component []
|
||||
[react/view {:style {:height 200 :margin-top 20}}
|
||||
[quo2.text/text {:accessibility-label :communities-screen-title
|
||||
:weight :semi-bold
|
||||
:size :label}
|
||||
"placeholder for channel list component"]])
|
||||
[react/scroll-view {:style {:margin-top 20}}
|
||||
[:<> {:style {:flex 1}}
|
||||
(map (fn [category]
|
||||
^{:key (get category 0)}
|
||||
[react/view {:flex 1}
|
||||
[divider-label/divider-label
|
||||
{:label (first category)
|
||||
:chevron-position :left}]
|
||||
[react/view
|
||||
{:margin-left 8
|
||||
:margin-top 10
|
||||
:margin-bottom 8}
|
||||
[list/flat-list
|
||||
{:shows-horizontal-scroll-indicator false
|
||||
:separator [react/view {:margin-top 4}]
|
||||
:data ((first category) list-of-channels)
|
||||
:render-fn channel/list-item}]]])
|
||||
list-of-channels)]])
|
||||
|
||||
(defn community-card-page-view [{:keys [name description locked joined
|
||||
status tokens cover tags] :as community}]
|
||||
[react/view
|
||||
{:style
|
||||
{:height "100%"
|
||||
{:flex 1
|
||||
:border-radius 20}}
|
||||
[react/view (styles/community-cover-container 148)
|
||||
|
||||
[react/image
|
||||
{:source cover
|
||||
:style {:height 180
|
||||
:style {:position :relative
|
||||
:flex 1}}]]
|
||||
[react/view (styles/card-view-content-container 20)
|
||||
[react/view (styles/card-view-chat-icon 80)
|
||||
[communities.icon/community-icon-redesign community 80]]
|
||||
(when (= status :gated)
|
||||
[react/view (styles/permission-tag-styles)
|
||||
[community-view/permission-tag-container
|
||||
{:locked locked
|
||||
:status status
|
||||
:tokens tokens}]])
|
||||
[community-view/community-title
|
||||
{:title name
|
||||
:size :large
|
||||
:description description}]
|
||||
[community-view/community-stats-column :card-view]
|
||||
[community-view/community-tags tags]
|
||||
[preview-list-component]
|
||||
(when (not joined)
|
||||
[quo2.button/button
|
||||
{:on-press #(>evt [:bottom-sheet/show-sheet
|
||||
{:content (fn [] [request-to-join/request-to-join community])
|
||||
:content-height 300}])
|
||||
:style
|
||||
{:width "100%"
|
||||
:margin-top 20
|
||||
:margin-left :auto
|
||||
:margin-right :auto}
|
||||
:before :main-icons/community}
|
||||
(i18n/label :join-open-community)])
|
||||
[react/view {:flex 1
|
||||
:height 20
|
||||
:border-radius 16
|
||||
:background-color (colors/theme-colors
|
||||
colors/white
|
||||
colors/neutral-90)}
|
||||
[react/view {:padding-horizontal 20}
|
||||
[react/view {:border-radius 40
|
||||
:border-width 1
|
||||
:border-color colors/white
|
||||
:position :absolute
|
||||
:top (- (/ 80 2))
|
||||
:left (/ 70 4)
|
||||
:padding 2
|
||||
:background-color (colors/theme-colors
|
||||
colors/white
|
||||
colors/neutral-90)}
|
||||
[communities.icon/community-icon-redesign community 80]]
|
||||
(when (and (not joined)
|
||||
(= status :gated))
|
||||
[react/view (styles/permission-tag-styles)
|
||||
[community-view/permission-tag-container
|
||||
{:locked locked
|
||||
:status status
|
||||
:tokens tokens}]])
|
||||
(when joined
|
||||
[react/view {:position :absolute
|
||||
:top 12
|
||||
:right 12}
|
||||
[status-tags/status-tag {:status {:type :positive} :label (i18n/label :joined)}]])
|
||||
[community-view/community-title
|
||||
{:title name
|
||||
:size :large
|
||||
:description description}]
|
||||
[community-view/community-stats-column :card-view]
|
||||
[community-view/community-tags tags]
|
||||
[preview-user-list]
|
||||
(when (not joined)
|
||||
[button/button
|
||||
{:on-press #(>evt [:bottom-sheet/show-sheet
|
||||
{:content (constantly [request-to-join/request-to-join community])
|
||||
:content-height 300}])
|
||||
:style
|
||||
{:width "100%"
|
||||
:margin-top 20
|
||||
:margin-left :auto
|
||||
:margin-right :auto}
|
||||
:before :main-icons2/communities}
|
||||
(i18n/label :join-open-community)])]
|
||||
[channel-list-component]]])
|
||||
|
||||
(defn overview []
|
||||
(let [community (<sub [:get-screen-params :community-overview])]
|
||||
[react/view {:style {}}
|
||||
(let [community-mock (<sub [:get-screen-params :community-overview]) ;;TODO stop using mock data and only pass community id
|
||||
community (<sub [:communities/community (:id community-mock)])]
|
||||
[react/view {:style
|
||||
{:height "100%"}}
|
||||
[community-card-page-view
|
||||
community]]))
|
||||
(merge community-mock {:joined (:joined community)})]]))
|
||||
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
(ns status-im.ui.screens.communities.request-to-join-bottom-sheet-redesign
|
||||
(:require [status-im.i18n.i18n :as i18n]
|
||||
[quo.core :as quo]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.react-native.resources :as resources]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.ui.components.react :as react]
|
||||
[quo2.components.markdown.text :as quo2.text]
|
||||
[quo2.components.buttons.button :as quo2.button]
|
||||
[quo2.components.icon :as quo2.icon]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.buttons.button :as button]
|
||||
[quo2.components.selectors.disclaimer :as disclaimer]
|
||||
[quo2.components.icon :as icon]
|
||||
[status-im.utils.handlers :refer [>evt]]
|
||||
;; [quo2.components.tags.context-tags :as quo2.context-tags]
|
||||
[quo2.components.tags.context-tags :as context-tags]
|
||||
[status-im.communities.core :as communities]
|
||||
[quo2.foundations.colors :as quo2.colors]))
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
;; TODO: update with real data
|
||||
(def community-rules [{:index 1
|
||||
@@ -46,84 +47,75 @@
|
||||
:width 18
|
||||
:margin-left 1
|
||||
:margin-right 9
|
||||
:background-color quo2.colors/white
|
||||
:border-color quo2.colors/neutral-20
|
||||
:background-color colors/white
|
||||
:border-color colors/neutral-20
|
||||
:border-width 1
|
||||
:border-radius 6}}
|
||||
[quo2.text/text {:style
|
||||
{:margin-left :auto
|
||||
:margin-right :auto
|
||||
:margin-top :auto
|
||||
:margin-bottom :auto}
|
||||
:accessibility-label :communities-rule-index
|
||||
:weight :medium
|
||||
:size :label}
|
||||
[text/text {:style
|
||||
{:margin-left :auto
|
||||
:margin-right :auto
|
||||
:margin-top :auto
|
||||
:margin-bottom :auto}
|
||||
:accessibility-label :communities-rule-index
|
||||
:weight :medium
|
||||
:size :label}
|
||||
|
||||
(str index)]]
|
||||
[quo2.text/text
|
||||
[text/text
|
||||
{:accessibility-label :communities-rule-title
|
||||
:weight :semi-bold
|
||||
:size :paragraph-2}
|
||||
title]]
|
||||
[quo2.text/text
|
||||
{:style {:margin-left 28}
|
||||
[text/text
|
||||
{:style {:margin-left 28 :margin-top 1}
|
||||
:accessibility-label :communities-rule-content
|
||||
:size :paragraph-2}
|
||||
content]])
|
||||
|
||||
(defn community-rules-list [rules]
|
||||
|
||||
[list/flat-list
|
||||
{:shows-horizontal-scroll-indicator false
|
||||
:data rules
|
||||
:separator [react/view {:margin-top 1}]
|
||||
:render-fn community-rule-item}])
|
||||
|
||||
(defn rules-checkbox [agreed-to-rules?]
|
||||
[react/view {:style
|
||||
{:flex 1
|
||||
:flex-direction :row
|
||||
:margin-top 20
|
||||
:background-color quo2.colors/neutral-20
|
||||
:padding 12
|
||||
:border-radius 12
|
||||
:border-width 1
|
||||
:border-color quo2.colors/neutral-30}}
|
||||
[quo/checkbox {:value @agreed-to-rules?
|
||||
:on-change #(swap! agreed-to-rules? not)}]
|
||||
[quo2.text/text {:accessibility-label :communities-accept-rules
|
||||
:size :paragraph-2
|
||||
:style {:margin-left 8}}
|
||||
(i18n/label :t/accept-community-rules)]])
|
||||
|
||||
(defn request-to-join [community]
|
||||
(let [agreed-to-rules? (reagent/atom false)]
|
||||
(fn []
|
||||
[react/view {:style {:flex 1 :margin-left 20 :margin-right 20 :margin-bottom 20}}
|
||||
[react/view {:style {:flex 1 :margin-left 20 :margin-right 20 :margin-bottom 20}}
|
||||
[react/view {:style {:flex 1 :flex-direction :row :align-items :center :justify-content :space-between}}
|
||||
|
||||
[quo2.text/text {:accessibility-label :communities-join-community
|
||||
:weight :semi-bold
|
||||
:size :heading-1}
|
||||
[text/text {:accessibility-label :communities-join-community
|
||||
:weight :semi-bold
|
||||
:size :heading-1}
|
||||
(i18n/label :t/join-open-community)]
|
||||
[react/view {:style {:height 32
|
||||
:width 32
|
||||
:align-items :center
|
||||
:background-color quo2.colors/white
|
||||
:border-color quo2.colors/neutral-20
|
||||
:background-color colors/white
|
||||
:border-color colors/neutral-20
|
||||
:border-width 1
|
||||
:border-radius 6
|
||||
:border-radius 8
|
||||
:display :flex
|
||||
:justify-content :center}}
|
||||
[quo2.icon/icon :main-icons2/info]]]
|
||||
;; TODO address issue with context tag image
|
||||
;; [quo2.context-tags/context-tag {:style {:margin-right :auto :margin-top 8}} (get-in community [:images :thumbnail :uri]) (:name community)]
|
||||
[quo2.text/text {:style {:margin-top 24}
|
||||
:accessibility-label :communities-rules-title
|
||||
:weight :semi-bold
|
||||
:size :paragraph-1}
|
||||
[icon/icon :main-icons2/info]]]
|
||||
;; TODO get tag image from community data
|
||||
[context-tags/context-tag
|
||||
{:style
|
||||
{:margin-right :auto
|
||||
:margin-top 8}}
|
||||
(resources/get-image :status-logo) (:name community)]
|
||||
[text/text {:style {:margin-top 24}
|
||||
:accessibility-label :communities-rules-title
|
||||
:weight :semi-bold
|
||||
:size :paragraph-1}
|
||||
(i18n/label :t/community-rules)]
|
||||
[community-rules-list community-rules]
|
||||
[rules-checkbox agreed-to-rules?]
|
||||
|
||||
[disclaimer/disclaimer
|
||||
{:container-style {:margin-top 20}
|
||||
:on-change #(swap! agreed-to-rules? not)}
|
||||
(i18n/label :t/accept-community-rules)]
|
||||
|
||||
[react/view {:style {:width "100%"
|
||||
:margin-top 32 :margin-bottom 16
|
||||
@@ -131,8 +123,10 @@
|
||||
:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :space-evenly}}
|
||||
[quo2.button/button {:on-press #(>evt [:bottom-sheet/hide])
|
||||
:type :grey :style {:flex 1 :margin-right 12}} (i18n/label :t/cancel)]
|
||||
[quo2.button/button
|
||||
{:on-press #(>evt [::communities/join (:id community)])
|
||||
:disabled (not agreed-to-rules?) :style {:flex 1}} (i18n/label :t/join-open-community)]]])))
|
||||
[button/button {:on-press #(>evt [:bottom-sheet/hide])
|
||||
:type :grey :style {:flex 1 :margin-right 12}} (i18n/label :t/cancel)]
|
||||
[button/button
|
||||
{:on-press (fn []
|
||||
(>evt [::communities/join (:id community)])
|
||||
(>evt [:bottom-sheet/hide]))
|
||||
:disabled (not @agreed-to-rules?) :style {:flex 1}} (i18n/label :t/join-open-community)]]])))
|
||||
|
||||
@@ -26,12 +26,10 @@
|
||||
[status-im.utils.config :as config]
|
||||
[quo2.components.markdown.text :as quo2.text]
|
||||
[status-im.qr-scanner.core :as qr-scanner]
|
||||
[status-im.utils.datetime :as datetime]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
|
||||
[status-im.ui.components.chat-icon.styles :as chat-icon.styles]
|
||||
[quo2.foundations.colors :as quo2.colors]
|
||||
[quo2.components.buttons.button :as quo2.button]
|
||||
[quo2.components.info.information-box :as information-box])
|
||||
[quo2.components.buttons.button :as quo2.button])
|
||||
(:require-macros [status-im.utils.views :as views]))
|
||||
|
||||
(defn home-tooltip-view []
|
||||
@@ -189,12 +187,9 @@
|
||||
[home-tooltip-view]
|
||||
[react/view {:height 68}])}])))
|
||||
|
||||
(def ens-banner-expire-timestamp 1665010800)
|
||||
|
||||
(views/defview communities-and-chats-old []
|
||||
(views/letsubs [{:keys [items search-filter]} [:home-items]
|
||||
hide-home-tooltip? [:hide-home-tooltip?]
|
||||
information-box-closed? [:information-box-closed? :ens-banner]]
|
||||
hide-home-tooltip? [:hide-home-tooltip?]]
|
||||
(if (and (empty? items)
|
||||
(empty? search-filter)
|
||||
hide-home-tooltip?
|
||||
@@ -210,19 +205,6 @@
|
||||
:header [:<>
|
||||
(when (or (seq items) @search-active? (seq search-filter))
|
||||
[search-input-wrapper-old search-filter (empty? items)])
|
||||
(when (< (datetime/timestamp-sec) ens-banner-expire-timestamp)
|
||||
[information-box/information-box
|
||||
{:type :informative
|
||||
:closable? true
|
||||
:closed? information-box-closed?
|
||||
:icon :main-icons/info
|
||||
:style {:margin 20}
|
||||
:button-label (i18n/label :t/open-dapp2)
|
||||
:on-button-press #(re-frame/dispatch
|
||||
[:browser.ui/open-url "https://ens-collect.status.im/"])
|
||||
:id :ens-banner
|
||||
:on-close #(re-frame/dispatch [:close-information-box :ens-banner true])}
|
||||
(i18n/label :t/ens-banner-message)])
|
||||
(when (and (empty? items)
|
||||
(or @search-active? (seq search-filter)))
|
||||
[start-suggestion search-filter])]
|
||||
|
||||
@@ -181,4 +181,4 @@
|
||||
;; TODO: Remove this (highly) temporary flag once the new Activity Center is
|
||||
;; usable enough to replace the old one **in the new UI**.
|
||||
(def new-activity-center-enabled?
|
||||
(atom false))
|
||||
(atom true))
|
||||
|
||||
@@ -240,3 +240,9 @@
|
||||
|
||||
(defn to-ms [sec]
|
||||
(* 1000 sec))
|
||||
|
||||
(defn now->iso8601 []
|
||||
(unparse {:format-str "HH:mm:ss.SSS"}
|
||||
(-> (timestamp)
|
||||
(from-long)
|
||||
(plus time-zone-offset))))
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
(ns status-im.utils.number)
|
||||
|
||||
(defn naive-round
|
||||
"Quickly and naively round number `n` up to `decimal-places`.
|
||||
|
||||
Example usage: use it to avoid re-renders caused by floating-point number
|
||||
changes in Reagent atoms. Such numbers can be rounded up to a certain number
|
||||
of `decimal-places` in order to avoid re-rendering due to tiny fractional
|
||||
changes.
|
||||
|
||||
Don't use this function for arbitrary-precision arithmetic."
|
||||
[n decimal-places]
|
||||
(let [scale (Math/pow 10 decimal-places)]
|
||||
(/ (Math/round (* n scale))
|
||||
scale)))
|
||||
@@ -1,10 +0,0 @@
|
||||
(ns status-im.utils.profiler)
|
||||
|
||||
(defmacro with-measure
|
||||
[name & body]
|
||||
`(let [start# (js/performance.now)
|
||||
res# (do ~@body)
|
||||
end# (js/performance.now)
|
||||
time# (.toFixed (- end# start#) 2)]
|
||||
(taoensso.timbre/info "[perf|" ~name "] => " time#)
|
||||
res#))
|
||||
@@ -1,39 +0,0 @@
|
||||
(ns status-im.utils.profiler
|
||||
"Performance profiling for react components."
|
||||
(:require-macros [status-im.utils.profiler])
|
||||
(:require [reagent.core :as reagent]
|
||||
[taoensso.timbre :as log]
|
||||
[oops.core :refer [oget ocall]]
|
||||
[goog.functions :as f]))
|
||||
|
||||
(defonce memo (atom {}))
|
||||
|
||||
(def td (js/require "tdigest"))
|
||||
(def tdigest (oget td "TDigest"))
|
||||
|
||||
(def react (js/require "react"))
|
||||
(def profiler (reagent/adapt-react-class (oget react "Profiler")))
|
||||
|
||||
(defn on-render-factory
|
||||
[label]
|
||||
(let [buf (new tdigest)
|
||||
log (f/debounce
|
||||
(fn [phase buf]
|
||||
(log/info "[profile:" label "(" phase ")]: \n"
|
||||
(ocall buf "summary")))
|
||||
300)]
|
||||
(fn [_ phase adur _ _ _ _]
|
||||
(.push buf adur)
|
||||
(log phase buf))))
|
||||
|
||||
(defn perf [{:keys [label]}]
|
||||
(let [this (reagent/current-component)
|
||||
children (reagent/children this)
|
||||
on-render (if-let [render-fn (get @memo label)]
|
||||
render-fn
|
||||
(do
|
||||
(swap! memo assoc label (on-render-factory label))
|
||||
(get @memo label)))]
|
||||
(into [profiler {:id label
|
||||
:on-render on-render}]
|
||||
children)))
|
||||
@@ -105,15 +105,6 @@
|
||||
(.then #(log/debug "[wallet-connect] session topic " topic " changed to account " (first accounts)))
|
||||
(.catch #(log/error "[wallet-connect] " %)))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:wc-2-change-session
|
||||
(fn [[client topic accounts]]
|
||||
(-> ^js client
|
||||
(.update (clj->js {:topic topic
|
||||
:state {:accounts accounts}}))
|
||||
(.then #(log/debug "[wallet-connect] session topic " topic " changed to account " (first accounts)))
|
||||
(.catch #(log/error "[wallet-connect] " %)))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:wc-2-pair
|
||||
(fn [[client uri]]
|
||||
|
||||
@@ -89,6 +89,7 @@ class TestrailReport(BaseTestReport):
|
||||
'include_all': False}
|
||||
run = self.post('add_run/%s' % self.project_id, request_body)
|
||||
self.run_id = run['id']
|
||||
print("Testrun: %sruns/view/%s" % (self.url, self.run_id))
|
||||
|
||||
def get_cases(self, section_ids):
|
||||
test_cases = list()
|
||||
@@ -157,9 +158,7 @@ class TestrailReport(BaseTestReport):
|
||||
else:
|
||||
for phase in test_cases:
|
||||
if phase != 'upgrade':
|
||||
print("For %s phase" % phase)
|
||||
for category in test_cases[phase]:
|
||||
print("For %s category" % category)
|
||||
for case in self.get_cases([test_cases[phase][category]]):
|
||||
case_ids.append(case['id'])
|
||||
return case_ids
|
||||
@@ -299,7 +298,8 @@ class TestrailReport(BaseTestReport):
|
||||
try:
|
||||
test_id = self.get('get_results_for_case/%s/%s' % (test_run_id, test_case_id))['results'][0]['test_id']
|
||||
return '%stests/view/%s' % (self.url, test_id)
|
||||
except KeyError:
|
||||
except (KeyError, JSONDecodeError):
|
||||
print('Cannot extract result for %s e2e' % test_case_id)
|
||||
return None
|
||||
|
||||
def get_not_executed_tests(self, test_run_id):
|
||||
|
||||
@@ -102,7 +102,7 @@ class TestCommandsMultipleDevicesMerged(MultipleSharedDeviceTestCase):
|
||||
[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 (rechecked 23.11.21, valid)
|
||||
# TODO: should be added PNs for receiver after getting more stable feature (rechecked 04.10.22, valid)
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@marks.testrail_id(6265)
|
||||
@@ -337,19 +337,19 @@ class TestOneToOneChatMultipleSharedDevices(MultipleSharedDeviceTestCase):
|
||||
self.chat_2.send_message(message_before_edit_1_1)
|
||||
|
||||
self.chat_2.edit_message_in_chat(message_before_edit_1_1, message_after_edit_1_1)
|
||||
if not self.home_1.element_by_text_part(message_after_edit_1_1).is_element_present():
|
||||
if not self.home_1.element_by_text_part(message_after_edit_1_1).is_element_displayed():
|
||||
self.errors.append('UNedited message version displayed on preview')
|
||||
self.home_1.get_chat(self.default_username_2).click()
|
||||
chat_element = self.chat_1.chat_element_by_text(message_after_edit_1_1)
|
||||
if not chat_element.is_element_present(30):
|
||||
if not chat_element.is_element_displayed(30):
|
||||
self.errors.append('No edited message in 1-1 chat displayed')
|
||||
if not self.chat_1.element_by_text_part("⌫ Edited").is_element_present(30):
|
||||
if not self.chat_1.element_by_text_part("⌫ Edited").is_element_displayed(30):
|
||||
self.errors.append('No mark in message bubble about this message was edited on receiver side')
|
||||
|
||||
self.device_2.just_fyi("Verify Device1 can not edit and delete received message from Device2")
|
||||
chat_element.long_press_element()
|
||||
for action in ("edit", "delete"):
|
||||
if self.chat_1.element_by_translation_id(action).is_element_present():
|
||||
if self.chat_1.element_by_translation_id(action).is_element_displayed():
|
||||
self.errors.append('Option to %s someone else message available!' % action)
|
||||
self.home_1.click_system_back_button()
|
||||
|
||||
@@ -532,9 +532,9 @@ class TestOneToOneChatMultipleSharedDevices(MultipleSharedDeviceTestCase):
|
||||
self.chat_2.image_message_in_chat.click_until_presence_of_element(self.chat_2.share_image_icon_button)
|
||||
self.chat_2.share_image_icon_button.click()
|
||||
self.chat_2.share_via_messenger()
|
||||
if not self.chat_2.image_in_android_messenger.is_element_present():
|
||||
if not self.chat_2.image_in_android_messenger.is_element_displayed():
|
||||
self.errors.append("Can't share image")
|
||||
self.chat_2.click_system_back_button_until_element_is_shown()
|
||||
self.chat_2.click_system_back_button_until_element_is_shown(element=self.chat_2.save_image_icon_button)
|
||||
self.chat_2.save_image_icon_button.click()
|
||||
self.chat_2.show_images_button.click()
|
||||
self.chat_2.allow_button.wait_and_click()
|
||||
@@ -1153,7 +1153,7 @@ class TestEnsStickersMultipleDevicesMerged(MultipleSharedDeviceTestCase):
|
||||
self.errors.append('Sticker was not sent from Recent')
|
||||
|
||||
# self.home_2.just_fyi('Check that can install stickers by tapping on sticker message')
|
||||
# TODO: disabled because of #13683 (rechecked 27.07.22, valid)
|
||||
# TODO: disabled because of #13683 (rechecked 04.10.22, valid)
|
||||
self.home_2.home_button.double_click()
|
||||
self.home_2.get_chat(self.sender['username']).click()
|
||||
# self.chat_2.chat_item.click()
|
||||
|
||||
@@ -50,7 +50,7 @@ class TestOnboardingOneDeviceMerged(MultipleSharedDeviceTestCase):
|
||||
self.home.just_fyi("Sharing contact code via messenger")
|
||||
self.profile.share_button.click()
|
||||
self.profile.share_via_messenger()
|
||||
if not self.profile.element_by_text_part(public_key).is_element_present():
|
||||
if not self.profile.element_by_text_part(public_key).is_element_displayed():
|
||||
self.errors.append("Can't share public key")
|
||||
self.profile.click_system_back_button_until_element_is_shown()
|
||||
|
||||
@@ -78,7 +78,7 @@ class TestOnboardingOneDeviceMerged(MultipleSharedDeviceTestCase):
|
||||
self.home.just_fyi("Sharing wallet address via messenger")
|
||||
request.share_button.click()
|
||||
wallet.share_via_messenger()
|
||||
if not wallet.element_by_text_part(address).is_element_present():
|
||||
if not wallet.element_by_text_part(address).is_element_displayed():
|
||||
self.errors.append("Can't share address")
|
||||
wallet.click_system_back_button_until_element_is_shown()
|
||||
|
||||
@@ -477,4 +477,4 @@ class TestRestoreOneDeviceMerged(MultipleSharedDeviceTestCase):
|
||||
transaction_amount_added = wallet.get_unique_amount()
|
||||
wallet.send_transaction(amount=transaction_amount_added, recipient=transaction_senders['ETH_8']['address'],
|
||||
keycard=True, sign_transaction=True)
|
||||
self.errors.verify_no_errors()
|
||||
self.errors.verify_no_errors()
|
||||
@@ -292,7 +292,7 @@ class TestPublicChatBrowserOneDeviceMerged(MultipleSharedDeviceTestCase):
|
||||
def test_public_chat_copy_and_paste_message_in_chat_input(self):
|
||||
message_text = {'text_message': 'mmmeowesage_text'}
|
||||
formatted_message = {'message_with_link': 'https://status.im',
|
||||
# TODO: blocked with 11161 (rechecked 23.11.21, valid)
|
||||
# TODO: blocked with 11161 (rechecked 04.10.22, valid)
|
||||
# 'message_with_tag': '#successishere'
|
||||
}
|
||||
message_input = self.chat.chat_message_input
|
||||
|
||||
@@ -155,7 +155,7 @@ class TestSendTxDeviceMerged(MultipleSharedDeviceTestCase):
|
||||
self.errors.append("Tx is not sent!")
|
||||
send_tx.ok_button.click()
|
||||
|
||||
# TODO: disabled due to 10838 (rechecked 27.07.22, valid)
|
||||
# TODO: disabled due to 10838 (rechecked 04.10.22, valid)
|
||||
# transactions_view = wallet.transaction_history_button.click()
|
||||
# transactions_view.transactions_table.find_transaction(amount=amount, asset=symbol)
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@@ -169,13 +169,13 @@ class TestActivityCenterMultipleDeviceMedium(MultipleSharedDeviceTestCase):
|
||||
self.home_1.home_button.double_click()
|
||||
|
||||
self.home_1.just_fyi("Check there are no unread messages counter on chats after message is read")
|
||||
if (self.home_1.notifications_unread_badge.is_element_present() or
|
||||
if (self.home_1.notifications_unread_badge.is_element_displayed() or
|
||||
self.home_1.get_chat_from_home_view(self.group_chat_name_1).new_messages_counter.text == "1"):
|
||||
self.errors.append("Unread message indicator is kept after message is read in chat")
|
||||
|
||||
self.home_1.just_fyi("Check there is an empty view on Activity Center")
|
||||
self.home_1.notifications_button.click()
|
||||
if not self.home_1.element_by_translation_id('empty-activity-center').is_element_present():
|
||||
if not self.home_1.element_by_translation_id('empty-activity-center').is_element_displayed():
|
||||
self.errors.append("Activity Center still has some chats after user has opened all of them")
|
||||
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@@ -280,18 +280,21 @@ class TestBrowserProfileOneDevice(MultipleSharedDeviceTestCase):
|
||||
@marks.testrail_id(702166)
|
||||
def test_profile_add_custom_network(self):
|
||||
self.home.get_back_to_home_view()
|
||||
rpc, name, id, symbol = 'https://polygon-rpc.com/', 'Polygon', '137', 'MATIC'
|
||||
profile = self.home.profile_button.click()
|
||||
profile.add_custom_network()
|
||||
profile.add_custom_network(rpc_url=rpc, netwrok_id=id, symbol=symbol, name=name)
|
||||
self.sign_in.sign_in()
|
||||
wallet = self.home.wallet_button.click()
|
||||
if not wallet.element_by_text_part(symbol).is_element_displayed():
|
||||
self.errors.append("No %s currency is shown when switching to custom network" % symbol)
|
||||
self.home.profile_button.click()
|
||||
profile.advanced_button.click()
|
||||
profile.network_settings_button.scroll_to_element(10, 'up')
|
||||
if not profile.element_by_text_part('custom_ropsten').is_element_displayed():
|
||||
self.driver.fail("Network custom_ropsten was not added!")
|
||||
if not profile.element_by_text_part(name).is_element_displayed():
|
||||
self.driver.fail("Custom network %s was not added!" % name)
|
||||
profile.get_back_to_home_view()
|
||||
# Switching back to Goerli for the next cases
|
||||
profile.switch_network('Goerli with upstream RPC')
|
||||
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@marks.testrail_id(702164)
|
||||
|
||||
@@ -148,7 +148,7 @@ class TestTimelineHistoryNodesBootnodesMultipleDeviceMergedMedium(MultipleShared
|
||||
self.profile_1.bootnodes_button.click()
|
||||
self.profile_1.add_bootnode_button.click()
|
||||
self.profile_1.specify_name_input.set_value('test')
|
||||
# TODO: blocked as validation is missing for bootnodes (rechecked 27.07.22, valid)
|
||||
# TODO: blocked as validation is missing for bootnodes (rechecked 04.10.22, 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')
|
||||
@@ -420,7 +420,7 @@ class TestChatMediumMultipleDevice(MultipleSharedDeviceTestCase):
|
||||
self.chat_1.send_message(self.message_1)
|
||||
self.chat_1.send_message(self.message_2)
|
||||
self.chat_1.pin_message(self.message_1)
|
||||
if not self.chat_1.chat_element_by_text(self.message_1).pinned_by_label.is_element_present():
|
||||
if not self.chat_1.chat_element_by_text(self.message_1).pinned_by_label.is_element_displayed():
|
||||
self.drivers[0].fail("Message is not pinned!")
|
||||
|
||||
self.home_1.just_fyi("Check that Device2 can pin Device1 message in 1-1 chat and two pinned "
|
||||
@@ -435,16 +435,16 @@ class TestChatMediumMultipleDevice(MultipleSharedDeviceTestCase):
|
||||
self.chat_1.chat_options.click()
|
||||
self.chat_1.view_profile_button.click()
|
||||
self.chat_1.pinned_messages_button.click()
|
||||
if not (self.chat_1.chat_element_by_text(self.message_1).pinned_by_label.is_element_present() and
|
||||
self.chat_1.chat_element_by_text(self.message_2).pinned_by_label.is_element_present() and
|
||||
self.chat_1.chat_element_by_text(self.message_1).is_element_present() and
|
||||
self.chat_1.chat_element_by_text(self.message_2).is_element_present()):
|
||||
if not (self.chat_1.chat_element_by_text(self.message_1).pinned_by_label.is_element_displayed() and
|
||||
self.chat_1.chat_element_by_text(self.message_2).pinned_by_label.is_element_displayed() and
|
||||
self.chat_1.chat_element_by_text(self.message_1).is_element_displayed() and
|
||||
self.chat_1.chat_element_by_text(self.message_2).is_element_displayed()):
|
||||
self.drivers[0].fail("Something missed on Pinned messaged on Device 1!")
|
||||
self.chat_2.pinned_messages_button.click()
|
||||
if not (self.chat_1.chat_element_by_text(self.message_1).pinned_by_label.is_element_present() and
|
||||
self.chat_2.chat_element_by_text(self.message_2).pinned_by_label.is_element_present() and
|
||||
self.chat_2.chat_element_by_text(self.message_1).is_element_present() and
|
||||
self.chat_2.chat_element_by_text(self.message_2).is_element_present()):
|
||||
if not (self.chat_1.chat_element_by_text(self.message_1).pinned_by_label.is_element_displayed() and
|
||||
self.chat_2.chat_element_by_text(self.message_2).pinned_by_label.is_element_displayed() and
|
||||
self.chat_2.chat_element_by_text(self.message_1).is_element_displayed() and
|
||||
self.chat_2.chat_element_by_text(self.message_2).is_element_displayed()):
|
||||
self.drivers[0].fail("Something missed on Pinned messaged on Device 2!")
|
||||
self.chat_1.close_button.click()
|
||||
|
||||
@@ -454,25 +454,25 @@ class TestChatMediumMultipleDevice(MultipleSharedDeviceTestCase):
|
||||
self.chat_1.send_message(self.message_4)
|
||||
self.chat_1.pin_message(self.message_3)
|
||||
self.chat_1.pin_message(self.message_4)
|
||||
if not self.chat_1.unpin_message_popup.is_element_present():
|
||||
if not self.chat_1.unpin_message_popup.is_element_displayed():
|
||||
self.drivers[0].fail("No 'Unpin' dialog appears when pining 4th message")
|
||||
|
||||
self.home_1.just_fyi("Unpin one message so that another could be pinned")
|
||||
self.chat_1.unpin_message_popup.message_text(self.message_1).click()
|
||||
self.chat_1.unpin_message_popup.click_unpin_message_button()
|
||||
|
||||
if self.chat_1.unpin_message_popup.is_element_present():
|
||||
if self.chat_1.unpin_message_popup.is_element_displayed():
|
||||
self.drivers[0].fail("Unpin message pop up keep staying after Unpin button pressed")
|
||||
if self.chat_1.chat_element_by_text(self.message_1).pinned_by_label.is_element_present():
|
||||
if self.chat_1.chat_element_by_text(self.message_1).pinned_by_label.is_element_displayed():
|
||||
self.drivers[0].fail("Message is not unpinned!")
|
||||
if not self.chat_1.chat_element_by_text(self.message_4).pinned_by_label.is_element_present():
|
||||
if not self.chat_1.chat_element_by_text(self.message_4).pinned_by_label.is_element_displayed():
|
||||
self.drivers[0].fail("Message is not pinned!")
|
||||
|
||||
self.home_1.just_fyi("Unpin another message and check it's unpinned for another user")
|
||||
self.chat_2.close_button.click()
|
||||
self.chat_2.pin_message(self.message_4, action="unpin")
|
||||
self.chat_1.chat_element_by_text(self.message_4).pinned_by_label.wait_for_invisibility_of_element()
|
||||
if self.chat_1.chat_element_by_text(self.message_4).pinned_by_label.is_element_present():
|
||||
if self.chat_1.chat_element_by_text(self.message_4).pinned_by_label.is_element_displayed():
|
||||
self.drivers[0].fail("Message_4 is not unpinned!")
|
||||
|
||||
@marks.testrail_id(702065)
|
||||
@@ -577,13 +577,13 @@ class TestChatMediumMultipleDevice(MultipleSharedDeviceTestCase):
|
||||
[home.get_chat(self.new_group_chat_name).click() for home in (self.home_1, self.home_2)]
|
||||
self.group_chat_1.send_message(self.message_1)
|
||||
self.group_chat_1.pin_message(self.message_1)
|
||||
if not (self.group_chat_1.chat_element_by_text(self.message_1).pinned_by_label.is_element_present(30) and
|
||||
self.group_chat_2.chat_element_by_text(self.message_1).pinned_by_label.is_element_present(30)):
|
||||
if not (self.group_chat_1.chat_element_by_text(self.message_1).pinned_by_label.is_element_displayed(30) and
|
||||
self.group_chat_2.chat_element_by_text(self.message_1).pinned_by_label.is_element_displayed(30)):
|
||||
self.errors.append("Message is not pinned in group chat!")
|
||||
|
||||
self.home_1.just_fyi("Check that non admin user can not unpin messages")
|
||||
self.group_chat_2.chat_element_by_text(self.message_1).long_press_element()
|
||||
if self.group_chat_2.element_by_translation_id("unpin").is_element_present():
|
||||
if self.group_chat_2.element_by_translation_id("unpin").is_element_displayed():
|
||||
self.errors.append("Unpin option is available for non-admin user")
|
||||
|
||||
self.home_1.just_fyi("Grant another user with admin rights and check he can unpin message now")
|
||||
@@ -593,8 +593,8 @@ class TestChatMediumMultipleDevice(MultipleSharedDeviceTestCase):
|
||||
options.make_admin_button.click()
|
||||
self.group_chat_2.click_system_back_button()
|
||||
self.group_chat_2.pin_message(self.message_1, action="unpin")
|
||||
if (self.group_chat_1.chat_element_by_text(self.message_1).pinned_by_label.is_element_present() and
|
||||
self.group_chat_2.chat_element_by_text(self.message_1).pinned_by_label.is_element_present()):
|
||||
if (self.group_chat_1.chat_element_by_text(self.message_1).pinned_by_label.is_element_displayed() and
|
||||
self.group_chat_2.chat_element_by_text(self.message_1).pinned_by_label.is_element_displayed()):
|
||||
self.errors.append("Message failed be unpinned by user who granted admin permissions!")
|
||||
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@@ -43,7 +43,7 @@ class TestDeeplinkChatProfileOneDevice(MultipleSharedDeviceTestCase):
|
||||
self.sign_in.open_weblink_and_login(deep_link)
|
||||
self.home = self.sign_in.get_home_view()
|
||||
self.home.plus_button.click_until_presence_of_element(self.home.start_new_chat_button)
|
||||
if not self.home.start_new_chat_button.is_element_present():
|
||||
if not self.home.start_new_chat_button.is_element_displayed():
|
||||
self.errors.append(
|
||||
"Can't navigate to start new chat after app opened from deep link with invalid public key")
|
||||
self.drivers[0].close_app()
|
||||
@@ -78,7 +78,7 @@ class TestDeeplinkChatProfileOneDevice(MultipleSharedDeviceTestCase):
|
||||
web_view = self.sign_in.get_chat_view()
|
||||
try:
|
||||
test_dapp_view = web_view.open_in_status_button.click()
|
||||
test_dapp_view.allow_button.is_element_present()
|
||||
test_dapp_view.allow_button.is_element_displayed()
|
||||
except NoSuchElementException:
|
||||
self.drivers[0].fail("DApp '%s' is not opened!" % dapp_name)
|
||||
self.home.reopen_app()
|
||||
@@ -96,7 +96,7 @@ class TestDeeplinkChatProfileOneDevice(MultipleSharedDeviceTestCase):
|
||||
chat.share_button.click()
|
||||
chat.share_via_messenger()
|
||||
if not chat.element_by_text_part(
|
||||
'https://join.status.im/u/%s' % dummy_user["public_key"]).is_element_present():
|
||||
'https://join.status.im/u/%s' % dummy_user["public_key"]).is_element_displayed():
|
||||
self.errors.append("Can't share public key of contact")
|
||||
for _ in range(2):
|
||||
chat.click_system_back_button_until_element_is_shown()
|
||||
@@ -112,7 +112,7 @@ class TestDeeplinkChatProfileOneDevice(MultipleSharedDeviceTestCase):
|
||||
self.public_chat.share_chat_button.click()
|
||||
self.public_chat.share_via_messenger()
|
||||
if not self.public_chat.element_by_text_part(
|
||||
'https://join.status.im/%s' % self.public_chat_name).is_element_present():
|
||||
'https://join.status.im/%s' % self.public_chat_name).is_element_displayed():
|
||||
self.errors.append("Can't share link to public chat")
|
||||
for _ in range(2):
|
||||
self.public_chat.click_system_back_button_until_element_is_shown()
|
||||
@@ -130,8 +130,8 @@ class TestDeeplinkChatProfileOneDevice(MultipleSharedDeviceTestCase):
|
||||
expeceted_text_1 = 'https://join.status.im/b/https://dap.ps'
|
||||
expeceted_text_2 = 'https://join.status.im/b/http://dap.ps'
|
||||
|
||||
if not (browser.element_by_text_part(expeceted_text_1).is_element_present() or
|
||||
browser.element_by_text_part(expeceted_text_2).is_element_present()):
|
||||
if not (browser.element_by_text_part(expeceted_text_1).is_element_displayed() or
|
||||
browser.element_by_text_part(expeceted_text_2).is_element_displayed()):
|
||||
self.errors.append("Can't share link to URL")
|
||||
|
||||
browser.click_system_back_button_until_element_is_shown()
|
||||
|
||||
@@ -108,8 +108,6 @@ class TestPairingSyncMediumMultipleDevicesMerged(MultipleSharedDeviceTestCase):
|
||||
self.profile_1.sync_all_button.wait_for_visibility_of_element(20)
|
||||
[device.profile_button.double_click() for device in (self.profile_1, self.profile_2)]
|
||||
[device.home_button.double_click() for device in (self.profile_1, self.device_3)]
|
||||
# TODO Relogin to workaround #13647, should be removed after fix of the issue(rechecked 27.07.22, valid)
|
||||
self.profile_2.relogin()
|
||||
self.device_3.put_app_to_background_and_back()
|
||||
|
||||
@marks.testrail_id(702133)
|
||||
|
||||
@@ -470,13 +470,13 @@ class TestChatManagement(SingleDeviceTestCase):
|
||||
sign_in = SignInView(self.driver)
|
||||
sign_in.create_user()
|
||||
wallet = sign_in.wallet_button.click()
|
||||
if wallet.backup_recovery_phrase_warning_text.is_element_present():
|
||||
if wallet.backup_recovery_phrase_warning_text.is_element_displayed():
|
||||
self.driver.fail("'Back up your seed phrase' warning is shown on Wallet while no funds are present")
|
||||
address = wallet.get_wallet_address()
|
||||
self.click = wallet.close_button.click()
|
||||
w3.donate_testnet_eth(address, 0.0001)
|
||||
wallet.wait_balance_is_changed()
|
||||
if not wallet.backup_recovery_phrase_warning_text.is_element_present(30):
|
||||
if not wallet.backup_recovery_phrase_warning_text.is_element_displayed(30):
|
||||
self.driver.fail("'Back up your seed phrase' warning is not shown on Wallet with funds")
|
||||
profile = wallet.get_profile_view()
|
||||
wallet.backup_recovery_phrase_warning_text.click()
|
||||
@@ -813,7 +813,7 @@ class TestChatManagement(SingleDeviceTestCase):
|
||||
no_link_tos_error_msg = 'Could not open Terms of Use from'
|
||||
|
||||
signin.just_fyi("Checking privacy policy and TOS links")
|
||||
if not signin.privacy_policy_link.is_element_present():
|
||||
if not signin.privacy_policy_link.is_element_displayed():
|
||||
self.errors.append('%s Sign in view!' % no_link_found_error_msg)
|
||||
if not signin.terms_of_use_link.is_element_displayed():
|
||||
self.driver.fail("No Terms of Use link on Sign in view!")
|
||||
@@ -990,6 +990,7 @@ class TestChatManagement(SingleDeviceTestCase):
|
||||
self.driver.fail("Unblocked user not added previously in contact list added in contacts!")
|
||||
|
||||
@marks.testrail_id(5721)
|
||||
@marks.xfail(reason="may be failed due to #14013")
|
||||
def test_group_chat_cant_add_more_twenty_participants(self):
|
||||
user_20_contacts = dict()
|
||||
user_20_contacts[
|
||||
@@ -1123,7 +1124,7 @@ class TestChatManagement(SingleDeviceTestCase):
|
||||
|
||||
@marks.testrail_id(6300)
|
||||
@marks.skip
|
||||
# TODO: waiting mode (rechecked 27.07.22, valid)
|
||||
# TODO: waiting mode (rechecked 04.10.22, valid)
|
||||
def test_webview_security(self):
|
||||
home_view = SignInView(self.driver).create_user()
|
||||
daap_view = home_view.dapp_tab_button.click()
|
||||
|
||||
@@ -95,10 +95,9 @@ class BaseElement(object):
|
||||
counter = 0
|
||||
self.driver.info("Click until `%s` by `%s`: `%s` will be presented" % (
|
||||
desired_element.name, desired_element.by, desired_element.locator))
|
||||
while not desired_element.is_element_present(1) and counter <= attempts:
|
||||
while not desired_element.is_element_displayed(1) and counter <= attempts:
|
||||
try:
|
||||
self.find_element().click()
|
||||
desired_element.is_element_present(5)
|
||||
return self.navigate()
|
||||
except (NoSuchElementException, TimeoutException):
|
||||
counter += 1
|
||||
@@ -178,11 +177,11 @@ class BaseElement(object):
|
||||
self.scroll_to_element(direction=direction)
|
||||
self.click()
|
||||
|
||||
def is_element_present(self, sec=5):
|
||||
try:
|
||||
return self.wait_for_element(sec)
|
||||
except TimeoutException:
|
||||
return False
|
||||
# def is_element_present(self, sec=5):
|
||||
# try:
|
||||
# return self.wait_for_element(sec)
|
||||
# except TimeoutException:
|
||||
# return False
|
||||
|
||||
def is_element_displayed(self, sec=5, ignored_exceptions=None):
|
||||
try:
|
||||
@@ -374,7 +373,7 @@ class Button(BaseElement):
|
||||
counter = 0
|
||||
self.driver.info("Click until `%s` by `%s`: `%s` is NOT presented" % (
|
||||
desired_element.name, desired_element.by, desired_element.locator))
|
||||
while desired_element.is_element_present(1) and counter <= attempts:
|
||||
while desired_element.is_element_displayed(1) and counter <= attempts:
|
||||
try:
|
||||
self.find_element().click()
|
||||
counter += 1
|
||||
|
||||