From b85eb4184a5d304a4d96e04da79e902cb68b1f39 Mon Sep 17 00:00:00 2001 From: Omar Basem Date: Fri, 30 Dec 2022 18:14:24 +0400 Subject: [PATCH] feat: group details screen (3.3) (#14654) * feat: group details screen (3) --- src/quo2/components/avatars/group_avatar.cljs | 4 +- src/quo2/components/selectors/selectors.cljs | 109 +++++++++++++---- src/status_im/group_chats/core.cljs | 40 +++++-- src/status_im/ui/screens/screens.cljs | 2 +- .../ui2/screens/common/contact_list/view.cljs | 8 +- .../common/contact_list_item/view.cljs | 83 ++++++------- .../contexts}/chat/group_details/style.cljs | 16 +-- .../contexts}/chat/group_details/view.cljs | 113 +++++++++--------- .../quo_preview/avatars/group_avatar.cljs | 2 +- src/status_im2/setup/db.cljs | 3 +- src/status_im2/subs/contact.cljs | 30 +++-- src/status_im2/subs/general.cljs | 2 +- src/status_im2/subs/root.cljs | 3 +- src/utils/collection.cljs | 6 + translations/en.json | 3 +- 15 files changed, 263 insertions(+), 161 deletions(-) rename src/{status_im/ui2/screens => status_im2/contexts}/chat/group_details/style.cljs (74%) rename src/{status_im/ui2/screens => status_im2/contexts}/chat/group_details/view.cljs (62%) diff --git a/src/quo2/components/avatars/group_avatar.cljs b/src/quo2/components/avatars/group_avatar.cljs index b4f70be3e8..8beaab72e1 100644 --- a/src/quo2/components/avatars/group_avatar.cljs +++ b/src/quo2/components/avatars/group_avatar.cljs @@ -24,7 +24,9 @@ :align-items :center :justify-content :center :border-radius (/ container-size 2) - :background-color (colors/custom-color-by-theme color 50 60)} + ;:background-color (colors/custom-color-by-theme color 50 60) ; TODO: this is temporary only. + ;Issue: https://github.com/status-im/status-mobile/issues/14566 + :background-color color} [icon/icon :i/group {:size icon-size :color colors/white-opa-70}]]))) diff --git a/src/quo2/components/selectors/selectors.cljs b/src/quo2/components/selectors/selectors.cljs index 43bef3b4d5..451c20b4fc 100644 --- a/src/quo2/components/selectors/selectors.cljs +++ b/src/quo2/components/selectors/selectors.cljs @@ -5,6 +5,26 @@ [reagent.core :as reagent] [quo2.components.selectors.styles :as style])) + +(defn- get-color + [checked? disabled? blurred-background?] + (cond + checked? + (colors/custom-color-by-theme + :primary + 50 + 60 + (when disabled? 30) + (when disabled? 30)) + blurred-background? + (colors/theme-colors + (colors/alpha colors/neutral-80 (if disabled? 0.05 0.1)) + (colors/alpha colors/white (if disabled? 0.05 0.1))) + :else + (colors/theme-colors + (colors/alpha colors/neutral-20 (if disabled? 0.4 1)) + (colors/alpha colors/neutral-70 (if disabled? 0.3 1))))) + (defn- handle-press [disabled? on-change checked?] (when (not disabled?) @@ -48,32 +68,75 @@ (colors/alpha colors/neutral-100 (if disabled? 0.3 1)) (colors/alpha colors/white (if disabled? 0.3 1)))}]])]]))) + (defn checkbox [{:keys [default-checked?]}] (let [checked? (reagent/atom (or default-checked? false))] - @(reagent/track - (fn [{:keys [on-change disabled? blurred-background? container-style]}] - [rn/touchable-without-feedback - {:on-press (handle-press disabled? on-change checked?)} - [rn/view - {:style (merge - container-style - {:height 20 - :width 20})} - [rn/view - {:style (style/checkbox-toggle checked? disabled? blurred-background?) - :accessibility-label (str "checkbox-" (if @checked? "on" "off")) - :accessibility-role :checkbox - :testID "checkbox-component"} - (when @checked? - [rn/view - {:style - {:height 20 - :width 20}} - [icons/icon :i/check-small - {:size 20 - :color colors/white}]])]]]) - checked?))) + (fn [{:keys [on-change disabled? blurred-background? container-style]}] + [rn/touchable-without-feedback + {:on-press (handle-press disabled? on-change checked?)} + [rn/view + {:style (merge + container-style + {:height 20 + :width 20})} + [rn/view + {:style {:flex 1 + :border-radius 6 + :border-width (if @checked? 0 1) + :background-color (cond + @checked? + (get-color @checked? disabled? blurred-background?) + blurred-background? + (colors/theme-colors + colors/white-opa-5 + colors/white-opa-10) + :else + (colors/theme-colors + colors/white + colors/neutral-80-opa-40)) + :border-color (if @checked? + :none + (get-color @checked? disabled? blurred-background?))} + :accessibility-label (str "checkbox-" (if @checked? "on" "off")) + :accessibility-role :checkbox + :testID "checkbox-component"} + (when @checked? + [rn/view + {:style + {:height 20 + :width 20}} + [icons/icon :i/check-small + {:size 20 + :color colors/white}]])]]]))) + +;; TODO (Omar): issue https://github.com/status-im/status-mobile/issues/14681 +;(defn checkbox +; [{:keys [default-checked?]}] +; (let [checked? (reagent/atom (or default-checked? false))] +; @(reagent/track +; (fn [{:keys [on-change disabled? blurred-background? container-style]}] +; [rn/touchable-without-feedback +; {:on-press (handle-press disabled? on-change checked?)} +; [rn/view +; {:style (merge +; container-style +; {:height 20 +; :width 20})} +; [rn/view +; {:style (style/checkbox-toggle checked? disabled? blurred-background?) +; :accessibility-label (str "checkbox-" (if @checked? "on" "off")) +; :accessibility-role :checkbox +; :testID "checkbox-component"} +; (when @checked? +; [rn/view +; {:style +; {:height 20 +; :width 20}} +; [icons/icon :i/check-small +; {:size 20 +; :color colors/white}]])]]]) +; checked?))) (defn radio [{:keys [default-checked?]}] diff --git a/src/status_im/group_chats/core.cljs b/src/status_im/group_chats/core.cljs index e679af2e6f..7007fa640c 100644 --- a/src/status_im/group_chats/core.cljs +++ b/src/status_im/group_chats/core.cljs @@ -39,7 +39,16 @@ {:json-rpc/call [{:method "wakuext_removeMemberFromGroupChat" :params [nil chat-id member] :js-response true - :on-success #(re-frame/dispatch [:chat-updated % do-not-navigate?])}]}) + :on-success #(re-frame/dispatch [:chat-updated % true])}]}) + +(rf/defn remove-members + {:events [:group-chats.ui/remove-members-pressed]} + [{{:keys [current-chat-id] :group-chat/keys [deselected-members]} :db :as cofx}] + {:json-rpc/call [{:method "wakuext_removeMembersFromGroupChat" + :params [nil current-chat-id deselected-members] + :js-response true + :on-success #(re-frame/dispatch [:chat-updated % true]) + :on-error #()}]}) (rf/defn join-chat {:events [:group-chats.ui/join-pressed]} @@ -79,11 +88,11 @@ (rf/defn add-members "Add members to a group chat" {:events [:group-chats.ui/add-members-pressed]} - [{{:keys [current-chat-id selected-participants]} :db :as cofx}] + [{{:keys [current-chat-id] :group-chat/keys [selected-participants]} :db :as cofx}] {:json-rpc/call [{:method "wakuext_addMembersToGroupChat" :params [nil current-chat-id selected-participants] :js-response true - :on-success #(re-frame/dispatch [:chat-updated %])}]}) + :on-success #(re-frame/dispatch [:chat-updated % true])}]}) (rf/defn add-members-from-invitation "Add members to a group chat" @@ -179,6 +188,16 @@ :type (= constants/invitation-state-removed))) +(rf/defn deselect-member + {:events [:deselect-member]} + [{:keys [db]} id] + {:db (update db :group-chat/deselected-members conj id)}) + +(rf/defn undo-deselect-member + {:events [:undo-deselect-member]} + [{:keys [db]} id] + {:db (update db :group-chat/deselected-members disj id)}) + (rf/defn deselect-contact {:events [:deselect-contact]} [{:keys [db]} id] @@ -192,17 +211,22 @@ (rf/defn deselect-participant {:events [:deselect-participant]} [{:keys [db]} id] - {:db (update db :selected-participants disj id)}) + {:db (update db :group-chat/selected-participants disj id)}) (rf/defn select-participant {:events [:select-participant]} [{:keys [db]} id] - {:db (update db :selected-participants conj id)}) + {:db (update db :group-chat/selected-participants conj id)}) -(rf/defn add-participants-toggle-list - {:events [:group/add-participants-toggle-list]} +(rf/defn clear-added-participants + {:events [:group/clear-added-participants]} [{db :db}] - {:db (assoc db :selected-participants #{})}) + {:db (assoc db :group-chat/selected-participants #{})}) + +(rf/defn clear-removed-members + {:events [:group/clear-removed-members]} + [{db :db}] + {:db (assoc db :group-chat/deselected-members #{})}) (rf/defn show-group-chat-profile {:events [:show-group-chat-profile]} diff --git a/src/status_im/ui/screens/screens.cljs b/src/status_im/ui/screens/screens.cljs index a82038ec52..64cd272faf 100644 --- a/src/status_im/ui/screens/screens.cljs +++ b/src/status_im/ui/screens/screens.cljs @@ -107,7 +107,7 @@ [status-im.ui.screens.wallet.settings.views :as wallet-settings] [status-im.ui.screens.wallet.swap.views :as wallet.swap] [status-im.ui.screens.wallet.transactions.views :as wallet-transactions] - [status-im.ui2.screens.chat.group-details.view :as group-details] + [status-im2.contexts.chat.group-details.view :as group-details] [status-im.ui2.screens.chat.photo-selector.view :as photo-selector] [status-im.ui2.screens.chat.components.new-chat.view :as new-chat-aio])) diff --git a/src/status_im/ui2/screens/common/contact_list/view.cljs b/src/status_im/ui2/screens/common/contact_list/view.cljs index 6e5b4eee69..52e573b9be 100644 --- a/src/status_im/ui2/screens/common/contact_list/view.cljs +++ b/src/status_im/ui2/screens/common/contact_list/view.cljs @@ -9,15 +9,15 @@ [quo/divider-label {:label title}]) (defn contact-list - [{:keys [start-a-new-chat?] :as data}] - (let [contacts (if start-a-new-chat? - (rf/sub [:contacts/sorted-and-grouped-by-first-letter]) + [data] + (let [contacts (if (:group data) + (rf/sub [:contacts/add-members-sections]) (rf/sub [:contacts/filtered-active-sections]))] [rn/section-list {:key-fn :title :sticky-section-headers-enabled false :sections contacts :render-section-header-fn contacts-section-header - :content-container-style {:padding-bottom 120} + :content-container-style {:padding-bottom 20} :render-data data :render-fn contact-list-item/contact-list-item}])) diff --git a/src/status_im2/common/contact_list_item/view.cljs b/src/status_im2/common/contact_list_item/view.cljs index 60558169c1..616446ede3 100644 --- a/src/status_im2/common/contact_list_item/view.cljs +++ b/src/status_im2/common/contact_list_item/view.cljs @@ -3,11 +3,11 @@ [quo2.foundations.colors :as colors] [react-native.core :as rn] [react-native.platform :as platform] - [reagent.core :as reagent] [status-im2.common.home.actions.view :as actions] [status-im2.contexts.chat.home.chat-list-item.style :as style] [utils.address :as utils.address] - [utils.re-frame :as rf])) + [utils.re-frame :as rf] + [reagent.core :as reagent])) (defn open-chat [chat-id] @@ -20,33 +20,36 @@ (rf/dispatch [:search/home-filter-changed nil])))) (defn action-icon - [{:keys [public-key] :as item} {:keys [icon start-a-new-chat? group added] :as extra-data} + [{:keys [public-key] :as item} {:keys [icon start-a-new-chat? group] :as extra-data} user-selected? on-toggle] - (let [{:keys [contacts]} group - member? (contains? contacts public-key) - checked? (reagent/atom (if start-a-new-chat? - user-selected? - member?))] + (let [{:keys [contacts admins]} group + member? (contains? contacts public-key) + current-pk (rf/sub [:multiaccount/public-key]) + admin? (get admins current-pk) + checked? (reagent/atom (if start-a-new-chat? + user-selected? + member?))] [rn/touchable-opacity {:on-press #(rf/dispatch [:bottom-sheet/show-sheet {:content (fn [] [actions/actions item extra-data])}]) :style {:position :absolute :right 20}} (if (= icon :options) - [quo/icon :i/options - {:size 20 - :color (colors/theme-colors colors/neutral-50 colors/neutral-40)}] - @(reagent/track! - (fn [] - [quo/checkbox - {:default-checked? @checked? - :on-change (fn [selected] - (if start-a-new-chat? - (on-toggle true @checked? public-key) - (if selected - (swap! added conj public-key) - (reset! added (remove #(= % public-key) @added)))))}]) - checked?))])) + [quo/icon :i/options {:size 20 :color (colors/theme-colors colors/neutral-50 colors/neutral-40)}] + [quo/checkbox + {:default-checked? @checked? + :accessibility-label :contact-toggle-check + :disabled? (and member? (not admin?)) + :on-change (fn [selected] + (if start-a-new-chat? + (on-toggle true @checked? public-key) + (if-not member? + (if selected + (rf/dispatch [:select-participant public-key true]) + (rf/dispatch [:deselect-participant public-key true])) + (if selected + (rf/dispatch [:undo-deselect-member public-key true]) + (rf/dispatch [:deselect-member public-key true])))))}])])) (defn contact-list-item [item _ _ {:keys [group start-a-new-chat? on-toggle] :as extra-data}] @@ -57,38 +60,36 @@ photo-path (when (seq images) (rf/sub [:chats/photo-path public-key])) current-pk (rf/sub [:multiaccount/public-key]) + online? (rf/sub [:visibility-status-updates/online? + public-key]) user-selected? (rf/sub [:is-contact-selected? public-key])] [rn/touchable-opacity - (merge {:style (style/container) - :active-opacity 1 - :on-press #(if start-a-new-chat? - (on-toggle true user-selected? public-key) - (open-chat public-key)) - :on-long-press #(when (some? group) - (rf/dispatch [:bottom-sheet/show-sheet - {:content (fn [] [actions/actions item extra-data])}]))}) + (merge + {:style (style/container) + :accessibility-label :contact + :active-opacity 1 + :on-press #(if start-a-new-chat? + (on-toggle true user-selected? public-key) + (open-chat public-key)) + :on-long-press #(when (some? group) + (rf/dispatch [:bottom-sheet/show-sheet + {:content (fn [] [actions/actions item extra-data])}]))}) [quo/user-avatar {:full-name display-name :profile-picture photo-path :status-indicator? true - :online? true + :online? online? :size :small :ring? false}] [rn/view {:style {:margin-left 8}} [rn/view {:style {:flex-direction :row}} [quo/text {:weight :semi-bold} display-name] (if ens-verified - [rn/view - {:style {:margin-left 5 - :margin-top 4}} + [rn/view {:style {:margin-left 5 :margin-top 4}} [quo/icon :i/verified - {:no-color true - :size 12 - :color (colors/theme-colors colors/success-50 colors/success-60)}]] + {:no-color true :size 12 :color (colors/theme-colors colors/success-50 colors/success-60)}]] (when added? - [rn/view - {:style {:margin-left 5 - :margin-top 4}} + [rn/view {:style {:margin-left 5 :margin-top 4}} [quo/icon :i/contact {:no-color true :size 12 @@ -98,4 +99,4 @@ :style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}} (utils.address/get-shortened-address public-key)]] (when-not (= current-pk public-key) - [:f> action-icon item extra-data user-selected? on-toggle])])) + [action-icon item extra-data user-selected? on-toggle])])) diff --git a/src/status_im/ui2/screens/chat/group_details/style.cljs b/src/status_im2/contexts/chat/group_details/style.cljs similarity index 74% rename from src/status_im/ui2/screens/chat/group_details/style.cljs rename to src/status_im2/contexts/chat/group_details/style.cljs index 8538f853aa..1b876e9c9c 100644 --- a/src/status_im/ui2/screens/chat/group_details/style.cljs +++ b/src/status_im2/contexts/chat/group_details/style.cljs @@ -1,5 +1,6 @@ -(ns status-im.ui2.screens.chat.group-details.style - (:require [quo2.foundations.colors :as colors])) +(ns status-im2.contexts.chat.group-details.style + (:require [quo2.foundations.colors :as colors] + [react-native.platform :as platform])) (defn actions-view [] @@ -33,12 +34,13 @@ :align-items :center :margin-bottom 24}) -(def bottom-container - {:position :absolute - :padding-horizontal 20 +(defn bottom-container + [safe-area] + {:padding-horizontal 20 :padding-vertical 12 - :padding-bottom 33 + :padding-bottom (+ 33 (:bottom safe-area)) :width "100%" :background-color colors/white :flex-direction :row - :bottom 0}) + :margin-bottom (if platform/ios? 0 70)}) + diff --git a/src/status_im/ui2/screens/chat/group_details/view.cljs b/src/status_im2/contexts/chat/group_details/view.cljs similarity index 62% rename from src/status_im/ui2/screens/chat/group_details/view.cljs rename to src/status_im2/contexts/chat/group_details/view.cljs index 1df6b57e00..43e1bd5a5f 100644 --- a/src/status_im/ui2/screens/chat/group_details/view.cljs +++ b/src/status_im2/contexts/chat/group_details/view.cljs @@ -1,13 +1,11 @@ -(ns status-im.ui2.screens.chat.group-details.view +(ns status-im2.contexts.chat.group-details.view (:require [i18n.i18n :as i18n] - [oops.core :refer [oget]] [quo.components.safe-area :as safe-area] [quo2.core :as quo2] [quo2.foundations.colors :as colors] [react-native.core :as rn] - [reagent.core :as reagent] [status-im.chat.models :as chat.models] - [status-im.ui2.screens.chat.group-details.style :as style] + [status-im2.contexts.chat.group-details.style :as style] [status-im.ui2.screens.common.contact-list.view :as contact-list] [status-im2.common.contact-list-item.view :as contact-list-item] [status-im2.common.home.actions.view :as actions] @@ -46,8 +44,10 @@ [back-button] [options-button]]) (defn count-container - [count] - [rn/view {:style (style/count-container)} + [count accessibility-label] + [rn/view + {:style (style/count-container) + :accessibility-label accessibility-label} [quo2/text {:size :label :weight :medium @@ -66,44 +66,41 @@ :weight :medium :style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}} title]]) -(def added (reagent/atom ())) - -(defn contact-requests-sheet - [group] - (let [added (reagent/atom ())] - (fn [] - [:f> - (fn [] - (let [{window-height :height} (rn/use-window-dimensions) - safe-area (safe-area/use-safe-area)] - [rn/view {:style {:height (- window-height (:top safe-area))}} - [rn/touchable-opacity - {:on-press #(rf/dispatch [:bottom-sheet/hide]) - :style (style/close-icon)} - [quo2/icon :i/close {:color (colors/theme-colors colors/neutral-100 colors/white)}]] - [quo2/text - {:size :heading-1 - :weight :semi-bold - :style {:margin-left 20}} - (i18n/label :t/add-members)] - [rn/text-input - {:placeholder (str (i18n/label :t/search) "...") - :style {:height 32 - :padding-horizontal 20 - :margin-vertical 12} - :on-change (fn [e] - (rf/dispatch [:contacts/set-search-query (oget e "nativeEvent.text")]))}] - [contact-list/contact-list - {:icon :check - :group group - :added added - :search? true}] - [rn/view {:style style/bottom-container} - [quo2/button - {:style {:flex 1} - :on-press #(rf/dispatch [:bottom-sheet/hide]) - :disabled (zero? (count @added))} - (i18n/label :t/save)]]]))]))) +(defn add-members-sheet + [group admin?] + [:f> + (fn [] + (let [{window-height :height} (rn/use-window-dimensions) + safe-area (safe-area/use-safe-area) + selected-participants (rf/sub [:group-chat/selected-participants]) + deselected-members (rf/sub [:group-chat/deselected-members])] + [rn/view {:style {:height (- window-height (:top safe-area))}} + [rn/touchable-opacity + {:on-press #(rf/dispatch [:bottom-sheet/hide]) + :accessibility-label :close-manage-members + :style (style/close-icon)} + [quo2/icon :i/close {:color (colors/theme-colors colors/neutral-100 colors/white)}]] + [quo2/text + {:size :heading-1 + :weight :semi-bold + :style {:margin-left 20}} + (i18n/label (if admin? :t/manage-members :t/add-members))] + [contact-list/contact-list + {:icon :check + :group group + :search? true}] + [rn/view {:style (style/bottom-container safe-area)} + [quo2/button + {:style {:flex 1} + :accessibility-label :save + :on-press (fn [] + (rf/dispatch [:group-chats.ui/add-members-pressed]) + (js/setTimeout #(rf/dispatch [:group-chats.ui/remove-members-pressed]) + 500) + (rf/dispatch [:bottom-sheet/hide])) + :disabled (and (zero? (count selected-participants)) + (zero? (count deselected-members)))} + (i18n/label :t/save)]]]))]) (defn group-details [] @@ -136,33 +133,39 @@ {:size 20 :color (colors/theme-colors colors/neutral-50 colors/neutral-40)}]]] [rn/view {:style (style/actions-view)} [rn/touchable-opacity - {:style (style/action-container color) - :on-press (fn [] - (rf/dispatch [:bottom-sheet/show-sheet :pinned-messages-list chat-id]))} + {:style (style/action-container color) + :accessibility-label :pinned-messages + :on-press (fn [] + (rf/dispatch [:bottom-sheet/show-sheet :pinned-messages-list chat-id]))} [rn/view {:style {:flex-direction :row :justify-content :space-between}} [quo2/icon :i/pin {:size 20 :color (colors/theme-colors colors/neutral-100 colors/white)}] - [count-container (count pinned-messages)]] + [count-container (count pinned-messages) :pinned-count]] [quo2/text {:style {:margin-top 16} :size :paragraph-1 :weight :medium} (i18n/label :t/pinned-messages)]] [rn/touchable-opacity - {:style (style/action-container color) - :on-press #(rf/dispatch [::chat.models/mute-chat-toggled chat-id (not muted)])} + {:style (style/action-container color) + :accessibility-label :toggle-mute + :on-press #(rf/dispatch [::chat.models/mute-chat-toggled chat-id (not muted)])} [quo2/icon (if muted :i/muted :i/activity-center) {:size 20 :color (colors/theme-colors colors/neutral-100 colors/white)}] [quo2/text {:style {:margin-top 16} :size :paragraph-1 :weight :medium} (i18n/label (if muted :unmute-group :mute-group))]] [rn/touchable-opacity - {:style (style/action-container color) - :on-press #(rf/dispatch - [:bottom-sheet/show-sheet - {:content (fn [] [contact-requests-sheet group])}])} + {:style (style/action-container color) + :accessibility-label :manage-members + :on-press (fn [] + (rf/dispatch [:group/clear-added-participants]) + (rf/dispatch [:group/clear-removed-members]) + (rf/dispatch + [:bottom-sheet/show-sheet + {:content (fn [] [add-members-sheet group admin?])}]))} [rn/view {:style {:flex-direction :row :justify-content :space-between}} [quo2/icon :i/add-user {:size 20 :color (colors/theme-colors colors/neutral-100 colors/white)}] - [count-container (count contacts)]] + [count-container (count contacts) :members-count]] [quo2/text {:style {:margin-top 16} :size :paragraph-1 :weight :medium} (i18n/label (if admin? :t/manage-members :t/add-members))]]] [rn/section-list diff --git a/src/status_im2/contexts/quo_preview/avatars/group_avatar.cljs b/src/status_im2/contexts/quo_preview/avatars/group_avatar.cljs index 59d76227cc..1c387e666d 100644 --- a/src/status_im2/contexts/quo_preview/avatars/group_avatar.cljs +++ b/src/status_im2/contexts/quo_preview/avatars/group_avatar.cljs @@ -23,7 +23,7 @@ (fn [c] {:key c :value c}) - (keys colors/customization))}]) + ["#ff0000" "#0000ff"])}]) ; TODO: this is temporary only. Issue: https://github.com/status-im/status-mobile/issues/14566 (defn cool-preview [] diff --git a/src/status_im2/setup/db.cljs b/src/status_im2/setup/db.cljs index 8148927991..057da1d820 100644 --- a/src/status_im2/setup/db.cljs +++ b/src/status_im2/setup/db.cljs @@ -13,7 +13,8 @@ :group/selected-contacts #{} :chats {} :current-chat-id nil - :selected-participants #{} + :group-chat/selected-participants #{} + :group-chat/deselected-members #{} :sync-state :done :link-previews-whitelist [] :app-state "active" diff --git a/src/status_im2/subs/contact.cljs b/src/status_im2/subs/contact.cljs index ba3430184c..271025d24e 100644 --- a/src/status_im2/subs/contact.cljs +++ b/src/status_im2/subs/contact.cljs @@ -8,7 +8,7 @@ [status-im.ui.screens.profile.visibility-status.utils :as visibility-status-utils] [status-im.utils.gfycat.core :as gfycat] [status-im.utils.image-server :as image-server] - [status-im.constants :as constants])) + [utils.collection])) (re-frame/reg-sub ::query-current-chat-contacts @@ -93,21 +93,20 @@ vals))) (re-frame/reg-sub - :contacts/sorted-and-grouped-by-first-letter + :contacts/add-members-sections + :<- [:contacts/current-chat-contacts] :<- [:contacts/active] - :<- [:selected-contacts-count] - (fn [[contacts selected-contacts-count]] - (->> contacts - (filter :mutual?) - (map #(assoc % - :allow-new-users? - (< selected-contacts-count - (dec constants/max-group-chat-participants)))) - (group-by (comp (fnil string/upper-case "") first :alias)) - (sort-by (fn [[title]] title)) - (map (fn [[title data]] - {:title title - :data data}))))) + (fn [[members contacts]] + (-> (reduce + (fn [acc contact] + (let [first-char (first (:alias contact))] + (if (get acc first-char) + (update-in acc [first-char :data] #(conj % contact)) + (assoc acc first-char {:title first-char :data [contact]})))) + {} + (utils.collection/distinct-by :public-key (concat members contacts))) + sort + vals))) (re-frame/reg-sub :contacts/sorted-contacts @@ -312,4 +311,3 @@ (seq offline) (assoc :offline {:title (i18n/label :t/offline) :data offline})))))) - diff --git a/src/status_im2/subs/general.cljs b/src/status_im2/subs/general.cljs index 03dfa90cc3..828b7292df 100644 --- a/src/status_im2/subs/general.cljs +++ b/src/status_im2/subs/general.cljs @@ -217,7 +217,7 @@ (re-frame/reg-sub :is-participant-selected? - :<- [:selected-participants] + :<- [:group-chat/selected-participants] (fn [selected-participants [_ element]] (-> selected-participants (contains? element)))) diff --git a/src/status_im2/subs/root.cljs b/src/status_im2/subs/root.cljs index cefd767f2d..35615d796b 100644 --- a/src/status_im2/subs/root.cljs +++ b/src/status_im2/subs/root.cljs @@ -99,7 +99,8 @@ (reg-root-key-sub :new-chat-name :new-chat-name) (reg-root-key-sub :group-chat-profile/editing? :group-chat-profile/editing?) (reg-root-key-sub :group-chat-profile/profile :group-chat-profile/profile) -(reg-root-key-sub :selected-participants :selected-participants) +(reg-root-key-sub :group-chat/selected-participants :group-chat/selected-participants) +(reg-root-key-sub :group-chat/deselected-members :group-chat/deselected-members) (reg-root-key-sub :chat/inputs :chat/inputs) (reg-root-key-sub :chat/memberships :chat/memberships) (reg-root-key-sub :camera-roll/photos :camera-roll/photos) diff --git a/src/utils/collection.cljs b/src/utils/collection.cljs index fb2cfc7e5c..0edbb10bd6 100644 --- a/src/utils/collection.cljs +++ b/src/utils/collection.cljs @@ -14,3 +14,9 @@ Similar to group-by except that the map values are single objects (depends on key uniqueness)." [key coll] (into {} (map #(vector (key %) %) coll))) + +(defn distinct-by + "Given a key and a collection returns a unique collection by that key" + [key coll] + (let [groups (group-by key coll)] + (map #(first (groups %)) (distinct (map key coll))))) diff --git a/translations/en.json b/translations/en.json index d7cec06f1e..aa3d98e4d4 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1923,5 +1923,6 @@ "search-contacts": "Search contacts", "who-are-you-looking-for": "Who are you looking for ?", "close-contact-search": "Close contact search", - "selected-count-from-max": "{{selected}}/{{max}}" + "selected-count-from-max": "{{selected}}/{{max}}", + "online": "Online" }