Chore: Migrate ShareView to Typescript (#3481)
This commit is contained in:
parent
8e55032118
commit
2b3542d4ae
|
@ -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';
|
||||||
|
|
|
@ -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);
|
|
@ -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;
|
|
@ -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;
|
|
@ -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,
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
|
|
@ -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;
|
Loading…
Reference in New Issue