Require braces on conditionals

This commit is contained in:
Jon Staab 2026-09-21 09:48:35 -07:00
parent ef6069e7a4
commit da6a63870a
88 changed files with 717 additions and 240 deletions

View file

@ -86,7 +86,9 @@ export const boot = async (
await shell.waitFor({state: "attached", timeout: ms(15)})
break
} catch (e) {
if (attempt === 3) throw e
if (attempt === 3) {
throw e
}
await page.reload()
}

View file

@ -46,7 +46,9 @@ export const watchFaults = (): FaultWatch => {
const record = (line: string, fault: boolean) => {
log.push(line)
if (fault) found.push(line)
if (fault) {
found.push(line)
}
}
return {

View file

@ -217,7 +217,9 @@ export const test = base.extend<HarnessFixtures, HarnessWorkerFixtures>({
let scenario: Maybe<Scenario>
const requireScenario = () => {
if (scenario) return scenario
if (scenario) {
return scenario
}
throw new Error("Seed a scenario before opening a page for a user")
}

View file

@ -15,7 +15,9 @@ export const makeContextStore = <T>(installer: string) => {
get: (context: BrowserContext) => {
const value = byContext.get(context)
if (value) return value
if (value) {
return value
}
throw new Error(`${installer} was never called for this browser context`)
},

View file

@ -56,7 +56,9 @@ export const makePublisher = ({name, enqueue, startedAt, sign}: PublisherOptions
})
return () => {
if (value) return value
if (value) {
return value
}
throw new Error(`An event seeded into "${name}" was read before seed() published it`)
}

View file

@ -115,7 +115,9 @@ export const seed = async (
const getSpace = (name: SpaceName) => {
const space = spaces.get(name)
if (space) return space
if (space) {
return space
}
throw new Error(`No space named "${name}" was seeded`)
}
@ -123,7 +125,9 @@ export const seed = async (
const getOpenRelay = (name: OpenRelayName) => {
const relay = opened.find(candidate => candidate.name === name)
if (relay) return relay
if (relay) {
return relay
}
throw new Error(`No open relay named "${name}" was seeded`)
}
@ -133,8 +137,12 @@ export const seed = async (
const roomList = roomLists.get(user.pubkey)
const relayList = relayLists.get(user.pubkey)
if (roomList) events.push(roomList)
if (relayList) events.push(relayList)
if (roomList) {
events.push(roomList)
}
if (relayList) {
events.push(relayList)
}
return events
}

View file

@ -91,7 +91,9 @@ export const seedSpace = ({zooid, enqueue, startedAt, name}: SeedSpaceOptions):
let testRelay: Maybe<TestRelay>
const relay = () => {
if (testRelay) return testRelay
if (testRelay) {
return testRelay
}
throw new Error(`Space "${name}" has not been seeded yet, await seed() first`)
}

View file

@ -139,7 +139,9 @@ const describeClockDrift = (drift: number) => {
const getTestUser = (pubkey: string) => {
const user = testUsersByPubkey.get(pubkey)
if (user) return user
if (user) {
return user
}
throw new Error(
`Cannot publish as ${pubkey}: zooid authenticates every write, so seeded events must be ` +
@ -190,7 +192,9 @@ export class Zooid {
// Verifies docker rather than bringing the container up, which `reset` does. Repeat calls are
// free, so the fixture can call this per test.
start = async () => {
if (this.started) return
if (this.started) {
return
}
const problem = await describeDockerProblem()
@ -381,7 +385,9 @@ export class Zooid {
const key = `${host} ${user.pubkey}`
const session = this.sessions.get(key)
if (session) return session
if (session) {
return session
}
const connection = connectToZooid(host)
const [, challenge] = await connection.wait(isRelayAuth)

View file

@ -46,11 +46,21 @@ export const makeTestRelay = ({name, url, publish}: TestRelayOptions): TestRelay
const tags = [["h", h]]
if (meta.name) tags.push(["name", meta.name])
if (meta.about) tags.push(["about", meta.about])
if (meta.picture) tags.push(["picture", meta.picture])
if (meta.closed) tags.push(["closed"])
if (meta.private) tags.push(["private"])
if (meta.name) {
tags.push(["name", meta.name])
}
if (meta.about) {
tags.push(["about", meta.about])
}
if (meta.picture) {
tags.push(["picture", meta.picture])
}
if (meta.closed) {
tags.push(["closed"])
}
if (meta.private) {
tags.push(["private"])
}
await event(users.admin, makeEvent(ROOM_EDIT_META, {tags, created_at: createdAt}))
},

View file

@ -46,6 +46,7 @@ export default [
"no-useless-escape": "off",
"no-extra-semi": "off",
"no-async-promise-executor": "off",
curly: ["error", "all"],
"prefer-const": ["error", {destructuring: "all"}],
"no-eq-null": "error",
"no-restricted-syntax": [

View file

@ -10,7 +10,9 @@ export const argon2id = ({
hashLength,
outputType,
}) => {
if (outputType !== "binary") throw new Error("Expected Pomade's binary Argon2id output")
if (outputType !== "binary") {
throw new Error("Expected Pomade's binary Argon2id output")
}
return argon2idAsync(password, salt, {
t: iterations,
m: memorySize,

View file

@ -3,6 +3,8 @@ import {verifyEvent} from "nostr-tools/pure"
// Welshman's optional accelerator expects verification to throw on invalid events.
export const initNostrWasm = async () => ({
verifyEvent(event) {
if (!verifyEvent(event)) throw new Error("Invalid Nostr event")
if (!verifyEvent(event)) {
throw new Error("Invalid Nostr event")
}
},
})

View file

@ -15,8 +15,11 @@ const run = (command, args, options = {}) =>
const child = spawn(command, args, {cwd: root, env, stdio: "inherit", ...options})
child.on("error", reject)
child.on("exit", (code, signal) => {
if (code === 0) resolve()
else reject(new Error(`${command} failed (${signal || code})`))
if (code === 0) {
resolve()
} else {
reject(new Error(`${command} failed (${signal || code})`))
}
})
})

View file

@ -235,7 +235,9 @@ export const attemptRelayAccess = async (url: string, claim = "") => {
const thunk = await publishJoinRequest(url, claim)
const error = await thunk.waitForError()
if (shouldIgnoreError(error)) return
if (shouldIgnoreError(error)) {
return
}
if (error.includes("invite code")) {
return "join request rejected"
@ -282,11 +284,21 @@ export class Access {
this.claimFailed,
],
([$loading, $isGeneric, $isExplicit, $roomInviteError, $roomCode, $claimFailed]) => {
if ($loading) return "loading" as const
if ($isGeneric) return "network" as const
if ($isExplicit || $roomInviteError) return "auth" as const
if (requireCode && !$roomCode) return "failed" as const
if ($claimFailed) return "noclaim" as const
if ($loading) {
return "loading" as const
}
if ($isGeneric) {
return "network" as const
}
if ($isExplicit || $roomInviteError) {
return "auth" as const
}
if (requireCode && !$roomCode) {
return "failed" as const
}
if ($claimFailed) {
return "noclaim" as const
}
return "ready" as const
},

View file

@ -81,7 +81,9 @@ const compareScores = (a: LayoutScore, b: LayoutScore): number => {
"aspectDeviation",
]
for (const field of fields) {
if (a[field] !== b[field]) return a[field] - b[field]
if (a[field] !== b[field]) {
return a[field] - b[field]
}
}
return 0
}
@ -121,7 +123,9 @@ const buildCandidate = (
const availHeight = (containerHeight - (totalRowCount - 1) * gap) / totalRowCount
const availWidth = (containerWidth - (columnCount - 1) * gap) / columnCount
if (availWidth <= 0 || availHeight <= 0) return undefined
if (availWidth <= 0 || availHeight <= 0) {
return undefined
}
const {tileWidth, tileHeight, aspectRatio} = fitTile(availWidth, availHeight, minAspect)
@ -171,7 +175,9 @@ export const computeAdaptiveGrid = (
containerWidth: number,
containerHeight: number,
): AdaptiveTileGrid | undefined => {
if (tileCount <= 0 || containerWidth <= 0 || containerHeight <= 0) return undefined
if (tileCount <= 0 || containerWidth <= 0 || containerHeight <= 0) {
return undefined
}
const minAspect = containerWidth / containerHeight >= 4 / 3 ? 1 : TILE_ASPECT_PORTRAIT
@ -186,7 +192,9 @@ export const computeAdaptiveGrid = (
containerHeight,
minAspect,
)
if (!candidate) continue
if (!candidate) {
continue
}
const {tileWidth, tileHeight, aspectRatio} = candidate.rows[0]
const verticalOverflow = Math.max(0, candidate.totalHeight - containerHeight)

View file

@ -71,14 +71,18 @@ export const joinVoiceRoom = async (
whenTimeout(15_000, {message: "Leaving previous call timed out.", signal: settle.signal}),
whenAborted(signal),
]).catch(e => {
if (e instanceof AbortError) throw e
if (e instanceof AbortError) {
throw e
}
})
// leaveVoiceRoom flips callState to Disconnected; re-assert Joining.
callState.set(CallState.Joining)
}
if (signal.aborted) throw new AbortError()
if (signal.aborted) {
throw new AbortError()
}
const {server_url, participant_token} = await Promise.race([
fetchLivekitToken(url, h, signal),
@ -89,7 +93,9 @@ export const joinVoiceRoom = async (
whenAborted(signal),
])
if (signal.aborted) throw new AbortError()
if (signal.aborted) {
throw new AbortError()
}
const liveKitRoom = new LiveKitRoom({adaptiveStream: true, dynacast: true})
activeRoom = liveKitRoom
@ -157,7 +163,9 @@ export const joinVoiceRoom = async (
clearReconnectSchedule()
playJoinSound()
} catch (e) {
if (isActive()) callState.set(CallState.Disconnected)
if (isActive()) {
callState.set(CallState.Disconnected)
}
if (e instanceof AbortError) {
clearReconnectSchedule()
return
@ -165,14 +173,18 @@ export const joinVoiceRoom = async (
throw e
} finally {
settle.abort()
if (isActive()) joinAbortController = undefined
if (isActive()) {
joinAbortController = undefined
}
}
}
export const leaveVoiceRoom = async () => {
clearReconnectSchedule()
const session = get(currentCallSession)
if (!session) return
if (!session) {
return
}
const audio = new Audio("/leave-voice-room.mp3")
audio.play().catch(() => {})
@ -220,7 +232,9 @@ export const cancelJoinVoiceRoom = () => {
export const toggleMute = async () => {
const session = get(currentCallSession)
if (!session) return
if (!session) {
return
}
callMicMuted.update(not)
@ -238,7 +252,9 @@ export const toggleMute = async () => {
export const toggleCamera = async () => {
const session = get(currentCallSession)
if (!session) return
if (!session) {
return
}
const cameraOn = !session.cameraOn
try {
@ -254,7 +270,9 @@ export const toggleCamera = async () => {
export const toggleScreenShare = async () => {
const session = get(currentCallSession)
if (!session) return
if (!session) {
return
}
const screenShareOn = !session.screenShareOn
try {
@ -273,7 +291,9 @@ export const switchCallActiveDevice = async (
targetDeviceId: string,
): Promise<void> => {
const session = get(currentCallSession)
if (!session) return
if (!session) {
return
}
const id = targetDeviceId === "" ? LIVEKIT_DEFAULT_DEVICE_ID : targetDeviceId
try {
await session.livekit.switchActiveDevice(kind, id)
@ -321,11 +341,15 @@ currentCallSession.subscribe(session => {
if (session) {
hadCallSession = true
audioResumeListener ??= App.addListener("appStateChange", ({isActive}) => {
if (isActive) void reacquireMicrophoneIfNeeded()
if (isActive) {
void reacquireMicrophoneIfNeeded()
}
})
return
}
if (!hadCallSession) return
if (!hadCallSession) {
return
}
hadCallSession = false
void audioResumeListener?.then(l => l.remove())
audioResumeListener = undefined
@ -334,7 +358,9 @@ currentCallSession.subscribe(session => {
})
const teardownRoom = (livekit: LiveKitRoom) => {
if (activeRoom === livekit) activeRoom = undefined
if (activeRoom === livekit) {
activeRoom = undefined
}
// Dropping the listeners keeps TrackUnsubscribed from removing the hidden
// audio elements onTrackSubscribed appended, so detach them here instead.
@ -361,7 +387,9 @@ const syncParticipantMedia = (participant: Participant) => {
const state = participantMediaFrom(participant)
participantMediaState.update(m => {
const prev = m.get(participant.identity)
if (prev?.muted === state.muted && prev?.cameraOn === state.cameraOn) return m
if (prev?.muted === state.muted && prev?.cameraOn === state.cameraOn) {
return m
}
const next = new Map(m)
next.set(participant.identity, state)
return next
@ -370,7 +398,9 @@ const syncParticipantMedia = (participant: Participant) => {
// LiveKit does not emit ParticipantConnected/Disconnected during reconnect.
const resyncAfterReconnect = (livekit: LiveKitRoom) => {
if (livekit !== activeRoom) return
if (livekit !== activeRoom) {
return
}
const next = new Map<string, ParticipantMediaState>()
for (const p of [livekit.localParticipant, ...livekit.remoteParticipants.values()]) {
@ -379,7 +409,9 @@ const resyncAfterReconnect = (livekit: LiveKitRoom) => {
participantMediaState.set(next)
const session = get(currentCallSession)
if (!session) return
if (!session) {
return
}
const {localParticipant} = livekit
callMicMuted.set(!localParticipant.isMicrophoneEnabled)
@ -398,7 +430,9 @@ const fetchLivekitToken = async (
): Promise<{server_url: string; participant_token: string}> => {
const endpoint = getLivekitEndpoint(url, roomId)
if (signal?.aborted) throw new DOMException("Aborted", "AbortError")
if (signal?.aborted) {
throw new DOMException("Aborted", "AbortError")
}
const template = await makeHttpAuth(endpoint, "GET")
const signedEvent = await user.get().signer.sign(template)
@ -443,7 +477,9 @@ const setUpMicrophone = async (
} catch (e) {
// Timeout or microphone rejection: join muted, the call is still usable. A
// genuine abort is surfaced to the caller so it can tear down the room.
if (e instanceof AbortError) throw e
if (e instanceof AbortError) {
throw e
}
if (!(e instanceof TimeoutError)) {
pushToast({theme: "error", message: "Could not access microphone"})
}
@ -453,12 +489,16 @@ const setUpMicrophone = async (
const reacquireMicrophoneIfNeeded = async () => {
const session = get(currentCallSession)
if (!session || get(callMicMuted)) return
if (!session || get(callMicMuted)) {
return
}
const track = session.livekit.localParticipant.getTrackPublication(
Track.Source.Microphone,
)?.audioTrack
if (!track || track.isMuted || track.isUserProvided) return
if (!track || track.isMuted || track.isUserProvided) {
return
}
// Mirrors LiveKit's own (mobile-only, video-track-only) reacquisition
// check: a capture device that died silently still reports readyState
@ -468,7 +508,9 @@ const reacquireMicrophoneIfNeeded = async () => {
const {mediaStreamTrack} = track
const needsReacquisition =
mediaStreamTrack.readyState !== "live" || mediaStreamTrack.muted || !mediaStreamTrack.enabled
if (!needsReacquisition) return
if (!needsReacquisition) {
return
}
try {
await track.restartTrack()
@ -488,7 +530,9 @@ const clearReconnectSchedule = () => {
const attemptReconnect = async () => {
const target = get(callTargetRoom)
if (!target) return
if (!target) {
return
}
try {
await joinVoiceRoom(target.url, target.h, reconnectMicMuted, reconnectMicDeviceId)
@ -503,8 +547,12 @@ const attemptReconnect = async () => {
}
const scheduleReconnect = () => {
if (reconnectTimeout !== undefined) return
if (!get(callTargetRoom)) return
if (reconnectTimeout !== undefined) {
return
}
if (!get(callTargetRoom)) {
return
}
if (reconnectAttempt >= RECONNECT_DELAYS.length) {
pushToast({theme: "error", message: "Voice connection lost."})
return
@ -519,13 +567,17 @@ const scheduleReconnect = () => {
}
const makeOnRoomReconnected = (livekit: LiveKitRoom) => () => {
if (livekit !== activeRoom) return
if (livekit !== activeRoom) {
return
}
resyncAfterReconnect(livekit)
}
const makeOnRoomDisconnected = (livekit: LiveKitRoom) => (reason?: DisconnectReason) => {
// Ignore disconnects from rooms that are no longer the active session.
if (livekit !== activeRoom) return
if (livekit !== activeRoom) {
return
}
// Livekit unsubscribes remote tracks before emitting Disconnected, so
// onTrackUnsubscribed has already removed their audio elements by now.
@ -599,10 +651,16 @@ const onLocalTrackUnpublished = (
publication: LocalTrackPublication,
participant: LocalParticipant,
) => {
if (publication.source !== Track.Source.ScreenShare) return
if (publication.source !== Track.Source.ScreenShare) {
return
}
const session = get(currentCallSession)
if (!session || participant.identity !== session.livekit.localParticipant.identity) return
if (!session.screenShareOn) return
if (!session || participant.identity !== session.livekit.localParticipant.identity) {
return
}
if (!session.screenShareOn) {
return
}
currentCallSession.set({...session, screenShareOn: false})
}

View file

@ -62,7 +62,9 @@ export const matchesTopic = (event: TrustedEvent, topic: string) =>
export const matchesQuery = (event: TrustedEvent, query: string) => {
const q = query.trim().toLowerCase()
if (!q) return true
if (!q) {
return true
}
const classified = reader(Classified)(event)
const haystack = [classified.title(), classified.summary(), event.content]

View file

@ -60,7 +60,9 @@
// offsetTop is relative to the nearest positioned ancestor, which the scroll container itself
// isn't — comparing bounding rects instead keeps this correct regardless of that
const recenter = (target: HTMLElement) => {
if (!element) return
if (!element) {
return
}
const elementRect = element.getBoundingClientRect()
const targetRect = target.getBoundingClientRect()
@ -70,7 +72,9 @@
}
$effect(() => {
if (!element || !contentEl) return
if (!element || !contentEl) {
return
}
const unpin = () => {
pinnedTarget = undefined
@ -82,7 +86,9 @@
// every one of those (unlike a MutationObserver, which misses an image's own load-driven
// reflow) and keeps the pin centered until the visitor scrolls themselves.
const observer = new ResizeObserver(() => {
if (pinnedTarget) recenter(pinnedTarget)
if (pinnedTarget) {
recenter(pinnedTarget)
}
})
observer.observe(contentEl)

View file

@ -68,7 +68,9 @@
const selectFiles = () => editor.then(ed => ed.chain().selectFiles().run())
const submit = async () => {
if ($uploading || loading) return
if ($uploading || loading) {
return
}
if (!title) {
return pushToast({

View file

@ -27,7 +27,9 @@
const {url, event, rsvps, onShowPeople}: Props = $props()
const setStatus = async (status: RsvpStatus) => {
if (loading) return
if (loading) {
return
}
loading = true

View file

@ -37,7 +37,9 @@
const spaceName = $derived($callTargetRoom ? displayRelayUrl($callTargetRoom.url) : "")
const goToRoom = () => {
if (!$callTargetRoom) return
if (!$callTargetRoom) {
return
}
void navigate(makeRoomPath($callTargetRoom.url, $callTargetRoom.h))
}

View file

@ -82,13 +82,17 @@
}
const submit = async () => {
if ($uploading || disabled) return
if ($uploading || disabled) {
return
}
const ed = await editor
const content = ed.getText({blockSeparator: "\n"}).trim()
const tags = ed.storage.nostr.getEditorTags()
if (!content) return
if (!content) {
return
}
try {
await onSubmit({content, tags})

View file

@ -26,8 +26,12 @@
const activeArg = $derived(draft?.args[draft.activeIndex])
const choices = $derived.by(() => {
if (activeArg?.type === "enum") return activeArg.choices
if (activeArg?.type === "bool") return ["true", "false"]
if (activeArg?.type === "enum") {
return activeArg.choices
}
if (activeArg?.type === "bool") {
return ["true", "false"]
}
return []
})

View file

@ -38,7 +38,9 @@
const selectFiles = () => editor.then(ed => ed.commands.selectFiles())
const submit = async () => {
if ($uploading || loading) return
if ($uploading || loading) {
return
}
const ed = await editor
const content = ed.getText({blockSeparator: "\n"}).trim()

View file

@ -82,7 +82,9 @@
const isBlock = (i: number) => {
const parsed = fullContent[i]
if (!parsed) return false
if (!parsed) {
return false
}
if (isLink(parsed) && $userSettingsValues.show_media && isStartAndEnd(i)) {
return true
@ -98,8 +100,12 @@
const isBoundary = (i: number) => {
const parsed = fullContent[i]
if (!parsed || isNewline(parsed)) return true
if (isText(parsed)) return Boolean(parsed.value.match(/^\s+$/))
if (!parsed || isNewline(parsed)) {
return true
}
if (isText(parsed)) {
return Boolean(parsed.value.match(/^\s+$/))
}
return false
}

View file

@ -41,7 +41,9 @@
const url = value.url.toString()
const isRoomOrRelay = isRoomId(url) || isRelayUrl(url)
const [href, external] = call(() => {
if (url.startsWith(PLATFORM_URL)) return [url.replace(PLATFORM_URL, ""), false]
if (url.startsWith(PLATFORM_URL)) {
return [url.replace(PLATFORM_URL, ""), false]
}
return [url, true]
})

View file

@ -40,9 +40,15 @@
})
const [href, external] = call(() => {
if (roomReference) return [makeRoomPath(roomReference.url, roomReference.h), false]
if (relayReference) return [makeSpacePath(relayReference), false]
if (url.startsWith(PLATFORM_URL)) return [url.replace(PLATFORM_URL, ""), false]
if (roomReference) {
return [makeRoomPath(roomReference.url, roomReference.h), false]
}
if (relayReference) {
return [makeSpacePath(relayReference), false]
}
if (url.startsWith(PLATFORM_URL)) {
return [url.replace(PLATFORM_URL, ""), false]
}
return [url, true]
})

View file

@ -79,8 +79,12 @@
const pubkeyFromEntity = (entity: string) => {
const {type, data} = nip19.decode(entity)
if (type === "npub") return data
if (type === "nprofile") return data.pubkey
if (type === "npub") {
return data
}
if (type === "nprofile") {
return data.pubkey
}
}
// Everyone the content mentions, so their names are asked for and awaited rather than read

View file

@ -48,8 +48,12 @@
const isBoundary = (i: number) => {
const parsed = fullContent[i]
if (!parsed || isNewline(parsed)) return true
if (isText(parsed)) return Boolean(parsed.value.match(/^\s+$/))
if (!parsed || isNewline(parsed)) {
return true
}
if (isText(parsed)) {
return Boolean(parsed.value.match(/^\s+$/))
}
return false
}

View file

@ -42,7 +42,9 @@
const selectFiles = () => editor.then(ed => ed.commands.selectFiles())
const submit = async () => {
if ($uploading) return
if ($uploading) {
return
}
const ed = await editor
let content = ed.getText({blockSeparator: "\n"}).trim()

View file

@ -55,7 +55,9 @@
const selectFiles = () => editor.then(ed => ed.commands.selectFiles())
const submit = async () => {
if ($uploading || loading) return
if ($uploading || loading) {
return
}
if (!title) {
return pushToast({

View file

@ -55,7 +55,9 @@
const {loading, bunker} = controller
const onSubmit = async () => {
if ($loading) return
if ($loading) {
return
}
try {
const {signerPubkey, connectSecret, relays} = Nip46Broker.parseBunkerUrl($bunker)

View file

@ -17,7 +17,9 @@
const t = track
level = 0
if (!t || error) return
if (!t || error) {
return
}
let frame: number | undefined
const context = new AudioContext()
@ -50,14 +52,18 @@
const NOISE_FLOOR = 0.03
const gated = rms < NOISE_FLOOR ? 0 : (rms - NOISE_FLOOR) / (1 - NOISE_FLOOR)
smoothed += (Math.min(1, gated * 4) - smoothed) * 0.5
if (smoothed < 0.005) smoothed = 0
if (smoothed < 0.005) {
smoothed = 0
}
level = smoothed
frame = requestAnimationFrame(tick)
}
tick()
return () => {
if (frame !== undefined) cancelAnimationFrame(frame)
if (frame !== undefined) {
cancelAnimationFrame(frame)
}
context.close().catch(() => {})
}
})

View file

@ -17,7 +17,9 @@
}
onMount(() => {
if (Capacitor.isNativePlatform()) return
if (Capacitor.isNativePlatform()) {
return
}
audioElement.load()

View file

@ -103,7 +103,9 @@
}
const submit = async () => {
if (loading) return
if (loading) {
return
}
if (!title.trim()) {
return pushToast({theme: "error", message: "Please provide a title for your poll."})

View file

@ -55,7 +55,9 @@
const openChat = () => goToChat([target])
const toggleFollow = async () => {
if (isSelf) return
if (isSelf) {
return
}
if (isFollowing) {
await $followLists.unfollow(target).then(publish)
@ -72,7 +74,9 @@
input.value = ""
if (!file || !$profile) return
if (!file || !$profile) {
return
}
bannerLoading = true

View file

@ -27,7 +27,9 @@
const copyCode = () => clip(code)
const shareProfile = async () => {
if (!canShare) return
if (!canShare) {
return
}
try {
await Share.share({url: code})

View file

@ -9,7 +9,9 @@
let progress = $state(0)
$effect(() => {
if (!element) return
if (!element) {
return
}
const target = element

View file

@ -62,10 +62,18 @@
$relaySearch
.searchValues(term)
.filter((url: string) => {
if (matchRelay?.(url) === false) return false
if ($relays.includes(url)) return false
if (isIPAddress(url)) return false
if ($blockedRelayUrls.includes(url)) return false
if (matchRelay?.(url) === false) {
return false
}
if ($relays.includes(url)) {
return false
}
if (isIPAddress(url)) {
return false
}
if ($blockedRelayUrls.includes(url)) {
return false
}
return true
})

View file

@ -100,7 +100,9 @@
const hidePopover = () => tippy?.hide()
const submit = async () => {
if ($uploading) return
if ($uploading) {
return
}
const ed = await editor
const content = ed.getText({blockSeparator: "\n"}).trim()

View file

@ -36,7 +36,9 @@
const copyInvite = () => clip(invite)
const shareInvite = async () => {
if (!canShare) return
if (!canShare) {
return
}
try {
await Share.share({url: invite})

View file

@ -100,7 +100,9 @@
const togglePin = async () => {
onClick()
if (!h) return
if (!h) {
return
}
// The optimistic write flips isPinned while the publish is in flight, so what this did is read
// before it goes out rather than after.

View file

@ -103,7 +103,9 @@
}
const togglePin = async () => {
if (!h) return
if (!h) {
return
}
history.back()

View file

@ -61,26 +61,34 @@
}
const prev = () => {
if (total === 0) return
if (total === 0) {
return
}
currentIndex = (currentIndex - 1 + total) % total
}
const next = () => {
if (total === 0) return
if (total === 0) {
return
}
currentIndex = (currentIndex + 1) % total
}
const jumpToMessage = () => {
if (!currentEvent) return
if (!currentEvent) {
return
}
collapse()
goToEvent(currentEvent)
}
const onDocumentPointerDown = (event: PointerEvent) => {
if (!expanded || !root) return
if (!expanded || !root) {
return
}
if (!root.contains(event.target as Node)) {
collapse()
@ -98,7 +106,9 @@
})
$effect(() => {
if (!expanded) return
if (!expanded) {
return
}
document.addEventListener("pointerdown", onDocumentPointerDown)

View file

@ -31,7 +31,9 @@
return
}
if (toastId || timeout) return
if (toastId || timeout) {
return
}
// deriveSocketStatus is throttled, so a socket that opens immediately still reports
// "Connecting" for the length of the throttle. Wait that out instead of flashing.

View file

@ -38,7 +38,9 @@
const copyInvite = () => clip(invite)
const shareInvite = async () => {
if (!canShare) return
if (!canShare) {
return
}
try {
await Share.share({url: invite})

View file

@ -49,7 +49,9 @@
const selectFiles = () => editor.then(ed => ed.commands.selectFiles())
const submit = async () => {
if ($uploading || loading) return
if ($uploading || loading) {
return
}
if (!title) {
return pushToast({

View file

@ -21,7 +21,9 @@
})
$effect(() => {
if (!containerEl) return
if (!containerEl) {
return
}
containerEl.addEventListener("touchmove", onTouchMove, {passive: false})
return () => containerEl?.removeEventListener("touchmove", onTouchMove)
})

View file

@ -127,13 +127,17 @@
const primaryTile = $derived.by(() => {
const k = $videoPrimaryTileKey
if (k === undefined) return undefined
if (k === undefined) {
return undefined
}
return videoTiles.find(t => tileKey(t) === k)
})
const secondaryTiles = $derived.by(() => {
const p = primaryTile
if (p === undefined) return videoTiles
if (p === undefined) {
return videoTiles
}
const pk = tileKey(p)
return videoTiles.filter(t => tileKey(t) !== pk)
})
@ -150,7 +154,9 @@
$effect(() => {
const k = $videoPrimaryTileKey
if (k === undefined) return
if (k === undefined) {
return
}
if (!videoTiles.some(t => tileKey(t) === k)) {
videoPrimaryTileKey.set(undefined)
}

View file

@ -16,7 +16,9 @@
$effect(() => {
const element = videoElement
const activeTrack = track
if (!element) return
if (!element) {
return
}
activeTrack.attach(element)
return () => {
activeTrack.detach(element)

View file

@ -35,7 +35,9 @@
?.mediaStreamTrack
const loadDevices = async () => {
if (!navigator.mediaDevices?.enumerateDevices) return
if (!navigator.mediaDevices?.enumerateDevices) {
return
}
try {
const devices = await navigator.mediaDevices.enumerateDevices()
audioInputs = devices.filter(spec({kind: "audioinput"}))
@ -85,7 +87,9 @@
// place with a new MediaStreamTrack rather than emitting a publish event,
// so the meter needs an explicit re-read here.
const session = $currentCallSession
if (session) micTrack = readMicTrack(session)
if (session) {
micTrack = readMicTrack(session)
}
}
const onOutputChange = () => {

View file

@ -48,7 +48,9 @@
)
const handleClick = async (e: MouseEvent) => {
if (isActive) return
if (isActive) {
return
}
if (isJoining) {
e.preventDefault()
@ -67,7 +69,9 @@
$effect(() => {
for (const p of $participants) {
if (p.pubkey) $profiles.load(p.pubkey, [url])
if (p.pubkey) {
$profiles.load(p.pubkey, [url])
}
}
})
</script>

View file

@ -38,7 +38,9 @@
let micError = $state(false)
const loadDevices = async () => {
if (!navigator.mediaDevices?.enumerateDevices) return
if (!navigator.mediaDevices?.enumerateDevices) {
return
}
try {
const devices = await navigator.mediaDevices.enumerateDevices()
audioInputs = devices.filter(spec({kind: "audioinput"}))
@ -75,7 +77,9 @@
stopMicPreview()
micError = false
if (withoutMic || !navigator.mediaDevices?.getUserMedia) return
if (withoutMic || !navigator.mediaDevices?.getUserMedia) {
return
}
let cancelled = false
const deviceId = selectedDeviceId
@ -92,7 +96,9 @@
previewStream = stream
previewTrack = stream.getAudioTracks()[0]
} catch {
if (!cancelled) micError = true
if (!cancelled) {
micError = true
}
}
})()
@ -108,12 +114,16 @@
}
const handleJoinError = (e: unknown) => {
if (e instanceof AbortError) return
if (e instanceof AbortError) {
return
}
console.error("Failed to join voice room", e)
let message = "Failed to join voice room"
if (e instanceof TimeoutError)
if (e instanceof TimeoutError) {
message = "Connection timed out. Please check your network and try again."
else if (e instanceof Error) message = e.message
} else if (e instanceof Error) {
message = e.message
}
pushToast({theme: "error", message})
}

View file

@ -48,7 +48,9 @@
const updateMethod = () => openSetup(tenant?.nwc_error ? "nwc" : "card")
const payOpenInvoice = () => {
if (openInvoice) openPayInvoice(openInvoice)
if (openInvoice) {
openPayInvoice(openInvoice)
}
}
const dismissAutopay = () => {
@ -60,7 +62,9 @@
let handledInvoiceId: string | undefined
$effect(() => {
const id = $page.url.searchParams.get("invoice") ?? undefined
if (!id || id === handledInvoiceId) return
if (!id || id === handledInvoiceId) {
return
}
handledInvoiceId = id

View file

@ -14,9 +14,15 @@
// The backend models the invoice lifecycle as timestamps, not a status field.
const status = $derived.by(() => {
if (isDraft) return "draft"
if (invoice.paid_at) return "paid"
if (invoice.voided_at) return "void"
if (isDraft) {
return "draft"
}
if (invoice.paid_at) {
return "paid"
}
if (invoice.voided_at) {
return "void"
}
return "open"
})
const isOpen = $derived(status === "open")

View file

@ -77,7 +77,9 @@
}
const markPaid = () => {
if (paid) return
if (paid) {
return
}
paid = true
stopPolling()
@ -121,10 +123,14 @@
// Silent poll until paid or the modal closes.
const pollOnce = async () => {
if (destroyed || paid) return
if (destroyed || paid) {
return
}
try {
if (await reconcile()) return
if (await reconcile()) {
return
}
} catch {
// keep polling
}
@ -135,7 +141,9 @@
}
const checkPayment = async () => {
if (checking) return
if (checking) {
return
}
checking = true
@ -161,7 +169,9 @@
}
const payWithCard = async () => {
if (redirecting) return
if (redirecting) {
return
}
redirecting = true

View file

@ -56,7 +56,9 @@
// The tenant's autopay wallet (a backend NWC, write-only), distinct from the
// app's own wallet.
const persistNwc = async (url?: string) => {
if (!url || saving) return
if (!url || saving) {
return
}
saving = true
@ -79,7 +81,9 @@
const useSpendingWallet = () => persistNwc(spendingWalletUrl)
const disconnectNwc = async () => {
if (saving) return
if (saving) {
return
}
saving = true

View file

@ -49,7 +49,9 @@
}
const save = async () => {
if (!canSave) return
if (!canSave) {
return
}
saving = true

View file

@ -100,7 +100,11 @@ export const displayContentCount = (kind: number, count: number) => {
export const DM_KINDS = [DIRECT_MESSAGE, DIRECT_MESSAGE_FILE]
export const displayReaction = (content: string) => {
if (!content || content === "+") return "❤️"
if (content === "-") return "👎"
if (!content || content === "+") {
return "❤️"
}
if (content === "-") {
return "👎"
}
return content
}

View file

@ -41,8 +41,12 @@ const mergeSorted = <T>(left: T[], right: T[], compare: (a: T, b: T) => number)
merged.push(compare(left[i], right[j]) <= 0 ? left[i++] : right[j++])
}
while (i < left.length) merged.push(left[i++])
while (j < right.length) merged.push(right[j++])
while (i < left.length) {
merged.push(left[i++])
}
while (j < right.length) {
merged.push(right[j++])
}
return merged
}
@ -119,7 +123,9 @@ export const makeFeedContext = ({
if (deletedChecks.size > 0) {
if (removed.size > 0) {
for (const checks of deletedChecks.values()) {
for (const check of checks) check()
for (const check of checks) {
check()
}
}
} else {
for (const event of added) {
@ -403,7 +409,9 @@ const makeFeedLoader = (load: () => Promise<FeedSpan>) => {
let running = false
const run = async () => {
if (running || get(state).status === "exhausted") return
if (running || get(state).status === "exhausted") {
return
}
running = true
@ -425,7 +433,9 @@ const makeFeedLoader = (load: () => Promise<FeedSpan>) => {
break
}
if (found > 0) break
if (found > 0) {
break
}
}
} finally {
running = false
@ -608,7 +618,9 @@ export const makeFeed = ({
// The window only moves once the relays have answered: a request the socket dropped looks
// exactly like an empty span, and walking past it would leave a hole nothing goes back for.
const loadOlder = async (): Promise<FeedSpan> => {
if (oldest < now() - int(2, YEAR)) return {found: 0, complete: true, exhausted: true}
if (oldest < now() - int(2, YEAR)) {
return {found: 0, complete: true, exhausted: true}
}
const until = oldest
const since = until - olderInterval
@ -641,7 +653,9 @@ export const makeFeed = ({
// A limit is answered with the newest events matching it, which reaches away from an anchor in
// the past rather than toward it, so this direction walks spans as it always has
const loadNewer = async (): Promise<FeedSpan> => {
if (newest >= now()) return {found: 0, complete: true, exhausted: true}
if (newest >= now()) {
return {found: 0, complete: true, exhausted: true}
}
const since = newest
const until = Math.min(now(), since + newerInterval)
@ -714,7 +728,9 @@ export const makeCalendarFeed = ({
const insertEvents = (newEvents: TrustedEvent[]) => {
const valid = newEvents.filter(e => !isNaN(getStart(e)) && !isNaN(getEnd(e)) && !seen.has(e.id))
if (valid.length === 0) return
if (valid.length === 0) {
return
}
for (const event of valid) {
seen.add(event.id)
@ -771,7 +787,9 @@ export const makeCalendarFeed = ({
}
const loadOlder = async (): Promise<FeedSpan> => {
if (oldest < now() - int(2, YEAR)) return {found: 0, complete: true, exhausted: true}
if (oldest < now() - int(2, YEAR)) {
return {found: 0, complete: true, exhausted: true}
}
const until = oldest
const since = until - interval
@ -785,7 +803,9 @@ export const makeCalendarFeed = ({
}
const loadNewer = async (): Promise<FeedSpan> => {
if (newest > now() + int(2, YEAR)) return {found: 0, complete: true, exhausted: true}
if (newest > now() + int(2, YEAR)) {
return {found: 0, complete: true, exhausted: true}
}
const since = newest
const until = since + interval

View file

@ -207,7 +207,9 @@ const getAuthHeader = (): Promise<string> => {
// A declined signature shouldn't be cached, or the user can't retry.
header.catch(() => {
if (cachedAuth?.header === header) cachedAuth = undefined
if (cachedAuth?.header === header) {
cachedAuth = undefined
}
})
cachedAuth = {pubkey, expiresAt: Date.now() + AUTH_TTL, header}
@ -232,7 +234,9 @@ const hostingRequest = async (method: string, path: string, init: RequestInit =
const hostingJson = async <T>(response: Response): Promise<T> => {
const payload: Maybe<HostingResponse<T>> = await response.json().catch(() => undefined)
if (response.ok && payload) return payload.data
if (response.ok && payload) {
return payload.data
}
throw new HostingError(payload?.error || `Request failed (${response.status})`, response.status)
}
@ -299,7 +303,9 @@ export const listRelayActivity = (id: string) =>
export const exportRelayData = async (id: string) => {
const response = await hostingRequest("GET", `/relays/${id}/export`)
if (response.ok) return response.text()
if (response.ok) {
return response.text()
}
// Only the failure path is a json envelope; success is the dump itself.
return hostingJson<string>(response)
@ -336,8 +342,12 @@ export const reconcileInvoice = (invoiceId: string) =>
// API utils
export const flagToBool = (value: number | undefined, fallback: boolean): boolean => {
if (value === 0) return false
if (value === 1) return true
if (value === 0) {
return false
}
if (value === 1) {
return true
}
return fallback
}
@ -391,7 +401,9 @@ export const ensureSessionTenant = async () => {
const promise = createTenant(hostingReturnUrl())
promise.catch(() => {
if (tenantPromise?.promise === promise) tenantPromise = undefined
if (tenantPromise?.promise === promise) {
tenantPromise = undefined
}
})
tenantPromise = {pubkey: $pubkey, promise}
@ -423,7 +435,9 @@ export const deriveRelayActivity = (id: Readable<Maybe<string>>) =>
derived<Readable<Maybe<string>>, RelayActivityState>(
id,
($id, set) => {
if (!$id) return set({loading: false, activity: []})
if (!$id) {
return set({loading: false, activity: []})
}
set({loading: true, activity: []})

View file

@ -4,7 +4,9 @@ import {Keyboard} from "@capacitor/keyboard"
import {noop} from "@welshman/lib"
export const syncKeyboard = () => {
if (!Capacitor.isPluginAvailable("Keyboard")) return noop
if (!Capacitor.isPluginAvailable("Keyboard")) {
return noop
}
const showListener = Keyboard.addListener("keyboardWillShow", () => {
document.body.classList.add("keyboard-open")
@ -17,7 +19,9 @@ export const syncKeyboard = () => {
// On Android, system-dismissing the IME during pause doesn't fire keyboardWillHide,
// so on resume we force a hide to re-sync native insets and clear our CSS state.
const resumeListener = App.addListener("appStateChange", ({isActive}) => {
if (isActive) Keyboard.hide()
if (isActive) {
Keyboard.hide()
}
})
return () => {

View file

@ -53,10 +53,14 @@ export const payInvoice = async (invoice: string, msats?: number) => {
if (isNWCWallet($wallet)) {
const params: {invoice: string; amount?: number} = {invoice}
if (msats) params.amount = msats
if (msats) {
params.amount = msats
}
return getNwcClient().payInvoice(params)
} else if (isWebLNWallet($wallet)) {
if (msats) throw new Error("Unable to pay zero invoices with webln")
if (msats) {
throw new Error("Unable to pay zero invoices with webln")
}
return getWebLn()
.enable()
.then(() => getWebLn().sendPayment(invoice))

View file

@ -8,8 +8,12 @@ import {logger, wraps, writer} from "@app/core"
import {PLATFORM_LOGEE} from "@app/env"
const formatValue = (value: unknown) => {
if (value instanceof Error) return value.stack || value.message
if (typeof value === "string") return value
if (value instanceof Error) {
return value.stack || value.message
}
if (typeof value === "string") {
return value
}
return tryCatch(() => JSON.stringify(value)) || String(value)
}
@ -72,7 +76,9 @@ export const sendLogs = async () => {
size += line.length
if (size > 32_000) break
if (size > 32_000) {
break
}
lines.unshift(line)
}

View file

@ -68,7 +68,9 @@ export const syncChecked = () => {
checked.update($checked => {
for (const path of paths) {
if (defer && path === defer) continue
if (defer && path === defer) {
continue
}
$checked[path] = now()
}
@ -212,7 +214,9 @@ const latestContentActivity = (url: string, events: TrustedEvent[]) => {
for (const event of events) {
const target = getContentTarget(event)
if (!target) continue
if (!target) {
continue
}
const path = makeContentPath(url, target.kind, target.idOrAddress)
@ -329,8 +333,12 @@ export const notifications = derived(
([$page, $allNotifications, $deferredRoomPath]) =>
new Set(
[...$allNotifications].filter(p => {
if (!$page.url.pathname.startsWith(p)) return true
if ($deferredRoomPath && p === $deferredRoomPath) return true
if (!$page.url.pathname.startsWith(p)) {
return true
}
if ($deferredRoomPath && p === $deferredRoomPath) {
return true
}
return false
}),
),

View file

@ -44,8 +44,12 @@ export const ingestPolicy: AppPolicy = app =>
const event = message[2]
const trusted = getSetting("trusted_relays").includes(socket.url)
if (isDVMKind(event.kind) || isEphemeralKind(event.kind)) return
if (!trusted && !verifyEvent(event)) return
if (isDVMKind(event.kind) || isEphemeralKind(event.kind)) {
return
}
if (!trusted && !verifyEvent(event)) {
return
}
app.tracker.track(event.id, socket.url)
app.repository.publish(event)
@ -62,13 +66,27 @@ export const ingestPolicy: AppPolicy = app =>
export const authPolicy = makeAppPolicyAuth((socket, $app) => {
const $pubkey = app.get().user?.pubkey
if (!$pubkey) return false
if ($app.use(BlockedRelayLists).urls($pubkey).get().includes(socket.url)) return false
if (getSetting("relay_auth") === RelayAuthMode.Aggressive) return true
if ($app.use(RoomLists).urls($pubkey).get().includes(socket.url)) return true
if ($app.use(RelayLists).urls($pubkey).get().includes(socket.url)) return true
if (get($app.use(Thunks).history).some(t => t.options.relays.includes(socket.url))) return true
if ($app.use(MessagingRelayLists).urls($pubkey).get().includes(socket.url)) return true
if (!$pubkey) {
return false
}
if ($app.use(BlockedRelayLists).urls($pubkey).get().includes(socket.url)) {
return false
}
if (getSetting("relay_auth") === RelayAuthMode.Aggressive) {
return true
}
if ($app.use(RoomLists).urls($pubkey).get().includes(socket.url)) {
return true
}
if ($app.use(RelayLists).urls($pubkey).get().includes(socket.url)) {
return true
}
if (get($app.use(Thunks).history).some(t => t.options.relays.includes(socket.url))) {
return true
}
if ($app.use(MessagingRelayLists).urls($pubkey).get().includes(socket.url)) {
return true
}
return false
})
@ -79,8 +97,12 @@ const makeBlockPolicy = ($app: IApp) => (socket: Socket) => {
socket.open = () => {
const $pubkey = $app.user?.pubkey
if (BLOCKED_RELAYS.includes(socket.url)) return
if ($pubkey && $app.use(BlockedRelayLists).urls($pubkey).get().includes(socket.url)) return
if (BLOCKED_RELAYS.includes(socket.url)) {
return
}
if ($pubkey && $app.use(BlockedRelayLists).urls($pubkey).get().includes(socket.url)) {
return
}
previousOpen()
}

View file

@ -38,7 +38,9 @@ export const loadPomadeSessions = async () => {
const result = await client.listSessions()
for (const message of result.messages) {
if (!message.res?.items) continue
if (!message.res?.items) {
continue
}
for (const item of message.res.items) {
const existing = sessionMap.get(item.client)

View file

@ -244,7 +244,9 @@ export const groupRoomMessages = ({
let previousCreatedAt = 0
for (const event of events) {
if (seenIds.has(event.id)) continue
if (seenIds.has(event.id)) {
continue
}
seenIds.add(event.id)

View file

@ -129,8 +129,12 @@ export const deriveIsMuted = (url: string, h: string) =>
export const getNotifyPreference = ({alerts}: SettingsValues, url: string, h?: string) => {
const pref = alerts.find(spec({url}))
if (!pref) return true
if (!h) return pref.notify
if (!pref) {
return true
}
if (!h) {
return pref.notify
}
return pref.notify ? !pref.exceptions.includes(h) : pref.exceptions.includes(h)
}

View file

@ -19,7 +19,9 @@ export const signerRequests = derived(
const requestsById = new Map<string, SignerRequest>()
for (const message of $messages) {
if (message.source !== "signer") continue
if (message.source !== "signer") {
continue
}
if (message.status === "pending") {
requestsById.set(message.id, {

View file

@ -118,19 +118,37 @@ export const isEventMuted = withGetter(
: undefined
return (e: TrustedEvent) => {
if (!$muteList) return false
if ($user.pubkey === e.pubkey) return false
if (mutedPubkeys.has(e.pubkey)) return true
if (mutedEvents.has(e.id)) return true
if (mutedAddresses.has(getAddress(e))) return true
if (getParents(e).some(v => mutedEvents.has(v) || mutedAddresses.has(v))) return true
if (tagValues(topicTags("t"), e.tags).some(t => mutedTopics.has(t))) return true
if (!$muteList) {
return false
}
if ($user.pubkey === e.pubkey) {
return false
}
if (mutedPubkeys.has(e.pubkey)) {
return true
}
if (mutedEvents.has(e.id)) {
return true
}
if (mutedAddresses.has(getAddress(e))) {
return true
}
if (getParents(e).some(v => mutedEvents.has(v) || mutedAddresses.has(v))) {
return true
}
if (tagValues(topicTags("t"), e.tags).some(t => mutedTopics.has(t))) {
return true
}
if (regex) {
const profile = profiles.get().get(e.pubkey)
if (profile?.display().toLowerCase().match(regex)) return true
if (profile?.nip05()?.match(regex)) return true
if (profile?.display().toLowerCase().match(regex)) {
return true
}
if (profile?.nip05()?.match(regex)) {
return true
}
}
return false

View file

@ -56,7 +56,9 @@ export const kv = call(() => {
const get = async <T>(key: string): Promise<T | undefined> => {
const result = await Preferences.get({key})
if (!result.value) return undefined
if (!result.value) {
return undefined
}
try {
return JSON.parse(result.value)
} catch (e) {
@ -91,7 +93,9 @@ export const ss = call(() => {
}
}
if (!value) return undefined
if (!value) {
return undefined
}
try {
return JSON.parse(value)
@ -402,7 +406,9 @@ class Storage {
)
return this.app.use(Relays).onItem((_url, relay) => {
if (relay) enqueue(relay)
if (relay) {
enqueue(relay)
}
})
}
@ -416,7 +422,9 @@ class Storage {
const enqueue = batch(FLUSH_INTERVAL, idleWrite(table.bulkPut))
return this.app.use(RelayStats).onItem((_url, stats) => {
if (stats) enqueue(stats)
if (stats) {
enqueue(stats)
}
})
}
@ -430,7 +438,9 @@ class Storage {
const enqueue = batch(FLUSH_INTERVAL, idleWrite(table.bulkPut))
return this.app.use(Handles).onItem((_nip05, handle) => {
if (handle) enqueue(handle)
if (handle) {
enqueue(handle)
}
})
}
@ -451,7 +461,9 @@ class Storage {
)
return this.app.use(Zappers).onItem((_lnurl, zapper) => {
if (zapper) enqueue(zapper)
if (zapper) {
enqueue(zapper)
}
})
}
@ -465,7 +477,9 @@ class Storage {
const enqueue = batch(FLUSH_INTERVAL, idleWrite(table.bulkPut))
return this.app.use(Plaintext).onItem((key, value) => {
if (value) enqueue({key, value})
if (value) {
enqueue({key, value})
}
})
}

View file

@ -78,7 +78,9 @@ type SyncOpts = {
}
const pullAndListen = ({url, signal, filters}: SyncOpts) => {
if (signal.aborted) return
if (signal.aborted) {
return
}
app
.get()

View file

@ -23,7 +23,9 @@
// When no date is provided the library initializes its time input from the
// epoch (a locale-dependent afternoon); reset it to something friendlier
onMount(() => {
if (!startDate) startDateTime = "12:00"
if (!startDate) {
startDateTime = "12:00"
}
})
const toggle = () => {

View file

@ -24,8 +24,12 @@
// When no date is provided the library initializes its time inputs from the
// epoch (a locale-dependent afternoon); reset them for the create flow
onMount(() => {
if (!startDate) startDateTime = "12:00"
if (!endDate) endDateTime = "13:00"
if (!startDate) {
startDateTime = "12:00"
}
if (!endDate) {
endDateTime = "13:00"
}
})
const toggle = () => {

View file

@ -61,7 +61,9 @@
if (isWebkit && isRemoteSvg(url)) {
fetchSvg(url).then(blobUrl => {
if (!blobUrl) return
if (!blobUrl) {
return
}
if (canceled) {
URL.revokeObjectURL(blobUrl)

View file

@ -18,7 +18,9 @@
const id = randomId()
const getImageUrl = (item: string | File): string => {
if (typeof item === "string") return item
if (typeof item === "string") {
return item
}
return URL.createObjectURL(item)
}

View file

@ -12,7 +12,9 @@ export {preventDefault, stopPropagation} from "svelte/legacy"
export const documentActive = readable(
typeof document === "undefined" ? true : !document.hidden && document.hasFocus(),
set => {
if (typeof document === "undefined") return
if (typeof document === "undefined") {
return
}
const update = () => set(!document.hidden && document.hasFocus())
@ -36,7 +38,9 @@ export const anchorDatepicker = (wrapper: HTMLElement) => {
const anchor = wrapper.querySelector("label")
const container = wrapper.querySelector(".calendars-container") as HTMLElement | null
if (!anchor || !container) return
if (!anchor || !container) {
return
}
const margin = 8
const rect = anchor.getBoundingClientRect()

View file

@ -86,7 +86,9 @@ export class IDB {
getAll = async <T>(table: string): Promise<T[]> => {
const connection = await this.live()
if (!connection) return []
if (!connection) {
return []
}
const tx = connection.transaction(table, "readonly")
const store = tx.objectStore(table)
@ -100,7 +102,9 @@ export class IDB {
bulkPut = async <T>(table: string, data: Iterable<T>) => {
const connection = await this.live()
if (!connection) return
if (!connection) {
return
}
const tx = connection.transaction(table, "readwrite")
const store = tx.objectStore(table)
@ -121,7 +125,9 @@ export class IDB {
bulkDelete = async (table: string, ids: Iterable<string>) => {
const connection = await this.live()
if (!connection) return
if (!connection) {
return
}
const tx = connection.transaction(table, "readwrite")
const store = tx.objectStore(table)

View file

@ -29,7 +29,9 @@ export const nsecEncode = (secret: string) => nip19.nsecEncode(hexToBytes(secret
export const nsecDecode = (nsec: string) => {
const {type, data} = nip19.decode(nsec)
if (type !== "nsec") throw new Error(`Invalid nsec: ${nsec}`)
if (type !== "nsec") {
throw new Error(`Invalid nsec: ${nsec}`)
}
return bytesToHex(data)
}
@ -58,12 +60,17 @@ export class TimeoutError extends Error {
/** Returns a promise that rejects with AbortError when signal aborts. Use with Promise.race. */
export const whenAborted = (signal?: AbortSignal) => {
if (!signal) return new Promise<never>(() => {})
if (!signal) {
return new Promise<never>(() => {})
}
return new Promise<never>((_, reject) => {
const onAborted = () => reject(new AbortError())
if (signal.aborted) onAborted()
else signal.addEventListener("abort", onAborted, {once: true})
if (signal.aborted) {
onAborted()
} else {
signal.addEventListener("abort", onAborted, {once: true})
}
})
}

View file

@ -228,7 +228,9 @@
// Listen for signer errors, report to user via toast
unsubscribers.push(
throttled(10_000, signerRequests).subscribe($requests => {
if ($toast) return
if ($toast) {
return
}
const longCutoff = Date.now() - 30_000
const shortCutoff = Date.now() - 10_000

View file

@ -66,9 +66,15 @@
})
const status = $derived.by(() => {
if (!tenant) return "inactive"
if (tenant.churned_at) return "delinquent"
if (hasPaidSubscription || openInvoice || autopayConfigured(tenant)) return "active"
if (!tenant) {
return "inactive"
}
if (tenant.churned_at) {
return "delinquent"
}
if (hasPaidSubscription || openInvoice || autopayConfigured(tenant)) {
return "active"
}
return "inactive"
})
@ -77,15 +83,23 @@
)
const nwc = $derived.by<PaymentMethodState>(() => {
if (!tenant?.nwc_is_set) return {kind: "not_set_up"}
if (tenant.nwc_error) return {kind: "error", message: tenant.nwc_error}
if (!tenant?.nwc_is_set) {
return {kind: "not_set_up"}
}
if (tenant.nwc_error) {
return {kind: "error", message: tenant.nwc_error}
}
return {kind: "ok"}
})
const card = $derived.by<PaymentMethodState>(() => {
if (!tenant?.stripe_payment_method_id) return {kind: "not_set_up"}
if (!tenant?.stripe_payment_method_id) {
return {kind: "not_set_up"}
}
// Don't surface Stripe's raw decline text to the tenant.
if (tenant.stripe_error) return {kind: "error", message: "Payment failed"}
if (tenant.stripe_error) {
return {kind: "error", message: "Payment failed"}
}
return {kind: "ok"}
})
@ -95,7 +109,9 @@
const refetch = async () => {
const pk = user.get().pubkey
if (!pk) return
if (!pk) {
return
}
const results = await Promise.allSettled([
getTenant(pk),
@ -106,10 +122,18 @@
const [t, inv, rel, draft] = results
if (t.status === "fulfilled") tenant = t.value
if (inv.status === "fulfilled") invoices = sortBy(invoice => -invoice.created_at, inv.value)
if (rel.status === "fulfilled") relays = rel.value
if (draft.status === "fulfilled") draftInvoice = draft.value
if (t.status === "fulfilled") {
tenant = t.value
}
if (inv.status === "fulfilled") {
invoices = sortBy(invoice => -invoice.created_at, inv.value)
}
if (rel.status === "fulfilled") {
relays = rel.value
}
if (draft.status === "fulfilled") {
draftInvoice = draft.value
}
if (results.some(spec({status: "rejected"}))) {
pushToast({theme: "error", message: "Failed to refresh billing data"})
@ -127,7 +151,9 @@
// invoices is left to the backend's dunning poll, which collects all of them.
const reconcile = async () => {
const pk = user.get().pubkey
if (!pk || reconciling) return
if (!pk || reconciling) {
return
}
reconciling = true
error = ""
@ -178,7 +204,9 @@
// Re-run on foreground return (native browser round-trip) to pick up a
// completed checkout or a portal-added card.
const resumeListener = App.addListener("appStateChange", ({isActive}) => {
if (isActive) void reconcile()
if (isActive) {
void reconcile()
}
})
return () => {

View file

@ -57,7 +57,9 @@
return createSearch(options, {
getValue: (relay: Relay) => relay.url,
sortFn: ({score, item}) => {
if (score && score > 0.1) return -score!
if (score && score > 0.1) {
return -score!
}
const wotScore = $spacePubkeysByUrl.get(item.url)?.size || 0

View file

@ -77,7 +77,9 @@
// Watch for relay errors and notify the user
// Direct links skip Discover — prompt to join when relay is not in the user's space list.
$effect(() => {
if (getModal()) return
if (getModal()) {
return
}
if (redirectUrl && redirectUrl !== url && !redirectPrompted.has(url)) {
redirectPrompted.add(url)

View file

@ -90,7 +90,9 @@
}
const submit = async () => {
if ($uploading || uploadingCover || loading) return
if ($uploading || uploadingCover || loading) {
return
}
if (!title) {
return pushToast({

View file

@ -74,9 +74,15 @@
return
}
if (event.key === "ArrowLeft") showPrevious()
if (event.key === "ArrowRight") showNext()
if (event.key === "t") showToday()
if (event.key === "ArrowLeft") {
showPrevious()
}
if (event.key === "ArrowRight") {
showNext()
}
if (event.key === "t") {
showToday()
}
}
const viewClass = (target: CalendarView) =>

View file

@ -92,7 +92,9 @@
const items = $derived.by(() => {
const matches = scoped.filter(event => tab === "all" || getStatus(event) === tab)
if (sort === "new") return sortBy(event => -event.created_at, matches)
if (sort === "new") {
return sortBy(event => -event.created_at, matches)
}
if (sort === "price-asc") {
return sortBy(event => reader(Classified)(event).price()?.amount ?? Infinity, matches)

View file

@ -86,7 +86,9 @@
const visibleMembers = $derived.by(() => {
const t = term.trim().toLowerCase()
if (!t) return $memberList
if (!t) {
return $memberList
}
return $memberList.filter(
({pubkey, roleList}) =>