yaver-feedback-react-native 0.5.4 → 0.6.0
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/README.md +48 -4
- package/dist/LoginScreen.d.ts +3 -4
- package/dist/LoginScreen.js +155 -224
- package/dist/ShakeDetector.js +22 -0
- package/dist/YaverFeedback.js +28 -0
- package/dist/auth.d.ts +40 -34
- package/dist/auth.js +113 -53
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -3
- package/dist/types.d.ts +2 -1
- package/package.json +7 -2
- package/src/LoginScreen.tsx +250 -313
- package/src/ShakeDetector.ts +22 -1
- package/src/YaverFeedback.ts +29 -0
- package/src/auth.ts +145 -68
- package/src/index.ts +3 -4
- package/src/types.ts +2 -1
package/README.md
CHANGED
|
@@ -16,11 +16,16 @@ Manual fallback:
|
|
|
16
16
|
npm install yaver-feedback-react-native
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
> **Mobile only.** This SDK targets React Native (iOS + Android). A `yaver-feedback-web` package exists for browser apps but currently expects a bring-your-own auth token — the equivalent in-app sign-in UX (Apple / Google / GitHub / GitLab / Microsoft / email) for the web SDK will land in a future release. Open an issue if you need it sooner.
|
|
20
|
+
|
|
19
21
|
### Peer dependencies
|
|
20
22
|
|
|
21
|
-
For full functionality, install these
|
|
23
|
+
For full functionality, install these peer dependencies:
|
|
22
24
|
|
|
23
25
|
```bash
|
|
26
|
+
# Auth (in-app browser OAuth + native Apple Sign-In)
|
|
27
|
+
npm install expo-web-browser expo-apple-authentication
|
|
28
|
+
|
|
24
29
|
# Device discovery (stored connections)
|
|
25
30
|
npm install @react-native-async-storage/async-storage
|
|
26
31
|
|
|
@@ -31,11 +36,28 @@ npm install react-native-view-shot
|
|
|
31
36
|
npm install react-native-audio-recorder-player
|
|
32
37
|
```
|
|
33
38
|
|
|
34
|
-
|
|
39
|
+
`expo-apple-authentication` is optional — if it's missing, "Continue with Apple" falls through to in-app browser OAuth on Android. iOS hosts that want true native Apple Sign-In must also enable the **Sign in with Apple** capability in Xcode.
|
|
40
|
+
|
|
41
|
+
Android hosts must register the OAuth callback in `AndroidManifest.xml`:
|
|
42
|
+
|
|
43
|
+
```xml
|
|
44
|
+
<intent-filter>
|
|
45
|
+
<action android:name="android.intent.action.VIEW" />
|
|
46
|
+
<category android:name="android.intent.category.DEFAULT" />
|
|
47
|
+
<category android:name="android.intent.category.BROWSABLE" />
|
|
48
|
+
<data android:scheme="yaver" android:host="oauth-callback" />
|
|
49
|
+
</intent-filter>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
iOS does not require any URL scheme registration — `ASWebAuthenticationSession` intercepts the redirect inside the auth session.
|
|
53
|
+
|
|
54
|
+
## Quick Start (0.6+: native auth)
|
|
35
55
|
|
|
36
|
-
Starting with **0.
|
|
56
|
+
Starting with **0.6.0**, the SDK login screen mirrors the Yaver mobile app: native Apple Sign-In on iOS, in-app browser OAuth for Google / GitHub / GitLab / Microsoft (no codes, no leaving the app), and inline email + password. The user never sees a verification code or has to switch to a web browser.
|
|
37
57
|
|
|
38
|
-
|
|
58
|
+
Drop in `<FeedbackModal />` and the first time a user triggers feedback without a cached session, they get:
|
|
59
|
+
|
|
60
|
+
1. A full-screen login modal — Apple (native) / Google / GitHub / GitLab / Microsoft (in-app browser) / email + password (inline).
|
|
39
61
|
2. A picker of the machines they can reach — **their own dev boxes** plus **guest-shared machines** (where another user invited them).
|
|
40
62
|
|
|
41
63
|
Both screens persist their selection to `AsyncStorage`, so subsequent launches reconnect silently.
|
|
@@ -574,12 +596,34 @@ YaverFeedback.init({
|
|
|
574
596
|
});
|
|
575
597
|
```
|
|
576
598
|
|
|
599
|
+
## Running Inside the Yaver Mobile App (super-host)
|
|
600
|
+
|
|
601
|
+
When a third-party app is loaded through Yaver's Hermes-push flow (so the
|
|
602
|
+
Yaver mobile app is the runtime container for your app), the SDK detects
|
|
603
|
+
that situation via the `YaverInfo` native module and **automatically
|
|
604
|
+
no-ops `YaverFeedback.init()`** — no ShakeDetector, no FeedbackModal, no
|
|
605
|
+
BlackBox stream from inside the guest. The user only ever sees Yaver's
|
|
606
|
+
native shake overlay ("Reload" + "Back to Yaver") and uses Yaver's
|
|
607
|
+
built-in feedback flow instead.
|
|
608
|
+
|
|
609
|
+
Standalone installs (TestFlight / App Store / Play from your own dev
|
|
610
|
+
account) are unaffected — the SDK behaves exactly as it did before.
|
|
611
|
+
|
|
612
|
+
If you need to opt out of the auto-suppression in a specific build (for
|
|
613
|
+
example, to compare behaviour in a super-host smoke test), set
|
|
614
|
+
`enabled: true` explicitly — the Yaver-host gate runs before the
|
|
615
|
+
`enabled` flag is evaluated on purpose.
|
|
616
|
+
|
|
577
617
|
## Trigger Modes
|
|
578
618
|
|
|
579
619
|
### Shake (default)
|
|
580
620
|
|
|
581
621
|
Shake the device to open the feedback modal. Uses the built-in shake event on iOS and `ShakeEvent` on Android.
|
|
582
622
|
|
|
623
|
+
When the app is loaded inside the Yaver mobile app (super-host), the
|
|
624
|
+
SDK yields shake handling to Yaver's native overlay — see the
|
|
625
|
+
"Running Inside the Yaver Mobile App" section above.
|
|
626
|
+
|
|
583
627
|
```typescript
|
|
584
628
|
YaverFeedback.init({ authToken, trigger: 'shake' });
|
|
585
629
|
```
|
package/dist/LoginScreen.d.ts
CHANGED
|
@@ -6,9 +6,8 @@ export interface YaverLoginScreenProps {
|
|
|
6
6
|
onCancel?: () => void;
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
|
-
* Full-screen in-SDK login.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* available as an inline fallback for headless environments.
|
|
9
|
+
* Full-screen in-SDK login. Mirrors the Yaver mobile app login UX: native
|
|
10
|
+
* Apple Sign-In on iOS, in-app browser OAuth for Google/GitHub/GitLab/
|
|
11
|
+
* Microsoft (no codes, no leaving the app), and inline email/password.
|
|
13
12
|
*/
|
|
14
13
|
export declare const YaverLoginScreen: React.FC<YaverLoginScreenProps>;
|
package/dist/LoginScreen.js
CHANGED
|
@@ -37,281 +37,212 @@ exports.YaverLoginScreen = void 0;
|
|
|
37
37
|
const react_1 = __importStar(require("react"));
|
|
38
38
|
const react_native_1 = require("react-native");
|
|
39
39
|
const auth_1 = require("./auth");
|
|
40
|
-
const PROVIDERS = [
|
|
41
|
-
{ id: 'apple', label: 'Apple', emoji: '' },
|
|
42
|
-
{ id: 'google', label: 'Google', emoji: 'G' },
|
|
43
|
-
{ id: 'github', label: 'GitHub', emoji: '' },
|
|
44
|
-
{ id: 'gitlab', label: 'GitLab', emoji: '' },
|
|
45
|
-
{ id: 'microsoft', label: 'Microsoft', emoji: 'M' },
|
|
46
|
-
];
|
|
47
40
|
/**
|
|
48
|
-
* Full-screen in-SDK login.
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* available as an inline fallback for headless environments.
|
|
41
|
+
* Full-screen in-SDK login. Mirrors the Yaver mobile app login UX: native
|
|
42
|
+
* Apple Sign-In on iOS, in-app browser OAuth for Google/GitHub/GitLab/
|
|
43
|
+
* Microsoft (no codes, no leaving the app), and inline email/password.
|
|
52
44
|
*/
|
|
53
45
|
const YaverLoginScreen = ({ onLoggedIn, onCancel, }) => {
|
|
54
|
-
const [
|
|
55
|
-
const [
|
|
56
|
-
const [
|
|
57
|
-
const [starting, setStarting] = (0, react_1.useState)(false);
|
|
58
|
-
const pollRef = (0, react_1.useRef)(null);
|
|
59
|
-
const expiredTimerRef = (0, react_1.useRef)(null);
|
|
60
|
-
const [emailMode, setEmailMode] = (0, react_1.useState)('login');
|
|
46
|
+
const [busyProvider, setBusyProvider] = (0, react_1.useState)(null);
|
|
47
|
+
const [showEmailForm, setShowEmailForm] = (0, react_1.useState)(false);
|
|
48
|
+
const [isSignUp, setIsSignUp] = (0, react_1.useState)(false);
|
|
61
49
|
const [fullName, setFullName] = (0, react_1.useState)('');
|
|
62
50
|
const [email, setEmail] = (0, react_1.useState)('');
|
|
63
51
|
const [password, setPassword] = (0, react_1.useState)('');
|
|
52
|
+
const [confirmPassword, setConfirmPassword] = (0, react_1.useState)('');
|
|
64
53
|
const [emailBusy, setEmailBusy] = (0, react_1.useState)(false);
|
|
65
|
-
const [emailError, setEmailError] = (0, react_1.useState)(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
54
|
+
const [emailError, setEmailError] = (0, react_1.useState)('');
|
|
55
|
+
const finish = async (token) => {
|
|
56
|
+
const user = await (0, auth_1.validateToken)(token);
|
|
57
|
+
await (0, auth_1.saveToken)(token);
|
|
58
|
+
if (user)
|
|
59
|
+
await (0, auth_1.saveUser)(user);
|
|
60
|
+
onLoggedIn(token);
|
|
61
|
+
};
|
|
62
|
+
const handleApple = async () => {
|
|
63
|
+
setBusyProvider('apple');
|
|
64
|
+
try {
|
|
65
|
+
const { token } = await (0, auth_1.signInWithApple)();
|
|
66
|
+
await finish(token);
|
|
69
67
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}, [mode]);
|
|
75
|
-
const stopPolling = () => {
|
|
76
|
-
if (pollRef.current) {
|
|
77
|
-
clearInterval(pollRef.current);
|
|
78
|
-
pollRef.current = null;
|
|
68
|
+
catch (e) {
|
|
69
|
+
const msg = e instanceof Error ? e.message : 'Apple Sign-In failed';
|
|
70
|
+
if (msg !== 'cancelled')
|
|
71
|
+
react_native_1.Alert.alert('Sign In Failed', msg);
|
|
79
72
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
expiredTimerRef.current = null;
|
|
73
|
+
finally {
|
|
74
|
+
setBusyProvider(null);
|
|
83
75
|
}
|
|
84
76
|
};
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
setCodeError(null);
|
|
88
|
-
stopPolling();
|
|
77
|
+
const handleOAuth = async (provider) => {
|
|
78
|
+
setBusyProvider(provider);
|
|
89
79
|
try {
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
machineName: `feedback-sdk-${react_native_1.Platform.OS}`,
|
|
93
|
-
preferredProvider,
|
|
94
|
-
});
|
|
95
|
-
setCode(result);
|
|
96
|
-
pollRef.current = setInterval(async () => {
|
|
97
|
-
const poll = await (0, auth_1.pollDeviceCode)(result.deviceCode);
|
|
98
|
-
if (poll.status === 'authorized') {
|
|
99
|
-
stopPolling();
|
|
100
|
-
const user = await (0, auth_1.validateToken)(poll.token);
|
|
101
|
-
await (0, auth_1.saveToken)(poll.token);
|
|
102
|
-
if (user)
|
|
103
|
-
await (0, auth_1.saveUser)(user);
|
|
104
|
-
onLoggedIn(poll.token);
|
|
105
|
-
}
|
|
106
|
-
else if (poll.status === 'expired') {
|
|
107
|
-
stopPolling();
|
|
108
|
-
setCodeError('Kod doldu — tekrar başlat.');
|
|
109
|
-
setCode(null);
|
|
110
|
-
}
|
|
111
|
-
}, 3000);
|
|
112
|
-
expiredTimerRef.current = setTimeout(() => {
|
|
113
|
-
stopPolling();
|
|
114
|
-
setCode(null);
|
|
115
|
-
setCodeError('Kod süresi doldu.');
|
|
116
|
-
}, Math.max(0, result.expiresAt - Date.now()));
|
|
80
|
+
const { token } = await (0, auth_1.signInWithOAuth)(provider);
|
|
81
|
+
await finish(token);
|
|
117
82
|
}
|
|
118
|
-
catch (
|
|
119
|
-
|
|
83
|
+
catch (e) {
|
|
84
|
+
const msg = e instanceof Error ? e.message : 'Sign-in failed';
|
|
85
|
+
if (msg !== 'cancelled')
|
|
86
|
+
react_native_1.Alert.alert('Sign In Failed', msg);
|
|
120
87
|
}
|
|
121
88
|
finally {
|
|
122
|
-
|
|
89
|
+
setBusyProvider(null);
|
|
123
90
|
}
|
|
124
91
|
};
|
|
125
|
-
const openVerification = () => {
|
|
126
|
-
if (!code)
|
|
127
|
-
return;
|
|
128
|
-
react_native_1.Linking.openURL(code.verificationUrl).catch(() => {
|
|
129
|
-
setCodeError('Tarayıcı açılamadı — URL’yi elle aç.');
|
|
130
|
-
});
|
|
131
|
-
};
|
|
132
92
|
const handleEmailSubmit = async () => {
|
|
133
|
-
setEmailError(
|
|
134
|
-
if (
|
|
135
|
-
|
|
136
|
-
|
|
93
|
+
setEmailError('');
|
|
94
|
+
if (isSignUp) {
|
|
95
|
+
if (!fullName.trim())
|
|
96
|
+
return setEmailError('Full name is required');
|
|
97
|
+
if (password !== confirmPassword)
|
|
98
|
+
return setEmailError('Passwords do not match');
|
|
99
|
+
if (password.length < 8)
|
|
100
|
+
return setEmailError('Password must be at least 8 characters');
|
|
137
101
|
}
|
|
102
|
+
if (!email.trim() || !password)
|
|
103
|
+
return setEmailError('Email and password are required');
|
|
138
104
|
setEmailBusy(true);
|
|
139
105
|
try {
|
|
140
|
-
const result =
|
|
141
|
-
? await (0, auth_1.signupWithEmail)(fullName.trim()
|
|
106
|
+
const result = isSignUp
|
|
107
|
+
? await (0, auth_1.signupWithEmail)(fullName.trim(), email.trim(), password)
|
|
142
108
|
: await (0, auth_1.loginWithEmail)(email.trim(), password);
|
|
143
|
-
|
|
144
|
-
await (0, auth_1.saveToken)(result.token);
|
|
145
|
-
if (user)
|
|
146
|
-
await (0, auth_1.saveUser)(user);
|
|
147
|
-
onLoggedIn(result.token);
|
|
109
|
+
await finish(result.token);
|
|
148
110
|
}
|
|
149
|
-
catch (
|
|
150
|
-
setEmailError(
|
|
111
|
+
catch (e) {
|
|
112
|
+
setEmailError(e instanceof Error ? e.message : 'Something went wrong');
|
|
151
113
|
}
|
|
152
114
|
finally {
|
|
153
115
|
setEmailBusy(false);
|
|
154
116
|
}
|
|
155
117
|
};
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
118
|
+
const renderProvider = (id, label, onPress) => (<react_native_1.Pressable key={id} style={({ pressed }) => [
|
|
119
|
+
styles.button,
|
|
120
|
+
pressed && styles.buttonPressed,
|
|
121
|
+
busyProvider === id && { opacity: 0.6 },
|
|
122
|
+
]} onPress={onPress} disabled={busyProvider !== null}>
|
|
123
|
+
{busyProvider === id ? (<react_native_1.ActivityIndicator color="#e0e0e0"/>) : (<react_native_1.Text style={styles.buttonText}>{label}</react_native_1.Text>)}
|
|
124
|
+
</react_native_1.Pressable>);
|
|
125
|
+
return (<react_native_1.SafeAreaView style={styles.safeArea}>
|
|
126
|
+
<react_native_1.KeyboardAvoidingView style={{ flex: 1 }} behavior={react_native_1.Platform.OS === 'ios' ? 'padding' : undefined}>
|
|
127
|
+
<react_native_1.ScrollView contentContainerStyle={styles.scrollContainer} keyboardShouldPersistTaps="handled">
|
|
128
|
+
<react_native_1.View style={styles.header}>
|
|
129
|
+
<react_native_1.Text style={styles.logo}>Yaver</react_native_1.Text>
|
|
130
|
+
<react_native_1.Text style={styles.subtitle}>Sign in to send feedback</react_native_1.Text>
|
|
131
|
+
{onCancel && (<react_native_1.Pressable onPress={onCancel} style={styles.cancel}>
|
|
132
|
+
<react_native_1.Text style={styles.cancelText}>Cancel</react_native_1.Text>
|
|
133
|
+
</react_native_1.Pressable>)}
|
|
134
|
+
</react_native_1.View>
|
|
164
135
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
E-posta
|
|
174
|
-
</react_native_1.Text>
|
|
175
|
-
</react_native_1.TouchableOpacity>
|
|
176
|
-
</react_native_1.View>
|
|
136
|
+
<react_native_1.View style={styles.buttons}>
|
|
137
|
+
{react_native_1.Platform.OS === 'ios'
|
|
138
|
+
? renderProvider('apple', 'Continue with Apple', handleApple)
|
|
139
|
+
: renderProvider('apple', 'Continue with Apple', () => handleOAuth('apple'))}
|
|
140
|
+
{renderProvider('google', 'Continue with Google', () => handleOAuth('google'))}
|
|
141
|
+
{renderProvider('github', 'Continue with GitHub', () => handleOAuth('github'))}
|
|
142
|
+
{renderProvider('gitlab', 'Continue with GitLab', () => handleOAuth('gitlab'))}
|
|
143
|
+
{renderProvider('microsoft', 'Continue with Microsoft', () => handleOAuth('microsoft'))}
|
|
177
144
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
<react_native_1.View style={styles.
|
|
185
|
-
<react_native_1.
|
|
145
|
+
{!showEmailForm ? (<react_native_1.Pressable style={({ pressed }) => [
|
|
146
|
+
styles.button,
|
|
147
|
+
pressed && styles.buttonPressed,
|
|
148
|
+
]} onPress={() => setShowEmailForm(true)} disabled={busyProvider !== null}>
|
|
149
|
+
<react_native_1.Text style={styles.buttonText}>Continue with Email</react_native_1.Text>
|
|
150
|
+
</react_native_1.Pressable>) : (<>
|
|
151
|
+
<react_native_1.View style={styles.divider}>
|
|
152
|
+
<react_native_1.View style={styles.dividerLine}/>
|
|
153
|
+
<react_native_1.Text style={styles.dividerText}>email</react_native_1.Text>
|
|
154
|
+
<react_native_1.View style={styles.dividerLine}/>
|
|
186
155
|
</react_native_1.View>
|
|
187
|
-
<react_native_1.
|
|
188
|
-
|
|
189
|
-
|
|
156
|
+
{isSignUp && (<react_native_1.TextInput style={styles.input} placeholder="Full Name" placeholderTextColor="#666" value={fullName} onChangeText={setFullName} autoCapitalize="words" autoCorrect={false}/>)}
|
|
157
|
+
<react_native_1.TextInput style={styles.input} placeholder="Email" placeholderTextColor="#666" value={email} onChangeText={setEmail} keyboardType="email-address" autoCapitalize="none" autoCorrect={false}/>
|
|
158
|
+
<react_native_1.TextInput style={styles.input} placeholder="Password" placeholderTextColor="#666" value={password} onChangeText={setPassword} secureTextEntry autoCapitalize="none"/>
|
|
159
|
+
{isSignUp && (<react_native_1.TextInput style={styles.input} placeholder="Confirm Password" placeholderTextColor="#666" value={confirmPassword} onChangeText={setConfirmPassword} secureTextEntry/>)}
|
|
190
160
|
|
|
191
|
-
<react_native_1.Text style={
|
|
192
|
-
Sağlayıcıyı önceden seçmek istersen:
|
|
193
|
-
</react_native_1.Text>
|
|
194
|
-
<react_native_1.View style={styles.providerRow}>
|
|
195
|
-
{PROVIDERS.map((p) => (<react_native_1.TouchableOpacity key={p.id} style={styles.providerButton} onPress={() => beginDeviceCode(p.id)}>
|
|
196
|
-
<react_native_1.Text style={styles.providerButtonText}>{p.label}</react_native_1.Text>
|
|
197
|
-
</react_native_1.TouchableOpacity>))}
|
|
198
|
-
</react_native_1.View>
|
|
199
|
-
</>) : (<react_native_1.TouchableOpacity style={styles.primaryButton} onPress={() => beginDeviceCode(undefined)}>
|
|
200
|
-
<react_native_1.Text style={styles.primaryButtonText}>Tekrar Başlat</react_native_1.Text>
|
|
201
|
-
</react_native_1.TouchableOpacity>)}
|
|
202
|
-
{codeError && <react_native_1.Text style={styles.error}>{codeError}</react_native_1.Text>}
|
|
203
|
-
</react_native_1.View>)}
|
|
161
|
+
{emailError ? (<react_native_1.Text style={styles.errorText}>{emailError}</react_native_1.Text>) : null}
|
|
204
162
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
163
|
+
<react_native_1.Pressable style={({ pressed }) => [
|
|
164
|
+
styles.submitButton,
|
|
165
|
+
pressed && styles.buttonPressed,
|
|
166
|
+
emailBusy && { opacity: 0.6 },
|
|
167
|
+
]} onPress={handleEmailSubmit} disabled={emailBusy}>
|
|
168
|
+
{emailBusy ? (<react_native_1.ActivityIndicator color="#fff"/>) : (<react_native_1.Text style={styles.submitButtonText}>
|
|
169
|
+
{isSignUp ? 'Create Account' : 'Sign In'}
|
|
170
|
+
</react_native_1.Text>)}
|
|
171
|
+
</react_native_1.Pressable>
|
|
214
172
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
173
|
+
<react_native_1.Pressable onPress={() => {
|
|
174
|
+
setIsSignUp(!isSignUp);
|
|
175
|
+
setEmailError('');
|
|
176
|
+
}}>
|
|
177
|
+
<react_native_1.Text style={styles.toggleText}>
|
|
178
|
+
{isSignUp
|
|
179
|
+
? 'Already have an account? Sign In'
|
|
180
|
+
: "Don't have an account? Sign Up"}
|
|
181
|
+
</react_native_1.Text>
|
|
182
|
+
</react_native_1.Pressable>
|
|
218
183
|
</>)}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
<react_native_1.Text style={styles.label}>Parola</react_native_1.Text>
|
|
224
|
-
<react_native_1.TextInput style={styles.input} value={password} onChangeText={setPassword} placeholder="••••••••" placeholderTextColor="#666" secureTextEntry autoCapitalize="none"/>
|
|
225
|
-
|
|
226
|
-
<react_native_1.TouchableOpacity style={styles.primaryButton} onPress={handleEmailSubmit} disabled={emailBusy}>
|
|
227
|
-
{emailBusy ? (<react_native_1.ActivityIndicator color="#fff"/>) : (<react_native_1.Text style={styles.primaryButtonText}>
|
|
228
|
-
{emailMode === 'signup' ? 'Kayıt Ol' : 'Giriş Yap'}
|
|
229
|
-
</react_native_1.Text>)}
|
|
230
|
-
</react_native_1.TouchableOpacity>
|
|
231
|
-
|
|
232
|
-
{emailError && <react_native_1.Text style={styles.error}>{emailError}</react_native_1.Text>}
|
|
233
|
-
</react_native_1.View>)}
|
|
234
|
-
</react_native_1.ScrollView>
|
|
184
|
+
</react_native_1.View>
|
|
185
|
+
</react_native_1.ScrollView>
|
|
186
|
+
</react_native_1.KeyboardAvoidingView>
|
|
235
187
|
</react_native_1.SafeAreaView>);
|
|
236
188
|
};
|
|
237
189
|
exports.YaverLoginScreen = YaverLoginScreen;
|
|
238
190
|
const styles = react_native_1.StyleSheet.create({
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
justifyContent: 'space-between',
|
|
245
|
-
marginBottom: 20,
|
|
191
|
+
safeArea: { flex: 1, backgroundColor: '#1a1a2e' },
|
|
192
|
+
scrollContainer: {
|
|
193
|
+
flexGrow: 1,
|
|
194
|
+
paddingHorizontal: 24,
|
|
195
|
+
justifyContent: 'center',
|
|
246
196
|
},
|
|
247
|
-
|
|
248
|
-
|
|
197
|
+
header: { alignItems: 'center', marginBottom: 40 },
|
|
198
|
+
logo: { fontSize: 44, fontWeight: '800', color: '#e0e0e0', letterSpacing: -1 },
|
|
199
|
+
subtitle: { fontSize: 15, color: '#9ca3af', marginTop: 6 },
|
|
200
|
+
cancel: { position: 'absolute', right: 0, top: 0, padding: 8 },
|
|
249
201
|
cancelText: { color: '#9ca3af', fontSize: 14 },
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
paddingVertical: 12,
|
|
254
|
-
borderRadius: 10,
|
|
255
|
-
backgroundColor: 'rgba(255,255,255,0.05)',
|
|
256
|
-
alignItems: 'center',
|
|
257
|
-
},
|
|
258
|
-
tabActive: { backgroundColor: 'rgba(99,102,241,0.25)' },
|
|
259
|
-
tabText: { color: '#9ca3af', fontSize: 14, fontWeight: '600' },
|
|
260
|
-
tabTextActive: { color: '#e0e0e0' },
|
|
261
|
-
subTab: {
|
|
262
|
-
flex: 1,
|
|
263
|
-
paddingVertical: 8,
|
|
264
|
-
borderRadius: 8,
|
|
265
|
-
alignItems: 'center',
|
|
266
|
-
backgroundColor: 'rgba(255,255,255,0.05)',
|
|
267
|
-
},
|
|
268
|
-
subTabActive: { backgroundColor: 'rgba(99,102,241,0.2)' },
|
|
269
|
-
subTabText: { color: '#e0e0e0', fontSize: 13 },
|
|
270
|
-
section: { marginTop: 4 },
|
|
271
|
-
hint: { color: '#9ca3af', fontSize: 13, lineHeight: 18 },
|
|
272
|
-
codeBox: {
|
|
273
|
-
backgroundColor: 'rgba(99,102,241,0.15)',
|
|
202
|
+
buttons: { gap: 12 },
|
|
203
|
+
button: {
|
|
204
|
+
backgroundColor: 'rgba(255,255,255,0.06)',
|
|
274
205
|
borderWidth: 1,
|
|
275
|
-
borderColor: 'rgba(
|
|
276
|
-
borderRadius: 14,
|
|
277
|
-
paddingVertical: 24,
|
|
278
|
-
alignItems: 'center',
|
|
279
|
-
marginTop: 16,
|
|
280
|
-
marginBottom: 16,
|
|
281
|
-
},
|
|
282
|
-
codeText: {
|
|
283
|
-
color: '#e0e7ff',
|
|
284
|
-
fontSize: 36,
|
|
285
|
-
fontWeight: '800',
|
|
286
|
-
letterSpacing: 6,
|
|
287
|
-
fontVariant: ['tabular-nums'],
|
|
288
|
-
},
|
|
289
|
-
primaryButton: {
|
|
290
|
-
backgroundColor: '#6366f1',
|
|
206
|
+
borderColor: 'rgba(255,255,255,0.12)',
|
|
291
207
|
borderRadius: 12,
|
|
292
208
|
paddingVertical: 14,
|
|
293
209
|
alignItems: 'center',
|
|
294
|
-
|
|
210
|
+
justifyContent: 'center',
|
|
295
211
|
},
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
212
|
+
buttonPressed: { opacity: 0.7 },
|
|
213
|
+
buttonText: { color: '#e0e0e0', fontSize: 15, fontWeight: '600' },
|
|
214
|
+
divider: {
|
|
215
|
+
flexDirection: 'row',
|
|
216
|
+
alignItems: 'center',
|
|
217
|
+
marginTop: 16,
|
|
218
|
+
marginBottom: 8,
|
|
303
219
|
},
|
|
304
|
-
|
|
305
|
-
|
|
220
|
+
dividerLine: { flex: 1, height: 1, backgroundColor: 'rgba(255,255,255,0.12)' },
|
|
221
|
+
dividerText: { marginHorizontal: 14, fontSize: 12, color: '#6b7280' },
|
|
306
222
|
input: {
|
|
307
|
-
backgroundColor: 'rgba(255,255,255,0.
|
|
223
|
+
backgroundColor: 'rgba(255,255,255,0.06)',
|
|
308
224
|
borderWidth: 1,
|
|
309
|
-
borderColor: 'rgba(255,255,255,0.
|
|
310
|
-
borderRadius:
|
|
225
|
+
borderColor: 'rgba(255,255,255,0.12)',
|
|
226
|
+
borderRadius: 12,
|
|
311
227
|
paddingHorizontal: 14,
|
|
312
|
-
paddingVertical:
|
|
228
|
+
paddingVertical: 13,
|
|
313
229
|
color: '#e0e0e0',
|
|
314
230
|
fontSize: 15,
|
|
315
231
|
},
|
|
316
|
-
|
|
232
|
+
errorText: { color: '#ef4444', fontSize: 13, textAlign: 'center' },
|
|
233
|
+
submitButton: {
|
|
234
|
+
backgroundColor: '#6366f1',
|
|
235
|
+
borderRadius: 12,
|
|
236
|
+
paddingVertical: 14,
|
|
237
|
+
alignItems: 'center',
|
|
238
|
+
justifyContent: 'center',
|
|
239
|
+
marginTop: 4,
|
|
240
|
+
},
|
|
241
|
+
submitButtonText: { color: '#fff', fontSize: 15, fontWeight: '700' },
|
|
242
|
+
toggleText: {
|
|
243
|
+
color: '#818cf8',
|
|
244
|
+
fontSize: 14,
|
|
245
|
+
textAlign: 'center',
|
|
246
|
+
marginTop: 4,
|
|
247
|
+
},
|
|
317
248
|
});
|
package/dist/ShakeDetector.js
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ShakeDetector = void 0;
|
|
4
4
|
const react_native_1 = require("react-native");
|
|
5
|
+
// Detects that the surrounding runtime is Yaver's super-host bridge. The
|
|
6
|
+
// YaverInfo native module is only registered inside Yaver's container
|
|
7
|
+
// (see mobile/ios/Yaver/YaverInfo.{swift,m} and the Android equivalent),
|
|
8
|
+
// so it is undefined in a standalone third-party app. When the SDK runs
|
|
9
|
+
// inside Yaver we yield shake handling to Yaver's native
|
|
10
|
+
// ShakeDetectingWindow — the user should only ever see the 2-button
|
|
11
|
+
// "Reload / Back to Yaver" overlay, never a FeedbackModal popped from
|
|
12
|
+
// inside the guest bundle.
|
|
13
|
+
function isRunningInsideYaverHost() {
|
|
14
|
+
try {
|
|
15
|
+
return !!react_native_1.NativeModules?.YaverInfo;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
5
21
|
const SHAKE_TIMEOUT_MS = 1000; // minimum time between shakes
|
|
6
22
|
const ACCEL_THRESHOLD_G = 1.8; // peak g-force that qualifies as a shake event
|
|
7
23
|
const ACCEL_MIN_HITS = 3; // peaks within the window before we fire
|
|
@@ -34,6 +50,12 @@ class ShakeDetector {
|
|
|
34
50
|
}
|
|
35
51
|
start(onShake) {
|
|
36
52
|
this.stop();
|
|
53
|
+
// When the app is loaded inside Yaver's super-host (Hermes push),
|
|
54
|
+
// Yaver owns the shake gesture and shows its own "Reload / Back to
|
|
55
|
+
// Yaver" overlay. We must not also fire the guest-side callback,
|
|
56
|
+
// otherwise the user gets both UIs at once.
|
|
57
|
+
if (isRunningInsideYaverHost())
|
|
58
|
+
return;
|
|
37
59
|
this.subscribeDevMenu(onShake);
|
|
38
60
|
this.subscribeAccelerometer(onShake);
|
|
39
61
|
}
|
package/dist/YaverFeedback.js
CHANGED
|
@@ -1,11 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.YaverFeedback = void 0;
|
|
4
|
+
const react_native_1 = require("react-native");
|
|
4
5
|
const Discovery_1 = require("./Discovery");
|
|
5
6
|
const BlackBox_1 = require("./BlackBox");
|
|
6
7
|
const P2PClient_1 = require("./P2PClient");
|
|
7
8
|
const ShakeDetector_1 = require("./ShakeDetector");
|
|
8
9
|
const auth_1 = require("./auth");
|
|
10
|
+
// Is this JS runtime the Yaver mobile app's super-host bridge? The
|
|
11
|
+
// YaverInfo native module is only registered inside Yaver's container
|
|
12
|
+
// (mobile/ios/Yaver/YaverInfo.{swift,m} + Android counterpart); a
|
|
13
|
+
// standalone app bundled by its own developer has no such module.
|
|
14
|
+
// When the SDK is loaded through Yaver's Hermes-push guest runtime we
|
|
15
|
+
// deliberately no-op every public entry point — Yaver owns the shake
|
|
16
|
+
// gesture ("Reload / Back to Yaver" overlay), the feedback capture
|
|
17
|
+
// flow, and the BlackBox streaming; running a second copy from inside
|
|
18
|
+
// the guest just produces duplicate UIs and double P2P sessions.
|
|
19
|
+
function isRunningInsideYaverHost() {
|
|
20
|
+
try {
|
|
21
|
+
return !!react_native_1.NativeModules?.YaverInfo;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Suppresses SDK activation when inside Yaver super-host. Callers of
|
|
28
|
+
// YaverFeedback.init / startReport / startBatchRecording / … early-out
|
|
29
|
+
// by checking this first so the SDK's side effects (accelerometer,
|
|
30
|
+
// BlackBox HTTP, SSE command channel, FeedbackModal mount) never start.
|
|
31
|
+
const YAVER_HOST_SUPPRESS = isRunningInsideYaverHost();
|
|
9
32
|
let config = null;
|
|
10
33
|
let enabled = false;
|
|
11
34
|
let p2pClient = null;
|
|
@@ -34,6 +57,11 @@ class YaverFeedback {
|
|
|
34
57
|
* via `YaverDiscovery` on the first `startReport()` call.
|
|
35
58
|
*/
|
|
36
59
|
static init(cfg) {
|
|
60
|
+
if (YAVER_HOST_SUPPRESS) {
|
|
61
|
+
// Running inside Yaver's super-host — yield to Yaver's native UX.
|
|
62
|
+
enabled = false;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
37
65
|
config = {
|
|
38
66
|
trigger: 'shake',
|
|
39
67
|
maxRecordingDuration: 120,
|