Re-enable ESLint rules and fix violations

Re-enable 17 ESLint rules that were disabled during the ESLint v9 migration,
and fix all violations across the codebase.

## Rules re-enabled:
- prefer-const (91 auto-fixed)
- no-var (4 auto-fixed)
- no-empty (13 fixed)
- no-irregular-whitespace (7 fixed)
- no-unsafe-optional-chaining (25 fixed)
- no-prototype-builtins (5 fixed)
- @typescript-eslint/no-unused-expressions (6 fixed)
- @typescript-eslint/no-non-null-asserted-optional-chain (3 fixed)
- @typescript-eslint/no-wrapper-object-types (1 fixed)
- @typescript-eslint/no-unsafe-function-type (1 fixed)
- no-async-promise-executor (3 fixed)
- prefer-rest-params (3 fixed)
- no-case-declarations (3 fixed)
- no-useless-escape (2 fixed)
- no-sparse-arrays (1 fixed)
- no-fallthrough (1 fixed)
- no-control-regex (1 disabled - intentional)

## Rules kept disabled (too many violations):
- @typescript-eslint/no-explicit-any (276)
- @typescript-eslint/ban-ts-comment (126)
- @typescript-eslint/no-empty-object-type (150)
- no-empty-pattern (56 - many intentional)
This commit is contained in:
Claude
2026-01-11 10:59:19 +00:00
parent c397920d60
commit fc45a779aa
103 changed files with 231 additions and 209 deletions
+2 -20
View File
@@ -180,34 +180,16 @@ export default tseslint.config(
{prefer: 'type-imports', fixStyle: 'inline-type-imports'}, {prefer: 'type-imports', fixStyle: 'inline-type-imports'},
], ],
'@typescript-eslint/no-require-imports': 'off', '@typescript-eslint/no-require-imports': 'off',
// Maintain previous behavior - these are stricter in typescript-eslint v8 // Keep disabled - too many violations to fix now
'@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-empty-object-type': 'off', '@typescript-eslint/no-empty-object-type': 'off',
'@typescript-eslint/no-unused-expressions': 'off',
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
'@typescript-eslint/no-wrapper-object-types': 'off',
'@typescript-eslint/no-unsafe-function-type': 'off',
// Import rules // Import rules
'import-x/consistent-type-specifier-style': ['warn', 'prefer-inline'], 'import-x/consistent-type-specifier-style': ['warn', 'prefer-inline'],
// Turn off rules that weren't enforced in previous config // Keep disabled - many are intentional empty destructuring patterns
'no-empty-pattern': 'off', 'no-empty-pattern': 'off',
'no-async-promise-executor': 'off',
'no-constant-binary-expression': 'warn',
'prefer-const': 'off',
'no-empty': 'off',
'no-unsafe-optional-chaining': 'off',
'no-prototype-builtins': 'off',
'no-var': 'off',
'prefer-rest-params': 'off',
'no-case-declarations': 'off',
'no-irregular-whitespace': 'off',
'no-useless-escape': 'off',
'no-sparse-arrays': 'off',
'no-fallthrough': 'off',
'no-control-regex': 'off',
}, },
}, },
+2 -5
View File
@@ -111,13 +111,12 @@ export async function prefetchConfig() {
return return
} }
configPrefetchPromise = new Promise(async resolve => { configPrefetchPromise = (async () => {
await cacheHydrationPromise await cacheHydrationPromise
const cached = getConfigFromCache() const cached = getConfigFromCache()
if (cached) { if (cached) {
logger.debug(`prefetchAgeAssuranceConfig: using cache`) logger.debug(`prefetchAgeAssuranceConfig: using cache`)
resolve()
} else { } else {
try { try {
logger.debug(`prefetchAgeAssuranceConfig: resolving...`) logger.debug(`prefetchAgeAssuranceConfig: resolving...`)
@@ -130,11 +129,9 @@ export async function prefetchConfig() {
logger.warn(`prefetchAgeAssuranceConfig: failed`, { logger.warn(`prefetchAgeAssuranceConfig: failed`, {
safeMessage: e.message, safeMessage: e.message,
}) })
} finally {
resolve()
} }
} }
}) })()
} }
export async function refetchConfig() { export async function refetchConfig() {
logger.debug(`refetchConfig: fetching...`) logger.debug(`refetchConfig: fetching...`)
+1
View File
@@ -8,6 +8,7 @@ import {type OtherRequiredData} from '#/ageAssurance/data'
import {IS_DEV, IS_E2E} from '#/env' import {IS_DEV, IS_E2E} from '#/env'
import {type Geolocation} from '#/geolocation' import {type Geolocation} from '#/geolocation'
// eslint-disable-next-line no-constant-binary-expression -- intentional debug toggle
export const enabled = (IS_DEV && false) || IS_E2E export const enabled = (IS_DEV && false) || IS_E2E
export const geolocation: Geolocation | undefined = enabled export const geolocation: Geolocation | undefined = enabled
+5 -1
View File
@@ -81,7 +81,11 @@ export function Outer({
const handleBackgroundPress = React.useCallback( const handleBackgroundPress = React.useCallback(
async (e: GestureResponderEvent) => { async (e: GestureResponderEvent) => {
webOptions?.onBackgroundPress ? webOptions.onBackgroundPress(e) : close() if (webOptions?.onBackgroundPress) {
webOptions.onBackgroundPress(e)
} else {
close()
}
}, },
[webOptions, close], [webOptions, close],
) )
@@ -167,7 +167,7 @@ function DialogInner({guide}: {guide?: Follow10ProgressGuide}) {
const results = hasSearchText const results = hasSearchText
? searchResults?.pages.flatMap(p => p.actors) ? searchResults?.pages.flatMap(p => p.actors)
: suggestions?.actors : suggestions?.actors
let _items: Item[] = [] const _items: Item[] = []
if (isFetchingSuggestions || isFetchingSearchResults) { if (isFetchingSuggestions || isFetchingSearchResults) {
const placeholders: Item[] = Array(10) const placeholders: Item[] = Array(10)
+1 -1
View File
@@ -286,7 +286,7 @@ function Bubble({
left -= left + cw - maxLeft left -= left + cw - maxLeft
} }
let tipLeft = const tipLeft =
targetMeasurements.x - targetMeasurements.x -
left + left +
targetMeasurements.width / 2 - targetMeasurements.width / 2 -
@@ -334,7 +334,7 @@ async function createProfileRecord(
imageUri && imageMime ? uploadBlob(agent, imageUri, imageMime) : undefined imageUri && imageMime ? uploadBlob(agent, imageUri, imageMime) : undefined
await agent.upsertProfile(async existing => { await agent.upsertProfile(async existing => {
let next: Un$Typed<AppBskyActorProfile.Record> = existing ?? {} const next: Un$Typed<AppBskyActorProfile.Record> = existing ?? {}
if (blobPromise) { if (blobPromise) {
const res = await blobPromise const res = await blobPromise
@@ -185,7 +185,9 @@ export function Update(_props: ScreenProps<ScreenID.Update>) {
try { try {
// fire off a confirmation email immediately // fire off a confirmation email immediately
await requestEmailVerification() await requestEmailVerification()
} catch {} } catch {
// no-op
}
} }
} catch (e) { } catch (e) {
logger.error('EmailDialog: update email failed', {safeMessage: e}) logger.error('EmailDialog: update email failed', {safeMessage: e})
+1 -1
View File
@@ -99,7 +99,7 @@ function DialogInner({
} = useRemoveLiveStatusMutation() } = useRemoveLiveStatusMutation()
const {minutesUntilExpiry, expiryDateTime} = useMemo(() => { const {minutesUntilExpiry, expiryDateTime} = useMemo(() => {
tick! void tick // revalidate every minute
const expiry = new Date(status.expiresAt ?? new Date()) const expiry = new Date(status.expiresAt ?? new Date())
return { return {
+1 -1
View File
@@ -52,7 +52,7 @@ function DialogInner({profile}: {profile: bsky.profile.AnyProfileView}) {
const time = useCallback( const time = useCallback(
(offset: number) => { (offset: number) => {
tick! void tick // revalidate every minute
const date = new Date() const date = new Date()
date.setMinutes(date.getMinutes() + offset) date.setMinutes(date.getMinutes() + offset)
@@ -211,8 +211,8 @@ function Inner(props: ReportDialogProps) {
logger.metric( logger.metric(
'reportDialog:success', 'reportDialog:success',
{ {
reason: state.selectedOption?.reason!, reason: state.selectedOption?.reason ?? '',
labeler: state.selectedLabeler?.creator.handle!, labeler: state.selectedLabeler?.creator.handle ?? '',
details: !!state.details, details: !!state.details,
}, },
{statsig: false}, {statsig: false},
@@ -719,7 +719,7 @@ function CategoryCard({
{option.title} {option.title}
</Text> </Text>
<Text <Text
style={[a.text_sm, , a.leading_snug, t.atoms.text_contrast_medium]}> style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
{option.description} {option.description}
</Text> </Text>
</View> </View>
+4 -4
View File
@@ -67,7 +67,7 @@ export async function resolve() {
* THIS PROMISE SHOULD NEVER `reject()`! We want the app to proceed with * THIS PROMISE SHOULD NEVER `reject()`! We want the app to proceed with
* startup, even if geolocation resolution fails. * startup, even if geolocation resolution fails.
*/ */
geolocationServicePromise = new Promise(async resolve => { geolocationServicePromise = (async () => {
let success = false let success = false
function cacheResponseOrThrow(response: Geolocation | undefined) { function cacheResponseOrThrow(response: Geolocation | undefined) {
@@ -111,10 +111,10 @@ export async function resolve() {
}, },
) )
}) })
} finally {
resolve({success})
} }
})
return {success}
})()
} }
} }
+1 -1
View File
@@ -75,7 +75,7 @@ export const USRegionNameToRegionCode: {
export function normalizeDeviceLocation( export function normalizeDeviceLocation(
location: LocationGeocodedAddress, location: LocationGeocodedAddress,
): Geolocation { ): Geolocation {
let {isoCountryCode, region} = location const {isoCountryCode, region} = location
let regionCode: string | undefined = region ?? undefined let regionCode: string | undefined = region ?? undefined
/* /*
+1 -1
View File
@@ -17,7 +17,7 @@ export function useActorStatus(actor?: bsky.profile.AnyProfileView) {
const config = useLiveNowConfig() const config = useLiveNowConfig()
return useMemo(() => { return useMemo(() => {
tick! // revalidate every minute void tick // revalidate every minute
if (shadowed && 'status' in shadowed && shadowed.status) { if (shadowed && 'status' in shadowed && shadowed.status) {
const isValid = validateStatus(shadowed.did, shadowed.status, config) const isValid = validateStatus(shadowed.did, shadowed.status, config)
+1 -1
View File
@@ -208,7 +208,7 @@ export class FeedViewPostsSlice {
getAuthors(): AuthorContext { getAuthors(): AuthorContext {
const feedPost = this._feedPost const feedPost = this._feedPost
let author: AppBskyActorDefs.ProfileViewBasic = feedPost.post.author const author: AppBskyActorDefs.ProfileViewBasic = feedPost.post.author
let parentAuthor: AppBskyActorDefs.ProfileViewBasic | undefined let parentAuthor: AppBskyActorDefs.ProfileViewBasic | undefined
let grandparentAuthor: AppBskyActorDefs.ProfileViewBasic | undefined let grandparentAuthor: AppBskyActorDefs.ProfileViewBasic | undefined
let rootAuthor: AppBskyActorDefs.ProfileViewBasic | undefined let rootAuthor: AppBskyActorDefs.ProfileViewBasic | undefined
+1 -1
View File
@@ -106,7 +106,7 @@ async function loggedOutFetch({
limit: number limit: number
cursor?: string cursor?: string
}) { }) {
let contentLangs = getAppLanguageAsContentLanguage() const contentLangs = getAppLanguageAsContentLanguage()
/** /**
* Copied from our root `Agent` class * Copied from our root `Agent` class
+1 -1
View File
@@ -129,7 +129,7 @@ export class MergeFeedAPI implements FeedAPI {
// assemble a response by sampling from feeds with content // assemble a response by sampling from feeds with content
const posts: AppBskyFeedDefs.FeedViewPost[] = [] const posts: AppBskyFeedDefs.FeedViewPost[] = []
while (posts.length < limit) { while (posts.length < limit) {
let slice = this.sampleItem() const slice = this.sampleItem()
if (slice[0]) { if (slice[0]) {
posts.push(slice[0]) posts.push(slice[0])
} else { } else {
+1 -1
View File
@@ -78,7 +78,7 @@ export async function post(
const writes: $Typed<ComAtprotoRepoApplyWrites.Create>[] = [] const writes: $Typed<ComAtprotoRepoApplyWrites.Create>[] = []
const uris: string[] = [] const uris: string[] = []
let now = new Date() const now = new Date()
let tid: TID | undefined let tid: TID | undefined
for (let i = 0; i < thread.posts.length; i++) { for (let i = 0; i < thread.posts.length; i++) {
+3 -1
View File
@@ -229,5 +229,7 @@ export async function imageToThumb(
if (img) { if (img) {
return await createComposerImage(img) return await createComposerImage(img)
} }
} catch {} } catch {
// no-op
}
} }
+2 -2
View File
@@ -67,7 +67,7 @@ export function isPlainArray(value: unknown) {
} }
// Copied from: https://github.com/jonschlinkert/is-plain-object // Copied from: https://github.com/jonschlinkert/is-plain-object
export function isPlainObject(o: any): o is Object { export function isPlainObject(o: any): o is object {
if (!hasObjectPrototype(o)) { if (!hasObjectPrototype(o)) {
return false return false
} }
@@ -85,7 +85,7 @@ export function isPlainObject(o: any): o is Object {
} }
// If constructor does not have an Object-specific method // If constructor does not have an Object-specific method
if (!prot.hasOwnProperty('isPrototypeOf')) { if (!Object.prototype.hasOwnProperty.call(prot, 'isPrototypeOf')) {
return false return false
} }
+1 -1
View File
@@ -4,7 +4,7 @@ import {getCurrentRoute} from '#/lib/routes/helpers'
export function useNavigationTabState() { export function useNavigationTabState() {
return useNavigationState(state => { return useNavigationState(state => {
let currentRoute = state ? getCurrentRoute(state).name : 'Home' const currentRoute = state ? getCurrentRoute(state).name : 'Home'
return { return {
isAtHome: currentRoute === 'Home', isAtHome: currentRoute === 'Home',
isAtSearch: currentRoute === 'Search', isAtSearch: currentRoute === 'Search',
+1
View File
@@ -8,6 +8,7 @@ import {useCallback, useInsertionEffect, useRef} from 'react'
// //
// Also, you should avoid calling the returned function during rendering // Also, you should avoid calling the returned function during rendering
// since the values captured by it are going to lag behind. // since the values captured by it are going to lag behind.
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type -- generic utility needs Function type
export function useNonReactiveCallback<T extends Function>(fn: T): T { export function useNonReactiveCallback<T extends Function>(fn: T): T {
const ref = useRef(fn) const ref = useRef(fn)
useInsertionEffect(() => { useInsertionEffect(() => {
+1 -1
View File
@@ -18,7 +18,7 @@ export function useTabFocusEffect(
useEffect(() => { useEffect(() => {
// check if inside // check if inside
let v = getTabState(state, tabName) !== TabState.Outside const v = getTabState(state, tabName) !== TabState.Outside
if (v !== isInside) { if (v !== isInside) {
// fire // fire
setIsInside(v) setIsInside(v)
+3 -3
View File
@@ -50,7 +50,7 @@ export async function saveImageToMediaLibrary(_opts: {uri: string}) {
} }
export async function getImageDim(path: string): Promise<Dimensions> { export async function getImageDim(path: string): Promise<Dimensions> {
var img = document.createElement('img') const img = document.createElement('img')
const promise = new Promise((resolve, reject) => { const promise = new Promise((resolve, reject) => {
img.onload = resolve img.onload = resolve
img.onerror = reject img.onerror = reject
@@ -139,8 +139,8 @@ function createResizedImage(
} else if (mode === 'contain') { } else if (mode === 'contain') {
scale = img.width > img.height ? width / img.width : height / img.height scale = img.width > img.height ? width / img.width : height / img.height
} }
let w = img.width * scale const w = img.width * scale
let h = img.height * scale const h = img.height * scale
canvas.width = w canvas.width = w
canvas.height = h canvas.height = h
+1 -1
View File
@@ -39,7 +39,7 @@ export class Router<T extends Record<string, any>> {
function createRoute(pattern: string): Route { function createRoute(pattern: string): Route {
const pathParamNames: Set<string> = new Set() const pathParamNames: Set<string> = new Set()
let matcherReInternal = pattern.replace(/:([\w]+)/g, (_m, name) => { const matcherReInternal = pattern.replace(/:([\w]+)/g, (_m, name) => {
pathParamNames.add(name) pathParamNames.add(name)
return `(?<${name}>[^/]+)` return `(?<${name}>[^/]+)`
}) })
+2 -2
View File
@@ -131,8 +131,8 @@ function toStringRecord<E extends keyof MetricEvents>(
metadata: MetricEvents[E] & FlatJSONRecord, metadata: MetricEvents[E] & FlatJSONRecord,
): Record<string, string> { ): Record<string, string> {
const record: Record<string, string> = {} const record: Record<string, string> = {}
for (let key in metadata) { for (const key in metadata) {
if (metadata.hasOwnProperty(key)) { if (Object.hasOwn(metadata, key)) {
if (typeof metadata[key] === 'string') { if (typeof metadata[key] === 'string') {
record[key] = metadata[key] record[key] = metadata[key]
} else { } else {
+1
View File
@@ -6,6 +6,7 @@ import {type ModerationUI} from '@atproto/api'
// \u2611 = ☑ // \u2611 = ☑
const CHECK_MARKS_RE = /[\u2705\u2713\u2714\u2611]/gu const CHECK_MARKS_RE = /[\u2705\u2713\u2714\u2611]/gu
const CONTROL_CHARS_RE = const CONTROL_CHARS_RE =
// eslint-disable-next-line no-control-regex -- intentionally matching control characters for sanitization
/[\u0000-\u001F\u007F-\u009F\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069]/g /[\u0000-\u001F\u007F-\u009F\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069]/g
const MULTIPLE_SPACES_RE = /[\s][\s\u200B]+/g const MULTIPLE_SPACES_RE = /[\s][\s\u200B]+/g
+14 -10
View File
@@ -426,7 +426,7 @@ export function parseEmbedPlayerFromUrl(
// link shortened flickr path // link shortened flickr path
if (urlp.hostname === 'flic.kr') { if (urlp.hostname === 'flic.kr') {
const b58alph = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ' const b58alph = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'
let [__, type, idBase58Enc] = urlp.pathname.split('/') const [__, type, idBase58Enc] = urlp.pathname.split('/')
let id = 0n let id = 0n
for (const char of idBase58Enc) { for (const char of idBase58Enc) {
const nextIdx = b58alph.indexOf(char) const nextIdx = b58alph.indexOf(char)
@@ -439,7 +439,7 @@ export function parseEmbedPlayerFromUrl(
} }
switch (type) { switch (type) {
case 'go': case 'go': {
const formattedGroupId = `${id}` const formattedGroupId = `${id}`
return { return {
type: 'flickr_album', type: 'flickr_album',
@@ -449,6 +449,7 @@ export function parseEmbedPlayerFromUrl(
-2, -2,
)}@N${formattedGroupId.slice(-2)}`, )}@N${formattedGroupId.slice(-2)}`,
} }
}
case 's': case 's':
return { return {
type: 'flickr_album', type: 'flickr_album',
@@ -537,13 +538,13 @@ export function parseTenorGif(urlp: URL):
return {success: false} return {success: false}
} }
let [__, id, filename] = urlp.pathname.split('/') const [, initialId, initialFilename] = urlp.pathname.split('/')
if (!id || !filename) { if (!initialId || !initialFilename) {
return {success: false} return {success: false}
} }
if (!id.includes('AAAAC')) { if (!initialId.includes('AAAAC')) {
return {success: false} return {success: false}
} }
@@ -559,16 +560,19 @@ export function parseTenorGif(urlp: URL):
width: Number(w), width: Number(w),
} }
let id: string
let filename: string
if (isWeb) { if (isWeb) {
if (isSafari) { if (isSafari) {
id = id.replace('AAAAC', 'AAAP1') id = initialId.replace('AAAAC', 'AAAP1')
filename = filename.replace('.gif', '.mp4') filename = initialFilename.replace('.gif', '.mp4')
} else { } else {
id = id.replace('AAAAC', 'AAAP3') id = initialId.replace('AAAAC', 'AAAP3')
filename = filename.replace('.gif', '.webm') filename = initialFilename.replace('.gif', '.webm')
} }
} else { } else {
id = id.replace('AAAAC', 'AAAAM') id = initialId.replace('AAAAC', 'AAAAM')
filename = initialFilename
} }
return { return {
+1 -1
View File
@@ -7,7 +7,7 @@ export function getMentionAt(
text: string, text: string,
cursorPos: number, cursorPos: number,
): FoundMention | undefined { ): FoundMention | undefined {
let re = /(^|\s)@([a-z0-9.-]*)/gi const re = /(^|\s)@([a-z0-9.-]*)/gi
let match let match
while ((match = re.exec(text))) { while ((match = re.exec(text))) {
const spaceOffset = match[1].length const spaceOffset = match[1].length
+3 -3
View File
@@ -24,9 +24,9 @@ export function niceDate(
} }
export function getAge(birthDate: Date): number { export function getAge(birthDate: Date): number {
var today = new Date() const today = new Date()
var age = today.getFullYear() - birthDate.getFullYear() let age = today.getFullYear() - birthDate.getFullYear()
var m = today.getMonth() - birthDate.getMonth() const m = today.getMonth() - birthDate.getMonth()
if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) { if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
age-- age--
} }
+14 -6
View File
@@ -28,7 +28,7 @@ const TRUSTED_REGEX = new RegExp(
export function isValidDomain(str: string): boolean { export function isValidDomain(str: string): boolean {
return !!TLDs.find(tld => { return !!TLDs.find(tld => {
let i = str.lastIndexOf(tld) const i = str.lastIndexOf(tld)
if (i === -1) { if (i === -1) {
return false return false
} }
@@ -123,7 +123,9 @@ export function isBskyPostUrl(url: string): boolean {
return /profile\/(?<name>[^/]+)\/post\/(?<rkey>[^/]+)/i.test( return /profile\/(?<name>[^/]+)\/post\/(?<rkey>[^/]+)/i.test(
urlp.pathname, urlp.pathname,
) )
} catch {} } catch {
// no-op
}
} }
return false return false
} }
@@ -135,7 +137,9 @@ export function isBskyCustomFeedUrl(url: string): boolean {
return /profile\/(?<name>[^/]+)\/feed\/(?<rkey>[^/]+)/i.test( return /profile\/(?<name>[^/]+)\/feed\/(?<rkey>[^/]+)/i.test(
urlp.pathname, urlp.pathname,
) )
} catch {} } catch {
// no-op
}
} }
return false return false
} }
@@ -291,10 +295,14 @@ export function labelToDomain(label: string): string | undefined {
} }
try { try {
return new URL(label).hostname.toLowerCase() return new URL(label).hostname.toLowerCase()
} catch {} } catch {
// no-op
}
try { try {
return new URL('https://' + label).hostname.toLowerCase() return new URL('https://' + label).hostname.toLowerCase()
} catch {} } catch {
// no-op
}
return undefined return undefined
} }
@@ -306,7 +314,7 @@ export function isPossiblyAUrl(str: string): boolean {
if (str.startsWith('https://')) { if (str.startsWith('https://')) {
return true return true
} }
const [firstWord] = str.split(/[\s\/]/) const [firstWord] = str.split(/[\s/]/)
return isValidDomain(firstWord) return isValidDomain(firstWord)
} }
+5 -2
View File
@@ -11,7 +11,10 @@ if (process.env.NODE_ENV !== 'production') {
// This is a hack to get it showing as a redbox on the web so we catch it early. // This is a hack to get it showing as a redbox on the web so we catch it early.
const realConsoleError = console.error const realConsoleError = console.error
const thrownErrors = new WeakSet() const thrownErrors = new WeakSet()
console.error = function consoleErrorWrapper(msgOrError) { console.error = function consoleErrorWrapper(
...args: Parameters<typeof console.error>
) {
const msgOrError = args[0]
if ( if (
typeof msgOrError === 'string' && typeof msgOrError === 'string' &&
msgOrError.startsWith('Unexpected text node') msgOrError.startsWith('Unexpected text node')
@@ -28,7 +31,7 @@ if (process.env.NODE_ENV !== 'production') {
thrownErrors.add(err) thrownErrors.add(err)
throw err throw err
} else if (!thrownErrors.has(msgOrError)) { } else if (!thrownErrors.has(msgOrError)) {
return realConsoleError.apply(this, arguments as any) return realConsoleError.apply(this, args)
} }
} }
} }
+3 -1
View File
@@ -123,7 +123,9 @@ function BookmarksInner() {
if (isFetchingNextPage || !hasNextPage || error) return if (isFetchingNextPage || !hasNextPage || error) return
try { try {
await fetchNextPage() await fetchNextPage()
} catch {} } catch {
// no-op
}
}, [isFetchingNextPage, hasNextPage, error, fetchNextPage]) }, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
const items = useMemo(() => { const items = useMemo(() => {
+1 -1
View File
@@ -29,7 +29,7 @@ export function NoFeedsPinned({
const addRecommendedFeeds = React.useCallback(async () => { const addRecommendedFeeds = React.useCallback(async () => {
let skippedTimeline = false let skippedTimeline = false
let skippedDiscover = false let skippedDiscover = false
let remainingSavedFeeds = [] const remainingSavedFeeds = []
// remove first instance of both timeline and discover, since we're going to overwrite them // remove first instance of both timeline and discover, since we're going to overwrite them
for (const savedFeed of preferences.savedFeeds) { for (const savedFeed of preferences.savedFeeds) {
@@ -156,7 +156,7 @@ export function MessageInputEmbed({
</View> </View>
) )
break break
case 'success': case 'success': {
const itemUrip = new AtUri(post.uri) const itemUrip = new AtUri(post.uri)
const itemHref = makeProfileLink(post.author, 'post', itemUrip.rkey) const itemHref = makeProfileLink(post.author, 'post', itemUrip.rkey)
@@ -203,6 +203,7 @@ export function MessageInputEmbed({
</View> </View>
) )
break break
}
} }
return ( return (
@@ -144,7 +144,7 @@ export function StepFinished() {
: undefined : undefined
await agent.upsertProfile(async existing => { await agent.upsertProfile(async existing => {
let next: Un$Typed<AppBskyActorProfile.Record> = existing ?? {} const next: Un$Typed<AppBskyActorProfile.Record> = existing ?? {}
if (blobPromise) { if (blobPromise) {
const res = await blobPromise const res = await blobPromise
@@ -82,7 +82,7 @@ export function StarterPackCard({
setIsFollowingAll(true) setIsFollowingAll(true)
setIsProcessing(false) setIsProcessing(false)
batchedUpdates(() => { batchedUpdates(() => {
for (let did of dids) { for (const did of dids) {
updateProfileShadow(queryClient, did, { updateProfileShadow(queryClient, did, {
followingUri: followUris.get(did), followingUri: followUris.get(did),
}) })
+3 -3
View File
@@ -375,7 +375,7 @@ export function Explore({
if (suggestedUsers.actors.length > 0 && moderationOpts) { if (suggestedUsers.actors.length > 0 && moderationOpts) {
// Currently the responses contain duplicate items. // Currently the responses contain duplicate items.
// Needs to be fixed on backend, but let's dedupe to be safe. // Needs to be fixed on backend, but let's dedupe to be safe.
let seen = new Set() const seen = new Set()
const profileItems: ExploreScreenItems[] = [] const profileItems: ExploreScreenItems[] = []
for (const actor of suggestedUsers.actors) { for (const actor of suggestedUsers.actors) {
// checking for following still necessary if search data is used // checking for following still necessary if search data is used
@@ -439,7 +439,7 @@ export function Explore({
if (useFullExperience) { if (useFullExperience) {
if (suggestedFeeds && preferences) { if (suggestedFeeds && preferences) {
let seen = new Set() const seen = new Set()
const feedItems: ExploreScreenItems[] = [] const feedItems: ExploreScreenItems[] = []
for (const feed of suggestedFeeds.feeds) { for (const feed of suggestedFeeds.feeds) {
if (!seen.has(feed.uri)) { if (!seen.has(feed.uri)) {
@@ -527,7 +527,7 @@ export function Explore({
if (feeds && preferences) { if (feeds && preferences) {
// Currently the responses contain duplicate items. // Currently the responses contain duplicate items.
// Needs to be fixed on backend, but let's dedupe to be safe. // Needs to be fixed on backend, but let's dedupe to be safe.
let seen = new Set() const seen = new Set()
const feedItems: ExploreScreenItems[] = [] const feedItems: ExploreScreenItems[] = []
for (const page of feeds.pages) { for (const page of feeds.pages) {
for (const feed of page.feeds) { for (const feed of page.feeds) {
+1 -1
View File
@@ -248,7 +248,7 @@ let SearchScreenPostResults = ({
return results?.pages.flatMap(page => page.posts) || [] return results?.pages.flatMap(page => page.posts) || []
}, [results]) }, [results])
const items = useMemo(() => { const items = useMemo(() => {
let temp: SearchResultSlice[] = [] const temp: SearchResultSlice[] = []
const seenUris = new Set() const seenUris = new Set()
for (const post of posts) { for (const post of posts) {
+1 -1
View File
@@ -10,7 +10,7 @@ export function parseSearchQuery(rawQuery: string) {
} }
// find remaining params in base // find remaining params in base
const rawParams = base.match(/[a-z]+:[a-z-\.@\d:"]+/gi) || [] const rawParams = base.match(/[a-z]+:[a-z-.@\d:"]+/gi) || []
for (const param of rawParams) { for (const param of rawParams) {
base = base.replace(param, '') base = base.replace(param, '')
+3 -3
View File
@@ -123,7 +123,7 @@ export function is18(date: Date) {
} }
export function reducer(s: SignupState, a: SignupAction): SignupState { export function reducer(s: SignupState, a: SignupAction): SignupState {
let next = {...s} const next = {...s}
switch (a.type) { switch (a.type) {
case 'prev': { case 'prev': {
@@ -330,11 +330,11 @@ export function useSubmitSignup() {
/* /*
* Must happen last so that if the user has multiple tabs open and * Must happen last so that if the user has multiple tabs open and
* createAccount fails, one tab is not stuck in onboarding — Eric * createAccount fails, one tab is not stuck in onboarding — Eric
*/ */
onboardingDispatch({type: 'start'}) onboardingDispatch({type: 'start'})
} catch (e: any) { } catch (e: any) {
let errMsg = e.toString() const errMsg = e.toString()
if (e instanceof ComAtprotoServerCreateAccount.InvalidInviteCodeError) { if (e instanceof ComAtprotoServerCreateAccount.InvalidInviteCodeError) {
dispatch({ dispatch({
type: 'setError', type: 'setError',
@@ -377,7 +377,7 @@ function Header({
setIsProcessing(false) setIsProcessing(false)
batchedUpdates(() => { batchedUpdates(() => {
for (let did of dids) { for (const did of dids) {
updateProfileShadow(queryClient, did, { updateProfileShadow(queryClient, did, {
followingUri: followUris.get(did), followingUri: followUris.get(did),
}) })
+2 -1
View File
@@ -74,8 +74,9 @@ export function Header({
break break
} }
case 'author': case 'author':
// TODO // falls through
default: { default: {
// TODO: implement author header
break break
} }
} }
+8 -8
View File
@@ -158,7 +158,7 @@ export function updatePostShadow(
value: Partial<PostShadow>, value: Partial<PostShadow>,
) { ) {
const cachedPosts = findPostsInCache(queryClient, uri) const cachedPosts = findPostsInCache(queryClient, uri)
for (let post of cachedPosts) { for (const post of cachedPosts) {
shadows.set(post, {...shadows.get(post), ...value}) shadows.set(post, {...shadows.get(post), ...value})
} }
batchedUpdates(() => { batchedUpdates(() => {
@@ -170,28 +170,28 @@ function* findPostsInCache(
queryClient: QueryClient, queryClient: QueryClient,
uri: string, uri: string,
): Generator<AppBskyFeedDefs.PostView, void> { ): Generator<AppBskyFeedDefs.PostView, void> {
for (let post of findAllPostsInFeedQueryData(queryClient, uri)) { for (const post of findAllPostsInFeedQueryData(queryClient, uri)) {
yield post yield post
} }
for (let post of findAllPostsInNotifsQueryData(queryClient, uri)) { for (const post of findAllPostsInNotifsQueryData(queryClient, uri)) {
yield post yield post
} }
for (let post of findAllPostsInThreadV2QueryData(queryClient, uri)) { for (const post of findAllPostsInThreadV2QueryData(queryClient, uri)) {
yield post yield post
} }
for (let post of findAllPostsInSearchQueryData(queryClient, uri)) { for (const post of findAllPostsInSearchQueryData(queryClient, uri)) {
yield post yield post
} }
for (let post of findAllPostsInQuoteQueryData(queryClient, uri)) { for (const post of findAllPostsInQuoteQueryData(queryClient, uri)) {
yield post yield post
} }
for (let post of findAllPostsInExploreFeedPreviewsQueryData( for (const post of findAllPostsInExploreFeedPreviewsQueryData(
queryClient, queryClient,
uri, uri,
)) { )) {
yield post yield post
} }
for (let post of findAllPostsInBookmarksQueryData(queryClient, uri)) { for (const post of findAllPostsInBookmarksQueryData(queryClient, uri)) {
yield post yield post
} }
} }
+1 -1
View File
@@ -195,7 +195,7 @@ export function updateProfileShadow(
value: Partial<ProfileShadow>, value: Partial<ProfileShadow>,
) { ) {
const cachedProfiles = findProfilesInCache(queryClient, did) const cachedProfiles = findProfilesInCache(queryClient, did)
for (let profile of cachedProfiles) { for (const profile of cachedProfiles) {
shadows.set(profile, {...shadows.get(profile), ...value}) shadows.set(profile, {...shadows.get(profile), ...value})
} }
batchedUpdates(() => { batchedUpdates(() => {
+1 -1
View File
@@ -276,7 +276,7 @@ function sendOrAggregateInteractionsForStats(
interactions: AppBskyFeedDefs.Interaction[], interactions: AppBskyFeedDefs.Interaction[],
feed: string, feed: string,
) { ) {
for (let interaction of interactions) { for (const interaction of interactions) {
switch (interaction.event) { switch (interaction.event) {
// Pressing "Show more" / "Show less" is relatively uncommon so we won't aggregate them. // Pressing "Show more" / "Show less" is relatively uncommon so we won't aggregate them.
// This lets us send the feed context together with them. // This lets us send the feed context together with them.
+1 -1
View File
@@ -46,7 +46,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
) )
const closeLightbox = useNonReactiveCallback(() => { const closeLightbox = useNonReactiveCallback(() => {
let wasActive = !!activeLightbox const wasActive = !!activeLightbox
setActiveLightbox(null) setActiveLightbox(null)
return wasActive return wasActive
}) })
+5 -11
View File
@@ -95,7 +95,7 @@ export class Convo {
this.convoId = params.convoId this.convoId = params.convoId
this.agent = params.agent this.agent = params.agent
this.events = params.events this.events = params.events
this.senderUserDid = params.agent.session?.did! this.senderUserDid = params.agent.session?.did ?? ''
if (params.placeholderData) { if (params.placeholderData) {
this.setupPlaceholderData(params.placeholderData) this.setupPlaceholderData(params.placeholderData)
@@ -557,11 +557,7 @@ export class Convo {
async fetchConvo() { async fetchConvo() {
if (this.pendingFetchConvo) return this.pendingFetchConvo if (this.pendingFetchConvo) return this.pendingFetchConvo
this.pendingFetchConvo = new Promise<{ this.pendingFetchConvo = (async () => {
convo: ChatBskyConvoDefs.ConvoView
sender: ChatBskyActorDefs.ProfileViewBasic | undefined
recipients: ChatBskyActorDefs.ProfileViewBasic[]
}>(async (resolve, reject) => {
try { try {
const response = await networkRetry(2, () => { const response = await networkRetry(2, () => {
return this.agent.api.chat.bsky.convo.getConvo( return this.agent.api.chat.bsky.convo.getConvo(
@@ -574,17 +570,15 @@ export class Convo {
const convo = response.data.convo const convo = response.data.convo
resolve({ return {
convo, convo,
sender: convo.members.find(m => m.did === this.senderUserDid), sender: convo.members.find(m => m.did === this.senderUserDid),
recipients: convo.members.filter(m => m.did !== this.senderUserDid), recipients: convo.members.filter(m => m.did !== this.senderUserDid),
}) }
} catch (e) {
reject(e)
} finally { } finally {
this.pendingFetchConvo = undefined this.pendingFetchConvo = undefined
} }
}) })()
return this.pendingFetchConvo return this.pendingFetchConvo
} }
+1 -1
View File
@@ -352,7 +352,7 @@ export class MessagesEventBus {
const {logs: events} = response.data const {logs: events} = response.data
let needsEmit = false let needsEmit = false
let batch: ChatBskyConvoGetLog.OutputSchema['logs'] = [] const batch: ChatBskyConvoGetLog.OutputSchema['logs'] = []
for (const ev of events) { for (const ev of events) {
/* /*
+2 -2
View File
@@ -60,7 +60,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
}) })
const closeModal = useNonReactiveCallback(() => { const closeModal = useNonReactiveCallback(() => {
let wasActive = activeModals.length > 0 const wasActive = activeModals.length > 0
setActiveModals(modals => { setActiveModals(modals => {
return modals.slice(0, -1) return modals.slice(0, -1)
}) })
@@ -68,7 +68,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
}) })
const closeAllModals = useNonReactiveCallback(() => { const closeAllModals = useNonReactiveCallback(() => {
let wasActive = activeModals.length > 0 const wasActive = activeModals.length > 0
setActiveModals([]) setActiveModals([])
return wasActive return wasActive
}) })
+1 -1
View File
@@ -108,7 +108,7 @@ function computeSuggestions({
searched?: AppBskyActorDefs.ProfileViewBasic[] searched?: AppBskyActorDefs.ProfileViewBasic[]
moderationOpts: ModerationOpts moderationOpts: ModerationOpts
}) { }) {
let items: AppBskyActorDefs.ProfileViewBasic[] = [] const items: AppBskyActorDefs.ProfileViewBasic[] = []
for (const item of searched) { for (const item of searched) {
if (!items.find(item2 => item2.handle === item.handle)) { if (!items.find(item2 => item2.handle === item.handle)) {
items.push(item) items.push(item)
@@ -135,7 +135,7 @@ export function* findAllPostsInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const bookmark of page.bookmarks) { for (const bookmark of page.bookmarks) {
if ( if (
!bsky.dangerousIsType<AppBskyFeedDefs.PostView>( !bsky.dangerousIsType<AppBskyFeedDefs.PostView>(
+2 -2
View File
@@ -364,7 +364,7 @@ export function* findAllPostsInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const item of page.posts) { for (const item of page.posts) {
if (didOrHandleUriMatches(atUri, item.post)) { if (didOrHandleUriMatches(atUri, item.post)) {
yield item.post yield item.post
@@ -420,7 +420,7 @@ export function* findAllProfilesInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const item of page.posts) { for (const item of page.posts) {
if (item.post.author.did === did) { if (item.post.author.did === did) {
yield item.post.author yield item.post.author
+3 -3
View File
@@ -439,7 +439,7 @@ export function usePinnedFeedsInfos() {
return [PWI_DISCOVER_FEED_STUB] return [PWI_DISCOVER_FEED_STUB]
} }
let resolved = new Map<string, FeedSourceInfo>() const resolved = new Map<string, FeedSourceInfo>()
// Get all feeds. We can do this in a batch. // Get all feeds. We can do this in a batch.
const pinnedFeeds = pinnedItems.filter(feed => feed.type === 'feed') const pinnedFeeds = pinnedItems.filter(feed => feed.type === 'feed')
@@ -476,7 +476,7 @@ export function usePinnedFeedsInfos() {
// order the feeds/lists in the order they were pinned // order the feeds/lists in the order they were pinned
const result: SavedFeedSourceInfo[] = [] const result: SavedFeedSourceInfo[] = []
for (let pinnedItem of pinnedItems) { for (const pinnedItem of pinnedItems) {
const feedInfo = resolved.get(pinnedItem.value) const feedInfo = resolved.get(pinnedItem.value)
if (feedInfo) { if (feedInfo) {
result.push({ result.push({
@@ -590,7 +590,7 @@ export function useSavedFeeds() {
}) })
const result: SavedFeedItem[] = [] const result: SavedFeedItem[] = []
for (let savedItem of savedItems) { for (const savedItem of savedItems) {
if (savedItem.type === 'timeline') { if (savedItem.type === 'timeline') {
result.push({ result.push({
type: 'timeline', type: 'timeline',
+1 -1
View File
@@ -103,7 +103,7 @@ export function* findAllProfilesInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const match of page.matches) { for (const match of page.matches) {
if (match.did === did) { if (match.did === did) {
yield match yield match
+3 -1
View File
@@ -120,7 +120,9 @@ export async function checkHandleAvailability(
logger.metric('signup:handleTaken', {typeahead}, {statsig: true}) logger.metric('signup:handleTaken', {typeahead}, {statsig: true})
return {available: false} as const return {available: false} as const
} }
} catch {} } catch {
// no-op
}
logger.metric('signup:handleAvailable', {typeahead}, {statsig: true}) logger.metric('signup:handleAvailable', {typeahead}, {statsig: true})
return {available: true} as const return {available: true} as const
} }
+1 -1
View File
@@ -54,7 +54,7 @@ export function* findAllProfilesInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const follow of page.followers) { for (const follow of page.followers) {
if (follow.did === did) { if (follow.did === did) {
yield follow yield follow
+1 -1
View File
@@ -103,7 +103,7 @@ export function* findAllProfilesInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
if (page.list.creator.did === did) { if (page.list.creator.did === did) {
yield page.list.creator yield page.list.creator
} }
+1 -1
View File
@@ -47,7 +47,7 @@ export function* findAllProfilesInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const block of page.blocks) { for (const block of page.blocks) {
if (block.did === did) { if (block.did === did) {
yield block yield block
+2 -2
View File
@@ -21,7 +21,7 @@ export function useMyListsQuery(filter: MyListsFilter) {
staleTime: STALE.MINUTES.ONE, staleTime: STALE.MINUTES.ONE,
queryKey: RQKEY(filter), queryKey: RQKEY(filter),
async queryFn() { async queryFn() {
let lists: AppBskyGraphDefs.ListView[] = [] const lists: AppBskyGraphDefs.ListView[] = []
const promises = [ const promises = [
accumulate(cursor => accumulate(cursor =>
agent.app.bsky.graph agent.app.bsky.graph
@@ -66,7 +66,7 @@ export function useMyListsQuery(filter: MyListsFilter) {
} }
const resultset = await Promise.all(promises) const resultset = await Promise.all(promises)
for (const res of resultset) { for (const res of resultset) {
for (let list of res) { for (const list of res) {
if ( if (
filter === 'curate' && filter === 'curate' &&
list.purpose !== 'app.bsky.graph.defs#curatelist' list.purpose !== 'app.bsky.graph.defs#curatelist'
+1 -1
View File
@@ -47,7 +47,7 @@ export function* findAllProfilesInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const mute of page.mutes) { for (const mute of page.mutes) {
if (mute.did === did) { if (mute.did === did) {
yield mute yield mute
+5 -5
View File
@@ -133,7 +133,7 @@ export function useNotificationFeedQuery(opts: {
// Keep track of the last run and whether we can reuse // Keep track of the last run and whether we can reuse
// some already selected pages from there. // some already selected pages from there.
let reusedPages = [] const reusedPages = []
if (lastRun.current) { if (lastRun.current) {
const { const {
data: lastData, data: lastData,
@@ -141,8 +141,8 @@ export function useNotificationFeedQuery(opts: {
result: lastResult, result: lastResult,
} = lastRun.current } = lastRun.current
let canReuse = true let canReuse = true
for (let key in selectArgs) { for (const key in selectArgs) {
if (selectArgs.hasOwnProperty(key)) { if (Object.hasOwn(selectArgs, key)) {
if ((selectArgs as any)[key] !== (lastArgs as any)[key]) { if ((selectArgs as any)[key] !== (lastArgs as any)[key]) {
// Can't do reuse anything if any input has changed. // Can't do reuse anything if any input has changed.
canReuse = false canReuse = false
@@ -287,7 +287,7 @@ export function* findAllPostsInQueryData(
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const item of page.items) { for (const item of page.items) {
if (item.type !== 'starterpack-joined') { if (item.type !== 'starterpack-joined') {
if (item.subject && didOrHandleUriMatches(atUri, item.subject)) { if (item.subject && didOrHandleUriMatches(atUri, item.subject)) {
@@ -317,7 +317,7 @@ export function* findAllProfilesInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const item of page.items) { for (const item of page.items) {
if ( if (
(item.type === 'follow' || item.type === 'contact-match') && (item.type === 'follow' || item.type === 'contact-match') &&
+1 -1
View File
@@ -71,7 +71,7 @@ export async function fetchPage({
) )
// group notifications which are essentially similar (follows, likes on a post) // group notifications which are essentially similar (follows, likes on a post)
let notifsGrouped = groupNotifications(notifs) const notifsGrouped = groupNotifications(notifs)
// we fetch subjects of notifications (usually posts) now instead of lazily // we fetch subjects of notifications (usually posts) now instead of lazily
// in the UI to avoid relayouts // in the UI to avoid relayouts
+5 -5
View File
@@ -244,7 +244,7 @@ export function usePostFeedQuery(
// Keep track of the last run and whether we can reuse // Keep track of the last run and whether we can reuse
// some already selected pages from there. // some already selected pages from there.
let reusedPages = [] const reusedPages = []
if (lastRun.current) { if (lastRun.current) {
const { const {
data: lastData, data: lastData,
@@ -252,8 +252,8 @@ export function usePostFeedQuery(
result: lastResult, result: lastResult,
} = lastRun.current } = lastRun.current
let canReuse = true let canReuse = true
for (let key in selectArgs) { for (const key in selectArgs) {
if (selectArgs.hasOwnProperty(key)) { if (Object.hasOwn(selectArgs, key)) {
if ((selectArgs as any)[key] !== (lastArgs as any)[key]) { if ((selectArgs as any)[key] !== (lastArgs as any)[key]) {
// Can't do reuse anything if any input has changed. // Can't do reuse anything if any input has changed.
canReuse = false canReuse = false
@@ -510,7 +510,7 @@ export function* findAllPostsInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const item of page.feed) { for (const item of page.feed) {
if (didOrHandleUriMatches(atUri, item.post)) { if (didOrHandleUriMatches(atUri, item.post)) {
yield item.post yield item.post
@@ -563,7 +563,7 @@ export function* findAllProfilesInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const item of page.feed) { for (const item of page.feed) {
if (item.post.author.did === did) { if (item.post.author.did === did) {
yield item.post.author yield item.post.author
+1 -1
View File
@@ -52,7 +52,7 @@ export function* findAllProfilesInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const like of page.likes) { for (const like of page.likes) {
if (like.actor.did === did) { if (like.actor.did === did) {
yield like.actor yield like.actor
+2 -2
View File
@@ -80,7 +80,7 @@ export function* findAllProfilesInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const item of page.posts) { for (const item of page.posts) {
if (item.author.did === did) { if (item.author.did === did) {
yield item.author yield item.author
@@ -108,7 +108,7 @@ export function* findAllPostsInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const post of page.posts) { for (const post of page.posts) {
if (didOrHandleUriMatches(atUri, post)) { if (didOrHandleUriMatches(atUri, post)) {
yield post yield post
+1 -1
View File
@@ -55,7 +55,7 @@ export function* findAllProfilesInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const repostedBy of page.repostedBy) { for (const repostedBy of page.repostedBy) {
if (repostedBy.did === did) { if (repostedBy.did === did) {
yield repostedBy yield repostedBy
+1 -1
View File
@@ -54,7 +54,7 @@ export function* findAllProfilesInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const follower of page.followers) { for (const follower of page.followers) {
if (follower.did === did) { if (follower.did === did) {
yield follower yield follower
+1 -1
View File
@@ -63,7 +63,7 @@ export function* findAllProfilesInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const follow of page.follows) { for (const follow of page.follows) {
if (follow.did === did) { if (follow.did === did) {
yield follow yield follow
+1 -1
View File
@@ -621,7 +621,7 @@ export function* findAllProfilesInQueryData(
if (!queryData) { if (!queryData) {
continue continue
} }
for (let profile of queryData.profiles) { for (const profile of queryData.profiles) {
if (profile.did === did) { if (profile.did === did) {
yield profile yield profile
} }
+5 -5
View File
@@ -87,7 +87,7 @@ export function useSearchPostsQuery({
// Keep track of the last run and whether we can reuse // Keep track of the last run and whether we can reuse
// some already selected pages from there. // some already selected pages from there.
let reusedPages = [] const reusedPages = []
if (lastRun.current) { if (lastRun.current) {
const { const {
data: lastData, data: lastData,
@@ -95,8 +95,8 @@ export function useSearchPostsQuery({
result: lastResult, result: lastResult,
} = lastRun.current } = lastRun.current
let canReuse = true let canReuse = true
for (let key in selectArgs) { for (const key in selectArgs) {
if (selectArgs.hasOwnProperty(key)) { if (Object.hasOwn(selectArgs, key)) {
if ((selectArgs as any)[key] !== (lastArgs as any)[key]) { if ((selectArgs as any)[key] !== (lastArgs as any)[key]) {
// Can't do reuse anything if any input has changed. // Can't do reuse anything if any input has changed.
canReuse = false canReuse = false
@@ -156,7 +156,7 @@ export function* findAllPostsInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const post of page.posts) { for (const post of page.posts) {
if (didOrHandleUriMatches(atUri, post)) { if (didOrHandleUriMatches(atUri, post)) {
yield post yield post
@@ -184,7 +184,7 @@ export function* findAllProfilesInQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const post of page.posts) { for (const post of page.posts) {
if (post.author.did === did) { if (post.author.did === did) {
yield post.author yield post.author
+1 -2
View File
@@ -119,8 +119,7 @@ export function useCreateStarterPackMutation({
descriptionFacets = rt.facets descriptionFacets = rt.facets
} }
let listRes const listRes = await createStarterPackList({
listRes = await createStarterPackList({
name, name,
description, description,
profiles, profiles,
+1 -1
View File
@@ -150,7 +150,7 @@ function* findAllProfilesInSuggestedFollowsQueryData(
if (!queryData?.pages) { if (!queryData?.pages) {
continue continue
} }
for (const page of queryData?.pages) { for (const page of queryData.pages) {
for (const actor of page.actors) { for (const actor of page.actors) {
if (actor.did === did) { if (actor.did === did) {
yield actor yield actor
+1 -1
View File
@@ -70,7 +70,7 @@ export function threadgateAllowUISettingToAllowRecordValue(
return undefined return undefined
} }
let allow: Exclude<AppBskyFeedThreadgate.Record['allow'], undefined> = [] const allow: Exclude<AppBskyFeedThreadgate.Record['allow'], undefined> = []
if (!threadgate.find(v => v.type === 'nobody')) { if (!threadgate.find(v => v.type === 'nobody')) {
for (const rule of threadgate) { for (const rule of threadgate) {
@@ -202,7 +202,7 @@ export function getThreadPlaceholder(
uri: string, uri: string,
): $Typed<AppBskyUnspeccedGetPostThreadV2.ThreadItem> | void { ): $Typed<AppBskyUnspeccedGetPostThreadV2.ThreadItem> | void {
let partial let partial
for (let item of getThreadPlaceholderCandidates(queryClient, uri)) { for (const item of getThreadPlaceholderCandidates(queryClient, uri)) {
/* /*
* Currently, the backend doesn't send full post info in some cases (for * Currently, the backend doesn't send full post info in some cases (for
* example, for quoted posts). We use missing `likeCount` as a way to * example, for quoted posts). We use missing `likeCount` as a way to
@@ -246,19 +246,19 @@ export function* getThreadPlaceholderCandidates(
* with >0 likes/reposts over a stale version with no metrics in order to * with >0 likes/reposts over a stale version with no metrics in order to
* avoid a notification->post scroll jump. * avoid a notification->post scroll jump.
*/ */
for (let post of findAllPostsInNotifsQueryData(queryClient, uri)) { for (const post of findAllPostsInNotifsQueryData(queryClient, uri)) {
yield postViewToThreadPlaceholder(post) yield postViewToThreadPlaceholder(post)
} }
for (let post of findAllPostsInFeedQueryData(queryClient, uri)) { for (const post of findAllPostsInFeedQueryData(queryClient, uri)) {
yield postViewToThreadPlaceholder(post) yield postViewToThreadPlaceholder(post)
} }
for (let post of findAllPostsInQuoteQueryData(queryClient, uri)) { for (const post of findAllPostsInQuoteQueryData(queryClient, uri)) {
yield postViewToThreadPlaceholder(post) yield postViewToThreadPlaceholder(post)
} }
for (let post of findAllPostsInSearchQueryData(queryClient, uri)) { for (const post of findAllPostsInSearchQueryData(queryClient, uri)) {
yield postViewToThreadPlaceholder(post) yield postViewToThreadPlaceholder(post)
} }
for (let post of findAllPostsInExploreFeedPreviewsQueryData( for (const post of findAllPostsInExploreFeedPreviewsQueryData(
queryClient, queryClient,
uri, uri,
)) { )) {
+1 -1
View File
@@ -128,7 +128,7 @@ export type ThreadItem =
* total number of replies, the reply index, etc. * total number of replies, the reply index, etc.
* *
* The idea here is that these values should be objectively true in all cases, * The idea here is that these values should be objectively true in all cases,
* such that we can use them later — either individually on in composite — to * such that we can use them later — either individually on in composite — to
* drive rendering behaviors. * drive rendering behaviors.
*/ */
export type TraversalMetadata = { export type TraversalMetadata = {
+1 -1
View File
@@ -1673,7 +1673,7 @@ describe('session', () => {
function run(initialState: State, actions: Action[]): State { function run(initialState: State, actions: Action[]): State {
let state = initialState let state = initialState
for (let action of actions) { for (const action of actions) {
state = reducer(state, action) state = reducer(state, action)
} }
return state return state
+1 -1
View File
@@ -374,7 +374,7 @@ export class Agent extends BaseAgent {
// WARN: In the factories above, we _manually set a proxy header_ for the agent after we do whatever it is we are supposed to do. // WARN: In the factories above, we _manually set a proxy header_ for the agent after we do whatever it is we are supposed to do.
// Ideally, we wouldn't be doing this. However, since there is so much logic that requires making calls to the PDS right now, it // Ideally, we wouldn't be doing this. However, since there is so much logic that requires making calls to the PDS right now, it
// feels safer to just let those run as-is and set the header afterward. // feels safer to just let those run as-is and set the header afterward.
let realFetch = globalThis.fetch const realFetch = globalThis.fetch
class BskyAppAgent extends BskyAgent { class BskyAppAgent extends BskyAgent {
persistSessionHandler: ((event: AtpSessionEvent) => void) | undefined = persistSessionHandler: ((event: AtpSessionEvent) => void) | undefined =
undefined undefined
+2 -2
View File
@@ -123,8 +123,8 @@ export function addSessionDebugLog(log: Log) {
} }
} }
let agentIds = new WeakMap<object, string>() const agentIds = new WeakMap<object, string>()
let realmId = Math.random().toString(36).slice(2) const realmId = Math.random().toString(36).slice(2)
let nextAgentId = 1 let nextAgentId = 1
function getAgentId(agent: object) { function getAgentId(agent: object) {
+1 -1
View File
@@ -107,7 +107,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
}) })
const closeComposer = useNonReactiveCallback(() => { const closeComposer = useNonReactiveCallback(() => {
let wasOpen = !!state const wasOpen = !!state
if (wasOpen) { if (wasOpen) {
setState(undefined) setState(undefined)
purgeTemporaryImageFiles() purgeTemporaryImageFiles()
+3 -1
View File
@@ -47,7 +47,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
if (isWeb) { if (isWeb) {
try { try {
sessionStorage.setItem('lastSelectedHomeFeed', feed) sessionStorage.setItem('lastSelectedHomeFeed', feed)
} catch {} } catch {
// no-op
}
} }
persisted.write('lastSelectedHomeFeed', feed) persisted.write('lastSelectedHomeFeed', feed)
}, []) }, [])
+2 -2
View File
@@ -36,7 +36,7 @@ const consumedSources = new Map<string, PostSource>()
export function setUnstablePostSource(key: string, source: PostSource) { export function setUnstablePostSource(key: string, source: PostSource) {
assertValidDevOnly( assertValidDevOnly(
key, key,
`setUnstablePostSource key should be a URI containing a handle, received ${key} — use buildPostSourceKey`, `setUnstablePostSource key should be a URI containing a handle, received ${key} — use buildPostSourceKey`,
) )
logger.debug('set', {key, source}) logger.debug('set', {key, source})
transientSources.set(key, source) transientSources.set(key, source)
@@ -53,7 +53,7 @@ export function useUnstablePostSource(key: string) {
const [source] = useState(() => { const [source] = useState(() => {
assertValidDevOnly( assertValidDevOnly(
key, key,
`consumeUnstablePostSource key should be a URI containing a handle, received ${key} — be sure to use buildPostSourceKey when setting the source`, `consumeUnstablePostSource key should be a URI containing a handle, received ${key} — be sure to use buildPostSourceKey when setting the source`,
true, true,
) )
const source = consumedSources.get(id) || transientSources.get(key) const source = consumedSources.get(id) || transientSources.get(key)
+3 -3
View File
@@ -519,7 +519,7 @@ export const ComposePost = ({
} finally { } finally {
if (postUri) { if (postUri) {
let index = 0 let index = 0
for (let post of thread.posts) { for (const post of thread.posts) {
logEvent('post:create', { logEvent('post:create', {
imageCount: imageCount:
post.embed.media?.type === 'images' post.embed.media?.type === 'images'
@@ -619,7 +619,7 @@ export const ComposePost = ({
if (publishOnUpload) { if (publishOnUpload) {
let erroredVideos = 0 let erroredVideos = 0
let uploadingVideos = 0 let uploadingVideos = 0
for (let post of thread.posts) { for (const post of thread.posts) {
if (post.embed.media?.type === 'video') { if (post.embed.media?.type === 'video') {
const video = post.embed.media.video const video = post.embed.media.video
if (video.status === 'error') { if (video.status === 'error') {
@@ -812,7 +812,7 @@ export const ComposePost = ({
) )
} }
let ComposerPost = React.memo(function ComposerPost({ const ComposerPost = React.memo(function ComposerPost({
post, post,
dispatch, dispatch,
textInput, textInput,
+1 -1
View File
@@ -413,7 +413,7 @@ export function SelectMediaButton({
msg`You can only select one GIF at a time.`, msg`You can only select one GIF at a time.`,
), ),
[SelectedAssetError.FileTooBig]: _( [SelectedAssetError.FileTooBig]: _(
msg`One or more of your selected files are too large. Maximum size is 100 MB.`, msg`One or more of your selected files are too large. Maximum size is 100 MB.`,
), ),
}[error] }[error]
}) })
+1 -1
View File
@@ -198,7 +198,7 @@ export function composerReducer(
const indexToRemove = state.thread.posts.findIndex( const indexToRemove = state.thread.posts.findIndex(
p => p.id === action.postId, p => p.id === action.postId,
) )
let nextPosts = [...state.thread.posts] const nextPosts = [...state.thread.posts]
if (indexToRemove !== -1) { if (indexToRemove !== -1) {
const postToRemove = state.thread.posts[indexToRemove] const postToRemove = state.thread.posts[indexToRemove]
if (postToRemove.embed.media?.type === 'video') { if (postToRemove.embed.media?.type === 'video') {
+2 -2
View File
@@ -392,7 +392,7 @@ function getCompressErrorMessage(e: unknown, _: I18n['_']): string | null {
} }
if (e instanceof VideoTooLargeError) { if (e instanceof VideoTooLargeError) {
return _( return _(
msg`The selected video is larger than 100 MB. Please try again with a smaller file.`, msg`The selected video is larger than 100 MB. Please try again with a smaller file.`,
) )
} }
logger.error('Error compressing video', {safeMessage: e}) logger.error('Error compressing video', {safeMessage: e})
@@ -431,7 +431,7 @@ function getUploadErrorMessage(e: unknown, _: I18n['_']): string | null {
) )
case 'file size (100000001 bytes) is larger than the maximum allowed size (100000000 bytes)': case 'file size (100000001 bytes) is larger than the maximum allowed size (100000000 bytes)':
return _( return _(
msg`The selected video is larger than 100 MB. Please try again with a smaller file.`, msg`The selected video is larger than 100 MB. Please try again with a smaller file.`,
) )
default: default:
return e.message return e.message
@@ -90,7 +90,7 @@ function iterateUris(str: string, cb: (from: number, to: number) => void) {
} }
uri = `https://${uri}` uri = `https://${uri}`
} }
let from = str.indexOf(match[2], match.index) const from = str.indexOf(match[2], match.index)
let to = from + match[2].length let to = from + match[2].length
// strip ending puncuation // strip ending puncuation
if (/[.,;!?]$/.test(uri)) { if (/[.,;!?]$/.test(uri)) {
@@ -17,7 +17,9 @@ export function useWebPreloadEmoji({immediate}: {immediate?: boolean} = {}) {
try { try {
const data = (await import('./EmojiPickerData.json')).default const data = (await import('./EmojiPickerData.json')).default
init({data}) init({data})
} catch (e) {} } catch (e) {
// no-op
}
}, []) }, [])
if (immediate) preload() if (immediate) preload()
return preload return preload
+1 -1
View File
@@ -95,7 +95,7 @@ export function ProfileFeedgens({
} else if (isEmpty) { } else if (isEmpty) {
items = items.concat([EMPTY]) items = items.concat([EMPTY])
} else if (data?.pages) { } else if (data?.pages) {
for (const page of data?.pages) { for (const page of data.pages) {
items = items.concat(page.feeds) items = items.concat(page.feeds)
} }
} else if (isError && !isEmpty) { } else if (isError && !isEmpty) {
@@ -191,7 +191,7 @@ const ImageItem = ({
.onEnd(() => { .onEnd(() => {
'worklet' 'worklet'
// Commit just the pinch. // Commit just the pinch.
let t = createTransform() const t = createTransform()
prependPinch( prependPinch(
t, t,
pinchScale.get(), pinchScale.get(),
@@ -220,7 +220,7 @@ const ImageItem = ({
} }
const nextPanTranslation = {x: e.translationX, y: e.translationY} const nextPanTranslation = {x: e.translationX, y: e.translationY}
let t = createTransform() const t = createTransform()
prependPan(t, nextPanTranslation) prependPan(t, nextPanTranslation)
prependPinch( prependPinch(
t, t,
@@ -239,7 +239,7 @@ const ImageItem = ({
.onEnd(() => { .onEnd(() => {
'worklet' 'worklet'
// Commit just the pan. // Commit just the pan.
let t = createTransform() const t = createTransform()
prependPan(t, panTranslation.get()) prependPan(t, panTranslation.get())
prependTransform(t, committedTransform.get()) prependTransform(t, committedTransform.get())
applyRounding(t) applyRounding(t)
@@ -265,7 +265,7 @@ const ImageItem = ({
const [, , committedScale] = readTransform(committedTransform.get()) const [, , committedScale] = readTransform(committedTransform.get())
if (committedScale !== 1) { if (committedScale !== 1) {
// Go back to 1:1 using the identity vector. // Go back to 1:1 using the identity vector.
let t = createTransform() const t = createTransform()
committedTransform.set(withClampedSpring(t)) committedTransform.set(withClampedSpring(t))
return return
} }
@@ -317,7 +317,7 @@ const ImageItem = ({
const {scaleAndMoveTransform, isHidden} = transforms.get() const {scaleAndMoveTransform, isHidden} = transforms.get()
// Apply the active adjustments on top of the committed transform before the gestures. // Apply the active adjustments on top of the committed transform before the gestures.
// This is matrix multiplication, so operations are applied in the reverse order. // This is matrix multiplication, so operations are applied in the reverse order.
let t = createTransform() const t = createTransform()
prependPan(t, panTranslation.get()) prependPan(t, panTranslation.get())
prependPinch(t, pinchScale.get(), pinchOrigin.get(), pinchTranslation.get()) prependPinch(t, pinchScale.get(), pinchOrigin.get(), pinchTranslation.get())
prependTransform(t, committedTransform.get()) prependTransform(t, committedTransform.get())
@@ -308,8 +308,8 @@ const getZoomRectAfterDoubleTap = (
// Next, we'll be calculating the rectangle to "zoom into" in screen coordinates. // Next, we'll be calculating the rectangle to "zoom into" in screen coordinates.
// We already know the zoom level, so this gives us the rectangle size. // We already know the zoom level, so this gives us the rectangle size.
let rectWidth = screenSize.width / zoom const rectWidth = screenSize.width / zoom
let rectHeight = screenSize.height / zoom const rectHeight = screenSize.height / zoom
// Before we settle on the zoomed rect, figure out the safe area it has to be inside. // Before we settle on the zoomed rect, figure out the safe area it has to be inside.
// We don't want to introduce new black bars or make existing black bars unbalanced. // We don't want to introduce new black bars or make existing black bars unbalanced.
+1 -1
View File
@@ -95,7 +95,7 @@ export function ProfileLists({
} else if (isEmpty) { } else if (isEmpty) {
items = items.concat([EMPTY]) items = items.concat([EMPTY])
} else if (data?.pages) { } else if (data?.pages) {
for (const page of data?.pages) { for (const page of data.pages) {
items = items.concat(page.lists) items = items.concat(page.lists)
} }
} else if (isError && !isEmpty) { } else if (isError && !isEmpty) {
@@ -76,7 +76,7 @@ export function NotificationFeed({
if (isEmpty) { if (isEmpty) {
arr = arr.concat([EMPTY_FEED_ITEM]) arr = arr.concat([EMPTY_FEED_ITEM])
} else if (data) { } else if (data) {
for (const page of data?.pages) { for (const page of data.pages) {
arr = arr.concat(page.items) arr = arr.concat(page.items)
} }
} }
+1 -1
View File
@@ -54,7 +54,7 @@ export function Pager({
// case we should preserve and restore scroll), or if it is somewhere below in the // case we should preserve and restore scroll), or if it is somewhere below in the
// viewport (in which case a scroll jump would be jarring). We determine this by // viewport (in which case a scroll jump would be jarring). We determine this by
// measuring where the "anchor" element is (which we place just above the tabbar). // measuring where the "anchor" element is (which we place just above the tabbar).
let anchorTop = anchorRef.current const anchorTop = anchorRef.current
? (anchorRef.current as Element).getBoundingClientRect().top ? (anchorRef.current as Element).getBoundingClientRect().top
: -scrollY // If there's no anchor, treat the top of the page as one. : -scrollY // If there's no anchor, treat the top of the page as one.
const isSticking = anchorTop <= 5 // This would be 0 if browser scrollTo() was reliable. const isSticking = anchorTop <= 5 // This would be 0 if browser scrollTo() was reliable.
+5 -5
View File
@@ -333,14 +333,14 @@ let PostFeed = ({
}, [enabled, isEmpty, disablePoll, checkForNew]) }, [enabled, isEmpty, disablePoll, checkForNew])
useEffect(() => { useEffect(() => {
let cleanup1: () => void | undefined, cleanup2: () => void | undefined
const subscription = AppState.addEventListener('change', nextAppState => { const subscription = AppState.addEventListener('change', nextAppState => {
// check for new on app foreground // check for new on app foreground
if (nextAppState === 'active') { if (nextAppState === 'active') {
checkForNew() checkForNew()
} }
}) })
cleanup1 = () => subscription.remove() const cleanup1 = () => subscription.remove()
let cleanup2: (() => void) | undefined
if (pollInterval) { if (pollInterval) {
// check for new on interval // check for new on interval
const i = setInterval(() => { const i = setInterval(() => {
@@ -349,7 +349,7 @@ let PostFeed = ({
cleanup2 = () => clearInterval(i) cleanup2 = () => clearInterval(i)
} }
return () => { return () => {
cleanup1?.() cleanup1()
cleanup2?.() cleanup2?.()
} }
}, [pollInterval, checkForNew]) }, [pollInterval, checkForNew])
@@ -402,7 +402,7 @@ let PostFeed = ({
feedKind = 'profile' feedKind = 'profile'
} }
let arr: FeedRow[] = [] const arr: FeedRow[] = []
if (KNOWN_SHUTDOWN_FEEDS.includes(feedUriOrActorDid)) { if (KNOWN_SHUTDOWN_FEEDS.includes(feedUriOrActorDid)) {
arr.push({ arr.push({
type: 'feedShutdownMsg', type: 'feedShutdownMsg',
@@ -481,7 +481,7 @@ let PostFeed = ({
}) })
} }
} else { } else {
for (const page of data?.pages) { for (const page of data.pages) {
for (const slice of page.slices) { for (const slice of page.slices) {
sliceIndex++ sliceIndex++
+1 -1
View File
@@ -422,7 +422,7 @@ function useResizeObserver(
} }
const resizeObserver = new ResizeObserver(entries => { const resizeObserver = new ResizeObserver(entries => {
batchedUpdates(() => { batchedUpdates(() => {
for (let entry of entries) { for (const entry of entries) {
const rect = entry.contentRect const rect = entry.contentRect
handleResize(rect.width, rect.height) handleResize(rect.width, rect.height)
} }
+4
View File
@@ -179,14 +179,18 @@ const emitter = new EventEmitter()
if (isWeb) { if (isWeb) {
const originalScroll = window.scroll const originalScroll = window.scroll
window.scroll = function () { window.scroll = function () {
emitter.emit('forced-scroll') emitter.emit('forced-scroll')
// eslint-disable-next-line prefer-rest-params
return originalScroll.apply(this, arguments as any) return originalScroll.apply(this, arguments as any)
} }
const originalScrollTo = window.scrollTo const originalScrollTo = window.scrollTo
window.scrollTo = function () { window.scrollTo = function () {
emitter.emit('forced-scroll') emitter.emit('forced-scroll')
// eslint-disable-next-line prefer-rest-params
return originalScrollTo.apply(this, arguments as any) return originalScrollTo.apply(this, arguments as any)
} }
} }
+6 -2
View File
@@ -153,9 +153,13 @@ export function Button({
async (event: GestureResponderEvent) => { async (event: GestureResponderEvent) => {
event.stopPropagation() event.stopPropagation()
event.preventDefault() event.preventDefault()
withLoading && setIsLoading(true) if (withLoading) {
setIsLoading(true)
}
await onPress?.(event) await onPress?.(event)
withLoading && setIsLoading(false) if (withLoading) {
setIsLoading(false)
}
}, },
[onPress, withLoading], [onPress, withLoading],
) )
+3 -1
View File
@@ -53,7 +53,9 @@ export function ListsScreen({}: Props) {
name: urip.hostname, name: urip.hostname,
rkey: urip.rkey, rkey: urip.rkey,
}) })
} catch {} } catch {
// no-op
}
}, },
[navigation], [navigation],
) )
+3 -1
View File
@@ -53,7 +53,9 @@ export function ModerationModlistsScreen({}: Props) {
name: urip.hostname, name: urip.hostname,
rkey: urip.rkey, rkey: urip.rkey,
}) })
} catch {} } catch {
// no-op
}
}, },
[navigation], [navigation],
) )

Some files were not shown because too many files have changed in this diff Show More