Live video on the AT Protocol
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 }
131 : {}),
132 },
133 android: {
134 adaptiveIcon: {
135 foregroundImage: "./assets/images/adaptive-icon.png",
136 backgroundColor: "#ffffff",
137 },
138 package: bundle,
139 versionCode: versionCode(pkg.version),
140 ...(isProd
141 ? {
142 googleServicesFile: "./google-services.json",
143 permissions: [
144 "android.permission.SCHEDULE_EXACT_ALARM",
145 "android.permission.POST_NOTIFICATIONS",
146 ],
147 }
148 : {}),
149 },
150 web: {
151 bundler: "metro",
152 output: "single",
153 favicon: "./assets/images/favicon.png",
154 },
155 plugins: [
156 withAndroidProfileable,
157 "expo-video",
158 "expo-web-browser",
159 streamplaceReactNativeWebRTC,
160 ["expo-sqlite", { useSQLCipher: true }],
161 "expo-file-system",
162 [
163 "expo-font",
164 {
165 fonts: [
166 "assets/fonts/FiraCode-Bold.ttf",
167 "assets/fonts/FiraCode-Light.ttf",
168 "assets/fonts/FiraCode-Medium.ttf",
169 "assets/fonts/FiraCode-Regular.ttf",
170 "assets/fonts/FiraCode-Retina.ttf",
171 "assets/fonts/FiraSans-Black.ttf",
172 "assets/fonts/FiraSans-BlackItalic.ttf",
173 "assets/fonts/FiraSans-Bold.ttf",
174 "assets/fonts/FiraSans-BoldItalic.ttf",
175 "assets/fonts/FiraSans-ExtraBold.ttf",
176 "assets/fonts/FiraSans-ExtraBoldItalic.ttf",
177 "assets/fonts/FiraSans-ExtraLight.ttf",
178 "assets/fonts/FiraSans-ExtraLightItalic.ttf",
179 "assets/fonts/FiraSans-Italic.ttf",
180 "assets/fonts/FiraSans-Light.ttf",
181 "assets/fonts/FiraSans-LightItalic.ttf",
182 "assets/fonts/FiraSans-Medium.ttf",
183 "assets/fonts/FiraSans-MediumItalic.ttf",
184 "assets/fonts/FiraSans-Regular.ttf",
185 "assets/fonts/FiraSans-SemiBold.ttf",
186 "assets/fonts/FiraSans-SemiBoldItalic.ttf",
187 "assets/fonts/FiraSans-Thin.ttf",
188 "assets/fonts/FiraSans-ThinItalic.ttf",
189 "assets/fonts/SpaceMono-Regular.ttf",
190 ],
191 },
192 ],
193 [
194 "expo-build-properties",
195 {
196 ios: {
197 useFrameworks: "static",
198 },
199 // uncomment to test OTA updates to http://localhost:8080
200 // android: {
201 // usesCleartextTraffic: true,
202 // },
203 },
204 ],
205 [
206 "expo-asset",
207 {
208 assets: ["assets"],
209 },
210 ],
211 [withConsistentVersionNumber, { version: pkg.version }],
212 ...(isProd
213 ? [
214 "@react-native-firebase/app",
215 "@react-native-firebase/messaging",
216 [withNotificationsIOS, {}],
217 ]
218 : ["expo-dev-launcher", withoutNotificationsIOS]),
219 ],
220 experiments: {
221 typedRoutes: true,
222 },
223 updates: isProd
224 ? {
225 url: `https://stream.place/api/manifest`,
226 enabled: true,
227 checkAutomatically: "ON_LOAD",
228 fallbackToCacheTimeout: 30000,
229 codeSigningCertificate: "./code-signing/certs/certificate.pem",
230 codeSigningMetadata: {
231 keyid: "main",
232 alg: "rsa-v1_5-sha256",
233 },
234 }
235 : {},
236 },
237 };
238}