Chore: Migrate containers/Avatar to hooks (#4139)
* Migrate containers/Avatar to hooks * Migrate to `useSelector` hook and `useRef` * Change user object prop to primitive type * fix re-render and update snapshot * fix lint * update pods Co-authored-by: GleidsonDaniel <gleidson10daniel@hotmail.com>
This commit is contained in:
parent
614d9afe65
commit
748e87acf3
|
@ -8,7 +8,6 @@ import { getAvatarURL } from '../../lib/methods/helpers/getAvatarUrl';
|
||||||
import { SubscriptionType } from '../../definitions';
|
import { SubscriptionType } from '../../definitions';
|
||||||
import Emoji from '../markdown/Emoji';
|
import Emoji from '../markdown/Emoji';
|
||||||
import { IAvatar } from './interfaces';
|
import { IAvatar } from './interfaces';
|
||||||
import { useTheme } from '../../theme';
|
|
||||||
|
|
||||||
const Avatar = React.memo(
|
const Avatar = React.memo(
|
||||||
({
|
({
|
||||||
|
@ -16,7 +15,8 @@ const Avatar = React.memo(
|
||||||
style,
|
style,
|
||||||
avatar,
|
avatar,
|
||||||
children,
|
children,
|
||||||
user,
|
userId,
|
||||||
|
token,
|
||||||
onPress,
|
onPress,
|
||||||
emoji,
|
emoji,
|
||||||
getCustomEmoji,
|
getCustomEmoji,
|
||||||
|
@ -31,8 +31,6 @@ const Avatar = React.memo(
|
||||||
type = SubscriptionType.DIRECT,
|
type = SubscriptionType.DIRECT,
|
||||||
externalProviderUrl
|
externalProviderUrl
|
||||||
}: IAvatar) => {
|
}: IAvatar) => {
|
||||||
const { theme } = useTheme();
|
|
||||||
|
|
||||||
if ((!text && !avatar && !emoji && !rid) || !server) {
|
if ((!text && !avatar && !emoji && !rid) || !server) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -46,14 +44,7 @@ const Avatar = React.memo(
|
||||||
let image;
|
let image;
|
||||||
if (emoji) {
|
if (emoji) {
|
||||||
image = (
|
image = (
|
||||||
<Emoji
|
<Emoji baseUrl={server} getCustomEmoji={getCustomEmoji} isMessageContainsOnlyEmoji literal={emoji} style={avatarStyle} />
|
||||||
theme={theme}
|
|
||||||
baseUrl={server}
|
|
||||||
getCustomEmoji={getCustomEmoji}
|
|
||||||
isMessageContainsOnlyEmoji
|
|
||||||
literal={emoji}
|
|
||||||
style={avatarStyle}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let uri = avatar;
|
let uri = avatar;
|
||||||
|
@ -62,7 +53,8 @@ const Avatar = React.memo(
|
||||||
type,
|
type,
|
||||||
text,
|
text,
|
||||||
size,
|
size,
|
||||||
user,
|
userId,
|
||||||
|
token,
|
||||||
avatar,
|
avatar,
|
||||||
server,
|
server,
|
||||||
avatarETag,
|
avatarETag,
|
||||||
|
|
|
@ -1,84 +1,65 @@
|
||||||
import React from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import { shallowEqual, useSelector } from 'react-redux';
|
||||||
import { Observable, Subscription } from 'rxjs';
|
import { Observable, Subscription } from 'rxjs';
|
||||||
|
|
||||||
|
import { IApplicationState, TSubscriptionModel, TUserModel } from '../../definitions';
|
||||||
import database from '../../lib/database';
|
import database from '../../lib/database';
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
import { IApplicationState, TSubscriptionModel, TUserModel } from '../../definitions';
|
|
||||||
import Avatar from './Avatar';
|
import Avatar from './Avatar';
|
||||||
import { IAvatar } from './interfaces';
|
import { IAvatar } from './interfaces';
|
||||||
|
|
||||||
class AvatarContainer extends React.Component<IAvatar, any> {
|
const AvatarContainer = ({
|
||||||
private subscription?: Subscription;
|
style,
|
||||||
|
text = '',
|
||||||
|
avatar,
|
||||||
|
emoji,
|
||||||
|
size,
|
||||||
|
borderRadius,
|
||||||
|
type,
|
||||||
|
children,
|
||||||
|
onPress,
|
||||||
|
getCustomEmoji,
|
||||||
|
isStatic,
|
||||||
|
rid
|
||||||
|
}: IAvatar): React.ReactElement => {
|
||||||
|
const subscription = useRef<Subscription>();
|
||||||
|
const [avatarETag, setAvatarETag] = useState<string | undefined>('');
|
||||||
|
|
||||||
static defaultProps = {
|
const isDirect = () => type === 'd';
|
||||||
text: '',
|
|
||||||
type: 'd'
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props: IAvatar) {
|
const server = useSelector((state: IApplicationState) => state.share.server.server || state.server.server);
|
||||||
super(props);
|
const serverVersion = useSelector((state: IApplicationState) => state.share.server.version || state.server.version);
|
||||||
this.state = { avatarETag: '' };
|
const { id, token } = useSelector(
|
||||||
this.init();
|
(state: IApplicationState) => ({
|
||||||
}
|
id: getUserSelector(state).id,
|
||||||
|
token: getUserSelector(state).token
|
||||||
|
}),
|
||||||
|
shallowEqual
|
||||||
|
);
|
||||||
|
|
||||||
componentDidUpdate(prevProps: IAvatar) {
|
const externalProviderUrl = useSelector(
|
||||||
const { text, type } = this.props;
|
(state: IApplicationState) => state.settings.Accounts_AvatarExternalProviderUrl as string
|
||||||
if (prevProps.text !== text || prevProps.type !== type) {
|
);
|
||||||
this.init();
|
const blockUnauthenticatedAccess = useSelector(
|
||||||
}
|
(state: IApplicationState) =>
|
||||||
}
|
(state.share.settings?.Accounts_AvatarBlockUnauthenticatedAccess as boolean) ??
|
||||||
|
state.settings.Accounts_AvatarBlockUnauthenticatedAccess ??
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: IAvatar, nextState: { avatarETag: string }) {
|
const init = async () => {
|
||||||
const { avatarETag } = this.state;
|
|
||||||
const { text, type, size, externalProviderUrl } = this.props;
|
|
||||||
if (nextProps.externalProviderUrl !== externalProviderUrl) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (nextState.avatarETag !== avatarETag) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (nextProps.text !== text) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (nextProps.type !== type) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (nextProps.size !== size) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
if (this.subscription?.unsubscribe) {
|
|
||||||
this.subscription.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get isDirect() {
|
|
||||||
const { type } = this.props;
|
|
||||||
return type === 'd';
|
|
||||||
}
|
|
||||||
|
|
||||||
init = async () => {
|
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const usersCollection = db.get('users');
|
const usersCollection = db.get('users');
|
||||||
const subsCollection = db.get('subscriptions');
|
const subsCollection = db.get('subscriptions');
|
||||||
|
|
||||||
let record;
|
let record;
|
||||||
try {
|
try {
|
||||||
if (this.isDirect) {
|
if (isDirect()) {
|
||||||
const { text } = this.props;
|
|
||||||
const [user] = await usersCollection.query(Q.where('username', text)).fetch();
|
const [user] = await usersCollection.query(Q.where('username', text)).fetch();
|
||||||
record = user;
|
record = user;
|
||||||
} else {
|
} else if (rid) {
|
||||||
const { rid } = this.props;
|
record = await subsCollection.find(rid);
|
||||||
if (rid) {
|
|
||||||
record = await subsCollection.find(rid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Record not found
|
// Record not found
|
||||||
|
@ -86,28 +67,46 @@ class AvatarContainer extends React.Component<IAvatar, any> {
|
||||||
|
|
||||||
if (record) {
|
if (record) {
|
||||||
const observable = record.observe() as Observable<TSubscriptionModel | TUserModel>;
|
const observable = record.observe() as Observable<TSubscriptionModel | TUserModel>;
|
||||||
this.subscription = observable.subscribe(r => {
|
subscription.current = observable.subscribe(r => {
|
||||||
const { avatarETag } = r;
|
setAvatarETag(r.avatarETag);
|
||||||
this.setState({ avatarETag });
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
useEffect(() => {
|
||||||
const { avatarETag } = this.state;
|
if (!avatarETag) {
|
||||||
const { serverVersion } = this.props;
|
init();
|
||||||
return <Avatar {...this.props} avatarETag={avatarETag} serverVersion={serverVersion} />;
|
}
|
||||||
}
|
return () => {
|
||||||
}
|
if (subscription?.current?.unsubscribe) {
|
||||||
|
subscription.current.unsubscribe();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [text, type, size, avatarETag, externalProviderUrl]);
|
||||||
|
|
||||||
const mapStateToProps = (state: IApplicationState) => ({
|
return (
|
||||||
user: getUserSelector(state),
|
<Avatar
|
||||||
server: state.share.server.server || state.server.server,
|
server={server}
|
||||||
serverVersion: state.share.server.version || state.server.version,
|
style={style}
|
||||||
blockUnauthenticatedAccess:
|
text={text}
|
||||||
(state.share.settings?.Accounts_AvatarBlockUnauthenticatedAccess as boolean) ??
|
avatar={avatar}
|
||||||
state.settings.Accounts_AvatarBlockUnauthenticatedAccess ??
|
emoji={emoji}
|
||||||
true,
|
size={size}
|
||||||
externalProviderUrl: state.settings.Accounts_AvatarExternalProviderUrl as string
|
borderRadius={borderRadius}
|
||||||
});
|
type={type}
|
||||||
export default connect(mapStateToProps)(AvatarContainer);
|
children={children}
|
||||||
|
userId={id}
|
||||||
|
token={token}
|
||||||
|
onPress={onPress}
|
||||||
|
getCustomEmoji={getCustomEmoji}
|
||||||
|
isStatic={isStatic}
|
||||||
|
rid={rid}
|
||||||
|
blockUnauthenticatedAccess={blockUnauthenticatedAccess}
|
||||||
|
externalProviderUrl={externalProviderUrl}
|
||||||
|
avatarETag={avatarETag}
|
||||||
|
serverVersion={serverVersion}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AvatarContainer;
|
||||||
|
|
|
@ -5,23 +5,21 @@ import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
export interface IAvatar {
|
export interface IAvatar {
|
||||||
server?: string;
|
server?: string;
|
||||||
style?: any;
|
style?: any;
|
||||||
text: string;
|
text?: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
emoji?: string;
|
emoji?: string;
|
||||||
size?: number;
|
size?: number;
|
||||||
borderRadius?: number;
|
borderRadius?: number;
|
||||||
type?: string;
|
type?: string;
|
||||||
children?: React.ReactElement | null;
|
children?: React.ReactElement | null;
|
||||||
user?: {
|
userId?: string;
|
||||||
id?: string;
|
token?: string;
|
||||||
token?: string;
|
|
||||||
};
|
|
||||||
onPress?: () => void;
|
onPress?: () => void;
|
||||||
getCustomEmoji?: TGetCustomEmoji;
|
getCustomEmoji?: TGetCustomEmoji;
|
||||||
avatarETag?: string;
|
avatarETag?: string;
|
||||||
isStatic?: boolean | string;
|
isStatic?: boolean | string;
|
||||||
rid?: string;
|
rid?: string;
|
||||||
blockUnauthenticatedAccess?: boolean;
|
blockUnauthenticatedAccess?: boolean;
|
||||||
serverVersion: string | null;
|
serverVersion?: string | null;
|
||||||
externalProviderUrl?: string;
|
externalProviderUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,8 @@ import { Text } from 'react-native';
|
||||||
|
|
||||||
import shortnameToUnicode from '../../lib/methods/helpers/shortnameToUnicode';
|
import shortnameToUnicode from '../../lib/methods/helpers/shortnameToUnicode';
|
||||||
import CustomEmoji from '../EmojiPicker/CustomEmoji';
|
import CustomEmoji from '../EmojiPicker/CustomEmoji';
|
||||||
import { themes } from '../../lib/constants';
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { TSupportedThemes } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
|
|
||||||
interface IEmoji {
|
interface IEmoji {
|
||||||
literal: string;
|
literal: string;
|
||||||
|
@ -14,13 +13,13 @@ interface IEmoji {
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
customEmojis?: any;
|
customEmojis?: any;
|
||||||
style?: object;
|
style?: object;
|
||||||
theme: TSupportedThemes;
|
|
||||||
onEmojiSelected?: Function;
|
onEmojiSelected?: Function;
|
||||||
tabEmojiStyle?: object;
|
tabEmojiStyle?: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Emoji = React.memo(
|
const Emoji = React.memo(
|
||||||
({ literal, isMessageContainsOnlyEmoji, getCustomEmoji, baseUrl, customEmojis = true, style = {}, theme }: IEmoji) => {
|
({ literal, isMessageContainsOnlyEmoji, getCustomEmoji, baseUrl, customEmojis = true, style = {} }: IEmoji) => {
|
||||||
|
const { colors } = useTheme();
|
||||||
const emojiUnicode = shortnameToUnicode(literal);
|
const emojiUnicode = shortnameToUnicode(literal);
|
||||||
const emoji: any = getCustomEmoji && getCustomEmoji(literal.replace(/:/g, ''));
|
const emoji: any = getCustomEmoji && getCustomEmoji(literal.replace(/:/g, ''));
|
||||||
if (emoji && customEmojis) {
|
if (emoji && customEmojis) {
|
||||||
|
@ -33,7 +32,7 @@ const Emoji = React.memo(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Text style={[{ color: themes[theme].bodyText }, isMessageContainsOnlyEmoji ? styles.textBig : styles.text, style]}>
|
<Text style={[{ color: colors.bodyText }, isMessageContainsOnlyEmoji ? styles.textBig : styles.text, style]}>
|
||||||
{emojiUnicode}
|
{emojiUnicode}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
|
|
@ -233,7 +233,7 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
};
|
};
|
||||||
|
|
||||||
renderEmoji = ({ literal }: TLiteral) => {
|
renderEmoji = ({ literal }: TLiteral) => {
|
||||||
const { getCustomEmoji, baseUrl = '', customEmojis, style, theme } = this.props;
|
const { getCustomEmoji, baseUrl = '', customEmojis, style } = this.props;
|
||||||
return (
|
return (
|
||||||
<MarkdownEmoji
|
<MarkdownEmoji
|
||||||
literal={literal}
|
literal={literal}
|
||||||
|
@ -242,7 +242,6 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
customEmojis={customEmojis}
|
customEmojis={customEmojis}
|
||||||
style={style}
|
style={style}
|
||||||
theme={theme}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,9 +6,10 @@ const formatUrl = (url: string, size: number, query?: string) => `${url}?format=
|
||||||
|
|
||||||
export const getAvatarURL = ({
|
export const getAvatarURL = ({
|
||||||
type,
|
type,
|
||||||
text,
|
text = '',
|
||||||
size = 25,
|
size = 25,
|
||||||
user = {},
|
userId,
|
||||||
|
token,
|
||||||
avatar,
|
avatar,
|
||||||
server,
|
server,
|
||||||
avatarETag,
|
avatarETag,
|
||||||
|
@ -30,10 +31,9 @@ export const getAvatarURL = ({
|
||||||
room = `@${text}`;
|
room = `@${text}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id, token } = user;
|
|
||||||
let query = '';
|
let query = '';
|
||||||
if (id && token && blockUnauthenticatedAccess) {
|
if (userId && token && blockUnauthenticatedAccess) {
|
||||||
query += `&rc_token=${token}&rc_uid=${id}`;
|
query += `&rc_token=${token}&rc_uid=${userId}`;
|
||||||
}
|
}
|
||||||
if (avatarETag) {
|
if (avatarETag) {
|
||||||
query += `&etag=${avatarETag}`;
|
query += `&etag=${avatarETag}`;
|
||||||
|
|
|
@ -36,7 +36,8 @@ const SelectChannel = ({
|
||||||
getAvatarURL({
|
getAvatarURL({
|
||||||
text: getRoomAvatar(item),
|
text: getRoomAvatar(item),
|
||||||
type: item.t,
|
type: item.t,
|
||||||
user: { id: userId, token },
|
userId,
|
||||||
|
token,
|
||||||
server,
|
server,
|
||||||
avatarETag: item.avatarETag,
|
avatarETag: item.avatarETag,
|
||||||
rid: item.rid,
|
rid: item.rid,
|
||||||
|
|
|
@ -40,7 +40,8 @@ const SelectUsers = ({
|
||||||
getAvatarURL({
|
getAvatarURL({
|
||||||
text: getRoomAvatar(item),
|
text: getRoomAvatar(item),
|
||||||
type: SubscriptionType.DIRECT,
|
type: SubscriptionType.DIRECT,
|
||||||
user: { id: userId, token },
|
userId,
|
||||||
|
token,
|
||||||
server,
|
server,
|
||||||
avatarETag: item.avatarETag,
|
avatarETag: item.avatarETag,
|
||||||
blockUnauthenticatedAccess,
|
blockUnauthenticatedAccess,
|
||||||
|
|
Loading…
Reference in New Issue