diff --git a/src/quo2/components/avatars/group_avatar.cljs b/src/quo2/components/avatars/group_avatar.cljs index b4f70be3e8..2e0e4c2650 100644 --- a/src/quo2/components/avatars/group_avatar.cljs +++ b/src/quo2/components/avatars/group_avatar.cljs @@ -1,6 +1,6 @@ (ns quo2.components.avatars.group-avatar - (:require [quo2.components.icon :as icon] - [quo2.foundations.colors :as colors] + (:require [quo2.foundations.colors :as colors] + [quo2.components.icon :as icon] [react-native.core :as rn])) (def sizes @@ -11,20 +11,17 @@ :medium 32 :large 48}}) -;; TODO: this implementation does not support group display picture (can only display default group -;; icon). -(defn group-avatar - [_] +;; TODO: this implementation does not support group display picture (can only display default group icon). +(defn group-avatar [_] (fn [{:keys [color size]}] (let [container-size (get-in sizes [:container size]) icon-size (get-in sizes [:icon size])] - [rn/view - {:width container-size - :height container-size - :align-items :center - :justify-content :center - :border-radius (/ container-size 2) - :background-color (colors/custom-color-by-theme color 50 60)} - [icon/icon :i/group - {:size icon-size - :color colors/white-opa-70}]]))) + [rn/view {:width container-size + :height container-size + :align-items :center + :justify-content :center + :border-radius (/ container-size 2) + ;: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/status_im/group_chats/core.cljs b/src/status_im/group_chats/core.cljs index 3877f69d3a..d367638ac6 100644 --- a/src/status_im/group_chats/core.cljs +++ b/src/status_im/group_chats/core.cljs @@ -37,17 +37,26 @@ {:events [:group-chats.ui/remove-member-pressed]} [_ chat-id member do-not-navigate?] {:json-rpc/call [{:method "wakuext_removeMemberFromGroupChat" - :params [nil chat-id member] - :js-response true - :on-success #(re-frame/dispatch [:chat-updated % do-not-navigate?])}]}) + :params [nil chat-id member] + :js-response true + :on-success #(re-frame/dispatch [:chat-updated % true])}]}) + +(fx/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 #()}]}) (fx/defn join-chat {:events [:group-chats.ui/join-pressed]} [_ chat-id] {:json-rpc/call [{:method "wakuext_confirmJoiningGroup" - :params [chat-id] - :js-response true - :on-success #(re-frame/dispatch [:chat-updated %])}]}) + :params [chat-id] + :js-response true + :on-success #(re-frame/dispatch [:chat-updated %])}]}) (fx/defn create {:events [:group-chats.ui/create-pressed] @@ -55,54 +64,54 @@ [{:keys [db] :as cofx} group-name] (let [selected-contacts (:group/selected-contacts db)] {:json-rpc/call [{:method "wakuext_createGroupChatWithMembers" - :params [nil group-name (into [] selected-contacts)] - :js-response true - :on-success #(re-frame/dispatch [:chat-updated %])}]})) + :params [nil group-name (into [] selected-contacts)] + :js-response true + :on-success #(re-frame/dispatch [:chat-updated %])}]})) (fx/defn create-from-link [cofx {:keys [chat-id invitation-admin chat-name]}] (if (get-in cofx [:db :chats chat-id]) {:dispatch [:chat.ui/navigate-to-chat chat-id]} {:json-rpc/call [{:method "wakuext_createGroupChatFromInvitation" - :params [chat-name chat-id invitation-admin] - :js-response true - :on-success #(re-frame/dispatch [:chat-updated %])}]})) + :params [chat-name chat-id invitation-admin] + :js-response true + :on-success #(re-frame/dispatch [:chat-updated %])}]})) (fx/defn make-admin {:events [:group-chats.ui/make-admin-pressed]} [_ chat-id member] {:json-rpc/call [{:method "wakuext_addAdminsToGroupChat" - :params [nil chat-id [member]] - :js-response true - :on-success #(re-frame/dispatch [:chat-updated %])}]}) + :params [nil chat-id [member]] + :js-response true + :on-success #(re-frame/dispatch [:chat-updated %])}]}) (fx/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 %])}]}) + :params [nil current-chat-id selected-participants] + :js-response true + :on-success #(re-frame/dispatch [:chat-updated % true])}]}) (fx/defn add-members-from-invitation "Add members to a group chat" {:events [:group-chats.ui/add-members-from-invitation]} [{{:keys [current-chat-id] :as db} :db :as cofx} id participant] - {:db (assoc-in db [:group-chat/invitations id :state] constants/invitation-state-approved) + {:db (assoc-in db [:group-chat/invitations id :state] constants/invitation-state-approved) :json-rpc/call [{:method "wakuext_addMembersToGroupChat" - :params [nil current-chat-id [participant]] - :js-response true - :on-success #(re-frame/dispatch [:chat-updated %])}]}) + :params [nil current-chat-id [participant]] + :js-response true + :on-success #(re-frame/dispatch [:chat-updated %])}]}) (fx/defn leave "Leave chat" {:events [:group-chats.ui/leave-chat-confirmed]} [{:keys [db] :as cofx} chat-id] {:json-rpc/call [{:method "wakuext_leaveGroupChat" - :params [nil chat-id true] - :js-response true - :on-success #(re-frame/dispatch [:chat-removed %])}]}) + :params [nil chat-id true] + :js-response true + :on-success #(re-frame/dispatch [:chat-removed %])}]}) (fx/defn remove "Remove chat" @@ -124,11 +133,11 @@ {:events [:group-chats.ui/name-changed]} [{:keys [db] :as cofx} chat-id new-name] (when (valid-name? new-name) - {:db (assoc-in db [:chats chat-id :name] new-name) + {:db (assoc-in db [:chats chat-id :name] new-name) :json-rpc/call [{:method "wakuext_changeGroupChatName" - :params [nil chat-id new-name] - :js-response true - :on-success #(re-frame/dispatch [:chat-updated %])}]})) + :params [nil chat-id new-name] + :js-response true + :on-success #(re-frame/dispatch [:chat-updated %])}]})) (fx/defn membership-retry {:events [:group-chats.ui/membership-retry]} @@ -146,20 +155,20 @@ [{{:keys [current-chat-id chats] :as db} :db :as cofx}] (let [{:keys [invitation-admin]} (get chats current-chat-id) message (get-in db [:chat/memberships current-chat-id :message])] - {:db (assoc-in db [:chat/memberships current-chat-id] nil) + {:db (assoc-in db [:chat/memberships current-chat-id] nil) :json-rpc/call [{:method "wakuext_sendGroupChatInvitationRequest" - :params [nil current-chat-id invitation-admin message] - :js-response true - :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]})) + :params [nil current-chat-id invitation-admin message] + :js-response true + :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]})) (fx/defn send-group-chat-membership-rejection "Send group chat membership rejection" {:events [:send-group-chat-membership-rejection]} [cofx invitation-id] {:json-rpc/call [{:method "wakuext_sendGroupChatInvitationRejection" - :params [nil invitation-id] - :js-response true - :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}) + :params [nil invitation-id] + :js-response true + :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}) (fx/defn handle-invitations [{db :db} invitations] @@ -179,6 +188,16 @@ :type (= constants/invitation-state-removed))) +(fx/defn deselect-member + {:events [:deselect-member]} + [{:keys [db]} id] + {:db (update db :group-chat/deselected-members conj id)}) + +(fx/defn undo-deselect-member + {:events [:undo-deselect-member]} + [{:keys [db]} id] + {:db (update db :group-chat/deselected-members disj id)}) + (fx/defn deselect-contact {:events [:deselect-contact]} [{:keys [db]} id] @@ -192,17 +211,22 @@ (fx/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)}) (fx/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)}) -(fx/defn add-participants-toggle-list - {:events [:group/add-participants-toggle-list]} +(fx/defn clear-added-participants + {:events [:group/clear-added-participants]} [{db :db}] - {:db (assoc db :selected-participants #{})}) + {:db (assoc db :group-chat/selected-participants #{})}) + +(fx/defn clear-removed-members + {:events [:group/clear-removed-members]} + [{db :db}] + {:db (assoc db :group-chat/deselected-members #{})}) (fx/defn show-group-chat-profile {:events [:show-group-chat-profile]} diff --git a/src/status_im/ui/screens/group/views.cljs b/src/status_im/ui/screens/group/views.cljs index 0e61906500..da0d633582 100644 --- a/src/status_im/ui/screens/group/views.cljs +++ b/src/status_im/ui/screens/group/views.cljs @@ -1,25 +1,26 @@ (ns status-im.ui.screens.group.views (:require [clojure.string :as string] - [quo.core :as quo] [re-frame.core :as re-frame] [reagent.core :as reagent] [status-im.constants :as constants] [status-im.i18n.i18n :as i18n] - [status-im.multiaccounts.core :as multiaccounts] [status-im.ui.components.chat-icon.screen :as chat-icon] + [status-im.multiaccounts.core :as multiaccounts] + [status-im.ui.components.keyboard-avoid-presentation + :as + kb-presentation] [status-im.ui.components.invite.views :as invite] - [status-im.ui.components.keyboard-avoid-presentation :as kb-presentation] [status-im.ui.components.list.views :as list] [status-im.ui.components.react :as react] [status-im.ui.components.search-input.view :as search] [status-im.ui.components.toolbar :as toolbar] [status-im.ui.components.topbar :as topbar] [status-im.ui.screens.group.styles :as styles] + [quo.core :as quo] [utils.debounce :as debounce]) (:require-macros [status-im.utils.views :as views])) -(defn- render-contact - [row] +(defn- render-contact [row] (let [[first-name second-name] (multiaccounts/contact-two-names row false)] [quo/list-item {:title first-name @@ -27,8 +28,7 @@ :icon [chat-icon/contact-icon-contacts-tab (multiaccounts/displayed-photo row)]}])) -(defn- on-toggle - [allow-new-users? checked? public-key] +(defn- on-toggle [allow-new-users? checked? public-key] (cond checked? @@ -39,8 +39,7 @@ allow-new-users?) (re-frame/dispatch [:select-contact public-key allow-new-users?]))) -(defn- on-toggle-participant - [allow-new-users? checked? public-key] +(defn- on-toggle-participant [allow-new-users? checked? public-key] (cond checked? @@ -51,229 +50,196 @@ allow-new-users?) (re-frame/dispatch [:select-participant public-key allow-new-users?]))) -(defn- toggle-item - [] +(defn- toggle-item [] (fn [allow-new-users? subs-name {:keys [public-key] :as contact} on-toggle] - (let [contact-selected? @(re-frame/subscribe [subs-name public-key]) + (let [contact-selected? @(re-frame/subscribe [subs-name public-key]) [first-name second-name] (multiaccounts/contact-two-names contact true)] [quo/list-item {:title first-name :subtitle second-name :icon [chat-icon/contact-icon-contacts-tab - (multiaccounts/displayed-photo contact)] + (multiaccounts/displayed-photo contact)] :on-press #(on-toggle allow-new-users? contact-selected? public-key) :active contact-selected? :accessory :checkbox}]))) -(defn- group-toggle-contact - [contact _ _ allow-new-users?] +(defn- group-toggle-contact [contact _ _ allow-new-users?] [toggle-item allow-new-users? :is-contact-selected? contact on-toggle]) -(defn- group-toggle-participant - [contact _ _ allow-new-users?] +(defn- group-toggle-participant [contact _ _ allow-new-users?] [toggle-item allow-new-users? :is-participant-selected? contact on-toggle-participant]) -(defn toggle-list - [{:keys [contacts render-fn render-data]}] - [list/flat-list - {:data contacts - :key-fn :public-key - :render-data render-data - :render-fn render-fn - :keyboardShouldPersistTaps :always}]) +(defn toggle-list [{:keys [contacts render-fn render-data]}] + [list/flat-list {:data contacts + :key-fn :public-key + :render-data render-data + :render-fn render-fn + :keyboardShouldPersistTaps :always}]) -(defn no-contacts - [{:keys [no-contacts]}] +(defn no-contacts [{:keys [no-contacts]}] [react/view {:style styles/no-contacts} [react/text {:style (styles/no-contact-text)} no-contacts] [invite/button]]) -(defn filter-contacts - [filter-text contacts] +(defn filter-contacts [filter-text contacts] (let [lower-filter-text (string/lower-case (str filter-text)) filter-fn (fn [{:keys [name alias nickname]}] (or - (string/includes? (string/lower-case (str name)) lower-filter-text) - (string/includes? (string/lower-case (str alias)) lower-filter-text) - (when nickname - (string/includes? (string/lower-case (str nickname)) - lower-filter-text))))] + (string/includes? (string/lower-case (str name)) lower-filter-text) + (string/includes? (string/lower-case (str alias)) lower-filter-text) + (when nickname + (string/includes? (string/lower-case (str nickname)) lower-filter-text))))] (if filter-text (filter filter-fn contacts) contacts))) ;; Set name of new group-chat -(views/defview new-group - [] - (views/letsubs [contacts [:selected-group-contacts] - group-name [:new-chat-name]] - (let [group-name-empty? (not (and (string? group-name) (not-empty group-name)))] - [react/keyboard-avoiding-view - {:style styles/group-container - :ignore-offset true} - [react/view {:flex 1} - [topbar/topbar - {:use-insets false - :title (i18n/label :t/new-group-chat) - :subtitle (i18n/label :t/group-chat-members-count - {:selected (inc (count contacts)) - :max constants/max-group-chat-participants})}] - [react/view - {:style {:padding-top 16 - :flex 1}} - [react/view {:style {:padding-horizontal 16}} - [quo/text-input - {:auto-focus true - :on-change-text #(re-frame/dispatch [:set :new-chat-name %]) - :default-value group-name - :placeholder (i18n/label :t/set-a-topic) - :accessibility-label :chat-name-input}] - [react/text {:style (styles/members-title)} - (i18n/label :t/members-title)]] - [react/view - {:style {:margin-top 8 - :flex 1}} - [list/flat-list - {:data contacts - :key-fn :address - :render-fn render-contact - :bounces false - :keyboard-should-persist-taps :always - :enable-empty-sections true}]]] - [toolbar/toolbar - {:show-border? true - :left - [quo/button - {:type :secondary - :before :main-icon/back - :accessibility-label :previous-button - :on-press #(re-frame/dispatch [:navigate-back])} - (i18n/label :t/back)] - :right - [quo/button - {:type :secondary - :accessibility-label :create-group-chat-button - :disabled group-name-empty? - :on-press #(debounce/dispatch-and-chill [:group-chats.ui/create-pressed - group-name] - 300)} - (i18n/label :t/create-group-chat)]}]]]))) +(views/defview new-group [] + (views/letsubs [contacts [:selected-group-contacts] + group-name [:new-chat-name]] + (let [group-name-empty? (not (and (string? group-name) (not-empty group-name)))] + [react/keyboard-avoiding-view {:style styles/group-container + :ignore-offset true} + [react/view {:flex 1} + [topbar/topbar {:use-insets false + :title (i18n/label :t/new-group-chat) + :subtitle (i18n/label :t/group-chat-members-count + {:selected (inc (count contacts)) + :max constants/max-group-chat-participants})}] + [react/view {:style {:padding-top 16 + :flex 1}} + [react/view {:style {:padding-horizontal 16}} + [quo/text-input + {:auto-focus true + :on-change-text #(re-frame/dispatch [:set :new-chat-name %]) + :default-value group-name + :placeholder (i18n/label :t/set-a-topic) + :accessibility-label :chat-name-input}] + [react/text {:style (styles/members-title)} + (i18n/label :t/members-title)]] + [react/view {:style {:margin-top 8 + :flex 1}} + [list/flat-list {:data contacts + :key-fn :address + :render-fn render-contact + :bounces false + :keyboard-should-persist-taps :always + :enable-empty-sections true}]]] + [toolbar/toolbar + {:show-border? true + :left + [quo/button {:type :secondary + :before :main-icon/back + :accessibility-label :previous-button + :on-press #(re-frame/dispatch [:navigate-back])} + (i18n/label :t/back)] + :right + [quo/button {:type :secondary + :accessibility-label :create-group-chat-button + :disabled group-name-empty? + :on-press #(debounce/dispatch-and-chill [:group-chats.ui/create-pressed group-name] + 300)} + (i18n/label :t/create-group-chat)]}]]]))) -(defn searchable-contact-list - [] +(defn searchable-contact-list [] (let [search-value (reagent/atom nil)] (fn [{:keys [contacts no-contacts-label toggle-fn allow-new-users?]}] [react/view {:style {:flex 1}} [react/view {:style (styles/search-container)} - [search/search-input-old - {:on-cancel #(reset! search-value nil) - :on-change #(reset! search-value %)}]] - [react/view - {:style {:flex 1 - :padding-vertical 8}} + [search/search-input-old {:on-cancel #(reset! search-value nil) + :on-change #(reset! search-value %)}]] + [react/view {:style {:flex 1 + :padding-vertical 8}} (if (seq contacts) - [toggle-list - {:contacts (filter-contacts @search-value contacts) - :render-data allow-new-users? - :render-fn toggle-fn}] + [toggle-list {:contacts (filter-contacts @search-value contacts) + :render-data allow-new-users? + :render-fn toggle-fn}] [no-contacts {:no-contacts no-contacts-label}])]]))) ;; Start group chat -(views/defview contact-toggle-list - [] - (views/letsubs [contacts [:contacts/active] - selected-contacts-count [:selected-contacts-count]] - [react/keyboard-avoiding-view - {:style styles/group-container - :ignore-offset true} - [topbar/topbar - {:use-insets false - :border-bottom false - :title (i18n/label :t/new-group-chat) - :subtitle (i18n/label :t/group-chat-members-count - {:selected (inc selected-contacts-count) - :max constants/max-group-chat-participants})}] - [searchable-contact-list - {:contacts contacts - :no-contacts-label (i18n/label :t/group-chat-no-contacts) - :toggle-fn group-toggle-contact - :allow-new-users? (< selected-contacts-count - (dec constants/max-group-chat-participants))}] - [toolbar/toolbar - {:show-border? true - :right - [quo/button - {:type :secondary - :after :main-icon/next - :accessibility-label :next-button - :on-press #(re-frame/dispatch [:navigate-to :new-group])} - (i18n/label :t/next)]}]])) +(views/defview contact-toggle-list [] + (views/letsubs [contacts [:contacts/active] + selected-contacts-count [:selected-contacts-count]] + [react/keyboard-avoiding-view {:style styles/group-container + :ignore-offset true} + [topbar/topbar {:use-insets false + :border-bottom false + :title (i18n/label :t/new-group-chat) + :subtitle (i18n/label :t/group-chat-members-count + {:selected (inc selected-contacts-count) + :max constants/max-group-chat-participants})}] + [searchable-contact-list + {:contacts contacts + :no-contacts-label (i18n/label :t/group-chat-no-contacts) + :toggle-fn group-toggle-contact + :allow-new-users? (< selected-contacts-count + (dec constants/max-group-chat-participants))}] + [toolbar/toolbar + {:show-border? true + :right + [quo/button {:type :secondary + :after :main-icon/next + :accessibility-label :next-button + :on-press #(re-frame/dispatch [:navigate-to :new-group])} + (i18n/label :t/next)]}]])) ;; Add participants to existing group chat -(views/defview add-participants-toggle-list - [] - (views/letsubs [contacts [:contacts/all-contacts-not-in-current-chat] - current-chat [:chats/current-chat] - selected-contacts-count [:selected-participants-count]] - (let [current-participants-count (count (:contacts current-chat))] - [kb-presentation/keyboard-avoiding-view {:style styles/group-container} - [topbar/topbar - {:use-insets false - :border-bottom false - :title (i18n/label :t/add-members) - :subtitle (i18n/label :t/group-chat-members-count - {:selected (+ current-participants-count selected-contacts-count) - :max constants/max-group-chat-participants})}] - [searchable-contact-list - {:contacts contacts - :no-contacts-label (i18n/label :t/group-chat-all-contacts-invited) - :toggle-fn group-toggle-participant - :allow-new-users? (< (+ current-participants-count - selected-contacts-count) - constants/max-group-chat-participants)}] - [toolbar/toolbar - {:show-border? true - :center - [quo/button - {:type :secondary - :accessibility-label :next-button - :disabled (zero? selected-contacts-count) - :on-press #(re-frame/dispatch [:group-chats.ui/add-members-pressed])} - (i18n/label :t/add)]}]]))) +(views/defview add-participants-toggle-list [] + (views/letsubs [contacts [:contacts/all-contacts-not-in-current-chat] + current-chat [:chats/current-chat] + selected-contacts-count [:group-chat/selected-participants-count]] + (let [current-participants-count (count (:contacts current-chat))] + [kb-presentation/keyboard-avoiding-view {:style styles/group-container} + [topbar/topbar {:use-insets false + :border-bottom false + :title (i18n/label :t/add-members) + :subtitle (i18n/label :t/group-chat-members-count + {:selected (+ current-participants-count selected-contacts-count) + :max constants/max-group-chat-participants})}] + [searchable-contact-list + {:contacts contacts + :no-contacts-label (i18n/label :t/group-chat-all-contacts-invited) + :toggle-fn group-toggle-participant + :allow-new-users? (< (+ current-participants-count + selected-contacts-count) + constants/max-group-chat-participants)}] + [toolbar/toolbar + {:show-border? true + :center + [quo/button {:type :secondary + :accessibility-label :next-button + :disabled (zero? selected-contacts-count) + :on-press #(re-frame/dispatch [:group-chats.ui/add-members-pressed])} + (i18n/label :t/add)]}]]))) -(views/defview edit-group-chat-name - [] - (views/letsubs [{:keys [name chat-id]} [:chats/current-chat] - new-group-chat-name (reagent/atom nil)] - [kb-presentation/keyboard-avoiding-view {:style styles/group-container} - [react/scroll-view - {:style {:padding 16 - :flex 1}} - [quo/text-input - {:on-change-text #(reset! new-group-chat-name %) - :default-value name - :on-submit-editing #(when (seq @new-group-chat-name) - (re-frame/dispatch [:group-chats.ui/name-changed chat-id - @new-group-chat-name])) - :placeholder (i18n/label :t/enter-contact-code) - :accessibility-label :new-chat-name - :return-key-type :go}]] - [react/view {:style {:flex 1}}] - [toolbar/toolbar - {:show-border? true - :center - [quo/button - {:type :secondary - :accessibility-label :done - :disabled (and (<= (count @new-group-chat-name) 1) - (not (nil? @new-group-chat-name))) - :on-press #(cond - (< 1 (count @new-group-chat-name)) - (re-frame/dispatch [:group-chats.ui/name-changed chat-id - @new-group-chat-name]) +(views/defview edit-group-chat-name [] + (views/letsubs [{:keys [name chat-id]} [:chats/current-chat] + new-group-chat-name (reagent/atom nil)] + [kb-presentation/keyboard-avoiding-view {:style styles/group-container} + [react/scroll-view {:style {:padding 16 + :flex 1}} + [quo/text-input + {:on-change-text #(reset! new-group-chat-name %) + :default-value name + :on-submit-editing #(when (seq @new-group-chat-name) + (re-frame/dispatch [:group-chats.ui/name-changed chat-id @new-group-chat-name])) + :placeholder (i18n/label :t/enter-contact-code) + :accessibility-label :new-chat-name + :return-key-type :go}]] + [react/view {:style {:flex 1}}] + [toolbar/toolbar + {:show-border? true + :center + [quo/button {:type :secondary + :accessibility-label :done + :disabled (and (<= (count @new-group-chat-name) 1) + (not (nil? @new-group-chat-name))) + :on-press #(cond + (< 1 (count @new-group-chat-name)) + (re-frame/dispatch [:group-chats.ui/name-changed chat-id @new-group-chat-name]) - (nil? @new-group-chat-name) - (re-frame/dispatch [:navigate-back]))} - (i18n/label :t/done)]}]])) + (nil? @new-group-chat-name) + (re-frame/dispatch [:navigate-back]))} + (i18n/label :t/done)]}]])) diff --git a/src/status_im/ui2/screens/chat/group_details/style.cljs b/src/status_im/ui2/screens/chat/group_details/style.cljs index 8538f853aa..73eca51b8f 100644 --- a/src/status_im/ui2/screens/chat/group_details/style.cljs +++ b/src/status_im/ui2/screens/chat/group_details/style.cljs @@ -1,5 +1,6 @@ (ns status-im.ui2.screens.chat.group-details.style - (:require [quo2.foundations.colors :as colors])) + (:require [quo2.foundations.colors :as colors] + [react-native.platform :as platform])) (defn actions-view [] @@ -33,12 +34,15 @@ :align-items :center :margin-bottom 24}) -(def bottom-container - {:position :absolute +(defn bottom-container [safe-area] + { + ;:position :absolute :padding-horizontal 20 :padding-vertical 12 - :padding-bottom 33 + :padding-bottom (+ 33 (if platform/ios? (:bottom safe-area) 0)) :width "100%" :background-color colors/white :flex-direction :row - :bottom 0}) + ;:padding-bottom (if platform/ios? (:bottom safe-area) 0) + :margin-bottom (if platform/ios? 0 70) + }) diff --git a/src/status_im/ui2/screens/chat/group_details/view.cljs b/src/status_im/ui2/screens/chat/group_details/view.cljs index 1df6b57e00..09623fae4a 100644 --- a/src/status_im/ui2/screens/chat/group_details/view.cljs +++ b/src/status_im/ui2/screens/chat/group_details/view.cljs @@ -1,176 +1,139 @@ (ns status-im.ui2.screens.chat.group-details.view - (:require [i18n.i18n :as i18n] - [oops.core :refer [oget]] - [quo.components.safe-area :as safe-area] - [quo2.core :as quo2] + (:require [react-native.core :as rn] [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-im.ui2.screens.common.contact-list.view :as contact-list] + [quo2.core :as quo2] + [utils.re-frame :as rf] + [i18n.i18n :as i18n] + [status-im.chat.models :as chat.models] [status-im2.common.contact-list-item.view :as contact-list-item] + [quo.components.safe-area :as safe-area] [status-im2.common.home.actions.view :as actions] - [utils.re-frame :as rf])) + [status-im.ui2.screens.common.contact-list.view :as contact-list])) -(defn back-button - [] - [quo2/button - {:type :grey - :size 32 - :width 32 - :style {:margin-left 20} - :accessibility-label :back-button - :on-press #(rf/dispatch [:navigate-back])} +(defn back-button [] + [quo2/button {:type :grey + :size 32 + :width 32 + :style {:margin-left 20} + :accessibility-label :back-button + :on-press #(rf/dispatch [:navigate-back])} [quo2/icon :i/arrow-left {:color (colors/theme-colors colors/neutral-100 colors/white)}]]) -(defn options-button - [] +(defn options-button [] (let [group (rf/sub [:chats/current-chat])] - [quo2/button - {:type :grey - :size 32 - :width 32 - :style {:margin-right 20} - :accessibility-label :options-button - :on-press #(rf/dispatch [:bottom-sheet/show-sheet - {:content (fn [] [actions/group-details-actions group])}])} + [quo2/button {:type :grey + :size 32 + :width 32 + :style {:margin-right 20} + :accessibility-label :options-button + :on-press #(rf/dispatch [:bottom-sheet/show-sheet + {:content (fn [] [actions/group-details-actions group])}])} [quo2/icon :i/options {:color (colors/theme-colors colors/neutral-100 colors/white)}]])) -(defn top-buttons - [] - [rn/view - {:style {:flex-direction :row - :padding-horizontal 20 - :justify-content :space-between}} +(defn top-buttons [] + [rn/view {:style {:flex-direction :row + :padding-horizontal 20 + :justify-content :space-between}} [back-button] [options-button]]) -(defn count-container - [count] - [rn/view {:style (style/count-container)} - [quo2/text - {:size :label - :weight :medium - :style {:text-align :center}} count]]) +(defn count-container [count accessibility-label] + [rn/view {:style (style/count-container) + :accessibility-label accessibility-label} + [quo2/text {:size :label + :weight :medium + :style {:text-align :center}} count]]) -(defn contacts-section-header - [{:keys [title]}] - [rn/view - {:style {:padding-horizontal 20 - :border-top-width 1 - :border-top-color colors/neutral-20 - :padding-vertical 8 - :margin-top 8}} - [quo2/text - {:size :paragraph-2 - :weight :medium - :style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}} title]]) +(defn contacts-section-header [{:keys [title]}] + [rn/view {:style {:padding-horizontal 20 :border-top-width 1 :border-top-color colors/neutral-20 :padding-vertical 8 :margin-top 8}} + [quo2/text {:size :paragraph-2 + :weight :medium + :style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}} title]]) -(def added (reagent/atom ())) +(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 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 group-details - [] - (let [{:keys [admins chat-id chat-name color public? muted contacts] :as group} (rf/sub - [:chats/current-chat]) - members (rf/sub [:contacts/group-members-sections]) +(defn group-details [] + (let [{:keys [admins chat-id chat-name color public? muted contacts] :as group} (rf/sub [:chats/current-chat]) + members (rf/sub [:contacts/group-members-sections]) pinned-messages (rf/sub [:chats/pinned chat-id]) - current-pk (rf/sub [:multiaccount/public-key]) - admin? (get admins current-pk)] - [rn/view - {:style {:flex 1 - :background-color (colors/theme-colors colors/white colors/neutral-95)}} - [quo2/header - {:left-component [back-button] - :right-component [options-button] - :background (colors/theme-colors colors/white colors/neutral-95)}] - [rn/view - {:style {:flex-direction :row - :margin-top 24 - :padding-horizontal 20}} - [quo2/group-avatar - {:color color - :size :medium}] - [quo2/text - {:weight :semi-bold - :size :heading-1 - :style {:margin-horizontal 8}} chat-name] + current-pk (rf/sub [:multiaccount/public-key]) + admin? (get admins current-pk)] + [rn/view {:style {:flex 1 + :background-color (colors/theme-colors colors/white colors/neutral-95)}} + [quo2/header {:left-component [back-button] + :right-component [options-button] + :background (colors/theme-colors colors/white colors/neutral-95)}] + [rn/view {:style {:flex-direction :row + :margin-top 24 + :padding-horizontal 20}} + [quo2/group-avatar {:color color + :size :medium}] + [quo2/text {:weight :semi-bold + :size :heading-1 + :style {:margin-horizontal 8}} chat-name] [rn/view {:style {:margin-top 8}} - [quo2/icon (if public? :i/world :i/privacy) - {:size 20 :color (colors/theme-colors colors/neutral-50 colors/neutral-40)}]]] + [quo2/icon (if public? :i/world :i/privacy) {: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]))} - [rn/view - {:style {:flex-direction :row - :justify-content :space-between}} + [rn/touchable-opacity {: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)]] - [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)])} - [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])}])} - [rn/view - {:style {:flex-direction :row - :justify-content :space-between}} + [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) + :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) + :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)]] - [quo2/text {:style {:margin-top 16} :size :paragraph-1 :weight :medium} - (i18n/label (if admin? :t/manage-members :t/add-members))]]] - [rn/section-list - {:key-fn :title - :sticky-section-headers-enabled false - :sections members - :render-section-header-fn contacts-section-header - :render-fn contact-list-item/contact-list-item - :render-data {:chat-id chat-id - :admin? admin? - :icon :options}}]])) + [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 {:key-fn :title + :sticky-section-headers-enabled false + :sections members + :render-section-header-fn contacts-section-header + :render-fn contact-list-item/contact-list-item + :render-data {:chat-id chat-id + :admin? admin? + :icon :options}}]])) 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 f67b783428..4ba3416e6c 100644 --- a/src/status_im/ui2/screens/common/contact_list/view.cljs +++ b/src/status_im/ui2/screens/common/contact_list/view.cljs @@ -1,21 +1,19 @@ (ns status-im.ui2.screens.common.contact-list.view - (:require [quo2.core :as quo] - [react-native.core :as rn] + (:require [react-native.core :as rn] [status-im2.common.contact-list-item.view :as contact-list-item] - [utils.re-frame :as rf])) + [utils.re-frame :as rf] + [quo2.core :as quo])) -(defn contacts-section-header - [{:keys [title]}] +(defn contacts-section-header [{:keys [title]}] [quo/divider-label {:label title}]) -(defn contact-list - [data] - (let [contacts (rf/sub [:contacts/filtered-active-sections])] +(defn contact-list [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} - :render-data data - :render-fn contact-list-item/contact-list-item}])) + :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 357925b390..5bf55c7d61 100644 --- a/src/status_im2/common/contact_list_item/view.cljs +++ b/src/status_im2/common/contact_list_item/view.cljs @@ -1,15 +1,14 @@ (ns status-im2.common.contact-list-item.view - (:require [quo2.core :as quo] - [quo2.foundations.colors :as colors] + (:require [quo2.foundations.colors :as colors] + [status-im2.contexts.chat.home.chat-list-item.style :as style] + [utils.re-frame :as rf] + [status-im2.common.home.actions.view :as actions] + [quo2.core :as quo] [react-native.core :as rn] [react-native.platform :as platform] - [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.address :as utils.address])) -(defn open-chat - [chat-id] +(defn open-chat [chat-id] (let [view-id (rf/sub [:view-id])] (when (= view-id :shell-stack) (rf/dispatch [:dismiss-keyboard]) @@ -18,62 +17,58 @@ (rf/dispatch [:chat.ui/navigate-to-chat chat-id])) (rf/dispatch [:search/home-filter-changed nil])))) -(defn action-icon - [{:keys [public-key] :as item} {:keys [icon group added] :as extra-data}] - (let [{:keys [contacts]} group - member? (contains? contacts public-key)] - [rn/touchable-opacity - {:on-press #(rf/dispatch [:bottom-sheet/show-sheet - {:content (fn [] [actions/actions item extra-data])}]) - :style {:position :absolute - :right 20}} +(defn action-icon [{:keys [public-key] :as item} {:keys [icon group] :as extra-data}] + (let [{:keys [contacts admins]} group + member? (contains? contacts public-key) + current-pk (rf/sub [:multiaccount/public-key]) + admin? (get admins current-pk)] + [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)}] - [quo/checkbox - {:default-checked? member? - :on-change (fn [selected] - (if selected - (swap! added conj public-key) - (reset! added (remove #(= % public-key) @added))))}])])) + [quo/checkbox {:default-checked? member? + :accessibility-label :contact-toggle-check + :disabled? (and member? (not admin?)) + :on-change (fn [selected] + (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 _ _ extra-data] +(defn contact-list-item [item _ _ extra-data] (let [{:keys [public-key ens-verified added? images]} item - display-name (first (rf/sub - [:contacts/contact-two-names-by-identity - public-key])) - photo-path (when (seq images) - (rf/sub [:chats/photo-path public-key])) - current-pk (rf/sub [:multiaccount/public-key])] - [rn/touchable-opacity - (merge {:style (style/container) - :active-opacity 1 - :on-press #(open-chat public-key) - :on-long-press #(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 - :size :small - :ring? false}] + display-name (first (rf/sub [:contacts/contact-two-names-by-identity public-key])) + 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])] + [rn/touchable-opacity (merge {:style (style/container) + :accessibility-label :contact + :active-opacity 1 + :on-press #(open-chat public-key) + :on-long-press #(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? 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}} - [quo/icon :i/verified - {:no-color true :size 12 :color (colors/theme-colors colors/success-50 colors/success-60)}]] + [quo/icon :i/verified {: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}} - [quo/icon :i/contact - {:no-color true - :size 12 - :color (colors/theme-colors colors/primary-50 colors/primary-60)}]]))] - [quo/text - {:size :paragraph-1 - :style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}} + [quo/icon :i/contact {:no-color true :size 12 :color (colors/theme-colors colors/primary-50 colors/primary-60)}]]))] + [quo/text {:size :paragraph-1 + :style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}} (utils.address/get-shortened-address public-key)]] (when-not (= current-pk public-key) [action-icon item extra-data])])) diff --git a/src/status_im2/common/home/actions/view.cljs b/src/status_im2/common/home/actions/view.cljs index 592eb01643..d7c9f1a829 100644 --- a/src/status_im2/common/home/actions/view.cljs +++ b/src/status_im2/common/home/actions/view.cljs @@ -1,14 +1,14 @@ (ns status-im2.common.home.actions.view (:require [i18n.i18n :as i18n] + [utils.re-frame :as rf] [quo2.components.drawers.action-drawers :as drawer] - [status-im.chat.models :as chat.models] - [status-im2.common.confirmation-drawer.view :as confirmation-drawer] ;;TODO move to - ;;status-im2 - [status-im2.common.constants :as constants] - [utils.re-frame :as rf])) + [status-im2.common.confirmation-drawer.view :as confirmation-drawer] -(defn- entry - [{:keys [icon label on-press danger? sub-label chevron? add-divider?]}] + ;;TODO move to status-im2 + [status-im2.common.constants :as constants] + [status-im.chat.models :as chat.models])) + +(defn- entry [{:keys [icon label on-press danger? sub-label chevron? add-divider? accessibility-label]}] {:pre [(keyword? icon) (string? label) (fn? on-press) @@ -20,94 +20,73 @@ :danger? danger? :sub-label sub-label :right-icon (when chevron? :i/chevron-right) - :add-divider? add-divider?}) + :add-divider? add-divider? + :accessibility-label accessibility-label}) -(defn hide-sheet-and-dispatch - [event] +(defn hide-sheet-and-dispatch [event] (rf/dispatch [:bottom-sheet/hide]) (rf/dispatch event)) -(defn show-profile-action - [chat-id] +(defn show-profile-action [chat-id] (hide-sheet-and-dispatch [:chat.ui/show-profile chat-id]) (rf/dispatch [:pin-message/load-pin-messages chat-id])) -(defn mark-all-read-action - [chat-id] +(defn mark-all-read-action [chat-id] (hide-sheet-and-dispatch [:chat/mark-all-as-read chat-id])) -(defn edit-nickname-action - [chat-id] +(defn edit-nickname-action [chat-id] (hide-sheet-and-dispatch [:chat.ui/edit-nickname chat-id])) -(defn mute-chat-action - [chat-id] +(defn mute-chat-action [chat-id] (hide-sheet-and-dispatch [::chat.models/mute-chat-toggled chat-id true])) -(defn unmute-chat-action - [chat-id] +(defn unmute-chat-action [chat-id] (hide-sheet-and-dispatch [::chat.models/mute-chat-toggled chat-id false])) -(defn clear-history-action - [{:keys [chat-id] :as item}] - (hide-sheet-and-dispatch - [:bottom-sheet/show-sheet - {:content (fn [] - (confirmation-drawer/confirmation-drawer - {:title (i18n/label :t/clear-history?) - :description (i18n/label :t/clear-history-confirmation-content) - :context item - :button-text (i18n/label :t/clear-history) - :on-press #(hide-sheet-and-dispatch [:chat.ui/clear-history chat-id])}))}])) +(defn clear-history-action [{:keys [chat-id] :as item}] + (hide-sheet-and-dispatch [:bottom-sheet/show-sheet + {:content (fn [] + (confirmation-drawer/confirmation-drawer {:title (i18n/label :t/clear-history?) + :description (i18n/label :t/clear-history-confirmation-content) + :context item + :button-text (i18n/label :t/clear-history) + :on-press #(hide-sheet-and-dispatch [:chat.ui/clear-history chat-id])}))}])) -(defn delete-chat-action - [{:keys [chat-id] :as item}] - (hide-sheet-and-dispatch - [:bottom-sheet/show-sheet - {:content (fn [] - (confirmation-drawer/confirmation-drawer - {:title (i18n/label :t/delete-chat?) - :description (i18n/label :t/delete-chat-confirmation) - :context item - :button-text (i18n/label :t/delete-chat) - :on-press #(hide-sheet-and-dispatch [:chat.ui/remove-chat chat-id])}))}])) +(defn delete-chat-action [{:keys [chat-id] :as item}] + (hide-sheet-and-dispatch [:bottom-sheet/show-sheet + {:content (fn [] + (confirmation-drawer/confirmation-drawer {:title (i18n/label :t/delete-chat?) + :description (i18n/label :t/delete-chat-confirmation) + :context item + :button-text (i18n/label :t/delete-chat) + :on-press #(hide-sheet-and-dispatch [:chat.ui/remove-chat chat-id])}))}])) -(defn leave-group-action - [item chat-id] - (hide-sheet-and-dispatch - [:bottom-sheet/show-sheet - {:content (fn [] - (confirmation-drawer/confirmation-drawer - {:title (i18n/label :t/leave-group?) - :description (i18n/label :t/leave-chat-confirmation) - :context item - :button-text (i18n/label :t/leave-group) - :on-press #(do - (rf/dispatch [:navigate-back]) - (hide-sheet-and-dispatch [:group-chats.ui/leave-chat-confirmed - chat-id]))}))}])) +(defn leave-group-action [item chat-id] + (hide-sheet-and-dispatch [:bottom-sheet/show-sheet + {:content (fn [] + (confirmation-drawer/confirmation-drawer {:title (i18n/label :t/leave-group?) + :description (i18n/label :t/leave-chat-confirmation) + :context item + :button-text (i18n/label :t/leave-group) + :on-press #(do + (rf/dispatch [:navigate-back]) + (hide-sheet-and-dispatch [:group-chats.ui/leave-chat-confirmed chat-id]))}))}])) -(defn block-user-action - [{:keys [public-key] :as item}] - (hide-sheet-and-dispatch - [:bottom-sheet/show-sheet - {:content (fn [] - (confirmation-drawer/confirmation-drawer - {:title (i18n/label :t/block-user?) - :description (i18n/label :t/block-contact-details) - :context item - :button-text (i18n/label :t/block-user) - :on-press #(hide-sheet-and-dispatch [:contact.ui/block-contact-confirmed - public-key])}))}])) +(defn block-user-action [{:keys [public-key] :as item}] + (hide-sheet-and-dispatch [:bottom-sheet/show-sheet + {:content (fn [] (confirmation-drawer/confirmation-drawer {:title (i18n/label :t/block-user?) + :description (i18n/label :t/block-contact-details) + :context item + :button-text (i18n/label :t/block-user) + :on-press #(hide-sheet-and-dispatch [:contact.ui/block-contact-confirmed public-key])}))}])) -(defn mute-chat-entry - [chat-id] +(defn mute-chat-entry [chat-id] (let [muted? (rf/sub [:chats/muted chat-id])] (entry {:icon (if muted? :i/muted :i/activity-center) :label (i18n/label - (if muted? - :unmute-chat - :mute-chat)) + (if muted? + :unmute-chat + :mute-chat)) :on-press (if muted? #(unmute-chat-action chat-id) #(mute-chat-action chat-id)) @@ -115,8 +94,7 @@ :sub-label nil :chevron? true}))) -(defn mark-as-read-entry - [chat-id] +(defn mark-as-read-entry [chat-id] (entry {:icon :i/correct :label (i18n/label :t/mark-as-read) :on-press #(mark-all-read-action chat-id) @@ -125,8 +103,7 @@ :chevron? false :add-divider? true})) -(defn clear-history-entry - [chat-id] +(defn clear-history-entry [chat-id] (entry {:icon :i/delete :label (i18n/label :t/clear-history) :on-press #(clear-history-action chat-id) @@ -135,8 +112,7 @@ :chevron? false :add-divider? true})) -(defn delete-chat-entry - [item] +(defn delete-chat-entry [item] (entry {:icon :i/delete :label (i18n/label :t/delete-chat) :on-press #(delete-chat-action item) @@ -144,8 +120,7 @@ :sub-label nil :chevron? false})) -(defn leave-group-entry - [item extra-data] +(defn leave-group-entry [item extra-data] (entry {:icon :i/log-out :label (i18n/label :t/leave-group) :on-press #(leave-group-action item (if extra-data (:chat-id extra-data) (:chat-id item))) @@ -154,8 +129,7 @@ :chevron? false :add-divider? extra-data})) -(defn view-profile-entry - [chat-id] +(defn view-profile-entry [chat-id] (entry {:icon :i/friend :label (i18n/label :t/view-profile) :on-press #(show-profile-action chat-id) @@ -163,8 +137,7 @@ :sub-label nil :chevron? false})) -(defn edit-nickname-entry - [chat-id] +(defn edit-nickname-entry [chat-id] (entry {:icon :i/edit :label (i18n/label :t/edit-nickname) :on-press #(edit-nickname-action chat-id) @@ -173,8 +146,7 @@ :chevron? false})) ;; TODO(OmarBasem): Requires design input. -(defn edit-name-image-entry - [] +(defn edit-name-image-entry [] (entry {:icon :i/edit :label (i18n/label :t/edit-name-and-image) :on-press #(js/alert "TODO: to be implemented, requires design input") @@ -183,8 +155,7 @@ :chevron? false})) ;; TODO(OmarBasem): Requires design input. -(defn notifications-entry - [add-divider?] +(defn notifications-entry [add-divider?] (entry {:icon :i/notifications :label (i18n/label :t/notifications) :on-press #(js/alert "TODO: to be implemented, requires design input") @@ -194,8 +165,7 @@ :add-divider? add-divider?})) ;; TODO(OmarBasem): Requires design input. -(defn fetch-messages-entry - [] +(defn fetch-messages-entry [] (entry {:icon :i/save :label (i18n/label :t/fetch-messages) :on-press #(js/alert "TODO: to be implemented, requires design input") @@ -204,8 +174,7 @@ :chevron? true})) ;; TODO(OmarBasem): Requires design input. -(defn pinned-messages-entry - [] +(defn pinned-messages-entry [] (entry {:icon :i/pin :label (i18n/label :t/pinned-messages) :on-press #(js/alert "TODO: to be implemented, requires design input") @@ -213,8 +182,7 @@ :sub-label nil :chevron? true})) -(defn remove-from-contacts-entry - [contact] +(defn remove-from-contacts-entry [contact] (entry {:icon :i/remove-user :label (i18n/label :t/remove-from-contacts) :on-press #(hide-sheet-and-dispatch [:contact.ui/remove-contact-pressed contact]) @@ -223,8 +191,7 @@ :chevron? false})) ;; TODO(OmarBasem): Requires design input. -(defn rename-entry - [] +(defn rename-entry [] (entry {:icon :i/edit :label (i18n/label :t/rename) :on-press #(js/alert "TODO: to be implemented, requires design input") @@ -233,8 +200,7 @@ :chevron? false})) ;; TODO(OmarBasem): Requires design input. -(defn show-qr-entry - [] +(defn show-qr-entry [] (entry {:icon :i/qr-code :label (i18n/label :t/show-qr) :on-press #(js/alert "TODO: to be implemented, requires design input") @@ -243,8 +209,7 @@ :chevron? false})) ;; TODO(OmarBasem): to be implemented. -(defn share-profile-entry - [] +(defn share-profile-entry [] (entry {:icon :i/share :label (i18n/label :t/share-profile) :on-press #(js/alert "TODO: to be implemented") @@ -253,8 +218,7 @@ :chevron? false})) ;; TODO(OmarBasem): to be implemented. -(defn share-group-entry - [] +(defn share-group-entry [] (entry {:icon :i/share :label (i18n/label :t/share) :on-press #(js/alert "TODO: to be implemented") @@ -263,8 +227,7 @@ :chevron? false})) ;; TODO(OmarBasem): Requires status-go impl. -(defn mark-untrustworthy-entry - [] +(defn mark-untrustworthy-entry [] (entry {:icon :i/alert :label (i18n/label :t/mark-untrustworthy) :on-press #(js/alert "TODO: to be implemented, requires status-go impl.") @@ -273,8 +236,7 @@ :chevron? false :add-divider? true})) -(defn block-user-entry - [item] +(defn block-user-entry [item] (entry {:icon :i/block :label (i18n/label :t/block-user) :on-press #(block-user-action item) @@ -282,30 +244,27 @@ :sub-label nil :chevron? false})) -(defn remove-from-group-entry - [{:keys [public-key]} chat-id] +(defn remove-from-group-entry [{:keys [public-key]} chat-id] (let [username (first (rf/sub [:contacts/contact-two-names-by-identity public-key]))] (entry {:icon :i/placeholder :label (i18n/label :t/remove-user-from-group {:username username}) - :on-press #(hide-sheet-and-dispatch [:group-chats.ui/remove-member-pressed chat-id - public-key true]) + :on-press #(hide-sheet-and-dispatch [:group-chats.ui/remove-member-pressed chat-id public-key true]) :danger? true :sub-label nil :chevron? false :add-divider? true}))) -(defn group-details-entry - [chat-id] +(defn group-details-entry [chat-id] (entry {:icon :i/members :label (i18n/label :t/group-details) + :accessibility-label :group-details :on-press #(hide-sheet-and-dispatch [:show-group-chat-profile chat-id]) :danger? false :sub-label nil :chevron? false})) ;; TODO(OmarBasem): to be implemented. -(defn add-members-entry - [] +(defn add-members-entry [] (entry {:icon :i/add-user :label (i18n/label :t/add-members) :on-press #(js/alert "TODO: to be implemented") @@ -314,8 +273,7 @@ :chevron? false})) ;; TODO(OmarBasem): to be implemented. -(defn manage-members-entry - [] +(defn manage-members-entry [] (entry {:icon :i/add-user :label (i18n/label :t/manage-members) :on-press #(js/alert "TODO: to be implemented") @@ -324,8 +282,7 @@ :chevron? false})) ;; TODO(OmarBasem): to be implemented. -(defn edit-group-entry - [] +(defn edit-group-entry [] (entry {:icon :i/edit :label (i18n/label :t/edit-name-and-image) :on-press #(js/alert "TODO: to be implemented") @@ -334,8 +291,7 @@ :chevron? false})) ;; TODO(OmarBasem): to be implemented. -(defn group-privacy-entry - [] +(defn group-privacy-entry [] (entry {:icon :i/privacy :label (i18n/label :t/change-group-privacy) :on-press #(js/alert "TODO: to be implemented") @@ -343,15 +299,13 @@ :sub-label nil :chevron? false})) -(defn destructive-actions - [{:keys [group-chat] :as item}] +(defn destructive-actions [{:keys [group-chat] :as item}] [(clear-history-entry item) (if group-chat (leave-group-entry item nil) (delete-chat-entry item))]) -(defn notification-actions - [{:keys [chat-id group-chat public?]} inside-chat?] +(defn notification-actions [{:keys [chat-id group-chat public?]} inside-chat?] [(mark-as-read-entry chat-id) (mute-chat-entry chat-id) (notifications-entry false) @@ -365,11 +319,12 @@ (when public? (share-group-entry))]) -(defn group-actions - [{:keys [chat-id admins]} inside-chat?] +(defn group-actions [{:keys [chat-id admins]} inside-chat?] (let [current-pub-key (rf/sub [:multiaccount/public-key]) - admin? (get admins current-pub-key)] - [(group-details-entry chat-id) + admin? (get admins current-pub-key) + removed? (rf/sub [:group-chat/removed-from-current-chat?])] + [(when-not removed? + (group-details-entry chat-id)) (when inside-chat? (if admin? (manage-members-entry) @@ -377,48 +332,39 @@ (when (and admin? inside-chat?) (edit-group-entry)) (when (and admin? inside-chat?) (group-privacy-entry))])) -(defn one-to-one-actions - [{:keys [chat-id] :as item} inside-chat?] - [drawer/action-drawer - [[(view-profile-entry chat-id) - (edit-nickname-entry chat-id)] - (notification-actions item inside-chat?) - (destructive-actions item)]]) +(defn one-to-one-actions [{:keys [chat-id] :as item} inside-chat?] + [drawer/action-drawer [[(view-profile-entry chat-id) + (edit-nickname-entry chat-id)] + (notification-actions item inside-chat?) + (destructive-actions item)]]) -(defn public-chat-actions - [{:keys [chat-id] :as item} inside-chat?] - [drawer/action-drawer - [[(group-details-entry chat-id) - (when inside-chat? - (add-members-entry))] - (notification-actions item inside-chat?) - (destructive-actions item)]]) +(defn public-chat-actions [{:keys [chat-id] :as item} inside-chat?] + [drawer/action-drawer [[(group-details-entry chat-id) + (when inside-chat? + (add-members-entry))] + (notification-actions item inside-chat?) + (destructive-actions item)]]) -(defn private-group-chat-actions - [item inside-chat?] - [drawer/action-drawer - [(group-actions item inside-chat?) - (notification-actions item inside-chat?) - (destructive-actions item)]]) +(defn private-group-chat-actions [item inside-chat?] + [drawer/action-drawer [(group-actions item inside-chat?) + (notification-actions item inside-chat?) + (destructive-actions item)]]) -(defn contact-actions - [{:keys [public-key] :as contact} {:keys [chat-id admin?] :as extra-data}] +(defn contact-actions [{:keys [public-key] :as contact} {:keys [chat-id admin?] :as extra-data}] (let [current-pub-key (rf/sub [:multiaccount/public-key])] - [drawer/action-drawer - [[(view-profile-entry public-key) - (remove-from-contacts-entry contact) - (rename-entry) - (show-qr-entry) - (share-profile-entry)] - [(mark-untrustworthy-entry) - (block-user-entry contact)] - (when (and admin? chat-id) - [(if (= current-pub-key public-key) - (leave-group-entry contact extra-data) - (remove-from-group-entry contact chat-id))])]])) + [drawer/action-drawer [[(view-profile-entry public-key) + (remove-from-contacts-entry contact) + (rename-entry) + (show-qr-entry) + (share-profile-entry)] + [(mark-untrustworthy-entry) + (block-user-entry contact)] + (when (and admin? chat-id) + [(if (= current-pub-key public-key) + (leave-group-entry contact extra-data) + (remove-from-group-entry contact chat-id))])]])) -(defn actions - [{:keys [chat-type] :as item} {:keys [inside-chat?] :as extra-data}] +(defn actions [{:keys [chat-type] :as item} {:keys [inside-chat?] :as extra-data}] (case chat-type constants/one-to-one-chat-type [one-to-one-actions item inside-chat?] @@ -428,11 +374,9 @@ [private-group-chat-actions item inside-chat?] [contact-actions item extra-data])) -(defn group-details-actions - [{:keys [admins] :as group}] +(defn group-details-actions [{:keys [admins] :as group}] (let [current-pub-key (rf/sub [:multiaccount/public-key]) - admin? (get admins current-pub-key)] - [drawer/action-drawer - [(when admin? [(edit-name-image-entry)]) - [(notifications-entry admin?)] - (destructive-actions group)]])) + admin? (get admins current-pub-key)] + [drawer/action-drawer [(when admin? [(edit-name-image-entry)]) + [(notifications-entry admin?)] + (destructive-actions group)]])) diff --git a/src/status_im2/contexts/chat/home/contact_request/view.cljs b/src/status_im2/contexts/chat/home/contact_request/view.cljs index b3b54928b9..d615f32e0d 100644 --- a/src/status_im2/contexts/chat/home/contact_request/view.cljs +++ b/src/status_im2/contexts/chat/home/contact_request/view.cljs @@ -19,7 +19,7 @@ {:on-press #(rf/dispatch [:bottom-sheet/hide]) :style (style/contact-requests-sheet)} [quo/icon :i/close]] - [rn/text {:size :heading-1 :weight :semi-bold} + [quo/text {:size :heading-1 :weight :semi-bold} (i18n/label :t/pending-requests)] [quo/tabs {:style {:margin-top 12 :margin-bottom 20} diff --git a/src/status_im2/contexts/chat/messages/view.cljs b/src/status_im2/contexts/chat/messages/view.cljs index 9b36500240..a4147fab24 100644 --- a/src/status_im2/contexts/chat/messages/view.cljs +++ b/src/status_im2/contexts/chat/messages/view.cljs @@ -1,26 +1,27 @@ (ns status-im2.contexts.chat.messages.view - (:require [quo2.core :as quo] + (:require [reagent.core :as reagent] [re-frame.db] + [i18n.i18n :as i18n] [react-native.core :as rn] - [reagent.core :as reagent] - [status-im.ui2.screens.chat.composer.view :as composer] - [status-im.ui2.screens.chat.pin-limit-popover.view :as pin-limit-popover] - [status-im2.common.constants :as constants] - [status-im2.contexts.chat.messages.list.view :as messages.list] - [status-im2.contexts.chat.messages.pin.banner.view :as pin.banner] ;;TODO move to status-im2 - [status-im2.navigation.state :as navigation.state] + [utils.re-frame :as rf] [utils.debounce :as debounce] - [utils.re-frame :as rf])) + [quo2.core :as quo] + [status-im2.common.constants :as constants] + [status-im2.navigation.state :as navigation.state] + [status-im2.contexts.chat.messages.list.view :as messages.list] + [status-im2.contexts.chat.messages.pin.banner.view :as pin.banner] -(defn navigate-back-handler - [] + ;;TODO move to status-im2 + [status-im.ui2.screens.chat.pin-limit-popover.view :as pin-limit-popover] + [status-im.ui2.screens.chat.composer.view :as composer])) + +(defn navigate-back-handler [] (when (and (not @navigation.state/curr-modal) (= (get @re-frame.db/app-db :view-id) :chat)) (rn/hw-back-remove-listener navigate-back-handler) (rf/dispatch [:close-chat]) (rf/dispatch [:navigate-back]))) -(defn page-nav - [] +(defn page-nav [] (let [{:keys [group-chat chat-id chat-name emoji chat-type]} (rf/sub [:chats/current-chat]) display-name (if (= chat-type constants/one-to-one-chat-type) (first (rf/sub [:contacts/contact-two-names-by-identity chat-id])) @@ -29,7 +30,7 @@ contact (when-not group-chat (rf/sub [:contacts/contact-by-address chat-id])) photo-path (when-not (empty? (:images contact)) (rf/sub [:chats/photo-path chat-id]))] [quo/page-nav - {:align-mid? true + {:align-mid? true :mid-section (if group-chat @@ -55,15 +56,14 @@ :icon :i/options :accessibility-label :options-button}]}])) -(defn chat-render - [] +(defn chat-render [] (let [;;we want to react only on these fields, do not use full chat map here - {:keys [chat-id show-input?] :as chat} (rf/sub [:chats/current-chat-chat-view]) - mutual-contact-requests-enabled? (rf/sub [:mutual-contact-requests/enabled?])] + show-input? (not (rf/sub [:group-chat/removed-from-current-chat?])) + {:keys [chat-id] :as chat} (rf/sub [:chats/current-chat-chat-view]) + mutual-contact-requests-enabled? (rf/sub [:mutual-contact-requests/enabled?])] [rn/keyboard-avoiding-view {:style {:flex 1}} [page-nav] - ;; TODO (flexsurfer) this should be in-app notification component in quo2 - ;; https://github.com/status-im/status-mobile/issues/14527 + ;; TODO (flexsurfer) this should be in-app notification component in quo2 https://github.com/status-im/status-mobile/issues/14527 [pin-limit-popover/pin-limit-popover chat-id] [pin.banner/banner chat-id] ;;MESSAGES LIST @@ -74,13 +74,17 @@ :bottom-space 15}] ;;INPUT COMPOSER (when show-input? - [composer/composer chat-id])])) + [composer/composer chat-id]) + [quo/floating-shell-button + {:jump-to {:on-press #(rf/dispatch [:shell/navigate-to-jump-to]) + :label (i18n/label :t/jump-to)}} + {:position :absolute + :bottom 117}]])) -(defn chat - [] +(defn chat [] (reagent/create-class - {:component-did-mount (fn [] - (rn/hw-back-remove-listener navigate-back-handler) - (rn/hw-back-add-listener navigate-back-handler)) - :component-will-unmount (fn [] (rn/hw-back-remove-listener navigate-back-handler)) - :reagent-render chat-render})) + {:component-did-mount (fn [] + (rn/hw-back-remove-listener navigate-back-handler) + (rn/hw-back-add-listener navigate-back-handler)) + :component-will-unmount (fn [] (rn/hw-back-remove-listener navigate-back-handler)) + :reagent-render chat-render})) 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..fdccf03e89 100644 --- a/src/status_im2/contexts/quo_preview/avatars/group_avatar.cljs +++ b/src/status_im2/contexts/quo_preview/avatars/group_avatar.cljs @@ -1,54 +1,48 @@ (ns status-im2.contexts.quo-preview.avatars.group-avatar - (:require [quo2.components.avatars.group-avatar :as quo2] - [quo2.foundations.colors :as colors] + (:require [reagent.core :as reagent] [react-native.core :as rn] - [reagent.core :as reagent] - [status-im2.contexts.quo-preview.preview :as preview])) + [status-im2.contexts.quo-preview.preview :as preview] + [quo2.foundations.colors :as colors] + [quo2.components.avatars.group-avatar :as quo2])) -(def descriptor - [{:label "Size" - :key :size - :type :select - :options [{:key :small - :value "Small"} - {:key :medium - :value "Medium"} - {:key :large - :value "Large"}]} - {:label "Color" - :key :color - :type :select - :options - (map - (fn [c] - {:key c - :value c}) - (keys colors/customization))}]) +(def descriptor [{:label "Size" + :key :size + :type :select + :options [{:key :small + :value "Small"} + {:key :medium + :value "Medium"} + {:key :large + :value "Large"}]} + {:label "Color" + :key :color + :type :select + :options + (map + (fn [c] + {:key c + :value c}) + ["#ff0000" "#0000ff"])}]) ; TODO: this is temporary only. Issue: https://github.com/status-im/status-mobile/issues/14566 -(defn cool-preview - [] +(defn cool-preview [] (let [state (reagent/atom {:theme :light :color :purple - :size :small})] + :size :small})] (fn [] [rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!} [rn/view {:padding-bottom 150} [rn/view {:flex 1} [preview/customizer state descriptor]] - [rn/view - {:padding-vertical 60 - :flex-direction :row - :justify-content :center} + [rn/view {:padding-vertical 60 + :flex-direction :row + :justify-content :center} [quo2/group-avatar @state]]]]))) -(defn preview-group-avatar - [] - [rn/view - {:background-color (colors/theme-colors colors/white - colors/neutral-90) - :flex 1} - [rn/flat-list - {:flex 1 - :keyboardShouldPersistTaps :always - :header [cool-preview] - :key-fn str}]]) +(defn preview-group-avatar [] + [rn/view {:background-color (colors/theme-colors colors/white + colors/neutral-90) + :flex 1} + [rn/flat-list {:flex 1 + :keyboardShouldPersistTaps :always + :header [cool-preview] + :key-fn str}]]) diff --git a/src/status_im2/setup/db.cljs b/src/status_im2/setup/db.cljs index fc55a1dc4a..f5916f7b81 100644 --- a/src/status_im2/setup/db.cljs +++ b/src/status_im2/setup/db.cljs @@ -1,43 +1,45 @@ (ns status-im2.setup.db - (:require [react-native.core :as rn] ;; TODO (14/11/22 flexsurfer move to status-im2 namespace - [status-im.fleet.core :as fleet] - [status-im.wallet.db :as wallet.db])) + (:require + [react-native.core :as rn] + ;; TODO (14/11/22 flexsurfer move to status-im2 namespace + [status-im.fleet.core :as fleet] + [status-im.wallet.db :as wallet.db])) ;; initial state of app-db -(def app-db - {:contacts/contacts {} - :pairing/installations {} - :group/selected-contacts #{} - :chats {} - :current-chat-id nil - :selected-participants #{} - :sync-state :done - :link-previews-whitelist [] - :app-state "active" - :wallet wallet.db/default-wallet - :wallet/all-tokens {} - :peers-count 0 - :node-info {} - :peers-summary [] - :transport/message-envelopes {} - :mailserver/mailservers (fleet/default-mailservers {}) - :mailserver/topics {} - :mailserver/pending-requests 0 - :chat/cooldowns 0 - :chat/inputs {} - :chat/cooldown-enabled? false - :chat/last-outgoing-message-sent-at 0 - :chat/spam-messages-frequency 0 - :chats-home-list #{} - :home-items-show-number 20 - :tooltips {} - :dimensions/window (rn/get-window) - :registry {} - :visibility-status-updates {} - :stickers/packs-pending #{} - :keycard {:nfc-enabled? false - :pin {:original [] - :confirmation [] - :current [] - :puk [] - :enter-step :original}}}) +(def app-db {:contacts/contacts {} + :pairing/installations {} + :group/selected-contacts #{} + :chats {} + :current-chat-id nil + :group-chat/selected-participants #{} + :group-chat/deselected-members #{} + :sync-state :done + :link-previews-whitelist [] + :app-state "active" + :wallet wallet.db/default-wallet + :wallet/all-tokens {} + :peers-count 0 + :node-info {} + :peers-summary [] + :transport/message-envelopes {} + :mailserver/mailservers (fleet/default-mailservers {}) + :mailserver/topics {} + :mailserver/pending-requests 0 + :chat/cooldowns 0 + :chat/inputs {} + :chat/cooldown-enabled? false + :chat/last-outgoing-message-sent-at 0 + :chat/spam-messages-frequency 0 + :chats-home-list #{} + :home-items-show-number 20 + :tooltips {} + :dimensions/window (rn/get-window) + :registry {} + :visibility-status-updates {} + :stickers/packs-pending #{} + :keycard {:nfc-enabled? false + :pin {:original [] + :confirmation [] + :current [] + :puk [] + :enter-step :original}}}) diff --git a/src/status_im2/subs/chat/chats.cljs b/src/status_im2/subs/chat/chats.cljs index 2cad2fbfbf..adbab1048b 100644 --- a/src/status_im2/subs/chat/chats.cljs +++ b/src/status_im2/subs/chat/chats.cljs @@ -1,425 +1,413 @@ (ns status-im2.subs.chat.chats - (:require [clojure.string :as string] + (:require [re-frame.core :as re-frame] + [clojure.string :as string] + [status-im2.common.constants :as constants] [quo.design-system.colors :as colors] - [re-frame.core :as re-frame] - [status-im.add-new.db :as db] [status-im.chat.models :as chat.models] - [status-im.chat.models.mentions :as mentions] [status-im.communities.core :as communities] - [status-im.group-chats.core :as group-chat] - [status-im.group-chats.db :as group-chats.db] - [status-im.i18n.i18n :as i18n] - [status-im.multiaccounts.core :as multiaccounts] - [status-im.utils.config :as config] [status-im.utils.image-server :as image-server] - [status-im2.common.constants :as constants])) + [status-im.multiaccounts.core :as multiaccounts] + [status-im.group-chats.core :as group-chat] + [status-im.chat.models.mentions :as mentions] + [status-im.group-chats.db :as group-chats.db] + [status-im.utils.config :as config] + [status-im.add-new.db :as db] + [status-im.i18n.i18n :as i18n])) (re-frame/reg-sub - :chats/chat - :<- [:chats/chats] - (fn [chats [_ chat-id]] - (get chats chat-id))) + :chats/chat + :<- [:chats/chats] + (fn [chats [_ chat-id]] + (get chats chat-id))) (re-frame/reg-sub - :chats/by-community-id - :<- [:chats/chats] - (fn [chats [_ community-id]] - (->> chats - (keep (fn [[_ chat]] - (when (= (:community-id chat) community-id) - chat))) - (sort-by :timestamp >)))) + :chats/by-community-id + :<- [:chats/chats] + (fn [chats [_ community-id]] + (->> chats + (keep (fn [[_ chat]] + (when (= (:community-id chat) community-id) + chat))) + (sort-by :timestamp >)))) (re-frame/reg-sub - :chats/with-empty-category-by-community-id - (fn [[_ community-id]] - [(re-frame/subscribe [:chats/by-community-id community-id]) - (re-frame/subscribe [:communities/community-chats community-id])]) - (fn [[chats comm-chats] [_ community-id]] - (filter #(string/blank? (get-in comm-chats - [(string/replace (:chat-id %) community-id "") :categoryID])) - chats))) + :chats/with-empty-category-by-community-id + (fn [[_ community-id]] + [(re-frame/subscribe [:chats/by-community-id community-id]) + (re-frame/subscribe [:communities/community-chats community-id])]) + (fn [[chats comm-chats] [_ community-id]] + (filter #(string/blank? (get-in comm-chats [(string/replace (:chat-id %) community-id "") :categoryID])) chats))) (re-frame/reg-sub - :chats/sorted-categories-by-community-id - (fn [[_ community-id]] - [(re-frame/subscribe [:chats/by-community-id community-id]) - (re-frame/subscribe [:communities/community-chats community-id])]) - (fn [[chats comm-chats] [_ community-id]] - (let [chat-cat (into {} - (map (fn [{:keys [id categoryID position]}] - {(str community-id id) {:categoryID categoryID - :position position}}) - (vals comm-chats)))] - (group-by :categoryID - (sort-by :position - (map #(cond-> (merge % (chat-cat (:chat-id %))) - (= community-id constants/status-community-id) - (assoc :color colors/blue)) - chats)))))) + :chats/sorted-categories-by-community-id + (fn [[_ community-id]] + [(re-frame/subscribe [:chats/by-community-id community-id]) + (re-frame/subscribe [:communities/community-chats community-id])]) + (fn [[chats comm-chats] [_ community-id]] + (let [chat-cat (into {} (map (fn [{:keys [id categoryID position]}] + {(str community-id id) {:categoryID categoryID + :position position}}) + (vals comm-chats)))] + (group-by :categoryID (sort-by :position + (map #(cond-> (merge % (chat-cat (:chat-id %))) + (= community-id constants/status-community-id) + (assoc :color colors/blue)) + chats)))))) (re-frame/reg-sub - :chats/category-by-chat-id - (fn [[_ community-id _]] - [(re-frame/subscribe [:communities/community community-id])]) - (fn [[{:keys [chats categories]}] [_ community-id chat-id]] - (get categories (get-in chats [(string/replace chat-id community-id "") :categoryID])))) + :chats/category-by-chat-id + (fn [[_ community-id _]] + [(re-frame/subscribe [:communities/community community-id])]) + (fn [[{:keys [chats categories]}] [_ community-id chat-id]] + (get categories (get-in chats [(string/replace chat-id community-id "") :categoryID])))) (re-frame/reg-sub - :chats/community-chat-by-id - (fn [[_ community-id _]] - [(re-frame/subscribe [:communities/community community-id])]) - (fn [[{:keys [chats]}] [_ community-id chat-id]] - (get chats (string/replace chat-id community-id "")))) + :chats/community-chat-by-id + (fn [[_ community-id _]] + [(re-frame/subscribe [:communities/community community-id])]) + (fn [[{:keys [chats]}] [_ community-id chat-id]] + (get chats (string/replace chat-id community-id "")))) (re-frame/reg-sub - :chats/home-list-chats - :<- [:chats/chats] - :<- [:chats-home-list] - (fn [[chats active-chats]] - (reduce #(if-let [item (get chats %2)] - (conj %1 item) - %1) - [] - active-chats))) + :chats/home-list-chats + :<- [:chats/chats] + :<- [:chats-home-list] + (fn [[chats active-chats]] + (reduce #(if-let [item (get chats %2)] + (conj %1 item) + %1) + [] + active-chats))) (re-frame/reg-sub - :chat-by-id - :<- [:chats/chats] - (fn [chats [_ chat-id]] - (get chats chat-id))) + :chat-by-id + :<- [:chats/chats] + (fn [chats [_ chat-id]] + (get chats chat-id))) (re-frame/reg-sub - :chats/synced-from - (fn [[_ chat-id] _] - (re-frame/subscribe [:chat-by-id chat-id])) - (fn [{:keys [synced-from]}] - synced-from)) + :chats/synced-from + (fn [[_ chat-id] _] + (re-frame/subscribe [:chat-by-id chat-id])) + (fn [{:keys [synced-from]}] + synced-from)) (re-frame/reg-sub - :chats/muted - (fn [[_ chat-id] _] - (re-frame/subscribe [:chat-by-id chat-id])) - (fn [{:keys [muted]}] - muted)) + :chats/muted + (fn [[_ chat-id] _] + (re-frame/subscribe [:chat-by-id chat-id])) + (fn [{:keys [muted]}] + muted)) (re-frame/reg-sub - :chats/chat-type - (fn [[_ chat-id] _] - (re-frame/subscribe [:chat-by-id chat-id])) - (fn [{:keys [chat-type]}] - chat-type)) + :chats/chat-type + (fn [[_ chat-id] _] + (re-frame/subscribe [:chat-by-id chat-id])) + (fn [{:keys [chat-type]}] + chat-type)) (re-frame/reg-sub - :chats/joined - (fn [[_ chat-id] _] - (re-frame/subscribe [:chat-by-id chat-id])) - (fn [{:keys [joined]}] - joined)) + :chats/joined + (fn [[_ chat-id] _] + (re-frame/subscribe [:chat-by-id chat-id])) + (fn [{:keys [joined]}] + joined)) (re-frame/reg-sub - :chats/synced-to-and-from - (fn [[_ chat-id] _] - (re-frame/subscribe [:chat-by-id chat-id])) - (fn [chat] - (select-keys chat [:synced-to :synced-from]))) + :chats/synced-to-and-from + (fn [[_ chat-id] _] + (re-frame/subscribe [:chat-by-id chat-id])) + (fn [chat] + (select-keys chat [:synced-to :synced-from]))) (re-frame/reg-sub - :chats/current-raw-chat - :<- [:chats/chats] - :<- [:chats/current-chat-id] - (fn [[chats current-chat-id]] - (get chats current-chat-id))) + :chats/current-raw-chat + :<- [:chats/chats] + :<- [:chats/current-chat-id] + (fn [[chats current-chat-id]] + (get chats current-chat-id))) (re-frame/reg-sub - :chats/current-chat-inputs - :<- [:chats/current-chat-id] - :<- [:chat/inputs] - (fn [[chat-id inputs]] - (get inputs chat-id))) + :chats/current-chat-inputs + :<- [:chats/current-chat-id] + :<- [:chat/inputs] + (fn [[chat-id inputs]] + (get inputs chat-id))) (re-frame/reg-sub - :chats/timeline-chat-input - :<- [:chat/inputs] - :<- [:multiaccount/public-key] - (fn [[inputs public-key]] - (get inputs (chat.models/profile-chat-topic public-key)))) + :chats/timeline-chat-input + :<- [:chat/inputs] + :<- [:multiaccount/public-key] + (fn [[inputs public-key]] + (get inputs (chat.models/profile-chat-topic public-key)))) (re-frame/reg-sub - :chats/timeline-chat-input-text - :<- [:chats/timeline-chat-input] - (fn [input] - (:input-text input))) + :chats/timeline-chat-input-text + :<- [:chats/timeline-chat-input] + (fn [input] + (:input-text input))) (re-frame/reg-sub - :chats/current-chat-membership - :<- [:chats/current-chat-id] - :<- [:chat/memberships] - (fn [[chat-id memberships]] - (get memberships chat-id))) + :chats/current-chat-membership + :<- [:chats/current-chat-id] + :<- [:chat/memberships] + (fn [[chat-id memberships]] + (get memberships chat-id))) (re-frame/reg-sub - :chats/current-chat - :<- [:chats/current-raw-chat] - :<- [:multiaccount/public-key] - :<- [:communities/current-community] - :<- [:contacts/blocked-set] - :<- [:contacts/contacts] - :<- [:chat/inputs] - :<- [:mutual-contact-requests/enabled?] - (fn [[{:keys [group-chat chat-id] :as current-chat} my-public-key community blocked-users-set contacts - inputs mutual-contact-requests-enabled?]] - (when current-chat - (cond-> current-chat - (chat.models/public-chat? current-chat) - (assoc :show-input? true) + :chats/current-chat + :<- [:chats/current-raw-chat] + :<- [:multiaccount/public-key] + :<- [:communities/current-community] + :<- [:contacts/blocked-set] + :<- [:contacts/contacts] + :<- [:chat/inputs] + :<- [:mutual-contact-requests/enabled?] + (fn [[{:keys [group-chat chat-id] :as current-chat} my-public-key community blocked-users-set contacts inputs mutual-contact-requests-enabled?]] + (when current-chat + (cond-> current-chat + (chat.models/public-chat? current-chat) + (assoc :show-input? true) - (and (chat.models/group-chat? current-chat) - (group-chats.db/member? my-public-key current-chat)) - (assoc :show-input? true - :member? true) + (and (chat.models/group-chat? current-chat) + (group-chats.db/member? my-public-key current-chat)) + (assoc :show-input? true + :member? true) - (and (chat.models/community-chat? current-chat) - (communities/can-post? community my-public-key (:chat-id current-chat))) - (assoc :show-input? true) + (and (chat.models/community-chat? current-chat) + (communities/can-post? community my-public-key (:chat-id current-chat))) + (assoc :show-input? true) - (not group-chat) - (assoc :show-input? - (and - (or - (not mutual-contact-requests-enabled?) - (get-in inputs [chat-id :metadata :sending-contact-request]) - (and mutual-contact-requests-enabled? - (= constants/contact-request-state-mutual - (get-in contacts [chat-id :contact-request-state])))) - (not (contains? blocked-users-set chat-id)))))))) + (not group-chat) + (assoc :show-input? + (and + (or + (not mutual-contact-requests-enabled?) + (get-in inputs [chat-id :metadata :sending-contact-request]) + (and mutual-contact-requests-enabled? + (= constants/contact-request-state-mutual + (get-in contacts [chat-id :contact-request-state])))) + (not (contains? blocked-users-set chat-id)))))))) (re-frame/reg-sub - :chats/current-chat-chat-view - :<- [:chats/current-chat] - (fn [current-chat] - (select-keys current-chat - [:chat-id :show-input? :group-chat :admins :invitation-admin :public? :chat-type :color - :chat-name :synced-to :synced-from :community-id :emoji]))) + :chats/current-chat-chat-view + :<- [:chats/current-chat] + (fn [current-chat] + (select-keys current-chat [:chat-id :show-input? :group-chat :admins :invitation-admin :public? :chat-type :color :chat-name :synced-to :synced-from :community-id :emoji]))) (re-frame/reg-sub - :current-chat/metadata - :<- [:chats/current-raw-chat] - (fn [current-chat] - (select-keys current-chat - [:community-id - :contacts - :public? - :group-chat - :chat-type - :chat-id - :chat-name - :color - :invitation-admin]))) + :current-chat/metadata + :<- [:chats/current-raw-chat] + (fn [current-chat] + (select-keys current-chat + [:community-id + :contacts + :public? + :group-chat + :chat-type + :chat-id + :chat-name + :color + :invitation-admin]))) (re-frame/reg-sub - :current-chat/one-to-one-chat? - :<- [:chats/current-raw-chat] - (fn [current-chat] - (not (or (chat.models/group-chat? current-chat) - (chat.models/public-chat? current-chat))))) + :current-chat/one-to-one-chat? + :<- [:chats/current-raw-chat] + (fn [current-chat] + (not (or (chat.models/group-chat? current-chat) + (chat.models/public-chat? current-chat))))) (re-frame/reg-sub - :chats/current-profile-chat - :<- [:contacts/current-contact-identity] - (fn [identity] - (chat.models/profile-chat-topic identity))) + :chats/current-profile-chat + :<- [:contacts/current-contact-identity] + (fn [identity] + (chat.models/profile-chat-topic identity))) (re-frame/reg-sub - :chats/photo-path - :<- [:contacts/contacts] - :<- [:profile/multiaccount] - :<- [:mediaserver/port] - (fn [[contacts {:keys [public-key] :as multiaccount} port] [_ id]] - (let [contact (or (when (= id public-key) - multiaccount) - (get contacts id))] - (if (nil? contact) - (image-server/get-identicons-uri port id) - (multiaccounts/displayed-photo contact))))) + :chats/photo-path + :<- [:contacts/contacts] + :<- [:profile/multiaccount] + :<- [:mediaserver/port] + (fn [[contacts {:keys [public-key] :as multiaccount} port] [_ id]] + (let [contact (or (when (= id public-key) + multiaccount) + (get contacts id))] + (if (nil? contact) + (image-server/get-identicons-uri port id) + (multiaccounts/displayed-photo contact))))) (re-frame/reg-sub - :chats/unread-messages-number - :<- [:chats/home-list-chats] - (fn [chats _] - (reduce (fn [{:keys [public other]} {:keys [unviewed-messages-count public?] :as chat}] - (if (or public? (chat.models/community-chat? chat)) - {:public (+ public unviewed-messages-count) - :other other} - {:other (+ other unviewed-messages-count) - :public public})) - {:public 0 - :other 0} - chats))) + :chats/unread-messages-number + :<- [:chats/home-list-chats] + (fn [chats _] + (reduce (fn [{:keys [public other]} {:keys [unviewed-messages-count public?] :as chat}] + (if (or public? (chat.models/community-chat? chat)) + {:public (+ public unviewed-messages-count) + :other other} + {:other (+ other unviewed-messages-count) + :public public})) + {:public 0 + :other 0} + chats))) (re-frame/reg-sub - :chats/current-chat-cooldown-enabled? - :<- [:chats/current-chat] - :<- [:chats/cooldown-enabled?] - (fn [[{:keys [public?]} cooldown-enabled?]] - (and public? - cooldown-enabled?))) + :chats/current-chat-cooldown-enabled? + :<- [:chats/current-chat] + :<- [:chats/cooldown-enabled?] + (fn [[{:keys [public?]} cooldown-enabled?]] + (and public? + cooldown-enabled?))) (re-frame/reg-sub - :chats/reply-message - :<- [:chats/current-chat-inputs] - (fn [{:keys [metadata]}] - (:responding-to-message metadata))) + :chats/reply-message + :<- [:chats/current-chat-inputs] + (fn [{:keys [metadata]}] + (:responding-to-message metadata))) (re-frame/reg-sub - :chats/edit-message - :<- [:chats/current-chat-inputs] - (fn [{:keys [metadata]}] - (:editing-message metadata))) + :chats/edit-message + :<- [:chats/current-chat-inputs] + (fn [{:keys [metadata]}] + (:editing-message metadata))) (re-frame/reg-sub - :chats/sending-contact-request - :<- [:chats/current-chat-inputs] - (fn [{:keys [metadata]}] - (:sending-contact-request metadata))) + :chats/sending-contact-request + :<- [:chats/current-chat-inputs] + (fn [{:keys [metadata]}] + (:sending-contact-request metadata))) (re-frame/reg-sub - :chats/sending-image - :<- [:chats/current-chat-inputs] - (fn [{:keys [metadata]}] - (:sending-image metadata))) + :chats/sending-image + :<- [:chats/current-chat-inputs] + (fn [{:keys [metadata]}] + (:sending-image metadata))) (re-frame/reg-sub - :chats/timeline-sending-image - :<- [:chats/timeline-chat-input] - (fn [{:keys [metadata]}] - (:sending-image metadata))) + :chats/timeline-sending-image + :<- [:chats/timeline-chat-input] + (fn [{:keys [metadata]}] + (:sending-image metadata))) (re-frame/reg-sub - :chats/chat-toolbar - :<- [:multiaccounts/login] - :<- [:chats/sending-image] - :<- [:mainnet?] - :<- [:current-chat/one-to-one-chat?] - :<- [:current-chat/metadata] - :<- [:chats/reply-message] - :<- [:chats/edit-message] - :<- [:chats/sending-contact-request] - (fn [[{:keys [processing]} sending-image mainnet? one-to-one-chat? {:keys [public?]} reply edit - sending-contact-request]] - (let [sending-image (seq sending-image)] - {:send (not processing) - :stickers (and (or config/stickers-test-enabled? mainnet?) - (not sending-image) - (not sending-contact-request) - (not reply)) - :image (and (not reply) - (not edit) - (not sending-contact-request) - (not public?)) - :extensions (and one-to-one-chat? - (or config/commands-enabled? mainnet?) - (not edit) - (not sending-contact-request) - (not reply)) - :audio (and (not sending-image) - (not reply) - (not edit) - (not sending-contact-request) - (not public?)) - :sending-image sending-image}))) + :chats/chat-toolbar + :<- [:multiaccounts/login] + :<- [:chats/sending-image] + :<- [:mainnet?] + :<- [:current-chat/one-to-one-chat?] + :<- [:current-chat/metadata] + :<- [:chats/reply-message] + :<- [:chats/edit-message] + :<- [:chats/sending-contact-request] + (fn [[{:keys [processing]} sending-image mainnet? one-to-one-chat? {:keys [public?]} reply edit sending-contact-request]] + (let [sending-image (seq sending-image)] + {:send (not processing) + :stickers (and (or config/stickers-test-enabled? mainnet?) + (not sending-image) + (not sending-contact-request) + (not reply)) + :image (and (not reply) + (not edit) + (not sending-contact-request) + (not public?)) + :extensions (and one-to-one-chat? + (or config/commands-enabled? mainnet?) + (not edit) + (not sending-contact-request) + (not reply)) + :audio (and (not sending-image) + (not reply) + (not edit) + (not sending-contact-request) + (not public?)) + :sending-image sending-image}))) (re-frame/reg-sub - :public-chat.new/topic-error-message - :<- [:public-group-topic] - (fn [topic] - (when-not (or (empty? topic) - (db/valid-topic? topic)) - (i18n/label :topic-name-error)))) + :public-chat.new/topic-error-message + :<- [:public-group-topic] + (fn [topic] + (when-not (or (empty? topic) + (db/valid-topic? topic)) + (i18n/label :topic-name-error)))) (defn filter-selected-contacts [selected-contacts contacts] (filter #(:added (contacts %)) selected-contacts)) (re-frame/reg-sub - :selected-contacts-count - :<- [:group/selected-contacts] - :<- [:contacts/contacts] - (fn [[selected-contacts contacts]] - (count (filter-selected-contacts selected-contacts contacts)))) + :selected-contacts-count + :<- [:group/selected-contacts] + :<- [:contacts/contacts] + (fn [[selected-contacts contacts]] + (count (filter-selected-contacts selected-contacts contacts)))) (re-frame/reg-sub - :selected-participants-count - :<- [:selected-participants] - (fn [selected-participants] - (count selected-participants))) + :group-chat/selected-participants-count + :<- [:group-chat/selected-participants] + (fn [selected-participants] + (count selected-participants))) -(defn filter-contacts - [selected-contacts active-contacts] +(defn filter-contacts [selected-contacts active-contacts] (filter #(selected-contacts (:public-key %)) active-contacts)) (re-frame/reg-sub - :selected-group-contacts - :<- [:group/selected-contacts] - :<- [:contacts/active] - (fn [[selected-contacts active-contacts]] - (filter-contacts selected-contacts active-contacts))) + :selected-group-contacts + :<- [:group/selected-contacts] + :<- [:contacts/active] + (fn [[selected-contacts active-contacts]] + (filter-contacts selected-contacts active-contacts))) (re-frame/reg-sub - :group-chat/inviter-info - (fn [[_ chat-id] _] - [(re-frame/subscribe [:chat-by-id chat-id]) - (re-frame/subscribe [:multiaccount/public-key])]) - (fn [[chat my-public-key]] - {:member? (group-chats.db/member? my-public-key chat) - :inviter-pk (group-chats.db/get-inviter-pk my-public-key chat)})) + :group-chat/inviter-info + (fn [[_ chat-id] _] + [(re-frame/subscribe [:chat-by-id chat-id]) + (re-frame/subscribe [:multiaccount/public-key])]) + (fn [[chat my-public-key]] + {:member? (group-chats.db/member? my-public-key chat) + :inviter-pk (group-chats.db/get-inviter-pk my-public-key chat)})) (re-frame/reg-sub - :group-chat/invitations-by-chat-id - :<- [:group-chat/invitations] - (fn [invitations [_ chat-id]] - (filter #(= (:chat-id %) chat-id) (vals invitations)))) + :group-chat/invitations-by-chat-id + :<- [:group-chat/invitations] + (fn [invitations [_ chat-id]] + (filter #(= (:chat-id %) chat-id) (vals invitations)))) (re-frame/reg-sub - :group-chat/pending-invitations-by-chat-id - (fn [[_ chat-id] _] - [(re-frame/subscribe [:group-chat/invitations-by-chat-id chat-id])]) - (fn [[invitations]] - (filter #(= constants/invitation-state-requested (:state %)) invitations))) + :group-chat/pending-invitations-by-chat-id + (fn [[_ chat-id] _] + [(re-frame/subscribe [:group-chat/invitations-by-chat-id chat-id])]) + (fn [[invitations]] + (filter #(= constants/invitation-state-requested (:state %)) invitations))) (re-frame/reg-sub - :group-chat/removed-from-current-chat? - :<- [:chats/current-raw-chat] - :<- [:multiaccount/public-key] - (fn [[current-chat pk]] - (group-chat/member-removed? current-chat pk))) + :group-chat/removed-from-current-chat? + :<- [:chats/current-raw-chat] + :<- [:multiaccount/public-key] + (fn [[current-chat pk]] + (group-chat/member-removed? current-chat pk))) (re-frame/reg-sub - :chats/mentionable-users - :<- [:chats/current-chat] - :<- [:contacts/blocked-set] - :<- [:contacts/contacts] - :<- [:multiaccount] - (fn [[{:keys [users community-id] :as chat} blocked all-contacts - {:keys [public-key] :as current-multiaccount}]] - (let [community-members @(re-frame/subscribe [:communities/community-members community-id]) - mentionable-users (mentions/get-mentionable-users chat - all-contacts - current-multiaccount - community-members) - members-left (into #{} (filter #(group-chat/member-removed? chat %) (keys users)))] - (apply dissoc mentionable-users (conj (concat blocked members-left) public-key))))) + :chats/mentionable-users + :<- [:chats/current-chat] + :<- [:contacts/blocked-set] + :<- [:contacts/contacts] + :<- [:multiaccount] + (fn [[{:keys [users community-id] :as chat} blocked all-contacts + {:keys [public-key] :as current-multiaccount}]] + (let [community-members @(re-frame/subscribe [:communities/community-members community-id]) + mentionable-users (mentions/get-mentionable-users chat all-contacts current-multiaccount community-members) + members-left (into #{} (filter #(group-chat/member-removed? chat %) (keys users)))] + (apply dissoc mentionable-users (conj (concat blocked members-left) public-key))))) (re-frame/reg-sub - :chat/mention-suggestions - :<- [:chats/current-chat-id] - :<- [:chats/mention-suggestions] - (fn [[chat-id mentions]] - (take 15 (get mentions chat-id)))) + :chat/mention-suggestions + :<- [:chats/current-chat-id] + :<- [:chats/mention-suggestions] + (fn [[chat-id mentions]] + (take 15 (get mentions chat-id)))) (re-frame/reg-sub - :chat/input-with-mentions - :<- [:chats/current-chat-id] - :<- [:chat/inputs-with-mentions] - (fn [[chat-id cursor]] - (get cursor chat-id))) + :chat/input-with-mentions + :<- [:chats/current-chat-id] + :<- [:chat/inputs-with-mentions] + (fn [[chat-id cursor]] + (get cursor chat-id))) diff --git a/src/status_im2/subs/contact.cljs b/src/status_im2/subs/contact.cljs index eb5c8b027c..fdbd4a8063 100644 --- a/src/status_im2/subs/contact.cljs +++ b/src/status_im2/subs/contact.cljs @@ -1,59 +1,54 @@ (ns status-im2.subs.contact - (:require [clojure.string :as string] - [i18n.i18n :as i18n] - [re-frame.core :as re-frame] + (:require [re-frame.core :as re-frame] [status-im.contact.db :as contact.db] - [status-im.ethereum.core :as ethereum] - [status-im.multiaccounts.core :as multiaccounts] + [status-im.utils.image-server :as image-server] [status-im.ui.screens.profile.visibility-status.utils :as visibility-status-utils] + [status-im.multiaccounts.core :as multiaccounts] [status-im.utils.gfycat.core :as gfycat] - [status-im.utils.image-server :as image-server])) + [status-im.ethereum.core :as ethereum] + [clojure.string :as string] + [utils.collection] + [i18n.i18n :as i18n] + [utils.re-frame :as rf])) (re-frame/reg-sub - ::query-current-chat-contacts - :<- [:chats/current-chat] - :<- [:contacts/contacts] - (fn [[chat contacts] [_ query-fn]] - (contact.db/query-chat-contacts chat contacts query-fn))) + ::query-current-chat-contacts + :<- [:chats/current-chat] + :<- [:contacts/contacts] + (fn [[chat contacts] [_ query-fn]] + (contact.db/query-chat-contacts chat contacts query-fn))) (re-frame/reg-sub - :multiaccount/profile-pictures-show-to - :<- [:multiaccount] - (fn [multiaccount] - (get multiaccount :profile-pictures-show-to))) + :multiaccount/profile-pictures-show-to + :<- [:multiaccount] + (fn [multiaccount] + (get multiaccount :profile-pictures-show-to))) (re-frame/reg-sub - :mutual-contact-requests/enabled? - :<- [:multiaccount] - (fn [settings] - (boolean (:mutual-contact-enabled? settings)))) + :mutual-contact-requests/enabled? + :<- [:multiaccount] + (fn [settings] + (boolean (:mutual-contact-enabled? settings)))) (re-frame/reg-sub - ::profile-pictures-visibility - :<- [:multiaccount] - (fn [multiaccount] - (get multiaccount :profile-pictures-visibility))) + ::profile-pictures-visibility + :<- [:multiaccount] + (fn [multiaccount] + (get multiaccount :profile-pictures-visibility))) -(defn- replace-contact-image-uri - [contact port identity] - (let [identicon (image-server/get-identicons-uri port identity) +(defn- replace-contact-image-uri [contact port identity] + (let [identicon (image-server/get-identicons-uri port identity) contact-images (:images contact) - contact-images (reduce (fn [acc image] - (let [image-name (:type image) - ; We pass the clock so that we reload the image if the image is - ; updated - clock (:clock image) - uri (image-server/get-contact-image-uri port - identity - image-name - clock)] - (assoc-in acc [(keyword image-name) :uri] uri))) + contact-images (reduce (fn [acc image] (let [image-name (:type image) + ; We pass the clock so that we reload the image if the image is updated + clock (:clock image) + uri (image-server/get-contact-image-uri port identity image-name clock)] + (assoc-in acc [(keyword image-name) :uri] uri))) contact-images (vals contact-images))] (assoc contact :identicon identicon :images contact-images))) -(defn- reduce-contacts-image-uri - [contacts port] +(defn- reduce-contacts-image-uri [contacts port] (reduce-kv (fn [acc public-key contact] (let [contact (replace-contact-image-uri contact port public-key)] (assoc acc public-key contact))) @@ -61,237 +56,250 @@ contacts)) (re-frame/reg-sub - :contacts/contacts - :<- [:contacts/contacts-raw] - :<- [::profile-pictures-visibility] - :<- [:multiaccount/public-key] - :<- [:mediaserver/port] - (fn [[contacts profile-pictures-visibility public-key port]] - (let [contacts (contact.db/enrich-contacts contacts profile-pictures-visibility public-key)] - (reduce-contacts-image-uri contacts port)))) + :contacts/contacts + :<- [:contacts/contacts-raw] + :<- [::profile-pictures-visibility] + :<- [:multiaccount/public-key] + :<- [:mediaserver/port] + (fn [[contacts profile-pictures-visibility public-key port]] + (let [contacts (contact.db/enrich-contacts contacts profile-pictures-visibility public-key)] + (reduce-contacts-image-uri contacts port)))) (re-frame/reg-sub - :contacts/active - :<- [:contacts/contacts] - (fn [contacts] - (contact.db/get-active-contacts contacts))) + :contacts/active + :<- [:contacts/contacts] + (fn [contacts] + (contact.db/get-active-contacts contacts))) (re-frame/reg-sub - :contacts/active-sections - :<- [:contacts/active] - (fn [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]})))) - {} - contacts) - sort - vals))) + :contacts/active-sections + :<- [:contacts/active] + (fn [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]})))) + {} + contacts) + sort + vals))) (re-frame/reg-sub - :contacts/sorted-contacts - :<- [:contacts/active] - (fn [active-contacts] - (->> active-contacts - (sort-by :alias) - (sort-by - #(visibility-status-utils/visibility-status-order (:public-key %)))))) + :contacts/add-members-sections + :<- [:contacts/current-chat-contacts] + :<- [:contacts/active] + (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/active-count - :<- [:contacts/active] - (fn [active-contacts] - (count active-contacts))) + :contacts/sorted-contacts + :<- [:contacts/active] + (fn [active-contacts] + (->> active-contacts + (sort-by :alias) + (sort-by + #(visibility-status-utils/visibility-status-order (:public-key %)))))) (re-frame/reg-sub - :contacts/blocked - :<- [:contacts/contacts] - (fn [contacts] - (->> contacts - (filter (fn [[_ contact]] - (:blocked contact))) - (contact.db/sort-contacts)))) + :contacts/active-count + :<- [:contacts/active] + (fn [active-contacts] + (count active-contacts))) (re-frame/reg-sub - :contacts/blocked-count - :<- [:contacts/blocked] - (fn [blocked-contacts] - (count blocked-contacts))) + :contacts/blocked + :<- [:contacts/contacts] + (fn [contacts] + (->> contacts + (filter (fn [[_ contact]] + (:blocked contact))) + (contact.db/sort-contacts)))) + +(re-frame/reg-sub + :contacts/blocked-count + :<- [:contacts/blocked] + (fn [blocked-contacts] + (count blocked-contacts))) (defn filter-recipient-contacts [search-filter {:keys [names]}] (let [{:keys [nickname three-words-name ens-name]} names] (or - (when ens-name - (string/includes? (string/lower-case (str ens-name)) search-filter)) - (string/includes? (string/lower-case three-words-name) search-filter) - (when nickname - (string/includes? (string/lower-case nickname) search-filter))))) + (when ens-name + (string/includes? (string/lower-case (str ens-name)) search-filter)) + (string/includes? (string/lower-case three-words-name) search-filter) + (when nickname + (string/includes? (string/lower-case nickname) search-filter))))) (re-frame/reg-sub - :contacts/active-with-ens-names - :<- [:contacts/active] - :<- [:search/recipient-filter] - (fn [[contacts search-filter]] - (let [contacts (filter :ens-verified contacts)] - (if (string/blank? search-filter) - contacts - (filter (partial filter-recipient-contacts - (string/lower-case search-filter)) - contacts))))) + :contacts/active-with-ens-names + :<- [:contacts/active] + :<- [:search/recipient-filter] + (fn [[contacts search-filter]] + (let [contacts (filter :ens-verified contacts)] + (if (string/blank? search-filter) + contacts + (filter (partial filter-recipient-contacts + (string/lower-case search-filter)) + contacts))))) -(defn- enrich-contact - [_ identity ens-name port] +(defn- enrich-contact [_ identity ens-name port] (let [contact (contact.db/enrich-contact - (contact.db/public-key-and-ens-name->new-contact identity ens-name))] + (contact.db/public-key-and-ens-name->new-contact identity ens-name))] (replace-contact-image-uri contact port identity))) (re-frame/reg-sub - :contacts/current-contact - :<- [:contacts/contacts] - :<- [:contacts/current-contact-identity] - :<- [:contacts/current-contact-ens-name] - :<- [:mediaserver/port] - (fn [[contacts identity ens-name port]] - (let [contact (get contacts identity)] - (cond-> contact - (nil? contact) - (enrich-contact identity ens-name port))))) + :contacts/current-contact + :<- [:contacts/contacts] + :<- [:contacts/current-contact-identity] + :<- [:contacts/current-contact-ens-name] + :<- [:mediaserver/port] + (fn [[contacts identity ens-name port]] + (let [contact (get contacts identity)] + (cond-> contact + (nil? contact) + (enrich-contact identity ens-name port))))) (re-frame/reg-sub - :contacts/contact-by-identity - :<- [:contacts/contacts] - (fn [contacts [_ identity]] - (multiaccounts/contact-by-identity contacts identity))) + :contacts/contact-by-identity + :<- [:contacts/contacts] + (fn [contacts [_ identity]] + (multiaccounts/contact-by-identity contacts identity))) (re-frame/reg-sub - :contacts/contact-added? - (fn [[_ identity] _] - [(re-frame/subscribe [:contacts/contact-by-identity identity])]) - (fn [[contact] _] - (:added contact))) + :contacts/contact-added? + (fn [[_ identity] _] + [(re-frame/subscribe [:contacts/contact-by-identity identity])]) + (fn [[contact] _] + (:added contact))) (re-frame/reg-sub - :contacts/contact-blocked? - (fn [[_ identity] _] - [(re-frame/subscribe [:contacts/contact-by-identity identity])]) - (fn [[contact] _] - (:blocked contact))) + :contacts/contact-blocked? + (fn [[_ identity] _] + [(re-frame/subscribe [:contacts/contact-by-identity identity])]) + (fn [[contact] _] + (:blocked contact))) (re-frame/reg-sub - :contacts/contact-two-names-by-identity - (fn [[_ identity] _] - [(re-frame/subscribe [:contacts/contact-by-identity identity]) - (re-frame/subscribe [:multiaccount])]) - (fn [[contact current-multiaccount] [_ identity]] - (multiaccounts/contact-two-names-by-identity contact - current-multiaccount - identity))) + :contacts/contact-two-names-by-identity + (fn [[_ identity] _] + [(re-frame/subscribe [:contacts/contact-by-identity identity]) + (re-frame/subscribe [:multiaccount])]) + (fn [[contact current-multiaccount] [_ identity]] + (multiaccounts/contact-two-names-by-identity contact current-multiaccount + identity))) (re-frame/reg-sub - :contacts/contact-name-by-identity - (fn [[_ identity] _] - [(re-frame/subscribe [:contacts/contact-two-names-by-identity identity])]) - (fn [[names] _] - (first names))) + :contacts/contact-name-by-identity + (fn [[_ identity] _] + [(re-frame/subscribe [:contacts/contact-two-names-by-identity identity])]) + (fn [[names] _] + (first names))) (re-frame/reg-sub - :messages/quote-info - :<- [:chats/messages] - :<- [:contacts/contacts] - :<- [:multiaccount] - (fn [[messages contacts current-multiaccount] [_ message-id]] - (when-let [message (get messages message-id)] - (let [identity (:from message) - me? (= (:public-key current-multiaccount) identity)] - (if me? - {:quote {:from identity - :text (get-in message [:content :text])} - :ens-name (:preferred-name current-multiaccount) - :alias (gfycat/generate-gfy identity)} - (let [contact (or (contacts identity) - (contact.db/public-key->new-contact identity))] - {:quote {:from identity - :text (get-in message [:content :text])} - :ens-name (when (:ens-verified contact) - (:name contact)) - :alias (or (:alias contact) - (gfycat/generate-gfy identity))})))))) + :messages/quote-info + :<- [:chats/messages] + :<- [:contacts/contacts] + :<- [:multiaccount] + (fn [[messages contacts current-multiaccount] [_ message-id]] + (when-let [message (get messages message-id)] + (let [identity (:from message) + me? (= (:public-key current-multiaccount) identity)] + (if me? + {:quote {:from identity + :text (get-in message [:content :text])} + :ens-name (:preferred-name current-multiaccount) + :alias (gfycat/generate-gfy identity)} + (let [contact (or (contacts identity) + (contact.db/public-key->new-contact identity))] + {:quote {:from identity + :text (get-in message [:content :text])} + :ens-name (when (:ens-verified contact) + (:name contact)) + :alias (or (:alias contact) + (gfycat/generate-gfy identity))})))))) (re-frame/reg-sub - :contacts/all-contacts-not-in-current-chat - :<- [::query-current-chat-contacts remove] - (fn [contacts] - (filter :added contacts))) + :contacts/all-contacts-not-in-current-chat + :<- [::query-current-chat-contacts remove] + (fn [contacts] + (filter :added contacts))) (re-frame/reg-sub - :contacts/current-chat-contacts - :<- [:chats/current-chat] - :<- [:contacts/contacts] - :<- [:multiaccount] - (fn [[{:keys [contacts admins]} all-contacts current-multiaccount]] - (contact.db/get-all-contacts-in-group-chat contacts admins all-contacts current-multiaccount))) + :contacts/current-chat-contacts + :<- [:chats/current-chat] + :<- [:contacts/contacts] + :<- [:multiaccount] + (fn [[{:keys [contacts admins]} all-contacts current-multiaccount]] + (contact.db/get-all-contacts-in-group-chat contacts admins all-contacts current-multiaccount))) (re-frame/reg-sub - :contacts/contacts-by-chat - (fn [[_ _ chat-id] _] - [(re-frame/subscribe [:chats/chat chat-id]) - (re-frame/subscribe [:contacts/contacts])]) - (fn [[chat all-contacts] [_ query-fn]] - (contact.db/query-chat-contacts chat all-contacts query-fn))) + :contacts/contacts-by-chat + (fn [[_ _ chat-id] _] + [(re-frame/subscribe [:chats/chat chat-id]) + (re-frame/subscribe [:contacts/contacts])]) + (fn [[chat all-contacts] [_ query-fn]] + (contact.db/query-chat-contacts chat all-contacts query-fn))) (re-frame/reg-sub - :contacts/contact-by-address - :<- [:contacts/contacts] - :<- [:multiaccount/contact] - (fn [[contacts multiaccount] [_ address]] - (if (ethereum/address= address (:public-key multiaccount)) - multiaccount - (contact.db/find-contact-by-address contacts address)))) + :contacts/contact-by-address + :<- [:contacts/contacts] + :<- [:multiaccount/contact] + (fn [[contacts multiaccount] [_ address]] + (if (ethereum/address= address (:public-key multiaccount)) + multiaccount + (contact.db/find-contact-by-address contacts address)))) (re-frame/reg-sub - :contacts/contacts-by-address - :<- [:contacts/contacts] - (fn [contacts] - (reduce (fn [acc [_ {:keys [address] :as contact}]] - (if address - (assoc acc address contact) - acc)) - {} - contacts))) + :contacts/contacts-by-address + :<- [:contacts/contacts] + (fn [contacts] + (reduce (fn [acc [_ {:keys [address] :as contact}]] + (if address + (assoc acc address contact) + acc)) + {} + contacts))) (re-frame/reg-sub - :contacts/filtered-active-sections - :<- [:contacts/active-sections] - :<- [:contacts/search-query] - (fn [[contacts query]] - (if (empty? query) - contacts - (->> contacts - (map (fn [item] - (update item - :data - (fn [data] - (filter #(string/includes? - (string/lower-case (:alias %)) - (string/lower-case query)) - data))))) - (remove #(empty? (:data %))))))) + :contacts/filtered-active-sections + :<- [:contacts/active-sections] + :<- [:contacts/search-query] + (fn [[contacts query]] + (if (empty? query) + contacts + (->> contacts + (map (fn [item] + (update item :data (fn [data] + (filter #(string/includes? + (string/lower-case (:alias %)) + (string/lower-case query)) + data))))) + (remove #(empty? (:data %))))))) (re-frame/reg-sub - :contacts/group-members-sections - :<- [:contacts/current-chat-contacts] - (fn [members] - (let [admins (filter :admin? members) - online (filter #(and (not (:admin? %)) (:online? %)) members) - offline (filter #(and (not (:admin? %)) (not (:online? %))) members)] - (vals (cond-> {} - (seq admins) (assoc :owner {:title (i18n/label :t/owner) :data admins}) - (seq online) (assoc :online {:title (i18n/label :t/online) :data online}) - (seq offline) (assoc :offline {:title (i18n/label :t/offline) :data offline})))))) - + :contacts/group-members-sections + :<- [:contacts/current-chat-contacts] + (fn [members] + (let [admins (filter :admin? members) + online (filter #(let [online (rf/sub [:visibility-status-updates/online? (:public-key %)])] + (and (not (:admin? %)) online)) members) + offline (filter #(let [online (rf/sub [:visibility-status-updates/online? (:public-key %)])] + (and (not (:admin? %)) (not online))) members)] + (vals (cond-> {} + (seq admins) (assoc :owner {:title (i18n/label :t/owner) :data admins}) + (seq online) (assoc :online {:title (i18n/label :t/online) :data online}) + (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 391f82051e..9d77be25f4 100644 --- a/src/status_im2/subs/root.cljs +++ b/src/status_im2/subs/root.cljs @@ -98,7 +98,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 aacdea1552..f5b5e1a120 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1912,5 +1912,6 @@ "instruction-after-qr-generated": "On your other device, navigate to the Syncing screen and select “Scan sync”", "show-existing-keys": "Show Existing Keys", "scan-sync-code": "Scan Sync Code", - "confirm-selection": "Confirm selection" + "confirm-selection": "Confirm selection", + "online": "Online" }