Skip to content

Commit fb7cb63

Browse files
committed
feat(reactions): added add reaction button to message menu + emoji selector
Signed-off-by: m-doescode <80221594+m-doescode@users.noreply.github.com>
1 parent 8b1129e commit fb7cb63

File tree

10 files changed

+444
-10
lines changed

10 files changed

+444
-10
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,15 @@
3737
"intl-pluralrules": "^2.0.1",
3838
"mobx": "^6.13.6",
3939
"mobx-react-lite": "^4.1.0",
40+
"patch-package": "^8.0.0",
4041
"react": "^19.0.0",
4142
"react-dom": "^19.0.0",
4243
"react-error-boundary": "^5.0.0",
4344
"react-i18next": "^15.4.1",
4445
"react-native": "^0.78.0",
4546
"react-native-device-info": "^14.0.4",
4647
"react-native-drawer-layout": "^4.1.1",
48+
"react-native-emoji-selector": "^0.2.0",
4749
"react-native-gesture-handler": "^2.24.0",
4850
"react-native-get-random-values": "^1.11.0",
4951
"react-native-keyboard-controller": "^1.16.6",
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
diff --git a/node_modules/react-native-emoji-selector/index.js b/node_modules/react-native-emoji-selector/index.js
2+
index 0737acd..a1d1f2c 100644
3+
--- a/node_modules/react-native-emoji-selector/index.js
4+
+++ b/node_modules/react-native-emoji-selector/index.js
5+
@@ -70,6 +70,7 @@ const categoryKeys = Object.keys(Categories);
6+
7+
const TabBar = ({ theme, activeCategory, onPress, width }) => {
8+
const tabSize = width / categoryKeys.length;
9+
+ if (tabSize - 12 < 1) return;
10+
11+
return categoryKeys.map(c => {
12+
const category = Categories[c];
13+
@@ -80,7 +81,7 @@ const TabBar = ({ theme, activeCategory, onPress, width }) => {
14+
onPress={() => onPress(category)}
15+
style={{
16+
flex: 1,
17+
- height: tabSize,
18+
+ height: tabSize + 12,
19+
borderColor: category === activeCategory ? theme : "#EEEEEE",
20+
borderBottomWidth: 2,
21+
alignItems: "center",
22+
@@ -91,7 +92,7 @@ const TabBar = ({ theme, activeCategory, onPress, width }) => {
23+
style={{
24+
textAlign: "center",
25+
paddingBottom: 8,
26+
- fontSize: tabSize - 24
27+
+ fontSize: tabSize - 12
28+
}}
29+
>
30+
{category.symbol}

src/Generic.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,11 @@ export const app = {
328328
`[FUNCTIONS] Tried to run uninitialised function openMessage (args: ${m})`,
329329
);
330330
},
331+
openAddReaction: (m: Message | null) => {
332+
console.log(
333+
`[FUNCTIONS] Tried to run uninitialised function openAddReaction (args: ${m})`
334+
);
335+
},
331336
openChannelContextMenu: (c: Channel | null) => {
332337
console.log(
333338
`[FUNCTIONS] Tried to run uninitialised function openChannelContextMenu (args: ${c})`,

src/Modals.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
ServerSettingsSheet,
3333
SettingsSheet,
3434
StatusSheet,
35+
AddReactionSheet
3536
} from '@clerotri/components/sheets';
3637

3738
// Modals appear to break on the new architecture unless you wrap them in a View. see also https://github.com/react-navigation/react-navigation/issues/12301#issuecomment-2501692557
@@ -179,6 +180,7 @@ export const Modals = observer(() => {
179180
<MemberListSheet />
180181
<PinnedMessagesSheet />
181182
<ServerInfoSheet />
183+
<AddReactionSheet />
182184
<FixedModal
183185
visible={!!imageViewerState.i}
184186
transparent={true}

src/components/common/BottomSheet.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ import {StyleSheet} from 'react-native';
44
import BottomSheetCore, {
55
BottomSheetBackdrop,
66
BottomSheetScrollView,
7+
BottomSheetView
78
} from '@gorhom/bottom-sheet';
89
import {observer} from 'mobx-react-lite';
910

1011
import {commonValues, Theme, ThemeContext} from '@clerotri/lib/themes';
1112

1213
export const BottomSheet = observer(
13-
({sheetRef, children}: {sheetRef: any; children: any}) => {
14+
({sheetRef, innerScroll, children}: {sheetRef: any; innerScroll?: boolean; children: any}) => {
1415
const {currentTheme} = useContext(ThemeContext);
1516
const localStyles = useMemo(
1617
() => generateLocalStyles(currentTheme),
@@ -28,8 +29,13 @@ export const BottomSheet = observer(
2829
backdropComponent={BottomSheetBackdrop}
2930
style={localStyles.sheet}
3031
backgroundStyle={localStyles.sheetBackground}
31-
handleIndicatorStyle={localStyles.handleIndicator}>
32-
<BottomSheetScrollView>{children}</BottomSheetScrollView>
32+
handleIndicatorStyle={localStyles.handleIndicator}
33+
enableContentPanningGesture={!innerScroll}>
34+
{innerScroll ? (
35+
<BottomSheetView style={{ flexDirection: 'column', flex: 1 }}>{children}</BottomSheetView>
36+
) : (
37+
<BottomSheetScrollView>{children}</BottomSheetScrollView>
38+
)}
3339
</BottomSheetCore>
3440
);
3541
},

src/components/common/messaging/MessageReactions.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import {useContext} from 'react';
22
import {Pressable, View} from 'react-native';
33
import {action} from 'mobx';
44
import {observer} from 'mobx-react-lite';
5+
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
56

67
import type {Message} from 'revolt.js';
78

9+
import {app} from '@clerotri/Generic';
810
import {client} from '@clerotri/lib/client';
911
import {Text} from '@clerotri/components/common/atoms';
1012
import {Image} from '@clerotri/crossplat/Image';
@@ -66,6 +68,31 @@ export const MessageReactions = observer(
6668
</Pressable>
6769
);
6870
})}
71+
72+
<Pressable
73+
key={`message-${msg._id}-add-reaction}`}
74+
onPress={action(() => {
75+
msg.channel?.havePermission('React')
76+
? app.openAddReaction(msg)
77+
: showToast('You cannot react to this message.');
78+
})}
79+
style={{
80+
padding: commonValues.sizes.small,
81+
borderRadius: commonValues.sizes.small,
82+
borderColor: currentTheme.backgroundTertiary,
83+
backgroundColor: currentTheme.backgroundSecondary,
84+
borderWidth: commonValues.sizes.xs,
85+
marginEnd: commonValues.sizes.small,
86+
marginVertical: commonValues.sizes.xs,
87+
}}>
88+
<View style={{flexDirection: 'row'}}>
89+
<MaterialIcon
90+
name="add-reaction"
91+
size={20}
92+
color={currentTheme.foregroundPrimary}
93+
/>
94+
</View>
95+
</Pressable>
6996
</View>
7097
);
7198
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { useContext, useRef, useState, useMemo } from 'react';
2+
import { View, Text } from 'react-native';
3+
import { observer } from 'mobx-react-lite';
4+
import EmojiSelector from 'react-native-emoji-selector';
5+
// import EmojiPicker from 'emoji-picker-react';
6+
7+
import type BottomSheetCore from '@gorhom/bottom-sheet';
8+
9+
import type { Message } from 'revolt.js';
10+
11+
import { client } from '@clerotri/lib/client';
12+
import { showToast } from '@clerotri/lib/utils';
13+
import { app, setFunction } from '@clerotri/Generic';
14+
import { BottomSheet } from '@clerotri/components/common/BottomSheet';
15+
import { ThemeContext } from '@clerotri/lib/themes';
16+
import { useBackHandler } from '@clerotri/lib/ui';
17+
18+
export const AddReactionSheet = observer(() => {
19+
const { currentTheme } = useContext(ThemeContext);
20+
21+
const [message, setMessage] = useState(null as Message | null);
22+
23+
const sheetRef = useRef<BottomSheetCore>(null);
24+
25+
useBackHandler(() => {
26+
if (message) {
27+
sheetRef.current?.close();
28+
return true;
29+
}
30+
31+
return false;
32+
});
33+
34+
setFunction('openAddReaction', async (m: Message | null) => {
35+
setMessage(m);
36+
m ? sheetRef.current?.expand() : sheetRef.current?.close();
37+
});
38+
39+
function selectEmoji(emoji: string) {
40+
if (!message) return;
41+
42+
const reaction = message.reactions.get(emoji) || [];
43+
44+
message.channel?.havePermission('React')
45+
? !Array.from(reaction).includes(client.user?._id!)
46+
? message.react(emoji)
47+
: message.unreact(emoji)
48+
: showToast('You cannot react to this message.');
49+
app.openAddReaction(null);
50+
}
51+
52+
return (
53+
// BottomSheet cannot wrap our children in a scroll view because it will
54+
// create a nested set of scroll views which causes issues
55+
<BottomSheet innerScroll={true} sheetRef={sheetRef}>
56+
<View style={{ paddingHorizontal: 16, flex: 1 }}>
57+
{!message ? (
58+
<></>
59+
) : (
60+
<EmojiSelector
61+
onEmojiSelected={selectEmoji}
62+
columns={8} />
63+
)}
64+
</View>
65+
</BottomSheet>
66+
);
67+
});

src/components/sheets/MessageMenuSheet.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,23 @@ export const MessageMenuSheet = observer(() => {
5656
}}>
5757
<ReplyMessage message={message} showSymbol={false} />
5858
</View>
59-
{message.channel?.havePermission('SendMessage') ? (
59+
{message.channel?.havePermission('React') ? (
60+
<ContextButton
61+
onPress={() => {
62+
app.openAddReaction(message);
63+
app.openMessage(null);
64+
}}>
65+
<View style={styles.iconContainer}>
66+
<MaterialIcon
67+
name="add-reaction"
68+
size={20}
69+
color={currentTheme.foregroundPrimary}
70+
/>
71+
</View>
72+
<Text>Add Reaction</Text>
73+
</ContextButton>
74+
) : null}
75+
{message.channel?.havePermission('SendMessage') && settings.get('ui.messaging.showReactions') ? (
6076
<ContextButton
6177
onPress={() => {
6278
let replyingMessages = [...app.getReplyingMessages()];

src/components/sheets/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ export {ServerInviteSheet} from './ServerInviteSheet';
1111
export {ServerSettingsSheet} from './ServerSettingsSheet';
1212
export {SettingsSheet} from './SettingsSheet';
1313
export {StatusSheet} from './StatusSheet';
14+
export {AddReactionSheet} from './AddReactionSheet';

0 commit comments

Comments
 (0)