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:
Danish Ahmed Mirza 2022-06-06 21:03:36 +05:30 committed by GitHub
parent 614d9afe65
commit 748e87acf3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 103 additions and 114 deletions

View File

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

View File

@ -1,113 +1,112 @@
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;
if (rid) {
record = await subsCollection.find(rid); record = await subsCollection.find(rid);
} }
}
} catch { } catch {
// Record not found // Record not found
} }
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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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