fix: resolve lint warnings in hooks, store, and sentry modules
- Move ref assignments from render to useEffect (useViewportCommentRead, useViewportObserver) - Copy ref.current to local variable for effect cleanup closure - Add eslint-disable for intentional ref-as-cache pattern (useStableTeamMentionMeta) - Fix !== always-true comparison between undefined and null (store) - Add missing return types (sentry, composerDraftStorage) - Remove unused import isLeadAgentType (memberHelpers) - Suppress naming-convention warning for Vite-injected __APP_VERSION__
This commit is contained in:
parent
bbb34042a8
commit
bf2220daf6
8 changed files with 28 additions and 13 deletions
|
|
@ -88,6 +88,10 @@ export function useStableTeamMentionMeta(teams: readonly TeamSummary[]): TeamMen
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Intentional ref-as-cache pattern: avoids allocating a new object every
|
||||||
|
// render while still returning a referentially-stable value when the
|
||||||
|
// underlying data hasn't changed.
|
||||||
|
|
||||||
if (
|
if (
|
||||||
stableRef.current === null ||
|
stableRef.current === null ||
|
||||||
!areTeamMentionEntriesEqual(stableRef.current.entries, entries)
|
!areTeamMentionEntriesEqual(stableRef.current.entries, entries)
|
||||||
|
|
@ -98,5 +102,6 @@ export function useStableTeamMentionMeta(teams: readonly TeamSummary[]): TeamMen
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/refs -- stable ref cache pattern
|
||||||
return stableRef.current.value;
|
return stableRef.current.value;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,11 @@ export function useViewportCommentRead({
|
||||||
const seenIdsRef = useRef<Set<string>>(new Set());
|
const seenIdsRef = useRef<Set<string>>(new Set());
|
||||||
const teamNameRef = useRef(teamName);
|
const teamNameRef = useRef(teamName);
|
||||||
const taskIdRef = useRef(taskId);
|
const taskIdRef = useRef(taskId);
|
||||||
teamNameRef.current = teamName;
|
|
||||||
taskIdRef.current = taskId;
|
useEffect(() => {
|
||||||
|
teamNameRef.current = teamName;
|
||||||
|
taskIdRef.current = taskId;
|
||||||
|
}, [teamName, taskId]);
|
||||||
|
|
||||||
// Reset tracked state when team/task changes
|
// Reset tracked state when team/task changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,10 @@ export function useViewportObserver({
|
||||||
registerElement: (value: string) => (el: HTMLElement | null) => void;
|
registerElement: (value: string) => (el: HTMLElement | null) => void;
|
||||||
} {
|
} {
|
||||||
const onVisibleChangeRef = useRef(onVisibleChange);
|
const onVisibleChangeRef = useRef(onVisibleChange);
|
||||||
onVisibleChangeRef.current = onVisibleChange;
|
|
||||||
|
useEffect(() => {
|
||||||
|
onVisibleChangeRef.current = onVisibleChange;
|
||||||
|
}, [onVisibleChange]);
|
||||||
|
|
||||||
const observerRef = useRef<IntersectionObserver | null>(null);
|
const observerRef = useRef<IntersectionObserver | null>(null);
|
||||||
const visibleValuesRef = useRef<Set<string>>(new Set());
|
const visibleValuesRef = useRef<Set<string>>(new Set());
|
||||||
|
|
@ -67,6 +70,9 @@ export function useViewportObserver({
|
||||||
// and produce false positives for all visible elements.
|
// and produce false positives for all visible elements.
|
||||||
if (!root) return;
|
if (!root) return;
|
||||||
|
|
||||||
|
// Capture ref values for cleanup closure
|
||||||
|
const visibleValues = visibleValuesRef.current;
|
||||||
|
|
||||||
const observer = new IntersectionObserver(
|
const observer = new IntersectionObserver(
|
||||||
(entries) => {
|
(entries) => {
|
||||||
let changed = false;
|
let changed = false;
|
||||||
|
|
@ -75,19 +81,19 @@ export function useViewportObserver({
|
||||||
if (!value) continue;
|
if (!value) continue;
|
||||||
|
|
||||||
if (entry.isIntersecting) {
|
if (entry.isIntersecting) {
|
||||||
if (!visibleValuesRef.current.has(value)) {
|
if (!visibleValues.has(value)) {
|
||||||
visibleValuesRef.current.add(value);
|
visibleValues.add(value);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (visibleValuesRef.current.has(value)) {
|
if (visibleValues.has(value)) {
|
||||||
visibleValuesRef.current.delete(value);
|
visibleValues.delete(value);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (changed) {
|
if (changed) {
|
||||||
onVisibleChangeRef.current(Array.from(visibleValuesRef.current));
|
onVisibleChangeRef.current(Array.from(visibleValues));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ root, threshold }
|
{ root, threshold }
|
||||||
|
|
@ -105,7 +111,7 @@ export function useViewportObserver({
|
||||||
return () => {
|
return () => {
|
||||||
observer.disconnect();
|
observer.disconnect();
|
||||||
observerRef.current = null;
|
observerRef.current = null;
|
||||||
visibleValuesRef.current.clear();
|
visibleValues.clear();
|
||||||
};
|
};
|
||||||
}, [root, threshold]);
|
}, [root, threshold]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ export function initSentryRenderer(): void {
|
||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- cross-version @sentry/core type mismatch
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- cross-version @sentry/core type mismatch
|
||||||
const beforeSend = (event: any) => (telemetryAllowed ? event : null);
|
const beforeSend = (event: any): any => (telemetryAllowed ? event : null);
|
||||||
|
|
||||||
if (window.electronAPI) {
|
if (window.electronAPI) {
|
||||||
// Electron renderer — uses IPC transport to main process.
|
// Electron renderer — uses IPC transport to main process.
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ function storageKey(teamName: string): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Legacy keys used by the old three-key approach. */
|
/** Legacy keys used by the old three-key approach. */
|
||||||
function legacyKeys(teamName: string) {
|
function legacyKeys(teamName: string): { text: string; chips: string; attachments: string } {
|
||||||
return {
|
return {
|
||||||
text: `draft:compose:${teamName}`,
|
text: `draft:compose:${teamName}`,
|
||||||
chips: `draft:compose:${teamName}:chips`,
|
chips: `draft:compose:${teamName}:chips`,
|
||||||
|
|
|
||||||
|
|
@ -695,7 +695,7 @@ export function initializeNotificationListeners(): () => void {
|
||||||
updateStatus: 'available',
|
updateStatus: 'available',
|
||||||
availableVersion: s.version ?? null,
|
availableVersion: s.version ?? null,
|
||||||
releaseNotes: s.releaseNotes ?? null,
|
releaseNotes: s.releaseNotes ?? null,
|
||||||
showUpdateDialog: s.version !== dismissed,
|
showUpdateDialog: (s.version ?? null) !== dismissed,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import {
|
||||||
MEMBER_COLOR_PALETTE,
|
MEMBER_COLOR_PALETTE,
|
||||||
normalizeMemberColorName,
|
normalizeMemberColorName,
|
||||||
} from '@shared/constants/memberColors';
|
} from '@shared/constants/memberColors';
|
||||||
import { isLeadAgentType, isLeadMember } from '@shared/utils/leadDetection';
|
import { isLeadMember } from '@shared/utils/leadDetection';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
LeadActivityState,
|
LeadActivityState,
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
* (main: process.env, renderer: import.meta.env).
|
* (main: process.env, renderer: import.meta.env).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention -- Vite `define` injects this global
|
||||||
declare const __APP_VERSION__: string;
|
declare const __APP_VERSION__: string;
|
||||||
|
|
||||||
/** Release identifier injected at build time via Vite `define`. */
|
/** Release identifier injected at build time via Vite `define`. */
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue