Chore: Migrate ShareView to Typescript (#3481)

This commit is contained in:
Reinaldo Neto 2021-11-16 13:19:50 -03:00 committed by GitHub
parent 8e55032118
commit 2b3542d4ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 162 additions and 98 deletions

View File

@ -1,5 +1,3 @@
// @ts-ignore
// eslint-disable-next-line import/no-unresolved
export * from './ImageViewer';
export * from './types';
export * from './ImageComponent';

View File

@ -1,5 +1,4 @@
import React from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, Text, View } from 'react-native';
import I18n from '../../i18n';
@ -35,7 +34,13 @@ const styles = StyleSheet.create({
}
});
const Header = React.memo(({ room, thread, theme }) => {
interface IHeader {
room: { prid?: string; t?: string };
thread: { id?: string };
theme: string;
}
const Header = React.memo(({ room, thread, theme }: IHeader) => {
let type;
if (thread?.id) {
type = 'thread';
@ -88,10 +93,5 @@ const Header = React.memo(({ room, thread, theme }) => {
</View>
);
});
Header.propTypes = {
room: PropTypes.object,
thread: PropTypes.object,
theme: PropTypes.string
};
export default withTheme(Header);

View File

@ -1,5 +1,4 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Video } from 'expo-av';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { ScrollView, StyleSheet, Text } from 'react-native';
@ -15,6 +14,7 @@ import I18n from '../../i18n';
import { isAndroid } from '../../utils/deviceInfo';
import { allowPreview } from './utils';
import { THUMBS_HEIGHT } from './constants';
import { IAttachment, IUseDimensions } from './interfaces';
const MESSAGEBOX_HEIGHT = 56;
@ -35,7 +35,17 @@ const styles = StyleSheet.create({
}
});
const IconPreview = React.memo(({ iconName, title, description, theme, width, height, danger }) => (
interface IIconPreview {
iconName: string;
title: string;
description?: string;
theme: string;
width: number;
height: number;
danger?: boolean;
}
const IconPreview = React.memo(({ iconName, title, description, theme, width, height, danger }: IIconPreview) => (
<ScrollView
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
contentContainerStyle={[styles.fileContainer, { width, height }]}>
@ -45,9 +55,16 @@ const IconPreview = React.memo(({ iconName, title, description, theme, width, he
</ScrollView>
));
const Preview = React.memo(({ item, theme, isShareExtension, length }) => {
interface IPreview {
item: IAttachment;
theme: string;
isShareExtension: boolean;
length: number;
}
const Preview = React.memo(({ item, theme, isShareExtension, length }: IPreview) => {
const type = item?.mime;
const { width, height } = useDimensions();
const { width, height } = useDimensions() as IUseDimensions;
const { isLandscape } = useOrientation();
const insets = useSafeAreaInsets();
const headerHeight = getHeaderHeight(isLandscape);
@ -111,21 +128,5 @@ const Preview = React.memo(({ item, theme, isShareExtension, length }) => {
/>
);
});
Preview.propTypes = {
item: PropTypes.object,
theme: PropTypes.string,
isShareExtension: PropTypes.bool,
length: PropTypes.number
};
IconPreview.propTypes = {
iconName: PropTypes.string,
title: PropTypes.string,
description: PropTypes.string,
theme: PropTypes.string,
width: PropTypes.number,
height: PropTypes.number,
danger: PropTypes.bool
};
export default Preview;

View File

@ -1,5 +1,4 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FlatList, Image, StyleSheet, View } from 'react-native';
import { RectButton, TouchableNativeFeedback, TouchableOpacity } from 'react-native-gesture-handler';
@ -9,6 +8,7 @@ import { CustomIcon } from '../../lib/Icons';
import { isIOS } from '../../utils/deviceInfo';
import { THUMBS_HEIGHT } from './constants';
import { allowPreview } from './utils';
import { IAttachment } from './interfaces';
const THUMB_SIZE = 64;
@ -60,23 +60,35 @@ const styles = StyleSheet.create({
}
});
const ThumbButton = isIOS ? TouchableOpacity : TouchableNativeFeedback;
interface IThumbContent {
item: IAttachment;
theme: string;
isShareExtension: boolean;
}
const ThumbContent = React.memo(({ item, theme, isShareExtension }) => {
interface IThumb extends IThumbContent {
onPress(item: IAttachment): void;
onRemove(item: IAttachment): void;
}
interface IThumbs extends Omit<IThumb, 'item'> {
attachments: IAttachment[];
}
const ThumbContent = React.memo(({ item, theme, isShareExtension }: IThumbContent) => {
const type = item?.mime;
if (type?.match(/image/)) {
// Disallow preview of images too big in order to prevent memory issues on iOS share extension
if (allowPreview(isShareExtension, item?.size)) {
return <Image source={{ uri: item.path }} style={[styles.thumb, { borderColor: themes[theme].borderColor }]} />;
} else {
}
return (
<View style={[styles.thumb, { borderColor: themes[theme].borderColor }]}>
<CustomIcon name='image' size={30} color={themes[theme].tintColor} />
</View>
);
}
}
if (type?.match(/video/)) {
if (isIOS) {
@ -85,7 +97,7 @@ const ThumbContent = React.memo(({ item, theme, isShareExtension }) => {
<CustomIcon name='camera' size={30} color={themes[theme].tintColor} />
</View>
);
} else {
}
const { uri } = item;
return (
<>
@ -94,13 +106,14 @@ const ThumbContent = React.memo(({ item, theme, isShareExtension }) => {
</>
);
}
}
// Multiple files upload of files different than image/video is not implemented, so there's no thumb
return null;
});
const Thumb = ({ item, theme, isShareExtension, onPress, onRemove }) => (
const ThumbButton: typeof React.Component = isIOS ? TouchableOpacity : TouchableNativeFeedback;
const Thumb = ({ item, theme, isShareExtension, onPress, onRemove }: IThumb) => (
<ThumbButton style={styles.item} onPress={() => onPress(item)} activeOpacity={0.7}>
<>
<ThumbContent item={item} theme={theme} isShareExtension={isShareExtension} />
@ -121,7 +134,7 @@ const Thumb = ({ item, theme, isShareExtension, onPress, onRemove }) => (
</ThumbButton>
);
const Thumbs = React.memo(({ attachments, theme, isShareExtension, onPress, onRemove }) => {
const Thumbs = React.memo(({ attachments, theme, isShareExtension, onPress, onRemove }: IThumbs) => {
if (attachments?.length > 1) {
return (
<FlatList
@ -143,24 +156,5 @@ const Thumbs = React.memo(({ attachments, theme, isShareExtension, onPress, onRe
}
return null;
});
Thumbs.propTypes = {
attachments: PropTypes.array,
theme: PropTypes.string,
isShareExtension: PropTypes.bool,
onPress: PropTypes.func,
onRemove: PropTypes.func
};
Thumb.propTypes = {
item: PropTypes.object,
theme: PropTypes.string,
isShareExtension: PropTypes.bool,
onPress: PropTypes.func,
onRemove: PropTypes.func
};
ThumbContent.propTypes = {
item: PropTypes.object,
theme: PropTypes.string,
isShareExtension: PropTypes.bool
};
export default Thumbs;

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
import { RouteProp } from '@react-navigation/native';
import { NativeModules, Text, View } from 'react-native';
import { connect } from 'react-redux';
import ShareExtension from 'rn-extensions-share';
@ -24,9 +25,61 @@ import Thumbs from './Thumbs';
import Preview from './Preview';
import Header from './Header';
import styles from './styles';
import { IAttachment, IServer } from './interfaces';
class ShareView extends Component {
constructor(props) {
interface IShareViewState {
selected: IAttachment;
loading: boolean;
readOnly: boolean;
attachments: IAttachment[];
text: string;
// TODO: Refactor when migrate room
room: any;
thread: any;
maxFileSize: number;
mediaAllowList: number;
}
interface IShareViewProps {
// TODO: Refactor after react-navigation
navigation: StackNavigationProp<any, 'ShareView'>;
route: RouteProp<
{
ShareView: {
attachments: IAttachment[];
isShareView?: boolean;
isShareExtension: boolean;
serverInfo: IServer;
text: string;
room: any;
thread: any; // change
};
},
'ShareView'
>;
theme: string;
user: {
id: string;
username: string;
token: string;
};
server: string;
FileUpload_MediaTypeWhiteList?: number;
FileUpload_MaxFileSize?: number;
}
interface IMessageBoxShareView {
text: string;
forceUpdate(): void;
}
class ShareView extends Component<IShareViewProps, IShareViewState> {
private messagebox: React.RefObject<IMessageBoxShareView>;
private files: any[];
private isShareExtension: boolean;
private serverInfo: any;
constructor(props: IShareViewProps) {
super(props);
this.messagebox = React.createRef();
this.files = props.route.params?.attachments ?? [];
@ -34,7 +87,7 @@ class ShareView extends Component {
this.serverInfo = props.route.params?.serverInfo ?? {};
this.state = {
selected: {},
selected: {} as IAttachment,
loading: false,
readOnly: false,
attachments: [],
@ -61,7 +114,7 @@ class ShareView extends Component {
const { room, thread, readOnly, attachments } = this.state;
const { navigation, theme } = this.props;
const options = {
const options: StackNavigationOptions = {
headerTitle: () => <Header room={room} thread={thread} />,
headerTitleAlign: 'left',
headerTintColor: themes[theme].previewTintColor
@ -69,9 +122,7 @@ class ShareView extends Component {
// if is share extension show default back button
if (!this.isShareExtension) {
options.headerLeft = () => (
<HeaderButton.CloseModal navigation={navigation} buttonStyle={{ color: themes[theme].previewTintColor }} />
);
options.headerLeft = () => <HeaderButton.CloseModal navigation={navigation} />;
}
if (!attachments.length && !readOnly) {
@ -203,10 +254,10 @@ class ShareView extends Component {
}
};
selectFile = item => {
selectFile = (item: IAttachment) => {
const { attachments, selected } = this.state;
if (attachments.length > 0) {
const { text } = this.messagebox.current;
const text = this.messagebox.current?.text;
const newAttachments = attachments.map(att => {
if (att.path === selected.path) {
att.description = text;
@ -217,7 +268,7 @@ class ShareView extends Component {
}
};
removeFile = item => {
removeFile = (item: IAttachment) => {
const { selected, attachments } = this.state;
let newSelected;
if (item.path === selected.path) {
@ -235,7 +286,7 @@ class ShareView extends Component {
});
};
onChangeText = text => {
onChangeText = (text: string) => {
this.setState({ text });
};
@ -318,21 +369,7 @@ class ShareView extends Component {
}
}
ShareView.propTypes = {
navigation: PropTypes.object,
route: PropTypes.object,
theme: PropTypes.string,
user: PropTypes.shape({
id: PropTypes.string.isRequired,
username: PropTypes.string.isRequired,
token: PropTypes.string.isRequired
}),
server: PropTypes.string,
FileUpload_MediaTypeWhiteList: PropTypes.string,
FileUpload_MaxFileSize: PropTypes.string
};
const mapStateToProps = state => ({
const mapStateToProps = (state: any) => ({
user: getUserSelector(state),
server: state.share.server.server || state.server.server,
FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList,

View File

@ -0,0 +1,33 @@
export interface IAttachment {
filename: string;
description?: string;
size: number;
mime?: string;
path: string;
canUpload: boolean;
error?: any;
uri: string;
}
export interface IUseDimensions {
width: number;
height: number;
}
// TODO: move this to specific folder
export interface IServer {
name: string;
iconURL: string;
useRealName: boolean;
FileUpload_MediaTypeWhiteList: string;
FileUpload_MaxFileSize: number;
roomsUpdatedAt: Date;
version: string;
lastLocalAuthenticatedSession: Date;
autoLock: boolean;
autoLockTime: number | null;
biometry: boolean | null;
uniqueID: string;
enterpriseModules: string;
E2E_Enable: boolean;
}

View File

@ -1,4 +0,0 @@
import { isAndroid } from '../../utils/deviceInfo';
// Limit preview to 3MB on iOS share extension
export const allowPreview = (isShareExtension, size) => isAndroid || !isShareExtension || size < 3000000;

View File

@ -0,0 +1,5 @@
import { isAndroid } from '../../utils/deviceInfo';
// Limit preview to 3MB on iOS share extension
export const allowPreview = (isShareExtension: boolean, size: number): boolean =>
isAndroid || !isShareExtension || size < 3000000;