Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
7 changes: 7 additions & 0 deletions components/brain/left-sidebar/waves/BrainLeftSidebarWave.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ const BrainLeftSidebarWave: React.FC<BrainLeftSidebarWaveProps> = ({
return getWaveRoute({
waveId: wave.id,
serialNo: wave.firstUnreadDropSerialNo ?? undefined,
extraParams: {
divider:
wave.firstUnreadDropSerialNo !== null
? String(wave.firstUnreadDropSerialNo)
: undefined,
},
isDirectMessage,
isApp,
});
Expand Down Expand Up @@ -94,6 +100,7 @@ const BrainLeftSidebarWave: React.FC<BrainLeftSidebarWaveProps> = ({
activeWave.set(nextWaveId, {
isDirectMessage,
serialNo: nextWaveId ? wave.firstUnreadDropSerialNo : undefined,
divider: nextWaveId ? wave.firstUnreadDropSerialNo : undefined,
});
},
[activeWave.set, activeWaveId, isDirectMessage, onWaveHover, wave.id, wave.firstUnreadDropSerialNo]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useCallback, useMemo } from 'react';
import type { ReadonlyURLSearchParams } from 'next/navigation';
import type { ReadonlyURLSearchParams } from "next/navigation";
import React, { useCallback, useMemo } from "react";

interface UseWaveNavigationOptions {
readonly basePath: string;
Expand Down Expand Up @@ -34,18 +34,19 @@ export const useWaveNavigation = ({
hasTouchScreen,
firstUnreadDropSerialNo,
}: UseWaveNavigationOptions): UseWaveNavigationResult => {
const currentWaveId = activeWaveId ?? searchParams?.get('wave') ?? undefined;
const isDirectMessage = basePath === '/messages';
const currentWaveId = activeWaveId ?? searchParams?.get("wave") ?? undefined;
const isDirectMessage = basePath === "/messages";

const href = useMemo(() => {
if (currentWaveId === waveId) {
return basePath;
}

const params = new URLSearchParams();
params.set('wave', waveId);
params.set("wave", waveId);
if (firstUnreadDropSerialNo) {
params.set('serialNo', String(firstUnreadDropSerialNo));
params.set("serialNo", String(firstUnreadDropSerialNo));
params.set("divider", String(firstUnreadDropSerialNo));
}
return `${basePath}?${params.toString()}`;
}, [basePath, currentWaveId, waveId, firstUnreadDropSerialNo]);
Expand Down Expand Up @@ -80,9 +81,17 @@ export const useWaveNavigation = ({
setActiveWave(nextWaveId, {
isDirectMessage,
serialNo: nextWaveId ? firstUnreadDropSerialNo : undefined,
divider: nextWaveId ? firstUnreadDropSerialNo : undefined,
});
},
[currentWaveId, isDirectMessage, onMouseEnter, setActiveWave, waveId, firstUnreadDropSerialNo]
[
currentWaveId,
isDirectMessage,
onMouseEnter,
setActiveWave,
waveId,
firstUnreadDropSerialNo,
]
);

return {
Expand Down
19 changes: 14 additions & 5 deletions components/brain/my-stream/MyStreamWave.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,19 @@ const MyStreamWave: React.FC<MyStreamWaveProps> = ({ waveId }) => {
},
});

// Get new drops count from the waves list
const newDropsCount = useMemo(() => {
// Check both regular waves and direct messages
// Get enhanced data from the waves list (has correct WS-updated values)
const enhancedData = useMemo(() => {
const waveFromList =
waves.list.find((w) => w.id === waveId) ??
directMessages.list.find((w) => w.id === waveId);
return waveFromList?.newDropsCount.count ?? 0;
return {
newDropsCount: waveFromList?.newDropsCount.count ?? 0,
firstUnreadSerialNo: waveFromList?.firstUnreadDropSerialNo ?? null,
};
}, [waves.list, directMessages.list, waveId]);

const newDropsCount = enhancedData.newDropsCount;

// Update wave data in title context
useSetWaveData(
wave ? { name: wave.name, newItemsCount: newDropsCount } : null
Expand Down Expand Up @@ -81,7 +85,12 @@ const MyStreamWave: React.FC<MyStreamWaveProps> = ({ waveId }) => {

// Create component instances with wave-specific props and stable measurements
const components: Record<MyStreamWaveTab, JSX.Element> = {
[MyStreamWaveTab.CHAT]: <MyStreamWaveChat wave={wave} />,
[MyStreamWaveTab.CHAT]: (
<MyStreamWaveChat
wave={wave}
firstUnreadSerialNo={enhancedData.firstUnreadSerialNo}
/>
),
[MyStreamWaveTab.LEADERBOARD]: (
<MyStreamWaveLeaderboard wave={wave} onDropClick={onDropClick} />
),
Expand Down
35 changes: 30 additions & 5 deletions components/brain/my-stream/MyStreamWaveChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,18 @@ import { useLayout } from "./layout/LayoutContext";
interface InitialDropState {
readonly waveId: string;
readonly serialNo: number;
readonly dividerSerialNo: number | null;
}

interface MyStreamWaveChatProps {
readonly wave: ApiWave;
readonly firstUnreadSerialNo: number | null;
}

const MyStreamWaveChat: React.FC<MyStreamWaveChatProps> = ({ wave }) => {
const MyStreamWaveChat: React.FC<MyStreamWaveChatProps> = ({
wave,
firstUnreadSerialNo,
}) => {
const router = useRouter();
const searchParams = useSearchParams();
const pathname = usePathname();
Expand All @@ -42,9 +47,14 @@ const MyStreamWaveChat: React.FC<MyStreamWaveChatProps> = ({ wave }) => {
const { isApp } = useDeviceInfo();
const [activeDrop, setActiveDrop] = useState<ActiveDropState | null>(null);

const initialDrop =
const scrollTarget =
initialDropState?.waveId === wave.id ? initialDropState.serialNo : null;

const dividerTarget =
initialDropState?.waveId === wave.id
? initialDropState.dividerSerialNo
: firstUnreadSerialNo;

useEffect(() => {
const dropParam = searchParams?.get("serialNo");
if (!dropParam) {
Expand All @@ -56,15 +66,29 @@ const MyStreamWaveChat: React.FC<MyStreamWaveChatProps> = ({ wave }) => {
return;
}

setInitialDropState({ waveId: wave.id, serialNo: parsed });
const dividerParam = searchParams?.get("divider");
const dividerParsed = dividerParam
? Number.parseInt(dividerParam, 10)
: null;
const dividerSerialNo =
dividerParsed !== null && Number.isFinite(dividerParsed)
? dividerParsed
: firstUnreadSerialNo;

setInitialDropState({
waveId: wave.id,
serialNo: parsed,
dividerSerialNo,
});

const params = new URLSearchParams(searchParams?.toString() || "");
params.delete("serialNo");
params.delete("divider");
const href = params.toString()
? `${pathname}?${params.toString()}`
: pathname || getHomeFeedRoute();
router.replace(href, { scroll: false });
}, [searchParams, router, pathname, wave.id]);
}, [searchParams, router, pathname, wave.id, firstUnreadSerialNo]);

const { waveViewStyle } = useLayout();

Expand Down Expand Up @@ -123,7 +147,8 @@ const MyStreamWaveChat: React.FC<MyStreamWaveChatProps> = ({ wave }) => {
onReply={handleReply}
onQuote={handleQuote}
activeDrop={activeDrop}
initialDrop={initialDrop}
initialDrop={scrollTarget}
dividerSerialNo={dividerTarget}
dropId={null}
isMuted={wave.metrics?.muted ?? false}
/>
Expand Down
28 changes: 8 additions & 20 deletions components/waves/drops/wave-drops-all/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ interface WaveDropsAllProps {
}) => void;
readonly activeDrop: ActiveDropState | null;
readonly initialDrop: number | null;
readonly dividerSerialNo?: number | null;
readonly onDropContentClick?: (drop: ExtendedDrop) => void;
readonly bottomPaddingClassName?: string;
readonly isMuted?: boolean;
Expand All @@ -57,6 +58,7 @@ const WaveDropsAllInner: React.FC<WaveDropsAllProps> = ({
onQuote,
activeDrop,
initialDrop,
dividerSerialNo,
onDropContentClick,
bottomPaddingClassName,
isMuted = false,
Expand All @@ -70,8 +72,7 @@ const WaveDropsAllInner: React.FC<WaveDropsAllProps> = ({
const { waveMessages, fetchNextPage, waitAndRevealDrop } =
useVirtualizedWaveDrops(waveId, dropId);

const { unreadDividerSerialNo, setUnreadDividerSerialNo } =
useUnreadDivider();
const { setUnreadDividerSerialNo } = useUnreadDivider();

const typingMessage = useWaveIsTyping(
waveId,
Expand Down Expand Up @@ -112,12 +113,8 @@ const WaveDropsAllInner: React.FC<WaveDropsAllProps> = ({
useEffect(() => {
setVisibleLatestSerial(null);
prevLatestSerialNoRef.current = null;
if (initialDrop === null) {
setUnreadDividerSerialNo(null);
} else {
setUnreadDividerSerialNo(initialDrop);
}
}, [waveId, initialDrop, setUnreadDividerSerialNo]);
setUnreadDividerSerialNo(dividerSerialNo ?? null);
}, [waveId, dividerSerialNo, setUnreadDividerSerialNo]);

const latestSerialNo = waveMessages?.drops?.[0]?.serial_no ?? null;

Expand All @@ -139,17 +136,6 @@ const WaveDropsAllInner: React.FC<WaveDropsAllProps> = ({
}
}, [latestSerialNo, isAtBottom, setUnreadDividerSerialNo]);

const wasNotAtBottomRef = useRef(false);

useEffect(() => {
if (!isAtBottom) {
wasNotAtBottomRef.current = true;
} else if (wasNotAtBottomRef.current && unreadDividerSerialNo !== null) {
setUnreadDividerSerialNo(null);
wasNotAtBottomRef.current = false;
}
}, [isAtBottom, unreadDividerSerialNo, setUnreadDividerSerialNo]);

useEffect(() => {
if (latestSerialNo === null) {
return;
Expand Down Expand Up @@ -329,13 +315,14 @@ const WaveDropsAll: React.FC<WaveDropsAllProps> = ({
onQuote,
activeDrop,
initialDrop,
dividerSerialNo,
onDropContentClick,
bottomPaddingClassName,
isMuted = false,
}) => {
return (
<UnreadDividerProvider
initialSerialNo={initialDrop}
initialSerialNo={dividerSerialNo ?? null}
key={`unread-divider-${waveId}`}>
<WaveDropsAllInner
waveId={waveId}
Expand All @@ -344,6 +331,7 @@ const WaveDropsAll: React.FC<WaveDropsAllProps> = ({
onQuote={onQuote}
activeDrop={activeDrop}
initialDrop={initialDrop}
dividerSerialNo={dividerSerialNo}
onDropContentClick={onDropContentClick}
bottomPaddingClassName={bottomPaddingClassName}
isMuted={isMuted}
Expand Down
15 changes: 14 additions & 1 deletion contexts/wave/hooks/useActiveWaveManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
interface WaveNavigationOptions {
isDirectMessage?: boolean;
serialNo?: number | string | null;
divider?: number | string | null;
}

const getWaveFromWindow = (): string | null => {
Expand Down Expand Up @@ -47,8 +48,20 @@ export function useActiveWaveManager() {
(waveId: string | null, options?: WaveNavigationOptions) => {
const isDirectMessage = options?.isDirectMessage ?? false;
const serialNo = options?.serialNo ?? undefined;
const divider = options?.divider;
return waveId
? getWaveRoute({ waveId, serialNo, isDirectMessage, isApp })
? getWaveRoute({
waveId,
serialNo,
extraParams: {
divider:
divider !== null && divider !== undefined
? String(divider)
: undefined,
},
isDirectMessage,
isApp,
})
: getWaveHomeRoute({ isDirectMessage, isApp });
},
[isApp]
Expand Down
7 changes: 6 additions & 1 deletion contexts/wave/hooks/useEnhancedWavesListCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,12 @@ function useEnhancedWavesListCore(
const forcedCount = forcedUnreadCounts[wave.id];
const apiFirstUnread = wave.metrics.first_unread_drop_serial_no ?? null;
const wsFirstUnread = wsData?.firstUnreadSerialNo ?? null;
const wasCleared = clearedUnreadWaveIds.has(wave.id);
let firstUnreadDropSerialNo: number | null = null;
if (!isCleared) {
if (apiFirstUnread !== null && wsFirstUnread !== null) {
if (wasCleared && hasNewWsDrops) {
firstUnreadDropSerialNo = wsFirstUnread;
} else if (apiFirstUnread !== null && wsFirstUnread !== null) {
firstUnreadDropSerialNo = Math.min(apiFirstUnread, wsFirstUnread);
} else {
firstUnreadDropSerialNo = apiFirstUnread ?? wsFirstUnread;
Expand All @@ -137,6 +140,8 @@ function useEnhancedWavesListCore(
unreadDropsCount = 0;
} else if (forcedCount !== undefined) {
unreadDropsCount = forcedCount + (wsData?.count ?? 0);
} else if (wasCleared && hasNewWsDrops) {
unreadDropsCount = wsData?.count ?? 0;
} else if (hasNewWsDrops) {
unreadDropsCount =
wave.metrics.your_unread_drops_count + (wsData?.count ?? 0);
Expand Down