Chore: Properly type Status (#3911)

Co-authored-by: Gleidson Daniel Silva <gleidson10daniel@hotmail.com>
This commit is contained in:
Diego Mello 2022-03-25 17:05:49 -03:00 committed by GitHub
parent 70cb252d1b
commit 902827422b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 74 additions and 72 deletions

View File

@ -7,6 +7,7 @@ import { themes } from '../../constants/colors';
import { MarkdownPreview } from '../markdown'; import { MarkdownPreview } from '../markdown';
import RoomTypeIcon from '../RoomTypeIcon'; import RoomTypeIcon from '../RoomTypeIcon';
import { withTheme } from '../../theme'; import { withTheme } from '../../theme';
import { TUserStatus } from '../../definitions';
const HIT_SLOP = { const HIT_SLOP = {
top: 5, top: 5,
@ -67,7 +68,7 @@ interface IRoomHeader {
prid: string; prid: string;
tmid: string; tmid: string;
teamMain: boolean; teamMain: boolean;
status: string; status: TUserStatus;
theme?: string; theme?: string;
usersTyping: []; usersTyping: [];
isGroupChat: boolean; isGroupChat: boolean;

View File

@ -2,7 +2,7 @@ import { dequal } from 'dequal';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { IApplicationState } from '../../definitions'; import { IApplicationState, TUserStatus } from '../../definitions';
import { withDimensions } from '../../dimensions'; import { withDimensions } from '../../dimensions';
import I18n from '../../i18n'; import I18n from '../../i18n';
import RoomHeader from './RoomHeader'; import RoomHeader from './RoomHeader';
@ -15,7 +15,7 @@ interface IRoomHeaderContainerProps {
tmid: string; tmid: string;
teamMain: boolean; teamMain: boolean;
usersTyping: []; usersTyping: [];
status: string; status: TUserStatus;
statusText: string; statusText: string;
connecting: boolean; connecting: boolean;
connected: boolean; connected: boolean;
@ -140,7 +140,7 @@ const mapStateToProps = (state: IApplicationState, ownProps: any) => {
connecting: state.meteor.connecting || state.server.loading, connecting: state.meteor.connecting || state.server.loading,
connected: state.meteor.connected, connected: state.meteor.connected,
usersTyping: state.usersTyping, usersTyping: state.usersTyping,
status, status: status as TUserStatus,
statusText statusText
}; };
}; };

View File

@ -5,6 +5,7 @@ import { CustomIcon } from '../lib/Icons';
import { STATUS_COLORS, themes } from '../constants/colors'; import { STATUS_COLORS, themes } from '../constants/colors';
import Status from './Status/Status'; import Status from './Status/Status';
import { withTheme } from '../theme'; import { withTheme } from '../theme';
import { TUserStatus } from '../definitions';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
icon: { icon: {
@ -17,7 +18,7 @@ interface IRoomTypeIcon {
type: string; type: string;
isGroupChat?: boolean; isGroupChat?: boolean;
teamMain?: boolean; teamMain?: boolean;
status?: string; status?: TUserStatus;
size?: number; size?: number;
style?: ViewStyle; style?: ViewStyle;
} }
@ -31,9 +32,10 @@ const RoomTypeIcon = React.memo(({ type, isGroupChat, status, style, theme, team
const iconStyle = [styles.icon, { color }, style]; const iconStyle = [styles.icon, { color }, style];
if (type === 'd' && !isGroupChat) { if (type === 'd' && !isGroupChat) {
return ( if (!status) {
<Status style={[iconStyle, { color: STATUS_COLORS[status!] ?? STATUS_COLORS.offline }]} size={size} status={status!} /> status = 'offline';
); }
return <Status style={[iconStyle, { color: STATUS_COLORS[status] }]} size={size} status={status} />;
} }
// TODO: move this to a separate function // TODO: move this to a separate function

View File

@ -3,15 +3,9 @@ import { StyleProp, TextStyle } from 'react-native';
import { CustomIcon } from '../../lib/Icons'; import { CustomIcon } from '../../lib/Icons';
import { STATUS_COLORS } from '../../constants/colors'; import { STATUS_COLORS } from '../../constants/colors';
import { IStatus } from './definition';
interface IStatus { const Status = React.memo(({ style, status = 'offline', size = 32, ...props }: Omit<IStatus, 'id'>) => {
status: string;
size: number;
style?: StyleProp<TextStyle>;
testID?: string;
}
const Status = React.memo(({ style, status = 'offline', size = 32, ...props }: IStatus) => {
const name = `status-${status}`; const name = `status-${status}`;
const isNameValid = CustomIcon.hasIcon(name); const isNameValid = CustomIcon.hasIcon(name);
const iconName = isNameValid ? name : 'status-offline'; const iconName = isNameValid ? name : 'status-offline';

View File

@ -0,0 +1,9 @@
import { TextProps } from 'react-native';
import { TUserStatus } from '../../definitions';
export interface IStatus extends TextProps {
id: string;
size: number;
status: TUserStatus;
}

View File

@ -1,20 +1,15 @@
import React, { memo } from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { useSelector } from 'react-redux';
import { IApplicationState, TUserStatus } from '../../definitions';
import Status from './Status'; import Status from './Status';
import { IStatus } from './definition';
interface IStatusContainer { const StatusContainer = ({ id, style, size = 32, ...props }: Omit<IStatus, 'status'>): React.ReactElement => {
style: any; const status = useSelector((state: IApplicationState) =>
size: number; state.meteor.connected ? state.activeUsers[id] && state.activeUsers[id].status : 'loading'
status: string; ) as TUserStatus;
} return <Status size={size} style={style} status={status} {...props} />;
};
const StatusContainer = memo(({ style, size = 32, status }: IStatusContainer) => ( export default StatusContainer;
<Status size={size} style={style} status={status} />
));
const mapStateToProps = (state: any, ownProps: any) => ({
status: state.meteor.connected ? state.activeUsers[ownProps.id] && state.activeUsers[ownProps.id].status : 'loading'
});
export default connect(mapStateToProps)(StatusContainer);

View File

@ -1,7 +1,7 @@
import Model from '@nozbe/watermelondb/Model'; import Model from '@nozbe/watermelondb/Model';
import { IUserEmail, IUserSettings } from './IUser'; import { IUserEmail, IUserSettings } from './IUser';
import { UserStatus } from './UserStatus'; import { TUserStatus } from './TUserStatus';
export interface ILoggedUser { export interface ILoggedUser {
id: string; id: string;
@ -9,7 +9,7 @@ export interface ILoggedUser {
username: string; username: string;
name: string; name: string;
language?: string; language?: string;
status: UserStatus; status: TUserStatus;
statusText?: string; statusText?: string;
customFields?: { customFields?: {
[key: string]: any; [key: string]: any;

View File

@ -1,6 +1,6 @@
import Model from '@nozbe/watermelondb/Model'; import Model from '@nozbe/watermelondb/Model';
import { UserStatus } from './UserStatus'; import { TUserStatus } from './TUserStatus';
import { IRocketChatRecord } from './IRocketChatRecord'; import { IRocketChatRecord } from './IRocketChatRecord';
import { ILoggedUser } from './ILoggedUser'; import { ILoggedUser } from './ILoggedUser';
@ -25,7 +25,7 @@ export interface IPersonalAccessToken extends ILoginToken {
export interface IUserRegistered { export interface IUserRegistered {
_id: string; _id: string;
type: string; type: string;
status: UserStatus; status: TUserStatus;
active: boolean; active: boolean;
name: string; name: string;
username: string; username: string;
@ -133,14 +133,14 @@ export interface IUser extends IRocketChatRecord, Omit<ILoggedUser, 'username' |
name?: string; name?: string;
services?: IUserServices; services?: IUserServices;
emails?: IUserEmail[]; emails?: IUserEmail[];
status: UserStatus; status: TUserStatus;
statusConnection?: string; statusConnection?: string;
lastLogin?: Date; lastLogin?: Date;
avatarOrigin?: string; avatarOrigin?: string;
avatarETag?: string; avatarETag?: string;
utcOffset?: number; utcOffset?: number;
language?: string; language?: string;
statusDefault?: UserStatus; statusDefault?: TUserStatus;
statusText?: string; statusText?: string;
oauth?: { oauth?: {
authorizedClients: string[]; authorizedClients: string[];

View File

@ -0,0 +1,3 @@
export const STATUSES = ['offline', 'online', 'away', 'busy'] as const;
export type TUserStatus = typeof STATUSES[number];

View File

@ -1,6 +0,0 @@
export enum UserStatus {
ONLINE = 'online',
AWAY = 'away',
OFFLINE = 'offline',
BUSY = 'busy'
}

View File

@ -26,6 +26,7 @@ export * from './ICertificate';
export * from './IUrl'; export * from './IUrl';
export * from './ICredentials'; export * from './ICredentials';
export * from './ISearch'; export * from './ISearch';
export * from './TUserStatus';
export interface IBaseScreen<T extends Record<string, object | undefined>, S extends string> { export interface IBaseScreen<T extends Record<string, object | undefined>, S extends string> {
navigation: StackNavigationProp<T, S>; navigation: StackNavigationProp<T, S>;

View File

@ -73,7 +73,6 @@ export const THEME_PREFERENCES_KEY = 'RC_THEME_PREFERENCES_KEY';
export const CRASH_REPORT_KEY = 'RC_CRASH_REPORT_KEY'; export const CRASH_REPORT_KEY = 'RC_CRASH_REPORT_KEY';
export const ANALYTICS_EVENTS_KEY = 'RC_ANALYTICS_EVENTS_KEY'; export const ANALYTICS_EVENTS_KEY = 'RC_ANALYTICS_EVENTS_KEY';
export const MIN_ROCKETCHAT_VERSION = '0.70.0'; export const MIN_ROCKETCHAT_VERSION = '0.70.0';
export const STATUSES = ['offline', 'online', 'away', 'busy'];
const RocketChat = { const RocketChat = {
TOKEN_KEY, TOKEN_KEY,

View File

@ -6,7 +6,6 @@ import { Q } from '@nozbe/watermelondb';
import log from '../../../utils/log'; import log from '../../../utils/log';
import { onRolesChanged } from '../../methods/getRoles'; import { onRolesChanged } from '../../methods/getRoles';
import { UserStatus } from '../../../definitions/UserStatus';
import { setActiveUsers } from '../../../actions/activeUsers'; import { setActiveUsers } from '../../../actions/activeUsers';
import protectedFunction from '../../methods/helpers/protectedFunction'; import protectedFunction from '../../methods/helpers/protectedFunction';
import database from '../../database'; import database from '../../database';
@ -17,8 +16,8 @@ import { store } from '../../auxStore';
import { loginRequest, setLoginServices, setUser } from '../../../actions/login'; import { loginRequest, setLoginServices, setUser } from '../../../actions/login';
import sdk from './sdk'; import sdk from './sdk';
import I18n from '../../../i18n'; import I18n from '../../../i18n';
import RocketChat, { MIN_ROCKETCHAT_VERSION, STATUSES } from '../rocketchat'; import RocketChat, { MIN_ROCKETCHAT_VERSION } from '../rocketchat';
import { ICredentials, ILoggedUser, IRocketChat } from '../../../definitions'; import { ICredentials, ILoggedUser, IRocketChat, STATUSES } from '../../../definitions';
import { isIOS } from '../../../utils/deviceInfo'; import { isIOS } from '../../../utils/deviceInfo';
import { connectRequest, connectSuccess, disconnect as disconnectAction } from '../../../actions/connect'; import { connectRequest, connectSuccess, disconnect as disconnectAction } from '../../../actions/connect';
import { updatePermission } from '../../../actions/permissions'; import { updatePermission } from '../../../actions/permissions';
@ -195,7 +194,7 @@ function connect(
const { user: loggedUser } = store.getState().login; const { user: loggedUser } = store.getState().login;
if (loggedUser && loggedUser.id === id) { if (loggedUser && loggedUser.id === id) {
store.dispatch(setUser({ status: STATUSES[status] as UserStatus, statusText })); store.dispatch(setUser({ status: STATUSES[status], statusText }));
} }
} else if (/updateAvatar/.test(eventName)) { } else if (/updateAvatar/.test(eventName)) {
const { username, etag } = ddpMessage.fields.args[0]; const { username, etag } = ddpMessage.fields.args[0];

View File

@ -12,6 +12,7 @@ import Touchable from './Touchable';
import Tag from './Tag'; import Tag from './Tag';
import I18n from '../../i18n'; import I18n from '../../i18n';
import { DisplayMode } from '../../constants/constantDisplayMode'; import { DisplayMode } from '../../constants/constantDisplayMode';
import { TUserStatus } from '../../definitions';
interface IRoomItem { interface IRoomItem {
rid: string; rid: string;
@ -24,7 +25,7 @@ interface IRoomItem {
avatarSize: number; avatarSize: number;
testID: string; testID: string;
width: number; width: number;
status: string; status: TUserStatus;
useRealName: boolean; useRealName: boolean;
theme: string; theme: string;
isFocused: boolean; isFocused: boolean;

View File

@ -1,10 +1,11 @@
import React from 'react'; import React from 'react';
import { TUserStatus } from '../../definitions';
import RoomTypeIcon from '../../containers/RoomTypeIcon'; import RoomTypeIcon from '../../containers/RoomTypeIcon';
interface ITypeIcon { interface ITypeIcon {
type: string; type: string;
status: string; status: TUserStatus;
prid: string; prid: string;
isGroupChat: boolean; isGroupChat: boolean;
teamMain: boolean; teamMain: boolean;

View File

@ -5,6 +5,7 @@ import I18n from '../../i18n';
import { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from './styles'; import { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from './styles';
import { formatDate } from '../../utils/room'; import { formatDate } from '../../utils/room';
import RoomItem from './RoomItem'; import RoomItem from './RoomItem';
import { TUserStatus } from '../../definitions';
export { ROW_HEIGHT, ROW_HEIGHT_CONDENSED }; export { ROW_HEIGHT, ROW_HEIGHT_CONDENSED };
interface IRoomItemContainerProps { interface IRoomItemContainerProps {
@ -16,7 +17,7 @@ interface IRoomItemContainerProps {
username: string; username: string;
avatarSize: number; avatarSize: number;
width: number; width: number;
status: string; status: TUserStatus;
toggleFav(): void; toggleFav(): void;
toggleRead(): void; toggleRead(): void;
hideChannel(): void; hideChannel(): void;
@ -53,7 +54,7 @@ class RoomItemContainer extends React.Component<IRoomItemContainerProps, any> {
private roomSubscription: any; private roomSubscription: any;
static defaultProps = { static defaultProps: Partial<IRoomItemContainerProps> = {
avatarSize: 48, avatarSize: 48,
status: 'offline', status: 'offline',
getUserPresence: () => {}, getUserPresence: () => {},
@ -233,7 +234,7 @@ const mapStateToProps = (state: any, ownProps: any) => {
} }
return { return {
connected: state.meteor.connected, connected: state.meteor.connected,
status status: status as TUserStatus
}; };
}; };

View File

@ -1,5 +1,4 @@
import { clearActiveUsers, setActiveUsers } from '../actions/activeUsers'; import { clearActiveUsers, setActiveUsers } from '../actions/activeUsers';
import { UserStatus } from '../definitions/UserStatus';
import { IActiveUsers, initialState } from './activeUsers'; import { IActiveUsers, initialState } from './activeUsers';
import { mockedStore } from './mockedStore'; import { mockedStore } from './mockedStore';
@ -9,7 +8,7 @@ describe('test reducer', () => {
expect(state).toEqual(initialState); expect(state).toEqual(initialState);
}); });
it('should return modified store after action', () => { it('should return modified store after action', () => {
const activeUsers: IActiveUsers = { any: { status: UserStatus.ONLINE, statusText: 'any' } }; const activeUsers: IActiveUsers = { any: { status: 'online', statusText: 'any' } };
mockedStore.dispatch(setActiveUsers(activeUsers)); mockedStore.dispatch(setActiveUsers(activeUsers));
const state = mockedStore.getState().activeUsers; const state = mockedStore.getState().activeUsers;
expect(state).toEqual({ ...activeUsers }); expect(state).toEqual({ ...activeUsers });

View File

@ -1,9 +1,8 @@
import { ACTIVE_USERS } from '../actions/actionsTypes'; import { ACTIVE_USERS } from '../actions/actionsTypes';
import { TApplicationActions } from '../definitions'; import { TApplicationActions, TUserStatus } from '../definitions';
import { UserStatus } from '../definitions/UserStatus';
export interface IActiveUser { export interface IActiveUser {
status: UserStatus; status: TUserStatus;
statusText: string; statusText: string;
} }

View File

@ -1,3 +1,4 @@
import { TUserStatus } from '../definitions';
import { import {
clearUser, clearUser,
loginFailure, loginFailure,
@ -8,7 +9,6 @@ import {
setLoginServices, setLoginServices,
setUser setUser
} from '../actions/login'; } from '../actions/login';
import { UserStatus } from '../definitions/UserStatus';
import { initialState } from './login'; import { initialState } from './login';
import { mockedStore } from './mockedStore'; import { mockedStore } from './mockedStore';
@ -49,7 +49,7 @@ describe('test selectedUsers reducer', () => {
isFromWebView: false, isFromWebView: false,
showMessageInMainThread: false, showMessageInMainThread: false,
enableMessageParserEarlyAdoption: false, enableMessageParserEarlyAdoption: false,
status: UserStatus.ONLINE, status: 'online' as TUserStatus,
statusText: 'online' statusText: 'online'
}; };
mockedStore.dispatch(loginSuccess(user)); mockedStore.dispatch(loginSuccess(user));

View File

@ -1,7 +1,6 @@
import { UserStatus } from '../definitions/UserStatus';
import * as types from '../actions/actionsTypes'; import * as types from '../actions/actionsTypes';
import { TActionsLogin } from '../actions/login'; import { TActionsLogin } from '../actions/login';
import { IUser } from '../definitions'; import { IUser, TUserStatus } from '../definitions';
export interface IUserLogin { export interface IUserLogin {
id: string; id: string;
@ -9,7 +8,7 @@ export interface IUserLogin {
username: string; username: string;
name: string; name: string;
language?: string; language?: string;
status: UserStatus; status: TUserStatus;
statusText: string; statusText: string;
roles: string[]; roles: string[];
avatarETag?: string; avatarETag?: string;

View File

@ -19,6 +19,7 @@ import Navigation from '../../lib/Navigation';
import SidebarItem from './SidebarItem'; import SidebarItem from './SidebarItem';
import styles from './styles'; import styles from './styles';
import { DrawerParamList } from '../../stacks/types'; import { DrawerParamList } from '../../stacks/types';
import { TUserStatus } from '../../definitions';
interface ISeparatorProps { interface ISeparatorProps {
theme: string; theme: string;
@ -40,7 +41,7 @@ interface ISidebarProps {
Site_Name: string; Site_Name: string;
user: { user: {
statusText: string; statusText: string;
status: string; status: TUserStatus;
username: string; username: string;
name: string; name: string;
roles: string[]; roles: string[];

View File

@ -2,7 +2,6 @@ import React from 'react';
import { FlatList, StyleSheet } from 'react-native'; import { FlatList, StyleSheet } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { UserStatus } from '../definitions/UserStatus';
import { setUser } from '../actions/login'; import { setUser } from '../actions/login';
import * as HeaderButton from '../containers/HeaderButton'; import * as HeaderButton from '../containers/HeaderButton';
import * as List from '../containers/List'; import * as List from '../containers/List';
@ -11,7 +10,7 @@ import SafeAreaView from '../containers/SafeAreaView';
import Status from '../containers/Status/Status'; import Status from '../containers/Status/Status';
import TextInput from '../containers/TextInput'; import TextInput from '../containers/TextInput';
import { LISTENER } from '../containers/Toast'; import { LISTENER } from '../containers/Toast';
import { IApplicationState, IBaseScreen, IUser } from '../definitions'; import { IApplicationState, IBaseScreen, IUser, TUserStatus } from '../definitions';
import I18n from '../i18n'; import I18n from '../i18n';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import { getUserSelector } from '../selectors/login'; import { getUserSelector } from '../selectors/login';
@ -20,7 +19,12 @@ import EventEmitter from '../utils/events';
import { showErrorAlert } from '../utils/info'; import { showErrorAlert } from '../utils/info';
import log, { events, logEvent } from '../utils/log'; import log, { events, logEvent } from '../utils/log';
const STATUS = [ interface IStatus {
id: TUserStatus;
name: string;
}
const STATUS: IStatus[] = [
{ {
id: 'online', id: 'online',
name: 'Online' name: 'Online'
@ -135,7 +139,7 @@ class StatusView extends React.Component<IStatusViewProps, IStatusViewState> {
value={statusText} value={statusText}
containerStyle={styles.inputContainer} containerStyle={styles.inputContainer}
onChangeText={text => this.setState({ statusText: text })} onChangeText={text => this.setState({ statusText: text })}
left={<Status testID={`status-view-current-${user.status}`} style={styles.inputLeft} status={user.status!} size={24} />} left={<Status testID={`status-view-current-${user.status}`} style={styles.inputLeft} status={user.status} size={24} />}
inputStyle={styles.inputStyle} inputStyle={styles.inputStyle}
placeholder={I18n.t('What_are_you_doing_right_now')} placeholder={I18n.t('What_are_you_doing_right_now')}
testID='status-view-input' testID='status-view-input'
@ -145,7 +149,7 @@ class StatusView extends React.Component<IStatusViewProps, IStatusViewState> {
); );
}; };
renderItem = ({ item }: { item: { id: string; name: string } }) => { renderItem = ({ item }: { item: IStatus }) => {
const { statusText } = this.state; const { statusText } = this.state;
const { user, dispatch } = this.props; const { user, dispatch } = this.props;
const { id, name } = item; const { id, name } = item;
@ -159,7 +163,7 @@ class StatusView extends React.Component<IStatusViewProps, IStatusViewState> {
try { try {
const result = await RocketChat.setUserStatus(item.id, statusText); const result = await RocketChat.setUserStatus(item.id, statusText);
if (result.success) { if (result.success) {
dispatch(setUser({ status: item.id as UserStatus })); dispatch(setUser({ status: item.id }));
} }
} catch (e: any) { } catch (e: any) {
showErrorAlert(I18n.t(e.data.errorType)); showErrorAlert(I18n.t(e.data.errorType));

File diff suppressed because one or more lines are too long