Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Communities: Show relevant tokens #18636

Merged
merged 3 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,22 @@
(render [account-permissions/view
{:account account
:token-details [{:token "SNT"
:amount "100"}]}])
:amount "100"
:type :token}]}])
(h/is-truthy (h/get-by-text "100 SNT")))

(h/test "render with multiple token details"
(render [account-permissions/view
{:account account
:token-details [{:token "SNT"
:amount "100"}
:amount "100"
:type :token}
{:token "ETH"
:amount "18"}
:amount "18"
:type :token}
{:token "BTM"
:amount "1000"}]}])
:amount "1000"
:type :token}]}])
(h/is-truthy (h/get-by-text "100 SNT"))
(h/is-truthy (h/get-by-text "18 ETH"))
(h/is-truthy (h/get-by-text "1000 BTM")))
Expand Down
16 changes: 9 additions & 7 deletions src/quo/components/wallet/account_permissions/view.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
[:address [:maybe :string]]
[:emoji [:maybe :string]]
[:customization-color [:maybe [:or :string :keyword]]]]]
[:token-details {:optional true} [:maybe [:vector required-tokens/?schema]]]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: :vector is rarely the schema we want to use because it will fail when lazy sequences are passed.

[:token-details {:optional true} [:maybe [:sequential required-tokens/?schema]]]
[:keycard? {:optional true} [:maybe :boolean]]
[:checked? {:optional true} [:maybe :boolean]]
[:disabled? {:optional true} [:maybe :boolean]]
Expand All @@ -47,14 +47,16 @@
(i18n/label :t/no-relevant-tokens)]

(let [token-length (dec (count tokens))]
(map-indexed (fn [idx {:keys [token amount]}]
(map-indexed (fn [idx {:keys [type token amount collectible-name collectible-img-src]}]
^{:key idx}
[required-tokens/view
{:container-style style/token-and-text
:type :token
:amount amount
:token token
:divider? (not= token-length idx)}])
{:container-style style/token-and-text
:type type
:amount amount
:token token
:collectible-img-src collectible-img-src
:collectible-name collectible-name
:divider? (not= token-length idx)}])
tokens)))]]))

(defn- view-internal
Expand Down
2 changes: 1 addition & 1 deletion src/quo/components/wallet/required_tokens/view.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
[:amount {:optional true} [:maybe [:or :string :int]]]
[:token {:optional true} [:maybe :string]]
[:token-img-src {:optional true} [:maybe :schema.common/image-source]]
[:collectible-img-src {:optional true} :schema.common/image-source]
[:collectible-img-src {:optional true} [:maybe :schema.common/image-source]]
[:collectible-name {:optional true} [:maybe :string]]
[:divider? {:optional true} [:maybe :boolean]]
[:theme :schema.common/theme]
Expand Down
2 changes: 1 addition & 1 deletion src/schema/common.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
{:min 1}
[:map
{:closed true}
[:method :string]
[:method [:or :keyword :string]]
[:params [:sequential :any]]
[:js-response {:optional true} :any]
[:on-success [:or fn? [:cat keyword? [:* :any]]]]
Expand Down
3 changes: 3 additions & 0 deletions src/status_im/constants.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@
community-token-permission-become-token-master
community-token-permission-become-token-owner})

(def ^:const community-token-type-erc20 1)
(def ^:const community-token-type-erc721 2)

;; Community rules for joining
(def ^:const community-rule-ens-only "ens-only")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
{:community-id id :password %}])}])
(rf/dispatch [:navigate-back]))

