feat: group details screen (3.3) (#14654)

* feat: group details screen (3)
This commit is contained in:
Omar Basem 2022-12-30 18:14:24 +04:00 committed by GitHub
parent 13e59635c7
commit b85eb4184a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 263 additions and 161 deletions

View File

@ -24,7 +24,9 @@
:align-items :center
:justify-content :center
:border-radius (/ container-size 2)
:background-color (colors/custom-color-by-theme color 50 60)}
;:background-color (colors/custom-color-by-theme color 50 60) ; TODO: this is temporary only.
;Issue: https://github.com/status-im/status-mobile/issues/14566
:background-color color}
[icon/icon :i/group
{:size icon-size
:color colors/white-opa-70}]])))

View File

@ -5,6 +5,26 @@
[reagent.core :as reagent]
[quo2.components.selectors.styles :as style]))
(defn- get-color
[checked? disabled? blurred-background?]
(cond
checked?
(colors/custom-color-by-theme
:primary
50
60
(when disabled? 30)
(when disabled? 30))
blurred-background?
(colors/theme-colors
(colors/alpha colors/neutral-80 (if disabled? 0.05 0.1))
(colors/alpha colors/white (if disabled? 0.05 0.1)))
:else
(colors/theme-colors
(colors/alpha colors/neutral-20 (if disabled? 0.4 1))
(colors/alpha colors/neutral-70 (if disabled? 0.3 1)))))
(defn- handle-press
[disabled? on-change checked?]
(when (not disabled?)
@ -48,32 +68,75 @@
(colors/alpha colors/neutral-100 (if disabled? 0.3 1))
(colors/alpha colors/white (if disabled? 0.3 1)))}]])]])))
(defn checkbox
[{:keys [default-checked?]}]
(let [checked? (reagent/atom (or default-checked? false))]
@(reagent/track
(fn [{:keys [on-change disabled? blurred-background? container-style]}]
[rn/touchable-without-feedback
{:on-press (handle-press disabled? on-change checked?)}
[rn/view
{:style (merge
container-style
{:height 20
:width 20})}
[rn/view
{:style (style/checkbox-toggle checked? disabled? blurred-background?)
:accessibility-label (str "checkbox-" (if @checked? "on" "off"))
:accessibility-role :checkbox
:testID "checkbox-component"}
(when @checked?
[rn/view
{:style
{:height 20
:width 20}}
[icons/icon :i/check-small
{:size 20
:color colors/white}]])]]])
checked?)))
(fn [{:keys [on-change disabled? blurred-background? container-style]}]
[rn/touchable-without-feedback
{:on-press (handle-press disabled? on-change checked?)}
[rn/view
{:style (merge
container-style
{:height 20
:width 20})}
[rn/view
{:style {:flex 1
:border-radius 6
:border-width (if @checked? 0 1)
:background-color (cond
@checked?
(get-color @checked? disabled? blurred-background?)
blurred-background?
(colors/theme-colors
colors/white-opa-5
colors/white-opa-10)
:else
(colors/theme-colors
colors/white
colors/neutral-80-opa-40))
:border-color (if @checked?
:none
(get-color @checked? disabled? blurred-background?))}
:accessibility-label (str "checkbox-" (if @checked? "on" "off"))
:accessibility-role :checkbox
:testID "checkbox-component"}
(when @checked?
[rn/view
{:style
{:height 20
:width 20}}
[icons/icon :i/check-small
{:size 20
:color colors/white}]])]]])))
;; TODO (Omar): issue https://github.com/status-im/status-mobile/issues/14681
;(defn checkbox
; [{:keys [default-checked?]}]
; (let [checked? (reagent/atom (or default-checked? false))]
; @(reagent/track
; (fn [{:keys [on-change disabled? blurred-background? container-style]}]
; [rn/touchable-without-feedback
; {:on-press (handle-press disabled? on-change checked?)}
; [rn/view
; {:style (merge
; container-style
; {:height 20
; :width 20})}
; [rn/view
; {:style (style/checkbox-toggle checked? disabled? blurred-background?)
; :accessibility-label (str "checkbox-" (if @checked? "on" "off"))
; :accessibility-role :checkbox
; :testID "checkbox-component"}
; (when @checked?
; [rn/view
; {:style
; {:height 20
; :width 20}}
; [icons/icon :i/check-small
; {:size 20
; :color colors/white}]])]]])
; checked?)))
(defn radio
[{:keys [default-checked?]}]

View File

@ -39,7 +39,16 @@
{:json-rpc/call [{:method "wakuext_removeMemberFromGroupChat"
:params [nil chat-id member]
:js-response true
:on-success #(re-frame/dispatch [:chat-updated % do-not-navigate?])}]})
:on-success #(re-frame/dispatch [:chat-updated % true])}]})
(rf/defn remove-members
{:events [:group-chats.ui/remove-members-pressed]}
[{{:keys [current-chat-id] :group-chat/keys [deselected-members]} :db :as cofx}]
{:json-rpc/call [{:method "wakuext_removeMembersFromGroupChat"
:params [nil current-chat-id deselected-members]
:js-response true
:on-success #(re-frame/dispatch [:chat-updated % true])
:on-error #()}]})
(rf/defn join-chat
{:events [:group-chats.ui/join-pressed]}
@ -79,11 +88,11 @@
(rf/defn add-members
"Add members to a group chat"
{:events [:group-chats.ui/add-members-pressed]}
[{{:keys [current-chat-id selected-participants]} :db :as cofx}]
[{{:keys [current-chat-id] :group-chat/keys [selected-participants]} :db :as cofx}]
{:json-rpc/call [{:method "wakuext_addMembersToGroupChat"
:params [nil current-chat-id selected-participants]
:js-response true
:on-success #(re-frame/dispatch [:chat-updated %])}]})
:on-success #(re-frame/dispatch [:chat-updated % true])}]})
(rf/defn add-members-from-invitation
"Add members to a group chat"
@ -179,6 +188,16 @@
:type
(= constants/invitation-state-removed)))
(rf/defn deselect-member
{:events [:deselect-member]}
[{:keys [db]} id]
{:db (update db :group-chat/deselected-members conj id)})
(rf/defn undo-deselect-member
{:events [:undo-deselect-member]}
[{:keys [db]} id]
{:db (update db :group-chat/deselected-members disj id)})
(rf/defn deselect-contact
{:events [:deselect-contact]}
[{:keys [db]} id]
@ -192,17 +211,22 @@
(rf/defn deselect-participant
{:events [:deselect-participant]}
[{:keys [db]} id]
{:db (update db :selected-participants disj id)})
{:db (update db :group-chat/selected-participants disj id)})
(rf/defn select-participant
{:events [:select-participant]}
[{:keys [db]} id]
{:db (update db :selected-participants conj id)})
{:db (update db :group-chat/selected-participants conj id)})
(rf/defn add-participants-toggle-list
{:events [:group/add-participants-toggle-list]}
(rf/defn clear-added-participants
{:events [:group/clear-added-participants]}
[{db :db}]
{:db (assoc db :selected-participants #{})})
{:db (assoc db :group-chat/selected-participants #{})})
(rf/defn clear-removed-members
{:events [:group/clear-removed-members]}
[{db :db}]
{:db (assoc db :group-chat/deselected-members #{})})
(rf/defn show-group-chat-profile
{:events [:show-group-chat-profile]}

View File

@ -107,7 +107,7 @@
[status-im.ui.screens.wallet.settings.views :as wallet-settings]
[status-im.ui.screens.wallet.swap.views :as wallet.swap]
[status-im.ui.screens.wallet.transactions.views :as wallet-transactions]
[status-im.ui2.screens.chat.group-details.view :as group-details]
[status-im2.contexts.chat.group-details.view :as group-details]
[status-im.ui2.screens.chat.photo-selector.view :as photo-selector]
[status-im.ui2.screens.chat.components.new-chat.view :as new-chat-aio]))

View File

@ -9,15 +9,15 @@
[quo/divider-label {:label title}])
(defn contact-list
[{:keys [start-a-new-chat?] :as data}]
(let [contacts (if start-a-new-chat?
(rf/sub [:contacts/sorted-and-grouped-by-first-letter])
[data]
(let [contacts (if (:group data)
(rf/sub [:contacts/add-members-sections])
(rf/sub [:contacts/filtered-active-sections]))]
[rn/section-list
{:key-fn :title
:sticky-section-headers-enabled false
:sections contacts
:render-section-header-fn contacts-section-header
:content-container-style {:padding-bottom 120}
:content-container-style {:padding-bottom 20}
:render-data data
:render-fn contact-list-item/contact-list-item}]))

View File

@ -3,11 +3,11 @@
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.platform :as platform]
[reagent.core :as reagent]
[status-im2.common.home.actions.view :as actions]
[status-im2.contexts.chat.home.chat-list-item.style :as style]
[utils.address :as utils.address]
[utils.re-frame :as rf]))
[utils.re-frame :as rf]
[reagent.core :as reagent]))
(defn open-chat
[chat-id]
@ -20,33 +20,36 @@
(rf/dispatch [:search/home-filter-changed nil]))))
(defn action-icon
[{:keys [public-key] :as item} {:keys [icon start-a-new-chat? group added] :as extra-data}
[{:keys [public-key] :as item} {:keys [icon start-a-new-chat? group] :as extra-data}
user-selected? on-toggle]
(let [{:keys [contacts]} group
member? (contains? contacts public-key)
checked? (reagent/atom (if start-a-new-chat?
user-selected?
member?))]
(let [{:keys [contacts admins]} group
member? (contains? contacts public-key)
current-pk (rf/sub [:multiaccount/public-key])
admin? (get admins current-pk)
checked? (reagent/atom (if start-a-new-chat?
user-selected?
member?))]
[rn/touchable-opacity
{:on-press #(rf/dispatch [:bottom-sheet/show-sheet
{:content (fn [] [actions/actions item extra-data])}])
:style {:position :absolute
:right 20}}
(if (= icon :options)
[quo/icon :i/options
{:size 20
:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}]
@(reagent/track!
(fn []
[quo/checkbox
{:default-checked? @checked?
:on-change (fn [selected]
(if start-a-new-chat?
(on-toggle true @checked? public-key)
(if selected
(swap! added conj public-key)
(reset! added (remove #(= % public-key) @added)))))}])
checked?))]))
[quo/icon :i/options {:size 20 :color (colors/theme-colors colors/neutral-50 colors/neutral-40)}]
[quo/checkbox
{:default-checked? @checked?
:accessibility-label :contact-toggle-check
:disabled? (and member? (not admin?))
:on-change (fn [selected]
(if start-a-new-chat?
(on-toggle true @checked? public-key)
(if-not member?
(if selected
(rf/dispatch [:select-participant public-key true])
(rf/dispatch [:deselect-participant public-key true]))
(if selected
(rf/dispatch [:undo-deselect-member public-key true])
(rf/dispatch [:deselect-member public-key true])))))}])]))
(defn contact-list-item
[item _ _ {:keys [group start-a-new-chat? on-toggle] :as extra-data}]
@ -57,38 +60,36 @@
photo-path (when (seq images)
(rf/sub [:chats/photo-path public-key]))
current-pk (rf/sub [:multiaccount/public-key])
online? (rf/sub [:visibility-status-updates/online?
public-key])
user-selected? (rf/sub [:is-contact-selected? public-key])]
[rn/touchable-opacity
(merge {:style (style/container)
:active-opacity 1
:on-press #(if start-a-new-chat?
(on-toggle true user-selected? public-key)
(open-chat public-key))
:on-long-press #(when (some? group)
(rf/dispatch [:bottom-sheet/show-sheet
{:content (fn [] [actions/actions item extra-data])}]))})
(merge
{:style (style/container)
:accessibility-label :contact
:active-opacity 1
:on-press #(if start-a-new-chat?
(on-toggle true user-selected? public-key)
(open-chat public-key))
:on-long-press #(when (some? group)
(rf/dispatch [:bottom-sheet/show-sheet
{:content (fn [] [actions/actions item extra-data])}]))})
[quo/user-avatar
{:full-name display-name
:profile-picture photo-path
:status-indicator? true
:online? true
:online? online?
:size :small
:ring? false}]
[rn/view {:style {:margin-left 8}}
[rn/view {:style {:flex-direction :row}}
[quo/text {:weight :semi-bold} display-name]
(if ens-verified
[rn/view
{:style {:margin-left 5
:margin-top 4}}
[rn/view {:style {:margin-left 5 :margin-top 4}}
[quo/icon :i/verified
{:no-color true
:size 12
:color (colors/theme-colors colors/success-50 colors/success-60)}]]
{:no-color true :size 12 :color (colors/theme-colors colors/success-50 colors/success-60)}]]
(when added?
[rn/view
{:style {:margin-left 5
:margin-top 4}}
[rn/view {:style {:margin-left 5 :margin-top 4}}
[quo/icon :i/contact
{:no-color true
:size 12
@ -98,4 +99,4 @@
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}}
(utils.address/get-shortened-address public-key)]]
(when-not (= current-pk public-key)
[:f> action-icon item extra-data user-selected? on-toggle])]))
[action-icon item extra-data user-selected? on-toggle])]))

View File

@ -1,5 +1,6 @@
(ns status-im.ui2.screens.chat.group-details.style
(:require [quo2.foundations.colors :as colors]))
(ns status-im2.contexts.chat.group-details.style
(:require [quo2.foundations.colors :as colors]
[react-native.platform :as platform]))
(defn actions-view
[]
@ -33,12 +34,13 @@
:align-items :center
:margin-bottom 24})
(def bottom-container
{:position :absolute
:padding-horizontal 20
(defn bottom-container
[safe-area]
{:padding-horizontal 20
:padding-vertical 12
:padding-bottom 33
:padding-bottom (+ 33 (:bottom safe-area))
:width "100%"
:background-color colors/white
:flex-direction :row
:bottom 0})
:margin-bottom (if platform/ios? 0 70)})

View File

@ -1,13 +1,11 @@
(ns status-im.ui2.screens.chat.group-details.view
(ns status-im2.contexts.chat.group-details.view
(:require [i18n.i18n :as i18n]
[oops.core :refer [oget]]
[quo.components.safe-area :as safe-area]
[quo2.core :as quo2]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.chat.models :as chat.models]
[status-im.ui2.screens.chat.group-details.style :as style]
[status-im2.contexts.chat.group-details.style :as style]
[status-im.ui2.screens.common.contact-list.view :as contact-list]
[status-im2.common.contact-list-item.view :as contact-list-item]
[status-im2.common.home.actions.view :as actions]
@ -46,8 +44,10 @@
[back-button] [options-button]])
(defn count-container
[count]
[rn/view {:style (style/count-container)}
[count accessibility-label]
[rn/view
{:style (style/count-container)
:accessibility-label accessibility-label}
[quo2/text
{:size :label
:weight :medium
@ -66,44 +66,41 @@
:weight :medium
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}} title]])
(def added (reagent/atom ()))
(defn contact-requests-sheet
[group]
(let [added (reagent/atom ())]
(fn []
[:f>
(fn []
(let [{window-height :height} (rn/use-window-dimensions)
safe-area (safe-area/use-safe-area)]
[rn/view {:style {:height (- window-height (:top safe-area))}}
[rn/touchable-opacity
{:on-press #(rf/dispatch [:bottom-sheet/hide])
:style (style/close-icon)}
[quo2/icon :i/close {:color (colors/theme-colors colors/neutral-100 colors/white)}]]
[quo2/text
{:size :heading-1
:weight :semi-bold
:style {:margin-left 20}}
(i18n/label :t/add-members)]
[rn/text-input
{:placeholder (str (i18n/label :t/search) "...")
:style {:height 32
:padding-horizontal 20
:margin-vertical 12}
:on-change (fn [e]
(rf/dispatch [:contacts/set-search-query (oget e "nativeEvent.text")]))}]
[contact-list/contact-list
{:icon :check
:group group
:added added
:search? true}]
[rn/view {:style style/bottom-container}
[quo2/button
{:style {:flex 1}
:on-press #(rf/dispatch [:bottom-sheet/hide])
:disabled (zero? (count @added))}
(i18n/label :t/save)]]]))])))
(defn add-members-sheet
[group admin?]
[:f>
(fn []
(let [{window-height :height} (rn/use-window-dimensions)
safe-area (safe-area/use-safe-area)
selected-participants (rf/sub [:group-chat/selected-participants])
deselected-members (rf/sub [:group-chat/deselected-members])]
[rn/view {:style {:height (- window-height (:top safe-area))}}
[rn/touchable-opacity
{:on-press #(rf/dispatch [:bottom-sheet/hide])
:accessibility-label :close-manage-members
:style (style/close-icon)}
[quo2/icon :i/close {:color (colors/theme-colors colors/neutral-100 colors/white)}]]
[quo2/text
{:size :heading-1
:weight :semi-bold
:style {:margin-left 20}}
(i18n/label (if admin? :t/manage-members :t/add-members))]
[contact-list/contact-list
{:icon :check
:group group
:search? true}]
[rn/view {:style (style/bottom-container safe-area)}
[quo2/button
{:style {:flex 1}
:accessibility-label :save
:on-press (fn []
(rf/dispatch [:group-chats.ui/add-members-pressed])
(js/setTimeout #(rf/dispatch [:group-chats.ui/remove-members-pressed])
500)
(rf/dispatch [:bottom-sheet/hide]))
:disabled (and (zero? (count selected-participants))
(zero? (count deselected-members)))}
(i18n/label :t/save)]]]))])
(defn group-details
[]
@ -136,33 +133,39 @@
{:size 20 :color (colors/theme-colors colors/neutral-50 colors/neutral-40)}]]]
[rn/view {:style (style/actions-view)}
[rn/touchable-opacity
{:style (style/action-container color)
:on-press (fn []
(rf/dispatch [:bottom-sheet/show-sheet :pinned-messages-list chat-id]))}
{:style (style/action-container color)
:accessibility-label :pinned-messages
:on-press (fn []
(rf/dispatch [:bottom-sheet/show-sheet :pinned-messages-list chat-id]))}
[rn/view
{:style {:flex-direction :row
:justify-content :space-between}}
[quo2/icon :i/pin {:size 20 :color (colors/theme-colors colors/neutral-100 colors/white)}]
[count-container (count pinned-messages)]]
[count-container (count pinned-messages) :pinned-count]]
[quo2/text {:style {:margin-top 16} :size :paragraph-1 :weight :medium}
(i18n/label :t/pinned-messages)]]
[rn/touchable-opacity
{:style (style/action-container color)
:on-press #(rf/dispatch [::chat.models/mute-chat-toggled chat-id (not muted)])}
{:style (style/action-container color)
:accessibility-label :toggle-mute
:on-press #(rf/dispatch [::chat.models/mute-chat-toggled chat-id (not muted)])}
[quo2/icon (if muted :i/muted :i/activity-center)
{:size 20 :color (colors/theme-colors colors/neutral-100 colors/white)}]
[quo2/text {:style {:margin-top 16} :size :paragraph-1 :weight :medium}
(i18n/label (if muted :unmute-group :mute-group))]]
[rn/touchable-opacity
{:style (style/action-container color)
:on-press #(rf/dispatch
[:bottom-sheet/show-sheet
{:content (fn [] [contact-requests-sheet group])}])}
{:style (style/action-container color)
:accessibility-label :manage-members
:on-press (fn []
(rf/dispatch [:group/clear-added-participants])
(rf/dispatch [:group/clear-removed-members])
(rf/dispatch
[:bottom-sheet/show-sheet
{:content (fn [] [add-members-sheet group admin?])}]))}
[rn/view
{:style {:flex-direction :row
:justify-content :space-between}}
[quo2/icon :i/add-user {:size 20 :color (colors/theme-colors colors/neutral-100 colors/white)}]
[count-container (count contacts)]]
[count-container (count contacts) :members-count]]
[quo2/text {:style {:margin-top 16} :size :paragraph-1 :weight :medium}
(i18n/label (if admin? :t/manage-members :t/add-members))]]]
[rn/section-list

View File

@ -23,7 +23,7 @@
(fn [c]
{:key c
:value c})
(keys colors/customization))}])
["#ff0000" "#0000ff"])}]) ; TODO: this is temporary only. Issue: https://github.com/status-im/status-mobile/issues/14566
(defn cool-preview
[]

View File

@ -13,7 +13,8 @@
:group/selected-contacts #{}
:chats {}
:current-chat-id nil
:selected-participants #{}
:group-chat/selected-participants #{}
:group-chat/deselected-members #{}
:sync-state :done
:link-previews-whitelist []
:app-state "active"

View File

@ -8,7 +8,7 @@
[status-im.ui.screens.profile.visibility-status.utils :as visibility-status-utils]
[status-im.utils.gfycat.core :as gfycat]
[status-im.utils.image-server :as image-server]
[status-im.constants :as constants]))
[utils.collection]))
(re-frame/reg-sub
::query-current-chat-contacts
@ -93,21 +93,20 @@
vals)))
(re-frame/reg-sub
:contacts/sorted-and-grouped-by-first-letter
:contacts/add-members-sections
:<- [:contacts/current-chat-contacts]
:<- [:contacts/active]
:<- [:selected-contacts-count]
(fn [[contacts selected-contacts-count]]
(->> contacts
(filter :mutual?)
(map #(assoc %
:allow-new-users?
(< selected-contacts-count
(dec constants/max-group-chat-participants))))
(group-by (comp (fnil string/upper-case "") first :alias))
(sort-by (fn [[title]] title))
(map (fn [[title data]]
{:title title
:data data})))))
(fn [[members contacts]]
(-> (reduce
(fn [acc contact]
(let [first-char (first (:alias contact))]
(if (get acc first-char)
(update-in acc [first-char :data] #(conj % contact))
(assoc acc first-char {:title first-char :data [contact]}))))
{}
(utils.collection/distinct-by :public-key (concat members contacts)))
sort
vals)))
(re-frame/reg-sub
:contacts/sorted-contacts
@ -312,4 +311,3 @@
(seq offline) (assoc :offline {:title (i18n/label :t/offline) :data offline}))))))

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

@ -99,7 +99,8 @@
(reg-root-key-sub :new-chat-name :new-chat-name)
(reg-root-key-sub :group-chat-profile/editing? :group-chat-profile/editing?)
(reg-root-key-sub :group-chat-profile/profile :group-chat-profile/profile)
(reg-root-key-sub :selected-participants :selected-participants)
(reg-root-key-sub :group-chat/selected-participants :group-chat/selected-participants)
(reg-root-key-sub :group-chat/deselected-members :group-chat/deselected-members)
(reg-root-key-sub :chat/inputs :chat/inputs)
(reg-root-key-sub :chat/memberships :chat/memberships)
(reg-root-key-sub :camera-roll/photos :camera-roll/photos)

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

@ -1923,5 +1923,6 @@
"search-contacts": "Search contacts",
"who-are-you-looking-for": "Who are you looking for ?",
"close-contact-search": "Close contact search",
"selected-count-from-max": "{{selected}}/{{max}}"
"selected-count-from-max": "{{selected}}/{{max}}",
"online": "Online"
}