From f9c342ef1e73e8b60501a35b67a2ad9e363f07e0 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Fri, 14 Jun 2024 11:29:12 -0500 Subject: [PATCH 1/6] replies: fix reply meta updating --- apps/tlon-web/src/state/channel/channel.ts | 10 +- apps/tlon-web/src/state/chat/chat.ts | 151 ++++++++++----------- 2 files changed, 73 insertions(+), 88 deletions(-) diff --git a/apps/tlon-web/src/state/channel/channel.ts b/apps/tlon-web/src/state/channel/channel.ts index c85bcbaa0c..2050078b48 100644 --- a/apps/tlon-web/src/state/channel/channel.ts +++ b/apps/tlon-web/src/state/channel/channel.ts @@ -39,6 +39,7 @@ import { Flag } from '@tloncorp/shared/dist/urbit/hark'; import { daToUnix, decToUd, udToDec, unixToDa } from '@urbit/api'; import { Poke } from '@urbit/http-api'; import bigInt from 'big-integer'; +import produce from 'immer'; import _, { last } from 'lodash'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import create from 'zustand'; @@ -468,7 +469,7 @@ function updateReplyMetaData( return undefined; } - const newPages = cache.pages.map((page) => { + cache.pages = cache.pages.map((page) => { const newPage = { ...page, }; @@ -497,11 +498,6 @@ function updateReplyMetaData( return newPage; }); - - return { - pages: newPages, - pageParams: cache.pageParams, - }; } const replyUpdater = ( @@ -1050,7 +1046,7 @@ export function useChannelsFirehose() { } = postResponse; queryClient.setQueryData( infinitePostsKey(nest), - (d) => updateReplyMetaData(d, time, meta) + (d) => produce(d, (draft) => updateReplyMetaData(draft, time, meta)) ); }); } diff --git a/apps/tlon-web/src/state/chat/chat.ts b/apps/tlon-web/src/state/chat/chat.ts index e5b4e6f9ba..a38e96c812 100644 --- a/apps/tlon-web/src/state/chat/chat.ts +++ b/apps/tlon-web/src/state/chat/chat.ts @@ -42,6 +42,7 @@ import { decToUd, udToDec } from '@urbit/api'; import { formatUd, unixToDa } from '@urbit/aura'; import { Poke } from '@urbit/http-api'; import bigInt, { BigInteger } from 'big-integer'; +import produce from 'immer'; import _ from 'lodash'; import { useCallback, useEffect, useMemo, useRef } from 'react'; import create from 'zustand'; @@ -457,120 +458,108 @@ function infiniteDMsUpdater(queryKey: QueryKey, data: WritDiff | WritResponse) { const replyParentQueryKey = ['dms', whom, id]; const { reply } = delta; if ('add' in reply.delta) { - queryClient.setQueriesData(queryKey, (queryData) => { - if (queryData === undefined) { - return undefined; - } + queryClient.setQueriesData(queryKey, (queryData) => + produce(queryData, (draft) => { + if (draft === undefined) { + return; + } - const allWritsInPages = queryData.pages.flatMap((page) => - Object.entries(page.writs) - ); + const allWritsInPages = draft.pages.flatMap((page) => + Object.entries(page.writs) + ); - const writFind = allWritsInPages.find(([k, w]) => w.seal.id === id); + const writFind = allWritsInPages.find(([k, w]) => w.seal.id === id); + + if (!writFind) { + return; + } - if (writFind) { - const replyId = writFind[0]; - const replyingWrit = writFind[1]; + const opId = writFind[0]; + const opWrit = writFind[1]; - if (replyingWrit === undefined || replyingWrit === null) { - return queryData; + if (opWrit === undefined || opWrit === null) { + return; } - const replyAuthor = + const opAuthor = 'add' in reply.delta ? reply.delta.add.memo.author : ''; // should never happen const updatedWrit = { - ...replyingWrit, + ...opWrit, seal: { - ...replyingWrit.seal, + ...opWrit.seal, meta: { - ...replyingWrit.seal.meta, - replyCount: replyingWrit.seal.meta.replyCount + 1, - repliers: [...replyingWrit.seal.meta.lastRepliers, replyAuthor], + ...opWrit.seal.meta, + replyCount: opWrit.seal.meta.replyCount + 1, + repliers: [...opWrit.seal.meta.lastRepliers, opAuthor], }, }, }; - const pageInCache = queryData.pages.find((page) => - Object.keys(page.writs).some((k) => k === replyId) + const pageInCache = draft.pages.find((page) => + Object.keys(page.writs).some((k) => k === opId) ); - const pageInCacheIdx = queryData.pages.findIndex((page) => - Object.keys(page.writs).some((k) => k === replyId) + const pageInCacheIdx = draft.pages.findIndex((page) => + Object.keys(page.writs).some((k) => k === opId) ); if (pageInCache === undefined) { - return queryData; + return; } - return { - pages: [ - ...queryData.pages.slice(0, pageInCacheIdx), - { - ...pageInCache, - writs: { - ...pageInCache.writs, - [replyId]: updatedWrit, - }, - }, - ...queryData.pages.slice(pageInCacheIdx + 1), - ], - pageParams: queryData.pageParams, - }; - } + pageInCache.writs[opId] = updatedWrit; + draft.pages = [ + ...draft.pages.slice(0, pageInCacheIdx), + pageInCache, + ...draft.pages.slice(pageInCacheIdx + 1), + ]; - return { - pages: queryData.pages, - pageParams: queryData.pageParams, - }; - }); + return; + }) + ); queryClient.setQueryData( replyParentQueryKey, - (queryData: WritInCache | undefined) => { - if (queryData === undefined) { - return undefined; - } + (queryData: WritInCache | undefined) => + produce(queryData, (draft) => { + if (draft === undefined) { + return; + } - if (!('add' in reply.delta)) { - return queryData; - } - const { memo } = reply.delta.add; + if (!('add' in reply.delta)) { + return; + } + const { id: replyId } = reply; + const { memo } = reply.delta.add; - const prevWrit = queryData as WritInCache; - const prevReplies = prevWrit.seal.replies; + const prevWrit = queryData as WritInCache; + const prevReplies = prevWrit.seal.replies; - const hasInCache = Object.entries(prevReplies).find(([k, r]) => { - return r.memo.sent === memo.sent && r.memo.author === memo.author; - }); + const hasInCache = Object.entries(prevReplies).find(([k, r]) => { + return r.memo.sent === memo.sent && r.memo.author === memo.author; + }); - if (hasInCache) { - return queryData; - } + if (hasInCache) { + return; + } - const replyId = unixToDa(memo.sent).toString(); - const newReply: Reply = { - seal: { - id: replyId, - 'parent-id': id!, - reacts: {}, - }, - memo, - }; + const newReply: Reply = { + seal: { + id: replyId, + 'parent-id': id!, + reacts: {}, + }, + memo, + }; - const newReplies = { - ...prevReplies, - [replyId]: newReply, - }; + const newReplies = { + ...prevReplies, + [formatUd(bigInt(reply.delta.add.time!))]: newReply, + }; - return { - ...prevWrit, - seal: { - ...prevWrit.seal, - replies: newReplies, - }, - }; - } + draft.seal.replies = newReplies; + }) ); } } From 3e08550429f6441dce7ca6d59b3d922ae58ba919 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Fri, 14 Jun 2024 11:29:41 -0500 Subject: [PATCH 2/6] chat-msg: actually check for unseen for divider --- apps/tlon-web/src/chat/ChatMessage/ChatMessage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/tlon-web/src/chat/ChatMessage/ChatMessage.tsx b/apps/tlon-web/src/chat/ChatMessage/ChatMessage.tsx index e78b270da5..d66913f1cb 100644 --- a/apps/tlon-web/src/chat/ChatMessage/ChatMessage.tsx +++ b/apps/tlon-web/src/chat/ChatMessage/ChatMessage.tsx @@ -93,7 +93,7 @@ function getUnreadDisplay( id: string, thread: Unread | undefined ): 'none' | 'top' | 'thread' | 'top-with-thread' { - const isTop = unread?.lastUnread?.id === id; + const isTop = unread?.lastUnread?.id === id && unread.status !== 'read'; // if this message is the oldest unread in the main chat, // and has an unread thread, show the divider and thread indicator From cfebbc71ca30e4e30b3443690034059b4e45d11b Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Fri, 14 Jun 2024 11:30:20 -0500 Subject: [PATCH 3/6] unreads: cleanup and prevent bad state transitions --- apps/tlon-web/src/chat/UnreadAlerts.tsx | 4 +--- apps/tlon-web/src/state/unreads.ts | 25 ++++++++++++------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/apps/tlon-web/src/chat/UnreadAlerts.tsx b/apps/tlon-web/src/chat/UnreadAlerts.tsx index dc9b5c0820..dba094d6cf 100644 --- a/apps/tlon-web/src/chat/UnreadAlerts.tsx +++ b/apps/tlon-web/src/chat/UnreadAlerts.tsx @@ -1,4 +1,4 @@ -import { ActivitySummary, getKey } from '@tloncorp/shared/dist/urbit/activity'; +import { getKey } from '@tloncorp/shared/dist/urbit/activity'; import { daToUnix } from '@urbit/api'; import bigInt from 'big-integer'; import { format, isToday } from 'date-fns'; @@ -11,8 +11,6 @@ import { pluralize, whomIsFlag } from '@/logic/utils'; import { useMarkDmReadMutation } from '@/state/chat'; import { useUnread, useUnreadsStore } from '@/state/unreads'; -import { useChatStore } from './useChatStore'; - interface UnreadAlertsProps { whom: string; root: string; diff --git a/apps/tlon-web/src/state/unreads.ts b/apps/tlon-web/src/state/unreads.ts index f11fff21b8..28c6aec7f0 100644 --- a/apps/tlon-web/src/state/unreads.ts +++ b/apps/tlon-web/src/state/unreads.ts @@ -48,7 +48,7 @@ export interface UnreadsStore { update: (unreads: Activity) => void; } -export const unreadStoreLogger = createDevLogger('UnreadsStore', false); +export const unreadStoreLogger = createDevLogger('UnreadsStore', true); function getUnreadStatus(count: number, notify: boolean): ReadStatus { if (count > 0 || notify) { @@ -254,14 +254,9 @@ export const useUnreadsStore = create((set, get) => ({ }) .forEach(([key, summary]) => { const source = draft.sources[key]; - unreadStoreLogger.log( - 'update', - key, - source, - summary, - draft.sources - ); + unreadStoreLogger.log('update', key, { ...source }, { ...summary }); const unread = getUnread(key, summary, draft.sources); + unreadStoreLogger.log('new unread', key, unread); draft.sources[key] = unread; Object.keys(unread.children || {}).forEach((child) => { @@ -284,11 +279,10 @@ export const useUnreadsStore = create((set, get) => ({ seen: (key) => { set( produce((draft: UnreadsStore) => { - if (!draft.sources[key]) { - draft.sources[key] = emptyUnread(); - } - const source = draft.sources[key]; + if (!source || source.status !== 'unread') { + return; + } unreadStoreLogger.log('seen', key); draft.sources[key] = { @@ -314,7 +308,7 @@ export const useUnreadsStore = create((set, get) => ({ set( produce((draft: UnreadsStore) => { const source = draft.sources[key]; - if (!source) { + if (!source || source.status === 'read') { return; } @@ -347,12 +341,17 @@ export const useUnreadsStore = create((set, get) => ({ delayedRead: (key, cb) => { const { sources, read } = get(); const source = sources[key] || emptyUnread(); + debugger; + if (source.status === 'read') { + return; + } if (source.readTimeout) { clearTimeout(source.readTimeout); } const readTimeout = setTimeout(() => { + unreadStoreLogger.log('delayedRead timeout reached', key); read(key); cb(); }, 15 * 1000); // 15 seconds From ac388e5f38f1a283b6fa6d1befeda1ec077fd92e Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Fri, 14 Jun 2024 11:30:48 -0500 Subject: [PATCH 4/6] unreads: fix delayed read clearing on navigation --- .../src/chat/ChatThread/ChatThread.tsx | 34 +++++-------- apps/tlon-web/src/chat/ChatWindow.tsx | 33 +++++++------ apps/tlon-web/src/dms/DMThread.tsx | 48 ++++++++----------- apps/tlon-web/src/dms/DmWindow.tsx | 33 +++++++------ 4 files changed, 66 insertions(+), 82 deletions(-) diff --git a/apps/tlon-web/src/chat/ChatThread/ChatThread.tsx b/apps/tlon-web/src/chat/ChatThread/ChatThread.tsx index 02d16229dc..5239800758 100644 --- a/apps/tlon-web/src/chat/ChatThread/ChatThread.tsx +++ b/apps/tlon-web/src/chat/ChatThread/ChatThread.tsx @@ -44,7 +44,7 @@ import { useRouteGroup, useVessel, } from '@/state/groups/groups'; -import { useUnread, useUnreadsStore } from '@/state/unreads'; +import { unreadStoreLogger, useUnread, useUnreadsStore } from '@/state/unreads'; import ChatScrollerPlaceholder from '../ChatScroller/ChatScrollerPlaceholder'; import { chatStoreLogger, useChatStore } from '../useChatStore'; @@ -133,11 +133,7 @@ export default function ChatThread() { const { compatible, text } = useChannelCompatibility(`chat/${flag}`); const { paddingBottom } = useBottomPadding(); const readTimeout = useUnread(chatUnreadsKey)?.readTimeout; - const clearOnNavRef = useRef({ - readTimeout, - chatUnreadsKey, - markRead, - }); + const path = location.pathname; const activeTab = useActiveTab(); const returnURL = useCallback( @@ -175,24 +171,16 @@ export default function ChatThread() { // read the messages once navigated away useEffect(() => { - clearOnNavRef.current = { - readTimeout, - chatUnreadsKey, - markRead, - }; - }, [readTimeout, chatUnreadsKey, markRead]); - - useEffect( - () => () => { - const curr = clearOnNavRef.current; - if (curr.readTimeout !== undefined && curr.readTimeout !== 0) { - chatStoreLogger.log('unmount read from thread'); - useUnreadsStore.getState().read(curr.chatUnreadsKey); - curr.markRead(); + return () => { + const winPath = window.location.pathname.replace('/apps/groups', ''); + if (winPath !== path && readTimeout) { + unreadStoreLogger.log(winPath, path); + unreadStoreLogger.log('marking read from dismount', chatUnreadsKey); + useUnreadsStore.getState().read(chatUnreadsKey); + markRead(); } - }, - [] - ); + }; + }, [path, readTimeout, chatUnreadsKey, markRead]); useEffect(() => { if (!idTimeIsNumber) { diff --git a/apps/tlon-web/src/chat/ChatWindow.tsx b/apps/tlon-web/src/chat/ChatWindow.tsx index 06054f9265..9e8789aa93 100644 --- a/apps/tlon-web/src/chat/ChatWindow.tsx +++ b/apps/tlon-web/src/chat/ChatWindow.tsx @@ -8,7 +8,12 @@ import React, { useRef, useState, } from 'react'; -import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; +import { + useLocation, + useNavigate, + useParams, + useSearchParams, +} from 'react-router-dom'; import { VirtuosoHandle } from 'react-virtuoso'; import ChatScroller from '@/chat/ChatScroller/ChatScroller'; @@ -17,7 +22,7 @@ import ArrowS16Icon from '@/components/icons/ArrowS16Icon'; import { useChannelCompatibility, useMarkChannelRead } from '@/logic/channel'; import { log } from '@/logic/utils'; import { useInfinitePosts } from '@/state/channel/channel'; -import { useUnread, useUnreadsStore } from '@/state/unreads'; +import { unreadStoreLogger, useUnread, useUnreadsStore } from '@/state/unreads'; import ChatScrollerPlaceholder from './ChatScroller/ChatScrollerPlaceholder'; import UnreadAlerts from './UnreadAlerts'; @@ -38,6 +43,7 @@ const ChatWindow = React.memo(function ChatWindowRaw({ scrollElementRef, isScrolling, }: ChatWindowProps) { + const location = useLocation(); const [searchParams, setSearchParams] = useSearchParams(); const { idTime } = useParams(); const scrollToId = useMemo( @@ -65,7 +71,7 @@ const ChatWindow = React.memo(function ChatWindowRaw({ const [showUnreadBanner, setShowUnreadBanner] = useState(false); const unreadsKey = getKey(whom); const readTimeout = useUnread(unreadsKey)?.readTimeout; - const clearOnNavRef = useRef({ readTimeout, nest, unreadsKey, markRead }); + const path = location.pathname; const { compatible } = useChannelCompatibility(nest); const navigate = useNavigate(); const latestMessageIndex = messages.length - 1; @@ -165,19 +171,16 @@ const ChatWindow = React.memo(function ChatWindowRaw({ // read the messages once navigated away useEffect(() => { - clearOnNavRef.current = { readTimeout, nest, unreadsKey, markRead }; - }, [readTimeout, nest, unreadsKey, markRead]); - - useEffect( - () => () => { - const curr = clearOnNavRef.current; - if (curr.readTimeout !== undefined && curr.readTimeout !== 0) { - useUnreadsStore.getState().read(curr.unreadsKey); - curr.markRead(); + return () => { + const winPath = window.location.pathname.replace('/apps/groups', ''); + if (winPath !== path && readTimeout) { + unreadStoreLogger.log(winPath, path); + unreadStoreLogger.log('marking read from dismount', unreadsKey); + useUnreadsStore.getState().read(unreadsKey); + markRead(); } - }, - [] - ); + }; + }, [path, readTimeout, unreadsKey, markRead]); useEffect(() => { const doRefetch = async () => { diff --git a/apps/tlon-web/src/dms/DMThread.tsx b/apps/tlon-web/src/dms/DMThread.tsx index ebe6ba9a47..fba50714d2 100644 --- a/apps/tlon-web/src/dms/DMThread.tsx +++ b/apps/tlon-web/src/dms/DMThread.tsx @@ -1,4 +1,8 @@ -import { MessageKey, getKey } from '@tloncorp/shared/dist/urbit/activity'; +import { + MessageKey, + getKey, + getThreadKey, +} from '@tloncorp/shared/dist/urbit/activity'; import { ReplyTuple } from '@tloncorp/shared/dist/urbit/channel'; import { formatUd } from '@urbit/aura'; import bigInt from 'big-integer'; @@ -32,14 +36,14 @@ import keyMap from '@/keyMap'; import { useDragAndDrop } from '@/logic/DragAndDropContext'; import { useBottomPadding } from '@/logic/position'; import { useIsScrolling } from '@/logic/scroll'; -import useMedia, { useIsMobile } from '@/logic/useMedia'; +import { useIsMobile } from '@/logic/useMedia'; import { useMarkDmReadMutation, useMultiDm, useSendReplyMutation, useWrit, } from '@/state/chat'; -import { useUnread, useUnreadsStore } from '@/state/unreads'; +import { unreadStoreLogger, useUnread, useUnreadsStore } from '@/state/unreads'; export default function DMThread() { const { ship, idTime, idShip } = useParams<{ @@ -68,16 +72,8 @@ export default function DMThread() { const scrollElementRef = useRef(null); const isScrolling = useIsScrolling(scrollElementRef); const { paddingBottom } = useBottomPadding(); - const unreadsKey = getKey(whom); + const unreadsKey = getThreadKey(whom, id); const readTimeout = useUnread(unreadsKey)?.readTimeout; - const { markDmRead } = useMarkDmReadMutation(whom); - const isSmall = useMedia('(max-width: 1023px)'); - const clearOnNavRef = useRef({ - isSmall, - readTimeout, - unreadsKey, - markDmRead, - }); const msgKey: MessageKey = useMemo( () => ({ id, @@ -85,6 +81,8 @@ export default function DMThread() { }), [id, time] ); + const { markDmRead } = useMarkDmReadMutation(whom, msgKey); + const path = location.pathname; const isClub = ship ? (ob.isValidPatp(ship) ? false : true) : false; const club = useMultiDm(ship || ''); @@ -138,24 +136,16 @@ export default function DMThread() { // read the messages once navigated away useEffect(() => { - clearOnNavRef.current = { isSmall, readTimeout, unreadsKey, markDmRead }; - }, [readTimeout, unreadsKey, isSmall, markDmRead]); - - useEffect( - () => () => { - const curr = clearOnNavRef.current; - if ( - curr.isSmall && - curr.readTimeout !== undefined && - curr.readTimeout !== 0 - ) { - chatStoreLogger.log('unmount read from thread'); - useUnreadsStore.getState().read(curr.unreadsKey); - curr.markDmRead(); + return () => { + const winPath = window.location.pathname.replace('/apps/groups', ''); + if (winPath !== path && readTimeout) { + unreadStoreLogger.log(winPath, path); + unreadStoreLogger.log('marking read from dismount', unreadsKey); + useUnreadsStore.getState().read(unreadsKey); + markDmRead(); } - }, - [] - ); + }; + }, [path, readTimeout, unreadsKey, markDmRead]); if (!writ || isLoading) return null; diff --git a/apps/tlon-web/src/dms/DmWindow.tsx b/apps/tlon-web/src/dms/DmWindow.tsx index ab7bbd5409..fd49068322 100644 --- a/apps/tlon-web/src/dms/DmWindow.tsx +++ b/apps/tlon-web/src/dms/DmWindow.tsx @@ -3,7 +3,12 @@ import { WritTuple } from '@tloncorp/shared/dist/urbit/dms'; import { udToDec } from '@urbit/api'; import bigInt from 'big-integer'; import { ReactElement, useCallback, useEffect, useMemo, useRef } from 'react'; -import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; +import { + useLocation, + useNavigate, + useParams, + useSearchParams, +} from 'react-router-dom'; import { VirtuosoHandle } from 'react-virtuoso'; import ChatScroller from '@/chat/ChatScroller/ChatScroller'; @@ -14,7 +19,7 @@ import ArrowS16Icon from '@/components/icons/ArrowS16Icon'; import { useIsScrolling } from '@/logic/scroll'; import { getPatdaParts, log } from '@/logic/utils'; import { useInfiniteDMs, useMarkDmReadMutation } from '@/state/chat'; -import { useUnread, useUnreadsStore } from '@/state/unreads'; +import { unreadStoreLogger, useUnread, useUnreadsStore } from '@/state/unreads'; interface DmWindowProps { whom: string; @@ -33,6 +38,7 @@ export default function DmWindow({ root, prefixedElement, }: DmWindowProps) { + const location = useLocation(); const [searchParams, setSearchParams] = useSearchParams(); const { idTime } = useParams(); const scrollToId = useMemo( @@ -45,7 +51,7 @@ export default function DmWindow({ const scrollElementRef = useRef(null); const isScrolling = useIsScrolling(scrollElementRef); const { markDmRead } = useMarkDmReadMutation(whom); - const clearOnNavRef = useRef({ readTimeout, unreadsKey, markDmRead }); + const path = location.pathname; const { writs, @@ -138,19 +144,16 @@ export default function DmWindow({ // read the messages once navigated away useEffect(() => { - clearOnNavRef.current = { readTimeout, unreadsKey, markDmRead }; - }, [readTimeout, unreadsKey, markDmRead]); - - useEffect( - () => () => { - const curr = clearOnNavRef.current; - if (curr.readTimeout !== undefined && curr.readTimeout !== 0) { - useUnreadsStore.getState().read(curr.unreadsKey); - curr.markDmRead(); + return () => { + const winPath = window.location.pathname.replace('/apps/groups', ''); + if (winPath !== path && readTimeout) { + unreadStoreLogger.log(winPath, path); + unreadStoreLogger.log('marking read from dismount', unreadsKey); + useUnreadsStore.getState().read(unreadsKey); + markDmRead(); } - }, - [] - ); + }; + }, [path, readTimeout, unreadsKey, markDmRead]); useEffect(() => { const doRefetch = async () => { From 0444560f53dff8a90c319e2ec0dc8f2c3b7f052c Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Fri, 14 Jun 2024 11:48:34 -0500 Subject: [PATCH 5/6] chat-msg: prevent banner from getting stuck by marking seen when unreads changes --- .../src/chat/ChatMessage/ChatMessage.tsx | 82 +++++++++---------- 1 file changed, 37 insertions(+), 45 deletions(-) diff --git a/apps/tlon-web/src/chat/ChatMessage/ChatMessage.tsx b/apps/tlon-web/src/chat/ChatMessage/ChatMessage.tsx index d66913f1cb..a8d23348a5 100644 --- a/apps/tlon-web/src/chat/ChatMessage/ChatMessage.tsx +++ b/apps/tlon-web/src/chat/ChatMessage/ChatMessage.tsx @@ -207,55 +207,47 @@ const ChatMessage = React.memo< () => isMessageHidden || isPostHidden, [isMessageHidden, isPostHidden] ); - const { ref: viewRef } = useInView({ + + const { ref: viewRef, inView } = useInView({ threshold: 1, - onChange: useCallback( - (inView: boolean) => { - // if no tracked unread we don't need to take any action - if (!unread) { - return; - } + }); - const unseen = unread.status === 'unread'; - /* the first fire of this function - which we don't to do anything with. */ - if (!inView && unseen) { - return; - } + useEffect(() => { + if (!inView || !unread) { + return; + } - const { seen: markSeen, delayedRead } = useUnreadsStore.getState(); - /* once the unseen marker comes into view we need to mark it - as seen and start a timer to mark it read so it goes away. - we ensure that the brief matches and hasn't changed before - doing so. we don't want to accidentally clear unreads when - the state has changed - */ - if ( - inView && - (unreadDisplay === 'top' || - unreadDisplay === 'top-with-thread') && - unseen - ) { - markSeen(unreadsKey); - delayedRead(unreadsKey, () => { - if (isDMOrMultiDM) { - markDmRead(); - } else { - markReadChannel(); - } - }); + const unseen = unread.status === 'unread'; + const { seen: markSeen, delayedRead } = useUnreadsStore.getState(); + /* once the unseen marker comes into view we need to mark it + as seen and start a timer to mark it read so it goes away. + we ensure that the brief matches and hasn't changed before + doing so. we don't want to accidentally clear unreads when + the state has changed + */ + if ( + inView && + (unreadDisplay === 'top' || unreadDisplay === 'top-with-thread') && + unseen + ) { + markSeen(unreadsKey); + delayedRead(unreadsKey, () => { + if (isDMOrMultiDM) { + markDmRead(); + } else { + markReadChannel(); } - }, - [ - unreadDisplay, - unread, - unreadsKey, - isDMOrMultiDM, - markReadChannel, - markDmRead, - ] - ), - }); + }); + } + }, [ + inView, + unread, + unreadsKey, + unreadDisplay, + isDMOrMultiDM, + markReadChannel, + markDmRead, + ]); const cacheId = { author: window.our, From 9daa132d430f76ea12d96f1e47fb1eea91a7afdd Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Fri, 14 Jun 2024 12:13:18 -0500 Subject: [PATCH 6/6] unreads: cleanup from PR review --- apps/tlon-web/src/state/chat/chat.ts | 4 ++-- apps/tlon-web/src/state/unreads.ts | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/tlon-web/src/state/chat/chat.ts b/apps/tlon-web/src/state/chat/chat.ts index a38e96c812..886603aa53 100644 --- a/apps/tlon-web/src/state/chat/chat.ts +++ b/apps/tlon-web/src/state/chat/chat.ts @@ -481,7 +481,7 @@ function infiniteDMsUpdater(queryKey: QueryKey, data: WritDiff | WritResponse) { return; } - const opAuthor = + const replyingAuthor = 'add' in reply.delta ? reply.delta.add.memo.author : ''; // should never happen const updatedWrit = { @@ -491,7 +491,7 @@ function infiniteDMsUpdater(queryKey: QueryKey, data: WritDiff | WritResponse) { meta: { ...opWrit.seal.meta, replyCount: opWrit.seal.meta.replyCount + 1, - repliers: [...opWrit.seal.meta.lastRepliers, opAuthor], + repliers: [...opWrit.seal.meta.lastRepliers, replyingAuthor], }, }, }; diff --git a/apps/tlon-web/src/state/unreads.ts b/apps/tlon-web/src/state/unreads.ts index 28c6aec7f0..e0ad24e962 100644 --- a/apps/tlon-web/src/state/unreads.ts +++ b/apps/tlon-web/src/state/unreads.ts @@ -48,7 +48,7 @@ export interface UnreadsStore { update: (unreads: Activity) => void; } -export const unreadStoreLogger = createDevLogger('UnreadsStore', true); +export const unreadStoreLogger = createDevLogger('UnreadsStore', false); function getUnreadStatus(count: number, notify: boolean): ReadStatus { if (count > 0 || notify) { @@ -341,7 +341,6 @@ export const useUnreadsStore = create((set, get) => ({ delayedRead: (key, cb) => { const { sources, read } = get(); const source = sources[key] || emptyUnread(); - debugger; if (source.status === 'read') { return; }