[NEW] Redesign quoted messages (#3883)

This commit is contained in:
Gerzon Z 2022-03-21 16:44:06 -04:00 committed by GitHub
parent 82acb917fc
commit b0580bf547
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 246 additions and 143 deletions

View File

@ -37,6 +37,7 @@ export const themes: any = {
infoText: '#6d6d72',
tintColor: '#1d74f5',
tintActive: '#549df9',
tintDisabled: '#88B4F5',
auxiliaryTintColor: '#6C727A',
actionTintColor: '#1d74f5',
separatorColor: '#cbcbcc',
@ -87,6 +88,7 @@ export const themes: any = {
infoText: '#6D6D72',
tintColor: '#1d74f5',
tintActive: '#549df9',
tintDisabled: '#88B4F5',
auxiliaryTintColor: '#f9f9f9',
actionTintColor: '#1d74f5',
separatorColor: '#2b2b2d',
@ -137,6 +139,7 @@ export const themes: any = {
infoText: '#6d6d72',
tintColor: '#1e9bfe',
tintActive: '#76b7fc',
tintDisabled: '#88B4F5', // TODO: Evaluate this with design team
auxiliaryTintColor: '#f9f9f9',
actionTintColor: '#1e9bfe',
separatorColor: '#272728',

View File

@ -1,5 +1,5 @@
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 styles from './styles';
@ -19,7 +19,7 @@ const Table = React.memo(({ children, numColumns, theme }: ITable) => {
const getTableWidth = () => numColumns * CELL_WIDTH;
const renderRows = (drawExtraBorders = true) => {
const tableStyle = [styles.table, { borderColor: themes[theme].borderColor }];
const tableStyle: ViewStyle[] = [styles.table, { borderColor: themes[theme].borderColor }];
if (drawExtraBorders) {
tableStyle.push(styles.tableExtraBorders);
}

View File

@ -1,5 +1,5 @@
import React from 'react';
import { Text, View } from 'react-native';
import { Text, View, ViewStyle } from 'react-native';
import { themes } from '../../constants/colors';
import styles from './styles';
@ -14,7 +14,7 @@ interface ITableCell {
export const CELL_WIDTH = 100;
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) {
cellStyle.push(styles.cellRightBorder);
}

View File

@ -1,5 +1,5 @@
import React from 'react';
import { View } from 'react-native';
import { View, ViewStyle } from 'react-native';
import { themes } from '../../constants/colors';
import styles from './styles';
@ -11,7 +11,7 @@ interface 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) {
rowStyle.push(styles.rowBottomBorder);
}

View File

@ -280,6 +280,7 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
renderHeading = ({ children, level }: any) => {
const { numberOfLines, theme } = this.props;
// @ts-ignore
const textStyle = styles[`heading${level}Text`];
return (
<Text numberOfLines={numberOfLines} style={[textStyle, { color: themes[theme].bodyText }]}>

View File

@ -7,7 +7,7 @@ const codeFontFamily = Platform.select({
android: { fontFamily: 'monospace' }
});
export default StyleSheet.create<any>({
export default StyleSheet.create({
container: {
alignItems: 'flex-start',
flexDirection: 'row'

View File

@ -10,10 +10,16 @@ import Reply from './Reply';
import Button from '../Button';
import styles from './styles';
import MessageContext from './Context';
import { useTheme } from '../../theme';
import { IAttachment } from '../../definitions';
import CollapsibleQuote from './Components/CollapsibleQuote';
const AttachedActions = ({ attachment, theme }: IMessageAttachedActions) => {
const AttachedActions = ({ attachment }: IMessageAttachedActions) => {
if (!attachment.actions) {
return null;
}
const { onAnswerButtonPress } = useContext(MessageContext);
const { theme } = useTheme();
const attachedButtons = attachment.actions.map((element: { type: string; msg: string; text: string }) => {
if (element.type === 'button') {
@ -30,46 +36,62 @@ const AttachedActions = ({ attachment, theme }: IMessageAttachedActions) => {
};
const Attachments = React.memo(
({ attachments, timeFormat, showAttachment, getCustomEmoji, theme }: IMessageAttachments) => {
// @ts-ignore
({ attachments, timeFormat, showAttachment, style, getCustomEmoji, isReply }: IMessageAttachments) => {
if (!attachments || attachments.length === 0) {
return null;
}
return attachments.map((file: any, index: number) => {
if (file.image_url) {
return (
<Image key={file.image_url} file={file} showAttachment={showAttachment} getCustomEmoji={getCustomEmoji} theme={theme} />
);
}
if (file.audio_url) {
return <Audio key={file.audio_url} file={file} getCustomEmoji={getCustomEmoji} theme={theme} />;
}
if (file.video_url) {
return (
<Video key={file.video_url} file={file} showAttachment={showAttachment} getCustomEmoji={getCustomEmoji} theme={theme} />
);
}
if (file.actions && file.actions.length > 0) {
return <AttachedActions attachment={file} theme={theme} />;
}
if (file.title)
return (
<CollapsibleQuote key={index} index={index} attachment={file} timeFormat={timeFormat} getCustomEmoji={getCustomEmoji} />
);
const { theme } = useTheme();
return attachments.map((file: IAttachment, index: number) => {
if (file && file.image_url) {
return (
<Reply
key={index}
index={index}
attachment={file}
timeFormat={timeFormat}
<Image
key={file.image_url}
file={file}
showAttachment={showAttachment}
getCustomEmoji={getCustomEmoji}
style={style}
isReply={isReply}
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) {
return (
<Video
key={file.video_url}
file={file}
showAttachment={showAttachment}
getCustomEmoji={getCustomEmoji}
style={style}
isReply={isReply}
theme={theme}
/>
);
}
if (file && file.actions && file.actions.length > 0) {
return <AttachedActions attachment={file} />;
}
if (file.title) {
return (
<CollapsibleQuote key={index} index={index} attachment={file} timeFormat={timeFormat} getCustomEmoji={getCustomEmoji} />
);
}
return <Reply key={index} index={index} attachment={file} timeFormat={timeFormat} getCustomEmoji={getCustomEmoji} />;
});
},
(prevProps, nextProps) => dequal(prevProps.attachments, nextProps.attachments) && prevProps.theme === nextProps.theme
(prevProps, nextProps) => dequal(prevProps.attachments, nextProps.attachments)
);
Attachments.displayName = 'MessageAttachments';

View File

@ -1,5 +1,5 @@
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 Slider from '@react-native-community/slider';
import moment from 'moment';
@ -16,19 +16,20 @@ import MessageContext from './Context';
import ActivityIndicator from '../ActivityIndicator';
import { withDimensions } from '../../dimensions';
import { TGetCustomEmoji } from '../../definitions/IEmoji';
import { IAttachment } from '../../definitions';
interface IButton {
loading: boolean;
paused: boolean;
theme: string;
disabled?: boolean;
onPress: Function;
}
interface IMessageAudioProps {
file: {
audio_url: string;
description: string;
};
file: IAttachment;
isReply?: boolean;
style?: StyleProp<TextStyle>[];
theme: string;
getCustomEmoji: TGetCustomEmoji;
scale?: number;
@ -89,16 +90,21 @@ const sliderAnimationConfig = {
delay: 0
};
const Button = React.memo(({ loading, paused, onPress, theme }: IButton) => (
const Button = React.memo(({ loading, paused, onPress, disabled, theme }: IButton) => (
<Touchable
style={styles.playPauseButton}
disabled={disabled}
onPress={onPress}
hitSlop={BUTTON_HIT_SLOP}
background={Touchable.SelectableBackgroundBorderless()}>
{loading ? (
<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>
));
@ -128,7 +134,7 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
const { baseUrl, user } = this.context;
let url = file.audio_url;
if (!url.startsWith('http')) {
if (url && !url.startsWith('http')) {
url = `${baseUrl}${file.audio_url}`;
}
@ -249,7 +255,7 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
render() {
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 { baseUrl, user } = this.context;
@ -259,29 +265,37 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
return (
<>
<Markdown
msg={description}
style={[isReply && style]}
baseUrl={baseUrl}
username={user.username}
getCustomEmoji={getCustomEmoji}
theme={theme}
/>
<View
style={[
styles.audioContainer,
{ 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
disabled={isReply}
style={styles.slider}
value={currentTime}
maximumValue={duration}
minimumValue={0}
// @ts-ignore
animateTransitions
animationConfig={sliderAnimationConfig}
thumbTintColor={isAndroid && themes[theme].tintColor}
thumbTintColor={isReply ? themes[theme].tintDisabled : isAndroid && themes[theme].tintColor}
minimumTrackTintColor={themes[theme].tintColor}
maximumTrackTintColor={themes[theme].auxiliaryText}
onValueChange={this.onValueChange}
/* @ts-ignore*/
thumbImage={isIOS && { uri: 'audio_thumb', scale }}
thumbImage={isIOS ? { uri: 'audio_thumb', scale } : undefined}
/>
<Text style={[styles.duration, { color: themes[theme].auxiliaryText }]}>{this.duration}</Text>
</View>
<Markdown msg={description} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} theme={theme} />
</>
);
}

View File

@ -1,5 +1,5 @@
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 { dequal } from 'dequal';
import { createImageProgress } from 'react-native-image-progress';
@ -12,9 +12,11 @@ import { formatAttachmentUrl } from '../../lib/utils';
import { themes } from '../../constants/colors';
import MessageContext from './Context';
import { TGetCustomEmoji } from '../../definitions/IEmoji';
import { IAttachment } from '../../definitions';
type TMessageButton = {
children: JSX.Element;
disabled?: boolean;
onPress: Function;
theme: string;
};
@ -25,17 +27,23 @@ type TMessageImage = {
};
interface IMessageImage {
file: { image_url: string; description?: string };
file: IAttachment;
imageUrl?: string;
showAttachment: Function;
showAttachment?: Function;
style?: StyleProp<TextStyle>[];
isReply?: boolean;
theme: string;
getCustomEmoji: TGetCustomEmoji;
}
const ImageProgress = createImageProgress(FastImage);
const Button = React.memo(({ children, onPress, theme }: TMessageButton) => (
<Touchable onPress={onPress} style={styles.imageContainer} background={Touchable.Ripple(themes[theme].bannerBackground)}>
const Button = React.memo(({ children, onPress, disabled, theme }: TMessageButton) => (
<Touchable
disabled={disabled}
onPress={onPress}
style={styles.imageContainer}
background={Touchable.Ripple(themes[theme].bannerBackground)}>
{children}
</Touchable>
));
@ -53,34 +61,41 @@ export const MessageImage = React.memo(({ img, theme }: TMessageImage) => (
));
const ImageContainer = React.memo(
({ file, imageUrl, showAttachment, getCustomEmoji, theme }: IMessageImage) => {
({ file, imageUrl, showAttachment, getCustomEmoji, style, isReply, theme }: IMessageImage) => {
const { baseUrl, user } = useContext(MessageContext);
const img = imageUrl || formatAttachmentUrl(file.image_url, user.id, user.token, baseUrl);
if (!img) {
return null;
}
const onPress = () => showAttachment(file);
const onPress = () => {
if (!showAttachment) {
return;
}
return showAttachment(file);
};
if (file.description) {
return (
<Button theme={theme} onPress={onPress}>
<Button disabled={isReply} theme={theme} onPress={onPress}>
<View>
<MessageImage img={img} theme={theme} />
<Markdown
msg={file.description}
style={[isReply && style]}
baseUrl={baseUrl}
username={user.username}
getCustomEmoji={getCustomEmoji}
theme={theme}
/>
<MessageImage img={img} theme={theme} />
</View>
</Button>
);
}
return (
<Button theme={theme} onPress={onPress}>
<Button disabled={isReply} theme={theme} onPress={onPress}>
<MessageImage img={img} theme={theme} />
</Button>
);

View File

@ -21,6 +21,9 @@ import { themes } from '../../constants/colors';
import { IMessage, IMessageInner, IMessageTouchable } from './interfaces';
const MessageInner = React.memo((props: IMessageInner) => {
const { attachments } = props;
const isCollapsible = attachments ? attachments[0] && attachments[0].collapsed : false;
if (props.type === 'discussion-created') {
return (
<>
@ -29,6 +32,7 @@ const MessageInner = React.memo((props: IMessageInner) => {
</>
);
}
if (props.type === 'jitsi_call_started') {
return (
<>
@ -38,6 +42,7 @@ const MessageInner = React.memo((props: IMessageInner) => {
</>
);
}
if (props.blocks && props.blocks.length) {
return (
<>
@ -48,11 +53,22 @@ const MessageInner = React.memo((props: IMessageInner) => {
</>
);
}
return (
<>
<User {...props} />
{isCollapsible ? (
<>
<Content {...props} />
<Attachments {...props} />
</>
) : (
<>
<Attachments {...props} />
<Content {...props} />
</>
)}
<Urls {...props} />
<Thread {...props} />
<Reactions {...props} />

View File

@ -1,7 +1,6 @@
import React, { useContext, useState } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import moment from 'moment';
import { transparentize } from 'color2k';
import { dequal } from 'dequal';
import FastImage from '@rocket.chat/react-native-fast-image';
@ -16,22 +15,24 @@ import { formatAttachmentUrl } from '../../lib/utils';
import { IAttachment } from '../../definitions/IAttachment';
import { TGetCustomEmoji } from '../../definitions/IEmoji';
import RCActivityIndicator from '../ActivityIndicator';
import Attachments from './Attachments';
import { useTheme } from '../../theme';
const styles = StyleSheet.create({
button: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
marginTop: 6,
marginVertical: 4,
alignSelf: 'flex-start',
borderWidth: 1,
borderRadius: 4
borderLeftWidth: 2
},
attachmentContainer: {
flex: 1,
borderRadius: 4,
flexDirection: 'column',
padding: 15
paddingVertical: 4,
paddingLeft: 8
},
backdrop: {
...StyleSheet.absoluteFillObject
@ -39,7 +40,8 @@ const styles = StyleSheet.create({
authorContainer: {
flex: 1,
flexDirection: 'row',
alignItems: 'center'
alignItems: 'center',
marginBottom: 8
},
author: {
flex: 1,
@ -48,9 +50,8 @@ const styles = StyleSheet.create({
},
time: {
fontSize: 12,
marginLeft: 10,
...sharedStyles.textRegular,
fontWeight: '300'
marginLeft: 8,
...sharedStyles.textRegular
},
fieldsContainer: {
flex: 1,
@ -114,7 +115,6 @@ interface IMessageReply {
attachment: IAttachment;
timeFormat?: string;
index: number;
theme: string;
getCustomEmoji: TGetCustomEmoji;
}
@ -123,10 +123,10 @@ const Title = React.memo(({ attachment, timeFormat, theme }: IMessageTitle) => {
return (
<View style={styles.authorContainer}>
{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}
{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>
);
});
@ -138,7 +138,16 @@ const Description = React.memo(
return null;
}
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) => {
if (prevProps.attachment.text !== nextProps.attachment.text) {
@ -195,12 +204,14 @@ const Fields = React.memo(
);
const Reply = React.memo(
({ attachment, timeFormat, index, getCustomEmoji, theme }: IMessageReply) => {
({ attachment, timeFormat, index, getCustomEmoji }: IMessageReply) => {
const [loading, setLoading] = useState(false);
if (!attachment) {
return null;
}
const { theme } = useTheme();
const { baseUrl, user, jumpToMessage } = useContext(MessageContext);
const onPress = async () => {
@ -221,15 +232,10 @@ const Reply = React.memo(
openLink(url, theme);
};
let { borderColor, chatComponentBackground: backgroundColor } = themes[theme];
try {
let { borderColor } = themes[theme];
if (attachment.color) {
backgroundColor = transparentize(attachment.color, 0.8);
borderColor = attachment.color;
}
} catch (e) {
// fallback to default
}
return (
<>
@ -240,7 +246,6 @@ const Reply = React.memo(
index > 0 && styles.marginTop,
attachment.description && styles.marginBottom,
{
backgroundColor,
borderColor
}
]}
@ -248,6 +253,13 @@ const Reply = React.memo(
disabled={loading}>
<View style={styles.attachmentContainer}>
<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} />
<Description 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';

View File

@ -97,7 +97,7 @@ const User = React.memo(
{textContent}
</Text>
</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} />}
</View>
);

View File

@ -1,5 +1,5 @@
import React, { useContext, useState } from 'react';
import { StyleSheet } from 'react-native';
import { StyleProp, StyleSheet, TextStyle } from 'react-native';
import { dequal } from 'dequal';
import Touchable from './Touchable';
@ -33,13 +33,15 @@ const styles = StyleSheet.create({
interface IMessageVideo {
file: IAttachment;
showAttachment: Function;
showAttachment?: Function;
getCustomEmoji: TGetCustomEmoji;
style?: StyleProp<TextStyle>[];
isReply?: boolean;
theme: string;
}
const Video = React.memo(
({ file, showAttachment, getCustomEmoji, theme }: IMessageVideo) => {
({ file, showAttachment, getCustomEmoji, style, isReply, theme }: IMessageVideo) => {
const { baseUrl, user } = useContext(MessageContext);
const [loading, setLoading] = useState(false);
@ -47,7 +49,7 @@ const Video = React.memo(
return null;
}
const onPress = async () => {
if (isTypeSupported(file.video_type)) {
if (isTypeSupported(file.video_type) && showAttachment) {
return showAttachment(file);
}
@ -73,19 +75,21 @@ const Video = React.memo(
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
msg={file.description}
baseUrl={baseUrl}
username={user.username}
getCustomEmoji={getCustomEmoji}
style={[isReply && style]}
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>
</>
);
},

View File

@ -1,24 +1,23 @@
import { MarkdownAST } from '@rocket.chat/message-parser';
import { StyleProp, TextStyle } from 'react-native';
import { IUserChannel, IUserMention } from '../markdown/interfaces';
import { TGetCustomEmoji } from '../../definitions/IEmoji';
import { IAttachment } from '../../definitions';
export type TMessageType = 'discussion-created' | 'jitsi_call_started';
export interface IMessageAttachments {
attachments: any;
attachments?: IAttachment[];
timeFormat?: string;
showAttachment: Function;
style?: StyleProp<TextStyle>[];
isReply?: boolean;
showAttachment?: Function;
getCustomEmoji: TGetCustomEmoji;
theme: string;
}
export interface IMessageAttachedActions {
attachment: {
actions: [];
text: string;
};
theme: string;
attachment: IAttachment;
}
export interface IMessageAvatar {

View File

@ -3,7 +3,7 @@ import { StyleSheet } from 'react-native';
import sharedStyles from '../../views/Styles';
import { isTablet } from '../../utils/deviceInfo';
export default StyleSheet.create<any>({
export default StyleSheet.create({
root: {
flexDirection: 'row'
},
@ -28,7 +28,6 @@ export default StyleSheet.create<any>({
},
flex: {
flexDirection: 'row'
// flex: 1
},
temp: { opacity: 0.3 },
marginTop: {
@ -100,7 +99,6 @@ export default StyleSheet.create<any>({
...sharedStyles.textSemibold
},
imageContainer: {
// flex: 1,
flexDirection: 'column',
borderRadius: 4
},
@ -141,7 +139,6 @@ export default StyleSheet.create<any>({
},
repliedThread: {
flexDirection: 'row',
// flex: 1,
alignItems: 'center',
marginTop: 6,
marginBottom: 12

View File

@ -10,13 +10,16 @@ export interface IAttachment {
image_type?: string;
video_url?: string;
video_type?: string;
audio_url?: string;
title_link_download?: boolean;
attachments?: IAttachment[];
fields?: IAttachment[];
image_dimensions?: { width?: number; height?: number };
image_preview?: string;
image_size?: number;
author_name?: string;
author_icon?: string;
actions?: [];
message_link?: string;
text?: string;
short?: boolean;
@ -24,7 +27,6 @@ export interface IAttachment {
author_link?: string;
color?: string;
thumb_url?: string;
attachments?: any[];
collapsed?: boolean;
}

View File

@ -1,7 +1,7 @@
import { coerce, gt, gte, lt, lte, SemVer } from 'semver';
export const formatAttachmentUrl = (attachmentUrl: string, userId: string, token: string, server: string): string => {
if (attachmentUrl.startsWith('http')) {
export const formatAttachmentUrl = (attachmentUrl: string | undefined, userId: string, token: string, server: string): string => {
if (attachmentUrl && attachmentUrl.startsWith('http')) {
if (attachmentUrl.includes('rc_token')) {
return encodeURI(attachmentUrl);
}

File diff suppressed because one or more lines are too long

View File

@ -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