Live video on the AT Protocol
at eli/postgres 294 lines 8.7 kB view raw
1import { 2 ConfigPlugin, 3 withAndroidManifest, 4 withEntitlementsPlist, 5 withXcodeProject, 6} from "expo/config-plugins"; 7import streamplaceReactNativeWebRTC from "../config-react-native-webrtc"; 8export const withNotificationsIOS: ConfigPlugin = (config) => { 9 config = withEntitlementsPlist(config, (config) => { 10 config.modResults["aps-environment"] = "production"; 11 return config; 12 }); 13 return config; 14}; 15 16export const withoutNotificationsIOS: ConfigPlugin = (config) => { 17 config = withEntitlementsPlist(config, (config) => { 18 delete config.modResults["aps-environment"]; 19 return config; 20 }); 21 return config; 22}; 23 24const withAndroidProfileable = (config) => { 25 return withAndroidManifest(config, (config) => { 26 const androidManifest = config.modResults.manifest; 27 if ( 28 !androidManifest.application || 29 androidManifest.application.length === 0 30 ) { 31 throw new Error("No application found in AndroidManifest.xml"); 32 } 33 const mainApplication = androidManifest.application[0]; 34 35 (mainApplication as any).profileable = [ 36 { 37 $: { 38 "android:shell": "true", 39 "android:enabled": "true", 40 }, 41 }, 42 ]; 43 44 return config; 45 }); 46}; 47 48const withConsistentVersionNumber = ( 49 config, 50 { version }: { version: string }, 51) => { 52 // if (!config.ios) { 53 // config.ios = {}; 54 // } 55 // if (!config.ios.infoPlist) { 56 // config.ios.infoPlist = {}; 57 // } 58 config = withXcodeProject(config, (config) => { 59 for (let [k, v] of Object.entries( 60 config.modResults.hash.project.objects.XCBuildConfiguration, 61 )) { 62 const obj = v as any; 63 if (!obj.buildSettings) { 64 continue; 65 } 66 if (typeof obj.buildSettings.MARKETING_VERSION !== "undefined") { 67 obj.buildSettings.MARKETING_VERSION = version; 68 } 69 if (typeof obj.buildSettings.CURRENT_PROJECT_VERSION !== "undefined") { 70 obj.buildSettings.CURRENT_PROJECT_VERSION = version; 71 } 72 } 73 return config; 74 }); 75 return config; 76}; 77 78// turn a semver string into a always-increasing integer for google 79export const versionCode = (verStr: string) => { 80 const [major, minor, patch] = verStr.split(".").map((x) => parseInt(x)); 81 return major * 1000 * 1000 + minor * 1000 + patch; 82}; 83 84export default function () { 85 const isProd = 86 process.env["SP_PRODUCTION_RELEASE"] === "true" || !!process.env.CI; 87 const pkg = require("./package.json"); 88 const name = isProd ? "Streamplace" : "Devplace"; 89 let bundle = isProd ? "tv.aquareum" : "tv.aquareum.dev"; 90 if (process.env["SP_BUNDLE_OVERRIDE"]) { 91 bundle = process.env["SP_BUNDLE_OVERRIDE"]; 92 } 93 let appleTeamId = process.env["SP_APPLE_TEAM_ID"]; 94 const scheme = process.env["SP_APP_SCHEME"] ?? bundle; 95 return { 96 expo: { 97 name: name, 98 slug: name, 99 version: pkg.version, 100 // Only rev this to the current version when native dependencies change! 101 runtimeVersion: pkg.runtimeVersion, 102 orientation: "default", 103 icon: "./assets/images/icon.png", 104 scheme: scheme, 105 userInterfaceStyle: "automatic", 106 splash: { 107 image: "./assets/images/splash.png", 108 resizeMode: "contain", 109 backgroundColor: "#ffffff", 110 }, 111 assetBundlePatterns: ["**/*"], 112 ios: { 113 supportsTablet: true, 114 bundleIdentifier: bundle, 115 infoPlist: { 116 UIBackgroundModes: ["fetch", "remote-notification"], 117 LSMinimumSystemVersion: "12.0", 118 }, 119 ...(appleTeamId 120 ? { 121 appleTeamId, 122 } 123 : {}), 124 ...(isProd 125 ? { 126 googleServicesFile: "./GoogleService-Info.plist", 127 entitlements: { 128 "aps-environment": "production", 129 }, 130 associatedDomains: ["applinks:stream.place"], 131 } 132 : {}), 133 }, 134 android: { 135 adaptiveIcon: { 136 foregroundImage: "./assets/images/adaptive-icon.png", 137 backgroundColor: "#ffffff", 138 }, 139 package: bundle, 140 edgeToEdgeEnabled: true, 141 versionCode: versionCode(pkg.version), 142 intentFilters: [ 143 { 144 action: "VIEW", 145 autoVerify: true, 146 data: [ 147 { 148 scheme: "https", 149 host: "stream.place", 150 pathPattern: "/.*:.*", 151 }, 152 ], 153 category: ["BROWSABLE", "DEFAULT"], 154 }, 155 { 156 action: "VIEW", 157 autoVerify: true, 158 data: [ 159 { 160 scheme: "https", 161 host: "stream.place", 162 pathPattern: "/.*\\\\..*", 163 }, 164 ], 165 category: ["BROWSABLE", "DEFAULT"], 166 }, 167 { 168 action: "VIEW", 169 autoVerify: true, 170 data: [ 171 { 172 scheme: "https", 173 host: "stream.place", 174 path: "/", 175 }, 176 ], 177 category: ["BROWSABLE", "DEFAULT"], 178 }, 179 ], 180 ...(isProd 181 ? { 182 googleServicesFile: "./google-services.json", 183 permissions: [ 184 "android.permission.SCHEDULE_EXACT_ALARM", 185 "android.permission.POST_NOTIFICATIONS", 186 ], 187 } 188 : {}), 189 }, 190 web: { 191 bundler: "metro", 192 output: "single", 193 favicon: "./assets/images/favicon.png", 194 }, 195 plugins: [ 196 withAndroidProfileable, 197 "expo-video", 198 "expo-web-browser", 199 streamplaceReactNativeWebRTC, 200 [ 201 "expo-video", 202 { 203 supportsBackgroundPlayback: true, 204 supportsPictureInPicture: true, 205 }, 206 ], 207 ["expo-sqlite", { useSQLCipher: true }], 208 "expo-file-system", 209 [ 210 "expo-font", 211 { 212 fonts: [ 213 "assets/fonts/FiraCode-Bold.ttf", 214 "assets/fonts/FiraCode-Light.ttf", 215 "assets/fonts/FiraCode-Medium.ttf", 216 "assets/fonts/FiraCode-Regular.ttf", 217 "assets/fonts/FiraCode-Retina.ttf", 218 "assets/fonts/FiraSans-Black.ttf", 219 "assets/fonts/FiraSans-BlackItalic.ttf", 220 "assets/fonts/FiraSans-Bold.ttf", 221 "assets/fonts/FiraSans-BoldItalic.ttf", 222 "assets/fonts/FiraSans-ExtraBold.ttf", 223 "assets/fonts/FiraSans-ExtraBoldItalic.ttf", 224 "assets/fonts/FiraSans-ExtraLight.ttf", 225 "assets/fonts/FiraSans-ExtraLightItalic.ttf", 226 "assets/fonts/FiraSans-Italic.ttf", 227 "assets/fonts/FiraSans-Light.ttf", 228 "assets/fonts/FiraSans-LightItalic.ttf", 229 "assets/fonts/FiraSans-Medium.ttf", 230 "assets/fonts/FiraSans-MediumItalic.ttf", 231 "assets/fonts/FiraSans-Regular.ttf", 232 "assets/fonts/FiraSans-SemiBold.ttf", 233 "assets/fonts/FiraSans-SemiBoldItalic.ttf", 234 "assets/fonts/FiraSans-Thin.ttf", 235 "assets/fonts/FiraSans-ThinItalic.ttf", 236 "assets/fonts/SpaceMono-Regular.ttf", 237 ], 238 }, 239 ], 240 [ 241 "expo-build-properties", 242 { 243 ios: { 244 useFrameworks: "static", 245 }, 246 // uncomment to test OTA updates to http://localhost:8080 247 // android: { 248 // usesCleartextTraffic: true, 249 // }, 250 }, 251 ], 252 [ 253 "expo-asset", 254 { 255 assets: ["assets"], 256 }, 257 ], 258 [withConsistentVersionNumber, { version: pkg.version }], 259 [ 260 "react-native-edge-to-edge", 261 { 262 android: { 263 parentTheme: "Default", 264 enforceNavigationBarContrast: false, 265 }, 266 }, 267 ], 268 ...(isProd 269 ? [ 270 "@react-native-firebase/app", 271 "@react-native-firebase/messaging", 272 [withNotificationsIOS, {}], 273 ] 274 : ["expo-dev-launcher", withoutNotificationsIOS]), 275 ], 276 experiments: { 277 typedRoutes: true, 278 }, 279 updates: isProd 280 ? { 281 url: `https://stream.place/api/manifest`, 282 enabled: true, 283 checkAutomatically: "ON_LOAD", 284 fallbackToCacheTimeout: 30000, 285 codeSigningCertificate: "./code-signing/certs/certificate.pem", 286 codeSigningMetadata: { 287 keyid: "main", 288 alg: "rsa-v1_5-sha256", 289 }, 290 } 291 : {}, 292 }, 293 }; 294}