Merge branch 'develop' into new.add-discusions-roomactionsview
This commit is contained in:
commit
5dc0a0a05f
File diff suppressed because it is too large
Load Diff
|
@ -14,7 +14,6 @@ public class BasePackageList {
|
||||||
new expo.modules.imageloader.ImageLoaderPackage(),
|
new expo.modules.imageloader.ImageLoaderPackage(),
|
||||||
new expo.modules.keepawake.KeepAwakePackage(),
|
new expo.modules.keepawake.KeepAwakePackage(),
|
||||||
new expo.modules.localauthentication.LocalAuthenticationPackage(),
|
new expo.modules.localauthentication.LocalAuthenticationPackage(),
|
||||||
new expo.modules.permissions.PermissionsPackage(),
|
|
||||||
new expo.modules.videothumbnails.VideoThumbnailsPackage(),
|
new expo.modules.videothumbnails.VideoThumbnailsPackage(),
|
||||||
new expo.modules.webbrowser.WebBrowserPackage()
|
new expo.modules.webbrowser.WebBrowserPackage()
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ScrollView, StyleSheet, View } from 'react-native';
|
import { ScrollView, ScrollViewProps, StyleSheet, View } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
|
@ -10,10 +10,10 @@ import AppVersion from './AppVersion';
|
||||||
import { isTablet } from '../utils/deviceInfo';
|
import { isTablet } from '../utils/deviceInfo';
|
||||||
import SafeAreaView from './SafeAreaView';
|
import SafeAreaView from './SafeAreaView';
|
||||||
|
|
||||||
interface IFormContainer {
|
interface IFormContainer extends ScrollViewProps {
|
||||||
theme: string;
|
theme: string;
|
||||||
testID: string;
|
testID: string;
|
||||||
children: JSX.Element;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet, Text, View } from 'react-native';
|
import { StyleProp, StyleSheet, Text, TextInputProps, TextStyle, View, ViewStyle } from 'react-native';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
import Touchable from 'react-native-platform-touchable';
|
||||||
|
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
|
@ -50,23 +50,21 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IRCTextInputProps {
|
interface IRCTextInputProps extends TextInputProps {
|
||||||
label: string;
|
label?: string;
|
||||||
error: {
|
error?: {
|
||||||
error: any;
|
error: any;
|
||||||
reason: any;
|
reason: any;
|
||||||
};
|
};
|
||||||
loading: boolean;
|
loading?: boolean;
|
||||||
secureTextEntry: boolean;
|
containerStyle?: StyleProp<ViewStyle>;
|
||||||
containerStyle: any;
|
inputStyle?: TextStyle;
|
||||||
inputStyle: object;
|
inputRef?: React.Ref<unknown>;
|
||||||
inputRef: any;
|
testID?: string;
|
||||||
testID: string;
|
iconLeft?: string;
|
||||||
iconLeft: string;
|
iconRight?: string;
|
||||||
iconRight: string;
|
left?: JSX.Element;
|
||||||
placeholder: string;
|
onIconRightPress?(): void;
|
||||||
left: JSX.Element;
|
|
||||||
onIconRightPress(): void;
|
|
||||||
theme: string;
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,17 +146,10 @@ export default class RCTextInput extends React.PureComponent<IRCTextInputProps,
|
||||||
return (
|
return (
|
||||||
<View style={[styles.inputContainer, containerStyle]}>
|
<View style={[styles.inputContainer, containerStyle]}>
|
||||||
{label ? (
|
{label ? (
|
||||||
<Text
|
<Text style={[styles.label, { color: themes[theme].titleText }, error?.error && { color: dangerColor }]}>{label}</Text>
|
||||||
contentDescription={null}
|
|
||||||
// @ts-ignore
|
|
||||||
accessibilityLabel={null}
|
|
||||||
style={[styles.label, { color: themes[theme].titleText }, error.error && { color: dangerColor }]}>
|
|
||||||
{label}
|
|
||||||
</Text>
|
|
||||||
) : null}
|
) : null}
|
||||||
<View style={styles.wrap}>
|
<View style={styles.wrap}>
|
||||||
<TextInput
|
<TextInput
|
||||||
/* @ts-ignore*/
|
|
||||||
style={[
|
style={[
|
||||||
styles.input,
|
styles.input,
|
||||||
iconLeft && styles.inputIconLeft,
|
iconLeft && styles.inputIconLeft,
|
||||||
|
@ -168,14 +159,13 @@ export default class RCTextInput extends React.PureComponent<IRCTextInputProps,
|
||||||
borderColor: themes[theme].separatorColor,
|
borderColor: themes[theme].separatorColor,
|
||||||
color: themes[theme].titleText
|
color: themes[theme].titleText
|
||||||
},
|
},
|
||||||
error.error && {
|
error?.error && {
|
||||||
color: dangerColor,
|
color: dangerColor,
|
||||||
borderColor: dangerColor
|
borderColor: dangerColor
|
||||||
},
|
},
|
||||||
inputStyle
|
inputStyle
|
||||||
]}
|
]}
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
/* @ts-ignore*/
|
|
||||||
autoCorrect={false}
|
autoCorrect={false}
|
||||||
autoCapitalize='none'
|
autoCapitalize='none'
|
||||||
underlineColorAndroid='transparent'
|
underlineColorAndroid='transparent'
|
||||||
|
@ -183,8 +173,6 @@ export default class RCTextInput extends React.PureComponent<IRCTextInputProps,
|
||||||
testID={testID}
|
testID={testID}
|
||||||
accessibilityLabel={placeholder}
|
accessibilityLabel={placeholder}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
/* @ts-ignore*/
|
|
||||||
contentDescription={placeholder}
|
|
||||||
theme={theme}
|
theme={theme}
|
||||||
{...inputProps}
|
{...inputProps}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text } from 'react-native';
|
import { Text } from 'react-native';
|
||||||
|
|
||||||
|
import { useTheme } from '../../theme';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { events, logEvent } from '../../utils/log';
|
import { events, logEvent } from '../../utils/log';
|
||||||
|
@ -9,20 +10,20 @@ interface IAtMention {
|
||||||
mention: string;
|
mention: string;
|
||||||
username: string;
|
username: string;
|
||||||
navToRoomInfo: Function;
|
navToRoomInfo: Function;
|
||||||
style: any;
|
style?: any;
|
||||||
useRealName: boolean;
|
useRealName: boolean;
|
||||||
theme: string;
|
|
||||||
mentions: any;
|
mentions: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, style = [], useRealName, theme }: IAtMention) => {
|
const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, style = [], useRealName }: IAtMention) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
if (mention === 'all' || mention === 'here') {
|
if (mention === 'all' || mention === 'here') {
|
||||||
return (
|
return (
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.mention,
|
styles.mention,
|
||||||
{
|
{
|
||||||
color: themes[theme].mentionGroupColor
|
color: themes[theme!].mentionGroupColor
|
||||||
},
|
},
|
||||||
...style
|
...style
|
||||||
]}>
|
]}>
|
||||||
|
@ -34,11 +35,11 @@ const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, styl
|
||||||
let mentionStyle = {};
|
let mentionStyle = {};
|
||||||
if (mention === username) {
|
if (mention === username) {
|
||||||
mentionStyle = {
|
mentionStyle = {
|
||||||
color: themes[theme].mentionMeColor
|
color: themes[theme!].mentionMeColor
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
mentionStyle = {
|
mentionStyle = {
|
||||||
color: themes[theme].mentionOtherColor
|
color: themes[theme!].mentionOtherColor
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +62,7 @@ const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, styl
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Text style={[styles.text, { color: themes[theme].bodyText }, ...style]}>{`@${mention}`}</Text>;
|
return <Text style={[styles.text, { color: themes[theme!].bodyText }, ...style]}>{`@${mention}`}</Text>;
|
||||||
});
|
});
|
||||||
|
|
||||||
export default AtMention;
|
export default AtMention;
|
||||||
|
|
|
@ -1,23 +1,26 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text } from 'react-native';
|
import { Text, TextStyle } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
import { useTheme } from '../../theme';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
interface IHashtag {
|
interface IHashtag {
|
||||||
hashtag: string;
|
hashtag: string;
|
||||||
navToRoomInfo: Function;
|
navToRoomInfo: Function;
|
||||||
style: [];
|
style?: TextStyle[];
|
||||||
theme: string;
|
|
||||||
channels: {
|
channels: {
|
||||||
|
[index: number]: string | number;
|
||||||
name: string;
|
name: string;
|
||||||
_id: number;
|
_id: number;
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [], theme }: IHashtag) => {
|
const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IHashtag) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
const handlePress = () => {
|
const handlePress = () => {
|
||||||
const index = channels.findIndex(channel => channel.name === hashtag);
|
const index = channels?.findIndex(channel => channel.name === hashtag);
|
||||||
const navParam = {
|
const navParam = {
|
||||||
t: 'c',
|
t: 'c',
|
||||||
rid: channels[index]._id
|
rid: channels[index]._id
|
||||||
|
@ -31,7 +34,7 @@ const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [], them
|
||||||
style={[
|
style={[
|
||||||
styles.mention,
|
styles.mention,
|
||||||
{
|
{
|
||||||
color: themes[theme].mentionOtherColor
|
color: themes[theme!].mentionOtherColor
|
||||||
},
|
},
|
||||||
...style
|
...style
|
||||||
]}
|
]}
|
||||||
|
@ -40,7 +43,7 @@ const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [], them
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return <Text style={[styles.text, { color: themes[theme].bodyText }, ...style]}>{`#${hashtag}`}</Text>;
|
return <Text style={[styles.text, { color: themes[theme!].bodyText }, ...style]}>{`#${hashtag}`}</Text>;
|
||||||
});
|
});
|
||||||
|
|
||||||
export default Hashtag;
|
export default Hashtag;
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Image, Text } from 'react-native';
|
||||||
import { Node, Parser } from 'commonmark';
|
import { Node, Parser } from 'commonmark';
|
||||||
import Renderer from 'commonmark-react-renderer';
|
import Renderer from 'commonmark-react-renderer';
|
||||||
import removeMarkdown from 'remove-markdown';
|
import removeMarkdown from 'remove-markdown';
|
||||||
|
import { MarkdownAST } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
|
@ -20,9 +21,20 @@ import MarkdownTableCell from './TableCell';
|
||||||
import mergeTextNodes from './mergeTextNodes';
|
import mergeTextNodes from './mergeTextNodes';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { isValidURL } from '../../utils/url';
|
import { isValidURL } from '../../utils/url';
|
||||||
|
import NewMarkdown from './new';
|
||||||
|
|
||||||
|
interface IUser {
|
||||||
|
_id: string;
|
||||||
|
username: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserMention = Pick<IUser, '_id' | 'username' | 'name'>;
|
||||||
|
|
||||||
interface IMarkdownProps {
|
interface IMarkdownProps {
|
||||||
msg: string;
|
msg: string;
|
||||||
|
md: MarkdownAST;
|
||||||
|
mentions: UserMention[];
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: Function;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
username: string;
|
username: string;
|
||||||
|
@ -35,7 +47,7 @@ interface IMarkdownProps {
|
||||||
name: string;
|
name: string;
|
||||||
_id: number;
|
_id: number;
|
||||||
}[];
|
}[];
|
||||||
mentions: object[];
|
enableMessageParser: boolean;
|
||||||
navToRoomInfo: Function;
|
navToRoomInfo: Function;
|
||||||
preview: boolean;
|
preview: boolean;
|
||||||
theme: string;
|
theme: string;
|
||||||
|
@ -97,7 +109,9 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
|
|
||||||
constructor(props: IMarkdownProps) {
|
constructor(props: IMarkdownProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.renderer = this.createRenderer();
|
if (!this.isNewMarkdown) {
|
||||||
|
this.renderer = this.createRenderer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createRenderer = () =>
|
createRenderer = () =>
|
||||||
|
@ -139,6 +153,11 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
renderParagraphsInLists: true
|
renderParagraphsInLists: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
get isNewMarkdown(): boolean {
|
||||||
|
const { md, enableMessageParser } = this.props;
|
||||||
|
return enableMessageParser && !!md;
|
||||||
|
}
|
||||||
|
|
||||||
editedMessage = (ast: any) => {
|
editedMessage = (ast: any) => {
|
||||||
const { isEdited } = this.props;
|
const { isEdited } = this.props;
|
||||||
if (isEdited) {
|
if (isEdited) {
|
||||||
|
@ -227,12 +246,12 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
};
|
};
|
||||||
|
|
||||||
renderHashtag = ({ hashtag }: { hashtag: string }) => {
|
renderHashtag = ({ hashtag }: { hashtag: string }) => {
|
||||||
const { channels, navToRoomInfo, style, theme } = this.props;
|
const { channels, navToRoomInfo, style } = this.props;
|
||||||
return <MarkdownHashtag hashtag={hashtag} channels={channels} navToRoomInfo={navToRoomInfo} theme={theme} style={style} />;
|
return <MarkdownHashtag hashtag={hashtag} channels={channels} navToRoomInfo={navToRoomInfo} style={style} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
renderAtMention = ({ mentionName }: { mentionName: string }) => {
|
renderAtMention = ({ mentionName }: { mentionName: string }) => {
|
||||||
const { username, mentions, navToRoomInfo, useRealName, style, theme } = this.props;
|
const { username, mentions, navToRoomInfo, useRealName, style } = this.props;
|
||||||
return (
|
return (
|
||||||
<MarkdownAtMention
|
<MarkdownAtMention
|
||||||
mentions={mentions}
|
mentions={mentions}
|
||||||
|
@ -240,7 +259,6 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
useRealName={useRealName}
|
useRealName={useRealName}
|
||||||
username={username}
|
username={username}
|
||||||
navToRoomInfo={navToRoomInfo}
|
navToRoomInfo={navToRoomInfo}
|
||||||
theme={theme}
|
|
||||||
style={style}
|
style={style}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -329,12 +347,44 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { msg, numberOfLines, preview = false, theme, style = [], testID } = this.props;
|
const {
|
||||||
|
msg,
|
||||||
|
md,
|
||||||
|
numberOfLines,
|
||||||
|
preview = false,
|
||||||
|
theme,
|
||||||
|
style = [],
|
||||||
|
testID,
|
||||||
|
mentions,
|
||||||
|
channels,
|
||||||
|
navToRoomInfo,
|
||||||
|
useRealName,
|
||||||
|
username,
|
||||||
|
getCustomEmoji,
|
||||||
|
baseUrl,
|
||||||
|
onLinkPress
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
if (!msg) {
|
if (!msg) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isNewMarkdown) {
|
||||||
|
return (
|
||||||
|
<NewMarkdown
|
||||||
|
username={username}
|
||||||
|
baseUrl={baseUrl}
|
||||||
|
getCustomEmoji={getCustomEmoji}
|
||||||
|
useRealName={useRealName}
|
||||||
|
tokens={md}
|
||||||
|
mentions={mentions}
|
||||||
|
channels={channels}
|
||||||
|
navToRoomInfo={navToRoomInfo}
|
||||||
|
onLinkPress={onLinkPress}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let m = formatText(msg);
|
let m = formatText(msg);
|
||||||
|
|
||||||
// Ex: '[ ](https://open.rocket.chat/group/test?msg=abcdef) Test'
|
// Ex: '[ ](https://open.rocket.chat/group/test?msg=abcdef) Test'
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleSheet, View } from 'react-native';
|
||||||
|
import { BigEmoji as BigEmojiProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import Emoji from './Emoji';
|
||||||
|
|
||||||
|
interface IBigEmojiProps {
|
||||||
|
value: BigEmojiProps['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flexDirection: 'row'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const BigEmoji = ({ value }: IBigEmojiProps): JSX.Element => (
|
||||||
|
<View style={styles.container}>
|
||||||
|
{value.map(block => (
|
||||||
|
<Emoji value={block.value} isBigEmoji />
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default BigEmoji;
|
|
@ -0,0 +1,40 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleSheet, Text } from 'react-native';
|
||||||
|
import { Bold as BoldProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import sharedStyles from '../../../views/Styles';
|
||||||
|
import Strike from './Strike';
|
||||||
|
import Italic from './Italic';
|
||||||
|
import Plain from './Plain';
|
||||||
|
import Link from './Link';
|
||||||
|
|
||||||
|
interface IBoldProps {
|
||||||
|
value: BoldProps['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
text: {
|
||||||
|
...sharedStyles.textBold
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const Bold = ({ value }: IBoldProps): JSX.Element => (
|
||||||
|
<Text style={styles.text}>
|
||||||
|
{value.map(block => {
|
||||||
|
switch (block.type) {
|
||||||
|
case 'LINK':
|
||||||
|
return <Link value={block.value} />;
|
||||||
|
case 'PLAIN_TEXT':
|
||||||
|
return <Plain value={block.value} />;
|
||||||
|
case 'STRIKE':
|
||||||
|
return <Strike value={block.value} />;
|
||||||
|
case 'ITALIC':
|
||||||
|
return <Italic value={block.value} />;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Bold;
|
|
@ -0,0 +1,39 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Text } from 'react-native';
|
||||||
|
import { Code as CodeProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import styles from '../styles';
|
||||||
|
import { themes } from '../../../constants/colors';
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
|
import CodeLine from './CodeLine';
|
||||||
|
|
||||||
|
interface ICodeProps {
|
||||||
|
value: CodeProps['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Code = ({ value }: ICodeProps): JSX.Element => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.codeBlock,
|
||||||
|
{
|
||||||
|
color: themes[theme!].bodyText,
|
||||||
|
backgroundColor: themes[theme!].bannerBackground,
|
||||||
|
borderColor: themes[theme!].borderColor
|
||||||
|
}
|
||||||
|
]}>
|
||||||
|
{value.map(block => {
|
||||||
|
switch (block.type) {
|
||||||
|
case 'CODE_LINE':
|
||||||
|
return <CodeLine value={block.value} />;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Code;
|
|
@ -0,0 +1,17 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Text } from 'react-native';
|
||||||
|
import { CodeLine as CodeLineProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
interface ICodeLineProps {
|
||||||
|
value: CodeLineProps['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const CodeLine = ({ value }: ICodeLineProps): JSX.Element | null => {
|
||||||
|
if (value.type !== 'PLAIN_TEXT') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Text>{value.value}</Text>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CodeLine;
|
|
@ -0,0 +1,29 @@
|
||||||
|
import React, { useContext } from 'react';
|
||||||
|
import { Text } from 'react-native';
|
||||||
|
import { Emoji as EmojiProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import shortnameToUnicode from '../../../utils/shortnameToUnicode';
|
||||||
|
import { themes } from '../../../constants/colors';
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
|
import styles from '../styles';
|
||||||
|
import CustomEmoji from '../../EmojiPicker/CustomEmoji';
|
||||||
|
import MarkdownContext from './MarkdownContext';
|
||||||
|
|
||||||
|
interface IEmojiProps {
|
||||||
|
value: EmojiProps['value'];
|
||||||
|
isBigEmoji?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Emoji = ({ value, isBigEmoji }: IEmojiProps): JSX.Element => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
const { baseUrl, getCustomEmoji } = useContext(MarkdownContext);
|
||||||
|
const emojiUnicode = shortnameToUnicode(`:${value.value}:`);
|
||||||
|
const emoji = getCustomEmoji?.(value.value);
|
||||||
|
|
||||||
|
if (emoji) {
|
||||||
|
return <CustomEmoji baseUrl={baseUrl} style={[isBigEmoji ? styles.customEmojiBig : styles.customEmoji]} emoji={emoji} />;
|
||||||
|
}
|
||||||
|
return <Text style={[{ color: themes[theme!].bodyText }, isBigEmoji ? styles.textBig : styles.text]}>{emojiUnicode}</Text>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Emoji;
|
|
@ -0,0 +1,32 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Text } from 'react-native';
|
||||||
|
import { Heading as HeadingProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import { themes } from '../../../constants/colors';
|
||||||
|
import styles from '../styles';
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
|
|
||||||
|
interface IHeadingProps {
|
||||||
|
value: HeadingProps['value'];
|
||||||
|
level: HeadingProps['level'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Heading = ({ value, level }: IHeadingProps): JSX.Element => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
const textStyle = styles[`heading${level}`];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Text style={[textStyle, { color: themes[theme!].bodyText }]}>
|
||||||
|
{value.map(block => {
|
||||||
|
switch (block.type) {
|
||||||
|
case 'PLAIN_TEXT':
|
||||||
|
return block.value;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Heading;
|
|
@ -0,0 +1,41 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Image as ImageProps } from '@rocket.chat/message-parser';
|
||||||
|
import { createImageProgress } from 'react-native-image-progress';
|
||||||
|
import * as Progress from 'react-native-progress';
|
||||||
|
import FastImage from '@rocket.chat/react-native-fast-image';
|
||||||
|
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
|
import { themes } from '../../../constants/colors';
|
||||||
|
import styles from '../../message/styles';
|
||||||
|
|
||||||
|
interface IImageProps {
|
||||||
|
value: ImageProps['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
type TMessageImage = {
|
||||||
|
img: string;
|
||||||
|
theme: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ImageProgress = createImageProgress(FastImage);
|
||||||
|
|
||||||
|
const MessageImage = ({ img, theme }: TMessageImage) => (
|
||||||
|
<ImageProgress
|
||||||
|
style={[styles.inlineImage, { borderColor: themes[theme].borderColor }]}
|
||||||
|
source={{ uri: encodeURI(img) }}
|
||||||
|
resizeMode={FastImage.resizeMode.cover}
|
||||||
|
indicator={Progress.Pie}
|
||||||
|
indicatorProps={{
|
||||||
|
color: themes[theme].actionTintColor
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Image = ({ value }: IImageProps): JSX.Element => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
const { src } = value;
|
||||||
|
|
||||||
|
return <MessageImage img={src.value} theme={theme!} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Image;
|
|
@ -0,0 +1,62 @@
|
||||||
|
import React, { useContext } from 'react';
|
||||||
|
import { Paragraph as ParagraphProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import Hashtag from '../Hashtag';
|
||||||
|
import AtMention from '../AtMention';
|
||||||
|
import Link from './Link';
|
||||||
|
import Plain from './Plain';
|
||||||
|
import Bold from './Bold';
|
||||||
|
import Strike from './Strike';
|
||||||
|
import Italic from './Italic';
|
||||||
|
import Emoji from './Emoji';
|
||||||
|
import InlineCode from './InlineCode';
|
||||||
|
import Image from './Image';
|
||||||
|
import MarkdownContext from './MarkdownContext';
|
||||||
|
|
||||||
|
interface IParagraphProps {
|
||||||
|
value: ParagraphProps['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Inline = ({ value }: IParagraphProps): JSX.Element => {
|
||||||
|
const { useRealName, username, navToRoomInfo, mentions, channels } = useContext(MarkdownContext);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{value.map(block => {
|
||||||
|
switch (block.type) {
|
||||||
|
case 'IMAGE':
|
||||||
|
return <Image value={block.value} />;
|
||||||
|
case 'PLAIN_TEXT':
|
||||||
|
return <Plain value={block.value} />;
|
||||||
|
case 'BOLD':
|
||||||
|
return <Bold value={block.value} />;
|
||||||
|
case 'STRIKE':
|
||||||
|
return <Strike value={block.value} />;
|
||||||
|
case 'ITALIC':
|
||||||
|
return <Italic value={block.value} />;
|
||||||
|
case 'LINK':
|
||||||
|
return <Link value={block.value} />;
|
||||||
|
case 'MENTION_USER':
|
||||||
|
return (
|
||||||
|
<AtMention
|
||||||
|
mention={block.value.value}
|
||||||
|
useRealName={useRealName}
|
||||||
|
username={username}
|
||||||
|
navToRoomInfo={navToRoomInfo}
|
||||||
|
mentions={mentions}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case 'EMOJI':
|
||||||
|
return <Emoji value={block.value} />;
|
||||||
|
case 'MENTION_CHANNEL':
|
||||||
|
return <Hashtag hashtag={block.value.value} navToRoomInfo={navToRoomInfo} channels={channels} />;
|
||||||
|
case 'INLINE_CODE':
|
||||||
|
return <InlineCode value={block.value} />;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Inline;
|
|
@ -0,0 +1,38 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Text } from 'react-native';
|
||||||
|
import { InlineCode as InlineCodeProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import styles from '../styles';
|
||||||
|
import { themes } from '../../../constants/colors';
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
|
|
||||||
|
interface IInlineCodeProps {
|
||||||
|
value: InlineCodeProps['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const InlineCode = ({ value }: IInlineCodeProps): JSX.Element => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.codeInline,
|
||||||
|
{
|
||||||
|
color: themes[theme!].bodyText,
|
||||||
|
backgroundColor: themes[theme!].bannerBackground,
|
||||||
|
borderColor: themes[theme!].borderColor
|
||||||
|
}
|
||||||
|
]}>
|
||||||
|
{(block => {
|
||||||
|
switch (block.type) {
|
||||||
|
case 'PLAIN_TEXT':
|
||||||
|
return <Text>{block.value}</Text>;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})(value)}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InlineCode;
|
|
@ -0,0 +1,39 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleSheet, Text } from 'react-native';
|
||||||
|
import { Italic as ItalicProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import Strike from './Strike';
|
||||||
|
import Bold from './Bold';
|
||||||
|
import Plain from './Plain';
|
||||||
|
import Link from './Link';
|
||||||
|
|
||||||
|
interface IItalicProps {
|
||||||
|
value: ItalicProps['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
text: {
|
||||||
|
fontStyle: 'italic'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const Italic = ({ value }: IItalicProps): JSX.Element => (
|
||||||
|
<Text style={styles.text}>
|
||||||
|
{value.map(block => {
|
||||||
|
switch (block.type) {
|
||||||
|
case 'LINK':
|
||||||
|
return <Link value={block.value} />;
|
||||||
|
case 'PLAIN_TEXT':
|
||||||
|
return <Plain value={block.value} />;
|
||||||
|
case 'STRIKE':
|
||||||
|
return <Strike value={block.value} />;
|
||||||
|
case 'BOLD':
|
||||||
|
return <Bold value={block.value} />;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Italic;
|
|
@ -0,0 +1,60 @@
|
||||||
|
import React, { useContext } from 'react';
|
||||||
|
import { Text, Clipboard } from 'react-native';
|
||||||
|
import { Link as LinkProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import styles from '../styles';
|
||||||
|
import I18n from '../../../i18n';
|
||||||
|
import { LISTENER } from '../../Toast';
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
|
import openLink from '../../../utils/openLink';
|
||||||
|
import EventEmitter from '../../../utils/events';
|
||||||
|
import { themes } from '../../../constants/colors';
|
||||||
|
import Strike from './Strike';
|
||||||
|
import Italic from './Italic';
|
||||||
|
import Bold from './Bold';
|
||||||
|
import MarkdownContext from './MarkdownContext';
|
||||||
|
|
||||||
|
interface ILinkProps {
|
||||||
|
value: LinkProps['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Link = ({ value }: ILinkProps): JSX.Element => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
const { onLinkPress } = useContext(MarkdownContext);
|
||||||
|
const { src, label } = value;
|
||||||
|
const handlePress = () => {
|
||||||
|
if (!src.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (onLinkPress) {
|
||||||
|
return onLinkPress(src.value);
|
||||||
|
}
|
||||||
|
openLink(src.value, theme);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onLongPress = () => {
|
||||||
|
Clipboard.setString(src.value);
|
||||||
|
EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Text onPress={handlePress} onLongPress={onLongPress} style={[styles.link, { color: themes[theme!].actionTintColor }]}>
|
||||||
|
{(block => {
|
||||||
|
switch (block.type) {
|
||||||
|
case 'PLAIN_TEXT':
|
||||||
|
return block.value;
|
||||||
|
case 'STRIKE':
|
||||||
|
return <Strike value={block.value} />;
|
||||||
|
case 'ITALIC':
|
||||||
|
return <Italic value={block.value} />;
|
||||||
|
case 'BOLD':
|
||||||
|
return <Bold value={block.value} />;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})(label)}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Link;
|
|
@ -0,0 +1,29 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { UserMention } from '../../message/interfaces';
|
||||||
|
|
||||||
|
interface IMarkdownContext {
|
||||||
|
mentions: UserMention[];
|
||||||
|
channels: {
|
||||||
|
name: string;
|
||||||
|
_id: number;
|
||||||
|
}[];
|
||||||
|
useRealName: boolean;
|
||||||
|
username: string;
|
||||||
|
baseUrl: string;
|
||||||
|
navToRoomInfo: Function;
|
||||||
|
getCustomEmoji?: Function;
|
||||||
|
onLinkPress?: Function;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultState = {
|
||||||
|
mentions: [],
|
||||||
|
channels: [],
|
||||||
|
useRealName: false,
|
||||||
|
username: '',
|
||||||
|
baseUrl: '',
|
||||||
|
navToRoomInfo: () => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const MarkdownContext = React.createContext<IMarkdownContext>(defaultState);
|
||||||
|
export default MarkdownContext;
|
|
@ -0,0 +1,28 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { View, Text } from 'react-native';
|
||||||
|
import { OrderedList as OrderedListProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import Inline from './Inline';
|
||||||
|
import styles from '../styles';
|
||||||
|
import { themes } from '../../../constants/colors';
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
|
|
||||||
|
interface IOrderedListProps {
|
||||||
|
value: OrderedListProps['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const OrderedList = ({ value }: IOrderedListProps): JSX.Element => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
{value.map((item, index) => (
|
||||||
|
<View style={styles.row}>
|
||||||
|
<Text style={[styles.text, { color: themes[theme!].bodyText }]}>{index + 1}. </Text>
|
||||||
|
<Inline value={item.value} />
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OrderedList;
|
|
@ -0,0 +1,23 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Text } from 'react-native';
|
||||||
|
import { Paragraph as ParagraphProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import Inline from './Inline';
|
||||||
|
import styles from '../styles';
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
|
import { themes } from '../../../constants/colors';
|
||||||
|
|
||||||
|
interface IParagraphProps {
|
||||||
|
value: ParagraphProps['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Paragraph = ({ value }: IParagraphProps): JSX.Element => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
return (
|
||||||
|
<Text style={[styles.text, { color: themes[theme!].bodyText }]}>
|
||||||
|
<Inline value={value} />
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Paragraph;
|
|
@ -0,0 +1,22 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Text } from 'react-native';
|
||||||
|
import { Plain as PlainProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import styles from '../styles';
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
|
import { themes } from '../../../constants/colors';
|
||||||
|
|
||||||
|
interface IPlainProps {
|
||||||
|
value: PlainProps['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Plain = ({ value }: IPlainProps): JSX.Element => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
return (
|
||||||
|
<Text accessibilityLabel={value} style={[styles.plainText, { color: themes[theme!].bodyText }]}>
|
||||||
|
{value}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Plain;
|
|
@ -0,0 +1,28 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { View } from 'react-native';
|
||||||
|
import { Quote as QuoteProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import { themes } from '../../../constants/colors';
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
|
import styles from '../styles';
|
||||||
|
import Paragraph from './Paragraph';
|
||||||
|
|
||||||
|
interface IQuoteProps {
|
||||||
|
value: QuoteProps['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Quote = ({ value }: IQuoteProps): JSX.Element => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<View style={[styles.quote, { backgroundColor: themes[theme!].borderColor }]} />
|
||||||
|
<View style={styles.childContainer}>
|
||||||
|
{value.map(item => (
|
||||||
|
<Paragraph value={item.value} />
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Quote;
|
|
@ -0,0 +1,39 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleSheet, Text } from 'react-native';
|
||||||
|
import { Strike as StrikeProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import Bold from './Bold';
|
||||||
|
import Italic from './Italic';
|
||||||
|
import Plain from './Plain';
|
||||||
|
import Link from './Link';
|
||||||
|
|
||||||
|
interface IStrikeProps {
|
||||||
|
value: StrikeProps['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
text: {
|
||||||
|
textDecorationLine: 'line-through'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const Strike = ({ value }: IStrikeProps): JSX.Element => (
|
||||||
|
<Text style={styles.text}>
|
||||||
|
{value.map(block => {
|
||||||
|
switch (block.type) {
|
||||||
|
case 'LINK':
|
||||||
|
return <Link value={block.value} />;
|
||||||
|
case 'PLAIN_TEXT':
|
||||||
|
return <Plain value={block.value} />;
|
||||||
|
case 'BOLD':
|
||||||
|
return <Bold value={block.value} />;
|
||||||
|
case 'ITALIC':
|
||||||
|
return <Italic value={block.value} />;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Strike;
|
|
@ -0,0 +1,28 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Text, View } from 'react-native';
|
||||||
|
import { Tasks as TasksProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import Inline from './Inline';
|
||||||
|
import styles from '../styles';
|
||||||
|
import { themes } from '../../../constants/colors';
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
|
|
||||||
|
interface ITasksProps {
|
||||||
|
value: TasksProps['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const TaskList = ({ value = [] }: ITasksProps): JSX.Element => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
{value.map(item => (
|
||||||
|
<View style={styles.row}>
|
||||||
|
<Text style={[styles.text, { color: themes[theme!].bodyText }]}>{item.status ? '- [x] ' : '- [ ] '}</Text>
|
||||||
|
<Inline value={item.value} />
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TaskList;
|
|
@ -0,0 +1,28 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { UnorderedList as UnorderedListProps } from '@rocket.chat/message-parser';
|
||||||
|
import { View, Text } from 'react-native';
|
||||||
|
|
||||||
|
import Inline from './Inline';
|
||||||
|
import styles from '../styles';
|
||||||
|
import { themes } from '../../../constants/colors';
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
|
|
||||||
|
interface IUnorderedListProps {
|
||||||
|
value: UnorderedListProps['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const UnorderedList = ({ value }: IUnorderedListProps): JSX.Element => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
{value.map(item => (
|
||||||
|
<View style={styles.row}>
|
||||||
|
<Text style={[styles.text, { color: themes[theme!].bodyText }]}>- </Text>
|
||||||
|
<Inline value={item.value} />
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UnorderedList;
|
|
@ -0,0 +1,77 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { MarkdownAST } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import Quote from './Quote';
|
||||||
|
import Paragraph from './Paragraph';
|
||||||
|
import Heading from './Heading';
|
||||||
|
import Code from './Code';
|
||||||
|
import BigEmoji from './BigEmoji';
|
||||||
|
import OrderedList from './OrderedList';
|
||||||
|
import UnorderedList from './UnorderedList';
|
||||||
|
import { UserMention } from '../../message/interfaces';
|
||||||
|
import TaskList from './TaskList';
|
||||||
|
import MarkdownContext from './MarkdownContext';
|
||||||
|
|
||||||
|
interface IBodyProps {
|
||||||
|
tokens: MarkdownAST;
|
||||||
|
mentions: UserMention[];
|
||||||
|
channels: {
|
||||||
|
name: string;
|
||||||
|
_id: number;
|
||||||
|
}[];
|
||||||
|
getCustomEmoji?: Function;
|
||||||
|
onLinkPress?: Function;
|
||||||
|
navToRoomInfo: Function;
|
||||||
|
useRealName: boolean;
|
||||||
|
username: string;
|
||||||
|
baseUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Body = ({
|
||||||
|
tokens,
|
||||||
|
mentions,
|
||||||
|
channels,
|
||||||
|
useRealName,
|
||||||
|
username,
|
||||||
|
navToRoomInfo,
|
||||||
|
getCustomEmoji,
|
||||||
|
baseUrl,
|
||||||
|
onLinkPress
|
||||||
|
}: IBodyProps): JSX.Element => (
|
||||||
|
<MarkdownContext.Provider
|
||||||
|
value={{
|
||||||
|
mentions,
|
||||||
|
channels,
|
||||||
|
useRealName,
|
||||||
|
username,
|
||||||
|
navToRoomInfo,
|
||||||
|
getCustomEmoji,
|
||||||
|
baseUrl,
|
||||||
|
onLinkPress
|
||||||
|
}}>
|
||||||
|
{tokens.map(block => {
|
||||||
|
switch (block.type) {
|
||||||
|
case 'BIG_EMOJI':
|
||||||
|
return <BigEmoji value={block.value} />;
|
||||||
|
case 'UNORDERED_LIST':
|
||||||
|
return <UnorderedList value={block.value} />;
|
||||||
|
case 'ORDERED_LIST':
|
||||||
|
return <OrderedList value={block.value} />;
|
||||||
|
case 'TASKS':
|
||||||
|
return <TaskList value={block.value} />;
|
||||||
|
case 'QUOTE':
|
||||||
|
return <Quote value={block.value} />;
|
||||||
|
case 'PARAGRAPH':
|
||||||
|
return <Paragraph value={block.value} />;
|
||||||
|
case 'CODE':
|
||||||
|
return <Code value={block.value} />;
|
||||||
|
case 'HEADING':
|
||||||
|
return <Heading value={block.value} level={block.level} />;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</MarkdownContext.Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Body;
|
|
@ -30,6 +30,10 @@ export default StyleSheet.create<any>({
|
||||||
del: {
|
del: {
|
||||||
textDecorationLine: 'line-through'
|
textDecorationLine: 'line-through'
|
||||||
},
|
},
|
||||||
|
plainText: {
|
||||||
|
fontSize: 16,
|
||||||
|
flexShrink: 1
|
||||||
|
},
|
||||||
text: {
|
text: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular
|
||||||
|
@ -70,12 +74,16 @@ export default StyleSheet.create<any>({
|
||||||
resizeMode: 'contain'
|
resizeMode: 'contain'
|
||||||
},
|
},
|
||||||
codeInline: {
|
codeInline: {
|
||||||
|
fontSize: 16,
|
||||||
...sharedStyles.textRegular,
|
...sharedStyles.textRegular,
|
||||||
...codeFontFamily,
|
...codeFontFamily,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderRadius: 4
|
borderRadius: 4,
|
||||||
|
paddingLeft: 2,
|
||||||
|
paddingTop: 2
|
||||||
},
|
},
|
||||||
codeBlock: {
|
codeBlock: {
|
||||||
|
fontSize: 16,
|
||||||
...sharedStyles.textRegular,
|
...sharedStyles.textRegular,
|
||||||
...codeFontFamily,
|
...codeFontFamily,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
|
|
|
@ -51,8 +51,10 @@ const Content = React.memo(
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
<Markdown
|
<Markdown
|
||||||
msg={props.msg}
|
msg={props.msg}
|
||||||
|
md={props.md}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
getCustomEmoji={props.getCustomEmoji}
|
getCustomEmoji={props.getCustomEmoji}
|
||||||
|
enableMessageParser={user.enableMessageParserEarlyAdoption}
|
||||||
username={user.username}
|
username={user.username}
|
||||||
isEdited={props.isEdited}
|
isEdited={props.isEdited}
|
||||||
numberOfLines={isPreview ? 1 : 0}
|
numberOfLines={isPreview ? 1 : 0}
|
||||||
|
@ -103,6 +105,9 @@ const Content = React.memo(
|
||||||
if (prevProps.isIgnored !== nextProps.isIgnored) {
|
if (prevProps.isIgnored !== nextProps.isIgnored) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!dequal(prevProps.md, nextProps.md)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!dequal(prevProps.mentions, nextProps.mentions)) {
|
if (!dequal(prevProps.mentions, nextProps.mentions)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -357,7 +357,8 @@ class MessageContainer extends React.Component<IMessageContainerProps, any> {
|
||||||
unread,
|
unread,
|
||||||
blocks,
|
blocks,
|
||||||
autoTranslate: autoTranslateMessage,
|
autoTranslate: autoTranslateMessage,
|
||||||
replies
|
replies,
|
||||||
|
md
|
||||||
} = item;
|
} = item;
|
||||||
|
|
||||||
let message = msg;
|
let message = msg;
|
||||||
|
@ -391,6 +392,7 @@ class MessageContainer extends React.Component<IMessageContainerProps, any> {
|
||||||
<Message
|
<Message
|
||||||
id={id}
|
id={id}
|
||||||
msg={message}
|
msg={message}
|
||||||
|
md={md}
|
||||||
rid={rid}
|
rid={rid}
|
||||||
author={u}
|
author={u}
|
||||||
ts={ts}
|
ts={ts}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { MarkdownAST } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
export interface IMessageAttachments {
|
export interface IMessageAttachments {
|
||||||
attachments: any;
|
attachments: any;
|
||||||
timeFormat: string;
|
timeFormat: string;
|
||||||
|
@ -48,12 +50,21 @@ export interface IMessageCallButton {
|
||||||
callJitsi: Function;
|
callJitsi: Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IUser {
|
||||||
|
_id: string;
|
||||||
|
username: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserMention = Pick<IUser, '_id' | 'username' | 'name'>;
|
||||||
|
|
||||||
export interface IMessageContent {
|
export interface IMessageContent {
|
||||||
isTemp: boolean;
|
isTemp: boolean;
|
||||||
isInfo: boolean;
|
isInfo: boolean;
|
||||||
tmid: string;
|
tmid: string;
|
||||||
isThreadRoom: boolean;
|
isThreadRoom: boolean;
|
||||||
msg: string;
|
msg: string;
|
||||||
|
md: MarkdownAST;
|
||||||
theme: string;
|
theme: string;
|
||||||
isEdited: boolean;
|
isEdited: boolean;
|
||||||
isEncrypted: boolean;
|
isEncrypted: boolean;
|
||||||
|
@ -62,7 +73,7 @@ export interface IMessageContent {
|
||||||
name: string;
|
name: string;
|
||||||
_id: number;
|
_id: number;
|
||||||
}[];
|
}[];
|
||||||
mentions: object[];
|
mentions: UserMention[];
|
||||||
navToRoomInfo: Function;
|
navToRoomInfo: Function;
|
||||||
useRealName: boolean;
|
useRealName: boolean;
|
||||||
isIgnored: boolean;
|
isIgnored: boolean;
|
||||||
|
|
|
@ -106,7 +106,6 @@ export default StyleSheet.create<any>({
|
||||||
},
|
},
|
||||||
image: {
|
image: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
// maxWidth: 400,
|
|
||||||
minHeight: isTablet ? 300 : 200,
|
minHeight: isTablet ? 300 : 200,
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -679,5 +679,7 @@
|
||||||
"Use": "Use",
|
"Use": "Use",
|
||||||
"Shortcut": "Atalho",
|
"Shortcut": "Atalho",
|
||||||
"Content": "Conteúdo",
|
"Content": "Conteúdo",
|
||||||
"No_canned_responses": "Não há respostas predefinidas"
|
"No_canned_responses": "Não há respostas predefinidas",
|
||||||
|
"Send_email_confirmation": "Enviar email de confirmação",
|
||||||
|
"sending_email_confirmation": "enviando email de confirmação"
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,4 +81,6 @@ export default class Message extends Model {
|
||||||
@field('e2e') e2e;
|
@field('e2e') e2e;
|
||||||
|
|
||||||
@field('tshow') tshow;
|
@field('tshow') tshow;
|
||||||
|
|
||||||
|
@json('md', sanitizer) md;
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,6 +190,15 @@ export default schemaMigrations({
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
toVersion: 14,
|
||||||
|
steps: [
|
||||||
|
addColumns({
|
||||||
|
table: 'messages',
|
||||||
|
columns: [{ name: 'md', type: 'string', isOptional: true }]
|
||||||
|
})
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,4 +25,6 @@ export default class User extends Model {
|
||||||
@field('show_message_in_main_thread') showMessageInMainThread;
|
@field('show_message_in_main_thread') showMessageInMainThread;
|
||||||
|
|
||||||
@field('is_from_webview') isFromWebView;
|
@field('is_from_webview') isFromWebView;
|
||||||
|
|
||||||
|
@field('enable_message_parser_early_adoption') enableMessageParserEarlyAdoption;
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,6 +94,15 @@ export default schemaMigrations({
|
||||||
columns: [{ name: 'is_from_webview', type: 'boolean', isOptional: true }]
|
columns: [{ name: 'is_from_webview', type: 'boolean', isOptional: true }]
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
toVersion: 12,
|
||||||
|
steps: [
|
||||||
|
addColumns({
|
||||||
|
table: 'users',
|
||||||
|
columns: [{ name: 'enable_message_parser_early_adoption', type: 'boolean', isOptional: true }]
|
||||||
|
})
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { appSchema, tableSchema } from '@nozbe/watermelondb';
|
import { appSchema, tableSchema } from '@nozbe/watermelondb';
|
||||||
|
|
||||||
export default appSchema({
|
export default appSchema({
|
||||||
version: 13,
|
version: 14,
|
||||||
tables: [
|
tables: [
|
||||||
tableSchema({
|
tableSchema({
|
||||||
name: 'subscriptions',
|
name: 'subscriptions',
|
||||||
|
@ -115,7 +115,8 @@ export default appSchema({
|
||||||
{ name: 'tmsg', type: 'string', isOptional: true },
|
{ name: 'tmsg', type: 'string', isOptional: true },
|
||||||
{ name: 'blocks', type: 'string', isOptional: true },
|
{ name: 'blocks', type: 'string', isOptional: true },
|
||||||
{ name: 'e2e', type: 'string', isOptional: true },
|
{ name: 'e2e', type: 'string', isOptional: true },
|
||||||
{ name: 'tshow', type: 'boolean', isOptional: true }
|
{ name: 'tshow', type: 'boolean', isOptional: true },
|
||||||
|
{ name: 'md', type: 'string', isOptional: true }
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
tableSchema({
|
tableSchema({
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { appSchema, tableSchema } from '@nozbe/watermelondb';
|
import { appSchema, tableSchema } from '@nozbe/watermelondb';
|
||||||
|
|
||||||
export default appSchema({
|
export default appSchema({
|
||||||
version: 11,
|
version: 12,
|
||||||
tables: [
|
tables: [
|
||||||
tableSchema({
|
tableSchema({
|
||||||
name: 'users',
|
name: 'users',
|
||||||
|
@ -16,7 +16,8 @@ export default appSchema({
|
||||||
{ name: 'login_email_password', type: 'boolean', isOptional: true },
|
{ name: 'login_email_password', type: 'boolean', isOptional: true },
|
||||||
{ name: 'show_message_in_main_thread', type: 'boolean', isOptional: true },
|
{ name: 'show_message_in_main_thread', type: 'boolean', isOptional: true },
|
||||||
{ name: 'avatar_etag', type: 'string', isOptional: true },
|
{ name: 'avatar_etag', type: 'string', isOptional: true },
|
||||||
{ name: 'is_from_webview', type: 'boolean', isOptional: true }
|
{ name: 'is_from_webview', type: 'boolean', isOptional: true },
|
||||||
|
{ name: 'enable_message_parser_early_adoption', type: 'boolean', isOptional: true }
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
tableSchema({
|
tableSchema({
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { Q } from '@nozbe/watermelondb';
|
||||||
import AsyncStorage from '@react-native-community/async-storage';
|
import AsyncStorage from '@react-native-community/async-storage';
|
||||||
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
|
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
|
||||||
import RNFetchBlob from 'rn-fetch-blob';
|
import RNFetchBlob from 'rn-fetch-blob';
|
||||||
|
import isEmpty from 'lodash/isEmpty';
|
||||||
|
|
||||||
import defaultSettings from '../constants/settings';
|
import defaultSettings from '../constants/settings';
|
||||||
import log from '../utils/log';
|
import log from '../utils/log';
|
||||||
|
@ -530,6 +531,10 @@ const RocketChat = {
|
||||||
return this.post('users.forgotPassword', { email }, false);
|
return this.post('users.forgotPassword', { email }, false);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
sendConfirmationEmail(email) {
|
||||||
|
return this.methodCallWrapper('sendConfirmationEmail', email);
|
||||||
|
},
|
||||||
|
|
||||||
loginTOTP(params, loginEmailPassword, isFromWebView = false) {
|
loginTOTP(params, loginEmailPassword, isFromWebView = false) {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
|
@ -622,7 +627,8 @@ const RocketChat = {
|
||||||
roles: result.me.roles,
|
roles: result.me.roles,
|
||||||
avatarETag: result.me.avatarETag,
|
avatarETag: result.me.avatarETag,
|
||||||
isFromWebView,
|
isFromWebView,
|
||||||
showMessageInMainThread: result.me.settings?.preferences?.showMessageInMainThread ?? true
|
showMessageInMainThread: result.me.settings?.preferences?.showMessageInMainThread ?? true,
|
||||||
|
enableMessageParserEarlyAdoption: result.me.settings?.preferences?.enableMessageParserEarlyAdoption ?? true
|
||||||
};
|
};
|
||||||
return user;
|
return user;
|
||||||
},
|
},
|
||||||
|
@ -1069,8 +1075,12 @@ const RocketChat = {
|
||||||
},
|
},
|
||||||
methodCallWrapper(method, ...params) {
|
methodCallWrapper(method, ...params) {
|
||||||
const { API_Use_REST_For_DDP_Calls } = reduxStore.getState().settings;
|
const { API_Use_REST_For_DDP_Calls } = reduxStore.getState().settings;
|
||||||
|
const { user } = reduxStore.getState().login;
|
||||||
if (API_Use_REST_For_DDP_Calls) {
|
if (API_Use_REST_For_DDP_Calls) {
|
||||||
return this.post(`method.call/${method}`, { message: EJSON.stringify({ method, params }) });
|
const url = isEmpty(user) ? 'method.callAnon' : 'method.call';
|
||||||
|
return this.post(`${url}/${method}`, {
|
||||||
|
message: EJSON.stringify({ method, params })
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const parsedParams = params.map(param => {
|
const parsedParams = params.map(param => {
|
||||||
if (param instanceof Date) {
|
if (param instanceof Date) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { I18nManager, StyleSheet, TextInput, TextInputProps } from 'react-native';
|
import { I18nManager, StyleProp, StyleSheet, TextInput, TextInputProps, TextStyle } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IThemedTextInput extends TextInputProps {
|
interface IThemedTextInput extends TextInputProps {
|
||||||
style: object;
|
style: StyleProp<TextStyle>;
|
||||||
theme: string;
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ import { encryptionInit, encryptionStop } from '../actions/encryption';
|
||||||
import UserPreferences from '../lib/userPreferences';
|
import UserPreferences from '../lib/userPreferences';
|
||||||
import { inquiryRequest, inquiryReset } from '../ee/omnichannel/actions/inquiry';
|
import { inquiryRequest, inquiryReset } from '../ee/omnichannel/actions/inquiry';
|
||||||
import { isOmnichannelStatusAvailable } from '../ee/omnichannel/lib';
|
import { isOmnichannelStatusAvailable } from '../ee/omnichannel/lib';
|
||||||
import Navigation from '../lib/Navigation';
|
|
||||||
|
|
||||||
const getServer = state => state.server.server;
|
const getServer = state => state.server.server;
|
||||||
const loginWithPasswordCall = args => RocketChat.loginWithPassword(args);
|
const loginWithPasswordCall = args => RocketChat.loginWithPassword(args);
|
||||||
|
@ -191,8 +190,6 @@ const handleLogout = function* handleLogout({ forcedByServer }) {
|
||||||
yield put(appStart({ root: ROOT_OUTSIDE }));
|
yield put(appStart({ root: ROOT_OUTSIDE }));
|
||||||
showErrorAlert(I18n.t('Logged_out_by_server'), I18n.t('Oops'));
|
showErrorAlert(I18n.t('Logged_out_by_server'), I18n.t('Oops'));
|
||||||
yield delay(300);
|
yield delay(300);
|
||||||
Navigation.navigate('NewServerView');
|
|
||||||
yield delay(300);
|
|
||||||
EventEmitter.emit('NewServer', { server });
|
EventEmitter.emit('NewServer', { server });
|
||||||
} else {
|
} else {
|
||||||
const serversDB = database.servers;
|
const serversDB = database.servers;
|
||||||
|
|
|
@ -10,6 +10,7 @@ import NewServerView from '../views/NewServerView';
|
||||||
import WorkspaceView from '../views/WorkspaceView';
|
import WorkspaceView from '../views/WorkspaceView';
|
||||||
import LoginView from '../views/LoginView';
|
import LoginView from '../views/LoginView';
|
||||||
import ForgotPasswordView from '../views/ForgotPasswordView';
|
import ForgotPasswordView from '../views/ForgotPasswordView';
|
||||||
|
import SendEmailConfirmationView from '../views/SendEmailConfirmationView';
|
||||||
import RegisterView from '../views/RegisterView';
|
import RegisterView from '../views/RegisterView';
|
||||||
import LegalView from '../views/LegalView';
|
import LegalView from '../views/LegalView';
|
||||||
import AuthenticationWebView from '../views/AuthenticationWebView';
|
import AuthenticationWebView from '../views/AuthenticationWebView';
|
||||||
|
@ -25,6 +26,11 @@ const _OutsideStack = () => {
|
||||||
<Outside.Screen name='WorkspaceView' component={WorkspaceView} options={WorkspaceView.navigationOptions} />
|
<Outside.Screen name='WorkspaceView' component={WorkspaceView} options={WorkspaceView.navigationOptions} />
|
||||||
<Outside.Screen name='LoginView' component={LoginView} options={LoginView.navigationOptions} />
|
<Outside.Screen name='LoginView' component={LoginView} options={LoginView.navigationOptions} />
|
||||||
<Outside.Screen name='ForgotPasswordView' component={ForgotPasswordView} options={ForgotPasswordView.navigationOptions} />
|
<Outside.Screen name='ForgotPasswordView' component={ForgotPasswordView} options={ForgotPasswordView.navigationOptions} />
|
||||||
|
<Outside.Screen
|
||||||
|
name='SendEmailConfirmationView'
|
||||||
|
component={SendEmailConfirmationView}
|
||||||
|
options={SendEmailConfirmationView.navigationOptions}
|
||||||
|
/>
|
||||||
<Outside.Screen name='RegisterView' component={RegisterView} options={RegisterView.navigationOptions} />
|
<Outside.Screen name='RegisterView' component={RegisterView} options={RegisterView.navigationOptions} />
|
||||||
<Outside.Screen name='LegalView' component={LegalView} options={LegalView.navigationOptions} />
|
<Outside.Screen name='LegalView' component={LegalView} options={LegalView.navigationOptions} />
|
||||||
</Outside.Navigator>
|
</Outside.Navigator>
|
||||||
|
|
|
@ -3,14 +3,14 @@ import hoistNonReactStatics from 'hoist-non-react-statics';
|
||||||
|
|
||||||
interface IThemeContextProps {
|
interface IThemeContextProps {
|
||||||
theme: string;
|
theme: string;
|
||||||
themePreferences: {
|
themePreferences?: {
|
||||||
currentTheme: 'automatic' | 'light';
|
currentTheme: 'automatic' | 'light';
|
||||||
darkLevel: string;
|
darkLevel: string;
|
||||||
};
|
};
|
||||||
setTheme: (newTheme?: {}) => void;
|
setTheme?: (newTheme?: {}) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ThemeContext = React.createContext<Partial<IThemeContextProps>>({ theme: 'light' });
|
export const ThemeContext = React.createContext<IThemeContextProps>({ theme: 'light' });
|
||||||
|
|
||||||
export function withTheme(Component: React.ComponentType<any>): (props: any) => JSX.Element {
|
export function withTheme(Component: React.ComponentType<any>): (props: any) => JSX.Element {
|
||||||
const ThemedComponent = (props: any) => (
|
const ThemedComponent = (props: any) => (
|
||||||
|
|
|
@ -11,6 +11,9 @@ export default {
|
||||||
FP_FORGOT_PASSWORD: 'fp_forgot_password',
|
FP_FORGOT_PASSWORD: 'fp_forgot_password',
|
||||||
FP_FORGOT_PASSWORD_F: 'fp_forgot_password_f',
|
FP_FORGOT_PASSWORD_F: 'fp_forgot_password_f',
|
||||||
|
|
||||||
|
// SEND EMAIL CONFIRMATION VIEW
|
||||||
|
SEC_SEND_EMAIL_CONFIRMATION: 'sec_send_email_confirmation',
|
||||||
|
|
||||||
// REGISTER VIEW
|
// REGISTER VIEW
|
||||||
REGISTER_DEFAULT_SIGN_UP: 'register_default_sign_up',
|
REGISTER_DEFAULT_SIGN_UP: 'register_default_sign_up',
|
||||||
REGISTER_DEFAULT_SIGN_UP_F: 'register_default_sign_up_f',
|
REGISTER_DEFAULT_SIGN_UP_F: 'register_default_sign_up_f',
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
import { isTablet } from './deviceInfo';
|
|
||||||
|
|
||||||
const guidelineBaseWidth = isTablet ? 600 : 375;
|
|
||||||
const guidelineBaseHeight = isTablet ? 800 : 667;
|
|
||||||
|
|
||||||
// TODO: we need to refactor this
|
|
||||||
const scale = (size, width) => (width / guidelineBaseWidth) * size;
|
|
||||||
const verticalScale = (size, height) => (height / guidelineBaseHeight) * size;
|
|
||||||
const moderateScale = (size, factor = 0.5, width) => size + (scale(size, width) - size) * factor;
|
|
||||||
|
|
||||||
export { scale, verticalScale, moderateScale };
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { isTablet } from './deviceInfo';
|
||||||
|
|
||||||
|
const guidelineBaseWidth = isTablet ? 600 : 375;
|
||||||
|
const guidelineBaseHeight = isTablet ? 800 : 667;
|
||||||
|
|
||||||
|
function scale({ size, width }: { size: number; width: number }): number {
|
||||||
|
return (width / guidelineBaseWidth) * size;
|
||||||
|
}
|
||||||
|
function verticalScale({ size, height }: { size: number; height: number }): number {
|
||||||
|
return (height / guidelineBaseHeight) * size;
|
||||||
|
}
|
||||||
|
function moderateScale({ size, factor = 0.5, width }: { size: number; factor?: number; width: number }): number {
|
||||||
|
return size + (scale({ size, width }) - size) * factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { scale, verticalScale, moderateScale };
|
|
@ -1,8 +1,9 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import { WebView, WebViewNavigation } from 'react-native-webview';
|
||||||
import { WebView } from 'react-native-webview';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import parse from 'url-parse';
|
import parse from 'url-parse';
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
import { WebViewMessage } from 'react-native-webview/lib/WebViewTypes';
|
||||||
|
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import { isIOS } from '../utils/deviceInfo';
|
import { isIOS } from '../utils/deviceInfo';
|
||||||
|
@ -40,17 +41,44 @@ window.addEventListener('popstate', function() {
|
||||||
});
|
});
|
||||||
`;
|
`;
|
||||||
|
|
||||||
class AuthenticationWebView extends React.PureComponent {
|
interface IRoute {
|
||||||
static propTypes = {
|
params: {
|
||||||
navigation: PropTypes.object,
|
authType: string;
|
||||||
route: PropTypes.object,
|
url: string;
|
||||||
server: PropTypes.string,
|
ssoToken?: string;
|
||||||
Accounts_Iframe_api_url: PropTypes.bool,
|
};
|
||||||
Accounts_Iframe_api_method: PropTypes.bool,
|
}
|
||||||
theme: PropTypes.string
|
|
||||||
|
interface INavigationOption {
|
||||||
|
navigation: StackNavigationProp<any, 'AuthenticationWebView'>;
|
||||||
|
route: IRoute;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IAuthenticationWebView extends INavigationOption {
|
||||||
|
server: string;
|
||||||
|
Accounts_Iframe_api_url: string;
|
||||||
|
Accounts_Iframe_api_method: string;
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
logging: boolean;
|
||||||
|
loading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class AuthenticationWebView extends React.PureComponent<IAuthenticationWebView, IState> {
|
||||||
|
private oauthRedirectRegex: RegExp;
|
||||||
|
private iframeRedirectRegex: RegExp;
|
||||||
|
|
||||||
|
static navigationOptions = ({ route, navigation }: INavigationOption) => {
|
||||||
|
const { authType } = route.params;
|
||||||
|
return {
|
||||||
|
headerLeft: () => <HeaderButton.CloseModal navigation={navigation} />,
|
||||||
|
title: ['saml', 'cas', 'iframe'].includes(authType) ? 'SSO' : 'OAuth'
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props: IAuthenticationWebView) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
logging: false,
|
logging: false,
|
||||||
|
@ -71,7 +99,7 @@ class AuthenticationWebView extends React.PureComponent {
|
||||||
navigation.pop();
|
navigation.pop();
|
||||||
};
|
};
|
||||||
|
|
||||||
login = params => {
|
login = (params: any) => {
|
||||||
const { logging } = this.state;
|
const { logging } = this.state;
|
||||||
if (logging) {
|
if (logging) {
|
||||||
return;
|
return;
|
||||||
|
@ -89,7 +117,7 @@ class AuthenticationWebView extends React.PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Force 3s delay so the server has time to evaluate the token
|
// Force 3s delay so the server has time to evaluate the token
|
||||||
debouncedLogin = debounce(params => this.login(params), 3000);
|
debouncedLogin = debounce((params: any) => this.login(params), 3000);
|
||||||
|
|
||||||
tryLogin = debounce(
|
tryLogin = debounce(
|
||||||
async () => {
|
async () => {
|
||||||
|
@ -104,7 +132,7 @@ class AuthenticationWebView extends React.PureComponent {
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
onNavigationStateChange = webViewState => {
|
onNavigationStateChange = (webViewState: WebViewNavigation | WebViewMessage) => {
|
||||||
const url = decodeURIComponent(webViewState.url);
|
const url = decodeURIComponent(webViewState.url);
|
||||||
const { route } = this.props;
|
const { route } = this.props;
|
||||||
const { authType } = route.params;
|
const { authType } = route.params;
|
||||||
|
@ -180,18 +208,10 @@ class AuthenticationWebView extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: any) => ({
|
||||||
server: state.server.server,
|
server: state.server.server,
|
||||||
Accounts_Iframe_api_url: state.settings.Accounts_Iframe_api_url,
|
Accounts_Iframe_api_url: state.settings.Accounts_Iframe_api_url,
|
||||||
Accounts_Iframe_api_method: state.settings.Accounts_Iframe_api_method
|
Accounts_Iframe_api_method: state.settings.Accounts_Iframe_api_method
|
||||||
});
|
});
|
||||||
|
|
||||||
AuthenticationWebView.navigationOptions = ({ route, navigation }) => {
|
|
||||||
const { authType } = route.params;
|
|
||||||
return {
|
|
||||||
headerLeft: () => <HeaderButton.CloseModal navigation={navigation} />,
|
|
||||||
title: ['saml', 'cas', 'iframe'].includes(authType) ? 'SSO' : 'OAuth'
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withTheme(AuthenticationWebView));
|
export default connect(mapStateToProps)(withTheme(AuthenticationWebView));
|
|
@ -1,8 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { ScrollView, Share, View } from 'react-native';
|
import { ScrollView, Share, View } from 'react-native';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { StackNavigationProp, StackNavigationOptions } from '@react-navigation/stack';
|
||||||
|
import { RouteProp } from '@react-navigation/core';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
inviteLinksClear as inviteLinksClearAction,
|
inviteLinksClear as inviteLinksClearAction,
|
||||||
|
@ -20,22 +22,28 @@ import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
import { events, logEvent } from '../../utils/log';
|
import { events, logEvent } from '../../utils/log';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
class InviteUsersView extends React.Component {
|
interface IInviteUsersView {
|
||||||
static navigationOptions = () => ({
|
navigation: StackNavigationProp<any, 'InviteUsersView'>;
|
||||||
title: I18n.t('Invite_users')
|
route: RouteProp<any, 'InviteUsersView'>;
|
||||||
});
|
theme: string;
|
||||||
|
timeDateFormat: string;
|
||||||
|
invite: {
|
||||||
|
url: string;
|
||||||
|
expires: number;
|
||||||
|
maxUses: number;
|
||||||
|
uses: number;
|
||||||
|
};
|
||||||
|
createInviteLink(rid: string): void;
|
||||||
|
clearInviteLink(): void;
|
||||||
|
}
|
||||||
|
class InviteUsersView extends React.Component<IInviteUsersView, any> {
|
||||||
|
private rid: string;
|
||||||
|
|
||||||
static propTypes = {
|
static navigationOptions: StackNavigationOptions = {
|
||||||
navigation: PropTypes.object,
|
title: I18n.t('Invite_users')
|
||||||
route: PropTypes.object,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
timeDateFormat: PropTypes.string,
|
|
||||||
invite: PropTypes.object,
|
|
||||||
createInviteLink: PropTypes.func,
|
|
||||||
clearInviteLink: PropTypes.func
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props: IInviteUsersView) {
|
||||||
super(props);
|
super(props);
|
||||||
this.rid = props.route.params?.rid;
|
this.rid = props.route.params?.rid;
|
||||||
}
|
}
|
||||||
|
@ -97,6 +105,7 @@ class InviteUsersView extends React.Component {
|
||||||
renderExpiration = () => {
|
renderExpiration = () => {
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
const expirationMessage = this.linkExpirationText();
|
const expirationMessage = this.linkExpirationText();
|
||||||
|
// @ts-ignore
|
||||||
return <Markdown msg={expirationMessage} username='' baseUrl='' theme={theme} />;
|
return <Markdown msg={expirationMessage} username='' baseUrl='' theme={theme} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -104,10 +113,10 @@ class InviteUsersView extends React.Component {
|
||||||
const { theme, invite } = this.props;
|
const { theme, invite } = this.props;
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={{ backgroundColor: themes[theme].backgroundColor }}>
|
<SafeAreaView style={{ backgroundColor: themes[theme].backgroundColor }}>
|
||||||
|
{/* @ts-ignore*/}
|
||||||
<ScrollView
|
<ScrollView
|
||||||
{...scrollPersistTaps}
|
{...scrollPersistTaps}
|
||||||
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
|
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
|
||||||
contentContainerStyle={styles.contentContainer}
|
|
||||||
showsVerticalScrollIndicator={false}>
|
showsVerticalScrollIndicator={false}>
|
||||||
<StatusBar />
|
<StatusBar />
|
||||||
<View style={styles.innerContainer}>
|
<View style={styles.innerContainer}>
|
||||||
|
@ -123,15 +132,15 @@ class InviteUsersView extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: any) => ({
|
||||||
timeDateFormat: state.settings.Message_TimeAndDateFormat,
|
timeDateFormat: state.settings.Message_TimeAndDateFormat,
|
||||||
days: state.inviteLinks.days,
|
days: state.inviteLinks.days,
|
||||||
maxUses: state.inviteLinks.maxUses,
|
maxUses: state.inviteLinks.maxUses,
|
||||||
invite: state.inviteLinks.invite
|
invite: state.inviteLinks.invite
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = (dispatch: Dispatch) => ({
|
||||||
createInviteLink: rid => dispatch(inviteLinksCreateAction(rid)),
|
createInviteLink: (rid: string) => dispatch(inviteLinksCreateAction(rid)),
|
||||||
clearInviteLink: () => dispatch(inviteLinksClearAction())
|
clearInviteLink: () => dispatch(inviteLinksClearAction())
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Alert, Keyboard, StyleSheet, Text, View } from 'react-native';
|
import { Alert, Keyboard, StyleSheet, Text, View } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
import { RouteProp } from '@react-navigation/core';
|
||||||
|
|
||||||
import Button from '../containers/Button';
|
import Button from '../containers/Button';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
|
@ -46,31 +47,35 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
class LoginView extends React.Component {
|
interface IProps {
|
||||||
static navigationOptions = ({ route, navigation }) => ({
|
navigation: StackNavigationProp<any>;
|
||||||
title: route.params?.title ?? 'Rocket.Chat',
|
route: RouteProp<any, 'RegisterView'>;
|
||||||
|
Site_Name: string;
|
||||||
|
Accounts_RegistrationForm: string;
|
||||||
|
Accounts_RegistrationForm_LinkReplacementText: string;
|
||||||
|
Accounts_EmailOrUsernamePlaceholder: string;
|
||||||
|
Accounts_PasswordPlaceholder: string;
|
||||||
|
Accounts_PasswordReset: boolean;
|
||||||
|
Accounts_ShowFormLogin: boolean;
|
||||||
|
isFetching: boolean;
|
||||||
|
error: {
|
||||||
|
error: string;
|
||||||
|
};
|
||||||
|
failure: boolean;
|
||||||
|
theme: string;
|
||||||
|
loginRequest: Function;
|
||||||
|
inviteLinkToken: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoginView extends React.Component<IProps, any> {
|
||||||
|
private passwordInput: any;
|
||||||
|
|
||||||
|
static navigationOptions = ({ route, navigation }: Partial<IProps>) => ({
|
||||||
|
title: route?.params?.title ?? 'Rocket.Chat',
|
||||||
headerRight: () => <HeaderButton.Legal testID='login-view-more' navigation={navigation} />
|
headerRight: () => <HeaderButton.Legal testID='login-view-more' navigation={navigation} />
|
||||||
});
|
});
|
||||||
|
|
||||||
static propTypes = {
|
constructor(props: IProps) {
|
||||||
navigation: PropTypes.object,
|
|
||||||
route: PropTypes.object,
|
|
||||||
Site_Name: PropTypes.string,
|
|
||||||
Accounts_RegistrationForm: PropTypes.string,
|
|
||||||
Accounts_RegistrationForm_LinkReplacementText: PropTypes.string,
|
|
||||||
Accounts_EmailOrUsernamePlaceholder: PropTypes.string,
|
|
||||||
Accounts_PasswordPlaceholder: PropTypes.string,
|
|
||||||
Accounts_PasswordReset: PropTypes.bool,
|
|
||||||
Accounts_ShowFormLogin: PropTypes.bool,
|
|
||||||
isFetching: PropTypes.bool,
|
|
||||||
error: PropTypes.object,
|
|
||||||
failure: PropTypes.bool,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
loginRequest: PropTypes.func,
|
|
||||||
inviteLinkToken: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
user: props.route.params?.username ?? '',
|
user: props.route.params?.username ?? '',
|
||||||
|
@ -78,10 +83,14 @@ class LoginView extends React.Component {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
UNSAFE_componentWillReceiveProps(nextProps: IProps) {
|
||||||
const { error } = this.props;
|
const { error } = this.props;
|
||||||
if (nextProps.failure && !dequal(error, nextProps.error)) {
|
if (nextProps.failure && !dequal(error, nextProps.error)) {
|
||||||
Alert.alert(I18n.t('Oops'), I18n.t('Login_error'));
|
if (nextProps.error?.error === 'error-invalid-email') {
|
||||||
|
this.resendEmailConfirmation();
|
||||||
|
} else {
|
||||||
|
Alert.alert(I18n.t('Oops'), I18n.t('Login_error'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,6 +114,12 @@ class LoginView extends React.Component {
|
||||||
navigation.navigate('ForgotPasswordView', { title: Site_Name });
|
navigation.navigate('ForgotPasswordView', { title: Site_Name });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
resendEmailConfirmation = () => {
|
||||||
|
const { user } = this.state;
|
||||||
|
const { navigation } = this.props;
|
||||||
|
navigation.navigate('SendEmailConfirmationView', { user });
|
||||||
|
};
|
||||||
|
|
||||||
valid = () => {
|
valid = () => {
|
||||||
const { user, password } = this.state;
|
const { user, password } = this.state;
|
||||||
return user.trim() && password.trim();
|
return user.trim() && password.trim();
|
||||||
|
@ -146,7 +161,7 @@ class LoginView extends React.Component {
|
||||||
placeholder={Accounts_EmailOrUsernamePlaceholder || I18n.t('Username_or_email')}
|
placeholder={Accounts_EmailOrUsernamePlaceholder || I18n.t('Username_or_email')}
|
||||||
keyboardType='email-address'
|
keyboardType='email-address'
|
||||||
returnKeyType='next'
|
returnKeyType='next'
|
||||||
onChangeText={value => this.setState({ user: value })}
|
onChangeText={(value: string) => this.setState({ user: value })}
|
||||||
onSubmitEditing={() => {
|
onSubmitEditing={() => {
|
||||||
this.passwordInput.focus();
|
this.passwordInput.focus();
|
||||||
}}
|
}}
|
||||||
|
@ -166,7 +181,7 @@ class LoginView extends React.Component {
|
||||||
returnKeyType='send'
|
returnKeyType='send'
|
||||||
secureTextEntry
|
secureTextEntry
|
||||||
onSubmitEditing={this.submit}
|
onSubmitEditing={this.submit}
|
||||||
onChangeText={value => this.setState({ password: value })}
|
onChangeText={(value: string) => this.setState({ password: value })}
|
||||||
testID='login-view-password'
|
testID='login-view-password'
|
||||||
textContentType='password'
|
textContentType='password'
|
||||||
autoCompleteType='password'
|
autoCompleteType='password'
|
||||||
|
@ -227,7 +242,7 @@ class LoginView extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: any) => ({
|
||||||
server: state.server.server,
|
server: state.server.server,
|
||||||
Site_Name: state.settings.Site_Name,
|
Site_Name: state.settings.Site_Name,
|
||||||
Accounts_ShowFormLogin: state.settings.Accounts_ShowFormLogin,
|
Accounts_ShowFormLogin: state.settings.Accounts_ShowFormLogin,
|
||||||
|
@ -242,8 +257,8 @@ const mapStateToProps = state => ({
|
||||||
inviteLinkToken: state.inviteLinks.token
|
inviteLinkToken: state.inviteLinks.token
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = (dispatch: any) => ({
|
||||||
loginRequest: params => dispatch(loginRequestAction(params))
|
loginRequest: (params: any) => dispatch(loginRequestAction(params))
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(LoginView));
|
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(LoginView));
|
|
@ -1,12 +1,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet, Text, View } from 'react-native';
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { BorderlessButton } from 'react-native-gesture-handler';
|
import { BorderlessButton } from 'react-native-gesture-handler';
|
||||||
|
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
import { CustomIcon } from '../../../lib/Icons';
|
import { CustomIcon } from '../../../lib/Icons';
|
||||||
import sharedStyles from '../../Styles';
|
import sharedStyles from '../../Styles';
|
||||||
import Touch from '../../../utils/touch';
|
import Touch from '../../../utils/touch';
|
||||||
|
import { IServer } from '../index';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -27,13 +27,20 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const Item = ({ item, theme, onPress, onDelete }) => (
|
interface IItem {
|
||||||
|
item: IServer;
|
||||||
|
theme: string;
|
||||||
|
onPress(url: string): void;
|
||||||
|
onDelete(item: IServer): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Item = ({ item, theme, onPress, onDelete }: IItem): JSX.Element => (
|
||||||
<Touch style={styles.container} onPress={() => onPress(item.url)} theme={theme} testID={`server-history-${item.url}`}>
|
<Touch style={styles.container} onPress={() => onPress(item.url)} theme={theme} testID={`server-history-${item.url}`}>
|
||||||
<View style={styles.content}>
|
<View style={styles.content}>
|
||||||
<Text numberOfLines={1} style={[styles.server, { color: themes[theme].bodyText }]}>
|
<Text numberOfLines={1} style={[styles.server, { color: themes[theme].bodyText }]}>
|
||||||
{item.url}
|
{item.url}
|
||||||
</Text>
|
</Text>
|
||||||
<Text numberOfLines={1} style={[styles.username, { color: themes[theme].auxiliaryText }]}>
|
<Text numberOfLines={1} style={{ color: themes[theme].auxiliaryText }}>
|
||||||
{item.username}
|
{item.username}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
@ -43,11 +50,4 @@ const Item = ({ item, theme, onPress, onDelete }) => (
|
||||||
</Touch>
|
</Touch>
|
||||||
);
|
);
|
||||||
|
|
||||||
Item.propTypes = {
|
|
||||||
item: PropTypes.object,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
onPress: PropTypes.func,
|
|
||||||
onDelete: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Item;
|
export default Item;
|
|
@ -1,12 +1,12 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { FlatList, StyleSheet, View } from 'react-native';
|
import { FlatList, StyleSheet, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import TextInput from '../../../containers/TextInput';
|
import TextInput from '../../../containers/TextInput';
|
||||||
import * as List from '../../../containers/List';
|
import * as List from '../../../containers/List';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
import I18n from '../../../i18n';
|
import I18n from '../../../i18n';
|
||||||
import Item from './Item';
|
import Item from './Item';
|
||||||
|
import { IServer } from '../index';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -28,7 +28,25 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const ServerInput = ({ text, theme, serversHistory, onChangeText, onSubmit, onDelete, onPressServerHistory }) => {
|
interface IServerInput {
|
||||||
|
text: string;
|
||||||
|
theme: string;
|
||||||
|
serversHistory: any[];
|
||||||
|
onChangeText(text: string): void;
|
||||||
|
onSubmit(): void;
|
||||||
|
onDelete(item: IServer): void;
|
||||||
|
onPressServerHistory(serverHistory: IServer): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ServerInput = ({
|
||||||
|
text,
|
||||||
|
theme,
|
||||||
|
serversHistory,
|
||||||
|
onChangeText,
|
||||||
|
onSubmit,
|
||||||
|
onDelete,
|
||||||
|
onPressServerHistory
|
||||||
|
}: IServerInput): JSX.Element => {
|
||||||
const [focused, setFocused] = useState(false);
|
const [focused, setFocused] = useState(false);
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
|
@ -68,14 +86,4 @@ const ServerInput = ({ text, theme, serversHistory, onChangeText, onSubmit, onDe
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
ServerInput.propTypes = {
|
|
||||||
text: PropTypes.string,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
serversHistory: PropTypes.array,
|
|
||||||
onChangeText: PropTypes.func,
|
|
||||||
onSubmit: PropTypes.func,
|
|
||||||
onDelete: PropTypes.func,
|
|
||||||
onPressServerHistory: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ServerInput;
|
export default ServerInput;
|
|
@ -1,5 +1,4 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Text, Keyboard, StyleSheet, View, BackHandler, Image } from 'react-native';
|
import { Text, Keyboard, StyleSheet, View, BackHandler, Image } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Base64 } from 'js-base64';
|
import { Base64 } from 'js-base64';
|
||||||
|
@ -7,6 +6,9 @@ import parse from 'url-parse';
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
import { TouchableOpacity } from 'react-native-gesture-handler';
|
import { TouchableOpacity } from 'react-native-gesture-handler';
|
||||||
import Orientation from 'react-native-orientation-locker';
|
import Orientation from 'react-native-orientation-locker';
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
|
||||||
import UserPreferences from '../../lib/userPreferences';
|
import UserPreferences from '../../lib/userPreferences';
|
||||||
import EventEmitter from '../../utils/events';
|
import EventEmitter from '../../utils/events';
|
||||||
|
@ -19,7 +21,6 @@ import FormContainer, { FormContainerInner } from '../../containers/FormContaine
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { events, logEvent } from '../../utils/log';
|
import { events, logEvent } from '../../utils/log';
|
||||||
import { animateNextTransition } from '../../utils/layoutAnimation';
|
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { BASIC_AUTH_KEY, setBasicAuth } from '../../utils/fetch';
|
import { BASIC_AUTH_KEY, setBasicAuth } from '../../utils/fetch';
|
||||||
import * as HeaderButton from '../../containers/HeaderButton';
|
import * as HeaderButton from '../../containers/HeaderButton';
|
||||||
|
@ -66,19 +67,32 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
class NewServerView extends React.Component {
|
export interface IServer extends Model {
|
||||||
static propTypes = {
|
url: string;
|
||||||
navigation: PropTypes.object,
|
username: string;
|
||||||
theme: PropTypes.string,
|
}
|
||||||
connecting: PropTypes.bool.isRequired,
|
interface INewServerView {
|
||||||
connectServer: PropTypes.func.isRequired,
|
navigation: StackNavigationProp<any, 'NewServerView'>;
|
||||||
selectServer: PropTypes.func.isRequired,
|
theme: string;
|
||||||
previousServer: PropTypes.string,
|
connecting: boolean;
|
||||||
inviteLinksClear: PropTypes.func,
|
connectServer(server: string, username?: string, fromServerHistory?: boolean): void;
|
||||||
serverFinishAdd: PropTypes.func
|
selectServer(server: string): void;
|
||||||
};
|
previousServer: string;
|
||||||
|
inviteLinksClear(): void;
|
||||||
|
serverFinishAdd(): void;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(props) {
|
interface IState {
|
||||||
|
text: string;
|
||||||
|
connectingOpen: boolean;
|
||||||
|
certificate: any;
|
||||||
|
serversHistory: IServer[];
|
||||||
|
}
|
||||||
|
|
||||||
|
class NewServerView extends React.Component<INewServerView, IState> {
|
||||||
|
constructor(props: INewServerView) {
|
||||||
super(props);
|
super(props);
|
||||||
if (!isTablet) {
|
if (!isTablet) {
|
||||||
Orientation.lockToPortrait();
|
Orientation.lockToPortrait();
|
||||||
|
@ -131,21 +145,21 @@ class NewServerView extends React.Component {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
onChangeText = text => {
|
onChangeText = (text: string) => {
|
||||||
this.setState({ text });
|
this.setState({ text });
|
||||||
this.queryServerHistory(text);
|
this.queryServerHistory(text);
|
||||||
};
|
};
|
||||||
|
|
||||||
queryServerHistory = async text => {
|
queryServerHistory = async (text?: string) => {
|
||||||
const db = database.servers;
|
const db = database.servers;
|
||||||
try {
|
try {
|
||||||
const serversHistoryCollection = db.get('servers_history');
|
const serversHistoryCollection = db.get('servers_history');
|
||||||
let whereClause = [Q.where('username', Q.notEq(null)), Q.experimentalSortBy('updated_at', Q.desc), Q.experimentalTake(3)];
|
let whereClause = [Q.where('username', Q.notEq(null)), Q.experimentalSortBy('updated_at', Q.desc), Q.experimentalTake(3)];
|
||||||
const likeString = sanitizeLikeString(text);
|
|
||||||
if (text) {
|
if (text) {
|
||||||
|
const likeString = sanitizeLikeString(text);
|
||||||
whereClause = [...whereClause, Q.where('url', Q.like(`%${likeString}%`))];
|
whereClause = [...whereClause, Q.where('url', Q.like(`%${likeString}%`))];
|
||||||
}
|
}
|
||||||
const serversHistory = await serversHistoryCollection.query(...whereClause).fetch();
|
const serversHistory = (await serversHistoryCollection.query(...whereClause).fetch()) as IServer[];
|
||||||
this.setState({ serversHistory });
|
this.setState({ serversHistory });
|
||||||
} catch {
|
} catch {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
|
@ -158,7 +172,7 @@ class NewServerView extends React.Component {
|
||||||
selectServer(previousServer);
|
selectServer(previousServer);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleNewServerEvent = event => {
|
handleNewServerEvent = (event: { server: string }) => {
|
||||||
let { server } = event;
|
let { server } = event;
|
||||||
if (!server) {
|
if (!server) {
|
||||||
return;
|
return;
|
||||||
|
@ -169,13 +183,11 @@ class NewServerView extends React.Component {
|
||||||
connectServer(server);
|
connectServer(server);
|
||||||
};
|
};
|
||||||
|
|
||||||
onPressServerHistory = serverHistory => {
|
onPressServerHistory = (serverHistory: IServer) => {
|
||||||
this.setState({ text: serverHistory?.url }, () =>
|
this.setState({ text: serverHistory.url }, () => this.submit(true, serverHistory?.username));
|
||||||
this.submit({ fromServerHistory: true, username: serverHistory?.username })
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
submit = async ({ fromServerHistory = false, username }) => {
|
submit = async (fromServerHistory?: boolean, username?: string) => {
|
||||||
logEvent(events.NS_CONNECT_TO_WORKSPACE);
|
logEvent(events.NS_CONNECT_TO_WORKSPACE);
|
||||||
const { text, certificate } = this.state;
|
const { text, certificate } = this.state;
|
||||||
const { connectServer } = this.props;
|
const { connectServer } = this.props;
|
||||||
|
@ -207,7 +219,7 @@ class NewServerView extends React.Component {
|
||||||
connectServer('https://open.rocket.chat');
|
connectServer('https://open.rocket.chat');
|
||||||
};
|
};
|
||||||
|
|
||||||
basicAuth = async (server, text) => {
|
basicAuth = async (server: string, text: string) => {
|
||||||
try {
|
try {
|
||||||
const parsedUrl = parse(text, true);
|
const parsedUrl = parse(text, true);
|
||||||
if (parsedUrl.auth.length) {
|
if (parsedUrl.auth.length) {
|
||||||
|
@ -222,14 +234,14 @@ class NewServerView extends React.Component {
|
||||||
|
|
||||||
chooseCertificate = async () => {
|
chooseCertificate = async () => {
|
||||||
try {
|
try {
|
||||||
const certificate = await SSLPinning.pickCertificate();
|
const certificate = await SSLPinning?.pickCertificate();
|
||||||
this.setState({ certificate });
|
this.setState({ certificate });
|
||||||
} catch {
|
} catch {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
completeUrl = url => {
|
completeUrl = (url: string) => {
|
||||||
const parsedUrl = parse(url, true);
|
const parsedUrl = parse(url, true);
|
||||||
if (parsedUrl.auth.length) {
|
if (parsedUrl.auth.length) {
|
||||||
url = parsedUrl.origin;
|
url = parsedUrl.origin;
|
||||||
|
@ -252,14 +264,11 @@ class NewServerView extends React.Component {
|
||||||
return url.replace(/\/+$/, '').replace(/\\/g, '/');
|
return url.replace(/\/+$/, '').replace(/\\/g, '/');
|
||||||
};
|
};
|
||||||
|
|
||||||
uriToPath = uri => uri.replace('file://', '');
|
uriToPath = (uri: string) => uri.replace('file://', '');
|
||||||
|
|
||||||
saveCertificate = certificate => {
|
|
||||||
animateNextTransition();
|
|
||||||
this.setState({ certificate });
|
|
||||||
};
|
|
||||||
|
|
||||||
handleRemove = () => {
|
handleRemove = () => {
|
||||||
|
// TODO: Remove ts-ignore when migrate the showConfirmationAlert
|
||||||
|
// @ts-ignore
|
||||||
showConfirmationAlert({
|
showConfirmationAlert({
|
||||||
message: I18n.t('You_will_unset_a_certificate_for_this_server'),
|
message: I18n.t('You_will_unset_a_certificate_for_this_server'),
|
||||||
confirmationText: I18n.t('Remove'),
|
confirmationText: I18n.t('Remove'),
|
||||||
|
@ -267,14 +276,15 @@ class NewServerView extends React.Component {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
deleteServerHistory = async item => {
|
deleteServerHistory = async (item: IServer) => {
|
||||||
const { serversHistory } = this.state;
|
|
||||||
const db = database.servers;
|
const db = database.servers;
|
||||||
try {
|
try {
|
||||||
await db.action(async () => {
|
await db.write(async () => {
|
||||||
await item.destroyPermanently();
|
await item.destroyPermanently();
|
||||||
});
|
});
|
||||||
this.setState({ serversHistory: serversHistory.filter(server => server.id !== item.id) });
|
this.setState((prevstate: IState) => ({
|
||||||
|
serversHistory: prevstate.serversHistory.filter((server: IServer) => server.id !== item.id)
|
||||||
|
}));
|
||||||
} catch {
|
} catch {
|
||||||
// Nothing
|
// Nothing
|
||||||
}
|
}
|
||||||
|
@ -288,20 +298,21 @@ class NewServerView extends React.Component {
|
||||||
style={[
|
style={[
|
||||||
styles.certificatePicker,
|
styles.certificatePicker,
|
||||||
{
|
{
|
||||||
marginBottom: verticalScale(previousServer && !isTablet ? 10 : 30, height)
|
marginBottom: verticalScale({ size: previousServer && !isTablet ? 10 : 30, height })
|
||||||
}
|
}
|
||||||
]}>
|
]}>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.chooseCertificateTitle,
|
styles.chooseCertificateTitle,
|
||||||
{ color: themes[theme].auxiliaryText, fontSize: moderateScale(13, null, width) }
|
{ color: themes[theme].auxiliaryText, fontSize: moderateScale({ size: 13, width }) }
|
||||||
]}>
|
]}>
|
||||||
{certificate ? I18n.t('Your_certificate') : I18n.t('Do_you_have_a_certificate')}
|
{certificate ? I18n.t('Your_certificate') : I18n.t('Do_you_have_a_certificate')}
|
||||||
</Text>
|
</Text>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={certificate ? this.handleRemove : this.chooseCertificate}
|
onPress={certificate ? this.handleRemove : this.chooseCertificate}
|
||||||
testID='new-server-choose-certificate'>
|
testID='new-server-choose-certificate'>
|
||||||
<Text style={[styles.chooseCertificate, { color: themes[theme].tintColor, fontSize: moderateScale(13, null, width) }]}>
|
<Text
|
||||||
|
style={[styles.chooseCertificate, { color: themes[theme].tintColor, fontSize: moderateScale({ size: 13, width }) }]}>
|
||||||
{certificate ?? I18n.t('Apply_Your_Certificate')}
|
{certificate ?? I18n.t('Apply_Your_Certificate')}
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
@ -321,10 +332,10 @@ class NewServerView extends React.Component {
|
||||||
style={[
|
style={[
|
||||||
styles.onboardingImage,
|
styles.onboardingImage,
|
||||||
{
|
{
|
||||||
marginBottom: verticalScale(10, height),
|
marginBottom: verticalScale({ size: 10, height }),
|
||||||
marginTop: isTablet ? 0 : verticalScale(marginTop, height),
|
marginTop: isTablet ? 0 : verticalScale({ size: marginTop, height }),
|
||||||
width: verticalScale(100, height),
|
width: verticalScale({ size: 100, height }),
|
||||||
height: verticalScale(100, height)
|
height: verticalScale({ size: 100, height })
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
source={require('../../static/images/logo.png')}
|
source={require('../../static/images/logo.png')}
|
||||||
|
@ -335,8 +346,8 @@ class NewServerView extends React.Component {
|
||||||
styles.title,
|
styles.title,
|
||||||
{
|
{
|
||||||
color: themes[theme].titleText,
|
color: themes[theme].titleText,
|
||||||
fontSize: moderateScale(22, null, width),
|
fontSize: moderateScale({ size: 22, width }),
|
||||||
marginBottom: verticalScale(8, height)
|
marginBottom: verticalScale({ size: 8, height })
|
||||||
}
|
}
|
||||||
]}>
|
]}>
|
||||||
Rocket.Chat
|
Rocket.Chat
|
||||||
|
@ -346,8 +357,8 @@ class NewServerView extends React.Component {
|
||||||
styles.subtitle,
|
styles.subtitle,
|
||||||
{
|
{
|
||||||
color: themes[theme].controlText,
|
color: themes[theme].controlText,
|
||||||
fontSize: moderateScale(16, null, width),
|
fontSize: moderateScale({ size: 16, width }),
|
||||||
marginBottom: verticalScale(30, height)
|
marginBottom: verticalScale({ size: 30, height })
|
||||||
}
|
}
|
||||||
]}>
|
]}>
|
||||||
{I18n.t('Onboarding_subtitle')}
|
{I18n.t('Onboarding_subtitle')}
|
||||||
|
@ -367,7 +378,7 @@ class NewServerView extends React.Component {
|
||||||
onPress={this.submit}
|
onPress={this.submit}
|
||||||
disabled={!text || connecting}
|
disabled={!text || connecting}
|
||||||
loading={!connectingOpen && connecting}
|
loading={!connectingOpen && connecting}
|
||||||
style={[styles.connectButton, { marginTop: verticalScale(16, height) }]}
|
style={[styles.connectButton, { marginTop: verticalScale({ size: 16, height }) }]}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
testID='new-server-view-button'
|
testID='new-server-view-button'
|
||||||
/>
|
/>
|
||||||
|
@ -377,8 +388,8 @@ class NewServerView extends React.Component {
|
||||||
styles.description,
|
styles.description,
|
||||||
{
|
{
|
||||||
color: themes[theme].auxiliaryText,
|
color: themes[theme].auxiliaryText,
|
||||||
fontSize: moderateScale(14, null, width),
|
fontSize: moderateScale({ size: 14, width }),
|
||||||
marginBottom: verticalScale(16, height)
|
marginBottom: verticalScale({ size: 16, height })
|
||||||
}
|
}
|
||||||
]}>
|
]}>
|
||||||
{I18n.t('Onboarding_join_open_description')}
|
{I18n.t('Onboarding_join_open_description')}
|
||||||
|
@ -400,14 +411,15 @@ class NewServerView extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: any) => ({
|
||||||
connecting: state.server.connecting,
|
connecting: state.server.connecting,
|
||||||
previousServer: state.server.previousServer
|
previousServer: state.server.previousServer
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = (dispatch: Dispatch) => ({
|
||||||
connectServer: (...params) => dispatch(serverRequest(...params)),
|
connectServer: (server: string, username: string & null, fromServerHistory?: boolean) =>
|
||||||
selectServer: server => dispatch(selectServerRequest(server)),
|
dispatch(serverRequest(server, username, fromServerHistory)),
|
||||||
|
selectServer: (server: string) => dispatch(selectServerRequest(server)),
|
||||||
inviteLinksClear: () => dispatch(inviteLinksClearAction()),
|
inviteLinksClear: () => dispatch(inviteLinksClearAction()),
|
||||||
serverFinishAdd: () => dispatch(serverFinishAddAction())
|
serverFinishAdd: () => dispatch(serverFinishAddAction())
|
||||||
});
|
});
|
|
@ -1,13 +1,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { FlatList, Text, View } from 'react-native';
|
import { FlatList, Text, View, RefreshControl } from 'react-native';
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import * as List from '../../containers/List';
|
import * as List from '../../containers/List';
|
||||||
import Avatar from '../../containers/Avatar';
|
import Avatar from '../../containers/Avatar';
|
||||||
import ActivityIndicator from '../../containers/ActivityIndicator';
|
|
||||||
import * as HeaderButton from '../../containers/HeaderButton';
|
import * as HeaderButton from '../../containers/HeaderButton';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import RocketChat from '../../lib/rocketchat';
|
import RocketChat from '../../lib/rocketchat';
|
||||||
|
@ -85,12 +84,16 @@ class ReadReceiptView extends React.Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
renderEmpty = () => {
|
renderEmpty = () => {
|
||||||
|
const { loading } = this.state;
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
|
if (loading) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[styles.listEmptyContainer, { backgroundColor: themes[theme].chatComponentBackground }]}
|
style={[styles.listEmptyContainer, { backgroundColor: themes[theme].chatComponentBackground }]}
|
||||||
testID='read-receipt-view'>
|
testID='read-receipt-view'>
|
||||||
<Text style={{ color: themes[theme].titleText }}>{I18n.t('No_Read_Receipts')}</Text>
|
<Text style={[styles.emptyText, { color: themes[theme].auxiliaryTintColor }]}>{I18n.t('No_Read_Receipts')}</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -107,9 +110,15 @@ class ReadReceiptView extends React.Component {
|
||||||
<View style={styles.infoContainer}>
|
<View style={styles.infoContainer}>
|
||||||
<View style={styles.item}>
|
<View style={styles.item}>
|
||||||
<Text style={[styles.name, { color: themes[theme].titleText }]}>{item?.user?.name}</Text>
|
<Text style={[styles.name, { color: themes[theme].titleText }]}>{item?.user?.name}</Text>
|
||||||
<Text style={{ color: themes[theme].auxiliaryText }}>{time}</Text>
|
<Text style={[styles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text style={{ color: themes[theme].auxiliaryText }}>{`@${item.user.username}`}</Text>
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.username,
|
||||||
|
{
|
||||||
|
color: themes[theme].auxiliaryText
|
||||||
|
}
|
||||||
|
]}>{`@${item.user.username}`}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -119,30 +128,25 @@ class ReadReceiptView extends React.Component {
|
||||||
const { receipts, loading } = this.state;
|
const { receipts, loading } = this.state;
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
|
|
||||||
if (!loading && receipts.length === 0) {
|
|
||||||
return this.renderEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView testID='read-receipt-view'>
|
<SafeAreaView testID='read-receipt-view'>
|
||||||
<StatusBar />
|
<StatusBar />
|
||||||
{loading ? (
|
<FlatList
|
||||||
<ActivityIndicator theme={theme} />
|
data={receipts}
|
||||||
) : (
|
renderItem={this.renderItem}
|
||||||
<FlatList
|
ItemSeparatorComponent={List.Separator}
|
||||||
data={receipts}
|
ListEmptyComponent={this.renderEmpty}
|
||||||
renderItem={this.renderItem}
|
contentContainerStyle={List.styles.contentContainerStyleFlatList}
|
||||||
ItemSeparatorComponent={List.Separator}
|
style={[
|
||||||
style={[
|
styles.list,
|
||||||
styles.list,
|
{
|
||||||
{
|
backgroundColor: themes[theme].chatComponentBackground,
|
||||||
backgroundColor: themes[theme].chatComponentBackground,
|
borderColor: themes[theme].separatorColor
|
||||||
borderColor: themes[theme].separatorColor
|
}
|
||||||
}
|
]}
|
||||||
]}
|
refreshControl={<RefreshControl refreshing={loading} onRefresh={this.load} tintColor={themes[theme].auxiliaryText} />}
|
||||||
keyExtractor={item => item._id}
|
keyExtractor={item => item._id}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,14 @@ export default StyleSheet.create({
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center'
|
justifyContent: 'center'
|
||||||
},
|
},
|
||||||
|
emptyText: {
|
||||||
|
fontSize: 16,
|
||||||
|
...sharedStyles.textRegular
|
||||||
|
},
|
||||||
item: {
|
item: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between'
|
justifyContent: 'space-between'
|
||||||
},
|
},
|
||||||
separator: {
|
separator: {
|
||||||
|
@ -20,6 +25,14 @@ export default StyleSheet.create({
|
||||||
...sharedStyles.textRegular,
|
...sharedStyles.textRegular,
|
||||||
fontSize: 17
|
fontSize: 17
|
||||||
},
|
},
|
||||||
|
username: {
|
||||||
|
...sharedStyles.textMedium,
|
||||||
|
fontSize: 14
|
||||||
|
},
|
||||||
|
time: {
|
||||||
|
...sharedStyles.textRegular,
|
||||||
|
fontSize: 12
|
||||||
|
},
|
||||||
infoContainer: {
|
infoContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
marginLeft: 10
|
marginLeft: 10
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Keyboard, StyleSheet, Text, View } from 'react-native';
|
import { Keyboard, StyleSheet, Text, View } from 'react-native';
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
import { RouteProp } from '@react-navigation/core';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import RNPickerSelect from 'react-native-picker-select';
|
import RNPickerSelect from 'react-native-picker-select';
|
||||||
|
|
||||||
|
@ -49,27 +50,37 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
class RegisterView extends React.Component {
|
interface IProps {
|
||||||
static navigationOptions = ({ route, navigation }) => ({
|
navigation: StackNavigationProp<any>;
|
||||||
title: route.params?.title ?? 'Rocket.Chat',
|
route: RouteProp<any, 'RegisterView'>;
|
||||||
|
server: string;
|
||||||
|
Site_Name: string;
|
||||||
|
Gitlab_URL: string;
|
||||||
|
CAS_enabled: boolean;
|
||||||
|
CAS_login_url: string;
|
||||||
|
Accounts_CustomFields: string;
|
||||||
|
Accounts_EmailVerification: boolean;
|
||||||
|
Accounts_ManuallyApproveNewUsers: boolean;
|
||||||
|
showLoginButton: boolean;
|
||||||
|
loginRequest: Function;
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RegisterView extends React.Component<IProps, any> {
|
||||||
|
private parsedCustomFields: any;
|
||||||
|
private usernameInput: any;
|
||||||
|
private passwordInput: any;
|
||||||
|
private emailInput: any;
|
||||||
|
private avatarUrl: any;
|
||||||
|
|
||||||
|
static navigationOptions = ({ route, navigation }: Partial<IProps>) => ({
|
||||||
|
title: route?.params?.title ?? 'Rocket.Chat',
|
||||||
headerRight: () => <HeaderButton.Legal testID='register-view-more' navigation={navigation} />
|
headerRight: () => <HeaderButton.Legal testID='register-view-more' navigation={navigation} />
|
||||||
});
|
});
|
||||||
|
|
||||||
static propTypes = {
|
constructor(props: IProps) {
|
||||||
navigation: PropTypes.object,
|
|
||||||
server: PropTypes.string,
|
|
||||||
Accounts_CustomFields: PropTypes.string,
|
|
||||||
Accounts_EmailVerification: PropTypes.bool,
|
|
||||||
Accounts_ManuallyApproveNewUsers: PropTypes.bool,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
Site_Name: PropTypes.string,
|
|
||||||
loginRequest: PropTypes.func,
|
|
||||||
showLoginButton: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
super(props);
|
||||||
const customFields = {};
|
const customFields: any = {};
|
||||||
this.parsedCustomFields = {};
|
this.parsedCustomFields = {};
|
||||||
if (props.Accounts_CustomFields) {
|
if (props.Accounts_CustomFields) {
|
||||||
try {
|
try {
|
||||||
|
@ -78,7 +89,7 @@ class RegisterView extends React.Component {
|
||||||
log(e);
|
log(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Object.keys(this.parsedCustomFields).forEach(key => {
|
Object.keys(this.parsedCustomFields).forEach((key: string) => {
|
||||||
if (this.parsedCustomFields[key].defaultValue) {
|
if (this.parsedCustomFields[key].defaultValue) {
|
||||||
customFields[key] = this.parsedCustomFields[key].defaultValue;
|
customFields[key] = this.parsedCustomFields[key].defaultValue;
|
||||||
}
|
}
|
||||||
|
@ -101,7 +112,7 @@ class RegisterView extends React.Component {
|
||||||
valid = () => {
|
valid = () => {
|
||||||
const { name, email, password, username, customFields } = this.state;
|
const { name, email, password, username, customFields } = this.state;
|
||||||
let requiredCheck = true;
|
let requiredCheck = true;
|
||||||
Object.keys(this.parsedCustomFields).forEach(key => {
|
Object.keys(this.parsedCustomFields).forEach((key: string) => {
|
||||||
if (this.parsedCustomFields[key].required) {
|
if (this.parsedCustomFields[key].required) {
|
||||||
requiredCheck = requiredCheck && customFields[key] && Boolean(customFields[key].trim());
|
requiredCheck = requiredCheck && customFields[key] && Boolean(customFields[key].trim());
|
||||||
}
|
}
|
||||||
|
@ -138,7 +149,7 @@ class RegisterView extends React.Component {
|
||||||
} else {
|
} else {
|
||||||
await loginRequest({ user: email, password });
|
await loginRequest({ user: email, password });
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
if (e.data?.errorType === 'username-invalid') {
|
if (e.data?.errorType === 'username-invalid') {
|
||||||
return loginRequest({ user: email, password });
|
return loginRequest({ user: email, password });
|
||||||
}
|
}
|
||||||
|
@ -150,7 +161,7 @@ class RegisterView extends React.Component {
|
||||||
this.setState({ saving: false });
|
this.setState({ saving: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
openContract = route => {
|
openContract = (route: string) => {
|
||||||
const { server, theme } = this.props;
|
const { server, theme } = this.props;
|
||||||
if (!server) {
|
if (!server) {
|
||||||
return;
|
return;
|
||||||
|
@ -167,19 +178,20 @@ class RegisterView extends React.Component {
|
||||||
try {
|
try {
|
||||||
return Object.keys(this.parsedCustomFields).map((key, index, array) => {
|
return Object.keys(this.parsedCustomFields).map((key, index, array) => {
|
||||||
if (this.parsedCustomFields[key].type === 'select') {
|
if (this.parsedCustomFields[key].type === 'select') {
|
||||||
const options = this.parsedCustomFields[key].options.map(option => ({ label: option, value: option }));
|
const options = this.parsedCustomFields[key].options.map((option: string) => ({ label: option, value: option }));
|
||||||
return (
|
return (
|
||||||
<RNPickerSelect
|
<RNPickerSelect
|
||||||
key={key}
|
key={key}
|
||||||
items={options}
|
items={options}
|
||||||
onValueChange={value => {
|
onValueChange={value => {
|
||||||
const newValue = {};
|
const newValue: { [key: string]: string | number } = {};
|
||||||
newValue[key] = value;
|
newValue[key] = value;
|
||||||
this.setState({ customFields: { ...customFields, ...newValue } });
|
this.setState({ customFields: { ...customFields, ...newValue } });
|
||||||
}}
|
}}
|
||||||
value={customFields[key]}>
|
value={customFields[key]}>
|
||||||
<TextInput
|
<TextInput
|
||||||
inputRef={e => {
|
inputRef={(e: any) => {
|
||||||
|
// @ts-ignore
|
||||||
this[key] = e;
|
this[key] = e;
|
||||||
}}
|
}}
|
||||||
placeholder={key}
|
placeholder={key}
|
||||||
|
@ -193,20 +205,22 @@ class RegisterView extends React.Component {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TextInput
|
<TextInput
|
||||||
inputRef={e => {
|
inputRef={(e: any) => {
|
||||||
|
// @ts-ignore
|
||||||
this[key] = e;
|
this[key] = e;
|
||||||
}}
|
}}
|
||||||
key={key}
|
key={key}
|
||||||
label={key}
|
label={key}
|
||||||
placeholder={key}
|
placeholder={key}
|
||||||
value={customFields[key]}
|
value={customFields[key]}
|
||||||
onChangeText={value => {
|
onChangeText={(value: string) => {
|
||||||
const newValue = {};
|
const newValue: { [key: string]: string | number } = {};
|
||||||
newValue[key] = value;
|
newValue[key] = value;
|
||||||
this.setState({ customFields: { ...customFields, ...newValue } });
|
this.setState({ customFields: { ...customFields, ...newValue } });
|
||||||
}}
|
}}
|
||||||
onSubmitEditing={() => {
|
onSubmitEditing={() => {
|
||||||
if (array.length - 1 > index) {
|
if (array.length - 1 > index) {
|
||||||
|
// @ts-ignore
|
||||||
return this[array[index + 1]].focus();
|
return this[array[index + 1]].focus();
|
||||||
}
|
}
|
||||||
this.avatarUrl.focus();
|
this.avatarUrl.focus();
|
||||||
|
@ -234,7 +248,7 @@ class RegisterView extends React.Component {
|
||||||
containerStyle={styles.inputContainer}
|
containerStyle={styles.inputContainer}
|
||||||
placeholder={I18n.t('Name')}
|
placeholder={I18n.t('Name')}
|
||||||
returnKeyType='next'
|
returnKeyType='next'
|
||||||
onChangeText={name => this.setState({ name })}
|
onChangeText={(name: string) => this.setState({ name })}
|
||||||
onSubmitEditing={() => {
|
onSubmitEditing={() => {
|
||||||
this.usernameInput.focus();
|
this.usernameInput.focus();
|
||||||
}}
|
}}
|
||||||
|
@ -249,7 +263,7 @@ class RegisterView extends React.Component {
|
||||||
}}
|
}}
|
||||||
placeholder={I18n.t('Username')}
|
placeholder={I18n.t('Username')}
|
||||||
returnKeyType='next'
|
returnKeyType='next'
|
||||||
onChangeText={username => this.setState({ username })}
|
onChangeText={(username: string) => this.setState({ username })}
|
||||||
onSubmitEditing={() => {
|
onSubmitEditing={() => {
|
||||||
this.emailInput.focus();
|
this.emailInput.focus();
|
||||||
}}
|
}}
|
||||||
|
@ -265,7 +279,7 @@ class RegisterView extends React.Component {
|
||||||
placeholder={I18n.t('Email')}
|
placeholder={I18n.t('Email')}
|
||||||
returnKeyType='next'
|
returnKeyType='next'
|
||||||
keyboardType='email-address'
|
keyboardType='email-address'
|
||||||
onChangeText={email => this.setState({ email })}
|
onChangeText={(email: string) => this.setState({ email })}
|
||||||
onSubmitEditing={() => {
|
onSubmitEditing={() => {
|
||||||
this.passwordInput.focus();
|
this.passwordInput.focus();
|
||||||
}}
|
}}
|
||||||
|
@ -281,7 +295,7 @@ class RegisterView extends React.Component {
|
||||||
placeholder={I18n.t('Password')}
|
placeholder={I18n.t('Password')}
|
||||||
returnKeyType='send'
|
returnKeyType='send'
|
||||||
secureTextEntry
|
secureTextEntry
|
||||||
onChangeText={value => this.setState({ password: value })}
|
onChangeText={(value: string) => this.setState({ password: value })}
|
||||||
onSubmitEditing={this.submit}
|
onSubmitEditing={this.submit}
|
||||||
testID='register-view-password'
|
testID='register-view-password'
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
@ -334,7 +348,7 @@ class RegisterView extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: any) => ({
|
||||||
server: state.server.server,
|
server: state.server.server,
|
||||||
Site_Name: state.settings.Site_Name,
|
Site_Name: state.settings.Site_Name,
|
||||||
Gitlab_URL: state.settings.API_Gitlab_URL,
|
Gitlab_URL: state.settings.API_Gitlab_URL,
|
||||||
|
@ -346,8 +360,8 @@ const mapStateToProps = state => ({
|
||||||
showLoginButton: getShowLoginButton(state)
|
showLoginButton: getShowLoginButton(state)
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = (dispatch: any) => ({
|
||||||
loginRequest: params => dispatch(loginRequestAction(params))
|
loginRequest: (params: any) => dispatch(loginRequestAction(params))
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(RegisterView));
|
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(RegisterView));
|
|
@ -14,6 +14,7 @@ import { animateNextTransition } from '../../../utils/layoutAnimation';
|
||||||
import ActivityIndicator from '../../../containers/ActivityIndicator';
|
import ActivityIndicator from '../../../containers/ActivityIndicator';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
import debounce from '../../../utils/debounce';
|
import debounce from '../../../utils/debounce';
|
||||||
|
import { compareServerVersion, methods } from '../../../lib/utils';
|
||||||
import List from './List';
|
import List from './List';
|
||||||
import NavBottomFAB from './NavBottomFAB';
|
import NavBottomFAB from './NavBottomFAB';
|
||||||
|
|
||||||
|
@ -43,7 +44,8 @@ class ListContainer extends React.Component {
|
||||||
tunread: PropTypes.array,
|
tunread: PropTypes.array,
|
||||||
ignored: PropTypes.array,
|
ignored: PropTypes.array,
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
showMessageInMainThread: PropTypes.bool
|
showMessageInMainThread: PropTypes.bool,
|
||||||
|
serverVersion: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -131,7 +133,7 @@ class ListContainer extends React.Component {
|
||||||
|
|
||||||
query = async () => {
|
query = async () => {
|
||||||
this.count += QUERY_SIZE;
|
this.count += QUERY_SIZE;
|
||||||
const { rid, tmid, showMessageInMainThread } = this.props;
|
const { rid, tmid, showMessageInMainThread, serverVersion } = this.props;
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
|
|
||||||
// handle servers with version < 3.0.0
|
// handle servers with version < 3.0.0
|
||||||
|
@ -172,7 +174,14 @@ class ListContainer extends React.Component {
|
||||||
if (tmid && this.thread) {
|
if (tmid && this.thread) {
|
||||||
messages = [...messages, this.thread];
|
messages = [...messages, this.thread];
|
||||||
}
|
}
|
||||||
messages = messages.filter(m => !m.t || !hideSystemMessages?.includes(m.t));
|
|
||||||
|
/**
|
||||||
|
* Since 3.16.0 server version, the backend don't response with messages if
|
||||||
|
* hide system message is enabled
|
||||||
|
*/
|
||||||
|
if (compareServerVersion(serverVersion, '3.16.0', methods.lowerThan) || hideSystemMessages.length) {
|
||||||
|
messages = messages.filter(m => !m.t || !hideSystemMessages?.includes(m.t));
|
||||||
|
}
|
||||||
|
|
||||||
if (this.mounted) {
|
if (this.mounted) {
|
||||||
this.setState({ messages }, () => this.update());
|
this.setState({ messages }, () => this.update());
|
||||||
|
|
|
@ -120,6 +120,7 @@ class RoomView extends React.Component {
|
||||||
Message_Read_Receipt_Enabled: PropTypes.bool,
|
Message_Read_Receipt_Enabled: PropTypes.bool,
|
||||||
Hide_System_Messages: PropTypes.array,
|
Hide_System_Messages: PropTypes.array,
|
||||||
baseUrl: PropTypes.string,
|
baseUrl: PropTypes.string,
|
||||||
|
serverVersion: PropTypes.string,
|
||||||
customEmojis: PropTypes.object,
|
customEmojis: PropTypes.object,
|
||||||
isMasterDetail: PropTypes.bool,
|
isMasterDetail: PropTypes.bool,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
|
@ -1154,7 +1155,7 @@ class RoomView extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
console.count(`${this.constructor.name}.render calls`);
|
console.count(`${this.constructor.name}.render calls`);
|
||||||
const { room, reactionsModalVisible, selectedMessage, loading, reacting, showingBlockingLoader } = this.state;
|
const { room, reactionsModalVisible, selectedMessage, loading, reacting, showingBlockingLoader } = this.state;
|
||||||
const { user, baseUrl, theme, navigation, Hide_System_Messages, width, height } = this.props;
|
const { user, baseUrl, theme, navigation, Hide_System_Messages, width, height, serverVersion } = this.props;
|
||||||
const { rid, t, sysMes, bannerClosed, announcement } = room;
|
const { rid, t, sysMes, bannerClosed, announcement } = room;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1182,6 +1183,7 @@ class RoomView extends React.Component {
|
||||||
navigation={navigation}
|
navigation={navigation}
|
||||||
hideSystemMessages={Array.isArray(sysMes) ? sysMes : Hide_System_Messages}
|
hideSystemMessages={Array.isArray(sysMes) ? sysMes : Hide_System_Messages}
|
||||||
showMessageInMainThread={user.showMessageInMainThread}
|
showMessageInMainThread={user.showMessageInMainThread}
|
||||||
|
serverVersion={serverVersion}
|
||||||
/>
|
/>
|
||||||
{this.renderFooter()}
|
{this.renderFooter()}
|
||||||
{this.renderActions()}
|
{this.renderActions()}
|
||||||
|
@ -1220,6 +1222,7 @@ const mapStateToProps = state => ({
|
||||||
Message_TimeFormat: state.settings.Message_TimeFormat,
|
Message_TimeFormat: state.settings.Message_TimeFormat,
|
||||||
customEmojis: state.customEmojis,
|
customEmojis: state.customEmojis,
|
||||||
baseUrl: state.server.server,
|
baseUrl: state.server.server,
|
||||||
|
serverVersion: state.server.version,
|
||||||
Message_Read_Receipt_Enabled: state.settings.Message_Read_Receipt_Enabled,
|
Message_Read_Receipt_Enabled: state.settings.Message_Read_Receipt_Enabled,
|
||||||
Hide_System_Messages: state.settings.Hide_System_Messages
|
Hide_System_Messages: state.settings.Hide_System_Messages
|
||||||
});
|
});
|
||||||
|
|
|
@ -613,6 +613,8 @@ class RoomsListView extends React.Component {
|
||||||
|
|
||||||
isRead = item => RocketChat.isRead(item);
|
isRead = item => RocketChat.isRead(item);
|
||||||
|
|
||||||
|
isSwipeEnabled = item => !(item?.search || item?.joinCodeRequired || item?.outside);
|
||||||
|
|
||||||
getUserPresence = uid => RocketChat.getUserPresence(uid);
|
getUserPresence = uid => RocketChat.getUserPresence(uid);
|
||||||
|
|
||||||
getUidDirectMessage = room => RocketChat.getUidDirectMessage(room);
|
getUidDirectMessage = room => RocketChat.getUidDirectMessage(room);
|
||||||
|
@ -928,6 +930,7 @@ class RoomsListView extends React.Component {
|
||||||
displayMode
|
displayMode
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const id = this.getUidDirectMessage(item);
|
const id = this.getUidDirectMessage(item);
|
||||||
|
const swipeEnabled = this.isSwipeEnabled(item);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RoomItem
|
<RoomItem
|
||||||
|
@ -950,6 +953,7 @@ class RoomsListView extends React.Component {
|
||||||
getIsRead={this.isRead}
|
getIsRead={this.isRead}
|
||||||
visitor={item.visitor}
|
visitor={item.visitor}
|
||||||
isFocused={currentItem?.rid === item.rid}
|
isFocused={currentItem?.rid === item.rid}
|
||||||
|
swipeEnabled={swipeEnabled}
|
||||||
showAvatar={showAvatar}
|
showAvatar={showAvatar}
|
||||||
displayMode={displayMode}
|
displayMode={displayMode}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
|
||||||
|
import TextInput from '../containers/TextInput';
|
||||||
|
import Button from '../containers/Button';
|
||||||
|
import { showErrorAlert } from '../utils/info';
|
||||||
|
import isValidEmail from '../utils/isValidEmail';
|
||||||
|
import I18n from '../i18n';
|
||||||
|
import RocketChat from '../lib/rocketchat';
|
||||||
|
import { useTheme } from '../theme';
|
||||||
|
import FormContainer, { FormContainerInner } from '../containers/FormContainer';
|
||||||
|
import log, { events, logEvent } from '../utils/log';
|
||||||
|
import sharedStyles from './Styles';
|
||||||
|
|
||||||
|
interface ISendEmailConfirmationView {
|
||||||
|
navigation: StackNavigationProp<any, 'SendEmailConfirmationView'>;
|
||||||
|
route: {
|
||||||
|
params: {
|
||||||
|
user?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const SendEmailConfirmationView = ({ navigation, route }: ISendEmailConfirmationView): JSX.Element => {
|
||||||
|
const [email, setEmail] = useState('');
|
||||||
|
const [invalidEmail, setInvalidEmail] = useState(true);
|
||||||
|
const [isFetching, setIsFetching] = useState(false);
|
||||||
|
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
|
const validate = (val: string) => {
|
||||||
|
const isInvalidEmail = !isValidEmail(val);
|
||||||
|
setEmail(val);
|
||||||
|
setInvalidEmail(isInvalidEmail);
|
||||||
|
};
|
||||||
|
|
||||||
|
const resendConfirmationEmail = async () => {
|
||||||
|
logEvent(events.SEC_SEND_EMAIL_CONFIRMATION);
|
||||||
|
if (invalidEmail || !email) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
setIsFetching(true);
|
||||||
|
const result = await RocketChat.sendConfirmationEmail(email);
|
||||||
|
if (result.success) {
|
||||||
|
navigation.pop();
|
||||||
|
showErrorAlert(I18n.t('Verify_email_desc'));
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
log(e);
|
||||||
|
const msg = e?.data?.error || I18n.t('There_was_an_error_while_action', { action: I18n.t('sending_email_confirmation') });
|
||||||
|
showErrorAlert(msg, I18n.t('Alert'));
|
||||||
|
}
|
||||||
|
setIsFetching(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
navigation.setOptions({
|
||||||
|
title: 'Rocket.Chat'
|
||||||
|
});
|
||||||
|
if (route.params?.user) {
|
||||||
|
validate(route.params.user);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormContainer theme={theme} testID='send-email-confirmation-view'>
|
||||||
|
<FormContainerInner>
|
||||||
|
<TextInput
|
||||||
|
autoFocus
|
||||||
|
placeholder={I18n.t('Email')}
|
||||||
|
keyboardType='email-address'
|
||||||
|
returnKeyType='send'
|
||||||
|
onChangeText={(email: string) => validate(email)}
|
||||||
|
onSubmitEditing={resendConfirmationEmail}
|
||||||
|
testID='send-email-confirmation-view-email'
|
||||||
|
containerStyle={sharedStyles.inputLastChild}
|
||||||
|
theme={theme}
|
||||||
|
value={email}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
title={I18n.t('Send_email_confirmation')}
|
||||||
|
type='primary'
|
||||||
|
onPress={resendConfirmationEmail}
|
||||||
|
testID='send-email-confirmation-view-submit'
|
||||||
|
loading={isFetching}
|
||||||
|
disabled={invalidEmail}
|
||||||
|
theme={theme}
|
||||||
|
/>
|
||||||
|
</FormContainerInner>
|
||||||
|
</FormContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default SendEmailConfirmationView;
|
|
@ -1,9 +1,9 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Clipboard, Linking, Share } from 'react-native';
|
import { Clipboard, Linking, Share } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import FastImage from '@rocket.chat/react-native-fast-image';
|
import FastImage from '@rocket.chat/react-native-fast-image';
|
||||||
import CookieManager from '@react-native-cookies/cookies';
|
import CookieManager from '@react-native-cookies/cookies';
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
|
||||||
import { logout as logoutAction } from '../../actions/login';
|
import { logout as logoutAction } from '../../actions/login';
|
||||||
import { selectServerRequest as selectServerRequestAction } from '../../actions/server';
|
import { selectServerRequest as selectServerRequestAction } from '../../actions/server';
|
||||||
|
@ -29,8 +29,25 @@ import database from '../../lib/database';
|
||||||
import { isFDroidBuild } from '../../constants/environment';
|
import { isFDroidBuild } from '../../constants/environment';
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
|
|
||||||
class SettingsView extends React.Component {
|
interface IProps {
|
||||||
static navigationOptions = ({ navigation, isMasterDetail }) => ({
|
navigation: StackNavigationProp<any, 'SettingsView'>;
|
||||||
|
server: {
|
||||||
|
version: string;
|
||||||
|
server: string;
|
||||||
|
};
|
||||||
|
theme: string;
|
||||||
|
isMasterDetail: boolean;
|
||||||
|
logout: Function;
|
||||||
|
selectServerRequest: Function;
|
||||||
|
user: {
|
||||||
|
roles: [];
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
appStart: Function;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SettingsView extends React.Component<IProps, any> {
|
||||||
|
static navigationOptions = ({ navigation, isMasterDetail }: Partial<IProps>) => ({
|
||||||
headerLeft: () =>
|
headerLeft: () =>
|
||||||
isMasterDetail ? (
|
isMasterDetail ? (
|
||||||
<HeaderButton.CloseModal navigation={navigation} testID='settings-view-close' />
|
<HeaderButton.CloseModal navigation={navigation} testID='settings-view-close' />
|
||||||
|
@ -40,26 +57,12 @@ class SettingsView extends React.Component {
|
||||||
title: I18n.t('Settings')
|
title: I18n.t('Settings')
|
||||||
});
|
});
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
navigation: PropTypes.object,
|
|
||||||
server: PropTypes.object,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
isMasterDetail: PropTypes.bool,
|
|
||||||
logout: PropTypes.func.isRequired,
|
|
||||||
selectServerRequest: PropTypes.func,
|
|
||||||
user: PropTypes.shape({
|
|
||||||
roles: PropTypes.array,
|
|
||||||
id: PropTypes.string
|
|
||||||
}),
|
|
||||||
appStart: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
checkCookiesAndLogout = async () => {
|
checkCookiesAndLogout = async () => {
|
||||||
const { logout, user } = this.props;
|
const { logout, user } = this.props;
|
||||||
const db = database.servers;
|
const db = database.servers;
|
||||||
const usersCollection = db.get('users');
|
const usersCollection = db.get('users');
|
||||||
try {
|
try {
|
||||||
const userRecord = await usersCollection.find(user.id);
|
const userRecord: any = await usersCollection.find(user.id);
|
||||||
if (userRecord.isFromWebView) {
|
if (userRecord.isFromWebView) {
|
||||||
showConfirmationAlert({
|
showConfirmationAlert({
|
||||||
title: I18n.t('Clear_cookies_alert'),
|
title: I18n.t('Clear_cookies_alert'),
|
||||||
|
@ -84,6 +87,7 @@ class SettingsView extends React.Component {
|
||||||
|
|
||||||
handleLogout = () => {
|
handleLogout = () => {
|
||||||
logEvent(events.SE_LOG_OUT);
|
logEvent(events.SE_LOG_OUT);
|
||||||
|
// @ts-ignore
|
||||||
showConfirmationAlert({
|
showConfirmationAlert({
|
||||||
message: I18n.t('You_will_be_logged_out_of_this_application'),
|
message: I18n.t('You_will_be_logged_out_of_this_application'),
|
||||||
confirmationText: I18n.t('Logout'),
|
confirmationText: I18n.t('Logout'),
|
||||||
|
@ -93,6 +97,7 @@ class SettingsView extends React.Component {
|
||||||
|
|
||||||
handleClearCache = () => {
|
handleClearCache = () => {
|
||||||
logEvent(events.SE_CLEAR_LOCAL_SERVER_CACHE);
|
logEvent(events.SE_CLEAR_LOCAL_SERVER_CACHE);
|
||||||
|
/* @ts-ignore */
|
||||||
showConfirmationAlert({
|
showConfirmationAlert({
|
||||||
message: I18n.t('This_will_clear_all_your_offline_data'),
|
message: I18n.t('This_will_clear_all_your_offline_data'),
|
||||||
confirmationText: I18n.t('Clear'),
|
confirmationText: I18n.t('Clear'),
|
||||||
|
@ -112,7 +117,8 @@ class SettingsView extends React.Component {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
navigateToScreen = screen => {
|
navigateToScreen = (screen: string) => {
|
||||||
|
/* @ts-ignore */
|
||||||
logEvent(events[`SE_GO_${screen.replace('View', '').toUpperCase()}`]);
|
logEvent(events[`SE_GO_${screen.replace('View', '').toUpperCase()}`]);
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.navigate(screen);
|
navigation.navigate(screen);
|
||||||
|
@ -160,7 +166,7 @@ class SettingsView extends React.Component {
|
||||||
this.saveToClipboard(getReadableVersion);
|
this.saveToClipboard(getReadableVersion);
|
||||||
};
|
};
|
||||||
|
|
||||||
saveToClipboard = async content => {
|
saveToClipboard = async (content: string) => {
|
||||||
await Clipboard.setString(content);
|
await Clipboard.setString(content);
|
||||||
EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
|
EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
|
||||||
};
|
};
|
||||||
|
@ -293,16 +299,16 @@ class SettingsView extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: any) => ({
|
||||||
server: state.server,
|
server: state.server,
|
||||||
user: getUserSelector(state),
|
user: getUserSelector(state),
|
||||||
isMasterDetail: state.app.isMasterDetail
|
isMasterDetail: state.app.isMasterDetail
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = (dispatch: any) => ({
|
||||||
logout: () => dispatch(logoutAction()),
|
logout: () => dispatch(logoutAction()),
|
||||||
selectServerRequest: params => dispatch(selectServerRequestAction(params)),
|
selectServerRequest: (params: any) => dispatch(selectServerRequestAction(params)),
|
||||||
appStart: params => dispatch(appStartAction(params))
|
appStart: (params: any) => dispatch(appStartAction(params))
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(SettingsView));
|
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(SettingsView));
|
|
@ -1,46 +1,75 @@
|
||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { Switch } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { events, logEvent } from '../../utils/log';
|
import log, { logEvent, events } from '../../utils/log';
|
||||||
import SafeAreaView from '../../containers/SafeAreaView';
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
import StatusBar from '../../containers/StatusBar';
|
import StatusBar from '../../containers/StatusBar';
|
||||||
import * as List from '../../containers/List';
|
import * as List from '../../containers/List';
|
||||||
|
import { SWITCH_TRACK_COLOR } from '../../constants/colors';
|
||||||
|
import { getUserSelector } from '../../selectors/login';
|
||||||
|
import RocketChat from '../../lib/rocketchat';
|
||||||
|
|
||||||
class UserPreferencesView extends React.Component {
|
const UserPreferencesView = ({ navigation }) => {
|
||||||
static navigationOptions = () => ({
|
const user = useSelector(state => getUserSelector(state));
|
||||||
title: I18n.t('Preferences')
|
const [enableParser, setEnableParser] = useState(user.enableMessageParserEarlyAdoption);
|
||||||
});
|
|
||||||
|
|
||||||
static propTypes = {
|
useEffect(() => {
|
||||||
navigation: PropTypes.object
|
navigation.setOptions({
|
||||||
};
|
title: I18n.t('Preferences')
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
navigateToScreen = (screen, params) => {
|
const navigateToScreen = (screen, params) => {
|
||||||
logEvent(events[`SE_GO_${screen.replace('View', '').toUpperCase()}`]);
|
logEvent(events[`SE_GO_${screen.replace('View', '').toUpperCase()}`]);
|
||||||
const { navigation } = this.props;
|
|
||||||
navigation.navigate(screen, params);
|
navigation.navigate(screen, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
const toggleMessageParser = async value => {
|
||||||
return (
|
try {
|
||||||
<SafeAreaView testID='preferences-view'>
|
await RocketChat.saveUserPreferences({ id: user.id, enableMessageParserEarlyAdoption: value });
|
||||||
<StatusBar />
|
setEnableParser(value);
|
||||||
<List.Container>
|
} catch (e) {
|
||||||
<List.Section>
|
log(e);
|
||||||
<List.Separator />
|
}
|
||||||
<List.Item
|
};
|
||||||
title='Notifications'
|
|
||||||
onPress={() => this.navigateToScreen('UserNotificationPrefView')}
|
const renderMessageParserSwitch = () => (
|
||||||
showActionIndicator
|
<Switch value={enableParser} trackColor={SWITCH_TRACK_COLOR} onValueChange={toggleMessageParser} />
|
||||||
testID='preferences-view-notifications'
|
);
|
||||||
/>
|
|
||||||
<List.Separator />
|
return (
|
||||||
</List.Section>
|
<SafeAreaView testID='preferences-view'>
|
||||||
</List.Container>
|
<StatusBar />
|
||||||
</SafeAreaView>
|
<List.Container>
|
||||||
);
|
<List.Section>
|
||||||
}
|
<List.Separator />
|
||||||
}
|
<List.Item
|
||||||
|
title='Notifications'
|
||||||
|
onPress={() => navigateToScreen('UserNotificationPrefView')}
|
||||||
|
showActionIndicator
|
||||||
|
testID='preferences-view-notifications'
|
||||||
|
/>
|
||||||
|
<List.Separator />
|
||||||
|
</List.Section>
|
||||||
|
<List.Section>
|
||||||
|
<List.Separator />
|
||||||
|
<List.Item
|
||||||
|
title='Enable_Message_Parser'
|
||||||
|
testID='preferences-view-enable-message-parser'
|
||||||
|
right={() => renderMessageParserSwitch()}
|
||||||
|
/>
|
||||||
|
<List.Separator />
|
||||||
|
</List.Section>
|
||||||
|
</List.Container>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
UserPreferencesView.propTypes = {
|
||||||
|
navigation: PropTypes.object
|
||||||
|
};
|
||||||
|
|
||||||
export default UserPreferencesView;
|
export default UserPreferencesView;
|
||||||
|
|
135
ios/Podfile.lock
135
ios/Podfile.lock
|
@ -4,36 +4,40 @@ PODS:
|
||||||
- React-Core
|
- React-Core
|
||||||
- CocoaAsyncSocket (7.6.5)
|
- CocoaAsyncSocket (7.6.5)
|
||||||
- DoubleConversion (1.1.6)
|
- DoubleConversion (1.1.6)
|
||||||
- EXAppleAuthentication (2.2.1):
|
- EXAppleAuthentication (3.2.1):
|
||||||
- UMCore
|
- UMCore
|
||||||
- EXAV (8.2.1):
|
- EXAV (9.2.3):
|
||||||
|
- ExpoModulesCore
|
||||||
- UMCore
|
- UMCore
|
||||||
- UMFileSystemInterface
|
- EXConstants (11.0.2):
|
||||||
- UMPermissionsInterface
|
- ExpoModulesCore
|
||||||
- EXConstants (9.1.1):
|
|
||||||
- UMConstantsInterface
|
|
||||||
- UMCore
|
- UMCore
|
||||||
- EXFileSystem (9.0.1):
|
- EXFileSystem (11.1.3):
|
||||||
|
- ExpoModulesCore
|
||||||
- UMCore
|
- UMCore
|
||||||
- UMFileSystemInterface
|
- EXHaptics (10.1.0):
|
||||||
- EXHaptics (8.2.1):
|
|
||||||
- UMCore
|
- UMCore
|
||||||
- EXImageLoader (1.1.1):
|
- EXImageLoader (2.2.0):
|
||||||
|
- ExpoModulesCore
|
||||||
- React-Core
|
- React-Core
|
||||||
- UMCore
|
- UMCore
|
||||||
- UMImageLoaderInterface
|
- EXKeepAwake (9.2.0):
|
||||||
- EXKeepAwake (8.2.1):
|
|
||||||
- UMCore
|
- UMCore
|
||||||
- EXLocalAuthentication (9.2.0):
|
- EXLocalAuthentication (11.1.1):
|
||||||
- UMConstantsInterface
|
|
||||||
- UMCore
|
- UMCore
|
||||||
- EXPermissions (9.0.1):
|
- ExpoModulesCore (0.2.0):
|
||||||
|
- ExpoModulesCore/Core (= 0.2.0)
|
||||||
|
- ExpoModulesCore/Interfaces (= 0.2.0)
|
||||||
- UMCore
|
- UMCore
|
||||||
- UMPermissionsInterface
|
- ExpoModulesCore/Core (0.2.0):
|
||||||
- EXVideoThumbnails (5.1.0):
|
|
||||||
- UMCore
|
- UMCore
|
||||||
- UMFileSystemInterface
|
- ExpoModulesCore/Interfaces (0.2.0):
|
||||||
- EXWebBrowser (8.3.1):
|
- ExpoModulesCore/Core
|
||||||
|
- UMCore
|
||||||
|
- EXVideoThumbnails (5.2.1):
|
||||||
|
- ExpoModulesCore
|
||||||
|
- UMCore
|
||||||
|
- EXWebBrowser (9.2.0):
|
||||||
- UMCore
|
- UMCore
|
||||||
- FBLazyVector (0.64.2)
|
- FBLazyVector (0.64.2)
|
||||||
- FBReactNativeSpec (0.64.2):
|
- FBReactNativeSpec (0.64.2):
|
||||||
|
@ -584,23 +588,14 @@ PODS:
|
||||||
- SDWebImage/Core (~> 5.5)
|
- SDWebImage/Core (~> 5.5)
|
||||||
- simdjson (0.9.6-fix2)
|
- simdjson (0.9.6-fix2)
|
||||||
- TOCropViewController (2.5.3)
|
- TOCropViewController (2.5.3)
|
||||||
- UMAppLoader (1.2.0)
|
- UMAppLoader (2.2.0)
|
||||||
- UMBarCodeScannerInterface (5.2.1)
|
- UMCore (7.1.2)
|
||||||
- UMCameraInterface (5.2.1)
|
- UMReactNativeAdapter (6.3.9):
|
||||||
- UMConstantsInterface (5.2.1)
|
- ExpoModulesCore
|
||||||
- UMCore (5.3.0)
|
|
||||||
- UMFaceDetectorInterface (5.2.1)
|
|
||||||
- UMFileSystemInterface (5.2.1)
|
|
||||||
- UMFontInterface (5.2.1)
|
|
||||||
- UMImageLoaderInterface (5.2.1)
|
|
||||||
- UMPermissionsInterface (5.2.1):
|
|
||||||
- UMCore
|
|
||||||
- UMReactNativeAdapter (5.4.0):
|
|
||||||
- React-Core
|
- React-Core
|
||||||
- UMCore
|
- UMCore
|
||||||
- UMFontInterface
|
- UMTaskManagerInterface (6.2.0):
|
||||||
- UMSensorsInterface (5.2.1)
|
- UMCore
|
||||||
- UMTaskManagerInterface (5.2.1)
|
|
||||||
- WatermelonDB (0.23.0):
|
- WatermelonDB (0.23.0):
|
||||||
- React
|
- React
|
||||||
- React-jsi
|
- React-jsi
|
||||||
|
@ -619,7 +614,7 @@ DEPENDENCIES:
|
||||||
- EXImageLoader (from `../node_modules/expo-image-loader/ios`)
|
- EXImageLoader (from `../node_modules/expo-image-loader/ios`)
|
||||||
- EXKeepAwake (from `../node_modules/expo-keep-awake/ios`)
|
- EXKeepAwake (from `../node_modules/expo-keep-awake/ios`)
|
||||||
- EXLocalAuthentication (from `../node_modules/expo-local-authentication/ios`)
|
- EXLocalAuthentication (from `../node_modules/expo-local-authentication/ios`)
|
||||||
- EXPermissions (from `../node_modules/expo-permissions/ios`)
|
- ExpoModulesCore (from `../node_modules/expo-modules-core/ios`)
|
||||||
- EXVideoThumbnails (from `../node_modules/expo-video-thumbnails/ios`)
|
- EXVideoThumbnails (from `../node_modules/expo-video-thumbnails/ios`)
|
||||||
- EXWebBrowser (from `../node_modules/expo-web-browser/ios`)
|
- EXWebBrowser (from `../node_modules/expo-web-browser/ios`)
|
||||||
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
|
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
|
||||||
|
@ -714,17 +709,8 @@ DEPENDENCIES:
|
||||||
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
|
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
|
||||||
- "simdjson (from `../node_modules/@nozbe/simdjson`)"
|
- "simdjson (from `../node_modules/@nozbe/simdjson`)"
|
||||||
- UMAppLoader (from `../node_modules/unimodules-app-loader/ios`)
|
- UMAppLoader (from `../node_modules/unimodules-app-loader/ios`)
|
||||||
- UMBarCodeScannerInterface (from `../node_modules/unimodules-barcode-scanner-interface/ios`)
|
|
||||||
- UMCameraInterface (from `../node_modules/unimodules-camera-interface/ios`)
|
|
||||||
- UMConstantsInterface (from `../node_modules/unimodules-constants-interface/ios`)
|
|
||||||
- "UMCore (from `../node_modules/@unimodules/core/ios`)"
|
- "UMCore (from `../node_modules/@unimodules/core/ios`)"
|
||||||
- UMFaceDetectorInterface (from `../node_modules/unimodules-face-detector-interface/ios`)
|
|
||||||
- UMFileSystemInterface (from `../node_modules/unimodules-file-system-interface/ios`)
|
|
||||||
- UMFontInterface (from `../node_modules/unimodules-font-interface/ios`)
|
|
||||||
- UMImageLoaderInterface (from `../node_modules/unimodules-image-loader-interface/ios`)
|
|
||||||
- UMPermissionsInterface (from `../node_modules/unimodules-permissions-interface/ios`)
|
|
||||||
- "UMReactNativeAdapter (from `../node_modules/@unimodules/react-native-adapter/ios`)"
|
- "UMReactNativeAdapter (from `../node_modules/@unimodules/react-native-adapter/ios`)"
|
||||||
- UMSensorsInterface (from `../node_modules/unimodules-sensors-interface/ios`)
|
|
||||||
- UMTaskManagerInterface (from `../node_modules/unimodules-task-manager-interface/ios`)
|
- UMTaskManagerInterface (from `../node_modules/unimodules-task-manager-interface/ios`)
|
||||||
- "WatermelonDB (from `../node_modules/@nozbe/watermelondb`)"
|
- "WatermelonDB (from `../node_modules/@nozbe/watermelondb`)"
|
||||||
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
|
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
|
||||||
|
@ -784,8 +770,8 @@ EXTERNAL SOURCES:
|
||||||
:path: "../node_modules/expo-keep-awake/ios"
|
:path: "../node_modules/expo-keep-awake/ios"
|
||||||
EXLocalAuthentication:
|
EXLocalAuthentication:
|
||||||
:path: "../node_modules/expo-local-authentication/ios"
|
:path: "../node_modules/expo-local-authentication/ios"
|
||||||
EXPermissions:
|
ExpoModulesCore:
|
||||||
:path: "../node_modules/expo-permissions/ios"
|
:path: "../node_modules/expo-modules-core/ios"
|
||||||
EXVideoThumbnails:
|
EXVideoThumbnails:
|
||||||
:path: "../node_modules/expo-video-thumbnails/ios"
|
:path: "../node_modules/expo-video-thumbnails/ios"
|
||||||
EXWebBrowser:
|
EXWebBrowser:
|
||||||
|
@ -926,28 +912,10 @@ EXTERNAL SOURCES:
|
||||||
:path: "../node_modules/@nozbe/simdjson"
|
:path: "../node_modules/@nozbe/simdjson"
|
||||||
UMAppLoader:
|
UMAppLoader:
|
||||||
:path: "../node_modules/unimodules-app-loader/ios"
|
:path: "../node_modules/unimodules-app-loader/ios"
|
||||||
UMBarCodeScannerInterface:
|
|
||||||
:path: "../node_modules/unimodules-barcode-scanner-interface/ios"
|
|
||||||
UMCameraInterface:
|
|
||||||
:path: "../node_modules/unimodules-camera-interface/ios"
|
|
||||||
UMConstantsInterface:
|
|
||||||
:path: "../node_modules/unimodules-constants-interface/ios"
|
|
||||||
UMCore:
|
UMCore:
|
||||||
:path: "../node_modules/@unimodules/core/ios"
|
:path: "../node_modules/@unimodules/core/ios"
|
||||||
UMFaceDetectorInterface:
|
|
||||||
:path: "../node_modules/unimodules-face-detector-interface/ios"
|
|
||||||
UMFileSystemInterface:
|
|
||||||
:path: "../node_modules/unimodules-file-system-interface/ios"
|
|
||||||
UMFontInterface:
|
|
||||||
:path: "../node_modules/unimodules-font-interface/ios"
|
|
||||||
UMImageLoaderInterface:
|
|
||||||
:path: "../node_modules/unimodules-image-loader-interface/ios"
|
|
||||||
UMPermissionsInterface:
|
|
||||||
:path: "../node_modules/unimodules-permissions-interface/ios"
|
|
||||||
UMReactNativeAdapter:
|
UMReactNativeAdapter:
|
||||||
:path: "../node_modules/@unimodules/react-native-adapter/ios"
|
:path: "../node_modules/@unimodules/react-native-adapter/ios"
|
||||||
UMSensorsInterface:
|
|
||||||
:path: "../node_modules/unimodules-sensors-interface/ios"
|
|
||||||
UMTaskManagerInterface:
|
UMTaskManagerInterface:
|
||||||
:path: "../node_modules/unimodules-task-manager-interface/ios"
|
:path: "../node_modules/unimodules-task-manager-interface/ios"
|
||||||
WatermelonDB:
|
WatermelonDB:
|
||||||
|
@ -965,17 +933,17 @@ SPEC CHECKSUMS:
|
||||||
BugsnagReactNative: a97b3132c1854fd7bf92350fabd505e3ebdd7829
|
BugsnagReactNative: a97b3132c1854fd7bf92350fabd505e3ebdd7829
|
||||||
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
||||||
DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de
|
DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de
|
||||||
EXAppleAuthentication: 5b3da71bada29e2423d8ea27e5538ef0d75aba62
|
EXAppleAuthentication: e8c537fcbe80670dd76fde7a07acb94af70ada00
|
||||||
EXAV: 86344030966e0da7e00556fbb97269d9ad16071d
|
EXAV: 67bcc1d0afeb1fab854b206c84b9f2afbd61d0cd
|
||||||
EXConstants: f907b3b6ce16e20d1750f22af1e095e924574bcb
|
EXConstants: 4cb52b6d8f636c767104a44bf7db3873e9c01a6f
|
||||||
EXFileSystem: 76875135b61708b9afa7e6a89b72a60ba0fdfa20
|
EXFileSystem: 0a04aba8da751b9ac954065911bcf166503f8267
|
||||||
EXHaptics: 5428b344a216ca5d9df6ca8f65720b2a1ad9f109
|
EXHaptics: 6dc4307ab0794fe7a87ec8d7d1c299cf103d6cb3
|
||||||
EXImageLoader: 02ca02c9cd5cc8a97b423207a73a791e0a86bea5
|
EXImageLoader: d3531a3fe530b22925c19977cb53bb43e3821fe6
|
||||||
EXKeepAwake: 8b0f68242f036b971f9f8976341823cbe6f50812
|
EXKeepAwake: f4105ef469be7b283f66ce2d7234bb71ac80cd26
|
||||||
EXLocalAuthentication: 985c65e08a6eb84f8f98b51f7435df138b18b9e8
|
EXLocalAuthentication: 88a1a69ea66c4934387d1eb503628170c853caef
|
||||||
EXPermissions: 80ac3acbdb145930079810fe5b08c022b3428aa8
|
ExpoModulesCore: 2734852616127a6c1fc23012197890a6f3763dc7
|
||||||
EXVideoThumbnails: cd257fc6e07884a704a5674d362a6410933acb68
|
EXVideoThumbnails: 442c3abadb51a81551a3b53705b7560de390e6f7
|
||||||
EXWebBrowser: d37a5ffdea1b65947352bc001dd9f732463725d4
|
EXWebBrowser: 76783ba5dcb8699237746ecf41a9643d428a4cc5
|
||||||
FBLazyVector: e686045572151edef46010a6f819ade377dfeb4b
|
FBLazyVector: e686045572151edef46010a6f819ade377dfeb4b
|
||||||
FBReactNativeSpec: 110d69378fce79af38271c39894b59fec7890221
|
FBReactNativeSpec: 110d69378fce79af38271c39894b59fec7890221
|
||||||
Firebase: 919186c8e119dd9372a45fd1dd17a8a942bc1892
|
Firebase: 919186c8e119dd9372a45fd1dd17a8a942bc1892
|
||||||
|
@ -1071,19 +1039,10 @@ SPEC CHECKSUMS:
|
||||||
SDWebImageWebPCoder: 36f8f47bd9879a8aea6044765c1351120fd8e3a8
|
SDWebImageWebPCoder: 36f8f47bd9879a8aea6044765c1351120fd8e3a8
|
||||||
simdjson: 85016870cd17207312b718ef6652eb6a1cd6a2b0
|
simdjson: 85016870cd17207312b718ef6652eb6a1cd6a2b0
|
||||||
TOCropViewController: 20a14b6a7a098308bf369e7c8d700dc983a974e6
|
TOCropViewController: 20a14b6a7a098308bf369e7c8d700dc983a974e6
|
||||||
UMAppLoader: 61049c8d55590b74e9ae1d5429bf68d96b4a2528
|
UMAppLoader: 21af63390e55c82e037fb9752d93114a80ecf16e
|
||||||
UMBarCodeScannerInterface: e5e4c87797d3d01214e25cd1618866caf5d4f17f
|
UMCore: ce3a4faa010239063b8343895b29a6d97b01069d
|
||||||
UMCameraInterface: 415ac060034edecacdbbaa739c223e3f276e0056
|
UMReactNativeAdapter: d03cefd0e4e4179ab8c490408589f1c8a6c8b785
|
||||||
UMConstantsInterface: 1a52f2d884c95e8829439da13e36b7669a1a8fb4
|
UMTaskManagerInterface: 2be431101b73604e64fbfffcf759336f9d8fccbb
|
||||||
UMCore: d98083b522b08c0a8ba3992bc263c624ae5d887c
|
|
||||||
UMFaceDetectorInterface: 67c6c82451338da01a4bc00ec46365a2a8ea9057
|
|
||||||
UMFileSystemInterface: 303d696ede28102a7e11d111808bd2ed2c5eb62f
|
|
||||||
UMFontInterface: 6edf1ee8bc55d2030766f8cf0a7b20a5d5a913b0
|
|
||||||
UMImageLoaderInterface: 9cdbf3bab6a513bddd88505cb2340fe02d6a11c0
|
|
||||||
UMPermissionsInterface: 019170ad655f464e3f8d23d2a8bcbda2e645cde4
|
|
||||||
UMReactNativeAdapter: 538efe92e781b5d7678cf95b34c46f2d0989a557
|
|
||||||
UMSensorsInterface: cb5bf31d52c4349f0ff9e3c049bbe4df0d80d383
|
|
||||||
UMTaskManagerInterface: 80653f25c55d9e6d79d6a0a65589fa213feaee11
|
|
||||||
WatermelonDB: 577c61fceff16e9f9103b59d14aee4850c0307b6
|
WatermelonDB: 577c61fceff16e9f9103b59d14aee4850c0307b6
|
||||||
Yoga: 575c581c63e0d35c9a83f4b46d01d63abc1100ac
|
Yoga: 575c581c63e0d35c9a83f4b46d01d63abc1100ac
|
||||||
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
|
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
|
||||||
|
|
20
package.json
20
package.json
|
@ -47,23 +47,25 @@
|
||||||
"@react-navigation/drawer": "5.12.5",
|
"@react-navigation/drawer": "5.12.5",
|
||||||
"@react-navigation/native": "5.9.4",
|
"@react-navigation/native": "5.9.4",
|
||||||
"@react-navigation/stack": "5.14.5",
|
"@react-navigation/stack": "5.14.5",
|
||||||
|
"@rocket.chat/message-parser": "0.30.0",
|
||||||
"@rocket.chat/react-native-fast-image": "^8.2.0",
|
"@rocket.chat/react-native-fast-image": "^8.2.0",
|
||||||
"@rocket.chat/sdk": "RocketChat/Rocket.Chat.js.SDK#mobile",
|
"@rocket.chat/sdk": "RocketChat/Rocket.Chat.js.SDK#mobile",
|
||||||
"@rocket.chat/ui-kit": "0.13.0",
|
"@rocket.chat/ui-kit": "0.13.0",
|
||||||
|
"@types/url-parse": "^1.4.4",
|
||||||
"bytebuffer": "^5.0.1",
|
"bytebuffer": "^5.0.1",
|
||||||
"color2k": "1.2.4",
|
"color2k": "1.2.4",
|
||||||
"commonmark": "git+https://github.com/RocketChat/commonmark.js.git",
|
"commonmark": "git+https://github.com/RocketChat/commonmark.js.git",
|
||||||
"commonmark-react-renderer": "git+https://github.com/RocketChat/commonmark-react-renderer.git",
|
"commonmark-react-renderer": "git+https://github.com/RocketChat/commonmark-react-renderer.git",
|
||||||
"dequal": "^2.0.2",
|
"dequal": "^2.0.2",
|
||||||
"ejson": "2.2.1",
|
"ejson": "2.2.1",
|
||||||
"expo-apple-authentication": "^2.2.1",
|
"expo-apple-authentication": "3.2.1",
|
||||||
"expo-av": "8.2.1",
|
"expo-av": "9.2.3",
|
||||||
"expo-file-system": "9.0.1",
|
"expo-file-system": "11.1.3",
|
||||||
"expo-haptics": "8.2.1",
|
"expo-haptics": "10.1.0",
|
||||||
"expo-keep-awake": "8.2.1",
|
"expo-keep-awake": "9.2.0",
|
||||||
"expo-local-authentication": "9.2.0",
|
"expo-local-authentication": "11.1.1",
|
||||||
"expo-video-thumbnails": "5.1.0",
|
"expo-video-thumbnails": "5.2.1",
|
||||||
"expo-web-browser": "8.3.1",
|
"expo-web-browser": "9.2.0",
|
||||||
"hoist-non-react-statics": "3.3.2",
|
"hoist-non-react-statics": "3.3.2",
|
||||||
"i18n-js": "3.8.0",
|
"i18n-js": "3.8.0",
|
||||||
"js-base64": "3.6.1",
|
"js-base64": "3.6.1",
|
||||||
|
@ -112,7 +114,7 @@
|
||||||
"react-native-simple-crypto": "RocketChat/react-native-simple-crypto#0.5.0",
|
"react-native-simple-crypto": "RocketChat/react-native-simple-crypto#0.5.0",
|
||||||
"react-native-slowlog": "^1.0.2",
|
"react-native-slowlog": "^1.0.2",
|
||||||
"react-native-ui-lib": "RocketChat/react-native-ui-lib#minor-improvements",
|
"react-native-ui-lib": "RocketChat/react-native-ui-lib#minor-improvements",
|
||||||
"react-native-unimodules": "0.10.1",
|
"react-native-unimodules": "^0.14.8",
|
||||||
"react-native-vector-icons": "8.1.0",
|
"react-native-vector-icons": "8.1.0",
|
||||||
"react-native-webview": "10.3.2",
|
"react-native-webview": "10.3.2",
|
||||||
"react-redux": "7.2.4",
|
"react-redux": "7.2.4",
|
||||||
|
|
|
@ -0,0 +1,627 @@
|
||||||
|
/* eslint-disable import/no-extraneous-dependencies */
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleSheet, View } from 'react-native';
|
||||||
|
import { storiesOf } from '@storybook/react-native';
|
||||||
|
|
||||||
|
import NewMarkdown from '../../app/containers/markdown/new';
|
||||||
|
import { themes } from '../../app/constants/colors';
|
||||||
|
|
||||||
|
const stories = storiesOf('NewMarkdown', module);
|
||||||
|
|
||||||
|
const theme = 'light';
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
marginHorizontal: 15,
|
||||||
|
backgroundColor: themes[theme].backgroundColor,
|
||||||
|
marginVertical: 50
|
||||||
|
},
|
||||||
|
separator: {
|
||||||
|
marginHorizontal: 10,
|
||||||
|
marginVertical: 10
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const getCustomEmoji = content => {
|
||||||
|
const customEmoji = {
|
||||||
|
marioparty: { name: content, extension: 'gif' },
|
||||||
|
nyan_rocket: { name: content, extension: 'png' }
|
||||||
|
}[content];
|
||||||
|
return customEmoji;
|
||||||
|
};
|
||||||
|
const baseUrl = 'https://open.rocket.chat';
|
||||||
|
|
||||||
|
const simpleTextMsg = [
|
||||||
|
{
|
||||||
|
type: 'PARAGRAPH',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'This is Rocket.Chat'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const longTextMsg = [
|
||||||
|
{
|
||||||
|
type: 'PARAGRAPH',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value:
|
||||||
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const lineBreakMsg = [
|
||||||
|
{
|
||||||
|
type: 'PARAGRAPH',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'a'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'b'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'c'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'd'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'e'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const sequentialEmptySpacesMsg = [
|
||||||
|
{
|
||||||
|
type: 'PARAGRAPH',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'a b c'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const boldOrUnderscoreMsg = [
|
||||||
|
{
|
||||||
|
type: 'PARAGRAPH',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'BOLD',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'This is bold'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: ' and '
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'ITALIC',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'this is italic'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
stories.add('Text', () => (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<NewMarkdown tokens={simpleTextMsg} />
|
||||||
|
<NewMarkdown tokens={longTextMsg} />
|
||||||
|
<NewMarkdown tokens={lineBreakMsg} />
|
||||||
|
<NewMarkdown tokens={sequentialEmptySpacesMsg} />
|
||||||
|
<NewMarkdown tokens={boldOrUnderscoreMsg} />
|
||||||
|
</View>
|
||||||
|
));
|
||||||
|
|
||||||
|
const allMentionTokens = [
|
||||||
|
{
|
||||||
|
type: 'PARAGRAPH',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'MENTION_USER',
|
||||||
|
value: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'rocket.cat'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const multipleMentionTokens = [
|
||||||
|
{
|
||||||
|
type: 'PARAGRAPH',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'MENTION_USER',
|
||||||
|
value: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'name'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: ' '
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'MENTION_USER',
|
||||||
|
value: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'rocket.cat'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: ' '
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'MENTION_USER',
|
||||||
|
value: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'here'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: ' '
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'MENTION_USER',
|
||||||
|
value: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'all'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const allMentions = [
|
||||||
|
{
|
||||||
|
_id: 'rocket.cat',
|
||||||
|
username: 'rocket.cat'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const multipleMentions = [
|
||||||
|
{
|
||||||
|
_id: 'name',
|
||||||
|
username: 'name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: 'rocket.cat',
|
||||||
|
username: 'rocket.cat'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: 'here',
|
||||||
|
username: 'here'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: 'all',
|
||||||
|
username: 'all'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
stories.add('Mentions', () => (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<NewMarkdown tokens={allMentionTokens} mentions={allMentions} navToRoomInfo={() => {}} style={[]} />
|
||||||
|
<NewMarkdown
|
||||||
|
tokens={multipleMentionTokens}
|
||||||
|
mentions={multipleMentions}
|
||||||
|
navToRoomInfo={() => {}}
|
||||||
|
style={[]}
|
||||||
|
username='rocket.cat'
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
));
|
||||||
|
|
||||||
|
const channelTokens = [
|
||||||
|
{
|
||||||
|
type: 'PARAGRAPH',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'MENTION_CHANNEL',
|
||||||
|
value: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'text_channel'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: ' and '
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'MENTION_CHANNEL',
|
||||||
|
value: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'not_a_channel'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const channelMention = [
|
||||||
|
{
|
||||||
|
_id: 'text_channel',
|
||||||
|
name: 'text_channel'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
stories.add('Hashtag', () => (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<NewMarkdown tokens={channelTokens} channels={channelMention} navToRoomInfo={() => {}} />
|
||||||
|
</View>
|
||||||
|
));
|
||||||
|
|
||||||
|
const bigEmojiTokens = [
|
||||||
|
{
|
||||||
|
type: 'BIG_EMOJI',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'EMOJI',
|
||||||
|
value: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'green_heart'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'EMOJI',
|
||||||
|
value: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'joy'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'EMOJI',
|
||||||
|
value: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'grin'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const emojiTokens = [
|
||||||
|
{
|
||||||
|
type: 'PARAGRAPH',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'EMOJI',
|
||||||
|
value: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'rocket'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'EMOJI',
|
||||||
|
value: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'facepalm'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'EMOJI',
|
||||||
|
value: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'nyan_rocket'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'EMOJI',
|
||||||
|
value: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'marioparty'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
stories.add('Emoji', () => (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<NewMarkdown tokens={bigEmojiTokens} />
|
||||||
|
<NewMarkdown tokens={emojiTokens} getCustomEmoji={getCustomEmoji} baseUrl={baseUrl} />
|
||||||
|
</View>
|
||||||
|
));
|
||||||
|
|
||||||
|
const blockQuoteTokens = [
|
||||||
|
{
|
||||||
|
type: 'QUOTE',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'PARAGRAPH',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'Rocket.Chat to the moon'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
stories.add('Block quote', () => (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<NewMarkdown tokens={blockQuoteTokens} />
|
||||||
|
</View>
|
||||||
|
));
|
||||||
|
|
||||||
|
const rocketChatLink = [
|
||||||
|
{
|
||||||
|
type: 'PARAGRAPH',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'LINK',
|
||||||
|
value: {
|
||||||
|
src: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'https://rocket.chat'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'https://rocket.chat'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const markdownLink = [
|
||||||
|
{
|
||||||
|
type: 'PARAGRAPH',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'LINK',
|
||||||
|
value: {
|
||||||
|
src: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'https://rocket.chat'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'Markdown link'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
stories.add('Links', () => (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<NewMarkdown tokens={rocketChatLink} />
|
||||||
|
<NewMarkdown tokens={markdownLink} />
|
||||||
|
</View>
|
||||||
|
));
|
||||||
|
|
||||||
|
stories.add('Headers', () => (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<NewMarkdown
|
||||||
|
tokens={[
|
||||||
|
{
|
||||||
|
type: 'HEADING',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: '# Header 1'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
level: 1
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<NewMarkdown
|
||||||
|
tokens={[
|
||||||
|
{
|
||||||
|
type: 'HEADING',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: '## Header 2'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
level: 2
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<NewMarkdown
|
||||||
|
tokens={[
|
||||||
|
{
|
||||||
|
type: 'HEADING',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: '### Header 3'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
level: 3
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<NewMarkdown
|
||||||
|
tokens={[
|
||||||
|
{
|
||||||
|
type: 'HEADING',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: '#### Header 4'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
level: 4
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<NewMarkdown
|
||||||
|
tokens={[
|
||||||
|
{
|
||||||
|
type: 'HEADING',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: '##### Header 5'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
level: 5
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<NewMarkdown
|
||||||
|
tokens={[
|
||||||
|
{
|
||||||
|
type: 'HEADING',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: '###### Header 6'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
level: 6
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
));
|
||||||
|
|
||||||
|
const inlineCodeToken = [
|
||||||
|
{
|
||||||
|
type: 'PARAGRAPH',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'INLINE_CODE',
|
||||||
|
value: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'inline code'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const multilineCodeToken = [
|
||||||
|
{
|
||||||
|
type: 'CODE',
|
||||||
|
language: 'none',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'CODE_LINE',
|
||||||
|
value: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'Multi line '
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'CODE_LINE',
|
||||||
|
value: {
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'Code'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
stories.add('Code', () => (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<NewMarkdown tokens={inlineCodeToken} style={[]} />
|
||||||
|
<NewMarkdown tokens={multilineCodeToken} style={[]} />
|
||||||
|
</View>
|
||||||
|
));
|
||||||
|
|
||||||
|
const unorederedListToken = [
|
||||||
|
{
|
||||||
|
type: 'UNORDERED_LIST',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'LIST_ITEM',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'Open Source'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'LIST_ITEM',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'Rocket.Chat'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const orderedListToken = [
|
||||||
|
{
|
||||||
|
type: 'ORDERED_LIST',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'LIST_ITEM',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'Open Source'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'LIST_ITEM',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'PLAIN_TEXT',
|
||||||
|
value: 'Rocket.Chat'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
stories.add('Lists', () => (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<NewMarkdown tokens={unorederedListToken} />
|
||||||
|
<NewMarkdown tokens={orderedListToken} />
|
||||||
|
</View>
|
||||||
|
));
|
|
@ -12,6 +12,7 @@ import './HeaderButtons';
|
||||||
import './UnreadBadge';
|
import './UnreadBadge';
|
||||||
import '../../app/views/ThreadMessagesView/Item.stories.js';
|
import '../../app/views/ThreadMessagesView/Item.stories.js';
|
||||||
import './Avatar';
|
import './Avatar';
|
||||||
|
import './NewMarkdown';
|
||||||
import '../../app/containers/BackgroundContainer/index.stories.js';
|
import '../../app/containers/BackgroundContainer/index.stories.js';
|
||||||
import '../../app/containers/RoomHeader/RoomHeader.stories.js';
|
import '../../app/containers/RoomHeader/RoomHeader.stories.js';
|
||||||
import '../../app/views/RoomView/LoadMore/LoadMore.stories';
|
import '../../app/views/RoomView/LoadMore/LoadMore.stories';
|
||||||
|
|
Loading…
Reference in New Issue