diff --git a/packages/react/src/components/ChatHeader/ChatHeader.js b/packages/react/src/components/ChatHeader/ChatHeader.js index 8b9392196..ff9ee4827 100644 --- a/packages/react/src/components/ChatHeader/ChatHeader.js +++ b/packages/react/src/components/ChatHeader/ChatHeader.js @@ -12,6 +12,8 @@ import { useToastStore, useThreadsMessageStore, useMentionsStore, + usePinnedMessageStore, + useStarredMessageStore, useFileStore, } from '../../store'; import { DynamicHeader } from '../DynamicHeader'; @@ -72,6 +74,10 @@ const ChatHeader = ({ const toggleShowMembers = useMemberStore((state) => state.toggleShowMembers); const showMembers = useMemberStore((state) => state.showMembers); const setShowSearch = useSearchMessageStore((state) => state.setShowSearch); + const setShowPinned = usePinnedMessageStore((state) => state.setShowPinned); + const setShowStarred = useStarredMessageStore( + (state) => state.setShowStarred + ); const setShowAllThreads = useThreadsMessageStore( (state) => state.setShowAllThreads ); @@ -101,18 +107,12 @@ const ChatHeader = ({ }, [RCInstance, setIsUserAuthenticated]); const showStarredMessage = useCallback(async () => { - const { messages } = await RCInstance.getStarredMessages(); - setMessages(messages); - setHeaderTitle('Starred Messages'); - setFilter(true); - }, [RCInstance, setMessages, setHeaderTitle, setFilter]); + setShowStarred(true); + }, [RCInstance, setShowStarred]); const showPinnedMessage = useCallback(async () => { - const { messages } = await RCInstance.getPinnedMessages(); - setMessages(messages); - setHeaderTitle('Pinned Messages'); - setFilter(true); - }, [RCInstance, setMessages, setHeaderTitle, setFilter]); + setShowPinned(true); + }, [RCInstance, setShowPinned]); const showChannelMembers = useCallback(async () => { const { members = [] } = await RCInstance.getChannelMembers( diff --git a/packages/react/src/components/MessageList/MessageList.js b/packages/react/src/components/MessageList/MessageList.js index 14bf6fc63..c33f80555 100644 --- a/packages/react/src/components/MessageList/MessageList.js +++ b/packages/react/src/components/MessageList/MessageList.js @@ -10,6 +10,8 @@ import { useFileStore, useMentionsStore, useThreadsMessageStore, + usePinnedMessageStore, + useStarredMessageStore, } from '../../store'; import RoomMembers from '../RoomMembers/RoomMember'; import MessageReportWindow from '../ReportMessage/MessageReportWindow'; @@ -20,6 +22,8 @@ import Roominfo from '../RoomInformation/RoomInformation'; import AllThreads from '../AllThreads/AllThreads'; import { Files } from '../Files'; import { Message } from '../Message'; +import PinnedMessages from '../PinnedMessages/PinnedMessages'; +import StarredMessages from '../StarredMessages/StarredMessages'; import { Icon } from '../Icon'; const MessageList = ({ messages }) => { @@ -36,6 +40,8 @@ const MessageList = ({ messages }) => { const showAllThreads = useThreadsMessageStore( (state) => state.showAllThreads ); + const showPinned = usePinnedMessageStore((state) => state.showPinned); + const showStarred = useStarredMessageStore((state) => state.showStarred); const isMessageNewDay = (current, previous) => !previous || !isSameDay(new Date(current.ts), new Date(previous.ts)); @@ -86,6 +92,8 @@ const MessageList = ({ messages }) => { {showAllThreads && } {showAllFiles && } {showMentions && } + {showPinned && } + {showStarred && } ); }; diff --git a/packages/react/src/components/PinnedMessages/PinnedMessages.js b/packages/react/src/components/PinnedMessages/PinnedMessages.js new file mode 100644 index 000000000..409fce044 --- /dev/null +++ b/packages/react/src/components/PinnedMessages/PinnedMessages.js @@ -0,0 +1,129 @@ +import React, { useState, useContext, useMemo, useEffect } from 'react'; +import { isSameDay, format } from 'date-fns'; +import RCContext from '../../context/RCInstance'; +import classes from './PinnedMessages.module.css'; +import { usePinnedMessageStore } from '../../store'; +import { Box } from '../Box'; +import { Icon } from '../Icon'; +import { ActionButton } from '../ActionButton'; +import { MessageDivider } from '../Message/MessageDivider'; +import { Message } from '../Message'; +import isMessageSequential from '../../lib/isMessageSequential'; +import { Throbber } from '../Throbber'; + +const PinnedMessages = () => { + const { RCInstance } = useContext(RCContext); + const setShowPinned = usePinnedMessageStore((state) => state.setShowPinned); + + const [messageList, setMessageList] = useState([]); + const [loading, setLoading] = useState(true); + + const toggleShowPinned = () => { + setShowPinned(false); + }; + + const getPinnedMessages = async () => { + const { messages } = await RCInstance.getPinnedMessages(); + setMessageList(messages); + setLoading(false); + }; + + useEffect(() => { + if (messageList.length === 0) { + getPinnedMessages(); + } + }, [messageList, getPinnedMessages]); + + const isMessageNewDay = (current, previous) => + !previous || !isSameDay(new Date(current.ts), new Date(previous.ts)); + + return ( + + + +

+ + + Pinned Messages + + + + +

+
+ + {loading ? ( + + + + ) : messageList.length === 0 ? ( + + + + No pinned messages + + + ) : ( + messageList.map((msg, index, arr) => { + const prev = arr[index + 1]; + const newDay = isMessageNewDay(msg, prev); + const sequential = isMessageSequential(msg, prev, 300); + return ( + + {newDay && ( + + + {format(new Date(msg.ts), 'MMMM d, yyyy')} + + + )} + + + ); + }) + )} + +
+
+ ); +}; +export default PinnedMessages; diff --git a/packages/react/src/components/PinnedMessages/PinnedMessages.module.css b/packages/react/src/components/PinnedMessages/PinnedMessages.module.css new file mode 100644 index 000000000..d8eba8916 --- /dev/null +++ b/packages/react/src/components/PinnedMessages/PinnedMessages.module.css @@ -0,0 +1,23 @@ +.sidebar { + position: fixed; + right: 0; + top: 0; + width: 350px; + height: 100%; + overflow-x: scroll; + overflow-y: scroll; + background-color: white; + box-shadow: -1px 0px 5px rgb(0 0 0 / 25%); + z-index: 100; +} + +.wrapContainer { + height: 100%; + display: flex; + flex-direction: column; +} +@media (max-width: 550px) { + .sidebar { + width: 100vw; + } +} diff --git a/packages/react/src/components/StarredMessages/StarredMessages.js b/packages/react/src/components/StarredMessages/StarredMessages.js new file mode 100644 index 000000000..28be5f148 --- /dev/null +++ b/packages/react/src/components/StarredMessages/StarredMessages.js @@ -0,0 +1,131 @@ +import React, { useState, useContext, useMemo, useEffect } from 'react'; +import { isSameDay, format } from 'date-fns'; +import RCContext from '../../context/RCInstance'; +import classes from './StarredMessages.module.css'; +import { useStarredMessageStore } from '../../store'; +import { Box } from '../Box'; +import { Icon } from '../Icon'; +import { ActionButton } from '../ActionButton'; +import { MessageDivider } from '../Message/MessageDivider'; +import { Message } from '../Message'; +import isMessageSequential from '../../lib/isMessageSequential'; +import { Throbber } from '../Throbber'; + +const StarredMessages = () => { + const { RCInstance } = useContext(RCContext); + const setShowStarred = useStarredMessageStore( + (state) => state.setShowStarred + ); + + const [messageList, setMessageList] = useState([]); + const [loading, setLoading] = useState(true); + + const toggleShowStarred = () => { + setShowStarred(false); + }; + + const getStarredMessages = async () => { + const { messages } = await RCInstance.getStarredMessages(); + setMessageList(messages); + setLoading(false); + }; + + useEffect(() => { + if (messageList.length === 0) { + getStarredMessages(); + } + }, [messageList, getStarredMessages]); + + const isMessageNewDay = (current, previous) => + !previous || !isSameDay(new Date(current.ts), new Date(previous.ts)); + + return ( + + + +

+ + + Starred Messages + + + + +

+
+ + {loading ? ( + + + + ) : messageList.length === 0 ? ( + + + + No starred messages + + + ) : ( + messageList.map((msg, index, arr) => { + const prev = arr[index + 1]; + const newDay = isMessageNewDay(msg, prev); + const sequential = isMessageSequential(msg, prev, 300); + return ( + + {newDay && ( + + + {format(new Date(msg.ts), 'MMMM d, yyyy')} + + + )} + + + ); + }) + )} + +
+
+ ); +}; +export default StarredMessages; diff --git a/packages/react/src/components/StarredMessages/StarredMessages.module.css b/packages/react/src/components/StarredMessages/StarredMessages.module.css new file mode 100644 index 000000000..d8eba8916 --- /dev/null +++ b/packages/react/src/components/StarredMessages/StarredMessages.module.css @@ -0,0 +1,23 @@ +.sidebar { + position: fixed; + right: 0; + top: 0; + width: 350px; + height: 100%; + overflow-x: scroll; + overflow-y: scroll; + background-color: white; + box-shadow: -1px 0px 5px rgb(0 0 0 / 25%); + z-index: 100; +} + +.wrapContainer { + height: 100%; + display: flex; + flex-direction: column; +} +@media (max-width: 550px) { + .sidebar { + width: 100vw; + } +} diff --git a/packages/react/src/store/index.js b/packages/react/src/store/index.js index 9fd11e4c3..c9a8cbf03 100644 --- a/packages/react/src/store/index.js +++ b/packages/react/src/store/index.js @@ -9,3 +9,5 @@ export { default as useChannelStore } from './channelStore'; export { default as useThreadsMessageStore } from './threadsMessageStore'; export { default as useFileStore } from './fileStore'; export { default as useMentionsStore } from './mentionsStore'; +export { default as usePinnedMessageStore } from './pinnedMessageStore'; +export { default as useStarredMessageStore } from './starredMessageStore'; diff --git a/packages/react/src/store/pinnedMessageStore.js b/packages/react/src/store/pinnedMessageStore.js new file mode 100644 index 000000000..36ee4e385 --- /dev/null +++ b/packages/react/src/store/pinnedMessageStore.js @@ -0,0 +1,8 @@ +import { create } from 'zustand'; + +const usePinnedMessageStore = create((set) => ({ + showPinned: false, + setShowPinned: (showPinned) => set(() => ({ showPinned })), +})); + +export default usePinnedMessageStore; diff --git a/packages/react/src/store/starredMessageStore.js b/packages/react/src/store/starredMessageStore.js new file mode 100644 index 000000000..a564df3c4 --- /dev/null +++ b/packages/react/src/store/starredMessageStore.js @@ -0,0 +1,8 @@ +import { create } from 'zustand'; + +const useStarredMessageStore = create((set) => ({ + showStarred: false, + setShowStarred: (showStarred) => set(() => ({ showStarred })), +})); + +export default useStarredMessageStore;