-4
.github/workflows/build-submit-android.yml
-4
.github/workflows/build-submit-android.yml
+1
-13
.github/workflows/bundle-deploy-eas-update.yml
+1
-13
.github/workflows/bundle-deploy-eas-update.yml
···
5
5
push:
6
6
branches:
7
7
- main
8
-
- hailey/eas-fab
9
8
workflow_dispatch:
10
9
inputs:
11
10
channel:
···
119
118
120
119
- name: 🏗️ Create Bundle
121
120
if: ${{ !steps.fingerprint.outputs.includes-changes }}
122
-
run: |
123
-
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
124
-
mv ./dist ./ios-dist
125
-
sed -i 's/"react-native-mmkv": "\^2\.12\.2"/"react-native-mmkv": "^3.3.0"/' package.json
126
-
yarn install
127
-
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
128
-
mv ./dist ./android-dist
129
-
121
+
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
130
122
131
123
- name: 📦 Package Bundle and 🚀 Deploy
132
124
if: ${{ !steps.fingerprint.outputs.includes-changes }}
···
282
274
with:
283
275
distribution: 'temurin'
284
276
java-version: '17'
285
-
286
-
- name: "Use upgraded MMKV for Fabric"
287
-
run: |
288
-
sed -i 's/"react-native-mmkv": "\^2\.12\.2"/"react-native-mmkv": "^3.3.0"/' package.json
289
277
290
278
- name: ⚙️ Install dependencies
291
279
run: yarn install
+1
-1
app.config.js
+1
-1
app.config.js
+1
-3
bskyembed/tailwind.config.cjs
+1
-3
bskyembed/tailwind.config.cjs
···
1
1
/** @type {import('tailwindcss').Config} */
2
2
module.exports = {
3
3
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
4
-
darkMode: ['variant', [
5
-
'&:is(.dark *):not(:is(.dark .light *))',
6
-
]],
4
+
darkMode: ['variant', ['&:is(.dark *):not(:is(.dark .light *))']],
7
5
theme: {
8
6
extend: {
9
7
colors: {
+11
-8
bskylink/src/db/index.ts
+11
-8
bskylink/src/db/index.ts
···
1
1
import assert from 'assert'
2
2
import {
3
3
Kysely,
4
-
KyselyPlugin,
4
+
type KyselyPlugin,
5
5
Migrator,
6
-
PluginTransformQueryArgs,
7
-
PluginTransformResultArgs,
6
+
type PluginTransformQueryArgs,
7
+
type PluginTransformResultArgs,
8
8
PostgresDialect,
9
-
QueryResult,
10
-
RootOperationNode,
11
-
UnknownRow,
9
+
type QueryResult,
10
+
type RootOperationNode,
11
+
type UnknownRow,
12
12
} from 'kysely'
13
13
import {default as Pg} from 'pg'
14
14
15
15
import {dbLogger as log} from '../logger.js'
16
16
import {default as migrations} from './migrations/index.js'
17
17
import {DbMigrationProvider} from './migrations/provider.js'
18
-
import {DbSchema} from './schema.js'
18
+
import {type DbSchema} from './schema.js'
19
19
20
20
export class Database {
21
21
migrator: Migrator
22
22
destroyed = false
23
23
24
-
constructor(public db: Kysely<DbSchema>, public cfg: PgConfig) {
24
+
constructor(
25
+
public db: Kysely<DbSchema>,
26
+
public cfg: PgConfig,
27
+
) {
25
28
this.migrator = new Migrator({
26
29
db,
27
30
migrationTableSchema: cfg.schema,
+7
-4
bskylink/src/index.ts
+7
-4
bskylink/src/index.ts
···
1
1
import events from 'node:events'
2
-
import http from 'node:http'
2
+
import type http from 'node:http'
3
3
4
4
import cors from 'cors'
5
5
import express from 'express'
6
-
import {createHttpTerminator, HttpTerminator} from 'http-terminator'
6
+
import {createHttpTerminator, type HttpTerminator} from 'http-terminator'
7
7
8
-
import {Config} from './config.js'
8
+
import {type Config} from './config.js'
9
9
import {AppContext} from './context.js'
10
10
import {default as routes, errorHandler} from './routes/index.js'
11
11
···
17
17
public server?: http.Server
18
18
private terminator?: HttpTerminator
19
19
20
-
constructor(public app: express.Application, public ctx: AppContext) {}
20
+
constructor(
21
+
public app: express.Application,
22
+
public ctx: AppContext,
23
+
) {}
21
24
22
25
static async create(cfg: Config): Promise<LinkService> {
23
26
let app = express()
+7
-4
bskyogcard/src/index.ts
+7
-4
bskyogcard/src/index.ts
···
1
1
import events from 'node:events'
2
-
import http from 'node:http'
2
+
import type http from 'node:http'
3
3
4
4
import express from 'express'
5
-
import {createHttpTerminator, HttpTerminator} from 'http-terminator'
5
+
import {createHttpTerminator, type HttpTerminator} from 'http-terminator'
6
6
7
-
import {Config} from './config.js'
7
+
import {type Config} from './config.js'
8
8
import {AppContext} from './context.js'
9
9
import {default as routes, errorHandler} from './routes/index.js'
10
10
···
15
15
public server?: http.Server
16
16
private terminator?: HttpTerminator
17
17
18
-
constructor(public app: express.Application, public ctx: AppContext) {}
18
+
constructor(
19
+
public app: express.Application,
20
+
public ctx: AppContext,
21
+
) {}
19
22
20
23
static async create(cfg: Config): Promise<CardService> {
21
24
let app = express()
+1
-1
modules/expo-emoji-picker/src/EmojiPicker.android.tsx
+1
-1
modules/expo-emoji-picker/src/EmojiPicker.android.tsx
+2
-3
package.json
+2
-3
package.json
···
61
61
"intl:push": "crowdin push translations --verbose -b main",
62
62
"nuke": "rm -rf ./node_modules && rm -rf ./ios && rm -rf ./android",
63
63
"update-extensions": "bash scripts/updateExtensions.sh",
64
-
"export-ios": "npx expo export --platform ios --dump-sourcemap && yarn upload-native-sourcemaps",
65
-
"export-android": "npx expo export --platform android --dump-sourcemap && yarn upload-native-sourcemaps",
64
+
"export": "npx expo export --dump-sourcemap && yarn upload-native-sourcemaps",
66
65
"upload-native-sourcemaps": "npx sentry-expo-upload-sourcemaps dist",
67
66
"make-deploy-bundle": "bash scripts/bundleUpdate.sh",
68
67
"generate-webpack-stats-file": "EXPO_PUBLIC_GENERATE_STATS=1 yarn build-web",
···
266
265
"lint-staged": "^13.2.3",
267
266
"lockfile-lint": "^4.14.0",
268
267
"metro-react-native-babel-preset": "^0.77.0",
269
-
"prettier": "^2.8.3",
268
+
"prettier": "^3.6.0",
270
269
"react-native-dotenv": "^3.4.11",
271
270
"react-refresh": "^0.14.0",
272
271
"svgo": "^3.3.2",
+9
-17
scripts/bundleUpdate.js
+9
-17
scripts/bundleUpdate.js
···
3
3
const fsp = fs.promises
4
4
const path = require('path')
5
5
6
-
const IOS_DIST_DIR = './ios-dist'
7
-
const ANDROID_DIST_DIR = './android-dist'
8
-
6
+
const DIST_DIR = './dist'
9
7
const BUNDLES_DIR = '/_expo/static/js'
10
-
11
-
const IOS_BUNDLE_DIR = path.join(IOS_DIST_DIR, BUNDLES_DIR, '/ios')
12
-
const ANDROID_BUNDLE_DIR = path.join(ANDROID_DIST_DIR, BUNDLES_DIR, '/android')
13
-
14
-
const IOS_METADATA_PATH = path.join(IOS_DIST_DIR, '/metadata.json')
15
-
const ANDROID_METADATA_PATH = path.join(ANDROID_DIST_DIR, '/metadata.json')
16
-
8
+
const IOS_BUNDLE_DIR = path.join(DIST_DIR, BUNDLES_DIR, '/ios')
9
+
const ANDROID_BUNDLE_DIR = path.join(DIST_DIR, BUNDLES_DIR, '/android')
10
+
const METADATA_PATH = path.join(DIST_DIR, '/metadata.json')
17
11
const DEST_DIR = './bundleTempDir'
18
12
19
13
// Weird, don't feel like figuring out _why_ it wants this
20
-
const IOS_METADATA = require(`../${IOS_METADATA_PATH}`)
21
-
const ANDROID_METADATA = require(`../${ANDROID_METADATA_PATH}`)
22
-
23
-
const IOS_METADATA_ASSETS = IOS_METADATA.fileMetadata.ios.assets
24
-
const ANDROID_METADATA_ASSETS = ANDROID_METADATA.fileMetadata.android.assets
14
+
const METADATA = require(`../${METADATA_PATH}`)
15
+
const IOS_METADATA_ASSETS = METADATA.fileMetadata.ios.assets
16
+
const ANDROID_METADATA_ASSETS = METADATA.fileMetadata.android.assets
25
17
26
18
const getMd5 = async path => {
27
19
return new Promise(res => {
···
68
60
69
61
console.log('Getting ios asset md5s and moving them...')
70
62
for (const asset of IOS_METADATA_ASSETS) {
71
-
const currPath = path.join(IOS_DIST_DIR, asset.path)
63
+
const currPath = path.join(DIST_DIR, asset.path)
72
64
const md5 = await getMd5(currPath)
73
65
const withExtPath = `assets/${md5}.${asset.ext}`
74
66
iosAssets.push(withExtPath)
···
77
69
78
70
console.log('Getting android asset md5s and moving them...')
79
71
for (const asset of ANDROID_METADATA_ASSETS) {
80
-
const currPath = path.join(ANDROID_DIST_DIR, asset.path)
72
+
const currPath = path.join(DIST_DIR, asset.path)
81
73
const md5 = await getMd5(currPath)
82
74
const withExtPath = `assets/${md5}.${asset.ext}`
83
75
androidAssets.push(withExtPath)
+3
-4
scripts/post-web-build.js
+3
-4
scripts/post-web-build.js
···
9
9
'scripts.html',
10
10
)
11
11
12
-
const {entrypoints} = require(path.join(
13
-
projectRoot,
14
-
'web-build/asset-manifest.json',
15
-
))
12
+
const {entrypoints} = require(
13
+
path.join(projectRoot, 'web-build/asset-manifest.json'),
14
+
)
16
15
17
16
console.log(`Found ${entrypoints.length} entrypoints`)
18
17
console.log(`Writing ${templateFile}`)
+8
-5
src/App.native.tsx
+8
-5
src/App.native.tsx
···
33
33
ensureGeolocationResolved,
34
34
Provider as GeolocationProvider,
35
35
} from '#/state/geolocation'
36
+
import {GlobalGestureEventsProvider} from '#/state/global-gesture-events'
36
37
import {Provider as HomeBadgeProvider} from '#/state/home-badge'
37
38
import {Provider as InvitesStateProvider} from '#/state/invites'
38
39
import {Provider as LightboxStateProvider} from '#/state/lightbox'
···
154
155
<HideBottomBarBorderProvider>
155
156
<GestureHandlerRootView
156
157
style={s.h100pct}>
157
-
<IntentDialogProvider>
158
-
<TestCtrls />
159
-
<Shell />
160
-
<NuxDialogs />
161
-
</IntentDialogProvider>
158
+
<GlobalGestureEventsProvider>
159
+
<IntentDialogProvider>
160
+
<TestCtrls />
161
+
<Shell />
162
+
<NuxDialogs />
163
+
</IntentDialogProvider>
164
+
</GlobalGestureEventsProvider>
162
165
</GestureHandlerRootView>
163
166
</HideBottomBarBorderProvider>
164
167
</ServiceAccountManager>
+4
src/alf/atoms.ts
+4
src/alf/atoms.ts
···
983
983
transition_none: web({
984
984
transitionProperty: 'none',
985
985
}),
986
+
transition_timing_default: web({
987
+
transitionTimingFunction: 'cubic-bezier(0.17, 0.73, 0.14, 1)',
988
+
transitionDuration: '100ms',
989
+
}),
986
990
transition_all: web({
987
991
transitionProperty: 'all',
988
992
transitionTimingFunction: 'cubic-bezier(0.17, 0.73, 0.14, 1)',
+3
-3
src/components/ContextMenu/index.tsx
+3
-3
src/components/ContextMenu/index.tsx
···
190
190
if (item) playHaptic('Light')
191
191
setHoveredMenuItem(item)
192
192
},
193
-
} satisfies ContextType),
193
+
}) satisfies ContextType,
194
194
[
195
195
measurement,
196
196
setMeasurement,
···
710
710
const xOffset = position
711
711
? position.x
712
712
: align === 'left'
713
-
? measurement.x
714
-
: measurement.x + measurement.width - layout.width
713
+
? measurement.x
714
+
: measurement.x + measurement.width - layout.width
715
715
716
716
registerHoverable(
717
717
id,
+4
-4
src/components/Link.tsx
+4
-4
src/components/Link.tsx
···
100
100
return typeof to === 'string'
101
101
? convertBskyAppUrlIfNeeded(sanitizeUrl(to))
102
102
: to.screen
103
-
? router.matchName(to.screen)?.build(to.params)
104
-
: to.href
105
-
? convertBskyAppUrlIfNeeded(sanitizeUrl(to.href))
106
-
: undefined
103
+
? router.matchName(to.screen)?.build(to.params)
104
+
: to.href
105
+
? convertBskyAppUrlIfNeeded(sanitizeUrl(to.href))
106
+
: undefined
107
107
}, [to])
108
108
109
109
if (!href) {
+3
-3
src/components/Post/Embed/ImageEmbed.tsx
+3
-3
src/components/Post/Embed/ImageEmbed.tsx
···
82
82
rest.viewContext === PostEmbedViewContext.ThreadHighlighted
83
83
? 'none'
84
84
: rest.viewContext ===
85
-
PostEmbedViewContext.FeedEmbedRecordWithMedia
86
-
? 'square'
87
-
: 'constrained'
85
+
PostEmbedViewContext.FeedEmbedRecordWithMedia
86
+
? 'square'
87
+
: 'constrained'
88
88
}
89
89
image={image}
90
90
onPress={(containerRef, dims) => onPress(0, [containerRef], [dims])}
+2
-2
src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx
+2
-2
src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx
+2
-2
src/components/PostControls/PostMenu/PostMenuItems.tsx
+2
-2
src/components/PostControls/PostMenu/PostMenuItems.tsx
+2
-2
src/components/Select/index.web.tsx
+2
-2
src/components/Select/index.web.tsx
+6
src/components/Tooltip/const.ts
+6
src/components/Tooltip/const.ts
+411
src/components/Tooltip/index.tsx
+411
src/components/Tooltip/index.tsx
···
1
+
import {
2
+
Children,
3
+
createContext,
4
+
useCallback,
5
+
useContext,
6
+
useMemo,
7
+
useRef,
8
+
useState,
9
+
} from 'react'
10
+
import {useWindowDimensions, View} from 'react-native'
11
+
import Animated, {Easing, ZoomIn} from 'react-native-reanimated'
12
+
import {useSafeAreaInsets} from 'react-native-safe-area-context'
13
+
14
+
import {atoms as a, select, useTheme} from '#/alf'
15
+
import {useOnGesture} from '#/components/hooks/useOnGesture'
16
+
import {Portal} from '#/components/Portal'
17
+
import {
18
+
ARROW_HALF_SIZE,
19
+
ARROW_SIZE,
20
+
BUBBLE_MAX_WIDTH,
21
+
MIN_EDGE_SPACE,
22
+
} from '#/components/Tooltip/const'
23
+
import {Text} from '#/components/Typography'
24
+
25
+
/**
26
+
* These are native specific values, not shared with web
27
+
*/
28
+
const ARROW_VISUAL_OFFSET = ARROW_SIZE / 1.25 // vibes-based, slightly off the target
29
+
const BUBBLE_SHADOW_OFFSET = ARROW_SIZE / 3 // vibes-based, provide more shadow beneath tip
30
+
31
+
type TooltipContextType = {
32
+
position: 'top' | 'bottom'
33
+
ready: boolean
34
+
onVisibleChange: (visible: boolean) => void
35
+
}
36
+
37
+
type TargetContextType = {
38
+
targetMeasurements:
39
+
| {
40
+
x: number
41
+
y: number
42
+
width: number
43
+
height: number
44
+
}
45
+
| undefined
46
+
targetRef: React.RefObject<View>
47
+
}
48
+
49
+
const TooltipContext = createContext<TooltipContextType>({
50
+
position: 'bottom',
51
+
ready: false,
52
+
onVisibleChange: () => {},
53
+
})
54
+
55
+
const TargetContext = createContext<TargetContextType>({
56
+
targetMeasurements: undefined,
57
+
targetRef: {current: null},
58
+
})
59
+
60
+
export function Outer({
61
+
children,
62
+
position = 'bottom',
63
+
visible: requestVisible,
64
+
onVisibleChange,
65
+
}: {
66
+
children: React.ReactNode
67
+
position?: 'top' | 'bottom'
68
+
visible: boolean
69
+
onVisibleChange: (visible: boolean) => void
70
+
}) {
71
+
/**
72
+
* Whether we have measured the target and are ready to show the tooltip.
73
+
*/
74
+
const [ready, setReady] = useState(false)
75
+
/**
76
+
* Lagging state to track the externally-controlled visibility of the
77
+
* tooltip.
78
+
*/
79
+
const [prevRequestVisible, setPrevRequestVisible] = useState<
80
+
boolean | undefined
81
+
>()
82
+
/**
83
+
* Needs to reference the element this Tooltip is attached to.
84
+
*/
85
+
const targetRef = useRef<View>(null)
86
+
const [targetMeasurements, setTargetMeasurements] = useState<
87
+
| {
88
+
x: number
89
+
y: number
90
+
width: number
91
+
height: number
92
+
}
93
+
| undefined
94
+
>(undefined)
95
+
96
+
if (requestVisible && !prevRequestVisible) {
97
+
setPrevRequestVisible(true)
98
+
99
+
if (targetRef.current) {
100
+
/*
101
+
* Once opened, measure the dimensions and position of the target
102
+
*/
103
+
targetRef.current.measure((_x, _y, width, height, pageX, pageY) => {
104
+
if (pageX !== undefined && pageY !== undefined && width && height) {
105
+
setTargetMeasurements({x: pageX, y: pageY, width, height})
106
+
setReady(true)
107
+
}
108
+
})
109
+
}
110
+
} else if (!requestVisible && prevRequestVisible) {
111
+
setPrevRequestVisible(false)
112
+
setTargetMeasurements(undefined)
113
+
setReady(false)
114
+
}
115
+
116
+
const ctx = useMemo(
117
+
() => ({position, ready, onVisibleChange}),
118
+
[position, ready, onVisibleChange],
119
+
)
120
+
const targetCtx = useMemo(
121
+
() => ({targetMeasurements, targetRef}),
122
+
[targetMeasurements, targetRef],
123
+
)
124
+
125
+
return (
126
+
<TooltipContext.Provider value={ctx}>
127
+
<TargetContext.Provider value={targetCtx}>
128
+
{children}
129
+
</TargetContext.Provider>
130
+
</TooltipContext.Provider>
131
+
)
132
+
}
133
+
134
+
export function Target({children}: {children: React.ReactNode}) {
135
+
const {targetRef} = useContext(TargetContext)
136
+
137
+
return (
138
+
<View collapsable={false} ref={targetRef}>
139
+
{children}
140
+
</View>
141
+
)
142
+
}
143
+
144
+
export function Content({
145
+
children,
146
+
label,
147
+
}: {
148
+
children: React.ReactNode
149
+
label: string
150
+
}) {
151
+
const {position, ready, onVisibleChange} = useContext(TooltipContext)
152
+
const {targetMeasurements} = useContext(TargetContext)
153
+
const requestClose = useCallback(() => {
154
+
onVisibleChange(false)
155
+
}, [onVisibleChange])
156
+
157
+
if (!ready || !targetMeasurements) return null
158
+
159
+
return (
160
+
<Portal>
161
+
<Bubble
162
+
label={label}
163
+
position={position}
164
+
/*
165
+
* Gotta pass these in here. Inside the Bubble, we're Potal-ed outside
166
+
* the context providers.
167
+
*/
168
+
targetMeasurements={targetMeasurements}
169
+
requestClose={requestClose}>
170
+
{children}
171
+
</Bubble>
172
+
</Portal>
173
+
)
174
+
}
175
+
176
+
function Bubble({
177
+
children,
178
+
label,
179
+
position,
180
+
requestClose,
181
+
targetMeasurements,
182
+
}: {
183
+
children: React.ReactNode
184
+
label: string
185
+
position: TooltipContextType['position']
186
+
requestClose: () => void
187
+
targetMeasurements: Exclude<
188
+
TargetContextType['targetMeasurements'],
189
+
undefined
190
+
>
191
+
}) {
192
+
const t = useTheme()
193
+
const insets = useSafeAreaInsets()
194
+
const dimensions = useWindowDimensions()
195
+
const [bubbleMeasurements, setBubbleMeasurements] = useState<
196
+
| {
197
+
width: number
198
+
height: number
199
+
}
200
+
| undefined
201
+
>(undefined)
202
+
const coords = useMemo(() => {
203
+
if (!bubbleMeasurements)
204
+
return {
205
+
top: 0,
206
+
bottom: 0,
207
+
left: 0,
208
+
right: 0,
209
+
tipTop: 0,
210
+
tipLeft: 0,
211
+
}
212
+
213
+
const {width: ww, height: wh} = dimensions
214
+
const maxTop = insets.top
215
+
const maxBottom = wh - insets.bottom
216
+
const {width: cw, height: ch} = bubbleMeasurements
217
+
const minLeft = MIN_EDGE_SPACE
218
+
const maxLeft = ww - minLeft
219
+
220
+
let computedPosition: 'top' | 'bottom' = position
221
+
let top = targetMeasurements.y + targetMeasurements.height
222
+
let left = Math.max(
223
+
minLeft,
224
+
targetMeasurements.x + targetMeasurements.width / 2 - cw / 2,
225
+
)
226
+
const tipTranslate = ARROW_HALF_SIZE * -1
227
+
let tipTop = tipTranslate
228
+
229
+
if (left + cw > maxLeft) {
230
+
left -= left + cw - maxLeft
231
+
}
232
+
233
+
let tipLeft =
234
+
targetMeasurements.x -
235
+
left +
236
+
targetMeasurements.width / 2 -
237
+
ARROW_HALF_SIZE
238
+
239
+
let bottom = top + ch
240
+
241
+
function positionTop() {
242
+
top = top - ch - targetMeasurements.height
243
+
bottom = top + ch
244
+
tipTop = tipTop + ch
245
+
computedPosition = 'top'
246
+
}
247
+
248
+
function positionBottom() {
249
+
top = targetMeasurements.y + targetMeasurements.height
250
+
bottom = top + ch
251
+
tipTop = tipTranslate
252
+
computedPosition = 'bottom'
253
+
}
254
+
255
+
if (position === 'top') {
256
+
positionTop()
257
+
if (top < maxTop) {
258
+
positionBottom()
259
+
}
260
+
} else {
261
+
if (bottom > maxBottom) {
262
+
positionTop()
263
+
}
264
+
}
265
+
266
+
if (computedPosition === 'bottom') {
267
+
top += ARROW_VISUAL_OFFSET
268
+
bottom += ARROW_VISUAL_OFFSET
269
+
} else {
270
+
top -= ARROW_VISUAL_OFFSET
271
+
bottom -= ARROW_VISUAL_OFFSET
272
+
}
273
+
274
+
return {
275
+
computedPosition,
276
+
top,
277
+
bottom,
278
+
left,
279
+
right: left + cw,
280
+
tipTop,
281
+
tipLeft,
282
+
}
283
+
}, [position, targetMeasurements, bubbleMeasurements, insets, dimensions])
284
+
285
+
const requestCloseWrapped = useCallback(() => {
286
+
setBubbleMeasurements(undefined)
287
+
requestClose()
288
+
}, [requestClose])
289
+
290
+
useOnGesture(
291
+
useCallback(
292
+
e => {
293
+
const {x, y} = e
294
+
const isInside =
295
+
x > coords.left &&
296
+
x < coords.right &&
297
+
y > coords.top &&
298
+
y < coords.bottom
299
+
300
+
if (!isInside) {
301
+
requestCloseWrapped()
302
+
}
303
+
},
304
+
[coords, requestCloseWrapped],
305
+
),
306
+
)
307
+
308
+
return (
309
+
<View
310
+
accessible
311
+
role="alert"
312
+
accessibilityHint=""
313
+
accessibilityLabel={label}
314
+
// android
315
+
importantForAccessibility="yes"
316
+
// ios
317
+
accessibilityViewIsModal
318
+
style={[
319
+
a.absolute,
320
+
a.align_start,
321
+
{
322
+
width: BUBBLE_MAX_WIDTH,
323
+
opacity: bubbleMeasurements ? 1 : 0,
324
+
top: coords.top,
325
+
left: coords.left,
326
+
},
327
+
]}>
328
+
<Animated.View
329
+
entering={ZoomIn.easing(Easing.out(Easing.exp))}
330
+
style={{transformOrigin: oppposite(position)}}>
331
+
<View
332
+
style={[
333
+
a.absolute,
334
+
a.top_0,
335
+
a.z_10,
336
+
t.atoms.bg,
337
+
select(t.name, {
338
+
light: t.atoms.bg,
339
+
dark: t.atoms.bg_contrast_100,
340
+
dim: t.atoms.bg_contrast_100,
341
+
}),
342
+
{
343
+
borderTopLeftRadius: a.rounded_2xs.borderRadius,
344
+
borderBottomRightRadius: a.rounded_2xs.borderRadius,
345
+
width: ARROW_SIZE,
346
+
height: ARROW_SIZE,
347
+
transform: [{rotate: '45deg'}],
348
+
top: coords.tipTop,
349
+
left: coords.tipLeft,
350
+
},
351
+
]}
352
+
/>
353
+
<View
354
+
style={[
355
+
a.px_md,
356
+
a.py_sm,
357
+
a.rounded_sm,
358
+
select(t.name, {
359
+
light: t.atoms.bg,
360
+
dark: t.atoms.bg_contrast_100,
361
+
dim: t.atoms.bg_contrast_100,
362
+
}),
363
+
t.atoms.shadow_md,
364
+
{
365
+
shadowOpacity: 0.2,
366
+
shadowOffset: {
367
+
width: 0,
368
+
height:
369
+
BUBBLE_SHADOW_OFFSET *
370
+
(coords.computedPosition === 'bottom' ? -1 : 1),
371
+
},
372
+
},
373
+
]}
374
+
onLayout={e => {
375
+
setBubbleMeasurements({
376
+
width: e.nativeEvent.layout.width,
377
+
height: e.nativeEvent.layout.height,
378
+
})
379
+
}}>
380
+
{children}
381
+
</View>
382
+
</Animated.View>
383
+
</View>
384
+
)
385
+
}
386
+
387
+
function oppposite(position: 'top' | 'bottom') {
388
+
switch (position) {
389
+
case 'top':
390
+
return 'center bottom'
391
+
case 'bottom':
392
+
return 'center top'
393
+
default:
394
+
return 'center'
395
+
}
396
+
}
397
+
398
+
export function TextBubble({children}: {children: React.ReactNode}) {
399
+
const c = Children.toArray(children)
400
+
return (
401
+
<Content label={c.join(' ')}>
402
+
<View style={[a.gap_xs]}>
403
+
{c.map((child, i) => (
404
+
<Text key={i} style={[a.text_sm, a.leading_snug]}>
405
+
{child}
406
+
</Text>
407
+
))}
408
+
</View>
409
+
</Content>
410
+
)
411
+
}
+112
src/components/Tooltip/index.web.tsx
+112
src/components/Tooltip/index.web.tsx
···
1
+
import {Children, createContext, useContext, useMemo} from 'react'
2
+
import {View} from 'react-native'
3
+
import {Popover} from 'radix-ui'
4
+
5
+
import {atoms as a, flatten, select, useTheme} from '#/alf'
6
+
import {transparentifyColor} from '#/alf/util/colorGeneration'
7
+
import {
8
+
ARROW_SIZE,
9
+
BUBBLE_MAX_WIDTH,
10
+
MIN_EDGE_SPACE,
11
+
} from '#/components/Tooltip/const'
12
+
import {Text} from '#/components/Typography'
13
+
14
+
type TooltipContextType = {
15
+
position: 'top' | 'bottom'
16
+
}
17
+
18
+
const TooltipContext = createContext<TooltipContextType>({
19
+
position: 'bottom',
20
+
})
21
+
22
+
export function Outer({
23
+
children,
24
+
position = 'bottom',
25
+
visible,
26
+
onVisibleChange,
27
+
}: {
28
+
children: React.ReactNode
29
+
position?: 'top' | 'bottom'
30
+
visible: boolean
31
+
onVisibleChange: (visible: boolean) => void
32
+
}) {
33
+
const ctx = useMemo(() => ({position}), [position])
34
+
return (
35
+
<Popover.Root open={visible} onOpenChange={onVisibleChange}>
36
+
<TooltipContext.Provider value={ctx}>{children}</TooltipContext.Provider>
37
+
</Popover.Root>
38
+
)
39
+
}
40
+
41
+
export function Target({children}: {children: React.ReactNode}) {
42
+
return (
43
+
<Popover.Trigger asChild>
44
+
<View collapsable={false}>{children}</View>
45
+
</Popover.Trigger>
46
+
)
47
+
}
48
+
49
+
export function Content({
50
+
children,
51
+
label,
52
+
}: {
53
+
children: React.ReactNode
54
+
label: string
55
+
}) {
56
+
const t = useTheme()
57
+
const {position} = useContext(TooltipContext)
58
+
return (
59
+
<Popover.Portal>
60
+
<Popover.Content
61
+
className="radix-popover-content"
62
+
aria-label={label}
63
+
side={position}
64
+
sideOffset={4}
65
+
collisionPadding={MIN_EDGE_SPACE}
66
+
style={flatten([
67
+
a.rounded_sm,
68
+
select(t.name, {
69
+
light: t.atoms.bg,
70
+
dark: t.atoms.bg_contrast_100,
71
+
dim: t.atoms.bg_contrast_100,
72
+
}),
73
+
{
74
+
minWidth: 'max-content',
75
+
boxShadow: select(t.name, {
76
+
light: `0 0 24px ${transparentifyColor(t.palette.black, 0.2)}`,
77
+
dark: `0 0 24px ${transparentifyColor(t.palette.black, 0.2)}`,
78
+
dim: `0 0 24px ${transparentifyColor(t.palette.black, 0.2)}`,
79
+
}),
80
+
},
81
+
])}>
82
+
<Popover.Arrow
83
+
width={ARROW_SIZE}
84
+
height={ARROW_SIZE / 2}
85
+
fill={select(t.name, {
86
+
light: t.atoms.bg.backgroundColor,
87
+
dark: t.atoms.bg_contrast_100.backgroundColor,
88
+
dim: t.atoms.bg_contrast_100.backgroundColor,
89
+
})}
90
+
/>
91
+
<View style={[a.px_md, a.py_sm, {maxWidth: BUBBLE_MAX_WIDTH}]}>
92
+
{children}
93
+
</View>
94
+
</Popover.Content>
95
+
</Popover.Portal>
96
+
)
97
+
}
98
+
99
+
export function TextBubble({children}: {children: React.ReactNode}) {
100
+
const c = Children.toArray(children)
101
+
return (
102
+
<Content label={c.join(' ')}>
103
+
<View style={[a.gap_xs]}>
104
+
{c.map((child, i) => (
105
+
<Text key={i} style={[a.text_sm, a.leading_snug]}>
106
+
{child}
107
+
</Text>
108
+
))}
109
+
</View>
110
+
</Content>
111
+
)
112
+
}
+12
-6
src/components/WhoCanReply.tsx
+12
-6
src/components/WhoCanReply.tsx
···
1
1
import React from 'react'
2
-
import {Keyboard, Platform, StyleProp, View, ViewStyle} from 'react-native'
3
2
import {
4
-
AppBskyFeedDefs,
3
+
Keyboard,
4
+
Platform,
5
+
type StyleProp,
6
+
View,
7
+
type ViewStyle,
8
+
} from 'react-native'
9
+
import {
10
+
type AppBskyFeedDefs,
5
11
AppBskyFeedPost,
6
-
AppBskyGraphDefs,
12
+
type AppBskyGraphDefs,
7
13
AtUri,
8
14
} from '@atproto/api'
9
15
import {msg, Trans} from '@lingui/macro'
···
13
19
import {makeListLink, makeProfileLink} from '#/lib/routes/links'
14
20
import {isNative} from '#/platform/detection'
15
21
import {
16
-
ThreadgateAllowUISetting,
22
+
type ThreadgateAllowUISetting,
17
23
threadgateViewToAllowUISetting,
18
24
} from '#/state/queries/threadgate'
19
25
import {atoms as a, useTheme} from '#/alf'
···
70
76
const description = anyoneCanReply
71
77
? _(msg`Everybody can reply`)
72
78
: noOneCanReply
73
-
? _(msg`Replies disabled`)
74
-
: _(msg`Some people can reply`)
79
+
? _(msg`Replies disabled`)
80
+
: _(msg`Some people can reply`)
75
81
76
82
const onPressOpen = () => {
77
83
if (isNative && Keyboard.isVisible()) {
+2
-2
src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx
+2
-2
src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx
+2
-2
src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx
+2
-2
src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx
+2
-2
src/components/dialogs/SearchablePeopleList.tsx
+2
-2
src/components/dialogs/SearchablePeopleList.tsx
+2
-2
src/components/dms/EmojiReactionPicker.tsx
+2
-2
src/components/dms/EmojiReactionPicker.tsx
+1
-2
src/components/dms/MessageContextMenu.tsx
+1
-2
src/components/dms/MessageContextMenu.tsx
+24
src/components/hooks/useOnGesture/index.ts
+24
src/components/hooks/useOnGesture/index.ts
···
1
+
import {useEffect} from 'react'
2
+
3
+
import {
4
+
type GlobalGestureEvents,
5
+
useGlobalGestureEvents,
6
+
} from '#/state/global-gesture-events'
7
+
8
+
/**
9
+
* Listen for global gesture events. Callback should be wrapped with
10
+
* `useCallback` or otherwise memoized to avoid unnecessary re-renders.
11
+
*/
12
+
export function useOnGesture(
13
+
onGestureCallback: (e: GlobalGestureEvents['begin']) => void,
14
+
) {
15
+
const ctx = useGlobalGestureEvents()
16
+
useEffect(() => {
17
+
ctx.register()
18
+
ctx.events.on('begin', onGestureCallback)
19
+
return () => {
20
+
ctx.unregister()
21
+
ctx.events.off('begin', onGestureCallback)
22
+
}
23
+
}, [ctx, onGestureCallback])
24
+
}
+1
src/components/hooks/useOnGesture/index.web.ts
+1
src/components/hooks/useOnGesture/index.web.ts
···
1
+
export function useOnGesture() {}
+23
-24
src/components/icons/VerifiedCheck.tsx
+23
-24
src/components/icons/VerifiedCheck.tsx
···
3
3
4
4
import {type Props, useCommonSVGProps} from '#/components/icons/common'
5
5
6
-
export const VerifiedCheck = React.forwardRef<Svg, Props>(function LogoImpl(
7
-
props,
8
-
ref,
9
-
) {
10
-
const {fill, size, style, ...rest} = useCommonSVGProps(props)
6
+
export const VerifiedCheck = React.forwardRef<Svg, Props>(
7
+
function LogoImpl(props, ref) {
8
+
const {fill, size, style, ...rest} = useCommonSVGProps(props)
11
9
12
-
return (
13
-
<Svg
14
-
fill="none"
15
-
{...rest}
16
-
ref={ref}
17
-
viewBox="0 0 24 24"
18
-
width={size}
19
-
height={size}
20
-
style={[style]}>
21
-
<Circle cx="12" cy="12" r="11.5" fill={fill} />
22
-
<Path
23
-
fill="#fff"
24
-
fillRule="evenodd"
25
-
clipRule="evenodd"
26
-
d="M17.659 8.175a1.361 1.361 0 0 1 0 1.925l-6.224 6.223a1.361 1.361 0 0 1-1.925 0L6.4 13.212a1.361 1.361 0 0 1 1.925-1.925l2.149 2.148 5.26-5.26a1.361 1.361 0 0 1 1.925 0Z"
27
-
/>
28
-
</Svg>
29
-
)
30
-
})
10
+
return (
11
+
<Svg
12
+
fill="none"
13
+
{...rest}
14
+
ref={ref}
15
+
viewBox="0 0 24 24"
16
+
width={size}
17
+
height={size}
18
+
style={[style]}>
19
+
<Circle cx="12" cy="12" r="11.5" fill={fill} />
20
+
<Path
21
+
fill="#fff"
22
+
fillRule="evenodd"
23
+
clipRule="evenodd"
24
+
d="M17.659 8.175a1.361 1.361 0 0 1 0 1.925l-6.224 6.223a1.361 1.361 0 0 1-1.925 0L6.4 13.212a1.361 1.361 0 0 1 1.925-1.925l2.149 2.148 5.26-5.26a1.361 1.361 0 0 1 1.925 0Z"
25
+
/>
26
+
</Svg>
27
+
)
28
+
},
29
+
)
+28
-29
src/components/icons/VerifierCheck.tsx
+28
-29
src/components/icons/VerifierCheck.tsx
···
3
3
4
4
import {type Props, useCommonSVGProps} from '#/components/icons/common'
5
5
6
-
export const VerifierCheck = React.forwardRef<Svg, Props>(function LogoImpl(
7
-
props,
8
-
ref,
9
-
) {
10
-
const {fill, size, style, ...rest} = useCommonSVGProps(props)
6
+
export const VerifierCheck = React.forwardRef<Svg, Props>(
7
+
function LogoImpl(props, ref) {
8
+
const {fill, size, style, ...rest} = useCommonSVGProps(props)
11
9
12
-
return (
13
-
<Svg
14
-
fill="none"
15
-
{...rest}
16
-
ref={ref}
17
-
viewBox="0 0 24 24"
18
-
width={size}
19
-
height={size}
20
-
style={[style]}>
21
-
<Path
22
-
fill={fill}
23
-
fillRule="evenodd"
24
-
clipRule="evenodd"
25
-
d="M8.792 1.615a4.154 4.154 0 0 1 6.416 0 4.154 4.154 0 0 0 3.146 1.515 4.154 4.154 0 0 1 4 5.017 4.154 4.154 0 0 0 .777 3.404 4.154 4.154 0 0 1-1.427 6.255 4.153 4.153 0 0 0-2.177 2.73 4.154 4.154 0 0 1-5.781 2.784 4.154 4.154 0 0 0-3.492 0 4.154 4.154 0 0 1-5.78-2.784 4.154 4.154 0 0 0-2.178-2.73A4.154 4.154 0 0 1 .87 11.551a4.154 4.154 0 0 0 .776-3.404A4.154 4.154 0 0 1 5.646 3.13a4.154 4.154 0 0 0 3.146-1.515Z"
26
-
/>
27
-
<Path
28
-
fill="#fff"
29
-
fillRule="evenodd"
30
-
clipRule="evenodd"
31
-
d="M17.861 8.26a1.438 1.438 0 0 1 0 2.033l-6.571 6.571a1.437 1.437 0 0 1-2.033 0L5.97 13.58a1.438 1.438 0 0 1 2.033-2.033l2.27 2.269 5.554-5.555a1.437 1.437 0 0 1 2.033 0Z"
32
-
/>
33
-
</Svg>
34
-
)
35
-
})
10
+
return (
11
+
<Svg
12
+
fill="none"
13
+
{...rest}
14
+
ref={ref}
15
+
viewBox="0 0 24 24"
16
+
width={size}
17
+
height={size}
18
+
style={[style]}>
19
+
<Path
20
+
fill={fill}
21
+
fillRule="evenodd"
22
+
clipRule="evenodd"
23
+
d="M8.792 1.615a4.154 4.154 0 0 1 6.416 0 4.154 4.154 0 0 0 3.146 1.515 4.154 4.154 0 0 1 4 5.017 4.154 4.154 0 0 0 .777 3.404 4.154 4.154 0 0 1-1.427 6.255 4.153 4.153 0 0 0-2.177 2.73 4.154 4.154 0 0 1-5.781 2.784 4.154 4.154 0 0 0-3.492 0 4.154 4.154 0 0 1-5.78-2.784 4.154 4.154 0 0 0-2.178-2.73A4.154 4.154 0 0 1 .87 11.551a4.154 4.154 0 0 0 .776-3.404A4.154 4.154 0 0 1 5.646 3.13a4.154 4.154 0 0 0 3.146-1.515Z"
24
+
/>
25
+
<Path
26
+
fill="#fff"
27
+
fillRule="evenodd"
28
+
clipRule="evenodd"
29
+
d="M17.861 8.26a1.438 1.438 0 0 1 0 2.033l-6.571 6.571a1.437 1.437 0 0 1-2.033 0L5.97 13.58a1.438 1.438 0 0 1 2.033-2.033l2.27 2.269 5.554-5.555a1.437 1.437 0 0 1 2.033 0Z"
30
+
/>
31
+
</Svg>
32
+
)
33
+
},
34
+
)
+4
-4
src/components/moderation/ContentHider.tsx
+4
-4
src/components/moderation/ContentHider.tsx
···
1
1
import React from 'react'
2
-
import {StyleProp, View, ViewStyle} from 'react-native'
3
-
import {ModerationUI} from '@atproto/api'
2
+
import {type StyleProp, View, type ViewStyle} from 'react-native'
3
+
import {type ModerationUI} from '@atproto/api'
4
4
import {msg, Trans} from '@lingui/macro'
5
5
import {useLingui} from '@lingui/react'
6
6
···
148
148
modui.noOverride
149
149
? _(msg`Learn more about the moderation applied to this content`)
150
150
: override
151
-
? _(msg`Hides the content`)
152
-
: _(msg`Shows the content`)
151
+
? _(msg`Hides the content`)
152
+
: _(msg`Shows the content`)
153
153
}>
154
154
{state => (
155
155
<View
+6
-6
src/components/moderation/ReportDialog/index.tsx
+6
-6
src/components/moderation/ReportDialog/index.tsx
···
512
512
backgroundColor: active
513
513
? t.palette.primary_500
514
514
: completed
515
-
? t.palette.primary_100
516
-
: t.atoms.bg_contrast_25.backgroundColor,
515
+
? t.palette.primary_100
516
+
: t.atoms.bg_contrast_25.backgroundColor,
517
517
borderColor: active
518
518
? t.palette.primary_500
519
519
: completed
520
-
? t.palette.primary_400
521
-
: t.atoms.border_contrast_low.borderColor,
520
+
? t.palette.primary_400
521
+
: t.atoms.border_contrast_low.borderColor,
522
522
},
523
523
]}>
524
524
{completed ? (
···
533
533
color: active
534
534
? 'white'
535
535
: completed
536
-
? t.palette.primary_700
537
-
: t.atoms.text_contrast_medium.color,
536
+
? t.palette.primary_700
537
+
: t.atoms.text_contrast_medium.color,
538
538
fontVariant: ['tabular-nums'],
539
539
width: 24,
540
540
height: 24,
+2
-2
src/components/verification/VerificationCheckButton.tsx
+2
-2
src/components/verification/VerificationCheckButton.tsx
···
130
130
verifiedByHidden
131
131
? t.atoms.bg_contrast_100.backgroundColor
132
132
: state.profile.isVerified
133
-
? t.palette.primary_500
134
-
: t.atoms.bg_contrast_100.backgroundColor
133
+
? t.palette.primary_500
134
+
: t.atoms.bg_contrast_100.backgroundColor
135
135
}
136
136
verifier={state.profile.role === 'verifier'}
137
137
/>
+7
-7
src/components/verification/VerificationsDialog.tsx
+7
-7
src/components/verification/VerificationsDialog.tsx
···
64
64
? _(msg`You are verified`)
65
65
: _(msg`Your verifications`)
66
66
: state.profile.isVerified
67
-
? _(msg`${userName} is verified`)
68
-
: _(
69
-
msg({
70
-
message: `${userName}'s verifications`,
71
-
comment: `Possessive, meaning "the verifications of {userName}"`,
72
-
}),
73
-
)
67
+
? _(msg`${userName} is verified`)
68
+
: _(
69
+
msg({
70
+
message: `${userName}'s verifications`,
71
+
comment: `Possessive, meaning "the verifications of {userName}"`,
72
+
}),
73
+
)
74
74
75
75
return (
76
76
<Dialog.ScrollableInner
+9
-9
src/lib/moderation.ts
+9
-9
src/lib/moderation.ts
···
1
1
import React from 'react'
2
2
import {
3
-
AppBskyLabelerDefs,
3
+
type AppBskyLabelerDefs,
4
4
BskyAgent,
5
-
ComAtprotoLabelDefs,
6
-
InterpretedLabelValueDefinition,
5
+
type ComAtprotoLabelDefs,
6
+
type InterpretedLabelValueDefinition,
7
7
LABELS,
8
-
ModerationCause,
9
-
ModerationOpts,
10
-
ModerationUI,
8
+
type ModerationCause,
9
+
type ModerationOpts,
10
+
type ModerationUI,
11
11
} from '@atproto/api'
12
12
13
13
import {sanitizeDisplayName} from '#/lib/strings/display-names'
14
14
import {sanitizeHandle} from '#/lib/strings/handles'
15
-
import {AppModerationCause} from '#/components/Pills'
15
+
import {type AppModerationCause} from '#/components/Pills'
16
16
17
17
export const ADULT_CONTENT_LABELS = ['sexual', 'nudity', 'porn']
18
18
export const OTHER_SELF_LABELS = ['graphic-media']
···
29
29
cause.source.type === 'labeler'
30
30
? cause.source.did
31
31
: cause.source.type === 'list'
32
-
? cause.source.list.uri
33
-
: 'user'
32
+
? cause.source.list.uri
33
+
: 'user'
34
34
if (cause.type === 'label') {
35
35
return `label:${cause.label.val}:${source}`
36
36
}
+6
-6
src/lib/moderation/useModerationCauseDescription.ts
+6
-6
src/lib/moderation/useModerationCauseDescription.ts
···
1
1
import React from 'react'
2
2
import {
3
3
BSKY_LABELER_DID,
4
-
ModerationCause,
5
-
ModerationCauseSource,
4
+
type ModerationCause,
5
+
type ModerationCauseSource,
6
6
} from '@atproto/api'
7
7
import {msg} from '@lingui/macro'
8
8
import {useLingui} from '@lingui/react'
···
12
12
import {useSession} from '#/state/session'
13
13
import {CircleBanSign_Stroke2_Corner0_Rounded as CircleBanSign} from '#/components/icons/CircleBanSign'
14
14
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
15
-
import {Props as SVGIconProps} from '#/components/icons/common'
15
+
import {type Props as SVGIconProps} from '#/components/icons/common'
16
16
import {EyeSlash_Stroke2_Corner0_Rounded as EyeSlash} from '#/components/icons/EyeSlash'
17
17
import {Warning_Stroke2_Corner0_Rounded as Warning} from '#/components/icons/Warning'
18
-
import {AppModerationCause} from '#/components/Pills'
18
+
import {type AppModerationCause} from '#/components/Pills'
19
19
import {useGlobalLabelStrings} from './useGlobalLabelStrings'
20
20
import {getDefinition, getLabelStrings} from './useLabelInfo'
21
21
···
153
153
def.identifier === '!no-unauthenticated'
154
154
? EyeSlash
155
155
: def.severity === 'alert'
156
-
? Warning
157
-
: CircleInfo,
156
+
? Warning
157
+
: CircleInfo,
158
158
name: strings.name,
159
159
description: strings.description,
160
160
source,
+55
-55
src/locale/locales/en/messages.po
+55
-55
src/locale/locales/en/messages.po
···
456
456
msgid "<0>{0}</0> is included in your starter pack"
457
457
msgstr ""
458
458
459
-
#: src/components/WhoCanReply.tsx:296
459
+
#: src/components/WhoCanReply.tsx:302
460
460
msgid "<0>{0}</0> members"
461
461
msgstr ""
462
462
···
866
866
msgid "An error occurred while compressing the video."
867
867
msgstr ""
868
868
869
-
#: src/state/queries/explore-feed-previews.tsx:184
869
+
#: src/state/queries/explore-feed-previews.tsx:173
870
870
msgid "An error occurred while fetching the feed."
871
871
msgstr ""
872
872
···
939
939
msgid "an unknown labeler"
940
940
msgstr ""
941
941
942
-
#: src/components/WhoCanReply.tsx:317
942
+
#: src/components/WhoCanReply.tsx:323
943
943
msgid "and"
944
944
msgstr ""
945
945
···
1066
1066
msgid "Are you sure you want to delete the app password \"{0}\"?"
1067
1067
msgstr ""
1068
1068
1069
-
#: src/components/dms/MessageContextMenu.tsx:189
1069
+
#: src/components/dms/MessageContextMenu.tsx:188
1070
1070
msgid "Are you sure you want to delete this message? The message will be deleted for you, but not for the other participant."
1071
1071
msgstr ""
1072
1072
···
1128
1128
msgid "At least 3 characters"
1129
1129
msgstr ""
1130
1130
1131
-
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:40
1131
+
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:48
1132
1132
msgctxt "Name of app icon variant"
1133
1133
msgid "Aurora"
1134
1134
msgstr ""
···
1302
1302
msgid "Bluesky cannot confirm the authenticity of the claimed date."
1303
1303
msgstr ""
1304
1304
1305
-
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:129
1305
+
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:165
1306
1306
msgctxt "Name of app icon variant"
1307
1307
msgid "Bluesky Classic™"
1308
1308
msgstr ""
···
2096
2096
msgid "Copy link to starter pack"
2097
2097
msgstr ""
2098
2098
2099
-
#: src/components/dms/MessageContextMenu.tsx:150
2100
-
#: src/components/dms/MessageContextMenu.tsx:153
2099
+
#: src/components/dms/MessageContextMenu.tsx:149
2100
+
#: src/components/dms/MessageContextMenu.tsx:152
2101
2101
msgid "Copy message text"
2102
2102
msgstr ""
2103
2103
···
2145
2145
msgid "Could not process your video"
2146
2146
msgstr ""
2147
2147
2148
-
#: src/state/queries/notifications/settings.ts:50
2148
+
#: src/state/queries/notifications/settings.ts:49
2149
2149
msgid "Could not update notification settings"
2150
2150
msgstr ""
2151
2151
···
2247
2247
msgid "Dark"
2248
2248
msgstr ""
2249
2249
2250
-
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:24
2250
+
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:28
2251
2251
msgctxt "Name of app icon variant"
2252
2252
msgid "Dark"
2253
2253
msgstr ""
···
2286
2286
msgid "Default icons"
2287
2287
msgstr ""
2288
2288
2289
-
#: src/components/dms/MessageContextMenu.tsx:191
2289
+
#: src/components/dms/MessageContextMenu.tsx:190
2290
2290
#: src/components/PostControls/PostMenu/PostMenuItems.tsx:701
2291
2291
#: src/screens/Messages/components/ChatStatusInfo.tsx:55
2292
2292
#: src/screens/Settings/AppPasswords.tsx:212
···
2334
2334
msgid "Delete Conversation"
2335
2335
msgstr ""
2336
2336
2337
-
#: src/components/dms/MessageContextMenu.tsx:164
2337
+
#: src/components/dms/MessageContextMenu.tsx:163
2338
2338
msgid "Delete for me"
2339
2339
msgstr ""
2340
2340
···
2342
2342
msgid "Delete list"
2343
2343
msgstr ""
2344
2344
2345
-
#: src/components/dms/MessageContextMenu.tsx:187
2345
+
#: src/components/dms/MessageContextMenu.tsx:186
2346
2346
msgid "Delete message"
2347
2347
msgstr ""
2348
2348
2349
-
#: src/components/dms/MessageContextMenu.tsx:162
2349
+
#: src/components/dms/MessageContextMenu.tsx:161
2350
2350
msgid "Delete message for me"
2351
2351
msgstr ""
2352
2352
···
2433
2433
msgid "Developer options"
2434
2434
msgstr ""
2435
2435
2436
-
#: src/components/WhoCanReply.tsx:179
2436
+
#: src/components/WhoCanReply.tsx:185
2437
2437
msgid "Dialog: adjust who can interact with this post"
2438
2438
msgstr ""
2439
2439
···
2759
2759
msgid "Edit User List"
2760
2760
msgstr ""
2761
2761
2762
-
#: src/components/WhoCanReply.tsx:91
2762
+
#: src/components/WhoCanReply.tsx:97
2763
2763
msgid "Edit who can reply"
2764
2764
msgstr ""
2765
2765
···
2984
2984
msgid "Everybody"
2985
2985
msgstr ""
2986
2986
2987
-
#: src/components/WhoCanReply.tsx:71
2987
+
#: src/components/WhoCanReply.tsx:77
2988
2988
msgid "Everybody can reply"
2989
2989
msgstr ""
2990
2990
2991
-
#: src/components/WhoCanReply.tsx:222
2991
+
#: src/components/WhoCanReply.tsx:228
2992
2992
msgid "Everybody can reply to this post."
2993
2993
msgstr ""
2994
2994
···
3409
3409
msgid "Fitness"
3410
3410
msgstr ""
3411
3411
3412
-
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:117
3412
+
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:149
3413
3413
msgctxt "Name of app icon variant"
3414
3414
msgid "Flat Black"
3415
3415
msgstr ""
3416
3416
3417
-
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:93
3417
+
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:117
3418
3418
msgctxt "Name of app icon variant"
3419
3419
msgid "Flat Blue"
3420
3420
msgstr ""
3421
3421
3422
-
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:105
3422
+
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:133
3423
3423
msgctxt "Name of app icon variant"
3424
3424
msgid "Flat White"
3425
3425
msgstr ""
···
3920
3920
msgid "Hides the content"
3921
3921
msgstr ""
3922
3922
3923
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:117
3923
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:121
3924
3924
msgid "Hmm, some kind of issue occurred when contacting the feed server. Please let the feed owner know about this issue."
3925
3925
msgstr ""
3926
3926
3927
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:105
3927
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:109
3928
3928
msgid "Hmm, the feed server appears to be misconfigured. Please let the feed owner know about this issue."
3929
3929
msgstr ""
3930
3930
3931
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:111
3931
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:115
3932
3932
msgid "Hmm, the feed server appears to be offline. Please let the feed owner know about this issue."
3933
3933
msgstr ""
3934
3934
3935
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:108
3935
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:112
3936
3936
msgid "Hmm, the feed server gave a bad response. Please let the feed owner know about this issue."
3937
3937
msgstr ""
3938
3938
3939
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:102
3939
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:106
3940
3940
msgid "Hmm, we're having trouble finding this feed. It may have been deleted."
3941
3941
msgstr ""
3942
3942
···
4680
4680
msgid "Mention notifications"
4681
4681
msgstr ""
4682
4682
4683
-
#: src/components/WhoCanReply.tsx:263
4683
+
#: src/components/WhoCanReply.tsx:269
4684
4684
msgid "mentioned users"
4685
4685
msgstr ""
4686
4686
···
4716
4716
msgid "Message from @{0}: {1}"
4717
4717
msgstr ""
4718
4718
4719
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:201
4719
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:205
4720
4720
msgid "Message from server: {0}"
4721
4721
msgstr ""
4722
4722
···
4737
4737
msgid "Messages"
4738
4738
msgstr ""
4739
4739
4740
-
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:81
4740
+
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:101
4741
4741
msgctxt "Name of app icon variant"
4742
4742
msgid "Midnight"
4743
4743
msgstr ""
···
5193
5193
msgid "No one"
5194
5194
msgstr ""
5195
5195
5196
-
#: src/components/WhoCanReply.tsx:246
5196
+
#: src/components/WhoCanReply.tsx:252
5197
5197
msgid "No one but the author can quote this post."
5198
5198
msgstr ""
5199
5199
···
5419
5419
msgid "Only .jpg and .png files are supported"
5420
5420
msgstr ""
5421
5421
5422
-
#: src/components/WhoCanReply.tsx:226
5422
+
#: src/components/WhoCanReply.tsx:232
5423
5423
msgid "Only {0} can reply."
5424
5424
msgstr ""
5425
5425
···
6057
6057
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."
6058
6058
msgstr ""
6059
6059
6060
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:68
6060
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:72
6061
6061
msgid "Posts hidden"
6062
6062
msgstr ""
6063
6063
···
6361
6361
#: src/screens/Settings/Settings.tsx:556
6362
6362
#: src/view/com/feeds/FeedSourceCard.tsx:322
6363
6363
#: src/view/com/modals/UserAddRemoveLists.tsx:235
6364
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:213
6364
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:217
6365
6365
msgid "Remove"
6366
6366
msgstr ""
6367
6367
···
6398
6398
6399
6399
#: src/view/com/posts/FeedShutdownMsg.tsx:116
6400
6400
#: src/view/com/posts/FeedShutdownMsg.tsx:120
6401
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:169
6401
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:173
6402
6402
msgid "Remove feed"
6403
6403
msgstr ""
6404
6404
6405
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:210
6405
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:214
6406
6406
msgid "Remove feed?"
6407
6407
msgstr ""
6408
6408
···
6455
6455
msgid "Remove subtitle file"
6456
6456
msgstr ""
6457
6457
6458
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:211
6458
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:215
6459
6459
msgid "Remove this feed from your saved feeds"
6460
6460
msgstr ""
6461
6461
···
6517
6517
msgid "Replies"
6518
6518
msgstr ""
6519
6519
6520
-
#: src/components/WhoCanReply.tsx:73
6520
+
#: src/components/WhoCanReply.tsx:79
6521
6521
msgid "Replies disabled"
6522
6522
msgstr ""
6523
6523
6524
-
#: src/components/WhoCanReply.tsx:224
6524
+
#: src/components/WhoCanReply.tsx:230
6525
6525
msgid "Replies to this post are disabled."
6526
6526
msgstr ""
6527
6527
···
6593
6593
msgid "Reply was successfully hidden"
6594
6594
msgstr ""
6595
6595
6596
-
#: src/components/dms/MessageContextMenu.tsx:172
6596
+
#: src/components/dms/MessageContextMenu.tsx:171
6597
6597
#: src/components/dms/MessagesListBlockedFooter.tsx:85
6598
6598
#: src/components/dms/MessagesListBlockedFooter.tsx:92
6599
6599
msgid "Report"
···
6627
6627
msgid "Report list"
6628
6628
msgstr ""
6629
6629
6630
-
#: src/components/dms/MessageContextMenu.tsx:170
6630
+
#: src/components/dms/MessageContextMenu.tsx:169
6631
6631
msgid "Report message"
6632
6632
msgstr ""
6633
6633
···
7646
7646
msgid "Some other feeds you might like"
7647
7647
msgstr ""
7648
7648
7649
-
#: src/components/WhoCanReply.tsx:74
7649
+
#: src/components/WhoCanReply.tsx:80
7650
7650
msgid "Some people can reply"
7651
7651
msgstr ""
7652
7652
···
7871
7871
msgid "Suggestive"
7872
7872
msgstr ""
7873
7873
7874
-
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:60
7874
+
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:72
7875
7875
msgctxt "Name of app icon variant"
7876
7876
msgid "Sunrise"
7877
7877
msgstr ""
7878
7878
7879
-
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:70
7879
+
#: src/screens/Settings/AppIconSettings/useAppIconSets.ts:86
7880
7880
msgctxt "Name of app icon variant"
7881
7881
msgid "Sunset"
7882
7882
msgstr ""
···
8165
8165
msgstr ""
8166
8166
8167
8167
#: src/screens/Search/Explore.tsx:990
8168
-
#: src/view/com/posts/PostFeed.tsx:657
8168
+
#: src/view/com/posts/PostFeed.tsx:656
8169
8169
msgid "There was an issue fetching posts. Tap here to try again."
8170
8170
msgstr ""
8171
8171
···
8186
8186
msgid "There was an issue fetching your service info"
8187
8187
msgstr ""
8188
8188
8189
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:145
8189
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:149
8190
8190
msgid "There was an issue removing this feed. Please check your internet connection and try again."
8191
8191
msgstr ""
8192
8192
···
8295
8295
msgid "This content is not available because one of the users involved has blocked the other."
8296
8296
msgstr ""
8297
8297
8298
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:114
8298
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:118
8299
8299
msgid "This content is not viewable without a Bluesky account."
8300
8300
msgstr ""
8301
8301
···
8319
8319
msgid "This feature is not available while using an App Password. Please sign in with your main password."
8320
8320
msgstr ""
8321
8321
8322
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:120
8322
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:124
8323
8323
msgid "This feed is currently receiving high traffic and is temporarily unavailable. Please try again later."
8324
8324
msgstr ""
8325
8325
···
8387
8387
msgid "This post claims to have been created on <0>{0}</0>, but was first seen by Bluesky on <1>{1}</1>."
8388
8388
msgstr ""
8389
8389
8390
-
#: src/components/WhoCanReply.tsx:217
8390
+
#: src/components/WhoCanReply.tsx:223
8391
8391
msgid "This post has an unknown type of threadgate on it. Your app may be out of date."
8392
8392
msgstr ""
8393
8393
···
8558
8558
msgid "Topic"
8559
8559
msgstr ""
8560
8560
8561
-
#: src/components/dms/MessageContextMenu.tsx:143
8562
-
#: src/components/dms/MessageContextMenu.tsx:145
8561
+
#: src/components/dms/MessageContextMenu.tsx:142
8562
+
#: src/components/dms/MessageContextMenu.tsx:144
8563
8563
#: src/components/PostControls/PostMenu/PostMenuItems.tsx:444
8564
8564
#: src/components/PostControls/PostMenu/PostMenuItems.tsx:446
8565
8565
#: src/screens/PostThread/components/ThreadItemAnchor.tsx:567
···
8984
8984
msgid "Username or email address"
8985
8985
msgstr ""
8986
8986
8987
-
#: src/components/WhoCanReply.tsx:280
8987
+
#: src/components/WhoCanReply.tsx:286
8988
8988
msgid "users followed by <0>@{0}</0>"
8989
8989
msgstr ""
8990
8990
8991
-
#: src/components/WhoCanReply.tsx:267
8991
+
#: src/components/WhoCanReply.tsx:273
8992
8992
msgid "users following <0>@{0}</0>"
8993
8993
msgstr ""
8994
8994
···
9188
9188
#: src/components/ProfileHoverCard/index.web.tsx:464
9189
9189
#: src/components/ProfileHoverCard/index.web.tsx:484
9190
9190
#: src/components/ProfileHoverCard/index.web.tsx:511
9191
-
#: src/view/com/posts/PostFeedErrorMessage.tsx:175
9191
+
#: src/view/com/posts/PostFeedErrorMessage.tsx:179
9192
9192
#: src/view/com/util/PostMeta.tsx:91
9193
9193
#: src/view/com/util/PostMeta.tsx:126
9194
9194
msgid "View profile"
···
9416
9416
msgid "Which languages would you like to see in your algorithmic feeds?"
9417
9417
msgstr ""
9418
9418
9419
-
#: src/components/WhoCanReply.tsx:183
9419
+
#: src/components/WhoCanReply.tsx:189
9420
9420
msgid "Who can interact with this post?"
9421
9421
msgstr ""
9422
9422
9423
-
#: src/components/WhoCanReply.tsx:91
9423
+
#: src/components/WhoCanReply.tsx:97
9424
9424
msgid "Who can reply"
9425
9425
msgstr ""
9426
9426
+6
-6
src/platform/polyfills.ts
+6
-6
src/platform/polyfills.ts
···
41
41
r1 === 64
42
42
? String.fromCharCode((bitmap >> 16) & 255)
43
43
: r2 === 64
44
-
? String.fromCharCode((bitmap >> 16) & 255, (bitmap >> 8) & 255)
45
-
: String.fromCharCode(
46
-
(bitmap >> 16) & 255,
47
-
(bitmap >> 8) & 255,
48
-
bitmap & 255,
49
-
)
44
+
? String.fromCharCode((bitmap >> 16) & 255, (bitmap >> 8) & 255)
45
+
: String.fromCharCode(
46
+
(bitmap >> 16) & 255,
47
+
(bitmap >> 8) & 255,
48
+
bitmap & 255,
49
+
)
50
50
}
51
51
return result
52
52
}
+2
-2
src/screens/Login/index.tsx
+2
-2
src/screens/Login/index.tsx
+1
-1
src/screens/Messages/ChatList.tsx
+1
-1
src/screens/Messages/ChatList.tsx
+2
-2
src/screens/Onboarding/StepFinished.tsx
+2
-2
src/screens/Onboarding/StepFinished.tsx
+2
-2
src/screens/Profile/Header/ProfileHeaderLabeler.tsx
+2
-2
src/screens/Profile/Header/ProfileHeaderLabeler.tsx
+2
-2
src/screens/Search/Explore.tsx
+2
-2
src/screens/Search/Explore.tsx
···
342
342
])
343
343
344
344
const topBorder = useMemo(
345
-
() => ({type: 'topBorder', key: 'top-border'} as const),
345
+
() => ({type: 'topBorder', key: 'top-border'}) as const,
346
346
[],
347
347
)
348
348
const trendingTopicsModule = useMemo(
349
-
() => ({type: 'trendingTopics', key: 'trending-topics'} as const),
349
+
() => ({type: 'trendingTopics', key: 'trending-topics'}) as const,
350
350
[],
351
351
)
352
352
const suggestedFollowsModule = useMemo(() => {
+12
-4
src/screens/Settings/AppIconSettings/useAppIconSets.ts
+12
-4
src/screens/Settings/AppIconSettings/useAppIconSets.ts
···
13
13
id: 'default_light',
14
14
name: _(msg({context: 'Name of app icon variant', message: 'Light'})),
15
15
iosImage: () => {
16
-
return require(`../../../../assets/app-icons/ios_icon_default_light.png`)
16
+
return require(
17
+
`../../../../assets/app-icons/ios_icon_default_light.png`,
18
+
)
17
19
},
18
20
androidImage: () => {
19
-
return require(`../../../../assets/app-icons/android_icon_default_light.png`)
21
+
return require(
22
+
`../../../../assets/app-icons/android_icon_default_light.png`,
23
+
)
20
24
},
21
25
},
22
26
{
23
27
id: 'default_dark',
24
28
name: _(msg({context: 'Name of app icon variant', message: 'Dark'})),
25
29
iosImage: () => {
26
-
return require(`../../../../assets/app-icons/ios_icon_default_dark.png`)
30
+
return require(
31
+
`../../../../assets/app-icons/ios_icon_default_dark.png`,
32
+
)
27
33
},
28
34
androidImage: () => {
29
-
return require(`../../../../assets/app-icons/android_icon_default_dark.png`)
35
+
return require(
36
+
`../../../../assets/app-icons/android_icon_default_dark.png`,
37
+
)
30
38
},
31
39
},
32
40
] satisfies AppIconSet[]
+2
-2
src/screens/Settings/components/ChangeHandleDialog.tsx
+2
-2
src/screens/Settings/components/ChangeHandleDialog.tsx
+2
-2
src/state/cache/profile-shadow.ts
+2
-2
src/state/cache/profile-shadow.ts
+83
src/state/global-gesture-events/index.tsx
+83
src/state/global-gesture-events/index.tsx
···
1
+
import {createContext, useContext, useMemo, useRef, useState} from 'react'
2
+
import {View} from 'react-native'
3
+
import {
4
+
Gesture,
5
+
GestureDetector,
6
+
type GestureStateChangeEvent,
7
+
type GestureUpdateEvent,
8
+
type PanGestureHandlerEventPayload,
9
+
} from 'react-native-gesture-handler'
10
+
import EventEmitter from 'eventemitter3'
11
+
12
+
export type GlobalGestureEvents = {
13
+
begin: GestureStateChangeEvent<PanGestureHandlerEventPayload>
14
+
update: GestureUpdateEvent<PanGestureHandlerEventPayload>
15
+
end: GestureStateChangeEvent<PanGestureHandlerEventPayload>
16
+
finalize: GestureStateChangeEvent<PanGestureHandlerEventPayload>
17
+
}
18
+
19
+
const Context = createContext<{
20
+
events: EventEmitter<GlobalGestureEvents>
21
+
register: () => void
22
+
unregister: () => void
23
+
}>({
24
+
events: new EventEmitter<GlobalGestureEvents>(),
25
+
register: () => {},
26
+
unregister: () => {},
27
+
})
28
+
29
+
export function GlobalGestureEventsProvider({
30
+
children,
31
+
}: {
32
+
children: React.ReactNode
33
+
}) {
34
+
const refCount = useRef(0)
35
+
const events = useMemo(() => new EventEmitter<GlobalGestureEvents>(), [])
36
+
const [enabled, setEnabled] = useState(false)
37
+
const ctx = useMemo(
38
+
() => ({
39
+
events,
40
+
register() {
41
+
refCount.current += 1
42
+
if (refCount.current === 1) {
43
+
setEnabled(true)
44
+
}
45
+
},
46
+
unregister() {
47
+
refCount.current -= 1
48
+
if (refCount.current === 0) {
49
+
setEnabled(false)
50
+
}
51
+
},
52
+
}),
53
+
[events, setEnabled],
54
+
)
55
+
const gesture = Gesture.Pan()
56
+
.runOnJS(true)
57
+
.enabled(enabled)
58
+
.simultaneousWithExternalGesture()
59
+
.onBegin(e => {
60
+
events.emit('begin', e)
61
+
})
62
+
.onUpdate(e => {
63
+
events.emit('update', e)
64
+
})
65
+
.onEnd(e => {
66
+
events.emit('end', e)
67
+
})
68
+
.onFinalize(e => {
69
+
events.emit('finalize', e)
70
+
})
71
+
72
+
return (
73
+
<Context.Provider value={ctx}>
74
+
<GestureDetector gesture={gesture}>
75
+
<View collapsable={false}>{children}</View>
76
+
</GestureDetector>
77
+
</Context.Provider>
78
+
)
79
+
}
80
+
81
+
export function useGlobalGestureEvents() {
82
+
return useContext(Context)
83
+
}
+9
src/state/global-gesture-events/index.web.tsx
+9
src/state/global-gesture-events/index.web.tsx
···
1
+
export function GlobalGestureEventsProvider(_props: {
2
+
children: React.ReactNode
3
+
}) {
4
+
throw new Error('GlobalGestureEventsProvider is not supported on web.')
5
+
}
6
+
7
+
export function useGlobalGestureEvents() {
8
+
throw new Error('useGlobalGestureEvents is not supported on web.')
9
+
}
+11
-22
src/state/queries/explore-feed-previews.tsx
+11
-22
src/state/queries/explore-feed-previews.tsx
···
36
36
const LIMIT = 8 // sliced to 6, overfetch to account for moderation
37
37
const PINNED_POST_URIS: Record<string, boolean> = {
38
38
// 📰 News
39
-
'at://did:plc:kkf4naxqmweop7dv4l2iqqf5/app.bsky.feed.post/3lgh27w2ngc2b':
40
-
true,
39
+
'at://did:plc:kkf4naxqmweop7dv4l2iqqf5/app.bsky.feed.post/3lgh27w2ngc2b': true,
41
40
// Gardening
42
-
'at://did:plc:5rw2on4i56btlcajojaxwcat/app.bsky.feed.post/3kjorckgcwc27':
43
-
true,
41
+
'at://did:plc:5rw2on4i56btlcajojaxwcat/app.bsky.feed.post/3kjorckgcwc27': true,
44
42
// Web Development Trending
45
-
'at://did:plc:m2sjv3wncvsasdapla35hzwj/app.bsky.feed.post/3lfaw445axs22':
46
-
true,
43
+
'at://did:plc:m2sjv3wncvsasdapla35hzwj/app.bsky.feed.post/3lfaw445axs22': true,
47
44
// Anime & Manga EN
48
-
'at://did:plc:tazrmeme4dzahimsykusrwrk/app.bsky.feed.post/3knxx2gmkns2y':
49
-
true,
45
+
'at://did:plc:tazrmeme4dzahimsykusrwrk/app.bsky.feed.post/3knxx2gmkns2y': true,
50
46
// 📽️ Film
51
-
'at://did:plc:2hwwem55ce6djnk6bn62cstr/app.bsky.feed.post/3llhpzhbq7c2g':
52
-
true,
47
+
'at://did:plc:2hwwem55ce6djnk6bn62cstr/app.bsky.feed.post/3llhpzhbq7c2g': true,
53
48
// PopSky
54
-
'at://did:plc:lfdf4srj43iwdng7jn35tjsp/app.bsky.feed.post/3lbblgly65c2g':
55
-
true,
49
+
'at://did:plc:lfdf4srj43iwdng7jn35tjsp/app.bsky.feed.post/3lbblgly65c2g': true,
56
50
// Science
57
-
'at://did:plc:hu2obebw3nhfj667522dahfg/app.bsky.feed.post/3kl33otd6ob2s':
58
-
true,
51
+
'at://did:plc:hu2obebw3nhfj667522dahfg/app.bsky.feed.post/3kl33otd6ob2s': true,
59
52
// Birds! 🦉
60
-
'at://did:plc:ffkgesg3jsv2j7aagkzrtcvt/app.bsky.feed.post/3lbg4r57yk22d':
61
-
true,
53
+
'at://did:plc:ffkgesg3jsv2j7aagkzrtcvt/app.bsky.feed.post/3lbg4r57yk22d': true,
62
54
// Astronomy
63
-
'at://did:plc:xy2zorw2ys47poflotxthlzg/app.bsky.feed.post/3kyzye4lujs2w':
64
-
true,
55
+
'at://did:plc:xy2zorw2ys47poflotxthlzg/app.bsky.feed.post/3kyzye4lujs2w': true,
65
56
// What's Cooking 🍽️
66
-
'at://did:plc:geoqe3qls5mwezckxxsewys2/app.bsky.feed.post/3lfqhgvxbqc2q':
67
-
true,
57
+
'at://did:plc:geoqe3qls5mwezckxxsewys2/app.bsky.feed.post/3lfqhgvxbqc2q': true,
68
58
// BookSky 💙📚 #booksky
69
-
'at://did:plc:geoqe3qls5mwezckxxsewys2/app.bsky.feed.post/3kgrm2rw5ww2e':
70
-
true,
59
+
'at://did:plc:geoqe3qls5mwezckxxsewys2/app.bsky.feed.post/3kgrm2rw5ww2e': true,
71
60
}
72
61
73
62
export type FeedPreviewItem =
+2
-3
src/state/queries/notifications/settings.ts
+2
-3
src/state/queries/notifications/settings.ts
···
36
36
mutationFn: async (
37
37
update: Partial<AppBskyNotificationDefs.Preferences>,
38
38
) => {
39
-
const response = await agent.app.bsky.notification.putPreferencesV2(
40
-
update,
41
-
)
39
+
const response =
40
+
await agent.app.bsky.notification.putPreferencesV2(update)
42
41
return response.data.preferences
43
42
},
44
43
onMutate: update => {
+5
-5
src/state/queries/notifications/unread.tsx
+5
-5
src/state/queries/notifications/unread.tsx
···
15
15
import {useModerationOpts} from '../../preferences/moderation-opts'
16
16
import {truncateAndInvalidate} from '../util'
17
17
import {RQKEY as RQKEY_NOTIFS} from './feed'
18
-
import {CachedFeedPage, FeedPage} from './types'
18
+
import {type CachedFeedPage, type FeedPage} from './types'
19
19
import {fetchPage} from './util'
20
20
21
21
const UPDATE_INTERVAL = 30 * 1e3 // 30sec
···
96
96
data.event === '30+'
97
97
? 30
98
98
: data.event === ''
99
-
? 0
100
-
: parseInt(data.event, 10) || 1,
99
+
? 0
100
+
: parseInt(data.event, 10) || 1,
101
101
}
102
102
setNumUnread(data.event)
103
103
}
···
167
167
unreadCount >= 30
168
168
? '30+'
169
169
: unreadCount === 0
170
-
? ''
171
-
: String(unreadCount)
170
+
? ''
171
+
: String(unreadCount)
172
172
173
173
// track last sync
174
174
const now = new Date()
+2
-2
src/state/queries/usePostThread/index.ts
+2
-2
src/state/queries/usePostThread/index.ts
+1
-1
src/state/queries/usePostThread/traversal.ts
+1
-1
src/state/queries/usePostThread/traversal.ts
···
444
444
const anchorPost = items.at(0)
445
445
const hasAnchorFromCache = anchorPost && anchorPost.type === 'threadPost'
446
446
const skeletonReplies = hasAnchorFromCache
447
-
? anchorPost.value.post.replyCount ?? 4
447
+
? (anchorPost.value.post.replyCount ?? 4)
448
448
: 4
449
449
450
450
if (!items.length) {
+5
-13
src/storage/index.ts
+5
-13
src/storage/index.ts
···
1
1
import {useCallback, useEffect, useState} from 'react'
2
2
import {MMKV} from 'react-native-mmkv'
3
3
4
-
import {Account, Device} from '#/storage/schema'
4
+
import {type Account, type Device} from '#/storage/schema'
5
5
6
6
export * from '#/storage/schema'
7
7
···
83
83
}
84
84
}
85
85
86
-
type StorageSchema<T extends Storage<any, any>> = T extends Storage<
87
-
any,
88
-
infer U
89
-
>
90
-
? U
91
-
: never
92
-
type StorageScopes<T extends Storage<any, any>> = T extends Storage<
93
-
infer S,
94
-
any
95
-
>
96
-
? S
97
-
: never
86
+
type StorageSchema<T extends Storage<any, any>> =
87
+
T extends Storage<any, infer U> ? U : never
88
+
type StorageScopes<T extends Storage<any, any>> =
89
+
T extends Storage<infer S, any> ? S : never
98
90
99
91
/**
100
92
* Hook to use a storage instance. Acts like a useState hook, but persists the
+37
-1
src/style.css
+37
-1
src/style.css
···
328
328
329
329
/* #/components/Select/index.web.tsx */
330
330
.radix-select-content {
331
-
box-shadow: 0px 6px 24px -10px rgba(22, 23, 24, 0.25),
331
+
box-shadow:
332
+
0px 6px 24px -10px rgba(22, 23, 24, 0.25),
332
333
0px 6px 12px -12px rgba(22, 23, 24, 0.15);
333
334
min-width: var(--radix-select-trigger-width);
334
335
max-height: var(--radix-select-content-available-height);
335
336
}
337
+
338
+
/*
339
+
* #/components/Tooltip/index.web.tsx
340
+
*/
341
+
.radix-popover-content {
342
+
animation-duration: 300ms;
343
+
animation-timing-function: cubic-bezier(0.17, 0.73, 0.14, 1);
344
+
will-change: transform, opacity;
345
+
}
346
+
.radix-popover-content[data-state='open'][data-side='top'] {
347
+
animation-name: radixPopoverSlideUpAndFade;
348
+
}
349
+
.radix-popover-content[data-state='open'][data-side='bottom'] {
350
+
animation-name: radixPopoverSlideDownAndFade;
351
+
}
352
+
@keyframes radixPopoverSlideUpAndFade {
353
+
from {
354
+
opacity: 0;
355
+
transform: translateY(2px);
356
+
}
357
+
to {
358
+
opacity: 1;
359
+
transform: translateY(0);
360
+
}
361
+
}
362
+
@keyframes radixPopoverSlideDownAndFade {
363
+
from {
364
+
opacity: 0;
365
+
transform: translateY(-2px);
366
+
}
367
+
to {
368
+
opacity: 1;
369
+
transform: translateY(0);
370
+
}
371
+
}
+16
-16
src/view/com/composer/Composer.tsx
+16
-16
src/view/com/composer/Composer.tsx
···
518
518
thread.posts.length > 1
519
519
? _(msg`Your posts have been published`)
520
520
: replyTo
521
-
? _(msg`Your reply has been published`)
522
-
: _(msg`Your post has been published`),
521
+
? _(msg`Your reply has been published`)
522
+
: _(msg`Your post has been published`),
523
523
)
524
524
}, [
525
525
_,
···
1000
1000
}),
1001
1001
)
1002
1002
: isThread
1003
-
? _(
1004
-
msg({
1005
-
message: 'Publish posts',
1006
-
comment:
1007
-
'Accessibility label for button to publish multiple posts in a thread',
1008
-
}),
1009
-
)
1010
-
: _(
1011
-
msg({
1012
-
message: 'Publish post',
1013
-
comment:
1014
-
'Accessibility label for button to publish a single post',
1015
-
}),
1016
-
)
1003
+
? _(
1004
+
msg({
1005
+
message: 'Publish posts',
1006
+
comment:
1007
+
'Accessibility label for button to publish multiple posts in a thread',
1008
+
}),
1009
+
)
1010
+
: _(
1011
+
msg({
1012
+
message: 'Publish post',
1013
+
comment:
1014
+
'Accessibility label for button to publish a single post',
1015
+
}),
1016
+
)
1017
1017
}
1018
1018
variant="solid"
1019
1019
color="primary"
+10
-10
src/view/com/composer/photos/Gallery.tsx
+10
-10
src/view/com/composer/photos/Gallery.tsx
···
1
1
import React from 'react'
2
2
import {
3
-
ImageStyle,
3
+
type ImageStyle,
4
4
Keyboard,
5
-
LayoutChangeEvent,
5
+
type LayoutChangeEvent,
6
6
StyleSheet,
7
7
TouchableOpacity,
8
8
View,
9
-
ViewStyle,
9
+
type ViewStyle,
10
10
} from 'react-native'
11
11
import {Image} from 'expo-image'
12
12
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
···
14
14
import {useLingui} from '@lingui/react'
15
15
16
16
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
17
-
import {Dimensions} from '#/lib/media/types'
17
+
import {type Dimensions} from '#/lib/media/types'
18
18
import {colors, s} from '#/lib/styles'
19
19
import {isNative} from '#/platform/detection'
20
-
import {ComposerImage, cropImage} from '#/state/gallery'
20
+
import {type ComposerImage, cropImage} from '#/state/gallery'
21
21
import {Text} from '#/view/com/util/text/Text'
22
22
import {useTheme} from '#/alf'
23
23
import * as Dialog from '#/components/Dialog'
24
-
import {PostAction} from '../state/composer'
24
+
import {type PostAction} from '../state/composer'
25
25
import {EditImageDialog} from './EditImageDialog'
26
26
import {ImageAltTextDialog} from './ImageAltTextDialog'
27
27
···
74
74
altTextControlStyle: isOverflow
75
75
? {left: 4, bottom: 4}
76
76
: !isMobile && images.length < 3
77
-
? {left: 8, top: 8}
78
-
: {left: 4, top: 4},
77
+
? {left: 8, top: 8}
78
+
: {left: 4, top: 4},
79
79
imageControlsStyle: {
80
80
display: 'flex' as const,
81
81
flexDirection: 'row' as const,
···
83
83
...(isOverflow
84
84
? {top: 4, right: 4, gap: 4}
85
85
: !isMobile && images.length < 3
86
-
? {top: 8, right: 8, gap: 8}
87
-
: {top: 4, right: 4, gap: 4}),
86
+
? {top: 8, right: 8, gap: 8}
87
+
: {top: 4, right: 4, gap: 4}),
88
88
zIndex: 1,
89
89
},
90
90
imageStyle: {
+21
-16
src/view/com/composer/state/composer.ts
+21
-16
src/view/com/composer/state/composer.ts
···
1
-
import {ImagePickerAsset} from 'expo-image-picker'
1
+
import {type ImagePickerAsset} from 'expo-image-picker'
2
2
import {
3
-
AppBskyFeedPostgate,
3
+
type AppBskyFeedPostgate,
4
4
AppBskyRichtextFacet,
5
-
BskyPreferences,
5
+
type BskyPreferences,
6
6
RichText,
7
7
} from '@atproto/api'
8
8
import {nanoid} from 'nanoid/non-secure'
9
9
10
-
import {SelfLabel} from '#/lib/moderation'
10
+
import {type SelfLabel} from '#/lib/moderation'
11
11
import {insertMentionAt} from '#/lib/strings/mention-manip'
12
12
import {shortenLinks} from '#/lib/strings/rich-text-manip'
13
13
import {
···
15
15
postUriToRelativePath,
16
16
toBskyAppUrl,
17
17
} from '#/lib/strings/url-helpers'
18
-
import {ComposerImage, createInitialImages} from '#/state/gallery'
18
+
import {type ComposerImage, createInitialImages} from '#/state/gallery'
19
19
import {createPostgateRecord} from '#/state/queries/postgate/util'
20
-
import {Gif} from '#/state/queries/tenor'
20
+
import {type Gif} from '#/state/queries/tenor'
21
21
import {threadgateRecordToAllowUISetting} from '#/state/queries/threadgate'
22
-
import {ThreadgateAllowUISetting} from '#/state/queries/threadgate'
23
-
import {ComposerOpts} from '#/state/shell/composer'
22
+
import {type ThreadgateAllowUISetting} from '#/state/queries/threadgate'
23
+
import {type ComposerOpts} from '#/state/shell/composer'
24
24
import {
25
-
LinkFacetMatch,
25
+
type LinkFacetMatch,
26
26
suggestLinkCardUri,
27
27
} from '#/view/com/composer/text-input/text-input-util'
28
-
import {createVideoState, VideoAction, videoReducer, VideoState} from './video'
28
+
import {
29
+
createVideoState,
30
+
type VideoAction,
31
+
videoReducer,
32
+
type VideoState,
33
+
} from './video'
29
34
30
35
type ImagesMedia = {
31
36
type: 'images'
···
514
519
text: initText
515
520
? initText
516
521
: initMention
517
-
? insertMentionAt(
518
-
`@${initMention}`,
519
-
initMention.length + 1,
520
-
`${initMention}`,
521
-
)
522
-
: '',
522
+
? insertMentionAt(
523
+
`@${initMention}`,
524
+
initMention.length + 1,
525
+
`${initMention}`,
526
+
)
527
+
: '',
523
528
})
524
529
525
530
let link: Link | undefined
+2
-2
src/view/com/composer/text-input/web/Autocomplete.tsx
+2
-2
src/view/com/composer/text-input/web/Autocomplete.tsx
+6
-6
src/view/com/lightbox/Lightbox.web.tsx
+6
-6
src/view/com/lightbox/Lightbox.web.tsx
···
1
1
import React, {useCallback, useEffect, useState} from 'react'
2
2
import {
3
3
Image,
4
-
ImageStyle,
4
+
type ImageStyle,
5
5
Pressable,
6
6
StyleSheet,
7
7
TouchableOpacity,
8
8
TouchableWithoutFeedback,
9
9
View,
10
-
ViewStyle,
10
+
type ViewStyle,
11
11
} from 'react-native'
12
12
import {
13
13
FontAwesomeIcon,
14
-
FontAwesomeIconStyle,
14
+
type FontAwesomeIconStyle,
15
15
} from '@fortawesome/react-native-fontawesome'
16
16
import {msg} from '@lingui/macro'
17
17
import {useLingui} from '@lingui/react'
···
21
21
import {colors, s} from '#/lib/styles'
22
22
import {useLightbox, useLightboxControls} from '#/state/lightbox'
23
23
import {Text} from '../util/text/Text'
24
-
import {ImageSource} from './ImageViewing/@types'
24
+
import {type ImageSource} from './ImageViewing/@types'
25
25
import ImageDefaultHeader from './ImageViewing/components/ImageDefaultHeader'
26
26
27
27
export function Lightbox() {
···
121
121
img.type === 'circle-avi'
122
122
? '50%'
123
123
: img.type === 'rect-avi'
124
-
? '10%'
125
-
: 0,
124
+
? '10%'
125
+
: 0,
126
126
} as ImageStyle
127
127
}
128
128
alt={img.alt}
+2
-2
src/view/com/post-thread/PostThreadItem.tsx
+2
-2
src/view/com/post-thread/PostThreadItem.tsx
+8
-4
src/view/com/posts/PostFeedErrorMessage.tsx
+8
-4
src/view/com/posts/PostFeedErrorMessage.tsx
···
1
1
import React from 'react'
2
2
import {View} from 'react-native'
3
-
import {AppBskyActorDefs, AppBskyFeedGetAuthorFeed, AtUri} from '@atproto/api'
3
+
import {
4
+
type AppBskyActorDefs,
5
+
AppBskyFeedGetAuthorFeed,
6
+
AtUri,
7
+
} from '@atproto/api'
4
8
import {msg as msgLingui, Trans} from '@lingui/macro'
5
9
import {useLingui} from '@lingui/react'
6
10
import {useNavigation} from '@react-navigation/native'
7
11
8
12
import {usePalette} from '#/lib/hooks/usePalette'
9
-
import {NavigationProp} from '#/lib/routes/types'
13
+
import {type NavigationProp} from '#/lib/routes/types'
10
14
import {cleanError} from '#/lib/strings/errors'
11
15
import {logger} from '#/logger'
12
-
import {FeedDescriptor} from '#/state/queries/post-feed'
16
+
import {type FeedDescriptor} from '#/state/queries/post-feed'
13
17
import {useRemoveFeedMutation} from '#/state/queries/preferences'
14
18
import * as Prompt from '#/components/Prompt'
15
19
import {EmptyState} from '../util/EmptyState'
···
119
123
[KnownError.FeedTooManyRequests]: _l(
120
124
msgLingui`This feed is currently receiving high traffic and is temporarily unavailable. Please try again later.`,
121
125
),
122
-
}[knownError]),
126
+
})[knownError],
123
127
[_l, knownError],
124
128
)
125
129
const [_, uri] = feedDesc.split('|')
+6
-6
src/view/com/profile/ProfileMenu.tsx
+6
-6
src/view/com/profile/ProfileMenu.tsx
···
495
495
msg`The account will be able to interact with you after unblocking.`,
496
496
)
497
497
: profile.associated?.labeler
498
-
? _(
499
-
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.`,
500
-
)
501
-
: _(
502
-
msg`Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.`,
503
-
)
498
+
? _(
499
+
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.`,
500
+
)
501
+
: _(
502
+
msg`Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.`,
503
+
)
504
504
}
505
505
onConfirm={blockAccount}
506
506
confirmButtonCta={
+8
-3
src/view/com/util/List.web.tsx
+8
-3
src/view/com/util/List.web.tsx
···
1
1
import React, {isValidElement, memo, startTransition, useRef} from 'react'
2
-
import {FlatListProps, StyleSheet, View, ViewProps} from 'react-native'
3
-
import {ReanimatedScrollEvent} from 'react-native-reanimated/lib/typescript/hook/commonTypes'
2
+
import {
3
+
type FlatListProps,
4
+
StyleSheet,
5
+
View,
6
+
type ViewProps,
7
+
} from 'react-native'
8
+
import {type ReanimatedScrollEvent} from 'react-native-reanimated/lib/typescript/hook/commonTypes'
4
9
5
10
import {batchedUpdates} from '#/lib/batchedUpdates'
6
11
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
···
205
210
behavior: animated ? 'smooth' : 'instant',
206
211
})
207
212
},
208
-
} as any), // TODO: Better types.
213
+
}) as any, // TODO: Better types.
209
214
[getScrollableNode],
210
215
)
211
216
+8
-8
src/view/screens/DebugMod.tsx
+8
-8
src/view/screens/DebugMod.tsx
···
113
113
}),
114
114
]
115
115
: scenario[0] === 'label' && target[0] === 'profile'
116
-
? [
117
-
mock.label({
118
-
src: isSelfLabel ? did : undefined,
119
-
val: label[0],
120
-
uri: `at://${did}/app.bsky.actor.profile/self`,
121
-
}),
122
-
]
123
-
: undefined,
116
+
? [
117
+
mock.label({
118
+
src: isSelfLabel ? did : undefined,
119
+
val: label[0],
120
+
uri: `at://${did}/app.bsky.actor.profile/self`,
121
+
}),
122
+
]
123
+
: undefined,
124
124
viewer: mock.actorViewerState({
125
125
following: isFollowing
126
126
? `at://${currentAccount?.did || ''}/app.bsky.graph.follow/1234`
+61
-59
svgo.config.mjs
+61
-59
svgo.config.mjs
···
1
1
const preset = [
2
-
"removeDoctype",
3
-
"removeXMLProcInst",
4
-
"removeComments",
5
-
"removeMetadata",
6
-
"removeEditorsNSData",
7
-
"cleanupAttrs",
8
-
"mergeStyles",
9
-
"inlineStyles",
10
-
"minifyStyles",
11
-
"cleanupIds",
12
-
"removeUselessDefs",
13
-
"cleanupNumericValues",
14
-
"convertColors",
15
-
"removeUnknownsAndDefaults",
16
-
"removeNonInheritableGroupAttrs",
17
-
"removeUselessStrokeAndFill",
18
-
"removeDimensions",
19
-
"cleanupEnableBackground",
20
-
"removeHiddenElems",
21
-
"removeEmptyText",
22
-
"convertShapeToPath",
23
-
"convertEllipseToCircle",
24
-
"moveElemsAttrsToGroup",
25
-
"moveGroupAttrsToElems",
26
-
"collapseGroups",
27
-
"convertPathData",
28
-
"convertTransform",
29
-
"removeEmptyAttrs",
30
-
"removeEmptyContainers",
31
-
"removeUnusedNS",
32
-
"mergePaths",
33
-
"sortAttrs",
34
-
"sortDefsChildren",
35
-
"removeTitle",
36
-
"removeDesc",
2
+
'removeDoctype',
3
+
'removeXMLProcInst',
4
+
'removeComments',
5
+
'removeMetadata',
6
+
'removeEditorsNSData',
7
+
'cleanupAttrs',
8
+
'mergeStyles',
9
+
'inlineStyles',
10
+
'minifyStyles',
11
+
'cleanupIds',
12
+
'removeUselessDefs',
13
+
'cleanupNumericValues',
14
+
'convertColors',
15
+
'removeUnknownsAndDefaults',
16
+
'removeNonInheritableGroupAttrs',
17
+
'removeUselessStrokeAndFill',
18
+
'removeDimensions',
19
+
'cleanupEnableBackground',
20
+
'removeHiddenElems',
21
+
'removeEmptyText',
22
+
'convertShapeToPath',
23
+
'convertEllipseToCircle',
24
+
'moveElemsAttrsToGroup',
25
+
'moveGroupAttrsToElems',
26
+
'collapseGroups',
27
+
'convertPathData',
28
+
'convertTransform',
29
+
'removeEmptyAttrs',
30
+
'removeEmptyContainers',
31
+
'removeUnusedNS',
32
+
'mergePaths',
33
+
'sortAttrs',
34
+
'sortDefsChildren',
35
+
'removeTitle',
36
+
'removeDesc',
37
37
]
38
38
39
39
export default {
40
-
plugins: [...preset.map(name => ({
41
-
name,
42
-
params: {
43
-
floatPrecision: 3,
44
-
transformPrecision: 5,
45
-
// minimise diff in ouput from svgomg
46
-
// maybe remove in future? will produce smaller output
47
-
convertToZ: false,
48
-
removeUseless: false,
49
-
}
50
-
})),
51
-
{
52
-
name: 'addTrailingWhitespace',
53
-
fn() {
54
-
return {
55
-
root: {
56
-
exit (root) {
57
-
root.children.push({ type: 'text', value: '\n' })
58
-
return root
59
-
}
40
+
plugins: [
41
+
...preset.map(name => ({
42
+
name,
43
+
params: {
44
+
floatPrecision: 3,
45
+
transformPrecision: 5,
46
+
// minimise diff in ouput from svgomg
47
+
// maybe remove in future? will produce smaller output
48
+
convertToZ: false,
49
+
removeUseless: false,
50
+
},
51
+
})),
52
+
{
53
+
name: 'addTrailingWhitespace',
54
+
fn() {
55
+
return {
56
+
root: {
57
+
exit(root) {
58
+
root.children.push({type: 'text', value: '\n'})
59
+
return root
60
+
},
61
+
},
60
62
}
61
-
}
62
-
}
63
-
}]
64
-
};
63
+
},
64
+
},
65
+
],
66
+
}
+4
-4
yarn.lock
+4
-4
yarn.lock
···
16220
16220
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
16221
16221
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
16222
16222
16223
-
prettier@^2.8.3:
16224
-
version "2.8.8"
16225
-
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
16226
-
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
16223
+
prettier@^3.6.0:
16224
+
version "3.6.0"
16225
+
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.6.0.tgz#18ec98d62cb0757a5d4eab40253ff3e6d0fc8dea"
16226
+
integrity sha512-ujSB9uXHJKzM/2GBuE0hBOUgC77CN3Bnpqa+g80bkv3T3A93wL/xlzDATHhnhkzifz/UE2SNOvmbTz5hSkDlHw==
16227
16227
16228
16228
pretty-bytes@^5.6.0:
16229
16229
version "5.6.0"