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