@authon/react-native 0.1.22 → 0.2.1

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/dist/index.js CHANGED
@@ -8,7 +8,14 @@ var AuthonMobileClient = class {
8
8
  apiUrl;
9
9
  publishableKey;
10
10
  tokens = null;
11
+ user = null;
11
12
  storage = null;
13
+ refreshTimer = null;
14
+ listeners = /* @__PURE__ */ new Map();
15
+ // Cached provider/branding data
16
+ _providers = [];
17
+ _branding = null;
18
+ _initialized = false;
12
19
  constructor(config) {
13
20
  this.publishableKey = config.publishableKey;
14
21
  this.apiUrl = (config.apiUrl || DEFAULT_API_URL).replace(/\/$/, "");
@@ -16,6 +23,7 @@ var AuthonMobileClient = class {
16
23
  setStorage(storage) {
17
24
  this.storage = storage;
18
25
  }
26
+ // ── Initialization ──
19
27
  async initialize() {
20
28
  if (!this.storage) return null;
21
29
  const stored = await this.storage.getItem(STORAGE_KEY);
@@ -24,6 +32,7 @@ var AuthonMobileClient = class {
24
32
  const tokens = JSON.parse(stored);
25
33
  if (tokens.expiresAt > Date.now()) {
26
34
  this.tokens = tokens;
35
+ this.scheduleRefresh(tokens.expiresAt);
27
36
  return tokens;
28
37
  }
29
38
  return await this.refreshToken(tokens.refreshToken);
@@ -32,17 +41,39 @@ var AuthonMobileClient = class {
32
41
  return null;
33
42
  }
34
43
  }
44
+ /** Fetch providers + branding from API (lazy, cached) */
45
+ async ensureInitialized() {
46
+ if (this._initialized) return;
47
+ try {
48
+ const [branding, providersRes] = await Promise.all([
49
+ this.request("GET", "/v1/auth/branding"),
50
+ this.request("GET", "/v1/auth/providers")
51
+ ]);
52
+ this._branding = branding;
53
+ this._providers = providersRes.providers;
54
+ this._initialized = true;
55
+ } catch (err) {
56
+ this.emit("error", err instanceof Error ? err : new Error(String(err)));
57
+ }
58
+ }
59
+ // ── Auth ──
35
60
  async signIn(params) {
36
61
  const res = await this.request("POST", "/v1/auth/signin", params);
37
62
  this.tokens = this.toTokenPair(res);
63
+ this.user = res.user;
38
64
  await this.persistTokens();
39
- return this.tokens;
65
+ this.scheduleRefresh(this.tokens.expiresAt);
66
+ this.emit("signedIn", res.user);
67
+ return { tokens: this.tokens, user: res.user };
40
68
  }
41
69
  async signUp(params) {
42
70
  const res = await this.request("POST", "/v1/auth/signup", params);
43
71
  this.tokens = this.toTokenPair(res);
72
+ this.user = res.user;
44
73
  await this.persistTokens();
45
- return this.tokens;
74
+ this.scheduleRefresh(this.tokens.expiresAt);
75
+ this.emit("signedIn", res.user);
76
+ return { tokens: this.tokens, user: res.user };
46
77
  }
47
78
  async signOut() {
48
79
  if (this.tokens) {
@@ -51,19 +82,23 @@ var AuthonMobileClient = class {
51
82
  } catch {
52
83
  }
53
84
  }
54
- this.tokens = null;
55
- if (this.storage) {
56
- await this.storage.removeItem(STORAGE_KEY);
57
- }
85
+ this.clearSession();
86
+ this.emit("signedOut");
58
87
  }
59
88
  async getUser() {
60
89
  if (!this.tokens) return null;
61
90
  try {
62
- return await this.request("GET", "/v1/auth/me");
91
+ const user = await this.request("GET", "/v1/auth/me");
92
+ this.user = user;
93
+ return user;
63
94
  } catch {
64
95
  return null;
65
96
  }
66
97
  }
98
+ getCachedUser() {
99
+ return this.user;
100
+ }
101
+ // ── Token management ──
67
102
  async refreshToken(refreshToken) {
68
103
  const token = refreshToken || this.tokens?.refreshToken;
69
104
  if (!token) return null;
@@ -77,13 +112,15 @@ var AuthonMobileClient = class {
77
112
  body: JSON.stringify({ refreshToken: token })
78
113
  });
79
114
  if (!res.ok) {
80
- this.tokens = null;
81
- if (this.storage) await this.storage.removeItem(STORAGE_KEY);
115
+ this.clearSession();
82
116
  return null;
83
117
  }
84
118
  const data = await res.json();
85
119
  this.tokens = this.toTokenPair(data);
120
+ this.user = data.user;
86
121
  await this.persistTokens();
122
+ this.scheduleRefresh(this.tokens.expiresAt);
123
+ this.emit("tokenRefreshed");
87
124
  return this.tokens;
88
125
  } catch {
89
126
  return null;
@@ -95,6 +132,98 @@ var AuthonMobileClient = class {
95
132
  isAuthenticated() {
96
133
  return this.tokens !== null && this.tokens.expiresAt > Date.now();
97
134
  }
135
+ // ── OAuth ──
136
+ async getProviders() {
137
+ await this.ensureInitialized();
138
+ return this._providers;
139
+ }
140
+ async getBranding() {
141
+ await this.ensureInitialized();
142
+ return this._branding;
143
+ }
144
+ async getOAuthUrl(provider, redirectUri) {
145
+ const params = new URLSearchParams({ redirectUri, flow: "redirect" });
146
+ return await this.request(
147
+ "GET",
148
+ `/v1/auth/oauth/${provider}/url?${params.toString()}`
149
+ );
150
+ }
151
+ async pollOAuth(state) {
152
+ try {
153
+ const res = await fetch(
154
+ `${this.apiUrl}/v1/auth/oauth/poll?state=${encodeURIComponent(state)}`,
155
+ { headers: { "x-api-key": this.publishableKey } }
156
+ );
157
+ if (!res.ok) return null;
158
+ const data = await res.json();
159
+ if (data.status === "completed" && data.accessToken) {
160
+ return data;
161
+ }
162
+ return null;
163
+ } catch {
164
+ return null;
165
+ }
166
+ }
167
+ /** Poll for OAuth completion (3 minute timeout, matching JS SDK) */
168
+ async completeOAuth(state) {
169
+ const maxAttempts = 360;
170
+ for (let i = 0; i < maxAttempts; i++) {
171
+ const result = await this.pollOAuth(state);
172
+ if (result) {
173
+ this.tokens = this.toTokenPair(result);
174
+ this.user = result.user;
175
+ await this.persistTokens();
176
+ this.scheduleRefresh(this.tokens.expiresAt);
177
+ this.emit("signedIn", result.user);
178
+ return { tokens: this.tokens, user: result.user };
179
+ }
180
+ await new Promise((r) => setTimeout(r, 500));
181
+ }
182
+ throw new Error("OAuth timeout");
183
+ }
184
+ getApiUrl() {
185
+ return this.apiUrl;
186
+ }
187
+ // ── Event system ──
188
+ on(event, listener) {
189
+ if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
190
+ const set = this.listeners.get(event);
191
+ set.add(listener);
192
+ return () => set.delete(listener);
193
+ }
194
+ emit(event, ...args) {
195
+ this.listeners.get(event)?.forEach((fn) => fn(...args));
196
+ }
197
+ // ── Cleanup ──
198
+ destroy() {
199
+ this.clearRefreshTimer();
200
+ this.listeners.clear();
201
+ }
202
+ // ── Private ──
203
+ clearSession() {
204
+ this.tokens = null;
205
+ this.user = null;
206
+ this.clearRefreshTimer();
207
+ if (this.storage) {
208
+ this.storage.removeItem(STORAGE_KEY).catch(() => {
209
+ });
210
+ }
211
+ }
212
+ clearRefreshTimer() {
213
+ if (this.refreshTimer) {
214
+ clearTimeout(this.refreshTimer);
215
+ this.refreshTimer = null;
216
+ }
217
+ }
218
+ /** Schedule auto-refresh 60 seconds before token expiry (like JS SDK) */
219
+ scheduleRefresh(expiresAt) {
220
+ this.clearRefreshTimer();
221
+ const refreshIn = Math.max(expiresAt - Date.now() - 6e4, 3e4);
222
+ this.refreshTimer = setTimeout(() => {
223
+ this.refreshToken().catch(() => {
224
+ });
225
+ }, refreshIn);
226
+ }
98
227
  async persistTokens() {
99
228
  if (this.storage && this.tokens) {
100
229
  await this.storage.setItem(STORAGE_KEY, JSON.stringify(this.tokens));
@@ -122,7 +251,8 @@ var AuthonMobileClient = class {
122
251
  });
123
252
  if (!res.ok) {
124
253
  const error = await res.json().catch(() => ({ message: res.statusText }));
125
- throw new Error(error.message || `Request failed with status ${res.status}`);
254
+ const msg = Array.isArray(error.message) ? error.message[0] : error.message || `Request failed with status ${res.status}`;
255
+ throw new Error(msg);
126
256
  }
127
257
  const text = await res.text();
128
258
  return text ? JSON.parse(text) : void 0;
@@ -142,6 +272,8 @@ function AuthonProvider({ children, storage, ...config }) {
142
272
  accessToken: null
143
273
  });
144
274
  const [user, setUser] = useState(null);
275
+ const [providers, setProviders] = useState([]);
276
+ const [branding, setBranding] = useState(null);
145
277
  if (!clientRef.current) {
146
278
  clientRef.current = new AuthonMobileClient(config);
147
279
  }
@@ -150,7 +282,13 @@ function AuthonProvider({ children, storage, ...config }) {
150
282
  if (storage) {
151
283
  client.setStorage(storage);
152
284
  }
153
- client.initialize().then(async (tokens) => {
285
+ const init = async () => {
286
+ const tokens = await client.initialize();
287
+ await client.ensureInitialized();
288
+ const p = await client.getProviders();
289
+ const b = await client.getBranding();
290
+ setProviders(p);
291
+ setBranding(b);
154
292
  if (tokens) {
155
293
  const u = await client.getUser();
156
294
  setUser(u);
@@ -164,11 +302,33 @@ function AuthonProvider({ children, storage, ...config }) {
164
302
  } else {
165
303
  setAuthState((prev) => ({ ...prev, isLoaded: true }));
166
304
  }
167
- });
305
+ };
306
+ init();
307
+ const unsubs = [
308
+ client.on("signedOut", () => {
309
+ setUser(null);
310
+ setAuthState({
311
+ isLoaded: true,
312
+ isSignedIn: false,
313
+ userId: null,
314
+ sessionId: null,
315
+ accessToken: null
316
+ });
317
+ }),
318
+ client.on("tokenRefreshed", () => {
319
+ const token = client.getAccessToken();
320
+ if (token) {
321
+ setAuthState((prev) => ({ ...prev, accessToken: token }));
322
+ }
323
+ })
324
+ ];
325
+ return () => {
326
+ unsubs.forEach((fn) => fn());
327
+ client.destroy();
328
+ };
168
329
  }, []);
169
330
  const signIn = useCallback(async (params) => {
170
- const tokens = await client.signIn(params);
171
- const u = await client.getUser();
331
+ const { tokens, user: u } = await client.signIn(params);
172
332
  setUser(u);
173
333
  setAuthState({
174
334
  isLoaded: true,
@@ -179,8 +339,7 @@ function AuthonProvider({ children, storage, ...config }) {
179
339
  });
180
340
  }, [client]);
181
341
  const signUp = useCallback(async (params) => {
182
- const tokens = await client.signUp(params);
183
- const u = await client.getUser();
342
+ const { tokens, user: u } = await client.signUp(params);
184
343
  setUser(u);
185
344
  setAuthState({
186
345
  isLoaded: true,
@@ -192,18 +351,37 @@ function AuthonProvider({ children, storage, ...config }) {
192
351
  }, [client]);
193
352
  const signOut = useCallback(async () => {
194
353
  await client.signOut();
195
- setUser(null);
196
- setAuthState({
197
- isLoaded: true,
198
- isSignedIn: false,
199
- userId: null,
200
- sessionId: null,
201
- accessToken: null
202
- });
203
354
  }, [client]);
204
355
  const getToken = useCallback(() => {
205
356
  return client.getAccessToken();
206
357
  }, [client]);
358
+ const startOAuth = useCallback(
359
+ async (provider, redirectUri) => {
360
+ const uri = redirectUri || `${client.getApiUrl()}/v1/auth/oauth/redirect`;
361
+ return client.getOAuthUrl(provider, uri);
362
+ },
363
+ [client]
364
+ );
365
+ const completeOAuthCb = useCallback(
366
+ async (state) => {
367
+ const { tokens, user: u } = await client.completeOAuth(state);
368
+ setUser(u);
369
+ setAuthState({
370
+ isLoaded: true,
371
+ isSignedIn: true,
372
+ userId: u?.id || null,
373
+ sessionId: null,
374
+ accessToken: tokens.accessToken
375
+ });
376
+ },
377
+ [client]
378
+ );
379
+ const on = useCallback(
380
+ (event, listener) => {
381
+ return client.on(event, listener);
382
+ },
383
+ [client]
384
+ );
207
385
  const value = useMemo(
208
386
  () => ({
209
387
  ...authState,
@@ -211,9 +389,15 @@ function AuthonProvider({ children, storage, ...config }) {
211
389
  signIn,
212
390
  signUp,
213
391
  signOut,
214
- getToken
392
+ getToken,
393
+ providers,
394
+ branding,
395
+ startOAuth,
396
+ completeOAuth: completeOAuthCb,
397
+ on,
398
+ client
215
399
  }),
216
- [authState, user, signIn, signUp, signOut, getToken]
400
+ [authState, user, signIn, signUp, signOut, getToken, providers, branding, startOAuth, completeOAuthCb, on, client]
217
401
  );
218
402
  return /* @__PURE__ */ jsx(AuthonContext.Provider, { value, children });
219
403
  }
@@ -233,10 +417,199 @@ function useUser() {
233
417
  const { isLoaded, isSignedIn, user } = useAuthon();
234
418
  return { isLoaded, isSignedIn, user };
235
419
  }
420
+
421
+ // src/icons.tsx
422
+ import "react";
423
+ import Svg, { Path, Rect } from "react-native-svg";
424
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
425
+ function GoogleIcon({ size = 20 }) {
426
+ return /* @__PURE__ */ jsxs(Svg, { viewBox: "0 0 24 24", width: size, height: size, children: [
427
+ /* @__PURE__ */ jsx2(Path, { fill: "#4285F4", d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" }),
428
+ /* @__PURE__ */ jsx2(Path, { fill: "#34A853", d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" }),
429
+ /* @__PURE__ */ jsx2(Path, { fill: "#FBBC05", d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" }),
430
+ /* @__PURE__ */ jsx2(Path, { fill: "#EA4335", d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" })
431
+ ] });
432
+ }
433
+ function AppleIcon({ size = 20, color = "#fff" }) {
434
+ return /* @__PURE__ */ jsx2(Svg, { viewBox: "0 0 24 24", width: size, height: size, fill: color, children: /* @__PURE__ */ jsx2(Path, { d: "M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z" }) });
435
+ }
436
+ function KakaoIcon({ size = 20 }) {
437
+ return /* @__PURE__ */ jsx2(Svg, { viewBox: "0 0 24 24", width: size, height: size, children: /* @__PURE__ */ jsx2(Path, { fill: "#191919", d: "M12 3C6.48 3 2 6.36 2 10.43c0 2.62 1.75 4.93 4.37 6.23l-1.12 4.14c-.1.36.31.65.62.44l4.93-3.26c.39.04.79.06 1.2.06 5.52 0 10-3.36 10-7.61C22 6.36 17.52 3 12 3z" }) });
438
+ }
439
+ function NaverIcon({ size = 20, color = "#fff" }) {
440
+ return /* @__PURE__ */ jsx2(Svg, { viewBox: "0 0 24 24", width: size, height: size, children: /* @__PURE__ */ jsx2(Path, { fill: color, d: "M16.273 12.845 7.376 0H0v24h7.726V11.156L16.624 24H24V0h-7.727v12.845Z" }) });
441
+ }
442
+ function FacebookIcon({ size = 20, color = "#fff" }) {
443
+ return /* @__PURE__ */ jsx2(Svg, { viewBox: "0 0 24 24", width: size, height: size, children: /* @__PURE__ */ jsx2(Path, { fill: color, d: "M24 12.07C24 5.41 18.63 0 12 0S0 5.4 0 12.07C0 18.1 4.39 23.1 10.13 24v-8.44H7.08v-3.49h3.04V9.41c0-3.02 1.8-4.7 4.54-4.7 1.31 0 2.68.24 2.68.24v2.97h-1.5c-1.5 0-1.96.93-1.96 1.89v2.26h3.33l-.53 3.49h-2.8V24C19.62 23.1 24 18.1 24 12.07z" }) });
444
+ }
445
+ function GithubIcon({ size = 20, color = "#fff" }) {
446
+ return /* @__PURE__ */ jsx2(Svg, { viewBox: "0 0 24 24", width: size, height: size, fill: color, children: /* @__PURE__ */ jsx2(Path, { d: "M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" }) });
447
+ }
448
+ function DiscordIcon({ size = 20, color = "#fff" }) {
449
+ return /* @__PURE__ */ jsx2(Svg, { viewBox: "0 0 24 24", width: size, height: size, fill: color, children: /* @__PURE__ */ jsx2(Path, { d: "M20.32 4.37a19.8 19.8 0 00-4.89-1.52.07.07 0 00-.08.04c-.21.38-.44.87-.61 1.26a18.27 18.27 0 00-5.49 0 12.64 12.64 0 00-.62-1.26.07.07 0 00-.08-.04 19.74 19.74 0 00-4.89 1.52.07.07 0 00-.03.03C1.11 8.39.34 12.28.73 16.12a.08.08 0 00.03.06 19.9 19.9 0 005.99 3.03.08.08 0 00.08-.03c.46-.63.87-1.3 1.22-2a.08.08 0 00-.04-.11 13.1 13.1 0 01-1.87-.9.08.08 0 01-.01-.13c.13-.09.25-.19.37-.29a.07.07 0 01.08-.01c3.93 1.8 8.18 1.8 12.07 0a.07.07 0 01.08 0c.12.1.25.2.37.3a.08.08 0 01-.01.12c-.6.35-1.22.65-1.87.9a.08.08 0 00-.04.1c.36.7.77 1.37 1.22 2a.08.08 0 00.08.03 19.83 19.83 0 006-3.03.08.08 0 00.03-.05c.47-4.87-.78-9.09-3.3-12.84a.06.06 0 00-.03-.03zM8.02 13.62c-1.11 0-2.03-1.02-2.03-2.28 0-1.26.9-2.28 2.03-2.28 1.14 0 2.04 1.03 2.03 2.28 0 1.26-.9 2.28-2.03 2.28zm7.5 0c-1.11 0-2.03-1.02-2.03-2.28 0-1.26.9-2.28 2.03-2.28 1.14 0 2.04 1.03 2.03 2.28 0 1.26-.89 2.28-2.03 2.28z" }) });
450
+ }
451
+ function XIcon({ size = 20, color = "#fff" }) {
452
+ return /* @__PURE__ */ jsx2(Svg, { viewBox: "0 0 24 24", width: size, height: size, fill: color, children: /* @__PURE__ */ jsx2(Path, { d: "M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" }) });
453
+ }
454
+ function LineIcon({ size = 20, color = "#fff" }) {
455
+ return /* @__PURE__ */ jsx2(Svg, { viewBox: "0 0 24 24", width: size, height: size, fill: color, children: /* @__PURE__ */ jsx2(Path, { d: "M19.365 9.863c.349 0 .63.285.63.631 0 .345-.281.63-.63.63H17.61v1.125h1.755c.349 0 .63.283.63.63 0 .344-.281.629-.63.629h-2.386c-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.63-.63h2.386c.346 0 .627.285.627.63 0 .349-.281.63-.63.63H17.61v1.125h1.755zm-3.855 3.016c0 .27-.174.51-.432.596-.064.021-.133.031-.199.031-.211 0-.391-.09-.51-.25l-2.443-3.317v2.94c0 .344-.279.629-.631.629-.346 0-.626-.285-.626-.629V8.108c0-.27.173-.51.43-.595.06-.023.136-.033.194-.033.195 0 .375.104.495.254l2.462 3.33V8.108c0-.345.282-.63.63-.63.345 0 .63.285.63.63v4.771zm-5.741 0c0 .344-.282.629-.631.629-.345 0-.627-.285-.627-.629V8.108c0-.345.282-.63.63-.63.346 0 .628.285.628.63v4.771zm-2.466.629H4.917c-.345 0-.63-.285-.63-.629V8.108c0-.345.285-.63.63-.63.348 0 .63.285.63.63v4.141h1.756c.348 0 .629.283.629.63 0 .344-.282.629-.629.629M24 10.314C24 4.943 18.615.572 12 .572S0 4.943 0 10.314c0 4.811 4.27 8.842 10.035 9.608.391.082.923.258 1.058.59.12.301.079.766.038 1.08l-.164 1.02c-.045.301-.24 1.186 1.049.645 1.291-.539 6.916-4.078 9.436-6.975C23.176 14.393 24 12.458 24 10.314" }) });
456
+ }
457
+ function MicrosoftIcon({ size = 20 }) {
458
+ return /* @__PURE__ */ jsxs(Svg, { viewBox: "0 0 24 24", width: size, height: size, children: [
459
+ /* @__PURE__ */ jsx2(Rect, { fill: "#F25022", x: 1, y: 1, width: 10, height: 10 }),
460
+ /* @__PURE__ */ jsx2(Rect, { fill: "#7FBA00", x: 13, y: 1, width: 10, height: 10 }),
461
+ /* @__PURE__ */ jsx2(Rect, { fill: "#00A4EF", x: 1, y: 13, width: 10, height: 10 }),
462
+ /* @__PURE__ */ jsx2(Rect, { fill: "#FFB900", x: 13, y: 13, width: 10, height: 10 })
463
+ ] });
464
+ }
465
+ var ICON_MAP = {
466
+ google: GoogleIcon,
467
+ apple: AppleIcon,
468
+ kakao: KakaoIcon,
469
+ naver: NaverIcon,
470
+ facebook: FacebookIcon,
471
+ github: GithubIcon,
472
+ discord: DiscordIcon,
473
+ x: XIcon,
474
+ line: LineIcon,
475
+ microsoft: MicrosoftIcon
476
+ };
477
+ function ProviderIcon({ provider, size = 20, color }) {
478
+ const Icon = ICON_MAP[provider];
479
+ if (!Icon) return null;
480
+ return /* @__PURE__ */ jsx2(Icon, { size, color });
481
+ }
482
+
483
+ // src/SocialButton.tsx
484
+ import "react";
485
+ import {
486
+ TouchableOpacity,
487
+ Text,
488
+ ActivityIndicator,
489
+ StyleSheet
490
+ } from "react-native";
491
+ import { PROVIDER_COLORS, PROVIDER_DISPLAY_NAMES } from "@authon/shared";
492
+ import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
493
+ function SocialButton({
494
+ provider,
495
+ onPress,
496
+ loading = false,
497
+ disabled = false,
498
+ label,
499
+ style,
500
+ labelStyle,
501
+ iconSize = 20,
502
+ borderRadius = 10,
503
+ height = 48
504
+ }) {
505
+ const colors = PROVIDER_COLORS[provider] || { bg: "#333", text: "#fff" };
506
+ const displayName = PROVIDER_DISPLAY_NAMES[provider] || provider;
507
+ const buttonLabel = label ?? `Continue with ${displayName}`;
508
+ const needsBorder = colors.bg.toLowerCase() === "#ffffff";
509
+ return /* @__PURE__ */ jsx3(
510
+ TouchableOpacity,
511
+ {
512
+ style: [
513
+ styles.button,
514
+ {
515
+ backgroundColor: colors.bg,
516
+ borderRadius,
517
+ height
518
+ },
519
+ needsBorder && styles.bordered,
520
+ style
521
+ ],
522
+ onPress: () => onPress(provider),
523
+ disabled: disabled || loading,
524
+ activeOpacity: 0.8,
525
+ children: loading ? /* @__PURE__ */ jsx3(ActivityIndicator, { color: colors.text, size: "small" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
526
+ /* @__PURE__ */ jsx3(ProviderIcon, { provider, size: iconSize, color: colors.text }),
527
+ /* @__PURE__ */ jsx3(
528
+ Text,
529
+ {
530
+ style: [
531
+ styles.label,
532
+ { color: colors.text },
533
+ labelStyle
534
+ ],
535
+ numberOfLines: 1,
536
+ children: buttonLabel
537
+ }
538
+ )
539
+ ] })
540
+ }
541
+ );
542
+ }
543
+ var styles = StyleSheet.create({
544
+ button: {
545
+ flexDirection: "row",
546
+ alignItems: "center",
547
+ justifyContent: "center",
548
+ gap: 10,
549
+ paddingHorizontal: 16
550
+ },
551
+ bordered: {
552
+ borderWidth: 1,
553
+ borderColor: "#dadce0"
554
+ },
555
+ label: {
556
+ fontSize: 15,
557
+ fontWeight: "600"
558
+ }
559
+ });
560
+
561
+ // src/SocialButtons.tsx
562
+ import { useState as useState2 } from "react";
563
+ import { View, StyleSheet as StyleSheet2, Linking } from "react-native";
564
+ import { jsx as jsx4 } from "react/jsx-runtime";
565
+ function SocialButtons({
566
+ onSuccess,
567
+ onError,
568
+ style,
569
+ gap = 10,
570
+ buttonProps
571
+ }) {
572
+ const { providers, startOAuth, completeOAuth } = useAuthon();
573
+ const [loadingProvider, setLoadingProvider] = useState2(null);
574
+ if (providers.length === 0) return null;
575
+ const handlePress = async (provider) => {
576
+ setLoadingProvider(provider);
577
+ try {
578
+ const { url, state } = await startOAuth(provider);
579
+ await Linking.openURL(url);
580
+ await completeOAuth(state);
581
+ onSuccess?.();
582
+ } catch (e) {
583
+ const error = e instanceof Error ? e : new Error(String(e));
584
+ if (error.message !== "OAuth timeout") {
585
+ onError?.(error);
586
+ }
587
+ } finally {
588
+ setLoadingProvider(null);
589
+ }
590
+ };
591
+ return /* @__PURE__ */ jsx4(View, { style: [styles2.container, { gap }, style], children: providers.map((provider) => /* @__PURE__ */ jsx4(
592
+ SocialButton,
593
+ {
594
+ provider,
595
+ onPress: handlePress,
596
+ loading: loadingProvider === provider,
597
+ disabled: !!loadingProvider,
598
+ ...buttonProps
599
+ },
600
+ provider
601
+ )) });
602
+ }
603
+ var styles2 = StyleSheet2.create({
604
+ container: {}
605
+ });
236
606
  export {
237
607
  AuthonContext,
238
608
  AuthonMobileClient,
239
609
  AuthonProvider,
610
+ ProviderIcon,
611
+ SocialButton,
612
+ SocialButtons,
240
613
  useAuthon,
241
614
  useUser
242
615
  };