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:
+2
-20
@@ -180,34 +180,16 @@ export default tseslint.config(
|
||||
{prefer: 'type-imports', fixStyle: 'inline-type-imports'},
|
||||
],
|
||||
'@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/ban-ts-comment': '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-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-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',
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -111,13 +111,12 @@ export async function prefetchConfig() {
|
||||
return
|
||||
}
|
||||
|
||||
configPrefetchPromise = new Promise(async resolve => {
|
||||
configPrefetchPromise = (async () => {
|
||||
await cacheHydrationPromise
|
||||
const cached = getConfigFromCache()
|
||||
|
||||
if (cached) {
|
||||
logger.debug(`prefetchAgeAssuranceConfig: using cache`)
|
||||
resolve()
|
||||
} else {
|
||||
try {
|
||||
logger.debug(`prefetchAgeAssuranceConfig: resolving...`)
|
||||
@@ -130,11 +129,9 @@ export async function prefetchConfig() {
|
||||
logger.warn(`prefetchAgeAssuranceConfig: failed`, {
|
||||
safeMessage: e.message,
|
||||
})
|
||||
} finally {
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
})
|
||||
})()
|
||||
}
|
||||
export async function refetchConfig() {
|
||||
logger.debug(`refetchConfig: fetching...`)
|
||||
|
||||
@@ -8,6 +8,7 @@ import {type OtherRequiredData} from '#/ageAssurance/data'
|
||||
import {IS_DEV, IS_E2E} from '#/env'
|
||||
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 geolocation: Geolocation | undefined = enabled
|
||||
|
||||
@@ -81,7 +81,11 @@ export function Outer({
|
||||
|
||||
const handleBackgroundPress = React.useCallback(
|
||||
async (e: GestureResponderEvent) => {
|
||||
webOptions?.onBackgroundPress ? webOptions.onBackgroundPress(e) : close()
|
||||
if (webOptions?.onBackgroundPress) {
|
||||
webOptions.onBackgroundPress(e)
|
||||
} else {
|
||||
close()
|
||||
}
|
||||
},
|
||||
[webOptions, close],
|
||||
)
|
||||
|
||||
@@ -167,7 +167,7 @@ function DialogInner({guide}: {guide?: Follow10ProgressGuide}) {
|
||||
const results = hasSearchText
|
||||
? searchResults?.pages.flatMap(p => p.actors)
|
||||
: suggestions?.actors
|
||||
let _items: Item[] = []
|
||||
const _items: Item[] = []
|
||||
|
||||
if (isFetchingSuggestions || isFetchingSearchResults) {
|
||||
const placeholders: Item[] = Array(10)
|
||||
|
||||
@@ -286,7 +286,7 @@ function Bubble({
|
||||
left -= left + cw - maxLeft
|
||||
}
|
||||
|
||||
let tipLeft =
|
||||
const tipLeft =
|
||||
targetMeasurements.x -
|
||||
left +
|
||||
targetMeasurements.width / 2 -
|
||||
|
||||
@@ -334,7 +334,7 @@ async function createProfileRecord(
|
||||
imageUri && imageMime ? uploadBlob(agent, imageUri, imageMime) : undefined
|
||||
|
||||
await agent.upsertProfile(async existing => {
|
||||
let next: Un$Typed<AppBskyActorProfile.Record> = existing ?? {}
|
||||
const next: Un$Typed<AppBskyActorProfile.Record> = existing ?? {}
|
||||
|
||||
if (blobPromise) {
|
||||
const res = await blobPromise
|
||||
|
||||
@@ -185,7 +185,9 @@ export function Update(_props: ScreenProps<ScreenID.Update>) {
|
||||
try {
|
||||
// fire off a confirmation email immediately
|
||||
await requestEmailVerification()
|
||||
} catch {}
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error('EmailDialog: update email failed', {safeMessage: e})
|
||||
|
||||
@@ -99,7 +99,7 @@ function DialogInner({
|
||||
} = useRemoveLiveStatusMutation()
|
||||
|
||||
const {minutesUntilExpiry, expiryDateTime} = useMemo(() => {
|
||||
tick!
|
||||
void tick // revalidate every minute
|
||||
|
||||
const expiry = new Date(status.expiresAt ?? new Date())
|
||||
return {
|
||||
|
||||
@@ -52,7 +52,7 @@ function DialogInner({profile}: {profile: bsky.profile.AnyProfileView}) {
|
||||
|
||||
const time = useCallback(
|
||||
(offset: number) => {
|
||||
tick!
|
||||
void tick // revalidate every minute
|
||||
|
||||
const date = new Date()
|
||||
date.setMinutes(date.getMinutes() + offset)
|
||||
|
||||
@@ -211,8 +211,8 @@ function Inner(props: ReportDialogProps) {
|
||||
logger.metric(
|
||||
'reportDialog:success',
|
||||
{
|
||||
reason: state.selectedOption?.reason!,
|
||||
labeler: state.selectedLabeler?.creator.handle!,
|
||||
reason: state.selectedOption?.reason ?? '',
|
||||
labeler: state.selectedLabeler?.creator.handle ?? '',
|
||||
details: !!state.details,
|
||||
},
|
||||
{statsig: false},
|
||||
@@ -719,7 +719,7 @@ function CategoryCard({
|
||||
{option.title}
|
||||
</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}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
@@ -67,7 +67,7 @@ export async function resolve() {
|
||||
* THIS PROMISE SHOULD NEVER `reject()`! We want the app to proceed with
|
||||
* startup, even if geolocation resolution fails.
|
||||
*/
|
||||
geolocationServicePromise = new Promise(async resolve => {
|
||||
geolocationServicePromise = (async () => {
|
||||
let success = false
|
||||
|
||||
function cacheResponseOrThrow(response: Geolocation | undefined) {
|
||||
@@ -111,10 +111,10 @@ export async function resolve() {
|
||||
},
|
||||
)
|
||||
})
|
||||
} finally {
|
||||
resolve({success})
|
||||
}
|
||||
})
|
||||
|
||||
return {success}
|
||||
})()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ export const USRegionNameToRegionCode: {
|
||||
export function normalizeDeviceLocation(
|
||||
location: LocationGeocodedAddress,
|
||||
): Geolocation {
|
||||
let {isoCountryCode, region} = location
|
||||
const {isoCountryCode, region} = location
|
||||
let regionCode: string | undefined = region ?? undefined
|
||||
|
||||
/*
|
||||
|
||||
@@ -17,7 +17,7 @@ export function useActorStatus(actor?: bsky.profile.AnyProfileView) {
|
||||
const config = useLiveNowConfig()
|
||||
|
||||
return useMemo(() => {
|
||||
tick! // revalidate every minute
|
||||
void tick // revalidate every minute
|
||||
|
||||
if (shadowed && 'status' in shadowed && shadowed.status) {
|
||||
const isValid = validateStatus(shadowed.did, shadowed.status, config)
|
||||
|
||||
@@ -208,7 +208,7 @@ export class FeedViewPostsSlice {
|
||||
|
||||
getAuthors(): AuthorContext {
|
||||
const feedPost = this._feedPost
|
||||
let author: AppBskyActorDefs.ProfileViewBasic = feedPost.post.author
|
||||
const author: AppBskyActorDefs.ProfileViewBasic = feedPost.post.author
|
||||
let parentAuthor: AppBskyActorDefs.ProfileViewBasic | undefined
|
||||
let grandparentAuthor: AppBskyActorDefs.ProfileViewBasic | undefined
|
||||
let rootAuthor: AppBskyActorDefs.ProfileViewBasic | undefined
|
||||
|
||||
@@ -106,7 +106,7 @@ async function loggedOutFetch({
|
||||
limit: number
|
||||
cursor?: string
|
||||
}) {
|
||||
let contentLangs = getAppLanguageAsContentLanguage()
|
||||
const contentLangs = getAppLanguageAsContentLanguage()
|
||||
|
||||
/**
|
||||
* Copied from our root `Agent` class
|
||||
|
||||
@@ -129,7 +129,7 @@ export class MergeFeedAPI implements FeedAPI {
|
||||
// assemble a response by sampling from feeds with content
|
||||
const posts: AppBskyFeedDefs.FeedViewPost[] = []
|
||||
while (posts.length < limit) {
|
||||
let slice = this.sampleItem()
|
||||
const slice = this.sampleItem()
|
||||
if (slice[0]) {
|
||||
posts.push(slice[0])
|
||||
} else {
|
||||
|
||||
@@ -78,7 +78,7 @@ export async function post(
|
||||
const writes: $Typed<ComAtprotoRepoApplyWrites.Create>[] = []
|
||||
const uris: string[] = []
|
||||
|
||||
let now = new Date()
|
||||
const now = new Date()
|
||||
let tid: TID | undefined
|
||||
|
||||
for (let i = 0; i < thread.posts.length; i++) {
|
||||
|
||||
@@ -229,5 +229,7 @@ export async function imageToThumb(
|
||||
if (img) {
|
||||
return await createComposerImage(img)
|
||||
}
|
||||
} catch {}
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ export function isPlainArray(value: unknown) {
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
return false
|
||||
}
|
||||
@@ -85,7 +85,7 @@ export function isPlainObject(o: any): o is Object {
|
||||
}
|
||||
|
||||
// If constructor does not have an Object-specific method
|
||||
if (!prot.hasOwnProperty('isPrototypeOf')) {
|
||||
if (!Object.prototype.hasOwnProperty.call(prot, 'isPrototypeOf')) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import {getCurrentRoute} from '#/lib/routes/helpers'
|
||||
|
||||
export function useNavigationTabState() {
|
||||
return useNavigationState(state => {
|
||||
let currentRoute = state ? getCurrentRoute(state).name : 'Home'
|
||||
const currentRoute = state ? getCurrentRoute(state).name : 'Home'
|
||||
return {
|
||||
isAtHome: currentRoute === 'Home',
|
||||
isAtSearch: currentRoute === 'Search',
|
||||
|
||||
@@ -8,6 +8,7 @@ import {useCallback, useInsertionEffect, useRef} from 'react'
|
||||
//
|
||||
// Also, you should avoid calling the returned function during rendering
|
||||
// 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 {
|
||||
const ref = useRef(fn)
|
||||
useInsertionEffect(() => {
|
||||
|
||||
@@ -18,7 +18,7 @@ export function useTabFocusEffect(
|
||||
|
||||
useEffect(() => {
|
||||
// check if inside
|
||||
let v = getTabState(state, tabName) !== TabState.Outside
|
||||
const v = getTabState(state, tabName) !== TabState.Outside
|
||||
if (v !== isInside) {
|
||||
// fire
|
||||
setIsInside(v)
|
||||
|
||||
@@ -50,7 +50,7 @@ export async function saveImageToMediaLibrary(_opts: {uri: string}) {
|
||||
}
|
||||
|
||||
export async function getImageDim(path: string): Promise<Dimensions> {
|
||||
var img = document.createElement('img')
|
||||
const img = document.createElement('img')
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
img.onload = resolve
|
||||
img.onerror = reject
|
||||
@@ -139,8 +139,8 @@ function createResizedImage(
|
||||
} else if (mode === 'contain') {
|
||||
scale = img.width > img.height ? width / img.width : height / img.height
|
||||
}
|
||||
let w = img.width * scale
|
||||
let h = img.height * scale
|
||||
const w = img.width * scale
|
||||
const h = img.height * scale
|
||||
|
||||
canvas.width = w
|
||||
canvas.height = h
|
||||
|
||||
@@ -39,7 +39,7 @@ export class Router<T extends Record<string, any>> {
|
||||
|
||||
function createRoute(pattern: string): Route {
|
||||
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)
|
||||
return `(?<${name}>[^/]+)`
|
||||
})
|
||||
|
||||
@@ -131,8 +131,8 @@ function toStringRecord<E extends keyof MetricEvents>(
|
||||
metadata: MetricEvents[E] & FlatJSONRecord,
|
||||
): Record<string, string> {
|
||||
const record: Record<string, string> = {}
|
||||
for (let key in metadata) {
|
||||
if (metadata.hasOwnProperty(key)) {
|
||||
for (const key in metadata) {
|
||||
if (Object.hasOwn(metadata, key)) {
|
||||
if (typeof metadata[key] === 'string') {
|
||||
record[key] = metadata[key]
|
||||
} else {
|
||||
|
||||
@@ -6,6 +6,7 @@ import {type ModerationUI} from '@atproto/api'
|
||||
// \u2611 = ☑
|
||||
const CHECK_MARKS_RE = /[\u2705\u2713\u2714\u2611]/gu
|
||||
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
|
||||
const MULTIPLE_SPACES_RE = /[\s][\s\u200B]+/g
|
||||
|
||||
|
||||
@@ -426,7 +426,7 @@ export function parseEmbedPlayerFromUrl(
|
||||
// link shortened flickr path
|
||||
if (urlp.hostname === 'flic.kr') {
|
||||
const b58alph = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'
|
||||
let [__, type, idBase58Enc] = urlp.pathname.split('/')
|
||||
const [__, type, idBase58Enc] = urlp.pathname.split('/')
|
||||
let id = 0n
|
||||
for (const char of idBase58Enc) {
|
||||
const nextIdx = b58alph.indexOf(char)
|
||||
@@ -439,7 +439,7 @@ export function parseEmbedPlayerFromUrl(
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'go':
|
||||
case 'go': {
|
||||
const formattedGroupId = `${id}`
|
||||
return {
|
||||
type: 'flickr_album',
|
||||
@@ -449,6 +449,7 @@ export function parseEmbedPlayerFromUrl(
|
||||
-2,
|
||||
)}@N${formattedGroupId.slice(-2)}`,
|
||||
}
|
||||
}
|
||||
case 's':
|
||||
return {
|
||||
type: 'flickr_album',
|
||||
@@ -537,13 +538,13 @@ export function parseTenorGif(urlp: URL):
|
||||
return {success: false}
|
||||
}
|
||||
|
||||
let [__, id, filename] = urlp.pathname.split('/')
|
||||
const [, initialId, initialFilename] = urlp.pathname.split('/')
|
||||
|
||||
if (!id || !filename) {
|
||||
if (!initialId || !initialFilename) {
|
||||
return {success: false}
|
||||
}
|
||||
|
||||
if (!id.includes('AAAAC')) {
|
||||
if (!initialId.includes('AAAAC')) {
|
||||
return {success: false}
|
||||
}
|
||||
|
||||
@@ -559,16 +560,19 @@ export function parseTenorGif(urlp: URL):
|
||||
width: Number(w),
|
||||
}
|
||||
|
||||
let id: string
|
||||
let filename: string
|
||||
if (isWeb) {
|
||||
if (isSafari) {
|
||||
id = id.replace('AAAAC', 'AAAP1')
|
||||
filename = filename.replace('.gif', '.mp4')
|
||||
id = initialId.replace('AAAAC', 'AAAP1')
|
||||
filename = initialFilename.replace('.gif', '.mp4')
|
||||
} else {
|
||||
id = id.replace('AAAAC', 'AAAP3')
|
||||
filename = filename.replace('.gif', '.webm')
|
||||
id = initialId.replace('AAAAC', 'AAAP3')
|
||||
filename = initialFilename.replace('.gif', '.webm')
|
||||
}
|
||||
} else {
|
||||
id = id.replace('AAAAC', 'AAAAM')
|
||||
id = initialId.replace('AAAAC', 'AAAAM')
|
||||
filename = initialFilename
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -7,7 +7,7 @@ export function getMentionAt(
|
||||
text: string,
|
||||
cursorPos: number,
|
||||
): FoundMention | undefined {
|
||||
let re = /(^|\s)@([a-z0-9.-]*)/gi
|
||||
const re = /(^|\s)@([a-z0-9.-]*)/gi
|
||||
let match
|
||||
while ((match = re.exec(text))) {
|
||||
const spaceOffset = match[1].length
|
||||
|
||||
@@ -24,9 +24,9 @@ export function niceDate(
|
||||
}
|
||||
|
||||
export function getAge(birthDate: Date): number {
|
||||
var today = new Date()
|
||||
var age = today.getFullYear() - birthDate.getFullYear()
|
||||
var m = today.getMonth() - birthDate.getMonth()
|
||||
const today = new Date()
|
||||
let age = today.getFullYear() - birthDate.getFullYear()
|
||||
const m = today.getMonth() - birthDate.getMonth()
|
||||
if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
|
||||
age--
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ const TRUSTED_REGEX = new RegExp(
|
||||
|
||||
export function isValidDomain(str: string): boolean {
|
||||
return !!TLDs.find(tld => {
|
||||
let i = str.lastIndexOf(tld)
|
||||
const i = str.lastIndexOf(tld)
|
||||
if (i === -1) {
|
||||
return false
|
||||
}
|
||||
@@ -123,7 +123,9 @@ export function isBskyPostUrl(url: string): boolean {
|
||||
return /profile\/(?<name>[^/]+)\/post\/(?<rkey>[^/]+)/i.test(
|
||||
urlp.pathname,
|
||||
)
|
||||
} catch {}
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -135,7 +137,9 @@ export function isBskyCustomFeedUrl(url: string): boolean {
|
||||
return /profile\/(?<name>[^/]+)\/feed\/(?<rkey>[^/]+)/i.test(
|
||||
urlp.pathname,
|
||||
)
|
||||
} catch {}
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -291,10 +295,14 @@ export function labelToDomain(label: string): string | undefined {
|
||||
}
|
||||
try {
|
||||
return new URL(label).hostname.toLowerCase()
|
||||
} catch {}
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
try {
|
||||
return new URL('https://' + label).hostname.toLowerCase()
|
||||
} catch {}
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
@@ -306,7 +314,7 @@ export function isPossiblyAUrl(str: string): boolean {
|
||||
if (str.startsWith('https://')) {
|
||||
return true
|
||||
}
|
||||
const [firstWord] = str.split(/[\s\/]/)
|
||||
const [firstWord] = str.split(/[\s/]/)
|
||||
return isValidDomain(firstWord)
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
const realConsoleError = console.error
|
||||
const thrownErrors = new WeakSet()
|
||||
console.error = function consoleErrorWrapper(msgOrError) {
|
||||
console.error = function consoleErrorWrapper(
|
||||
...args: Parameters<typeof console.error>
|
||||
) {
|
||||
const msgOrError = args[0]
|
||||
if (
|
||||
typeof msgOrError === 'string' &&
|
||||
msgOrError.startsWith('Unexpected text node')
|
||||
@@ -28,7 +31,7 @@ if (process.env.NODE_ENV !== 'production') {
|
||||
thrownErrors.add(err)
|
||||
throw err
|
||||
} else if (!thrownErrors.has(msgOrError)) {
|
||||
return realConsoleError.apply(this, arguments as any)
|
||||
return realConsoleError.apply(this, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,9 @@ function BookmarksInner() {
|
||||
if (isFetchingNextPage || !hasNextPage || error) return
|
||||
try {
|
||||
await fetchNextPage()
|
||||
} catch {}
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
}, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
|
||||
|
||||
const items = useMemo(() => {
|
||||
|
||||
@@ -29,7 +29,7 @@ export function NoFeedsPinned({
|
||||
const addRecommendedFeeds = React.useCallback(async () => {
|
||||
let skippedTimeline = false
|
||||
let skippedDiscover = false
|
||||
let remainingSavedFeeds = []
|
||||
const remainingSavedFeeds = []
|
||||
|
||||
// remove first instance of both timeline and discover, since we're going to overwrite them
|
||||
for (const savedFeed of preferences.savedFeeds) {
|
||||
|
||||
@@ -156,7 +156,7 @@ export function MessageInputEmbed({
|
||||
</View>
|
||||
)
|
||||
break
|
||||
case 'success':
|
||||
case 'success': {
|
||||
const itemUrip = new AtUri(post.uri)
|
||||
const itemHref = makeProfileLink(post.author, 'post', itemUrip.rkey)
|
||||
|
||||
@@ -203,6 +203,7 @@ export function MessageInputEmbed({
|
||||
</View>
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -144,7 +144,7 @@ export function StepFinished() {
|
||||
: undefined
|
||||
|
||||
await agent.upsertProfile(async existing => {
|
||||
let next: Un$Typed<AppBskyActorProfile.Record> = existing ?? {}
|
||||
const next: Un$Typed<AppBskyActorProfile.Record> = existing ?? {}
|
||||
|
||||
if (blobPromise) {
|
||||
const res = await blobPromise
|
||||
|
||||
@@ -82,7 +82,7 @@ export function StarterPackCard({
|
||||
setIsFollowingAll(true)
|
||||
setIsProcessing(false)
|
||||
batchedUpdates(() => {
|
||||
for (let did of dids) {
|
||||
for (const did of dids) {
|
||||
updateProfileShadow(queryClient, did, {
|
||||
followingUri: followUris.get(did),
|
||||
})
|
||||
|
||||
@@ -375,7 +375,7 @@ export function Explore({
|
||||
if (suggestedUsers.actors.length > 0 && moderationOpts) {
|
||||
// Currently the responses contain duplicate items.
|
||||
// Needs to be fixed on backend, but let's dedupe to be safe.
|
||||
let seen = new Set()
|
||||
const seen = new Set()
|
||||
const profileItems: ExploreScreenItems[] = []
|
||||
for (const actor of suggestedUsers.actors) {
|
||||
// checking for following still necessary if search data is used
|
||||
@@ -439,7 +439,7 @@ export function Explore({
|
||||
|
||||
if (useFullExperience) {
|
||||
if (suggestedFeeds && preferences) {
|
||||
let seen = new Set()
|
||||
const seen = new Set()
|
||||
const feedItems: ExploreScreenItems[] = []
|
||||
for (const feed of suggestedFeeds.feeds) {
|
||||
if (!seen.has(feed.uri)) {
|
||||
@@ -527,7 +527,7 @@ export function Explore({
|
||||
if (feeds && preferences) {
|
||||
// Currently the responses contain duplicate items.
|
||||
// Needs to be fixed on backend, but let's dedupe to be safe.
|
||||
let seen = new Set()
|
||||
const seen = new Set()
|
||||
const feedItems: ExploreScreenItems[] = []
|
||||
for (const page of feeds.pages) {
|
||||
for (const feed of page.feeds) {
|
||||
|
||||
@@ -248,7 +248,7 @@ let SearchScreenPostResults = ({
|
||||
return results?.pages.flatMap(page => page.posts) || []
|
||||
}, [results])
|
||||
const items = useMemo(() => {
|
||||
let temp: SearchResultSlice[] = []
|
||||
const temp: SearchResultSlice[] = []
|
||||
|
||||
const seenUris = new Set()
|
||||
for (const post of posts) {
|
||||
|
||||
@@ -10,7 +10,7 @@ export function parseSearchQuery(rawQuery: string) {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
base = base.replace(param, '')
|
||||
|
||||
@@ -123,7 +123,7 @@ export function is18(date: Date) {
|
||||
}
|
||||
|
||||
export function reducer(s: SignupState, a: SignupAction): SignupState {
|
||||
let next = {...s}
|
||||
const next = {...s}
|
||||
|
||||
switch (a.type) {
|
||||
case 'prev': {
|
||||
@@ -330,11 +330,11 @@ export function useSubmitSignup() {
|
||||
|
||||
/*
|
||||
* 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'})
|
||||
} catch (e: any) {
|
||||
let errMsg = e.toString()
|
||||
const errMsg = e.toString()
|
||||
if (e instanceof ComAtprotoServerCreateAccount.InvalidInviteCodeError) {
|
||||
dispatch({
|
||||
type: 'setError',
|
||||
|
||||
@@ -377,7 +377,7 @@ function Header({
|
||||
|
||||
setIsProcessing(false)
|
||||
batchedUpdates(() => {
|
||||
for (let did of dids) {
|
||||
for (const did of dids) {
|
||||
updateProfileShadow(queryClient, did, {
|
||||
followingUri: followUris.get(did),
|
||||
})
|
||||
|
||||
@@ -74,8 +74,9 @@ export function Header({
|
||||
break
|
||||
}
|
||||
case 'author':
|
||||
// TODO
|
||||
// falls through
|
||||
default: {
|
||||
// TODO: implement author header
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+8
-8
@@ -158,7 +158,7 @@ export function updatePostShadow(
|
||||
value: Partial<PostShadow>,
|
||||
) {
|
||||
const cachedPosts = findPostsInCache(queryClient, uri)
|
||||
for (let post of cachedPosts) {
|
||||
for (const post of cachedPosts) {
|
||||
shadows.set(post, {...shadows.get(post), ...value})
|
||||
}
|
||||
batchedUpdates(() => {
|
||||
@@ -170,28 +170,28 @@ function* findPostsInCache(
|
||||
queryClient: QueryClient,
|
||||
uri: string,
|
||||
): Generator<AppBskyFeedDefs.PostView, void> {
|
||||
for (let post of findAllPostsInFeedQueryData(queryClient, uri)) {
|
||||
for (const post of findAllPostsInFeedQueryData(queryClient, uri)) {
|
||||
yield post
|
||||
}
|
||||
for (let post of findAllPostsInNotifsQueryData(queryClient, uri)) {
|
||||
for (const post of findAllPostsInNotifsQueryData(queryClient, uri)) {
|
||||
yield post
|
||||
}
|
||||
for (let post of findAllPostsInThreadV2QueryData(queryClient, uri)) {
|
||||
for (const post of findAllPostsInThreadV2QueryData(queryClient, uri)) {
|
||||
yield post
|
||||
}
|
||||
for (let post of findAllPostsInSearchQueryData(queryClient, uri)) {
|
||||
for (const post of findAllPostsInSearchQueryData(queryClient, uri)) {
|
||||
yield post
|
||||
}
|
||||
for (let post of findAllPostsInQuoteQueryData(queryClient, uri)) {
|
||||
for (const post of findAllPostsInQuoteQueryData(queryClient, uri)) {
|
||||
yield post
|
||||
}
|
||||
for (let post of findAllPostsInExploreFeedPreviewsQueryData(
|
||||
for (const post of findAllPostsInExploreFeedPreviewsQueryData(
|
||||
queryClient,
|
||||
uri,
|
||||
)) {
|
||||
yield post
|
||||
}
|
||||
for (let post of findAllPostsInBookmarksQueryData(queryClient, uri)) {
|
||||
for (const post of findAllPostsInBookmarksQueryData(queryClient, uri)) {
|
||||
yield post
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -195,7 +195,7 @@ export function updateProfileShadow(
|
||||
value: Partial<ProfileShadow>,
|
||||
) {
|
||||
const cachedProfiles = findProfilesInCache(queryClient, did)
|
||||
for (let profile of cachedProfiles) {
|
||||
for (const profile of cachedProfiles) {
|
||||
shadows.set(profile, {...shadows.get(profile), ...value})
|
||||
}
|
||||
batchedUpdates(() => {
|
||||
|
||||
@@ -276,7 +276,7 @@ function sendOrAggregateInteractionsForStats(
|
||||
interactions: AppBskyFeedDefs.Interaction[],
|
||||
feed: string,
|
||||
) {
|
||||
for (let interaction of interactions) {
|
||||
for (const interaction of interactions) {
|
||||
switch (interaction.event) {
|
||||
// Pressing "Show more" / "Show less" is relatively uncommon so we won't aggregate them.
|
||||
// This lets us send the feed context together with them.
|
||||
|
||||
@@ -46,7 +46,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
)
|
||||
|
||||
const closeLightbox = useNonReactiveCallback(() => {
|
||||
let wasActive = !!activeLightbox
|
||||
const wasActive = !!activeLightbox
|
||||
setActiveLightbox(null)
|
||||
return wasActive
|
||||
})
|
||||
|
||||
@@ -95,7 +95,7 @@ export class Convo {
|
||||
this.convoId = params.convoId
|
||||
this.agent = params.agent
|
||||
this.events = params.events
|
||||
this.senderUserDid = params.agent.session?.did!
|
||||
this.senderUserDid = params.agent.session?.did ?? ''
|
||||
|
||||
if (params.placeholderData) {
|
||||
this.setupPlaceholderData(params.placeholderData)
|
||||
@@ -557,11 +557,7 @@ export class Convo {
|
||||
async fetchConvo() {
|
||||
if (this.pendingFetchConvo) return this.pendingFetchConvo
|
||||
|
||||
this.pendingFetchConvo = new Promise<{
|
||||
convo: ChatBskyConvoDefs.ConvoView
|
||||
sender: ChatBskyActorDefs.ProfileViewBasic | undefined
|
||||
recipients: ChatBskyActorDefs.ProfileViewBasic[]
|
||||
}>(async (resolve, reject) => {
|
||||
this.pendingFetchConvo = (async () => {
|
||||
try {
|
||||
const response = await networkRetry(2, () => {
|
||||
return this.agent.api.chat.bsky.convo.getConvo(
|
||||
@@ -574,17 +570,15 @@ export class Convo {
|
||||
|
||||
const convo = response.data.convo
|
||||
|
||||
resolve({
|
||||
return {
|
||||
convo,
|
||||
sender: convo.members.find(m => m.did === this.senderUserDid),
|
||||
recipients: convo.members.filter(m => m.did !== this.senderUserDid),
|
||||
})
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
}
|
||||
} finally {
|
||||
this.pendingFetchConvo = undefined
|
||||
}
|
||||
})
|
||||
})()
|
||||
|
||||
return this.pendingFetchConvo
|
||||
}
|
||||
|
||||
@@ -352,7 +352,7 @@ export class MessagesEventBus {
|
||||
const {logs: events} = response.data
|
||||
|
||||
let needsEmit = false
|
||||
let batch: ChatBskyConvoGetLog.OutputSchema['logs'] = []
|
||||
const batch: ChatBskyConvoGetLog.OutputSchema['logs'] = []
|
||||
|
||||
for (const ev of events) {
|
||||
/*
|
||||
|
||||
@@ -60,7 +60,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
})
|
||||
|
||||
const closeModal = useNonReactiveCallback(() => {
|
||||
let wasActive = activeModals.length > 0
|
||||
const wasActive = activeModals.length > 0
|
||||
setActiveModals(modals => {
|
||||
return modals.slice(0, -1)
|
||||
})
|
||||
@@ -68,7 +68,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
})
|
||||
|
||||
const closeAllModals = useNonReactiveCallback(() => {
|
||||
let wasActive = activeModals.length > 0
|
||||
const wasActive = activeModals.length > 0
|
||||
setActiveModals([])
|
||||
return wasActive
|
||||
})
|
||||
|
||||
@@ -108,7 +108,7 @@ function computeSuggestions({
|
||||
searched?: AppBskyActorDefs.ProfileViewBasic[]
|
||||
moderationOpts: ModerationOpts
|
||||
}) {
|
||||
let items: AppBskyActorDefs.ProfileViewBasic[] = []
|
||||
const items: AppBskyActorDefs.ProfileViewBasic[] = []
|
||||
for (const item of searched) {
|
||||
if (!items.find(item2 => item2.handle === item.handle)) {
|
||||
items.push(item)
|
||||
|
||||
@@ -135,7 +135,7 @@ export function* findAllPostsInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const bookmark of page.bookmarks) {
|
||||
if (
|
||||
!bsky.dangerousIsType<AppBskyFeedDefs.PostView>(
|
||||
|
||||
@@ -364,7 +364,7 @@ export function* findAllPostsInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const item of page.posts) {
|
||||
if (didOrHandleUriMatches(atUri, item.post)) {
|
||||
yield item.post
|
||||
@@ -420,7 +420,7 @@ export function* findAllProfilesInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const item of page.posts) {
|
||||
if (item.post.author.did === did) {
|
||||
yield item.post.author
|
||||
|
||||
@@ -439,7 +439,7 @@ export function usePinnedFeedsInfos() {
|
||||
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.
|
||||
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
|
||||
const result: SavedFeedSourceInfo[] = []
|
||||
for (let pinnedItem of pinnedItems) {
|
||||
for (const pinnedItem of pinnedItems) {
|
||||
const feedInfo = resolved.get(pinnedItem.value)
|
||||
if (feedInfo) {
|
||||
result.push({
|
||||
@@ -590,7 +590,7 @@ export function useSavedFeeds() {
|
||||
})
|
||||
|
||||
const result: SavedFeedItem[] = []
|
||||
for (let savedItem of savedItems) {
|
||||
for (const savedItem of savedItems) {
|
||||
if (savedItem.type === 'timeline') {
|
||||
result.push({
|
||||
type: 'timeline',
|
||||
|
||||
@@ -103,7 +103,7 @@ export function* findAllProfilesInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const match of page.matches) {
|
||||
if (match.did === did) {
|
||||
yield match
|
||||
|
||||
@@ -120,7 +120,9 @@ export async function checkHandleAvailability(
|
||||
logger.metric('signup:handleTaken', {typeahead}, {statsig: true})
|
||||
return {available: false} as const
|
||||
}
|
||||
} catch {}
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
logger.metric('signup:handleAvailable', {typeahead}, {statsig: true})
|
||||
return {available: true} as const
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export function* findAllProfilesInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const follow of page.followers) {
|
||||
if (follow.did === did) {
|
||||
yield follow
|
||||
|
||||
@@ -103,7 +103,7 @@ export function* findAllProfilesInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
if (page.list.creator.did === did) {
|
||||
yield page.list.creator
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ export function* findAllProfilesInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const block of page.blocks) {
|
||||
if (block.did === did) {
|
||||
yield block
|
||||
|
||||
@@ -21,7 +21,7 @@ export function useMyListsQuery(filter: MyListsFilter) {
|
||||
staleTime: STALE.MINUTES.ONE,
|
||||
queryKey: RQKEY(filter),
|
||||
async queryFn() {
|
||||
let lists: AppBskyGraphDefs.ListView[] = []
|
||||
const lists: AppBskyGraphDefs.ListView[] = []
|
||||
const promises = [
|
||||
accumulate(cursor =>
|
||||
agent.app.bsky.graph
|
||||
@@ -66,7 +66,7 @@ export function useMyListsQuery(filter: MyListsFilter) {
|
||||
}
|
||||
const resultset = await Promise.all(promises)
|
||||
for (const res of resultset) {
|
||||
for (let list of res) {
|
||||
for (const list of res) {
|
||||
if (
|
||||
filter === 'curate' &&
|
||||
list.purpose !== 'app.bsky.graph.defs#curatelist'
|
||||
|
||||
@@ -47,7 +47,7 @@ export function* findAllProfilesInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const mute of page.mutes) {
|
||||
if (mute.did === did) {
|
||||
yield mute
|
||||
|
||||
@@ -133,7 +133,7 @@ export function useNotificationFeedQuery(opts: {
|
||||
|
||||
// Keep track of the last run and whether we can reuse
|
||||
// some already selected pages from there.
|
||||
let reusedPages = []
|
||||
const reusedPages = []
|
||||
if (lastRun.current) {
|
||||
const {
|
||||
data: lastData,
|
||||
@@ -141,8 +141,8 @@ export function useNotificationFeedQuery(opts: {
|
||||
result: lastResult,
|
||||
} = lastRun.current
|
||||
let canReuse = true
|
||||
for (let key in selectArgs) {
|
||||
if (selectArgs.hasOwnProperty(key)) {
|
||||
for (const key in selectArgs) {
|
||||
if (Object.hasOwn(selectArgs, key)) {
|
||||
if ((selectArgs as any)[key] !== (lastArgs as any)[key]) {
|
||||
// Can't do reuse anything if any input has changed.
|
||||
canReuse = false
|
||||
@@ -287,7 +287,7 @@ export function* findAllPostsInQueryData(
|
||||
continue
|
||||
}
|
||||
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const item of page.items) {
|
||||
if (item.type !== 'starterpack-joined') {
|
||||
if (item.subject && didOrHandleUriMatches(atUri, item.subject)) {
|
||||
@@ -317,7 +317,7 @@ export function* findAllProfilesInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const item of page.items) {
|
||||
if (
|
||||
(item.type === 'follow' || item.type === 'contact-match') &&
|
||||
|
||||
@@ -71,7 +71,7 @@ export async function fetchPage({
|
||||
)
|
||||
|
||||
// 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
|
||||
// in the UI to avoid relayouts
|
||||
|
||||
@@ -244,7 +244,7 @@ export function usePostFeedQuery(
|
||||
|
||||
// Keep track of the last run and whether we can reuse
|
||||
// some already selected pages from there.
|
||||
let reusedPages = []
|
||||
const reusedPages = []
|
||||
if (lastRun.current) {
|
||||
const {
|
||||
data: lastData,
|
||||
@@ -252,8 +252,8 @@ export function usePostFeedQuery(
|
||||
result: lastResult,
|
||||
} = lastRun.current
|
||||
let canReuse = true
|
||||
for (let key in selectArgs) {
|
||||
if (selectArgs.hasOwnProperty(key)) {
|
||||
for (const key in selectArgs) {
|
||||
if (Object.hasOwn(selectArgs, key)) {
|
||||
if ((selectArgs as any)[key] !== (lastArgs as any)[key]) {
|
||||
// Can't do reuse anything if any input has changed.
|
||||
canReuse = false
|
||||
@@ -510,7 +510,7 @@ export function* findAllPostsInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const item of page.feed) {
|
||||
if (didOrHandleUriMatches(atUri, item.post)) {
|
||||
yield item.post
|
||||
@@ -563,7 +563,7 @@ export function* findAllProfilesInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const item of page.feed) {
|
||||
if (item.post.author.did === did) {
|
||||
yield item.post.author
|
||||
|
||||
@@ -52,7 +52,7 @@ export function* findAllProfilesInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const like of page.likes) {
|
||||
if (like.actor.did === did) {
|
||||
yield like.actor
|
||||
|
||||
@@ -80,7 +80,7 @@ export function* findAllProfilesInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const item of page.posts) {
|
||||
if (item.author.did === did) {
|
||||
yield item.author
|
||||
@@ -108,7 +108,7 @@ export function* findAllPostsInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const post of page.posts) {
|
||||
if (didOrHandleUriMatches(atUri, post)) {
|
||||
yield post
|
||||
|
||||
@@ -55,7 +55,7 @@ export function* findAllProfilesInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const repostedBy of page.repostedBy) {
|
||||
if (repostedBy.did === did) {
|
||||
yield repostedBy
|
||||
|
||||
@@ -54,7 +54,7 @@ export function* findAllProfilesInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const follower of page.followers) {
|
||||
if (follower.did === did) {
|
||||
yield follower
|
||||
|
||||
@@ -63,7 +63,7 @@ export function* findAllProfilesInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const follow of page.follows) {
|
||||
if (follow.did === did) {
|
||||
yield follow
|
||||
|
||||
@@ -621,7 +621,7 @@ export function* findAllProfilesInQueryData(
|
||||
if (!queryData) {
|
||||
continue
|
||||
}
|
||||
for (let profile of queryData.profiles) {
|
||||
for (const profile of queryData.profiles) {
|
||||
if (profile.did === did) {
|
||||
yield profile
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ export function useSearchPostsQuery({
|
||||
|
||||
// Keep track of the last run and whether we can reuse
|
||||
// some already selected pages from there.
|
||||
let reusedPages = []
|
||||
const reusedPages = []
|
||||
if (lastRun.current) {
|
||||
const {
|
||||
data: lastData,
|
||||
@@ -95,8 +95,8 @@ export function useSearchPostsQuery({
|
||||
result: lastResult,
|
||||
} = lastRun.current
|
||||
let canReuse = true
|
||||
for (let key in selectArgs) {
|
||||
if (selectArgs.hasOwnProperty(key)) {
|
||||
for (const key in selectArgs) {
|
||||
if (Object.hasOwn(selectArgs, key)) {
|
||||
if ((selectArgs as any)[key] !== (lastArgs as any)[key]) {
|
||||
// Can't do reuse anything if any input has changed.
|
||||
canReuse = false
|
||||
@@ -156,7 +156,7 @@ export function* findAllPostsInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const post of page.posts) {
|
||||
if (didOrHandleUriMatches(atUri, post)) {
|
||||
yield post
|
||||
@@ -184,7 +184,7 @@ export function* findAllProfilesInQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const post of page.posts) {
|
||||
if (post.author.did === did) {
|
||||
yield post.author
|
||||
|
||||
@@ -119,8 +119,7 @@ export function useCreateStarterPackMutation({
|
||||
descriptionFacets = rt.facets
|
||||
}
|
||||
|
||||
let listRes
|
||||
listRes = await createStarterPackList({
|
||||
const listRes = await createStarterPackList({
|
||||
name,
|
||||
description,
|
||||
profiles,
|
||||
|
||||
@@ -150,7 +150,7 @@ function* findAllProfilesInSuggestedFollowsQueryData(
|
||||
if (!queryData?.pages) {
|
||||
continue
|
||||
}
|
||||
for (const page of queryData?.pages) {
|
||||
for (const page of queryData.pages) {
|
||||
for (const actor of page.actors) {
|
||||
if (actor.did === did) {
|
||||
yield actor
|
||||
|
||||
@@ -70,7 +70,7 @@ export function threadgateAllowUISettingToAllowRecordValue(
|
||||
return undefined
|
||||
}
|
||||
|
||||
let allow: Exclude<AppBskyFeedThreadgate.Record['allow'], undefined> = []
|
||||
const allow: Exclude<AppBskyFeedThreadgate.Record['allow'], undefined> = []
|
||||
|
||||
if (!threadgate.find(v => v.type === 'nobody')) {
|
||||
for (const rule of threadgate) {
|
||||
|
||||
@@ -202,7 +202,7 @@ export function getThreadPlaceholder(
|
||||
uri: string,
|
||||
): $Typed<AppBskyUnspeccedGetPostThreadV2.ThreadItem> | void {
|
||||
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
|
||||
* 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
|
||||
* avoid a notification->post scroll jump.
|
||||
*/
|
||||
for (let post of findAllPostsInNotifsQueryData(queryClient, uri)) {
|
||||
for (const post of findAllPostsInNotifsQueryData(queryClient, uri)) {
|
||||
yield postViewToThreadPlaceholder(post)
|
||||
}
|
||||
for (let post of findAllPostsInFeedQueryData(queryClient, uri)) {
|
||||
for (const post of findAllPostsInFeedQueryData(queryClient, uri)) {
|
||||
yield postViewToThreadPlaceholder(post)
|
||||
}
|
||||
for (let post of findAllPostsInQuoteQueryData(queryClient, uri)) {
|
||||
for (const post of findAllPostsInQuoteQueryData(queryClient, uri)) {
|
||||
yield postViewToThreadPlaceholder(post)
|
||||
}
|
||||
for (let post of findAllPostsInSearchQueryData(queryClient, uri)) {
|
||||
for (const post of findAllPostsInSearchQueryData(queryClient, uri)) {
|
||||
yield postViewToThreadPlaceholder(post)
|
||||
}
|
||||
for (let post of findAllPostsInExploreFeedPreviewsQueryData(
|
||||
for (const post of findAllPostsInExploreFeedPreviewsQueryData(
|
||||
queryClient,
|
||||
uri,
|
||||
)) {
|
||||
|
||||
@@ -128,7 +128,7 @@ export type ThreadItem =
|
||||
* total number of replies, the reply index, etc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
export type TraversalMetadata = {
|
||||
|
||||
@@ -1673,7 +1673,7 @@ describe('session', () => {
|
||||
|
||||
function run(initialState: State, actions: Action[]): State {
|
||||
let state = initialState
|
||||
for (let action of actions) {
|
||||
for (const action of actions) {
|
||||
state = reducer(state, action)
|
||||
}
|
||||
return state
|
||||
|
||||
@@ -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.
|
||||
// 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.
|
||||
let realFetch = globalThis.fetch
|
||||
const realFetch = globalThis.fetch
|
||||
class BskyAppAgent extends BskyAgent {
|
||||
persistSessionHandler: ((event: AtpSessionEvent) => void) | undefined =
|
||||
undefined
|
||||
|
||||
@@ -123,8 +123,8 @@ export function addSessionDebugLog(log: Log) {
|
||||
}
|
||||
}
|
||||
|
||||
let agentIds = new WeakMap<object, string>()
|
||||
let realmId = Math.random().toString(36).slice(2)
|
||||
const agentIds = new WeakMap<object, string>()
|
||||
const realmId = Math.random().toString(36).slice(2)
|
||||
let nextAgentId = 1
|
||||
|
||||
function getAgentId(agent: object) {
|
||||
|
||||
@@ -107,7 +107,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
})
|
||||
|
||||
const closeComposer = useNonReactiveCallback(() => {
|
||||
let wasOpen = !!state
|
||||
const wasOpen = !!state
|
||||
if (wasOpen) {
|
||||
setState(undefined)
|
||||
purgeTemporaryImageFiles()
|
||||
|
||||
@@ -47,7 +47,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
if (isWeb) {
|
||||
try {
|
||||
sessionStorage.setItem('lastSelectedHomeFeed', feed)
|
||||
} catch {}
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
persisted.write('lastSelectedHomeFeed', feed)
|
||||
}, [])
|
||||
|
||||
@@ -36,7 +36,7 @@ const consumedSources = new Map<string, PostSource>()
|
||||
export function setUnstablePostSource(key: string, source: PostSource) {
|
||||
assertValidDevOnly(
|
||||
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})
|
||||
transientSources.set(key, source)
|
||||
@@ -53,7 +53,7 @@ export function useUnstablePostSource(key: string) {
|
||||
const [source] = useState(() => {
|
||||
assertValidDevOnly(
|
||||
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,
|
||||
)
|
||||
const source = consumedSources.get(id) || transientSources.get(key)
|
||||
|
||||
@@ -519,7 +519,7 @@ export const ComposePost = ({
|
||||
} finally {
|
||||
if (postUri) {
|
||||
let index = 0
|
||||
for (let post of thread.posts) {
|
||||
for (const post of thread.posts) {
|
||||
logEvent('post:create', {
|
||||
imageCount:
|
||||
post.embed.media?.type === 'images'
|
||||
@@ -619,7 +619,7 @@ export const ComposePost = ({
|
||||
if (publishOnUpload) {
|
||||
let erroredVideos = 0
|
||||
let uploadingVideos = 0
|
||||
for (let post of thread.posts) {
|
||||
for (const post of thread.posts) {
|
||||
if (post.embed.media?.type === 'video') {
|
||||
const video = post.embed.media.video
|
||||
if (video.status === 'error') {
|
||||
@@ -812,7 +812,7 @@ export const ComposePost = ({
|
||||
)
|
||||
}
|
||||
|
||||
let ComposerPost = React.memo(function ComposerPost({
|
||||
const ComposerPost = React.memo(function ComposerPost({
|
||||
post,
|
||||
dispatch,
|
||||
textInput,
|
||||
|
||||
@@ -413,7 +413,7 @@ export function SelectMediaButton({
|
||||
msg`You can only select one GIF at a time.`,
|
||||
),
|
||||
[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]
|
||||
})
|
||||
|
||||
@@ -198,7 +198,7 @@ export function composerReducer(
|
||||
const indexToRemove = state.thread.posts.findIndex(
|
||||
p => p.id === action.postId,
|
||||
)
|
||||
let nextPosts = [...state.thread.posts]
|
||||
const nextPosts = [...state.thread.posts]
|
||||
if (indexToRemove !== -1) {
|
||||
const postToRemove = state.thread.posts[indexToRemove]
|
||||
if (postToRemove.embed.media?.type === 'video') {
|
||||
|
||||
@@ -392,7 +392,7 @@ function getCompressErrorMessage(e: unknown, _: I18n['_']): string | null {
|
||||
}
|
||||
if (e instanceof VideoTooLargeError) {
|
||||
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})
|
||||
@@ -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)':
|
||||
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:
|
||||
return e.message
|
||||
|
||||
@@ -90,7 +90,7 @@ function iterateUris(str: string, cb: (from: number, to: number) => void) {
|
||||
}
|
||||
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
|
||||
// strip ending puncuation
|
||||
if (/[.,;!?]$/.test(uri)) {
|
||||
|
||||
@@ -17,7 +17,9 @@ export function useWebPreloadEmoji({immediate}: {immediate?: boolean} = {}) {
|
||||
try {
|
||||
const data = (await import('./EmojiPickerData.json')).default
|
||||
init({data})
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
// no-op
|
||||
}
|
||||
}, [])
|
||||
if (immediate) preload()
|
||||
return preload
|
||||
|
||||
@@ -95,7 +95,7 @@ export function ProfileFeedgens({
|
||||
} else if (isEmpty) {
|
||||
items = items.concat([EMPTY])
|
||||
} else if (data?.pages) {
|
||||
for (const page of data?.pages) {
|
||||
for (const page of data.pages) {
|
||||
items = items.concat(page.feeds)
|
||||
}
|
||||
} else if (isError && !isEmpty) {
|
||||
|
||||
@@ -191,7 +191,7 @@ const ImageItem = ({
|
||||
.onEnd(() => {
|
||||
'worklet'
|
||||
// Commit just the pinch.
|
||||
let t = createTransform()
|
||||
const t = createTransform()
|
||||
prependPinch(
|
||||
t,
|
||||
pinchScale.get(),
|
||||
@@ -220,7 +220,7 @@ const ImageItem = ({
|
||||
}
|
||||
|
||||
const nextPanTranslation = {x: e.translationX, y: e.translationY}
|
||||
let t = createTransform()
|
||||
const t = createTransform()
|
||||
prependPan(t, nextPanTranslation)
|
||||
prependPinch(
|
||||
t,
|
||||
@@ -239,7 +239,7 @@ const ImageItem = ({
|
||||
.onEnd(() => {
|
||||
'worklet'
|
||||
// Commit just the pan.
|
||||
let t = createTransform()
|
||||
const t = createTransform()
|
||||
prependPan(t, panTranslation.get())
|
||||
prependTransform(t, committedTransform.get())
|
||||
applyRounding(t)
|
||||
@@ -265,7 +265,7 @@ const ImageItem = ({
|
||||
const [, , committedScale] = readTransform(committedTransform.get())
|
||||
if (committedScale !== 1) {
|
||||
// Go back to 1:1 using the identity vector.
|
||||
let t = createTransform()
|
||||
const t = createTransform()
|
||||
committedTransform.set(withClampedSpring(t))
|
||||
return
|
||||
}
|
||||
@@ -317,7 +317,7 @@ const ImageItem = ({
|
||||
const {scaleAndMoveTransform, isHidden} = transforms.get()
|
||||
// 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.
|
||||
let t = createTransform()
|
||||
const t = createTransform()
|
||||
prependPan(t, panTranslation.get())
|
||||
prependPinch(t, pinchScale.get(), pinchOrigin.get(), pinchTranslation.get())
|
||||
prependTransform(t, committedTransform.get())
|
||||
|
||||
@@ -308,8 +308,8 @@ const getZoomRectAfterDoubleTap = (
|
||||
|
||||
// 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.
|
||||
let rectWidth = screenSize.width / zoom
|
||||
let rectHeight = screenSize.height / zoom
|
||||
const rectWidth = screenSize.width / zoom
|
||||
const rectHeight = screenSize.height / zoom
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -95,7 +95,7 @@ export function ProfileLists({
|
||||
} else if (isEmpty) {
|
||||
items = items.concat([EMPTY])
|
||||
} else if (data?.pages) {
|
||||
for (const page of data?.pages) {
|
||||
for (const page of data.pages) {
|
||||
items = items.concat(page.lists)
|
||||
}
|
||||
} else if (isError && !isEmpty) {
|
||||
|
||||
@@ -76,7 +76,7 @@ export function NotificationFeed({
|
||||
if (isEmpty) {
|
||||
arr = arr.concat([EMPTY_FEED_ITEM])
|
||||
} else if (data) {
|
||||
for (const page of data?.pages) {
|
||||
for (const page of data.pages) {
|
||||
arr = arr.concat(page.items)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export function Pager({
|
||||
// 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
|
||||
// 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
|
||||
: -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.
|
||||
|
||||
@@ -333,14 +333,14 @@ let PostFeed = ({
|
||||
}, [enabled, isEmpty, disablePoll, checkForNew])
|
||||
|
||||
useEffect(() => {
|
||||
let cleanup1: () => void | undefined, cleanup2: () => void | undefined
|
||||
const subscription = AppState.addEventListener('change', nextAppState => {
|
||||
// check for new on app foreground
|
||||
if (nextAppState === 'active') {
|
||||
checkForNew()
|
||||
}
|
||||
})
|
||||
cleanup1 = () => subscription.remove()
|
||||
const cleanup1 = () => subscription.remove()
|
||||
let cleanup2: (() => void) | undefined
|
||||
if (pollInterval) {
|
||||
// check for new on interval
|
||||
const i = setInterval(() => {
|
||||
@@ -349,7 +349,7 @@ let PostFeed = ({
|
||||
cleanup2 = () => clearInterval(i)
|
||||
}
|
||||
return () => {
|
||||
cleanup1?.()
|
||||
cleanup1()
|
||||
cleanup2?.()
|
||||
}
|
||||
}, [pollInterval, checkForNew])
|
||||
@@ -402,7 +402,7 @@ let PostFeed = ({
|
||||
feedKind = 'profile'
|
||||
}
|
||||
|
||||
let arr: FeedRow[] = []
|
||||
const arr: FeedRow[] = []
|
||||
if (KNOWN_SHUTDOWN_FEEDS.includes(feedUriOrActorDid)) {
|
||||
arr.push({
|
||||
type: 'feedShutdownMsg',
|
||||
@@ -481,7 +481,7 @@ let PostFeed = ({
|
||||
})
|
||||
}
|
||||
} else {
|
||||
for (const page of data?.pages) {
|
||||
for (const page of data.pages) {
|
||||
for (const slice of page.slices) {
|
||||
sliceIndex++
|
||||
|
||||
|
||||
@@ -422,7 +422,7 @@ function useResizeObserver(
|
||||
}
|
||||
const resizeObserver = new ResizeObserver(entries => {
|
||||
batchedUpdates(() => {
|
||||
for (let entry of entries) {
|
||||
for (const entry of entries) {
|
||||
const rect = entry.contentRect
|
||||
handleResize(rect.width, rect.height)
|
||||
}
|
||||
|
||||
@@ -179,14 +179,18 @@ const emitter = new EventEmitter()
|
||||
|
||||
if (isWeb) {
|
||||
const originalScroll = window.scroll
|
||||
|
||||
window.scroll = function () {
|
||||
emitter.emit('forced-scroll')
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
return originalScroll.apply(this, arguments as any)
|
||||
}
|
||||
|
||||
const originalScrollTo = window.scrollTo
|
||||
|
||||
window.scrollTo = function () {
|
||||
emitter.emit('forced-scroll')
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
return originalScrollTo.apply(this, arguments as any)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,9 +153,13 @@ export function Button({
|
||||
async (event: GestureResponderEvent) => {
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
withLoading && setIsLoading(true)
|
||||
if (withLoading) {
|
||||
setIsLoading(true)
|
||||
}
|
||||
await onPress?.(event)
|
||||
withLoading && setIsLoading(false)
|
||||
if (withLoading) {
|
||||
setIsLoading(false)
|
||||
}
|
||||
},
|
||||
[onPress, withLoading],
|
||||
)
|
||||
|
||||
@@ -53,7 +53,9 @@ export function ListsScreen({}: Props) {
|
||||
name: urip.hostname,
|
||||
rkey: urip.rkey,
|
||||
})
|
||||
} catch {}
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
},
|
||||
[navigation],
|
||||
)
|
||||
|
||||
@@ -53,7 +53,9 @@ export function ModerationModlistsScreen({}: Props) {
|
||||
name: urip.hostname,
|
||||
rkey: urip.rkey,
|
||||
})
|
||||
} catch {}
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
},
|
||||
[navigation],
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user