speed up fetching closed group's members avatar
parent
af75b6f0e2
commit
5ba7f20162
@ -1,59 +1,57 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useMembersAvatars } from '../../hooks/useMembersAvatars';
|
||||||
import { Avatar, AvatarSize } from '../Avatar';
|
import { Avatar, AvatarSize } from '../Avatar';
|
||||||
import { ConversationAvatar } from '../session/usingClosedConversationDetails';
|
|
||||||
|
|
||||||
interface Props {
|
type Props = {
|
||||||
size: number;
|
size: number;
|
||||||
memberAvatars: Array<ConversationAvatar>; // this is added by usingClosedConversationDetails
|
closedGroupId: string;
|
||||||
onAvatarClick?: () => void;
|
onAvatarClick?: () => void;
|
||||||
}
|
};
|
||||||
|
|
||||||
export class ClosedGroupAvatar extends React.PureComponent<Props> {
|
function getClosedGroupAvatarsSize(size: AvatarSize): AvatarSize {
|
||||||
public getClosedGroupAvatarsSize(size: AvatarSize): AvatarSize {
|
// Always use the size directly under the one requested
|
||||||
// Always use the size directly under the one requested
|
switch (size) {
|
||||||
switch (size) {
|
case AvatarSize.XS:
|
||||||
case AvatarSize.S:
|
return AvatarSize.XS;
|
||||||
return AvatarSize.XS;
|
case AvatarSize.S:
|
||||||
case AvatarSize.M:
|
return AvatarSize.XS;
|
||||||
return AvatarSize.S;
|
case AvatarSize.M:
|
||||||
case AvatarSize.L:
|
return AvatarSize.S;
|
||||||
return AvatarSize.M;
|
case AvatarSize.L:
|
||||||
case AvatarSize.XL:
|
return AvatarSize.M;
|
||||||
return AvatarSize.L;
|
case AvatarSize.XL:
|
||||||
case AvatarSize.HUGE:
|
return AvatarSize.L;
|
||||||
return AvatarSize.XL;
|
case AvatarSize.HUGE:
|
||||||
default:
|
return AvatarSize.XL;
|
||||||
throw new Error(`Invalid size request for closed group avatar: ${size}`);
|
default:
|
||||||
}
|
throw new Error(`Invalid size request for closed group avatar: ${size}`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public render() {
|
export const ClosedGroupAvatar = (props: Props) => {
|
||||||
const { memberAvatars, size, onAvatarClick } = this.props;
|
const { closedGroupId, size, onAvatarClick } = props;
|
||||||
const avatarsDiameter = this.getClosedGroupAvatarsSize(size);
|
|
||||||
|
|
||||||
const conv1 = memberAvatars.length > 0 ? memberAvatars[0] : undefined;
|
const memberAvatars = useMembersAvatars(closedGroupId);
|
||||||
const conv2 = memberAvatars.length > 1 ? memberAvatars[1] : undefined;
|
const avatarsDiameter = getClosedGroupAvatarsSize(size);
|
||||||
const name1 = conv1?.name || conv1?.id || undefined;
|
const firstMember = memberAvatars?.[0];
|
||||||
const name2 = conv2?.name || conv2?.id || undefined;
|
const secondMember = memberAvatars?.[1];
|
||||||
|
|
||||||
// use the 2 first members as group avatars
|
return (
|
||||||
return (
|
<div className="module-avatar__icon-closed">
|
||||||
<div className="module-avatar__icon-closed">
|
<Avatar
|
||||||
<Avatar
|
avatarPath={firstMember?.avatarPath}
|
||||||
avatarPath={conv1?.avatarPath}
|
name={firstMember?.name}
|
||||||
name={name1}
|
size={avatarsDiameter}
|
||||||
size={avatarsDiameter}
|
pubkey={firstMember?.id}
|
||||||
pubkey={conv1?.id}
|
onAvatarClick={onAvatarClick}
|
||||||
onAvatarClick={onAvatarClick}
|
/>
|
||||||
/>
|
<Avatar
|
||||||
<Avatar
|
avatarPath={secondMember?.avatarPath}
|
||||||
avatarPath={conv2?.avatarPath}
|
name={secondMember?.name}
|
||||||
name={name2}
|
size={avatarsDiameter}
|
||||||
size={avatarsDiameter}
|
pubkey={secondMember?.id}
|
||||||
pubkey={conv2?.id}
|
onAvatarClick={onAvatarClick}
|
||||||
onAvatarClick={onAvatarClick}
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
import { GroupUtils, UserUtils } from '../../session/utils';
|
|
||||||
import { PubKey } from '../../session/types';
|
|
||||||
import React from 'react';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
import { getConversationController } from '../../session/conversations';
|
|
||||||
|
|
||||||
export type ConversationAvatar = {
|
|
||||||
avatarPath?: string;
|
|
||||||
id?: string; // member's pubkey
|
|
||||||
name?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type State = {
|
|
||||||
memberAvatars?: Array<ConversationAvatar>; // this is added by usingClosedConversationDetails
|
|
||||||
};
|
|
||||||
|
|
||||||
export function usingClosedConversationDetails(WrappedComponent: any) {
|
|
||||||
return class extends React.Component<any, State> {
|
|
||||||
constructor(props: any) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
memberAvatars: undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentDidMount() {
|
|
||||||
this.fetchClosedConversationDetails();
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentWillReceiveProps() {
|
|
||||||
this.fetchClosedConversationDetails();
|
|
||||||
}
|
|
||||||
|
|
||||||
public render() {
|
|
||||||
return <WrappedComponent memberAvatars={this.state.memberAvatars} {...this.props} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
private fetchClosedConversationDetails() {
|
|
||||||
const { isPublic, type, conversationType, isGroup, id } = this.props;
|
|
||||||
|
|
||||||
if (!isPublic && (conversationType === 'group' || type === 'group' || isGroup)) {
|
|
||||||
const groupId = id;
|
|
||||||
const ourPrimary = UserUtils.getOurPubKeyFromCache();
|
|
||||||
let members = GroupUtils.getGroupMembers(PubKey.cast(groupId));
|
|
||||||
|
|
||||||
const ourself = members.find(m => m.key !== ourPrimary.key);
|
|
||||||
// add ourself back at the back, so it's shown only if only 1 member and we are still a member
|
|
||||||
members = members.filter(m => m.key !== ourPrimary.key);
|
|
||||||
members.sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));
|
|
||||||
if (ourself) {
|
|
||||||
members.push(ourPrimary);
|
|
||||||
}
|
|
||||||
// no need to forward more than 2 conversations for rendering the group avatar
|
|
||||||
members = members.slice(0, 2);
|
|
||||||
const memberConvos = _.compact(members.map(m => getConversationController().get(m.key)));
|
|
||||||
const memberAvatars = memberConvos.map(m => {
|
|
||||||
return {
|
|
||||||
avatarPath: m.getAvatarPath() || undefined,
|
|
||||||
id: m.id,
|
|
||||||
name: m.get('name') || m.get('profileName') || m.id,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
this.setState({ memberAvatars });
|
|
||||||
} else {
|
|
||||||
this.setState({ memberAvatars: undefined });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { getConversationController } from '../session/conversations';
|
|
||||||
import { UserUtils } from '../session/utils';
|
|
||||||
import { ReduxConversationType } from '../state/ducks/conversations';
|
|
||||||
|
|
||||||
export function useMembersAvatars(conversation: ReduxConversationType | undefined) {
|
|
||||||
const [membersAvatars, setMembersAvatars] = useState<
|
|
||||||
| Array<{
|
|
||||||
avatarPath: string | undefined;
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
}>
|
|
||||||
| undefined
|
|
||||||
>(undefined);
|
|
||||||
|
|
||||||
useEffect(
|
|
||||||
() => {
|
|
||||||
if (!conversation) {
|
|
||||||
setMembersAvatars(undefined);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { isPublic, isGroup, members: convoMembers } = conversation;
|
|
||||||
if (!isPublic && isGroup) {
|
|
||||||
const ourPrimary = UserUtils.getOurPubKeyStrFromCache();
|
|
||||||
|
|
||||||
const ourself = convoMembers?.find(m => m !== ourPrimary) || undefined;
|
|
||||||
// add ourself back at the back, so it's shown only if only 1 member and we are still a member
|
|
||||||
let membersFiltered = convoMembers?.filter(m => m !== ourPrimary) || [];
|
|
||||||
membersFiltered.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
||||||
if (ourself) {
|
|
||||||
membersFiltered.push(ourPrimary);
|
|
||||||
}
|
|
||||||
// no need to forward more than 2 conversations for rendering the group avatar
|
|
||||||
membersFiltered = membersFiltered.slice(0, 2);
|
|
||||||
const memberConvos = _.compact(
|
|
||||||
membersFiltered.map(m => getConversationController().get(m))
|
|
||||||
);
|
|
||||||
const memberAvatars = memberConvos.map(m => {
|
|
||||||
return {
|
|
||||||
avatarPath: m.getAvatarPath() || undefined,
|
|
||||||
id: m.id as string,
|
|
||||||
name: (m.get('name') || m.get('profileName') || m.id) as string,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
setMembersAvatars(memberAvatars);
|
|
||||||
} else {
|
|
||||||
setMembersAvatars(undefined);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
conversation ? [conversation.members, conversation.id] : []
|
|
||||||
);
|
|
||||||
|
|
||||||
return membersAvatars;
|
|
||||||
}
|
|
@ -0,0 +1,55 @@
|
|||||||
|
import { UserUtils } from '../session/utils';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { StateType } from '../state/reducer';
|
||||||
|
|
||||||
|
export type ConversationAvatar = {
|
||||||
|
avatarPath?: string;
|
||||||
|
id: string; // member's pubkey
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useMembersAvatars(closedGroupPubkey: string | undefined) {
|
||||||
|
const ourPrimary = UserUtils.getOurPubKeyStrFromCache();
|
||||||
|
|
||||||
|
return useSelector((state: StateType): Array<ConversationAvatar> | undefined => {
|
||||||
|
if (!closedGroupPubkey) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const groupConvo = state.conversations.conversationLookup[closedGroupPubkey];
|
||||||
|
|
||||||
|
if (groupConvo.isPrivate || groupConvo.isPublic || !groupConvo.isGroup) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
// this must be a closed group
|
||||||
|
const originalMembers = groupConvo.members;
|
||||||
|
if (!originalMembers || originalMembers.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const allMembersSorted = originalMembers.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
||||||
|
|
||||||
|
// no need to forward more than 2 conversations for rendering the group avatar
|
||||||
|
const usAtTheEndMaxTwo = _.sortBy(allMembersSorted, a => (a === ourPrimary ? 1 : 0)).slice(
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
const memberConvos = _.compact(
|
||||||
|
usAtTheEndMaxTwo
|
||||||
|
.map(m => state.conversations.conversationLookup[m])
|
||||||
|
.map(m => {
|
||||||
|
if (!m) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const userName = m.name || m.profileName || m.id;
|
||||||
|
|
||||||
|
return {
|
||||||
|
avatarPath: m.avatarPath || undefined,
|
||||||
|
id: m.id,
|
||||||
|
name: userName,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return memberConvos && memberConvos.length ? memberConvos : undefined;
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue