@dubsdotapp/expo 0.5.19 → 0.5.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app.plugin.js +26 -29
- package/dist/index.js +67 -88
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +67 -88
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/ui/AuthGate.tsx +107 -112
package/app.plugin.js
CHANGED
|
@@ -15,14 +15,21 @@ const GMS_PLUGIN = 'apply plugin: "com.google.gms.google-services"';
|
|
|
15
15
|
/**
|
|
16
16
|
* @dubsdotapp/expo Expo config plugin.
|
|
17
17
|
*
|
|
18
|
-
* 1.
|
|
18
|
+
* 1. Stages the developer's google-services.json into android/app/ at prebuild time
|
|
19
|
+
* (each consumer must bring their own Firebase project — no shared fallback)
|
|
19
20
|
* 2. Adds the Google Services Gradle classpath + plugin so Firebase initialises
|
|
20
21
|
*
|
|
22
|
+
* The plugin looks for google-services.json in two locations (in priority order):
|
|
23
|
+
* 1. <projectRoot>/google-services.json ← recommended (committed by the dev)
|
|
24
|
+
* 2. <projectRoot>/android/app/google-services.json (already in place — leave alone)
|
|
25
|
+
*
|
|
26
|
+
* If neither exists, prebuild fails with a clear error. Push will not work without it.
|
|
27
|
+
*
|
|
21
28
|
* Usage in app.json:
|
|
22
29
|
* ["@dubsdotapp/expo"]
|
|
23
30
|
*/
|
|
24
31
|
function withDubsNotifications(config) {
|
|
25
|
-
// Step 1 —
|
|
32
|
+
// Step 1 — stage google-services.json into android/app/
|
|
26
33
|
config = withDangerousMod(config, [
|
|
27
34
|
'android',
|
|
28
35
|
async (cfg) => {
|
|
@@ -34,38 +41,28 @@ function withDubsNotifications(config) {
|
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
const projectRoot = cfg.modRequest.projectRoot;
|
|
37
|
-
const
|
|
44
|
+
const targetPath = path.join(projectRoot, 'android', 'app', 'google-services.json');
|
|
45
|
+
const rootPath = path.join(projectRoot, 'google-services.json');
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (fs.existsSync(filePath) || fs.existsSync(rootGoogleServices)) {
|
|
47
|
+
if (fs.existsSync(targetPath)) {
|
|
48
|
+
// Already in place (e.g. committed under android/app/) — leave it alone
|
|
42
49
|
return cfg;
|
|
43
50
|
}
|
|
44
51
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
client: [
|
|
52
|
-
{
|
|
53
|
-
client_info: {
|
|
54
|
-
mobilesdk_app_id: '1:161838368570:android:5d77e05a49f7efc64b7203',
|
|
55
|
-
android_client_info: { package_name: packageName },
|
|
56
|
-
},
|
|
57
|
-
oauth_client: [],
|
|
58
|
-
api_key: [{ current_key: 'AIzaSyCtJKF-o1cZ3Y89oM5FlXIGsjFchETzzkM' }],
|
|
59
|
-
services: { appinvite_service: { other_platform_oauth_client: [] } },
|
|
60
|
-
},
|
|
61
|
-
],
|
|
62
|
-
configuration_version: '1',
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
66
|
-
fs.writeFileSync(filePath, JSON.stringify(googleServices, null, 2));
|
|
52
|
+
if (fs.existsSync(rootPath)) {
|
|
53
|
+
// Developer dropped it at project root — copy into android/app/ where Gradle expects
|
|
54
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
55
|
+
fs.copyFileSync(rootPath, targetPath);
|
|
56
|
+
return cfg;
|
|
57
|
+
}
|
|
67
58
|
|
|
68
|
-
|
|
59
|
+
throw new Error(
|
|
60
|
+
'@dubsdotapp/expo plugin: google-services.json not found.\n' +
|
|
61
|
+
' Download it from your Firebase project (Project settings → General → Your apps) and place it at:\n' +
|
|
62
|
+
` ${path.join(projectRoot, 'google-services.json')}\n` +
|
|
63
|
+
' The package name in Firebase must match android.package in app.json: ' +
|
|
64
|
+
`${packageName}`,
|
|
65
|
+
);
|
|
69
66
|
},
|
|
70
67
|
]);
|
|
71
68
|
|
package/dist/index.js
CHANGED
|
@@ -3438,7 +3438,7 @@ function AuthGate({
|
|
|
3438
3438
|
appName = "Dubs",
|
|
3439
3439
|
accentColor
|
|
3440
3440
|
}) {
|
|
3441
|
-
const { client, pushEnabled } = useDubs();
|
|
3441
|
+
const { client, pushEnabled, uiConfig } = useDubs();
|
|
3442
3442
|
const auth = useAuth();
|
|
3443
3443
|
const [phase, setPhase] = (0, import_react28.useState)("init");
|
|
3444
3444
|
const [registrationPhase, setRegistrationPhase] = (0, import_react28.useState)(false);
|
|
@@ -3475,10 +3475,10 @@ function AuthGate({
|
|
|
3475
3475
|
if (auth.status === "needsRegistration") setRegistrationPhase(true);
|
|
3476
3476
|
}, [auth.status]);
|
|
3477
3477
|
(0, import_react28.useEffect)(() => {
|
|
3478
|
-
if (pushEnabled && auth.status === "authenticated" && registrationPhase && !isRestoredSession) {
|
|
3478
|
+
if (pushEnabled && uiConfig.pushConfigured?.android && auth.status === "authenticated" && registrationPhase && !isRestoredSession) {
|
|
3479
3479
|
setShowPushSetup(true);
|
|
3480
3480
|
}
|
|
3481
|
-
}, [pushEnabled, auth.status, registrationPhase, isRestoredSession]);
|
|
3481
|
+
}, [pushEnabled, uiConfig.pushConfigured?.android, auth.status, registrationPhase, isRestoredSession]);
|
|
3482
3482
|
(0, import_react28.useEffect)(() => {
|
|
3483
3483
|
if (auth.token) onSaveToken(auth.token);
|
|
3484
3484
|
}, [auth.token]);
|
|
@@ -3497,23 +3497,14 @@ function AuthGate({
|
|
|
3497
3497
|
if (renderLoading) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: renderLoading("authenticating") });
|
|
3498
3498
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DefaultLoadingScreen, { status: "authenticating", appName, accentColor });
|
|
3499
3499
|
}
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3503
|
-
PushSetupScreen,
|
|
3504
|
-
{
|
|
3505
|
-
accentColor,
|
|
3506
|
-
appName,
|
|
3507
|
-
onComplete: () => setShowPushSetup(false)
|
|
3508
|
-
}
|
|
3509
|
-
);
|
|
3510
|
-
}
|
|
3500
|
+
const inRegistrationFlow = registrationPhase && (auth.status === "needsRegistration" || auth.status === "registering" || auth.status === "authenticated" && showPushSetup);
|
|
3501
|
+
if (auth.status === "authenticated" && !inRegistrationFlow) {
|
|
3511
3502
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(AuthContext.Provider, { value: auth, children: [
|
|
3512
3503
|
pushEnabled && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PushTokenRestorer, {}),
|
|
3513
3504
|
children
|
|
3514
3505
|
] });
|
|
3515
3506
|
}
|
|
3516
|
-
if (
|
|
3507
|
+
if (inRegistrationFlow) {
|
|
3517
3508
|
const isRegistering = auth.status === "registering";
|
|
3518
3509
|
const regError = auth.status === "error" ? auth.error : null;
|
|
3519
3510
|
if (renderRegistration) {
|
|
@@ -3527,7 +3518,9 @@ function AuthGate({
|
|
|
3527
3518
|
error: regError,
|
|
3528
3519
|
client,
|
|
3529
3520
|
appName,
|
|
3530
|
-
accentColor
|
|
3521
|
+
accentColor,
|
|
3522
|
+
pushStepActive: showPushSetup,
|
|
3523
|
+
onPushComplete: () => setShowPushSetup(false)
|
|
3531
3524
|
}
|
|
3532
3525
|
);
|
|
3533
3526
|
}
|
|
@@ -3607,10 +3600,13 @@ function DefaultRegistrationScreen({
|
|
|
3607
3600
|
error,
|
|
3608
3601
|
client,
|
|
3609
3602
|
appName,
|
|
3610
|
-
accentColor
|
|
3603
|
+
accentColor,
|
|
3604
|
+
pushStepActive,
|
|
3605
|
+
onPushComplete
|
|
3611
3606
|
}) {
|
|
3612
3607
|
const t = useDubsTheme();
|
|
3613
3608
|
const accent = accentColor || t.accent;
|
|
3609
|
+
const push = usePushNotifications();
|
|
3614
3610
|
const [step, setStep] = (0, import_react28.useState)(0);
|
|
3615
3611
|
const [avatarSeed, setAvatarSeed] = (0, import_react28.useState)(generateSeed);
|
|
3616
3612
|
const [avatarStyle, setAvatarStyle] = (0, import_react28.useState)("adventurer");
|
|
@@ -3846,6 +3842,58 @@ function DefaultRegistrationScreen({
|
|
|
3846
3842
|
)
|
|
3847
3843
|
] })
|
|
3848
3844
|
] });
|
|
3845
|
+
(0, import_react28.useEffect)(() => {
|
|
3846
|
+
if (pushStepActive && step !== 3) {
|
|
3847
|
+
animateToStep(3);
|
|
3848
|
+
}
|
|
3849
|
+
}, [pushStepActive]);
|
|
3850
|
+
const handleEnablePush = async () => {
|
|
3851
|
+
try {
|
|
3852
|
+
await push.register();
|
|
3853
|
+
} catch {
|
|
3854
|
+
}
|
|
3855
|
+
onPushComplete?.();
|
|
3856
|
+
};
|
|
3857
|
+
const renderPushStep = () => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native8.View, { style: s.stepContainer, children: [
|
|
3858
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native8.View, { style: s.stepTop, children: [
|
|
3859
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.Text, { style: [s.title, { color: t.text }], children: "Enable Notifications" }),
|
|
3860
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.Text, { style: [s.subtitle, { color: t.textMuted }], children: "Stay in the loop with real-time updates" }),
|
|
3861
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StepIndicator, { currentStep: 3 }),
|
|
3862
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.View, { style: pushStyles.iconContainer, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.View, { style: [pushStyles.bellCircle, { backgroundColor: accent + "20" }], children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.Text, { style: [pushStyles.bellIcon, { color: accent }], children: "\u{1F514}" }) }) }),
|
|
3863
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native8.View, { style: pushStyles.benefitsList, children: [
|
|
3864
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.Text, { style: [pushStyles.benefitsHeader, { color: t.text }], children: "Get real-time updates when:" }),
|
|
3865
|
+
[
|
|
3866
|
+
"A fight you picked on goes LIVE",
|
|
3867
|
+
"Your pick wins or loses",
|
|
3868
|
+
"Final results and rankings"
|
|
3869
|
+
].map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native8.View, { style: pushStyles.benefitRow, children: [
|
|
3870
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.View, { style: [pushStyles.bulletDot, { backgroundColor: accent }] }),
|
|
3871
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.Text, { style: [pushStyles.benefitText, { color: t.textMuted }], children: item })
|
|
3872
|
+
] }, i))
|
|
3873
|
+
] })
|
|
3874
|
+
] }),
|
|
3875
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native8.View, { style: s.bottomRow, children: [
|
|
3876
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3877
|
+
import_react_native8.TouchableOpacity,
|
|
3878
|
+
{
|
|
3879
|
+
style: [s.secondaryBtn, { borderColor: t.border }],
|
|
3880
|
+
onPress: onPushComplete,
|
|
3881
|
+
activeOpacity: 0.7,
|
|
3882
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.Text, { style: [s.secondaryBtnText, { color: t.textMuted }], children: "Maybe Later" })
|
|
3883
|
+
}
|
|
3884
|
+
),
|
|
3885
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3886
|
+
import_react_native8.TouchableOpacity,
|
|
3887
|
+
{
|
|
3888
|
+
style: [s.primaryBtn, { backgroundColor: accent, flex: 1, opacity: push.loading ? 0.7 : 1 }],
|
|
3889
|
+
onPress: handleEnablePush,
|
|
3890
|
+
disabled: push.loading,
|
|
3891
|
+
activeOpacity: 0.8,
|
|
3892
|
+
children: push.loading ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.ActivityIndicator, { color: "#FFFFFF", size: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.Text, { style: s.primaryBtnText, children: "Enable Notifications" })
|
|
3893
|
+
}
|
|
3894
|
+
)
|
|
3895
|
+
] })
|
|
3896
|
+
] });
|
|
3849
3897
|
const renderStep = () => {
|
|
3850
3898
|
switch (step) {
|
|
3851
3899
|
case 0:
|
|
@@ -3854,6 +3902,8 @@ function DefaultRegistrationScreen({
|
|
|
3854
3902
|
return renderUsernameStep();
|
|
3855
3903
|
case 2:
|
|
3856
3904
|
return renderReferralStep();
|
|
3905
|
+
case 3:
|
|
3906
|
+
return renderPushStep();
|
|
3857
3907
|
default:
|
|
3858
3908
|
return null;
|
|
3859
3909
|
}
|
|
@@ -3894,77 +3944,6 @@ function PushTokenRestorer() {
|
|
|
3894
3944
|
}, []);
|
|
3895
3945
|
return null;
|
|
3896
3946
|
}
|
|
3897
|
-
function PushSetupScreen({
|
|
3898
|
-
accentColor,
|
|
3899
|
-
appName,
|
|
3900
|
-
onComplete
|
|
3901
|
-
}) {
|
|
3902
|
-
const t = useDubsTheme();
|
|
3903
|
-
const accent = accentColor || t.accent;
|
|
3904
|
-
const push = usePushNotifications();
|
|
3905
|
-
const fadeAnim = (0, import_react28.useRef)(new import_react_native8.Animated.Value(0)).current;
|
|
3906
|
-
const slideAnim = (0, import_react28.useRef)(new import_react_native8.Animated.Value(30)).current;
|
|
3907
|
-
(0, import_react28.useEffect)(() => {
|
|
3908
|
-
import_react_native8.Animated.parallel([
|
|
3909
|
-
import_react_native8.Animated.timing(fadeAnim, { toValue: 1, duration: 300, useNativeDriver: true }),
|
|
3910
|
-
import_react_native8.Animated.timing(slideAnim, { toValue: 0, duration: 300, useNativeDriver: true })
|
|
3911
|
-
]).start();
|
|
3912
|
-
}, [fadeAnim, slideAnim]);
|
|
3913
|
-
const handleEnable = async () => {
|
|
3914
|
-
await push.register();
|
|
3915
|
-
onComplete();
|
|
3916
|
-
};
|
|
3917
|
-
const benefits = [
|
|
3918
|
-
"A fight you picked on goes LIVE",
|
|
3919
|
-
"Your pick wins or loses",
|
|
3920
|
-
"Final results and rankings"
|
|
3921
|
-
];
|
|
3922
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.View, { style: [s.container, { backgroundColor: t.background }], children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
3923
|
-
import_react_native8.Animated.View,
|
|
3924
|
-
{
|
|
3925
|
-
style: [
|
|
3926
|
-
s.stepContainer,
|
|
3927
|
-
{ opacity: fadeAnim, transform: [{ translateY: slideAnim }] }
|
|
3928
|
-
],
|
|
3929
|
-
children: [
|
|
3930
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native8.View, { style: s.stepTop, children: [
|
|
3931
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.Text, { style: [s.title, { color: t.text }], children: "Enable Notifications" }),
|
|
3932
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.Text, { style: [s.subtitle, { color: t.textMuted }], children: "Stay in the loop with real-time updates" }),
|
|
3933
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StepIndicator, { currentStep: 3 }),
|
|
3934
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.View, { style: pushStyles.iconContainer, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.View, { style: [pushStyles.bellCircle, { backgroundColor: accent + "20" }], children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.Text, { style: [pushStyles.bellIcon, { color: accent }], children: "\u{1F514}" }) }) }),
|
|
3935
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native8.View, { style: pushStyles.benefitsList, children: [
|
|
3936
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.Text, { style: [pushStyles.benefitsHeader, { color: t.text }], children: "Get real-time updates when:" }),
|
|
3937
|
-
benefits.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native8.View, { style: pushStyles.benefitRow, children: [
|
|
3938
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.View, { style: [pushStyles.bulletDot, { backgroundColor: accent }] }),
|
|
3939
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.Text, { style: [pushStyles.benefitText, { color: t.textMuted }], children: item })
|
|
3940
|
-
] }, i))
|
|
3941
|
-
] })
|
|
3942
|
-
] }),
|
|
3943
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native8.View, { style: s.bottomRow, children: [
|
|
3944
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3945
|
-
import_react_native8.TouchableOpacity,
|
|
3946
|
-
{
|
|
3947
|
-
style: [s.secondaryBtn, { borderColor: t.border }],
|
|
3948
|
-
onPress: onComplete,
|
|
3949
|
-
activeOpacity: 0.7,
|
|
3950
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.Text, { style: [s.secondaryBtnText, { color: t.textMuted }], children: "Maybe Later" })
|
|
3951
|
-
}
|
|
3952
|
-
),
|
|
3953
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3954
|
-
import_react_native8.TouchableOpacity,
|
|
3955
|
-
{
|
|
3956
|
-
style: [s.primaryBtn, { backgroundColor: accent, flex: 1, opacity: push.loading ? 0.7 : 1 }],
|
|
3957
|
-
onPress: handleEnable,
|
|
3958
|
-
disabled: push.loading,
|
|
3959
|
-
activeOpacity: 0.8,
|
|
3960
|
-
children: push.loading ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.ActivityIndicator, { color: "#FFFFFF", size: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native8.Text, { style: s.primaryBtnText, children: "Enable Notifications" })
|
|
3961
|
-
}
|
|
3962
|
-
)
|
|
3963
|
-
] })
|
|
3964
|
-
]
|
|
3965
|
-
}
|
|
3966
|
-
) });
|
|
3967
|
-
}
|
|
3968
3947
|
var pushStyles = import_react_native8.StyleSheet.create({
|
|
3969
3948
|
iconContainer: { alignItems: "center", marginVertical: 24 },
|
|
3970
3949
|
bellCircle: { width: 100, height: 100, borderRadius: 50, justifyContent: "center", alignItems: "center" },
|