From 21c75c8e545a514b6e8e0f182664836cc5b78662 Mon Sep 17 00:00:00 2001 From: Jhonattan2121 Date: Sun, 1 Feb 2026 00:08:17 -0300 Subject: [PATCH 1/3] feat(portfolio): auto-fill logged user and keep editable defaults --- .../organisms/FidgetSettingsEditor.tsx | 26 ++- src/common/fidgets/FidgetWrapper.tsx | 64 +++++++- src/fidgets/token/Portfolio.tsx | 151 +++++++++++++++++- src/fidgets/token/PortfolioUsernameInput.tsx | 65 ++++++++ 4 files changed, 285 insertions(+), 21 deletions(-) create mode 100644 src/fidgets/token/PortfolioUsernameInput.tsx diff --git a/src/common/components/organisms/FidgetSettingsEditor.tsx b/src/common/components/organisms/FidgetSettingsEditor.tsx index 3af96aba2..80b0833a9 100644 --- a/src/common/components/organisms/FidgetSettingsEditor.tsx +++ b/src/common/components/organisms/FidgetSettingsEditor.tsx @@ -208,9 +208,19 @@ export const FidgetSettingsEditor: React.FC = ({ // Subscribe directly to zustand for settings - eliminates prop lag const zustandSettings = useFidgetSettings(currentSpaceId, currentTabName, fidgetId); + const skipDefaults = useMemo(() => { + if (properties.fidgetName === "Portfolio") { + return ["farcasterUsername", "walletAddresses"]; + } + return undefined; + }, [properties.fidgetName]); + const normalizedSettings = useMemo( - () => fillWithDefaults(zustandSettings ?? settings, properties.fields), - [zustandSettings, settings, properties.fields], + () => + fillWithDefaults(zustandSettings ?? settings, properties.fields, { + skipDefaults, + }), + [zustandSettings, settings, properties.fields, skipDefaults], ); const [state, setState] = useState(normalizedSettings); @@ -219,18 +229,22 @@ export const FidgetSettingsEditor: React.FC = ({ // Update local state when zustand settings change useEffect(() => { if (zustandSettings) { - const normalized = fillWithDefaults(zustandSettings, properties.fields); + const normalized = fillWithDefaults(zustandSettings, properties.fields, { + skipDefaults, + }); setState(normalized); } - }, [zustandSettings, properties.fields]); + }, [zustandSettings, properties.fields, skipDefaults]); const saveWithValidation = useCallback( (nextState: FidgetSettings, shouldUnselect?: boolean) => { - const filledState = fillWithDefaults(nextState, properties.fields); + const filledState = fillWithDefaults(nextState, properties.fields, { + skipDefaults, + }); setState(filledState); onSave(filledState, shouldUnselect); }, - [properties.fields, onSave], + [properties.fields, onSave, skipDefaults], ); // Save handler for inline field changes (doesn't unselect editor) diff --git a/src/common/fidgets/FidgetWrapper.tsx b/src/common/fidgets/FidgetWrapper.tsx index e242de00e..9beb3ccec 100644 --- a/src/common/fidgets/FidgetWrapper.tsx +++ b/src/common/fidgets/FidgetWrapper.tsx @@ -43,6 +43,10 @@ export const getSettingsWithDefaults = ( settings: FidgetSettings, config: FidgetProperties, ): FidgetSettings => { + const skipDefaults = + config.fidgetName === "Portfolio" + ? new Set(["farcasterUsername", "walletAddresses"]) + : null; return reduce( config.fields, (acc, f) => { @@ -56,7 +60,11 @@ export const getSettingsWithDefaults = ( value !== null && (typeof value !== "string" || value.trim() !== ""); - acc[f.fieldName] = hasValue ? value : f.default ?? ""; + if (!hasValue && skipDefaults?.has(f.fieldName)) { + acc[f.fieldName] = value ?? ""; + } else { + acc[f.fieldName] = hasValue ? value : f.default ?? ""; + } return acc; }, {}, @@ -90,14 +98,38 @@ export function FidgetWrapper({ // Generic settings backfill: any fidget can use lastFetchSettings in config.data // to automatically backfill empty settings. This is useful when fidgets are created // from external sources (e.g., URL parameters) and need to populate settings. - const lastFetchSettings = (bundle.config?.data as { - lastFetchSettings?: Partial; - } | undefined)?.lastFetchSettings; + const dataConfig = bundle.config?.data as + | { + lastFetchSettings?: Partial; + disableBackfill?: boolean; + backfillOverrideKeys?: string[]; + } + | undefined; + const lastFetchSettings = dataConfig?.lastFetchSettings; + const disableBackfill = dataConfig?.disableBackfill === true; + const backfillOverrideKeys = dataConfig?.backfillOverrideKeys ?? []; + + const defaultSettingsMap = useMemo( + () => + reduce( + bundle.properties.fields, + (acc, field) => { + acc[field.fieldName] = field.default; + return acc; + }, + {} as Record, + ), + [bundle.properties.fields], + ); + const noDefaultOverride = + bundle.properties.fidgetName === "Portfolio" + ? new Set(["farcasterUsername", "walletAddresses"]) + : null; const derivedSettings = useMemo(() => { // Use zustand settings directly (they're already the latest), fall back to props const baseSettings = (zustandSettings ?? bundle.config.settings ?? {}) as FidgetSettings; - if (!lastFetchSettings || typeof lastFetchSettings !== "object") { + if (disableBackfill || !lastFetchSettings || typeof lastFetchSettings !== "object") { return baseSettings; } @@ -107,8 +139,19 @@ export function FidgetWrapper({ // Helper to only fill empty settings const setValue = (key: string, value: unknown) => { const current = nextSettings[key]; + const defaultValue = defaultSettingsMap[key]; + const isDefaultValue = + !noDefaultOverride?.has(key) && isEqual(current, defaultValue); + const forceOverride = backfillOverrideKeys.includes(key); + const isEmpty = + current === undefined || + current === null || + current === "" || + isDefaultValue || + forceOverride; + // Don't overwrite existing non-empty values - if (current !== undefined && current !== null && current !== "") { + if (!isEmpty) { return; } @@ -132,7 +175,13 @@ export function FidgetWrapper({ }); return changed ? nextSettings : baseSettings; - }, [zustandSettings, bundle.config.settings, lastFetchSettings]); + }, [ + zustandSettings, + bundle.config.settings, + lastFetchSettings, + defaultSettingsMap, + disableBackfill, + ]); const settingsWithDefaults = useMemo( () => getSettingsWithDefaults(derivedSettings, bundle.properties), @@ -140,6 +189,7 @@ export function FidgetWrapper({ ); const shouldAttemptBackfill = + !disableBackfill && !!lastFetchSettings && !isEqual(derivedSettings, bundle.config.settings ?? {}); diff --git a/src/fidgets/token/Portfolio.tsx b/src/fidgets/token/Portfolio.tsx index 113c4185e..d086eadad 100644 --- a/src/fidgets/token/Portfolio.tsx +++ b/src/fidgets/token/Portfolio.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect, useMemo } from "react"; import TextInput from "@/common/components/molecules/TextInput"; import SettingsSelector from "@/common/components/molecules/SettingsSelector"; import { @@ -9,6 +9,14 @@ import { } from "@/common/fidgets"; import { defaultStyleFields, WithMargin } from "@/fidgets/helpers"; import { GiTwoCoins } from "react-icons/gi"; +import useCurrentFid from "@/common/lib/hooks/useCurrentFid"; +import { useLoadFarcasterUser } from "@/common/data/queries/farcaster"; +import { useNeynarUser } from "@/common/lib/hooks/useNeynarUser"; +import { useFarcasterSigner } from "@/fidgets/farcaster"; +import PortfolioUsernameInput, { + getPortfolioPrimaryAddress, +} from "./PortfolioUsernameInput"; + export type PortfolioFidgetSettings = { trackType: "farcaster" | "address"; @@ -54,10 +62,7 @@ const portfolioProperties: FidgetProperties = { disabledIf: (settings) => settings.trackType !== "farcaster", inputSelector: (props) => ( - + ), group: "settings", @@ -90,6 +95,8 @@ const portfolioProperties: FidgetProperties = { const Portfolio: React.FC> = ({ settings, + data, + saveData, }) => { const { trackType, @@ -100,12 +107,141 @@ const Portfolio: React.FC> = ({ fidgetShadow, } = settings; + const currentFid = useCurrentFid(); + const farcasterSigner = useFarcasterSigner("portfolio"); + const effectiveFid = (currentFid ?? farcasterSigner.fid) ?? -1; + const { data: currentUserData } = useLoadFarcasterUser( + effectiveFid, + effectiveFid > 0 ? effectiveFid : undefined, + ); + const loggedInUsername = useMemo(() => { + const username = currentUserData?.users?.[0]?.username; + return typeof username === "string" ? username.trim() : ""; + }, [currentUserData]); + + const normalizedUsername = useMemo( + () => (farcasterUsername || "").trim().replace(/^@/, ""), + [farcasterUsername], + ); + + const effectiveUsername = (normalizedUsername || loggedInUsername || "").toLowerCase(); + const { user: effectiveUser } = useNeynarUser( + effectiveUsername ? effectiveUsername : undefined, + ); + + const derivedAddresses = useMemo( + () => getPortfolioPrimaryAddress(effectiveUser), + [effectiveUser], + ); + + const resolvedFarcasterUsername = useMemo(() => { + const normalized = (farcasterUsername || "").trim().replace(/^@/, ""); + return normalized; + }, [farcasterUsername]); + + const resolvedWalletAddresses = useMemo(() => { + const normalized = (walletAddresses || "").trim(); + return normalized; + }, [walletAddresses]); + + useEffect(() => { + const isDefaultUsername = !normalizedUsername; + if (!isDefaultUsername || !loggedInUsername) return; + + const previous = + (data as { lastFetchSettings?: { farcasterUsername?: string } } | undefined) + ?.lastFetchSettings?.farcasterUsername; + + if (previous === loggedInUsername) return; + + void saveData({ + ...(data || {}), + lastFetchSettings: { + ...(data as { lastFetchSettings?: Record } | undefined) + ?.lastFetchSettings, + farcasterUsername: loggedInUsername, + }, + }); + }, [normalizedUsername, loggedInUsername, data, saveData]); + + useEffect(() => { + if (!effectiveUsername || !derivedAddresses) return; + + const addressInput = (walletAddresses || "").trim(); + const meta = data as + | { + autoAddressUsername?: string; + autoAddressValue?: string; + backfillOverrideKeys?: string[]; + } + | undefined; + const addressWasAuto = meta?.autoAddressValue + ? addressInput === meta.autoAddressValue + : false; + const usernameChanged = meta?.autoAddressUsername !== effectiveUsername; + const shouldAuto = !addressInput || addressWasAuto || usernameChanged; + + if (!shouldAuto) return; + + if (addressInput === derivedAddresses) return; + + void saveData({ + ...(data || {}), + autoAddressUsername: effectiveUsername, + autoAddressValue: derivedAddresses, + backfillOverrideKeys: Array.from( + new Set([...(meta?.backfillOverrideKeys ?? []), "walletAddresses"]), + ), + lastFetchSettings: { + ...(data as { lastFetchSettings?: Record } | undefined) + ?.lastFetchSettings, + walletAddresses: derivedAddresses, + }, + }); + }, [effectiveUsername, derivedAddresses, walletAddresses, data, saveData]); + + useEffect(() => { + const addressInput = (walletAddresses || "").trim(); + const meta = data as + | { + autoAddressUsername?: string; + autoAddressValue?: string; + lastFetchSettings?: Record; + backfillOverrideKeys?: string[]; + } + | undefined; + + if (!meta?.autoAddressValue) return; + + if (!addressInput || addressInput === meta.autoAddressValue) return; + + const { lastFetchSettings, ...rest } = meta; + if (!lastFetchSettings || !("walletAddresses" in lastFetchSettings)) return; + + const { walletAddresses: _ignored, ...nextLastFetch } = lastFetchSettings; + const nextOverrideKeys = (meta?.backfillOverrideKeys ?? []).filter( + (key) => key !== "walletAddresses", + ); + void saveData({ + ...(data || {}), + ...rest, + autoAddressUsername: undefined, + autoAddressValue: undefined, + backfillOverrideKeys: nextOverrideKeys.length > 0 ? nextOverrideKeys : undefined, + lastFetchSettings: nextLastFetch, + }); + }, [walletAddresses, data, saveData]); + const baseUrl = "https://balance-fidget.replit.app"; const url = trackType === "address" - ? `${baseUrl}/portfolio/${encodeURIComponent(walletAddresses)}` + ? resolvedWalletAddresses + ? `${baseUrl}/portfolio/${encodeURIComponent(resolvedWalletAddresses)}` + : baseUrl : trackType === "farcaster" - ? `${baseUrl}/fc/${encodeURIComponent(farcasterUsername)}` + ? resolvedFarcasterUsername + ? `${baseUrl}/fc/${encodeURIComponent(resolvedFarcasterUsername)}` + : baseUrl : baseUrl; return ( @@ -128,4 +264,3 @@ export default { fidget: Portfolio, properties: portfolioProperties, } as FidgetModule>; - diff --git a/src/fidgets/token/PortfolioUsernameInput.tsx b/src/fidgets/token/PortfolioUsernameInput.tsx new file mode 100644 index 000000000..48bffc0bd --- /dev/null +++ b/src/fidgets/token/PortfolioUsernameInput.tsx @@ -0,0 +1,65 @@ +import React, { useEffect, useMemo, useRef } from "react"; +import TextInput from "@/common/components/molecules/TextInput"; +import { useNeynarUser } from "@/common/lib/hooks/useNeynarUser"; + +type PortfolioInputProps = { + id?: string; + value: string; + onChange?: (value: string) => void; + updateSettings?: (partial: Record) => void; + className?: string; +}; + +const getPrimaryAddress = (user?: { + verifications?: string[]; + verified_addresses?: { eth_addresses?: string[] }; + custody_address?: string; +} | null) => { + const verifications = (user?.verifications || []) + .map((addr) => (typeof addr === "string" ? addr.trim() : "")) + .filter(Boolean); + if (verifications.length > 0) return verifications[0]!; + + const verified = (user?.verified_addresses?.eth_addresses || []) + .map((addr) => (typeof addr === "string" ? addr.trim() : "")) + .filter(Boolean); + if (verified.length > 0) return verified[0]!; + + const custody = user?.custody_address; + return typeof custody === "string" ? custody.trim() : ""; +}; + +const PortfolioUsernameInput: React.FC = ({ + value, + onChange, + updateSettings, + ...rest +}) => { + const normalized = (value || "").trim().replace(/^@/, ""); + const { user } = useNeynarUser(normalized ? normalized : undefined); + const primaryAddress = useMemo(() => getPrimaryAddress(user), [user]); + const lastAppliedRef = useRef(""); + + useEffect(() => { + if (!normalized || !primaryAddress || !updateSettings) return; + if (lastAppliedRef.current === primaryAddress) return; + lastAppliedRef.current = primaryAddress; + updateSettings({ + farcasterUsername: normalized, + walletAddresses: primaryAddress, + }); + }, [normalized, primaryAddress, updateSettings]); + + return ( + { + onChange?.(next); + }} + /> + ); +}; + +export const getPortfolioPrimaryAddress = getPrimaryAddress; +export default PortfolioUsernameInput; From 0e33c7777dd6ec9b5a6a6aad1d8df006bbf57000 Mon Sep 17 00:00:00 2001 From: Jhonattan2121 Date: Sun, 1 Feb 2026 00:33:47 -0300 Subject: [PATCH 2/3] feat(portfolio): auto-fill logged user and sync address from username --- .../organisms/FidgetSettingsEditor.tsx | 26 ++------ src/common/fidgets/FidgetWrapper.tsx | 28 ++------- src/fidgets/token/Portfolio.tsx | 62 ++----------------- 3 files changed, 16 insertions(+), 100 deletions(-) diff --git a/src/common/components/organisms/FidgetSettingsEditor.tsx b/src/common/components/organisms/FidgetSettingsEditor.tsx index 80b0833a9..3af96aba2 100644 --- a/src/common/components/organisms/FidgetSettingsEditor.tsx +++ b/src/common/components/organisms/FidgetSettingsEditor.tsx @@ -208,19 +208,9 @@ export const FidgetSettingsEditor: React.FC = ({ // Subscribe directly to zustand for settings - eliminates prop lag const zustandSettings = useFidgetSettings(currentSpaceId, currentTabName, fidgetId); - const skipDefaults = useMemo(() => { - if (properties.fidgetName === "Portfolio") { - return ["farcasterUsername", "walletAddresses"]; - } - return undefined; - }, [properties.fidgetName]); - const normalizedSettings = useMemo( - () => - fillWithDefaults(zustandSettings ?? settings, properties.fields, { - skipDefaults, - }), - [zustandSettings, settings, properties.fields, skipDefaults], + () => fillWithDefaults(zustandSettings ?? settings, properties.fields), + [zustandSettings, settings, properties.fields], ); const [state, setState] = useState(normalizedSettings); @@ -229,22 +219,18 @@ export const FidgetSettingsEditor: React.FC = ({ // Update local state when zustand settings change useEffect(() => { if (zustandSettings) { - const normalized = fillWithDefaults(zustandSettings, properties.fields, { - skipDefaults, - }); + const normalized = fillWithDefaults(zustandSettings, properties.fields); setState(normalized); } - }, [zustandSettings, properties.fields, skipDefaults]); + }, [zustandSettings, properties.fields]); const saveWithValidation = useCallback( (nextState: FidgetSettings, shouldUnselect?: boolean) => { - const filledState = fillWithDefaults(nextState, properties.fields, { - skipDefaults, - }); + const filledState = fillWithDefaults(nextState, properties.fields); setState(filledState); onSave(filledState, shouldUnselect); }, - [properties.fields, onSave, skipDefaults], + [properties.fields, onSave], ); // Save handler for inline field changes (doesn't unselect editor) diff --git a/src/common/fidgets/FidgetWrapper.tsx b/src/common/fidgets/FidgetWrapper.tsx index 9beb3ccec..237dd5711 100644 --- a/src/common/fidgets/FidgetWrapper.tsx +++ b/src/common/fidgets/FidgetWrapper.tsx @@ -43,10 +43,6 @@ export const getSettingsWithDefaults = ( settings: FidgetSettings, config: FidgetProperties, ): FidgetSettings => { - const skipDefaults = - config.fidgetName === "Portfolio" - ? new Set(["farcasterUsername", "walletAddresses"]) - : null; return reduce( config.fields, (acc, f) => { @@ -60,11 +56,7 @@ export const getSettingsWithDefaults = ( value !== null && (typeof value !== "string" || value.trim() !== ""); - if (!hasValue && skipDefaults?.has(f.fieldName)) { - acc[f.fieldName] = value ?? ""; - } else { - acc[f.fieldName] = hasValue ? value : f.default ?? ""; - } + acc[f.fieldName] = hasValue ? value : f.default ?? ""; return acc; }, {}, @@ -99,15 +91,10 @@ export function FidgetWrapper({ // to automatically backfill empty settings. This is useful when fidgets are created // from external sources (e.g., URL parameters) and need to populate settings. const dataConfig = bundle.config?.data as - | { - lastFetchSettings?: Partial; - disableBackfill?: boolean; - backfillOverrideKeys?: string[]; - } + | { lastFetchSettings?: Partial; disableBackfill?: boolean } | undefined; const lastFetchSettings = dataConfig?.lastFetchSettings; const disableBackfill = dataConfig?.disableBackfill === true; - const backfillOverrideKeys = dataConfig?.backfillOverrideKeys ?? []; const defaultSettingsMap = useMemo( () => @@ -121,10 +108,6 @@ export function FidgetWrapper({ ), [bundle.properties.fields], ); - const noDefaultOverride = - bundle.properties.fidgetName === "Portfolio" - ? new Set(["farcasterUsername", "walletAddresses"]) - : null; const derivedSettings = useMemo(() => { // Use zustand settings directly (they're already the latest), fall back to props @@ -140,15 +123,12 @@ export function FidgetWrapper({ const setValue = (key: string, value: unknown) => { const current = nextSettings[key]; const defaultValue = defaultSettingsMap[key]; - const isDefaultValue = - !noDefaultOverride?.has(key) && isEqual(current, defaultValue); - const forceOverride = backfillOverrideKeys.includes(key); + const isDefaultValue = isEqual(current, defaultValue); const isEmpty = current === undefined || current === null || current === "" || - isDefaultValue || - forceOverride; + isDefaultValue; // Don't overwrite existing non-empty values if (!isEmpty) { diff --git a/src/fidgets/token/Portfolio.tsx b/src/fidgets/token/Portfolio.tsx index d086eadad..3e7e3f599 100644 --- a/src/fidgets/token/Portfolio.tsx +++ b/src/fidgets/token/Portfolio.tsx @@ -57,7 +57,6 @@ const portfolioProperties: FidgetProperties = { { fieldName: "farcasterUsername", displayName: "Username", - default: "nounspacetom", required: false, disabledIf: (settings) => settings.trackType !== "farcaster", inputSelector: (props) => ( @@ -70,7 +69,6 @@ const portfolioProperties: FidgetProperties = { { fieldName: "walletAddresses", displayName: "Address(es)", - default: "0x06AE622bF2029Db79Bdebd38F723f1f33f95F6C5", required: false, disabledIf: (settings) => settings.trackType !== "address", inputSelector: (props) => ( @@ -145,8 +143,7 @@ const Portfolio: React.FC> = ({ }, [walletAddresses]); useEffect(() => { - const isDefaultUsername = !normalizedUsername; - if (!isDefaultUsername || !loggedInUsername) return; + if (normalizedUsername || !loggedInUsername) return; const previous = (data as { lastFetchSettings?: { farcasterUsername?: string } } | undefined) @@ -168,30 +165,15 @@ const Portfolio: React.FC> = ({ if (!effectiveUsername || !derivedAddresses) return; const addressInput = (walletAddresses || "").trim(); - const meta = data as - | { - autoAddressUsername?: string; - autoAddressValue?: string; - backfillOverrideKeys?: string[]; - } - | undefined; - const addressWasAuto = meta?.autoAddressValue - ? addressInput === meta.autoAddressValue - : false; - const usernameChanged = meta?.autoAddressUsername !== effectiveUsername; - const shouldAuto = !addressInput || addressWasAuto || usernameChanged; + if (addressInput) return; - if (!shouldAuto) return; - - if (addressInput === derivedAddresses) return; + const previous = + (data as { lastFetchSettings?: { walletAddresses?: string } } | undefined) + ?.lastFetchSettings?.walletAddresses; + if (previous === derivedAddresses) return; void saveData({ ...(data || {}), - autoAddressUsername: effectiveUsername, - autoAddressValue: derivedAddresses, - backfillOverrideKeys: Array.from( - new Set([...(meta?.backfillOverrideKeys ?? []), "walletAddresses"]), - ), lastFetchSettings: { ...(data as { lastFetchSettings?: Record } | undefined) ?.lastFetchSettings, @@ -200,38 +182,6 @@ const Portfolio: React.FC> = ({ }); }, [effectiveUsername, derivedAddresses, walletAddresses, data, saveData]); - useEffect(() => { - const addressInput = (walletAddresses || "").trim(); - const meta = data as - | { - autoAddressUsername?: string; - autoAddressValue?: string; - lastFetchSettings?: Record; - backfillOverrideKeys?: string[]; - } - | undefined; - - if (!meta?.autoAddressValue) return; - - if (!addressInput || addressInput === meta.autoAddressValue) return; - - const { lastFetchSettings, ...rest } = meta; - if (!lastFetchSettings || !("walletAddresses" in lastFetchSettings)) return; - - const { walletAddresses: _ignored, ...nextLastFetch } = lastFetchSettings; - const nextOverrideKeys = (meta?.backfillOverrideKeys ?? []).filter( - (key) => key !== "walletAddresses", - ); - void saveData({ - ...(data || {}), - ...rest, - autoAddressUsername: undefined, - autoAddressValue: undefined, - backfillOverrideKeys: nextOverrideKeys.length > 0 ? nextOverrideKeys : undefined, - lastFetchSettings: nextLastFetch, - }); - }, [walletAddresses, data, saveData]); - const baseUrl = "https://balance-fidget.replit.app"; const url = trackType === "address" From 62ec8453de49e0b7e2203134c2cbfdf5c816a82a Mon Sep 17 00:00:00 2001 From: Jhonattan2121 Date: Wed, 4 Feb 2026 12:48:23 -0300 Subject: [PATCH 3/3] fix: auto-fill portfolio username on homebase --- src/fidgets/token/Portfolio.tsx | 14 +++++++--- src/fidgets/token/PortfolioUsernameInput.tsx | 27 ++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/fidgets/token/Portfolio.tsx b/src/fidgets/token/Portfolio.tsx index 3e7e3f599..38c301807 100644 --- a/src/fidgets/token/Portfolio.tsx +++ b/src/fidgets/token/Portfolio.tsx @@ -13,6 +13,7 @@ import useCurrentFid from "@/common/lib/hooks/useCurrentFid"; import { useLoadFarcasterUser } from "@/common/data/queries/farcaster"; import { useNeynarUser } from "@/common/lib/hooks/useNeynarUser"; import { useFarcasterSigner } from "@/fidgets/farcaster"; +import { useAppStore } from "@/common/data/stores/app"; import PortfolioUsernameInput, { getPortfolioPrimaryAddress, } from "./PortfolioUsernameInput"; @@ -108,9 +109,14 @@ const Portfolio: React.FC> = ({ const currentFid = useCurrentFid(); const farcasterSigner = useFarcasterSigner("portfolio"); const effectiveFid = (currentFid ?? farcasterSigner.fid) ?? -1; + const associatedFid = useAppStore( + (state) => state.account.getCurrentIdentity()?.associatedFids?.[0], + ); + const lookupFid = + effectiveFid > 0 ? effectiveFid : associatedFid ?? -1; const { data: currentUserData } = useLoadFarcasterUser( - effectiveFid, - effectiveFid > 0 ? effectiveFid : undefined, + lookupFid, + lookupFid > 0 ? lookupFid : undefined, ); const loggedInUsername = useMemo(() => { const username = currentUserData?.users?.[0]?.username; @@ -134,8 +140,8 @@ const Portfolio: React.FC> = ({ const resolvedFarcasterUsername = useMemo(() => { const normalized = (farcasterUsername || "").trim().replace(/^@/, ""); - return normalized; - }, [farcasterUsername]); + return normalized || loggedInUsername || ""; + }, [farcasterUsername, loggedInUsername]); const resolvedWalletAddresses = useMemo(() => { const normalized = (walletAddresses || "").trim(); diff --git a/src/fidgets/token/PortfolioUsernameInput.tsx b/src/fidgets/token/PortfolioUsernameInput.tsx index 48bffc0bd..ada6ca014 100644 --- a/src/fidgets/token/PortfolioUsernameInput.tsx +++ b/src/fidgets/token/PortfolioUsernameInput.tsx @@ -1,6 +1,10 @@ import React, { useEffect, useMemo, useRef } from "react"; import TextInput from "@/common/components/molecules/TextInput"; import { useNeynarUser } from "@/common/lib/hooks/useNeynarUser"; +import useCurrentFid from "@/common/lib/hooks/useCurrentFid"; +import { useLoadFarcasterUser } from "@/common/data/queries/farcaster"; +import { useFarcasterSigner } from "@/fidgets/farcaster"; +import { useAppStore } from "@/common/data/stores/app"; type PortfolioInputProps = { id?: string; @@ -35,10 +39,33 @@ const PortfolioUsernameInput: React.FC = ({ updateSettings, ...rest }) => { + const currentFid = useCurrentFid(); + const farcasterSigner = useFarcasterSigner("portfolio-settings"); + const associatedFid = useAppStore( + (state) => state.account.getCurrentIdentity()?.associatedFids?.[0], + ); + const effectiveFid = (currentFid ?? farcasterSigner.fid) ?? -1; + const lookupFid = effectiveFid > 0 ? effectiveFid : associatedFid ?? -1; + const { data: currentUserData } = useLoadFarcasterUser( + lookupFid, + lookupFid > 0 ? lookupFid : undefined, + ); + const loggedInUsername = useMemo(() => { + const username = currentUserData?.users?.[0]?.username; + return typeof username === "string" ? username.trim() : ""; + }, [currentUserData]); const normalized = (value || "").trim().replace(/^@/, ""); const { user } = useNeynarUser(normalized ? normalized : undefined); const primaryAddress = useMemo(() => getPrimaryAddress(user), [user]); const lastAppliedRef = useRef(""); + const lastAutoUsernameRef = useRef(""); + + useEffect(() => { + if (normalized || !loggedInUsername || !updateSettings) return; + if (lastAutoUsernameRef.current === loggedInUsername) return; + lastAutoUsernameRef.current = loggedInUsername; + updateSettings({ farcasterUsername: loggedInUsername }); + }, [normalized, loggedInUsername, updateSettings]); useEffect(() => { if (!normalized || !primaryAddress || !updateSettings) return;