(defn f-view-internal
(defn view
[]
(let [{id :community-id} (rf/sub [:get-screen-params])
{:keys [name color images]} (rf/sub [:communities/community id])
Expand Down Expand Up @@ -83,7 +83,3 @@
:track-icon :i/face-id
:customization-color color
:on-complete #(join-community-and-navigate-back id)}]]]))

(defn view
[]
[:f> f-view-internal])
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
(ns status-im.contexts.communities.actions.addresses-for-permissions.events
(:require
[schema.core :as schema]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))

(defn get-permissioned-balances
[{[event-id _] :original-event} [community-id]]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: this is a nice way to grab the current event ID. Often the case when we want to log a descriptive error.

{:fx [[:json-rpc/call
[{:method :wakuext_getCommunityPermissionedBalances
:params [{:communityId community-id}]
:on-success [:communities/get-permissioned-balances-success community-id]
:on-error (fn [error]
(log/error "failed to get balances"
{:community-id community-id
:event event-id
:error error}))}]]]})

(schema/=> get-permissioned-balances
[:=>
[:catn
[:cofx :schema.re-frame/cofx]
[:args [:schema [:catn [:community-id :string]]]]]
[:map {:closed true}
[:fx
[:tuple
[:tuple [:= :json-rpc/call] :schema.common/rpc-call]]]]])

(rf/reg-event-fx :communities/get-permissioned-balances get-permissioned-balances)

(defn get-permissioned-balances-success
[{:keys [db]} [community-id response]]
{:db (assoc-in db [:communities/permissioned-balances community-id] response)})

(def ^:private ?account-address keyword?)

(def ^:private ?permissioned-balances-response
[:map-of
?account-address
[:sequential
[:map
[:type int?]
[:symbol string?]
[:decimals int?]
[:amount string?]]]])

(schema/=> get-permissioned-balances-success
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yqrashawn There's a strong pattern in the way we write schemas for event handlers. We could go just a small step further to help alleviate the verbosity for 95% of our use cases. Something to be discussed, but sharing here to give some visibility of the idea.

In the example below, the first argument to :=> would be the event args directly, the rest is almost always the same, and when they aren't, the dev would be able to use schema/=> instead.

(schema/event=> get-permissioned-balances-success
  [:=>
   [:catn
    [:community-id string?]
    [:response ?permissioned-balances-response]]
   [:map {:closed true}
    [:db map?]]])

cc @clauxx

Copy link
Member

@clauxx clauxx Jan 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be great to get rid of the boilerplate, I agree. Not as verbose as events, but for quo components we could do the same and just pass a prop map schema.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we definitely need this.

I'm thinking about something like ring's middleware and wrap- functions
Or functions start with assoc- or walk- to transform schema.
These kinds of functions are easier to document, understand and maintain.

I checked Malli's schema transformation the other day. There are these get assoc functions implemented for [LensSchema](https://github.com/metosin/malli/blob/1d6efc23fef01ab333f872b357760de61eac0a21/src/malli/util.cljc#L303), but I didn't manage to figure out what's lens schema. Maybe something like specter?

If that's what we are using. It would be great.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking about something like ring's middleware and wrap- functions

@yqrashawn Last week I wrote a malli transformer similar to malli.util/optional-keys just to get the feeling of it and to solve one pain point I have in status-mobile. No PR, just experimentation. Transformers are interesting also because they're just functions and we can compose them, which if I understood you correctly is something you would like to have.

OTOH, transformers may be a bazooka to solve a simple problem of just eliminating the most essential boilerplate to define event handler schemas. For that, we just need a vanilla Clojure function and no need to use specific Malli APIs.

but I didn't manage to figure out what's lens schema.

I only know what lenses are and why they exist in functional languages, but I have zero knowledge about this part of Malli. Looks like something to be explored!

Maybe something like specter?

I've never used specter outside playground stuff, but it's surely one of a kind Clojure library. A bit too alien if you ask me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OTOH, transformers may be a bazooka to solve a simple problem of just eliminating the most essential boilerplate to define event handler schemas. For that, we just need a vanilla Clojure function and no need to use specific Malli APIs.

Totally agree

[:=>
[:catn
[:cofx :schema.re-frame/cofx]
[:args
[:schema
[:catn
[:community-id string?]
[:response ?permissioned-balances-response]]]]]
map?])

(rf/reg-event-fx :communities/get-permissioned-balances-success get-permissioned-balances-success)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
(ns status-im.contexts.communities.actions.addresses-for-permissions.events-test
(:require
[cljs.test :refer [is deftest]]
[status-im.contexts.communities.actions.addresses-for-permissions.events :as sut]))

(def community-id "0x1")

(deftest get-permissioned-balances-test
(let [cofx {:db {}}]
(is (match? {:fx [[:json-rpc/call
[{:method :wakuext_getCommunityPermissionedBalances
:params [{:communityId community-id}]
:on-success [:communities/get-permissioned-balances-success community-id]
:on-error fn?}]]]}
(sut/get-permissioned-balances cofx [community-id])))))
Original file line number Diff line number Diff line change
Expand Up @@ -3,90 +3,114 @@
[quo.foundations.colors :as colors]
[react-native.core :as rn]
[status-im.common.not-implemented :as not-implemented]
[status-im.common.resources :as resources]
[status-im.constants :as constants]
[status-im.contexts.communities.actions.addresses-for-permissions.style :as style]
[status-im.contexts.communities.utils :as communities.utils]
[utils.i18n :as i18n]
[utils.money :as money]
[utils.re-frame :as rf]))

(defn- balances->components-props
[balances]
(for [{:keys [amount decimals type name] :as balance} balances]
(cond-> balance
true
(assoc :type
(condp = type
constants/community-token-type-erc20 :token
constants/community-token-type-erc721 :collectible
:token))

(= type constants/community-token-type-erc721)
(assoc :collectible-name name
:collectible-img-src (resources/get-mock-image :collectible))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: I'll create a follow-up issue to use the real collectible image. It just didn't seem important for this PR since there's a bunch of things going on in the status-go side.


(= type constants/community-token-type-erc20)
(assoc :amount (str (money/with-precision (money/token->unit amount decimals) 3))
:token (:symbol balance)))))

(defn- account-item
[item _ _ [selected-addresses community-id]]
[quo/account-permissions
{:account {:name (:name item)
:address (:address item)
:emoji (:emoji item)
:customization-color (:customization-color item)}
:token-details []
:checked? (contains? selected-addresses (:address item))
:on-change #(rf/dispatch [:communities/toggle-selected-permission-address
(:address item) community-id])
:container-style {:margin-bottom 8}}])
[{:keys [customization-color address name emoji]} _ _ [selected-addresses community-id]]
(let [balances (rf/sub [:communities/permissioned-balances-by-address community-id address])]
[quo/account-permissions
{:account {:name name
:address address
:emoji emoji
:customization-color customization-color}
:token-details (balances->components-props balances)
:checked? (contains? selected-addresses address)
:on-change #(rf/dispatch [:communities/toggle-selected-permission-address
address community-id])
:container-style {:margin-bottom 8}}]))

