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 './ImageViewer';
export * from './types'; export * from './types';
export * from './ImageComponent'; export * from './ImageComponent';

View File

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

View File

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

View File

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

View File

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