[NEW] Redesign quoted messages (#3883)
This commit is contained in:
parent
82acb917fc
commit
b0580bf547
|
@ -37,6 +37,7 @@ export const themes: any = {
|
||||||
infoText: '#6d6d72',
|
infoText: '#6d6d72',
|
||||||
tintColor: '#1d74f5',
|
tintColor: '#1d74f5',
|
||||||
tintActive: '#549df9',
|
tintActive: '#549df9',
|
||||||
|
tintDisabled: '#88B4F5',
|
||||||
auxiliaryTintColor: '#6C727A',
|
auxiliaryTintColor: '#6C727A',
|
||||||
actionTintColor: '#1d74f5',
|
actionTintColor: '#1d74f5',
|
||||||
separatorColor: '#cbcbcc',
|
separatorColor: '#cbcbcc',
|
||||||
|
@ -87,6 +88,7 @@ export const themes: any = {
|
||||||
infoText: '#6D6D72',
|
infoText: '#6D6D72',
|
||||||
tintColor: '#1d74f5',
|
tintColor: '#1d74f5',
|
||||||
tintActive: '#549df9',
|
tintActive: '#549df9',
|
||||||
|
tintDisabled: '#88B4F5',
|
||||||
auxiliaryTintColor: '#f9f9f9',
|
auxiliaryTintColor: '#f9f9f9',
|
||||||
actionTintColor: '#1d74f5',
|
actionTintColor: '#1d74f5',
|
||||||
separatorColor: '#2b2b2d',
|
separatorColor: '#2b2b2d',
|
||||||
|
@ -137,6 +139,7 @@ export const themes: any = {
|
||||||
infoText: '#6d6d72',
|
infoText: '#6d6d72',
|
||||||
tintColor: '#1e9bfe',
|
tintColor: '#1e9bfe',
|
||||||
tintActive: '#76b7fc',
|
tintActive: '#76b7fc',
|
||||||
|
tintDisabled: '#88B4F5', // TODO: Evaluate this with design team
|
||||||
auxiliaryTintColor: '#f9f9f9',
|
auxiliaryTintColor: '#f9f9f9',
|
||||||
actionTintColor: '#1e9bfe',
|
actionTintColor: '#1e9bfe',
|
||||||
separatorColor: '#272728',
|
separatorColor: '#272728',
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
import { ScrollView, Text, TouchableOpacity, View, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
import { CELL_WIDTH } from './TableCell';
|
import { CELL_WIDTH } from './TableCell';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
@ -19,7 +19,7 @@ const Table = React.memo(({ children, numColumns, theme }: ITable) => {
|
||||||
const getTableWidth = () => numColumns * CELL_WIDTH;
|
const getTableWidth = () => numColumns * CELL_WIDTH;
|
||||||
|
|
||||||
const renderRows = (drawExtraBorders = true) => {
|
const renderRows = (drawExtraBorders = true) => {
|
||||||
const tableStyle = [styles.table, { borderColor: themes[theme].borderColor }];
|
const tableStyle: ViewStyle[] = [styles.table, { borderColor: themes[theme].borderColor }];
|
||||||
if (drawExtraBorders) {
|
if (drawExtraBorders) {
|
||||||
tableStyle.push(styles.tableExtraBorders);
|
tableStyle.push(styles.tableExtraBorders);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, View } from 'react-native';
|
import { Text, View, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
@ -14,7 +14,7 @@ interface ITableCell {
|
||||||
export const CELL_WIDTH = 100;
|
export const CELL_WIDTH = 100;
|
||||||
|
|
||||||
const TableCell = React.memo(({ isLastCell, align, children, theme }: ITableCell) => {
|
const TableCell = React.memo(({ isLastCell, align, children, theme }: ITableCell) => {
|
||||||
const cellStyle = [styles.cell, { borderColor: themes[theme].borderColor }];
|
const cellStyle: ViewStyle[] = [styles.cell, { borderColor: themes[theme].borderColor }];
|
||||||
if (!isLastCell) {
|
if (!isLastCell) {
|
||||||
cellStyle.push(styles.cellRightBorder);
|
cellStyle.push(styles.cellRightBorder);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View } from 'react-native';
|
import { View, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
@ -11,7 +11,7 @@ interface ITableRow {
|
||||||
}
|
}
|
||||||
|
|
||||||
const TableRow = React.memo(({ isLastRow, children: _children, theme }: ITableRow) => {
|
const TableRow = React.memo(({ isLastRow, children: _children, theme }: ITableRow) => {
|
||||||
const rowStyle = [styles.row, { borderColor: themes[theme].borderColor }];
|
const rowStyle: ViewStyle[] = [styles.row, { borderColor: themes[theme].borderColor }];
|
||||||
if (!isLastRow) {
|
if (!isLastRow) {
|
||||||
rowStyle.push(styles.rowBottomBorder);
|
rowStyle.push(styles.rowBottomBorder);
|
||||||
}
|
}
|
||||||
|
|
|
@ -280,6 +280,7 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
|
|
||||||
renderHeading = ({ children, level }: any) => {
|
renderHeading = ({ children, level }: any) => {
|
||||||
const { numberOfLines, theme } = this.props;
|
const { numberOfLines, theme } = this.props;
|
||||||
|
// @ts-ignore
|
||||||
const textStyle = styles[`heading${level}Text`];
|
const textStyle = styles[`heading${level}Text`];
|
||||||
return (
|
return (
|
||||||
<Text numberOfLines={numberOfLines} style={[textStyle, { color: themes[theme].bodyText }]}>
|
<Text numberOfLines={numberOfLines} style={[textStyle, { color: themes[theme].bodyText }]}>
|
||||||
|
|
|
@ -7,7 +7,7 @@ const codeFontFamily = Platform.select({
|
||||||
android: { fontFamily: 'monospace' }
|
android: { fontFamily: 'monospace' }
|
||||||
});
|
});
|
||||||
|
|
||||||
export default StyleSheet.create<any>({
|
export default StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
alignItems: 'flex-start',
|
alignItems: 'flex-start',
|
||||||
flexDirection: 'row'
|
flexDirection: 'row'
|
||||||
|
|
|
@ -10,10 +10,16 @@ import Reply from './Reply';
|
||||||
import Button from '../Button';
|
import Button from '../Button';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import MessageContext from './Context';
|
import MessageContext from './Context';
|
||||||
|
import { useTheme } from '../../theme';
|
||||||
|
import { IAttachment } from '../../definitions';
|
||||||
import CollapsibleQuote from './Components/CollapsibleQuote';
|
import CollapsibleQuote from './Components/CollapsibleQuote';
|
||||||
|
|
||||||
const AttachedActions = ({ attachment, theme }: IMessageAttachedActions) => {
|
const AttachedActions = ({ attachment }: IMessageAttachedActions) => {
|
||||||
|
if (!attachment.actions) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const { onAnswerButtonPress } = useContext(MessageContext);
|
const { onAnswerButtonPress } = useContext(MessageContext);
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
const attachedButtons = attachment.actions.map((element: { type: string; msg: string; text: string }) => {
|
const attachedButtons = attachment.actions.map((element: { type: string; msg: string; text: string }) => {
|
||||||
if (element.type === 'button') {
|
if (element.type === 'button') {
|
||||||
|
@ -30,46 +36,62 @@ const AttachedActions = ({ attachment, theme }: IMessageAttachedActions) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Attachments = React.memo(
|
const Attachments = React.memo(
|
||||||
({ attachments, timeFormat, showAttachment, getCustomEmoji, theme }: IMessageAttachments) => {
|
// @ts-ignore
|
||||||
|
({ attachments, timeFormat, showAttachment, style, getCustomEmoji, isReply }: IMessageAttachments) => {
|
||||||
if (!attachments || attachments.length === 0) {
|
if (!attachments || attachments.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return attachments.map((file: any, index: number) => {
|
const { theme } = useTheme();
|
||||||
if (file.image_url) {
|
|
||||||
|
return attachments.map((file: IAttachment, index: number) => {
|
||||||
|
if (file && file.image_url) {
|
||||||
return (
|
return (
|
||||||
<Image key={file.image_url} file={file} showAttachment={showAttachment} getCustomEmoji={getCustomEmoji} theme={theme} />
|
<Image
|
||||||
|
key={file.image_url}
|
||||||
|
file={file}
|
||||||
|
showAttachment={showAttachment}
|
||||||
|
getCustomEmoji={getCustomEmoji}
|
||||||
|
style={style}
|
||||||
|
isReply={isReply}
|
||||||
|
theme={theme}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (file.audio_url) {
|
|
||||||
return <Audio key={file.audio_url} file={file} getCustomEmoji={getCustomEmoji} theme={theme} />;
|
if (file && file.audio_url) {
|
||||||
|
return (
|
||||||
|
<Audio key={file.audio_url} file={file} getCustomEmoji={getCustomEmoji} isReply={isReply} style={style} theme={theme} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.video_url) {
|
if (file.video_url) {
|
||||||
return (
|
return (
|
||||||
<Video key={file.video_url} file={file} showAttachment={showAttachment} getCustomEmoji={getCustomEmoji} theme={theme} />
|
<Video
|
||||||
|
key={file.video_url}
|
||||||
|
file={file}
|
||||||
|
showAttachment={showAttachment}
|
||||||
|
getCustomEmoji={getCustomEmoji}
|
||||||
|
style={style}
|
||||||
|
isReply={isReply}
|
||||||
|
theme={theme}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (file.actions && file.actions.length > 0) {
|
|
||||||
return <AttachedActions attachment={file} theme={theme} />;
|
if (file && file.actions && file.actions.length > 0) {
|
||||||
|
return <AttachedActions attachment={file} />;
|
||||||
}
|
}
|
||||||
if (file.title)
|
if (file.title) {
|
||||||
return (
|
return (
|
||||||
<CollapsibleQuote key={index} index={index} attachment={file} timeFormat={timeFormat} getCustomEmoji={getCustomEmoji} />
|
<CollapsibleQuote key={index} index={index} attachment={file} timeFormat={timeFormat} getCustomEmoji={getCustomEmoji} />
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return <Reply key={index} index={index} attachment={file} timeFormat={timeFormat} getCustomEmoji={getCustomEmoji} />;
|
||||||
<Reply
|
|
||||||
key={index}
|
|
||||||
index={index}
|
|
||||||
attachment={file}
|
|
||||||
timeFormat={timeFormat}
|
|
||||||
getCustomEmoji={getCustomEmoji}
|
|
||||||
theme={theme}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
(prevProps, nextProps) => dequal(prevProps.attachments, nextProps.attachments) && prevProps.theme === nextProps.theme
|
(prevProps, nextProps) => dequal(prevProps.attachments, nextProps.attachments)
|
||||||
);
|
);
|
||||||
|
|
||||||
Attachments.displayName = 'MessageAttachments';
|
Attachments.displayName = 'MessageAttachments';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Easing, StyleSheet, Text, View } from 'react-native';
|
import { Easing, StyleProp, StyleSheet, Text, TextStyle, View } from 'react-native';
|
||||||
import { Audio } from 'expo-av';
|
import { Audio } from 'expo-av';
|
||||||
import Slider from '@react-native-community/slider';
|
import Slider from '@react-native-community/slider';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
@ -16,19 +16,20 @@ import MessageContext from './Context';
|
||||||
import ActivityIndicator from '../ActivityIndicator';
|
import ActivityIndicator from '../ActivityIndicator';
|
||||||
import { withDimensions } from '../../dimensions';
|
import { withDimensions } from '../../dimensions';
|
||||||
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
import { IAttachment } from '../../definitions';
|
||||||
|
|
||||||
interface IButton {
|
interface IButton {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
paused: boolean;
|
paused: boolean;
|
||||||
theme: string;
|
theme: string;
|
||||||
|
disabled?: boolean;
|
||||||
onPress: Function;
|
onPress: Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMessageAudioProps {
|
interface IMessageAudioProps {
|
||||||
file: {
|
file: IAttachment;
|
||||||
audio_url: string;
|
isReply?: boolean;
|
||||||
description: string;
|
style?: StyleProp<TextStyle>[];
|
||||||
};
|
|
||||||
theme: string;
|
theme: string;
|
||||||
getCustomEmoji: TGetCustomEmoji;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
scale?: number;
|
scale?: number;
|
||||||
|
@ -89,16 +90,21 @@ const sliderAnimationConfig = {
|
||||||
delay: 0
|
delay: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
const Button = React.memo(({ loading, paused, onPress, theme }: IButton) => (
|
const Button = React.memo(({ loading, paused, onPress, disabled, theme }: IButton) => (
|
||||||
<Touchable
|
<Touchable
|
||||||
style={styles.playPauseButton}
|
style={styles.playPauseButton}
|
||||||
|
disabled={disabled}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
hitSlop={BUTTON_HIT_SLOP}
|
hitSlop={BUTTON_HIT_SLOP}
|
||||||
background={Touchable.SelectableBackgroundBorderless()}>
|
background={Touchable.SelectableBackgroundBorderless()}>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<ActivityIndicator style={[styles.playPauseButton, styles.audioLoading]} />
|
<ActivityIndicator style={[styles.playPauseButton, styles.audioLoading]} />
|
||||||
) : (
|
) : (
|
||||||
<CustomIcon name={paused ? 'play-filled' : 'pause-filled'} size={36} color={themes[theme].tintColor} />
|
<CustomIcon
|
||||||
|
name={paused ? 'play-filled' : 'pause-filled'}
|
||||||
|
size={36}
|
||||||
|
color={disabled ? themes[theme].tintDisabled : themes[theme].tintColor}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</Touchable>
|
</Touchable>
|
||||||
));
|
));
|
||||||
|
@ -128,7 +134,7 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
|
||||||
const { baseUrl, user } = this.context;
|
const { baseUrl, user } = this.context;
|
||||||
|
|
||||||
let url = file.audio_url;
|
let url = file.audio_url;
|
||||||
if (!url.startsWith('http')) {
|
if (url && !url.startsWith('http')) {
|
||||||
url = `${baseUrl}${file.audio_url}`;
|
url = `${baseUrl}${file.audio_url}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,7 +255,7 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { loading, paused, currentTime, duration } = this.state;
|
const { loading, paused, currentTime, duration } = this.state;
|
||||||
const { file, getCustomEmoji, theme, scale } = this.props;
|
const { file, getCustomEmoji, theme, scale, isReply, style } = this.props;
|
||||||
const { description } = file;
|
const { description } = file;
|
||||||
const { baseUrl, user } = this.context;
|
const { baseUrl, user } = this.context;
|
||||||
|
|
||||||
|
@ -259,29 +265,37 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<Markdown
|
||||||
|
msg={description}
|
||||||
|
style={[isReply && style]}
|
||||||
|
baseUrl={baseUrl}
|
||||||
|
username={user.username}
|
||||||
|
getCustomEmoji={getCustomEmoji}
|
||||||
|
theme={theme}
|
||||||
|
/>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
styles.audioContainer,
|
styles.audioContainer,
|
||||||
{ backgroundColor: themes[theme].chatComponentBackground, borderColor: themes[theme].borderColor }
|
{ backgroundColor: themes[theme].chatComponentBackground, borderColor: themes[theme].borderColor }
|
||||||
]}>
|
]}>
|
||||||
<Button loading={loading} paused={paused} onPress={this.togglePlayPause} theme={theme} />
|
<Button disabled={isReply} loading={loading} paused={paused} onPress={this.togglePlayPause} theme={theme} />
|
||||||
<Slider
|
<Slider
|
||||||
|
disabled={isReply}
|
||||||
style={styles.slider}
|
style={styles.slider}
|
||||||
value={currentTime}
|
value={currentTime}
|
||||||
maximumValue={duration}
|
maximumValue={duration}
|
||||||
minimumValue={0}
|
minimumValue={0}
|
||||||
|
// @ts-ignore
|
||||||
animateTransitions
|
animateTransitions
|
||||||
animationConfig={sliderAnimationConfig}
|
animationConfig={sliderAnimationConfig}
|
||||||
thumbTintColor={isAndroid && themes[theme].tintColor}
|
thumbTintColor={isReply ? themes[theme].tintDisabled : isAndroid && themes[theme].tintColor}
|
||||||
minimumTrackTintColor={themes[theme].tintColor}
|
minimumTrackTintColor={themes[theme].tintColor}
|
||||||
maximumTrackTintColor={themes[theme].auxiliaryText}
|
maximumTrackTintColor={themes[theme].auxiliaryText}
|
||||||
onValueChange={this.onValueChange}
|
onValueChange={this.onValueChange}
|
||||||
/* @ts-ignore*/
|
thumbImage={isIOS ? { uri: 'audio_thumb', scale } : undefined}
|
||||||
thumbImage={isIOS && { uri: 'audio_thumb', scale }}
|
|
||||||
/>
|
/>
|
||||||
<Text style={[styles.duration, { color: themes[theme].auxiliaryText }]}>{this.duration}</Text>
|
<Text style={[styles.duration, { color: themes[theme].auxiliaryText }]}>{this.duration}</Text>
|
||||||
</View>
|
</View>
|
||||||
<Markdown msg={description} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} theme={theme} />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { View } from 'react-native';
|
import { StyleProp, TextStyle, View } from 'react-native';
|
||||||
import FastImage from '@rocket.chat/react-native-fast-image';
|
import FastImage from '@rocket.chat/react-native-fast-image';
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
import { createImageProgress } from 'react-native-image-progress';
|
import { createImageProgress } from 'react-native-image-progress';
|
||||||
|
@ -12,9 +12,11 @@ import { formatAttachmentUrl } from '../../lib/utils';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import MessageContext from './Context';
|
import MessageContext from './Context';
|
||||||
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
import { IAttachment } from '../../definitions';
|
||||||
|
|
||||||
type TMessageButton = {
|
type TMessageButton = {
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
|
disabled?: boolean;
|
||||||
onPress: Function;
|
onPress: Function;
|
||||||
theme: string;
|
theme: string;
|
||||||
};
|
};
|
||||||
|
@ -25,17 +27,23 @@ type TMessageImage = {
|
||||||
};
|
};
|
||||||
|
|
||||||
interface IMessageImage {
|
interface IMessageImage {
|
||||||
file: { image_url: string; description?: string };
|
file: IAttachment;
|
||||||
imageUrl?: string;
|
imageUrl?: string;
|
||||||
showAttachment: Function;
|
showAttachment?: Function;
|
||||||
|
style?: StyleProp<TextStyle>[];
|
||||||
|
isReply?: boolean;
|
||||||
theme: string;
|
theme: string;
|
||||||
getCustomEmoji: TGetCustomEmoji;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ImageProgress = createImageProgress(FastImage);
|
const ImageProgress = createImageProgress(FastImage);
|
||||||
|
|
||||||
const Button = React.memo(({ children, onPress, theme }: TMessageButton) => (
|
const Button = React.memo(({ children, onPress, disabled, theme }: TMessageButton) => (
|
||||||
<Touchable onPress={onPress} style={styles.imageContainer} background={Touchable.Ripple(themes[theme].bannerBackground)}>
|
<Touchable
|
||||||
|
disabled={disabled}
|
||||||
|
onPress={onPress}
|
||||||
|
style={styles.imageContainer}
|
||||||
|
background={Touchable.Ripple(themes[theme].bannerBackground)}>
|
||||||
{children}
|
{children}
|
||||||
</Touchable>
|
</Touchable>
|
||||||
));
|
));
|
||||||
|
@ -53,34 +61,41 @@ export const MessageImage = React.memo(({ img, theme }: TMessageImage) => (
|
||||||
));
|
));
|
||||||
|
|
||||||
const ImageContainer = React.memo(
|
const ImageContainer = React.memo(
|
||||||
({ file, imageUrl, showAttachment, getCustomEmoji, theme }: IMessageImage) => {
|
({ file, imageUrl, showAttachment, getCustomEmoji, style, isReply, theme }: IMessageImage) => {
|
||||||
const { baseUrl, user } = useContext(MessageContext);
|
const { baseUrl, user } = useContext(MessageContext);
|
||||||
const img = imageUrl || formatAttachmentUrl(file.image_url, user.id, user.token, baseUrl);
|
const img = imageUrl || formatAttachmentUrl(file.image_url, user.id, user.token, baseUrl);
|
||||||
if (!img) {
|
if (!img) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const onPress = () => showAttachment(file);
|
const onPress = () => {
|
||||||
|
if (!showAttachment) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return showAttachment(file);
|
||||||
|
};
|
||||||
|
|
||||||
if (file.description) {
|
if (file.description) {
|
||||||
return (
|
return (
|
||||||
<Button theme={theme} onPress={onPress}>
|
<Button disabled={isReply} theme={theme} onPress={onPress}>
|
||||||
<View>
|
<View>
|
||||||
<MessageImage img={img} theme={theme} />
|
|
||||||
<Markdown
|
<Markdown
|
||||||
msg={file.description}
|
msg={file.description}
|
||||||
|
style={[isReply && style]}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
username={user.username}
|
username={user.username}
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
|
<MessageImage img={img} theme={theme} />
|
||||||
</View>
|
</View>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button theme={theme} onPress={onPress}>
|
<Button disabled={isReply} theme={theme} onPress={onPress}>
|
||||||
<MessageImage img={img} theme={theme} />
|
<MessageImage img={img} theme={theme} />
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,6 +21,9 @@ import { themes } from '../../constants/colors';
|
||||||
import { IMessage, IMessageInner, IMessageTouchable } from './interfaces';
|
import { IMessage, IMessageInner, IMessageTouchable } from './interfaces';
|
||||||
|
|
||||||
const MessageInner = React.memo((props: IMessageInner) => {
|
const MessageInner = React.memo((props: IMessageInner) => {
|
||||||
|
const { attachments } = props;
|
||||||
|
const isCollapsible = attachments ? attachments[0] && attachments[0].collapsed : false;
|
||||||
|
|
||||||
if (props.type === 'discussion-created') {
|
if (props.type === 'discussion-created') {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -29,6 +32,7 @@ const MessageInner = React.memo((props: IMessageInner) => {
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.type === 'jitsi_call_started') {
|
if (props.type === 'jitsi_call_started') {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -38,6 +42,7 @@ const MessageInner = React.memo((props: IMessageInner) => {
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.blocks && props.blocks.length) {
|
if (props.blocks && props.blocks.length) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -48,11 +53,22 @@ const MessageInner = React.memo((props: IMessageInner) => {
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<User {...props} />
|
<User {...props} />
|
||||||
<Content {...props} />
|
{isCollapsible ? (
|
||||||
<Attachments {...props} />
|
<>
|
||||||
|
<Content {...props} />
|
||||||
|
<Attachments {...props} />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Attachments {...props} />
|
||||||
|
<Content {...props} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<Urls {...props} />
|
<Urls {...props} />
|
||||||
<Thread {...props} />
|
<Thread {...props} />
|
||||||
<Reactions {...props} />
|
<Reactions {...props} />
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useState } from 'react';
|
||||||
import { StyleSheet, Text, View } from 'react-native';
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { transparentize } from 'color2k';
|
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
import FastImage from '@rocket.chat/react-native-fast-image';
|
import FastImage from '@rocket.chat/react-native-fast-image';
|
||||||
|
|
||||||
|
@ -16,22 +15,24 @@ import { formatAttachmentUrl } from '../../lib/utils';
|
||||||
import { IAttachment } from '../../definitions/IAttachment';
|
import { IAttachment } from '../../definitions/IAttachment';
|
||||||
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
import RCActivityIndicator from '../ActivityIndicator';
|
import RCActivityIndicator from '../ActivityIndicator';
|
||||||
|
import Attachments from './Attachments';
|
||||||
|
import { useTheme } from '../../theme';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
button: {
|
button: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginTop: 6,
|
marginVertical: 4,
|
||||||
alignSelf: 'flex-start',
|
alignSelf: 'flex-start',
|
||||||
borderWidth: 1,
|
borderLeftWidth: 2
|
||||||
borderRadius: 4
|
|
||||||
},
|
},
|
||||||
attachmentContainer: {
|
attachmentContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
padding: 15
|
paddingVertical: 4,
|
||||||
|
paddingLeft: 8
|
||||||
},
|
},
|
||||||
backdrop: {
|
backdrop: {
|
||||||
...StyleSheet.absoluteFillObject
|
...StyleSheet.absoluteFillObject
|
||||||
|
@ -39,7 +40,8 @@ const styles = StyleSheet.create({
|
||||||
authorContainer: {
|
authorContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center'
|
alignItems: 'center',
|
||||||
|
marginBottom: 8
|
||||||
},
|
},
|
||||||
author: {
|
author: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
@ -48,9 +50,8 @@ const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
time: {
|
time: {
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
marginLeft: 10,
|
marginLeft: 8,
|
||||||
...sharedStyles.textRegular,
|
...sharedStyles.textRegular
|
||||||
fontWeight: '300'
|
|
||||||
},
|
},
|
||||||
fieldsContainer: {
|
fieldsContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
@ -114,7 +115,6 @@ interface IMessageReply {
|
||||||
attachment: IAttachment;
|
attachment: IAttachment;
|
||||||
timeFormat?: string;
|
timeFormat?: string;
|
||||||
index: number;
|
index: number;
|
||||||
theme: string;
|
|
||||||
getCustomEmoji: TGetCustomEmoji;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,10 +123,10 @@ const Title = React.memo(({ attachment, timeFormat, theme }: IMessageTitle) => {
|
||||||
return (
|
return (
|
||||||
<View style={styles.authorContainer}>
|
<View style={styles.authorContainer}>
|
||||||
{attachment.author_name ? (
|
{attachment.author_name ? (
|
||||||
<Text style={[styles.author, { color: themes[theme].bodyText }]}>{attachment.author_name}</Text>
|
<Text style={[styles.author, { color: themes[theme].auxiliaryTintColor }]}>{attachment.author_name}</Text>
|
||||||
) : null}
|
) : null}
|
||||||
{attachment.title ? <Text style={[styles.title, { color: themes[theme].bodyText }]}>{attachment.title}</Text> : null}
|
{attachment.title ? <Text style={[styles.title, { color: themes[theme].bodyText }]}>{attachment.title}</Text> : null}
|
||||||
{time ? <Text style={[styles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text> : null}
|
{time ? <Text style={[styles.time, { color: themes[theme].auxiliaryTintColor }]}>{time}</Text> : null}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -138,7 +138,16 @@ const Description = React.memo(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const { baseUrl, user } = useContext(MessageContext);
|
const { baseUrl, user } = useContext(MessageContext);
|
||||||
return <Markdown msg={text} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} theme={theme} />;
|
return (
|
||||||
|
<Markdown
|
||||||
|
msg={text}
|
||||||
|
style={[{ color: themes[theme].auxiliaryTintColor, fontSize: 14 }]}
|
||||||
|
baseUrl={baseUrl}
|
||||||
|
username={user.username}
|
||||||
|
getCustomEmoji={getCustomEmoji}
|
||||||
|
theme={theme}
|
||||||
|
/>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
(prevProps, nextProps) => {
|
(prevProps, nextProps) => {
|
||||||
if (prevProps.attachment.text !== nextProps.attachment.text) {
|
if (prevProps.attachment.text !== nextProps.attachment.text) {
|
||||||
|
@ -195,12 +204,14 @@ const Fields = React.memo(
|
||||||
);
|
);
|
||||||
|
|
||||||
const Reply = React.memo(
|
const Reply = React.memo(
|
||||||
({ attachment, timeFormat, index, getCustomEmoji, theme }: IMessageReply) => {
|
({ attachment, timeFormat, index, getCustomEmoji }: IMessageReply) => {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
if (!attachment) {
|
if (!attachment) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { theme } = useTheme();
|
||||||
const { baseUrl, user, jumpToMessage } = useContext(MessageContext);
|
const { baseUrl, user, jumpToMessage } = useContext(MessageContext);
|
||||||
|
|
||||||
const onPress = async () => {
|
const onPress = async () => {
|
||||||
|
@ -221,14 +232,9 @@ const Reply = React.memo(
|
||||||
openLink(url, theme);
|
openLink(url, theme);
|
||||||
};
|
};
|
||||||
|
|
||||||
let { borderColor, chatComponentBackground: backgroundColor } = themes[theme];
|
let { borderColor } = themes[theme];
|
||||||
try {
|
if (attachment.color) {
|
||||||
if (attachment.color) {
|
borderColor = attachment.color;
|
||||||
backgroundColor = transparentize(attachment.color, 0.8);
|
|
||||||
borderColor = attachment.color;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// fallback to default
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -240,7 +246,6 @@ const Reply = React.memo(
|
||||||
index > 0 && styles.marginTop,
|
index > 0 && styles.marginTop,
|
||||||
attachment.description && styles.marginBottom,
|
attachment.description && styles.marginBottom,
|
||||||
{
|
{
|
||||||
backgroundColor,
|
|
||||||
borderColor
|
borderColor
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
|
@ -248,6 +253,13 @@ const Reply = React.memo(
|
||||||
disabled={loading}>
|
disabled={loading}>
|
||||||
<View style={styles.attachmentContainer}>
|
<View style={styles.attachmentContainer}>
|
||||||
<Title attachment={attachment} timeFormat={timeFormat} theme={theme} />
|
<Title attachment={attachment} timeFormat={timeFormat} theme={theme} />
|
||||||
|
<Attachments
|
||||||
|
attachments={attachment.attachments}
|
||||||
|
getCustomEmoji={getCustomEmoji}
|
||||||
|
timeFormat={timeFormat}
|
||||||
|
style={[{ color: themes[theme].auxiliaryTintColor, fontSize: 14, marginBottom: 8 }]}
|
||||||
|
isReply
|
||||||
|
/>
|
||||||
<UrlImage image={attachment.thumb_url} />
|
<UrlImage image={attachment.thumb_url} />
|
||||||
<Description attachment={attachment} getCustomEmoji={getCustomEmoji} theme={theme} />
|
<Description attachment={attachment} getCustomEmoji={getCustomEmoji} theme={theme} />
|
||||||
<Fields attachment={attachment} getCustomEmoji={getCustomEmoji} theme={theme} />
|
<Fields attachment={attachment} getCustomEmoji={getCustomEmoji} theme={theme} />
|
||||||
|
@ -273,7 +285,7 @@ const Reply = React.memo(
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(prevProps, nextProps) => dequal(prevProps.attachment, nextProps.attachment) && prevProps.theme === nextProps.theme
|
(prevProps, nextProps) => dequal(prevProps.attachment, nextProps.attachment)
|
||||||
);
|
);
|
||||||
|
|
||||||
Reply.displayName = 'MessageReply';
|
Reply.displayName = 'MessageReply';
|
||||||
|
|
|
@ -97,7 +97,7 @@ const User = React.memo(
|
||||||
{textContent}
|
{textContent}
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Text style={[messageStyles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text>
|
<Text style={[messageStyles.time, { color: themes[theme].auxiliaryTintColor }]}>{time}</Text>
|
||||||
{hasError && <MessageError hasError={hasError} theme={theme} {...props} />}
|
{hasError && <MessageError hasError={hasError} theme={theme} {...props} />}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useState } from 'react';
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleProp, StyleSheet, TextStyle } from 'react-native';
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
|
|
||||||
import Touchable from './Touchable';
|
import Touchable from './Touchable';
|
||||||
|
@ -33,13 +33,15 @@ const styles = StyleSheet.create({
|
||||||
|
|
||||||
interface IMessageVideo {
|
interface IMessageVideo {
|
||||||
file: IAttachment;
|
file: IAttachment;
|
||||||
showAttachment: Function;
|
showAttachment?: Function;
|
||||||
getCustomEmoji: TGetCustomEmoji;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
|
style?: StyleProp<TextStyle>[];
|
||||||
|
isReply?: boolean;
|
||||||
theme: string;
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Video = React.memo(
|
const Video = React.memo(
|
||||||
({ file, showAttachment, getCustomEmoji, theme }: IMessageVideo) => {
|
({ file, showAttachment, getCustomEmoji, style, isReply, theme }: IMessageVideo) => {
|
||||||
const { baseUrl, user } = useContext(MessageContext);
|
const { baseUrl, user } = useContext(MessageContext);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
@ -47,7 +49,7 @@ const Video = React.memo(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const onPress = async () => {
|
const onPress = async () => {
|
||||||
if (isTypeSupported(file.video_type)) {
|
if (isTypeSupported(file.video_type) && showAttachment) {
|
||||||
return showAttachment(file);
|
return showAttachment(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,19 +75,21 @@ const Video = React.memo(
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Touchable
|
|
||||||
onPress={onPress}
|
|
||||||
style={[styles.button, { backgroundColor: themes[theme].videoBackground }]}
|
|
||||||
background={Touchable.Ripple(themes[theme].bannerBackground)}>
|
|
||||||
{loading ? <RCActivityIndicator /> : <CustomIcon name='play-filled' size={54} color={themes[theme].buttonText} />}
|
|
||||||
</Touchable>
|
|
||||||
<Markdown
|
<Markdown
|
||||||
msg={file.description}
|
msg={file.description}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
username={user.username}
|
username={user.username}
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
|
style={[isReply && style]}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
|
<Touchable
|
||||||
|
disabled={isReply}
|
||||||
|
onPress={onPress}
|
||||||
|
style={[styles.button, { backgroundColor: themes[theme].videoBackground }]}
|
||||||
|
background={Touchable.Ripple(themes[theme].bannerBackground)}>
|
||||||
|
{loading ? <RCActivityIndicator /> : <CustomIcon name='play-filled' size={54} color={themes[theme].buttonText} />}
|
||||||
|
</Touchable>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,24 +1,23 @@
|
||||||
import { MarkdownAST } from '@rocket.chat/message-parser';
|
import { MarkdownAST } from '@rocket.chat/message-parser';
|
||||||
|
import { StyleProp, TextStyle } from 'react-native';
|
||||||
|
|
||||||
import { IUserChannel, IUserMention } from '../markdown/interfaces';
|
import { IUserChannel, IUserMention } from '../markdown/interfaces';
|
||||||
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
import { IAttachment } from '../../definitions';
|
||||||
|
|
||||||
export type TMessageType = 'discussion-created' | 'jitsi_call_started';
|
export type TMessageType = 'discussion-created' | 'jitsi_call_started';
|
||||||
|
|
||||||
export interface IMessageAttachments {
|
export interface IMessageAttachments {
|
||||||
attachments: any;
|
attachments?: IAttachment[];
|
||||||
timeFormat?: string;
|
timeFormat?: string;
|
||||||
showAttachment: Function;
|
style?: StyleProp<TextStyle>[];
|
||||||
|
isReply?: boolean;
|
||||||
|
showAttachment?: Function;
|
||||||
getCustomEmoji: TGetCustomEmoji;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
theme: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMessageAttachedActions {
|
export interface IMessageAttachedActions {
|
||||||
attachment: {
|
attachment: IAttachment;
|
||||||
actions: [];
|
|
||||||
text: string;
|
|
||||||
};
|
|
||||||
theme: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMessageAvatar {
|
export interface IMessageAvatar {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { StyleSheet } from 'react-native';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { isTablet } from '../../utils/deviceInfo';
|
import { isTablet } from '../../utils/deviceInfo';
|
||||||
|
|
||||||
export default StyleSheet.create<any>({
|
export default StyleSheet.create({
|
||||||
root: {
|
root: {
|
||||||
flexDirection: 'row'
|
flexDirection: 'row'
|
||||||
},
|
},
|
||||||
|
@ -28,7 +28,6 @@ export default StyleSheet.create<any>({
|
||||||
},
|
},
|
||||||
flex: {
|
flex: {
|
||||||
flexDirection: 'row'
|
flexDirection: 'row'
|
||||||
// flex: 1
|
|
||||||
},
|
},
|
||||||
temp: { opacity: 0.3 },
|
temp: { opacity: 0.3 },
|
||||||
marginTop: {
|
marginTop: {
|
||||||
|
@ -100,7 +99,6 @@ export default StyleSheet.create<any>({
|
||||||
...sharedStyles.textSemibold
|
...sharedStyles.textSemibold
|
||||||
},
|
},
|
||||||
imageContainer: {
|
imageContainer: {
|
||||||
// flex: 1,
|
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
borderRadius: 4
|
borderRadius: 4
|
||||||
},
|
},
|
||||||
|
@ -141,7 +139,6 @@ export default StyleSheet.create<any>({
|
||||||
},
|
},
|
||||||
repliedThread: {
|
repliedThread: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
// flex: 1,
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginTop: 6,
|
marginTop: 6,
|
||||||
marginBottom: 12
|
marginBottom: 12
|
||||||
|
|
|
@ -10,13 +10,16 @@ export interface IAttachment {
|
||||||
image_type?: string;
|
image_type?: string;
|
||||||
video_url?: string;
|
video_url?: string;
|
||||||
video_type?: string;
|
video_type?: string;
|
||||||
|
audio_url?: string;
|
||||||
title_link_download?: boolean;
|
title_link_download?: boolean;
|
||||||
|
attachments?: IAttachment[];
|
||||||
fields?: IAttachment[];
|
fields?: IAttachment[];
|
||||||
image_dimensions?: { width?: number; height?: number };
|
image_dimensions?: { width?: number; height?: number };
|
||||||
image_preview?: string;
|
image_preview?: string;
|
||||||
image_size?: number;
|
image_size?: number;
|
||||||
author_name?: string;
|
author_name?: string;
|
||||||
author_icon?: string;
|
author_icon?: string;
|
||||||
|
actions?: [];
|
||||||
message_link?: string;
|
message_link?: string;
|
||||||
text?: string;
|
text?: string;
|
||||||
short?: boolean;
|
short?: boolean;
|
||||||
|
@ -24,7 +27,6 @@ export interface IAttachment {
|
||||||
author_link?: string;
|
author_link?: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
thumb_url?: string;
|
thumb_url?: string;
|
||||||
attachments?: any[];
|
|
||||||
collapsed?: boolean;
|
collapsed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { coerce, gt, gte, lt, lte, SemVer } from 'semver';
|
import { coerce, gt, gte, lt, lte, SemVer } from 'semver';
|
||||||
|
|
||||||
export const formatAttachmentUrl = (attachmentUrl: string, userId: string, token: string, server: string): string => {
|
export const formatAttachmentUrl = (attachmentUrl: string | undefined, userId: string, token: string, server: string): string => {
|
||||||
if (attachmentUrl.startsWith('http')) {
|
if (attachmentUrl && attachmentUrl.startsWith('http')) {
|
||||||
if (attachmentUrl.includes('rc_token')) {
|
if (attachmentUrl.includes('rc_token')) {
|
||||||
return encodeURI(attachmentUrl);
|
return encodeURI(attachmentUrl);
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -485,6 +485,24 @@ stories.add('Message with reply', () => (
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
<Message
|
||||||
|
msg='Looks cool!'
|
||||||
|
attachments={[
|
||||||
|
{
|
||||||
|
author_name: 'rocket.cat',
|
||||||
|
attachments: [
|
||||||
|
{
|
||||||
|
author_name: 'rocket.cat',
|
||||||
|
ts: date,
|
||||||
|
timeFormat: 'LT',
|
||||||
|
description: 'What you think about this one?',
|
||||||
|
image_url: 'https://octodex.github.com/images/yaktocat.png'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
text: ''
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue