diff --git a/src/packages/frontend/messages/list.tsx b/src/packages/frontend/messages/list.tsx index 1b7b3f59f8..9d6415fe30 100644 --- a/src/packages/frontend/messages/list.tsx +++ b/src/packages/frontend/messages/list.tsx @@ -6,8 +6,14 @@ import Message from "./message"; import { Icon } from "@cocalc/frontend/components/icon"; import { redux } from "@cocalc/frontend/app-framework"; import dayjs from "dayjs"; -import { expandToThreads, getFilteredMessages, isNullDate } from "./util"; -import { isFolder } from "./types"; +import { + expandToThreads, + getFilteredMessages, + isNullDate, + isThreadRead, + isInFolderThreaded, +} from "./util"; +import { isFolder, Folder } from "./types"; export default function MessagesList({ messages, threads, filter }) { const [showThread, setShowThread] = useState(null); @@ -18,16 +24,21 @@ export default function MessagesList({ messages, threads, filter }) { null, ); - const filteredMessages: MessageType[] = useMemo(() => { - if (messages == null || threads == null || filter == "message-compose") { - return []; - } + const folder: Folder = useMemo(() => { const folder = filter.split("-")[1]; if (!isFolder(folder)) { + // BUG -- should never happen! + return "inbox" as Folder; + } + return folder; + }, [filter]); + + const filteredMessages: MessageType[] = useMemo(() => { + if (messages == null || threads == null) { return []; } return getFilteredMessages({ messages, threads, folder }); - }, [messages, threads, filter]); + }, [messages, threads, folder]); useEffect(() => { if (checkedMessageIds.size > 0) { @@ -49,7 +60,7 @@ export default function MessagesList({ messages, threads, filter }) { useEffect(() => { setCheckedMessageIds(new Set()); setShowThread(null); - }, [filter]); + }, [folder]); if (messages == null) { return ; @@ -78,10 +89,10 @@ export default function MessagesList({ messages, threads, filter }) { /> Back - {filter != "messages-sent" && ( + {folder != "sent" && ( @@ -128,8 +139,8 @@ export default function MessagesList({ messages, threads, filter }) { return ( <> - {filter == "messages-sent" &&
} - {filter != "messages-sent" && ( + {folder == "sent" &&
} + {folder != "sent" && ( { @@ -155,9 +166,9 @@ export default function MessagesList({ messages, threads, filter }) { marginRight: "30px", }} /> - {checkedMessageIds.size > 0 && filter != "messages-sent" && ( + {checkedMessageIds.size > 0 && ( { if (shiftKey && mostRecentChecked != null) { // set the range of id's between this message and the most recent one @@ -210,7 +221,7 @@ export default function MessagesList({ messages, threads, filter }) { message={message} showThread={showThread} setShowThread={setShowThread} - filter={filter} + folder={folder} /> )} @@ -219,8 +230,9 @@ export default function MessagesList({ messages, threads, filter }) { ); } +// These are actions for an entire THREAD in all cases. function Actions({ - filter, + folder, checkedMessageIds, messages, setShowThread, @@ -230,7 +242,7 @@ function Actions({ - {filter != "messages-trash" && ( + {folder != "trash" && ( )} - {filter == "messages-trash" && ( + {folder == "trash" && ( )} - {filter != "messages-trash" && ( + {folder != "trash" && ( )} - {filter != "messages-trash" && ( + {folder != "trash" && (
e.stopPropagation()}> - read this - message + read{" "} + ) : ( <> has not read - this message ) } @@ -174,20 +183,18 @@ export function MessageInThread(props: InThreadProps) { if (props.showBody) { return ; } else { - return ( - - ); + return ; } } function MessageFull({ message, - filter, + folder, threads, inThread, setShowThread, }: Props) { - const read = isRead(message); + const read = isRead({ message, folder }); const user = ( )} @@ -263,32 +270,14 @@ function MessageFull({ justifyContent: "center", }} > - - read this - message - - ) : ( - <> - has not - read this message - - ) - } - > -   - - +
@@ -306,6 +295,13 @@ function MessageFull({ <> to + )}{" "} + {isRead({ message, folder }) ? ( + <> + (read ) + + ) : ( + <>(has not read) )}
@@ -317,27 +313,27 @@ function MessageFull({ >
- {!inThread && - message.from_type == "account" && - filter != "messages-sent" && ( -
- -
- )} + {!inThread && message.from_type == "account" && folder != "sent" && ( +
+ +
+ )}
); } -function getTag(message, filter) { +function getTag({ message, threads }) { if ( - filter != "messages-sent" && - filter != "messages-inbox" && - !message.saved && - !message.deleted + isInFolderThreaded({ + message: fromJS(message), + threads, + folder: "inbox", + }) ) { return Inbox; } + if (!isNullDate(message.expire)) { return ( >(new Set()); if (thread_id == null) { @@ -33,7 +33,7 @@ export default function Thread({ thread_id, threads, filter, style }: Props) { { if (add) { @@ -50,7 +50,13 @@ export default function Thread({ thread_id, threads, filter, style }: Props) { ); } -export function ThreadCount({ thread_id, threads }: Props) { +export function ThreadCount({ + thread_id, + threads, +}: { + thread_id?: number; + threads: iThreads; +}) { if (thread_id == null) { return null; } diff --git a/src/packages/frontend/messages/util.ts b/src/packages/frontend/messages/util.ts index 385691eaef..d43fb5c4d1 100644 --- a/src/packages/frontend/messages/util.ts +++ b/src/packages/frontend/messages/util.ts @@ -88,8 +88,60 @@ function isInFolderNotThreaded({ } } -export function isRead(message: Message) { - return !isNullDate(message.read); +// If the folder is anything but "sent", then the +// message is read if *we* have read it, where any +// message we have sent we have automatically read, and any +// message we receive is read if we mark it read. +// (except sending message to ourselves) +// If the folder is sent then things are switched: we +// care if the other person read it -- if they sent it, +// then of course they read it. If we sent it, then they +// read it if it is marked read. +// Also note that we message.read can bew new Date(0) rather +// than null! +export function isRead({ + message, + folder, +}: { + message: Message; + folder: Folder; +}) { + if (folder != "sent") { + if (isFromMe(message)) { + if (isToMe(message)) { + return !isNullDate(message.read); + } + return true; + } + return !isNullDate(message.read); + } else { + if (isFromMe(message)) { + return !isNullDate(message.read); + } + return true; + } +} + +// true if every single message in the thread is read +export function isThreadRead({ + message, + threads, + folder, +}: { + message: Message; + folder: Folder; + threads?: iThreads; +}) { + const thread_id = message.thread_id; + if (threads == null || thread_id == null) { + return isRead({ message, folder }); + } + for (const message1 of threads.get(thread_id) ?? []) { + if (!isRead({ message: message1.toJS(), folder })) { + return false; + } + } + return true; } export function isNullDate(date: Date | number | undefined | null): boolean {