Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 36 additions & 9 deletions lib/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,21 @@ import type { Address } from "viem";
/**
* Server action that resolves an Ethereum address to a Farcaster profile.
* Prefers cached DB data, falls back to live API.
* Accepts an optional pre-fetched dbUser to avoid redundant DB lookups.
*/
export async function getFarcasterProfile(
address: string,
dbUser?: User | null,
): Promise<FarcasterProfile | null> {
// Try DB first (only if user has a FID — wallet-only users have no Farcaster profile)
const dbUser = await getUserFromDB(address);
if (dbUser && dbUser.fid != null) {
const user = dbUser !== undefined ? dbUser : await getUserFromDB(address);
if (user && user.fid != null) {
return {
fid: dbUser.fid,
username: dbUser.username ?? "",
displayName: dbUser.display_name ?? dbUser.username ?? "",
pfpUrl: dbUser.pfp_url ?? null,
bio: dbUser.bio ?? null,
fid: user.fid,
username: user.username ?? "",
displayName: user.display_name ?? user.username ?? "",
pfpUrl: user.pfp_url ?? null,
bio: user.bio ?? null,
};
}
// Fallback to live API
Expand All @@ -34,12 +36,17 @@ export async function getFarcasterProfile(
/**
* Server action that resolves ERC-8004 agent metadata from a wallet address.
* Checks DB cache first, falls back to RPC. Caches externally registered agents.
* Accepts an optional pre-fetched dbUser to avoid redundant DB lookups.
*/
export async function fetchAgentMetadata(
address: string,
preloadedUser?: User | null,
): Promise<AgentMetadata | null> {
// DB-first: check cached agent data (agent-specific lookup)
const dbUser = await getAgentUserFromDB(address);
// DB-first: check cached agent data.
// Use preloaded user if it has agent_id; otherwise fall back to agent-specific lookup.
const dbUser = preloadedUser?.agent_id != null
? preloadedUser
: await getAgentUserFromDB(address);
if (dbUser?.agent_id != null) {
return {
agentId: String(dbUser.agent_id),
Expand Down Expand Up @@ -83,6 +90,26 @@ export async function fetchAgentMetadata(
return meta;
}

/**
* Fetch the full user profile in a single DB lookup.
* Returns dbUser, fcProfile, and agentMeta derived from one shared row.
* External API fallbacks still fire when DB data is missing.
*/
export async function getFullUserProfile(
address: string,
): Promise<{
dbUser: User | null;
fcProfile: FarcasterProfile | null;
agentMeta: AgentMetadata | null;
}> {
const dbUser = await getUserFromDB(address);
const [fcProfile, agentMeta] = await Promise.all([
getFarcasterProfile(address, dbUser),
fetchAgentMetadata(address, dbUser),
]);
return { dbUser, fcProfile, agentMeta };
}

/**
* Check if a user exists in the DB (any row, with or without agent_id).
* Returns true if a known user, false if completely unknown wallet.
Expand Down
27 changes: 11 additions & 16 deletions src/app/profile/[address]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { formatUnits, type Address } from "viem";
import Link from "next/link";
import { supabase, type Storyline, type Donation, type TradeHistory, type User } from "../../../../lib/supabase";

Check warning on line 9 in src/app/profile/[address]/page.tsx

View workflow job for this annotation

GitHub Actions / lint-and-typecheck

'TradeHistory' is defined but never used
import { STORY_FACTORY, RESERVE_LABEL, EXPLORER_URL, MCV2_BOND, PLOT_TOKEN } from "../../../../lib/contracts/constants";
import { getFarcasterProfile, fetchAgentMetadata, getUserFromDB } from "../../../../lib/actions";
import { getFullUserProfile } from "../../../../lib/actions";
import { truncateAddress } from "../../../../lib/utils";
import { formatPrice } from "../../../../lib/format";
import { getTokenPrice, mcv2BondAbi, erc20Abi, type TokenPriceInfo } from "../../../../lib/price";

Check warning on line 14 in src/app/profile/[address]/page.tsx

View workflow job for this annotation

GitHub Actions / lint-and-typecheck

'TokenPriceInfo' is defined but never used
import { browserClient } from "../../../../lib/rpc";
import type { FarcasterProfile } from "../../../../lib/farcaster";
import type { AgentMetadata } from "../../../../lib/contracts/erc8004";
Expand All @@ -30,23 +30,18 @@

const [tab, setTab] = useState<Tab>("stories");

// DB user data (cached profiles)
const { data: dbUser } = useQuery({
queryKey: ["db-user", address],
queryFn: () => getUserFromDB(address),
// Unified profile fetch: single DB lookup, derives FC + agent from shared result
const { data: fullProfile, isLoading: profileLoading } = useQuery({
queryKey: ["full-profile", address],
queryFn: () => getFullUserProfile(address),
});

const { data: fcProfile, isLoading: fcLoading } = useQuery({
queryKey: ["fc-profile", address],
queryFn: () => getFarcasterProfile(address),
});

const { data: agentMeta, isLoading: agentLoading } = useQuery({
queryKey: ["agent-meta", address],
queryFn: () => fetchAgentMetadata(address),
});

const isAgent = !agentLoading && agentMeta !== null && agentMeta !== undefined;
const dbUser = fullProfile?.dbUser ?? null;
const fcProfile = fullProfile?.fcProfile ?? null;
const fcLoading = profileLoading;
const agentMeta = fullProfile?.agentMeta ?? null;
const agentLoading = profileLoading;
const isAgent = !profileLoading && agentMeta !== null && agentMeta !== undefined;

// Cumulative claimed royalties (on-chain)
const { data: claimedRoyalties } = useQuery({
Expand Down Expand Up @@ -114,7 +109,7 @@
if (r <= 0) clearInterval(interval);
}, 1000);
return () => clearInterval(interval);
}, [dbUser?.steemhunt_fetched_at]);

Check warning on line 112 in src/app/profile/[address]/page.tsx

View workflow job for this annotation

GitHub Actions / lint-and-typecheck

React Hook useEffect has a missing dependency: 'COOLDOWN_MS'. Either include it or remove the dependency array

const onCooldown = cooldownRemaining > 0;

Expand Down
Loading