diff --git a/__tests__/components/brain/left-sidebar/waves/BrainLeftSidebarWave.test.tsx b/__tests__/components/brain/left-sidebar/waves/BrainLeftSidebarWave.test.tsx
index 27a442e98b..03c60b8acb 100644
--- a/__tests__/components/brain/left-sidebar/waves/BrainLeftSidebarWave.test.tsx
+++ b/__tests__/components/brain/left-sidebar/waves/BrainLeftSidebarWave.test.tsx
@@ -89,7 +89,7 @@ describe('BrainLeftSidebarWave', () => {
render();
const link = screen.getByRole('link');
await userEvent.click(link);
- expect(setActiveWave).toHaveBeenCalledWith('1', { isDirectMessage: false, serialNo: null });
+ expect(setActiveWave).toHaveBeenCalledWith('1', { isDirectMessage: false, serialNo: null, divider: null });
});
it('shows drop indicators for non-chat waves', () => {
@@ -101,7 +101,7 @@ describe('BrainLeftSidebarWave', () => {
it('includes firstUnreadDropSerialNo in href when present', () => {
const waveWithUnread = { ...baseWave, id: '3', firstUnreadDropSerialNo: 42 };
render();
- expect(screen.getByRole('link')).toHaveAttribute('href', '/waves?wave=3&serialNo=42');
+ expect(screen.getByRole('link')).toHaveAttribute('href', '/waves?divider=42&wave=3&serialNo=42');
});
it('does not include serialNo in href when firstUnreadDropSerialNo is null', () => {
diff --git a/components/brain/left-sidebar/waves/BrainLeftSidebarWave.tsx b/components/brain/left-sidebar/waves/BrainLeftSidebarWave.tsx
index 1994da7dc8..08fb5823c6 100644
--- a/components/brain/left-sidebar/waves/BrainLeftSidebarWave.tsx
+++ b/components/brain/left-sidebar/waves/BrainLeftSidebarWave.tsx
@@ -1,22 +1,22 @@
"use client";
-import React, { useMemo, useCallback } from "react";
-import Link from "next/link";
-import { usePrefetchWaveData } from "@/hooks/usePrefetchWaveData";
-import { ApiWaveType } from "@/generated/models/ApiWaveType";
import WavePicture from "@/components/waves/WavePicture";
-import BrainLeftSidebarWaveDropTime from "./BrainLeftSidebarWaveDropTime";
import { MinimalWave } from "@/contexts/wave/hooks/useEnhancedWavesList";
-import BrainLeftSidebarWavePin from "./BrainLeftSidebarWavePin";
-import { formatAddress, isValidEthAddress } from "../../../../helpers/Helpers";
-import useDeviceInfo from "../../../../hooks/useDeviceInfo";
import { useMyStream } from "@/contexts/wave/MyStreamContext";
+import { ApiWaveType } from "@/generated/models/ApiWaveType";
+import { usePrefetchWaveData } from "@/hooks/usePrefetchWaveData";
+import { faBellSlash } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import Link from "next/link";
+import React, { useCallback, useMemo } from "react";
+import { formatAddress, isValidEthAddress } from "../../../../helpers/Helpers";
import {
getWaveHomeRoute,
getWaveRoute,
} from "../../../../helpers/navigation.helpers";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faBellSlash } from "@fortawesome/free-solid-svg-icons";
+import useDeviceInfo from "../../../../hooks/useDeviceInfo";
+import BrainLeftSidebarWaveDropTime from "./BrainLeftSidebarWaveDropTime";
+import BrainLeftSidebarWavePin from "./BrainLeftSidebarWavePin";
interface BrainLeftSidebarWaveProps {
readonly wave: MinimalWave;
@@ -32,6 +32,7 @@ const BrainLeftSidebarWave: React.FC = ({
isDirectMessage = false,
}) => {
const { activeWave } = useMyStream();
+ const { id: activeWaveId, set: setActiveWave } = activeWave;
const prefetchWaveData = usePrefetchWaveData();
const { isApp, hasTouchScreen } = useDeviceInfo();
const isDropWave = wave.type !== ApiWaveType.Chat;
@@ -45,10 +46,15 @@ const BrainLeftSidebarWave: React.FC = ({
if (markerIndex !== -1) {
const prefix = wave.name.slice(0, markerIndex + marker.length);
const addressStart = markerIndex + marker.length;
- const candidateAddress = wave.name.slice(addressStart, addressStart + 42);
+ const candidateAddress = wave.name.slice(
+ addressStart,
+ addressStart + 42
+ );
if (isValidEthAddress(candidateAddress)) {
- const suffix = wave.name.slice(addressStart + candidateAddress.length);
+ const suffix = wave.name.slice(
+ addressStart + candidateAddress.length
+ );
return `${prefix}${formatAddress(candidateAddress)}${suffix}`;
}
}
@@ -56,8 +62,6 @@ const BrainLeftSidebarWave: React.FC = ({
return wave.name;
}, [wave.name, wave.type]);
- const activeWaveId = activeWave.id;
-
const href = useMemo(() => {
if (activeWaveId === wave.id) {
return getWaveHomeRoute({ isDirectMessage, isApp });
@@ -65,10 +69,19 @@ const BrainLeftSidebarWave: React.FC = ({
return getWaveRoute({
waveId: wave.id,
serialNo: wave.firstUnreadDropSerialNo ?? undefined,
+ extraParams: wave.firstUnreadDropSerialNo
+ ? { divider: String(wave.firstUnreadDropSerialNo) }
+ : undefined,
isDirectMessage,
isApp,
});
- }, [activeWaveId, isApp, isDirectMessage, wave.id, wave.firstUnreadDropSerialNo]);
+ }, [
+ activeWaveId,
+ isApp,
+ isDirectMessage,
+ wave.id,
+ wave.firstUnreadDropSerialNo,
+ ]);
const unreadCount = Math.max(wave.unreadDropsCount, wave.newDropsCount.count);
const haveNewDrops = unreadCount > 0;
@@ -85,22 +98,37 @@ const BrainLeftSidebarWave: React.FC = ({
const handleWaveClick = useCallback(
(event: React.MouseEvent) => {
if (event.defaultPrevented) return;
- if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button === 1) {
+ if (
+ event.metaKey ||
+ event.ctrlKey ||
+ event.shiftKey ||
+ event.altKey ||
+ event.button === 1
+ ) {
return;
}
event.preventDefault();
onWaveHover();
const nextWaveId = wave.id === activeWaveId ? null : wave.id;
- activeWave.set(nextWaveId, {
+ setActiveWave(nextWaveId, {
isDirectMessage,
- serialNo: nextWaveId ? wave.firstUnreadDropSerialNo : undefined,
+ serialNo: nextWaveId ? wave.firstUnreadDropSerialNo : null,
+ divider: nextWaveId ? wave.firstUnreadDropSerialNo : null,
});
},
- [activeWave.set, activeWaveId, isDirectMessage, onWaveHover, wave.id, wave.firstUnreadDropSerialNo]
+ [
+ setActiveWave,
+ activeWaveId,
+ isDirectMessage,
+ onWaveHover,
+ wave.id,
+ wave.firstUnreadDropSerialNo,
+ ]
);
const getAvatarRingClasses = () => {
- if (isActive) return "tw-ring-1 tw-ring-offset-2 tw-ring-offset-iron-900 tw-ring-primary-400";
+ if (isActive)
+ return "tw-ring-1 tw-ring-offset-2 tw-ring-offset-iron-900 tw-ring-primary-400";
return "tw-ring-1 tw-ring-iron-700";
};
@@ -123,7 +151,9 @@ const BrainLeftSidebarWave: React.FC = ({
void;
readonly onHover: (waveId: string) => void;
readonly prefetchWaveData: (waveId: string) => void;
@@ -34,8 +38,8 @@ 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) {
@@ -43,9 +47,10 @@ export const useWaveNavigation = ({
}
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]);
@@ -79,10 +84,18 @@ export const useWaveNavigation = ({
const nextWaveId = waveId === currentWaveId ? null : waveId;
setActiveWave(nextWaveId, {
isDirectMessage,
- serialNo: nextWaveId ? firstUnreadDropSerialNo : undefined,
+ serialNo: nextWaveId ? firstUnreadDropSerialNo : null,
+ divider: nextWaveId ? firstUnreadDropSerialNo : null,
});
},
- [currentWaveId, isDirectMessage, onMouseEnter, setActiveWave, waveId, firstUnreadDropSerialNo]
+ [
+ currentWaveId,
+ isDirectMessage,
+ onMouseEnter,
+ setActiveWave,
+ waveId,
+ firstUnreadDropSerialNo,
+ ]
);
return {
diff --git a/components/brain/my-stream/MyStreamWave.tsx b/components/brain/my-stream/MyStreamWave.tsx
index c7902be998..70014856d3 100644
--- a/components/brain/my-stream/MyStreamWave.tsx
+++ b/components/brain/my-stream/MyStreamWave.tsx
@@ -45,15 +45,19 @@ const MyStreamWave: React.FC = ({ 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
@@ -81,7 +85,12 @@ const MyStreamWave: React.FC = ({ waveId }) => {
// Create component instances with wave-specific props and stable measurements
const components: Record = {
- [MyStreamWaveTab.CHAT]: ,
+ [MyStreamWaveTab.CHAT]: (
+
+ ),
[MyStreamWaveTab.LEADERBOARD]: (
),
diff --git a/components/brain/my-stream/MyStreamWaveChat.tsx b/components/brain/my-stream/MyStreamWaveChat.tsx
index fa3d80d886..17c7531b62 100644
--- a/components/brain/my-stream/MyStreamWaveChat.tsx
+++ b/components/brain/my-stream/MyStreamWaveChat.tsx
@@ -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 = ({ wave }) => {
+const MyStreamWaveChat: React.FC = ({
+ wave,
+ firstUnreadSerialNo,
+}) => {
const router = useRouter();
const searchParams = useSearchParams();
const pathname = usePathname();
@@ -42,9 +47,14 @@ const MyStreamWaveChat: React.FC = ({ wave }) => {
const { isApp } = useDeviceInfo();
const [activeDrop, setActiveDrop] = useState(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) {
@@ -56,15 +66,29 @@ const MyStreamWaveChat: React.FC = ({ 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();
@@ -123,7 +147,8 @@ const MyStreamWaveChat: React.FC = ({ wave }) => {
onReply={handleReply}
onQuote={handleQuote}
activeDrop={activeDrop}
- initialDrop={initialDrop}
+ initialDrop={scrollTarget}
+ dividerSerialNo={dividerTarget}
dropId={null}
isMuted={wave.metrics?.muted ?? false}
/>
diff --git a/components/waves/CreateDropContent.tsx b/components/waves/CreateDropContent.tsx
index 86674584da..7d030f20ef 100644
--- a/components/waves/CreateDropContent.tsx
+++ b/components/waves/CreateDropContent.tsx
@@ -74,30 +74,33 @@ import {
} from "./utils/getMissingRequirements";
// Use next/dynamic for lazy loading with SSR support
-const TermsSignatureFlow = dynamic(() => import("../terms/TermsSignatureFlow"));
+const TermsSignatureFlow = dynamic(
+ () => import("../terms/TermsSignatureFlow"),
+ { loading: () => null }
+);
export type CreateDropMetadataType =
| {
- readonly id: string;
- key: string;
- readonly type: ApiWaveMetadataType.String;
- value: string | null;
- readonly required: boolean;
- }
+ readonly id: string;
+ key: string;
+ readonly type: ApiWaveMetadataType.String;
+ value: string | null;
+ readonly required: boolean;
+ }
| {
- readonly id: string;
- key: string;
- readonly type: ApiWaveMetadataType.Number;
- value: number | null;
- readonly required: boolean;
- }
+ readonly id: string;
+ key: string;
+ readonly type: ApiWaveMetadataType.Number;
+ value: number | null;
+ readonly required: boolean;
+ }
| {
- readonly id: string;
- key: string;
- readonly type: null;
- value: string | null;
- readonly required: boolean;
- };
+ readonly id: string;
+ key: string;
+ readonly type: null;
+ value: string | null;
+ readonly required: boolean;
+ };
interface CreateDropContentProps {
readonly activeDrop: ActiveDropState | null;
@@ -373,8 +376,10 @@ const getOptimisticDrop = (
author: {
id: connectedProfile.id,
handle: connectedProfile.handle,
- active_main_stage_submission_ids: connectedProfile.active_main_stage_submission_ids,
- winner_main_stage_drop_ids: connectedProfile.winner_main_stage_drop_ids ?? [],
+ active_main_stage_submission_ids:
+ connectedProfile.active_main_stage_submission_ids,
+ winner_main_stage_drop_ids:
+ connectedProfile.winner_main_stage_drop_ids ?? [],
pfp: connectedProfile.pfp ?? null,
banner1_color: connectedProfile.banner1 ?? null,
banner2_color: connectedProfile.banner2 ?? null,
@@ -401,9 +406,9 @@ const getOptimisticDrop = (
})),
quoted_drop: part.quoted_drop
? {
- ...part.quoted_drop,
- is_deleted: false,
- }
+ ...part.quoted_drop,
+ is_deleted: false,
+ }
: null,
replies_count: 0,
quotes_count: 0,
@@ -487,21 +492,18 @@ const CreateDropContent: React.FC = ({
requiredMetadata: wave.participation.required_metadata,
});
- const hasMetadata = useMemo(
- () => hasMetadataContent(metadata),
- [metadata]
- );
+ const hasMetadata = useMemo(() => hasMetadataContent(metadata), [metadata]);
const getMarkdown = useMemo(
() =>
editorState
? exportDropMarkdown(editorState, [
- ...SAFE_MARKDOWN_TRANSFORMERS,
- MENTION_TRANSFORMER,
- HASHTAG_TRANSFORMER,
- IMAGE_TRANSFORMER,
- EMOJI_TRANSFORMER,
- ])
+ ...SAFE_MARKDOWN_TRANSFORMERS,
+ MENTION_TRANSFORMER,
+ HASHTAG_TRANSFORMER,
+ IMAGE_TRANSFORMER,
+ EMOJI_TRANSFORMER,
+ ])
: null,
[editorState]
);
@@ -619,9 +621,9 @@ const CreateDropContent: React.FC = ({
quoted_drop:
activeDrop?.action === ActiveDropAction.QUOTE
? {
- drop_id: activeDrop.drop.id,
- drop_part_id: activeDrop.partId,
- }
+ drop_id: activeDrop.drop.id,
+ drop_part_id: activeDrop.partId,
+ }
: null,
media: files,
},
@@ -643,22 +645,23 @@ const CreateDropContent: React.FC = ({
const hasPartsInDrop = (drop?.parts.length ?? 0) > 0;
const hasCurrentContent = !!(markdown?.trim().length || files.length);
- const newParts = hasPartsInDrop && !hasCurrentContent
- ? drop?.parts ?? []
- : [
- ...(drop?.parts ?? []),
- {
- content: markdown?.length ? markdown : null,
- quoted_drop:
- activeDrop?.action === ActiveDropAction.QUOTE
- ? {
- drop_id: activeDrop.drop.id,
- drop_part_id: activeDrop.partId,
- }
- : null,
- media: files,
- },
- ];
+ const newParts =
+ hasPartsInDrop && !hasCurrentContent
+ ? drop?.parts ?? []
+ : [
+ ...(drop?.parts ?? []),
+ {
+ content: markdown?.length ? markdown : null,
+ quoted_drop:
+ activeDrop?.action === ActiveDropAction.QUOTE
+ ? {
+ drop_id: activeDrop.drop.id,
+ drop_part_id: activeDrop.partId,
+ }
+ : null,
+ media: files,
+ },
+ ];
const parts = ensurePartsWithFallback(newParts, hasMetadata);
@@ -852,7 +855,7 @@ const CreateDropContent: React.FC = ({
import("@capacitor/core").then(({ Capacitor }) => {
if (Capacitor.getPlatform() === "android") {
import("@capacitor/keyboard").then(({ Keyboard }) => {
- Keyboard.hide().catch(() => { });
+ Keyboard.hide().catch(() => {});
});
}
});
@@ -972,8 +975,9 @@ const CreateDropContent: React.FC = ({
updatedFiles = updatedFiles.slice(-MAX_DROP_UPLOAD_FILES);
setToast({
- message: `File limit exceeded. The ${removedCount} oldest file${removedCount > 1 ? "s were" : " was"
- } removed to maintain the ${MAX_DROP_UPLOAD_FILES}-file limit. New files have been added.`,
+ message: `File limit exceeded. The ${removedCount} oldest file${
+ removedCount > 1 ? "s were" : " was"
+ } removed to maintain the ${MAX_DROP_UPLOAD_FILES}-file limit. New files have been added.`,
type: "warning",
});
}
@@ -981,12 +985,15 @@ const CreateDropContent: React.FC = ({
setFiles(updatedFiles);
};
- const handleEditorStateChange = useCallback((newEditorState: EditorState) => {
- setEditorState(newEditorState);
- if (!isWideContainer) {
- setShowOptions(false);
- }
- }, [isWideContainer]);
+ const handleEditorStateChange = useCallback(
+ (newEditorState: EditorState) => {
+ setEditorState(newEditorState);
+ if (!isWideContainer) {
+ setShowOptions(false);
+ }
+ },
+ [isWideContainer]
+ );
const removeFile = (file: File, partIndex?: number) => {
if (partIndex !== undefined) {
@@ -1079,7 +1086,8 @@ const CreateDropContent: React.FC = ({
}
}, [isApp, editingDropId, activeDrop, onCancelReplyQuote]);
- const isChatClosed = wave.wave.type === ApiWaveType.Chat && !wave.chat.enabled;
+ const isChatClosed =
+ wave.wave.type === ApiWaveType.Chat && !wave.chat.enabled;
if (isChatClosed) {
return (
@@ -1167,8 +1175,7 @@ const CreateDropContent: React.FC = ({
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: "auto" }}
exit={{ opacity: 0, height: 0 }}
- transition={{ duration: 0.3 }}
- >
+ transition={{ duration: 0.3 }}>
= ({
removeFile={removeFile}
disabled={submitting}
/>
-
- Loading Terms... }>
-
-
+
);
};
diff --git a/components/waves/drops/wave-drops-all/index.tsx b/components/waves/drops/wave-drops-all/index.tsx
index f4272ee04a..8021b500ab 100644
--- a/components/waves/drops/wave-drops-all/index.tsx
+++ b/components/waves/drops/wave-drops-all/index.tsx
@@ -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;
@@ -57,6 +58,7 @@ const WaveDropsAllInner: React.FC = ({
onQuote,
activeDrop,
initialDrop,
+ dividerSerialNo,
onDropContentClick,
bottomPaddingClassName,
isMuted = false,
@@ -70,8 +72,7 @@ const WaveDropsAllInner: React.FC = ({
const { waveMessages, fetchNextPage, waitAndRevealDrop } =
useVirtualizedWaveDrops(waveId, dropId);
- const { unreadDividerSerialNo, setUnreadDividerSerialNo } =
- useUnreadDivider();
+ const { setUnreadDividerSerialNo } = useUnreadDivider();
const typingMessage = useWaveIsTyping(
waveId,
@@ -112,12 +113,8 @@ const WaveDropsAllInner: React.FC = ({
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;
@@ -139,17 +136,6 @@ const WaveDropsAllInner: React.FC = ({
}
}, [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;
@@ -329,13 +315,14 @@ const WaveDropsAll: React.FC = ({
onQuote,
activeDrop,
initialDrop,
+ dividerSerialNo,
onDropContentClick,
bottomPaddingClassName,
isMuted = false,
}) => {
return (
= ({
onQuote={onQuote}
activeDrop={activeDrop}
initialDrop={initialDrop}
+ dividerSerialNo={dividerSerialNo}
onDropContentClick={onDropContentClick}
bottomPaddingClassName={bottomPaddingClassName}
isMuted={isMuted}
diff --git a/contexts/wave/MyStreamContext.tsx b/contexts/wave/MyStreamContext.tsx
index 7f7417f8bb..9fea45a9d0 100644
--- a/contexts/wave/MyStreamContext.tsx
+++ b/contexts/wave/MyStreamContext.tsx
@@ -52,6 +52,7 @@ interface ActiveWaveContextData {
isDirectMessage?: boolean;
replace?: boolean;
serialNo?: number | string | null;
+ divider?: number | null;
}
) => void;
}
diff --git a/contexts/wave/hooks/useActiveWaveManager.ts b/contexts/wave/hooks/useActiveWaveManager.ts
index c9b94e08b4..692214954d 100644
--- a/contexts/wave/hooks/useActiveWaveManager.ts
+++ b/contexts/wave/hooks/useActiveWaveManager.ts
@@ -1,17 +1,15 @@
"use client";
-import { useMemo, useCallback } from "react";
-import { useSearchParams } from "next/navigation";
-import useDeviceInfo from "@/hooks/useDeviceInfo";
+import { getWaveHomeRoute, getWaveRoute } from "@/helpers/navigation.helpers";
import { useClientNavigation } from "@/hooks/useClientNavigation";
-import {
- getWaveHomeRoute,
- getWaveRoute,
-} from "@/helpers/navigation.helpers";
+import useDeviceInfo from "@/hooks/useDeviceInfo";
+import { useSearchParams } from "next/navigation";
+import { useCallback, useMemo } from "react";
interface WaveNavigationOptions {
isDirectMessage?: boolean;
serialNo?: number | string | null;
+ divider?: number | null;
}
const getWaveFromWindow = (): string | null => {
@@ -47,8 +45,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]
diff --git a/contexts/wave/hooks/useEnhancedWavesListCore.ts b/contexts/wave/hooks/useEnhancedWavesListCore.ts
index 9a94ef6ff1..10607ab457 100644
--- a/contexts/wave/hooks/useEnhancedWavesListCore.ts
+++ b/contexts/wave/hooks/useEnhancedWavesListCore.ts
@@ -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;
@@ -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);