From a4dc268bc32d273ed4177657e90062698c7b539e Mon Sep 17 00:00:00 2001 From: yqrashawn Date: Fri, 22 Dec 2023 10:21:10 +0800 Subject: [PATCH] feat: spectate community before join (#18070) --- src/status_im/common/universal_links.cljs | 2 +- .../chat/messages/link_preview/events.cljs | 108 +++++++++++--- .../messages/link_preview/events_test.cljs | 139 ++++++++++++++++++ .../chat/messages/link_preview/view.cljs | 2 +- .../contexts/communities/events.cljs | 27 ++-- .../contexts/communities/overview/events.cljs | 81 ++++++---- .../contexts/communities/overview/view.cljs | 19 +-- 7 files changed, 304 insertions(+), 74 deletions(-) create mode 100644 src/status_im/contexts/chat/messages/link_preview/events_test.cljs diff --git a/src/status_im/common/universal_links.cljs b/src/status_im/common/universal_links.cljs index 6838b6da0e..2658be036d 100644 --- a/src/status_im/common/universal_links.cljs +++ b/src/status_im/common/universal_links.cljs @@ -86,7 +86,7 @@ (native-module/deserialize-and-compress-key community-id (fn [deserialized-key] - (rf/dispatch [:chat.ui/resolve-community-info (str deserialized-key)]) + (rf/dispatch [:chat.ui/fetch-community (str deserialized-key)]) (rf/dispatch [:handle-navigation-to-desktop-community-from-mobile (str deserialized-key)])))) (rf/defn handle-community-chat diff --git a/src/status_im/contexts/chat/messages/link_preview/events.cljs b/src/status_im/contexts/chat/messages/link_preview/events.cljs index 98ad436873..cf8af4377e 100644 --- a/src/status_im/contexts/chat/messages/link_preview/events.cljs +++ b/src/status_im/contexts/chat/messages/link_preview/events.cljs @@ -1,7 +1,8 @@ (ns status-im.contexts.chat.messages.link-preview.events (:require [camel-snake-kebab.core :as csk] - [status-im.contexts.communities.events :as communities] + [legacy.status-im.mailserver.core :as mailserver] + [schema.core :as schema] [status-im.contexts.profile.settings.events :as profile.settings.events] [taoensso.timbre :as log] [utils.collection] @@ -41,32 +42,93 @@ (boolean enabled?) {})) -(rf/defn handle-community-resolved - {:events [:chat.ui/community-resolved]} - [{:keys [db] :as cofx} community-id community] - (rf/merge cofx - (cond-> {:db (update db :communities/resolve-community-info dissoc community-id)} +(defn community-resolved + [{:keys [db]} [community-id community]] + (when community + {:db (update db :communities/resolve-community-info dissoc community-id) + :fx [[:dispatch [:communities/handle-community community]] + [:dispatch + [:chat.ui/cache-link-preview-data (community-link community-id) community]]]})) - (some? community) - (assoc :dispatch - [:chat.ui/cache-link-preview-data (community-link community-id) community])) - (communities/handle-community community))) +(rf/reg-event-fx :chat.ui/community-resolved community-resolved) -(rf/defn handle-community-failed-to-resolve - {:events [:chat.ui/community-failed-to-resolve]} - [{:keys [db]} community-id] +(defn community-failed-to-resolve + [{:keys [db]} [community-id]] {:db (update db :communities/resolve-community-info dissoc community-id)}) -(rf/defn resolve-community-info - {:events [:chat.ui/resolve-community-info]} - [{:keys [db]} community-id] - {:db (assoc-in db [:communities/resolve-community-info community-id] true) - :json-rpc/call [{:method "wakuext_requestCommunityInfoFromMailserver" - :params [community-id] - :on-success #(rf/dispatch [:chat.ui/community-resolved community-id %]) - :on-error #(do - (rf/dispatch [:chat.ui/community-failed-to-resolve community-id]) - (log/error "Failed to request community info from mailserver"))}]}) +(rf/reg-event-fx :chat.ui/community-failed-to-resolve community-failed-to-resolve) + +(defn fetch-community + [{:keys [db]} [community-id]] + (when community-id + {:db (assoc-in db [:communities/resolve-community-info community-id] true) + :json-rpc/call [{:method "wakuext_fetchCommunity" + :params [{:CommunityKey community-id + :TryDatabase true + :WaitForResponse true}] + :on-success (fn [community] + (rf/dispatch [:chat.ui/community-resolved community-id community])) + :on-error (fn [err] + (rf/dispatch [:chat.ui/community-failed-to-resolve community-id]) + (log/error {:message + "Failed to request community info from mailserver" + :error err}))}]})) + +(schema/=> fetch-community + [:=> + [:catn + [:cofx :schema.re-frame/cofx] + [:args + [:schema [:catn [:community-id [:? :string]]]]]] + [:map + [:db map?] + [:json-rpc/call :schema.common/rpc-call]]]) + +(rf/reg-event-fx :chat.ui/fetch-community fetch-community) + +(defn spectate-community-success + [{:keys [db]} [{:keys [communities]}]] + (when-let [community (first communities)] + {:db (-> db + (assoc-in [:communities (:id community) :spectated] true) + (assoc-in [:communities (:id community) :spectating] false)) + :fx [[:dispatch [:communities/handle-community community]] + [:dispatch [::mailserver/request-messages]]]})) + +(rf/reg-event-fx :chat.ui/spectate-community-success spectate-community-success) + +(defn spectate-community-failed + [{:keys [db]} [community-id]] + {:db (assoc-in db [:communities community-id :spectating] false)}) + +(rf/reg-event-fx :chat.ui/spectate-community-failed spectate-community-failed) + +(defn spectate-community + [{:keys [db]} [community-id]] + (let [{:keys [spectated spectating joined]} (get-in db [:communities community-id])] + (when (and (not joined) (not spectated) (not spectating)) + {:db (assoc-in db [:communities community-id :spectating] true) + :json-rpc/call [{:method "wakuext_spectateCommunity" + :params [community-id] + :on-success [:chat.ui/spectate-community-success] + :on-error (fn [err] + (log/error {:message + "Failed to spectate community" + :error err}) + (rf/dispatch [:chat.ui/spectate-community-failed + community-id]))}]}))) + +(schema/=> spectate-community + [:=> + [:catn + [:cofx :schema.re-frame/cofx] + [:args + [:schema [:catn [:community-id [:? :string]]]]]] + [:map + [:db map?] + [:json-rpc/call :schema.common/rpc-call]]]) + +(rf/reg-event-fx :chat.ui/spectate-community spectate-community) (rf/defn save-link-preview-whitelist {:events [:chat.ui/link-preview-whitelist-received]} diff --git a/src/status_im/contexts/chat/messages/link_preview/events_test.cljs b/src/status_im/contexts/chat/messages/link_preview/events_test.cljs new file mode 100644 index 0000000000..e34de49b29 --- /dev/null +++ b/src/status_im/contexts/chat/messages/link_preview/events_test.cljs @@ -0,0 +1,139 @@ +(ns status-im.contexts.chat.messages.link-preview.events-test + (:require [cljs.test :as t] + [legacy.status-im.mailserver.core :as mailserver] + matcher-combinators.test + [status-im.contexts.chat.messages.link-preview.events :as sut])) + +(t/deftest fetch-community + (t/testing "with community id" + (t/testing "update resolving indicator in db" + (t/is (match? + {:db {:communities/resolve-community-info {"community-id" true}}} + (sut/fetch-community {} ["community-id"])))) + (t/testing "call the fetch community rpc method with correct community id" + (t/is (match? + {:json-rpc/call [{:method "wakuext_fetchCommunity" + :params [{:CommunityKey "community-id" + :TryDatabase true + :WaitForResponse true}]}]} + (sut/fetch-community {} ["community-id"]))))) + (t/testing "with nil community id" + (t/testing "do nothing" + (t/is (match? + nil + (sut/fetch-community {} nil)))))) + +(t/deftest community-failed-to-resolve + (t/testing "given a community id" + (t/testing "remove community id from resolving indicator in db" + (t/is (match? + nil + (get-in (sut/community-failed-to-resolve {:db {:communities/resolve-community-info + {"community-id" true}}} + ["community-id"]) + [:db :communities/resolve-community-info "community-id"])))))) + +(t/deftest community-resolved + (with-redefs [sut/community-link (fn [id] (str "community-link+" id))] + (t/testing "given a community" + (let [cofx {:db {:communities/resolve-community-info {"community-id" true}}} + arg ["community-id" {:id "community-id"}]] + (t/testing "remove community id from resolving indicator in db" + (t/is (match? + nil + (get-in (sut/community-resolved cofx arg) + [:db :communities/resolve-community-info "community-id"])))) + (t/testing "dispatch fxs" + (t/is (match? + {:fx [[:dispatch [:communities/handle-community {:id "community-id"}]] + [:dispatch + [:chat.ui/cache-link-preview-data "community-link+community-id" + {:id "community-id"}]]]} + (sut/community-resolved cofx arg)))))) + (t/testing "given a joined community" + (let [cofx {:db {:communities/resolve-community-info {"community-id" true}}} + arg ["community-id" {:id "community-id" :joined true}]] + (t/testing "dispatch fxs, do not spectate community" + (t/is (match? + {:fx [[:dispatch [:communities/handle-community {:id "community-id"}]] + [:dispatch + [:chat.ui/cache-link-preview-data "community-link+community-id" + {:id "community-id"}]]]} + (sut/community-resolved cofx arg)))))) + (t/testing "given a token-gated community" + (let [cofx {:db {:communities/resolve-community-info {"community-id" true}}} + arg ["community-id" {:id "community-id" :tokenPermissions [1]}]] + (t/testing "dispatch fxs, do not spectate community" + (t/is (match? + {:fx [[:dispatch [:communities/handle-community {:id "community-id"}]] + [:dispatch + [:chat.ui/cache-link-preview-data "community-link+community-id" + {:id "community-id"}]]]} + (sut/community-resolved cofx arg)))))) + (t/testing "given nil community" + (t/testing "do nothing" + (t/is (match? + nil + (sut/community-resolved {} ["community-id" nil]))))))) + +(t/deftest spectate-community + (t/testing "given a joined community" + (t/testing "do nothing" + (t/is (match? + nil + (sut/spectate-community {:db {:communities {"community-id" {:joined true}}}} + ["community-id"]))))) + (t/testing "given a spectated community" + (t/testing "do nothing" + (t/is (match? + nil + (sut/spectate-community {:db {:communities {"community-id" {:spectated true}}}} + ["community-id"]))))) + (t/testing "given a spectating community" + (t/testing "do nothing" + (t/is (match? + nil + (sut/spectate-community {:db {:communities {"community-id" {:spectating true}}}} + ["community-id"]))))) + (t/testing "given a community" + (t/testing "mark community spectating" + (t/is (match? + {:db {:communities {"community-id" {:spectating true}}}} + (sut/spectate-community {:db {:communities {"community-id" {}}}} ["community-id"])))) + (t/testing "call spectate community rpc with correct community id" + (t/is (match? + {:json-rpc/call [{:method "wakuext_spectateCommunity" + :params ["community-id"]}]} + (sut/spectate-community {:db {:communities {"community-id" {}}}} ["community-id"])))))) + +(t/deftest spectate-community-failed + (t/testing "mark community spectating false" + (t/is (match? + {:db {:communities {"community-id" {:spectating false}}}} + (sut/spectate-community-failed {} ["community-id"]))))) + +(t/deftest spectate-community-success + (t/testing "given communities" + (t/testing "mark first community spectating false" + (t/is (match? + {:db {:communities {"community-id" {:spectating false}}}} + (sut/spectate-community-success {} [{:communities [{:id "community-id"}]}])))) + (t/testing "mark first community spectated true" + (t/is (match? + {:db {:communities {"community-id" {:spectated true}}}} + (sut/spectate-community-success {} [{:communities [{:id "community-id"}]}])))) + (t/testing "dispatch fxs for first community" + (t/is (match? + {:fx [[:dispatch [:communities/handle-community {:id "community-id"}]] + [:dispatch [::mailserver/request-messages]]]} + (sut/spectate-community-success {} [{:communities [{:id "community-id"}]}]))))) + (t/testing "given empty community" + (t/testing "do nothing" + (t/is (match? + nil + (sut/spectate-community-success {} [{:communities []}]))))) + (t/testing "given nil community" + (t/testing "do nothing" + (t/is (match? + nil + (sut/spectate-community-success {} [])))))) diff --git a/src/status_im/contexts/chat/messages/link_preview/view.cljs b/src/status_im/contexts/chat/messages/link_preview/view.cljs index a351329c23..90d615b1e1 100644 --- a/src/status_im/contexts/chat/messages/link_preview/view.cljs +++ b/src/status_im/contexts/chat/messages/link_preview/view.cljs @@ -81,7 +81,7 @@ (fn [] (when-not cached-preview-data (let [community-id (community-id-from-link community-link)] - (rf/dispatch [:chat.ui/resolve-community-info community-id])))) + (rf/dispatch [:chat.ui/fetch-community community-id])))) :reagent-render (fn [] (when cached-preview-data diff --git a/src/status_im/contexts/communities/events.cljs b/src/status_im/contexts/communities/events.cljs index b3be55baec..3e928c449c 100644 --- a/src/status_im/contexts/communities/events.cljs +++ b/src/status_im/contexts/communities/events.cljs @@ -63,10 +63,21 @@ {} (:communityTokensMetadata c))))) -(rf/defn handle-community - [{:keys [db]} {:keys [id] :as community}] - (when id - {:db (assoc-in db [:communities id] (<-rpc community))})) +(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-token-permissions? (not (seq token-permissions))] + {:db (assoc-in db [:communities id] community) + :fx [(when (and has-token-permissions? (not joined)) + [:dispatch [:chat.ui/spectate-community id]]) + (when (and has-token-permissions? (nil? token-permissions-check)) + [:dispatch [:communities/check-permissions-to-join-community id]]) + (when (and has-token-permissions? (not (get-in db [:community-channels-permissions id]))) + [:dispatch [:communities/check-all-community-channels-permissions id]])]})))) (rf/defn handle-removed-chats [{:keys [db]} chat-ids] @@ -109,10 +120,9 @@ (rf/defn handle-communities {:events [:community/fetch-success]} [{:keys [db]} communities] - {:db (reduce (fn [db {:keys [id] :as community}] - (assoc-in db [:communities id] (<-rpc community))) - db - communities)}) + {:fx + (->> communities + (map #(vector :dispatch [:communities/handle-community %])))}) (rf/reg-event-fx :communities/request-to-join-result (fn [{:keys [db]} [community-id request-id response-js]] @@ -138,7 +148,6 @@ :on-success #(rf/dispatch [:communities/request-to-join-result community-id request-id %]) :on-error #(log/error "failed to accept requests-to-join" community-id request-id %)}]})) - (rf/reg-event-fx :communities/get-user-requests-to-join-success (fn [{:keys [db]} [requests]] {:db (assoc db diff --git a/src/status_im/contexts/communities/overview/events.cljs b/src/status_im/contexts/communities/overview/events.cljs index 51ea7a82c6..082c0ab71d 100644 --- a/src/status_im/contexts/communities/overview/events.cljs +++ b/src/status_im/contexts/communities/overview/events.cljs @@ -8,41 +8,60 @@ (rf/reg-event-fx :communities/check-all-community-channels-permissions-success (fn [{:keys [db]} [community-id response]] - {:db (assoc-in db - [:community-channels-permissions community-id] - (data-store/rpc->channel-permissions (:channels response)))})) + {:db (-> db + (assoc-in [:community-channels-permissions community-id] + (data-store/rpc->channel-permissions (:channels response))) + (assoc-in [:communities community-id :checking-all-channels-permissions?] false))})) + +(rf/reg-event-fx :communities/check-all-community-channels-permissions-failed + (fn [{:keys [db]} [community-id]] + {:db (assoc-in db [:communities community-id :checking-all-channels-permissions?] false)})) (rf/reg-event-fx :communities/check-all-community-channels-permissions - (fn [_ [community-id]] - {:fx [[:json-rpc/call - [{:method "wakuext_checkAllCommunityChannelsPermissions" - :params [{:CommunityID community-id}] - :on-success [:communities/check-all-community-channels-permissions-success community-id] - :on-error (fn [error] - (log/error "failed to check channels permissions" - {:error error - :community-id community-id - :event - :communities/check-all-community-channels-permissions}))}]]]})) + (fn [{:keys [db]} [community-id]] + (when (get-in db [:communities community-id]) + {:db (assoc-in db [:communities community-id :checking-all-channels-permissions?] true) + :fx [[:json-rpc/call + [{:method "wakuext_checkAllCommunityChannelsPermissions" + :params [{:CommunityID community-id}] + :on-success [:communities/check-all-community-channels-permissions-success community-id] + :on-error (fn [error] + (rf/dispatch [:communities/check-all-community-channels-permissions-failed + community-id]) + (log/error "failed to check channels permissions" + {:error error + :community-id community-id + :event + :communities/check-all-community-channels-permissions}))}]]]}))) -(rf/defn check-permissions-to-join-community-success - {:events [:communities/check-permissions-to-join-community-success]} - [{:keys [db]} community-id result] - {:db (-> db - (assoc-in [:communities community-id :checking-permissions?] false) - (assoc-in [:communities community-id :token-permissions-check] result))}) +(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))})) -(rf/defn check-permissions-to-join-community - {:events [:communities/check-permissions-to-join-community]} - [{:keys [db]} community-id] - {: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}] - :on-success #(rf/dispatch [:communities/check-permissions-to-join-community-success - community-id %]) - :on-error #(log/error "failed to request to join community" community-id %)}]}) +(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]] + (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}] + :on-success [:communities/check-permissions-to-join-community-success + community-id] + :on-error (fn [err] + (rf/dispatch + [:communities/check-permissions-to-join-community-failed + community-id]) + (log/error "failed to request to join community" + community-id + err))}]})))) (defn request-to-join [{:keys [db]} [{:keys [community-id password]}]] diff --git a/src/status_im/contexts/communities/overview/view.cljs b/src/status_im/contexts/communities/overview/view.cljs index c6cb8e050f..a90755146a 100644 --- a/src/status_im/contexts/communities/overview/view.cljs +++ b/src/status_im/contexts/communities/overview/view.cljs @@ -212,14 +212,14 @@ (defn add-handlers [community-id - joined + joined-or-spectated {:keys [id locked?] :or {locked? false} :as chat}] (merge chat (when (and (not locked?) id) - {:on-press (when joined + {:on-press (when joined-or-spectated (fn [] (rf/dispatch [:dismiss-keyboard]) (debounce/dispatch-and-chill [:chat/navigate-to-chat (str community-id id)] @@ -231,12 +231,12 @@ :community-id community-id}))) (defn add-handlers-to-chats - [community-id joined chats] - (mapv (partial add-handlers community-id joined) chats)) + [community-id joined-or-spectated chats] + (mapv (partial add-handlers community-id joined-or-spectated) chats)) (defn add-handlers-to-categorized-chats - [community-id categorized-chats joined] - (let [add-on-press (partial add-handlers-to-chats community-id joined)] + [community-id categorized-chats joined-or-spectated] + (let [add-on-press (partial add-handlers-to-chats community-id joined-or-spectated)] (map (fn [[category v]] [category (update v :chats add-on-press)]) categorized-chats))) @@ -261,12 +261,13 @@ (defn community-content [community] (rf/dispatch [:communities/check-all-community-channels-permissions (:id community)]) - (fn [{:keys [name description joined images tags color id token-permissions] :as community} + (fn [{:keys [name description joined spectated images tags color id token-permissions] :as community} pending? {:keys [on-category-layout collapsed? on-first-channel-height-changed]}] - (let [chats-by-category (rf/sub [:communities/categorized-channels id])] + (let [joined-or-spectated (or joined spectated) + chats-by-category (rf/sub [:communities/categorized-channels id])] [:<> [rn/view {:style style/community-content-container} (when-not collapsed? @@ -286,7 +287,7 @@ :community-id id :community-color color :on-first-channel-height-changed on-first-channel-height-changed} - (add-handlers-to-categorized-chats id chats-by-category joined)])]))) + (add-handlers-to-categorized-chats id chats-by-category joined-or-spectated)])]))) (defn sticky-category-header [_]