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

[CSK-873] lock messages from being edited #191

Merged
merged 4 commits into from
Nov 26, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .pubnub.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
---
name: pubnub-js-chat
version: v0.8.3
version: v0.8.4
scm: github.com/pubnub/js-chat
schema: 1
files:
- lib/dist/index.js
changelog:
- date: 2024-11-26
version: v0.8.4
changes:
- type: feature
text: "Lock messages from being edited."
- type: feature
text: "Remove reactions list if everyone removed them."
- date: 2024-11-15
version: v0.8.3
changes:
Expand Down
2 changes: 1 addition & 1 deletion lib/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pubnub/chat",
"version": "0.8.3",
"version": "0.8.4",
"description": "PubNub JavaScript Chat SDK",
"author": "PubNub <[email protected]>",
"license": "SEE LICENSE IN LICENSE",
Expand Down
4 changes: 4 additions & 0 deletions lib/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@ export const MESSAGE_THREAD_ID_PREFIX = "PUBNUB_INTERNAL_THREAD"
export const INTERNAL_MODERATION_PREFIX = "PUBNUB_INTERNAL_MODERATION_"
export const INTERNAL_ADMIN_CHANNEL = "PUBNUB_INTERNAL_ADMIN_CHANNEL"
export const ERROR_LOGGER_KEY_PREFIX = "PUBNUB_INTERNAL_ERROR_LOGGER"
export const INTERNAL_MODERATOR_DATA = {
id: "PUBNUB_INTERNAL_MODERATOR",
type: "internal",
}
4 changes: 4 additions & 0 deletions lib/src/entities/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,10 @@ export class Message {
async editText(newText: string) {
const type = this.chat.editMessageActionName
try {
if (this.meta?.PUBNUB_INTERNAL_AUTOMODERATED && !this.chat.currentUser.isInternalModerator) {
throw "The automoderated message can no longer be edited"
}

const { data } = await this.chat.sdk.addMessageAction({
channel: this.channelId,
messageTimetoken: this.timetoken,
Expand Down
11 changes: 10 additions & 1 deletion lib/src/entities/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { Chat } from "./chat"
import { DeleteParameters, OptionalAllBut } from "../types"
import { Channel } from "./channel"
import { Membership } from "./membership"
import { INTERNAL_ADMIN_CHANNEL, INTERNAL_MODERATION_PREFIX } from "../constants"
import {
INTERNAL_ADMIN_CHANNEL,
INTERNAL_MODERATION_PREFIX,
INTERNAL_MODERATOR_DATA,
} from "../constants"
import { getErrorProxiedEntity } from "../error-logging"

export type UserFields = Pick<
Expand Down Expand Up @@ -56,6 +60,11 @@ export class User {
return getErrorProxiedEntity(new User(chat, data), chat.errorLogger)
}

/** @internal */
get isInternalModerator() {
return this.id === INTERNAL_MODERATOR_DATA.id && this.type === INTERNAL_MODERATOR_DATA.type
}

get active() {
return !!(
this.lastActiveTimestamp &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type UseActionsMenuParams = {
onQuote: (message: Message) => void
removeThreadReply?: boolean
onPinMessage: (message: Message | ThreadMessage) => void
onEditMessage: (message: Message | ThreadMessage) => void
onToggleEmoji: (message: Message) => void
onDeleteMessage: (message: Message) => void
}
Expand All @@ -29,6 +30,7 @@ export function useActionsMenu({
onPinMessage,
onToggleEmoji,
onDeleteMessage,
onEditMessage,
}: UseActionsMenuParams) {
const bottomSheetModalRef = useRef<BottomSheetModal>(null)
const navigation = useNavigation<HomeStackNavigation>()
Expand All @@ -38,7 +40,7 @@ export function useActionsMenu({
)

// variables
const snapPoints = useMemo(() => ["25%", "50%"], [])
const snapPoints = useMemo(() => ["25%", "50%", "75%"], [])

// callbacks
const handlePresentModalPress = useCallback(({ message }: { message: EnhancedIMessage }) => {
Expand All @@ -63,7 +65,7 @@ export function useActionsMenu({
const ActionsMenuComponent = () => (
<BottomSheetModal
ref={bottomSheetModalRef}
index={1}
index={2}
snapPoints={snapPoints}
onChange={handleSheetChanges}
style={styles.container}
Expand Down Expand Up @@ -161,6 +163,26 @@ export function useActionsMenu({
Pin message
</Button>
<Gap value={16} />
{currentlyFocusedMessage?.originalPnMessage.userId === chat?.currentUser.id && (
<>
<Button
size="md"
align="left"
icon="edit"
variant="outlined"
onPress={() => {
if (currentlyFocusedMessage) {
onEditMessage(currentlyFocusedMessage.originalPnMessage)
setCurrentlyFocusedMessage(null)
bottomSheetModalRef.current?.dismiss()
}
}}
>
Edit message
</Button>
<Gap value={16} />
</>
)}
</>
) : null}
{currentlyFocusedMessage?.originalPnMessage.userId === chat?.currentUser.id ? (
Expand Down
4 changes: 4 additions & 0 deletions samples/react-native-group-chat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@react-navigation/stack": "^6.3.17",
"expo": "^51.0.38",
"expo-clipboard": "~6.0.3",
"expo-crypto": "^14.0.1",
"expo-font": "~12.0.10",
"expo-status-bar": "~1.12.1",
"nanoid": "4.0.2",
Expand Down Expand Up @@ -53,5 +54,8 @@
"react-native-svg-transformer": "^1.1.0",
"typescript": "~5.3.3"
},
"browser": {
"crypto": false
},
"private": true
}
84 changes: 75 additions & 9 deletions samples/react-native-group-chat/screens/ordinary/chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from "react-native"
import { GiftedChat, Bubble } from "react-native-gifted-chat"
import { StackScreenProps } from "@react-navigation/stack"
import { User, MessageDraft, Message, Channel } from "@pubnub/chat"
import { User, MessageDraft, Message, Channel, ThreadMessage } from "@pubnub/chat"

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

const handleEditMessage = useCallback(
async (message: Message) => {
if (!currentChannel) {
return
}
const newMessageDraft = currentChannel.createMessageDraft({
userSuggestionSource: "global",
isTypingIndicatorTriggered: currentChannel.type !== "public",
})
newMessageDraft.value = message.text
setText(newMessageDraft.value)
setGiftedChatMappedMessages((curr) =>
curr.map((m) => {
return {
...m,
isBeingEdited: m.originalPnMessage.timetoken === message.timetoken,
}
})
)

setMessageDraft(newMessageDraft)
},
[currentChannel]
)

const { ActionsMenuComponent, handlePresentModalPress } = useActionsMenu({
onQuote: handleQuote,
onPinMessage: handlePin,
onToggleEmoji: handleEmoji,
onDeleteMessage: handleDeleteMessage,
onEditMessage: handleEditMessage,
})

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

function resetMessageBeingEdited() {
setGiftedChatMappedMessages((curr) =>
curr.map((m) => {
return {
...m,
isBeingEdited: false,
}
})
)
}

const resetInput = () => {
if (!messageDraft) {
return
Expand All @@ -254,6 +291,11 @@ export function ChatScreen({}: StackScreenProps<HomeStackParamList, "Chat">) {
messageDraft.files = undefined
setText("")
setImage("")
resetMessageBeingEdited()
}

function getMessageBeingEdited() {
return giftedChatMappedMessages.find((m) => m.isBeingEdited)?.originalPnMessage
}

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

try {
await messageDraft.send()
const messageBeingEdited = getMessageBeingEdited()
if (messageBeingEdited) {
await messageBeingEdited.editText(messageDraft.value)
} else {
await messageDraft.send()
}
} catch (error) {
let alertFn = (_: string) => null
if (Platform.OS === "web") {
alertFn = alert
} else {
alertFn = Alert.alert
}

if (typeof error === "string") {
alertFn(error)
resetMessageBeingEdited()
}
const e = error as { status: { errorData: { status: number } } }
if (e?.status?.errorData?.status !== 403) {
return
}
if (Platform.OS === "web") {
alert(`You cannot send messages to this channel: ${currentChannel?.id}`)
} else {
Alert.alert("You cannot send messages to this channel:", currentChannel?.id)
}
alertFn(`You cannot send messages to this channel: ${currentChannel?.id}`)
}
resetInput()
}
Expand Down Expand Up @@ -311,13 +365,25 @@ export function ChatScreen({}: StackScreenProps<HomeStackParamList, "Chat">) {
}

const renderBubble = (props: Bubble<EnhancedIMessage>["props"]) => {
const isBeingEditedStyles = props.currentMessage.isBeingEdited
? {
borderWidth: 1,
borderColor: colors.teal700,
}
: {}

return (
<View>
<Bubble
{...props}
wrapperStyle={{
left: { padding: 12, backgroundColor: colors.neutral50 },
right: { marginLeft: 0, padding: 12, backgroundColor: colors.teal100 },
right: {
marginLeft: 0,
padding: 12,
backgroundColor: colors.teal100,
...isBeingEditedStyles,
},
}}
/>
{props.currentMessage?.originalPnMessage.hasThread ? (
Expand Down Expand Up @@ -384,7 +450,7 @@ export function ChatScreen({}: StackScreenProps<HomeStackParamList, "Chat">) {
<SafeAreaView style={styles.content}>
<GiftedChat
messages={giftedChatMappedMessages}
onSend={(messages) => onSend(messages)}
onSend={onSend}
onInputTextChanged={handleInputChange}
renderMessageText={renderMessageText}
renderFooter={renderFooter}
Expand Down
5 changes: 5 additions & 0 deletions samples/react-native-group-chat/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ module.exports = async function (env, argv) {
}
})

config.resolve.fallback = {
...config.resolve.fallback,
crypto: require.resolve("expo-crypto"),
}

// Finally return the new config for the CLI to use.
return config
}
14 changes: 13 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6494,7 +6494,7 @@ __metadata:
languageName: node
linkType: hard

"base64-js@npm:^1.0.2, base64-js@npm:^1.2.3, base64-js@npm:^1.3.1, base64-js@npm:^1.5.1":
"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":
version: 1.5.1
resolution: "base64-js@npm:1.5.1"
checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf
Expand Down Expand Up @@ -9334,6 +9334,17 @@ __metadata:
languageName: node
linkType: hard

"expo-crypto@npm:^14.0.1":
version: 14.0.1
resolution: "expo-crypto@npm:14.0.1"
dependencies:
base64-js: "npm:^1.3.0"
peerDependencies:
expo: "*"
checksum: 10c0/3a240c83c4e4282d6c23267efdb25276fba73c9848c98eca019e8b48326941662eeb131babab04fec5913cae02a62c7e5b8f161d1657fa2e59048fa429253a5a
languageName: node
linkType: hard

"expo-file-system@npm:~17.0.1":
version: 17.0.1
resolution: "expo-file-system@npm:17.0.1"
Expand Down Expand Up @@ -15518,6 +15529,7 @@ __metadata:
babel-plugin-transform-inline-environment-variables: "npm:^0.4.4"
expo: "npm:^51.0.38"
expo-clipboard: "npm:~6.0.3"
expo-crypto: "npm:^14.0.1"
expo-font: "npm:~12.0.10"
expo-status-bar: "npm:~1.12.1"
nanoid: "npm:4.0.2"
Expand Down
Loading