diff --git a/packages/react/src/hooks/useFetchChatData.js b/packages/react/src/hooks/useFetchChatData.js index 583ad431e5..36b815b450 100644 --- a/packages/react/src/hooks/useFetchChatData.js +++ b/packages/react/src/hooks/useFetchChatData.js @@ -23,6 +23,12 @@ const useFetchChatData = (showRoles) => { const setViewUserInfoPermissions = useUserStore( (state) => state.setViewUserInfoPermissions ); + const setUserPinPermissions = useUserStore( + (state) => state.setUserPinPermissions + ); + const setEditMessagePermissions = useMessageStore( + (state) => state.setEditMessagePermissions + ); const getMessagesAndRoles = useCallback( async (anonymousMode) => { @@ -73,7 +79,13 @@ const useFetchChatData = (showRoles) => { } const permissions = await RCInstance.permissionInfo(); - setViewUserInfoPermissions(permissions.update[70]); + const permissionsMap = permissions.update.reduce((map, item) => { + map[item._id] = item; + return map; + }); + setUserPinPermissions(permissionsMap['pin-message']); + setEditMessagePermissions(permissionsMap['edit-message']); + setViewUserInfoPermissions(permissionsMap['view-full-other-user-info']); } catch (e) { console.error(e); } diff --git a/packages/react/src/hooks/useRCAuth.js b/packages/react/src/hooks/useRCAuth.js index c0b1d3a3b4..83b013353b 100644 --- a/packages/react/src/hooks/useRCAuth.js +++ b/packages/react/src/hooks/useRCAuth.js @@ -1,12 +1,7 @@ import { useContext } from 'react'; import { useToastBarDispatch } from '@embeddedchat/ui-elements'; import RCContext from '../context/RCInstance'; -import { - useUserStore, - totpModalStore, - useLoginStore, - useMessageStore, -} from '../store'; +import { useUserStore, totpModalStore, useLoginStore } from '../store'; export const useRCAuth = () => { const { RCInstance } = useContext(RCContext); @@ -25,18 +20,11 @@ export const useRCAuth = () => { ); const setPassword = useUserStore((state) => state.setPassword); const setEmailorUser = useUserStore((state) => state.setEmailorUser); - const setUserPinPermissions = useUserStore( - (state) => state.setUserPinPermissions - ); - const setEditMessagePermissions = useMessageStore( - (state) => state.setEditMessagePermissions - ); const dispatchToastMessage = useToastBarDispatch(); const handleLogin = async (userOrEmail, password, code) => { try { const res = await RCInstance.login(userOrEmail, password, code); - const permissions = await RCInstance.permissionInfo(); if (res.error === 'Unauthorized' || res.error === 403) { dispatchToastMessage({ type: 'error', @@ -68,8 +56,6 @@ export const useRCAuth = () => { setIsTotpModalOpen(false); setEmailorUser(null); setPassword(null); - setUserPinPermissions(permissions.update[150]); - setEditMessagePermissions(permissions.update[28]); dispatchToastMessage({ type: 'success', message: 'Successfully logged in', diff --git a/packages/react/src/views/EmbeddedChat.js b/packages/react/src/views/EmbeddedChat.js index 17b1ff1a41..f3b94c7b48 100644 --- a/packages/react/src/views/EmbeddedChat.js +++ b/packages/react/src/views/EmbeddedChat.js @@ -66,7 +66,6 @@ const EmbeddedChat = (props) => { const [isSynced, setIsSynced] = useState(!remoteOpt); const { getToken, saveToken, deleteToken } = getTokenStorage(secure); const { - isUserAuthenticated, setIsUserAuthenticated, setUsername: setAuthenticatedUsername, setUserAvatarUrl: setAuthenticatedAvatarUrl, @@ -84,13 +83,6 @@ const EmbeddedChat = (props) => { })); const setIsLoginIn = useLoginStore((state) => state.setIsLoginIn); - const setUserPinPermissions = useUserStore( - (state) => state.setUserPinPermissions - ); - - const setEditMessagePermissions = useMessageStore( - (state) => state.setEditMessagePermissions - ); if (isClosable && !setClosableState) { throw Error( 'Please provide a setClosableState to props when isClosable = true' @@ -132,9 +124,6 @@ const EmbeddedChat = (props) => { setIsLoginIn(true); try { await RCInstance.autoLogin(auth); - const permissions = await RCInstance.permissionInfo(); - setUserPinPermissions(permissions.update[150]); - setEditMessagePermissions(permissions.update[28]); } catch (error) { console.error(error); } finally { diff --git a/packages/react/src/views/MessageAggregators/PinnedMessages.js b/packages/react/src/views/MessageAggregators/PinnedMessages.js index 884e13529c..77ba07b835 100644 --- a/packages/react/src/views/MessageAggregators/PinnedMessages.js +++ b/packages/react/src/views/MessageAggregators/PinnedMessages.js @@ -1,14 +1,38 @@ -import React from 'react'; -import { useComponentOverrides } from '@embeddedchat/ui-elements'; +import React, { useContext } from 'react'; +import { + useComponentOverrides, + useToastBarDispatch, +} from '@embeddedchat/ui-elements'; import { MessageAggregator } from './common/MessageAggregator'; +import RCContext from '../../context/RCInstance'; const PinnedMessages = () => { const { variantOverrides } = useComponentOverrides('PinnedMessages'); const viewType = variantOverrides.viewType || 'Sidebar'; + const { RCInstance } = useContext(RCContext); + const dispatchToastMessage = useToastBarDispatch(); + + const unpin = async (msg) => { + const isPinned = msg.pinned; + const Unpin = await RCInstance.unpinMessage(msg._id); + if (Unpin.error) { + dispatchToastMessage({ + type: 'error', + message: 'Error pinning message', + }); + } else { + dispatchToastMessage({ + type: 'success', + message: isPinned ? 'Message unpinned' : 'Message pinned', + }); + } + }; return ( msg.pinned} viewType={viewType} diff --git a/packages/react/src/views/MessageAggregators/StarredMessages.js b/packages/react/src/views/MessageAggregators/StarredMessages.js index 5ced944f06..0be1e1b22c 100644 --- a/packages/react/src/views/MessageAggregators/StarredMessages.js +++ b/packages/react/src/views/MessageAggregators/StarredMessages.js @@ -1,7 +1,11 @@ -import React, { useCallback, useEffect } from 'react'; -import { useComponentOverrides } from '@embeddedchat/ui-elements'; +import React, { useCallback, useContext } from 'react'; +import { + useComponentOverrides, + useToastBarDispatch, +} from '@embeddedchat/ui-elements'; import { useStarredMessageStore, useUserStore } from '../../store'; import { MessageAggregator } from './common/MessageAggregator'; +import RCContext from '../../context/RCInstance'; const StarredMessages = () => { const authenticatedUserId = useUserStore((state) => state.userId); @@ -10,16 +14,33 @@ const StarredMessages = () => { const starredMessages = useStarredMessageStore( (state) => state.starredMessages ); + const setStarredMessages = useStarredMessageStore( + (state) => state.setStarredMessages + ); + const dispatchToastMessage = useToastBarDispatch(); + const { RCInstance } = useContext(RCContext); const shouldRender = useCallback( (msg) => msg.starred && msg.starred.some((star) => star._id === authenticatedUserId), [authenticatedUserId] ); + + const unstar = async (msg) => { + await RCInstance.unstarMessage(msg._id); + dispatchToastMessage({ + type: 'success', + message: 'Message unstarred', + }); + setStarredMessages(starredMessages.filter((str) => str._id !== msg._id)); + }; + return ( state.messages); + const currentUserRoles = useUserStore((state) => state.roles); const threadMessages = useMessageStore((state) => state.threadMessages) || []; const allMessages = useMemo( () => [...messages, ...[...threadMessages].reverse()], @@ -49,6 +56,13 @@ export const MessageAggregator = ({ fetchedMessageList || searchFiltered || allMessages, shouldRender ); + const dispatchToastMessage = useToastBarDispatch(); + const pinPermissions = useUserStore( + (state) => state.userPinPermissions.roles + ); + + const pinRoles = new Set(pinPermissions); + const isAllowedToPin = currentUserRoles.some((role) => pinRoles.has(role)); const setShowSidebar = useSidebarStore((state) => state.setShowSidebar); const openThread = useMessageStore((state) => state.openThread); @@ -101,6 +115,28 @@ export const MessageAggregator = ({ } }; + const getMessageLink = async (id) => { + const host = await RCInstance.getHost(); + const res = await RCInstance.channelInfo(); + return `${host}/channel/${res.room.name}/?msg=${id}`; + }; + + const copyLink = async (id) => { + try { + const messageLink = await getMessageLink(id); + await navigator.clipboard.writeText(messageLink); + dispatchToastMessage({ + type: 'success', + message: 'Message link copied successfully', + }); + } catch (err) { + dispatchToastMessage({ + type: 'error', + message: 'Error in copying message link', + }); + } + }; + const isMessageNewDay = (current, previous) => !previous || shouldRender(previous) || @@ -183,18 +219,61 @@ export const MessageAggregator = ({ }} /> - setJumpToMessage(msg)} - css={{ - position: 'relative', - zIndex: 10, - marginRight: '5px', - }} - > - - + {!isStarredMessageDisplay && !isPinnedMessageDisplay && ( + setJumpToMessage(msg)} + css={{ + position: 'relative', + zIndex: 10, + marginRight: '5px', + }} + > + + + )} + + {(isStarredMessageDisplay || isPinnedMessageDisplay) && ( + + unpin(msg), + label: 'Unpin', + icon: 'pin', + } + : isStarredMessageDisplay + ? { + id: 'unstar', + action: () => unstar(msg), + label: 'Remove star', + icon: 'star', + } + : {}, + { + id: 'copyLink', + action: () => copyLink(msg._id), + label: 'Copy link', + icon: 'link', + }, + { + id: 'jumptomessage', + action: () => setJumpToMessage(msg), + label: 'Jump to message', + icon: 'arrow-jump', + }, + ]} + /> + + )} )} diff --git a/packages/react/src/views/MessageAggregators/common/MessageAggregator.styles.js b/packages/react/src/views/MessageAggregators/common/MessageAggregator.styles.js index 1f18222e3e..625e3a0578 100644 --- a/packages/react/src/views/MessageAggregators/common/MessageAggregator.styles.js +++ b/packages/react/src/views/MessageAggregators/common/MessageAggregator.styles.js @@ -9,6 +9,8 @@ const getMessageAggregatorStyles = () => { justify-content: initial; align-items: initial; max-width: 100%; + overflow-y: scroll; + padding-right: 0; `, noMessageStyles: css` diff --git a/packages/ui-elements/src/components/Icon/icons/ArrowJump.js b/packages/ui-elements/src/components/Icon/icons/ArrowJump.js new file mode 100644 index 0000000000..17e69514b9 --- /dev/null +++ b/packages/ui-elements/src/components/Icon/icons/ArrowJump.js @@ -0,0 +1,9 @@ +import React from 'react'; + +const ArrowJump = (props) => ( + + + +); + +export default ArrowJump; diff --git a/packages/ui-elements/src/components/Icon/icons/index.js b/packages/ui-elements/src/components/Icon/icons/index.js index 3e23f0667d..d37e7685c6 100644 --- a/packages/ui-elements/src/components/Icon/icons/index.js +++ b/packages/ui-elements/src/components/Icon/icons/index.js @@ -63,6 +63,7 @@ import Arc from './Arc'; import Avatar from './Avatar'; import FormatText from './FormatText'; import Cog from './Cog'; +import ArrowJump from './ArrowJump'; const icons = { file: File, @@ -130,6 +131,7 @@ const icons = { avatar: Avatar, 'format-text': FormatText, cog: Cog, + 'arrow-jump': ArrowJump, }; export default icons;