Skip to content

Commit 3db52af

Browse files
[CSK-873] lock messages from being edited (#191)
* feat(lib): lock messages from being edited * feat(lib): rm * feat(lib): rm * PubNub SDK v0.8.4 release. --------- Co-authored-by: PubNub Release Bot <120067856+pubnub-release-bot@users.noreply.github.com>
1 parent 7539ab6 commit 3db52af

File tree

10 files changed

+148
-15
lines changed

10 files changed

+148
-15
lines changed

.pubnub.yml

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
---
22
name: pubnub-js-chat
3-
version: v0.8.3
3+
version: v0.8.4
44
scm: github.com/pubnub/js-chat
55
schema: 1
66
files:
77
- lib/dist/index.js
88
changelog:
9+
- date: 2024-11-26
10+
version: v0.8.4
11+
changes:
12+
- type: feature
13+
text: "Lock messages from being edited."
14+
- type: feature
15+
text: "Remove reactions list if everyone removed them."
916
- date: 2024-11-15
1017
version: v0.8.3
1118
changes:

lib/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pubnub/chat",
3-
"version": "0.8.3",
3+
"version": "0.8.4",
44
"description": "PubNub JavaScript Chat SDK",
55
"author": "PubNub <support@pubnub.com>",
66
"license": "SEE LICENSE IN LICENSE",

lib/src/constants.ts

+4
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@ export const MESSAGE_THREAD_ID_PREFIX = "PUBNUB_INTERNAL_THREAD"
22
export const INTERNAL_MODERATION_PREFIX = "PUBNUB_INTERNAL_MODERATION_"
33
export const INTERNAL_ADMIN_CHANNEL = "PUBNUB_INTERNAL_ADMIN_CHANNEL"
44
export const ERROR_LOGGER_KEY_PREFIX = "PUBNUB_INTERNAL_ERROR_LOGGER"
5+
export const INTERNAL_MODERATOR_DATA = {
6+
id: "PUBNUB_INTERNAL_MODERATOR",
7+
type: "internal",
8+
}

lib/src/entities/message.ts

+4
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,10 @@ export class Message {
210210
async editText(newText: string) {
211211
const type = this.chat.editMessageActionName
212212
try {
213+
if (this.meta?.PUBNUB_INTERNAL_AUTOMODERATED && !this.chat.currentUser.isInternalModerator) {
214+
throw "The automoderated message can no longer be edited"
215+
}
216+
213217
const { data } = await this.chat.sdk.addMessageAction({
214218
channel: this.channelId,
215219
messageTimetoken: this.timetoken,

lib/src/entities/user.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import { Chat } from "./chat"
33
import { DeleteParameters, OptionalAllBut } from "../types"
44
import { Channel } from "./channel"
55
import { Membership } from "./membership"
6-
import { INTERNAL_ADMIN_CHANNEL, INTERNAL_MODERATION_PREFIX } from "../constants"
6+
import {
7+
INTERNAL_ADMIN_CHANNEL,
8+
INTERNAL_MODERATION_PREFIX,
9+
INTERNAL_MODERATOR_DATA,
10+
} from "../constants"
711
import { getErrorProxiedEntity } from "../error-logging"
812

913
export type UserFields = Pick<
@@ -56,6 +60,11 @@ export class User {
5660
return getErrorProxiedEntity(new User(chat, data), chat.errorLogger)
5761
}
5862

63+
/** @internal */
64+
get isInternalModerator() {
65+
return this.id === INTERNAL_MODERATOR_DATA.id && this.type === INTERNAL_MODERATOR_DATA.type
66+
}
67+
5968
get active() {
6069
return !!(
6170
this.lastActiveTimestamp &&

samples/react-native-group-chat/components/actions-menu/ActionsMenu.tsx

+24-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type UseActionsMenuParams = {
1919
onQuote: (message: Message) => void
2020
removeThreadReply?: boolean
2121
onPinMessage: (message: Message | ThreadMessage) => void
22+
onEditMessage: (message: Message | ThreadMessage) => void
2223
onToggleEmoji: (message: Message) => void
2324
onDeleteMessage: (message: Message) => void
2425
}
@@ -29,6 +30,7 @@ export function useActionsMenu({
2930
onPinMessage,
3031
onToggleEmoji,
3132
onDeleteMessage,
33+
onEditMessage,
3234
}: UseActionsMenuParams) {
3335
const bottomSheetModalRef = useRef<BottomSheetModal>(null)
3436
const navigation = useNavigation<HomeStackNavigation>()
@@ -38,7 +40,7 @@ export function useActionsMenu({
3840
)
3941

4042
// variables
41-
const snapPoints = useMemo(() => ["25%", "50%"], [])
43+
const snapPoints = useMemo(() => ["25%", "50%", "75%"], [])
4244

4345
// callbacks
4446
const handlePresentModalPress = useCallback(({ message }: { message: EnhancedIMessage }) => {
@@ -63,7 +65,7 @@ export function useActionsMenu({
6365
const ActionsMenuComponent = () => (
6466
<BottomSheetModal
6567
ref={bottomSheetModalRef}
66-
index={1}
68+
index={2}
6769
snapPoints={snapPoints}
6870
onChange={handleSheetChanges}
6971
style={styles.container}
@@ -161,6 +163,26 @@ export function useActionsMenu({
161163
Pin message
162164
</Button>
163165
<Gap value={16} />
166+
{currentlyFocusedMessage?.originalPnMessage.userId === chat?.currentUser.id && (
167+
<>
168+
<Button
169+
size="md"
170+
align="left"
171+
icon="edit"
172+
variant="outlined"
173+
onPress={() => {
174+
if (currentlyFocusedMessage) {
175+
onEditMessage(currentlyFocusedMessage.originalPnMessage)
176+
setCurrentlyFocusedMessage(null)
177+
bottomSheetModalRef.current?.dismiss()
178+
}
179+
}}
180+
>
181+
Edit message
182+
</Button>
183+
<Gap value={16} />
184+
</>
185+
)}
164186
</>
165187
) : null}
166188
{currentlyFocusedMessage?.originalPnMessage.userId === chat?.currentUser.id ? (

samples/react-native-group-chat/package.json

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"@react-navigation/stack": "^6.3.17",
2020
"expo": "^51.0.38",
2121
"expo-clipboard": "~6.0.3",
22+
"expo-crypto": "^14.0.1",
2223
"expo-font": "~12.0.10",
2324
"expo-status-bar": "~1.12.1",
2425
"nanoid": "4.0.2",
@@ -53,5 +54,8 @@
5354
"react-native-svg-transformer": "^1.1.0",
5455
"typescript": "~5.3.3"
5556
},
57+
"browser": {
58+
"crypto": false
59+
},
5660
"private": true
5761
}

samples/react-native-group-chat/screens/ordinary/chat/Chat.tsx

+75-9
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
} from "react-native"
1212
import { GiftedChat, Bubble } from "react-native-gifted-chat"
1313
import { StackScreenProps } from "@react-navigation/stack"
14-
import { User, MessageDraft, Message, Channel } from "@pubnub/chat"
14+
import { User, MessageDraft, Message, Channel, ThreadMessage } from "@pubnub/chat"
1515

1616
import { EnhancedIMessage, mapPNMessageToGChatMessage } from "../../../utils"
1717
import { ChatContext } from "../../../context"
@@ -107,11 +107,37 @@ export function ChatScreen({}: StackScreenProps<HomeStackParamList, "Chat">) {
107107
}
108108
}, [])
109109

110+
const handleEditMessage = useCallback(
111+
async (message: Message) => {
112+
if (!currentChannel) {
113+
return
114+
}
115+
const newMessageDraft = currentChannel.createMessageDraft({
116+
userSuggestionSource: "global",
117+
isTypingIndicatorTriggered: currentChannel.type !== "public",
118+
})
119+
newMessageDraft.value = message.text
120+
setText(newMessageDraft.value)
121+
setGiftedChatMappedMessages((curr) =>
122+
curr.map((m) => {
123+
return {
124+
...m,
125+
isBeingEdited: m.originalPnMessage.timetoken === message.timetoken,
126+
}
127+
})
128+
)
129+
130+
setMessageDraft(newMessageDraft)
131+
},
132+
[currentChannel]
133+
)
134+
110135
const { ActionsMenuComponent, handlePresentModalPress } = useActionsMenu({
111136
onQuote: handleQuote,
112137
onPinMessage: handlePin,
113138
onToggleEmoji: handleEmoji,
114139
onDeleteMessage: handleDeleteMessage,
140+
onEditMessage: handleEditMessage,
115141
})
116142

117143
useEffect(() => {
@@ -245,6 +271,17 @@ export function ChatScreen({}: StackScreenProps<HomeStackParamList, "Chat">) {
245271
}
246272
}, [currentChannel, currentChannelMembership])
247273

274+
function resetMessageBeingEdited() {
275+
setGiftedChatMappedMessages((curr) =>
276+
curr.map((m) => {
277+
return {
278+
...m,
279+
isBeingEdited: false,
280+
}
281+
})
282+
)
283+
}
284+
248285
const resetInput = () => {
249286
if (!messageDraft) {
250287
return
@@ -254,6 +291,11 @@ export function ChatScreen({}: StackScreenProps<HomeStackParamList, "Chat">) {
254291
messageDraft.files = undefined
255292
setText("")
256293
setImage("")
294+
resetMessageBeingEdited()
295+
}
296+
297+
function getMessageBeingEdited() {
298+
return giftedChatMappedMessages.find((m) => m.isBeingEdited)?.originalPnMessage
257299
}
258300

259301
const onSend = async () => {
@@ -262,17 +304,29 @@ export function ChatScreen({}: StackScreenProps<HomeStackParamList, "Chat">) {
262304
}
263305

264306
try {
265-
await messageDraft.send()
307+
const messageBeingEdited = getMessageBeingEdited()
308+
if (messageBeingEdited) {
309+
await messageBeingEdited.editText(messageDraft.value)
310+
} else {
311+
await messageDraft.send()
312+
}
266313
} catch (error) {
314+
let alertFn = (_: string) => null
315+
if (Platform.OS === "web") {
316+
alertFn = alert
317+
} else {
318+
alertFn = Alert.alert
319+
}
320+
321+
if (typeof error === "string") {
322+
alertFn(error)
323+
resetMessageBeingEdited()
324+
}
267325
const e = error as { status: { errorData: { status: number } } }
268326
if (e?.status?.errorData?.status !== 403) {
269327
return
270328
}
271-
if (Platform.OS === "web") {
272-
alert(`You cannot send messages to this channel: ${currentChannel?.id}`)
273-
} else {
274-
Alert.alert("You cannot send messages to this channel:", currentChannel?.id)
275-
}
329+
alertFn(`You cannot send messages to this channel: ${currentChannel?.id}`)
276330
}
277331
resetInput()
278332
}
@@ -311,13 +365,25 @@ export function ChatScreen({}: StackScreenProps<HomeStackParamList, "Chat">) {
311365
}
312366

313367
const renderBubble = (props: Bubble<EnhancedIMessage>["props"]) => {
368+
const isBeingEditedStyles = props.currentMessage.isBeingEdited
369+
? {
370+
borderWidth: 1,
371+
borderColor: colors.teal700,
372+
}
373+
: {}
374+
314375
return (
315376
<View>
316377
<Bubble
317378
{...props}
318379
wrapperStyle={{
319380
left: { padding: 12, backgroundColor: colors.neutral50 },
320-
right: { marginLeft: 0, padding: 12, backgroundColor: colors.teal100 },
381+
right: {
382+
marginLeft: 0,
383+
padding: 12,
384+
backgroundColor: colors.teal100,
385+
...isBeingEditedStyles,
386+
},
321387
}}
322388
/>
323389
{props.currentMessage?.originalPnMessage.hasThread ? (
@@ -384,7 +450,7 @@ export function ChatScreen({}: StackScreenProps<HomeStackParamList, "Chat">) {
384450
<SafeAreaView style={styles.content}>
385451
<GiftedChat
386452
messages={giftedChatMappedMessages}
387-
onSend={(messages) => onSend(messages)}
453+
onSend={onSend}
388454
onInputTextChanged={handleInputChange}
389455
renderMessageText={renderMessageText}
390456
renderFooter={renderFooter}

samples/react-native-group-chat/webpack.config.js

+5
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ module.exports = async function (env, argv) {
6565
}
6666
})
6767

68+
config.resolve.fallback = {
69+
...config.resolve.fallback,
70+
crypto: require.resolve("expo-crypto"),
71+
}
72+
6873
// Finally return the new config for the CLI to use.
6974
return config
7075
}

yarn.lock

+13-1
Original file line numberDiff line numberDiff line change
@@ -6494,7 +6494,7 @@ __metadata:
64946494
languageName: node
64956495
linkType: hard
64966496

6497-
"base64-js@npm:^1.0.2, base64-js@npm:^1.2.3, base64-js@npm:^1.3.1, base64-js@npm:^1.5.1":
6497+
"base64-js@npm:^1.0.2, base64-js@npm:^1.2.3, base64-js@npm:^1.3.0, base64-js@npm:^1.3.1, base64-js@npm:^1.5.1":
64986498
version: 1.5.1
64996499
resolution: "base64-js@npm:1.5.1"
65006500
checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf
@@ -9334,6 +9334,17 @@ __metadata:
93349334
languageName: node
93359335
linkType: hard
93369336

9337+
"expo-crypto@npm:^14.0.1":
9338+
version: 14.0.1
9339+
resolution: "expo-crypto@npm:14.0.1"
9340+
dependencies:
9341+
base64-js: "npm:^1.3.0"
9342+
peerDependencies:
9343+
expo: "*"
9344+
checksum: 10c0/3a240c83c4e4282d6c23267efdb25276fba73c9848c98eca019e8b48326941662eeb131babab04fec5913cae02a62c7e5b8f161d1657fa2e59048fa429253a5a
9345+
languageName: node
9346+
linkType: hard
9347+
93379348
"expo-file-system@npm:~17.0.1":
93389349
version: 17.0.1
93399350
resolution: "expo-file-system@npm:17.0.1"
@@ -15518,6 +15529,7 @@ __metadata:
1551815529
babel-plugin-transform-inline-environment-variables: "npm:^0.4.4"
1551915530
expo: "npm:^51.0.38"
1552015531
expo-clipboard: "npm:~6.0.3"
15532+
expo-crypto: "npm:^14.0.1"
1552115533
expo-font: "npm:~12.0.10"
1552215534
expo-status-bar: "npm:~1.12.1"
1552315535
nanoid: "npm:4.0.2"

0 commit comments

Comments
 (0)