Bluesky app fork with some witchin' additions 馃挮
witchsky.app
bluesky
fork
client
1// @ts-check
2const pkg = require('./package.json')
3
4/**
5 * @param {import('@expo/config-types').ExpoConfig} _config
6 * @returns {{ expo: import('@expo/config-types').ExpoConfig }}
7 */
8module.exports = function (_config) {
9 /**
10 * App version number. Should be incremented as part of a release cycle.
11 */
12 const VERSION = pkg.version
13
14 /**
15 * Uses built-in Expo env vars
16 *
17 * @see https://docs.expo.dev/build-reference/variables/#built-in-environment-variables
18 */
19 const PLATFORM = process.env.EAS_BUILD_PLATFORM ?? 'web'
20
21 const IS_TESTFLIGHT = process.env.EXPO_PUBLIC_ENV === 'testflight'
22 const IS_PRODUCTION = process.env.EXPO_PUBLIC_ENV === 'production'
23 const IS_DEV = !IS_TESTFLIGHT && !IS_PRODUCTION
24
25 const ASSOCIATED_DOMAINS = [
26 'applinks:bsky.app',
27 'applinks:staging.bsky.app',
28 'appclips:bsky.app',
29 'appclips:go.bsky.app', // Allows App Clip to work when scanning QR codes
30 // When testing local services, enter an ngrok (et al) domain here. It must use a standard HTTP/HTTPS port.
31 ...(IS_DEV || IS_TESTFLIGHT ? [] : []),
32 ]
33
34 const UPDATES_ENABLED = IS_TESTFLIGHT || IS_PRODUCTION
35
36 const USE_SENTRY = Boolean(process.env.SENTRY_AUTH_TOKEN)
37
38 return {
39 expo: {
40 version: VERSION,
41 name: 'Bluesky',
42 slug: 'bluesky',
43 scheme: 'bluesky',
44 owner: 'blueskysocial',
45 runtimeVersion: {
46 policy: 'appVersion',
47 },
48 icon: './assets/app-icons/ios_icon_default_next.png',
49 userInterfaceStyle: 'automatic',
50 primaryColor: '#1083fe',
51 newArchEnabled: false,
52 ios: {
53 supportsTablet: false,
54 bundleIdentifier: 'xyz.blueskyweb.app',
55 config: {
56 usesNonExemptEncryption: false,
57 },
58 icon:
59 PLATFORM === 'web' // web build doesn't like .icon files
60 ? './assets/app-icons/ios_icon_default_next.png'
61 : './assets/app-icons/ios_icon_default.icon',
62 infoPlist: {
63 UIBackgroundModes: ['remote-notification'],
64 NSCameraUsageDescription:
65 'Used for profile pictures, posts, and other kinds of content.',
66 NSMicrophoneUsageDescription:
67 'Used for posts and other kinds of content.',
68 NSPhotoLibraryAddUsageDescription:
69 'Used to save images to your library.',
70 NSPhotoLibraryUsageDescription:
71 'Used for profile pictures, posts, and other kinds of content',
72 CFBundleSpokenName: 'Blue Sky',
73 CFBundleLocalizations: [
74 'en',
75 'an',
76 'ast',
77 'ca',
78 'cy',
79 'da',
80 'de',
81 'el',
82 'eo',
83 'es',
84 'eu',
85 'fi',
86 'fr',
87 'fy',
88 'ga',
89 'gd',
90 'gl',
91 'hi',
92 'hu',
93 'ia',
94 'id',
95 'it',
96 'ja',
97 'km',
98 'ko',
99 'ne',
100 'nl',
101 'pl',
102 'pt-BR',
103 'pt-PT',
104 'ro',
105 'ru',
106 'sv',
107 'th',
108 'tr',
109 'uk',
110 'vi',
111 'yue',
112 'zh-Hans',
113 'zh-Hant',
114 ],
115 },
116 associatedDomains: ASSOCIATED_DOMAINS,
117 entitlements: {
118 'com.apple.developer.kernel.increased-memory-limit': true,
119 'com.apple.developer.kernel.extended-virtual-addressing': true,
120 'com.apple.security.application-groups': 'group.app.bsky',
121 // 'com.apple.developer.device-information.user-assigned-device-name': true,
122 },
123 privacyManifests: {
124 NSPrivacyCollectedDataTypes: [
125 {
126 NSPrivacyCollectedDataType: 'NSPrivacyCollectedDataTypeCrashData',
127 NSPrivacyCollectedDataTypeLinked: false,
128 NSPrivacyCollectedDataTypeTracking: false,
129 NSPrivacyCollectedDataTypePurposes: [
130 'NSPrivacyCollectedDataTypePurposeAppFunctionality',
131 ],
132 },
133 {
134 NSPrivacyCollectedDataType:
135 'NSPrivacyCollectedDataTypePerformanceData',
136 NSPrivacyCollectedDataTypeLinked: false,
137 NSPrivacyCollectedDataTypeTracking: false,
138 NSPrivacyCollectedDataTypePurposes: [
139 'NSPrivacyCollectedDataTypePurposeAppFunctionality',
140 ],
141 },
142 {
143 NSPrivacyCollectedDataType:
144 'NSPrivacyCollectedDataTypeOtherDiagnosticData',
145 NSPrivacyCollectedDataTypeLinked: false,
146 NSPrivacyCollectedDataTypeTracking: false,
147 NSPrivacyCollectedDataTypePurposes: [
148 'NSPrivacyCollectedDataTypePurposeAppFunctionality',
149 ],
150 },
151 ],
152 NSPrivacyAccessedAPITypes: [
153 {
154 NSPrivacyAccessedAPIType:
155 'NSPrivacyAccessedAPICategoryFileTimestamp',
156 NSPrivacyAccessedAPITypeReasons: ['C617.1', '3B52.1', '0A2A.1'],
157 },
158 {
159 NSPrivacyAccessedAPIType: 'NSPrivacyAccessedAPICategoryDiskSpace',
160 NSPrivacyAccessedAPITypeReasons: ['E174.1', '85F4.1'],
161 },
162 {
163 NSPrivacyAccessedAPIType:
164 'NSPrivacyAccessedAPICategorySystemBootTime',
165 NSPrivacyAccessedAPITypeReasons: ['35F9.1'],
166 },
167 {
168 NSPrivacyAccessedAPIType:
169 'NSPrivacyAccessedAPICategoryUserDefaults',
170 NSPrivacyAccessedAPITypeReasons: ['CA92.1', '1C8F.1'],
171 },
172 ],
173 },
174 },
175 androidStatusBar: {
176 barStyle: 'light-content',
177 },
178 // Dark nav bar in light mode is better than light nav bar in dark mode
179 androidNavigationBar: {
180 barStyle: 'light-content',
181 },
182 android: {
183 icon: './assets/app-icons/android_icon_default_next.png',
184 adaptiveIcon: {
185 foregroundImage: './assets/icon-android-foreground.png',
186 monochromeImage: './assets/icon-android-monochrome.png',
187 backgroundColor: '#006AFF',
188 },
189 googleServicesFile: './google-services.json',
190 package: 'xyz.blueskyweb.app',
191 intentFilters: [
192 {
193 action: 'VIEW',
194 autoVerify: true,
195 data: [
196 {
197 scheme: 'https',
198 host: 'bsky.app',
199 },
200 ...(IS_DEV
201 ? [
202 {
203 scheme: 'http',
204 host: 'localhost:19006',
205 },
206 ]
207 : []),
208 ],
209 category: ['BROWSABLE', 'DEFAULT'],
210 },
211 ],
212 },
213 web: {
214 favicon: './assets/favicon.png',
215 },
216 updates: {
217 url: 'https://updates.bsky.app/manifest',
218 enabled: UPDATES_ENABLED,
219 fallbackToCacheTimeout: 30000,
220 codeSigningCertificate: UPDATES_ENABLED
221 ? './code-signing/certificate.pem'
222 : undefined,
223 codeSigningMetadata: UPDATES_ENABLED
224 ? {
225 keyid: 'main',
226 alg: 'rsa-v1_5-sha256',
227 }
228 : undefined,
229 checkAutomatically: 'NEVER',
230 },
231 plugins: [
232 'expo-video',
233 'expo-localization',
234 'expo-web-browser',
235 [
236 'react-native-edge-to-edge',
237 {android: {enforceNavigationBarContrast: false}},
238 ],
239 ...(USE_SENTRY
240 ? [
241 /** @type {[string, any]} */ ([
242 '@sentry/react-native/expo',
243 {
244 organization: 'blueskyweb',
245 project: 'app',
246 url: 'https://sentry.io',
247 },
248 ]),
249 ]
250 : []),
251 [
252 'expo-build-properties',
253 {
254 ios: {
255 deploymentTarget: '15.1',
256 buildReactNativeFromSource: true,
257 ccacheEnabled: IS_DEV,
258 extraPods: [
259 {
260 name: 'MCEmojiPicker',
261 git: 'https://github.com/bluesky-social/MCEmojiPicker.git',
262 branch: 'main',
263 },
264 ],
265 },
266 android: {
267 compileSdkVersion: 35,
268 targetSdkVersion: 35,
269 buildToolsVersion: '35.0.0',
270 buildReactNativeFromSource: IS_PRODUCTION,
271 },
272 },
273 ],
274 [
275 'expo-notifications',
276 {
277 icon: './assets/icon-android-notification.png',
278 color: '#1185fe',
279 sounds: PLATFORM === 'ios' ? ['assets/dm.aiff'] : ['assets/dm.mp3'],
280 },
281 ],
282 'react-native-compressor',
283 [
284 '@bitdrift/react-native',
285 {
286 networkInstrumentation: true,
287 },
288 ],
289 './plugins/starterPackAppClipExtension/withStarterPackAppClip.js',
290 './plugins/withGradleJVMHeapSizeIncrease.js',
291 './plugins/withAndroidManifestLargeHeapPlugin.js',
292 './plugins/withAndroidManifestFCMIconPlugin.js',
293 './plugins/withAndroidManifestIntentQueriesPlugin.js',
294 './plugins/withAndroidStylesAccentColorPlugin.js',
295 './plugins/withAndroidDayNightThemePlugin.js',
296 './plugins/withAndroidNoJitpackPlugin.js',
297 './plugins/shareExtension/withShareExtensions.js',
298 './plugins/notificationsExtension/withNotificationsExtension.js',
299 [
300 'expo-font',
301 {
302 fonts: [
303 './assets/fonts/inter/InterVariable.woff2',
304 './assets/fonts/inter/InterVariable-Italic.woff2',
305 // Android only
306 './assets/fonts/inter/Inter-Regular.otf',
307 './assets/fonts/inter/Inter-Italic.otf',
308 './assets/fonts/inter/Inter-Medium.otf',
309 './assets/fonts/inter/Inter-MediumItalic.otf',
310 './assets/fonts/inter/Inter-SemiBold.otf',
311 './assets/fonts/inter/Inter-SemiBoldItalic.otf',
312 './assets/fonts/inter/Inter-Bold.otf',
313 './assets/fonts/inter/Inter-BoldItalic.otf',
314 ],
315 },
316 ],
317 [
318 'expo-splash-screen',
319 {
320 ios: {
321 enableFullScreenImage_legacy: true, // iOS only
322 backgroundColor: '#006AFF', // primary_500
323 image: './assets/splash/splash.png',
324 resizeMode: 'cover',
325 dark: {
326 enableFullScreenImage_legacy: true, // iOS only
327 backgroundColor: '#002861', // primary_900
328 image: './assets/splash/splash-dark.png',
329 resizeMode: 'cover',
330 },
331 },
332 android: {
333 backgroundColor: '#006AFF', // primary_500
334 image: './assets/splash/android-splash-logo-white.png',
335 imageWidth: 102, // even division of 306px
336 dark: {
337 backgroundColor: '#002861', // primary_900
338 image: './assets/splash/android-splash-logo-white.png',
339 imageWidth: 102,
340 },
341 },
342 },
343 ],
344 [
345 '@mozzius/expo-dynamic-app-icon',
346 {
347 /**
348 * Default set
349 */
350 default_light: {
351 ios: './assets/app-icons/ios_icon_legacy_light.png',
352 android: './assets/app-icons/android_icon_legacy_light.png',
353 prerendered: true,
354 },
355 default_dark: {
356 ios: './assets/app-icons/ios_icon_legacy_dark.png',
357 android: './assets/app-icons/android_icon_legacy_dark.png',
358 prerendered: true,
359 },
360
361 /**
362 * Bluesky+ core set
363 */
364 core_aurora: {
365 ios: './assets/app-icons/ios_icon_core_aurora.png',
366 android: './assets/app-icons/android_icon_core_aurora.png',
367 prerendered: true,
368 },
369 core_bonfire: {
370 ios: './assets/app-icons/ios_icon_core_bonfire.png',
371 android: './assets/app-icons/android_icon_core_bonfire.png',
372 prerendered: true,
373 },
374 core_sunrise: {
375 ios: './assets/app-icons/ios_icon_core_sunrise.png',
376 android: './assets/app-icons/android_icon_core_sunrise.png',
377 prerendered: true,
378 },
379 core_sunset: {
380 ios: './assets/app-icons/ios_icon_core_sunset.png',
381 android: './assets/app-icons/android_icon_core_sunset.png',
382 prerendered: true,
383 },
384 core_midnight: {
385 ios: './assets/app-icons/ios_icon_core_midnight.png',
386 android: './assets/app-icons/android_icon_core_midnight.png',
387 prerendered: true,
388 },
389 core_flat_blue: {
390 ios: './assets/app-icons/ios_icon_core_flat_blue.png',
391 android: './assets/app-icons/android_icon_core_flat_blue.png',
392 prerendered: true,
393 },
394 core_flat_white: {
395 ios: './assets/app-icons/ios_icon_core_flat_white.png',
396 android: './assets/app-icons/android_icon_core_flat_white.png',
397 prerendered: true,
398 },
399 core_flat_black: {
400 ios: './assets/app-icons/ios_icon_core_flat_black.png',
401 android: './assets/app-icons/android_icon_core_flat_black.png',
402 prerendered: true,
403 },
404 core_classic: {
405 ios: './assets/app-icons/ios_icon_core_classic.png',
406 android: './assets/app-icons/android_icon_core_classic.png',
407 prerendered: true,
408 },
409 },
410 ],
411 ['expo-screen-orientation', {initialOrientation: 'PORTRAIT_UP'}],
412 ['expo-location'],
413 [
414 'expo-contacts',
415 {
416 contactsPermission:
417 'I agree to allow Bluesky to use my contacts for friend discovery until I opt out.',
418 },
419 ],
420 ],
421 extra: {
422 eas: {
423 build: {
424 experimental: {
425 ios: {
426 appExtensions: [
427 {
428 targetName: 'Share-with-Bluesky',
429 bundleIdentifier: 'xyz.blueskyweb.app.Share-with-Bluesky',
430 entitlements: {
431 'com.apple.security.application-groups': [
432 'group.app.bsky',
433 ],
434 },
435 },
436 {
437 targetName: 'BlueskyNSE',
438 bundleIdentifier: 'xyz.blueskyweb.app.BlueskyNSE',
439 entitlements: {
440 'com.apple.security.application-groups': [
441 'group.app.bsky',
442 ],
443 },
444 },
445 {
446 targetName: 'BlueskyClip',
447 bundleIdentifier: 'xyz.blueskyweb.app.AppClip',
448 },
449 ],
450 },
451 },
452 },
453 projectId: '55bd077a-d905-4184-9c7f-94789ba0f302',
454 },
455 },
456 },
457 }
458}