(defn view
[]
(let [{id :community-id} (rf/sub [:get-screen-params])
{:keys [name color images]} (rf/sub [:communities/community id])
{:keys [highest-permission-role]} (rf/sub [:community/token-gated-overview id])
accounts (rf/sub [:wallet/accounts-with-customization-color])
selected-addresses (rf/sub [:communities/selected-permission-addresses id])
highest-role-text
(i18n/label
(communities.utils/role->translation-key highest-permission-role))]
[rn/safe-area-view {:style style/container}
[quo/drawer-top
{:type :context-tag
:title (i18n/label :t/addresses-for-permissions)
:context-tag-type :community
:community-name name
:button-icon :i/info
:on-button-press not-implemented/alert
:community-logo (get-in images [:thumbnail :uri])
:customization-color color}]
(let [{id :community-id} (rf/sub [:get-screen-params])]
(rf/dispatch [:communities/get-permissioned-balances id])
(fn []
(let [{:keys [name color images]} (rf/sub [:communities/community id])
{:keys [highest-permission-role]} (rf/sub [:community/token-gated-overview id])
accounts (rf/sub [:wallet/accounts-with-customization-color])
selected-addresses (rf/sub [:communities/selected-permission-addresses id])
highest-role-text (i18n/label (communities.utils/role->translation-key
highest-permission-role))]
[rn/safe-area-view {:style style/container}
[quo/drawer-top
{:type :context-tag
:title (i18n/label :t/addresses-for-permissions)
:context-tag-type :community
:community-name name
:button-icon :i/info
:on-button-press not-implemented/alert
:community-logo (get-in images [:thumbnail :uri])
:customization-color color}]

[rn/flat-list
{:render-fn account-item
:render-data [selected-addresses id]
:content-container-style {:padding 20}
:key-fn :address
:data accounts}]
[rn/flat-list
{:render-fn account-item
:render-data [selected-addresses id]
:content-container-style {:padding 20}
:key-fn :address
:data accounts}]

(when (and highest-permission-role (seq selected-addresses))
[rn/view
{:style style/highest-role}
[quo/text
{:size :paragraph-2
:style {:color colors/neutral-50}}
(i18n/label :t/eligible-to-join-as)]
[quo/context-tag
{:type :icon
:icon :i/members
:size 24
:context highest-role-text}]])
(when (and highest-permission-role (seq selected-addresses))
[rn/view
{:style style/highest-role}
[quo/text
{:size :paragraph-2
:style {:color colors/neutral-50}}
(i18n/label :t/eligible-to-join-as)]
[quo/context-tag
{:type :icon
:icon :i/members
:size 24
:context highest-role-text}]])

(when (empty? selected-addresses)
[rn/view
{:style style/error-message}
[quo/icon
:i/info
{:color colors/danger-50
:size 16}]
[quo/text
{:size :paragraph-2
:style {:color colors/danger-50}}
(i18n/label :t/no-addresses-selected)]])
(when (empty? selected-addresses)
[rn/view
{:style style/error-message}
[quo/icon
:i/info
{:color colors/danger-50
:size 16}]
[quo/text
{:size :paragraph-2
:style {:color colors/danger-50}}
(i18n/label :t/no-addresses-selected)]])

[rn/view {:style style/buttons}
[quo/button
{:type :grey
:container-style {:flex 1}
:on-press (fn []
(rf/dispatch [:communities/reset-selected-permission-addresses id])
(rf/dispatch [:navigate-back]))}
(i18n/label :t/cancel)]
[quo/button
{:container-style {:flex 1}
:customization-color color
:disabled? (empty? selected-addresses)
:on-press (fn []
(rf/dispatch [:communities/update-previous-permission-addresses id])
[rn/view {:style style/buttons}
[quo/button
{:type :grey
:container-style {:flex 1}
:on-press (fn []
(rf/dispatch [:communities/reset-selected-permission-addresses id])
(rf/dispatch [:navigate-back]))}
(i18n/label :t/confirm-changes)]]]))
(i18n/label :t/cancel)]
[quo/button
{:container-style {:flex 1}
:customization-color color
:disabled? (empty? selected-addresses)
:on-press (fn []
(rf/dispatch [:communities/update-previous-permission-addresses id])
(rf/dispatch [:navigate-back]))}
(i18n/label :t/confirm-changes)]]]))))
1 change: 1 addition & 0 deletions src/status_im/contexts/communities/events.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
[schema.core :as schema]
[status-im.constants :as constants]
[status-im.contexts.chat.messenger.messages.link-preview.events :as link-preview.events]
status-im.contexts.communities.actions.addresses-for-permissions.events
status-im.contexts.communities.actions.community-options.events
status-im.contexts.communities.actions.leave.events
[status-im.navigation.events :as navigation]
Expand Down
5 changes: 5 additions & 0 deletions src/status_im/subs/communities.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,11 @@
(fn [communities [_ community-id]]
(get-in communities [community-id :intro-message])))

(re-frame/reg-sub :communities/permissioned-balances-by-address
:<- [:communities/permissioned-balances]
(fn [balances [_ community-id account-address]]
(get-in balances [community-id (keyword account-address)])))

(re-frame/reg-sub
:communities/selected-permission-addresses
(fn [[_ community-id]]
Expand Down
1 change: 1 addition & 0 deletions src/status_im/subs/root.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@
(reg-root-key-sub :communities/collapsed-categories :communities/collapsed-categories)
(reg-root-key-sub :communities/selected-tab :communities/selected-tab)
(reg-root-key-sub :contract-communities :contract-communities)
(reg-root-key-sub :communities/permissioned-balances :communities/permissioned-balances)

;;activity center
(reg-root-key-sub :activity-center :activity-center)
Expand Down
Loading