18638- [Communities] Implement Permissions Drawer (#18988)
* Community Permissions Sheet * Request to join screen: join as role --------- Co-authored-by: Ajay Sivan <ajayesivan@gmail.com>
This commit is contained in:
parent
90913500b4
commit
1e37765197
|
@ -8,6 +8,7 @@
|
|||
[status-im.contexts.communities.actions.addresses-for-permissions.view :as addresses-for-permissions]
|
||||
[status-im.contexts.communities.actions.airdrop-addresses.view :as airdrop-addresses]
|
||||
[status-im.contexts.communities.actions.community-rules.view :as community-rules]
|
||||
[status-im.contexts.communities.actions.permissions-sheet.view :as permissions-sheet]
|
||||
[status-im.contexts.communities.utils :as communities.utils]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
@ -16,6 +17,7 @@
|
|||
[]
|
||||
(let [{id :community-id} (rf/sub [:get-screen-params])
|
||||
{:keys [name color images joined]} (rf/sub [:communities/community id])
|
||||
has-permissions? (rf/sub [:communities/has-permissions? id])
|
||||
airdrop-account (rf/sub [:communities/airdrop-account id])
|
||||
revealed-accounts (rf/sub [:communities/accounts-to-reveal id])
|
||||
{:keys [highest-permission-role]} (rf/sub [:community/token-gated-overview id])
|
||||
|
@ -55,17 +57,28 @@
|
|||
:on-press (fn [password]
|
||||
(rf/dispatch [:communities/request-to-join-with-addresses
|
||||
{:community-id id :password password}]))}])
|
||||
(navigate-back)))]
|
||||
(navigate-back)))
|
||||
|
||||
open-permission-sheet
|
||||
(rn/use-callback (fn []
|
||||
(rf/dispatch [:show-bottom-sheet
|
||||
{:content (fn [] [permissions-sheet/view id])}]))
|
||||
[id])]
|
||||
(rn/use-mount
|
||||
(fn []
|
||||
(rf/dispatch [:communities/initialize-permission-addresses id])))
|
||||
|
||||
[rn/safe-area-view {:style style/container}
|
||||
[quo/page-nav
|
||||
{:text-align :left
|
||||
:icon-name :i/close
|
||||
:on-press navigate-back
|
||||
:accessibility-label :back-button}]
|
||||
(cond-> {:text-align :left
|
||||
:icon-name :i/close
|
||||
:on-press navigate-back
|
||||
:accessibility-label :back-button}
|
||||
has-permissions?
|
||||
(assoc :right-side
|
||||
[{:icon-left :i/unlocked
|
||||
:on-press open-permission-sheet
|
||||
:label (i18n/label :t/permissions)}]))]
|
||||
[quo/page-top
|
||||
{:title (if can-edit-addresses?
|
||||
(i18n/label :t/edit-shared-addresses)
|
||||
|
@ -88,7 +101,7 @@
|
|||
{:list-type :settings
|
||||
:data [{:title (if joined
|
||||
(i18n/label :t/you-are-a-role {:role highest-role-text})
|
||||
(i18n/label :t/join-as-a {:role highest-role-text}))
|
||||
(i18n/label :t/join-as {:role highest-role-text}))
|
||||
:on-press show-addresses-for-permissions
|
||||
:description :text
|
||||
:action :arrow
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
[quo.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[react-native.gesture :as gesture]
|
||||
[status-im.common.not-implemented :as not-implemented]
|
||||
[status-im.common.password-authentication.view :as password-authentication]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.communities.actions.addresses-for-permissions.style :as style]
|
||||
[status-im.contexts.communities.actions.permissions-sheet.view :as permissions-sheet]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.money :as money]
|
||||
[utils.re-frame :as rf]))
|
||||
|
@ -151,14 +151,23 @@
|
|||
(defn- page-top
|
||||
[{:keys [community-id identical-choices? can-edit-addresses?]}]
|
||||
(let [{:keys [name logo color]} (rf/sub [:communities/for-context-tag community-id])
|
||||
confirm-discard-changes (rn/use-callback
|
||||
(fn []
|
||||
(if identical-choices?
|
||||
(rf/dispatch [:dismiss-modal :addresses-for-permissions])
|
||||
(rf/dispatch [:show-bottom-sheet
|
||||
{:content (fn [] [confirm-discard-drawer
|
||||
community-id])}])))
|
||||
[identical-choices?])]
|
||||
has-permissions? (rf/sub [:communities/has-permissions? community-id])
|
||||
confirm-discard-changes
|
||||
(rn/use-callback
|
||||
(fn []
|
||||
(if identical-choices?
|
||||
(rf/dispatch [:dismiss-modal :addresses-for-permissions])
|
||||
(rf/dispatch [:show-bottom-sheet
|
||||
{:content (fn [] [confirm-discard-drawer
|
||||
community-id])}])))
|
||||
[identical-choices? community-id])
|
||||
|
||||
open-permission-sheet
|
||||
(rn/use-callback (fn []
|
||||
(rf/dispatch [:show-bottom-sheet
|
||||
{:content (fn [] [permissions-sheet/view
|
||||
community-id])}]))
|
||||
[community-id])]
|
||||
[:<>
|
||||
(when can-edit-addresses?
|
||||
[quo/page-nav
|
||||
|
@ -175,15 +184,16 @@
|
|||
:community-logo logo
|
||||
:community-name name}}]
|
||||
[quo/drawer-top
|
||||
{:type :context-tag
|
||||
:title (i18n/label :t/addresses-for-permissions)
|
||||
:context-tag-type :community
|
||||
:community-name name
|
||||
:button-icon :i/info
|
||||
:button-type :grey
|
||||
:on-button-press not-implemented/alert
|
||||
:community-logo logo
|
||||
:customization-color color}])]))
|
||||
(cond-> {:type :context-tag
|
||||
:title (i18n/label :t/addresses-for-permissions)
|
||||
:context-tag-type :community
|
||||
:community-name name
|
||||
:community-logo logo
|
||||
:customization-color color}
|
||||
has-permissions?
|
||||
(assoc :button-icon :i/info
|
||||
:button-type :grey
|
||||
:on-button-press open-permission-sheet))])]))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
(ns status-im.contexts.communities.actions.permissions-sheet.style)
|
||||
|
||||
(def container
|
||||
{:flex 1
|
||||
:padding-horizontal 20})
|
|
@ -0,0 +1,38 @@
|
|||
(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)]))
|
|
@ -30,7 +30,9 @@
|
|||
{:db (assoc-in db
|
||||
[:communities id]
|
||||
(assoc community :last-opened-at (max last-opened-at previous-last-opened-at)))
|
||||
:fx [(when (not joined)
|
||||
:fx [[:dispatch
|
||||
[:communities/check-permissions-to-join-community-with-all-addresses id]]
|
||||
(when (not joined)
|
||||
[:dispatch [:chat.ui/spectate-community id]])
|
||||
(when (nil? token-permissions-check)
|
||||
[:dispatch [:communities/check-permissions-to-join-community id]])]}))))
|
||||
|
|
|
@ -195,7 +195,9 @@
|
|||
(is (match? community-id
|
||||
(-> effects :db :communities (get community-id) :id)))
|
||||
(is (match?
|
||||
[[:dispatch [:chat.ui/spectate-community community-id]]
|
||||
[[:dispatch
|
||||
[:communities/check-permissions-to-join-community-with-all-addresses community-id]]
|
||||
[:dispatch [:chat.ui/spectate-community community-id]]
|
||||
[:dispatch [:communities/check-permissions-to-join-community community-id]]]
|
||||
(filter some? (:fx effects))))))
|
||||
|
||||
|
@ -203,14 +205,18 @@
|
|||
(let [community (assoc community :joined true)
|
||||
effects (events/handle-community {} [community])]
|
||||
(is (match?
|
||||
[[:dispatch [:communities/check-permissions-to-join-community community-id]]]
|
||||
[[:dispatch
|
||||
[:communities/check-permissions-to-join-community-with-all-addresses community-id]]
|
||||
[:dispatch [:communities/check-permissions-to-join-community 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 [:chat.ui/spectate-community community-id]]]
|
||||
[[:dispatch
|
||||
[:communities/check-permissions-to-join-community-with-all-addresses community-id]]
|
||||
[:dispatch [:chat.ui/spectate-community community-id]]]
|
||||
(filter some? (:fx effects))))))
|
||||
(testing "given a community with lower clock"
|
||||
(let [effects (events/handle-community {:db {:communities {community-id {:clock 3}}}} [community])]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
(ns status-im.contexts.communities.overview.events
|
||||
(:require
|
||||
[status-im.contexts.communities.utils :as utils]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
@ -36,6 +37,38 @@
|
|||
community-id
|
||||
err))}]}))))
|
||||
|
||||
(rf/reg-event-fx :communities/check-permissions-to-join-community-with-all-addresses-success
|
||||
(fn [{:keys [db]} [community-id result]]
|
||||
{:db (assoc-in db
|
||||
[:communities/permissions-check-all community-id]
|
||||
{:checking? false
|
||||
:check result})}))
|
||||
|
||||
(rf/reg-event-fx :communities/check-permissions-to-join-community-with-all-addresses-failed
|
||||
(fn [{:keys [db]} [community-id]]
|
||||
{:db (assoc-in db [:communities/permissions-check-all community-id :checking?] false)}))
|
||||
|
||||
(rf/reg-event-fx :communities/check-permissions-to-join-community-with-all-addresses
|
||||
(fn [{:keys [db]} [community-id]]
|
||||
(let [accounts (utils/sorted-non-watch-only-accounts db)
|
||||
addresses (set (map :address accounts))]
|
||||
{:db (assoc-in db [:communities/permissions-check community-id :checking?] true)
|
||||
:json-rpc/call [{:method "wakuext_checkPermissionsToJoinCommunity"
|
||||
:params [(cond-> {:communityId community-id}
|
||||
(seq addresses)
|
||||
(assoc :addresses addresses))]
|
||||
:on-success
|
||||
[:communities/check-permissions-to-join-community-with-all-addresses-success
|
||||
community-id]
|
||||
:on-error
|
||||
(fn [err]
|
||||
(rf/dispatch
|
||||
[:communities/check-permissions-to-join-community-with-all-addresses-failed
|
||||
community-id])
|
||||
(log/error "failed to check permissions for all addresses"
|
||||
community-id
|
||||
err))}]})))
|
||||
|
||||
(defn request-to-join
|
||||
[{:keys [db]}
|
||||
[{:keys [community-id password]}]]
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
[oops.core :as oops]
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme :as theme]
|
||||
[react-native.blur :as blur]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
|
@ -97,20 +98,21 @@
|
|||
|
||||
(defn- info-button
|
||||
[]
|
||||
[rn/pressable
|
||||
{:on-press
|
||||
#(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content
|
||||
(fn []
|
||||
[quo/documentation-drawers
|
||||
{:title (i18n/label :t/token-gated-communities)
|
||||
:show-button? true
|
||||
:button-label (i18n/label :t/read-more)
|
||||
:button-icon :info}
|
||||
[quo/text (i18n/label :t/token-gated-communities-info)]])}])}
|
||||
[rn/view
|
||||
[quo/icon :i/info {:no-color true}]]])
|
||||
(let [theme (theme/use-theme)]
|
||||
[rn/pressable
|
||||
{:on-press
|
||||
#(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content
|
||||
(fn []
|
||||
[quo/documentation-drawers
|
||||
{:title (i18n/label :t/token-gated-communities)
|
||||
:show-button? true
|
||||
:button-label (i18n/label :t/read-more)
|
||||
:button-icon :info}
|
||||
[quo/text (i18n/label :t/token-gated-communities-info)]])}])}
|
||||
[rn/view
|
||||
[quo/icon :i/info {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}]]]))
|
||||
|
||||
(defn- network-not-supported
|
||||
[]
|
||||
|
|
|
@ -266,8 +266,8 @@
|
|||
:position position
|
||||
:mentions-count (or unviewed-mentions-count 0)
|
||||
:can-post? can-post?
|
||||
;; NOTE: this is a troolean nil->no permissions, true->no access, false ->
|
||||
;; has access
|
||||
;; NOTE: this is a troolean nil->no permissions, true->no access, false
|
||||
;; -> has access
|
||||
:locked? (when token-gated?
|
||||
(and (not can-view?)
|
||||
(not can-post?)))
|
||||
|
@ -320,6 +320,12 @@
|
|||
(fn [permissions [_ id]]
|
||||
(get permissions id)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:communities/checking-permissions-all-by-id
|
||||
:<- [:communities/permissions-check-all]
|
||||
(fn [permissions [_ id]]
|
||||
(get permissions id)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:community/token-gated-overview
|
||||
(fn [[_ community-id]]
|
||||
|
@ -368,3 +374,35 @@
|
|||
(map (fn [{sym :symbol image :image}]
|
||||
{sym image}))
|
||||
(into {}))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:community/token-permissions
|
||||
(fn [[_ community-id]]
|
||||
[(re-frame/subscribe [:communities/community community-id])
|
||||
(re-frame/subscribe [:communities/checking-permissions-all-by-id community-id])])
|
||||
(fn [[{:keys [token-images]}
|
||||
{:keys [checking? check]}] _]
|
||||
(let [roles (:roles check)
|
||||
member-and-satisifed-roles (filter #(or (= (:type %)
|
||||
constants/community-token-permission-become-member)
|
||||
(:satisfied %))
|
||||
roles)]
|
||||
(mapv (fn [role]
|
||||
{:role (:type role)
|
||||
:satisfied? (:satisfied role)
|
||||
:tokens (map (fn [{:keys [tokenRequirement]}]
|
||||
(map
|
||||
(partial token-requirement->token
|
||||
checking?
|
||||
token-images)
|
||||
tokenRequirement))
|
||||
(:criteria role))})
|
||||
member-and-satisifed-roles))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:communities/has-permissions?
|
||||
(fn [[_ community-id]]
|
||||
[(re-frame/subscribe [:community/token-permissions community-id])])
|
||||
(fn [[permissions] _]
|
||||
(let [all-tokens (apply concat (map :tokens permissions))]
|
||||
(boolean (some seq all-tokens)))))
|
||||
|
|
|
@ -387,3 +387,94 @@
|
|||
(is (match? {"DOGE" "data:image/jpeg;base64,/9j/2wCEAAYEBQYFBAYGBQYH"
|
||||
"BTC" "data:image/jpeg;base64,/9j/2wCEAAYEBQYFBAYGBQYH"}
|
||||
(rf/sub [sub-name community-id])))))
|
||||
|
||||
(h/deftest-sub :community/token-permissions
|
||||
[sub-name]
|
||||
(testing
|
||||
"with visible permissions"
|
||||
(let
|
||||
[checking? false
|
||||
token-image-eth "data:image/jpeg;base64,/9j/2w"
|
||||
checks
|
||||
{:checking? checking?
|
||||
:check
|
||||
{:roles
|
||||
[{:type 1
|
||||
:satisfied true
|
||||
:criteria [{:roles 1
|
||||
:tokenRequirement [{:satisfied true
|
||||
:criteria {:contract_addresses
|
||||
{:421614
|
||||
0x0000000000000000000000000000000000000000
|
||||
:11155111
|
||||
0x0000000000000000000000000000000000000000
|
||||
:11155420
|
||||
0x0000000000000000000000000000000000000000}
|
||||
:type 1
|
||||
:symbol "ETH"
|
||||
:amount 1
|
||||
:decimals 18
|
||||
:amountInWei 1000000000000000000}}]
|
||||
:criteria [true]}]}
|
||||
{:type 2
|
||||
:satisfied false
|
||||
:criteria [{:roles 2
|
||||
:tokenRequirement [{:satisfied false
|
||||
:criteria {:contract_addresses
|
||||
{:11155111
|
||||
0x3e622317f8c93f7328350cf0b56d9ed4c620c5d6}
|
||||
:type 1
|
||||
:symbol "DAI"
|
||||
:amount 10
|
||||
:decimals 18
|
||||
:amountInWei 10000000000000000000}}]
|
||||
:criteria [false]}]}]}
|
||||
}
|
||||
community {:id community-id
|
||||
:checking-permissions? checking?
|
||||
:token-images {"ETH" token-image-eth}
|
||||
:name "Community super name"
|
||||
:can-request-access? false
|
||||
:outroMessage "bla"
|
||||
:verified false}]
|
||||
(swap! rf-db/app-db assoc-in [:communities community-id] community)
|
||||
(swap! rf-db/app-db assoc-in [:communities/permissions-check-all community-id] checks)
|
||||
(is
|
||||
(match? [{:role 1
|
||||
:satisfied? true
|
||||
:tokens
|
||||
[[{:symbol "ETH"
|
||||
:sufficient? true
|
||||
:loading? false
|
||||
:amount "1"
|
||||
:img-src token-image-eth}]]}
|
||||
{:role 2
|
||||
:satisfied? false
|
||||
:tokens [[{:symbol "DAI"
|
||||
:sufficient? false
|
||||
:loading? false
|
||||
:amount "10"
|
||||
:img-src nil}]]}]
|
||||
(rf/sub [sub-name community-id])))))
|
||||
(testing
|
||||
"without any visible permissions"
|
||||
(let
|
||||
[checking? false
|
||||
token-image-eth "data:image/jpeg;base64,/9j/2w"
|
||||
checks
|
||||
{:checking? checking?
|
||||
:check
|
||||
{:roles []}
|
||||
}
|
||||
community {:id community-id
|
||||
:checking-permissions? checking?
|
||||
:token-images {"ETH" token-image-eth}
|
||||
:name "Community super name"
|
||||
:can-request-access? false
|
||||
:outroMessage "bla"
|
||||
:verified false}]
|
||||
(swap! rf-db/app-db assoc-in [:communities community-id] community)
|
||||
(swap! rf-db/app-db assoc-in [:communities/permissions-check-all community-id] checks)
|
||||
(is
|
||||
(match? []
|
||||
(rf/sub [sub-name community-id]))))))
|
||||
|
|
|
@ -149,6 +149,7 @@
|
|||
(reg-root-key-sub :contract-communities :contract-communities)
|
||||
(reg-root-key-sub :communities/permissioned-balances :communities/permissioned-balances)
|
||||
(reg-root-key-sub :communities/permissions-check :communities/permissions-check)
|
||||
(reg-root-key-sub :communities/permissions-check-all :communities/permissions-check-all)
|
||||
(reg-root-key-sub :communities/all-addresses-to-reveal :communities/all-addresses-to-reveal)
|
||||
(reg-root-key-sub :communities/all-airdrop-addresses :communities/all-airdrop-addresses)
|
||||
(reg-root-key-sub :communities/selected-share-all-addresses :communities/selected-share-all-addresses)
|
||||
|
|
|
@ -191,7 +191,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": "Join as a {{role}}",
|
||||
"join-as": "Join as {{role}}",
|
||||
"all-addresses": "All addresses",
|
||||
"for-airdrops": "For airdrops",
|
||||
"members-label": "Members",
|
||||
|
@ -2297,6 +2297,7 @@
|
|||
"you-eligible-to-join-as": "You’re eligible to join as {{role}}",
|
||||
"eligible-to-join-as": "Eligible to join as",
|
||||
"you-not-eligible-to-join": "You’re not eligible to join",
|
||||
"you-not-eligible-to-join-as": "You’re not eligible to join as {{role}}",
|
||||
"you-hold-number-of-hold-tokens-of-these": "You hold {{number-of-hold-tokens}} of these:",
|
||||
"addresses-dont-contain-tokens-needed": "These addresses don’t contain tokens needed to join",
|
||||
"you-hodl": "You hodl:",
|
||||
|
|
Loading…
Reference in New Issue