Group details screen (3) (#14494)

* group details screen (3)
This commit is contained in:
Omar Basem 2022-12-23 07:18:09 +04:00 committed by GitHub
parent 1af6d925d2
commit e21b8d4396
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 1201 additions and 1306 deletions

View File

@ -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}]])))

View File

@ -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]}

View File

@ -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)]}]]))

View File

@ -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)
})

View File

@ -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}}]]))

View File

@ -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}]))

View File

@ -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])]))

View File

@ -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)]]))

View File

@ -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}

View File

@ -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}))

View File

@ -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}]])

View File

@ -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}}})

View File

@ -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)))

View File

@ -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}))))))

View File

@ -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))))

View File

@ -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)

View File

@ -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)))))

View File

@ -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"
}