Chore: Evaluate RoomView - TypeScript (#4134)

* Chore: Evaluate RoomView - TypeScript

* fix messagebox and list refs

* fix the refs

* refactor other refs

* remove any from privates

* storyshot tweak
This commit is contained in:
Reinaldo Neto 2022-05-02 19:58:23 -03:00 committed by GitHub
parent cb1dabbc16
commit a84d4e9534
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 77 additions and 67 deletions

View File

@ -7,8 +7,8 @@ export type TActionSheetOptionsItem = { title: string; icon: TIconsName; onPress
export type TActionSheetOptions = {
options: TActionSheetOptionsItem[];
headerHeight: number;
customHeader: React.ReactElement | null;
headerHeight?: number;
customHeader?: React.ReactElement | null;
hasCancel?: boolean;
};
interface IActionSheetProvider {

View File

@ -19,7 +19,7 @@ import { IApplicationState, ILoggedUser, TAnyMessageModel, TSubscriptionModel }
import { getPermalinkMessage, hasPermission } from '../../lib/methods';
import { Services } from '../../lib/services';
export interface IMessageActions {
export interface IMessageActionsProps {
room: TSubscriptionModel;
tmid?: string;
user: Pick<ILoggedUser, 'id'>;
@ -43,8 +43,12 @@ export interface IMessageActions {
pinMessagePermission?: string[];
}
export interface IMessageActions {
showMessageActions: (message: TAnyMessageModel) => Promise<void>;
}
const MessageActions = React.memo(
forwardRef(
forwardRef<IMessageActions, IMessageActionsProps>(
(
{
room,
@ -68,7 +72,7 @@ const MessageActions = React.memo(
deleteMessagePermission,
forceDeleteMessagePermission,
pinMessagePermission
}: IMessageActions,
},
ref
) => {
let permissions = {

View File

@ -128,7 +128,7 @@ interface IMessageBoxState {
}
class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
private text: string;
public text: string;
private selection: { start: number; end: number };
@ -1182,4 +1182,6 @@ const dispatchToProps = {
typing: (rid: any, status: any) => userTypingAction(rid, status)
};
export default connect(mapStateToProps, dispatchToProps, null, { forwardRef: true })(withActionSheet(MessageBox)) as any;
export type MessageBoxType = MessageBox;
export default connect(mapStateToProps, dispatchToProps, null, { forwardRef: true })(withActionSheet(MessageBox));

View File

@ -9,9 +9,12 @@ import log from '../utils/log';
import { TMessageModel } from '../definitions';
import { resendMessage } from '../lib/methods';
const MessageErrorActions = forwardRef(({ tmid }: { tmid: string }, ref) => {
// TODO - remove this any after merge ActionSheet evaluate
const { showActionSheet }: any = useActionSheet();
export interface IMessageErrorActions {
showMessageErrorActions: (message: TMessageModel) => void;
}
const MessageErrorActions = forwardRef<IMessageErrorActions, { tmid?: string }>(({ tmid }, ref) => {
const { showActionSheet } = useActionSheet();
const handleResend = protectedFunction(async (message: TMessageModel) => {
await resendMessage(message, tmid);

View File

@ -50,8 +50,12 @@ export interface IJoinCodeProps {
theme: TSupportedThemes;
}
export interface IJoinCode {
show: () => void;
}
const JoinCode = React.memo(
forwardRef(({ rid, t, onJoin, isMasterDetail, theme }: IJoinCodeProps, ref) => {
forwardRef<IJoinCode, IJoinCodeProps>(({ rid, t, onJoin, isMasterDetail, theme }, ref) => {
const [visible, setVisible] = useState(false);
const [error, setError] = useState(false);
const [code, setCode] = useState('');
@ -125,4 +129,5 @@ const JoinCode = React.memo(
const mapStateToProps = (state: IApplicationState) => ({
isMasterDetail: state.app.isMasterDetail
});
export default connect(mapStateToProps, null, null, { forwardRef: true })(JoinCode);

View File

@ -17,8 +17,10 @@ const styles = StyleSheet.create({
}
});
export type TListRef = React.RefObject<FlatList & { getNode: () => FlatList }>;
export interface IListProps extends FlatListProps<any> {
listRef: any;
listRef: TListRef;
}
const List = ({ listRef, ...props }: IListProps) => (

View File

@ -16,7 +16,7 @@ import debounce from '../../../utils/debounce';
import { animateNextTransition } from '../../../utils/layoutAnimation';
import log from '../../../utils/log';
import EmptyRoom from '../EmptyRoom';
import List, { IListProps } from './List';
import List, { IListProps, TListRef } from './List';
import NavBottomFAB from './NavBottomFAB';
import { loadMissedMessages, loadThreadMessages } from '../../../lib/methods';
import { Services } from '../../../lib/services';
@ -43,7 +43,7 @@ export interface IListContainerProps {
tmid?: string;
theme: TSupportedThemes;
loading: boolean;
listRef: React.RefObject<IListProps>;
listRef: TListRef;
hideSystemMessages?: string[];
tunread?: string[];
ignored?: string[];
@ -272,8 +272,7 @@ class ListContainer extends React.Component<IListContainerProps, IListContainerS
handleScrollToIndexFailed: FlatListProps<any>['onScrollToIndexFailed'] = params => {
const { listRef } = this.props;
// @ts-ignore
listRef.current.getNode().scrollToIndex({ index: params.highestMeasuredFrameIndex, animated: false });
listRef.current?.getNode().scrollToIndex({ index: params.highestMeasuredFrameIndex, animated: false });
};
jumpToMessage = (messageId: string) =>
@ -283,8 +282,7 @@ class ListContainer extends React.Component<IListContainerProps, IListContainerS
const { listRef } = this.props;
const index = messages.findIndex(item => item.id === messageId);
if (index > -1) {
// @ts-ignore
listRef.current.getNode().scrollToIndex({ index, viewPosition: 0.5, viewOffset: 100 });
listRef.current?.getNode().scrollToIndex({ index, viewPosition: 0.5, viewOffset: 100 });
await new Promise(res => setTimeout(res, 300));
if (!this.viewableItems?.map(vi => vi.key).includes(messageId)) {
if (!this.jumping) {
@ -300,8 +298,7 @@ class ListContainer extends React.Component<IListContainerProps, IListContainerS
}, 10000);
await setTimeout(() => resolve(), 300);
} else {
// @ts-ignore
listRef.current.getNode().scrollToIndex({ index: messages.length - 1, animated: false });
listRef.current?.getNode().scrollToIndex({ index: messages.length - 1, animated: false });
if (!this.jumping) {
return resolve();
}
@ -316,8 +313,7 @@ class ListContainer extends React.Component<IListContainerProps, IListContainerS
jumpToBottom = () => {
const { listRef } = this.props;
// @ts-ignore
listRef.current.getNode().scrollToOffset({ offset: -100 });
listRef.current?.getNode().scrollToOffset({ offset: -100 });
};
renderFooter = () => {
@ -367,4 +363,6 @@ class ListContainer extends React.Component<IListContainerProps, IListContainerS
}
}
export type ListContainerType = ListContainer;
export default ListContainer;

View File

@ -15,8 +15,8 @@ import { replyBroadcast } from '../../actions/messages';
import database from '../../lib/database';
import Message from '../../containers/message';
import MessageActions, { IMessageActions } from '../../containers/MessageActions';
import MessageErrorActions from '../../containers/MessageErrorActions';
import MessageBox, { IMessageBoxProps } from '../../containers/MessageBox';
import MessageErrorActions, { IMessageErrorActions } from '../../containers/MessageErrorActions';
import MessageBox, { MessageBoxType } from '../../containers/MessageBox';
import log, { events, logEvent } from '../../utils/log';
import EventEmitter from '../../utils/events';
import I18n from '../../i18n';
@ -58,10 +58,10 @@ import Separator from './Separator';
import RightButtons from './RightButtons';
import LeftButtons from './LeftButtons';
import styles from './styles';
import JoinCode, { IJoinCodeProps } from './JoinCode';
import JoinCode, { IJoinCode } from './JoinCode';
import UploadProgress from './UploadProgress';
import ReactionPicker from './ReactionPicker';
import List, { IListContainerProps, IListProps } from './List';
import List, { ListContainerType } from './List';
import { ChatsStackParamList } from '../../stacks/types';
import {
IApplicationState,
@ -80,6 +80,8 @@ import {
} from '../../definitions';
import { ICustomEmojis } from '../../reducers/customEmojis';
import { E2E_MESSAGE_TYPE, E2E_STATUS, MESSAGE_TYPE_ANY_LOAD, MessageTypeLoad, themes } from '../../lib/constants';
import { TListRef } from './List/List';
import { ModalStackParamList } from '../../stacks/MasterDetailStack/types';
import {
callJitsi,
canAutoTranslate as canAutoTranslateMethod,
@ -94,6 +96,8 @@ import {
} from '../../lib/methods';
import { Services } from '../../lib/services';
type TStateAttrsUpdate = keyof IRoomViewState;
const stateAttrsUpdate = [
'joined',
'lastOpen',
@ -107,7 +111,10 @@ const stateAttrsUpdate = [
'readOnly',
'member',
'showingBlockingLoader'
];
] as TStateAttrsUpdate[];
type TRoomUpdate = keyof TSubscriptionModel;
const roomAttrsUpdate = [
'f',
'ro',
@ -130,7 +137,7 @@ const roomAttrsUpdate = [
'teamMain',
'teamId',
'onHold'
] as const;
] as TRoomUpdate[];
interface IRoomViewProps extends IBaseScreen<ChatsStackParamList, 'RoomView'> {
user: Pick<ILoggedUser, 'id' | 'username' | 'token' | 'showMessageInMainThread'>;
@ -151,14 +158,12 @@ interface IRoomViewProps extends IBaseScreen<ChatsStackParamList, 'RoomView'> {
insets: EdgeInsets;
}
type TRoomUpdate = typeof roomAttrsUpdate[number];
interface IRoomViewState {
[key: string]: any;
joined: boolean;
room: TSubscriptionModel | { rid: string; t: string; name?: string; fname?: string; prid?: string; joinCodeRequired?: boolean };
roomUpdate: {
[K in TRoomUpdate]?: any; // TODO: get type from TSubscriptionModel
[K in TRoomUpdate]?: any;
};
member: any;
lastOpen: Date | null;
@ -182,23 +187,27 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
private tmid?: string;
private jumpToMessageId?: string;
private jumpToThreadId?: string;
// TODO: review these refs
private messagebox: React.RefObject<IMessageBoxProps>;
private list: React.RefObject<IListContainerProps>;
private joinCode: React.RefObject<IJoinCodeProps>;
private flatList: React.RefObject<IListProps>;
private messagebox: React.RefObject<MessageBoxType>;
private list: React.RefObject<ListContainerType>;
private joinCode: React.RefObject<IJoinCode>;
private flatList: TListRef;
private mounted: boolean;
private sub?: any;
private offset = 0;
private didMountInteraction: any;
private subSubscription?: Subscription;
private queryUnreads?: Subscription;
private retryInit = 0;
private retryInitTimeout?: number;
private retryFindCount = 0;
private retryFindTimeout?: number;
private messageErrorActions?: React.RefObject<any>; // TODO: type me
private messageActions?: React.RefObject<IMessageActions>;
private messageErrorActions?: IMessageErrorActions | null;
private messageActions?: IMessageActions | null;
// Type of InteractionManager.runAfterInteractions
private didMountInteraction?: {
then: (onfulfilled?: (() => any) | undefined, onrejected?: (() => any) | undefined) => Promise<any>;
done: (...args: any[]) => any;
cancel: () => void;
};
private sub?: RoomClass;
constructor(props: IRoomViewProps) {
super(props);
@ -312,6 +321,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
if (member.statusText !== nextState.member.statusText) {
return true;
}
const stateUpdated = stateAttrsUpdate.some(key => nextState[key] !== state[key]);
if (stateUpdated) {
return true;
@ -340,8 +350,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
if (appState === 'foreground' && appState !== prevProps.appState && this.rid) {
// Fire List.query() just to keep observables working
if (this.list && this.list.current) {
// @ts-ignore TODO: is this working?
this.list.current?.query?.();
this.list.current?.query();
}
}
// If it's not direct message
@ -379,7 +388,6 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
const db = database.active;
this.mounted = false;
if (!editing && this.messagebox && this.messagebox.current) {
// @ts-ignore
const { text } = this.messagebox.current;
let obj: TSubscriptionModel | TThreadModel | null = null;
if (this.tmid) {
@ -394,9 +402,9 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
}
if (obj) {
try {
const object = obj;
await db.write(async () => {
// FIXME: why do I need to tell ts this is non null if we have that if condition above?
await obj!.update(r => {
await object.update(r => {
r.draftMessage = text;
});
});
@ -543,20 +551,18 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
});
};
goRoomActionsView = (screen?: string) => {
goRoomActionsView = (screen?: keyof ModalStackParamList) => {
logEvent(events.ROOM_GO_RA);
const { room, member, joined } = this.state;
const { navigation, isMasterDetail } = this.props;
if (isMasterDetail) {
// @ts-ignore TODO: find a way to make it work
navigation.navigate('ModalStackNavigator', {
// @ts-ignore
navigation.navigate('ModalStackNavigator', {
screen: screen ?? 'RoomActionsView',
params: {
rid: this.rid as string,
t: this.t as SubscriptionType,
// @ts-ignore
room,
room: room as ISubscription,
member,
showCloseModal: !!screen,
joined
@ -695,7 +701,6 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
};
errorActionsShow = (message: TAnyMessageModel) => {
// @ts-ignore
this.messageErrorActions?.showMessageErrorActions(message);
};
@ -745,7 +750,6 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
};
onMessageLongPress = (message: TAnyMessageModel) => {
// @ts-ignore
this.messageActions?.showMessageActions(message);
};
@ -875,10 +879,8 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
if (message.fromServer && !message.tmid && this.rid) {
await loadSurroundingMessages({ messageId, rid: this.rid });
}
// @ts-ignore
await Promise.race([this.list.current.jumpToMessage(message.id), new Promise(res => setTimeout(res, 5000))]);
// @ts-ignore
this.list.current.cancelJumpToMessage();
await Promise.race([this.list.current?.jumpToMessage(message.id), new Promise(res => setTimeout(res, 5000))]);
this.list.current?.cancelJumpToMessage();
}
} catch (e) {
log(e);
@ -920,8 +922,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
const { user } = this.props;
sendMessage(rid, message, this.tmid || tmid, user, tshow).then(() => {
if (this.list && this.list.current) {
// @ts-ignore
this.list.current.update();
this.list.current?.update();
}
this.setLastOpen(null);
Review.pushPositiveEvent();
@ -959,7 +960,6 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
} else {
const { joinCodeRequired, rid } = room;
if (joinCodeRequired) {
// @ts-ignore
this.joinCode.current?.show();
} else {
await Services.joinRoom(rid, null, this.t as any);
@ -1102,20 +1102,20 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
if (handleCommandScroll(event)) {
const offset = input === 'UIKeyInputUpArrow' ? 100 : -100;
this.offset += offset;
// @ts-ignore
this.flatList?.scrollToOffset({ offset: this.offset });
this.flatList?.current?.scrollToOffset({ offset: this.offset });
} else if (handleCommandRoomActions(event)) {
this.goRoomActionsView();
} else if (handleCommandSearchMessages(event)) {
this.goRoomActionsView('SearchMessagesView');
} else if (handleCommandReplyLatest(event)) {
if (this.list && this.list.current) {
// @ts-ignore
const message = this.list.current.getLastMessage();
if (message) {
this.onReplyInit(message, false);
}
}
}
}
};
blockAction = ({
@ -1360,7 +1360,6 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
return (
<>
<MessageActions
// @ts-ignore
ref={ref => (this.messageActions = ref)}
tmid={this.tmid}
room={room}
@ -1371,7 +1370,6 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
onReactionPress={this.onReactionPress}
isReadOnly={readOnly}
/>
{/* @ts-ignore TODO: missing interface on MessageErrorActions */}
<MessageErrorActions ref={ref => (this.messageErrorActions = ref)} tmid={this.tmid} />
</>
);
@ -1396,11 +1394,9 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
<StatusBar />
<Banner title={I18n.t('Announcement')} text={announcement} bannerClosed={bannerClosed} closeBanner={this.closeBanner} />
<List
// @ts-ignore
ref={this.list}
listRef={this.flatList}
rid={rid}
t={t}
tmid={this.tmid}
theme={theme}
tunread={tunread}