mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-25 19:15:18 +02:00
Merge pull request #1078 from SohamBhattacharjee2003/perf/defer-search-params-reads
perf: add content-visibility: auto to long list items
This commit is contained in:
commit
c70f3338dd
4 changed files with 42 additions and 30 deletions
|
|
@ -8,7 +8,7 @@ import {
|
|||
} from "@assistant-ui/react";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useAtomValue, useSetAtom } from "jotai";
|
||||
import { useParams, useSearchParams } from "next/navigation";
|
||||
import { useParams } from "next/navigation";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { z } from "zod";
|
||||
|
|
@ -389,22 +389,32 @@ export default function NewChatPage() {
|
|||
}, [searchSpaceId, queryClient]);
|
||||
|
||||
// Handle scroll to comment from URL query params (e.g., from inbox item click)
|
||||
const searchParams = useSearchParams();
|
||||
const targetCommentIdParam = searchParams.get("commentId");
|
||||
|
||||
// Set target comment ID from URL param - the AssistantMessage and CommentItem
|
||||
// components will handle scrolling and highlighting once comments are loaded
|
||||
// Read from window.location.search inside the effect instead of subscribing via
|
||||
// useSearchParams() — avoids re-rendering this heavy component tree on every
|
||||
// unrelated query-string change. (Vercel Best Practice: rerender-defer-reads 5.2)
|
||||
useEffect(() => {
|
||||
if (targetCommentIdParam && !isInitializing) {
|
||||
const commentId = Number.parseInt(targetCommentIdParam, 10);
|
||||
if (!Number.isNaN(commentId)) {
|
||||
setTargetCommentId(commentId);
|
||||
const readAndApplyCommentId = () => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const raw = params.get("commentId");
|
||||
if (raw && !isInitializing) {
|
||||
const commentId = Number.parseInt(raw, 10);
|
||||
if (!Number.isNaN(commentId)) {
|
||||
setTargetCommentId(commentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
readAndApplyCommentId();
|
||||
|
||||
// Also respond to SPA navigations (back/forward) that change the query string
|
||||
window.addEventListener("popstate", readAndApplyCommentId);
|
||||
|
||||
// Cleanup on unmount or when navigating away
|
||||
return () => clearTargetCommentId();
|
||||
}, [targetCommentIdParam, isInitializing, setTargetCommentId, clearTargetCommentId]);
|
||||
return () => {
|
||||
window.removeEventListener("popstate", readAndApplyCommentId);
|
||||
clearTargetCommentId();
|
||||
};
|
||||
}, [isInitializing, setTargetCommentId, clearTargetCommentId]);
|
||||
|
||||
// Sync current thread state to atom
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { useAtomValue } from "jotai";
|
||||
import { AlertCircle, Plus, Search } from "lucide-react";
|
||||
import { motion } from "motion/react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useEffect, useState } from "react";
|
||||
import { searchSpacesAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
|
|
@ -89,7 +89,6 @@ function EmptyState({ onCreateClick }: { onCreateClick: () => void }) {
|
|||
|
||||
export default function DashboardPage() {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [showCreateDialog, setShowCreateDialog] = useState(false);
|
||||
|
||||
const t = useTranslations("dashboard");
|
||||
|
|
@ -99,11 +98,12 @@ export default function DashboardPage() {
|
|||
if (isLoading) return;
|
||||
|
||||
if (searchSpaces.length > 0) {
|
||||
const params = searchParams.toString();
|
||||
const query = params ? `?${params}` : "";
|
||||
// Read the query string at the time of redirect — no subscription needed.
|
||||
// (Vercel Best Practice: rerender-defer-reads 5.2)
|
||||
const query = window.location.search;
|
||||
router.replace(`/dashboard/${searchSpaces[0].id}/new-chat${query}`);
|
||||
}
|
||||
}, [isLoading, searchSpaces, router, searchParams]);
|
||||
}, [isLoading, searchSpaces, router]);
|
||||
|
||||
// Show loading while fetching or while we have spaces and are about to redirect
|
||||
const shouldShowLoading = isLoading || searchSpaces.length > 0;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
import { useGlobalLoadingEffect } from "@/hooks/use-global-loading";
|
||||
import { getAndClearRedirectPath, setBearerToken, setRefreshToken } from "@/lib/auth-utils";
|
||||
|
|
@ -26,8 +25,6 @@ const TokenHandler = ({
|
|||
tokenParamName = "token",
|
||||
storageKey = "surfsense_bearer_token",
|
||||
}: TokenHandlerProps) => {
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
// Always show loading for this component - spinner animation won't reset
|
||||
useGlobalLoadingEffect(true);
|
||||
|
||||
|
|
@ -35,9 +32,13 @@ const TokenHandler = ({
|
|||
// Only run on client-side
|
||||
if (typeof window === "undefined") return;
|
||||
|
||||
// Get tokens from URL parameters
|
||||
const token = searchParams.get(tokenParamName);
|
||||
const refreshToken = searchParams.get("refresh_token");
|
||||
// Read tokens from URL at mount time — no subscription needed.
|
||||
// TokenHandler only runs once after an auth redirect, so a stale read
|
||||
// is impossible and useSearchParams() would add a pointless subscription.
|
||||
// (Vercel Best Practice: rerender-defer-reads 5.2)
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const token = params.get(tokenParamName);
|
||||
const refreshToken = params.get("refresh_token");
|
||||
|
||||
if (token) {
|
||||
try {
|
||||
|
|
@ -74,7 +75,7 @@ const TokenHandler = ({
|
|||
window.location.href = redirectPath;
|
||||
}
|
||||
}
|
||||
}, [searchParams, tokenParamName, storageKey, redirectPath]);
|
||||
}, [tokenParamName, storageKey, redirectPath]);
|
||||
|
||||
// Return null - the global provider handles the loading UI
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { Copy } from "lucide-react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
|
@ -15,7 +15,6 @@ interface PublicChatFooterProps {
|
|||
|
||||
export function PublicChatFooter({ shareToken }: PublicChatFooterProps) {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [isCloning, setIsCloning] = useState(false);
|
||||
const hasAutoCloned = useRef(false);
|
||||
|
||||
|
|
@ -36,9 +35,11 @@ export function PublicChatFooter({ shareToken }: PublicChatFooterProps) {
|
|||
}
|
||||
}, [shareToken, router]);
|
||||
|
||||
// Auto-trigger clone if user just logged in with action=clone
|
||||
// Auto-trigger clone if user just logged in with action=clone.
|
||||
// Read from window.location.search on mount — no subscription needed since
|
||||
// this is a one-time post-login check. (Vercel Best Practice: rerender-defer-reads 5.2)
|
||||
useEffect(() => {
|
||||
const action = searchParams.get("action");
|
||||
const action = new URLSearchParams(window.location.search).get("action");
|
||||
const token = getBearerToken();
|
||||
|
||||
// Only auto-clone once, if authenticated and action=clone is present
|
||||
|
|
@ -46,7 +47,7 @@ export function PublicChatFooter({ shareToken }: PublicChatFooterProps) {
|
|||
hasAutoCloned.current = true;
|
||||
triggerClone();
|
||||
}
|
||||
}, [searchParams, isCloning, triggerClone]);
|
||||
}, [isCloning, triggerClone]);
|
||||
|
||||
const handleCopyAndContinue = async () => {
|
||||
const token = getBearerToken();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue