Feature/allow user to select a picture for a group chat #18983 (#19128)

This commit is contained in:
flexsurfer 2024-04-09 17:20:17 +02:00 committed by GitHub
parent 14d76eb4d4
commit d48f933502
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 150 additions and 119 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -125,6 +125,7 @@
:profile-public-key (.-profile chat)
:highlight (.-highlight chat)
:active (.-active chat)
:image (.-image chat)
:members (types/js->clj (.-members chat))
:hide-if-permissions-not-met (.-hideIfPermissionsNotMet chat)}
rpc->type

View File

@ -44,7 +44,6 @@
[legacy.status-im.ui.screens.sync-settings.views :as sync-settings]
[legacy.status-im.ui.screens.wakuv2-settings.edit-node.views :as edit-wakuv2-node]
[legacy.status-im.ui.screens.wakuv2-settings.views :as wakuv2-settings]
[status-im.contexts.chat.group-details.view :as group-details]
[utils.i18n :as i18n]))
(defn topbar-options
@ -69,10 +68,6 @@
:component progress/progress}
;;CHAT
{:name :group-chat-profile
;;TODO animated-header
:options {:insets {:top? true}}
:component group-details/group-details}
{:name :group-chat-invite
;;TODO parameter in the event
:options {:insets {:top? true}}

View File

@ -2,6 +2,7 @@
(:require [clojure.string :as string]
[quo.components.avatars.channel-avatar.view :as channel-avatar]
[quo.components.avatars.collection-avatar.view :as collection-avatar]
[quo.components.avatars.group-avatar.view :as group-avatar]
[quo.components.inputs.address-input.view :as address-input]
[quo.components.inputs.recovery-phrase.view :as recovery-phrase]
[quo.components.inputs.search-input.view :as search-input]
@ -36,14 +37,17 @@
[{:keys [title title-accessibility-label input counter-top counter-bottom
title-right title-right-props]
avatar-props :avatar}]
(let [title-props (assoc title-right-props
:title title
:right title-right
:accessibility-label title-accessibility-label)]
(let [avatar-props (assoc avatar-props :size :size-32)
title-props (assoc title-right-props
:title title
:right title-right
:accessibility-label title-accessibility-label)]
[rn/view {:style style/header}
[rn/view {:style style/header-title}
(when avatar-props
[channel-avatar/view (assoc avatar-props :size :size-32)])
(if (:group? avatar-props)
[group-avatar/view avatar-props]
[channel-avatar/view avatar-props]))
[standard-title/view title-props]]
(when (= input :recovery-phrase)
[header-counter counter-top counter-bottom])]))

View File

@ -0,0 +1,27 @@
(ns react-native.image-crop-picker
(:require ["react-native-image-crop-picker" :default image-picker]))
(defn show-access-error
[o]
(js/console.log (.-message ^js o)))
(defn show-image-picker
([callback]
(show-image-picker callback nil))
([callback
{:keys [media-type]
:or {media-type "any"}
:as props}]
(-> ^js image-picker
(.openPicker (clj->js (merge {:mediaType media-type} props)))
(.then #(callback (.-path ^js %)))
(.catch show-access-error))))
(defn show-image-picker-camera
([callback]
(show-image-picker-camera callback nil))
([callback props]
(-> ^js image-picker
(.openCamera (clj->js props))
(.then #(callback (.-path ^js %)))
(.catch show-access-error))))

View File

@ -1,6 +1,5 @@
(ns status-im.common.profile-picture-picker.view
(ns status-im.common.avatar-picture-picker.view
(:require
["react-native-image-crop-picker" :default image-picker]
[quo.core :as quo]
[react-native.permissions :as permissions]
[react-native.platform :as platform]
@ -16,50 +15,13 @@
:width crop-size
:height crop-size})
(defn show-access-error
[o]
(when (= "E_PERMISSION_MISSING" (.-code ^js o))
(js/console.log (i18n/label :t/error))))
(defn show-image-picker
([images-fn]
(show-image-picker images-fn nil))
([images-fn
{:keys [media-type]
:or {media-type "any"}
:as props}]
(-> ^js image-picker
(.openPicker (clj->js (merge {:mediaType media-type}
props)))
(.then images-fn)
(.catch show-access-error))))
(defn show-image-picker-camera
([images-fn]
(show-image-picker-camera images-fn nil))
([images-fn props]
(-> ^js image-picker
(.openCamera (clj->js props))
(.then images-fn)
(.catch show-access-error))))
(defn pick-pic
[update-profile-pic-callback]
(defn hide-sheet-and-dispatch
[event]
(rf/dispatch [:hide-bottom-sheet])
(show-image-picker
#(update-profile-pic-callback (.-path ^js %))
crop-opts))
(defn take-pic
[update-profile-pic-callback]
(rf/dispatch [:hide-bottom-sheet])
(show-image-picker-camera
#(update-profile-pic-callback (.-path ^js %))
crop-opts))
(rf/dispatch event))
(defn view
[{:keys [update-profile-pic-callback has-picture?]}]
[{:keys [on-result has-picture?]}]
[quo/action-drawer
[[{:icon :i/camera
:accessibility-label :take-photo-button
@ -70,10 +32,10 @@
:read-external-storage
:read-media-images)
:write-external-storage]
:on-allowed (fn [] (take-pic update-profile-pic-callback))
:on-denied (fn []
(log/info
"user has denied permissions to click picture"))}))}
:on-allowed #(hide-sheet-and-dispatch [:image-crop-picker/show-camera
on-result crop-opts])
:on-denied #(log/info
"user has denied permissions to click picture")}))}
{:icon :i/image
:accessibility-label :select-from-gallery-button
:label (i18n/label :t/profile-pic-pick)
@ -83,10 +45,10 @@
:read-external-storage
:read-media-images)
:write-external-storage]
:on-allowed (fn [] (pick-pic update-profile-pic-callback))
:on-denied (fn []
(log/info
"user has denied permissions to select picture"))}))}
:on-allowed #(hide-sheet-and-dispatch [:image-crop-picker/show on-result
crop-opts])
:on-denied #(log/info
"user has denied permissions to select picture")}))}
(when has-picture?
{:accessibility-label :remove-profile-picture
:add-divider? true
@ -96,5 +58,4 @@
:label (i18n/label :t/profile-pic-remove)
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
(update-profile-pic-callback nil))})]]])
(on-result nil))})]]])

