diff --git a/app.config.js b/app.config.js index 36af08415..c6b2fc6d4 100644 --- a/app.config.js +++ b/app.config.js @@ -29,8 +29,8 @@ module.exports = function (_config) { const UPDATES_CHANNEL = IS_TESTFLIGHT ? 'testflight' : IS_PRODUCTION - ? 'production' - : undefined + ? 'production' + : undefined const UPDATES_ENABLED = !!UPDATES_CHANNEL const USE_SENTRY = Boolean(process.env.SENTRY_AUTH_TOKEN) diff --git a/bskyembed/tailwind.config.cjs b/bskyembed/tailwind.config.cjs index 0e9f5b8ea..adbeadd4c 100644 --- a/bskyembed/tailwind.config.cjs +++ b/bskyembed/tailwind.config.cjs @@ -1,9 +1,7 @@ /** @type {import('tailwindcss').Config} */ module.exports = { content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], - darkMode: ['variant', [ - '&:is(.dark *):not(:is(.dark .light *))', - ]], + darkMode: ['variant', ['&:is(.dark *):not(:is(.dark .light *))']], theme: { extend: { colors: { diff --git a/bskyembed/tsconfig.snippet.json b/bskyembed/tsconfig.snippet.json index fee21964a..163db7d30 100644 --- a/bskyembed/tsconfig.snippet.json +++ b/bskyembed/tsconfig.snippet.json @@ -1,4 +1,3 @@ - { "compilerOptions": { "target": "ES5", diff --git a/bskylink/src/db/index.ts b/bskylink/src/db/index.ts index 5f201cc07..7fe6aa536 100644 --- a/bskylink/src/db/index.ts +++ b/bskylink/src/db/index.ts @@ -1,27 +1,30 @@ import assert from 'assert' import { Kysely, - KyselyPlugin, + type KyselyPlugin, Migrator, - PluginTransformQueryArgs, - PluginTransformResultArgs, + type PluginTransformQueryArgs, + type PluginTransformResultArgs, PostgresDialect, - QueryResult, - RootOperationNode, - UnknownRow, + type QueryResult, + type RootOperationNode, + type UnknownRow, } from 'kysely' import {default as Pg} from 'pg' import {dbLogger as log} from '../logger.js' import {default as migrations} from './migrations/index.js' import {DbMigrationProvider} from './migrations/provider.js' -import {DbSchema} from './schema.js' +import {type DbSchema} from './schema.js' export class Database { migrator: Migrator destroyed = false - constructor(public db: Kysely, public cfg: PgConfig) { + constructor( + public db: Kysely, + public cfg: PgConfig, + ) { this.migrator = new Migrator({ db, migrationTableSchema: cfg.schema, diff --git a/bskylink/src/index.ts b/bskylink/src/index.ts index ca425eee8..5e7f5444a 100644 --- a/bskylink/src/index.ts +++ b/bskylink/src/index.ts @@ -1,11 +1,11 @@ import events from 'node:events' -import http from 'node:http' +import type http from 'node:http' import cors from 'cors' import express from 'express' -import {createHttpTerminator, HttpTerminator} from 'http-terminator' +import {createHttpTerminator, type HttpTerminator} from 'http-terminator' -import {Config} from './config.js' +import {type Config} from './config.js' import {AppContext} from './context.js' import {default as routes, errorHandler} from './routes/index.js' @@ -17,7 +17,10 @@ export class LinkService { public server?: http.Server private terminator?: HttpTerminator - constructor(public app: express.Application, public ctx: AppContext) {} + constructor( + public app: express.Application, + public ctx: AppContext, + ) {} static async create(cfg: Config): Promise { let app = express() diff --git a/bskyogcard/src/index.ts b/bskyogcard/src/index.ts index ef8d48494..139af01f7 100644 --- a/bskyogcard/src/index.ts +++ b/bskyogcard/src/index.ts @@ -1,10 +1,10 @@ import events from 'node:events' -import http from 'node:http' +import type http from 'node:http' import express from 'express' -import {createHttpTerminator, HttpTerminator} from 'http-terminator' +import {createHttpTerminator, type HttpTerminator} from 'http-terminator' -import {Config} from './config.js' +import {type Config} from './config.js' import {AppContext} from './context.js' import {default as routes, errorHandler} from './routes/index.js' @@ -15,7 +15,10 @@ export class CardService { public server?: http.Server private terminator?: HttpTerminator - constructor(public app: express.Application, public ctx: AppContext) {} + constructor( + public app: express.Application, + public ctx: AppContext, + ) {} static async create(cfg: Config): Promise { let app = express() diff --git a/modules/expo-emoji-picker/src/EmojiPicker.android.tsx b/modules/expo-emoji-picker/src/EmojiPicker.android.tsx index d8493b72e..e980c80fb 100644 --- a/modules/expo-emoji-picker/src/EmojiPicker.android.tsx +++ b/modules/expo-emoji-picker/src/EmojiPicker.android.tsx @@ -12,7 +12,7 @@ const EmojiPicker = ({onEmojiSelected}: EmojiPickerViewProps) => { flex: 1, width: '100%', backgroundColor: scheme === 'dark' ? '#000' : '#fff', - } as const), + }) as const, [scheme], ) diff --git a/package.json b/package.json index e35dd96b1..eddec7928 100644 --- a/package.json +++ b/package.json @@ -266,7 +266,7 @@ "lint-staged": "^13.2.3", "lockfile-lint": "^4.14.0", "metro-react-native-babel-preset": "^0.77.0", - "prettier": "^2.8.3", + "prettier": "^3.6.0", "react-native-dotenv": "^3.4.11", "react-refresh": "^0.14.0", "svgo": "^3.3.2", diff --git a/scripts/post-web-build.js b/scripts/post-web-build.js index 3b1b62dfd..5b7b07c0d 100644 --- a/scripts/post-web-build.js +++ b/scripts/post-web-build.js @@ -9,10 +9,9 @@ const templateFile = path.join( 'scripts.html', ) -const {entrypoints} = require(path.join( - projectRoot, - 'web-build/asset-manifest.json', -)) +const {entrypoints} = require( + path.join(projectRoot, 'web-build/asset-manifest.json'), +) console.log(`Found ${entrypoints.length} entrypoints`) console.log(`Writing ${templateFile}`) diff --git a/src/components/ContextMenu/index.tsx b/src/components/ContextMenu/index.tsx index 4a0814dfe..dd7be13d0 100644 --- a/src/components/ContextMenu/index.tsx +++ b/src/components/ContextMenu/index.tsx @@ -190,7 +190,7 @@ export function Root({children}: {children: React.ReactNode}) { if (item) playHaptic('Light') setHoveredMenuItem(item) }, - } satisfies ContextType), + }) satisfies ContextType, [ measurement, setMeasurement, @@ -710,8 +710,8 @@ export function Item({ const xOffset = position ? position.x : align === 'left' - ? measurement.x - : measurement.x + measurement.width - layout.width + ? measurement.x + : measurement.x + measurement.width - layout.width registerHoverable( id, diff --git a/src/components/Link.tsx b/src/components/Link.tsx index 28cd19418..127b2edfb 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -99,10 +99,10 @@ export function useLink({ return typeof to === 'string' ? convertBskyAppUrlIfNeeded(sanitizeUrl(to)) : to.screen - ? router.matchName(to.screen)?.build(to.params) - : to.href - ? convertBskyAppUrlIfNeeded(sanitizeUrl(to.href)) - : undefined + ? router.matchName(to.screen)?.build(to.params) + : to.href + ? convertBskyAppUrlIfNeeded(sanitizeUrl(to.href)) + : undefined }, [to]) if (!href) { diff --git a/src/components/Post/Embed/ImageEmbed.tsx b/src/components/Post/Embed/ImageEmbed.tsx index 030d237a0..4ed17230e 100644 --- a/src/components/Post/Embed/ImageEmbed.tsx +++ b/src/components/Post/Embed/ImageEmbed.tsx @@ -77,9 +77,9 @@ export function ImageEmbed({ rest.viewContext === PostEmbedViewContext.ThreadHighlighted ? 'none' : rest.viewContext === - PostEmbedViewContext.FeedEmbedRecordWithMedia - ? 'square' - : 'constrained' + PostEmbedViewContext.FeedEmbedRecordWithMedia + ? 'square' + : 'constrained' } image={image} onPress={(containerRef, dims) => onPress(0, [containerRef], [dims])} diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx index 6d14deafc..676b52661 100644 --- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx +++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx @@ -317,8 +317,8 @@ export function Controls({ !focused ? msg`Unmute video` : playing - ? msg`Pause video` - : msg`Play video`, + ? msg`Pause video` + : msg`Play video`, )} accessibilityHint="" style={[ diff --git a/src/components/PostControls/PostMenu/PostMenuItems.tsx b/src/components/PostControls/PostMenu/PostMenuItems.tsx index 01ddd0bcf..f0ef9ed05 100644 --- a/src/components/PostControls/PostMenu/PostMenuItems.tsx +++ b/src/components/PostControls/PostMenu/PostMenuItems.tsx @@ -600,8 +600,8 @@ let PostMenuItems = ({ isDetachPending ? Loader : quoteEmbed.isDetached - ? Eye - : EyeSlash + ? Eye + : EyeSlash } position="right" /> diff --git a/src/components/Select/index.web.tsx b/src/components/Select/index.web.tsx index 0de74c562..a4d3fb80b 100644 --- a/src/components/Select/index.web.tsx +++ b/src/components/Select/index.web.tsx @@ -111,8 +111,8 @@ export function Trigger({children, label}: TriggerProps) { borderColor: focused ? t.palette.primary_500 : hovered - ? t.palette.contrast_100 - : t.palette.contrast_25, + ? t.palette.contrast_100 + : t.palette.contrast_25, }, ])}> {children} diff --git a/src/components/WhoCanReply.tsx b/src/components/WhoCanReply.tsx index 29f4ac5bc..22a514194 100644 --- a/src/components/WhoCanReply.tsx +++ b/src/components/WhoCanReply.tsx @@ -1,9 +1,15 @@ import React from 'react' -import {Keyboard, Platform, StyleProp, View, ViewStyle} from 'react-native' import { - AppBskyFeedDefs, + Keyboard, + Platform, + type StyleProp, + View, + type ViewStyle, +} from 'react-native' +import { + type AppBskyFeedDefs, AppBskyFeedPost, - AppBskyGraphDefs, + type AppBskyGraphDefs, AtUri, } from '@atproto/api' import {msg, Trans} from '@lingui/macro' @@ -13,7 +19,7 @@ import {HITSLOP_10} from '#/lib/constants' import {makeListLink, makeProfileLink} from '#/lib/routes/links' import {isNative} from '#/platform/detection' import { - ThreadgateAllowUISetting, + type ThreadgateAllowUISetting, threadgateViewToAllowUISetting, } from '#/state/queries/threadgate' import {atoms as a, useTheme} from '#/alf' @@ -70,8 +76,8 @@ export function WhoCanReply({post, isThreadAuthor, style}: WhoCanReplyProps) { const description = anyoneCanReply ? _(msg`Everybody can reply`) : noOneCanReply - ? _(msg`Replies disabled`) - : _(msg`Some people can reply`) + ? _(msg`Replies disabled`) + : _(msg`Some people can reply`) const onPressOpen = () => { if (isNative && Keyboard.isVisible()) { diff --git a/src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx b/src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx index 1896ff27d..2b0e86c61 100644 --- a/src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx +++ b/src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx @@ -185,8 +185,8 @@ export function Disable() { state.emailStatus === 'pending' ? Loader : state.emailStatus === 'success' - ? Check - : Envelope + ? Check + : Envelope } /> diff --git a/src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx b/src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx index 7a126792a..bf893701c 100644 --- a/src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx +++ b/src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx @@ -116,8 +116,8 @@ export function Enable() { state.status === 'pending' ? Loader : state.status === 'success' - ? Check - : ShieldIcon + ? Check + : ShieldIcon } /> diff --git a/src/components/dialogs/SearchablePeopleList.tsx b/src/components/dialogs/SearchablePeopleList.tsx index 81655be0f..159f623b9 100644 --- a/src/components/dialogs/SearchablePeopleList.tsx +++ b/src/components/dialogs/SearchablePeopleList.tsx @@ -390,8 +390,8 @@ function DefaultProfileCard({ !enabled ? {opacity: 0.5} : pressed || focused || hovered - ? t.atoms.bg_contrast_25 - : t.atoms.bg, + ? t.atoms.bg_contrast_25 + : t.atoms.bg, ]}> {children} diff --git a/src/components/icons/VerifiedCheck.tsx b/src/components/icons/VerifiedCheck.tsx index 9d0aa9158..a98fdfc5d 100644 --- a/src/components/icons/VerifiedCheck.tsx +++ b/src/components/icons/VerifiedCheck.tsx @@ -3,28 +3,27 @@ import Svg, {Circle, Path} from 'react-native-svg' import {type Props, useCommonSVGProps} from '#/components/icons/common' -export const VerifiedCheck = React.forwardRef(function LogoImpl( - props, - ref, -) { - const {fill, size, style, ...rest} = useCommonSVGProps(props) +export const VerifiedCheck = React.forwardRef( + function LogoImpl(props, ref) { + const {fill, size, style, ...rest} = useCommonSVGProps(props) - return ( - - - - - ) -}) + return ( + + + + + ) + }, +) diff --git a/src/components/icons/VerifierCheck.tsx b/src/components/icons/VerifierCheck.tsx index 143c24b97..4674fdde8 100644 --- a/src/components/icons/VerifierCheck.tsx +++ b/src/components/icons/VerifierCheck.tsx @@ -3,33 +3,32 @@ import Svg, {Path} from 'react-native-svg' import {type Props, useCommonSVGProps} from '#/components/icons/common' -export const VerifierCheck = React.forwardRef(function LogoImpl( - props, - ref, -) { - const {fill, size, style, ...rest} = useCommonSVGProps(props) +export const VerifierCheck = React.forwardRef( + function LogoImpl(props, ref) { + const {fill, size, style, ...rest} = useCommonSVGProps(props) - return ( - - - - - ) -}) + return ( + + + + + ) + }, +) diff --git a/src/components/moderation/ContentHider.tsx b/src/components/moderation/ContentHider.tsx index d3f74c0c5..9e94a413c 100644 --- a/src/components/moderation/ContentHider.tsx +++ b/src/components/moderation/ContentHider.tsx @@ -1,6 +1,6 @@ import React from 'react' -import {StyleProp, View, ViewStyle} from 'react-native' -import {ModerationUI} from '@atproto/api' +import {type StyleProp, View, type ViewStyle} from 'react-native' +import {type ModerationUI} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -148,8 +148,8 @@ function ContentHiderActive({ modui.noOverride ? _(msg`Learn more about the moderation applied to this content`) : override - ? _(msg`Hides the content`) - : _(msg`Shows the content`) + ? _(msg`Hides the content`) + : _(msg`Shows the content`) }> {state => ( {completed ? ( @@ -533,8 +533,8 @@ function StepTitle({ color: active ? 'white' : completed - ? t.palette.primary_700 - : t.atoms.text_contrast_medium.color, + ? t.palette.primary_700 + : t.atoms.text_contrast_medium.color, fontVariant: ['tabular-nums'], width: 24, height: 24, diff --git a/src/components/verification/VerificationCheckButton.tsx b/src/components/verification/VerificationCheckButton.tsx index 9278a3072..2ccc730f6 100644 --- a/src/components/verification/VerificationCheckButton.tsx +++ b/src/components/verification/VerificationCheckButton.tsx @@ -130,8 +130,8 @@ export function Badge({ verifiedByHidden ? t.atoms.bg_contrast_100.backgroundColor : state.profile.isVerified - ? t.palette.primary_500 - : t.atoms.bg_contrast_100.backgroundColor + ? t.palette.primary_500 + : t.atoms.bg_contrast_100.backgroundColor } verifier={state.profile.role === 'verifier'} /> diff --git a/src/components/verification/VerificationsDialog.tsx b/src/components/verification/VerificationsDialog.tsx index 665bf3ba3..447e39e97 100644 --- a/src/components/verification/VerificationsDialog.tsx +++ b/src/components/verification/VerificationsDialog.tsx @@ -64,13 +64,13 @@ function Inner({ ? _(msg`You are verified`) : _(msg`Your verifications`) : state.profile.isVerified - ? _(msg`${userName} is verified`) - : _( - msg({ - message: `${userName}'s verifications`, - comment: `Possessive, meaning "the verifications of {userName}"`, - }), - ) + ? _(msg`${userName} is verified`) + : _( + msg({ + message: `${userName}'s verifications`, + comment: `Possessive, meaning "the verifications of {userName}"`, + }), + ) return ( { r1 === 64 ? String.fromCharCode((bitmap >> 16) & 255) : r2 === 64 - ? String.fromCharCode((bitmap >> 16) & 255, (bitmap >> 8) & 255) - : String.fromCharCode( - (bitmap >> 16) & 255, - (bitmap >> 8) & 255, - bitmap & 255, - ) + ? String.fromCharCode((bitmap >> 16) & 255, (bitmap >> 8) & 255) + : String.fromCharCode( + (bitmap >> 16) & 255, + (bitmap >> 8) & 255, + bitmap & 255, + ) } return result } diff --git a/src/screens/Login/index.tsx b/src/screens/Login/index.tsx index e4e2f43f0..601d76606 100644 --- a/src/screens/Login/index.tsx +++ b/src/screens/Login/index.tsx @@ -49,8 +49,8 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => { requestedAccount ? Forms.Login : accounts.length - ? Forms.ChooseAccount - : Forms.Login, + ? Forms.ChooseAccount + : Forms.Login, ) const { diff --git a/src/screens/Messages/ChatList.tsx b/src/screens/Messages/ChatList.tsx index 9fd54f1b0..388d23ec2 100644 --- a/src/screens/Messages/ChatList.tsx +++ b/src/screens/Messages/ChatList.tsx @@ -155,7 +155,7 @@ export function MessagesScreen({navigation, route}: Props) { profiles: inboxPreviewConvos.slice(0, 3), }, ...conversations.map( - convo => ({type: 'CONVERSATION', conversation: convo} as const), + convo => ({type: 'CONVERSATION', conversation: convo}) as const, ), ] satisfies ListItem[] } diff --git a/src/screens/Onboarding/StepFinished.tsx b/src/screens/Onboarding/StepFinished.tsx index e725b7b80..b30f61053 100644 --- a/src/screens/Onboarding/StepFinished.tsx +++ b/src/screens/Onboarding/StepFinished.tsx @@ -176,8 +176,8 @@ export function StepFinished() { avatarResult: profileStepResults.isCreatedAvatar ? 'created' : profileStepResults.image - ? 'uploaded' - : 'default', + ? 'uploaded' + : 'default', }) })(), requestNotificationsPermission('AfterOnboarding'), diff --git a/src/screens/Profile/Header/ProfileHeaderLabeler.tsx b/src/screens/Profile/Header/ProfileHeaderLabeler.tsx index cdb95423f..2185fd9c8 100644 --- a/src/screens/Profile/Header/ProfileHeaderLabeler.tsx +++ b/src/screens/Profile/Header/ProfileHeaderLabeler.tsx @@ -214,8 +214,8 @@ let ProfileHeaderLabeler = ({ ? t.palette.contrast_50 : t.palette.contrast_25 : state.hovered || state.pressed - ? tokens.color.temp_purple_dark - : tokens.color.temp_purple, + ? tokens.color.temp_purple_dark + : tokens.color.temp_purple, }, ]}> ({type: 'topBorder', key: 'top-border'} as const), + () => ({type: 'topBorder', key: 'top-border'}) as const, [], ) const trendingTopicsModule = useMemo( - () => ({type: 'trendingTopics', key: 'trending-topics'} as const), + () => ({type: 'trendingTopics', key: 'trending-topics'}) as const, [], ) const suggestedFollowsModule = useMemo(() => { diff --git a/src/screens/Settings/AppIconSettings/useAppIconSets.ts b/src/screens/Settings/AppIconSettings/useAppIconSets.ts index 5c0ff698e..fd3caeb30 100644 --- a/src/screens/Settings/AppIconSettings/useAppIconSets.ts +++ b/src/screens/Settings/AppIconSettings/useAppIconSets.ts @@ -2,7 +2,7 @@ import {useMemo} from 'react' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {AppIconSet} from '#/screens/Settings/AppIconSettings/types' +import {type AppIconSet} from '#/screens/Settings/AppIconSettings/types' export function useAppIconSets() { const {_} = useLingui() @@ -13,20 +13,28 @@ export function useAppIconSets() { id: 'default_light', name: _(msg({context: 'Name of app icon variant', message: 'Light'})), iosImage: () => { - return require(`../../../../assets/app-icons/ios_icon_default_light.png`) + return require( + `../../../../assets/app-icons/ios_icon_default_light.png`, + ) }, androidImage: () => { - return require(`../../../../assets/app-icons/android_icon_default_light.png`) + return require( + `../../../../assets/app-icons/android_icon_default_light.png`, + ) }, }, { id: 'default_dark', name: _(msg({context: 'Name of app icon variant', message: 'Dark'})), iosImage: () => { - return require(`../../../../assets/app-icons/ios_icon_default_dark.png`) + return require( + `../../../../assets/app-icons/ios_icon_default_dark.png`, + ) }, androidImage: () => { - return require(`../../../../assets/app-icons/android_icon_default_dark.png`) + return require( + `../../../../assets/app-icons/android_icon_default_dark.png`, + ) }, }, ] satisfies AppIconSet[] @@ -39,10 +47,14 @@ export function useAppIconSets() { id: 'core_aurora', name: _(msg({context: 'Name of app icon variant', message: 'Aurora'})), iosImage: () => { - return require(`../../../../assets/app-icons/ios_icon_core_aurora.png`) + return require( + `../../../../assets/app-icons/ios_icon_core_aurora.png`, + ) }, androidImage: () => { - return require(`../../../../assets/app-icons/android_icon_core_aurora.png`) + return require( + `../../../../assets/app-icons/android_icon_core_aurora.png`, + ) }, }, // { @@ -59,20 +71,28 @@ export function useAppIconSets() { id: 'core_sunrise', name: _(msg({context: 'Name of app icon variant', message: 'Sunrise'})), iosImage: () => { - return require(`../../../../assets/app-icons/ios_icon_core_sunrise.png`) + return require( + `../../../../assets/app-icons/ios_icon_core_sunrise.png`, + ) }, androidImage: () => { - return require(`../../../../assets/app-icons/android_icon_core_sunrise.png`) + return require( + `../../../../assets/app-icons/android_icon_core_sunrise.png`, + ) }, }, { id: 'core_sunset', name: _(msg({context: 'Name of app icon variant', message: 'Sunset'})), iosImage: () => { - return require(`../../../../assets/app-icons/ios_icon_core_sunset.png`) + return require( + `../../../../assets/app-icons/ios_icon_core_sunset.png`, + ) }, androidImage: () => { - return require(`../../../../assets/app-icons/android_icon_core_sunset.png`) + return require( + `../../../../assets/app-icons/android_icon_core_sunset.png`, + ) }, }, { @@ -81,10 +101,14 @@ export function useAppIconSets() { msg({context: 'Name of app icon variant', message: 'Midnight'}), ), iosImage: () => { - return require(`../../../../assets/app-icons/ios_icon_core_midnight.png`) + return require( + `../../../../assets/app-icons/ios_icon_core_midnight.png`, + ) }, androidImage: () => { - return require(`../../../../assets/app-icons/android_icon_core_midnight.png`) + return require( + `../../../../assets/app-icons/android_icon_core_midnight.png`, + ) }, }, { @@ -93,10 +117,14 @@ export function useAppIconSets() { msg({context: 'Name of app icon variant', message: 'Flat Blue'}), ), iosImage: () => { - return require(`../../../../assets/app-icons/ios_icon_core_flat_blue.png`) + return require( + `../../../../assets/app-icons/ios_icon_core_flat_blue.png`, + ) }, androidImage: () => { - return require(`../../../../assets/app-icons/android_icon_core_flat_blue.png`) + return require( + `../../../../assets/app-icons/android_icon_core_flat_blue.png`, + ) }, }, { @@ -105,10 +133,14 @@ export function useAppIconSets() { msg({context: 'Name of app icon variant', message: 'Flat White'}), ), iosImage: () => { - return require(`../../../../assets/app-icons/ios_icon_core_flat_white.png`) + return require( + `../../../../assets/app-icons/ios_icon_core_flat_white.png`, + ) }, androidImage: () => { - return require(`../../../../assets/app-icons/android_icon_core_flat_white.png`) + return require( + `../../../../assets/app-icons/android_icon_core_flat_white.png`, + ) }, }, { @@ -117,10 +149,14 @@ export function useAppIconSets() { msg({context: 'Name of app icon variant', message: 'Flat Black'}), ), iosImage: () => { - return require(`../../../../assets/app-icons/ios_icon_core_flat_black.png`) + return require( + `../../../../assets/app-icons/ios_icon_core_flat_black.png`, + ) }, androidImage: () => { - return require(`../../../../assets/app-icons/android_icon_core_flat_black.png`) + return require( + `../../../../assets/app-icons/android_icon_core_flat_black.png`, + ) }, }, { @@ -132,10 +168,14 @@ export function useAppIconSets() { }), ), iosImage: () => { - return require(`../../../../assets/app-icons/ios_icon_core_classic.png`) + return require( + `../../../../assets/app-icons/ios_icon_core_classic.png`, + ) }, androidImage: () => { - return require(`../../../../assets/app-icons/android_icon_core_classic.png`) + return require( + `../../../../assets/app-icons/android_icon_core_classic.png`, + ) }, }, ] satisfies AppIconSet[] diff --git a/src/screens/Settings/components/ChangeHandleDialog.tsx b/src/screens/Settings/components/ChangeHandleDialog.tsx index a39d958ab..59e004252 100644 --- a/src/screens/Settings/components/ChangeHandleDialog.tsx +++ b/src/screens/Settings/components/ChangeHandleDialog.tsx @@ -525,8 +525,8 @@ function OwnHandlePage({goToServiceHandle}: {goToServiceHandle: () => void}) { isVerified ? _(msg`Update to ${domain}`) : dnsPanel - ? _(msg`Verify DNS Record`) - : _(msg`Verify Text File`) + ? _(msg`Verify DNS Record`) + : _(msg`Verify Text File`) } variant="solid" size="large" diff --git a/src/state/cache/profile-shadow.ts b/src/state/cache/profile-shadow.ts index 31bf55d13..0d3bb1b17 100644 --- a/src/state/cache/profile-shadow.ts +++ b/src/state/cache/profile-shadow.ts @@ -144,8 +144,8 @@ function mergeShadow( 'status' in shadow ? shadow.status : 'status' in profile - ? profile.status - : undefined, + ? profile.status + : undefined, }) } diff --git a/src/state/queries/explore-feed-previews.tsx b/src/state/queries/explore-feed-previews.tsx index 45bfc5c48..f02adde79 100644 --- a/src/state/queries/explore-feed-previews.tsx +++ b/src/state/queries/explore-feed-previews.tsx @@ -36,38 +36,27 @@ const RQKEY = (feeds: string[]) => [RQKEY_ROOT, feeds] const LIMIT = 8 // sliced to 6, overfetch to account for moderation const PINNED_POST_URIS: Record = { // πŸ“° News - 'at://did:plc:kkf4naxqmweop7dv4l2iqqf5/app.bsky.feed.post/3lgh27w2ngc2b': - true, + 'at://did:plc:kkf4naxqmweop7dv4l2iqqf5/app.bsky.feed.post/3lgh27w2ngc2b': true, // Gardening - 'at://did:plc:5rw2on4i56btlcajojaxwcat/app.bsky.feed.post/3kjorckgcwc27': - true, + 'at://did:plc:5rw2on4i56btlcajojaxwcat/app.bsky.feed.post/3kjorckgcwc27': true, // Web Development Trending - 'at://did:plc:m2sjv3wncvsasdapla35hzwj/app.bsky.feed.post/3lfaw445axs22': - true, + 'at://did:plc:m2sjv3wncvsasdapla35hzwj/app.bsky.feed.post/3lfaw445axs22': true, // Anime & Manga EN - 'at://did:plc:tazrmeme4dzahimsykusrwrk/app.bsky.feed.post/3knxx2gmkns2y': - true, + 'at://did:plc:tazrmeme4dzahimsykusrwrk/app.bsky.feed.post/3knxx2gmkns2y': true, // πŸ“½οΈ Film - 'at://did:plc:2hwwem55ce6djnk6bn62cstr/app.bsky.feed.post/3llhpzhbq7c2g': - true, + 'at://did:plc:2hwwem55ce6djnk6bn62cstr/app.bsky.feed.post/3llhpzhbq7c2g': true, // PopSky - 'at://did:plc:lfdf4srj43iwdng7jn35tjsp/app.bsky.feed.post/3lbblgly65c2g': - true, + 'at://did:plc:lfdf4srj43iwdng7jn35tjsp/app.bsky.feed.post/3lbblgly65c2g': true, // Science - 'at://did:plc:hu2obebw3nhfj667522dahfg/app.bsky.feed.post/3kl33otd6ob2s': - true, + 'at://did:plc:hu2obebw3nhfj667522dahfg/app.bsky.feed.post/3kl33otd6ob2s': true, // Birds! πŸ¦‰ - 'at://did:plc:ffkgesg3jsv2j7aagkzrtcvt/app.bsky.feed.post/3lbg4r57yk22d': - true, + 'at://did:plc:ffkgesg3jsv2j7aagkzrtcvt/app.bsky.feed.post/3lbg4r57yk22d': true, // Astronomy - 'at://did:plc:xy2zorw2ys47poflotxthlzg/app.bsky.feed.post/3kyzye4lujs2w': - true, + 'at://did:plc:xy2zorw2ys47poflotxthlzg/app.bsky.feed.post/3kyzye4lujs2w': true, // What's Cooking 🍽️ - 'at://did:plc:geoqe3qls5mwezckxxsewys2/app.bsky.feed.post/3lfqhgvxbqc2q': - true, + 'at://did:plc:geoqe3qls5mwezckxxsewys2/app.bsky.feed.post/3lfqhgvxbqc2q': true, // BookSky πŸ’™πŸ“š #booksky - 'at://did:plc:geoqe3qls5mwezckxxsewys2/app.bsky.feed.post/3kgrm2rw5ww2e': - true, + 'at://did:plc:geoqe3qls5mwezckxxsewys2/app.bsky.feed.post/3kgrm2rw5ww2e': true, } export type FeedPreviewItem = diff --git a/src/state/queries/notifications/settings.ts b/src/state/queries/notifications/settings.ts index 986d0b01d..02c0fe23b 100644 --- a/src/state/queries/notifications/settings.ts +++ b/src/state/queries/notifications/settings.ts @@ -36,9 +36,8 @@ export function useNotificationSettingsUpdateMutation() { mutationFn: async ( update: Partial, ) => { - const response = await agent.app.bsky.notification.putPreferencesV2( - update, - ) + const response = + await agent.app.bsky.notification.putPreferencesV2(update) return response.data.preferences }, onMutate: update => { diff --git a/src/state/queries/notifications/unread.tsx b/src/state/queries/notifications/unread.tsx index ba2377a78..da8d68126 100644 --- a/src/state/queries/notifications/unread.tsx +++ b/src/state/queries/notifications/unread.tsx @@ -14,7 +14,7 @@ import {useAgent, useSession} from '#/state/session' import {useModerationOpts} from '../../preferences/moderation-opts' import {truncateAndInvalidate} from '../util' import {RQKEY as RQKEY_NOTIFS} from './feed' -import {CachedFeedPage, FeedPage} from './types' +import {type CachedFeedPage, type FeedPage} from './types' import {fetchPage} from './util' const UPDATE_INTERVAL = 30 * 1e3 // 30sec @@ -94,8 +94,8 @@ export function Provider({children}: React.PropsWithChildren<{}>) { data.event === '30+' ? 30 : data.event === '' - ? 0 - : parseInt(data.event, 10) || 1, + ? 0 + : parseInt(data.event, 10) || 1, } setNumUnread(data.event) } @@ -164,8 +164,8 @@ export function Provider({children}: React.PropsWithChildren<{}>) { unreadCount >= 30 ? '30+' : unreadCount === 0 - ? '' - : String(unreadCount) + ? '' + : String(unreadCount) // track last sync const now = new Date() diff --git a/src/state/queries/usePostThread/index.ts b/src/state/queries/usePostThread/index.ts index 782888cfb..8495df04c 100644 --- a/src/state/queries/usePostThread/index.ts +++ b/src/state/queries/usePostThread/index.ts @@ -52,8 +52,8 @@ export function usePostThread({anchor}: {anchor?: string}) { return view === 'linear' ? LINEAR_VIEW_BELOW : isWeb && gtPhone - ? TREE_VIEW_BELOW_DESKTOP - : TREE_VIEW_BELOW + ? TREE_VIEW_BELOW_DESKTOP + : TREE_VIEW_BELOW }, [view, gtPhone]) const postThreadQueryKey = createPostThreadQueryKey({ diff --git a/src/state/queries/usePostThread/traversal.ts b/src/state/queries/usePostThread/traversal.ts index 124591125..7d8cd6464 100644 --- a/src/state/queries/usePostThread/traversal.ts +++ b/src/state/queries/usePostThread/traversal.ts @@ -444,7 +444,7 @@ export function buildThread({ const anchorPost = items.at(0) const hasAnchorFromCache = anchorPost && anchorPost.type === 'threadPost' const skeletonReplies = hasAnchorFromCache - ? anchorPost.value.post.replyCount ?? 4 + ? (anchorPost.value.post.replyCount ?? 4) : 4 if (!items.length) { diff --git a/src/storage/index.ts b/src/storage/index.ts index 9b39e1c1a..4d45134e1 100644 --- a/src/storage/index.ts +++ b/src/storage/index.ts @@ -1,7 +1,7 @@ import {useCallback, useEffect, useState} from 'react' import {MMKV} from 'react-native-mmkv' -import {Account, Device} from '#/storage/schema' +import {type Account, type Device} from '#/storage/schema' export * from '#/storage/schema' @@ -83,18 +83,10 @@ export class Storage { } } -type StorageSchema> = T extends Storage< - any, - infer U -> - ? U - : never -type StorageScopes> = T extends Storage< - infer S, - any -> - ? S - : never +type StorageSchema> = + T extends Storage ? U : never +type StorageScopes> = + T extends Storage ? S : never /** * Hook to use a storage instance. Acts like a useState hook, but persists the diff --git a/src/style.css b/src/style.css index 53e4d369b..a4a2c455f 100644 --- a/src/style.css +++ b/src/style.css @@ -328,7 +328,8 @@ input[type='range'][orient='vertical']::-moz-range-thumb { /* #/components/Select/index.web.tsx */ .radix-select-content { - box-shadow: 0px 6px 24px -10px rgba(22, 23, 24, 0.25), + box-shadow: + 0px 6px 24px -10px rgba(22, 23, 24, 0.25), 0px 6px 12px -12px rgba(22, 23, 24, 0.15); min-width: var(--radix-select-trigger-width); max-height: var(--radix-select-content-available-height); diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index de060c6c2..cc2859be9 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -518,8 +518,8 @@ export const ComposePost = ({ thread.posts.length > 1 ? _(msg`Your posts have been published`) : replyTo - ? _(msg`Your reply has been published`) - : _(msg`Your post has been published`), + ? _(msg`Your reply has been published`) + : _(msg`Your post has been published`), ) }, [ _, @@ -1000,20 +1000,20 @@ function ComposerTopBar({ }), ) : isThread - ? _( - msg({ - message: 'Publish posts', - comment: - 'Accessibility label for button to publish multiple posts in a thread', - }), - ) - : _( - msg({ - message: 'Publish post', - comment: - 'Accessibility label for button to publish a single post', - }), - ) + ? _( + msg({ + message: 'Publish posts', + comment: + 'Accessibility label for button to publish multiple posts in a thread', + }), + ) + : _( + msg({ + message: 'Publish post', + comment: + 'Accessibility label for button to publish a single post', + }), + ) } variant="solid" color="primary" diff --git a/src/view/com/composer/photos/Gallery.tsx b/src/view/com/composer/photos/Gallery.tsx index bc18c81f1..c44461b60 100644 --- a/src/view/com/composer/photos/Gallery.tsx +++ b/src/view/com/composer/photos/Gallery.tsx @@ -1,12 +1,12 @@ import React from 'react' import { - ImageStyle, + type ImageStyle, Keyboard, - LayoutChangeEvent, + type LayoutChangeEvent, StyleSheet, TouchableOpacity, View, - ViewStyle, + type ViewStyle, } from 'react-native' import {Image} from 'expo-image' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' @@ -14,14 +14,14 @@ import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' -import {Dimensions} from '#/lib/media/types' +import {type Dimensions} from '#/lib/media/types' import {colors, s} from '#/lib/styles' import {isNative} from '#/platform/detection' -import {ComposerImage, cropImage} from '#/state/gallery' +import {type ComposerImage, cropImage} from '#/state/gallery' import {Text} from '#/view/com/util/text/Text' import {useTheme} from '#/alf' import * as Dialog from '#/components/Dialog' -import {PostAction} from '../state/composer' +import {type PostAction} from '../state/composer' import {EditImageDialog} from './EditImageDialog' import {ImageAltTextDialog} from './ImageAltTextDialog' @@ -74,8 +74,8 @@ const GalleryInner = ({images, containerInfo, dispatch}: GalleryInnerProps) => { altTextControlStyle: isOverflow ? {left: 4, bottom: 4} : !isMobile && images.length < 3 - ? {left: 8, top: 8} - : {left: 4, top: 4}, + ? {left: 8, top: 8} + : {left: 4, top: 4}, imageControlsStyle: { display: 'flex' as const, flexDirection: 'row' as const, @@ -83,8 +83,8 @@ const GalleryInner = ({images, containerInfo, dispatch}: GalleryInnerProps) => { ...(isOverflow ? {top: 4, right: 4, gap: 4} : !isMobile && images.length < 3 - ? {top: 8, right: 8, gap: 8} - : {top: 4, right: 4, gap: 4}), + ? {top: 8, right: 8, gap: 8} + : {top: 4, right: 4, gap: 4}), zIndex: 1, }, imageStyle: { diff --git a/src/view/com/composer/state/composer.ts b/src/view/com/composer/state/composer.ts index f5a55f175..c673f2134 100644 --- a/src/view/com/composer/state/composer.ts +++ b/src/view/com/composer/state/composer.ts @@ -1,13 +1,13 @@ -import {ImagePickerAsset} from 'expo-image-picker' +import {type ImagePickerAsset} from 'expo-image-picker' import { - AppBskyFeedPostgate, + type AppBskyFeedPostgate, AppBskyRichtextFacet, - BskyPreferences, + type BskyPreferences, RichText, } from '@atproto/api' import {nanoid} from 'nanoid/non-secure' -import {SelfLabel} from '#/lib/moderation' +import {type SelfLabel} from '#/lib/moderation' import {insertMentionAt} from '#/lib/strings/mention-manip' import {shortenLinks} from '#/lib/strings/rich-text-manip' import { @@ -15,17 +15,22 @@ import { postUriToRelativePath, toBskyAppUrl, } from '#/lib/strings/url-helpers' -import {ComposerImage, createInitialImages} from '#/state/gallery' +import {type ComposerImage, createInitialImages} from '#/state/gallery' import {createPostgateRecord} from '#/state/queries/postgate/util' -import {Gif} from '#/state/queries/tenor' +import {type Gif} from '#/state/queries/tenor' import {threadgateRecordToAllowUISetting} from '#/state/queries/threadgate' -import {ThreadgateAllowUISetting} from '#/state/queries/threadgate' -import {ComposerOpts} from '#/state/shell/composer' +import {type ThreadgateAllowUISetting} from '#/state/queries/threadgate' +import {type ComposerOpts} from '#/state/shell/composer' import { - LinkFacetMatch, + type LinkFacetMatch, suggestLinkCardUri, } from '#/view/com/composer/text-input/text-input-util' -import {createVideoState, VideoAction, videoReducer, VideoState} from './video' +import { + createVideoState, + type VideoAction, + videoReducer, + type VideoState, +} from './video' type ImagesMedia = { type: 'images' @@ -514,12 +519,12 @@ export function createComposerState({ text: initText ? initText : initMention - ? insertMentionAt( - `@${initMention}`, - initMention.length + 1, - `${initMention}`, - ) - : '', + ? insertMentionAt( + `@${initMention}`, + initMention.length + 1, + `${initMention}`, + ) + : '', }) let link: Link | undefined diff --git a/src/view/com/composer/text-input/web/Autocomplete.tsx b/src/view/com/composer/text-input/web/Autocomplete.tsx index 62f19c63d..94ecb53cc 100644 --- a/src/view/com/composer/text-input/web/Autocomplete.tsx +++ b/src/view/com/composer/text-input/web/Autocomplete.tsx @@ -209,8 +209,8 @@ function AutocompleteProfileCard({ itemIndex === 0 ? styles.firstMention : itemIndex === totalItems - 1 - ? styles.lastMention - : undefined, + ? styles.lastMention + : undefined, ]} onPress={onPress} accessibilityRole="button"> diff --git a/src/view/com/lightbox/Lightbox.web.tsx b/src/view/com/lightbox/Lightbox.web.tsx index f6b6223ce..97811da7f 100644 --- a/src/view/com/lightbox/Lightbox.web.tsx +++ b/src/view/com/lightbox/Lightbox.web.tsx @@ -1,17 +1,17 @@ import React, {useCallback, useEffect, useState} from 'react' import { Image, - ImageStyle, + type ImageStyle, Pressable, StyleSheet, TouchableOpacity, TouchableWithoutFeedback, View, - ViewStyle, + type ViewStyle, } from 'react-native' import { FontAwesomeIcon, - FontAwesomeIconStyle, + type FontAwesomeIconStyle, } from '@fortawesome/react-native-fontawesome' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -21,7 +21,7 @@ import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {colors, s} from '#/lib/styles' import {useLightbox, useLightboxControls} from '#/state/lightbox' import {Text} from '../util/text/Text' -import {ImageSource} from './ImageViewing/@types' +import {type ImageSource} from './ImageViewing/@types' import ImageDefaultHeader from './ImageViewing/components/ImageDefaultHeader' export function Lightbox() { @@ -121,8 +121,8 @@ function LightboxInner({ img.type === 'circle-avi' ? '50%' : img.type === 'rect-avi' - ? '10%' - : 0, + ? '10%' + : 0, } as ImageStyle } alt={img.alt} diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index 592224ff5..54eea0493 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -630,8 +630,8 @@ let PostThreadItemLoaded = ({ showChildReplyLine && !isThreadedChild ? 0 : isThreadedChildAdjacentBot - ? 4 - : 8, + ? 4 + : 8, }, ]}> {/* If we are in threaded mode, the avatar is rendered in PostMeta */} diff --git a/src/view/com/posts/PostFeed.tsx b/src/view/com/posts/PostFeed.tsx index cbbe4a00f..1d0649b2e 100644 --- a/src/view/com/posts/PostFeed.tsx +++ b/src/view/com/posts/PostFeed.tsx @@ -132,12 +132,11 @@ type FeedRow = key: string } -export function getItemsForFeedback(feedRow: FeedRow): - | { - item: FeedPostSliceItem - feedContext: string | undefined - reqId: string | undefined - }[] { +export function getItemsForFeedback(feedRow: FeedRow): { + item: FeedPostSliceItem + feedContext: string | undefined + reqId: string | undefined +}[] { if (feedRow.type === 'sliceItem') { return feedRow.slice.items.map(item => ({ item, diff --git a/src/view/com/posts/PostFeedErrorMessage.tsx b/src/view/com/posts/PostFeedErrorMessage.tsx index 39347b89c..808ef9fff 100644 --- a/src/view/com/posts/PostFeedErrorMessage.tsx +++ b/src/view/com/posts/PostFeedErrorMessage.tsx @@ -1,15 +1,19 @@ import React from 'react' import {View} from 'react-native' -import {AppBskyActorDefs, AppBskyFeedGetAuthorFeed, AtUri} from '@atproto/api' +import { + type AppBskyActorDefs, + AppBskyFeedGetAuthorFeed, + AtUri, +} from '@atproto/api' import {msg as msgLingui, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useNavigation} from '@react-navigation/native' import {usePalette} from '#/lib/hooks/usePalette' -import {NavigationProp} from '#/lib/routes/types' +import {type NavigationProp} from '#/lib/routes/types' import {cleanError} from '#/lib/strings/errors' import {logger} from '#/logger' -import {FeedDescriptor} from '#/state/queries/post-feed' +import {type FeedDescriptor} from '#/state/queries/post-feed' import {useRemoveFeedMutation} from '#/state/queries/preferences' import * as Prompt from '#/components/Prompt' import {EmptyState} from '../util/EmptyState' @@ -119,7 +123,7 @@ function FeedgenErrorMessage({ [KnownError.FeedTooManyRequests]: _l( msgLingui`This feed is currently receiving high traffic and is temporarily unavailable. Please try again later.`, ), - }[knownError]), + })[knownError], [_l, knownError], ) const [_, uri] = feedDesc.split('|') diff --git a/src/view/com/profile/ProfileMenu.tsx b/src/view/com/profile/ProfileMenu.tsx index d18ba12c1..879bf22f9 100644 --- a/src/view/com/profile/ProfileMenu.tsx +++ b/src/view/com/profile/ProfileMenu.tsx @@ -461,12 +461,12 @@ let ProfileMenu = ({ msg`The account will be able to interact with you after unblocking.`, ) : profile.associated?.labeler - ? _( - msg`Blocking will not prevent labels from being applied on your account, but it will stop this account from replying in your threads or interacting with you.`, - ) - : _( - msg`Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.`, - ) + ? _( + msg`Blocking will not prevent labels from being applied on your account, but it will stop this account from replying in your threads or interacting with you.`, + ) + : _( + msg`Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.`, + ) } onConfirm={blockAccount} confirmButtonCta={ diff --git a/src/view/com/util/List.web.tsx b/src/view/com/util/List.web.tsx index 18f7d6fa7..55dafb3dd 100644 --- a/src/view/com/util/List.web.tsx +++ b/src/view/com/util/List.web.tsx @@ -1,6 +1,11 @@ import React, {isValidElement, memo, startTransition, useRef} from 'react' -import {FlatListProps, StyleSheet, View, ViewProps} from 'react-native' -import {ReanimatedScrollEvent} from 'react-native-reanimated/lib/typescript/hook/commonTypes' +import { + type FlatListProps, + StyleSheet, + View, + type ViewProps, +} from 'react-native' +import {type ReanimatedScrollEvent} from 'react-native-reanimated/lib/typescript/hook/commonTypes' import {batchedUpdates} from '#/lib/batchedUpdates' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' @@ -205,7 +210,7 @@ function ListImpl( behavior: animated ? 'smooth' : 'instant', }) }, - } as any), // TODO: Better types. + }) as any, // TODO: Better types. [getScrollableNode], ) diff --git a/src/view/screens/DebugMod.tsx b/src/view/screens/DebugMod.tsx index 0ccf9b67a..d2e492f7e 100644 --- a/src/view/screens/DebugMod.tsx +++ b/src/view/screens/DebugMod.tsx @@ -112,14 +112,14 @@ export const DebugModScreen = ({}: NativeStackScreenProps< }), ] : scenario[0] === 'label' && target[0] === 'profile' - ? [ - mock.label({ - src: isSelfLabel ? did : undefined, - val: label[0], - uri: `at://${did}/app.bsky.actor.profile/self`, - }), - ] - : undefined, + ? [ + mock.label({ + src: isSelfLabel ? did : undefined, + val: label[0], + uri: `at://${did}/app.bsky.actor.profile/self`, + }), + ] + : undefined, viewer: mock.actorViewerState({ following: isFollowing ? `at://${currentAccount?.did || ''}/app.bsky.graph.follow/1234` diff --git a/svgo.config.mjs b/svgo.config.mjs index d7c98cd54..9c203557c 100644 --- a/svgo.config.mjs +++ b/svgo.config.mjs @@ -1,64 +1,66 @@ const preset = [ - "removeDoctype", - "removeXMLProcInst", - "removeComments", - "removeMetadata", - "removeEditorsNSData", - "cleanupAttrs", - "mergeStyles", - "inlineStyles", - "minifyStyles", - "cleanupIds", - "removeUselessDefs", - "cleanupNumericValues", - "convertColors", - "removeUnknownsAndDefaults", - "removeNonInheritableGroupAttrs", - "removeUselessStrokeAndFill", - "removeDimensions", - "cleanupEnableBackground", - "removeHiddenElems", - "removeEmptyText", - "convertShapeToPath", - "convertEllipseToCircle", - "moveElemsAttrsToGroup", - "moveGroupAttrsToElems", - "collapseGroups", - "convertPathData", - "convertTransform", - "removeEmptyAttrs", - "removeEmptyContainers", - "removeUnusedNS", - "mergePaths", - "sortAttrs", - "sortDefsChildren", - "removeTitle", - "removeDesc", + 'removeDoctype', + 'removeXMLProcInst', + 'removeComments', + 'removeMetadata', + 'removeEditorsNSData', + 'cleanupAttrs', + 'mergeStyles', + 'inlineStyles', + 'minifyStyles', + 'cleanupIds', + 'removeUselessDefs', + 'cleanupNumericValues', + 'convertColors', + 'removeUnknownsAndDefaults', + 'removeNonInheritableGroupAttrs', + 'removeUselessStrokeAndFill', + 'removeDimensions', + 'cleanupEnableBackground', + 'removeHiddenElems', + 'removeEmptyText', + 'convertShapeToPath', + 'convertEllipseToCircle', + 'moveElemsAttrsToGroup', + 'moveGroupAttrsToElems', + 'collapseGroups', + 'convertPathData', + 'convertTransform', + 'removeEmptyAttrs', + 'removeEmptyContainers', + 'removeUnusedNS', + 'mergePaths', + 'sortAttrs', + 'sortDefsChildren', + 'removeTitle', + 'removeDesc', ] export default { - plugins: [...preset.map(name => ({ - name, - params: { - floatPrecision: 3, - transformPrecision: 5, - // minimise diff in ouput from svgomg - // maybe remove in future? will produce smaller output - convertToZ: false, - removeUseless: false, - } - })), - { - name: 'addTrailingWhitespace', - fn() { - return { - root: { - exit (root) { - root.children.push({ type: 'text', value: '\n' }) - return root - } + plugins: [ + ...preset.map(name => ({ + name, + params: { + floatPrecision: 3, + transformPrecision: 5, + // minimise diff in ouput from svgomg + // maybe remove in future? will produce smaller output + convertToZ: false, + removeUseless: false, + }, + })), + { + name: 'addTrailingWhitespace', + fn() { + return { + root: { + exit(root) { + root.children.push({type: 'text', value: '\n'}) + return root + }, + }, } - } - } - }] -}; + }, + }, + ], +} diff --git a/yarn.lock b/yarn.lock index fe3ce7caf..8a9298e95 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16220,10 +16220,10 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier@^2.8.3: - version "2.8.8" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" - integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +prettier@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.6.0.tgz#18ec98d62cb0757a5d4eab40253ff3e6d0fc8dea" + integrity sha512-ujSB9uXHJKzM/2GBuE0hBOUgC77CN3Bnpqa+g80bkv3T3A93wL/xlzDATHhnhkzifz/UE2SNOvmbTz5hSkDlHw== pretty-bytes@^5.6.0: version "5.6.0" -- 2.51.2 From c894dda0bfa8a7f89ad1f9ad95fc11698e8d9822 Mon Sep 17 00:00:00 2001 From: hailey Date: Mon, 23 Jun 2025 10:11:17 -0700 Subject: [PATCH 2/4] disable new arch (#8561) --- .github/workflows/build-submit-android.yml | 4 --- .../workflows/bundle-deploy-eas-update.yml | 14 +--------- app.config.js | 2 +- package.json | 3 +-- scripts/bundleUpdate.js | 26 +++++++------------ 5 files changed, 12 insertions(+), 37 deletions(-) diff --git a/.github/workflows/build-submit-android.yml b/.github/workflows/build-submit-android.yml index 8a8388ee8..f75c95052 100644 --- a/.github/workflows/build-submit-android.yml +++ b/.github/workflows/build-submit-android.yml @@ -52,10 +52,6 @@ jobs: distribution: 'temurin' java-version: '17' - - name: "Use upgraded MMKV for Fabric" - run: | - sed -i 's/"react-native-mmkv": "\^2\.12\.2"/"react-native-mmkv": "^3.3.0"/' package.json - - name: βš™οΈ Install dependencies run: yarn install diff --git a/.github/workflows/bundle-deploy-eas-update.yml b/.github/workflows/bundle-deploy-eas-update.yml index 1b2fba0b7..5a4702f96 100644 --- a/.github/workflows/bundle-deploy-eas-update.yml +++ b/.github/workflows/bundle-deploy-eas-update.yml @@ -5,7 +5,6 @@ on: push: branches: - main - - hailey/eas-fab workflow_dispatch: inputs: channel: @@ -119,14 +118,7 @@ jobs: - name: πŸ—οΈ Create Bundle if: ${{ !steps.fingerprint.outputs.includes-changes }} - run: | - SENTRY_DIST=${{ steps.sentry.outputs.SENTRY_DIST }} SENTRY_RELEASE=${{ steps.sentry.outputs.SENTRY_RELEASE }} SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_DSN=${{ secrets.SENTRY_DSN }} EXPO_PUBLIC_ENV="${{ inputs.channel || 'testflight' }}" yarn export-ios - mv ./dist ./ios-dist - sed -i 's/"react-native-mmkv": "\^2\.12\.2"/"react-native-mmkv": "^3.3.0"/' package.json - yarn install - SENTRY_DIST=${{ steps.sentry.outputs.SENTRY_DIST }} SENTRY_RELEASE=${{ steps.sentry.outputs.SENTRY_RELEASE }} SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_DSN=${{ secrets.SENTRY_DSN }} EXPO_PUBLIC_ENV="${{ inputs.channel || 'testflight' }}" yarn export-android - mv ./dist ./android-dist - + run: SENTRY_DIST=${{ steps.sentry.outputs.SENTRY_DIST }} SENTRY_RELEASE=${{ steps.sentry.outputs.SENTRY_RELEASE }} SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_DSN=${{ secrets.SENTRY_DSN }} EXPO_PUBLIC_ENV="${{ inputs.channel || 'testflight' }}" yarn export - name: πŸ“¦ Package Bundle and πŸš€ Deploy if: ${{ !steps.fingerprint.outputs.includes-changes }} @@ -283,10 +275,6 @@ jobs: distribution: 'temurin' java-version: '17' - - name: "Use upgraded MMKV for Fabric" - run: | - sed -i 's/"react-native-mmkv": "\^2\.12\.2"/"react-native-mmkv": "^3.3.0"/' package.json - - name: βš™οΈ Install dependencies run: yarn install diff --git a/app.config.js b/app.config.js index c6b2fc6d4..cbe028273 100644 --- a/app.config.js +++ b/app.config.js @@ -219,7 +219,7 @@ module.exports = function (_config) { compileSdkVersion: 35, targetSdkVersion: 35, buildToolsVersion: '35.0.0', - newArchEnabled: true, + newArchEnabled: false, }, }, ], diff --git a/package.json b/package.json index eddec7928..651d55f22 100644 --- a/package.json +++ b/package.json @@ -61,8 +61,7 @@ "intl:push": "crowdin push translations --verbose -b main", "nuke": "rm -rf ./node_modules && rm -rf ./ios && rm -rf ./android", "update-extensions": "bash scripts/updateExtensions.sh", - "export-ios": "npx expo export --platform ios --dump-sourcemap && yarn upload-native-sourcemaps", - "export-android": "npx expo export --platform android --dump-sourcemap && yarn upload-native-sourcemaps", + "export": "npx expo export --dump-sourcemap && yarn upload-native-sourcemaps", "upload-native-sourcemaps": "npx sentry-expo-upload-sourcemaps dist", "make-deploy-bundle": "bash scripts/bundleUpdate.sh", "generate-webpack-stats-file": "EXPO_PUBLIC_GENERATE_STATS=1 yarn build-web", diff --git a/scripts/bundleUpdate.js b/scripts/bundleUpdate.js index a5f1fb641..00217dcd7 100644 --- a/scripts/bundleUpdate.js +++ b/scripts/bundleUpdate.js @@ -3,25 +3,17 @@ const fs = require('fs') const fsp = fs.promises const path = require('path') -const IOS_DIST_DIR = './ios-dist' -const ANDROID_DIST_DIR = './android-dist' - +const DIST_DIR = './dist' const BUNDLES_DIR = '/_expo/static/js' - -const IOS_BUNDLE_DIR = path.join(IOS_DIST_DIR, BUNDLES_DIR, '/ios') -const ANDROID_BUNDLE_DIR = path.join(ANDROID_DIST_DIR, BUNDLES_DIR, '/android') - -const IOS_METADATA_PATH = path.join(IOS_DIST_DIR, '/metadata.json') -const ANDROID_METADATA_PATH = path.join(ANDROID_DIST_DIR, '/metadata.json') - +const IOS_BUNDLE_DIR = path.join(DIST_DIR, BUNDLES_DIR, '/ios') +const ANDROID_BUNDLE_DIR = path.join(DIST_DIR, BUNDLES_DIR, '/android') +const METADATA_PATH = path.join(DIST_DIR, '/metadata.json') const DEST_DIR = './bundleTempDir' // Weird, don't feel like figuring out _why_ it wants this -const IOS_METADATA = require(`../${IOS_METADATA_PATH}`) -const ANDROID_METADATA = require(`../${ANDROID_METADATA_PATH}`) - -const IOS_METADATA_ASSETS = IOS_METADATA.fileMetadata.ios.assets -const ANDROID_METADATA_ASSETS = ANDROID_METADATA.fileMetadata.android.assets +const METADATA = require(`../${METADATA_PATH}`) +const IOS_METADATA_ASSETS = METADATA.fileMetadata.ios.assets +const ANDROID_METADATA_ASSETS = METADATA.fileMetadata.android.assets const getMd5 = async path => { return new Promise(res => { @@ -68,7 +60,7 @@ const moveFiles = async () => { console.log('Getting ios asset md5s and moving them...') for (const asset of IOS_METADATA_ASSETS) { - const currPath = path.join(IOS_DIST_DIR, asset.path) + const currPath = path.join(DIST_DIR, asset.path) const md5 = await getMd5(currPath) const withExtPath = `assets/${md5}.${asset.ext}` iosAssets.push(withExtPath) @@ -77,7 +69,7 @@ const moveFiles = async () => { console.log('Getting android asset md5s and moving them...') for (const asset of ANDROID_METADATA_ASSETS) { - const currPath = path.join(ANDROID_DIST_DIR, asset.path) + const currPath = path.join(DIST_DIR, asset.path) const md5 = await getMd5(currPath) const withExtPath = `assets/${md5}.${asset.ext}` androidAssets.push(withExtPath) -- 2.51.2 From cd820709b611a0381222a34d2bc6ff49fa380452 Mon Sep 17 00:00:00 2001 From: pfrazee <1270099+pfrazee@users.noreply.github.com> Date: Tue, 24 Jun 2025 02:42:09 +0000 Subject: [PATCH 3/4] Nightly source-language update --- src/locale/locales/en/messages.po | 110 +++++++++++++++--------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/src/locale/locales/en/messages.po b/src/locale/locales/en/messages.po index 1d0fffcce..36787e0d8 100644 --- a/src/locale/locales/en/messages.po +++ b/src/locale/locales/en/messages.po @@ -456,7 +456,7 @@ msgstr "" msgid "<0>{0} is included in your starter pack" msgstr "" -#: src/components/WhoCanReply.tsx:296 +#: src/components/WhoCanReply.tsx:302 msgid "<0>{0} members" msgstr "" @@ -866,7 +866,7 @@ msgstr "" msgid "An error occurred while compressing the video." msgstr "" -#: src/state/queries/explore-feed-previews.tsx:184 +#: src/state/queries/explore-feed-previews.tsx:173 msgid "An error occurred while fetching the feed." msgstr "" @@ -939,7 +939,7 @@ msgstr "" msgid "an unknown labeler" msgstr "" -#: src/components/WhoCanReply.tsx:317 +#: src/components/WhoCanReply.tsx:323 msgid "and" msgstr "" @@ -1066,7 +1066,7 @@ msgstr "" msgid "Are you sure you want to delete the app password \"{0}\"?" msgstr "" -#: src/components/dms/MessageContextMenu.tsx:189 +#: src/components/dms/MessageContextMenu.tsx:188 msgid "Are you sure you want to delete this message? The message will be deleted for you, but not for the other participant." msgstr "" @@ -1128,7 +1128,7 @@ msgstr "" msgid "At least 3 characters" msgstr "" -#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:40 +#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:48 msgctxt "Name of app icon variant" msgid "Aurora" msgstr "" @@ -1302,7 +1302,7 @@ msgstr "" msgid "Bluesky cannot confirm the authenticity of the claimed date." msgstr "" -#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:129 +#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:165 msgctxt "Name of app icon variant" msgid "Bluesky Classicβ„’" msgstr "" @@ -2096,8 +2096,8 @@ msgstr "" msgid "Copy link to starter pack" msgstr "" -#: src/components/dms/MessageContextMenu.tsx:150 -#: src/components/dms/MessageContextMenu.tsx:153 +#: src/components/dms/MessageContextMenu.tsx:149 +#: src/components/dms/MessageContextMenu.tsx:152 msgid "Copy message text" msgstr "" @@ -2145,7 +2145,7 @@ msgstr "" msgid "Could not process your video" msgstr "" -#: src/state/queries/notifications/settings.ts:50 +#: src/state/queries/notifications/settings.ts:49 msgid "Could not update notification settings" msgstr "" @@ -2247,7 +2247,7 @@ msgstr "" msgid "Dark" msgstr "" -#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:24 +#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:28 msgctxt "Name of app icon variant" msgid "Dark" msgstr "" @@ -2286,7 +2286,7 @@ msgstr "" msgid "Default icons" msgstr "" -#: src/components/dms/MessageContextMenu.tsx:191 +#: src/components/dms/MessageContextMenu.tsx:190 #: src/components/PostControls/PostMenu/PostMenuItems.tsx:701 #: src/screens/Messages/components/ChatStatusInfo.tsx:55 #: src/screens/Settings/AppPasswords.tsx:212 @@ -2334,7 +2334,7 @@ msgstr "" msgid "Delete Conversation" msgstr "" -#: src/components/dms/MessageContextMenu.tsx:164 +#: src/components/dms/MessageContextMenu.tsx:163 msgid "Delete for me" msgstr "" @@ -2342,11 +2342,11 @@ msgstr "" msgid "Delete list" msgstr "" -#: src/components/dms/MessageContextMenu.tsx:187 +#: src/components/dms/MessageContextMenu.tsx:186 msgid "Delete message" msgstr "" -#: src/components/dms/MessageContextMenu.tsx:162 +#: src/components/dms/MessageContextMenu.tsx:161 msgid "Delete message for me" msgstr "" @@ -2433,7 +2433,7 @@ msgstr "" msgid "Developer options" msgstr "" -#: src/components/WhoCanReply.tsx:179 +#: src/components/WhoCanReply.tsx:185 msgid "Dialog: adjust who can interact with this post" msgstr "" @@ -2759,7 +2759,7 @@ msgstr "" msgid "Edit User List" msgstr "" -#: src/components/WhoCanReply.tsx:91 +#: src/components/WhoCanReply.tsx:97 msgid "Edit who can reply" msgstr "" @@ -2984,11 +2984,11 @@ msgstr "" msgid "Everybody" msgstr "" -#: src/components/WhoCanReply.tsx:71 +#: src/components/WhoCanReply.tsx:77 msgid "Everybody can reply" msgstr "" -#: src/components/WhoCanReply.tsx:222 +#: src/components/WhoCanReply.tsx:228 msgid "Everybody can reply to this post." msgstr "" @@ -3409,17 +3409,17 @@ msgstr "" msgid "Fitness" msgstr "" -#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:117 +#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:149 msgctxt "Name of app icon variant" msgid "Flat Black" msgstr "" -#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:93 +#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:117 msgctxt "Name of app icon variant" msgid "Flat Blue" msgstr "" -#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:105 +#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:133 msgctxt "Name of app icon variant" msgid "Flat White" msgstr "" @@ -3920,23 +3920,23 @@ msgstr "" msgid "Hides the content" msgstr "" -#: src/view/com/posts/PostFeedErrorMessage.tsx:117 +#: src/view/com/posts/PostFeedErrorMessage.tsx:121 msgid "Hmm, some kind of issue occurred when contacting the feed server. Please let the feed owner know about this issue." msgstr "" -#: src/view/com/posts/PostFeedErrorMessage.tsx:105 +#: src/view/com/posts/PostFeedErrorMessage.tsx:109 msgid "Hmm, the feed server appears to be misconfigured. Please let the feed owner know about this issue." msgstr "" -#: src/view/com/posts/PostFeedErrorMessage.tsx:111 +#: src/view/com/posts/PostFeedErrorMessage.tsx:115 msgid "Hmm, the feed server appears to be offline. Please let the feed owner know about this issue." msgstr "" -#: src/view/com/posts/PostFeedErrorMessage.tsx:108 +#: src/view/com/posts/PostFeedErrorMessage.tsx:112 msgid "Hmm, the feed server gave a bad response. Please let the feed owner know about this issue." msgstr "" -#: src/view/com/posts/PostFeedErrorMessage.tsx:102 +#: src/view/com/posts/PostFeedErrorMessage.tsx:106 msgid "Hmm, we're having trouble finding this feed. It may have been deleted." msgstr "" @@ -4680,7 +4680,7 @@ msgstr "" msgid "Mention notifications" msgstr "" -#: src/components/WhoCanReply.tsx:263 +#: src/components/WhoCanReply.tsx:269 msgid "mentioned users" msgstr "" @@ -4716,7 +4716,7 @@ msgstr "" msgid "Message from @{0}: {1}" msgstr "" -#: src/view/com/posts/PostFeedErrorMessage.tsx:201 +#: src/view/com/posts/PostFeedErrorMessage.tsx:205 msgid "Message from server: {0}" msgstr "" @@ -4737,7 +4737,7 @@ msgstr "" msgid "Messages" msgstr "" -#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:81 +#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:101 msgctxt "Name of app icon variant" msgid "Midnight" msgstr "" @@ -5193,7 +5193,7 @@ msgstr "" msgid "No one" msgstr "" -#: src/components/WhoCanReply.tsx:246 +#: src/components/WhoCanReply.tsx:252 msgid "No one but the author can quote this post." msgstr "" @@ -5419,7 +5419,7 @@ msgstr "" msgid "Only .jpg and .png files are supported" msgstr "" -#: src/components/WhoCanReply.tsx:226 +#: src/components/WhoCanReply.tsx:232 msgid "Only {0} can reply." msgstr "" @@ -6057,7 +6057,7 @@ msgstr "" msgid "Posts can be muted based on their text, their tags, or both. We recommend avoiding common words that appear in many posts, since it can result in no posts being shown." msgstr "" -#: src/view/com/posts/PostFeedErrorMessage.tsx:68 +#: src/view/com/posts/PostFeedErrorMessage.tsx:72 msgid "Posts hidden" msgstr "" @@ -6361,7 +6361,7 @@ msgstr "" #: src/screens/Settings/Settings.tsx:556 #: src/view/com/feeds/FeedSourceCard.tsx:322 #: src/view/com/modals/UserAddRemoveLists.tsx:235 -#: src/view/com/posts/PostFeedErrorMessage.tsx:213 +#: src/view/com/posts/PostFeedErrorMessage.tsx:217 msgid "Remove" msgstr "" @@ -6398,11 +6398,11 @@ msgstr "" #: src/view/com/posts/FeedShutdownMsg.tsx:116 #: src/view/com/posts/FeedShutdownMsg.tsx:120 -#: src/view/com/posts/PostFeedErrorMessage.tsx:169 +#: src/view/com/posts/PostFeedErrorMessage.tsx:173 msgid "Remove feed" msgstr "" -#: src/view/com/posts/PostFeedErrorMessage.tsx:210 +#: src/view/com/posts/PostFeedErrorMessage.tsx:214 msgid "Remove feed?" msgstr "" @@ -6455,7 +6455,7 @@ msgstr "" msgid "Remove subtitle file" msgstr "" -#: src/view/com/posts/PostFeedErrorMessage.tsx:211 +#: src/view/com/posts/PostFeedErrorMessage.tsx:215 msgid "Remove this feed from your saved feeds" msgstr "" @@ -6517,11 +6517,11 @@ msgstr "" msgid "Replies" msgstr "" -#: src/components/WhoCanReply.tsx:73 +#: src/components/WhoCanReply.tsx:79 msgid "Replies disabled" msgstr "" -#: src/components/WhoCanReply.tsx:224 +#: src/components/WhoCanReply.tsx:230 msgid "Replies to this post are disabled." msgstr "" @@ -6593,7 +6593,7 @@ msgstr "" msgid "Reply was successfully hidden" msgstr "" -#: src/components/dms/MessageContextMenu.tsx:172 +#: src/components/dms/MessageContextMenu.tsx:171 #: src/components/dms/MessagesListBlockedFooter.tsx:85 #: src/components/dms/MessagesListBlockedFooter.tsx:92 msgid "Report" @@ -6627,7 +6627,7 @@ msgstr "" msgid "Report list" msgstr "" -#: src/components/dms/MessageContextMenu.tsx:170 +#: src/components/dms/MessageContextMenu.tsx:169 msgid "Report message" msgstr "" @@ -7646,7 +7646,7 @@ msgstr "" msgid "Some other feeds you might like" msgstr "" -#: src/components/WhoCanReply.tsx:74 +#: src/components/WhoCanReply.tsx:80 msgid "Some people can reply" msgstr "" @@ -7871,12 +7871,12 @@ msgstr "" msgid "Suggestive" msgstr "" -#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:60 +#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:72 msgctxt "Name of app icon variant" msgid "Sunrise" msgstr "" -#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:70 +#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:86 msgctxt "Name of app icon variant" msgid "Sunset" msgstr "" @@ -8165,7 +8165,7 @@ msgid "There was an issue fetching notifications. Tap here to try again." msgstr "" #: src/screens/Search/Explore.tsx:990 -#: src/view/com/posts/PostFeed.tsx:657 +#: src/view/com/posts/PostFeed.tsx:656 msgid "There was an issue fetching posts. Tap here to try again." msgstr "" @@ -8186,7 +8186,7 @@ msgstr "" msgid "There was an issue fetching your service info" msgstr "" -#: src/view/com/posts/PostFeedErrorMessage.tsx:145 +#: src/view/com/posts/PostFeedErrorMessage.tsx:149 msgid "There was an issue removing this feed. Please check your internet connection and try again." msgstr "" @@ -8295,7 +8295,7 @@ msgstr "" msgid "This content is not available because one of the users involved has blocked the other." msgstr "" -#: src/view/com/posts/PostFeedErrorMessage.tsx:114 +#: src/view/com/posts/PostFeedErrorMessage.tsx:118 msgid "This content is not viewable without a Bluesky account." msgstr "" @@ -8319,7 +8319,7 @@ msgstr "" msgid "This feature is not available while using an App Password. Please sign in with your main password." msgstr "" -#: src/view/com/posts/PostFeedErrorMessage.tsx:120 +#: src/view/com/posts/PostFeedErrorMessage.tsx:124 msgid "This feed is currently receiving high traffic and is temporarily unavailable. Please try again later." msgstr "" @@ -8387,7 +8387,7 @@ msgstr "" msgid "This post claims to have been created on <0>{0}, but was first seen by Bluesky on <1>{1}." msgstr "" -#: src/components/WhoCanReply.tsx:217 +#: src/components/WhoCanReply.tsx:223 msgid "This post has an unknown type of threadgate on it. Your app may be out of date." msgstr "" @@ -8558,8 +8558,8 @@ msgstr "" msgid "Topic" msgstr "" -#: src/components/dms/MessageContextMenu.tsx:143 -#: src/components/dms/MessageContextMenu.tsx:145 +#: src/components/dms/MessageContextMenu.tsx:142 +#: src/components/dms/MessageContextMenu.tsx:144 #: src/components/PostControls/PostMenu/PostMenuItems.tsx:444 #: src/components/PostControls/PostMenu/PostMenuItems.tsx:446 #: src/screens/PostThread/components/ThreadItemAnchor.tsx:567 @@ -8984,11 +8984,11 @@ msgstr "" msgid "Username or email address" msgstr "" -#: src/components/WhoCanReply.tsx:280 +#: src/components/WhoCanReply.tsx:286 msgid "users followed by <0>@{0}" msgstr "" -#: src/components/WhoCanReply.tsx:267 +#: src/components/WhoCanReply.tsx:273 msgid "users following <0>@{0}" msgstr "" @@ -9188,7 +9188,7 @@ msgstr "" #: src/components/ProfileHoverCard/index.web.tsx:464 #: src/components/ProfileHoverCard/index.web.tsx:484 #: src/components/ProfileHoverCard/index.web.tsx:511 -#: src/view/com/posts/PostFeedErrorMessage.tsx:175 +#: src/view/com/posts/PostFeedErrorMessage.tsx:179 #: src/view/com/util/PostMeta.tsx:91 #: src/view/com/util/PostMeta.tsx:126 msgid "View profile" @@ -9416,11 +9416,11 @@ msgstr "" msgid "Which languages would you like to see in your algorithmic feeds?" msgstr "" -#: src/components/WhoCanReply.tsx:183 +#: src/components/WhoCanReply.tsx:189 msgid "Who can interact with this post?" msgstr "" -#: src/components/WhoCanReply.tsx:91 +#: src/components/WhoCanReply.tsx:97 msgid "Who can reply" msgstr "" -- 2.51.2 From 4c1515169af81f5eb861e476d2bc07f17c8635fd Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Tue, 24 Jun 2025 22:03:23 -0500 Subject: [PATCH 4/4] Tooltip (#8555) * Working overlay, WIP * Ok working with no overlay and global gesture handler * Ok pretty good on native * Cleanup * Cleanup * add animation * add transform origin to animation * Some a11y * Improve colors * Explicitly wrap gesture handler * Add easier abstraction * Web * Fix animation * Cleanup and remove provider * Include demo for now * Ok diff interface to avoid collapsed views * Use dimensions hook * Adjust overlap, clarify intent of consts * Revert testing edits --------- Co-authored-by: Samuel Newman --- src/App.native.tsx | 13 +- src/alf/atoms.ts | 4 + src/components/Tooltip/const.ts | 6 + src/components/Tooltip/index.tsx | 411 ++++++++++++++++++ src/components/Tooltip/index.web.tsx | 112 +++++ src/components/hooks/useOnGesture/index.ts | 24 + .../hooks/useOnGesture/index.web.ts | 1 + src/state/global-gesture-events/index.tsx | 83 ++++ src/state/global-gesture-events/index.web.tsx | 9 + src/style.css | 35 ++ 10 files changed, 693 insertions(+), 5 deletions(-) create mode 100644 src/components/Tooltip/const.ts create mode 100644 src/components/Tooltip/index.tsx create mode 100644 src/components/Tooltip/index.web.tsx create mode 100644 src/components/hooks/useOnGesture/index.ts create mode 100644 src/components/hooks/useOnGesture/index.web.ts create mode 100644 src/state/global-gesture-events/index.tsx create mode 100644 src/state/global-gesture-events/index.web.tsx diff --git a/src/App.native.tsx b/src/App.native.tsx index 81d4a870e..c42b11746 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -33,6 +33,7 @@ import { ensureGeolocationResolved, Provider as GeolocationProvider, } from '#/state/geolocation' +import {GlobalGestureEventsProvider} from '#/state/global-gesture-events' import {Provider as HomeBadgeProvider} from '#/state/home-badge' import {Provider as InvitesStateProvider} from '#/state/invites' import {Provider as LightboxStateProvider} from '#/state/lightbox' @@ -154,11 +155,13 @@ function InnerApp() { - - - - - + + + + + + + diff --git a/src/alf/atoms.ts b/src/alf/atoms.ts index 79ec41679..440ac16ac 100644 --- a/src/alf/atoms.ts +++ b/src/alf/atoms.ts @@ -983,6 +983,10 @@ export const atoms = { transition_none: web({ transitionProperty: 'none', }), + transition_timing_default: web({ + transitionTimingFunction: 'cubic-bezier(0.17, 0.73, 0.14, 1)', + transitionDuration: '100ms', + }), transition_all: web({ transitionProperty: 'all', transitionTimingFunction: 'cubic-bezier(0.17, 0.73, 0.14, 1)', diff --git a/src/components/Tooltip/const.ts b/src/components/Tooltip/const.ts new file mode 100644 index 000000000..cad7e0106 --- /dev/null +++ b/src/components/Tooltip/const.ts @@ -0,0 +1,6 @@ +import {atoms as a} from '#/alf' + +export const BUBBLE_MAX_WIDTH = 240 +export const ARROW_SIZE = 12 +export const ARROW_HALF_SIZE = ARROW_SIZE / 2 +export const MIN_EDGE_SPACE = a.px_lg.paddingLeft diff --git a/src/components/Tooltip/index.tsx b/src/components/Tooltip/index.tsx new file mode 100644 index 000000000..446cf18fc --- /dev/null +++ b/src/components/Tooltip/index.tsx @@ -0,0 +1,411 @@ +import { + Children, + createContext, + useCallback, + useContext, + useMemo, + useRef, + useState, +} from 'react' +import {useWindowDimensions, View} from 'react-native' +import Animated, {Easing, ZoomIn} from 'react-native-reanimated' +import {useSafeAreaInsets} from 'react-native-safe-area-context' + +import {atoms as a, select, useTheme} from '#/alf' +import {useOnGesture} from '#/components/hooks/useOnGesture' +import {Portal} from '#/components/Portal' +import { + ARROW_HALF_SIZE, + ARROW_SIZE, + BUBBLE_MAX_WIDTH, + MIN_EDGE_SPACE, +} from '#/components/Tooltip/const' +import {Text} from '#/components/Typography' + +/** + * These are native specific values, not shared with web + */ +const ARROW_VISUAL_OFFSET = ARROW_SIZE / 1.25 // vibes-based, slightly off the target +const BUBBLE_SHADOW_OFFSET = ARROW_SIZE / 3 // vibes-based, provide more shadow beneath tip + +type TooltipContextType = { + position: 'top' | 'bottom' + ready: boolean + onVisibleChange: (visible: boolean) => void +} + +type TargetContextType = { + targetMeasurements: + | { + x: number + y: number + width: number + height: number + } + | undefined + targetRef: React.RefObject +} + +const TooltipContext = createContext({ + position: 'bottom', + ready: false, + onVisibleChange: () => {}, +}) + +const TargetContext = createContext({ + targetMeasurements: undefined, + targetRef: {current: null}, +}) + +export function Outer({ + children, + position = 'bottom', + visible: requestVisible, + onVisibleChange, +}: { + children: React.ReactNode + position?: 'top' | 'bottom' + visible: boolean + onVisibleChange: (visible: boolean) => void +}) { + /** + * Whether we have measured the target and are ready to show the tooltip. + */ + const [ready, setReady] = useState(false) + /** + * Lagging state to track the externally-controlled visibility of the + * tooltip. + */ + const [prevRequestVisible, setPrevRequestVisible] = useState< + boolean | undefined + >() + /** + * Needs to reference the element this Tooltip is attached to. + */ + const targetRef = useRef(null) + const [targetMeasurements, setTargetMeasurements] = useState< + | { + x: number + y: number + width: number + height: number + } + | undefined + >(undefined) + + if (requestVisible && !prevRequestVisible) { + setPrevRequestVisible(true) + + if (targetRef.current) { + /* + * Once opened, measure the dimensions and position of the target + */ + targetRef.current.measure((_x, _y, width, height, pageX, pageY) => { + if (pageX !== undefined && pageY !== undefined && width && height) { + setTargetMeasurements({x: pageX, y: pageY, width, height}) + setReady(true) + } + }) + } + } else if (!requestVisible && prevRequestVisible) { + setPrevRequestVisible(false) + setTargetMeasurements(undefined) + setReady(false) + } + + const ctx = useMemo( + () => ({position, ready, onVisibleChange}), + [position, ready, onVisibleChange], + ) + const targetCtx = useMemo( + () => ({targetMeasurements, targetRef}), + [targetMeasurements, targetRef], + ) + + return ( + + + {children} + + + ) +} + +export function Target({children}: {children: React.ReactNode}) { + const {targetRef} = useContext(TargetContext) + + return ( + + {children} + + ) +} + +export function Content({ + children, + label, +}: { + children: React.ReactNode + label: string +}) { + const {position, ready, onVisibleChange} = useContext(TooltipContext) + const {targetMeasurements} = useContext(TargetContext) + const requestClose = useCallback(() => { + onVisibleChange(false) + }, [onVisibleChange]) + + if (!ready || !targetMeasurements) return null + + return ( + + + {children} + + + ) +} + +function Bubble({ + children, + label, + position, + requestClose, + targetMeasurements, +}: { + children: React.ReactNode + label: string + position: TooltipContextType['position'] + requestClose: () => void + targetMeasurements: Exclude< + TargetContextType['targetMeasurements'], + undefined + > +}) { + const t = useTheme() + const insets = useSafeAreaInsets() + const dimensions = useWindowDimensions() + const [bubbleMeasurements, setBubbleMeasurements] = useState< + | { + width: number + height: number + } + | undefined + >(undefined) + const coords = useMemo(() => { + if (!bubbleMeasurements) + return { + top: 0, + bottom: 0, + left: 0, + right: 0, + tipTop: 0, + tipLeft: 0, + } + + const {width: ww, height: wh} = dimensions + const maxTop = insets.top + const maxBottom = wh - insets.bottom + const {width: cw, height: ch} = bubbleMeasurements + const minLeft = MIN_EDGE_SPACE + const maxLeft = ww - minLeft + + let computedPosition: 'top' | 'bottom' = position + let top = targetMeasurements.y + targetMeasurements.height + let left = Math.max( + minLeft, + targetMeasurements.x + targetMeasurements.width / 2 - cw / 2, + ) + const tipTranslate = ARROW_HALF_SIZE * -1 + let tipTop = tipTranslate + + if (left + cw > maxLeft) { + left -= left + cw - maxLeft + } + + let tipLeft = + targetMeasurements.x - + left + + targetMeasurements.width / 2 - + ARROW_HALF_SIZE + + let bottom = top + ch + + function positionTop() { + top = top - ch - targetMeasurements.height + bottom = top + ch + tipTop = tipTop + ch + computedPosition = 'top' + } + + function positionBottom() { + top = targetMeasurements.y + targetMeasurements.height + bottom = top + ch + tipTop = tipTranslate + computedPosition = 'bottom' + } + + if (position === 'top') { + positionTop() + if (top < maxTop) { + positionBottom() + } + } else { + if (bottom > maxBottom) { + positionTop() + } + } + + if (computedPosition === 'bottom') { + top += ARROW_VISUAL_OFFSET + bottom += ARROW_VISUAL_OFFSET + } else { + top -= ARROW_VISUAL_OFFSET + bottom -= ARROW_VISUAL_OFFSET + } + + return { + computedPosition, + top, + bottom, + left, + right: left + cw, + tipTop, + tipLeft, + } + }, [position, targetMeasurements, bubbleMeasurements, insets, dimensions]) + + const requestCloseWrapped = useCallback(() => { + setBubbleMeasurements(undefined) + requestClose() + }, [requestClose]) + + useOnGesture( + useCallback( + e => { + const {x, y} = e + const isInside = + x > coords.left && + x < coords.right && + y > coords.top && + y < coords.bottom + + if (!isInside) { + requestCloseWrapped() + } + }, + [coords, requestCloseWrapped], + ), + ) + + return ( + + + + { + setBubbleMeasurements({ + width: e.nativeEvent.layout.width, + height: e.nativeEvent.layout.height, + }) + }}> + {children} + + + + ) +} + +function oppposite(position: 'top' | 'bottom') { + switch (position) { + case 'top': + return 'center bottom' + case 'bottom': + return 'center top' + default: + return 'center' + } +} + +export function TextBubble({children}: {children: React.ReactNode}) { + const c = Children.toArray(children) + return ( + + + {c.map((child, i) => ( + + {child} + + ))} + + + ) +} diff --git a/src/components/Tooltip/index.web.tsx b/src/components/Tooltip/index.web.tsx new file mode 100644 index 000000000..739a714cd --- /dev/null +++ b/src/components/Tooltip/index.web.tsx @@ -0,0 +1,112 @@ +import {Children, createContext, useContext, useMemo} from 'react' +import {View} from 'react-native' +import {Popover} from 'radix-ui' + +import {atoms as a, flatten, select, useTheme} from '#/alf' +import {transparentifyColor} from '#/alf/util/colorGeneration' +import { + ARROW_SIZE, + BUBBLE_MAX_WIDTH, + MIN_EDGE_SPACE, +} from '#/components/Tooltip/const' +import {Text} from '#/components/Typography' + +type TooltipContextType = { + position: 'top' | 'bottom' +} + +const TooltipContext = createContext({ + position: 'bottom', +}) + +export function Outer({ + children, + position = 'bottom', + visible, + onVisibleChange, +}: { + children: React.ReactNode + position?: 'top' | 'bottom' + visible: boolean + onVisibleChange: (visible: boolean) => void +}) { + const ctx = useMemo(() => ({position}), [position]) + return ( + + {children} + + ) +} + +export function Target({children}: {children: React.ReactNode}) { + return ( + + {children} + + ) +} + +export function Content({ + children, + label, +}: { + children: React.ReactNode + label: string +}) { + const t = useTheme() + const {position} = useContext(TooltipContext) + return ( + + + + + {children} + + + + ) +} + +export function TextBubble({children}: {children: React.ReactNode}) { + const c = Children.toArray(children) + return ( + + + {c.map((child, i) => ( + + {child} + + ))} + + + ) +} diff --git a/src/components/hooks/useOnGesture/index.ts b/src/components/hooks/useOnGesture/index.ts new file mode 100644 index 000000000..6f0560661 --- /dev/null +++ b/src/components/hooks/useOnGesture/index.ts @@ -0,0 +1,24 @@ +import {useEffect} from 'react' + +import { + type GlobalGestureEvents, + useGlobalGestureEvents, +} from '#/state/global-gesture-events' + +/** + * Listen for global gesture events. Callback should be wrapped with + * `useCallback` or otherwise memoized to avoid unnecessary re-renders. + */ +export function useOnGesture( + onGestureCallback: (e: GlobalGestureEvents['begin']) => void, +) { + const ctx = useGlobalGestureEvents() + useEffect(() => { + ctx.register() + ctx.events.on('begin', onGestureCallback) + return () => { + ctx.unregister() + ctx.events.off('begin', onGestureCallback) + } + }, [ctx, onGestureCallback]) +} diff --git a/src/components/hooks/useOnGesture/index.web.ts b/src/components/hooks/useOnGesture/index.web.ts new file mode 100644 index 000000000..6129fde10 --- /dev/null +++ b/src/components/hooks/useOnGesture/index.web.ts @@ -0,0 +1 @@ +export function useOnGesture() {} diff --git a/src/state/global-gesture-events/index.tsx b/src/state/global-gesture-events/index.tsx new file mode 100644 index 000000000..f8d87a8f9 --- /dev/null +++ b/src/state/global-gesture-events/index.tsx @@ -0,0 +1,83 @@ +import {createContext, useContext, useMemo, useRef, useState} from 'react' +import {View} from 'react-native' +import { + Gesture, + GestureDetector, + type GestureStateChangeEvent, + type GestureUpdateEvent, + type PanGestureHandlerEventPayload, +} from 'react-native-gesture-handler' +import EventEmitter from 'eventemitter3' + +export type GlobalGestureEvents = { + begin: GestureStateChangeEvent + update: GestureUpdateEvent + end: GestureStateChangeEvent + finalize: GestureStateChangeEvent +} + +const Context = createContext<{ + events: EventEmitter + register: () => void + unregister: () => void +}>({ + events: new EventEmitter(), + register: () => {}, + unregister: () => {}, +}) + +export function GlobalGestureEventsProvider({ + children, +}: { + children: React.ReactNode +}) { + const refCount = useRef(0) + const events = useMemo(() => new EventEmitter(), []) + const [enabled, setEnabled] = useState(false) + const ctx = useMemo( + () => ({ + events, + register() { + refCount.current += 1 + if (refCount.current === 1) { + setEnabled(true) + } + }, + unregister() { + refCount.current -= 1 + if (refCount.current === 0) { + setEnabled(false) + } + }, + }), + [events, setEnabled], + ) + const gesture = Gesture.Pan() + .runOnJS(true) + .enabled(enabled) + .simultaneousWithExternalGesture() + .onBegin(e => { + events.emit('begin', e) + }) + .onUpdate(e => { + events.emit('update', e) + }) + .onEnd(e => { + events.emit('end', e) + }) + .onFinalize(e => { + events.emit('finalize', e) + }) + + return ( + + + {children} + + + ) +} + +export function useGlobalGestureEvents() { + return useContext(Context) +} diff --git a/src/state/global-gesture-events/index.web.tsx b/src/state/global-gesture-events/index.web.tsx new file mode 100644 index 000000000..5d6d53369 --- /dev/null +++ b/src/state/global-gesture-events/index.web.tsx @@ -0,0 +1,9 @@ +export function GlobalGestureEventsProvider(_props: { + children: React.ReactNode +}) { + throw new Error('GlobalGestureEventsProvider is not supported on web.') +} + +export function useGlobalGestureEvents() { + throw new Error('useGlobalGestureEvents is not supported on web.') +} diff --git a/src/style.css b/src/style.css index a4a2c455f..35ffe0d3a 100644 --- a/src/style.css +++ b/src/style.css @@ -334,3 +334,38 @@ input[type='range'][orient='vertical']::-moz-range-thumb { min-width: var(--radix-select-trigger-width); max-height: var(--radix-select-content-available-height); } + +/* + * #/components/Tooltip/index.web.tsx + */ +.radix-popover-content { + animation-duration: 300ms; + animation-timing-function: cubic-bezier(0.17, 0.73, 0.14, 1); + will-change: transform, opacity; +} +.radix-popover-content[data-state='open'][data-side='top'] { + animation-name: radixPopoverSlideUpAndFade; +} +.radix-popover-content[data-state='open'][data-side='bottom'] { + animation-name: radixPopoverSlideDownAndFade; +} +@keyframes radixPopoverSlideUpAndFade { + from { + opacity: 0; + transform: translateY(2px); + } + to { + opacity: 1; + transform: translateY(0); + } +} +@keyframes radixPopoverSlideDownAndFade { + from { + opacity: 0; + transform: translateY(-2px); + } + to { + opacity: 1; + transform: translateY(0); + } +}