From 1acf8f0d07562d3e9b9ca67d6b44a6ce8804f3e2 Mon Sep 17 00:00:00 2001 From: yqrashawn Date: Fri, 26 Jan 2024 15:38:10 +0800 Subject: [PATCH] feat: show highest permission role when join community (#18497) * feat: get revealed accounts for joined community Signed-off-by: yqrashawn * Show token requirements only when membership permissions are present. - Add membership-permissions? to community state. * feat: show highest permission role in overview and req to join Signed-off-by: yqrashawn * feat: get highest perm role in addr for perms Signed-off-by: yqrashawn * feat: ui for address for perm drawer * test: unit test and spec for handle-community Signed-off-by: yqrashawn * fix: for #18581 * refactor: based on PR feedback - move <-rpc to data store - `role->translation-key` - highest-permission-role-text -> highest-role-text - filter out nils for handle community test Signed-off-by: yqrashawn --------- Signed-off-by: yqrashawn Co-authored-by: Ajay Sivan --- .../status_im/data_store/communities.cljs | 68 +++++- src/quo/components/utilities/token/view.cljs | 2 +- .../messenger/messages/transport/events.cljs | 3 +- .../actions/accounts_selection/view.cljs | 21 +- .../addresses_for_permissions/style.cljs | 7 + .../addresses_for_permissions/view.cljs | 24 +- .../contexts/communities/events.cljs | 213 ++++++++++-------- .../contexts/communities/events_test.cljs | 77 +++++++ .../contexts/communities/overview/events.cljs | 19 +- .../contexts/communities/overview/view.cljs | 26 ++- src/status_im/contexts/communities/utils.cljs | 13 ++ src/status_im/subs/communities.cljs | 72 ++++-- src/status_im/subs/communities_test.cljs | 72 +++--- translations/en.json | 7 +- 14 files changed, 439 insertions(+), 185 deletions(-) create mode 100644 src/status_im/contexts/communities/utils.cljs diff --git a/src/legacy/status_im/data_store/communities.cljs b/src/legacy/status_im/data_store/communities.cljs index 45d6be4133..3553b4989a 100644 --- a/src/legacy/status_im/data_store/communities.cljs +++ b/src/legacy/status_im/data_store/communities.cljs @@ -1,5 +1,8 @@ (ns legacy.status-im.data-store.communities - (:require [clojure.set :as set])) + (:require + [clojure.set :as set] + [clojure.walk :as walk] + [status-im.constants :as constants])) (defn rpc->channel-permissions [rpc-channels-permissions] @@ -7,3 +10,66 @@ (fn [{:keys [viewAndPostPermissions viewOnlyPermissions]}] {:view-only (set/rename-keys viewOnlyPermissions {:satisfied :satisfied?}) :view-and-post (set/rename-keys viewAndPostPermissions {:satisfied :satisfied?})}))) + +(defn <-revealed-accounts-rpc + [accounts] + (mapv + #(set/rename-keys % {:isAirdropAddress :airdrop-address?}) + (js->clj accounts :keywordize-keys true))) + +(defn <-request-to-join-community-rpc + [r] + (set/rename-keys r + {:communityId :community-id + :publicKey :public-key + :chatId :chat-id})) + +(defn <-requests-to-join-community-rpc + [requests key-fn] + (reduce #(assoc %1 (key-fn %2) (<-request-to-join-community-rpc %2)) {} requests)) + +(defn <-chats-rpc + [chats] + (reduce-kv (fn [acc k v] + (assoc acc + (name k) + (-> v + (assoc :can-post? (:canPost v)) + (dissoc :canPost) + (update :members walk/stringify-keys)))) + {} + chats)) + +(defn <-categories-rpc + [categ] + (reduce-kv #(assoc %1 (name %2) %3) {} categ)) + +(defn <-rpc + [c] + (-> c + (set/rename-keys {:canRequestAccess :can-request-access? + :canManageUsers :can-manage-users? + :canDeleteMessageForEveryone :can-delete-message-for-everyone? + :canJoin :can-join? + :requestedToJoinAt :requested-to-join-at + :isMember :is-member? + :adminSettings :admin-settings + :tokenPermissions :token-permissions + :communityTokensMetadata :tokens-metadata + :introMessage :intro-message + :muteTill :muted-till}) + (update :admin-settings + set/rename-keys + {:pinMessageAllMembersEnabled :pin-message-all-members-enabled?}) + (update :members walk/stringify-keys) + (update :chats <-chats-rpc) + (update :token-permissions seq) + (update :categories <-categories-rpc) + (assoc :membership-permissions? + (some #(= (:type %) constants/community-token-permission-become-member) + (vals (:tokenPermissions c)))) + (assoc :token-images + (reduce (fn [acc {sym :symbol image :image}] + (assoc acc sym image)) + {} + (:communityTokensMetadata c))))) diff --git a/src/quo/components/utilities/token/view.cljs b/src/quo/components/utilities/token/view.cljs index 8fe7fb023c..88bca5a896 100644 --- a/src/quo/components/utilities/token/view.cljs +++ b/src/quo/components/utilities/token/view.cljs @@ -15,7 +15,7 @@ [:token {:optional true} [:or keyword? string?]] [:style {:optional true} map?] ;; Ignores `token` and uses this as parameter to `rn/image`'s source. - [:image-source {:optional true} [:or :schema.common/image-source :string]]]] + [:image-source {:optional true} [:maybe [:or :schema.common/image-source :string]]]]] :any]) (defn- size->number diff --git a/src/status_im/contexts/chat/messenger/messages/transport/events.cljs b/src/status_im/contexts/chat/messenger/messages/transport/events.cljs index 6e785375bc..38cd1c5695 100644 --- a/src/status_im/contexts/chat/messenger/messages/transport/events.cljs +++ b/src/status_im/contexts/chat/messenger/messages/transport/events.cljs @@ -6,6 +6,7 @@ [legacy.status-im.chat.models.message :as models.message] [legacy.status-im.data-store.activities :as data-store.activities] [legacy.status-im.data-store.chats :as data-store.chats] + [legacy.status-im.data-store.communities :as data-store.communities] [legacy.status-im.data-store.invitations :as data-store.invitations] [legacy.status-im.group-chats.core :as models.group] [legacy.status-im.multiaccounts.update.core :as update.core] @@ -127,7 +128,7 @@ (seq requests-to-join-community) (let [requests (->> requests-to-join-community types/js->clj - (map communities/<-request-to-join-community-rpc))] + (map data-store.communities/<-request-to-join-community-rpc))] (js-delete response-js "requestsToJoinCommunity") (rf/merge cofx (process-next response-js sync-handler) diff --git a/src/status_im/contexts/communities/actions/accounts_selection/view.cljs b/src/status_im/contexts/communities/actions/accounts_selection/view.cljs index 1dc6fb3517..93ea436b81 100644 --- a/src/status_im/contexts/communities/actions/accounts_selection/view.cljs +++ b/src/status_im/contexts/communities/actions/accounts_selection/view.cljs @@ -6,25 +6,28 @@ [status-im.common.password-authentication.view :as password-authentication] [status-im.contexts.communities.actions.accounts-selection.style :as style] [status-im.contexts.communities.actions.community-rules.view :as community-rules] + [status-im.contexts.communities.utils :as communities.utils] [utils.i18n :as i18n] [utils.re-frame :as rf])) (defn- join-community-and-navigate-back [id] - (rf/dispatch [:password-authentication/show - {:content (fn [] [password-authentication/view])} + (rf/dispatch [:password-authentication/show {:content (fn [] [password-authentication/view])} {:label (i18n/label :t/join-open-community) :on-press #(rf/dispatch [:communities/request-to-join-with-addresses - {:community-id id - :password %}])}]) + {:community-id id :password %}])}]) (rf/dispatch [:navigate-back])) (defn f-view-internal [] - (let [{id :community-id} (rf/sub [:get-screen-params]) - {:keys [name color images]} (rf/sub [:communities/community id]) - airdrop-account (rf/sub [:communities/airdrop-account id]) - selected-accounts (rf/sub [:communities/selected-permission-accounts id])] + (let [{id :community-id} (rf/sub [:get-screen-params]) + {:keys [name color images]} (rf/sub [:communities/community id]) + airdrop-account (rf/sub [:communities/airdrop-account id]) + selected-accounts (rf/sub [:communities/selected-permission-accounts id]) + {:keys [highest-permission-role]} (rf/sub [:community/token-gated-overview id]) + highest-role-text (i18n/label (communities.utils/role->translation-key + highest-permission-role + :t/member))] [rn/view {:style style/container} [quo/page-nav {:text-align :left @@ -48,7 +51,7 @@ (i18n/label :t/address-to-share)] [quo/category {:list-type :settings - :data [{:title (i18n/label :t/join-as-a-member) + :data [{:title (i18n/label :t/join-as-a {:role highest-role-text}) :on-press #(rf/dispatch [:open-modal :addresses-for-permissions {:community-id id}]) :description :text diff --git a/src/status_im/contexts/communities/actions/addresses_for_permissions/style.cljs b/src/status_im/contexts/communities/actions/addresses_for_permissions/style.cljs index f226945c26..727dddc294 100644 --- a/src/status_im/contexts/communities/actions/addresses_for_permissions/style.cljs +++ b/src/status_im/contexts/communities/actions/addresses_for_permissions/style.cljs @@ -13,3 +13,10 @@ :gap 4 :justify-content :center :margin-bottom 8}) + +(def highest-role + {:flex-direction :row + :gap 4 + :justify-content :center + :align-items :center + :margin-bottom 8}) diff --git a/src/status_im/contexts/communities/actions/addresses_for_permissions/view.cljs b/src/status_im/contexts/communities/actions/addresses_for_permissions/view.cljs index b26e8c946d..f0fd1a8688 100644 --- a/src/status_im/contexts/communities/actions/addresses_for_permissions/view.cljs +++ b/src/status_im/contexts/communities/actions/addresses_for_permissions/view.cljs @@ -4,6 +4,7 @@ [react-native.core :as rn] [status-im.common.not-implemented :as not-implemented] [status-im.contexts.communities.actions.addresses-for-permissions.style :as style] + [status-im.contexts.communities.utils :as communities.utils] [utils.i18n :as i18n] [utils.re-frame :as rf])) @@ -22,10 +23,14 @@ (defn view [] - (let [{id :community-id} (rf/sub [:get-screen-params]) + (let [{id :community-id} (rf/sub [:get-screen-params]) {:keys [name color images]} (rf/sub [:communities/community id]) - accounts (rf/sub [:wallet/accounts-with-customization-color]) - selected-addresses (rf/sub [:communities/selected-permission-addresses 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 @@ -43,6 +48,19 @@ :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 {:role ""})] + [quo/context-tag + {:type :icon + :icon :i/members + :size 24 + :context highest-role-text}]]) + (when (empty? selected-addresses) [rn/view {:style style/error-message} diff --git a/src/status_im/contexts/communities/events.cljs b/src/status_im/contexts/communities/events.cljs index 16fe8a85b5..f1f90ef266 100644 --- a/src/status_im/contexts/communities/events.cljs +++ b/src/status_im/contexts/communities/events.cljs @@ -1,96 +1,56 @@ (ns status-im.contexts.communities.events - (:require [clojure.set :as set] - [clojure.string :as string] - [clojure.walk :as walk] - [legacy.status-im.data-store.chats :as data-store.chats] - [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] - status-im.contexts.communities.actions.community-options.events - status-im.contexts.communities.actions.leave.events - [status-im.navigation.events :as navigation] - [taoensso.timbre :as log] - [utils.i18n :as i18n] - [utils.re-frame :as rf])) + (: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] + status-im.contexts.communities.actions.community-options.events + status-im.contexts.communities.actions.leave.events + [status-im.navigation.events :as navigation] + [taoensso.timbre :as log] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) -(defn <-request-to-join-community-rpc - [r] - (set/rename-keys r - {:communityId :community-id - :publicKey :public-key - :chatId :chat-id})) +(defn handle-community + [{:keys [db]} [community-js]] + (when community-js + (let [{:keys [token-permissions + token-permissions-check joined id] + :as community} (data-store.communities/<-rpc community-js) + has-channel-perm? (fn [id-perm-tuple] + (let [{:keys [type]} (second id-perm-tuple)] + (or (= type constants/community-token-permission-can-view-channel) + (= + type + constants/community-token-permission-can-view-and-post-channel))))] + {:db (assoc-in db [:communities id] community) + :fx [[:dispatch [:communities/initialize-permission-addresses id]] + (when (not joined) + [:dispatch [:chat.ui/spectate-community id]]) + (when (nil? token-permissions-check) + [:dispatch [:communities/check-permissions-to-join-community id]]) + (when (some has-channel-perm? token-permissions) + [:dispatch [:communities/check-all-community-channels-permissions id]]) + (when joined + [:dispatch [:communities/get-revealed-accounts id]])]}))) -(defn <-requests-to-join-community-rpc - [requests key-fn] - (reduce #(assoc %1 (key-fn %2) (<-request-to-join-community-rpc %2)) {} requests)) +(rf/reg-event-fx :communities/handle-community handle-community) -(defn <-chats-rpc - [chats] - (reduce-kv (fn [acc k v] - (assoc acc - (name k) - (-> v - (assoc :can-post? (:canPost v)) - (dissoc :canPost) - (update :members walk/stringify-keys)))) - {} - chats)) - -(defn <-categories-rpc - [categ] - (reduce-kv #(assoc %1 (name %2) %3) {} categ)) - -(defn <-rpc - [c] - (-> c - (set/rename-keys {:canRequestAccess :can-request-access? - :canManageUsers :can-manage-users? - :canDeleteMessageForEveryone :can-delete-message-for-everyone? - :canJoin :can-join? - :requestedToJoinAt :requested-to-join-at - :isMember :is-member? - :adminSettings :admin-settings - :tokenPermissions :token-permissions - :communityTokensMetadata :tokens-metadata - :introMessage :intro-message - :muteTill :muted-till}) - (update :admin-settings - set/rename-keys - {:pinMessageAllMembersEnabled :pin-message-all-members-enabled?}) - (update :members walk/stringify-keys) - (update :chats <-chats-rpc) - (update :token-permissions seq) - (update :categories <-categories-rpc) - (assoc :token-images - (reduce (fn [acc {sym :symbol image :image}] - (assoc acc sym image)) - {} - (:communityTokensMetadata c))))) - -(rf/reg-event-fx :communities/handle-community - (fn [{:keys [db]} - [community-js]] - (when community-js - (let [{:keys [token-permissions - token-permissions-check joined id] - :as community} (<-rpc community-js) - has-channel-perm? (fn [id-perm-tuple] - (let [{:keys [type]} (second id-perm-tuple)] - (or (= type constants/community-token-permission-can-view-channel) - (= - type - constants/community-token-permission-can-view-and-post-channel))))] - {:db (assoc-in db [:communities id] community) - :fx [[:dispatch [:communities/initialize-permission-addresses id]] - (when (not joined) - [:dispatch [:chat.ui/spectate-community id]]) - (when (nil? token-permissions-check) - [:dispatch [:communities/check-permissions-to-join-community id]]) - (when (some has-channel-perm? token-permissions) - [:dispatch [:communities/check-all-community-channels-permissions id]])]})))) +(schema/=> handle-community + [:=> + [:catn + [:cofx :schema.re-frame/cofx] + [:args + [:schema [:catn [:community-js map?]]]]] + [:maybe + [:map + [:db [:map [:communities map?]]] + [:fx vector?]]]]) (rf/defn handle-removed-chats [{:keys [db]} chat-ids] @@ -164,7 +124,7 @@ (fn [{:keys [db]} [requests]] {:db (assoc db :communities/my-pending-requests-to-join - (<-requests-to-join-community-rpc requests :communityId))})) + (data-store.communities/<-requests-to-join-community-rpc requests :communityId))})) (rf/reg-event-fx :communities/get-user-requests-to-join (fn [_] @@ -240,12 +200,19 @@ (defn toggle-selected-permission-address [{:keys [db]} [address community-id]] - {:db (update-in db - [:communities community-id :selected-permission-addresses] - (fn [selected-addresses] - (if (contains? selected-addresses address) - (disj selected-addresses address) - (conj selected-addresses address))))}) + (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) @@ -255,7 +222,8 @@ (when community-id {:db (assoc-in db [:communities community-id :selected-permission-addresses] - (get-in db [:communities community-id :previous-permission-addresses]))}))) + (get-in db [:communities community-id :previous-permission-addresses])) + :fx [[:dispatch [:communities/check-permissions-to-join-community community-id]]]}))) (rf/reg-event-fx :communities/share-community-channel-url-with-data (fn [_ [chat-id]] @@ -405,3 +373,54 @@ (if pop-to-root? [:dispatch [:chat/pop-to-root-and-navigate-to-chat chat-id]] [:dispatch [:chat/navigate-to-chat chat-id]])]}))) + +(defn get-revealed-accounts + [{:keys [db]} [community-id]] + (let [{:keys [joined fetching-revealed-accounts] + :as community} (get-in db [:communities community-id])] + (when (and community joined (not fetching-revealed-accounts)) + {:db (assoc-in db [:communities community-id :fetching-revealed-accounts] true) + :json-rpc/call + [{:method "wakuext_getRevealedAccounts" + :params [community-id (get-in db [:profile/profile :public-key])] + :js-response true + :on-success [:communities/get-revealed-accounts-success community-id] + :on-error (fn [err] + (log/error {:message "failed to fetch revealed accounts" + :community-id community-id + :err err}) + (rf/dispatch [:communities/get-revealed-accounts-failed community-id]))}]}))) + +(rf/reg-event-fx :communities/get-revealed-accounts get-revealed-accounts) + +(schema/=> get-revealed-accounts + [:=> + [:catn + [:cofx :schema.re-frame/cofx] + [:args + [:schema [:catn [:community-id [:? :string]]]]]] + [:maybe + [:map + [:db map?] + [:json-rpc/call :schema.common/rpc-call]]]]) + +(rf/reg-event-fx :communities/get-revealed-accounts-success + (fn [{:keys [db]} [community-id revealed-accounts-js]] + (when-let [community (get-in db [:communities community-id])] + (let [revealed-accounts + (reduce + (fn [acc {:keys [address] :as revealed-account}] + (assoc acc address (dissoc revealed-account :address))) + {} + (data-store.communities/<-revealed-accounts-rpc revealed-accounts-js)) + + community-with-revealed-accounts + (-> community + (assoc :revealed-accounts revealed-accounts) + (dissoc :fetching-revealed-accounts))] + {:db (assoc-in db [:communities community-id] community-with-revealed-accounts)})))) + +(rf/reg-event-fx :communities/get-revealed-accounts-failed + (fn [{:keys [db]} [community-id]] + (when (get-in db [:communities community-id]) + {:db (update-in db [:communities community-id] dissoc :fetching-revealed-accounts)}))) diff --git a/src/status_im/contexts/communities/events_test.cljs b/src/status_im/contexts/communities/events_test.cljs index 1a5acdeea2..510b6a122c 100644 --- a/src/status_im/contexts/communities/events_test.cljs +++ b/src/status_im/contexts/communities/events_test.cljs @@ -2,6 +2,7 @@ (:require [cljs.test :refer [deftest is testing]] [legacy.status-im.mailserver.core :as mailserver] matcher-combinators.test + [status-im.constants :as constants] [status-im.contexts.chat.messenger.messages.link-preview.events :as link-preview.events] [status-im.contexts.communities.events :as events])) @@ -193,3 +194,79 @@ (is (match? nil (events/spectate-community-success {} [])))))) + +(deftest get-revealed-accounts + (let [community {:id community-id}] + (testing "given a unjoined community" + (is (match? + nil + (events/get-revealed-accounts {:db {:communities {community-id community}}} [community-id])))) + (testing "given a already :fetching-revealed-accounts community" + (is (match? + nil + (events/get-revealed-accounts + {:db {:communities {community-id (assoc community :fetching-revealed-accounts true)}}} + [community-id])))) + (testing "given joined community" + (let [community (assoc community :joined true) + db {:communities {community-id community} + :profile/profile {:public-key "profile-public-key"}} + effects (events/get-revealed-accounts {:db db} [community-id])] + (is (match? (assoc-in db [:communities community-id :fetching-revealed-accounts] true) + (:db effects))) + (is (match? {:method "wakuext_getRevealedAccounts" + :params [community-id "profile-public-key"]} + (-> effects :json-rpc/call first (select-keys [:method :params])))))))) + +(deftest handle-community + (let [community {:id community-id}] + (testing "given a unjoined community" + (let [effects (events/handle-community {} [community])] + (is (match? community-id + (-> effects :db :communities (get community-id) :id))) + (is (match? + [[:dispatch [:communities/initialize-permission-addresses community-id]] + [:dispatch [:chat.ui/spectate-community community-id]] + [:dispatch [:communities/check-permissions-to-join-community community-id]]] + (filter some? (:fx effects)))))) + (testing "given a joined community" + (let [community (assoc community :joined true) + effects (events/handle-community {} [community])] + (is (match? + [[:dispatch [:communities/initialize-permission-addresses community-id]] + [:dispatch [:communities/check-permissions-to-join-community community-id]] + [:dispatch [:communities/get-revealed-accounts community-id]]] + (filter some? (:fx effects)))))) + (testing "given a community with token-permissions-check" + (let [community (assoc community :token-permissions-check :fake-token-permissions-check) + effects (events/handle-community {} [community])] + (is (match? + [[:dispatch [:communities/initialize-permission-addresses community-id]] + [:dispatch [:chat.ui/spectate-community community-id]]] + (filter some? (:fx effects)))))) + (testing "given a community with view channel permission" + (let [community (assoc community + :token-permissions + [["perm-id" {:type constants/community-token-permission-can-view-channel}]]) + effects (events/handle-community {} [community])] + (is (match? + [[:dispatch [:communities/initialize-permission-addresses community-id]] + [:dispatch [:chat.ui/spectate-community community-id]] + [:dispatch [:communities/check-permissions-to-join-community community-id]] + [:dispatch + [:communities/check-all-community-channels-permissions community-id]]] + (filter some? (:fx effects)))))) + + (testing "given a community with post in channel permission" + (let [community (assoc community + :token-permissions + [["perm-id" + {:type constants/community-token-permission-can-view-and-post-channel}]]) + effects (events/handle-community {} [community])] + (is (match? + [[:dispatch [:communities/initialize-permission-addresses community-id]] + [:dispatch [:chat.ui/spectate-community community-id]] + [:dispatch [:communities/check-permissions-to-join-community community-id]] + [:dispatch + [:communities/check-all-community-channels-permissions community-id]]] + (filter some? (:fx effects)))))))) diff --git a/src/status_im/contexts/communities/overview/events.cljs b/src/status_im/contexts/communities/overview/events.cljs index b4704c3945..2e685500b2 100644 --- a/src/status_im/contexts/communities/overview/events.cljs +++ b/src/status_im/contexts/communities/overview/events.cljs @@ -34,26 +34,31 @@ :communities/check-all-community-channels-permissions}))}]]]}))) (rf/reg-event-fx :communities/check-permissions-to-join-community-success - (fn [{:keys [db]} [community-id result]] - {:db (-> db - (assoc-in [:communities community-id :checking-permissions?] false) - (assoc-in [:communities community-id :token-permissions-check] result))})) + (fn [{:keys [db]} [community-id based-on-client-selection? result]] + (let [token-permissions-check (cond-> result + based-on-client-selection? (assoc :based-on-client-selection? true))] + {:db (-> db + (assoc-in [:communities community-id :checking-permissions?] false) + (assoc-in [:communities community-id :token-permissions-check] + token-permissions-check))}))) (rf/reg-event-fx :communities/check-permissions-to-join-community-failed (fn [{:keys [db]} [community-id]] {:db (assoc-in db [:communities community-id :checking-permissions?] false)})) (rf/reg-event-fx :communities/check-permissions-to-join-community - (fn [{:keys [db]} [community-id]] + (fn [{:keys [db]} [community-id addresses based-on-client-selection?]] (when-let [community (get-in db [:communities community-id])] (when-not (:checking-permissions? community) {:db (-> db (assoc-in [:communities community-id :checking-permissions?] true) (assoc-in [:communities community-id :can-request-access?] false)) :json-rpc/call [{:method "wakuext_checkPermissionsToJoinCommunity" - :params [{:communityId community-id}] + :params [(cond-> {:communityId community-id} + addresses + (assoc :addresses addresses))] :on-success [:communities/check-permissions-to-join-community-success - community-id] + community-id based-on-client-selection?] :on-error (fn [err] (rf/dispatch [:communities/check-permissions-to-join-community-failed diff --git a/src/status_im/contexts/communities/overview/view.cljs b/src/status_im/contexts/communities/overview/view.cljs index a8fbf1d440..3dbbf3a4be 100644 --- a/src/status_im/contexts/communities/overview/view.cljs +++ b/src/status_im/contexts/communities/overview/view.cljs @@ -16,6 +16,7 @@ [status-im.contexts.communities.actions.community-options.view :as options] [status-im.contexts.communities.overview.style :as style] [status-im.contexts.communities.overview.utils :as utils] + [status-im.contexts.communities.utils :as communities.utils] [utils.debounce :as debounce] [utils.i18n :as i18n] [utils.re-frame :as rf])) @@ -140,7 +141,11 @@ [] (fn [{:keys [id color]}] (let [{:keys [can-request-access? - number-of-hold-tokens tokens]} (rf/sub [:community/token-gated-overview id])] + number-of-hold-tokens tokens + highest-permission-role]} (rf/sub [:community/token-gated-overview id]) + highest-role-text + (i18n/label + (communities.utils/role->translation-key highest-permission-role :t/member))] [rn/view {:style (style/token-gated-container)} [rn/view {:style {:padding-horizontal 12 @@ -150,7 +155,7 @@ :flex 1}} [quo/text {:weight :medium} (if can-request-access? - (i18n/label :t/you-eligible-to-join) + (i18n/label :t/you-eligible-to-join-as {:role highest-role-text}) (i18n/label :t/you-not-eligible-to-join))] [info-button]] (when (pos? number-of-hold-tokens) @@ -163,23 +168,26 @@ {:tokens tokens :padding? true}] [quo/button - {:on-press #(join-gated-community id) + {:on-press + (if config/community-accounts-selection-enabled? + #(rf/dispatch [:open-modal :community-account-selection {:community-id id}]) + #(join-gated-community id)) :accessibility-label :join-community-button :customization-color color - :container-style {:margin-horizontal 12 :margin-top 12 :margin-bottom 12} - :disabled? (not can-request-access?) - :icon-left (if can-request-access? :i/unlocked :i/locked)} + :container-style {:margin-horizontal 12 :margin-top 12 :margin-bottom 12} + :disabled? (not can-request-access?) + :icon-left (if can-request-access? :i/unlocked :i/locked)} (i18n/label :t/join-open-community)]]))) (defn join-community - [{:keys [joined color permissions token-permissions id] :as community} + [{:keys [joined color permissions token-permissions membership-permissions? id] :as community} pending?] (let [access-type (get-access-type (:access permissions)) unknown-access? (= access-type :unknown-access) invite-only? (= access-type :invite-only)] [:<> (when-not (or joined pending? invite-only? unknown-access?) - (if (seq token-permissions) + (if membership-permissions? [token-gates community] [quo/button {:on-press @@ -190,7 +198,7 @@ :accessibility-label :show-request-to-join-screen-button :customization-color color :icon-left :i/communities} - (i18n/label :t/request-to-join-community)])) + (i18n/label :t/request-to-join)])) (when (not (or joined pending? token-permissions)) [quo/text diff --git a/src/status_im/contexts/communities/utils.cljs b/src/status_im/contexts/communities/utils.cljs new file mode 100644 index 0000000000..51bffa37a9 --- /dev/null +++ b/src/status_im/contexts/communities/utils.cljs @@ -0,0 +1,13 @@ +(ns status-im.contexts.communities.utils + (:require + [status-im.constants :as constants])) + +(defn role->translation-key + ([role] (role->translation-key role nil)) + ([role fallback-to] + (condp = role + constants/community-token-permission-become-token-owner :t/token-owner + constants/community-token-permission-become-token-master :t/token-master + constants/community-token-permission-become-admin :t/admin + constants/community-token-permission-become-member :t/member + fallback-to))) diff --git a/src/status_im/subs/communities.cljs b/src/status_im/subs/communities.cljs index 678f203815..a45c1dde83 100644 --- a/src/status_im/subs/communities.cljs +++ b/src/status_im/subs/communities.cljs @@ -297,32 +297,62 @@ (fn [collapsed-categories [_ community-id]] (get collapsed-categories community-id))) +(defn- permission-id->permission-value + [token-permissions permission-id] + (get (into {} token-permissions) permission-id)) + (re-frame/reg-sub :community/token-gated-overview (fn [[_ community-id]] [(re-frame/subscribe [:communities/community community-id])]) (fn [[{:keys [token-permissions-check token-permissions checking-permissions? token-images]}] _] - {:can-request-access? (:satisfied token-permissions-check) - :number-of-hold-tokens (reduce - (fn [acc [_ {:keys [criteria]}]] - (reduce #(+ %1 (if %2 1 0)) acc criteria)) - 0 - (:permissions token-permissions-check)) - :tokens (->> token-permissions - (filter (fn [[_ {:keys [type]}]] - (= type constants/community-token-permission-become-member))) - (map (fn [[perm-key {:keys [token_criteria]}]] - (let [check-criteria (get-in token-permissions-check - [:permissions perm-key :criteria])] - (map - (fn [{sym :symbol amount :amount} sufficient?] - {:symbol sym - :sufficient? (when (seq check-criteria) sufficient?) - :loading? checking-permissions? - :amount amount - :img-src (get token-images sym)}) - token_criteria - (or check-criteria token_criteria))))))})) + (let [can-request-access? (:satisfied token-permissions-check) + role-permissions #{constants/community-token-permission-become-admin + constants/community-token-permission-become-member + constants/community-token-permission-become-token-master + constants/community-token-permission-become-token-owner} + highest-permission-role + (when can-request-access? + (->> token-permissions-check + :permissions + (reduce-kv + (fn [highest-permission-role permission-id {:keys [criteria]}] + (if-let [permission-type + (and (first criteria) + (some #{(:type (permission-id->permission-value token-permissions + permission-id))} + role-permissions))] + (if highest-permission-role + (min highest-permission-role permission-type) + permission-type) + highest-permission-role)) + nil))) + highest-permission-role (if (and can-request-access? (nil? highest-permission-role)) + constants/community-token-permission-become-member + highest-permission-role)] + {:can-request-access? can-request-access? + :highest-permission-role highest-permission-role + :number-of-hold-tokens (reduce + (fn [acc [_ {:keys [criteria]}]] + (reduce #(+ %1 (if %2 1 0)) acc criteria)) + 0 + (:permissions token-permissions-check)) + :tokens (->> + token-permissions + (filter (fn [[_ {:keys [type]}]] + (= type constants/community-token-permission-become-member))) + (map (fn [[perm-key {:keys [token_criteria]}]] + (let [check-criteria (get-in token-permissions-check + [:permissions perm-key :criteria])] + (map + (fn [{sym :symbol amount :amount} sufficient?] + {:symbol sym + :sufficient? (when (seq check-criteria) sufficient?) + :loading? checking-permissions? + :amount amount + :img-src (get token-images sym)}) + token_criteria + (or check-criteria token_criteria))))))}))) (re-frame/reg-sub :community/images diff --git a/src/status_im/subs/communities_test.cljs b/src/status_im/subs/communities_test.cljs index 38ea25d322..56239baf4b 100644 --- a/src/status_im/subs/communities_test.cljs +++ b/src/status_im/subs/communities_test.cljs @@ -1,6 +1,7 @@ (ns status-im.subs.communities-test (:require [cljs.test :refer [is testing]] + matcher-combinators.test [re-frame.db :as rf-db] [status-im.constants :as constants] status-im.subs.communities @@ -17,7 +18,7 @@ (swap! rf-db/app-db assoc :communities raw-communities) - (is (= raw-communities (rf/sub [sub-name])))))) + (is (match? raw-communities (rf/sub [sub-name])))))) (h/deftest-sub :communities/section-list [sub-name] @@ -26,29 +27,29 @@ :communities {"0x1" {:name "civilized monkeys"} "0x2" {:name "Civilized rats"}}) - (is (= [{:title "C" - :data [{:name "civilized monkeys"} - {:name "Civilized rats"}]}] - (rf/sub [sub-name])))) + (is (match? [{:title "C" + :data [{:name "civilized monkeys"} + {:name "Civilized rats"}]}] + (rf/sub [sub-name])))) (testing "sorts by section ascending" (swap! rf-db/app-db assoc :communities {"0x3" {:name "Memorable"} "0x1" {:name "Civilized monkeys"}}) - (is (= [{:title "C" :data [{:name "Civilized monkeys"}]} - {:title "M" :data [{:name "Memorable"}]}] - (rf/sub [sub-name])))) + (is (match? [{:title "C" :data [{:name "Civilized monkeys"}]} + {:title "M" :data [{:name "Memorable"}]}] + (rf/sub [sub-name])))) (testing "builds default section for communities without a name" (swap! rf-db/app-db assoc :communities {"0x2" {:id "0x2"} "0x1" {:id "0x1"}}) - (is (= [{:title "" - :data [{:id "0x2"} - {:id "0x1"}]}] - (rf/sub [sub-name]))))) + (is (match? [{:title "" + :data [{:id "0x2"} + {:id "0x1"}]}] + (rf/sub [sub-name]))))) (h/deftest-sub :communities/unviewed-counts [sub-name] @@ -64,17 +65,17 @@ "0x102" {:community-id community-id :unviewed-mentions-count 5 :unviewed-messages-count 1}}) - (is (= {:unviewed-messages-count 3 - :unviewed-mentions-count 8} - (rf/sub [sub-name community-id])))) + (is (match? {:unviewed-messages-count 3 + :unviewed-mentions-count 8} + (rf/sub [sub-name community-id])))) (testing "defaults to zero when count keys are not present" (swap! rf-db/app-db assoc :chats {"0x100" {:community-id community-id}}) - (is (= {:unviewed-messages-count 0 - :unviewed-mentions-count 0} - (rf/sub [sub-name community-id]))))) + (is (match? {:unviewed-messages-count 0 + :unviewed-mentions-count 0} + (rf/sub [sub-name community-id]))))) (h/deftest-sub :communities/categorized-channels [sub-name] @@ -350,16 +351,16 @@ (swap! rf-db/app-db assoc :communities/my-pending-requests-to-join {}) - (is (= {} - (rf/sub [sub-name])))) + (is (match? {} + (rf/sub [sub-name])))) (testing "users requests to join different communities" (swap! rf-db/app-db assoc :communities/my-pending-requests-to-join {:community-id-1 {:id :request-id-1} :community-id-2 {:id :request-id-2}}) - (is (= {:community-id-1 {:id :request-id-1} - :community-id-2 {:id :request-id-2}} - (rf/sub [sub-name]))))) + (is (match? {:community-id-1 {:id :request-id-1} + :community-id-2 {:id :request-id-2}} + (rf/sub [sub-name]))))) (h/deftest-sub :communities/my-pending-request-to-join [sub-name] @@ -367,15 +368,15 @@ (swap! rf-db/app-db assoc :communities/my-pending-requests-to-join {}) - (is (= nil - (rf/sub [sub-name :community-id-1])))) + (is (match? nil + (rf/sub [sub-name :community-id-1])))) (testing "users request to join a specific communities" (swap! rf-db/app-db assoc :communities/my-pending-requests-to-join {:community-id-1 {:id :request-id-1} :community-id-2 {:id :request-id-2}}) - (is (= :request-id-1 - (rf/sub [sub-name :community-id-1]))))) + (is (match? :request-id-1 + (rf/sub [sub-name :community-id-1]))))) (h/deftest-sub :community/token-gated-overview [sub-name] @@ -385,6 +386,7 @@ community {:id community-id :checking-permissions? checking-permissions? :permissions {:access 3} + :highest-permission-role constants/community-token-permission-become-admin :token-images {"ETH" token-image-eth} :token-permissions [[:permission-id-01 {:id "permission-id-01" @@ -451,14 +453,14 @@ :outroMessage "bla" :verified false}] (swap! rf-db/app-db assoc-in [:communities community-id] community) - (is (= {:can-request-access? true - :number-of-hold-tokens 2 - :tokens [[{:symbol "ETH" - :amount "0.001" - :sufficient? nil - :loading? checking-permissions? - :img-src token-image-eth}]]} - (rf/sub [sub-name community-id]))))) + (is (match? {:can-request-access? true + :number-of-hold-tokens 2 + :tokens [[{:symbol "ETH" + :amount "0.001" + :sufficient? nil + :loading? checking-permissions? + :img-src token-image-eth}]]} + (rf/sub [sub-name community-id]))))) (h/deftest-sub :communities/airdrop-account [sub-name] diff --git a/translations/en.json b/translations/en.json index 3541dfd56c..76c6af62b4 100644 --- a/translations/en.json +++ b/translations/en.json @@ -184,7 +184,7 @@ "no-addresses-selected": "At least 1 address must be shared with community", "confirm-changes": "Confirm changes", "airdrop-addresses": "Address for airdrops", - "join-as-a-member": "Join as a Member", + "join-as-a": "Join as a {{role}}", "all-addresses": "All addresses", "for-airdrops": "For airdrops", "members-label": "Members", @@ -2016,6 +2016,9 @@ "mentions": "Mentions", "mention": "Mention", "admin": "Admin", + "member": "Member", + "token-master": "Token Master", + "token-owner": "Token Owner", "replies": "Replies", "replied": "Replied", "identity-verification": "Identity verification", @@ -2252,7 +2255,9 @@ "sync-devices-complete-title": "Device sync complete!", "sync-devices-complete-sub-title": "Your devices are now in sync", "synced-with": "Synced with", + "eligible-to-join-as": "Eligible to join as {{role}}", "you-eligible-to-join": "You’re eligible to join", + "you-eligible-to-join-as": "You’re eligible to join as {{role}}", "you-not-eligible-to-join": "You’re not eligible to join", "you-hold-number-of-hold-tokens-of-these": "You hold {{number-of-hold-tokens}} of these:", "token-gated-communities": "Token gated communities",