View File

@ -349,7 +349,11 @@
[chat-id]
(entry {:icon :i/members
:label (i18n/label :t/group-details)
:on-press #(hide-sheet-and-dispatch [:navigate-to :group-chat-profile chat-id])
:on-press (fn []
(rf/dispatch [:chats-list/load-chat chat-id])
(rf/dispatch [:pin-message/load-pin-messages chat-id])
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:navigate-to :group-details chat-id]))
:danger? false
:accessibility-label :group-details
:sub-label nil
@ -441,7 +445,7 @@
(defn private-group-chat-actions
[item inside-chat?]
[quo/action-drawer
(let [show-group-actions? (:group-chat-member? item)]
(let [show-group-actions? (:group-chat item)]
[(when show-group-actions?
(group-actions item inside-chat?))
(notification-actions item inside-chat? show-group-actions?)

View File

@ -0,0 +1,21 @@
(ns status-im.common.image-crop-picker.events
(:require [react-native.image-crop-picker :as image-crop-picker]
[utils.re-frame :as rf]))
(rf/reg-fx :effect.image-crop-picker/show
(fn [[callback crop-opts]]
(image-crop-picker/show-image-picker callback crop-opts)))
(rf/reg-fx :effect.image-crop-picker/show-camera
(fn [[callback crop-opts]]
(image-crop-picker/show-image-picker-camera callback crop-opts)))
(rf/reg-event-fx
:image-crop-picker/show
(fn [_ [callback crop-opts]]
{:effect.image-crop-picker/show [callback crop-opts]}))
(rf/reg-event-fx
:image-crop-picker/show-camera
(fn [_ [callback crop-opts]]
{:effect.image-crop-picker/show-camera [callback crop-opts]}))

View File

@ -84,11 +84,11 @@
(let [{:keys [view-id current-chat-id]} db
{:keys [all-chats chats-home-list removed-chats]}
(reduce
(fn [acc {:keys [chat-id profile-public-key timeline? community-id active muted] :as chat}]
(fn [acc {:keys [chat-id community-id active muted] :as chat}]
(if (not (or active muted))
(update acc :removed-chats conj chat-id)
(cond-> acc
(and (not profile-public-key) (not timeline?) (not community-id) active)
(and (not community-id) active)
(update :chats-home-list conj chat-id)
:always
(assoc-in [:all-chats chat-id] chat))))

View File

@ -1,7 +1,8 @@
(ns status-im.contexts.chat.group-create.events
(:require [legacy.status-im.data-store.chats :as data-store.chats]
[oops.core :as oops]
[re-frame.core :as rf]))
[re-frame.core :as rf]
[status-im.common.avatar-picture-picker.view :as avatar-picture-picker]))
(rf/reg-event-fx :group-chat/create
(fn [{:keys [db]} [group-name color image]]
@ -28,7 +29,12 @@
(rf/reg-event-fx :group-chat/edit
(fn [_ [{:keys [chat-id group-name color image]}]]
{:json-rpc/call [{:method "chat_editChat"
:params ["" chat-id group-name (name color) image]
:params ["" chat-id group-name (name color)
{:imagePath image
:x 0
:y 0
:width avatar-picture-picker/crop-size
:height avatar-picture-picker/crop-size}]
:js-response true
:on-success #(rf/dispatch [:group-chat/edit-success
(data-store.chats/<-rpc-js %)])}]}))

View File

@ -4,6 +4,7 @@
[quo.foundations.colors :as colors]
[quo.theme]
[react-native.core :as rn]
[status-im.common.avatar-picture-picker.view :as avatar-picture-picker]
[status-im.common.floating-button-page.view :as floating-button-page]
[status-im.constants :as constants]
[status-im.contexts.chat.group-create.style :as style]
@ -15,30 +16,29 @@
[utils.responsiveness :as responsiveness]))
(defn avatar
[{:keys [customization-color]}]
[rn/pressable {:style style/avatar}
;;NOTE with hole-view group-avatar doesn't change it's background color
#_[hole-view/hole-view
{:holes [style/hole]}]
[quo/group-avatar
{:customization-color customization-color
:size :size-80}]
[quo/button
{:on-press (fn []
#_(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[profile-picture-picker/view
{:update-profile-pic-callback on-change-profile-pic
:has-picture? has-picture?}])
:theme :dark
:shell? true}]))
:container-style style/camera
:icon-only? true
:type :grey
:background :photo
:size 32}
:i/camera]])
[{:keys [customization-color group-image set-group-image]}]
(let [on-press (rn/use-callback (fn []
(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[avatar-picture-picker/view
{:on-result set-group-image}])}])))]
[rn/view {:style style/avatar}
;;NOTE with hole-view group-avatar doesn't change it's background color
#_[hole-view/hole-view
{:holes [style/hole]}]
[quo/group-avatar
{:customization-color customization-color
:size :size-80
:picture group-image}]
[quo/button
{:on-press on-press
:container-style style/camera
:icon-only? true
:type :grey
:background :photo
:size 32}
:i/camera]]))
(defn view
[]
@ -63,10 +63,12 @@
set-error-message] (rn/use-state nil)
group-name-empty? (not (and (string? group-name) (not-empty group-name)))
[group-color set-group-color] (rn/use-state (rand-nth colors/account-colors))
[group-image set-group-image] (rn/use-state nil)
create-group-on-press (rn/use-callback #(debounce/throttle-and-dispatch
[:group-chat/create group-name group-color]
[:group-chat/create group-name group-color
group-image]
300)
[group-name group-color])
[group-name group-color group-image])
back-on-press (rn/use-callback #(rf/dispatch [:navigate-back]))
on-change-text (rn/use-callback
(fn [text]
@ -91,7 +93,10 @@
:on-press create-group-on-press}
(i18n/label :t/create-group-chat)]}
[:<>
[avatar {:customization-color group-color}]
[avatar
{:customization-color group-color
:group-image group-image
:set-group-image set-group-image}]
[quo/title-input
{:on-change-text on-change-text
:default-value default-value

View File

@ -3,6 +3,7 @@
[quo.core :as quo]
[quo.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im.common.contact-list-item.view :as contact-list-item]
[status-im.common.contact-list.view :as contact-list]
@ -43,7 +44,7 @@
[]
(let [selected-participants (rf/sub [:group-chat/selected-participants])
deselected-members (rf/sub [:group-chat/deselected-members])
chat-id (rf/sub [:get-screen-params :group-chat-profile])
chat-id (rf/sub [:get-screen-params :group-details])
{:keys [admins] :as group} (rf/sub [:chats/chat-by-id chat-id])
admin? (get admins (rf/sub [:multiaccount/public-key]))]
[rn/view {:flex 1 :margin-top 20}
@ -107,8 +108,8 @@
(defn group-details
[]
(let [chat-id (rf/sub [:get-screen-params :group-chat-profile])
{:keys [admins chat-id chat-name color muted contacts]
(let [chat-id (rf/sub [:get-screen-params :group-details])
{:keys [admins chat-id chat-name color muted contacts image]
:as group} (rf/sub [:chats/chat-by-id chat-id])
members (rf/sub [:contacts/group-members-sections chat-id])
pinned-messages (rf/sub [:chats/pinned chat-id])
@ -121,6 +122,7 @@
:customization-color color}]
[quo/page-nav
{:type :no-title
:margin-top (safe-area/get-top)
:background :photo
:right-side [{:icon-name :i/options
:on-press #(rf/dispatch [:show-bottom-sheet
@ -130,7 +132,9 @@
:on-press #(rf/dispatch [:navigate-back])}]
[quo/page-top
{:title chat-name
:avatar {:customization-color color}}]
:avatar {:group? true
:picture (when image {:uri image})
:customization-color color}}]
[quo/channel-actions
{:container-style style/actions-view
:actions [{:accessibility-label :pinned-messages
@ -167,7 +171,8 @@
:render-section-footer-fn contacts-section-footer
:render-data {:chat-id chat-id
:admin? admin?}
:render-fn contact-item-render}]
:render-fn contact-item-render
:separator [rn/view {:style {:height 4}}]}]
[quo/floating-shell-button
{:jump-to {:on-press #(rf/dispatch [:shell/navigate-to-jump-to])
:customization-color profile-color

View File

@ -169,7 +169,7 @@
preview-text]))
(defn avatar-view
[{:keys [contact chat-id full-name color muted?]}]
[{:keys [contact chat-id full-name color muted? image]}]
(if contact ; `contact` is passed when it's not a group chat
(let [online? (rf/sub [:visibility-status-updates/online? chat-id])
photo-path (rf/sub [:chats/photo-path chat-id])]
@ -182,6 +182,7 @@
(assoc :ring? false))])
[quo/group-avatar
{:customization-color color
:picture (when image {:uri image})
:size :size-32}]))
(defn- notification-layout
@ -225,7 +226,7 @@
unviewed-messages-count]])))
(defn chat-item
[{:keys [chat-id group-chat color name last-message timestamp muted]
[{:keys [chat-id group-chat color name last-message timestamp muted image]
:as item}]
(let [[primary-name secondary-name]
(if group-chat
@ -239,7 +240,8 @@
:chat-id chat-id
:full-name primary-name
:color color
:muted? muted}]
:muted? muted
:image image}]
[rn/view {:style {:flex-shrink 1}}
[rn/view {:style style/chat-data-container}
[quo/author

View File

@ -10,7 +10,7 @@
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im.common.profile-picture-picker.view :as profile-picture-picker]
[status-im.common.avatar-picture-picker.view :as profile-picture-picker]
[status-im.common.validation.profile :as profile-validator]
[status-im.constants :as c]
[status-im.contexts.onboarding.create-profile.style :as style]
@ -145,8 +145,8 @@
[:show-bottom-sheet
{:content (fn []
[profile-picture-picker/view
{:update-profile-pic-callback on-change-profile-pic
:has-picture? false}])
{:on-result on-change-profile-pic
:has-picture? false}])
:shell? true}]))
:image-picker-props {:profile-picture @profile-pic
:full-name (if (seq @full-name)

View File

@ -1,6 +1,6 @@
(ns status-im.contexts.profile.edit.header.events
(:require [clojure.string :as string]
[status-im.common.profile-picture-picker.view :as profile-picture-picker]
[status-im.common.avatar-picture-picker.view :as profile-picture-picker]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))

View File

@ -1,7 +1,7 @@
(ns status-im.contexts.profile.edit.header.events-test
(:require [cljs.test :refer [deftest is]]
matcher-combinators.test
[status-im.common.profile-picture-picker.view :as profile-picture-picker]
[status-im.common.avatar-picture-picker.view :as profile-picture-picker]
[status-im.contexts.profile.edit.header.events :as sut]))
(deftest edit-picture-test

View File

@ -1,7 +1,7 @@
(ns status-im.contexts.profile.edit.header.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.common.profile-picture-picker.view :as profile-picture-picker]
[status-im.common.avatar-picture-picker.view :as profile-picture-picker]
[status-im.contexts.profile.edit.style :as style]
[status-im.contexts.profile.utils :as profile.utils]
[utils.i18n :as i18n]
@ -36,8 +36,8 @@
[:show-bottom-sheet
{:content (fn []
[profile-picture-picker/view
{:update-profile-pic-callback on-change-profile-pic
:has-picture? has-picture?}])
{:on-result on-change-profile-pic
:has-picture? has-picture?}])
:theme :dark
:shell? true}]))
:container-style style/camera-button

View File

@ -7,6 +7,7 @@
status-im.common.async-storage.effects
status-im.common.emoji-picker.events
status-im.common.font.events
status-im.common.image-crop-picker.events
[status-im.common.json-rpc.events]
status-im.common.log
status-im.common.password-authentication.events

View File

@ -125,6 +125,9 @@
:skip-background? true}
:component group-create/view}
{:name :group-details
:component group-details/group-details}
{:name :community-requests-to-join
:options {:sheet? true}
:component join-menu/view}

View File

@ -184,8 +184,6 @@
inputs]]
(when current-chat
(cond-> current-chat
(chat.events/public-chat? current-chat)
(assoc :able-to-send-message? true)
(and (chat.events/community-chat? current-chat)
(get-in community [:chats (subs (:chat-id current-chat) 68) :can-post?]))

View File

@ -121,8 +121,6 @@
(reg-root-key-sub :camera-roll/selected-album :camera-roll/selected-album)
;;group chat
(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 :group-chat/selected-participants :group-chat/selected-participants)
(reg-root-key-sub :group-chat/deselected-members :group-chat/deselected-members)

View File

@ -3,7 +3,7 @@
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
"owner": "status-im",
"repo": "status-go",
"version": "v0.179.0",
"commit-sha1": "0db27a8be6b482c3d4687909e156e38654911f88",
"src-sha256": "0qcdf90sz0y0486dsgjindanavilkh9czzijlk8r0zz867n5b896"
"version": "feature/add-image-to-chat-preview",
"commit-sha1": "b91e78593f2b397dcba9da1c41a755e9cdcec531",
"src-sha256": "0l6h18s3l016bjx5g5mvg9r7lhz85gb0r99ajsh91didk44v12i3"
}