Compare commits

...
Author SHA1 Message Date
Siddarth Kumar c4dc896813 debug!: point to store node investigation branch 2024-04-17 15:40:43 +02:00
Siddarth Kumar e73cd73c1c override gomobile to enable & use cgo 2024-04-17 15:40:43 +02:00
Siddarth Kumar adc222a88d debug:! CGO_ENABLED=1;
https://github.com/status-im/status-go/compare/d246699c...5cccd862
2024-04-17 15:40:43 +02:00
Siddarth Kumar d6ef1eb775 point to status-go with go upgrade
https://github.com/status-im/status-go/compare/d246699c...ddff9e07
2024-04-17 15:40:43 +02:00
Siddarth Kumar 966cd511e6 set go to 1.20 in nix overlay 2024-04-17 15:40:43 +02:00
Siddarth Kumar 4134d18cbc nuke unused lib rn-snoopy (#19691)
## Summary

We don't use `rn-snoopy` anymore and this commit nukes it.
2024-04-17 15:27:00 +02:00
Sean Hagstrom 8600c1d3a5 Allow for env variables for configuring delete-message undo time limit for toasts (#19653)
* tweak: allow for env variable to configure undo-time-limit when deleting a message in chat

* tweak: add env variable undo timeout for deleting messages for everyone

* tweak: extend timeout duration for undo toast when deleting a message for a user

* tweak: extend timeout duration for undo toast when deleting a message for everyone
2024-04-17 15:12:27 +02:00
flexsurfer 570c62e30e replace ratom in quo wallet component (#19687) 2024-04-17 14:30:33 +02:00
flexsurfer 549efdb77c Show the users that the keycard feature will be coming soon (#19686) 2024-04-17 13:58:03 +02:00
Parvesh Monu 8807a63989 fix role is not updated correctly when unselecting accounts (#19636) 2024-04-17 16:21:32 +05:30
Alexander c0be0b7670 Update QR code scanner to handle channel and community links (#19507)
* Experiment

* Fix

* Fix

* Fix
2024-04-17 12:27:01 +02:00
John Ngei 6e854428f2 fix: pinned messages empty state drawer margin issues 2024-04-17 10:28:49 +02:00
Icaro Motta c81dd8e3e3 Don't treat dynamic data as a function (#19672)
Sometimes an integration test was throwing "TypeError: Cannot read properties of
null (reading 'call')" because a value was nil in the app-db, but the code was
treating it as if it was always a map and could be called as a function.

The original code wasn't respecting Clojure's nil punning good practices. In
Clojure, it's often recommended to not use dynamic data as functions, because if
they're nil, in CLJS we'll get an exception and as usual in CLJS, the stacktrace
won't be very readable.

We should prefer contains? or get, which will work just fine with nil values,
and only use data as a function if it's static, e.g. a map defined in a def.
2024-04-16 14:48:28 -03:00
Omar Basem 24f4c4a07e Remove watch-only accounts from bridging (#19569)
Remove watch-only accounts from bridging (#19569)
2024-04-16 20:33:03 +04:00
Ajay Sivan 98762c74f1 Community detail token gating component (#19532) 2024-04-16 09:11:18 -07:00
Ibrahem Khalil f3aeddb53a Update doc/troubleshooting.md (#19673) 2024-04-16 17:46:14 +02:00
mmilad75 f5fcc9f45e Wallet: Token Options should be specific to watch only account #18543 (#19583) 2024-04-16 19:02:50 +03:30
Parvesh Monu 9aa9dc1b4c Redirect to community channel when following universal link (#19566) 2024-04-16 20:30:24 +05:30
Sean Hagstrom 61974dae4d Implement share button inside profile settings (#19559)
* tweak: implement the share button inside the profile settings page

* chore: define event and effect handler for opening share sheet

* tidy: refactor share buttons to use open-share event

* tidy: refactor open-share effect to use react-native-share bindings

* tidy: remove unused code for old bindings to react share sheet

* tidy: move effects.share/open definition to navigation effects

* tweak: ensure navigation effects are loaded

* tidy: use open-share dispatch in ui

* tidy: use open-share dispatch instead of calling open function directly

* tidy: refactor react-native.share/open to only receive options and return promise

* tidy: refactor open-share effect to receive map of content and handlers

* tidy: replace strings with keywords

* tweak: always handle failure case when opening share-sheet

* tweak: rename :content to :options

* fix: conditionally call on-error

* tweak: add extra information when logging error from attempting to share-sheet
2024-04-16 13:26:52 +02:00
Parvesh Monu 8b3e639abd fix error on long tap on token-gated channel from the join community screen (#19652) 2024-04-16 16:33:09 +05:30
jakub 7c901fa045 ci: use virtualenv to avoid race conditions
Desktop QA tests also use Pytest packages and their versions are
different, so we can't install them globally, it needs to be done per
build using `WORKSPACE_TMP` as destination.

Signed-off-by: Jakub Sokołowski <jakub@status.im>
2024-04-16 13:13:53 +03:00
frank 8a12ad3510 fix_: logout deadlock (#19640) 2024-04-16 17:50:47 +08:00
Brian Sztamfater c8044cd2f2 fix: wallet header spacing to match other tabs (#19632) 2024-04-16 06:31:09 -03:00
Sean Hagstrom 6872d1a857 fix: do not throw exception when passing nil to utils.address/get-abbreviated-profile-url (#19593) 2024-04-16 09:51:35 +02:00
Brian Sztamfater 98d4969ca5 feat: add ability to tap to disable from networks (#19392) 2024-04-15 12:04:44 -03:00
Parvesh Monu 5412092ca1 fix zero addresses are selected by default when joining a community (#19626) 2024-04-15 17:30:31 +05:30
Parvesh Monu a8695116e2 fix error is shown after scan button is tapped on the browser page (#19601) 2024-04-15 15:24:09 +05:30
Yevheniia Berdnyk 76773a4165 e2e: fix for sharing community flow 2024-04-15 12:11:24 +03:00
yqrashawn 32fc7b14e8 fix: add/rename missing sub :current-chat/one-to-one-chat? (#19628)
Signed-off-by: yqrashawn <namy.19@gmail.com>
2024-04-15 17:10:12 +08:00
Sean Hagstrom 82a8f9f44d fix: rewire legacy community share button to send invites (#19622) 2024-04-15 10:52:18 +02:00
John Ngei 8df72d0c29 updated standard auth drawer title size and weight 2024-04-13 23:04:49 +02:00
Mohsen 794cd57961 [#19299] fix: design feedback for settings screen (#19609) 2024-04-12 19:37:07 +03:00
John Ngei 6bc29088ab fix: community spacing issues in token gating community page
* fix: community spacing issues in token gating community page

* fix: spacing issues between token rows
2024-04-12 17:35:03 +02:00
codemaster 658f62e8ce Composer-photo selection drawer issues (#19613) 2024-04-12 07:44:35 -07:00
flexsurfer e6b346ca32 migrate reagent part 7 (#19316) 2024-04-12 15:46:17 +02:00
yqrashawn 00b0755a73 feat: system messge icon based on contact customization color (#19558)
Signed-off-by: yqrashawn <namy.19@gmail.com>
2024-04-12 20:46:09 +08:00
yqrashawn f8cc85f5e2 fix: vertical align texts in multiline input on iOS (#19565)
* fix: vertical align texts in multiline input on iOS

Signed-off-by: yqrashawn <namy.19@gmail.com>

* fix(copy): Chat Key -> Chatkey

---------

Signed-off-by: yqrashawn <namy.19@gmail.com>
2024-04-12 20:25:04 +08:00
Ibrahem Khalil 6ba89d0c6e Community channel avatar is missing on Channel screen (#19459) 2024-04-12 01:18:21 +02:00
Ibrahem Khalil 2614131426 [18979] Implement context action Invite contacts to community (#18989) 2024-04-12 00:12:02 +02:00
139 changed files with 2648 additions and 1522 deletions
+2
View File
@@ -35,3 +35,5 @@ LOCAL_PAIRING_ENABLED=1
FAST_CREATE_COMMUNITY_ENABLED=1
TEST_NETWORKS_ENABLED=1
SHOW_NOT_IMPLEMENTED_FEATURES=1
DELETE_MESSAGE_FOR_ME_UNDO_TIME_LIMIT=10000
DELETE_MESSAGE_UNDO_TIME_LIMIT=10000
+8 -1
View File
@@ -36,6 +36,12 @@ pipeline {
))
}
environment {
/* Avoid race conditions with other builds using virtualenv. */
VIRTUAL_ENV = "${WORKSPACE_TMP}/venv"
PATH = "${VIRTUAL_ENV}/bin:${PATH}"
}
stages {
stage('Fetch') {
steps { script {
@@ -52,7 +58,8 @@ pipeline {
stage('Setup') {
steps { script {
dir('test/appium') {
sh 'pip3 install --user -r requirements.txt'
sh "python3 -m venv ${VIRTUAL_ENV}"
sh 'pip3 install -r requirements.txt'
}
} }
}
+8 -1
View File
@@ -54,6 +54,12 @@ pipeline {
))
}
environment {
/* Avoid race conditions with other builds using virtualenv. */
VIRTUAL_ENV = "${WORKSPACE_TMP}/venv"
PATH = "${VIRTUAL_ENV}/bin:${PATH}"
}
stages {
stage('Prep') {
steps { script {
@@ -79,7 +85,8 @@ pipeline {
stage('Setup') {
steps { script {
dir('test/appium') {
sh 'pip3 install --user -r requirements.txt'
sh "python3 -m venv ${VIRTUAL_ENV}"
sh 'pip3 install -r requirements.txt'
}
} }
}
+7 -1
View File
@@ -35,6 +35,11 @@ pipeline {
))
}
environment {
/* Avoid race conditions with other builds using virtualenv. */
VIRTUAL_ENV = "${WORKSPACE_TMP}/venv"
PATH = "${VIRTUAL_ENV}/bin:${PATH}"
}
stages {
stage('Prep') {
@@ -51,7 +56,8 @@ pipeline {
stage('Setup') {
steps { script {
dir('test/appium') {
sh 'pip3 install --user -r requirements.txt'
sh "python3 -m venv ${VIRTUAL_ENV}"
sh 'pip3 install -r requirements.txt'
}
} }
}
+16
View File
@@ -128,3 +128,19 @@ Status-mobile uses `shadow-cljs` for hot reloading changes and uses its own [rel
### Solution
Open react native's [In-App Developer Menu](https://reactnative.dev/docs/debugging#accessing-the-in-app-developer-menu) and press "Disable Fast Refresh" or "Disable Hot Reloading"
# App crashing after running `make run-ios`
### Cause
It's possible that installing XCode from a `.xip` file might cause XCode to act funny
Since it's not installed from the App Store, Or you might be missing a Rosetta installation.
[Reference/Similar issue](https://github.com/expo/expo-cli/issues/3197)
You might see something like this after running `make run-ios`:
```
Underlying error (domain=FBSOpenApplicationServiceErrorDomain, code=1):
```
### Solution
Run `softwareupdate --install-rosetta --agree-to-license`
to accept XCode's license and install Rosetta.
+23 -19
View File
@@ -60,26 +60,30 @@ in {
version = "15.0";
allowHigher = true;
};
go = super.go_1_19;
go = super.go_1_20;
clang = super.clang_15;
buildGoPackage = super.buildGo119Package;
buildGoModule = super.buildGo119Module;
gomobile = (super.gomobile.overrideAttrs (old: {
patches = [
(self.fetchurl { # https://github.com/golang/mobile/pull/84
url = "https://github.com/golang/mobile/commit/f20e966e05b8f7e06bed500fa0da81cf6ebca307.patch";
sha256 = "sha256-TZ/Yhe8gMRQUZFAs9G5/cf2b9QGtTHRSObBFD5Pbh7Y=";
})
(self.fetchurl { # https://github.com/golang/go/issues/58426
url = "https://github.com/golang/mobile/commit/406ed3a7b8e44dc32844953647b49696d8847d51.patch";
sha256 = "sha256-dqbYukHkQEw8npOkKykOAzMC3ot/Y4DEuh7fE+ptlr8=";
})
];
})).override {
# FIXME: No Android SDK packages for aarch64-darwin.
withAndroidPkgs = stdenv.system != "aarch64-darwin";
androidPkgs = self.androidEnvCustom.compose;
};
buildGoPackage = super.buildGo120Package;
buildGoModule = super.buildGo120Module;
gomobile = super.gomobile.override {
# FIXME: No Android SDK packages for aarch64-darwin.
withAndroidPkgs = stdenv.system != "aarch64-darwin";
androidPkgs = self.androidEnvCustom.compose;
buildGoModule = args: super.buildGo120Module ( args // rec {
CGO_ENABLED=1;
GODEBUG="netdns=cgo";
patches = [
(self.fetchurl { # https://github.com/golang/mobile/pull/84
url = "https://github.com/golang/mobile/commit/f20e966e05b8f7e06bed500fa0da81cf6ebca307.patch";
sha256 = "sha256-TZ/Yhe8gMRQUZFAs9G5/cf2b9QGtTHRSObBFD5Pbh7Y=";
})
(self.fetchurl { # https://github.com/golang/go/issues/58426
url = "https://github.com/golang/mobile/commit/406ed3a7b8e44dc32844953647b49696d8847d51.patch";
sha256 = "sha256-dqbYukHkQEw8npOkKykOAzMC3ot/Y4DEuh7fE+ptlr8=";
})
];
});
};
# Android environement
androidEnvCustom = callPackage ./pkgs/android-sdk { };
+2
View File
@@ -35,6 +35,8 @@ in buildGoPackage {
ANDROID_HOME = optionalString isAndroid androidPkgs.sdk;
CGO_ENABLED=1;
# Ensure XCode is present for iOS, instead of failing at the end of the build.
preConfigure = optionalString isIOS enforceXCodeAvailable;
-1
View File
@@ -94,7 +94,6 @@
"prettier": "^2.8.8",
"process": "0.11.10",
"react-test-renderer": "18.1.0",
"rn-snoopy": "git+https://github.com/status-im/rn-snoopy.git#refs/tags/v2.0.2-status",
"shadow-cljs": "2.26.2"
},
"binary": {
Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

+9 -50
View File
@@ -4,6 +4,7 @@
[legacy.status-im.bottom-sheet.events :as bottom-sheet]
legacy.status-im.communities.e2e
[re-frame.core :as re-frame]
[status-im.contexts.shell.activity-center.events :as activity-center]
[status-im.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
@@ -22,60 +23,10 @@
{}
requests))
(defn- fetch-community-id-input
[{:keys [db]}]
(:communities/community-id-input db))
(rf/defn handle-response
[_ response-js]
{:dispatch [:sanitize-messages-and-process-response response-js]})
(rf/defn invite-users
{:events [::invite-people-confirmation-pressed]}
[cofx user-pk contacts]
(let [community-id (fetch-community-id-input cofx)
pks (if (seq user-pk)
(conj contacts user-pk)
contacts)]
(when (seq pks)
{:json-rpc/call [{:method "wakuext_inviteUsersToCommunity"
:params [{:communityId community-id
:users pks}]
:js-response true
:on-success #(re-frame/dispatch [::people-invited %])
:on-error #(do
(log/error "failed to invite-user community" %)
(re-frame/dispatch [::failed-to-invite-people %]))}]})))
(rf/defn share-community
{:events [::share-community-confirmation-pressed]}
[cofx user-pk contacts]
(let [community-id (fetch-community-id-input cofx)
pks (if (seq user-pk)
(conj contacts user-pk)
contacts)]
(when (seq pks)
{:json-rpc/call [{:method "wakuext_shareCommunity"
:params [{:communityId community-id
:users pks}]
:js-response true
:on-success #(re-frame/dispatch [::people-invited %])
:on-error #(do
(log/error "failed to invite-user community" %)
(re-frame/dispatch [::failed-to-share-community %]))}]})))
(re-frame/reg-event-fx :communities/invite-people-pressed
(fn [{:keys [db]} [id]]
{:db (assoc db :communities/community-id-input id)
:fx [[:dispatch [:hide-bottom-sheet]]
[:dispatch [:open-modal :legacy-invite-people-community {:invite? true}]]]}))
(re-frame/reg-event-fx :communities/share-community-pressed
(fn [{:keys [db]} [id]]
{:db (assoc db :communities/community-id-input id)
:fx [[:dispatch [:hide-bottom-sheet]]
[:dispatch [:open-modal :legacy-invite-people-community {}]]]}))
(rf/defn people-invited
{:events [::people-invited]}
[cofx response-js]
@@ -90,6 +41,14 @@
[:sanitize-messages-and-process-response response-js]
[:activity-center.notifications/fetch-unread-count]]}))
(rf/defn member-banned
{:events [::member-banned]}
[cofx response-js]
(rf/merge cofx
(bottom-sheet/hide-bottom-sheet-old)
(handle-response response-js)
(activity-center/notifications-fetch-unread-count)))
(rf/defn member-ban
{:events [::member-ban]}
[cofx community-id public-key]
@@ -2,7 +2,6 @@
(:require
[legacy.status-im.browser.core :as browser]
[legacy.status-im.browser.webview-ref :as webview-ref]
[legacy.status-im.qr-scanner.core :as qr-scanner]
[legacy.status-im.ui.components.chat-icon.screen :as chat-icon]
[legacy.status-im.ui.components.colors :as colors]
[legacy.status-im.ui.components.connectivity.view :as connectivity]
@@ -105,9 +104,7 @@
(if empty-tab
[react/touchable-highlight
{:accessibility-label :universal-qr-scanner
:on-press #(re-frame/dispatch
[::qr-scanner/scan-code
{:handler ::qr-scanner/on-scan-success}])}
:on-press #(re-frame/dispatch [:open-modal :shell-qr-reader])}
[icons/icon :main-icons/qr {:color colors/black}]]
[react/touchable-highlight
{:on-press #(re-frame/dispatch
@@ -1,16 +1,15 @@
(ns legacy.status-im.ui.screens.communities.invite
(:require
[clojure.string :as string]
[legacy.status-im.communities.core :as communities]
[legacy.status-im.ui.components.chat-icon.screen :as chat-icon.screen]
[legacy.status-im.ui.components.core :as quo]
[legacy.status-im.ui.components.list.item :as list.item]
[legacy.status-im.ui.components.toolbar :as toolbar]
[legacy.status-im.ui.components.topbar :as topbar]
[quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.constants :as constants]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@@ -49,18 +48,35 @@
contacts-selected (reagent/atom #{})
{:keys [invite?]} (rf/sub [:get-screen-params])]
(fn []
(let [contacts-data (rf/sub [:contacts/active])
{:keys [permissions
can-manage-users?]}
(let [theme (quo.theme/use-theme)
contacts-data (rf/sub [:contacts/active])
{community-id :id
:keys [permissions
can-manage-users?]}
(rf/sub [:communities/edited-community])
selected @contacts-selected
selected-contacts-count (count selected)
contacts (map (fn [{:keys [public-key] :as contact}]
(assoc contact :active (contains? selected public-key)))
contacts-data)
;; no-membership communities can only be shared
can-invite? (and can-manage-users?
invite?
(not= (:access permissions) constants/community-no-membership-access))]
(not= (:access permissions) constants/community-no-membership-access))
on-press-share-community (rn/use-callback
(fn []
(rf/dispatch [:communities/share-community-confirmation-pressed
selected community-id])
(rf/dispatch [:navigate-back])
(rf/dispatch [:toasts/upsert
{:type :positive
:theme theme
:text (if (= 1 selected-contacts-count)
(i18n/label :t/one-user-was-invited)
(i18n/label
:t/n-users-were-invited
{:count selected-contacts-count}))}]))
[community-id selected selected-contacts-count theme])]
[:<>
[topbar/topbar
{:title (i18n/label (if can-invite?
@@ -81,11 +97,8 @@
:center
[quo/button
{:disabled (and (string/blank? @user-pk)
(zero? (count selected)))
(zero? selected-contacts-count))
:accessibility-label :share-community-link
:type :secondary
:on-press #(debounce/throttle-and-dispatch
[::communities/share-community-confirmation-pressed @user-pk
selected]
3000)}
:on-press on-press-share-community}
(i18n/label (if can-invite? :t/invite :t/share))]}]]))))
@@ -13,3 +13,11 @@
:background-color (colors/theme-colors (colors/custom-color customization-color 50)
(colors/custom-color customization-color 60)
theme)})
(defn avatar-identifier
[theme]
{:text-align :center
:font-size 36
:color (colors/theme-colors colors/black
colors/white
theme)})
@@ -1,7 +1,9 @@
(ns quo.components.avatars.group-avatar.view
(:require
[clojure.string :as string]
[quo.components.avatars.group-avatar.style :as style]
[quo.components.icon :as icon]
[quo.components.markdown.text :as text]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
@@ -21,7 +23,7 @@
(defn- view-internal
[_]
(fn [{:keys [size theme customization-color picture icon-name]
(fn [{:keys [size theme customization-color picture icon-name emoji chat-name]
:or {size :size-20
customization-color :blue
picture nil
@@ -38,8 +40,27 @@
{:source picture
:style {:width container-size
:height container-size}}]
[icon/icon icon-name
{:size icon-size
:color colors/white-opa-70}])])))
(cond
emoji
(if (= size :size-80)
[rn/text
{:style (style/avatar-identifier theme)}
emoji]
[text/text
{:size :paragraph-1
:style (dissoc (style/avatar-identifier theme) :font-size)}
emoji])
chat-name
(if (= size :size-80)
[rn/text
{:style (style/avatar-identifier theme)}
((comp first string/upper-case) chat-name)]
[text/text
{:size :paragraph-1}
((comp first string/upper-case) chat-name)])
:else
[icon/icon icon-name
{:size icon-size
:color colors/white-opa-70}]))])))
(def view (quo.theme/with-theme view-internal))
@@ -0,0 +1,71 @@
(ns quo.components.community.community-detail-token-gating.component-spec
(:require
[quo.core :as quo]
[test-helpers.component :as h]))
(h/describe "Community Detail Token Gating Component"
(h/test "render with member permissions"
(h/render
[quo/community-detail-token-gating
{:permissions [{:role 2
:role-text "Member"
:satisfied? true
:tokens [[{:symbol "ETH"
:sufficient? true
:collectible? false
:amount 0.8
:img-src nil}]
[{:symbol "ETH"
:sufficient? false
:collectible? false
:amount 1
:img-src nil}
{:symbol "STT"
:sufficient? false
:collectible? false
:amount 10
:img-src nil}]]}]}])
(h/is-truthy (h/get-by-translation-text :t/you-eligible-to-join-as {:role "Member"}))
(h/is-truthy (h/get-by-text "0.8 ETH"))
(h/is-truthy (h/get-by-text "1 ETH"))
(h/is-truthy (h/get-by-text "10 STT")))
(h/test "render with admin permissions"
(h/render
[quo/community-detail-token-gating
{:permissions [{:role 1
:role-text "Admin"
:satisfied? true
:tokens [[{:symbol "ETH"
:sufficient? true
:collectible? false
:amount 2
:img-src nil}]]}]}])
(h/is-truthy (h/get-by-translation-text :t/you-eligible-to-join-as {:role "Admin"}))
(h/is-truthy (h/get-by-text "2 ETH")))
(h/test "render with token master permissions"
(h/render-with-theme-provider
[quo/community-detail-token-gating
{:permissions [{:role 5
:role-text "Token Master"
:satisfied? true
:tokens [[{:symbol "TMANI "
:sufficient? true
:collectible? true
:img-src {:uri "mock-image"}}]]}]}])
(h/is-truthy (h/get-by-translation-text :t/you-eligible-to-join-as {:role "Token Master"}))
(h/is-truthy (h/get-by-text "TMANI")))
(h/test "render with token owner permissions"
(h/render-with-theme-provider
[quo/community-detail-token-gating
{:permissions [{:role 6
:role-text "Token Owner"
:satisfied? true
:tokens [[{:symbol "TOANI"
:sufficient? true
:collectible? true
:img-src {:uri "mock-image"}}]]}]}])
(h/is-truthy (h/get-by-translation-text :t/you-eligible-to-join-as {:role "Token Owner"}))
(h/is-truthy (h/get-by-text "TOANI"))))
@@ -0,0 +1,20 @@
(ns quo.components.community.community-detail-token-gating.style)
(def container
{:padding-horizontal 20
:margin-vertical -4})
(defn token-row
[first?]
{:flex-direction :row
:margin-top (if first? 8 4)
:row-gap 10
:column-gap 8
:flex-wrap :wrap
:margin-bottom 16})
(def divider
{:padding-left 0
:height 28
:padding-top 0
:align-items :flex-start})
@@ -0,0 +1,74 @@
(ns quo.components.community.community-detail-token-gating.view
(:require [clojure.string :as string]
[quo.components.community.community-detail-token-gating.style :as style]
[quo.components.dividers.divider-label.view :as divider-label]
[quo.components.markdown.text :as text]
[quo.components.tags.collectible-tag.view :as collectible-tag]
[quo.components.tags.token-tag.view :as token-tag]
[quo.foundations.colors :as colors]
[react-native.core :as rn]
[utils.i18n :as i18n]))
(defn- token-view
[{:keys [collectible? img-src amount sufficient?] :as token}]
(let [token-symbol (:symbol token)]
(if collectible?
[collectible-tag/view
{:collectible-name token-symbol
:size :size-24
:collectible-img-src img-src
:options (when sufficient?
:hold)}]
[token-tag/view
{:token-symbol token-symbol
:size :size-24
:token-value amount
:token-img-src img-src
:options (when sufficient? :hold)}])))
(defn- tokens-row
[{:keys [tokens divider? first?]}]
[:<>
[rn/view
{:style (style/token-row first?)}
(map-indexed (fn [token-index token]
^{:key (str "token-" token-index)}
[token-view token])
tokens)]
(when-not divider?
[divider-label/view
{:container-style style/divider}
[text/text
{:size :label
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}}
(string/lower-case (i18n/label :t/or))]])])
(defn- role-view
[{:keys [role tokens satisfied? role-text]}]
(when (seq tokens)
^{:key (str "role-" role)}
[:<>
[text/text
{:size :paragraph-1
:weight :medium
:style {:margin-top 4}}
(if satisfied?
(i18n/label :t/you-eligible-to-join-as {:role role-text})
(i18n/label :t/you-not-eligible-to-join-as {:role role-text}))]
[text/text
{:size :paragraph-2
:style {:margin-bottom 8}}
(i18n/label (if satisfied? :t/you-hodl :t/you-must-hold))]
(map-indexed (fn [index tokens-item]
^{:key (str role "-tokens-" index)}
[tokens-row
{:tokens tokens-item
:first? (zero? index)
:divider? (= index (dec (count tokens)))}])
tokens)]))
(defn view
[{:keys [permissions]}]
[rn/view {:style style/container}
(map role-view permissions)])
+7 -6
View File
@@ -102,7 +102,7 @@
:right 8})
(def token-tag-spacing
{:margin-top 10
{:padding-top 10
:margin-right 8})
(defn token-row
@@ -117,11 +117,12 @@
(defn token-row-or-text
[padding? theme]
(merge
{:margin-top 4
:color (colors/theme-colors
colors/neutral-50
colors/neutral-40
theme)}
{:padding-top 4
:margin-bottom -2
:color (colors/theme-colors
colors/neutral-50
colors/neutral-40
theme)}
(when padding?
{:padding-left 12})))
@@ -7,8 +7,14 @@
:align-items :center})
(def image
{:width 80
:height 80})
{:width 72
:height 72})
(def image-container
{:width 80
:height 80
:align-items :center
:justify-content :center})
(def text-container
{:margin-top 12
@@ -15,9 +15,10 @@
[rn/view {:style (merge styles/container container-style)}
(if placeholder?
[rn/view {:style styles/image-placeholder}]
[fast-image/fast-image
{:style styles/image
:source image}])
[rn/view {:style styles/image-container}
[fast-image/fast-image
{:style styles/image
:source image}]])
[rn/view {:style styles/text-container}
[text/text
{:style (styles/title blur?)
+8 -4
View File
@@ -1,7 +1,8 @@
(ns quo.components.inputs.input.style
(:require
[quo.components.markdown.text :as text]
[quo.foundations.colors :as colors]))
[quo.foundations.colors :as colors]
[react-native.platform :as platform]))
(defn variants-colors
[blur? theme]
@@ -97,9 +98,12 @@
(assoc base-props
:text-align-vertical :top
:line-height 22)
(assoc base-props
:height (if small? 30 38)
:line-height nil))))
(cond-> base-props
:always
(assoc :height (if small? 30 38)
:line-height nil)
platform/ios?
(assoc :padding-top (+ padding 2))))))
(defn right-icon-touchable-area
[small?]
+7 -4
View File
@@ -41,21 +41,24 @@
(defn user
[{:keys [short-chat-key primary-name secondary-name photo-path online? contact? verified?
untrustworthy? on-press on-long-press accessory customization-color theme
allow-multiple-presses?]}]
allow-multiple-presses? disabled?]}]
[rn/touchable-highlight
{:style container-style
:underlay-color (colors/resolve-color customization-color theme 5)
:allow-multiple-presses? allow-multiple-presses?
:accessibility-label :user-list
:on-press (when on-press on-press)
:on-long-press (when on-long-press on-long-press)}
:on-long-press (when on-long-press on-long-press)
:disabled disabled?}
[:<>
[user-avatar/user-avatar
{:full-name primary-name
:profile-picture photo-path
:online? online?
:size :small}]
[rn/view {:style {:margin-horizontal 8 :flex 1}}
[rn/view
{:style {:margin-horizontal 8
:flex 1}}
[author/view
{:primary-name primary-name
:secondary-name secondary-name
@@ -69,4 +72,4 @@
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}}
short-chat-key])]
(when accessory
[action-icon accessory customization-color theme])]])
[action-icon accessory customization-color disabled? theme])]])
@@ -10,5 +10,5 @@
[:blur? {:optional true} [:maybe :boolean]]
[:collectible-img-src :schema.common/image-source]
[:collectible-name :string]
[:collectible-id :string]]]]
[:collectible-id {:optional true} [:maybe :string]]]]]
:any])
@@ -41,12 +41,13 @@
:weight :medium
:style (style/label theme)}
collectible-name]
[text/text
{:size :paragraph-2
:weight :medium
:margin-left 5
:style (style/label theme)}
collectible-id]]]
(when collectible-id
[text/text
{:size :paragraph-2
:weight :medium
:margin-left 5
:style (style/label theme)}
collectible-id])]]
(when options
[rn/view {:style (style/options-icon size)}
[icons/icon (if (= options :hold) :i/hold :i/add-token)
@@ -4,7 +4,6 @@
[:map
[:type {:optional true} [:enum :default :watch-only :add-account :empty :missing-keypair]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:theme :schema.common/theme]
[:metrics? {:optional true} [:maybe :boolean]]
[:on-press {:optional true} [:maybe fn?]]])
@@ -11,7 +11,6 @@
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.linear-gradient :as linear-gradient]
[reagent.core :as reagent]
[schema.core :as schema]))
(defn- loading-view
@@ -96,87 +95,87 @@
:end {:x 1 :y 0}}])
(defn- user-account
[_]
(let [pressed? (reagent/atom false)
on-press-in #(reset! pressed? true)
on-press-out #(reset! pressed? false)]
(fn [{:keys [name balance percentage-value loading? amount customization-color type emoji metrics?
theme on-press]}]
(let [watch-only? (= :watch-only type)
missing-keypair? (= :missing-keypair type)]
(if loading?
[loading-view
{:customization-color customization-color
:type type
:theme theme
:metrics? metrics?}]
[rn/pressable
{:on-press-in on-press-in
:on-press-out on-press-out
:style (style/card {:customization-color customization-color
:type type
:theme theme
:pressed? @pressed?
:metrics? metrics?})
:on-press on-press}
(when (and customization-color (and (not watch-only?) (not missing-keypair?)))
[customization-colors/overlay
{:customization-color customization-color
:border-radius 16
:theme theme
:pressed? @pressed?}])
[rn/view {:style style/profile-container}
[rn/view {:style {:padding-bottom 2 :margin-right 2}}
[text/text {:style style/emoji} emoji]]
[rn/view {:style style/watch-only-container}
[text/text
{:size :paragraph-2
:weight :medium
:number-of-lines 1
:max-width 110
:margin-right 4
:ellipis-mode :tail
:style (style/account-name type theme)}
name]
(when watch-only? [icon/icon :i/reveal {:color colors/neutral-50 :size 12}])
(when missing-keypair?
[icon/icon :i/alert {:color (properties/alert-icon-color theme) :size 12}])]]
[text/text
{:size :heading-2
:weight :semi-bold
:style (style/account-value type theme)}
balance]
(when metrics?
[rn/view {:style style/metrics-container}
[metrics-percentage type theme percentage-value]
(when (not= :empty type)
[metrics-info type theme amount])])
(when watch-only?
[gradient-overview theme customization-color])])))))
[{:keys [name balance percentage-value loading? amount customization-color type emoji metrics?
on-press]}]
(let [theme (quo.theme/use-theme-value)
[pressed? set-pressed] (rn/use-state false)
on-press-in (rn/use-callback #(set-pressed true))
on-press-out (rn/use-callback #(set-pressed false))
watch-only? (= :watch-only type)
missing-keypair? (= :missing-keypair type)]
(if loading?
[loading-view
{:customization-color customization-color
:type type
:theme theme
:metrics? metrics?}]
[rn/pressable
{:on-press-in on-press-in
:on-press-out on-press-out
:style (style/card {:customization-color customization-color
:type type
:theme theme
:pressed? pressed?
:metrics? metrics?})
:on-press on-press}
(when (and customization-color (and (not watch-only?) (not missing-keypair?)))
[customization-colors/overlay
{:customization-color customization-color
:border-radius 16
:theme theme
:pressed? pressed?}])
[rn/view {:style style/profile-container}
[rn/view {:style {:padding-bottom 2 :margin-right 2}}
[text/text {:style style/emoji} emoji]]
[rn/view {:style style/watch-only-container}
[text/text
{:size :paragraph-2
:weight :medium
:number-of-lines 1
:max-width 110
:margin-right 4
:ellipis-mode :tail
:style (style/account-name type theme)}
name]
(when watch-only? [icon/icon :i/reveal {:color colors/neutral-50 :size 12}])
(when missing-keypair?
[icon/icon :i/alert {:color (properties/alert-icon-color theme) :size 12}])]]
[text/text
{:size :heading-2
:weight :semi-bold
:style (style/account-value type theme)}
balance]
(when metrics?
[rn/view {:style style/metrics-container}
[metrics-percentage type theme percentage-value]
(when (not= :empty type)
[metrics-info type theme amount])])
(when watch-only?
[gradient-overview theme customization-color])])))
(defn- add-account-view
[_]
(let [pressed? (reagent/atom false)]
(fn [{:keys [on-press customization-color theme metrics?]}]
[rn/pressable
{:on-press on-press
:on-press-in #(reset! pressed? true)
:on-press-out #(reset! pressed? false)
:style (style/add-account-container {:theme theme
:metrics? metrics?
:pressed? @pressed?})}
[button/button
{:type :primary
:size 24
:icon true
:accessibility-label :add-account
:on-press on-press
:pressed? @pressed?
:on-press-in #(reset! pressed? true)
:on-press-out #(reset! pressed? false)
:customization-color customization-color
:icon-only? true}
:i/add]])))
[{:keys [on-press customization-color metrics?]}]
(let [theme (quo.theme/use-theme-value)
[pressed? set-pressed] (rn/use-state false)
on-press-in (rn/use-callback #(set-pressed true))
on-press-out (rn/use-callback #(set-pressed false))]
[rn/pressable
{:on-press on-press
:on-press-in on-press-in
:on-press-out on-press-out
:style (style/add-account-container {:theme theme
:metrics? metrics?
:pressed? pressed?})}
[button/button
{:on-press on-press
:type :primary
:size 24
:icon true
:accessibility-label :add-account
:pressed? pressed?
:icon-only? true
:customization-color customization-color}
:i/add]]))
(defn- view-internal
[{:keys [type] :as props}]
@@ -185,6 +184,4 @@
:add-account [add-account-view props]
nil))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal component-schema/?schema)))
(def view (schema/instrument #'view-internal component-schema/?schema))
@@ -6,9 +6,7 @@
[:props
[:map {:closed true}
[:status {:optional true} [:maybe [:enum :default :error]]]
[:theme :schema.common/theme]
[:on-inc-press {:optional true} [:maybe fn?]]
[:on-dec-press {:optional true} [:maybe fn?]]
[:on-change-text {:optional true} [:maybe fn?]]
[:container-style {:optional true} [:maybe :map]]
[:min-value {:optional true} [:maybe :int]]
[:max-value {:optional true} [:maybe :int]]
@@ -4,7 +4,7 @@
[quo.components.markdown.text :as text]
[quo.components.wallet.amount-input.schema :as amount-input.schema]
[quo.components.wallet.amount-input.style :as style]
[quo.theme :as quo.theme]
[quo.theme]
[react-native.core :as rn]
[schema.core :as schema]))
@@ -21,35 +21,34 @@
icon])
(defn- view-internal
[{:keys [on-inc-press on-dec-press theme status value min-value max-value
[{:keys [on-inc-press on-dec-press status value min-value max-value
container-style]
:or {value 0
min-value 0
max-value 999999999}}]
[rn/view
{:style (merge style/container container-style)}
[amount-button
{:theme theme
:accessibility-label :amount-input-dec-button
:icon :i/remove
:on-press on-dec-press
:disabled? (>= min-value value)}]
[rn/view {:style style/input-container}
[text/text
{:number-of-lines 1
:accessibility-label :amount-input
:weight :semi-bold
:size :heading-1
:align-self :center
:style (style/input-text theme (or status :default))}
value]]
[amount-button
{:theme theme
:icon :i/add
:accessibility-label :amount-input-inc-button
:on-press on-inc-press
:disabled? (>= value max-value)}]])
(let [theme (quo.theme/use-theme-value)]
[rn/view
{:style (merge style/container container-style)}
[amount-button
{:theme theme
:accessibility-label :amount-input-dec-button
:icon :i/remove
:on-press on-dec-press
:disabled? (>= min-value value)}]
[rn/view {:style style/input-container}
[text/text
{:number-of-lines 1
:accessibility-label :amount-input
:weight :semi-bold
:size :heading-1
:align-self :center
:style (style/input-text theme (or status :default))}
value]]
[amount-button
{:theme theme
:icon :i/add
:accessibility-label :amount-input-inc-button
:on-press on-inc-press
:disabled? (>= value max-value)}]]))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal amount-input.schema/?schema)))
(def view (schema/instrument #'view-internal amount-input.schema/?schema))
+4 -5
View File
@@ -37,8 +37,9 @@
:border? true}]))
(defn title-view
[{:keys [details action selected? type blur? customization-color on-options-press theme]}]
(let [{:keys [full-name]} details]
[{:keys [details action selected? type blur? customization-color on-options-press]}]
(let [theme (quo.theme/use-theme-value)
{:keys [full-name]} details]
[rn/view
{:style style/title-container
:accessibility-label :title}
@@ -89,7 +90,7 @@
[item & _rest]
[account-list-card/view item])
(defn- view-internal
(defn view
[{:keys [accounts action container-style selected? on-press] :as props}]
[rn/pressable
{:style (style/container (merge props
@@ -108,5 +109,3 @@
:render-fn acc-list-card
:separator [rn/view {:style {:height 8}}]
:style {:padding-horizontal 8}}]])
(def view (quo.theme/with-theme view-internal))
@@ -0,0 +1,23 @@
(ns quo.components.wallet.network-link.helpers)
(def ^:private central-figure-width 63)
(defn calculate-side-lines-path-1x
"Calculates the `d` attribute for the side lines based on the SVG width."
[width]
(let [side-offset (/ (- width central-figure-width) 2)]
{:left (str "M0 57 L" side-offset " 57")
:right (str "M" (+ side-offset central-figure-width) " 1 L" width " 1")}))
(defn calculate-transform
"Calculates the transform attribute for the central figure based on the SVG width."
[width]
(let [translate-x (/ (- width central-figure-width) 2)]
(str "translate(" translate-x " 0)")))
(defn calculate-side-lines-path-2x
"Calculates the `d` attribute for the side lines based on the SVG width."
[width]
(let [side-offset (/ (- width central-figure-width) 2)]
{:left (str "M0 113 L" side-offset " 113")
:right (str "M" (+ side-offset central-figure-width) " 1 L" width " 1")}))
@@ -0,0 +1,34 @@
(ns quo.components.wallet.network-link.style)
(def left-circle-container
{:position :absolute
:left -3})
(def right-circle-container
{:position :absolute
:right -3})
(def bottom-left-circle-container
{:position :absolute
:bottom -3
:left -3})
(def top-right-circle-container
{:position :absolute
:top -3
:right -3})
(def link-linear-container
{:flex-direction :row
:align-items :center
:height 10})
(def link-1x-container
{:flex 1
:height 58
:justify-content :center})
(def link-2x-container
{:flex 1
:height 114
:justify-content :center})
+131 -62
View File
@@ -1,82 +1,151 @@
(ns quo.components.wallet.network-link.view
(:require
[oops.core :refer [oget]]
[quo.components.wallet.network-link.helpers :as helpers]
[quo.components.wallet.network-link.schema :as component-schema]
[quo.components.wallet.network-link.style :as style]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.svg :as svg]
[schema.core :as schema]))
(defn- circle
[fill stroke]
[svg/svg
{:height "8"
:width "8"}
[svg/circle
{:cx "4"
:cy "4"
:r "3.5"
:fill fill
:stroke stroke
:strokeWidth "1"}]])
(defn- line
[stroke width]
[svg/svg
{:height "10"
:width "100%"
:view-box (str "0 0 " width " 10")}
[svg/path
{:d (str "M0,5 L" width ",5")
:stroke stroke
:stroke-width "1"}]])
(defn link-linear
[{:keys [source theme]}]
[svg/svg {:xmlns "http://www.w3.org/2000/svg" :width "73" :height "10" :fill :none}
[svg/path {:stroke (colors/resolve-color source theme) :d "M68 5H5"}]
[svg/circle
{:cx "68"
:cy "5"
:r "4"
:fill (colors/theme-colors colors/white colors/neutral-90 theme)
:stroke (colors/resolve-color source theme)}]
[svg/circle
{:cx "5"
:cy "5"
:r "4"
:fill (colors/theme-colors colors/white colors/neutral-90 theme)
:stroke (colors/resolve-color source theme)}]])
(let [[container-width
set-container-width] (rn/use-state 100)
stroke-color (colors/resolve-color source theme)
fill-color (colors/theme-colors colors/white colors/neutral-90 theme)
on-layout (rn/use-callback #(set-container-width
(oget % :nativeEvent :layout :width)))]
[rn/view
{:style style/link-linear-container
:on-layout on-layout}
[line stroke-color container-width]
[rn/view {:style style/left-circle-container}
[circle fill-color stroke-color]]
[rn/view {:style style/right-circle-container}
[circle fill-color stroke-color]]]))
(defn link-1x
[{:keys [source destination theme]}]
[svg/svg {:xmlns "http://www.w3.org/2000/svg" :width "73" :height "66" :fill :none}
[svg/path
{:stroke "url(#gradient)" :d "M68 5h-9.364c-11.046 0-20 8.954-20 20v16c0 11.046-8.955 20-20 20H5"}]
[svg/circle
{:cx "68"
:cy "5"
:r "4"
:fill (colors/theme-colors colors/white colors/neutral-90 theme)
:stroke (colors/resolve-color destination theme)}]
[svg/circle
{:cx "5"
:cy "61"
:r "4"
:fill (colors/theme-colors colors/white colors/neutral-90 theme)
:stroke (colors/resolve-color source theme)}]
[svg/defs
[svg/linear-gradient
{:id "gradient" :x1 "72.271" :x2 "82.385" :y1 "5" :y2 "34.155" :gradientUnits "userSpaceOnUse"}
[svg/stop {:stopColor (colors/resolve-color destination theme)}]
[svg/stop {:offset "1" :stopColor (colors/resolve-color source theme)}]]]])
(let [[container-width
set-container-width] (rn/use-state 100)
stroke-color "url(#gradient)"
source-color (colors/resolve-color source theme)
destination-color (colors/resolve-color destination theme)
fill-color (colors/theme-colors colors/white colors/neutral-90 theme)
view-box (str "0 0 " container-width " 58")
side-lines-path (helpers/calculate-side-lines-path-1x container-width)
central-transform (helpers/calculate-transform container-width)
on-layout (rn/use-callback #(set-container-width
(oget % :nativeEvent :layout :width)))]
[rn/view
{:style style/link-1x-container
:on-layout on-layout}
[svg/svg
{:xmlns "http://www.w3.org/2000/svg"
:height "100%"
:width "100%"
:view-box view-box
:fill :none}
[svg/path
{:d (:left side-lines-path)
:stroke source-color}]
[svg/path
{:d
"M63 1L53.6356 1C42.5899 1 33.6356 9.9543 33.6356 21L33.6356 37C33.6356 48.0457 24.6813 57 13.6356 57L2.85889e-05 57"
:transform central-transform
:stroke stroke-color}]
[svg/path
{:d (:right side-lines-path)
:stroke destination-color}]
[svg/defs
[svg/linear-gradient
{:id "gradient"
:x1 "72.271"
:x2 "82.385"
:y1 "5"
:y2 "34.155"
:gradient-units "userSpaceOnUse"}
[svg/stop {:stop-color (colors/resolve-color destination theme)}]
[svg/stop {:offset "1" :stop-color (colors/resolve-color source theme)}]]]]
[rn/view {:style style/bottom-left-circle-container}
[circle fill-color source-color]]
[rn/view {:style style/top-right-circle-container}
[circle fill-color destination-color]]]))
(defn link-2x
[{:keys [source destination theme]}]
[svg/svg
{:width "73" :height "122" :viewBox "0 0 73 122" :fill "none" :xmlns "http://www.w3.org/2000/svg"}
[svg/path
{:d
"M67.9999 5L58.6356 5C47.5899 5 38.6356 13.9543 38.6356 25L38.6356 97C38.6356 108.046 29.6813 117 18.6356 117L5.00006 117"
:stroke "url(#gradient)"}]
[svg/circle
{:cx "68"
:cy "5"
:r "4"
:fill (colors/theme-colors colors/white colors/neutral-90 theme)
:stroke (colors/resolve-color destination theme)}]
[svg/circle
{:cx "5"
:cy "117"
:r "4"
:fill (colors/theme-colors colors/white colors/neutral-90 theme)
:stroke (colors/resolve-color source theme)}]
[svg/defs
[svg/linear-gradient
{:id "gradient"
:x1 "72.2711"
:y1 "5.00001"
:x2 "102.867"
:y2 "49.0993"
:gradientUnits "userSpaceOnUse"}
[svg/stop {:stop-color (colors/resolve-color destination theme)}]
[svg/stop {:offset "1" :stop-color (colors/resolve-color source theme)}]]]])
(let [[container-width
set-container-width] (rn/use-state 100)
stroke-color "url(#gradient)"
source-color (colors/resolve-color source theme)
destination-color (colors/resolve-color destination theme)
fill-color (colors/theme-colors colors/white colors/neutral-90 theme)
view-box (str "0 0 " container-width " 114")
side-lines-path (helpers/calculate-side-lines-path-2x container-width)
central-transform (helpers/calculate-transform container-width)
on-layout (rn/use-callback #(set-container-width
(oget % :nativeEvent :layout :width)))]
[rn/view
{:style style/link-2x-container
:on-layout on-layout}
[svg/svg
{:xmlns "http://www.w3.org/2000/svg"
:height "100%"
:width "100%"
:view-box view-box
:fill :none}
[svg/path
{:d (:left side-lines-path)
:stroke source-color}]
[svg/path
{:d
"M62.9999 1L53.6356 1C42.5899 1 33.6356 9.9543 33.6356 21L33.6356 93C33.6356 104.046 24.6813 113 13.6356 113L5.71778e-05 113"
:transform central-transform
:stroke stroke-color}]
[svg/path
{:d (:right side-lines-path)
:stroke destination-color}]
[svg/defs
[svg/linear-gradient
{:id "gradient"
:x1 "72.2711"
:y1 "5.00001"
:x2 "102.867"
:y2 "49.0993"
:gradient-units "userSpaceOnUse"}
[svg/stop {:stop-color (colors/resolve-color destination theme)}]
[svg/stop {:offset "1" :stop-color (colors/resolve-color source theme)}]]]]
[rn/view {:style style/bottom-left-circle-container}
[circle fill-color source-color]]
[rn/view {:style style/top-right-circle-container}
[circle fill-color destination-color]]]))
(defn- view-internal
[{:keys [shape container-style] :as props}]
@@ -12,6 +12,5 @@
[:amount :int]
[:max-amount :int]
[:network-name [:or :string :keyword]]]]]]
[:container-style {:optional true} [:maybe :map]]
[:theme :schema.common/theme]]]]
[:container-style {:optional true} [:maybe :map]]]]]
:any])
@@ -29,13 +29,13 @@
:background-color (colors/resolve-color network-name nil 10)})
(defn network-bar
[{:keys [max-width on-top? bar-division? theme]
[{:keys [bar-max-width on-top? bar-division? theme]
{:keys [network-name translate-x-shared-value]} :bar}
width-shared-value]
(reanimated/apply-animations-to-style
{:width width-shared-value
:transform [{:translate-x translate-x-shared-value}]}
{:max-width max-width
{:max-width bar-max-width
:flex-direction :row
:justify-content :flex-end
:background-color (colors/resolve-color network-name nil)
@@ -8,7 +8,6 @@
[react-native.core :as rn]
[react-native.gesture :as gesture]
[react-native.reanimated :as reanimated]
[reagent.core :as reagent]
[schema.core :as schema]
[utils.number]))
@@ -22,60 +21,73 @@
(js/clearTimeout (k @timeouts))
(swap! timeouts assoc k (js/setTimeout exec-fn-and-remove-timeout ms))))
(defn- f-slider
[slider-shared-values]
[rn/view {:style style/slider-container}
[reanimated/view {:style (style/slider slider-shared-values)}]])
(def get-gesture
(memoize
(fn
[[detecting-gesture? slider-width-shared-value max-amount
slider-height-shared-value
amount-shared-value amount-on-gesture-start width->amount
slider-opacity-shared-value on-new-amount set-detecting-gesture]]
(-> (gesture/gesture-pan)
(gesture/enabled detecting-gesture?)
(gesture/on-begin
(fn [_]
(animation/increase-slider slider-width-shared-value slider-height-shared-value)
(reset! amount-on-gesture-start (reanimated/get-shared-value amount-shared-value))))
(gesture/on-update
(fn [event]
(let [new-amount (-> (oops/oget event "translationX")
(width->amount)
(+ @amount-on-gesture-start)
(utils.number/value-in-range 1 max-amount))]
(reanimated/set-shared-value amount-shared-value new-amount))))
(gesture/on-finalize
(fn [_]
(animation/decrease-slider slider-width-shared-value slider-height-shared-value)
(animation/hide-slider slider-opacity-shared-value)
(on-new-amount (reanimated/get-shared-value amount-shared-value))
(add-new-timeout :turn-off-gesture #(set-detecting-gesture false) 20)))))))
(defn f-network-bar
[_]
(let [detecting-gesture? (reagent/atom false)
amount-on-gesture-start (atom 0)]
(fn [{:keys [total-width total-amount on-press on-new-amount allow-press?]
{:keys [amount-shared-value
max-amount]} :bar
:as props}]
(let [slider-width-shared-value (reanimated/use-shared-value 4)
slider-height-shared-value (reanimated/use-shared-value 32)
slider-opacity-shared-value (reanimated/use-shared-value 0)
network-bar-shared-value (reanimated/interpolate amount-shared-value
[0 total-amount]
[0 total-width])
width->amount #(/ (* % total-amount) total-width)]
[rn/pressable
{:on-press (fn []
(when (and (not @detecting-gesture?) allow-press?)
(on-press)
(reset! detecting-gesture? true)
(animation/show-slider slider-opacity-shared-value)))}
[reanimated/view
{:style (style/network-bar props network-bar-shared-value)
:accessibility-label :network-routing-bar}
[gesture/gesture-detector
{:gesture
(-> (gesture/gesture-pan)
(gesture/enabled @detecting-gesture?)
(gesture/on-begin
(fn [_]
(animation/increase-slider slider-width-shared-value slider-height-shared-value)
(reset! amount-on-gesture-start (reanimated/get-shared-value amount-shared-value))))
(gesture/on-update
(fn [event]
(let [new-amount (-> (oops/oget event "translationX")
(width->amount)
(+ @amount-on-gesture-start)
(utils.number/value-in-range 1 max-amount))]
(reanimated/set-shared-value amount-shared-value new-amount))))
(gesture/on-finalize
(fn [_]
(animation/decrease-slider slider-width-shared-value slider-height-shared-value)
(animation/hide-slider slider-opacity-shared-value)
(on-new-amount (reanimated/get-shared-value amount-shared-value))
(add-new-timeout :turn-off-gesture #(reset! detecting-gesture? false) 20))))}
[:f> f-slider
{:width-shared-value slider-width-shared-value
:height-shared-value slider-height-shared-value
:opacity-shared-value slider-opacity-shared-value}]]]]))))
(defn network-bar
[{:keys [total-width total-amount on-press on-new-amount allow-press?
bar-idx bar-width bar-max-width]
{:keys [amount-shared-value
max-amount]
:as bar} :bar
:as props}]
(let [[detecting-gesture?
set-detecting-gesture] (rn/use-state false)
amount-on-gesture-start (rn/use-ref-atom 0)
slider-width-shared-value (reanimated/use-shared-value 4)
slider-height-shared-value (reanimated/use-shared-value 32)
slider-opacity-shared-value (reanimated/use-shared-value 0)
network-bar-shared-value (reanimated/interpolate amount-shared-value
[0 total-amount]
[0 total-width])
width->amount #(/ (* % total-amount) total-width)
gesture (get-gesture
[detecting-gesture? slider-width-shared-value max-amount
slider-height-shared-value
amount-shared-value amount-on-gesture-start width->amount
slider-opacity-shared-value on-new-amount set-detecting-gesture])
on-press (rn/use-callback
(fn []
(when (and (not detecting-gesture?) allow-press?)
(on-press bar bar-idx bar-width bar-max-width)
(set-detecting-gesture true)
(animation/show-slider slider-opacity-shared-value)))
[detecting-gesture? allow-press? on-press])]
[rn/pressable
{:on-press on-press}
[reanimated/view
{:style (style/network-bar props network-bar-shared-value)
:accessibility-label :network-routing-bar}
[gesture/gesture-detector {:gesture gesture}
[rn/view {:style style/slider-container}
[reanimated/view
{:style (style/slider {:width-shared-value slider-width-shared-value
:height-shared-value slider-height-shared-value
:opacity-shared-value slider-opacity-shared-value})}]]]]]))
(defn- add-bar-shared-values
[{:keys [amount] :as network}]
@@ -93,101 +105,113 @@
(interleave (repeat [rn/view {:style (style/dashed-line-line network-name)}])
(repeat [rn/view {:style style/dashed-line-space}])))))
(defn f-network-routing-bars
[_]
(let [selected-network-idx (reagent/atom nil)
press-locked? (reagent/atom false)
lock-press #(reset! press-locked? true)
unlock-press #(reset! press-locked? false)
reset-state-values #(reset! selected-network-idx nil)]
(fn [{:keys [networks total-width total-amount requesting-data? on-amount-selected]}]
(let [bar-opacity-shared-value (reanimated/use-shared-value 0)
network-bars (map add-bar-shared-values networks)
amount->width #(* % (/ total-width total-amount))
bars-widths-negative (map #(-> % get-negative-amount amount->width)
network-bars)
last-bar-idx (dec (count network-bars))]
(rn/use-effect
#(when (and (not requesting-data?) @selected-network-idx)
(let [bar (nth network-bars @selected-network-idx)]
(animation/hide-pressed-bar bar amount->width))
(animation/update-bar-values-and-reset-animations
{:new-network-values networks
:network-bars network-bars
:amount->width amount->width
:reset-values-fn reset-state-values
:lock-press-fn lock-press
:unlock-press-fn unlock-press
:add-new-timeout add-new-timeout}))
[requesting-data?])
[:<>
(doall
(for [[bar-idx bar] (map-indexed vector network-bars)
:let [bar-max-width (amount->width (:max-amount bar))
bar-width (-> (:amount-shared-value bar)
(reanimated/get-shared-value)
(amount->width))
hide-division? (or (= last-bar-idx bar-idx) @selected-network-idx)
this-bar-selected? (= @selected-network-idx bar-idx)]]
^{:key (str "network-bar-" bar-idx)}
[:f> f-network-bar
{:bar bar
:max-width bar-max-width
:total-width total-width
:total-amount total-amount
:bar-division? hide-division?
:on-top? this-bar-selected?
:allow-press? (and (or (not @selected-network-idx) this-bar-selected?)
(not requesting-data?)
(not @press-locked?))
:on-press (fn []
(when-not @selected-network-idx
(let [[previous-bars [_ & next-bars]] (split-at bar-idx network-bars)
number-previous-bars bar-idx]
(animation/move-previous-bars
{:bars previous-bars
:bars-widths-negative bars-widths-negative})
(animation/move-pressed-bar
{:bar bar
:bars-widths-negative bars-widths-negative
:number-previous-bars number-previous-bars})
(animation/move-next-bars
{:bars next-bars
:bars-widths-negative bars-widths-negative
:number-previous-bars (inc number-previous-bars)
:extra-offset (max 0 (- bar-max-width bar-width))
:add-new-timeout add-new-timeout}))
(animation/show-max-limit-bar bar-opacity-shared-value)
(reset! selected-network-idx bar-idx)))
:on-new-amount (fn [new-amount]
(animation/hide-max-limit-bar bar-opacity-shared-value)
(when on-amount-selected
(on-amount-selected new-amount @selected-network-idx)))}]))
(defn- network-routing-bars
[{:keys [networks total-width total-amount requesting-data? on-amount-selected]}]
(let [[selected-network-idx
set-selected-network-idx] (rn/use-state nil)
[press-locked?
set-press-locked] (rn/use-state false)
lock-press (rn/use-callback #(set-press-locked true))
unlock-press (rn/use-callback #(set-press-locked false))
reset-state-values (rn/use-callback #(set-selected-network-idx nil))
bar-opacity-shared-value (reanimated/use-shared-value 0)
network-bars (map add-bar-shared-values networks)
amount->width #(* % (/ total-width total-amount))
bars-widths-negative (map #(-> % get-negative-amount amount->width)
network-bars)
last-bar-idx (dec (count network-bars))
network-bar-on-press (rn/use-callback
(fn [bar bar-idx bar-width bar-max-width]
(when-not selected-network-idx
(let [[previous-bars
[_ & next-bars]] (split-at bar-idx network-bars)]
(animation/move-previous-bars
{:bars previous-bars
:bars-widths-negative bars-widths-negative})
(animation/move-pressed-bar
{:bar bar
:bars-widths-negative bars-widths-negative
:number-previous-bars bar-idx})
(animation/move-next-bars
{:bars next-bars
:bars-widths-negative bars-widths-negative
:number-previous-bars (inc bar-idx)
:extra-offset (max 0 (- bar-max-width bar-width))
:add-new-timeout add-new-timeout}))
(animation/show-max-limit-bar bar-opacity-shared-value)
(set-selected-network-idx bar-idx)))
[selected-network-idx bars-widths-negative network-bars])
on-new-amount (rn/use-callback
(fn [new-amount]
(animation/hide-max-limit-bar bar-opacity-shared-value)
(when on-amount-selected
(on-amount-selected new-amount selected-network-idx)))
[on-amount-selected selected-network-idx])]
(rn/use-effect
#(when (and (not requesting-data?) selected-network-idx)
(let [bar (nth network-bars selected-network-idx)]
(animation/hide-pressed-bar bar amount->width))
(animation/update-bar-values-and-reset-animations
{:new-network-values networks
:network-bars network-bars
:amount->width amount->width
:reset-values-fn reset-state-values
:lock-press-fn lock-press
:unlock-press-fn unlock-press
:add-new-timeout add-new-timeout}))
[requesting-data?])
[:<>
(doall
(for [[bar-idx bar] (map-indexed vector network-bars)
:let [bar-max-width (amount->width (:max-amount bar))
bar-width (-> (:amount-shared-value bar)
(reanimated/get-shared-value)
(amount->width))
hide-division? (or (= last-bar-idx bar-idx) selected-network-idx)
this-bar-selected? (= selected-network-idx bar-idx)]]
^{:key (str "network-bar-" bar-idx)}
[network-bar
{:bar bar
:bar-idx bar-idx
:bar-width bar-width
:bar-max-width bar-max-width
:total-width total-width
:total-amount total-amount
:bar-division? hide-division?
:on-top? this-bar-selected?
:allow-press? (and (or (not selected-network-idx) this-bar-selected?)
(not requesting-data?)
(not press-locked?))
:on-press network-bar-on-press
:on-new-amount on-new-amount}]))
(let [{:keys [max-amount network-name]} (some->> @selected-network-idx
(nth network-bars))
limit-bar-width (amount->width max-amount)]
[reanimated/view
{:style (style/max-limit-bar
{:opacity-shared-value bar-opacity-shared-value
:width limit-bar-width})}
[rn/view {:style (style/max-limit-bar-background network-name)}]
[dashed-line network-name]])]))))
(let [{:keys [max-amount network-name]} (some->> selected-network-idx
(nth network-bars))
limit-bar-width (amount->width max-amount)]
[reanimated/view
{:style (style/max-limit-bar
{:opacity-shared-value bar-opacity-shared-value
:width limit-bar-width})}
[rn/view {:style (style/max-limit-bar-background network-name)}]
[dashed-line network-name]])]))
(defn view-internal
[{:keys [networks container-style theme] :as params}]
(reagent/with-let [total-width (reagent/atom nil)]
[{:keys [networks container-style] :as params}]
(let [theme (quo.theme/use-theme-value)
[total-width
set-total-width] (rn/use-state nil)
on-layout (rn/use-callback #(let [width (oops/oget % "nativeEvent.layout.width")]
(when (not= width total-width)
(set-total-width width))))]
(rn/use-unmount (fn []
(doseq [[_ living-timeout] @timeouts]
(js/clearTimeout living-timeout))))
[rn/view
{:accessibility-label :network-routing
:style (style/container container-style theme)
:on-layout #(reset! total-width (oops/oget % "nativeEvent.layout.width"))}
(when @total-width
:on-layout on-layout}
(when total-width
^{:key (str "network-routing-" (count networks))}
[:f> f-network-routing-bars (assoc params :total-width @total-width)])]
(finally
(doseq [[_ living-timeout] @timeouts]
(js/clearTimeout living-timeout)))))
[network-routing-bars (assoc params :total-width total-width)])]))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal network-routing-schema/?schema)))
(def view (schema/instrument #'view-internal network-routing-schema/?schema))
@@ -14,6 +14,5 @@
[:networks {:optional true}
[:maybe [:sequential [:map [:source [:maybe :schema.common/image-source]]]]]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:value {:optional true} [:maybe :string]]
[:theme :schema.common/theme]]]]
[:value {:optional true} [:maybe :string]]]]]
:any])
+37 -35
View File
@@ -12,7 +12,6 @@
[quo.foundations.common :as common]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[schema.core :as schema]))
(defn fiat-format
@@ -75,7 +74,8 @@
[token-name-text theme text]])
(defn input-section
[{:keys [on-change-text value value-atom on-selection-change on-token-press]}]
[{:keys [on-change-text value value-internal set-value-internal on-selection-change
on-token-press]}]
(let [input-ref (atom nil)
set-ref #(reset! input-ref %)
focus-input #(when-let [ref ^js @input-ref]
@@ -83,7 +83,7 @@
controlled-input? (some? value)
handle-on-change-text (fn [v]
(when-not controlled-input?
(reset! value-atom v))
(set-value-internal v))
(when on-change-text
(on-change-text v)))
handle-selection-change (fn [^js e]
@@ -117,41 +117,43 @@
:on-selection-change handle-selection-change
:selection (clj->js selection)}
controlled-input? (assoc :value value)
(not controlled-input?) (assoc :default-value @value-atom))]]
(not controlled-input?) (assoc :default-value value-internal))]]
[token-label
{:theme theme
:text (if crypto? token currency)
:value (if controlled-input? value @value-atom)}]])))
:value (if controlled-input? value value-internal)}]])))
(defn- view-internal
[]
(let [width (:width (rn/get-window))
value-atom (reagent/atom nil)
crypto? (reagent/atom true)]
(fn [{:keys [theme container-style value on-swap] :as props}]
(let [handle-on-swap (fn []
(swap! crypto? not)
(when on-swap (on-swap @crypto?)))]
[rn/view {:style (merge (style/main-container width) container-style)}
[rn/view {:style style/amount-container}
[input-section
(assoc props
:value-atom value-atom
:crypto? @crypto?)]
[button/button
{:icon true
:icon-only? true
:size 32
:on-press handle-on-swap
:type :outline
:accessibility-label :reorder}
:i/reorder]]
[divider-line/view {:container-style (style/divider theme)}]
[data-info
(assoc props
:crypto? @crypto?
:amount (or value @value-atom))]]))))
[{:keys [container-style value on-swap] :as props}]
(let [theme (quo.theme/use-theme-value)
width (:width (rn/get-window))
[value-internal set-value-internal] (rn/use-state nil)
[crypto? set-crypto] (rn/use-state true)
handle-on-swap (rn/use-callback
(fn []
(set-crypto (not crypto?))
(when on-swap (on-swap (not crypto?))))
[crypto? on-swap])]
[rn/view {:style (merge (style/main-container width) container-style)}
[rn/view {:style style/amount-container}
[input-section
(assoc props
:value-internal value-internal
:set-value-internal set-value-internal
:crypto? crypto?)]
[button/button
{:icon true
:icon-only? true
:size 32
:on-press handle-on-swap
:type :outline
:accessibility-label :reorder}
:i/reorder]]
[divider-line/view {:container-style (style/divider theme)}]
[data-info
(assoc props
:theme theme
:crypto? crypto?
:amount (or value value-internal))]]))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal component-schema/?schema)))
(def view (schema/instrument #'view-internal component-schema/?schema))
@@ -13,7 +13,6 @@
[:blur? {:optional true} [:maybe :boolean]]
[:on-press {:optional true} [:maybe fn?]]
[:state {:optional true} [:maybe [:= :disabled]]]
[:theme :schema.common/theme]
[:second-tag-prefix {:optional true} [:maybe :keyword]]
[:third-tag-prefix {:optional true} [:maybe :keyword]]
[:fourth-tag-prefix {:optional true} [:maybe :keyword]]
@@ -8,7 +8,6 @@
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.hole-view :as hole-view]
[reagent.core :as reagent]
[schema.core :as schema]
[utils.i18n :as i18n]))
@@ -100,39 +99,37 @@
[context-tag/view (merge props {:size 24 :blur? blur?})]])
(defn- view-internal
[_]
(let [pressed? (reagent/atom false)]
(fn
[{:keys [state theme blur?
on-press
first-tag second-tag third-tag fourth-tag
second-tag-prefix third-tag-prefix fourth-tag-prefix]
:as props}]
[rn/pressable
{:style (style/wallet-activity-container {:pressed? @pressed?
:theme theme
:blur? blur?})
:accessibility-label :wallet-activity
:disabled (= state :disabled)
:on-press on-press
:on-press-in (fn [] (reset! pressed? true))
:on-press-out (fn [] (reset! pressed? false))}
[rn/view
{:style {:flex-direction :row}}
[transaction-icon-view props]
[rn/view
{:style style/content-container}
[transaction-header props]
[rn/view {:style style/content-line}
(when first-tag [prop-tag first-tag blur?])
(when second-tag-prefix [prop-text second-tag-prefix theme])
(when second-tag [prop-tag second-tag blur?])]
[rn/view {:style style/content-line}
(when third-tag-prefix [prop-text third-tag-prefix theme])
(when third-tag [prop-tag third-tag blur?])
(when fourth-tag-prefix [prop-text fourth-tag-prefix theme])
(when fourth-tag [prop-tag fourth-tag blur?])]]]])))
[{:keys [state blur? first-tag second-tag third-tag fourth-tag on-press
second-tag-prefix third-tag-prefix fourth-tag-prefix]
:as props}]
(let [theme (quo.theme/use-theme-value)
[pressed?
set-pressed] (rn/use-state false)
on-press-in (rn/use-callback #(set-pressed true))
on-press-out (rn/use-callback #(set-pressed false))]
[rn/pressable
{:style (style/wallet-activity-container {:pressed? pressed?
:theme theme
:blur? blur?})
:accessibility-label :wallet-activity
:disabled (= state :disabled)
:on-press on-press
:on-press-in on-press-in
:on-press-out on-press-out}
[rn/view
{:style {:flex-direction :row}}
[transaction-icon-view props]
[rn/view
{:style style/content-container}
[transaction-header props]
[rn/view {:style style/content-line}
(when first-tag [prop-tag first-tag blur?])
(when second-tag-prefix [prop-text second-tag-prefix theme])
(when second-tag [prop-tag second-tag blur?])]
[rn/view {:style style/content-line}
(when third-tag-prefix [prop-text third-tag-prefix theme])
(when third-tag [prop-tag third-tag blur?])
(when fourth-tag-prefix [prop-text fourth-tag-prefix theme])
(when fourth-tag [prop-tag fourth-tag blur?])]]]]))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal component-schema/?schema)))
(def view (schema/instrument #'view-internal component-schema/?schema))
+2
View File
@@ -32,6 +32,7 @@
quo.components.community.channel-action.view
quo.components.community.channel-actions.view
quo.components.community.community-card-view
quo.components.community.community-detail-token-gating.view
quo.components.community.community-list-view
quo.components.community.community-stat.view
quo.components.community.community-view
@@ -224,6 +225,7 @@
;;;; Community
(def community-card-view-item quo.components.community.community-card-view/view)
(def community-detail-token-gating quo.components.community.community-detail-token-gating.view/view)
(def communities-membership-list-item
quo.components.community.community-list-view/communities-membership-list-item)
(def community-stats-column quo.components.community.community-view/community-stats-column)
+1
View File
@@ -16,6 +16,7 @@
quo.components.calendar.calendar.component-spec
quo.components.calendar.calendar.month-picker.component-spec
quo.components.colors.color-picker.component-spec
quo.components.community.community-detail-token-gating.component-spec
quo.components.community.community-stat.component-spec
quo.components.counter.collectible-counter.component-spec
quo.components.counter.counter.component-spec
+4 -1
View File
@@ -10,7 +10,10 @@
(def ui-themed
{:no-funds
{:light (js/require "../resources/images/ui2/no-funds-light.png")
:dark (js/require "../resources/images/ui2/no-funds-dark.png")}})
:dark (js/require "../resources/images/ui2/no-funds-dark.png")}
:no-contacts-to-chat
{:light (js/require "../resources/images/ui2/no-contacts-to-chat-light.png")
:dark (js/require "../resources/images/ui2/no-contacts-to-chat-dark.png")}})
(defn get-themed-image
[k theme]
-4
View File
@@ -226,7 +226,3 @@
(defn open-url [link] (.openURL ^js linking link))
(def set-status-bar-style react-native/StatusBar.setBarStyle)
(defn sharing
[content]
(.share (.-Share ^js react-native) (clj->js content)))
+1 -5
View File
@@ -4,9 +4,5 @@
(defn open
([options]
(open options #() #()))
([options on-success on-error]
(-> ^js react-native-share
(.open (clj->js options))
(.then on-success)
(.catch on-error))))
(.open (clj->js options)))))
+1 -1
View File
@@ -12,5 +12,5 @@
(defn contacts-section-header
[{:keys [title]}]
(let [theme (quo.theme/use-theme-value)]
[quo/divider-label {:container-style {:background-color (style/contacts-section-header theme)}}
[quo/divider-label {:container-style (style/contacts-section-header theme)}
title]))
@@ -5,7 +5,7 @@
[utils.re-frame :as rf]))
(defn contact-list-item
[{:keys [on-press on-long-press accessory allow-multiple-presses?]}
[{:keys [on-press on-long-press accessory allow-multiple-presses? disabled?]}
{:keys [primary-name secondary-name public-key compressed-key ens-verified added?]}
theme]
(let [photo-path (rf/sub [:chats/photo-path public-key])
@@ -24,4 +24,5 @@
:contact? added?
:on-press on-press
:on-long-press on-long-press
:accessory accessory}]))
:accessory accessory
:disabled? disabled?}]))
+13 -10
View File
@@ -40,13 +40,15 @@
params: sequence - A positional sequence of zero or more arguments.
on-success/on-error: function/vector (optional) - When a function, it will be
called with the transformed response as the sole argument. When a vector, it
is expected to be a valid re-frame event vector, and the event will be
dispatched with the transformed response conj'ed at the end.
called with the transformed response and id (request-id) as the argument.
When a vector, it is expected to be a valid re-frame event vector, and the event
will be dispatched with the transformed response and id conj'ed at the end.
js-response: boolean - When non-nil, the successful response will not be
recursively converted to Clojure data structures. Default: nil.
id: (optional) - id passed while making the RPC call will be returned in response
number-of-retries: integer - The maximum number of retries in case of failure.
Default: nil.
@@ -56,14 +58,14 @@
Note that on-error is optional, but if not provided, a default implementation
will be used.
"
[{:keys [method params on-success on-error js-response] :as arg}]
[{:keys [method params on-success on-error js-response id] :as arg}]
(let [params (or params [])
on-error (or on-error
(on-error-retry call arg)
#(log/warn :json-rpc/error method :error % :params params))]
(native-module/call-private-rpc
(transforms/clj->json {:jsonrpc "2.0"
:id 1
:id (or id 1)
:method method
:params params})
(fn [raw-response]
@@ -79,12 +81,13 @@
(rf/dispatch (conj on-error error))
(on-error error)))
(when on-success
(let [result (if js-response
(.-result response-js)
(transforms/js->clj (.-result response-js)))]
(let [result (if js-response
(.-result response-js)
(transforms/js->clj (.-result response-js)))
request-id (.-id response-js)]
(if (vector? on-success)
(rf/dispatch (conj on-success result))
(on-success result)))))))))))
(rf/dispatch (conj on-success result request-id))
(on-success result request-id)))))))))))
(re-frame/reg-fx
:json-rpc/call
+6 -5
View File
@@ -3,7 +3,6 @@
[react-native.cameraroll :as cameraroll]
[react-native.fs :as fs]
[react-native.platform :as platform]
[react-native.share :as share]
[utils.re-frame :as rf]))
(def config
@@ -15,10 +14,12 @@
(blob/fetch uri
config
(fn [downloaded-url]
(share/open {:url (str (when platform/android? "file://") downloaded-url)
:isNewTask true}
#(fs/unlink downloaded-url)
#(fs/unlink downloaded-url))))))
(rf/dispatch [:open-share
{:options {:url (str (when platform/android? "file://")
downloaded-url)
:isNewTask true}
:on-success #(fs/unlink downloaded-url)
:on-error #(fs/unlink downloaded-url)}])))))
(rf/reg-fx :effects.lightbox/save-image-to-gallery
(fn [[uri on-success]]
+16 -4
View File
@@ -100,7 +100,7 @@
ens?
(assoc-in [:route-params :ens-name] fragment)
(and (or (= handler :community) (= handler :community-chat)) compressed-key?)
(and (= handler :community) compressed-key?)
(assoc-in [:route-params :community-id] fragment)
(and equal-end-of-base64url (= handler :community) (:community-data route-params))
@@ -119,6 +119,13 @@
(re-find constants/regx-starts-with-uuid (:community-data route-params)))
(assoc-in [:route-params :community-channel-id] (:community-data route-params))
(and fragment
(= handler :community-chat)
(:community-data route-params)
(string? (:community-data route-params))
(not (re-find constants/regx-starts-with-uuid (:community-data route-params))))
(assoc-in [:route-params :community-encoded-data?] true)
(and equal-end-of-base64url (= handler :user) (:user-data route-params))
(update-in [:route-params :user-data] #(str % equal-end-of-base64url))
@@ -203,6 +210,10 @@
(cb {:type :private-chat
:error :invalid-chat-id})))))
(defn parse-shared-url
[uri]
(re-frame/dispatch [:shared-urls/parse-shared-url uri]))
(defn match-community-channel-async
[{:keys [community-channel-id community-id]} cb]
(if (validators/valid-compressed-key? community-id)
@@ -307,9 +318,10 @@
(and (= handler :community-chat) (:community-channel-id route-params) (:community-id route-params))
(match-community-channel-async route-params cb)
(and (= handler :community-chat) (:community-id route-params))
(cb {:type :community
:community-id (:community-id route-params)})
(and (= handler :community-chat)
(:community-encoded-data? route-params)
(:community-id route-params))
(parse-shared-url uri)
;; NOTE: removed in `match-uri`, might need this in the future
(= handler :wallet-account)
+2
View File
@@ -42,12 +42,14 @@
[:community-chat
{:community-data
"G54AAKwObLdpiGjXnckYzRcOSq0QQAS_CURGfqVU42ceGHCObstUIknTTZDOKF3E8y2MSicncpO7fTskXnoACiPKeejvjtLTGWNxUhlT7fyQS7Jrr33UVHluxv_PLjV2ePGw5GQ33innzeK34pInIgUGs5RjdQifMVmURalxxQKwiuoY5zwIjixWWRHqjHM="
:community-encoded-data? true
:community-id "zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11"}]
"status-app://cc/G54AAKwObLdpiGjXnckYzRcOSq0QQAS_CURGfqVU42ceGHCObstUIknTTZDOKF3E8y2MSicncpO7fTskXnoACiPKeejvjtLTGWNxUhlT7fyQS7Jrr33UVHluxv_PLjV2ePGw5GQ33innzeK34pInIgUGs5RjdQifMVmURalxxQKwiuoY5zwIjixWWRHqjHM=#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11"
[:community-chat
{:community-data
"G54AAKwObLdpiGjXnckYzRcOSq0QQAS_CURGfqVU42ceGHCObstUIknTTZDOKF3E8y2MSicncpO7fTskXnoACiPKeejvjtLTGWNxUhlT7fyQS7Jrr33UVHluxv_PLjV2ePGw5GQ33innzeK34pInIgUGs5RjdQifMVmURalxxQKwiuoY5zwIjixWWRHqjHM="
:community-encoded-data? true
:community-id "zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11"}]
"https://status.app/cc/c432709e-fc73-440d-bb67-cb3a0929dfda#zQ3shZL6dXiFCbDyxnXxwQa9v8QFC2q19subFtyxd7kVszMVo"
@@ -0,0 +1,20 @@
(ns status-im.common.shared-urls.data-store
(:require
[clojure.set :as set]))
(defn <-rpc
[{:keys [community channel contact]}]
{:community (set/rename-keys
community
{:communityId :community-id
:displayName :display-name
:membersCount :members-count
:tagIndices :tag-indices})
:channel (set/rename-keys
channel
{:displayName :display-name
:channelUuid :channel-uuid})
:contact (set/rename-keys
contact
{:displayName :display-name
:publicKey :public-key})})
@@ -0,0 +1,33 @@
(ns status-im.common.shared-urls.events
(:require
[clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.common.shared-urls.data-store :as data-store]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(defn parse-shared-url-success
[url response]
(let [parsed-data (data-store/<-rpc response)
cb #(rf/dispatch [:universal-links/match-value url %])]
(cond
(:channel parsed-data)
(cb {:type :community-chat
:chat-id (str (get-in parsed-data [:community :community-id])
(get-in parsed-data [:channel :channel-uuid]))
:community-id (get-in parsed-data [:community :community-id])})
(:community parsed-data)
(cb {:type :community :community-id (get-in parsed-data [:community :community-id])}))))
(defn parse-shared-url
[_ [uri]]
(let [url (string/replace uri #"status-app:/" "https://status.app")]
{:json-rpc/call
[{:method "wakuext_parseSharedURL"
:params [url]
:on-success #(parse-shared-url-success url %)
:on-error (fn [err]
(log/error "failed to parse shared url" {:error err :url url}))}]}))
(re-frame/reg-event-fx :shared-urls/parse-shared-url parse-shared-url)
@@ -18,8 +18,8 @@
[rn/view
[quo/text
{:accessibility-label :sync-code-generated
:weight :bold
:size :heading-1
:weight :semi-bold
:size :heading-2
:style {:margin-bottom 4}}
(i18n/label :t/enter-password)]
[rn/view
+7 -5
View File
@@ -116,14 +116,16 @@
(log/debug "universal-links: no url")))
(rf/defn on-handle
{:events [::match-value]}
[cofx url {:keys [type chat-id] :as data}]
{:events [:universal-links/match-value]}
[cofx url {:keys [type chat-id community-id] :as data}]
(case type
:group-chat (handle-group-chat cofx data)
:private-chat (handle-private-chat cofx data)
:community-requests (handle-community-requests cofx data)
:community (communities.events/navigate-to-serialized-community cofx data)
:community-chat {:dispatch [:communities/navigate-to-community-chat chat-id :pop-to-root]}
:community (communities.events/navigate-to-community-overview cofx [community-id])
:community-chat (communities.events/navigate-to-community-chat cofx
[chat-id :pop-to-root
community-id])
:contact (handle-view-profile cofx data)
:browser (handle-browse cofx data)
:eip681 (handle-eip681 cofx data)
@@ -136,7 +138,7 @@
{:router/handle-uri {:chain (chain/chain-keyword db)
:chats (:chats db)
:uri url
:cb #(re-frame/dispatch [::match-value url %])}})
:cb #(re-frame/dispatch [:universal-links/match-value url %])}})
(rf/defn store-url-for-later
"Store the url in the db to be processed on login"
+13
View File
@@ -2,6 +2,7 @@
(:require
[clojure.string :as string]
[react-native.config :as react-native-config]
[status-im.constants :as constants]
[utils.ens.core :as utils.ens]
[utils.ethereum.chain :as chain]))
@@ -70,6 +71,18 @@
; currently not supported in status-go
(def enable-remove-profile-picture? false)
(defn env-variable->int
[env-var-name default-value]
(js/parseInt (get-config env-var-name default-value)))
(def delete-message-for-me-undo-time-limit-ms
(env-variable->int :DELETE_MESSAGE_FOR_ME_UNDO_TIME_LIMIT
constants/delete-message-for-me-undo-time-limit-ms))
(def delete-message-undo-time-limit-ms
(env-variable->int :DELETE_MESSAGE_UNDO_TIME_LIMIT
constants/delete-message-undo-time-limit-ms))
(def verify-transaction-chain-id (js/parseInt (get-config :VERIFY_TRANSACTION_CHAIN_ID "1")))
(def verify-transaction-url
(if (= :mainnet (chain/chain-id->chain-keyword verify-transaction-chain-id))
+2
View File
@@ -448,6 +448,8 @@
(def ^:const optimism-short-name "opt")
(def ^:const arbitrum-short-name "arb1")
(def ^:const default-multichain-address-prefix "eth:opt:arb1:")
(def ^:const mainnet-abbreviated-name "Eth.")
(def ^:const optimism-abbreviated-name "Opt.")
(def ^:const arbitrum-abbreviated-name "Arb1.")
@@ -101,6 +101,7 @@
{:keys [primary-name public-key]} (when one-contact-selected?
(rf/sub [:contacts/contact-by-identity
(first selected-contacts)]))]
(rn/use-unmount #(rf/dispatch [:group-chat/clear-contacts]))
[rn/view {:flex 1}
[rn/view {:padding-horizontal 20}
[quo/button
+1 -2
View File
@@ -5,7 +5,6 @@
[re-frame.core :as re-frame]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[react-native.share :as share]
[status-im.common.contact-list-item.view :as contact-list-item]
[status-im.common.contact-list.view :as contact-list]
[status-im.common.home.actions.view :as actions]
@@ -122,7 +121,7 @@
[:show-bottom-sheet {:content chat.actions.view/new-chat}])
:accessibility-label :new-chat-button}
:card-props
{:on-press #(share/open {:url profile-link})
{:on-press #(rf/dispatch [:open-share {:options {:url profile-link}}])
:banner (resources/get-image :invite-friends)
:title (i18n/label :t/invite-friends-to-status)
:description (i18n/label :t/share-invite-link)}})
@@ -41,8 +41,13 @@
(defn f-send-button
[props state animations window-height images? btn-opacity z-index edit]
(let [{:keys [text-value]} state
customization-color (rf/sub [:profile/customization-color])]
(let [{:keys [text-value]} state
profile-customization-color (rf/sub [:profile/customization-color])
{:keys [chat-id chat-type]
chat-color :color} (rf/sub [:chats/current-chat-chat-view])
contact-customization-color (when (= chat-type constants/one-to-one-chat-type)
(rf/sub [:contacts/contact-customization-color-by-address
chat-id]))]
(rn/use-effect (fn []
;; Handle send button opacity animation and z-index when input content changes
(if (or (seq @text-value) images?)
@@ -60,7 +65,7 @@
[quo/button
{:icon-only? true
:size 32
:customization-color customization-color
:customization-color (or contact-customization-color chat-color profile-customization-color)
:accessibility-label :send-message-button
:on-press #(send-message props state animations window-height edit)}
:i/arrow-up]]))
@@ -8,13 +8,13 @@
:margin-bottom (when-not community? 12)})
(def community-tag-container
{:margin-horizontal 20 :margin-top 4 :margin-bottom 12})
{:margin-horizontal 20
:margin-top 4
:margin-bottom 12})
(def no-pinned-messages-container
{:justify-content :center
:align-items :center
:margin 12
:margin-bottom (when platform/android? 12)})
:align-items :center})
(def list-footer
{:height (when platform/android? 12)})
@@ -29,11 +29,15 @@
(defn pinned-message
[{:keys [from quoted-message timestamp-str]}]
(let [[primary-name _] (rf/sub [:contacts/contact-two-names-by-identity from])
customization-color (rf/sub [:profile/customization-color])]
(let [[primary-name _] (rf/sub [:contacts/contact-two-names-by-identity from])
one-to-one-chat? (rf/sub [:chats/current-chat-one-to-one?])
current-chat-color (rf/sub [:chats/current-chat-color])
contact-customization-color (rf/sub [:contacts/contact-customization-color-by-address from])]
[quo/system-message
{:type :pinned
:pinned-by primary-name
:customization-color customization-color
:customization-color (if one-to-one-chat?
contact-customization-color
current-chat-color)
:child [reply/quoted-message quoted-message false true]
:timestamp timestamp-str}]))
@@ -79,16 +79,17 @@
(defn system-message-contact-request
[{:keys [chat-id timestamp-str from]} type]
(let [[primary-name _] (rf/sub [:contacts/contact-two-names-by-identity chat-id])
contact (rf/sub [:contacts/contact-by-address chat-id])
photo-path (when (seq (:images contact)) (rf/sub [:chats/photo-path chat-id]))
customization-color (rf/sub [:profile/customization-color])
public-key (rf/sub [:profile/public-key])]
(let [[primary-name _] (rf/sub [:contacts/contact-two-names-by-identity chat-id])
{:keys [images]
contact-customization-color
:customization-color} (rf/sub [:contacts/contact-by-address chat-id])
photo-path (when (seq images) (rf/sub [:chats/photo-path chat-id]))
public-key (rf/sub [:profile/public-key])]
[quo/system-message
{:type type
:timestamp timestamp-str
:display-name primary-name
:customization-color customization-color
:customization-color contact-customization-color
:photo-path photo-path
:incoming? (not= public-key from)}]))
@@ -7,6 +7,7 @@
[react-native.gesture :as gesture]
[reagent.core :as reagent]
[status-im.common.contact-list-item.view :as contact-list-item]
[status-im.config :as config]
[status-im.constants :as constants]
[status-im.contexts.chat.messenger.composer.reply.view :as reply]
[status-im.contexts.chat.messenger.messages.drawers.style :as style]
@@ -140,7 +141,7 @@
(rf/dispatch
[:hide-bottom-sheet])
(rf/dispatch [:chat.ui/delete-message-for-me message-data
constants/delete-message-for-me-undo-time-limit-ms]))
config/delete-message-for-me-undo-time-limit-ms]))
:label (i18n/label :t/delete-for-me)
:icon :i/delete
@@ -155,7 +156,7 @@
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:chat.ui/delete-message message-data
constants/delete-message-undo-time-limit-ms]))
config/delete-message-undo-time-limit-ms]))
:label (i18n/label :t/delete-for-everyone)
:icon :i/delete
:id :delete-for-all}])))
@@ -1,6 +1,8 @@
(ns status-im.contexts.chat.messenger.messages.list.style
(:require
[quo.foundations.colors :as colors]
[quo.foundations.shadows :as shadows]
[quo.theme :as quo.theme]
[react-native.reanimated :as reanimated]
[status-im.contexts.chat.messenger.messages.constants :as messages.constants]))
@@ -23,10 +25,12 @@
[bottom theme top-margin]
(reanimated/apply-animations-to-style
{:bottom bottom}
{:background-color (colors/theme-colors colors/white colors/neutral-95 theme)
:padding-horizontal 20
:border-radius 20
:margin-top top-margin}))
(merge
(shadows/get 2 (quo.theme/get-theme) :inverted)
{:background-color (colors/theme-colors colors/white colors/neutral-95 theme)
:padding-horizontal 20
:border-radius 20
:margin-top top-margin})))
(defn header-image
[scale top left theme]
@@ -1,5 +1,6 @@
(ns status-im.contexts.chat.messenger.messages.list.view
(:require
[clojure.string :as string]
[legacy.status-im.ui.screens.chat.group :as chat.group]
[oops.core :as oops]
[quo.core :as quo]
@@ -112,19 +113,24 @@
[rn/view {:style {:height height}}]))
(defn list-footer-avatar
[{:keys [distance-from-list-top display-name online? profile-picture theme group-chat color]}]
(let [scale (reanimated/interpolate distance-from-list-top
[0 messages.constants/header-container-top-margin]
[1 0.4]
messages.constants/default-extrapolation-option)
top (reanimated/interpolate distance-from-list-top
[0 messages.constants/header-container-top-margin]
[-44 -12]
messages.constants/default-extrapolation-option)
left (reanimated/interpolate distance-from-list-top
[0 messages.constants/header-container-top-margin]
[16 -8]
messages.constants/default-extrapolation-option)]
[{:keys [distance-from-list-top display-name online? profile-picture theme group-chat color
emoji chat-type chat-name last-message]}]
(let [scale (reanimated/interpolate
distance-from-list-top
[0 (if (seq last-message) messages.constants/header-container-top-margin 0)]
[1 0.4]
messages.constants/default-extrapolation-option)
top (reanimated/interpolate
distance-from-list-top
[0 (if (seq last-message) messages.constants/header-container-top-margin 0)]
[-44 -12]
messages.constants/default-extrapolation-option)
left (reanimated/interpolate
distance-from-list-top
[0 (if (seq last-message) messages.constants/header-container-top-margin 0)]
[16 -8]
messages.constants/default-extrapolation-option)
community-channel? (= chat-type constants/community-chat-type)]
[reanimated/view
{:style (style/header-image scale top left theme)}
(if group-chat
@@ -132,7 +138,10 @@
{:customization-color color
:size :size-80
:picture profile-picture
:override-theme :dark}]
:emoji (when (and (not (string/blank? emoji))
community-channel?)
(string/trim emoji))
:chat-name chat-name}]
[quo/user-avatar
{:full-name display-name
:online? online?
@@ -140,15 +149,17 @@
:size :big}])]))
(defn chat-display-name
[{:keys [distance-from-list-top display-name contact theme]}]
(let [top (reanimated/interpolate distance-from-list-top
[0 messages.constants/header-container-top-margin]
[0 -35]
messages.constants/default-extrapolation-option)
left (reanimated/interpolate distance-from-list-top
[0 messages.constants/header-container-top-margin]
[0 40]
messages.constants/default-extrapolation-option)]
[{:keys [distance-from-list-top display-name contact theme last-message]}]
(let [top (reanimated/interpolate
distance-from-list-top
[0 (if (seq last-message) messages.constants/header-container-top-margin 0)]
[0 -35]
messages.constants/default-extrapolation-option)
left (reanimated/interpolate
distance-from-list-top
[0 (if (seq last-message) messages.constants/header-container-top-margin 0)]
[0 40]
messages.constants/default-extrapolation-option)]
[reanimated/view
{:style (style/user-name-container top left)}
[rn/view
@@ -195,54 +206,55 @@
muted?)))}]}]))
(defn bio-and-actions
[{:keys [distance-from-list-top bio chat-id customization-color]}]
(let [has-bio (seq bio)
[{:keys [distance-from-list-top bio chat-id customization-color last-message description]}]
(let [has-bio (seq (or bio description))
top (reanimated/interpolate
distance-from-list-top
[0 messages.constants/header-container-top-margin]
[0 (if (seq last-message) messages.constants/header-container-top-margin 0)]
[(if has-bio 8 16) (if has-bio -28 -20)]
messages.constants/default-extrapolation-option)]
[reanimated/view
{:style (style/bio-and-actions top)}
(when has-bio
[quo/text bio])
[quo/text (or bio description)])
[actions chat-id customization-color]]))
(defn footer-component
[{:keys [chat distance-from-list-top theme customization-color]}]
(let [{:keys [chat-id chat-name emoji chat-type
group-chat color]} chat
display-name (cond
(= chat-type constants/one-to-one-chat-type)
(first (rf/sub [:contacts/contact-two-names-by-identity chat-id]))
(= chat-type constants/community-chat-type)
(str (when emoji (str emoji " ")) "# " chat-name)
:else (str emoji chat-name))
{:keys [bio]} (rf/sub [:contacts/contact-by-identity chat-id])
online? (rf/sub [:visibility-status-updates/online? chat-id])
contact (when-not group-chat
(rf/sub [:contacts/contact-by-address chat-id]))
photo-path (rf/sub [:chats/photo-path chat-id])
top-margin (+ (safe-area/get-top)
messages.constants/top-bar-height
messages.constants/header-container-top-margin
32)
background-color (colors/theme-colors
(colors/resolve-color customization-color theme 20)
(colors/resolve-color customization-color theme 40)
theme)
bottom (reanimated/interpolate
distance-from-list-top
[0 messages.constants/header-container-top-margin]
[32 -4]
messages.constants/default-extrapolation-option)
background-opacity (reanimated/interpolate
distance-from-list-top
[messages.constants/header-container-top-margin
(+ messages.constants/header-animation-distance
messages.constants/header-container-top-margin)]
[1 0]
messages.constants/default-extrapolation-option)]
group-chat color description
last-message]} chat
display-name (cond
(= chat-type constants/one-to-one-chat-type)
(first (rf/sub [:contacts/contact-two-names-by-identity chat-id]))
(= chat-type constants/community-chat-type)
(str "# " chat-name)
:else (str emoji chat-name))
{:keys [bio]} (rf/sub [:contacts/contact-by-identity chat-id])
online? (rf/sub [:visibility-status-updates/online? chat-id])
contact (when-not group-chat
(rf/sub [:contacts/contact-by-address chat-id]))
photo-path (rf/sub [:chats/photo-path chat-id])
top-margin (+ (safe-area/get-top)
messages.constants/top-bar-height
messages.constants/header-container-top-margin
32)
background-color (colors/theme-colors
(colors/resolve-color customization-color theme 20)
(colors/resolve-color customization-color theme 40)
theme)
bottom (reanimated/interpolate
distance-from-list-top
[0 messages.constants/header-container-top-margin]
[32 -4]
messages.constants/default-extrapolation-option)
background-opacity (reanimated/interpolate
distance-from-list-top
[messages.constants/header-container-top-margin
(+ messages.constants/header-animation-distance
messages.constants/header-container-top-margin)]
[1 0]
messages.constants/default-extrapolation-option)]
[:<>
[reanimated/view
{:style (style/background-container background-color background-opacity top-margin)}]
@@ -254,18 +266,25 @@
:theme theme
:profile-picture photo-path
:group-chat group-chat
:color color}]
:color color
:emoji emoji
:chat-type chat-type
:chat-name chat-name
:last-message last-message}]
[chat-display-name
{:distance-from-list-top distance-from-list-top
:display-name display-name
:theme theme
:contact contact
:group-chat group-chat}]
:group-chat group-chat
:last-message last-message}]
[bio-and-actions
{:distance-from-list-top distance-from-list-top
:bio bio
:chat-id chat-id
:customization-color customization-color}]]]))
:customization-color customization-color
:description description
:last-message last-message}]]]))
(defn list-footer
[props]
@@ -1,5 +1,6 @@
(ns status-im.contexts.chat.messenger.messages.navigation.view
(:require
[clojure.string :as string]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[re-frame.db]
@@ -28,7 +29,7 @@
[:contacts/contact-two-names-by-identity
chat-id]))
(= chat-type constants/community-chat-type)
(str (when emoji (str emoji " ")) "# " chat-name)
(str "# " chat-name)
:else (str emoji chat-name))
online? (when-not group-chat (rf/sub [:visibility-status-updates/online? chat-id]))
photo-path (when-not group-chat (rf/sub [:chats/photo-path chat-id]))
@@ -53,7 +54,10 @@
{:customization-color color
:size :size-32
:picture photo-path
:override-theme :dark}]
:override-theme :dark
:emoji (when-not (string/blank? emoji)
(string/trim emoji))
:chat-name chat-name}]
[quo/user-avatar
{:full-name display-name
:online? online?
@@ -111,27 +115,29 @@
(defn view
[{:keys [distance-from-list-top chat-screen-layout-calculations-complete?]}]
(let [{:keys [chat-id chat-type] :as chat} (rf/sub [:chats/current-chat-chat-view])
all-loaded? (reanimated/use-shared-value false)
all-loaded-sub (rf/sub [:chats/all-loaded? chat-id])
top-insets (safe-area/get-top)
top-bar-height messages.constants/top-bar-height
navigation-view-height (+ top-bar-height top-insets)
navigation-buttons-opacity (worklets/navigation-buttons-complete-opacity
chat-screen-layout-calculations-complete?)
reached-threshold? (messages.worklets/use-messages-scrolled-to-threshold
distance-from-list-top
top-bar-height)
button-background (if reached-threshold? :photo :blur)]
(let [{:keys [chat-id chat-type last-message]
:as chat} (rf/sub [:chats/current-chat-chat-view])
all-loaded? (reanimated/use-shared-value false)
all-loaded-sub (rf/sub [:chats/all-loaded? chat-id])
top-insets (safe-area/get-top)
top-bar-height messages.constants/top-bar-height
navigation-view-height (+ top-bar-height top-insets)
navigation-buttons-opacity (worklets/navigation-buttons-complete-opacity
chat-screen-layout-calculations-complete?)
reached-threshold? (messages.worklets/use-messages-scrolled-to-threshold
distance-from-list-top
top-bar-height)
button-background (if reached-threshold? :photo :blur)]
(rn/use-effect (fn [] (reanimated/set-shared-value all-loaded? all-loaded-sub))
[all-loaded-sub])
[rn/view
{:style (style/navigation-view navigation-view-height messages.constants/pinned-banner-height)}
[animated-background-and-pinned-banner
{:chat-id chat-id
:navigation-view-height navigation-view-height
:distance-from-list-top distance-from-list-top
:all-loaded? all-loaded?}]
(when (seq last-message)
[animated-background-and-pinned-banner
{:chat-id chat-id
:navigation-view-height navigation-view-height
:distance-from-list-top distance-from-list-top
:all-loaded? all-loaded?}])
[rn/view {:style (style/header-container top-insets top-bar-height)}
[reanimated/view {:style (style/button-animation-container navigation-buttons-opacity)}
[quo/button
@@ -130,6 +130,9 @@
[cofx chat-id]
(navigation/show-bottom-sheet
cofx
{:content (fn [] [pinned-messages-menu/view
{:chat-id chat-id
:disable-message-long-press? (not= :chat (get-in cofx [:db :view-id]))}])}))
{:padding-bottom-override 21
:content (fn [] [pinned-messages-menu/view
{:chat-id chat-id
:disable-message-long-press? (not= :chat
(get-in cofx
[:db :view-id]))}])}))
@@ -31,5 +31,5 @@
(def divider
{:padding-horizontal 20
:margin-top 16
:margin-top 12
:margin-bottom 8})
@@ -53,7 +53,8 @@
[{:keys [title]}]
(when-not (= title no-title)
[quo/divider-label
{:container-style style/divider}
{:container-style style/divider
:tight? false}
title]))
(defn key-fn
@@ -7,7 +7,7 @@
[bottom-inset]
{:left 0
:right 0
:height (+ bottom-inset (if platform/ios? 65 85))
:height (+ bottom-inset (if platform/ios? 51 85))
:position :absolute
:bottom 0})
@@ -73,19 +73,25 @@
(defn check-permissions-to-join-for-selection
[{:keys [db]} [community-id addresses]]
(if (empty? addresses)
;; When there are no addresses we can't just check permissions, otherwise
;; status-go will consider all possible addresses and the user will see the
;; incorrect highest permission role.
{:db (update db :communities/permissions-checks-for-selection dissoc community-id)}
{:db (assoc-in db [:communities/permissions-checks-for-selection community-id :checking?] true)
:fx [[:json-rpc/call
[{:method :wakuext_checkPermissionsToJoinCommunity
:params [{:communityId community-id :addresses addresses}]
:on-success [:communities/check-permissions-to-join-during-selection-success
community-id]
:on-error [:communities/check-permissions-to-join-during-selection-failure
community-id addresses]}]]]}))
(let [request-id (rand-int 10000000)]
(if (empty? addresses)
;; When there are no addresses we can't just check permissions, otherwise
;; status-go will consider all possible addresses and the user will see the
;; incorrect highest permission role.
{:db (update db :communities/permissions-checks-for-selection dissoc community-id)}
{:db (update-in db
[:communities/permissions-checks-for-selection community-id]
assoc
:checking? true
:latest-request-id request-id)
:fx [[:json-rpc/call
[{:method :wakuext_checkPermissionsToJoinCommunity
:params [{:communityId community-id :addresses addresses}]
:id request-id
:on-success [:communities/check-permissions-to-join-during-selection-success
community-id]
:on-error [:communities/check-permissions-to-join-during-selection-failure
community-id addresses]}]]]})))
;; This event should be used to check permissions temporarily because it won't
;; mutate the state `:communities/permissions-check` (used by many other
@@ -94,12 +100,14 @@
check-permissions-to-join-for-selection)
(rf/reg-event-fx :communities/check-permissions-to-join-during-selection-success
(fn [{:keys [db]} [community-id result]]
{:db (assoc-in db
[:communities/permissions-checks-for-selection community-id]
{:checking? false
:based-on-client-selection? true
:check result})}))
(fn [{:keys [db]} [community-id result request-id]]
(when (= (get-in db [:communities/permissions-checks-for-selection community-id :latest-request-id])
request-id)
{:db (assoc-in db
[:communities/permissions-checks-for-selection community-id]
{:checking? false
:based-on-client-selection? true
:check result})})))
(rf/reg-event-fx :communities/check-permissions-to-join-during-selection-failure
(fn [_ [community-id addresses error]]
@@ -0,0 +1,48 @@
(ns status-im.contexts.communities.actions.invite-contacts.style
(:require
[quo.foundations.colors :as colors]
[react-native.safe-area :as safe-area]))
(def contact-selection-heading
{:flex-direction :row
:justify-content :space-between
:align-items :flex-end
:margin-top 24
:margin-bottom 16})
(def chat-button
{:position :absolute
:bottom (safe-area/get-bottom)
:left 20
:right 20})
(defn no-contacts
[]
{:margin-bottom (+ 96 (safe-area/get-bottom))
:flex 1
:justify-content :center
:align-items :center})
(def context-tag
{:align-self :flex-start
:margin-top -8
:margin-bottom 12})
(def no-contacts-text
{:margin-bottom 2
:margin-top 12})
(def no-contacts-button-container
{:margin-top 20
:margin-bottom 12})
(defn section-list-container-style
[theme]
{:padding-bottom 70
:background-color (colors/theme-colors colors/white
colors/neutral-95
theme)})
(defn invite-to-community-text
[theme]
{:color (colors/theme-colors colors/neutral-100 colors/white theme)})
@@ -0,0 +1,141 @@
(ns status-im.contexts.communities.actions.invite-contacts.view
(:require
[quo.core :as quo]
[quo.foundations.resources :as resources]
[quo.theme]
[react-native.core :as rn]
[react-native.gesture :as gesture]
[react-native.share :as share]
[status-im.common.contact-list-item.view :as contact-list-item]
[status-im.common.contact-list.view :as contact-list]
[status-im.contexts.communities.actions.invite-contacts.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- no-contacts-view
[{:keys [theme id]}]
(let [customization-color (rf/sub [:profile/customization-color])
{:keys [universal-profile-url]} (rf/sub [:profile/profile])
on-press-share-community (rn/use-callback
#(rf/dispatch [:communities/share-community-url-with-data
id]))
on-press-share-profile (rn/use-callback #(share/open {:url universal-profile-url})
[universal-profile-url])]
[rn/view
{:style (style/no-contacts)}
[rn/image {:source (resources/get-themed-image :no-contacts-to-chat theme)}]
[quo/text
{:weight :semi-bold
:size :paragraph-1
:style style/no-contacts-text}
(i18n/label :t/you-have-no-contacts)]
[quo/text
{:weight :regular
:size :paragraph-2}
(i18n/label :t/dont-yell-at-me)]
[quo/button
{:customization-color customization-color
:theme theme
:type :primary
:size 32
:container-style style/no-contacts-button-container
:on-press on-press-share-community}
(i18n/label :t/send-community-link)]
[quo/button
{:customization-color customization-color
:theme theme
:type :grey
:size 32
:on-press on-press-share-profile}
(i18n/label :t/invite-friends-to-status)]]))
(defn- contact-item
[{:keys [public-key]
:as item}]
(let [user-selected? (rf/sub [:is-contact-selected? public-key])
{:keys [id]} (rf/sub [:get-screen-params])
community-members-keys (set (keys (rf/sub [:communities/community-members id])))
community-member? (boolean (community-members-keys public-key))
on-toggle (fn []
(when-not community-member?
(if user-selected?
(rf/dispatch [:deselect-contact public-key])
(rf/dispatch [:select-contact public-key]))))]
[contact-list-item/contact-list-item
{:on-press on-toggle
:allow-multiple-presses? true
:accessory {:type :checkbox
:disabled? community-member?
:checked? (or community-member? user-selected?)
:on-check on-toggle}
:disabled? community-member?}
item]))
(defn view-internal
[{:keys [theme]}]
(fn []
(rn/use-unmount #(rf/dispatch [:group-chat/clear-contacts]))
(let [customization-color (rf/sub [:profile/customization-color])
{:keys [id]} (rf/sub [:get-screen-params])
contacts (rf/sub [:contacts/filtered-active-sections])
selected (rf/sub [:group/selected-contacts])
{:keys [name images]} (rf/sub [:communities/community id])
selected-contacts-count (count selected)
on-press (fn []
(rf/dispatch [:communities/share-community-confirmation-pressed
selected id])
(rf/dispatch [:navigate-back])
(rf/dispatch [:toasts/upsert
{:type :positive
:theme theme
:text (if (= 1 selected-contacts-count)
(i18n/label :t/one-user-was-invited)
(i18n/label
:t/n-users-were-invited
{:count selected-contacts-count}))}]))
{window-height :height} (rn/get-window)]
[rn/view {:style {:flex 1}}
[rn/view {:style {:padding-horizontal 20}}
[quo/button
{:type :grey
:size 32
:icon-only? true
:on-press #(rf/dispatch [:navigate-back])}
:i/close]
[rn/view {:style style/contact-selection-heading}
[quo/text
{:weight :semi-bold
:size :heading-1
:style (style/invite-to-community-text theme)}
(i18n/label :t/invite-to-community)]]
[quo/context-tag
{:type :community
:size 24
:community-logo (:thumbnail images)
:community-name name
:container-style style/context-tag}]]
(if (empty? contacts)
[no-contacts-view
{:theme theme
:id id}]
[:<>
[gesture/section-list
{:key-fn :public-key
:sticky-section-headers-enabled true
:sections contacts
:render-section-header-fn contact-list/contacts-section-header
:content-container-style (style/section-list-container-style theme)
:render-fn contact-item
:style {:height window-height}}]
(when (pos? selected-contacts-count)
[quo/button
{:type :primary
:accessibility-label :next-button
:customization-color customization-color
:container-style style/chat-button
:on-press on-press}
(if (= 1 selected-contacts-count)
(i18n/label :t/invite-1-user)
(i18n/label :t/invite-n-users {:count selected-contacts-count}))])])])))
(def view (quo.theme/with-theme view-internal))
@@ -1,5 +0,0 @@
(ns status-im.contexts.communities.actions.permissions-sheet.style)
(def container
{:flex 1
:padding-horizontal 20})
@@ -1,38 +1,12 @@
(ns status-im.contexts.communities.actions.permissions-sheet.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
[react-native.gesture :as gesture]
[status-im.contexts.communities.actions.permissions-sheet.style :as style]
[status-im.contexts.communities.utils :as communities.utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- role-text
[role]
(i18n/label
(communities.utils/role->translation-key role
:t/token-master)))
(defn- permission-view
[{:keys [tokens role satisfied?]}]
(when (seq tokens)
^{:key role}
[rn/view {:style {:margin-bottom 20}}
[quo/text {:weight :medium}
(if satisfied?
(i18n/label :t/you-eligible-to-join-as {:role (role-text role)})
(i18n/label :t/you-not-eligible-to-join-as {:role (role-text role)}))]
[quo/text {:size :paragraph-2 :style {:padding-bottom 8}}
(if satisfied?
(i18n/label :t/you-hodl)
(i18n/label :t/you-must-hold))]
[quo/token-requirement-list
{:tokens tokens
:container-style {:padding-horizontal 20}}]]))
(defn view
[id]
(let [permissions (rf/sub [:community/token-permissions id])]
[gesture/scroll-view {:style style/container}
(map permission-view permissions)]))
[gesture/scroll-view
[quo/community-detail-token-gating {:permissions permissions}]
]))
+106 -62
View File
@@ -1,11 +1,8 @@
(ns status-im.contexts.communities.events
(:require
[clojure.string :as string]
[legacy.status-im.data-store.chats :as data-store.chats]
[legacy.status-im.data-store.communities :as data-store.communities]
[legacy.status-im.mailserver.core :as mailserver]
[react-native.platform :as platform]
[react-native.share :as share]
[schema.core :as schema]
[status-im.constants :as constants]
[status-im.contexts.chat.messenger.messages.link-preview.events :as link-preview.events]
@@ -14,9 +11,9 @@
status-im.contexts.communities.actions.airdrop-addresses.events
status-im.contexts.communities.actions.community-options.events
status-im.contexts.communities.actions.leave.events
[status-im.contexts.communities.utils :as utils]
[status-im.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn handle-community
@@ -152,43 +149,83 @@
:on-success #(rf/dispatch [:communities/fetched-collapsed-categories-success %])
:on-error #(log/error "failed to fetch collapsed community categories" %)}]}))
(rf/reg-event-fx :communities/get-community-channel-share-data
(fn [_ [chat-id on-success]]
(let [{:keys [community-id channel-id]} (data-store.chats/decode-chat-id chat-id)]
{:json-rpc/call
[{:method "wakuext_shareCommunityChannelURLWithData"
:params [{:CommunityID community-id :ChannelID channel-id}]
:on-success on-success
:on-error (fn [err]
(log/error "failed to retrieve community channel url with data"
{:error err
:chat-id chat-id
:event :communities/get-community-channel-share-data}))}]})))
(defn update-previous-permission-addresses
[{:keys [db]} [community-id]]
(when community-id
(let [accounts (utils/sorted-non-watch-only-accounts db)
selected-permission-addresses (get-in db
[:communities community-id
:selected-permission-addresses])
selected-accounts (filter #(contains? selected-permission-addresses (:address %))
accounts)
current-airdrop-address (get-in db [:communities community-id :airdrop-address])
share-all-addresses? (get-in db [:communities community-id :share-all-addresses?])]
{:db (update-in db
[:communities community-id]
assoc
:previous-share-all-addresses? share-all-addresses?
:previous-permission-addresses selected-permission-addresses
:airdrop-address (if (contains? selected-permission-addresses
current-airdrop-address)
current-airdrop-address
(:address (first selected-accounts))))})))
(rf/reg-event-fx :communities/share-community-channel-url-with-data
(fn [_ [chat-id]]
(let [title (i18n/label :t/channel-on-status)
on-success (fn [url]
(share/open
(if platform/ios?
{:activityItemSources [{:placeholderItem {:type "text"
:content title}
:item {:default {:type "url"
:content url}}
:linkMetadata {:title title}}]}
{:title title
:subject title
:message url
:url url
:isNewTask true})))]
{:fx [[:dispatch [:communities/get-community-channel-share-data chat-id on-success]]]})))
(rf/reg-event-fx :communities/update-previous-permission-addresses
update-previous-permission-addresses)
(rf/reg-event-fx :communities/share-community-channel-url-qr-code
(fn [_ [chat-id]]
(let [on-success #(rf/dispatch [:open-modal :share-community-channel
{:chat-id chat-id
:url %}])]
{:fx [[:dispatch [:communities/get-community-channel-share-data chat-id on-success]]]})))
(defn toggle-selected-permission-address
[{:keys [db]} [address community-id]]
(let [selected-permission-addresses
(get-in db [:communities community-id :selected-permission-addresses])
updated-selected-permission-addresses
(if (contains? selected-permission-addresses address)
(disj selected-permission-addresses address)
(conj selected-permission-addresses address))]
{:db (assoc-in db
[:communities community-id :selected-permission-addresses]
updated-selected-permission-addresses)
:fx [(when community-id
[:dispatch
[:communities/check-permissions-to-join-community community-id
updated-selected-permission-addresses :based-on-client-selection]])]}))
(rf/reg-event-fx :communities/toggle-selected-permission-address
toggle-selected-permission-address)
(defn toggle-share-all-addresses
[{:keys [db]} [community-id]]
(let [share-all-addresses? (get-in db [:communities community-id :share-all-addresses?])
next-share-all-addresses? (not share-all-addresses?)
accounts (utils/sorted-non-watch-only-accounts db)
addresses (set (map :address accounts))]
{:db (update-in db
[:communities community-id]
assoc
:share-all-addresses? next-share-all-addresses?
:selected-permission-addresses addresses)
:fx [(when (and community-id next-share-all-addresses?)
[:dispatch
[:communities/check-permissions-to-join-community community-id
addresses :based-on-client-selection]])]}))
(rf/reg-event-fx :communities/toggle-share-all-addresses
toggle-share-all-addresses)
(rf/reg-event-fx :communities/reset-selected-permission-addresses
(fn [{:keys [db]} [community-id]]
(when community-id
{:db (update-in db
[:communities community-id]
assoc
:selected-permission-addresses
(get-in db [:communities community-id :previous-permission-addresses])
:share-all-addresses?
(get-in db [:communities community-id :previous-share-all-addresses?]))
:fx [[:dispatch [:communities/check-permissions-to-join-community community-id]]]})))
(rf/reg-event-fx :communities/set-airdrop-address
(fn [{:keys [db]} [address community-id]]
{:db (assoc-in db [:communities community-id :airdrop-address] address)}))
(defn community-fetched
[{:keys [db]} [community-id community]]
@@ -288,8 +325,8 @@
(rf/reg-event-fx :chat.ui/spectate-community spectate-community)
(rf/defn navigate-to-serialized-community
[_ {:keys [community-id]}]
(defn navigate-to-serialized-community
[community-id]
{:serialization/deserialize-and-compress-key
{:serialized-key community-id
:on-success #(rf/dispatch [:communities/navigate-to-community-overview %])
@@ -297,27 +334,30 @@
:error %
:community-id community-id})}})
(rf/reg-event-fx :communities/navigate-to-community-overview
(fn [{:keys [db] :as cofx} [deserialized-key]]
(let [current-view-id (:view-id db)]
(if (string/starts-with? deserialized-key constants/serialization-key)
(navigate-to-serialized-community cofx deserialized-key)
(rf/merge
cofx
{:fx [[:dispatch
[:communities/fetch-community
{:community-id deserialized-key
:update-last-opened-at? true}]]
[:dispatch [:navigate-to :community-overview deserialized-key]]
(when (get-in db [:communities deserialized-key :joined])
[:dispatch
[:activity-center.notifications/dismiss-community-overview deserialized-key]])]}
(when-not (or (= current-view-id :shell) (= current-view-id :communities-stack))
(navigation/pop-to-root :shell-stack)))))))
(rf/defn navigate-to-community-overview
[{:keys [db] :as cofx} [community-id]]
(let [current-view-id (:view-id db)]
(if (string/starts-with? community-id constants/serialization-key)
(navigate-to-serialized-community community-id)
(rf/merge
cofx
{:fx [[:dispatch
[:communities/fetch-community
{:community-id community-id
:update-last-opened-at? true}]]
[:dispatch [:navigate-to :community-overview community-id]]
(when (get-in db [:communities community-id :joined])
[:dispatch
[:activity-center.notifications/dismiss-community-overview community-id]])]}
(when-not (or (= current-view-id :shell) (= current-view-id :communities-stack))
(navigation/pop-to-root :shell-stack))))))
(rf/reg-event-fx :communities/navigate-to-community-chat
(fn [{:keys [db]} [chat-id pop-to-root?]]
(let [{:keys [community-id]} (get-in db [:chats chat-id])]
(rf/reg-event-fx :communities/navigate-to-community-overview navigate-to-community-overview)
(defn navigate-to-community-chat
[{:keys [db]} [chat-id pop-to-root? community-id]]
(let [community-id (or community-id (get-in db [:chats chat-id :community-id]))]
(merge
{:fx [(when community-id
[:dispatch
[:communities/fetch-community
@@ -325,7 +365,11 @@
:update-last-opened-at? true}]])
(if pop-to-root?
[:dispatch [:chat/pop-to-root-and-navigate-to-chat chat-id]]
[:dispatch [:chat/navigate-to-chat chat-id]])]})))
[:dispatch [:chat/navigate-to-chat chat-id]])]}
(when-not (get-in db [:chats chat-id :community-id])
{:db (assoc-in db [:chats chat-id :community-id] community-id)}))))
(rf/reg-event-fx :communities/navigate-to-community-chat navigate-to-community-chat)
(defn get-revealed-accounts
[{:keys [db]} [community-id on-success]]
@@ -11,7 +11,7 @@
(def community-tag-container
{:padding-horizontal screen-horizontal-padding
:margin-horizontal (- screen-horizontal-padding)
:margin-bottom 20})
:margin-bottom 16})
(def community-content-container
{:padding-horizontal screen-horizontal-padding})
@@ -64,4 +64,5 @@
{:border-radius 16
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-80)
:border-width 1
:padding-top 10})
:padding-top 10
:margin-bottom 106})
@@ -38,8 +38,9 @@
{:keys [name emoji muted? id mentions-count unread-messages? on-press locked? color] :as chat}]
(let [sheet-content [actions/chat-actions
(assoc chat
:chat-type constants/community-chat-type
:chat-id (str community-id id))
:community-id community-id
:chat-type constants/community-chat-type
:chat-id (str community-id id))
false]
notification (cond
muted? :mute
@@ -161,7 +162,7 @@
(i18n/label :t/you-eligible-to-join-as {:role highest-role-text})
(i18n/label :t/you-not-eligible-to-join))]
[info-button]]
[quo/text {:style {:padding-horizontal 12 :padding-bottom 18} :size :paragraph-2}
[quo/text {:style {:padding-horizontal 12 :padding-bottom 6} :size :paragraph-2}
(if can-request-access?
(i18n/label :t/you-hodl)
(i18n/label :t/you-must-hold))]
@@ -175,7 +176,7 @@
#(rf/dispatch [:open-modal :community-requests-to-join {:id id}]))
:accessibility-label :join-community-button
:customization-color color
:container-style {:margin-horizontal 12 :margin-top 12 :margin-bottom 12}
:container-style {:margin-horizontal 12 :margin-top 8 :margin-bottom 12}
:disabled? (not can-request-access?)
:icon-left (if can-request-access? :i/unlocked :i/locked)}
(i18n/label :t/request-to-join)]])))
@@ -0,0 +1,101 @@
(ns status-im.contexts.communities.sharing.events
(:require [legacy.status-im.data-store.chats :as data-store.chats]
[react-native.platform :as platform]
[react-native.share :as share]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(rf/reg-event-fx :communities/invite-people-pressed
(fn [{:keys [db]} [id]]
{:db (assoc db :communities/community-id-input id)
:fx [[:dispatch [:hide-bottom-sheet]]
[:dispatch [:open-modal :invite-people-community {:id id}]]]}))
(rf/reg-event-fx :communities/share-community-pressed
(fn [{:keys [db]} [id]]
{:db (assoc db :communities/community-id-input id)
:fx [[:dispatch [:hide-bottom-sheet]]
[:dispatch [:open-modal :legacy-invite-people-community {:id id}]]]}))
(rf/reg-event-fx :communities/share-community-confirmation-pressed
(fn [_ [users-public-keys community-id]]
{:fx [[:json-rpc/call
[{:method "wakuext_shareCommunity"
:params [{:communityId community-id
:users users-public-keys}]
:js-response true
:on-success [:sanitize-messages-and-process-response]
:on-error (fn [err]
(log/error {:message "failed to share community"
:community-id community-id
:err err}))}]]]}))
(rf/reg-event-fx :communities/share-community-channel-url-qr-code
(fn [_ [chat-id]]
(let [on-success #(rf/dispatch [:open-modal :share-community-channel
{:chat-id chat-id
:url %}])]
{:fx [[:dispatch [:communities/get-community-channel-share-data chat-id on-success]]]})))
(rf/reg-event-fx :communities/share-community-channel-url-with-data
(fn [_ [chat-id]]
(let [title (i18n/label :t/channel-on-status)
on-success (fn [url]
(rf/dispatch [:open-share
{:options (if platform/ios?
{:activityItemSources
[{:placeholderItem {:type :text
:content title}
:item {:default {:type :url
:content url}}
:linkMetadata {:title title}}]}
{:title title
:subject title
:message url
:url url
:isNewTask true})}]))]
{:fx [[:dispatch [:communities/get-community-channel-share-data chat-id on-success]]]})))
(rf/reg-event-fx :communities/get-community-channel-share-data
(fn [_ [chat-id on-success]]
(let [{:keys [community-id channel-id]} (data-store.chats/decode-chat-id chat-id)]
{:json-rpc/call
[{:method "wakuext_shareCommunityChannelURLWithData"
:params [{:CommunityID community-id :ChannelID channel-id}]
:on-success on-success
:on-error (fn [err]
(log/error "failed to retrieve community channel url with data"
{:error err
:chat-id chat-id
:event :communities/get-community-channel-share-data}))}]})))
(rf/reg-event-fx :communities/get-community-share-data
(fn [_ [community-id on-success]]
{:json-rpc/call
[{:method "wakuext_shareCommunityURLWithData"
:params [community-id]
:on-success on-success
:on-error (fn [err]
(log/error "failed to retrieve community url with data"
{:error err
:community-id community-id
:event :communities/get-community-share-data}))}]}))
(rf/reg-event-fx :communities/share-community-channel-url-with-data
(fn [_ [chat-id]]
(let [title (i18n/label :t/channel-on-status)
on-success (fn [url]
(share/open
(if platform/ios?
{:activityItemSources [{:placeholderItem {:type "text"
:content title}
:item {:default {:type "url"
:content url}}
:linkMetadata {:title title}}]}
{:title title
:subject title
:message url
:url url
:isNewTask true})))]
{:fx [[:dispatch [:communities/get-community-channel-share-data chat-id on-success]]]})))
@@ -49,12 +49,13 @@
[:screen/onboarding.enter-seed-phrase
:screen/onboarding.new-to-status]])}]
[rn/view {:style style/space-between-suboptions}]
[quo/small-option-card
{:variant :icon
:title (i18n/label :t/use-keycard)
:subtitle (i18n/label :t/use-keycard-subtitle)
:image (resources/get-image :use-keycard)
:on-press #(js/alert "TODO: to be implemented")}]]]))
(when config/show-not-implemented-features?
[quo/small-option-card
{:variant :icon
:title (i18n/label :t/use-keycard)
:subtitle (i18n/label :t/use-keycard-subtitle)
:image (resources/get-image :use-keycard)
:on-press #(js/alert "TODO: to be implemented")}])]]))
(defn getting-started-doc
[]
@@ -0,0 +1,68 @@
(ns status-im.contexts.preview.quo.community.community-detail-token-gating
(:require [quo.core :as quo]
[reagent.core :as reagent]
[status-im.common.resources :as resources]
[status-im.contexts.preview.quo.preview :as preview]))
(def descriptor
[{:key :role
:type :select
:options [{:key :one-role}
{:key :two-roles}
{:key :three-roles}
{:key :four-roles}]}])
(def roles
{:member
{:role 2
:role-text "Member"
:satisfied? true
:tokens
[[{:symbol "ETH" :sufficient? true :collectible? false :loading? false :amount 0.8 :img-src nil}]
[{:symbol "ETH" :sufficient? false :collectible? false :loading? false :amount 1 :img-src nil}
{:symbol "STT" :sufficient? false :collectible? false :loading? false :amount 10 :img-src nil}]]}
:admin
{:role 1
:role-text "Admin"
:satisfied? true
:tokens
[[{:symbol "ETH" :sufficient? true :collectible? false :loading? false :amount 2 :img-src nil}]]}
:token-master
{:role 5
:role-text "Token Master"
:satisfied? true
:tokens
[[{:symbol "TMANI"
:sufficient? true
:collectible? true
:loading? false
:amount nil
:img-src (resources/mock-images :collectible1)}]]}
:token-owner
{:role 6
:role-text "Token Owner"
:satisfied? true
:tokens
[[{:symbol "TOANI"
:sufficient? true
:collectible? true
:loading? false
:amount nil
:img-src (resources/mock-images :collectible)}]]}})
(def permissions
{:one-role [(:member roles)]
:two-roles [(:admin roles) (:member roles)]
:three-roles [(:token-master roles) (:admin roles) (:member roles)]
:four-roles [(:token-owner roles) (:token-master roles) (:admin roles) (:member roles)]})
(defn view
[]
(let [state (reagent/atom {:role :one-role})]
(fn []
[preview/preview-container
{:state state
:descriptor descriptor
:blur? (:blur? @state)
:show-blur-background? true}
[quo/community-detail-token-gating {:permissions ((:role @state) permissions)}]])))
@@ -39,6 +39,8 @@
channel-actions]
[status-im.contexts.preview.quo.community.community-card-view :as
community-card]
[status-im.contexts.preview.quo.community.community-detail-token-gating :as
community-detail-token-gating]
[status-im.contexts.preview.quo.community.community-membership-list-view
:as community-membership-list-view]
[status-im.contexts.preview.quo.community.community-stat :as community-stat]
@@ -260,6 +262,8 @@
:component color/view}]
:community [{:name :community-card-view
:component community-card/view}
{:name :community-detail-token-gating
:component community-detail-token-gating/view}
{:name :community-membership-list-view
:component community-membership-list-view/view}
{:name :community-stat
@@ -1,6 +1,7 @@
(ns status-im.contexts.preview.quo.wallet.network-link
(:require
[quo.core :as quo]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.contexts.preview.quo.preview :as preview]))
@@ -23,17 +24,21 @@
:options networks}
{:key :destination
:type :select
:options networks}])
:options networks}
{:key :width
:type :number}])
(defn view
[]
(let [state (reagent/atom {:shape :linear
:source :ethereum
:destination :optimism})]
:destination :optimism
:width 63})]
(fn []
[preview/preview-container
{:state state
:descriptor descriptor
:component-container-style {:padding-top 40
:align-items :center}}
[quo/network-link @state]])))
[rn/view {:style {:width (max (:width @state) 63)}}
[quo/network-link @state]]])))
@@ -48,7 +48,8 @@
(fn []
(rf/dispatch [:universal-links/generate-profile-url
{:public-key public-key
:on-success #(rn/sharing {:message %})}]))
:on-success #(rf/dispatch [:open-share
{:options {:message %}}])}]))
[public-key])
on-remove-contact (rn/use-callback
(fn []
@@ -1,6 +1,5 @@
(ns status-im.contexts.profile.contact.share.view
(:require [legacy.status-im.ui.components.list-selection :as list-selection]
[quo.core :as quo]
(:require [quo.core :as quo]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.qr-codes.view :as qr-codes]
@@ -19,7 +18,8 @@
abbreviated-url (rn/use-memo (fn []
(address/get-abbreviated-profile-url universal-profile-url))
[universal-profile-url])
on-share-press (rn/use-callback #(list-selection/open-share {:message universal-profile-url})
on-share-press (rn/use-callback #(rf/dispatch [:open-share
{:options {:message universal-profile-url}}])
[universal-profile-url])
on-copy-press (rn/use-callback (fn []
(rf/dispatch [:share/copy-text-and-show-toast
@@ -34,7 +34,7 @@
(when config/show-not-implemented-features?
{:title (i18n/label :t/dapps)
:on-press not-implemented/alert
:image-props :i/placeholder
:image-props :i/dapps
:image :icon
:blur? true
:action :arrow})
@@ -5,7 +5,6 @@
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[react-native.safe-area :as safe-area]
[status-im.common.not-implemented :as not-implemented]
[status-im.contexts.profile.settings.header.view :as settings.header]
[status-im.contexts.profile.settings.list-items :as settings.items]
[status-im.contexts.profile.settings.style :as style]
@@ -19,7 +18,7 @@
[rf/delay-render
[quo/category
{:list-type :settings
:container-style {:padding-bottom 0}
:container-style {:padding-bottom 12}
:blur? true
:data data}]])
@@ -62,7 +61,10 @@
:on-press #(rf/dispatch [:navigate-back])
:right-side [{:icon-name :i/qr-code
:on-press #(debounce/throttle-and-dispatch [:open-modal :share-shell] 1000)}
{:icon-name :i/share :on-press not-implemented/alert}]}]]
{:icon-name :i/share
:on-press #(rf/dispatch [:open-share
{:options {:message (:universal-profile-url
profile)}}])}]}]]
[rn/flat-list
{:key :list
:header [settings.header/view {:scroll-y scroll-y}]
@@ -22,13 +22,15 @@
(defn floating-button
[shared-values]
[quo/floating-shell-button
{:jump-to {:on-press #(animation/close-home-stack true)
:label (i18n/label :t/jump-to)
:customization-color (rf/sub [:profile/customization-color])}}
{:position :absolute
:bottom (utils/bottom-tabs-container-height)}
(:home-stack-opacity shared-values)])
(let [current-screen-id (rf/sub [:view-id])]
(when-not (= current-screen-id :settings)
[quo/floating-shell-button
{:jump-to {:on-press #(animation/close-home-stack true)
:label (i18n/label :t/jump-to)
:customization-color (rf/sub [:profile/customization-color])}}
{:position :absolute
:bottom (utils/bottom-tabs-container-height)}
(:home-stack-opacity shared-values)])))
(defn f-shell-stack
[]
@@ -71,13 +71,10 @@
(defn on-qr-code-scanned
[scanned-text]
(cond
(text-for-url-path? scanned-text router/community-with-data-path)
;; TODO: https://github.com/status-im/status-mobile/issues/18743
nil
(text-for-url-path? scanned-text router/channel-path)
;; TODO: https://github.com/status-im/status-mobile/issues/18743
nil
(or
(text-for-url-path? scanned-text router/community-with-data-path)
(text-for-url-path? scanned-text router/channel-path))
(debounce/debounce-and-dispatch [:universal-links/handle-url scanned-text] 300)
(text-for-url-path? scanned-text router/user-with-data-path)
(let [address (extract-id scanned-text)]
@@ -1,7 +1,6 @@
(ns status-im.contexts.shell.share.profile.view
(:require
[clojure.string :as string]
[legacy.status-im.ui.components.list-selection :as list-selection]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[react-native.core :as rn]
@@ -30,7 +29,7 @@
:width (- window-width (* style/screen-padding 2))
:qr-data universal-profile-url
:qr-data-label-shown abbreviated-url
:on-share-press #(list-selection/open-share {:message universal-profile-url})
:on-share-press #(rf/dispatch [:open-share {:options {:message universal-profile-url}}])
:on-text-press #(rf/dispatch [:share/copy-text-and-show-toast
{:text-to-copy universal-profile-url
:post-copy-message (i18n/label :t/link-to-profile-copied)}])
@@ -4,7 +4,6 @@
[quo.core :as quo]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.share :as share]
[reagent.core :as reagent]
[status-im.constants :as constants]
[status-im.contexts.shell.share.style :as style]
@@ -20,18 +19,18 @@
(defn- share-action
[address share-title]
(share/open
(if platform/ios?
{:activityItemSources [{:placeholderItem {:type "text"
:content address}
:item {:default {:type "text"
:content
address}}
:linkMetadata {:title share-title}}]}
{:title share-title
:subject share-title
:message address
:isNewTask true})))
(rf/dispatch [:open-share
{:options (if platform/ios?
{:activityItemSources [{:placeholderItem {:type :text
:content address}
:item {:default {:type :text
:content
address}}
:linkMetadata {:title share-title}}]}
{:title share-title
:subject share-title
:message address
:isNewTask true})}]))
(defn- open-preferences
[selected-networks account]
@@ -4,7 +4,6 @@
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[react-native.share :as share]
[reagent.core :as reagent]
[status-im.constants :as constants]
[status-im.contexts.wallet.account.share-address.style :as style]
@@ -18,18 +17,18 @@
(defn- share-action
[address share-title]
(share/open
(if platform/ios?
{:activityItemSources [{:placeholderItem {:type "text"
:content address}
:item {:default {:type "text"
:content
address}}
:linkMetadata {:title share-title}}]}
{:title share-title
:subject share-title
:message address
:isNewTask true})))
(rf/dispatch [:open-share
{:options (if platform/ios?
{:activityItemSources [{:placeholderItem {:type :text
:content address}
:item {:default {:type :text
:content
address}}
:linkMetadata {:title share-title}}]}
{:title share-title
:subject share-title
:message address
:isNewTask true})}]))
(defn- open-preferences
[selected-networks]
@@ -6,20 +6,10 @@
[status-im.contexts.wallet.account.style :as style]
[status-im.contexts.wallet.account.tabs.view :as tabs]
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
[status-im.contexts.wallet.common.temp :as temp]
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn buy-drawer
[]
[:<>
[quo/drawer-top {:title (i18n/label :t/buy-tokens)}]
[rn/flat-list
{:data temp/buy-tokens-list
:style {:padding-horizontal 8
:padding-bottom 8}
:render-fn quo/settings-item}]])
(def first-tab-id :assets)
(defn tabs-data
@@ -55,7 +45,7 @@
:flow-id :wallet-flow}]))
:receive-action #(rf/dispatch [:open-modal :screen/wallet.share-address {:status :receive}])
:buy-action #(rf/dispatch [:show-bottom-sheet
{:content buy-drawer}])
{:content buy-token/view}])
:bridge-action #(rf/dispatch [:wallet/start-bridge])}])
[quo/tabs
{:style style/tabs
@@ -47,7 +47,7 @@
:image :icon
:image-props :i/derivated-path
:action :button
:action-props {:on-press #(if (ff/enabled? :ff/wallet.edit-derivation-path)
:action-props {:on-press #(if (ff/enabled? ::ff/wallet.edit-derivation-path)
(rf/dispatch [:standard-auth/authorize
{:on-auth-success on-auth-success
:auth-button-label (i18n/label :t/continue)}])
@@ -50,7 +50,8 @@
[account-switcher/view
{:on-press #(rf/dispatch [:navigate-back])
:icon-name :i/arrow-left
:accessibility-label :top-bar}]
:accessibility-label :top-bar
:switcher-type :select-account}]
[quo/page-top {:title bridge-to-title}]
[rn/view style/content-container
[bridge-token-component (assoc mainnet :network-name :t/mainnet) account-token]]
@@ -15,6 +15,7 @@
:button-one-label (i18n/label :t/confirm-bridge)
:button-one-props {:icon-left :i/bridge}
:on-navigate-back (fn []
(rf/dispatch [:wallet/clean-disabled-from-networks])
(rf/dispatch [:navigate-back]))}]])
(def view (quo.theme/with-theme view-internal))
@@ -16,7 +16,8 @@
[rn/view {:style {:flex 1}}
[account-switcher/view
{:on-press #(rf/dispatch [:navigate-back])
:accessibility-label :top-bar}]
:accessibility-label :top-bar
:switcher-type :select-account}]
[quo/page-top {:title (i18n/label :t/bridge)}]
[quo/input
{:container-style style/input-container
@@ -1,52 +1,80 @@
(ns status-im.contexts.wallet.common.token-value.view
(:require [quo.core :as quo]
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
[status-im.feature-flags :as ff]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- action-buy
[]
{:icon :i/buy
:accessibility-label :buy
:label (i18n/label :t/buy)
:on-press #(rf/dispatch [:show-bottom-sheet
{:content buy-token/view}])
:right-icon :i/external})
(defn- action-send
[token-data]
{:icon :i/send
:accessibility-label :send
:label (i18n/label :t/send)
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:wallet/clean-send-data])
(rf/dispatch [:wallet/send-select-token
{:token token-data
:start-flow? true}]))})
(defn- action-receive
[]
{:icon :i/receive
:accessibility-label :receive
:label (i18n/label :t/receive)
:on-press #(rf/dispatch [:open-modal :screen/wallet.share-address {:status :receive}])})
(defn- action-bridge
[]
{:icon :i/bridge
:accessibility-label :bridge
:label (i18n/label :t/bridge)
:on-press #(js/alert "to be implemented")})
(defn- action-manage-tokens
[watch-only?]
{:icon :i/settings
:accessibility-label :settings
:label (i18n/label :t/manage-tokens)
:on-press #(js/alert "to be implemented")
:add-divider? (not watch-only?)})
(defn- action-hide
[]
{:icon :i/hide
:accessibility-label :hide
:label (i18n/label :t/hide)
:on-press #(js/alert "to be implemented")})
(defn token-value-drawer
[token]
[token watch-only?]
(let [token-data (first (rf/sub [:wallet/current-viewing-account-tokens-filtered (:token token)]))]
[:<>
[quo/action-drawer
[[{:icon :i/buy
:accessibility-label :buy
:label (i18n/label :t/buy)
:on-press #(js/alert "to be implemented")
:right-icon :i/external}
{:icon :i/send
:accessibility-label :send
:label (i18n/label :t/send)
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:wallet/clean-send-data])
(rf/dispatch [:wallet/send-select-token
{:token token-data
:start-flow? true}]))}
{:icon :i/receive
:accessibility-label :receive
:label (i18n/label :t/receive)
:on-press #(js/alert "to be implemented")}
{:icon :i/bridge
:accessibility-label :bridge
:label (i18n/label :t/bridge)
:on-press #(js/alert "to be implemented")}
{:icon :i/settings
:accessibility-label :settings
:label (i18n/label :t/manage-tokens)
:on-press #(js/alert "to be implemented")
:add-divider? true}
{:icon :i/hide
:accessibility-label :hide
:label (i18n/label :t/hide)
:on-press #(js/alert "to be implemented")}]]]]))
[quo/action-drawer
[(cond->> [(when (ff/enabled? ::ff/wallet.assets-modal-manage-tokens)
(action-manage-tokens watch-only?))
(when (ff/enabled? ::ff/wallet.assets-modal-hide)
(action-hide))]
(not watch-only?) (concat [(action-buy)
(action-send token-data)
(action-receive)
(action-bridge)]))]]))
(defn view
[item _ _ {:keys [watch-only?]}]
[quo/token-value
(cond-> item
(not watch-only?)
(or (not watch-only?) (ff/enabled? ::ff/wallet.long-press-watch-only-asset))
(assoc :on-long-press
#(rf/dispatch
[:show-bottom-sheet
{:content (fn [] [token-value-drawer item])
{:content (fn [] [token-value-drawer item watch-only?])
:selected-item (fn [] [quo/token-value item])}])))])
@@ -0,0 +1,18 @@
(ns status-im.contexts.wallet.common.utils.networks
(:require [clojure.string :as string]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.utils :as utils]))
(defn resolve-receiver-networks
[{:keys [prefix testnet-enabled? goerli-enabled?]}]
(let [prefix (if (string/blank? prefix)
constants/default-multichain-address-prefix
prefix)
prefix-seq (string/split prefix #":")]
(->> prefix-seq
(remove string/blank?)
(mapv
#(utils/network->chain-id
{:network %
:testnet-enabled? testnet-enabled?
:goerli-enabled? goerli-enabled?})))))

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