diff --git a/mobile/app/_layout.tsx b/mobile/app/_layout.tsx index e6e091edf72..fc65614e71b 100644 --- a/mobile/app/_layout.tsx +++ b/mobile/app/_layout.tsx @@ -52,7 +52,9 @@ export default function RootLayout() { } void Linking.getInitialURL().then((url) => { - if (url) handleUrl(url) + if (url) { + handleUrl(url) + } }) const sub = Linking.addEventListener('url', ({ url }) => handleUrl(url)) diff --git a/mobile/app/h/[hostId]/accounts.tsx b/mobile/app/h/[hostId]/accounts.tsx index 8b358dd18fc..e38c6579336 100644 --- a/mobile/app/h/[hostId]/accounts.tsx +++ b/mobile/app/h/[hostId]/accounts.tsx @@ -39,10 +39,14 @@ export default function AccountsScreen() { const [busyAccountId, setBusyAccountId] = useState(null) useEffect(() => { - if (!hostId) return + if (!hostId) { + return + } let stale = false void loadHosts().then((hosts) => { - if (stale) return + if (stale) { + return + } const host = hosts.find((h) => h.id === hostId) if (!host) { setError('Host not found') @@ -60,9 +64,13 @@ export default function AccountsScreen() { // when the user switches accounts. Falls back to a one-shot accounts.list // if the subscription stream errors. useEffect(() => { - if (!client || connState !== 'connected') return + if (!client || connState !== 'connected') { + return + } const unsubscribe = client.subscribe('accounts.subscribe', null, (payload) => { - if (!payload || typeof payload !== 'object') return + if (!payload || typeof payload !== 'object') { + return + } const evt = payload as { type?: string; snapshot?: AccountsSnapshot } if ((evt.type === 'ready' || evt.type === 'snapshot') && evt.snapshot) { setSnapshot(evt.snapshot) @@ -73,7 +81,9 @@ export default function AccountsScreen() { }, [client, connState]) const refresh = useCallback(async () => { - if (!client) return + if (!client) { + return + } setRefreshing(true) try { const res = await client.sendRequest('accounts.list') @@ -92,7 +102,9 @@ export default function AccountsScreen() { const selectAccount = useCallback( async (provider: ProviderKey, accountId: string | null) => { - if (!client) return + if (!client) { + return + } setBusyAccountId(accountId ?? `${provider}:default`) const method = provider === 'claude' ? 'accounts.selectClaude' : 'accounts.selectCodex' try { @@ -115,7 +127,9 @@ export default function AccountsScreen() { ) const renderProviderSection = (provider: ProviderKey, title: string) => { - if (!snapshot) return null + if (!snapshot) { + return null + } const state = provider === 'claude' ? snapshot.claude : snapshot.codex const activeUsage = getActiveProviderRateLimits(snapshot, provider) const Icon = provider === 'claude' ? ClaudeIcon : OpenAIIcon diff --git a/mobile/app/h/[hostId]/files/[worktreeId].tsx b/mobile/app/h/[hostId]/files/[worktreeId].tsx index 37782a205aa..1f3d620d068 100644 --- a/mobile/app/h/[hostId]/files/[worktreeId].tsx +++ b/mobile/app/h/[hostId]/files/[worktreeId].tsx @@ -175,7 +175,9 @@ export default function MobileFileExplorerScreen() { const openFile = useCallback( async (relativePath: string, kind: 'text' | 'binary') => { - if (!client || kind === 'binary') return + if (!client || kind === 'binary') { + return + } setOpeningPath(relativePath) try { const response = await client.sendRequest('files.open', { diff --git a/mobile/app/h/[hostId]/index.tsx b/mobile/app/h/[hostId]/index.tsx index 30069d6be2c..d64f94269d6 100644 --- a/mobile/app/h/[hostId]/index.tsx +++ b/mobile/app/h/[hostId]/index.tsx @@ -130,8 +130,12 @@ const GROUP_OPTIONS: PickerOption[] = [ ] function getWorktreeStatus(w: Worktree): 'working' | 'active' | 'permission' | 'done' | 'inactive' { - if (w.status) return w.status - if (w.liveTerminalCount > 0) return 'active' + if (w.status) { + return w.status + } + if (w.liveTerminalCount > 0) { + return 'active' + } return 'inactive' } @@ -139,9 +143,15 @@ function getWorktreeStatus(w: Worktree): 'working' | 'active' | 'permission' | ' // worktrees with idle terminal prompts had no recent output and were excluded. // Any worktree with live terminals or unread output counts as "active". function isWorktreeActive(w: Worktree): boolean { - if (w.unread) return true - if (w.status) return w.status !== 'inactive' - if (w.liveTerminalCount > 0) return true + if (w.unread) { + return true + } + if (w.status) { + return w.status !== 'inactive' + } + if (w.liveTerminalCount > 0) { + return true + } return false } @@ -163,21 +173,29 @@ const WORKSPACE_STATUS_ORDER: ReturnType[] = [ function sortWorktrees(worktrees: Worktree[], mode: SortMode): Worktree[] { return [...worktrees].sort((a, b) => { - if (mode === 'name') return (a.displayName || a.repo).localeCompare(b.displayName || b.repo) - if (mode === 'recent') return (b.lastOutputAt ?? 0) - (a.lastOutputAt ?? 0) + if (mode === 'name') { + return (a.displayName || a.repo).localeCompare(b.displayName || b.repo) + } + if (mode === 'recent') { + return (b.lastOutputAt ?? 0) - (a.lastOutputAt ?? 0) + } if (mode === 'repo') { const repoComparison = a.repo.localeCompare(b.repo, undefined, { sensitivity: 'base' }) return repoComparison || (a.displayName || a.repo).localeCompare(b.displayName || b.repo) } // 'smart' — attention-first - if (a.unread !== b.unread) return a.unread ? -1 : 1 + if (a.unread !== b.unread) { + return a.unread ? -1 : 1 + } const aStatus = getWorktreeStatus(a) const bStatus = getWorktreeStatus(b) const statusOrder = { permission: 0, working: 1, done: 2, active: 3, inactive: 4 } - if (statusOrder[aStatus] !== statusOrder[bStatus]) + if (statusOrder[aStatus] !== statusOrder[bStatus]) { return statusOrder[aStatus] - statusOrder[bStatus] - if ((a.lastOutputAt ?? 0) !== (b.lastOutputAt ?? 0)) + } + if ((a.lastOutputAt ?? 0) !== (b.lastOutputAt ?? 0)) { return (b.lastOutputAt ?? 0) - (a.lastOutputAt ?? 0) + } return (a.displayName || a.repo).localeCompare(b.displayName || b.repo) }) } @@ -218,11 +236,19 @@ const PR_GROUP_LABELS: Record = { const PR_GROUP_ORDER: PRGroupKey[] = ['done', 'in-review', 'in-progress', 'closed'] function getPRGroupKey(w: Worktree): PRGroupKey { - if (!w.linkedPR) return 'in-progress' + if (!w.linkedPR) { + return 'in-progress' + } const s = w.linkedPR.state.toLowerCase() - if (s === 'merged') return 'done' - if (s === 'closed') return 'closed' - if (s === 'draft') return 'in-progress' + if (s === 'merged') { + return 'done' + } + if (s === 'closed') { + return 'closed' + } + if (s === 'draft') { + return 'in-progress' + } return 'in-review' } @@ -265,8 +291,11 @@ function buildSections( for (const w of unpinned) { const key = w.repo || 'Unknown' const list = byRepo.get(key) - if (list) list.push(w) - else byRepo.set(key, [w]) + if (list) { + list.push(w) + } else { + byRepo.set(key, [w]) + } } for (const [repo, items] of byRepo) { sections.push({ title: repo, data: items }) @@ -276,8 +305,11 @@ function buildSections( for (const w of unpinned) { const key = getWorktreeStatus(w) const list = byStatus.get(key) - if (list) list.push(w) - else byStatus.set(key, [w]) + if (list) { + list.push(w) + } else { + byStatus.set(key, [w]) + } } for (const status of WORKSPACE_STATUS_ORDER) { const items = byStatus.get(status) @@ -290,8 +322,11 @@ function buildSections( for (const w of unpinned) { const key = getPRGroupKey(w) const list = byGroup.get(key) - if (list) list.push(w) - else byGroup.set(key, [w]) + if (list) { + list.push(w) + } else { + byGroup.set(key, [w]) + } } for (const groupKey of PR_GROUP_ORDER) { const items = byGroup.get(groupKey) @@ -368,11 +403,15 @@ export default function HostScreen() { // Load persisted pins and preferences useEffect(() => { - if (!hostId) return + if (!hostId) { + return + } let stale = false void (async () => { const [pins, prefs] = await Promise.all([loadPinnedIds(hostId), loadPreferences(hostId)]) - if (stale) return + if (stale) { + return + } setPinnedIds(pins) setSortMode(prefs.sortMode as SortMode) setFilters({ @@ -413,10 +452,14 @@ export default function HostScreen() { setWorktrees([]) setLastKnownWorktrees([]) } - if (!hostId) return + if (!hostId) { + return + } let stale = false void loadHosts().then((hosts) => { - if (stale) return + if (stale) { + return + } const host = hosts.find((h) => h.id === hostId) if (!host) { setError('Host not found') @@ -431,13 +474,17 @@ export default function HostScreen() { }, [hostId]) const fetchWorktrees = useCallback(async () => { - if (!client || connState !== 'connected') return + if (!client || connState !== 'connected') { + return + } const requestClient = client const requestHostId = hostId try { const response = await requestClient.sendRequest('worktree.ps') - if (clientRef.current !== requestClient || hostId !== requestHostId) return + if (clientRef.current !== requestClient || hostId !== requestHostId) { + return + } if (response.ok) { const result = (response as RpcSuccess).result as { worktrees: Worktree[] } setWorktrees(result.worktrees) @@ -447,8 +494,12 @@ export default function HostScreen() { void requestClient .sendRequest('repo.list') .then((repoResponse) => { - if (clientRef.current !== requestClient || hostId !== requestHostId) return - if (!repoResponse.ok) return + if (clientRef.current !== requestClient || hostId !== requestHostId) { + return + } + if (!repoResponse.ok) { + return + } const repoResult = (repoResponse as RpcSuccess).result as { repos: RepoSummary[] } setRepoColorsByName( new Map( @@ -486,7 +537,9 @@ export default function HostScreen() { if (serverPinned.size === prev.size && [...serverPinned].every((id) => prev.has(id))) { return prev } - if (hostId) void savePinnedIds(hostId, serverPinned) + if (hostId) { + void savePinnedIds(hostId, serverPinned) + } return serverPinned }) } @@ -502,14 +555,20 @@ export default function HostScreen() { // Today's compat constants are wide-open so this never blocks; the wire // format is in place to flip a switch in a future release. useEffect(() => { - if (connState !== 'connected' || !client) return + if (connState !== 'connected' || !client) { + return + } let cancelled = false const requestClient = client void (async () => { try { const response = await requestClient.sendRequest('status.get') - if (cancelled || clientRef.current !== requestClient) return - if (!response.ok) return + if (cancelled || clientRef.current !== requestClient) { + return + } + if (!response.ok) { + return + } const status = (response as RpcSuccess).result as DesktopStatus const verdict = evaluateCompat({ desktopProtocolVersion: status.protocolVersion, @@ -538,7 +597,9 @@ export default function HostScreen() { useFocusEffect( useCallback(() => { - if (connState !== 'connected') return + if (connState !== 'connected') { + return + } void fetchWorktrees() // Why: React Navigation keeps previous stack screens mounted; only // poll the host list while this route is visible. @@ -553,9 +614,14 @@ export default function HostScreen() { (worktreeId: string, pinned: boolean) => { setPinnedIds((prev) => { const next = new Set(prev) - if (pinned) next.add(worktreeId) - else next.delete(worktreeId) - if (hostId) void savePinnedIds(hostId, next) + if (pinned) { + next.add(worktreeId) + } else { + next.delete(worktreeId) + } + if (hostId) { + void savePinnedIds(hostId, next) + } return next }) }, @@ -593,7 +659,9 @@ export default function HostScreen() { const handleDeleteWorktree = useCallback( async (item: Worktree) => { - if (!client) return + if (!client) { + return + } const removeFromList = (list: Worktree[]) => list.filter((w) => w.worktreeId !== item.worktreeId) @@ -619,7 +687,9 @@ export default function HostScreen() { ) const handleRemoveHost = useCallback(async () => { - if (!hostId) return + if (!hostId) { + return + } // Why: close the shared client first so its WebSocket is gone before // the host record disappears; otherwise the next loadHosts() the // provider does (e.g. on remount) wouldn't find this host but the @@ -648,7 +718,9 @@ export default function HostScreen() { const handleSortChange = useCallback( (value: SortMode) => { setSortMode(value) - if (hostId) void savePreferences(hostId, { sortMode: value }) + if (hostId) { + void savePreferences(hostId, { sortMode: value }) + } }, [hostId] ) @@ -656,10 +728,11 @@ export default function HostScreen() { const toggleActiveFilter = useCallback(() => { setFilters((prev) => { const next = { ...prev, activeOnly: !prev.activeOnly } - if (hostId) + if (hostId) { void savePreferences(hostId, { filterMode: next.activeOnly ? 'active' : 'all' }) + } return next }) }, [hostId]) @@ -668,10 +741,15 @@ export default function HostScreen() { (repo: string) => { setFilters((prev) => { const next = new Set(prev.selectedRepos) - if (next.has(repo)) next.delete(repo) - else next.add(repo) + if (next.has(repo)) { + next.delete(repo) + } else { + next.add(repo) + } const updated = { ...prev, selectedRepos: next } - if (hostId) void savePreferences(hostId, { selectedRepos: [...next] }) + if (hostId) { + void savePreferences(hostId, { selectedRepos: [...next] }) + } return updated }) }, @@ -680,12 +758,16 @@ export default function HostScreen() { const clearFilters = useCallback(() => { setFilters({ activeOnly: false, selectedRepos: new Set() }) - if (hostId) void savePreferences(hostId, { filterMode: 'all', selectedRepos: [] }) + if (hostId) { + void savePreferences(hostId, { filterMode: 'all', selectedRepos: [] }) + } }, [hostId]) const activeFilterCount = useMemo(() => { let count = 0 - if (filters.activeOnly) count++ + if (filters.activeOnly) { + count++ + } count += filters.selectedRepos.size return count }, [filters]) @@ -693,7 +775,9 @@ export default function HostScreen() { const handleGroupChange = useCallback( (value: GroupMode) => { setGroupMode(value) - if (hostId) void savePreferences(hostId, { groupMode: value }) + if (hostId) { + void savePreferences(hostId, { groupMode: value }) + } }, [hostId] ) @@ -716,7 +800,9 @@ export default function HostScreen() { const uniqueRepos = useMemo(() => { const repos = new Map() for (const w of displayWorktrees) { - if (!repos.has(w.repo)) repos.set(w.repo, repoColorsByName.get(w.repo) ?? repoColor(w.repo)) + if (!repos.has(w.repo)) { + repos.set(w.repo, repoColorsByName.get(w.repo) ?? repoColor(w.repo)) + } } return [...repos.entries()].map(([name, color]) => ({ name, color })) }, [displayWorktrees, repoColorsByName]) @@ -730,9 +816,14 @@ export default function HostScreen() { (title: string) => { setCollapsedGroups((prev) => { const next = new Set(prev) - if (next.has(title)) next.delete(title) - else next.add(title) - if (hostId) void savePreferences(hostId, { collapsedGroups: [...next] }) + if (next.has(title)) { + next.delete(title) + } else { + next.add(title) + } + if (hostId) { + void savePreferences(hostId, { collapsedGroups: [...next] }) + } return next }) }, @@ -799,7 +890,9 @@ export default function HostScreen() { const verdict = headerVerdict const isError = isErrorVerdict(verdict) const showReconnectButton = isError && hostId && verdict.kind !== 'auth-failed' - if (!showReconnectButton) return null + if (!showReconnectButton) { + return null + } return ( { - if (!section.title) return null + if (!section.title) { + return null + } const isCollapsed = collapsedGroups.has(section.title) const rawSection = rawSections.find((s) => s.title === section.title) const count = rawSection?.data.length ?? 0 @@ -1272,7 +1367,9 @@ function ListSeparator() { function repoColor(name: string): string { const palette = ['#f97316', '#8b5cf6', '#06b6d4', '#ec4899', '#84cc16', '#f59e0b', '#6366f1'] let hash = 0 - for (let i = 0; i < name.length; i++) hash = (hash * 31 + name.charCodeAt(i)) | 0 + for (let i = 0; i < name.length; i++) { + hash = (hash * 31 + name.charCodeAt(i)) | 0 + } return palette[Math.abs(hash) % palette.length]! } diff --git a/mobile/app/h/[hostId]/session/[worktreeId].tsx b/mobile/app/h/[hostId]/session/[worktreeId].tsx index f30870d9a30..89ad2c97014 100644 --- a/mobile/app/h/[hostId]/session/[worktreeId].tsx +++ b/mobile/app/h/[hostId]/session/[worktreeId].tsx @@ -640,7 +640,9 @@ function DiffLineRow({ ]} disabled={commentsBusy} onPress={() => { - if (commentLine !== undefined) onStartComment(commentLine) + if (commentLine !== undefined) { + onStartComment(commentLine) + } }} accessibilityLabel={`Add note on line ${commentLine}`} > @@ -697,7 +699,9 @@ function DiffLineRow({ ]} disabled={!commentDraft.trim() || commentsBusy} onPress={() => { - if (commentLine !== undefined) onSubmitComment(commentLine) + if (commentLine !== undefined) { + onSubmitComment(commentLine) + } }} > Save note @@ -769,7 +773,9 @@ function FileReader({ const submitComment = useCallback( (lineNumber: number) => { - if (!diffCommentActions) return + if (!diffCommentActions) { + return + } void diffCommentActions.onAdd(relativePath, lineNumber, commentDraft).then((added) => { if (added) { setActiveCommentLine(null) @@ -797,7 +803,9 @@ function FileReader({ onDraftChange={setCommentDraft} onSubmitComment={submitComment} onDeleteComment={(commentId) => { - if (diffCommentActions) void diffCommentActions.onDelete(commentId) + if (diffCommentActions) { + void diffCommentActions.onDelete(commentId) + } }} /> ), @@ -1134,7 +1142,9 @@ export default function SessionScreen() { }, []) const clearToastHideTimer = useCallback(() => { - if (!toastHideTimerRef.current) return + if (!toastHideTimerRef.current) { + return + } clearTimeout(toastHideTimerRef.current) toastHideTimerRef.current = null }, []) @@ -1150,7 +1160,9 @@ export default function SessionScreen() { duration: 150, useNativeDriver: true }).start(({ finished }) => { - if (!finished || toastSeqRef.current !== seq) return + if (!finished || toastSeqRef.current !== seq) { + return + } toastHideTimerRef.current = setTimeout(() => { toastHideTimerRef.current = null Animated.timing(toastOpacityRef.current, { @@ -1226,7 +1238,9 @@ export default function SessionScreen() { // auto-fit the PTY without a separate RPC round-trip. const measureViewportOnce = useCallback( async (handle: string) => { - if (viewportMeasuredRef.current) return + if (viewportMeasuredRef.current) { + return + } const dims = await getTerminalRef(handle)?.measureFitDimensions( terminalFrameHeightRef.current || undefined ) @@ -1240,9 +1254,15 @@ export default function SessionScreen() { const subscribeToTerminal = useCallback( (handle: string) => { - if (!client) return - if (terminalUnsubsRef.current.has(handle)) return - if (subscribingHandlesRef.current.has(handle)) return + if (!client) { + return + } + if (terminalUnsubsRef.current.has(handle)) { + return + } + if (subscribingHandlesRef.current.has(handle)) { + return + } if (!getTerminalRef(handle)) { return } @@ -1267,7 +1287,9 @@ export default function SessionScreen() { capabilities: { terminalBinaryStream: 1 } }, (result) => { - if (subscribeSeqRef.current.get(handle) !== seq) return + if (subscribeSeqRef.current.get(handle) !== seq) { + return + } const data = result as Record // Why: stale-event filter. Server-side state machine bumps a // monotonic seq on every applyLayout. Drop `resized` events @@ -1364,7 +1386,9 @@ export default function SessionScreen() { // phone dims. See log dump 2026-05-06 confirming the // race + measure-result null pattern. await getTerminalRef(handle)?.awaitReady() - if (subscribeSeqRef.current.get(handle) !== seq) return + if (subscribeSeqRef.current.get(handle) !== seq) { + return + } const dims = await getTerminalRef(handle)?.measureFitDimensions( terminalFrameHeightRef.current || undefined ) @@ -1374,8 +1398,12 @@ export default function SessionScreen() { // its own subscription. Tearing it down here would reset // the freshly-armed initialized flag and re-subscribe a // stale generation. - if (subscribeSeqRef.current.get(handle) !== seq) return - if (!getTerminalRef(handle)) return + if (subscribeSeqRef.current.get(handle) !== seq) { + return + } + if (!getTerminalRef(handle)) { + return + } // Why: we just got `scrollback` with cols=80 (server's // default fallback for null viewport). That means the // server-side subscriber record was registered before we @@ -1452,8 +1480,12 @@ export default function SessionScreen() { const toggleInFlightRef = useRef>(new Set()) const toggleDisplayMode = useCallback( async (handle: string) => { - if (!client) return - if (toggleInFlightRef.current.has(handle)) return + if (!client) { + return + } + if (toggleInFlightRef.current.has(handle)) { + return + } const current = terminalModes.get(handle) ?? 'auto' // Why: 'phone' on the wire is an observation ("currently phone-fitted"), // not a setting. The toggle only ever requests 'auto' or 'desktop'. @@ -1488,8 +1520,12 @@ export default function SessionScreen() { const fetchTerminals = useCallback( async (opts: { allowEmptyLoaded?: boolean } = {}) => { - if (!client) return - if (fetchTerminalsInFlightRef.current) return + if (!client) { + return + } + if (fetchTerminalsInFlightRef.current) { + return + } fetchTerminalsInFlightRef.current = true const allowEmptyLoaded = opts.allowEmptyLoaded ?? true @@ -1520,7 +1556,9 @@ export default function SessionScreen() { terminalRefs.current.delete(handle) initializedHandlesRef.current.delete(handle) setTerminalKeyboardMetrics((prev) => { - if (!prev.has(handle)) return prev + if (!prev.has(handle)) { + return prev + } const next = new Map(prev) next.delete(handle) return next @@ -1535,7 +1573,9 @@ export default function SessionScreen() { // for the tab strip, and createParams puts new tabs at the end. const seen = new Set() const deduped = result.terminals.filter((t) => { - if (seen.has(t.handle)) return false + if (seen.has(t.handle)) { + return false + } seen.add(t.handle) return true }) @@ -1690,7 +1730,9 @@ export default function SessionScreen() { const readMarkdownTab = useCallback( async (tab: Extract) => { - if (!client) return + if (!client) { + return + } setMarkdownDocs((prev) => new Map(prev).set(tab.id, { status: 'loading' })) try { const response = await client.sendRequest('markdown.readTab', { @@ -1733,7 +1775,9 @@ export default function SessionScreen() { const readFileTab = useCallback( async (tab: Extract) => { - if (!client) return + if (!client) { + return + } setFileDocs((prev) => new Map(prev).set(tab.id, { status: 'loading' })) try { if (tab.diffSource === 'staged' || tab.diffSource === 'unstaged') { @@ -1847,7 +1891,9 @@ export default function SessionScreen() { const addDiffCommentForFile = useCallback( async (filePath: string, lineNumber: number, body: string): Promise => { - if (diffCommentBusy) return false + if (diffCommentBusy) { + return false + } const nextId = `mobile-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}` const result = addMobileDiffComment(diffCommentsRef.current, { id: nextId, @@ -1857,7 +1903,9 @@ export default function SessionScreen() { body, createdAt: Date.now() }) - if (!result.comment) return false + if (!result.comment) { + return false + } const previous = diffCommentsRef.current setDiffCommentBusy(true) setDiffComments(result.comments) @@ -1880,10 +1928,14 @@ export default function SessionScreen() { const deleteDiffCommentForFile = useCallback( async (commentId: string): Promise => { - if (diffCommentBusy) return + if (diffCommentBusy) { + return + } const previous = diffCommentsRef.current const next = removeMobileDiffComments(previous, new Set([commentId])) - if (next.length === previous.length) return + if (next.length === previous.length) { + return + } setDiffCommentBusy(true) setDiffComments(next) try { @@ -1902,7 +1954,9 @@ export default function SessionScreen() { const copyDiffCommentsToClipboard = useCallback(async (): Promise => { const comments = diffCommentsRef.current - if (comments.length === 0) return + if (comments.length === 0) { + return + } try { await Clipboard.setStringAsync(formatDiffComments(comments)) triggerSuccess() @@ -1915,7 +1969,9 @@ export default function SessionScreen() { const sendDiffCommentsToAgent = useCallback((): void => { const comments = diffCommentsRef.current.filter((comment) => !comment.sentAt) - if (comments.length === 0) return + if (comments.length === 0) { + return + } setPendingDiffNotesDelivery({ comments: [...comments], prompt: formatDiffComments(comments) @@ -1926,7 +1982,9 @@ export default function SessionScreen() { async (delivered: readonly DiffComment[]): Promise => { const previous = diffCommentsRef.current const next = removeDeliveredMobileDiffComments(previous, delivered) - if (next.length === previous.length) return + if (next.length === previous.length) { + return + } setDiffCommentBusy(true) setDiffComments(next) try { @@ -1943,7 +2001,9 @@ export default function SessionScreen() { const updateMarkdownLocalContent = useCallback((tabId: string, content: string) => { setMarkdownDocs((prev) => { const current = prev.get(tabId) - if (current?.status !== 'ready') return prev + if (current?.status !== 'ready') { + return prev + } const next = new Map(prev) next.set(tabId, { ...current, @@ -1958,7 +2018,9 @@ export default function SessionScreen() { const copyMarkdownLocalContent = useCallback( async (tabId: string) => { const current = markdownDocs.get(tabId) - if (current?.status !== 'ready') return + if (current?.status !== 'ready') { + return + } await Clipboard.setStringAsync(current.localContent) triggerSuccess() showToast('Copied') @@ -2008,7 +2070,9 @@ export default function SessionScreen() { const discardMarkdownLocalContent = useCallback( (tab: Extract) => { const current = markdownDocs.get(tab.id) - if (current?.status !== 'ready') return + if (current?.status !== 'ready') { + return + } if (!current.isDirty) { void readMarkdownTab(tab) return @@ -2029,16 +2093,24 @@ export default function SessionScreen() { const saveMarkdownTab = useCallback( async (tab: Extract) => { - if (!client) return + if (!client) { + return + } const current = markdownDocs.get(tab.id) - if (current?.status !== 'ready' || current.saving || !current.editable) return - if (markdownSaveInFlightRef.current.has(tab.id)) return + if (current?.status !== 'ready' || current.saving || !current.editable) { + return + } + if (markdownSaveInFlightRef.current.has(tab.id)) { + return + } markdownSaveInFlightRef.current.add(tab.id) const saveSeq = (markdownSaveSeqRef.current.get(tab.id) ?? 0) + 1 markdownSaveSeqRef.current.set(tab.id, saveSeq) setMarkdownDocs((prev) => { const existing = prev.get(tab.id) - if (existing?.status !== 'ready') return prev + if (existing?.status !== 'ready') { + return prev + } return new Map(prev).set(tab.id, { ...existing, saving: true, saveError: undefined }) }) try { @@ -2080,7 +2152,9 @@ export default function SessionScreen() { } setMarkdownDocs((prev) => { const existing = prev.get(tab.id) - if (existing?.status !== 'ready') return prev + if (existing?.status !== 'ready') { + return prev + } return new Map(prev).set(tab.id, { ...existing, saving: false, @@ -2097,14 +2171,20 @@ export default function SessionScreen() { const fetchSessionTabsInFlightRef = useRef(false) const fetchSessionTabs = useCallback(async () => { - if (!client) return - if (fetchSessionTabsInFlightRef.current) return + if (!client) { + return + } + if (fetchSessionTabsInFlightRef.current) { + return + } fetchSessionTabsInFlightRef.current = true try { const response = await client.sendRequest('session.tabs.list', { worktree: `id:${worktreeId}` }) - if (!response.ok) return + if (!response.ok) { + return + } const result = (response as RpcSuccess).result as SessionTabsResult applySessionTabs(result) } catch { @@ -2115,9 +2195,13 @@ export default function SessionScreen() { }, [applySessionTabs, client, worktreeId]) useEffect(() => { - if (connState === 'connected') return + if (connState === 'connected') { + return + } for (const queued of terminalGestureInputQueuesRef.current.values()) { - if (queued.timer) clearTimeout(queued.timer) + if (queued.timer) { + clearTimeout(queued.timer) + } } terminalGestureInputQueuesRef.current.clear() terminalGestureInputInFlightRef.current.clear() @@ -2132,14 +2216,18 @@ export default function SessionScreen() { void client .sendRequest('status.get') .then((response) => { - if (stale || !response.ok) return + if (stale || !response.ok) { + return + } const status = (response as RpcSuccess).result as RuntimeStatusResult setBrowserScreencastSupported( status.capabilities?.includes('browser.screencast.v1') === true ) }) .catch(() => { - if (!stale) setBrowserScreencastSupported(false) + if (!stale) { + setBrowserScreencastSupported(false) + } }) return () => { stale = true @@ -2151,12 +2239,18 @@ export default function SessionScreen() { // The shared client itself stays alive across screens; we just need // the token alongside the client. useEffect(() => { - if (!hostId) return + if (!hostId) { + return + } let stale = false void loadHosts().then((hosts) => { - if (stale) return + if (stale) { + return + } const host = hosts.find((h) => h.id === hostId) - if (host) deviceTokenRef.current = host.deviceToken + if (host) { + deviceTokenRef.current = host.deviceToken + } }) return () => { stale = true @@ -2171,7 +2265,9 @@ export default function SessionScreen() { useCallback(() => { let stale = false void loadTerminalAccessoryLayout().then((layout) => { - if (!stale) setVisibleBuiltInIds(layout.visibleBuiltInIds) + if (!stale) { + setVisibleBuiltInIds(layout.visibleBuiltInIds) + } }) return () => { stale = true @@ -2183,11 +2279,15 @@ export default function SessionScreen() { let mounted = true const refresh = () => { void loadTerminalAccessoryLayout().then((layout) => { - if (mounted) setVisibleBuiltInIds(layout.visibleBuiltInIds) + if (mounted) { + setVisibleBuiltInIds(layout.visibleBuiltInIds) + } }) } const sub = AppState.addEventListener('change', (s: AppStateStatus) => { - if (s === 'active') refresh() + if (s === 'active') { + refresh() + } }) return () => { mounted = false @@ -2202,17 +2302,27 @@ export default function SessionScreen() { // and the server phone-fits to dims a few rows too tall). const refitTimerRef = useRef | null>(null) const scheduleViewportRefit = useCallback(() => { - if (refitTimerRef.current) clearTimeout(refitTimerRef.current) + if (refitTimerRef.current) { + clearTimeout(refitTimerRef.current) + } refitTimerRef.current = setTimeout(() => { const handle = activeHandleRef.current - if (!handle) return + if (!handle) { + return + } const ref = terminalRefs.current.get(handle) - if (!ref) return + if (!ref) { + return + } void (async () => { const dims = await ref.measureFitDimensions(terminalFrameHeightRef.current || undefined) - if (!dims) return + if (!dims) { + return + } const prev = viewportRef.current - if (prev && prev.cols === dims.cols && prev.rows === dims.rows) return + if (prev && prev.cols === dims.cols && prev.rows === dims.rows) { + return + } viewportRef.current = dims viewportMeasuredRef.current = true // Why: prefer the in-place viewport update RPC over the legacy @@ -2229,7 +2339,9 @@ export default function SessionScreen() { client: { id: deviceToken, type: 'mobile' as const }, viewport: dims }) - if (response.ok) return + if (response.ok) { + return + } } catch { // Fall through to legacy resubscribe. } @@ -2253,7 +2365,9 @@ export default function SessionScreen() { const showSub = Keyboard.addListener(showEvent, onShow) const hideSub = Keyboard.addListener(hideEvent, onHide) return () => { - if (refitTimerRef.current) clearTimeout(refitTimerRef.current) + if (refitTimerRef.current) { + clearTimeout(refitTimerRef.current) + } showSub.remove() hideSub.remove() } @@ -2270,7 +2384,9 @@ export default function SessionScreen() { const tabStripVisible = terminals.length > 1 const prevTabStripVisibleRef = useRef(tabStripVisible) useEffect(() => { - if (prevTabStripVisibleRef.current === tabStripVisible) return + if (prevTabStripVisibleRef.current === tabStripVisible) { + return + } prevTabStripVisibleRef.current = tabStripVisible viewportMeasuredRef.current = false scheduleViewportRefit() @@ -2307,7 +2423,9 @@ export default function SessionScreen() { pendingActiveTerminalHandleRef.current = null initialEmptySessionAutoCreateRef.current = null for (const queued of terminalGestureInputQueuesRef.current.values()) { - if (queued.timer) clearTimeout(queued.timer) + if (queued.timer) { + clearTimeout(queued.timer) + } } terminalGestureInputQueuesRef.current.clear() terminalGestureInputInFlightRef.current.clear() @@ -2327,7 +2445,9 @@ export default function SessionScreen() { }, [clearDelayedActionTimers, clearTerminalCache, worktreeId]) useEffect(() => { - if (connState !== 'connected') return + if (connState !== 'connected') { + return + } // Why: the RPC client auto-resends terminal.subscribe on reconnect. // Keep the current xterm visible while the binary snapshot hydrates, // instead of clearing to a blank "Loading terminals" surface. @@ -2344,7 +2464,9 @@ export default function SessionScreen() { let disposed = false const timers: ReturnType[] = [] function addTimer(fn: () => void, ms: number) { - if (disposed) return + if (disposed) { + return + } timers.push(setTimeout(fn, ms)) } void (async () => { @@ -2355,23 +2477,33 @@ export default function SessionScreen() { }) .catch(() => null) } - if (disposed) return + if (disposed) { + return + } await fetchSessionTabs().catch(() => null) - if (disposed) return + if (disposed) { + return + } await fetchTerminals({ allowEmptyLoaded: false }) - if (disposed) return + if (disposed) { + return + } addTimer(() => void fetchTerminals({ allowEmptyLoaded: false }), 750) addTimer(() => void fetchTerminals({ allowEmptyLoaded: true }), 1500) if (client && created === '1') { addTimer(() => { - if (activeHandleRef.current) return + if (activeHandleRef.current) { + return + } void (async () => { await client .sendRequest('worktree.activate', { worktree: `id:${worktreeId}` }) .catch(() => null) - if (disposed) return + if (disposed) { + return + } await fetchTerminals({ allowEmptyLoaded: true }) addTimer(() => void fetchTerminals({ allowEmptyLoaded: true }), 750) })() @@ -2380,12 +2512,16 @@ export default function SessionScreen() { })() return () => { disposed = true - for (const t of timers) clearTimeout(t) + for (const t of timers) { + clearTimeout(t) + } } }, [client, connState, created, fetchSessionTabs, fetchTerminals, worktreeId]) useEffect(() => { - if (!client || connState !== 'connected') return + if (!client || connState !== 'connected') { + return + } const unsubscribe = client.subscribe( 'session.tabs.subscribe', { worktree: `id:${worktreeId}` }, @@ -2416,7 +2552,9 @@ export default function SessionScreen() { useFocusEffect( useCallback(() => { - if (connState !== 'connected') return + if (connState !== 'connected') { + return + } void fetchSessionTabs() void fetchTerminals() // Why: the live tab subscription stays mounted for stream ownership, @@ -2598,7 +2736,9 @@ export default function SessionScreen() { ) useEffect(() => { - if (activeSessionTab?.type !== 'markdown') return + if (activeSessionTab?.type !== 'markdown') { + return + } const doc = markdownDocs.get(activeSessionTab.id) if (!doc) { void readMarkdownTab(activeSessionTab) @@ -2606,7 +2746,9 @@ export default function SessionScreen() { }, [activeSessionTab, markdownDocs, readMarkdownTab]) useEffect(() => { - if (activeSessionTab?.type !== 'file') return + if (activeSessionTab?.type !== 'file') { + return + } const doc = fileDocs.get(activeSessionTab.id) if (!doc) { void readFileTab(activeSessionTab) @@ -2614,7 +2756,9 @@ export default function SessionScreen() { }, [activeSessionTab, fileDocs, readFileTab]) async function handleSend() { - if (!client || !activeHandle || sendingRef.current) return + if (!client || !activeHandle || sendingRef.current) { + return + } sendingRef.current = true const text = input @@ -2640,7 +2784,9 @@ export default function SessionScreen() { } async function handleAccessoryKey(bytes: string) { - if (!client || !activeHandle || !canSend) return + if (!client || !activeHandle || !canSend) { + return + } try { await client.sendRequest('terminal.send', { @@ -2658,7 +2804,9 @@ export default function SessionScreen() { const sendLiveTerminalInput = useCallback( (handle: string, bytes: string) => { - if (bytes.length === 0) return + if (bytes.length === 0) { + return + } if (!isTerminalLiveInputWithinByteLimit(bytes)) { triggerError() showToast('Input too large (max 256 KiB)', 1500) @@ -2690,20 +2838,26 @@ export default function SessionScreen() { ) const focusLiveInput = useCallback(() => { - if (!canSend || !liveInputEnabled) return + if (!canSend || !liveInputEnabled) { + return + } liveInputRef.current?.focus() }, [canSend, liveInputEnabled]) const handleTerminalTap = useCallback( (handle: string) => { - if (handle !== activeHandleRef.current) return + if (handle !== activeHandleRef.current) { + return + } focusLiveInput() }, [focusLiveInput] ) const toggleLiveInput = useCallback(() => { - if (!activeHandle) return + if (!activeHandle) { + return + } const nextEnabled = !liveInputTerminalHandles.has(activeHandle) setLiveInputTerminalHandles((prev) => { const next = new Set(prev) @@ -2749,10 +2903,16 @@ export default function SessionScreen() { const handleLiveInputKeyPress = useCallback( (event: { nativeEvent: { key: string } }) => { - if (!activeHandle) return - if (!liveInputTerminalHandles.has(activeHandle)) return + if (!activeHandle) { + return + } + if (!liveInputTerminalHandles.has(activeHandle)) { + return + } const bytes = getTerminalLiveSpecialKeyBytes(event.nativeEvent.key) - if (!bytes) return + if (!bytes) { + return + } sendLiveTerminalInput(activeHandle, bytes) setLiveInputCapture('') liveInputRef.current?.setNativeProps({ text: '' }) @@ -2761,8 +2921,12 @@ export default function SessionScreen() { ) const handleLiveInputSubmit = useCallback(() => { - if (!activeHandle) return - if (!liveInputTerminalHandles.has(activeHandle)) return + if (!activeHandle) { + return + } + if (!liveInputTerminalHandles.has(activeHandle)) { + return + } sendLiveTerminalInput(activeHandle, '\r') setLiveInputCapture('') liveInputRef.current?.setNativeProps({ text: '' }) @@ -2799,19 +2963,25 @@ export default function SessionScreen() { const flushTerminalGestureInput = useCallback(async (handle: string) => { const queued = terminalGestureInputQueuesRef.current.get(handle) - if (!queued) return + if (!queued) { + return + } if (queued.timer) { clearTimeout(queued.timer) queued.timer = null } - if (terminalGestureInputInFlightRef.current.has(handle)) return + if (terminalGestureInputInFlightRef.current.has(handle)) { + return + } terminalGestureInputQueuesRef.current.delete(handle) const isActive = handle === activeHandleRef.current && activeSessionTabTypeRef.current === 'terminal' const isFresh = Date.now() - queued.lastUpdatedMs <= TERMINAL_GESTURE_INPUT_MAX_QUEUE_AGE_MS const rpc = clientRef.current - if (!rpc || connStateRef.current !== 'connected' || !isActive || !isFresh) return + if (!rpc || connStateRef.current !== 'connected' || !isActive || !isFresh) { + return + } terminalGestureInputInFlightRef.current.add(handle) try { @@ -2830,7 +3000,9 @@ export default function SessionScreen() { const next = terminalGestureInputQueuesRef.current.get(handle) if (next) { if (Date.now() - next.lastUpdatedMs > TERMINAL_GESTURE_INPUT_MAX_QUEUE_AGE_MS) { - if (next.timer) clearTimeout(next.timer) + if (next.timer) { + clearTimeout(next.timer) + } terminalGestureInputQueuesRef.current.delete(handle) } else { void flushTerminalGestureInput(handle) @@ -2854,7 +3026,9 @@ export default function SessionScreen() { } if (current) { - if (current.timer) clearTimeout(current.timer) + if (current.timer) { + clearTimeout(current.timer) + } if (!terminalGestureInputInFlightRef.current.has(handle)) { void flushTerminalGestureInput(handle) } else { @@ -2892,23 +3066,34 @@ export default function SessionScreen() { const handleTerminalInput = useCallback( async (handle: string, bytes: string) => { - if (!client || connState !== 'connected' || bytes.length === 0) return - if (handle !== activeHandleRef.current || activeSessionTabTypeRef.current !== 'terminal') + if (!client || connState !== 'connected' || bytes.length === 0) { return + } + if (handle !== activeHandleRef.current || activeSessionTabTypeRef.current !== 'terminal') { + return + } const modes = ptyModesRef.current.get(handle) // Why: WebView gesture bytes can become PTY input here, so mouse-aware // reports stay behind validation and SSH-safe rate limiting. - if (!modes?.altScreen && !isGestureMouseTrackingMode(modes?.mouseTrackingMode)) return + if (!modes?.altScreen && !isGestureMouseTrackingMode(modes?.mouseTrackingMode)) { + return + } const sequenceCount = countTerminalGestureInputSequences(bytes) - if (sequenceCount == null) return - if (!allowTerminalGestureInput(handle, sequenceCount)) return + if (sequenceCount == null) { + return + } + if (!allowTerminalGestureInput(handle, sequenceCount)) { + return + } enqueueTerminalGestureInput(handle, bytes, sequenceCount) }, [allowTerminalGestureInput, client, connState, enqueueTerminalGestureInput] ) async function handleClearTerminal(target: Terminal) { - if (!client) return + if (!client) { + return + } getTerminalRef(target.handle)?.clear() try { await client.sendRequest('terminal.clearBuffer', { @@ -2974,14 +3159,20 @@ export default function SessionScreen() { ) const handleSelectionMode = useCallback((handle: string, active: boolean) => { - if (handle !== activeHandleRef.current) return + if (handle !== activeHandleRef.current) { + return + } setSelectModeActive(active) - if (active) Keyboard.dismiss() + if (active) { + Keyboard.dismiss() + } }, []) const handleSelectionCopy = useCallback( async (handle: string, text: string) => { - if (handle !== activeHandleRef.current) return + if (handle !== activeHandleRef.current) { + return + } if (!text || text.length === 0) { terminalRefs.current.get(handle)?.cancelSelect() return @@ -2993,7 +3184,9 @@ export default function SessionScreen() { // every clipboard write, so our toast would be redundant; iOS shows // nothing on copy (it only banners on paste), so the in-app toast is // the only success signal there. - if (Platform.OS === 'ios') showToast('Copied') + if (Platform.OS === 'ios') { + showToast('Copied') + } terminalRefs.current.get(handle)?.cancelSelect() } catch (e) { triggerError() @@ -3011,7 +3204,9 @@ export default function SessionScreen() { const handleSelectionEvicted = useCallback( (handle: string) => { - if (handle !== activeHandleRef.current) return + if (handle !== activeHandleRef.current) { + return + } // eslint-disable-next-line no-console console.warn('[mobile-clip] selection evicted') showToast('Selection cleared (scrolled out of buffer)', 1500) @@ -3044,17 +3239,26 @@ export default function SessionScreen() { ) const handleHaptic = useCallback((kind: 'selection' | 'success' | 'error' | 'edge-bump') => { - if (kind === 'selection') triggerSelection() - else if (kind === 'success') triggerSuccess() - else if (kind === 'error') triggerError() - else if (kind === 'edge-bump') triggerEdgeBump() + if (kind === 'selection') { + triggerSelection() + } else if (kind === 'success') { + triggerSuccess() + } else if (kind === 'error') { + triggerError() + } else if (kind === 'edge-bump') { + triggerEdgeBump() + } }, []) const handlePaste = useCallback(async () => { - if (!client || !activeHandle || !canSend) return + if (!client || !activeHandle || !canSend) { + return + } try { const text = await Clipboard.getStringAsync() - if (text.length === 0) return + if (text.length === 0) { + return + } const modes = ptyModesRef.current.get(activeHandle) || { bracketedPasteMode: false, altScreen: false, @@ -3094,7 +3298,9 @@ export default function SessionScreen() { const isDisconnected = connState !== 'connected' // eslint-disable-next-line no-console console.warn('[mobile-clip] paste failed', { name: err.name, message: err.message }) - if (isDisconnected) showToast('Paste failed (disconnected)', 1500) + if (isDisconnected) { + showToast('Paste failed (disconnected)', 1500) + } } }, [client, activeHandle, canSend, connState, showToast]) @@ -3103,13 +3309,16 @@ export default function SessionScreen() { let mounted = true const refresh = () => { void Clipboard.hasStringAsync().then((has) => { - if (mounted) setCanPaste(has) + if (mounted) { + setCanPaste(has) + } }) } refresh() const sub = AppState.addEventListener('change', (s: AppStateStatus) => { - if (s === 'active') refresh() - else if (selectModeActive && activeHandleRef.current) { + if (s === 'active') { + refresh() + } else if (selectModeActive && activeHandleRef.current) { terminalRefs.current.get(activeHandleRef.current)?.cancelSelect() } }) @@ -3191,7 +3400,9 @@ export default function SessionScreen() { agent?: MobileNewTabAgentOption['agent'], options?: { initialPrompt?: string; onPromptSent?: () => void } ) { - if (!client || creating) return + if (!client || creating) { + return + } setCreating(true) setCreateError('') @@ -3293,7 +3504,9 @@ export default function SessionScreen() { } async function handleCreateMarkdownNote() { - if (!client || creatingMarkdown) return + if (!client || creatingMarkdown) { + return + } setCreatingMarkdown(true) setCreateError('') @@ -3337,7 +3550,9 @@ export default function SessionScreen() { } async function handleCreateBrowser(rawUrl = 'about:blank'): Promise { - if (!client || creatingBrowser) return false + if (!client || creatingBrowser) { + return false + } if (browserScreencastSupported !== true) { showToast('Desktop update required for mobile browser streaming', 1600) return false @@ -3404,7 +3619,9 @@ export default function SessionScreen() { } async function handleRenameTerminal(value: string) { - if (!client || !renameTarget) return + if (!client || !renameTarget) { + return + } const target = renameTarget setRenameTarget(null) @@ -3432,7 +3649,9 @@ export default function SessionScreen() { } async function handleCloseTerminal(target: Terminal) { - if (!client) return + if (!client) { + return + } try { const response = await client.sendRequest('terminal.close', { @@ -3462,7 +3681,9 @@ export default function SessionScreen() { } async function handleCloseSessionTab(tab: MobileSessionTab) { - if (!client) return + if (!client) { + return + } try { const response = await client.sendRequest('session.tabs.close', { worktree: `id:${worktreeId}`, @@ -3489,7 +3710,9 @@ export default function SessionScreen() { } const isPhoneMode = (handle: string | null): boolean => { - if (!handle) return false + if (!handle) { + return false + } const mode = terminalModes.get(handle) return mode === 'auto' || mode === 'phone' || mode === undefined } @@ -3543,7 +3766,9 @@ export default function SessionScreen() { : keyboardHeight : 0 const activeTerminalKeyboardLift = (() => { - if (keyboardLift <= 0 || !activeHandle) return 0 + if (keyboardLift <= 0 || !activeHandle) { + return 0 + } const metrics = terminalKeyboardMetrics.get(activeHandle) if (!metrics || metrics.rows <= 0 || terminalFrameHeightRef.current <= 0) { return keyboardLift @@ -3625,7 +3850,9 @@ export default function SessionScreen() { onPress: () => { const delivery = pendingDiffNotesDelivery setPendingDiffNotesDelivery(null) - if (!delivery) return + if (!delivery) { + return + } void handleCreateTerminal(option.agent, { initialPrompt: delivery.prompt, onPromptSent: () => void clearDeliveredDiffComments(delivery.comments) @@ -4031,15 +4258,21 @@ export default function SessionScreen() { ]} disabled={!canSend} onPressIn={() => { - if (!key.repeatable) return + if (!key.repeatable) { + return + } void handleAccessoryKey(key.bytes) startAccessoryRepeat(key.bytes) }} onPressOut={() => { - if (key.repeatable) stopAccessoryRepeat() + if (key.repeatable) { + stopAccessoryRepeat() + } }} onPress={() => { - if (key.repeatable) return + if (key.repeatable) { + return + } void handleAccessoryKey(key.bytes) }} accessibilityLabel={key.accessibilityLabel ?? `Send ${key.label}`} @@ -4241,7 +4474,9 @@ export default function SessionScreen() { onPress: () => { const delivery = pendingDiffNotesDelivery setPendingDiffNotesDelivery(null) - if (!delivery) return + if (!delivery) { + return + } void Clipboard.setStringAsync(delivery.prompt) .then(() => { triggerSuccess() diff --git a/mobile/app/h/[hostId]/source-control/[worktreeId].tsx b/mobile/app/h/[hostId]/source-control/[worktreeId].tsx index cce6bf253c2..8b0e82be3d4 100644 --- a/mobile/app/h/[hostId]/source-control/[worktreeId].tsx +++ b/mobile/app/h/[hostId]/source-control/[worktreeId].tsx @@ -236,8 +236,12 @@ function formatBranchEntryMeta(entry: MobileGitBranchChangeEntry): string | null } function diffLinePrefix(kind: MobileDiffLine['kind']): string { - if (kind === 'add') return '+' - if (kind === 'delete') return '-' + if (kind === 'add') { + return '+' + } + if (kind === 'delete') { + return '-' + } return ' ' } @@ -336,7 +340,9 @@ export default function MobileSourceControlScreen() { setBranchCompareState((prev) => (prev.kind === 'ready' ? prev : { kind: 'loading' })) try { const baseRef = await resolveMobileBranchCompareBaseRef(client, worktreeId) - if (!isCurrentLoad()) return false + if (!isCurrentLoad()) { + return false + } if (!baseRef) { setBranchCompareState({ kind: 'idle' }) return true @@ -345,7 +351,9 @@ export default function MobileSourceControlScreen() { worktree: `id:${worktreeId}`, baseRef }) - if (!isCurrentLoad()) return false + if (!isCurrentLoad()) { + return false + } if (!response.ok) { if (isMobileGitUnavailable(response.error?.code, response.error?.message)) { setBranchCompareState({ kind: 'idle' }) @@ -359,7 +367,9 @@ export default function MobileSourceControlScreen() { }) return true } catch (err) { - if (!isCurrentLoad()) return false + if (!isCurrentLoad()) { + return false + } const message = err instanceof Error ? err.message : 'Unable to load committed changes' setBranchCompareState((prev) => { if (options?.preserveReadyOnFailure && prev.kind === 'ready') { @@ -404,14 +414,18 @@ export default function MobileSourceControlScreen() { } return false } - if (!isCurrentLoad()) return false + if (!isCurrentLoad()) { + return false + } setScreenState((prev) => (prev.kind === 'ready' ? prev : { kind: 'loading' })) try { for (let attempt = 0; attempt <= SELECTOR_RETRY_COUNT; attempt += 1) { const response = await client.sendRequest('git.status', { worktree: `id:${worktreeId}` }) - if (!isCurrentLoad()) return false + if (!isCurrentLoad()) { + return false + } if (response.ok) { const result = (response as RpcSuccess).result as MobileGitStatusResult setScreenState({ kind: 'ready', status: result }) @@ -433,13 +447,17 @@ export default function MobileSourceControlScreen() { isMobileGitTransientRefreshError(response.error?.code, response.error?.message) if (shouldRetry && attempt < SELECTOR_RETRY_COUNT) { await wait(SELECTOR_RETRY_DELAY_MS) - if (!isCurrentLoad()) return false + if (!isCurrentLoad()) { + return false + } continue } throw new Error(response.error?.message || 'Unable to load source control') } } catch (err) { - if (!isCurrentLoad()) return false + if (!isCurrentLoad()) { + return false + } const message = err instanceof Error ? err.message : 'Unable to load source control' setScreenState((prev) => { // Why: git mutations can succeed while the immediate status refresh @@ -590,13 +608,17 @@ export default function MobileSourceControlScreen() { runner: () => Promise, options?: { clearCommitMessage?: boolean } ) => { - if (busyActionRef.current) return false + if (busyActionRef.current) { + return false + } busyActionRef.current = actionId setBusyAction(actionId) setActionError(null) try { await runner() - if (!mountedRef.current) return false + if (!mountedRef.current) { + return false + } if (options?.clearCommitMessage) { setCommitMessage('') } @@ -604,7 +626,9 @@ export default function MobileSourceControlScreen() { await loadStatus({ preserveReadyOnFailure: true, force: true }) return true } catch (err) { - if (!mountedRef.current) return false + if (!mountedRef.current) { + return false + } triggerError() setActionError(err instanceof Error ? err.message : 'Source control action failed') return false @@ -655,19 +679,25 @@ export default function MobileSourceControlScreen() { const stageAll = useCallback(async () => { const filePaths = stageablePaths - if (filePaths.length === 0) return + if (filePaths.length === 0) { + return + } await runGitAction('stage-all', 'git.bulkStage', { filePaths }) }, [runGitAction, stageablePaths]) const unstageAll = useCallback(async () => { const filePaths = unstageablePaths - if (filePaths.length === 0) return + if (filePaths.length === 0) { + return + } await runGitAction('unstage-all', 'git.bulkUnstage', { filePaths }) }, [runGitAction, unstageablePaths]) const commit = useCallback(async () => { const message = commitMessage.trim() - if (!message) return false + if (!message) { + return false + } return await runGitWorkflow( 'commit', async () => { @@ -680,8 +710,12 @@ export default function MobileSourceControlScreen() { const runCommitFollowUps = useCallback( async (actionId: string, afterCommit: () => Promise) => { const message = commitMessage.trim() - if (!message) return false - if (busyActionRef.current) return false + if (!message) { + return false + } + if (busyActionRef.current) { + return false + } busyActionRef.current = actionId setBusyAction(actionId) setActionError(null) @@ -690,13 +724,17 @@ export default function MobileSourceControlScreen() { await sendCommitRequest(message) didCommit = true await afterCommit() - if (!mountedRef.current) return false + if (!mountedRef.current) { + return false + } setCommitMessage('') triggerSuccess() await loadStatus({ preserveReadyOnFailure: true, force: true }) return true } catch (err) { - if (!mountedRef.current) return false + if (!mountedRef.current) { + return false + } triggerError() const message = err instanceof Error ? err.message : 'Source control action failed' if (didCommit) { @@ -778,10 +816,16 @@ export default function MobileSourceControlScreen() { const openFile = useCallback( async (entry: MobileGitStatusEntry) => { - if (entry.status === 'deleted' || entry.conflictStatus === 'unresolved') return - if (openingPathRef.current || busyActionRef.current) return + if (entry.status === 'deleted' || entry.conflictStatus === 'unresolved') { + return + } + if (openingPathRef.current || busyActionRef.current) { + return + } if (!client || connState !== 'connected') { - if (!mountedRef.current) return + if (!mountedRef.current) { + return + } setActionError('Waiting for desktop...') return } @@ -803,7 +847,9 @@ export default function MobileSourceControlScreen() { if (!response.ok) { throw new Error(response.error?.message || 'Unable to open diff') } - if (!mountedRef.current) return + if (!mountedRef.current) { + return + } triggerSelection() if (origin === 'session') { router.back() @@ -818,7 +864,9 @@ export default function MobileSourceControlScreen() { `/h/${encodeURIComponent(hostId)}/session/${encodeURIComponent(worktreeId)}${query ? `?${query}` : ''}` ) } catch (err) { - if (!mountedRef.current) return + if (!mountedRef.current) { + return + } triggerError() setActionError(err instanceof Error ? err.message : 'Unable to open diff') } finally { @@ -835,9 +883,13 @@ export default function MobileSourceControlScreen() { const openBranchDiff = useCallback( async (entry: MobileGitBranchChangeEntry) => { - if (openingBranchPathRef.current || openingPathRef.current || busyActionRef.current) return + if (openingBranchPathRef.current || openingPathRef.current || busyActionRef.current) { + return + } if (!client || connState !== 'connected') { - if (!mountedRef.current) return + if (!mountedRef.current) { + return + } setActionError('Waiting for desktop...') return } @@ -872,7 +924,9 @@ export default function MobileSourceControlScreen() { throw new Error('Binary branch diff preview unavailable on mobile') } const diff = buildMobileDiffLines(result.originalContent, result.modifiedContent) - if (!mountedRef.current) return + if (!mountedRef.current) { + return + } setBranchDiffPreview({ kind: 'ready', entry, @@ -882,7 +936,9 @@ export default function MobileSourceControlScreen() { }) triggerSelection() } catch (err) { - if (!mountedRef.current) return + if (!mountedRef.current) { + return + } triggerError() setBranchDiffPreview({ kind: 'error', diff --git a/mobile/app/h/[hostId]/tasks.tsx b/mobile/app/h/[hostId]/tasks.tsx index f0c4620bf9f..03cb5fb044b 100644 --- a/mobile/app/h/[hostId]/tasks.tsx +++ b/mobile/app/h/[hostId]/tasks.tsx @@ -897,11 +897,17 @@ function taskTime(value: string): number { function formatUpdatedAt(value: string): string { const time = taskTime(value) - if (!time) return '' + if (!time) { + return '' + } const minutes = Math.max(0, Math.floor((Date.now() - time) / 60_000)) - if (minutes < 60) return `${minutes}m` + if (minutes < 60) { + return `${minutes}m` + } const hours = Math.floor(minutes / 60) - if (hours < 24) return `${hours}h` + if (hours < 24) { + return `${hours}h` + } return `${Math.floor(hours / 24)}d` } @@ -942,8 +948,12 @@ function normalizeLinearFilter(value: unknown): LinearFilter { } function githubKindFromQuery(query: string, fallbackPreset: GitHubPreset): GitHubTaskKind { - if (/\bis:pr\b/i.test(query)) return 'prs' - if (/\bis:issue\b/i.test(query)) return 'issues' + if (/\bis:pr\b/i.test(query)) { + return 'prs' + } + if (/\bis:issue\b/i.test(query)) { + return 'issues' + } return fallbackPreset === 'prs' || fallbackPreset === 'my-prs' || fallbackPreset === 'review' ? 'prs' : 'issues' @@ -957,18 +967,24 @@ function parseProjectInput( input: string ): { owner: string; number: number; viewNumber?: number } | null { const trimmed = input.trim() - if (!trimmed) return null + if (!trimmed) { + return null + } const short = /^([A-Za-z0-9][A-Za-z0-9-]*)\/(\d+)$/.exec(trimmed) if (short) { return { owner: short[1]!, number: Number(short[2]) } } try { const url = new URL(trimmed) - if (url.hostname !== 'github.com') return null + if (url.hostname !== 'github.com') { + return null + } const parts = url.pathname.split('/').filter(Boolean) if ((parts[0] === 'orgs' || parts[0] === 'users') && parts[2] === 'projects' && parts[3]) { const number = Number(parts[3]) - if (!Number.isInteger(number) || number < 1) return null + if (!Number.isInteger(number) || number < 1) { + return null + } const viewNumber = parts[4] === 'views' && parts[5] && Number.isInteger(Number(parts[5])) ? Number(parts[5]) @@ -986,8 +1002,12 @@ function parseProjectInput( } function projectRowType(row: GitHubProjectRow): 'issue' | 'pr' | null { - if (row.itemType === 'ISSUE') return 'issue' - if (row.itemType === 'PULL_REQUEST') return 'pr' + if (row.itemType === 'ISSUE') { + return 'issue' + } + if (row.itemType === 'PULL_REQUEST') { + return 'pr' + } return null } @@ -1014,33 +1034,55 @@ const GITHUB_PROJECT_OPTION_COLORS: Record = { } function githubProjectOptionColor(color: string | null | undefined): string { - if (!color) return colors.textMuted + if (!color) { + return colors.textMuted + } const upper = color.toUpperCase() const mapped = GITHUB_PROJECT_OPTION_COLORS[upper] - if (mapped) return mapped + if (mapped) { + return mapped + } const hex = color.startsWith('#') ? color : `#${color}` return /^#[0-9a-fA-F]{6}$/.test(hex) ? hex : colors.textMuted } function projectRowStatusLabel(row: GitHubProjectRow): string { - if (row.itemType === 'DRAFT_ISSUE') return 'Draft' - if (row.itemType === 'REDACTED') return 'Redacted' - if (row.content.isDraft) return 'Draft' - if (row.content.state === 'MERGED') return 'Merged' - if (row.content.state === 'CLOSED') return 'Closed' + if (row.itemType === 'DRAFT_ISSUE') { + return 'Draft' + } + if (row.itemType === 'REDACTED') { + return 'Redacted' + } + if (row.content.isDraft) { + return 'Draft' + } + if (row.content.state === 'MERGED') { + return 'Merged' + } + if (row.content.state === 'CLOSED') { + return 'Closed' + } return 'Open' } function scopeGitHubTaskSearch(query: string, kind: GitHubTaskKind): string { const trimmed = query.trim() - if (!trimmed) return getTaskPresetQuery(kind === 'prs' ? 'prs' : 'issues') - if (/\bis:(?:issue|pr)\b/i.test(trimmed)) return trimmed + if (!trimmed) { + return getTaskPresetQuery(kind === 'prs' ? 'prs' : 'issues') + } + if (/\bis:(?:issue|pr)\b/i.test(trimmed)) { + return trimmed + } return `${kind === 'prs' ? 'is:pr' : 'is:issue'} ${trimmed}` } function gitHubStatusLabel(item: GitHubWorkItem): string { - if (item.state === 'merged') return 'Merged' - if (item.state === 'draft') return 'Draft' + if (item.state === 'merged') { + return 'Merged' + } + if (item.state === 'draft') { + return 'Draft' + } return item.state === 'closed' ? 'Closed' : 'Open' } @@ -1062,9 +1104,15 @@ function createGitHubTask(repo: RepoSummary, item: Omit): string { - if (todo.targetType === 'MergeRequest') return 'Merge request' - if (todo.targetType === 'Issue') return 'Issue' + if (todo.targetType === 'MergeRequest') { + return 'Merge request' + } + if (todo.targetType === 'Issue') { + return 'Issue' + } return 'GitLab todo' } function gitLabTodoTargetRef(todo: Pick): string { - if (!todo.targetIid) return '' - if (todo.targetType === 'MergeRequest') return `!${todo.targetIid}` - if (todo.targetType === 'Issue') return `#${todo.targetIid}` + if (!todo.targetIid) { + return '' + } + if (todo.targetType === 'MergeRequest') { + return `!${todo.targetIid}` + } + if (todo.targetType === 'Issue') { + return `#${todo.targetIid}` + } return String(todo.targetIid) } @@ -1133,7 +1191,9 @@ function reconcileRepoSelection( repos: RepoSummary[], persisted: string[] | null | undefined ): Set { - if (!persisted || persisted.length === 0) return new Set() + if (!persisted || persisted.length === 0) { + return new Set() + } const availableIds = new Set(repos.filter(isHostedTaskRepo).map((repo) => repo.id)) const selected = persisted.filter((id) => availableIds.has(id)) return selected.length === 0 ? new Set() : new Set(selected) @@ -1191,7 +1251,9 @@ function getGitHubReviewerRows(item: { const byLogin = new Map() for (const user of item.reviewRequests ?? []) { const login = user.login.trim() - if (!login) continue + if (!login) { + continue + } byLogin.set(login.toLowerCase(), { login, name: user.name, @@ -1202,7 +1264,9 @@ function getGitHubReviewerRows(item: { for (const review of item.latestReviews ?? []) { const login = review.login.trim() const key = login.toLowerCase() - if (!login || byLogin.has(key)) continue + if (!login || byLogin.has(key)) { + continue + } byLogin.set(key, { login, name: null, @@ -1218,11 +1282,19 @@ function getGitHubReviewSummary(item: { reviewRequests?: GitHubAssignableUser[] latestReviews?: GitHubPRReviewSummary[] }): string { - if (item.reviewDecision === 'APPROVED') return 'Approved' - if (item.reviewDecision === 'CHANGES_REQUESTED') return 'Changes requested' + if (item.reviewDecision === 'APPROVED') { + return 'Approved' + } + if (item.reviewDecision === 'CHANGES_REQUESTED') { + return 'Changes requested' + } const rows = getGitHubReviewerRows(item) - if (rows.length === 0) return 'No reviewers' - if (rows.length === 1) return `${rows[0]!.login} - ${rows[0]!.stateLabel}` + if (rows.length === 0) { + return 'No reviewers' + } + if (rows.length === 1) { + return `${rows[0]!.login} - ${rows[0]!.stateLabel}` + } return `${rows[0]!.login} +${rows.length - 1}` } @@ -1299,8 +1371,12 @@ function getGitHubMergeLabel(item: GitHubWorkItem): string { } function getHostedReviewMergeMethodLabel(method: HostedReviewMergeMethod): string { - if (method === 'squash') return 'Squash and merge' - if (method === 'rebase') return 'Rebase and merge' + if (method === 'squash') { + return 'Squash and merge' + } + if (method === 'rebase') { + return 'Rebase and merge' + } return 'Create merge commit' } @@ -1377,22 +1453,38 @@ function getGitHubPRSignalTone( signal: 'review' | 'checks' | 'merge' ): 'neutral' | 'success' | 'warning' | 'danger' { if (signal === 'review') { - if (item.reviewDecision === 'APPROVED') return 'success' - if (item.reviewDecision === 'CHANGES_REQUESTED') return 'danger' - if (item.reviewRequests && item.reviewRequests.length > 0) return 'warning' + if (item.reviewDecision === 'APPROVED') { + return 'success' + } + if (item.reviewDecision === 'CHANGES_REQUESTED') { + return 'danger' + } + if (item.reviewRequests && item.reviewRequests.length > 0) { + return 'warning' + } return 'neutral' } if (signal === 'checks') { - if (item.checksSummary?.state === 'success') return 'success' - if (item.checksSummary?.state === 'failure') return 'danger' - if (item.checksSummary?.state === 'pending') return 'warning' + if (item.checksSummary?.state === 'success') { + return 'success' + } + if (item.checksSummary?.state === 'failure') { + return 'danger' + } + if (item.checksSummary?.state === 'pending') { + return 'warning' + } return 'neutral' } - if (item.mergeable === 'CONFLICTING' || item.mergeStateStatus === 'BLOCKED') return 'danger' + if (item.mergeable === 'CONFLICTING' || item.mergeStateStatus === 'BLOCKED') { + return 'danger' + } if (item.mergeStateStatus === 'BEHIND' || item.checksSummary?.state === 'pending') { return 'warning' } - if (item.mergeable === 'MERGEABLE' || item.mergeStateStatus === 'CLEAN') return 'success' + if (item.mergeable === 'MERGEABLE' || item.mergeStateStatus === 'CLEAN') { + return 'success' + } return 'neutral' } @@ -1403,7 +1495,9 @@ function mergeGitHubAssignableUsers( const byLogin = new Map() for (const user of [...users, ...seeds]) { const login = user.login.trim() - if (!login || byLogin.has(login.toLowerCase())) continue + if (!login || byLogin.has(login.toLowerCase())) { + continue + } byLogin.set(login.toLowerCase(), { ...user, login }) } return [...byLogin.values()] @@ -1417,7 +1511,9 @@ function getGitHubReviewerSeedUsers(item: { const byLogin = new Map() const add = (user: GitHubAssignableUser): void => { const login = user.login.trim() - if (!login || byLogin.has(login.toLowerCase())) return + if (!login || byLogin.has(login.toLowerCase())) { + return + } byLogin.set(login.toLowerCase(), { ...user, login }) } for (const user of item.reviewRequests ?? []) { @@ -1532,15 +1628,21 @@ function linearIssueSecondaryParts( displayProperties: ReadonlySet ): string[] { const parts = [issue.identifier] - if (displayProperties.has('priority')) parts.push(getLinearPriorityLabel(issue.priority)) + if (displayProperties.has('priority')) { + parts.push(getLinearPriorityLabel(issue.priority)) + } if (displayProperties.has('assignee') && issue.assignee?.displayName) { parts.push(issue.assignee.displayName) } - if (displayProperties.has('team')) parts.push(issue.team.name) + if (displayProperties.has('team')) { + parts.push(issue.team.name) + } if (displayProperties.has('labels') && issue.labels.length > 0) { parts.push(issue.labels.slice(0, 2).join(', ')) } - if (displayProperties.has('updated')) parts.push(formatUpdatedAt(issue.updatedAt)) + if (displayProperties.has('updated')) { + parts.push(formatUpdatedAt(issue.updatedAt)) + } return parts } @@ -1580,14 +1682,30 @@ function editableProjectFields(table: GitHubProjectTable | null): GitHubProjectF function projectFieldValueLabel(row: GitHubProjectRow, field: GitHubProjectField): string { const value = row.fieldValuesByFieldId?.[field.id] - if (!value) return 'Empty' - if (value.kind === 'single-select') return value.name - if (value.kind === 'iteration') return value.title - if (value.kind === 'text') return value.text || 'Empty' - if (value.kind === 'number') return String(value.number) - if (value.kind === 'date') return value.date - if (value.kind === 'labels') return value.labels.map((label) => label.name).join(', ') || 'Empty' - if (value.kind === 'users') return value.users.map((user) => user.login).join(', ') || 'Empty' + if (!value) { + return 'Empty' + } + if (value.kind === 'single-select') { + return value.name + } + if (value.kind === 'iteration') { + return value.title + } + if (value.kind === 'text') { + return value.text || 'Empty' + } + if (value.kind === 'number') { + return String(value.number) + } + if (value.kind === 'date') { + return value.date + } + if (value.kind === 'labels') { + return value.labels.map((label) => label.name).join(', ') || 'Empty' + } + if (value.kind === 'users') { + return value.users.map((user) => user.login).join(', ') || 'Empty' + } return 'Empty' } @@ -1622,7 +1740,9 @@ function projectSummaryFields(table: GitHubProjectTable | null): GitHubProjectFi } function projectFieldVisibilityKey(table: GitHubProjectTable | null): string | null { - if (!table) return null + if (!table) { + return null + } // Why: desktop scopes column visibility to project + view; matching that // avoids hiding fields across unrelated Project views with colliding IDs. return `${table.project.id}:${table.selectedView.id}` @@ -1630,10 +1750,18 @@ function projectFieldVisibilityKey(table: GitHubProjectTable | null): string | n function projectFieldDraftValue(row: GitHubProjectRow, field: GitHubProjectField): string { const value = row.fieldValuesByFieldId?.[field.id] - if (!value) return '' - if (value.kind === 'text') return value.text - if (value.kind === 'number') return String(value.number) - if (value.kind === 'date') return value.date + if (!value) { + return '' + } + if (value.kind === 'text') { + return value.text + } + if (value.kind === 'number') { + return String(value.number) + } + if (value.kind === 'date') { + return value.date + } return '' } @@ -1714,14 +1842,22 @@ function optimisticProjectFieldValue( duration: iteration?.duration ?? 0 } } - if (value.kind === 'number') return { kind: 'number', fieldId: field.id, number: value.number } - if (value.kind === 'date') return { kind: 'date', fieldId: field.id, date: value.date } + if (value.kind === 'number') { + return { kind: 'number', fieldId: field.id, number: value.number } + } + if (value.kind === 'date') { + return { kind: 'date', fieldId: field.id, date: value.date } + } return { kind: 'text', fieldId: field.id, text: value.kind === 'text' ? value.text : '' } } function taskKindLabel(item: TaskItem): string { - if (item.provider === 'github') return item.source.type === 'pr' ? 'Pull request' : 'Issue' - if (item.provider === 'gitlab') return item.source.type === 'mr' ? 'Merge request' : 'Issue' + if (item.provider === 'github') { + return item.source.type === 'pr' ? 'Pull request' : 'Issue' + } + if (item.provider === 'gitlab') { + return item.source.type === 'mr' ? 'Merge request' : 'Issue' + } if (item.provider === 'gitlabTodo') { return `${gitLabTodoTargetLabel(item.source)} todo` } @@ -1729,8 +1865,12 @@ function taskKindLabel(item: TaskItem): string { } function taskExternalOpenLabel(item: TaskItem): string { - if (item.provider === 'github') return 'Open in GitHub' - if (item.provider === 'gitlab' || item.provider === 'gitlabTodo') return 'Open in GitLab' + if (item.provider === 'github') { + return 'Open in GitHub' + } + if (item.provider === 'gitlab' || item.provider === 'gitlabTodo') { + return 'Open in GitLab' + } return 'Open in Linear' } @@ -1753,15 +1893,21 @@ function commentAuthor(comment: DetailComment): string { } function commentDate(value: string | undefined): string { - if (!value) return '' + if (!value) { + return '' + } const time = Date.parse(value) return Number.isFinite(time) ? new Date(time).toLocaleDateString() : '' } function formatDurationSeconds(value: number | null | undefined): string { - if (typeof value !== 'number' || !Number.isFinite(value)) return '' + if (typeof value !== 'number' || !Number.isFinite(value)) { + return '' + } const seconds = Math.max(0, Math.floor(value)) - if (seconds >= 60) return `${Math.floor(seconds / 60)}m ${seconds % 60}s` + if (seconds >= 60) { + return `${Math.floor(seconds / 60)}m ${seconds % 60}s` + } return `${seconds}s` } @@ -1788,7 +1934,9 @@ function groupDetailComments(comments: DetailComment[]): DetailCommentGroup[] { const emittedThreads = new Set() for (const comment of comments) { - if (!comment.threadId) continue + if (!comment.threadId) { + continue + } const existing = threads.get(comment.threadId) if (existing) { existing.replies.push(comment) @@ -1802,10 +1950,14 @@ function groupDetailComments(comments: DetailComment[]): DetailCommentGroup[] { groups.push({ kind: 'standalone', comment }) continue } - if (emittedThreads.has(comment.threadId)) continue + if (emittedThreads.has(comment.threadId)) { + continue + } emittedThreads.add(comment.threadId) const thread = threads.get(comment.threadId) - if (thread) groups.push({ kind: 'thread', threadId: comment.threadId, ...thread }) + if (thread) { + groups.push({ kind: 'thread', threadId: comment.threadId, ...thread }) + } } return groups @@ -1828,13 +1980,17 @@ function isResolvedDetailCommentGroup(group: DetailCommentGroup): boolean { } function discussionSummary(count: number): string { - if (count === 0) return 'No comments yet' + if (count === 0) { + return 'No comments yet' + } return `${count} ${count === 1 ? 'comment' : 'comments'}` } function renderCommentReactions(comment: DetailComment): ReactNode { const reactions = (comment.reactions ?? []).filter((reaction) => reaction.count > 0) - if (reactions.length === 0) return null + if (reactions.length === 0) { + return null + } return ( {reactions.map((reaction) => ( @@ -1853,8 +2009,12 @@ function formatDiffLineNumber(value: number | undefined): string { } function diffLinePrefix(kind: GitHubPrFileDiffLine['kind']): string { - if (kind === 'added') return '+' - if (kind === 'removed') return '-' + if (kind === 'added') { + return '+' + } + if (kind === 'removed') { + return '-' + } return ' ' } @@ -1971,7 +2131,9 @@ function buildPartialRepositoryNotice(failedCount: number, totalCount: number): function repoColor(name: string): string { const palette = ['#f97316', '#8b5cf6', '#06b6d4', '#ec4899', '#84cc16', '#f59e0b', '#6366f1'] let hash = 0 - for (let i = 0; i < name.length; i += 1) hash = (hash * 31 + name.charCodeAt(i)) | 0 + for (let i = 0; i < name.length; i += 1) { + hash = (hash * 31 + name.charCodeAt(i)) | 0 + } return palette[Math.abs(hash) % palette.length]! } @@ -1980,8 +2142,12 @@ function getRepoBadgeColor(repo: RepoSummary | undefined, fallbackName: string): } function setupSourceLabel(source: string | null): string { - if (source === 'orca.yaml') return 'orca.yaml' - if (source === 'legacy') return 'local hooks' + if (source === 'orca.yaml') { + return 'orca.yaml' + } + if (source === 'legacy') { + return 'local hooks' + } return 'repository hooks' } @@ -2343,7 +2509,9 @@ export default function MobileTasksScreen() { [githubProjectTable, githubRepoSlugCache, hostedRepos] ) const visibleGitHubProjectGroups = useMemo(() => { - if (!githubProjectTable) return [] + if (!githubProjectTable) { + return [] + } const normalizedTable = normalizeProjectTableForMobileSort( githubProjectTable, visibleGitHubProjectRows, @@ -2365,7 +2533,9 @@ export default function MobileTasksScreen() { return visibleGitHubProjectGroups.flatMap((group) => { const collapsed = collapsedGitHubProjectGroups.has(group.key) const header: ProjectListEntry = { type: 'group', group, collapsed } - if (collapsed) return [header] + if (collapsed) { + return [header] + } return [ header, ...group.rows.map((row) => ({ @@ -2545,13 +2715,17 @@ export default function MobileTasksScreen() { const logins = new Set() for (const assignee of projectRowItem?.content.assignees ?? []) { const login = assignee.login.trim() - if (login) logins.add(login) + if (login) { + logins.add(login) + } } for (const reviewer of projectRowDetail?.provider === 'github' ? getGitHubReviewerSeedUsers(projectRowDetail) : []) { const login = reviewer.login.trim() - if (login) logins.add(login) + if (login) { + logins.add(login) + } } return [...logins].sort().join(',') }, [projectRowDetail, projectRowItem?.content.assignees]) @@ -2564,7 +2738,9 @@ export default function MobileTasksScreen() { const persistTaskResumeState = useCallback( (updates: Partial) => { - if (!client || !taskUiReady) return + if (!client || !taskUiReady) { + return + } const next = { ...taskResumeRef.current, ...updates } taskResumeRef.current = next void client.sendRequest('ui.set', { taskResumeState: next }).catch(() => { @@ -2576,7 +2752,9 @@ export default function MobileTasksScreen() { const toggleGitHubProjectFieldVisibility = useCallback( (fieldId: string) => { - if (!githubProjectFieldVisibilityScope) return + if (!githubProjectFieldVisibilityScope) { + return + } setGithubProjectHiddenFieldIdsByView((current) => { const hidden = new Set(current[githubProjectFieldVisibilityScope] ?? []) if (hidden.has(fieldId)) { @@ -2599,7 +2777,9 @@ export default function MobileTasksScreen() { const persistTaskSource = useCallback( (nextProvider: TaskProvider) => { - if (!client || !taskUiReady) return + if (!client || !taskUiReady) { + return + } void client.sendRequest('settings.update', { defaultTaskSource: nextProvider }).catch(() => { // Best-effort: a failed settings write should not block switching views. }) @@ -2609,7 +2789,9 @@ export default function MobileTasksScreen() { const persistRepoSelection = useCallback( (selection: Set, allRepos: RepoSummary[]) => { - if (!client || !taskUiReady) return + if (!client || !taskUiReady) { + return + } const nextSelection = selection.size === 0 || selection.size === allRepos.length ? null : [...selection] defaultRepoSelectionRef.current = nextSelection @@ -2625,7 +2807,9 @@ export default function MobileTasksScreen() { const persistDefaultGitHubPreset = useCallback( (preset: GitHubPreset) => { setDefaultGitHubPreset(preset) - if (!client || !taskUiReady) return + if (!client || !taskUiReady) { + return + } void client.sendRequest('settings.update', { defaultTaskViewPreset: preset }).catch(() => { // Best-effort: the current session still uses the selected preset. }) @@ -2636,7 +2820,9 @@ export default function MobileTasksScreen() { const persistGitHubProjectSettings = useCallback( (nextSettings: GitHubProjectSettings) => { setGithubProjectSettings(nextSettings) - if (!client || !taskUiReady) return + if (!client || !taskUiReady) { + return + } void client.sendRequest('settings.update', { githubProjects: nextSettings }).catch(() => { // Best-effort: project selection can still work for the current session. }) @@ -2646,7 +2832,9 @@ export default function MobileTasksScreen() { const persistSetupHookTrust = useCallback( async (repoId: string, contentHash: string, alwaysTrust: boolean): Promise => { - if (!client) return + if (!client) { + return + } const next = trustedOrcaHooksWithSetupApproval({ trust: trustedOrcaHooks, repoId, @@ -2788,7 +2976,9 @@ export default function MobileTasksScreen() { const hydrateTaskState = async (): Promise => { const statusResponse = await client.sendRequest('status.get') - if (stale) return + if (stale) { + return + } if (!isSuccess(statusResponse)) { throw new Error(statusResponse.error.message) } @@ -2849,7 +3039,9 @@ export default function MobileTasksScreen() { client.sendRequest('preflight.check'), client.sendRequest('linear.status') ]) - if (stale) return + if (stale) { + return + } const settings = isSuccess(settingsResponse) ? (((settingsResponse.result as { settings?: RuntimeTaskSettings }).settings ?? @@ -2935,7 +3127,9 @@ export default function MobileTasksScreen() { } void hydrateTaskState().catch((err) => { - if (stale) return + if (stale) { + return + } setError(err instanceof Error ? err.message : 'Failed to load Tasks settings') setTaskStateHydrated(false) }) @@ -2946,12 +3140,16 @@ export default function MobileTasksScreen() { }, [client, connState, requestedTaskSource, resetWorkspaceCreateState]) useEffect(() => { - if (visibleProviders.includes(provider)) return + if (visibleProviders.includes(provider)) { + return + } setProvider(resolveVisibleTaskProvider(provider, visibleProviders)) }, [provider, visibleProviders]) const loadRepos = useCallback(async (): Promise => { - if (!client || connState !== 'connected') return [] + if (!client || connState !== 'connected') { + return [] + } const response = await client.sendRequest('repo.list') if (!isSuccess(response)) { throw new Error(response.error.message) @@ -2964,7 +3162,9 @@ export default function MobileTasksScreen() { setSelectedRepoIds(reconcileRepoSelection(result.repos, defaultRepoSelectionRef.current)) } else { setSelectedRepoIds((current) => { - if (current.size === 0) return current + if (current.size === 0) { + return current + } const availableIds = new Set(result.repos.filter(isHostedTaskRepo).map((repo) => repo.id)) const next = new Set([...current].filter((id) => availableIds.has(id))) return next.size === current.size ? current : next @@ -2974,7 +3174,9 @@ export default function MobileTasksScreen() { }, [client, connState]) const loadLinearContext = useCallback(async (): Promise => { - if (!client || connState !== 'connected' || !tasksSupported) return + if (!client || connState !== 'connected' || !tasksSupported) { + return + } const statusResponse = await client.sendRequest('linear.status') if (!isSuccess(statusResponse)) { throw new Error(statusResponse.error.message) @@ -3007,7 +3209,9 @@ export default function MobileTasksScreen() { const persistLinearTeamSelection = useCallback( (teamIds: Set, allTeams: LinearTeam[]) => { - if (!client || !taskUiReady) return + if (!client || !taskUiReady) { + return + } const selection = teamIds.size === allTeams.length ? null : [...teamIds] defaultLinearTeamSelectionRef.current = selection void client @@ -3142,15 +3346,20 @@ export default function MobileTasksScreen() { const loadTasks = useCallback( async (options: { silent?: boolean } = {}): Promise => { - if (!client || connState !== 'connected' || !tasksSupported || !taskStateHydrated) return + if (!client || connState !== 'connected' || !tasksSupported || !taskStateHydrated) { + return + } const generation = loadGenerationRef.current + 1 loadGenerationRef.current = generation const requestClient = client const isCurrent = () => loadGenerationRef.current === generation && clientRef.current === requestClient setError('') - if (options.silent) setRefreshing(true) - else setLoading(true) + if (options.silent) { + setRefreshing(true) + } else { + setLoading(true) + } try { if (provider !== 'github' || githubMode !== 'items') { setGithubPages([]) @@ -3168,7 +3377,9 @@ export default function MobileTasksScreen() { return } const currentRepos = reposRef.current.length > 0 ? reposRef.current : await loadRepos() - if (!isCurrent()) return + if (!isCurrent()) { + return + } if (provider === 'github' || provider === 'gitlab') { const supportedRepos = currentRepos.filter(isHostedTaskRepo) const queriedRepos = @@ -3176,7 +3387,9 @@ export default function MobileTasksScreen() { ? supportedRepos : supportedRepos.filter((repo) => selectedRepoIds.has(repo.id)) if (queriedRepos.length === 0) { - if (!isCurrent()) return + if (!isCurrent()) { + return + } setItems([]) setGithubPages([]) setGithubCurrentPage(0) @@ -3187,7 +3400,9 @@ export default function MobileTasksScreen() { } if (provider === 'github') { const page = await fetchGitHubItemsPage(requestClient, queriedRepos) - if (!isCurrent()) return + if (!isCurrent()) { + return + } setGithubRepoSources((current) => ({ ...current, ...page.sourcesByRepoId })) setGithubSourceErrors(page.sourceErrors) setGithubSourceFallbacks(page.sourceFallbacks) @@ -3227,7 +3442,9 @@ export default function MobileTasksScreen() { if (!isSuccess(response)) { throw new Error(response.error.message) } - if (!isCurrent()) return + if (!isCurrent()) { + return + } setItems( ((response.result as GitLabTodo[]) ?? []) .map(createGitLabTodoTask) @@ -3272,7 +3489,9 @@ export default function MobileTasksScreen() { } } ) - if (!isCurrent()) return + if (!isCurrent()) { + return + } const failedCount = results.filter((result) => result.error).length if (failedCount === queriedRepos.length) { throw new Error( @@ -3311,11 +3530,15 @@ export default function MobileTasksScreen() { ? issues.filter((issue) => selectedLinearTeamIds.has(issue.team.id)) : issues const sorted = [...filtered].sort((a, b) => compareLinearIssues(a, b, linearOrderBy)) - if (!isCurrent()) return + if (!isCurrent()) { + return + } setItems(sorted.map(createLinearTask)) } } catch (err) { - if (!isCurrent()) return + if (!isCurrent()) { + return + } setItems([]) setGithubSourceErrors([]) setGithubSourceFallbacks([]) @@ -3350,9 +3573,13 @@ export default function MobileTasksScreen() { ) const connectLinearAccount = useCallback(async (): Promise => { - if (!client || connState !== 'connected' || !taskUiReady) return + if (!client || connState !== 'connected' || !taskUiReady) { + return + } const apiKey = linearApiKeyDraft.trim() - if (!apiKey || linearConnectState === 'connecting') return + if (!apiKey || linearConnectState === 'connecting') { + return + } setLinearConnectState('connecting') setLinearConnectError('') try { @@ -3508,7 +3735,9 @@ export default function MobileTasksScreen() { ) const loadGitHubProjects = useCallback(async (): Promise => { - if (!client || connState !== 'connected' || !tasksSupported) return + if (!client || connState !== 'connected' || !tasksSupported) { + return + } setGithubProjectError('') setGithubProjectPartialFailures([]) const response = await client.sendRequest('github.project.listAccessible', {}) @@ -3531,7 +3760,9 @@ export default function MobileTasksScreen() { const loadGitHubProjectViews = useCallback( async (project: GitHubProjectRef): Promise => { - if (!client || connState !== 'connected' || !tasksSupported || !taskStateHydrated) return [] + if (!client || connState !== 'connected' || !tasksSupported || !taskStateHydrated) { + return [] + } const response = await client.sendRequest('github.project.listViews', { owner: project.owner, ownerType: project.ownerType, @@ -3637,7 +3868,9 @@ export default function MobileTasksScreen() { const selectGitHubProject = useCallback( async (project: GitHubProjectRef, options: { viewNumber?: number } = {}): Promise => { - if (!tasksSupported || !taskStateHydrated) return + if (!tasksSupported || !taskStateHydrated) { + return + } setGithubProjectLoading(true) setGithubProjectError('') try { @@ -3697,7 +3930,9 @@ export default function MobileTasksScreen() { ) const resolveGitHubProjectFromInput = useCallback(async (): Promise => { - if (!client || connState !== 'connected' || !tasksSupported || !taskStateHydrated) return + if (!client || connState !== 'connected' || !tasksSupported || !taskStateHydrated) { + return + } const input = githubProjectPasteInput.trim() if (!parseProjectInput(input)) { setGithubProjectPasteError('Expected a project URL or owner/number.') @@ -3750,7 +3985,9 @@ export default function MobileTasksScreen() { ]) useEffect(() => { - if (!taskStateHydrated) return + if (!taskStateHydrated) { + return + } const timer = setTimeout(() => { setAppliedQuery( provider === 'github' ? scopeGitHubTaskSearch(query, githubKind) : query.trim() @@ -3769,7 +4006,9 @@ export default function MobileTasksScreen() { }, []) useEffect(() => { - if (!taskUiReady || provider !== 'github' || githubMode !== 'items') return + if (!taskUiReady || provider !== 'github' || githubMode !== 'items') { + return + } const trimmed = appliedQuery.trim() persistTaskResumeState({ githubMode: 'items', @@ -3779,7 +4018,9 @@ export default function MobileTasksScreen() { }, [appliedQuery, githubMode, githubPreset, persistTaskResumeState, provider, taskUiReady]) useEffect(() => { - if (!taskUiReady || provider !== 'linear') return + if (!taskUiReady || provider !== 'linear') { + return + } persistTaskResumeState({ linearPreset: linearFilter, linearQuery: appliedQuery.trim() @@ -3787,19 +4028,25 @@ export default function MobileTasksScreen() { }, [appliedQuery, linearFilter, persistTaskResumeState, provider, taskUiReady]) useEffect(() => { - if (connState !== 'connected' || !taskStateHydrated) return + if (connState !== 'connected' || !taskStateHydrated) { + return + } void loadTasks() }, [connState, loadTasks, taskStateHydrated]) useEffect(() => { - if (!taskStateHydrated || provider !== 'linear' || !linearConnected) return + if (!taskStateHydrated || provider !== 'linear' || !linearConnected) { + return + } void loadLinearContext().catch((err) => { setError(err instanceof Error ? err.message : 'Failed to load Linear context') }) }, [linearConnected, loadLinearContext, provider, taskStateHydrated]) useEffect(() => { - if (!taskUiReady || provider !== 'github' || githubMode !== 'project') return + if (!taskUiReady || provider !== 'github' || githubMode !== 'project') { + return + } persistTaskResumeState({ githubMode: 'project' }) if (activeGitHubProject && activeGitHubProjectViewId) { void loadGitHubProjectTable({ queryOverride: appliedGithubProjectSearch }) @@ -3824,14 +4071,18 @@ export default function MobileTasksScreen() { ]) useEffect(() => { - if (!taskUiReady || !showGitHubProjectPicker) return + if (!taskUiReady || !showGitHubProjectPicker) { + return + } void loadGitHubProjects().catch((err) => { setGithubProjectError(err instanceof Error ? err.message : 'Failed to load projects') }) }, [loadGitHubProjects, showGitHubProjectPicker, taskUiReady]) useEffect(() => { - if (!tasksSupported || !taskStateHydrated || !showCreateTask) return + if (!tasksSupported || !taskStateHydrated || !showCreateTask) { + return + } setCreatingTask(false) if (provider === 'github' || provider === 'gitlab') { setCreateRepoId((current) => @@ -3841,13 +4092,17 @@ export default function MobileTasksScreen() { ) return } - if (!client) return + if (!client) { + return + } let stale = false setCreateTeamId(null) void client .sendRequest('linear.listTeams') .then((response) => { - if (stale) return + if (stale) { + return + } if (isSuccess(response)) { const teams = response.result as LinearTeam[] setLinearTeams(teams) @@ -3886,7 +4141,9 @@ export default function MobileTasksScreen() { void client .sendRequest('linear.teamStates', baseParams) .then((statesResponse) => { - if (stale) return + if (stale) { + return + } if (isSuccess(statesResponse)) { setLinearStates(statesResponse.result as LinearState[]) } else { @@ -3899,7 +4156,9 @@ export default function MobileTasksScreen() { } }) .finally(() => { - if (!stale) setLinearStatesLoading(false) + if (!stale) { + setLinearStatesLoading(false) + } }) return () => { stale = true @@ -3973,7 +4232,9 @@ export default function MobileTasksScreen() { { timeoutMs: 30_000 } ) .then((response) => { - if (stale) return + if (stale) { + return + } if (!isSuccess(response)) { throw new Error(response.error.message) } @@ -3985,7 +4246,9 @@ export default function MobileTasksScreen() { } }) .finally(() => { - if (!stale) setItemLabelsLoading(false) + if (!stale) { + setItemLabelsLoading(false) + } }) } else { setItemAvailableLabels([]) @@ -4003,7 +4266,9 @@ export default function MobileTasksScreen() { { timeoutMs: 30_000 } ) .then((response) => { - if (stale) return + if (stale) { + return + } if (!isSuccess(response)) { throw new Error(response.error.message) } @@ -4017,7 +4282,9 @@ export default function MobileTasksScreen() { } }) .finally(() => { - if (!stale) setItemAssignableUsersLoading(false) + if (!stale) { + setItemAssignableUsersLoading(false) + } }) return () => { @@ -4076,7 +4343,9 @@ export default function MobileTasksScreen() { viewerViewedState?: 'DISMISSED' | 'VIEWED' | 'UNVIEWED' }> } | null - if (!details) throw new Error('Details not found') + if (!details) { + throw new Error('Details not found') + } if (!stale) { setDetailPayload({ provider: 'github', @@ -4125,7 +4394,9 @@ export default function MobileTasksScreen() { duration?: number | null }> } | null - if (!details) throw new Error('Details not found') + if (!details) { + throw new Error('Details not found') + } if (!stale) { setDetailPayload({ provider: 'gitlab', @@ -4164,7 +4435,9 @@ export default function MobileTasksScreen() { const comments = isSuccess(commentsResponse) ? ((commentsResponse.result as DetailComment[]) ?? []) : [] - if (!issue) throw new Error('Details not found') + if (!issue) { + throw new Error('Details not found') + } if (!stale) { setDetailPayload({ provider: 'linear', @@ -4199,7 +4472,9 @@ export default function MobileTasksScreen() { } }) .finally(() => { - if (!stale) setDetailLoading(false) + if (!stale) { + setDetailLoading(false) + } }) return () => { @@ -4269,7 +4544,9 @@ export default function MobileTasksScreen() { { timeoutMs: 30_000 } ) .then((response) => { - if (stale) return + if (stale) { + return + } if (!isSuccess(response)) { throw new Error(response.error.message) } @@ -4327,7 +4604,9 @@ export default function MobileTasksScreen() { } }) .finally(() => { - if (!stale) setProjectRowDetailLoading(false) + if (!stale) { + setProjectRowDetailLoading(false) + } }) return () => { @@ -4355,7 +4634,9 @@ export default function MobileTasksScreen() { { timeoutMs: 30_000 } ) .then((response) => { - if (stale) return + if (stale) { + return + } if (!isSuccess(response)) { throw new Error(response.error.message) } @@ -4373,7 +4654,9 @@ export default function MobileTasksScreen() { } }) .finally(() => { - if (!stale) setProjectLabelsLoading(false) + if (!stale) { + setProjectLabelsLoading(false) + } }) return () => { @@ -4405,7 +4688,9 @@ export default function MobileTasksScreen() { { timeoutMs: 30_000 } ) .then((response) => { - if (stale) return + if (stale) { + return + } if (!isSuccess(response)) { throw new Error(response.error.message) } @@ -4425,7 +4710,9 @@ export default function MobileTasksScreen() { } }) .finally(() => { - if (!stale) setProjectAssignableUsersLoading(false) + if (!stale) { + setProjectAssignableUsersLoading(false) + } }) return () => { @@ -4453,7 +4740,9 @@ export default function MobileTasksScreen() { { timeoutMs: 30_000 } ) .then((response) => { - if (stale) return + if (stale) { + return + } if (!isSuccess(response)) { throw new Error(response.error.message) } @@ -4473,7 +4762,9 @@ export default function MobileTasksScreen() { } }) .finally(() => { - if (!stale) setProjectIssueTypesLoading(false) + if (!stale) { + setProjectIssueTypesLoading(false) + } }) return () => { @@ -4688,7 +4979,9 @@ export default function MobileTasksScreen() { void client .sendRequest('repo.sparsePresets', { repo: `id:${workspaceCreateTargetRepo.id}` }) .then((response) => { - if (stale) return + if (stale) { + return + } if (!isSuccess(response)) { throw new Error(response.error.message) } @@ -4710,7 +5003,9 @@ export default function MobileTasksScreen() { } }) .finally(() => { - if (!stale) setWorkspaceSparsePresetsLoading(false) + if (!stale) { + setWorkspaceSparsePresetsLoading(false) + } }) return () => { @@ -4755,7 +5050,9 @@ export default function MobileTasksScreen() { { timeoutMs: 30_000 } ) .then((response) => { - if (stale) return + if (stale) { + return + } if (!isSuccess(response)) { throw new Error(response.error.message) } @@ -4777,7 +5074,9 @@ export default function MobileTasksScreen() { } }) .finally(() => { - if (!stale) setWorkspaceBaseBranchLoading(false) + if (!stale) { + setWorkspaceBaseBranchLoading(false) + } }) return () => { @@ -4893,7 +5192,9 @@ export default function MobileTasksScreen() { void client .sendRequest('ssh.getState', { targetId: workspaceCreateTargetConnectionId }) .then((response) => { - if (stale) return + if (stale) { + return + } if (!isSuccess(response)) { throw new Error(response.error.message) } @@ -5010,13 +5311,17 @@ export default function MobileTasksScreen() { : client.sendRequest('preflight.detectAgents') void request .then((response) => { - if (stale) return + if (stale) { + return + } setWorkspaceDetectedAgentIds( isSuccess(response) ? new Set(response.result as string[]) : new Set() ) }) .catch(() => { - if (!stale) setWorkspaceDetectedAgentIds(new Set()) + if (!stale) { + setWorkspaceDetectedAgentIds(new Set()) + } }) return () => { stale = true @@ -5069,7 +5374,9 @@ export default function MobileTasksScreen() { setupTrust?: RepoHooksResponse['setupTrust'] } > => { - if (!client || !tasksSupported) return { kind: 'decision', decision: override ?? 'inherit' } + if (!client || !tasksSupported) { + return { kind: 'decision', decision: override ?? 'inherit' } + } const response = await client.sendRequest('repo.hooks', { repo: `id:${repo.id}` }) if (!isSuccess(response)) { throw new Error(response.error.message) @@ -5109,7 +5416,9 @@ export default function MobileTasksScreen() { sparseCheckoutOverride?: { directories: string[]; presetId?: string }, approvedSetupContentHash?: string ): Promise => { - if (!client || !tasksSupported || !taskStateHydrated) return + if (!client || !tasksSupported || !taskStateHydrated) { + return + } setCreatingKey(item.key) setError('') try { @@ -5348,7 +5657,9 @@ export default function MobileTasksScreen() { const createWorkspaceFromProjectRow = useCallback( async (row: GitHubProjectRow): Promise => { - if (!tasksSupported) return + if (!tasksSupported) { + return + } const kind = projectRowType(row) const repo = findProjectRowRepo(row) if (!kind || !row.content.number || !row.content.url) { @@ -5403,7 +5714,9 @@ export default function MobileTasksScreen() { row: GitHubProjectRow, updates: { title?: string; body?: string; state?: 'open' | 'closed' } ): Promise => { - if (!client || projectMutating) return + if (!client || projectMutating) { + return + } const type = projectRowType(row) const slug = splitRepositorySlug(row.content.repository) if (!type || !slug || !row.content.number) { @@ -5432,7 +5745,9 @@ export default function MobileTasksScreen() { throw new Error(result.error?.message ?? 'Failed to update GitHub item') } setProjectRowItem((current) => { - if (!current || current.id !== row.id) return current + if (!current || current.id !== row.id) { + return current + } return { ...current, content: { @@ -5483,10 +5798,14 @@ export default function MobileTasksScreen() { const addProjectRowComment = useCallback( async (row: GitHubProjectRow): Promise => { - if (!client || projectMutating) return + if (!client || projectMutating) { + return + } const slug = splitRepositorySlug(row.content.repository) const body = projectCommentDraft.trim() - if (!slug || !row.content.number || !body) return + if (!slug || !row.content.number || !body) { + return + } setProjectMutating(true) try { const response = await client.sendRequest( @@ -5527,7 +5846,9 @@ export default function MobileTasksScreen() { const updateProjectRowComment = useCallback( async (row: GitHubProjectRow, comment: DetailComment): Promise => { - if (!client || projectMutating) return + if (!client || projectMutating) { + return + } const slug = splitRepositorySlug(row.content.repository) const commentId = Number(comment.id) const body = projectEditingCommentDraft.trim() @@ -5585,7 +5906,9 @@ export default function MobileTasksScreen() { const deleteProjectRowComment = useCallback( async (row: GitHubProjectRow, comment: DetailComment): Promise => { - if (!client || projectMutating) return + if (!client || projectMutating) { + return + } const slug = splitRepositorySlug(row.content.repository) const commentId = Number(comment.id) if (!slug || !Number.isInteger(commentId) || commentId <= 0) { @@ -5696,10 +6019,14 @@ export default function MobileTasksScreen() { const replyToProjectGitHubComment = useCallback( async (row: GitHubProjectRow, comment: DetailComment): Promise => { const repo = findProjectRowRepo(row) - if (!client || projectMutating || !repo || !row.content.number) return + if (!client || projectMutating || !repo || !row.content.number) { + return + } const key = String(comment.id) const body = (itemReplyDrafts[key] ?? '').trim() - if (!body) return + if (!body) { + return + } setProjectMutating(true) setProjectRowDetailError('') try { @@ -5781,7 +6108,9 @@ export default function MobileTasksScreen() { removeAssignees?: string[] } ): Promise => { - if (!client || projectMutating) return + if (!client || projectMutating) { + return + } const slug = splitRepositorySlug(row.content.repository) if (!slug || !row.content.number) { setProjectRowDetailError('This project item cannot be edited from mobile.') @@ -5809,16 +6138,24 @@ export default function MobileTasksScreen() { const applyContentUpdate = (candidate: GitHubProjectRow): GitHubProjectRow => { const labels = new Map(candidate.content.labels.map((label) => [label.name, label])) for (const label of updates.addLabels ?? []) { - if (!labels.has(label)) labels.set(label, { name: label, color: '808080' }) + if (!labels.has(label)) { + labels.set(label, { name: label, color: '808080' }) + } + } + for (const label of updates.removeLabels ?? []) { + labels.delete(label) } - for (const label of updates.removeLabels ?? []) labels.delete(label) const assignees = new Map( candidate.content.assignees.map((assignee) => [assignee.login, assignee]) ) for (const login of updates.addAssignees ?? []) { - if (!assignees.has(login)) assignees.set(login, { login, name: null }) + if (!assignees.has(login)) { + assignees.set(login, { login, name: null }) + } + } + for (const login of updates.removeAssignees ?? []) { + assignees.delete(login) } - for (const login of updates.removeAssignees ?? []) assignees.delete(login) return { ...candidate, content: { @@ -5879,7 +6216,9 @@ export default function MobileTasksScreen() { field: GitHubProjectField, value: GitHubProjectFieldMutationValue | null ): Promise => { - if (!client || !githubProjectTable || projectMutating) return + if (!client || !githubProjectTable || projectMutating) { + return + } setProjectMutating(true) try { const response = await client.sendRequest( @@ -5943,7 +6282,9 @@ export default function MobileTasksScreen() { const mutateProjectRowIssueType = useCallback( async (row: GitHubProjectRow, issueType: GitHubIssueType | null): Promise => { - if (!client || projectMutating) return + if (!client || projectMutating) { + return + } const slug = splitRepositorySlug(row.content.repository) if (row.itemType !== 'ISSUE' || !slug || !row.content.number) { setProjectRowDetailError('This project issue type cannot be edited from mobile.') @@ -5997,9 +6338,13 @@ export default function MobileTasksScreen() { const requestProjectGitHubReviewers = useCallback( async (row: GitHubProjectRow, logins?: string[]): Promise => { const repo = findProjectRowRepo(row) - if (!client || projectMutating || row.itemType !== 'PULL_REQUEST' || !repo) return + if (!client || projectMutating || row.itemType !== 'PULL_REQUEST' || !repo) { + return + } const reviewers = logins ?? splitReviewerList(projectReviewersDraft) - if (reviewers.length === 0 || !row.content.number) return + if (reviewers.length === 0 || !row.content.number) { + return + } setProjectMutating(true) setProjectRowDetailError('') try { @@ -6025,7 +6370,9 @@ export default function MobileTasksScreen() { ? projectRowDetail.reviewRequests : []) { const login = reviewer.login.trim() - if (login) byLogin.set(login.toLowerCase(), reviewer) + if (login) { + byLogin.set(login.toLowerCase(), reviewer) + } } for (const login of reviewers) { const normalized = login.trim().replace(/^@/, '') @@ -6145,7 +6492,9 @@ export default function MobileTasksScreen() { const toggleProjectGitHubFileViewed = useCallback( async (row: GitHubProjectRow, file: GitHubDetailFile): Promise => { const repo = findProjectRowRepo(row) - if (!client || projectMutating || row.itemType !== 'PULL_REQUEST' || !repo) return + if (!client || projectMutating || row.itemType !== 'PULL_REQUEST' || !repo) { + return + } if (projectRowDetail?.provider !== 'github' || !projectRowDetail.pullRequestId) { setProjectRowDetailError('Unable to sync viewed state for this pull request.') return @@ -6268,7 +6617,9 @@ export default function MobileTasksScreen() { } const draftKey = `${file.path}:${line}` const body = (prFileCommentDrafts[draftKey] ?? '').trim() - if (!body) return + if (!body) { + return + } setProjectMutating(true) setProjectRowDetailError('') try { @@ -6388,7 +6739,9 @@ export default function MobileTasksScreen() { const toggleGitHubStatus = useCallback( async (item: Extract): Promise => { - if (!client || mutatingStatus || item.source.state === 'merged') return + if (!client || mutatingStatus || item.source.state === 'merged') { + return + } setMutatingStatus(true) setError('') const nextState = item.source.state === 'closed' ? 'open' : 'closed' @@ -6427,7 +6780,9 @@ export default function MobileTasksScreen() { const toggleGitLabStatus = useCallback( async (item: Extract): Promise => { - if (!client || mutatingStatus || item.source.state === 'merged') return + if (!client || mutatingStatus || item.source.state === 'merged') { + return + } setMutatingStatus(true) setError('') const nextState = item.source.state === 'closed' ? 'opened' : 'closed' @@ -6476,7 +6831,9 @@ export default function MobileTasksScreen() { removeAssignees?: string[] } ): Promise => { - if (!client || mutatingStatus) return + if (!client || mutatingStatus) { + return + } setMutatingStatus(true) setError('') try { @@ -6578,9 +6935,13 @@ export default function MobileTasksScreen() { item: Extract, updates: { title?: string; body?: string } ): Promise => { - if (!client || mutatingStatus || item.source.type !== 'pr') return + if (!client || mutatingStatus || item.source.type !== 'pr') { + return + } const nextTitle = updates.title?.trim() - if (updates.title !== undefined && !nextTitle) return + if (updates.title !== undefined && !nextTitle) { + return + } setMutatingStatus(true) setError('') try { @@ -6652,7 +7013,9 @@ export default function MobileTasksScreen() { removeAssignees?: string[] } ): Promise => { - if (!client || mutatingStatus) return + if (!client || mutatingStatus) { + return + } setMutatingStatus(true) setError('') try { @@ -6766,9 +7129,13 @@ export default function MobileTasksScreen() { async ( item: Extract | Extract ): Promise => { - if (!client || mutatingStatus) return + if (!client || mutatingStatus) { + return + } const body = itemCommentDraft.trim() - if (!body) return + if (!body) { + return + } setMutatingStatus(true) setError('') try { @@ -6857,9 +7224,13 @@ export default function MobileTasksScreen() { const requestGitHubReviewers = useCallback( async (item: Extract, logins?: string[]): Promise => { - if (!client || mutatingStatus || item.source.type !== 'pr') return + if (!client || mutatingStatus || item.source.type !== 'pr') { + return + } const reviewers = logins ?? splitReviewerList(itemReviewersDraft) - if (reviewers.length === 0) return + if (reviewers.length === 0) { + return + } setMutatingStatus(true) setError('') try { @@ -6885,7 +7256,9 @@ export default function MobileTasksScreen() { ? detailPayload.reviewRequests : (item.source.reviewRequests ?? [])) { const login = reviewer.login.trim() - if (login) byLogin.set(login.toLowerCase(), reviewer) + if (login) { + byLogin.set(login.toLowerCase(), reviewer) + } } for (const login of reviewers) { const normalized = login.trim().replace(/^@/, '') @@ -6936,7 +7309,9 @@ export default function MobileTasksScreen() { const refreshGitHubChecks = useCallback( async (item: Extract): Promise => { - if (!client || mutatingStatus || item.source.type !== 'pr') return + if (!client || mutatingStatus || item.source.type !== 'pr') { + return + } setMutatingStatus(true) setError('') try { @@ -6990,7 +7365,9 @@ export default function MobileTasksScreen() { const rerunGitHubChecks = useCallback( async (item: Extract, failedOnly: boolean): Promise => { - if (!client || mutatingStatus || item.source.type !== 'pr') return + if (!client || mutatingStatus || item.source.type !== 'pr') { + return + } setMutatingStatus(true) setError('') try { @@ -7026,7 +7403,9 @@ export default function MobileTasksScreen() { item: Extract, file: NonNullable['files'][number]> ): Promise => { - if (!client || mutatingStatus || item.source.type !== 'pr') return + if (!client || mutatingStatus || item.source.type !== 'pr') { + return + } if (detailPayload?.provider !== 'github' || !detailPayload.pullRequestId) { setError('Unable to sync viewed state for this pull request.') return @@ -7077,7 +7456,9 @@ export default function MobileTasksScreen() { item: Extract, comment: DetailComment ): Promise => { - if (!client || mutatingStatus || item.source.type !== 'pr' || !comment.threadId) return + if (!client || mutatingStatus || item.source.type !== 'pr' || !comment.threadId) { + return + } const resolve = !comment.isResolved setMutatingStatus(true) setError('') @@ -7179,14 +7560,18 @@ export default function MobileTasksScreen() { file: GitHubDetailFile, line: number ): Promise => { - if (!client || mutatingStatus || item.source.type !== 'pr') return + if (!client || mutatingStatus || item.source.type !== 'pr') { + return + } if (detailPayload?.provider !== 'github' || !detailPayload.headSha) { setError('Unable to comment without the PR head SHA.') return } const draftKey = `${file.path}:${line}` const body = (prFileCommentDrafts[draftKey] ?? '').trim() - if (!body) return + if (!body) { + return + } setMutatingStatus(true) setError('') try { @@ -7245,10 +7630,14 @@ export default function MobileTasksScreen() { item: Extract, comment: DetailComment ): Promise => { - if (!client || mutatingStatus) return + if (!client || mutatingStatus) { + return + } const key = String(comment.id) const body = (itemReplyDrafts[key] ?? '').trim() - if (!body) return + if (!body) { + return + } setMutatingStatus(true) setError('') try { @@ -7325,9 +7714,15 @@ export default function MobileTasksScreen() { item: Extract | Extract, method: HostedReviewMergeMethod ): Promise => { - if (!client || mutatingStatus) return - if (item.provider === 'github' && item.source.type !== 'pr') return - if (item.provider === 'gitlab' && item.source.type !== 'mr') return + if (!client || mutatingStatus) { + return + } + if (item.provider === 'github' && item.source.type !== 'pr') { + return + } + if (item.provider === 'gitlab' && item.source.type !== 'mr') { + return + } if (item.provider === 'github' && isGitHubPrMergeBlocked(item)) { setError('GitHub reports merge conflicts. Open in GitHub to continue.') return @@ -7380,7 +7775,9 @@ export default function MobileTasksScreen() { state: LinearState, options: { closeDetail?: boolean } = {} ): Promise => { - if (!client || !taskUiReady || mutatingStatus) return + if (!client || !taskUiReady || mutatingStatus) { + return + } setMutatingStatus(true) setError('') try { @@ -7428,9 +7825,13 @@ export default function MobileTasksScreen() { const addLinearComment = useCallback( async (item: Extract): Promise => { - if (!client || mutatingStatus) return + if (!client || mutatingStatus) { + return + } const body = linearCommentDraft.trim() - if (!body) return + if (!body) { + return + } setMutatingStatus(true) setError('') try { @@ -7473,7 +7874,9 @@ export default function MobileTasksScreen() { const openLinearSubIssue = useCallback( async (child: LinearIssueChild, workspaceId?: string): Promise => { - if (!client || mutatingStatus) return + if (!client || mutatingStatus) { + return + } setMutatingStatus(true) setError('') try { @@ -7501,9 +7904,13 @@ export default function MobileTasksScreen() { const createLinearSubIssue = useCallback( async (item: Extract): Promise => { - if (!client || mutatingStatus) return + if (!client || mutatingStatus) { + return + } const title = linearSubIssueTitle.trim() - if (!title) return + if (!title) { + return + } setMutatingStatus(true) setError('') try { @@ -7559,9 +7966,13 @@ export default function MobileTasksScreen() { ) const createTask = useCallback(async (): Promise => { - if (!client || !tasksSupported || !taskStateHydrated || creatingTask) return + if (!client || !tasksSupported || !taskStateHydrated || creatingTask) { + return + } const title = createTitle.trim() - if (!title) return + if (!title) { + return + } setCreatingTask(true) setError('') try { @@ -7694,7 +8105,9 @@ export default function MobileTasksScreen() { const setGitHubIssueSourcePreference = useCallback( async (repo: RepoSummary, preference: 'upstream' | 'origin'): Promise => { - if (!client || !taskUiReady) return + if (!client || !taskUiReady) { + return + } setError('') try { const response = await client.sendRequest( @@ -8095,10 +8508,18 @@ export default function MobileTasksScreen() { : `${selectedLinearTeamIds.size} teams` const effectiveLinearDisplayProperties = useMemo(() => { const next = new Set(linearDisplayProperties) - if (linearGroupBy === 'status') next.delete('state') - if (linearGroupBy === 'assignee') next.delete('assignee') - if (linearGroupBy === 'priority') next.delete('priority') - if (linearGroupBy === 'team') next.delete('team') + if (linearGroupBy === 'status') { + next.delete('state') + } + if (linearGroupBy === 'assignee') { + next.delete('assignee') + } + if (linearGroupBy === 'priority') { + next.delete('priority') + } + if (linearGroupBy === 'team') { + next.delete('team') + } if (selectedLinearTeamIds.size <= 1 && !linearTeamPropertyTouched) { next.delete('team') } else if (selectedLinearTeamIds.size > 1 && !linearTeamPropertyTouched) { @@ -8192,8 +8613,12 @@ export default function MobileTasksScreen() { ...githubProjectSettings.recent.map(githubProjectKey) ]) return githubProjects.filter((project) => { - if (pinnedOrRecentKeys.has(githubProjectKey(project))) return false - if (!queryText) return true + if (pinnedOrRecentKeys.has(githubProjectKey(project))) { + return false + } + if (!queryText) { + return true + } return ( project.title.toLowerCase().includes(queryText) || project.owner.toLowerCase().includes(queryText) || @@ -8262,7 +8687,9 @@ export default function MobileTasksScreen() { style={styles.iconButton} disabled={!taskUiReady || loading || refreshing || githubProjectLoading} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } if (provider === 'github' && githubMode === 'project') { void loadGitHubProjectTable({ queryOverride: appliedGithubProjectSearch }) return @@ -8277,7 +8704,9 @@ export default function MobileTasksScreen() { style={styles.iconButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } if (provider === 'linear' && !linearConnected) { setLinearApiKeyDraft('') setLinearConnectState('idle') @@ -8305,7 +8734,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowProviderPicker(true) }} > @@ -8318,7 +8749,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowRepoPicker(true) }} > @@ -8345,7 +8778,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowGitHubKindPicker(true) }} > @@ -8357,7 +8792,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowGitHubPresetPicker(true) }} > @@ -8368,7 +8805,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowGitHubIssueSourcePicker(true) }} > @@ -8384,7 +8823,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowGitHubProjectPicker(true) }} > @@ -8395,7 +8836,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowGitHubProjectViewPicker(true) }} > @@ -8409,7 +8852,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowGitHubProjectSortPicker(true) }} > @@ -8423,7 +8868,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowGitHubProjectFieldsPicker(true) }} > @@ -8446,7 +8893,9 @@ export default function MobileTasksScreen() { style={styles.segmentIconButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } void Linking.openURL(selectedGitHubProjectViewUrl) }} > @@ -8464,7 +8913,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowGitLabViewPicker(true) }} > @@ -8477,7 +8928,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowGitLabFilterPicker(true) }} > @@ -8494,7 +8947,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowLinearWorkspacePicker(true) }} > @@ -8505,7 +8960,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowLinearTeamPicker(true) }} > @@ -8515,7 +8972,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowLinearFilterPicker(true) }} > @@ -8525,7 +8984,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowLinearViewPicker(true) }} > @@ -8535,7 +8996,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowLinearGroupPicker(true) }} > @@ -8545,7 +9008,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowLinearOrderPicker(true) }} > @@ -8555,7 +9020,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowLinearDisplayPicker(true) }} > @@ -8569,7 +9036,9 @@ export default function MobileTasksScreen() { style={styles.segmentButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowSortPicker(true) }} > @@ -8603,7 +9072,9 @@ export default function MobileTasksScreen() { autoCorrect={false} returnKeyType="search" onSubmitEditing={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } if (provider === 'github' && githubMode === 'project') { applyGitHubProjectSearch() return @@ -8755,7 +9226,9 @@ export default function MobileTasksScreen() { style={[styles.targetButton, styles.centerActionButton]} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setLinearApiKeyDraft('') setLinearConnectState('idle') setLinearConnectError('') @@ -8777,7 +9250,9 @@ export default function MobileTasksScreen() { style={[styles.targetButton, styles.centerActionButton]} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowGitHubProjectPicker(true) }} > @@ -8816,8 +9291,11 @@ export default function MobileTasksScreen() { onPress={() => setCollapsedGitHubProjectGroups((current) => { const next = new Set(current) - if (next.has(entry.group.key)) next.delete(entry.group.key) - else next.add(entry.group.key) + if (next.has(entry.group.key)) { + next.delete(entry.group.key) + } else { + next.add(entry.group.key) + } return next }) } @@ -9139,7 +9617,9 @@ export default function MobileTasksScreen() { style={styles.paginationLabelButton} disabled={githubPaginationLoading} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowGitHubPagePicker(true) }} > @@ -9814,7 +10294,9 @@ export default function MobileTasksScreen() { setPendingGitHubProjectViewSelection(null) return } - if (!activeGitHubProject || !activeGitHubProjectKey) return + if (!activeGitHubProject || !activeGitHubProjectKey) { + return + } commitGitHubProjectView(activeGitHubProject, viewId) }} onClose={() => { @@ -10043,7 +10525,9 @@ export default function MobileTasksScreen() { style={styles.repoPickerRow} disabled={mutatingStatus} onPress={() => { - if (!linearStatusPickerItem) return + if (!linearStatusPickerItem) { + return + } void setLinearStatus(linearStatusPickerItem, state, { closeDetail: false }).then(() => setLinearStatusPickerItem(null)) @@ -10176,7 +10660,9 @@ export default function MobileTasksScreen() { style={styles.targetButton} disabled={!taskUiReady} onPress={() => { - if (!taskUiReady) return + if (!taskUiReady) { + return + } setShowCreateTargetPicker(true) }} > @@ -10298,8 +10784,11 @@ export default function MobileTasksScreen() { : ((selectedCreateTarget as LinearTeam | null)?.id ?? '') } onSelect={(value) => { - if (provider === 'github' || provider === 'gitlab') setCreateRepoId(value) - else setCreateTeamId(value) + if (provider === 'github' || provider === 'gitlab') { + setCreateRepoId(value) + } else { + setCreateTeamId(value) + } }} onClose={() => setShowCreateTargetPicker(false)} /> @@ -10307,7 +10796,9 @@ export default function MobileTasksScreen() { { - if (linearConnectState !== 'connecting') setShowLinearConnect(false) + if (linearConnectState !== 'connecting') { + setShowLinearConnect(false) + } }} > @@ -10810,7 +11301,9 @@ export default function MobileTasksScreen() { { - if (!workspaceSparseSaving) setWorkspaceSparseDraft(null) + if (!workspaceSparseSaving) { + setWorkspaceSparseDraft(null) + } }} zIndex={TASK_SECONDARY_DRAWER_Z_INDEX + 2} > @@ -11103,7 +11596,9 @@ export default function MobileTasksScreen() { { - if (projectRepoNotInOrca.url) void Linking.openURL(projectRepoNotInOrca.url) + if (projectRepoNotInOrca.url) { + void Linking.openURL(projectRepoNotInOrca.url) + } }} > @@ -11750,7 +12245,9 @@ export default function MobileTasksScreen() { style={styles.fileActionRow} disabled={!check.url} onPress={() => { - if (check.url) void Linking.openURL(check.url) + if (check.url) { + void Linking.openURL(check.url) + } }} > @@ -12695,7 +13192,9 @@ export default function MobileTasksScreen() { style={styles.fileActionRow} disabled={!check.url} onPress={() => { - if (check.url) void Linking.openURL(check.url) + if (check.url) { + void Linking.openURL(check.url) + } }} > @@ -12811,7 +13310,9 @@ export default function MobileTasksScreen() { style={styles.fileCard} disabled={!job.webUrl} onPress={() => { - if (job.webUrl) void Linking.openURL(job.webUrl) + if (job.webUrl) { + void Linking.openURL(job.webUrl) + } }} > @@ -13155,7 +13656,9 @@ export default function MobileTasksScreen() { pendingHostedMerge ? getHostedReviewMergeMethodLabel(pendingHostedMerge.method) : 'Merge' } onConfirm={() => { - if (!taskUiReady || !pendingHostedMerge) return + if (!taskUiReady || !pendingHostedMerge) { + return + } void mergeHostedReview(pendingHostedMerge.item, pendingHostedMerge.method) }} onCancel={() => setPendingHostedMerge(null)} @@ -13174,7 +13677,9 @@ export default function MobileTasksScreen() { : 'Merge' } onConfirm={() => { - if (!taskUiReady || !pendingProjectGitHubMerge) return + if (!taskUiReady || !pendingProjectGitHubMerge) { + return + } void mergeProjectGitHubPullRequest( pendingProjectGitHubMerge.row, pendingProjectGitHubMerge.method @@ -13201,7 +13706,9 @@ export default function MobileTasksScreen() { } destructive={pendingHostedStateChange?.nextState === 'closed'} onConfirm={() => { - if (!taskUiReady || !pendingHostedStateChange) return + if (!taskUiReady || !pendingHostedStateChange) { + return + } if (pendingHostedStateChange.source === 'task') { if (pendingHostedStateChange.item.provider === 'gitlab') { void toggleGitLabStatus(pendingHostedStateChange.item) @@ -14614,9 +15121,15 @@ const styles = StyleSheet.create({ }) function getPrSignalToneStyle(tone: 'neutral' | 'success' | 'warning' | 'danger') { - if (tone === 'success') return styles.prSignalSuccess - if (tone === 'warning') return styles.prSignalWarning - if (tone === 'danger') return styles.prSignalDanger + if (tone === 'success') { + return styles.prSignalSuccess + } + if (tone === 'warning') { + return styles.prSignalWarning + } + if (tone === 'danger') { + return styles.prSignalDanger + } return null } diff --git a/mobile/app/index.tsx b/mobile/app/index.tsx index 489a20c6ae0..b0e011c0cdc 100644 --- a/mobile/app/index.tsx +++ b/mobile/app/index.tsx @@ -105,9 +105,13 @@ function formatDuration(ms: number): string { const totalHours = Math.floor(totalMinutes / 60) const days = Math.floor(totalHours / 24) const hours = totalHours % 24 - if (days > 0) return `${days}d ${hours}h` + if (days > 0) { + return `${days}d ${hours}h` + } const minutes = totalMinutes % 60 - if (totalHours > 0) return `${totalHours}h ${minutes}m` + if (totalHours > 0) { + return `${totalHours}h ${minutes}m` + } return `${totalMinutes}m` } @@ -134,7 +138,9 @@ function fetchStats( client .sendRequest('stats.summary') .then((response) => { - if (disposed()) return + if (disposed()) { + return + } if (response.ok) { setStats(response.result as StatsSummary) } @@ -157,7 +163,9 @@ function fetchWorktreeInfo( // momentarily flip to "0 worktrees" / disappear during reconnects. const markLoadedIfMissing = () => { setInfo((prev) => { - if (prev[hostId]) return prev + if (prev[hostId]) { + return prev + } return { ...prev, [hostId]: { @@ -173,7 +181,9 @@ function fetchWorktreeInfo( client .sendRequest('worktree.ps') .then((response) => { - if (disposed()) return + if (disposed()) { + return + } if (response.ok) { const result = response.result as { worktrees: WorktreeSummary[] } const worktrees = result.worktrees ?? [] @@ -195,7 +205,9 @@ function fetchWorktreeInfo( } }) .catch(() => { - if (!disposed()) markLoadedIfMissing() + if (!disposed()) { + markLoadedIfMissing() + } }) } @@ -210,7 +222,9 @@ function fetchAccountsSnapshot( client .sendRequest('accounts.list') .then((response) => { - if (disposed()) return + if (disposed()) { + return + } if (response.ok) { const snapshot = response.result as AccountsSnapshot setSnapshots((prev) => ({ ...prev, [hostId]: snapshot })) @@ -233,7 +247,9 @@ function fetchTaskProviders( client.sendRequest('linear.status') ]) .then(([settingsResponse, preflightResponse, linearResponse]) => { - if (disposed()) return + if (disposed()) { + return + } const settings = settingsResponse.ok ? (((settingsResponse.result as { settings?: HomeTaskSettings }).settings ?? {}) as HomeTaskSettings) @@ -252,7 +268,9 @@ function fetchTaskProviders( setProviders((prev) => ({ ...prev, [hostId]: providers })) }) .catch(() => { - if (disposed()) return + if (disposed()) { + return + } setProviders((prev) => (prev[hostId] ? prev : { ...prev, [hostId]: ['github'] })) }) } @@ -302,7 +320,9 @@ export default function HomeScreen() { // openEntry on cold start (which serialised behind the first one and // showed up as multi-second connect latency). useEffect(() => { - if (hosts.length > 0) primeHosts(hosts) + if (hosts.length > 0) { + primeHosts(hosts) + } }, [hosts, primeHosts]) const allClientsRef = useRef>([]) // Why: the focus callback stays stable to avoid refetching on every @@ -318,11 +338,15 @@ export default function HomeScreen() { // Stream/list responses overwrite this seed in place when they arrive. const hydratedRef = useRef(false) useEffect(() => { - if (hydratedRef.current) return + if (hydratedRef.current) { + return + } hydratedRef.current = true let cancelled = false void loadHomeSnapshot().then((snap) => { - if (cancelled || !snap) return + if (cancelled || !snap) { + return + } setWorktreeInfo((prev) => (Object.keys(prev).length > 0 ? prev : snap.worktreeInfo)) setAccountsByHost((prev) => (Object.keys(prev).length > 0 ? prev : snap.accountsByHost)) for (const [hostId, info] of Object.entries(snap.worktreeInfo)) { @@ -357,10 +381,14 @@ export default function HomeScreen() { useCallback(() => { let stale = false void loadHosts().then((h) => { - if (!stale) setHosts(h) + if (!stale) { + setHosts(h) + } }) void AsyncStorage.getItem('orca:last-visited-worktree').then((raw) => { - if (stale || !raw) return + if (stale || !raw) { + return + } try { setLastVisited(JSON.parse(raw)) } catch {} @@ -427,7 +455,9 @@ export default function HomeScreen() { // already tracked — otherwise the initial-acquire frame (entry not // yet materialised) would briefly flip every host to 'disconnected'. for (const host of hosts) { - if (liveIds.has(host.id)) continue + if (liveIds.has(host.id)) { + continue + } if (!host.publicKeyB64 || !host.deviceToken) { if (next[host.id] !== 'auth-failed') { next[host.id] = 'auth-failed' @@ -470,7 +500,9 @@ export default function HomeScreen() { } if (!unsubAccounts) { unsubAccounts = entry.client.subscribe('accounts.subscribe', null, (payload) => { - if (!payload || typeof payload !== 'object') return + if (!payload || typeof payload !== 'object') { + return + } const evt = payload as { type?: string; snapshot?: AccountsSnapshot } if ((evt.type === 'ready' || evt.type === 'snapshot') && evt.snapshot) { setAccountsByHost((prev) => ({ ...prev, [entry.hostId]: evt.snapshot! })) @@ -503,7 +535,9 @@ export default function HomeScreen() { }) } return () => { - for (const c of cleanups) c() + for (const c of cleanups) { + c() + } } // Why: depend on the host-id set AND each entry's client identity, so // resubscriptions don't fire on every render that produces a new @@ -538,10 +572,14 @@ export default function HomeScreen() { if (lastVisited && hostStates[lastVisited.hostId] === 'connected') { const cached = getCachedWorktrees(lastVisited.hostId) as WorktreeSummary[] | null const match = cached?.find((w) => w.worktreeId === lastVisited.worktreeId) - if (match) return { hostId: lastVisited.hostId, worktree: match } + if (match) { + return { hostId: lastVisited.hostId, worktree: match } + } } for (const host of sortedHosts) { - if (hostStates[host.id] !== 'connected') continue + if (hostStates[host.id] !== 'connected') { + continue + } const info = worktreeInfo[host.id] if (info?.lastActiveWorktree) { return { hostId: host.id, worktree: info.lastActiveWorktree } @@ -556,12 +594,18 @@ export default function HomeScreen() { const accountsHosts = useMemo(() => { const items: Array<{ host: HostProfile; snapshot: AccountsSnapshot }> = [] for (const host of sortedHosts) { - if (hostStates[host.id] !== 'connected') continue + if (hostStates[host.id] !== 'connected') { + continue + } const snap = accountsByHost[host.id] - if (!snap) continue + if (!snap) { + continue + } const hasClaude = snap.claude.accounts.length > 0 const hasCodex = snap.codex.accounts.length > 0 - if (hasClaude || hasCodex) items.push({ host, snapshot: snap }) + if (hasClaude || hasCodex) { + items.push({ host, snapshot: snap }) + } } return items }, [sortedHosts, hostStates, accountsByHost]) @@ -575,7 +619,9 @@ export default function HomeScreen() { : [] const openTasks = useCallback( (provider?: TaskProvider) => { - if (!primaryConnectedHost) return + if (!primaryConnectedHost) { + return + } const suffix = provider ? `?taskSource=${provider}` : '' router.push(`/h/${primaryConnectedHost.id}/tasks${suffix}`) }, @@ -636,7 +682,9 @@ export default function HomeScreen() { ) async function handleRename(newName: string) { - if (!renameTarget) return + if (!renameTarget) { + return + } try { await renameHost(renameTarget.id, newName) setRenameTarget(null) @@ -647,7 +695,9 @@ export default function HomeScreen() { } async function handleRemove() { - if (!confirmRemove) return + if (!confirmRemove) { + return + } try { // Why: close the shared client first so the WebSocket is gone // before the host record disappears from loadHosts(). @@ -921,7 +971,9 @@ export default function HomeScreen() { provider === 'claude' ? snapshot.claude.accounts : snapshot.codex.accounts - if (accounts.length === 0) return null + if (accounts.length === 0) { + return null + } const limits = getActiveProviderRateLimits(snapshot, provider) const isFetching = limits?.status === 'fetching' || limits?.status === 'idle' @@ -977,7 +1029,9 @@ export default function HomeScreen() { message={actionTarget ? endpointLabel(actionTarget.endpoint) : undefined} actions={(() => { const host = actionTarget - if (!host) return [] + if (!host) { + return [] + } const state = hostStates[host.id] ?? 'connecting' const isLive = state === 'connected' || diff --git a/mobile/app/pair-confirm.tsx b/mobile/app/pair-confirm.tsx index 8e95ea438d1..90fe3c4b47f 100644 --- a/mobile/app/pair-confirm.tsx +++ b/mobile/app/pair-confirm.tsx @@ -73,7 +73,9 @@ export default function PairConfirmScreen() { }, []) async function confirm() { - if (!offer) return + if (!offer) { + return + } setStatus('connecting') logsRef.current = [] setLogs([]) @@ -92,7 +94,9 @@ export default function PairConfirmScreen() { try { client = connect(offer.endpoint, offer.deviceToken, offer.publicKeyB64, { onLog: (entry) => { - if (!mountedRef.current || activePairingAttemptRef.current !== attempt) return + if (!mountedRef.current || activePairingAttemptRef.current !== attempt) { + return + } logsRef.current = [...logsRef.current, entry] setLogs(logsRef.current) } @@ -103,7 +107,9 @@ export default function PairConfirmScreen() { if (activePairingAttemptRef.current === attempt) { activePairingAttemptRef.current = null } - if (!mountedRef.current || !attemptIsCurrent) return + if (!mountedRef.current || !attemptIsCurrent) { + return + } } catch (err) { const timedOut = attempt.timedOut const attemptIsCurrent = activePairingAttemptRef.current === attempt @@ -111,7 +117,9 @@ export default function PairConfirmScreen() { if (activePairingAttemptRef.current === attempt) { activePairingAttemptRef.current = null } - if (!mountedRef.current || !attemptIsCurrent) return + if (!mountedRef.current || !attemptIsCurrent) { + return + } console.warn('[pair-confirm] connect failed', err) setStatus('error') setErrorMessage( @@ -123,7 +131,9 @@ export default function PairConfirmScreen() { } if (!response.ok) { - if (!mountedRef.current) return + if (!mountedRef.current) { + return + } setStatus('error') setErrorMessage( response.error.code === 'unauthorized' @@ -144,10 +154,14 @@ export default function PairConfirmScreen() { publicKeyB64: offer.publicKeyB64, lastConnected: Date.now() }) - if (!mountedRef.current) return + if (!mountedRef.current) { + return + } router.replace(`/h/${hostId}`) } catch (err) { - if (!mountedRef.current) return + if (!mountedRef.current) { + return + } console.warn('[pair-confirm] save failed', err) setStatus('error') setErrorMessage( diff --git a/mobile/app/pair-scan.tsx b/mobile/app/pair-scan.tsx index 79314437b51..b1f68126048 100644 --- a/mobile/app/pair-scan.tsx +++ b/mobile/app/pair-scan.tsx @@ -59,7 +59,9 @@ export default function PairScanScreen() { const handleBarCodeScanned = useCallback( ({ data }: { data: string }) => { - if (processingRef.current) return + if (processingRef.current) { + return + } processingRef.current = true const offer = decodePairingUrl(data) @@ -77,7 +79,9 @@ export default function PairScanScreen() { const handlePasteSubmit = useCallback((input: string) => { setPasteVisible(false) - if (processingRef.current) return + if (processingRef.current) { + return + } processingRef.current = true const offer = parsePairingCode(input) @@ -111,7 +115,9 @@ export default function PairScanScreen() { try { client = connect(offer.endpoint, offer.deviceToken, offer.publicKeyB64, { onLog: (entry) => { - if (!mountedRef.current || activePairingAttemptRef.current !== attempt) return + if (!mountedRef.current || activePairingAttemptRef.current !== attempt) { + return + } logsRef.current = [...logsRef.current, entry] setLogs(logsRef.current) } @@ -122,7 +128,9 @@ export default function PairScanScreen() { if (activePairingAttemptRef.current === attempt) { activePairingAttemptRef.current = null } - if (!mountedRef.current || !attemptIsCurrent) return + if (!mountedRef.current || !attemptIsCurrent) { + return + } } catch (err) { const timedOut = attempt.timedOut const attemptIsCurrent = activePairingAttemptRef.current === attempt @@ -130,7 +138,9 @@ export default function PairScanScreen() { if (activePairingAttemptRef.current === attempt) { activePairingAttemptRef.current = null } - if (!mountedRef.current || !attemptIsCurrent) return + if (!mountedRef.current || !attemptIsCurrent) { + return + } console.warn('[pair] connect failed', err) setStatus('error') setErrorMessage( @@ -143,7 +153,9 @@ export default function PairScanScreen() { } if (!response.ok) { - if (!mountedRef.current) return + if (!mountedRef.current) { + return + } if (response.error.code === 'unauthorized') { setStatus('error') setErrorMessage('Authentication failed — token may be expired') @@ -167,10 +179,14 @@ export default function PairScanScreen() { publicKeyB64: offer.publicKeyB64, lastConnected: Date.now() }) - if (!mountedRef.current) return + if (!mountedRef.current) { + return + } router.replace(`/h/${hostId}`) } catch (err) { - if (!mountedRef.current) return + if (!mountedRef.current) { + return + } console.warn('[pair] save failed', err) setStatus('error') setErrorMessage( diff --git a/mobile/app/terminal-settings.tsx b/mobile/app/terminal-settings.tsx index 19f9f9de9c2..2ceefc65516 100644 --- a/mobile/app/terminal-settings.tsx +++ b/mobile/app/terminal-settings.tsx @@ -47,9 +47,13 @@ const AUTO_RESTORE_FIT_OPTIONS: (PickerOption & { ms: number | nul ] function valueFromMs(ms: number | null | undefined): RestoreValue { - if (ms == null) return 'indefinite' + if (ms == null) { + return 'indefinite' + } const exact = AUTO_RESTORE_FIT_OPTIONS.find((o) => o.ms === ms) - if (exact) return exact.value + if (exact) { + return exact.value + } // Why: server may return a non-preset ms (custom value, future preset, // or server-side clamp). Snap to the closest finite preset so the // picker's selected radio agrees with the row sublabel rendered by @@ -57,7 +61,9 @@ function valueFromMs(ms: number | null | undefined): RestoreValue { let closest: (typeof AUTO_RESTORE_FIT_OPTIONS)[number] | null = null let bestDelta = Infinity for (const opt of AUTO_RESTORE_FIT_OPTIONS) { - if (opt.ms == null) continue + if (opt.ms == null) { + continue + } const delta = Math.abs(opt.ms - ms) if (delta < bestDelta) { bestDelta = delta @@ -68,8 +74,12 @@ function valueFromMs(ms: number | null | undefined): RestoreValue { } function autoRestoreSummary(ms: number | null | undefined): string { - if (ms === undefined) return '…' - if (ms === null) return AUTO_RESTORE_FIT_OPTIONS[0]!.label + if (ms === undefined) { + return '…' + } + if (ms === null) { + return AUTO_RESTORE_FIT_OPTIONS[0]!.label + } const exact = AUTO_RESTORE_FIT_OPTIONS.find((o) => o.ms === ms) return exact ? exact.label : `After ${Math.round(ms / 1000)}s` } @@ -174,7 +184,9 @@ export default function TerminalSettingsScreen() { const refreshShortcutLayout = useCallback(() => { const refreshSeq = layoutWriteSeqRef.current void loadTerminalAccessoryLayout().then((layout) => { - if (pendingLayoutWritesRef.current > 0 || refreshSeq !== layoutWriteSeqRef.current) return + if (pendingLayoutWritesRef.current > 0 || refreshSeq !== layoutWriteSeqRef.current) { + return + } setVisibleBuiltInIds(layout.visibleBuiltInIds) }) }, []) @@ -230,11 +242,15 @@ export default function TerminalSettingsScreen() { let cancelled = false for (const host of hosts) { const client = hostClientsById.get(host.id) ?? null - if (!client) continue + if (!client) { + continue + } void client .sendRequest('terminal.getAutoRestoreFit') .then((resp) => { - if (cancelled) return + if (cancelled) { + return + } const value = (resp as { ms?: number | null } | null)?.ms // Why: reconnect/status ticks can replay the same value; preserving // object identity avoids rerendering every settings row again. @@ -253,9 +269,13 @@ export default function TerminalSettingsScreen() { async function selectValue(hostId: string, value: RestoreValue) { const client = hostClientsById.get(hostId) ?? null - if (!client) return + if (!client) { + return + } const opt = AUTO_RESTORE_FIT_OPTIONS.find((o) => o.value === value) - if (!opt) return + if (!opt) { + return + } setHostMs((prev) => setTerminalAutoRestoreFitMsForHost(prev, hostId, opt.ms)) try { const resp = (await client.sendRequest('terminal.setAutoRestoreFit', { @@ -397,7 +417,9 @@ export default function TerminalSettingsScreen() { options={AUTO_RESTORE_FIT_OPTIONS} selected={valueFromMs(pickerHost ? hostMs[pickerHost.id] : null)} onSelect={(v) => { - if (pickerHost) void selectValue(pickerHost.id, v) + if (pickerHost) { + void selectValue(pickerHost.id, v) + } }} onClose={() => setPickerHostId(null)} /> diff --git a/mobile/app/troubleshoot.tsx b/mobile/app/troubleshoot.tsx index 42bf651e59e..0a1a63b7c27 100644 --- a/mobile/app/troubleshoot.tsx +++ b/mobile/app/troubleshoot.tsx @@ -160,7 +160,9 @@ export default function TroubleshootScreen() { results.push({ label: 'Paired hosts', status: 'warn', detail: 'Could not read host data' }) } - if (!isCurrentRun()) return + if (!isCurrentRun()) { + return + } setChecks([...results]) const internetCheck = startDiagnosticFetchTimeout(5000) @@ -169,14 +171,18 @@ export default function TroubleshootScreen() { const resp = await fetch('https://dns.google/resolve?name=example.com&type=A', { signal: internetCheck.signal }) - if (!isCurrentRun()) return + if (!isCurrentRun()) { + return + } results.push( resp.ok ? { label: 'Internet', status: 'pass', detail: 'Connected' } : { label: 'Internet', status: 'warn', detail: 'Unexpected response' } ) } catch { - if (!isCurrentRun()) return + if (!isCurrentRun()) { + return + } results.push({ label: 'Internet', status: 'fail', detail: 'No connection' }) } finally { internetCheck.dispose() @@ -185,15 +191,21 @@ export default function TroubleshootScreen() { } } - if (!isCurrentRun()) return + if (!isCurrentRun()) { + return + } setChecks([...results]) try { const hosts = await loadHosts() for (const host of hosts) { - if (!isCurrentRun()) return + if (!isCurrentRun()) { + return + } const reachable = await testHostReachability(host.endpoint) - if (!isCurrentRun()) return + if (!isCurrentRun()) { + return + } results.push({ label: host.name, status: reachable ? 'pass' : 'fail', @@ -207,7 +219,9 @@ export default function TroubleshootScreen() { results.push({ label: 'Hosts', status: 'warn', detail: 'Could not test' }) } - if (!isCurrentRun()) return + if (!isCurrentRun()) { + return + } results.push({ label: 'Platform', diff --git a/mobile/scripts/mock-server.ts b/mobile/scripts/mock-server.ts index 72cd9db0d1f..e1a91969730 100644 --- a/mobile/scripts/mock-server.ts +++ b/mobile/scripts/mock-server.ts @@ -36,11 +36,15 @@ function e2eeEncrypt(plaintext: string, sharedKey: Uint8Array): string { function e2eeDecrypt(encrypted: string, sharedKey: Uint8Array): string | null { const bundle = Uint8Array.from(Buffer.from(encrypted, 'base64')) - if (bundle.length < nacl.box.nonceLength + nacl.box.overheadLength) return null + if (bundle.length < nacl.box.nonceLength + nacl.box.overheadLength) { + return null + } const nonce = bundle.slice(0, nacl.box.nonceLength) const ciphertext = bundle.slice(nacl.box.nonceLength) const plaintext = nacl.box.open.after(ciphertext, nonce, sharedKey) - if (!plaintext) return null + if (!plaintext) { + return null + } return new TextDecoder().decode(plaintext) } @@ -147,7 +151,9 @@ function toGitStatusEntry(entry: FakeGitEntry): MobileGitStatusEntry { } function stageFakeGitEntry(entry: FakeGitEntry, filePaths: Set): FakeGitEntry { - if (!filePaths.has(entry.path)) return entry + if (!filePaths.has(entry.path)) { + return entry + } if (entry.area === 'untracked') { return { ...entry, area: 'staged', status: 'added', stagedFromUntracked: true } } @@ -155,7 +161,9 @@ function stageFakeGitEntry(entry: FakeGitEntry, filePaths: Set): FakeGit } function unstageFakeGitEntry(entry: FakeGitEntry, filePaths: Set): FakeGitEntry { - if (!filePaths.has(entry.path)) return entry + if (!filePaths.has(entry.path)) { + return entry + } if (entry.stagedFromUntracked) { return { ...entry, area: 'untracked', status: 'untracked', stagedFromUntracked: false } } diff --git a/mobile/scripts/repro-terminal-colors.ts b/mobile/scripts/repro-terminal-colors.ts index 89f0bb55d56..3c21df64176 100644 --- a/mobile/scripts/repro-terminal-colors.ts +++ b/mobile/scripts/repro-terminal-colors.ts @@ -140,7 +140,9 @@ async function listHandles( async function ensureSecondHandle(ws: WebSocket, handleA: string): Promise { const terminals = await listHandles(ws) const existing = terminals.find((terminal) => terminal.handle !== handleA) - if (existing) return existing.handle + if (existing) { + return existing.handle + } const created = await send(ws, 'terminal.create', { worktree: worktreeSelector, @@ -174,7 +176,9 @@ async function captureSnapshot(ws: WebSocket, label: string, handle: string): Pr } }) streamListeners.set(id, (result) => { - if (result.type !== 'scrollback') return + if (result.type !== 'scrollback') { + return + } clearTimeout(timeout) pending.delete(id) streamListeners.delete(id) @@ -333,7 +337,9 @@ ws.on('message', (data) => { return } const plaintext = decrypt(raw) - if (!plaintext) return + if (!plaintext) { + return + } const response = JSON.parse(plaintext) as RpcResponse const result = response.result @@ -344,7 +350,9 @@ ws.on('message', (data) => { } const request = pending.get(response.id) - if (!request || request.method === 'terminal.subscribe') return + if (!request || request.method === 'terminal.subscribe') { + return + } pending.delete(response.id) request.resolve(response) }) diff --git a/mobile/scripts/repro-worktree-startup-stream.ts b/mobile/scripts/repro-worktree-startup-stream.ts index bc1ab769564..e4db6182f00 100644 --- a/mobile/scripts/repro-worktree-startup-stream.ts +++ b/mobile/scripts/repro-worktree-startup-stream.ts @@ -338,7 +338,9 @@ ws.on('message', (data) => { return } const plaintext = decrypt(raw) - if (!plaintext) return + if (!plaintext) { + return + } const response = JSON.parse(plaintext) as RpcResponse const result = response.result @@ -349,7 +351,9 @@ ws.on('message', (data) => { } const request = pending.get(response.id) - if (!request || request.method === 'terminal.subscribe') return + if (!request || request.method === 'terminal.subscribe') { + return + } pending.delete(response.id) request.resolve(response) }) diff --git a/mobile/scripts/test-subscribe.ts b/mobile/scripts/test-subscribe.ts index a33ddd0c22b..56b90a48c52 100644 --- a/mobile/scripts/test-subscribe.ts +++ b/mobile/scripts/test-subscribe.ts @@ -114,7 +114,9 @@ function formatResponse(response: RpcResponse): string { } async function chooseWorktree(ws: WebSocket): Promise { - if (worktreeSelector) return worktreeSelector + if (worktreeSelector) { + return worktreeSelector + } const response = await send(ws, 'worktree.ps') if (!response.ok) { @@ -275,7 +277,9 @@ ws.on('message', (data) => { // connect flow above. The global listener only cares about encrypted RPC. return } - if (!plaintext) return + if (!plaintext) { + return + } const response = JSON.parse(plaintext) as RpcResponse if (response._meta?.runtimeId) { runtimeId = response._meta.runtimeId diff --git a/mobile/src/browser/MobileBrowserPane.tsx b/mobile/src/browser/MobileBrowserPane.tsx index 5efeee30893..0b45fad1422 100644 --- a/mobile/src/browser/MobileBrowserPane.tsx +++ b/mobile/src/browser/MobileBrowserPane.tsx @@ -430,7 +430,9 @@ export function MobileBrowserPane({ busyRef.current = true setBusy(true) let startupTimer: ReturnType | null = setTimeout(() => { - if (streamGenerationRef.current !== generation) return + if (streamGenerationRef.current !== generation) { + return + } busyRef.current = false setBusy(false) setError('Browser stream timed out.') @@ -449,7 +451,9 @@ export function MobileBrowserPane({ ...streamRequest }, (payload) => { - if (streamGenerationRef.current !== generation) return + if (streamGenerationRef.current !== generation) { + return + } const event = payload as { type?: string message?: string @@ -509,7 +513,9 @@ export function MobileBrowserPane({ }, { onBinaryFrame: (frame) => { - if (streamGenerationRef.current !== generation) return + if (streamGenerationRef.current !== generation) { + return + } clearStartupTimer() if (cacheKey) { applyFrameThrottled(frame, cacheKey) @@ -758,9 +764,13 @@ export function MobileBrowserPane({ clearLongPressTimer() longPressTimerRef.current = setTimeout(() => { const start = startPointRef.current - if (!start) return + if (!start) { + return + } const point = mapTouchPoint(start.x, start.y) - if (!point) return + if (!point) { + return + } rightClickSentRef.current = true void sendPointerClick(point, 'right') onToast('Right click') diff --git a/mobile/src/cache/home-snapshot-cache.ts b/mobile/src/cache/home-snapshot-cache.ts index 57256be8ea0..a591bb01d70 100644 --- a/mobile/src/cache/home-snapshot-cache.ts +++ b/mobile/src/cache/home-snapshot-cache.ts @@ -34,10 +34,14 @@ let memoryCache: HomeSnapshot | null = null let writeTimer: ReturnType | null = null export async function loadHomeSnapshot(): Promise { - if (memoryCache) return memoryCache + if (memoryCache) { + return memoryCache + } try { const raw = await AsyncStorage.getItem(STORAGE_KEY) - if (!raw) return null + if (!raw) { + return null + } const parsed = JSON.parse(raw) as HomeSnapshot if ( typeof parsed !== 'object' || @@ -58,7 +62,9 @@ export async function loadHomeSnapshot(): Promise { // (one per provider fetch finishing) doesn't hammer AsyncStorage. export function saveHomeSnapshot(snapshot: HomeSnapshot): void { memoryCache = snapshot - if (writeTimer) clearTimeout(writeTimer) + if (writeTimer) { + clearTimeout(writeTimer) + } writeTimer = setTimeout(() => { writeTimer = null void AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(snapshot)).catch(() => {}) diff --git a/mobile/src/cache/worktree-cache.ts b/mobile/src/cache/worktree-cache.ts index 9654dd357ad..0b05f78b260 100644 --- a/mobile/src/cache/worktree-cache.ts +++ b/mobile/src/cache/worktree-cache.ts @@ -20,13 +20,17 @@ export function setCachedWorktrees(hostId: string, worktrees: unknown[]): void { cache.set(hostId, { worktrees, at: Date.now() }) if (cache.size > MAX_ENTRIES) { const oldest = cache.keys().next().value - if (oldest) cache.delete(oldest) + if (oldest) { + cache.delete(oldest) + } } } export function getCachedWorktrees(hostId: string): unknown[] | null { const entry = cache.get(hostId) - if (!entry) return null + if (!entry) { + return null + } if (Date.now() - entry.at > MAX_AGE_MS) { cache.delete(hostId) return null diff --git a/mobile/src/components/ActionSheetModal.tsx b/mobile/src/components/ActionSheetModal.tsx index f7483c8d1d5..edb7f9b8449 100644 --- a/mobile/src/components/ActionSheetModal.tsx +++ b/mobile/src/components/ActionSheetModal.tsx @@ -23,8 +23,12 @@ type Props = { } function iconForAction(label: string, destructive?: boolean, icon?: LucideIcon): LucideIcon { - if (icon) return icon - if (destructive || /delete|remove/i.test(label)) return Trash2 + if (icon) { + return icon + } + if (destructive || /delete|remove/i.test(label)) { + return Trash2 + } return Edit3 } diff --git a/mobile/src/components/BottomDrawer.tsx b/mobile/src/components/BottomDrawer.tsx index a0d154c4296..c4fbbe32a1b 100644 --- a/mobile/src/components/BottomDrawer.tsx +++ b/mobile/src/components/BottomDrawer.tsx @@ -61,7 +61,9 @@ export function BottomDrawer({ // Why: hidden drawers are rendered by parent screens even while closed; keep // their Reanimated/Gesture setup out of hot paths like commit-message typing. - if (!resolvedMounted) return null + if (!resolvedMounted) { + return null + } return ( { - if (!visible) return + if (!visible) { + return + } const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow' const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide' @@ -142,7 +146,9 @@ function MountedBottomDrawer({ }, [visible, insets.bottom]) useEffect(() => { - if (!visible) return + if (!visible) { + return + } const sub = BackHandler.addEventListener('hardwareBackPress', () => { onClose() @@ -215,7 +221,9 @@ function MountedBottomDrawer({ } }) .onEnd((e) => { - if (!contentDragCanDismiss.value || scrollOffsetY.value > TOP_SCROLL_EPSILON) return + if (!contentDragCanDismiss.value || scrollOffsetY.value > TOP_SCROLL_EPSILON) { + return + } const translationY = e.translationY - contentDragStartY.value if (translationY > DISMISS_THRESHOLD || e.velocityY > 500) { diff --git a/mobile/src/components/ConnectionLog.tsx b/mobile/src/components/ConnectionLog.tsx index afe07503c14..ad49e46d870 100644 --- a/mobile/src/components/ConnectionLog.tsx +++ b/mobile/src/components/ConnectionLog.tsx @@ -28,15 +28,21 @@ function formatTime(ts: number, baseTs: number): string { // Why: show elapsed seconds since the first entry — absolute wall-clock // time isn't actionable when debugging "why is connecting stuck". const elapsed = Math.max(0, ts - baseTs) / 1000 - if (elapsed < 10) return `+${elapsed.toFixed(2)}s` - if (elapsed < 100) return `+${elapsed.toFixed(1)}s` + if (elapsed < 10) { + return `+${elapsed.toFixed(2)}s` + } + if (elapsed < 100) { + return `+${elapsed.toFixed(1)}s` + } return `+${Math.round(elapsed)}s` } export function ConnectionLog({ entries, title }: Props) { const scrollRef = useRef(null) - if (entries.length === 0) return null + if (entries.length === 0) { + return null + } const baseTs = entries[0]!.ts return ( diff --git a/mobile/src/components/CustomKeyModal.tsx b/mobile/src/components/CustomKeyModal.tsx index 5299563b012..4dff4ca1a2d 100644 --- a/mobile/src/components/CustomKeyModal.tsx +++ b/mobile/src/components/CustomKeyModal.tsx @@ -119,7 +119,9 @@ export function CustomKeyModal({ visible, onClose, onKeysChanged, onManageShortc const previewKeyLabel = useMemo(() => { const special = SPECIAL_KEY_BY_ID[shortcutKey] - if (special) return special.label + if (special) { + return special.label + } return shortcutKey.length === 1 ? shortcutKey.toUpperCase() : shortcutKey }, [shortcutKey]) @@ -156,14 +158,18 @@ export function CustomKeyModal({ visible, onClose, onKeysChanged, onManageShortc const handleShortcutSave = useCallback(() => { const built = buildTerminalShortcutKey({ key: shortcutKey, modifiers: shortcutModifiers }) - if (!built) return + if (!built) { + return + } void addKey({ label: built.label, bytes: built.bytes, enter: false }) }, [addKey, shortcutKey, shortcutModifiers]) const handleMacroSave = useCallback(() => { const label = macroLabel.trim() || macroText.trim().slice(0, 12) const text = macroText - if (!label || !text) return + if (!label || !text) { + return + } const bytes = macroEnter ? `${text}\r` : text void addKey({ label, bytes, enter: false }) }, [addKey, macroLabel, macroText, macroEnter]) @@ -324,7 +330,9 @@ export function CustomKeyModal({ visible, onClose, onKeysChanged, onManageShortc {group.ids.map((id) => { const key = SPECIAL_KEY_BY_ID[id] - if (!key) return null + if (!key) { + return null + } const selected = shortcutKey === id const flexBasis = `${100 / group.columns}%` as const return ( diff --git a/mobile/src/components/MobileAgentIcon.tsx b/mobile/src/components/MobileAgentIcon.tsx index 2e6ecf28b11..74b6658f054 100644 --- a/mobile/src/components/MobileAgentIcon.tsx +++ b/mobile/src/components/MobileAgentIcon.tsx @@ -79,11 +79,21 @@ function AgentLetterIcon({ letter, size = 16 }: { letter: string; size?: number } export function MobileAgentIcon({ agentId, size = 16 }: { agentId: string; size?: number }) { - if (agentId === 'claude') return - if (agentId === 'codex') return - if (agentId === 'pi') return - if (agentId === 'omp') return - if (agentId === 'aider') return + if (agentId === 'claude') { + return + } + if (agentId === 'codex') { + return + } + if (agentId === 'pi') { + return + } + if (agentId === 'omp') { + return + } + if (agentId === 'aider') { + return + } if (agentId === '__blank__' || agentId === 'blank') { return } diff --git a/mobile/src/components/MobileRichMarkdownEditor.tsx b/mobile/src/components/MobileRichMarkdownEditor.tsx index 35121cc5b67..e50bc2e19ca 100644 --- a/mobile/src/components/MobileRichMarkdownEditor.tsx +++ b/mobile/src/components/MobileRichMarkdownEditor.tsx @@ -29,13 +29,21 @@ const EDITOR_DOCUMENT_URL = `${EDITOR_DOCUMENT_ORIGIN}/rich-markdown-editor` function normalizeExternalEditorUrl(value: string): string | null { const url = value.trim() - if (!url) return null + if (!url) { + return null + } for (let index = 0; index < url.length; index += 1) { const code = url.charCodeAt(index) - if (code <= 32 || code === 127) return null + if (code <= 32 || code === 127) { + return null + } + } + if (/^mailto:/i.test(url)) { + return url + } + if (!/^https?:\/\//i.test(url)) { + return null } - if (/^mailto:/i.test(url)) return url - if (!/^https?:\/\//i.test(url)) return null try { const parsed = new URL(url) return parsed.protocol === 'http:' || parsed.protocol === 'https:' ? parsed.toString() : null @@ -128,7 +136,9 @@ function MobileRichMarkdownEditorInner({ content, editable, onChange }: Props) { ) useEffect(() => { - if (!readyRef.current) return + if (!readyRef.current) { + return + } if (currentWebViewContentRef.current !== content) { applyContent(content) } @@ -148,7 +158,9 @@ function MobileRichMarkdownEditorInner({ content, editable, onChange }: Props) { } catch { return } - if (!message || typeof message !== 'object') return + if (!message || typeof message !== 'object') { + return + } const editorMessage = message as Partial if ('type' in message && message.type === 'ready') { readyRef.current = true diff --git a/mobile/src/components/NewWorktreeModal.tsx b/mobile/src/components/NewWorktreeModal.tsx index 05856e3bb00..f307d2432d2 100644 --- a/mobile/src/components/NewWorktreeModal.tsx +++ b/mobile/src/components/NewWorktreeModal.tsx @@ -79,7 +79,9 @@ type SetupTrustPrompt = { function repoColor(name: string): string { const palette = ['#f97316', '#8b5cf6', '#06b6d4', '#ec4899', '#84cc16', '#f59e0b', '#6366f1'] let hash = 0 - for (let i = 0; i < name.length; i += 1) hash = (hash * 31 + name.charCodeAt(i)) | 0 + for (let i = 0; i < name.length; i += 1) { + hash = (hash * 31 + name.charCodeAt(i)) | 0 + } return palette[Math.abs(hash) % palette.length]! } @@ -230,7 +232,9 @@ export function NewWorktreeModal({ setShowAgentPicker(false) return } - if (!client) return + if (!client) { + return + } let stale = false setName('') setNote('') @@ -262,7 +266,9 @@ export function NewWorktreeModal({ client.sendRequest('settings.get'), client.sendRequest('ui.get') ]) - if (stale) return + if (stale) { + return + } if (settingsResponse.ok) { const result = (settingsResponse as RpcSuccess).result as { settings: RuntimeSettings } setRuntimeSettings(result.settings) @@ -283,9 +289,13 @@ export function NewWorktreeModal({ } } } catch { - if (!stale) setRepos([]) + if (!stale) { + setRepos([]) + } } finally { - if (!stale) setLoading(false) + if (!stale) { + setLoading(false) + } } })() return () => { @@ -303,7 +313,9 @@ export function NewWorktreeModal({ void client .sendRequest('ssh.getState', { targetId: selectedRepoConnectionId }) .then((response) => { - if (stale) return + if (stale) { + return + } if (!response.ok) { throw new Error(response.error.message) } @@ -333,7 +345,9 @@ export function NewWorktreeModal({ }, [client, selectedRepoConnectionId, visible]) useEffect(() => { - if (!visible || !client) return + if (!visible || !client) { + return + } if (selectedRepoConnectionId && sshGate.status !== 'connected') { setDetectedAgentIds(null) return @@ -347,12 +361,16 @@ export function NewWorktreeModal({ connectionId: selectedRepoConnectionId }) : await client.sendRequest('preflight.detectAgents') - if (stale) return + if (stale) { + return + } setDetectedAgentIds( response.ok ? new Set((response as RpcSuccess).result as string[]) : new Set() ) } catch { - if (!stale) setDetectedAgentIds(new Set()) + if (!stale) { + setDetectedAgentIds(new Set()) + } } })() return () => { @@ -373,7 +391,9 @@ export function NewWorktreeModal({ const response = await client.sendRequest('repo.hooks', { repo: `id:${selectedRepo.id}` }) - if (stale) return + if (stale) { + return + } if (response.ok) { const result = (response as RpcSuccess).result as RepoHooksResponse const cmd = result.hooks?.scripts?.setup?.trim() || null @@ -404,7 +424,9 @@ export function NewWorktreeModal({ }, [client, selectedRepo]) async function connectSelectedSshRepo(): Promise { - if (!client || !selectedRepoConnectionId) return + if (!client || !selectedRepoConnectionId) { + return + } setSshConnecting(true) setSshState({ targetId: selectedRepoConnectionId, @@ -447,7 +469,9 @@ export function NewWorktreeModal({ contentHash: string, alwaysTrust: boolean ): Promise { - if (!client) return + if (!client) { + return + } const next = trustedOrcaHooksWithSetupApproval({ trust: trustedOrcaHooks, repoId, @@ -462,7 +486,9 @@ export function NewWorktreeModal({ } async function handleCreate(options: CreateOptions = {}) { - if (!client || !selectedRepo) return + if (!client || !selectedRepo) { + return + } setCreating(true) setError('') @@ -566,8 +592,12 @@ export function NewWorktreeModal({ setupDecision, name: candidateName } - if (selectedAgent.id !== '__blank__') params.createdWithAgent = selectedAgent.id - if (note.trim()) params.comment = note.trim() + if (selectedAgent.id !== '__blank__') { + params.createdWithAgent = selectedAgent.id + } + if (note.trim()) { + params.comment = note.trim() + } const response = await client.sendRequest('worktree.create', params, { timeoutMs: WORKTREE_CREATE_TIMEOUT_MS @@ -715,7 +745,9 @@ export function NewWorktreeModal({ autoFocus={repos.length <= 1} returnKeyType="done" onSubmitEditing={() => { - if (canCreate) void handleCreate() + if (canCreate) { + void handleCreate() + } }} /> diff --git a/mobile/src/components/PickerModal.tsx b/mobile/src/components/PickerModal.tsx index 99935379cb5..25da404632f 100644 --- a/mobile/src/components/PickerModal.tsx +++ b/mobile/src/components/PickerModal.tsx @@ -79,14 +79,18 @@ function PickerModalContent({ opt.disabled && styles.rowDisabled ]} onPress={() => { - if (opt.disabled) return + if (opt.disabled) { + return + } onSelect(opt.value) onClose() }} onLongPress={ onLongSelect ? () => { - if (opt.disabled) return + if (opt.disabled) { + return + } onLongSelect(opt.value) onClose() } diff --git a/mobile/src/components/mobile-markdown-parser.ts b/mobile/src/components/mobile-markdown-parser.ts index e4dbe6024f9..b6375f389c5 100644 --- a/mobile/src/components/mobile-markdown-parser.ts +++ b/mobile/src/components/mobile-markdown-parser.ts @@ -42,7 +42,9 @@ export function parseMobileMarkdown(content: string): MobileMarkdownBlock[] { code.push(lines[index] ?? '') index += 1 } - if (index < lines.length) index += 1 + if (index < lines.length) { + index += 1 + } blocks.push({ type: 'code', text: code.join('\n'), language: fence[1] }) continue } diff --git a/mobile/src/components/mobile-rich-markdown-editor-html.test.ts b/mobile/src/components/mobile-rich-markdown-editor-html.test.ts index 810ef180eb9..f21674478b1 100644 --- a/mobile/src/components/mobile-rich-markdown-editor-html.test.ts +++ b/mobile/src/components/mobile-rich-markdown-editor-html.test.ts @@ -18,10 +18,14 @@ function extractFunctionSource(script: string, name: string): string { let depth = 0 for (let index = bodyStart; index < script.length; index += 1) { const char = script[index] - if (char === '{') depth += 1 + if (char === '{') { + depth += 1 + } if (char === '}') { depth -= 1 - if (depth === 0) return script.slice(start, index + 1) + if (depth === 0) { + return script.slice(start, index + 1) + } } } throw new Error(`Could not extract ${name}`) diff --git a/mobile/src/components/worktree-name-suggestion.ts b/mobile/src/components/worktree-name-suggestion.ts index e1f14d5464c..5ac28bbddf4 100644 --- a/mobile/src/components/worktree-name-suggestion.ts +++ b/mobile/src/components/worktree-name-suggestion.ts @@ -39,13 +39,17 @@ export function getSuggestedCreatureName( } // Lowercased to match branch-name convention (fix/seahorse, not fix/Seahorse). const available = MARINE_CREATURES.map(normalize).filter((name) => !used.has(name)) - if (available.length > 0) return pickRandom(available, random) + if (available.length > 0) { + return pickRandom(available, random) + } let suffix = 2 while (true) { const numbered = MARINE_CREATURES.map((name) => `${normalize(name)}-${suffix}`).filter( (name) => !used.has(name) ) - if (numbered.length > 0) return pickRandom(numbered, random) + if (numbered.length > 0) { + return pickRandom(numbered, random) + } suffix += 1 } } diff --git a/mobile/src/diagnostics/diagnostic-fetch-timeout.ts b/mobile/src/diagnostics/diagnostic-fetch-timeout.ts index 8cc4fe2cad4..8c9036d2e0c 100644 --- a/mobile/src/diagnostics/diagnostic-fetch-timeout.ts +++ b/mobile/src/diagnostics/diagnostic-fetch-timeout.ts @@ -15,7 +15,9 @@ export function startDiagnosticFetchTimeout(timeoutMs: number): DiagnosticFetchT }, timeoutMs) function dispose() { - if (disposed) return + if (disposed) { + return + } disposed = true if (timer) { clearTimeout(timer) diff --git a/mobile/src/notifications/mobile-notifications.ts b/mobile/src/notifications/mobile-notifications.ts index a26aa1dc545..a8e9caa488b 100644 --- a/mobile/src/notifications/mobile-notifications.ts +++ b/mobile/src/notifications/mobile-notifications.ts @@ -58,10 +58,14 @@ function configureNotificationChannel(): void { async function showLocalNotification(event: NotificationEvent, hostId: string): Promise { const enabled = await loadPushNotificationsEnabled() - if (!enabled) return + if (!enabled) { + return + } const granted = await ensureNotificationPermissions() - if (!granted) return + if (!granted) { + return + } await Notifications.scheduleNotificationAsync({ content: { @@ -99,10 +103,14 @@ export function subscribeToDesktopNotifications(client: RpcClient, hostId: strin return } if (event.type === 'end') { - if (disposed) unsubscribeStream() + if (disposed) { + unsubscribeStream() + } + return + } + if (disposed) { return } - if (disposed) return if (event.type === 'notification') { void showLocalNotification(event as NotificationEvent, hostId) } diff --git a/mobile/src/session/mobile-diff-comments.ts b/mobile/src/session/mobile-diff-comments.ts index ec14dc51304..15c628a58de 100644 --- a/mobile/src/session/mobile-diff-comments.ts +++ b/mobile/src/session/mobile-diff-comments.ts @@ -43,15 +43,21 @@ export function formatDiffComments(comments: readonly DiffComment[]): string { } export function normalizeMobileDiffComments(value: unknown, worktreeId: string): DiffComment[] { - if (!Array.isArray(value)) return [] + if (!Array.isArray(value)) { + return [] + } return value.flatMap((candidate): DiffComment[] => { - if (!isRecord(candidate)) return [] + if (!isRecord(candidate)) { + return [] + } const id = typeof candidate.id === 'string' ? candidate.id : '' const filePath = typeof candidate.filePath === 'string' ? candidate.filePath : '' const lineNumber = typeof candidate.lineNumber === 'number' ? candidate.lineNumber : NaN const body = typeof candidate.body === 'string' ? candidate.body.trim() : '' const createdAt = typeof candidate.createdAt === 'number' ? candidate.createdAt : Date.now() - if (!id || !filePath || !Number.isFinite(lineNumber) || !body) return [] + if (!id || !filePath || !Number.isFinite(lineNumber) || !body) { + return [] + } return [ { id, @@ -103,7 +109,9 @@ export function removeMobileDiffComments( comments: readonly DiffComment[], ids: ReadonlySet ): DiffComment[] { - if (ids.size === 0) return [...comments] + if (ids.size === 0) { + return [...comments] + } return comments.filter((comment) => !ids.has(comment.id)) } @@ -123,7 +131,9 @@ export function removeDeliveredMobileDiffComments( comments: readonly DiffComment[], delivered: readonly DiffComment[] ): DiffComment[] { - if (delivered.length === 0) return [...comments] + if (delivered.length === 0) { + return [...comments] + } const deliveredById = new Map(delivered.map((comment) => [comment.id, comment])) return comments.filter((comment) => { const snapshot = deliveredById.get(comment.id) diff --git a/mobile/src/session/mobile-file-syntax.ts b/mobile/src/session/mobile-file-syntax.ts index f716fdc96ce..91f7ba77150 100644 --- a/mobile/src/session/mobile-file-syntax.ts +++ b/mobile/src/session/mobile-file-syntax.ts @@ -172,14 +172,30 @@ function tokenKindForClasses(className: unknown): MobileSyntaxTokenKind | null { : [] const tokens = new Set(classes.map((value) => value.replace(/^hljs-/, ''))) - if (hasAny(tokens, ['comment', 'quote'])) return 'comment' - if (hasAny(tokens, ['keyword', 'selector-tag', 'tag', 'name'])) return 'keyword' - if (hasAny(tokens, ['string', 'regexp', 'symbol', 'bullet'])) return 'string' - if (hasAny(tokens, ['number', 'literal'])) return 'number' - if (hasAny(tokens, ['type', 'built_in', 'class', 'title.class'])) return 'type' - if (hasAny(tokens, ['title.function', 'function', 'title'])) return 'function' - if (hasAny(tokens, ['attr', 'attribute', 'property', 'variable', 'params'])) return 'variable' - if (hasAny(tokens, ['meta', 'doctag', 'subst', 'section'])) return 'meta' + if (hasAny(tokens, ['comment', 'quote'])) { + return 'comment' + } + if (hasAny(tokens, ['keyword', 'selector-tag', 'tag', 'name'])) { + return 'keyword' + } + if (hasAny(tokens, ['string', 'regexp', 'symbol', 'bullet'])) { + return 'string' + } + if (hasAny(tokens, ['number', 'literal'])) { + return 'number' + } + if (hasAny(tokens, ['type', 'built_in', 'class', 'title.class'])) { + return 'type' + } + if (hasAny(tokens, ['title.function', 'function', 'title'])) { + return 'function' + } + if (hasAny(tokens, ['attr', 'attribute', 'property', 'variable', 'params'])) { + return 'variable' + } + if (hasAny(tokens, ['meta', 'doctag', 'subst', 'section'])) { + return 'meta' + } return null } diff --git a/mobile/src/source-control/mobile-git-status.ts b/mobile/src/source-control/mobile-git-status.ts index 9c78b0f9d0b..c6bfde9b8c2 100644 --- a/mobile/src/source-control/mobile-git-status.ts +++ b/mobile/src/source-control/mobile-git-status.ts @@ -44,8 +44,12 @@ function compareGitStatusEntries(a: MobileGitStatusEntry, b: MobileGitStatusEntr } function getConflictSortRank(entry: MobileGitStatusEntry): number { - if (entry.conflictStatus === 'unresolved') return 0 - if (entry.conflictStatus === 'resolved_locally') return 1 + if (entry.conflictStatus === 'unresolved') { + return 0 + } + if (entry.conflictStatus === 'resolved_locally') { + return 1 + } return 2 } diff --git a/mobile/src/storage/preferences.ts b/mobile/src/storage/preferences.ts index fdb71b2a04c..4bb837b307e 100644 --- a/mobile/src/storage/preferences.ts +++ b/mobile/src/storage/preferences.ts @@ -12,7 +12,9 @@ const NOTIF_KEY = 'orca:pushNotificationsEnabled' export async function loadPushNotificationsEnabled(): Promise { try { const raw = await AsyncStorage.getItem(NOTIF_KEY) - if (raw === null) return false + if (raw === null) { + return false + } return raw === 'true' } catch { return false @@ -55,7 +57,9 @@ function allowedString(value: unknown, allowed: Set, fallback: string): export async function loadPinnedIds(hostId: string): Promise> { try { const raw = await AsyncStorage.getItem(PINS_PREFIX + hostId) - if (!raw) return new Set() + if (!raw) { + return new Set() + } return new Set(stringArray(JSON.parse(raw))) } catch { return new Set() @@ -69,7 +73,9 @@ export async function savePinnedIds(hostId: string, ids: Set): Promise { try { const raw = await AsyncStorage.getItem(PREFS_PREFIX + hostId) - if (!raw) return DEFAULT_PREFS + if (!raw) { + return DEFAULT_PREFS + } const parsed = JSON.parse(raw) as Partial return { sortMode: allowedString(parsed.sortMode, SORT_MODES, DEFAULT_PREFS.sortMode), diff --git a/mobile/src/tasks/github-project-repo-match.ts b/mobile/src/tasks/github-project-repo-match.ts index a3457d90fff..bf70f71dab7 100644 --- a/mobile/src/tasks/github-project-repo-match.ts +++ b/mobile/src/tasks/github-project-repo-match.ts @@ -16,9 +16,13 @@ type CachedSlugState = export function normalizeGitHubRepositorySlug(value: string | null | undefined): string | null { const trimmed = value?.trim() - if (!trimmed) return null + if (!trimmed) { + return null + } const [owner, repo, extra] = trimmed.split('/') - if (!owner || !repo || extra) return null + if (!owner || !repo || extra) { + return null + } return `${owner}/${repo}`.toLowerCase() } @@ -27,8 +31,12 @@ function cachedSlugStateForRepo( slugsByRepoId: Record ): CachedSlugState { const cached = slugsByRepoId[repo.id] - if (!cached) return { status: 'missing' } - if (cached.path !== repo.path) return { status: 'stale' } + if (!cached) { + return { status: 'missing' } + } + if (cached.path !== repo.path) { + return { status: 'stale' } + } return { status: 'resolved', slug: normalizeGitHubRepositorySlug(cached.slug) } } @@ -38,7 +46,9 @@ export function findRepoForGitHubProjectRepository( slugsByRepoId: Record = {} ): GitHubProjectRepoMatch | null { const slug = normalizeGitHubRepositorySlug(repository) - if (!slug) return null + if (!slug) { + return null + } const slugStates = new Map( repos.map((repo) => [repo.id, cachedSlugStateForRepo(repo, slugsByRepoId)]) @@ -47,8 +57,12 @@ export function findRepoForGitHubProjectRepository( const state = slugStates.get(repo.id) return state?.status === 'resolved' && state.slug === slug }) - if (slugMatches.length === 1) return slugMatches[0]! - if (slugMatches.length > 1) return null + if (slugMatches.length === 1) { + return slugMatches[0]! + } + if (slugMatches.length > 1) { + return null + } return ( repos.find((repo) => { diff --git a/mobile/src/tasks/workspace-ssh-gate.ts b/mobile/src/tasks/workspace-ssh-gate.ts index 43c7e88d277..db9464c4f4c 100644 --- a/mobile/src/tasks/workspace-ssh-gate.ts +++ b/mobile/src/tasks/workspace-ssh-gate.ts @@ -12,13 +12,27 @@ function isWorkspaceSshConnectInProgress(status: SshConnectionStatus | null): bo } export function workspaceSshStatusLabel(status: SshConnectionStatus | null): string { - if (status === 'connected') return 'Connected' - if (status === 'connecting') return 'Connecting' - if (status === 'deploying-relay') return 'Deploying relay' - if (status === 'reconnecting') return 'Reconnecting' - if (status === 'auth-failed') return 'Authentication failed' - if (status === 'reconnection-failed') return 'Reconnect failed' - if (status === 'error') return 'Connection failed' + if (status === 'connected') { + return 'Connected' + } + if (status === 'connecting') { + return 'Connecting' + } + if (status === 'deploying-relay') { + return 'Deploying relay' + } + if (status === 'reconnecting') { + return 'Reconnecting' + } + if (status === 'auth-failed') { + return 'Authentication failed' + } + if (status === 'reconnection-failed') { + return 'Reconnect failed' + } + if (status === 'error') { + return 'Connection failed' + } return 'Disconnected' } diff --git a/mobile/src/terminal/TerminalWebView.tsx b/mobile/src/terminal/TerminalWebView.tsx index 98bf72d53d8..9aa43d5d10f 100644 --- a/mobile/src/terminal/TerminalWebView.tsx +++ b/mobile/src/terminal/TerminalWebView.tsx @@ -2059,7 +2059,9 @@ export const TerminalWebView = forwardRef(function }) } else if (msg.type === 'terminal-input') { const bytes = typeof msg.bytes === 'string' ? msg.bytes : '' - if (bytes.length > 0) onTerminalInput?.(bytes) + if (bytes.length > 0) { + onTerminalInput?.(bytes) + } } else if (msg.type === 'terminal-tap') { onTerminalTap?.() } else if (msg.type === 'keyboard-avoidance-metrics') { @@ -2144,7 +2146,9 @@ export const TerminalWebView = forwardRef(function measureFitDimensions( containerHeight?: number ): Promise<{ cols: number; rows: number } | null> { - if (!isWebReadyRef.current) return Promise.resolve(null) + if (!isWebReadyRef.current) { + return Promise.resolve(null) + } return new Promise((resolve) => { measureResolveRef.current?.(null) let timeout: ReturnType | null = null @@ -2184,7 +2188,9 @@ export const TerminalWebView = forwardRef(function // immediately if no init is pending. Capped at 3s so a stuck // WebView doesn't hang the caller. const p = readyPromiseRef.current - if (!p) return + if (!p) { + return + } await new Promise((resolve) => { let settled = false const timeout = setTimeout(() => { diff --git a/mobile/src/terminal/terminal-accessory-layout.ts b/mobile/src/terminal/terminal-accessory-layout.ts index 0aac137e38f..42c45ef0764 100644 --- a/mobile/src/terminal/terminal-accessory-layout.ts +++ b/mobile/src/terminal/terminal-accessory-layout.ts @@ -23,7 +23,9 @@ function defaultPreference(ids = builtInIds()): TerminalAccessoryLayoutPreferenc } function stringArray(value: unknown): string[] | null { - if (!Array.isArray(value)) return null + if (!Array.isArray(value)) { + return null + } return value.every((item): item is string => typeof item === 'string') ? value : null } @@ -31,7 +33,9 @@ function dedupeKnownIds(ids: string[], builtInSet: Set): string[] { const seen = new Set() const out: string[] = [] for (const id of ids) { - if (!builtInSet.has(id) || seen.has(id)) continue + if (!builtInSet.has(id) || seen.has(id)) { + continue + } seen.add(id) out.push(id) } @@ -52,7 +56,9 @@ export function normalizeTerminalAccessoryLayoutPreference( currentBuiltInIds = builtInIds() ): TerminalAccessoryLayoutPreference { const fallback = defaultPreference(currentBuiltInIds) - if (!value || typeof value !== 'object') return fallback + if (!value || typeof value !== 'object') { + return fallback + } const candidate = value as { version?: unknown @@ -61,7 +67,9 @@ export function normalizeTerminalAccessoryLayoutPreference( } const visibleInput = stringArray(candidate.visibleBuiltInIds) const knownInput = stringArray(candidate.knownBuiltInIds) - if (candidate.version !== 1 || !visibleInput || !knownInput) return fallback + if (candidate.version !== 1 || !visibleInput || !knownInput) { + return fallback + } const builtInSet = new Set(currentBuiltInIds) const knownInputSet = new Set(knownInput.filter((id) => builtInSet.has(id))) @@ -129,7 +137,9 @@ export function getVisibleTerminalAccessoryKeys( export async function loadTerminalAccessoryLayout(): Promise { try { const raw = await AsyncStorage.getItem(TERMINAL_ACCESSORY_LAYOUT_STORAGE_KEY) - if (!raw) return defaultPreference() + if (!raw) { + return defaultPreference() + } return normalizeTerminalAccessoryLayoutPreference(JSON.parse(raw)) } catch { return defaultPreference() diff --git a/mobile/src/terminal/terminal-gesture-input.ts b/mobile/src/terminal/terminal-gesture-input.ts index 83450a1730b..6b84c4824fe 100644 --- a/mobile/src/terminal/terminal-gesture-input.ts +++ b/mobile/src/terminal/terminal-gesture-input.ts @@ -37,7 +37,9 @@ function isSgrMouseGestureSequence(bytes: string, offset: number): number | null } const sequence = bytes.slice(offset, end + 1) const match = SGR_MOUSE_GESTURE_SEQUENCE_RE.exec(sequence) - if (!match) return null + if (!match) { + return null + } const button = match[1] const col = Number(match[2]) const row = Number(match[3]) diff --git a/mobile/src/transport/browser-screencast-protocol.ts b/mobile/src/transport/browser-screencast-protocol.ts index 625ef1efb5c..01ee4353f6c 100644 --- a/mobile/src/transport/browser-screencast-protocol.ts +++ b/mobile/src/transport/browser-screencast-protocol.ts @@ -40,8 +40,12 @@ export type BrowserScreencastFrame = { } function byteToFormat(value: number): BrowserScreencastFormat | null { - if (value === 1) return 'jpeg' - if (value === 2) return 'png' + if (value === 1) { + return 'jpeg' + } + if (value === 2) { + return 'png' + } return null } diff --git a/mobile/src/transport/client-context.tsx b/mobile/src/transport/client-context.tsx index 8807c4c33e9..c6ebbcc5332 100644 --- a/mobile/src/transport/client-context.tsx +++ b/mobile/src/transport/client-context.tsx @@ -74,17 +74,25 @@ export function RpcClientProvider({ children }: { children: ReactNode }) { function notifyHostState(hostId: string, state: ConnectionState) { const set = stateListenersRef.current.get(hostId) - if (!set) return - for (const listener of set) listener(state) + if (!set) { + return + } + for (const listener of set) { + listener(state) + } } function notifyAllHosts() { - for (const listener of allHostsListenersRef.current) listener() + for (const listener of allHostsListenersRef.current) { + listener() + } } const closeEntry = useCallback((hostId: string) => { const entry = storeRef.current.get(hostId) - if (!entry) return + if (!entry) { + return + } entry.unsubState() entry.client.close() storeRef.current.delete(hostId) @@ -123,12 +131,16 @@ export function RpcClientProvider({ children }: { children: ReactNode }) { notifyAllHosts() return null } - if (!host) return null + if (!host) { + return null + } } // Re-check after any await — another acquire() may have completed. const after = storeRef.current.get(hostId) - if (after) return after + if (after) { + return after + } let client: RpcClient try { @@ -143,7 +155,9 @@ export function RpcClientProvider({ children }: { children: ReactNode }) { } const unsubState = client.onStateChange((state) => { const cur = storeRef.current.get(hostId) - if (!cur) return + if (!cur) { + return + } cur.state = state notifyHostState(hostId, state) }) @@ -171,7 +185,9 @@ export function RpcClientProvider({ children }: { children: ReactNode }) { // pass inside openEntry. const acquire = useCallback( (hostId: string, host?: HostProfile): RpcClient | null => { - if (host) primedHostsRef.current.set(hostId, host) + if (host) { + primedHostsRef.current.set(hostId, host) + } const existing = storeRef.current.get(hostId) if (existing) { existing.refCount += 1 @@ -181,7 +197,9 @@ export function RpcClientProvider({ children }: { children: ReactNode }) { // try again once the state listener fires; consumers are expected to // call acquire() inside an effect that re-runs on state changes. void openEntry(hostId).then((entry) => { - if (!entry) return + if (!entry) { + return + } entry.refCount += 1 }) return null @@ -190,7 +208,9 @@ export function RpcClientProvider({ children }: { children: ReactNode }) { ) const primeHosts = useCallback((hosts: HostProfile[]) => { - for (const host of hosts) primedHostsRef.current.set(host.id, host) + for (const host of hosts) { + primedHostsRef.current.set(host.id, host) + } }, []) // Why: refcount dropping to 0 no longer triggers an idle-close. The @@ -203,7 +223,9 @@ export function RpcClientProvider({ children }: { children: ReactNode }) { // unmount (app shutdown). const release = useCallback((hostId: string) => { const entry = storeRef.current.get(hostId) - if (!entry) return + if (!entry) { + return + } entry.refCount = Math.max(0, entry.refCount - 1) }, []) @@ -222,7 +244,9 @@ export function RpcClientProvider({ children }: { children: ReactNode }) { storeRef.current.delete(hostId) } const fresh = await openEntry(hostId) - if (fresh) fresh.refCount = savedRefCount + if (fresh) { + fresh.refCount = savedRefCount + } }, [openEntry] ) @@ -249,9 +273,13 @@ export function RpcClientProvider({ children }: { children: ReactNode }) { set.add(listener) return () => { const s = stateListenersRef.current.get(hostId) - if (!s) return + if (!s) { + return + } s.delete(listener) - if (s.size === 0) stateListenersRef.current.delete(hostId) + if (s.size === 0) { + stateListenersRef.current.delete(hostId) + } } }, [] @@ -286,7 +314,9 @@ export function RpcClientProvider({ children }: { children: ReactNode }) { useEffect(() => { const store = storeRef.current return () => { - for (const [hostId] of store) closeEntry(hostId) + for (const [hostId] of store) { + closeEntry(hostId) + } } }, []) @@ -324,7 +354,9 @@ export function RpcClientProvider({ children }: { children: ReactNode }) { function useCtx(): ContextValue { const ctx = useContext(Ctx) - if (!ctx) throw new Error('useHostClient must be used inside ') + if (!ctx) { + throw new Error('useHostClient must be used inside ') + } return ctx } @@ -351,7 +383,9 @@ export function useHostClient(hostId: string | undefined): { let cancelled = false // Subscribe before acquire so any state change during open is captured. const unsub = ctx.subscribeHostState(hostId, (next) => { - if (cancelled) return + if (cancelled) { + return + } setState(next) // Why: if the client was null at first acquire (async open), the // first state change ('connecting'/'handshaking'/'connected') is our @@ -395,16 +429,24 @@ export function useAllHostClients(hostIds: string[]): Array<{ const [tick, setTick] = useState(0) useEffect(() => { - if (hostIds.length === 0) return - for (const id of hostIds) ctx.acquire(id) + if (hostIds.length === 0) { + return + } + for (const id of hostIds) { + ctx.acquire(id) + } const unsubs: Array<() => void> = [] for (const id of hostIds) { unsubs.push(ctx.subscribeHostState(id, () => setTick((n) => n + 1))) } unsubs.push(ctx.subscribeAllHosts(() => setTick((n) => n + 1))) return () => { - for (const u of unsubs) u() - for (const id of hostIds) ctx.release(id) + for (const u of unsubs) { + u() + } + for (const id of hostIds) { + ctx.release(id) + } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [key]) @@ -454,7 +496,9 @@ export function useReconnectAttempt(hostId: string | undefined): number { const ctx = useCtx() const [, force] = useState(0) useEffect(() => { - if (!hostId) return + if (!hostId) { + return + } return ctx.subscribeHostState(hostId, () => force((n) => n + 1)) }, [ctx, hostId]) return hostId ? ctx.getReconnectAttempt(hostId) : 0 @@ -469,7 +513,9 @@ export function useLastConnectedAt(hostId: string | undefined): number | null { const ctx = useCtx() const [, force] = useState(0) useEffect(() => { - if (!hostId) return + if (!hostId) { + return + } return ctx.subscribeHostState(hostId, () => force((n) => n + 1)) }, [ctx, hostId]) return hostId ? ctx.getLastConnectedAt(hostId) : null diff --git a/mobile/src/transport/connection-health.ts b/mobile/src/transport/connection-health.ts index 6bc5ba673a1..4ce81beffd2 100644 --- a/mobile/src/transport/connection-health.ts +++ b/mobile/src/transport/connection-health.ts @@ -43,7 +43,9 @@ export function classifyConnection(args: { } // Connected / connecting / handshaking are normal. - if (state === 'connected') return { kind: 'normal', label: 'Connected' } + if (state === 'connected') { + return { kind: 'normal', label: 'Connected' } + } if (state === 'connecting' || state === 'handshaking') { return { kind: 'normal', label: 'Connecting…' } } diff --git a/mobile/src/transport/host-names.ts b/mobile/src/transport/host-names.ts index cdb4418cbe4..8ac9ef83e85 100644 --- a/mobile/src/transport/host-names.ts +++ b/mobile/src/transport/host-names.ts @@ -9,7 +9,9 @@ export function getNextHostNameFromHosts(hosts: readonly HostNameSource[]): stri for (const host of hosts) { const match = HOST_NUMBER_PATTERN.exec(host.name) - if (!match) continue + if (!match) { + continue + } const hostNumber = Number.parseInt(match[1]!, 10) if (hostNumber > largestHostNumber) { diff --git a/mobile/src/transport/host-store.ts b/mobile/src/transport/host-store.ts index 9a022869af9..5db04aeeb97 100644 --- a/mobile/src/transport/host-store.ts +++ b/mobile/src/transport/host-store.ts @@ -39,7 +39,9 @@ let inflightLoad: Promise | null = null export async function loadHosts(): Promise { // Why: deduplicate concurrent loadHosts() calls so multiple screens // mounting simultaneously share one Keychain read pass. - if (inflightLoad) return inflightLoad + if (inflightLoad) { + return inflightLoad + } inflightLoad = doLoadHosts().finally(() => { inflightLoad = null }) @@ -48,14 +50,18 @@ export async function loadHosts(): Promise { async function doLoadHosts(): Promise { const raw = await AsyncStorage.getItem(STORAGE_KEY) - if (!raw) return [] + if (!raw) { + return [] + } let parsed: unknown try { parsed = JSON.parse(raw) } catch { return [] } - if (!Array.isArray(parsed)) return [] + if (!Array.isArray(parsed)) { + return [] + } const out: HostProfile[] = [] for (const item of parsed) { @@ -67,7 +73,9 @@ async function doLoadHosts(): Promise { continue } const stored = StoredHostProfileSchema.safeParse(item) - if (!stored.success) continue + if (!stored.success) { + continue + } let token = tokenCache.get(stored.data.id) if (!token) { @@ -97,14 +105,20 @@ async function doLoadHosts(): Promise { async function loadStoredHosts(): Promise { const raw = await AsyncStorage.getItem(STORAGE_KEY) - if (!raw) return [] + if (!raw) { + return [] + } try { const parsed = JSON.parse(raw) as unknown - if (!Array.isArray(parsed)) return [] + if (!Array.isArray(parsed)) { + return [] + } return parsed.flatMap((item) => { // Why: same drop-old-records rule as loadHosts; keeps internal // mutators from re-persisting pre-v0.0.3 entries. - if (item && typeof item === 'object' && 'deviceToken' in item) return [] + if (item && typeof item === 'object' && 'deviceToken' in item) { + return [] + } const result = StoredHostProfileSchema.safeParse(item) return result.success ? [result.data] : [] }) diff --git a/mobile/src/transport/pairing-connection-attempt.ts b/mobile/src/transport/pairing-connection-attempt.ts index c2c1372ff97..0a2d564949a 100644 --- a/mobile/src/transport/pairing-connection-attempt.ts +++ b/mobile/src/transport/pairing-connection-attempt.ts @@ -16,13 +16,17 @@ export function startPairingConnectionAttempt({ let timer: ReturnType | null = null function closeClientOnce() { - if (clientClosed) return + if (clientClosed) { + return + } clientClosed = true closeClient() } function dispose() { - if (disposed) return + if (disposed) { + return + } disposed = true if (timer) { clearTimeout(timer) diff --git a/mobile/src/transport/pairing.ts b/mobile/src/transport/pairing.ts index a3a22b8bf6e..46cb6cc07ae 100644 --- a/mobile/src/transport/pairing.ts +++ b/mobile/src/transport/pairing.ts @@ -8,7 +8,9 @@ import { PairingOfferSchema, type PairingOffer } from './types' export function decodePairingUrl(url: string): PairingOffer | null { try { const code = extractPairingCodeFromUrl(url) - if (!code) return null + if (!code) { + return null + } return decodePairingBase64(code) } catch { return null @@ -19,7 +21,9 @@ export function decodePairingUrl(url: string): PairingOffer | null { // extraction here makes QR scan, paste, and external deep-link flows // accept the same URL shapes. export function extractPairingCodeFromUrl(url: string): string | null { - if (!url.startsWith('orca://pair')) return null + if (!url.startsWith('orca://pair')) { + return null + } const queryIndex = url.indexOf('?') if (queryIndex !== -1) { const query = url.slice(queryIndex + 1).split('#')[0] ?? '' @@ -41,7 +45,9 @@ export function extractPairingCodeFromUrl(url: string): string | null { // copied from desktop. export function parsePairingCode(input: string): PairingOffer | null { const trimmed = input.trim() - if (!trimmed) return null + if (!trimmed) { + return null + } try { if (trimmed.startsWith('orca://pair')) { return decodePairingUrl(trimmed) diff --git a/mobile/src/transport/rpc-client.test.ts b/mobile/src/transport/rpc-client.test.ts index 600ce49788d..6dd06eefd3f 100644 --- a/mobile/src/transport/rpc-client.test.ts +++ b/mobile/src/transport/rpc-client.test.ts @@ -34,7 +34,9 @@ class MockWebSocket { emitCloseOnClose = true sent: string[] = [] close = vi.fn(() => { - if (this.readyState === MockWebSocket.CLOSED) return + if (this.readyState === MockWebSocket.CLOSED) { + return + } this.readyState = MockWebSocket.CLOSED if (this.emitCloseOnClose) { this.onclose?.() diff --git a/mobile/src/transport/rpc-client.ts b/mobile/src/transport/rpc-client.ts index 896a9037061..a6a80485fd1 100644 --- a/mobile/src/transport/rpc-client.ts +++ b/mobile/src/transport/rpc-client.ts @@ -149,7 +149,9 @@ export function connect( const onLog = options.onLog let logCounter = 0 function emitLog(level: ConnectionLogLevel, message: string, detail?: string) { - if (!onLog) return + if (!onLog) { + return + } onLog({ id: `log-${++logCounter}-${Date.now()}`, ts: Date.now(), @@ -216,7 +218,9 @@ export function connect( } function setState(next: ConnectionState) { - if (state === next) return + if (state === next) { + return + } const prev = state const dwelt = Date.now() - stateEnteredAt state = next @@ -258,8 +262,12 @@ export function connect( } function waitForConnected(timeoutMs?: number): Promise { - if (state === 'connected') return Promise.resolve() - if (intentionallyClosed) return Promise.reject(new Error('Client closed')) + if (state === 'connected') { + return Promise.resolve() + } + if (intentionallyClosed) { + return Promise.reject(new Error('Client closed')) + } if (state === 'reconnecting' && reconnectAttempt >= GIVE_UP_AFTER_ATTEMPTS && !reconnectTimer) { // Why: after the retry cap there is no future state transition to // release callers waiting before their per-request timeout starts. @@ -290,7 +298,9 @@ export function connect( } function openConnection() { - if (intentionallyClosed) return + if (intentionallyClosed) { + return + } const now = Date.now() wsConstructionCounter++ @@ -644,10 +654,14 @@ export function connect( event, (_k, v) => { if (typeof v === 'object' && v !== null) { - if (seen.has(v as object)) return '[circular]' + if (seen.has(v as object)) { + return '[circular]' + } seen.add(v as object) } - if (typeof v === 'function') return '[fn]' + if (typeof v === 'function') { + return '[fn]' + } return v }, 0 @@ -696,10 +710,14 @@ export function connect( event, (_k, v) => { if (typeof v === 'object' && v !== null) { - if (seen.has(v as object)) return '[circular]' + if (seen.has(v as object)) { + return '[circular]' + } seen.add(v as object) } - if (typeof v === 'function') return '[fn]' + if (typeof v === 'function') { + return '[fn]' + } return v }, 0 @@ -798,7 +816,9 @@ export function connect( // Why: only probe while the channel is actually in 'connected'. The // sendRequest path itself waits for connected, but a probe scheduled // during a reconnect would just stack up timeouts and confuse logs. - if (state !== 'connected' || !ws) return + if (state !== 'connected' || !ws) { + return + } const probeWs = ws // Why: short timeout (8s) — server's heartbeat is 15s, so if we // don't see *anything* back within 8s the link is almost certainly @@ -823,11 +843,15 @@ export function connect( }, 8_000) pending.set(id, { resolve: () => { - if (timedOut) return + if (timedOut) { + return + } clearTimeout(timeout) }, reject: () => { - if (timedOut) return + if (timedOut) { + return + } clearTimeout(timeout) } })