Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Messaging Markdown (light) Support #552

Merged
merged 1 commit into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
38 changes: 36 additions & 2 deletions src/hooks/Circles/usePrivatePosts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@

export const postToMessage = (
post: Partial<Post> & Pick<Post, 'id'>,
): IMessage => {
): IMessage | IMessage[] => {
if (!post.authorId) {
throw Error(
t(
Expand All @@ -54,7 +54,7 @@
);
}

return {
const message = {
_id: post.id,
text: post.message || '',
createdAt: post.createdAt ? new Date(post.createdAt) : new Date(),
Expand All @@ -64,6 +64,40 @@
avatar: post.author?.profile.picture,
},
};

const markdownImages = message.text.match(/\!\[.+\]\(.+\)/g);

Check warning on line 68 in src/hooks/Circles/usePrivatePosts.ts

View workflow job for this annotation

GitHub Actions / main

Unnecessary escape character: \!

if (!markdownImages) {
return message;
}

const messages: IMessage[] = [];
let text = message.text;

for (let i = 0; i < markdownImages.length; i++) {
const imageMarkdown = markdownImages[i];
const imageUrl = imageMarkdown.match(/\!\[.+\]\((.+)\)/)?.[1];

Check warning on line 79 in src/hooks/Circles/usePrivatePosts.ts

View workflow job for this annotation

GitHub Actions / main

Unnecessary escape character: \!
const parts = text.split(imageMarkdown);
text = parts[1].trim();

messages.push({
...message,
_id: `${message._id}-${i}`,
text: parts[0].trim(),
});
messages.push({
...message,
_id: `${message._id}-${i}-image`,
text: '',
image: imageUrl,
});
Comment on lines +83 to +93

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar with the repo much less this feature, but this seems odd to me. You're adding the message twice and just changing whether it's image or text? Why not just

Suggested change
messages.push({
...message,
_id: `${message._id}-${i}`,
text: parts[0].trim(),
});
messages.push({
...message,
_id: `${message._id}-${i}-image`,
text: '',
image: imageUrl,
});
messages.push({
...message,
_id: `${message._id}-${i}`,
text: parts[0].trim(),
image: imageUrl,
});

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gifted chat renders the text below the image. For markdown images, we want them to appear in the order they were written so this splits the message up by image occurrences and interleaves them correctly.

Example: For the following markdown

Picture 1
![some-pic](https://some-url.com/1)
Picture 2
![some-pic-2](https://some-url.com/2)
Aren't these cool?

If we put the image on the message, gifted chat would display

<some-pic-1>
Picture 1
<some-pic-2>
Picture 2
Aren't these cool?

With my proposed solution we would see

Picture 1
<some-pic-1>
Picture 2
<some-pic-2>
Aren't these cool?

}

if (text.length) {
messages.push({ ...message, text });
}

return messages.reverse();
};

const uniqSort = (userIds: Array<string | undefined>) => {
Expand Down
92 changes: 78 additions & 14 deletions src/screens/DirectMessagesScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
import { User } from '../types';
import { ScreenProps } from './utils/stack-helpers';
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
import { Platform } from 'react-native';
import { Linking, Platform, StyleProp, TextStyle } from 'react-native';
import { ParseShape } from 'react-native-parsed-text';

export type DirectMessageParams = {
users: User[];
Expand Down Expand Up @@ -131,20 +132,24 @@
left: styles.leftText,
right: styles.rightText,
}}
nextMessage={props.nextMessage}
usernameStyle={styles.signatureText}
renderTime={(timeProps) => (
<Time
{...timeProps}
timeTextStyle={{
left: styles.leftTimeText,
right: styles.rightTimeText,
}}
containerStyle={{
left: styles.leftTimeContainer,
right: styles.rightTimeContainer,
}}
/>
)}
renderTime={(timeProps) =>
timeProps.currentMessage?.createdAt !==
(timeProps as any).nextMessage?.createdAt && (
<Time
{...timeProps}
timeTextStyle={{
left: styles.leftTimeText,
right: styles.rightTimeText,
}}
containerStyle={{
left: styles.leftTimeContainer,
right: styles.rightTimeContainer,
}}
/>
)
}
/>
);
}}
Expand Down Expand Up @@ -187,10 +192,69 @@
</Send>
);
}}
parsePatterns={messageTextParsers}
/>
);
};

