fix: Correct Top8 fidget username handling and profile registration#1762
fix: Correct Top8 fidget username handling and profile registration#1762Jhonattan2121 wants to merge 16 commits intocanaryfrom
Conversation
…n queue management
… required for signup
…actor related logic
…and adjust profile editability checks.
…e yarn install state.
- Remove default fallback to 'nounspacetom' in Top8 fidget to avoid incorrect user data. - Pass correct username during initial profile space registration to ensure Top8 works dynamically.
📝 WalkthroughWalkthroughReplaces an authenticator-first Farcaster flow with wallet-address-based FID inference, on-demand signer/authenticator initialization, a new /api/fid-link/infer endpoint, readiness polling utilities, relaxed fidRegistrations DB typings, signer lifecycle APIs, UI/fidget updates, and related provider/store changes. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Client as Client App
participant Store as App/Farcaster Store
participant API as /api/fid-link/infer
participant DB as Supabase
participant Neynar as Neynar API
User->>Client: Connect wallet / open app
Client->>Store: inferFidForCurrentIdentity(walletAddress)
Store->>API: POST {identityPublicKey,walletAddress,timestamp,signature}
API->>DB: Verify identityPublicKey ↔ walletAddress
alt NEYNAR_API_KEY present
API->>Neynar: Lookup fid by wallet address
Neynar-->>API: fid or null
end
alt fid found
API->>DB: Upsert/insert fidRegistrations (preserve signer fields)
API-->>Store: {fid,identityPublicKey,created,inferredFromAddress}
Store-->>Client: use inferred fid
else
API-->>Store: null
Store-->>Client: null (client may fall back to authenticator flow)
end
sequenceDiagram
autonumber
actor User
participant Client as Client App
participant AuthMgr as AuthenticatorManager
participant Modal as LoginModal
participant FarAuth as Farcaster Authenticator
User->>Client: Trigger action needing signer
Client->>AuthMgr: ensure Farcaster authenticator installed/initialized
AuthMgr->>AuthMgr: install/init (functional queue + readiness)
AuthMgr->>Modal: open initializer modal (if needed)
Modal->>FarAuth: run initializer
FarAuth-->>AuthMgr: mark initialized
AuthMgr-->>Client: ready (waitForAuthenticatorReady true)
Client->>FarAuth: getAccountFid / create signer
FarAuth-->>Client: signer + fid
Client-->>User: proceed with action
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (9)
src/pages/api/space/identities.ts (1)
109-116:⚠️ Potential issue | 🟠 MajorValidate wallet address before using
.ilike
.ilikeuses SQL‐style pattern matching (%and_are wildcards). Sincequery.addressis unvalidated, a value containing%or_could match multiple records. Enforce the expected 0x-prefixed hex format (or escape wildcards) before querying.🔧 Suggested fix
if (query.address) { const address = Array.isArray(query.address) ? query.address[0] : query.address; + if (!/^0x[0-9a-fA-F]{40}$/.test(address)) { + return res.status(400).json({ + result: "error", + error: { message: "Invalid wallet address" }, + }); + } const supabase = createSupabaseServerClient(); const { data, error } = await supabase .from("walletIdentities") .select() // wallet addresses may be checksummed; match case-insensitively .ilike("walletAddress", address);src/fidgets/farcaster/index.tsx (1)
56-139:⚠️ Potential issue | 🟠 MajorHonor
authenticatorNamethroughout the hook.
ensureSigner, the installed check, and FID lookups are hard‑coded toFARCASTER_AUTHENTICATOR_NAME, so passing a non‑defaultauthenticatorNamewon’t work. Use the parameter consistently.🔧 Suggested fix
- const { setModalOpen, isAuthenticatorInstalled } = useAppStore((state) => ({ - setModalOpen: state.setup.setModalOpen, - isAuthenticatorInstalled: !!state.account.authenticatorConfig[FARCASTER_AUTHENTICATOR_NAME], - })); + const { setModalOpen, authenticatorConfig } = useAppStore((state) => ({ + setModalOpen: state.setup.setModalOpen, + authenticatorConfig: state.account.authenticatorConfig, + })); + const isAuthenticatorInstalled = !!authenticatorConfig[authenticatorName]; @@ - if (!isAuthenticatorInstalled) { - await authenticatorManager.installAuthenticators([FARCASTER_AUTHENTICATOR_NAME]); - authenticatorManager.initializeAuthenticators([FARCASTER_AUTHENTICATOR_NAME]); - } else { - authenticatorManager.initializeAuthenticators([FARCASTER_AUTHENTICATOR_NAME]); - } + if (!isAuthenticatorInstalled) { + await authenticatorManager.installAuthenticators([authenticatorName]); + authenticatorManager.initializeAuthenticators([authenticatorName]); + } else { + authenticatorManager.initializeAuthenticators([authenticatorName]); + } @@ - const ready = await waitForAuthenticatorReady(authenticatorManager, FARCASTER_AUTHENTICATOR_NAME); + const ready = await waitForAuthenticatorReady(authenticatorManager, authenticatorName); @@ - .then((initilizedAuths) => setHasSigner(indexOf(initilizedAuths, FARCASTER_AUTHENTICATOR_NAME) !== -1)); - }, [authenticatorManager.lastUpdatedAt]); + .then((initilizedAuths) => setHasSigner(indexOf(initilizedAuths, authenticatorName) !== -1)); + }, [authenticatorManager.lastUpdatedAt, authenticatorName]); @@ - authenticatorId: FARCASTER_AUTHENTICATOR_NAME, + authenticatorId: authenticatorName, @@ - }, [authenticatorManager.lastUpdatedAt, hasSigner, authenticatorManager, fidgetId, currentFid]); + }, [authenticatorManager.lastUpdatedAt, hasSigner, authenticatorManager, fidgetId, currentFid, authenticatorName]);src/authenticators/AuthenticatorManager.tsx (2)
249-305:⚠️ Potential issue | 🟠 MajorUse a functional update when completing the initializer queue.
completeInstallingCurrentInitializercapturesinitializationQueuefrom its closure, which can become stale. When the function is passed toCurrentInitializerComponentand called asynchronously, it may reference an outdated queue value, potentially dropping items if the queue changed since the component was rendered.🔧 Suggested fix
function completeInstallingCurrentInitializer() { - setInitializationQueue(tail(initializationQueue)); + setInitializationQueue((queue) => tail(queue)); setCurrentInitializer(undefined); }
165-272:⚠️ Potential issue | 🟠 MajorInclude
modalOpen,currentStep, andsetModalOpenin the memo dependencies to prevent stale gating logic.
callMethodevaluates!modalOpen && !isLookup && currentStep === SetupStep.DONEto conditionally callsetModalOpen, but the memoizedauthenticatorManagercaptures these values only once at creation. When setup state changes, the closure continues using stale values, causing the modal auto-open guard to behave incorrectly during setup transitions.🔧 Suggested fix
- [ - authenticatorConfig, - installedAuthenticators, - currentInitializer, - identityPublicKey, - walletAddress, - ], + [ + authenticatorConfig, + installedAuthenticators, + currentInitializer, + identityPublicKey, + walletAddress, + modalOpen, + currentStep, + setModalOpen, + ],src/pages/api/fid-link.ts (2)
41-82:⚠️ Potential issue | 🟠 MajorDistinguish Neynar lookup failures from invalid keys to enable proper retries.
Catching and suppressing the lookup error masks transient failures as invalid keys (400), blocking legitimate requests during Neynar outages. The codebase pattern (seen in
publishMessage.tsandgetFids.ts) is to propagate service errors as 5xx responses. Refactor to throw fromcheckSigningKeyValidForFid()on lookup failure, wrap the call at line 74 in try-catch, and return 503 Service Unavailable for lookup failures while preserving 400 for genuinely invalid keys.🔧 Suggested adjustment
async function checkSigningKeyValidForFid(fid: number, signingKey: string) { try { - const result = await neynar.lookupDeveloperManagedSigner({publicKey: signingKey}); + const result = await neynar.lookupDeveloperManagedSigner({ publicKey: signingKey }); return result.fid === fid && result.status === "approved"; - } catch { - return false; + } catch (err) { + throw new Error("signer_lookup_failed"); } } @@ - if (!(await checkSigningKeyValidForFid(reqBody.fid, reqBody.signingPublicKey))) { - res.status(400).json({ - result: "error", - error: { - message: `Signing key ${reqBody.signingPublicKey} is not valid for fid ${reqBody.fid}`, - }, - }); - return; - } + let isSigningKeyValid: boolean; + try { + isSigningKeyValid = await checkSigningKeyValidForFid( + reqBody.fid, + reqBody.signingPublicKey, + ); + } catch { + res.status(503).json({ + result: "error", + error: { message: "Signer lookup failed. Please retry." }, + }); + return; + } + if (!isSigningKeyValid) { + res.status(400).json({ + result: "error", + error: { + message: `Signing key ${reqBody.signingPublicKey} is not valid for fid ${reqBody.fid}`, + }, + }); + return; + }
173-177:⚠️ Potential issue | 🟠 MajorAdd filtering by
isSigningKeyValidor include validation status in response.This query returns all FIDs regardless of signature validity. The
/inferendpoint intentionally creates records withisSigningKeyValid: falseto enable space editing without a validated signing key, but Farcaster Write Actions should fail. The existing TODO comment ("// TO DO: Refresh that these signatures are valid") confirms this is incomplete work.Either:
- Filter the query to include only
.eq("isSigningKeyValid", true)if all callers require validated FIDs, or- Include
isSigningKeyValidin the response so callers can distinguish between validated and unvalidated FIDs and act accordinglyCurrently, downstream callers like
getFidsForCurrentIdentity()receive the mixed list without context on which FIDs are validated.src/fidgets/farcaster/Top8.tsx (1)
23-33:⚠️ Potential issue | 🟠 MajorFallback username is still applied when settings.username is missing.
usernamestill defaults to"nounspacetom"(and the field default remains the same), so missing settings will continue to render the default profile. The trimmed/empty check only helps when the user explicitly clears the field.🛠️ Suggested fix to avoid default fallback
fields: [ { fieldName: "username", displayName: "Farcaster Username", - default: "nounspacetom", + default: "", required: true, inputSelector: (props) => ( <WithMargin> <TextInput {...props} /> </WithMargin> ), group: "settings", },const { - username = "nounspacetom", + username = "", size = 0.6, background, fidgetBorderColor, fidgetBorderWidth, fidgetShadow, } = settings;Also applies to: 54-68
src/fidgets/farcaster/components/CreateCast.tsx (1)
415-439:⚠️ Potential issue | 🟡 MinorAvoid prompting signer flow before draft validation.
getOrCreateSigner()runs before the empty-draft and mention-resolution checks, which can open the Farcaster modal even when submission should be rejected. Consider validating first, then acquiring a signer.Proposed fix
- const activeSigner = signer ?? (await getOrCreateSigner()); - if ((!draft?.text && !draft?.embeds?.length) || !activeSigner) { + if (!draft?.text && !draft?.embeds?.length) { console.error( - "Submission failed: Missing text or embeds, or signer is undefined.", + "Submission failed: Missing text or embeds.", { draftText: draft?.text, draftEmbedsLength: draft?.embeds?.length, - signerUndefined: !activeSigner, }, ); return false; } @@ - try { - const result = await publishPost(draft, fid, activeSigner); + const activeSigner = signer ?? (await getOrCreateSigner()); + if (!activeSigner) { + console.error("Submission failed: signer is undefined."); + return false; + } + try { + const result = await publishPost(draft, fid, activeSigner);src/common/data/stores/app/space/spaceStore.ts (1)
247-255:⚠️ Potential issue | 🟡 MinorGuard
localSpaces[spaceId]before accessing.tabs.
Add a null check forget().space.localSpaces[spaceId]to avoid dereferencing undefined.Proposed fix
- const localCopy = cloneDeep( - get().space.localSpaces[spaceId].tabs[tabName], - ); - if (!localCopy) { + const localSpace = get().space.localSpaces[spaceId]; + const localCopy = localSpace ? cloneDeep(localSpace.tabs[tabName]) : undefined; + if (!localCopy) { console.warn(`Tab ${tabName} not found in localSpaces for space ${spaceId}`); return; }
🤖 Fix all issues with AI agents
In `@src/app/`(spaces)/PublicSpace.tsx:
- Around line 254-259: The registration path can call registerSpaceFid with an
empty space name; update the isProfileSpace branch to guard that
spacePageData.spaceName is non-empty (truthy) before calling registerSpaceFid
(and also ensure currentUserFid is present), otherwise skip/delay registration
until the profile name is available; reference the isProfileSpace check,
spacePageData.spaceName, spacePageData.spacePageUrl, currentUserFid, and
registerSpaceFid when making the change.
In
`@src/authenticators/farcaster/signers/NounspaceManagedSignerAuthenticator.tsx`:
- Around line 228-240: The devSignin flow calls saveData(...) and immediately
calls done(), which can complete initialization before the async saveData
finishes; change devSignin to await the async saveData call (i.e., make
devSignin async if not already) and only call done() after saveData resolves
(handle/rethrow or log errors from saveData to preserve behavior). Ensure you
update the devSignin function signature and await saveData(...) before invoking
done().
In `@src/pages/api/fid-link/infer.ts`:
- Around line 194-212: The insert into fidRegistrations using
supabase.from("fidRegistrations").insert(...) can fail with duplicate-key errors
on concurrent/retried calls; update the handler so that when insertError
indicates a unique-constraint/duplicate-key violation you treat it as success:
catch that specific error from the insert call (check insertError.code/message),
then re-query fidRegistrations for the existing row by inferredFid (and
identityPublicKey) to retrieve the record, and return that record in the same
successful response path instead of returning a 500; keep the original error
handling for other error types.
| } else if (isProfileSpace(spacePageData) && !isNil(currentUserFid)) { | ||
| newSpaceId = await registerSpaceFid( | ||
| currentUserFid, | ||
| spacePageData.defaultTab, | ||
| spacePageData.spaceName, | ||
| spacePageData.spacePageUrl(spacePageData.defaultTab), | ||
| ); |
There was a problem hiding this comment.
Guard against missing profile space names.
If spacePageData.spaceName can be empty while the profile data loads, registration will persist an empty name. Consider delaying registration until it’s available.
Proposed fix
- } else if (isProfileSpace(spacePageData) && !isNil(currentUserFid)) {
- newSpaceId = await registerSpaceFid(
- currentUserFid,
- spacePageData.spaceName,
- spacePageData.spacePageUrl(spacePageData.defaultTab),
- );
- } else if (isChannelSpace(spacePageData) && !isNil(currentUserFid)) {
+ } else if (isProfileSpace(spacePageData) && !isNil(currentUserFid)) {
+ if (!spacePageData.spaceName) {
+ console.warn("Profile space name not resolved yet; delaying registration.");
+ return;
+ }
+ newSpaceId = await registerSpaceFid(
+ currentUserFid,
+ spacePageData.spaceName,
+ spacePageData.spacePageUrl(spacePageData.defaultTab),
+ );
+ } else if (isChannelSpace(spacePageData) && !isNil(currentUserFid)) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } else if (isProfileSpace(spacePageData) && !isNil(currentUserFid)) { | |
| newSpaceId = await registerSpaceFid( | |
| currentUserFid, | |
| spacePageData.defaultTab, | |
| spacePageData.spaceName, | |
| spacePageData.spacePageUrl(spacePageData.defaultTab), | |
| ); | |
| } else if (isProfileSpace(spacePageData) && !isNil(currentUserFid)) { | |
| if (!spacePageData.spaceName) { | |
| console.warn("Profile space name not resolved yet; delaying registration."); | |
| return; | |
| } | |
| newSpaceId = await registerSpaceFid( | |
| currentUserFid, | |
| spacePageData.spaceName, | |
| spacePageData.spacePageUrl(spacePageData.defaultTab), | |
| ); | |
| } else if (isChannelSpace(spacePageData) && !isNil(currentUserFid)) { |
🤖 Prompt for AI Agents
In `@src/app/`(spaces)/PublicSpace.tsx around lines 254 - 259, The registration
path can call registerSpaceFid with an empty space name; update the
isProfileSpace branch to guard that spacePageData.spaceName is non-empty
(truthy) before calling registerSpaceFid (and also ensure currentUserFid is
present), otherwise skip/delay registration until the profile name is available;
reference the isProfileSpace check, spacePageData.spaceName,
spacePageData.spacePageUrl, currentUserFid, and registerSpaceFid when making the
change.
| function devSignin() { | ||
| // In development, generate test signing keys so Quick Auth can work | ||
| // These are random keys - not linked to a real Farcaster account | ||
| const newPrivKey = ed25519.utils.randomPrivateKey(); | ||
| const publicKeyHex = `0x${bytesToHex(ed25519.getPublicKey(newPrivKey))}`; | ||
| const privateKeyHex = `0x${bytesToHex(newPrivKey)}`; | ||
|
|
||
| saveData({ | ||
| ...data, | ||
| status: "completed", | ||
| accountFid: Number(devFid), | ||
| accountType: "signer", // Use signer type so keys are available | ||
| publicKeyHex: publicKeyHex, | ||
| privateKeyHex: privateKeyHex, | ||
| accountType: "account", | ||
| publicKeyHex, | ||
| privateKeyHex: `0x${bytesToHex(newPrivKey)}`, | ||
| }); | ||
| done(); | ||
| } |
There was a problem hiding this comment.
Await saveData before calling done().
saveData is async in other paths; calling done() immediately can mark initialization complete before keys persist.
✅ Suggested fix
- function devSignin() {
+ async function devSignin() {
const newPrivKey = ed25519.utils.randomPrivateKey();
const publicKeyHex = `0x${bytesToHex(ed25519.getPublicKey(newPrivKey))}`;
- saveData({
+ await saveData({
...data,
status: "completed",
accountFid: Number(devFid),
accountType: "account",
publicKeyHex,
privateKeyHex: `0x${bytesToHex(newPrivKey)}`,
});
done();
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function devSignin() { | |
| // In development, generate test signing keys so Quick Auth can work | |
| // These are random keys - not linked to a real Farcaster account | |
| const newPrivKey = ed25519.utils.randomPrivateKey(); | |
| const publicKeyHex = `0x${bytesToHex(ed25519.getPublicKey(newPrivKey))}`; | |
| const privateKeyHex = `0x${bytesToHex(newPrivKey)}`; | |
| saveData({ | |
| ...data, | |
| status: "completed", | |
| accountFid: Number(devFid), | |
| accountType: "signer", // Use signer type so keys are available | |
| publicKeyHex: publicKeyHex, | |
| privateKeyHex: privateKeyHex, | |
| accountType: "account", | |
| publicKeyHex, | |
| privateKeyHex: `0x${bytesToHex(newPrivKey)}`, | |
| }); | |
| done(); | |
| } | |
| async function devSignin() { | |
| const newPrivKey = ed25519.utils.randomPrivateKey(); | |
| const publicKeyHex = `0x${bytesToHex(ed25519.getPublicKey(newPrivKey))}`; | |
| await saveData({ | |
| ...data, | |
| status: "completed", | |
| accountFid: Number(devFid), | |
| accountType: "account", | |
| publicKeyHex, | |
| privateKeyHex: `0x${bytesToHex(newPrivKey)}`, | |
| }); | |
| done(); | |
| } |
🤖 Prompt for AI Agents
In `@src/authenticators/farcaster/signers/NounspaceManagedSignerAuthenticator.tsx`
around lines 228 - 240, The devSignin flow calls saveData(...) and immediately
calls done(), which can complete initialization before the async saveData
finishes; change devSignin to await the async saveData call (i.e., make
devSignin async if not already) and only call done() after saveData resolves
(handle/rethrow or log errors from saveData to preserve behavior). Ensure you
update the devSignin function signature and await saveData(...) before invoking
done().
| // Persist the inferred link even if no Farcaster Signer is present. | ||
| // This allows the server to recognize the Identity Key as a valid owner of the Space (linked to the FID), | ||
| // enabling Space Editing to proceed. Farcaster Write Actions (Casting) will still fail or prompt for a signer | ||
| // because isSigningKeyValid is false. | ||
| const { error: insertError } = await supabase | ||
| .from("fidRegistrations") | ||
| .insert({ | ||
| fid: inferredFid, | ||
| identityPublicKey: body.identityPublicKey, | ||
| created: serverNow, | ||
| isSigningKeyValid: false, | ||
| signingPublicKey: null, | ||
| signature: null, | ||
| signingKeyLastValidatedAt: null, | ||
| }); | ||
|
|
||
| if (insertError) { | ||
| return res.status(500).json({ result: "error", error: { message: insertError.message } }); | ||
| } |
There was a problem hiding this comment.
Make the insert path idempotent to avoid duplicate-key 500s.
Concurrent/retried calls for the same FID can cause the insert to fail and block signup. Consider treating duplicate inserts as success by re-reading the existing row and returning it.
🛠️ Suggested handling for duplicate insert retries
const { error: insertError } = await supabase
.from("fidRegistrations")
.insert({
fid: inferredFid,
identityPublicKey: body.identityPublicKey,
created: serverNow,
isSigningKeyValid: false,
signingPublicKey: null,
signature: null,
signingKeyLastValidatedAt: null,
});
if (insertError) {
- return res.status(500).json({ result: "error", error: { message: insertError.message } });
+ const { data: existingAfter, error: refetchError } = await supabase
+ .from("fidRegistrations")
+ .select("fid, created, identityPublicKey")
+ .eq("fid", inferredFid)
+ .maybeSingle();
+ if (!refetchError && existingAfter) {
+ return res.status(200).json({
+ result: "success",
+ value: {
+ fid: existingAfter.fid,
+ identityPublicKey: existingAfter.identityPublicKey,
+ created: existingAfter.created,
+ inferredFromAddress: walletAddress,
+ },
+ });
+ }
+ return res.status(500).json({ result: "error", error: { message: insertError.message } });
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Persist the inferred link even if no Farcaster Signer is present. | |
| // This allows the server to recognize the Identity Key as a valid owner of the Space (linked to the FID), | |
| // enabling Space Editing to proceed. Farcaster Write Actions (Casting) will still fail or prompt for a signer | |
| // because isSigningKeyValid is false. | |
| const { error: insertError } = await supabase | |
| .from("fidRegistrations") | |
| .insert({ | |
| fid: inferredFid, | |
| identityPublicKey: body.identityPublicKey, | |
| created: serverNow, | |
| isSigningKeyValid: false, | |
| signingPublicKey: null, | |
| signature: null, | |
| signingKeyLastValidatedAt: null, | |
| }); | |
| if (insertError) { | |
| return res.status(500).json({ result: "error", error: { message: insertError.message } }); | |
| } | |
| // Persist the inferred link even if no Farcaster Signer is present. | |
| // This allows the server to recognize the Identity Key as a valid owner of the Space (linked to the FID), | |
| // enabling Space Editing to proceed. Farcaster Write Actions (Casting) will still fail or prompt for a signer | |
| // because isSigningKeyValid is false. | |
| const { error: insertError } = await supabase | |
| .from("fidRegistrations") | |
| .insert({ | |
| fid: inferredFid, | |
| identityPublicKey: body.identityPublicKey, | |
| created: serverNow, | |
| isSigningKeyValid: false, | |
| signingPublicKey: null, | |
| signature: null, | |
| signingKeyLastValidatedAt: null, | |
| }); | |
| if (insertError) { | |
| const { data: existingAfter, error: refetchError } = await supabase | |
| .from("fidRegistrations") | |
| .select("fid, created, identityPublicKey") | |
| .eq("fid", inferredFid) | |
| .maybeSingle(); | |
| if (!refetchError && existingAfter) { | |
| return res.status(200).json({ | |
| result: "success", | |
| value: { | |
| fid: existingAfter.fid, | |
| identityPublicKey: existingAfter.identityPublicKey, | |
| created: existingAfter.created, | |
| inferredFromAddress: walletAddress, | |
| }, | |
| }); | |
| } | |
| return res.status(500).json({ result: "error", error: { message: insertError.message } }); | |
| } |
🤖 Prompt for AI Agents
In `@src/pages/api/fid-link/infer.ts` around lines 194 - 212, The insert into
fidRegistrations using supabase.from("fidRegistrations").insert(...) can fail
with duplicate-key errors on concurrent/retried calls; update the handler so
that when insertError indicates a unique-constraint/duplicate-key violation you
treat it as success: catch that specific error from the insert call (check
insertError.code/message), then re-query fidRegistrations for the existing row
by inferredFid (and identityPublicKey) to retrieve the record, and return that
record in the same successful response path instead of returning a 500; keep the
original error handling for other error types.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
… Top8 by default when no specific username is provided.
…user and introduce a `disableSettingsBackfill` property for fidgets.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/fidgets/farcaster/Top8.tsx`:
- Around line 111-115: The effectiveUsername computation still includes the
hardcoded "nounspacetom" and redundant branches; update the logic in the
effectiveUsername assignment to remove the hardcoded fallback and redundant
checks so it yields either normalizedCurrentUsername when shouldUseLoggedIn is
true, or normalizedSettingsUsername otherwise, and default to an empty string if
neither exists (e.g., effectiveUsername = shouldUseLoggedIn ?
normalizedCurrentUsername || "" : normalizedSettingsUsername || ""); keep
iframeUrl's existing handling of empty usernames intact.
🧹 Nitpick comments (1)
src/fidgets/farcaster/Top8.tsx (1)
101-106: Consider extracting the username derivation logic to avoid duplication.Both
Top8UsernameInput(lines 30-35) and the mainTop8component (lines 101-106) make identicaluseCurrentFid()anduseLoadFarcasterUser()calls. While React Query's caching mitigates redundant network requests, extracting this into a custom hook would improve maintainability.Example custom hook
const useCurrentUsername = () => { const currentFid = useCurrentFid(); const { data } = useLoadFarcasterUser(currentFid ?? -1); return useMemo( () => data?.users?.[0]?.username?.trim(), [data], ); };
Overview
This PR fixes an issue where the Top8 fidget would default to "nounspacetom" for users who didn't have their username explicitly saved in the fidget configuration.
Changes
Why
Previously, new profile spaces were being created without the specific username in the config, causing the fidgets to fall back to the default account. This ensures new users get their own Top8 data correctly configured upon registration.
Summary by CodeRabbit
New Features
Bug Fixes
Style