From 10c2fbe65f5e904b7f7f77f5dede1e29dbabcea1 Mon Sep 17 00:00:00 2001 From: Arjun Patel Date: Tue, 9 Jun 2026 10:42:26 -0700 Subject: [PATCH 1/2] infra: cleanup tsconfig and add typecheck to desktop --- .github/workflows/pr-quality-gate-js.yml | 3 +++ js/desktop/forge.config.ts | 3 +-- js/desktop/package.json | 2 +- js/desktop/tsconfig.json | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pr-quality-gate-js.yml b/.github/workflows/pr-quality-gate-js.yml index 217c4f9..9253c04 100644 --- a/.github/workflows/pr-quality-gate-js.yml +++ b/.github/workflows/pr-quality-gate-js.yml @@ -59,6 +59,9 @@ jobs: - name: Install dependencies run: yarn install --immutable + - name: Typecheck + run: yarn typecheck + - name: Run Code Linter run: yarn lint diff --git a/js/desktop/forge.config.ts b/js/desktop/forge.config.ts index d0af626..2294ee7 100644 --- a/js/desktop/forge.config.ts +++ b/js/desktop/forge.config.ts @@ -6,7 +6,6 @@ import { MakerRpm } from '@electron-forge/maker-rpm'; import { VitePlugin } from '@electron-forge/plugin-vite'; import { FusesPlugin } from '@electron-forge/plugin-fuses'; import { FuseV1Options, FuseVersion } from '@electron/fuses'; -import type { SignToolOptions } from '@electron/windows-sign'; // Windows code signing via Azure Trusted Signing. Dormant unless the workflow // provisions the dlib + metadata file and exports these env vars, so local @@ -17,7 +16,7 @@ import type { SignToolOptions } from '@electron/windows-sign'; const windowsSign = process.env.AZURE_METADATA_JSON_PATH ? { debug: true, signToolPath: process.env.SIGNTOOL_PATH, - hashes: ['sha256'] as SignToolOptions['hashes'], + hashes: ['sha256'] as any, // eslint-disable-line @typescript-eslint/no-explicit-any timestampServer: 'http://timestamp.acs.microsoft.com', signWithParams: [ '/v', diff --git a/js/desktop/package.json b/js/desktop/package.json index d5ce88d..17f4c6f 100644 --- a/js/desktop/package.json +++ b/js/desktop/package.json @@ -17,7 +17,7 @@ "lint": "eslint --ext .ts,.tsx .", "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"", "format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"", - "compile": "npx tsc --noEmit 2>&1 | grep '^src/'", + "typecheck": "npx tsc --noEmit", "web:dev": "cross-env APP_ENV=dev vite --config vite.web.config.mts", "web:build": "cross-env APP_ENV=prod vite build --config vite.web.config.mts", "web:build:ci": "vite build --config vite.web.config.mts", diff --git a/js/desktop/tsconfig.json b/js/desktop/tsconfig.json index 8e0caad..ccb5f77 100644 --- a/js/desktop/tsconfig.json +++ b/js/desktop/tsconfig.json @@ -1,14 +1,14 @@ { "compilerOptions": { "target": "ESNext", - "module": "commonjs", + "module": "ESNext", "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "noImplicitAny": true, "sourceMap": true, "outDir": "dist", - "moduleResolution": "node", + "moduleResolution": "bundler", "resolveJsonModule": true, "jsx": "react-jsx", "strict": true, -- 2.54.0 From 92894c4af88fa3bfec278193417cd0da93b89942 Mon Sep 17 00:00:00 2001 From: Arjun Patel Date: Tue, 9 Jun 2026 10:43:27 -0700 Subject: [PATCH 2/2] paginate open streams (#252) * paginate for open and closed streams * fix: add platform abstraction for deeplink (#251) * fix: add platform abstraction for deeplink This was crashing App.tsx due to the deep link listener calling methods on window which do not exist. * fix: apply CodeRabbit auto-fixes Fixed 1 file(s) based on 1 unresolved review comment. Co-authored-by: CodeRabbit * log error --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: CodeRabbit * cleanup * fix: import --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: CodeRabbit --- js/desktop/src/App.tsx | 1 + js/desktop/src/hooks/use-stream-particles.ts | 34 ++++++-------------- 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/js/desktop/src/App.tsx b/js/desktop/src/App.tsx index 8a84a3b..2858f50 100644 --- a/js/desktop/src/App.tsx +++ b/js/desktop/src/App.tsx @@ -11,6 +11,7 @@ import NetworkSelector from '@/features/network-selector'; import NetworkRoot from '@/features/network-root'; import ParticleViewResolver from '@/features/particles/particle-view-resolver'; import Layout from '@/features/layout'; +import { logError } from '@/lib/errors'; import NetworkSettingsPage from '@/features/network-settings'; import { Toaster } from '@/components/ui/sonner'; import { PusherProvider } from '@/lib/pusher-provider'; diff --git a/js/desktop/src/hooks/use-stream-particles.ts b/js/desktop/src/hooks/use-stream-particles.ts index 41bcae1..7be9ff6 100644 --- a/js/desktop/src/hooks/use-stream-particles.ts +++ b/js/desktop/src/hooks/use-stream-particles.ts @@ -10,8 +10,8 @@ export type StreamParticle = Particle & { properties: StreamProperties; }; -const CLOSED_INITIAL_PAGE_SIZE = 50; -const CLOSED_PAGE_INCREMENT = 50; +const INITIAL_PAGE_SIZE = 12; +const PAGE_INCREMENT = 12; // Stable where-constraint references so the Firestore subscription only // re-attaches when the tab actually changes, not on every render. @@ -28,11 +28,7 @@ function useVisibilityScopes(userId?: string, networkId?: string) { } interface UseStreamParticlesOptions { - /** - * Which streams to subscribe to. Open streams are loaded in full (bounded - * by active work — full realtime coverage is needed for autoplay/huddles). - * Closed streams are paginated via `loadMore`. - */ + // Which streams to subscribe to status: 'open' | 'closed'; } @@ -40,9 +36,9 @@ interface UseStreamParticlesResult { streams: StreamParticle[]; isLoading: boolean; networkId: string; - /** True when more closed streams may exist beyond the current window. */ + /** True when more streams may exist beyond the current window. */ canLoadMore: boolean; - /** Extend the pagination window. No-op on the open tab. */ + /** Extend the pagination window. */ loadMore: () => void; } @@ -54,21 +50,10 @@ export function useStreamParticles( const user = useAuthStore((s) => s.user); const visibilityScopes = useVisibilityScopes(user?.id, networkId); - const [closedLimit, setClosedLimit] = useState(CLOSED_INITIAL_PAGE_SIZE); - const [prevStatus, setPrevStatus] = useState(status); - - // Switching back to the closed tab starts a fresh window, avoiding an - // ever-growing subscription across a long session. - if (status !== prevStatus) { - setPrevStatus(status); - if (status === 'closed') { - setClosedLimit(CLOSED_INITIAL_PAGE_SIZE); - } - } + const [limit, setLimit] = useState(INITIAL_PAGE_SIZE); const whereFilter: QueryFieldFilterConstraint = status === 'open' ? OPEN_STATUS_FILTER : CLOSED_STATUS_FILTER; - const limit = status === 'closed' ? closedLimit : undefined; const { children, isLoading } = useLiveParticleChildren(path, { orderByField: 'last_child_created_at', @@ -85,12 +70,11 @@ export function useStreamParticles( // Heuristic: if we got back as many items as we asked for, assume there // might be more. Clicking load-more when there are no more is a no-op. - const canLoadMore = status === 'closed' && streams.length >= closedLimit; + const canLoadMore = streams.length >= limit; const loadMore = useCallback(() => { - if (status !== 'closed') return; - setClosedLimit((prev) => prev + CLOSED_PAGE_INCREMENT); - }, [status]); + setLimit((prev) => prev + PAGE_INCREMENT); + }, []); return { streams, isLoading, networkId, canLoadMore, loadMore }; } -- 2.54.0