@proveanything/smartlinks-auth-ui 0.5.16 → 0.5.17

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
@@ -12154,10 +12154,34 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
12154
12154
  }
12155
12155
  });
12156
12156
  }, []);
12157
- // Sync contact to Smartlinks (non-blocking)
12157
+ // Sync contact to Smartlinks (non-blocking).
12158
+ // Strategy: call publicGetMine() first — the "me" endpoint tells us if a
12159
+ // contact already exists for this (org, userId) pair. If it does, we skip
12160
+ // the upsert entirely (the backend's "upsert" currently calls
12161
+ // prisma.contact.create() unconditionally and 400s on the unique
12162
+ // constraint, so we must not call it for returning users). Only when no
12163
+ // contact exists do we create one via publicUpsert.
12158
12164
  const syncContact = React.useCallback(async (authUser, customFields) => {
12159
12165
  if (!collectionId || !shouldSyncContacts)
12160
12166
  return null;
12167
+ // 1. Look up existing contact via the "me" endpoint.
12168
+ try {
12169
+ const myContactResponse = await smartlinks__namespace.contact.publicGetMine(collectionId);
12170
+ const existing = myContactResponse?.contact;
12171
+ if (existing?.contactId) {
12172
+ if (!proxyMode) {
12173
+ await tokenStorage.saveContactId(existing.contactId);
12174
+ }
12175
+ setContact(existing);
12176
+ setContactId(existing.contactId);
12177
+ notifyAuthStateChange('CONTACT_SYNCED', authUser, token, accountData, accountInfo, isVerified, existing, existing.contactId);
12178
+ return existing.contactId;
12179
+ }
12180
+ }
12181
+ catch (lookupErr) {
12182
+ // Non-fatal — fall through to create.
12183
+ }
12184
+ // 2. No existing contact — create one.
12161
12185
  try {
12162
12186
  const result = await smartlinks__namespace.contact.publicUpsert(collectionId, {
12163
12187
  userId: authUser.uid,
@@ -14791,18 +14815,40 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
14791
14815
  try {
14792
14816
  // No ensureAccount and no phone number — the backend creates/links the
14793
14817
  // account from the inbound WhatsApp webhook (sender's number is the proof).
14794
- // Resolve reply config: per-call prop wins, otherwise fall back to AuthKit default.
14795
- const replyConfig = whatsappReply ?? config?.whatsappReply;
14818
+ // Resolve reply config: per-call prop wins, then AuthKit config, then a
14819
+ // sensible built-in default so the post-verification WhatsApp reply ALWAYS
14820
+ // carries a "Continue" CTA back to the app with the resume token in the URL.
14821
+ // Without this, a user verifying in WhatsApp on one device/browser can't
14822
+ // resume the session by tapping back into our app from the chat thread.
14796
14823
  const effectiveRedirectUrl = getRedirectUrl();
14797
14824
  const resumableRedirectUrl = appendWhatsAppResumeParams(effectiveRedirectUrl, '{{token}}');
14825
+ const friendlyClientName = (clientName && clientName.trim()) || 'this app';
14826
+ const defaultReplyConfig = {
14827
+ enabled: true,
14828
+ text: `You're verified! Tap below to continue signing in to ${friendlyClientName}.`,
14829
+ cta: {
14830
+ body: `You're verified! Tap below to continue signing in to ${friendlyClientName}.`,
14831
+ buttonLabel: 'Continue',
14832
+ buttonUrl: '{{returnUrl}}',
14833
+ },
14834
+ };
14835
+ const replyConfig = whatsappReply ??
14836
+ config?.whatsappReply ??
14837
+ defaultReplyConfig;
14798
14838
  const reply = buildWhatsAppReply(replyConfig, {
14799
14839
  name: displayName,
14800
14840
  clientName,
14801
14841
  returnUrl: resumableRedirectUrl,
14802
14842
  clientId,
14803
14843
  });
14804
- // Resolve outbound prefill message: per-call prop wins, then AuthKit default.
14805
- const prefillMessage = whatsappPrefillMessage ?? config?.whatsappPrefillMessage;
14844
+ // Resolve outbound prefill message: per-call prop wins, then AuthKit config,
14845
+ // then a sensible built-in default so the wa.me body is never blank and always
14846
+ // carries the verification token (the SDK appends {{token}} if missing, but we
14847
+ // also want friendly human context for the recipient).
14848
+ const defaultPrefill = `Hi! I'd like to sign in to ${friendlyClientName}. Code: {{token}}`;
14849
+ const prefillMessage = whatsappPrefillMessage ??
14850
+ config?.whatsappPrefillMessage ??
14851
+ defaultPrefill;
14806
14852
  // Pass collected signup details as contactData so the backend creates the
14807
14853
  // contact with name/customFields already attached and returns a fully
14808
14854
  // formed session.user — no follow-up updateProfile call needed.
@@ -14924,9 +14970,12 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
14924
14970
  color: config?.branding?.inheritHostStyles
14925
14971
  ? 'hsl(var(--foreground, 215 25% 15%))'
14926
14972
  : (resolvedTheme === 'dark' ? '#f1f5f9' : 'inherit')
14927
- }, children: successMessage?.includes('verified') ? 'Email Verified!' :
14928
- successMessage?.includes('Magic link') ? 'Check Your Email!' :
14929
- mode === 'register' ? 'Account Created!' : 'Login Successful!' }), jsxRuntime.jsx("p", { style: {
14973
+ }, children: successMessage?.includes('WhatsApp') ? 'WhatsApp Verified!' :
14974
+ successMessage?.includes('Phone') ? 'Phone Verified!' :
14975
+ successMessage?.includes('Email verified') || successMessage?.includes('email verified') ? 'Email Verified!' :
14976
+ successMessage?.includes('Magic link') ? 'Check Your Email!' :
14977
+ successMessage?.includes('verified') ? 'Verified!' :
14978
+ mode === 'register' ? 'Account Created!' : 'Login Successful!' }), jsxRuntime.jsx("p", { style: {
14930
14979
  color: config?.branding?.inheritHostStyles
14931
14980
  ? 'hsl(var(--muted-foreground, 215 15% 45%))'
14932
14981
  : (resolvedTheme === 'dark' ? '#94a3b8' : '#6B7280'),