export const messageTextParsers = (
linkStyle: StyleProp<TextStyle>,
disabled = false,
): ParseShape[] => [
{
pattern: /\!\[(.+)\]\((\S+)\)/,

Check warning on line 205 in src/screens/DirectMessagesScreen.tsx

View workflow job for this annotation

GitHub Actions / main

Unnecessary escape character: \!
renderText: (_fullString: string, matches: string[]) => `<${matches[1]}>`,
},
{
pattern: /\[(.+)\]\((\S+)\)/,
style: linkStyle,
renderText: (_fullString: string, matches: string[]) => matches[1],
onPress: (url: string) => {
const matches = url.match(/\[.+\]\((.+)\)/) || [];
Linking.openURL(matches[1]);
},
disabled,
},
{
type: 'url',
style: linkStyle,
onPress: (url: string) => Linking.openURL(url),
disabled,
},
{
type: 'phone',
style: linkStyle,
onPress: (url: string) => Linking.openURL(`tel:${url}`),
disabled,
},
{
type: 'email',
style: linkStyle,
onPress: (url: string) => Linking.openURL(`mailto:${url}`),
disabled,
},
{
pattern: /\*{3}(.+)\*{3}?/,
style: { fontWeight: 'bold', fontStyle: 'italic' },
renderText: (match: string) => match.slice(3, -3),
},
{
pattern: /[\*_]{2}(.+)[\*_]{2}?/,

Check warning on line 242 in src/screens/DirectMessagesScreen.tsx

View workflow job for this annotation

GitHub Actions / main

Unnecessary escape character: \*

Check warning on line 242 in src/screens/DirectMessagesScreen.tsx

View workflow job for this annotation

GitHub Actions / main

Unnecessary escape character: \*
style: { fontWeight: 'bold' },
renderText: (match: string) => match.slice(2, -2),
},
{
pattern: /[\*_]{1}(.+)[\*_]{1}?/,

Check warning on line 247 in src/screens/DirectMessagesScreen.tsx

View workflow job for this annotation

GitHub Actions / main

Unnecessary escape character: \*

Check warning on line 247 in src/screens/DirectMessagesScreen.tsx

View workflow job for this annotation

GitHub Actions / main

Unnecessary escape character: \*
style: { fontStyle: 'italic' },
renderText: (match: string) => match.slice(1, -1),
},
{
pattern: /~{2}(.+)~{2}?/,
style: { textDecorationLine: 'line-through' },
renderText: (match: string) => match.slice(2, -2),
},
];

const defaultStyles = createStyles('DirectMessagesScreen', (theme) => ({
activityIndicator: {
view: {
Expand Down
31 changes: 22 additions & 9 deletions src/screens/MessageScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ import { useUser } from '../hooks';
import { useProfilesForTile } from '../hooks/useMessagingProfiles';
import { User } from '../types';
import { ParamListBase } from '@react-navigation/native';
import { DirectMessageParams } from './DirectMessagesScreen';
import {
DirectMessageParams,
messageTextParsers,
} from './DirectMessagesScreen';
import { ComposeMessageParams } from './ComposeMessageScreen';
import {
ScreenParamTypes as BaseScreenParamTypes,
Expand All @@ -38,6 +41,7 @@ import {
import compact from 'lodash/compact';
import Swipeable from 'react-native-gesture-handler/Swipeable';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import ParsedText from 'react-native-parsed-text';

export type MessageTileParams = {
tileId: string;
Expand Down Expand Up @@ -206,7 +210,7 @@ export function MessageScreen<ParamList extends ParamListBase>({
style={styles.listItemView}
descriptionNumberOfLines={1}
descriptionStyle={
node
node.hasUnread
? [styles.listItemSubtitleText, styles.newMessageText]
: styles.listItemSubtitleText
}
Expand All @@ -223,12 +227,19 @@ export function MessageScreen<ParamList extends ParamListBase>({
.map((profile) => profile.profile.displayName)
.join(', ')}
description={
node.latestMessageUserId === userData?.id
? t('message-preview-prefixed', {
defaultValue: 'You: {{messageText}}',
messageText: node.latestMessageText,
})
: node.latestMessageText
<ParsedText
parse={messageTextParsers(
styles.linkMessagePreviewText,
true,
)}
>
{node.latestMessageUserId === userData?.id
? t('message-preview-prefixed', {
defaultValue: 'You: {{messageText}}',
messageText: node.latestMessageText,
})
: node.latestMessageText}
</ParsedText>
}
right={() => renderRight(node.latestMessageTime)}
/>
Expand Down Expand Up @@ -375,7 +386,6 @@ const defaultStyles = createStyles('MessageScreen', (theme) => {
listItemTimeText: { paddingLeft: 15 },
newMessageText: {
color: theme.colors.text,
fontWeight: '600',
},
multiGiftedAvatarView: {
flex: 1,
Expand Down Expand Up @@ -433,6 +443,9 @@ const defaultStyles = createStyles('MessageScreen', (theme) => {
paddingVertical: 20,
},
createMessageIcon: {} as { color: string | undefined },
linkMessagePreviewText: {
textDecorationLine: 'underline',
},
};
});

Expand Down