diff --git a/__tests__/components/providers/MixpanelSetup.test.tsx b/__tests__/components/providers/MixpanelSetup.test.tsx
index 7ce69c6cb3..704d08515b 100644
--- a/__tests__/components/providers/MixpanelSetup.test.tsx
+++ b/__tests__/components/providers/MixpanelSetup.test.tsx
@@ -8,7 +8,12 @@ const identifyMock = jest.fn();
const initAnalyticsMock = jest.fn();
const trackPageViewMock = jest.fn();
-let connectedProfile: { id: number } | null = null;
+let connectedProfile: {
+ id: number;
+ normalised_handle?: string | null;
+ wallets?: Array<{ wallet: string }>;
+} | null = null;
+let fetchingProfile = false;
let pathname = "/";
let performanceConsent: boolean | undefined = undefined;
let searchParams = new URLSearchParams();
@@ -16,6 +21,7 @@ let searchParams = new URLSearchParams();
jest.mock("@/components/auth/Auth", () => ({
useAuth: () => ({
connectedProfile,
+ fetchingProfile,
}),
}));
@@ -41,6 +47,7 @@ jest.mock("@/services/analytics/mixpanel", () => ({
describe("MixpanelSetup", () => {
beforeEach(() => {
connectedProfile = null;
+ fetchingProfile = false;
pathname = "/";
performanceConsent = undefined;
searchParams = new URLSearchParams();
@@ -134,6 +141,88 @@ describe("MixpanelSetup", () => {
});
});
+ it("tracks anonymous profile views separately from signed-in viewers", () => {
+ performanceConsent = true;
+ pathname = "/alice/collected";
+
+ render();
+
+ expect(trackPageViewMock).toHaveBeenCalledWith("/alice/collected", {
+ has_connected_profile: false,
+ logical_page: "profile_collected",
+ page_group: "profile",
+ profile_viewer_context: "anonymous",
+ route_pattern: "/:handle/collected",
+ });
+ });
+
+ it("marks own profile tabs as self views", () => {
+ performanceConsent = true;
+ pathname = "/alice/collected";
+ connectedProfile = {
+ id: 42,
+ normalised_handle: "alice",
+ wallets: [],
+ };
+
+ render();
+
+ expect(trackPageViewMock).toHaveBeenCalledWith("/alice/collected", {
+ has_connected_profile: true,
+ logical_page: "profile_collected",
+ page_group: "profile",
+ profile_viewer_context: "self",
+ route_pattern: "/:handle/collected",
+ });
+ });
+
+ it("marks other users' profile tabs as other views", () => {
+ performanceConsent = true;
+ pathname = "/bob/collected";
+ connectedProfile = {
+ id: 42,
+ normalised_handle: "alice",
+ wallets: [{ wallet: "0xabc" }],
+ };
+
+ render();
+
+ expect(trackPageViewMock).toHaveBeenCalledWith("/bob/collected", {
+ has_connected_profile: true,
+ logical_page: "profile_collected",
+ page_group: "profile",
+ profile_viewer_context: "other",
+ route_pattern: "/:handle/collected",
+ });
+ });
+
+ it("waits for profile ownership data before tracking profile page views", () => {
+ performanceConsent = true;
+ pathname = "/alice/collected";
+ fetchingProfile = true;
+
+ const { rerender } = render();
+
+ expect(trackPageViewMock).not.toHaveBeenCalled();
+
+ fetchingProfile = false;
+ connectedProfile = {
+ id: 42,
+ normalised_handle: "alice",
+ wallets: [],
+ };
+ rerender();
+
+ expect(trackPageViewMock).toHaveBeenCalledTimes(1);
+ expect(trackPageViewMock).toHaveBeenCalledWith("/alice/collected", {
+ has_connected_profile: true,
+ logical_page: "profile_collected",
+ page_group: "profile",
+ profile_viewer_context: "self",
+ route_pattern: "/:handle/collected",
+ });
+ });
+
it("identifies connected profiles and resets when consent is revoked", () => {
performanceConsent = true;
pathname = "/waves";
diff --git a/__tests__/components/user/layout/UserPageTabs.test.tsx b/__tests__/components/user/layout/UserPageTabs.test.tsx
index d26400fb11..d2cc56bc4e 100644
--- a/__tests__/components/user/layout/UserPageTabs.test.tsx
+++ b/__tests__/components/user/layout/UserPageTabs.test.tsx
@@ -1,7 +1,6 @@
-import { AuthContext } from "@/components/auth/Auth";
import UserPageTabs from "@/components/user/layout/UserPageTabs";
import { USER_PAGE_TAB_IDS } from "@/components/user/layout/userTabs.config";
-import { render, screen } from "@testing-library/react";
+import { render, screen, waitFor } from "@testing-library/react";
import {
useParams,
usePathname,
@@ -15,6 +14,10 @@ jest.mock("next/navigation", () => ({
useSearchParams: jest.fn(),
useParams: jest.fn(),
}));
+const useAuthMock = jest.fn();
+jest.mock("@/components/auth/Auth", () => ({
+ useAuth: () => useAuthMock(),
+}));
const capacitorMock = jest.fn();
jest.mock("@/hooks/useCapacitor", () => ({
__esModule: true,
@@ -32,14 +35,24 @@ const {
useCookieConsent,
} = require("@/components/cookies/CookieConsentContext");
-const renderTabs = (
- showWaves: boolean,
- isIos: boolean,
- country: string = "US",
- connectedProfile: any = null
-) => {
- (useRouter as jest.Mock).mockReturnValue({ push: jest.fn() });
- (usePathname as jest.Mock).mockReturnValue("/[user]");
+const renderTabs = ({
+ showWaves,
+ isIos,
+ country = "US",
+ connectedProfile = null,
+ fetchingProfile = false,
+ pathname = "/[user]",
+}: {
+ showWaves: boolean;
+ isIos: boolean;
+ country?: string;
+ connectedProfile?: any;
+ fetchingProfile?: boolean;
+ pathname?: string;
+}) => {
+ const router = { push: jest.fn(), replace: jest.fn() };
+ (useRouter as jest.Mock).mockReturnValue(router);
+ (usePathname as jest.Mock).mockReturnValue(pathname);
(useSearchParams as jest.Mock).mockReturnValue(new URLSearchParams());
(useParams as jest.Mock).mockReturnValue({ user: "testuser" });
capacitorMock.mockReturnValue({ isIos });
@@ -49,16 +62,20 @@ const renderTabs = (
consent: jest.fn(),
reject: jest.fn(),
});
- return render(
-
-
-
- );
+ useAuthMock.mockReturnValue({
+ showWaves,
+ connectedProfile,
+ fetchingProfile,
+ });
+ return {
+ router,
+ ...render(),
+ };
};
describe("UserPageTabs", () => {
it("filters tabs based on context and platform", () => {
- renderTabs(false, false);
+ renderTabs({ showWaves: false, isIos: false });
const tabs = screen.getAllByTestId("tab").map((t) => t.textContent);
expect(tabs).not.toContain(USER_PAGE_TAB_IDS.BRAIN);
expect(tabs).not.toContain("stats");
@@ -66,41 +83,90 @@ describe("UserPageTabs", () => {
});
it("hides subscriptions tab on iOS non-US", () => {
- renderTabs(true, true, "CA");
+ renderTabs({ showWaves: true, isIos: true, country: "CA" });
const tabs = screen.getAllByTestId("tab").map((t) => t.textContent);
expect(tabs).not.toContain(USER_PAGE_TAB_IDS.SUBSCRIPTIONS);
});
it("shows proxy tab when viewing own profile by handle", () => {
- renderTabs(false, false, "US", {
- normalised_handle: "testuser",
- wallets: [],
+ renderTabs({
+ showWaves: false,
+ isIos: false,
+ connectedProfile: {
+ normalised_handle: "testuser",
+ wallets: [],
+ },
});
const tabs = screen.getAllByTestId("tab").map((t) => t.textContent);
expect(tabs).toContain(USER_PAGE_TAB_IDS.PROXY);
});
it("shows proxy tab when viewing own profile by wallet", () => {
- renderTabs(false, false, "US", {
- normalised_handle: "otherhandle",
- wallets: [{ wallet: "testuser" }],
+ renderTabs({
+ showWaves: false,
+ isIos: false,
+ connectedProfile: {
+ normalised_handle: "otherhandle",
+ wallets: [{ wallet: "testuser" }],
+ },
});
const tabs = screen.getAllByTestId("tab").map((t) => t.textContent);
expect(tabs).toContain(USER_PAGE_TAB_IDS.PROXY);
});
it("hides proxy tab when viewing another user's profile", () => {
- renderTabs(false, false, "US", {
- normalised_handle: "anotheruser",
- wallets: [{ wallet: "0xSomeOtherWallet" }],
+ renderTabs({
+ showWaves: false,
+ isIos: false,
+ connectedProfile: {
+ normalised_handle: "anotheruser",
+ wallets: [{ wallet: "0xSomeOtherWallet" }],
+ },
});
const tabs = screen.getAllByTestId("tab").map((t) => t.textContent);
expect(tabs).not.toContain(USER_PAGE_TAB_IDS.PROXY);
});
it("hides proxy tab when not connected", () => {
- renderTabs(false, false, "US", null);
+ renderTabs({ showWaves: false, isIos: false });
const tabs = screen.getAllByTestId("tab").map((t) => t.textContent);
expect(tabs).not.toContain(USER_PAGE_TAB_IDS.PROXY);
});
+
+ it("keeps the proxy tab visible while ownership is still loading", () => {
+ const { router } = renderTabs({
+ showWaves: false,
+ isIos: false,
+ pathname: "/testuser/proxy",
+ fetchingProfile: true,
+ });
+
+ const tabs = screen.getAllByTestId("tab").map((t) => t.textContent);
+ expect(tabs).toContain(USER_PAGE_TAB_IDS.PROXY);
+ expect(router.replace).not.toHaveBeenCalled();
+ });
+
+ it("redirects away from the proxy tab after loading when the profile is not owned", async () => {
+ const { rerender, router } = renderTabs({
+ showWaves: false,
+ isIos: false,
+ pathname: "/testuser/proxy",
+ fetchingProfile: true,
+ });
+
+ useAuthMock.mockReturnValue({
+ showWaves: false,
+ connectedProfile: {
+ normalised_handle: "someoneelse",
+ wallets: [{ wallet: "0xSomeOtherWallet" }],
+ },
+ fetchingProfile: false,
+ });
+
+ rerender();
+
+ await waitFor(() => {
+ expect(router.replace).toHaveBeenCalledWith("/testuser");
+ });
+ });
});
diff --git a/__tests__/helpers/ProfileHelpers.extra.test.ts b/__tests__/helpers/ProfileHelpers.extra.test.ts
index 10af8321b9..6ffa808998 100644
--- a/__tests__/helpers/ProfileHelpers.extra.test.ts
+++ b/__tests__/helpers/ProfileHelpers.extra.test.ts
@@ -1,59 +1,163 @@
-import { getProfileConnectedStatus, profileAndConsolidationsToProfileMin } from '@/helpers/ProfileHelpers';
-import { ProfileConnectedStatus } from '@/entities/IProfile';
+import {
+ getProfileConnectedStatus,
+ getProfileViewerContext,
+ isOwnProfileRoute,
+ profileAndConsolidationsToProfileMin,
+} from "@/helpers/ProfileHelpers";
+import { ProfileConnectedStatus } from "@/entities/IProfile";
-describe('getProfileConnectedStatus', () => {
- it('returns NOT_CONNECTED when profile missing', () => {
- expect(getProfileConnectedStatus({ profile: null, isProxy: false })).toBe(ProfileConnectedStatus.NOT_CONNECTED);
+describe("getProfileConnectedStatus", () => {
+ it("returns NOT_CONNECTED when profile missing", () => {
+ expect(getProfileConnectedStatus({ profile: null, isProxy: false })).toBe(
+ ProfileConnectedStatus.NOT_CONNECTED
+ );
});
- it('returns PROXY when isProxy true', () => {
- expect(getProfileConnectedStatus({ profile: {} as any, isProxy: true })).toBe(ProfileConnectedStatus.PROXY);
+ it("returns PROXY when isProxy true", () => {
+ expect(
+ getProfileConnectedStatus({ profile: {} as any, isProxy: true })
+ ).toBe(ProfileConnectedStatus.PROXY);
});
- it('returns NO_PROFILE when handle missing', () => {
+ it("returns NO_PROFILE when handle missing", () => {
const profile = { handle: null } as any;
- expect(getProfileConnectedStatus({ profile, isProxy: false })).toBe(ProfileConnectedStatus.NO_PROFILE);
+ expect(getProfileConnectedStatus({ profile, isProxy: false })).toBe(
+ ProfileConnectedStatus.NO_PROFILE
+ );
});
- it('returns HAVE_PROFILE otherwise', () => {
- const profile = { handle: 'user' } as any;
- expect(getProfileConnectedStatus({ profile, isProxy: false })).toBe(ProfileConnectedStatus.HAVE_PROFILE);
+ it("returns HAVE_PROFILE otherwise", () => {
+ const profile = { handle: "user" } as any;
+ expect(getProfileConnectedStatus({ profile, isProxy: false })).toBe(
+ ProfileConnectedStatus.HAVE_PROFILE
+ );
});
});
-describe('profileAndConsolidationsToProfileMin', () => {
- it('returns null when id or handle missing', () => {
- expect(profileAndConsolidationsToProfileMin({ profile: { id: '', handle: '' } as any })).toBeNull();
+describe("isOwnProfileRoute", () => {
+ it("matches the connected profile by normalized handle", () => {
+ expect(
+ isOwnProfileRoute({
+ connectedProfile: {
+ normalised_handle: "alice",
+ wallets: [],
+ } as any,
+ handleOrWallet: "Alice",
+ })
+ ).toBe(true);
});
- it('maps fields correctly', () => {
+ it("matches the connected profile by wallet address", () => {
+ expect(
+ isOwnProfileRoute({
+ connectedProfile: {
+ normalised_handle: "alice",
+ wallets: [{ wallet: "0xabc123" }],
+ } as any,
+ handleOrWallet: "0xAbC123",
+ })
+ ).toBe(true);
+ });
+
+ it("returns false for another profile", () => {
+ expect(
+ isOwnProfileRoute({
+ connectedProfile: {
+ normalised_handle: "alice",
+ wallets: [{ wallet: "0xabc123" }],
+ } as any,
+ handleOrWallet: "bob",
+ })
+ ).toBe(false);
+ });
+});
+
+describe("getProfileViewerContext", () => {
+ it("returns anonymous when no viewer is connected", () => {
+ expect(
+ getProfileViewerContext({
+ connectedProfile: null,
+ handleOrWallet: "alice",
+ })
+ ).toBe("anonymous");
+ });
+
+ it("returns self for the connected user's profile", () => {
+ expect(
+ getProfileViewerContext({
+ connectedProfile: {
+ normalised_handle: "alice",
+ wallets: [],
+ } as any,
+ handleOrWallet: "alice",
+ })
+ ).toBe("self");
+ });
+
+ it("returns other for another signed-in user's profile", () => {
+ expect(
+ getProfileViewerContext({
+ connectedProfile: {
+ normalised_handle: "alice",
+ wallets: [],
+ } as any,
+ handleOrWallet: "bob",
+ })
+ ).toBe("other");
+ });
+
+ it("returns null when the profile target is missing", () => {
+ expect(
+ getProfileViewerContext({
+ connectedProfile: null,
+ handleOrWallet: " ",
+ })
+ ).toBeNull();
+ });
+});
+
+describe("profileAndConsolidationsToProfileMin", () => {
+ it("returns null when id or handle missing", () => {
+ expect(
+ profileAndConsolidationsToProfileMin({
+ profile: { id: "", handle: "" } as any,
+ })
+ ).toBeNull();
+ });
+
+ it("maps fields correctly", () => {
const profile = {
- id: '1',
- handle: 'user',
- pfp: 'img',
- banner1: 'b1',
- banner2: 'b2',
+ id: "1",
+ handle: "user",
+ pfp: "img",
+ banner1: "b1",
+ banner2: "b2",
cic: 1,
rep: 2,
tdh: 3,
level: 4,
- primary_wallet: '0x1'
+ primary_wallet: "0x1",
} as any;
const res = profileAndConsolidationsToProfileMin({ profile })!;
expect(res).toEqual({
- id: '1',
- handle: 'user',
- pfp: 'img',
- banner1_color: 'b1',
- banner2_color: 'b2',
+ id: "1",
+ handle: "user",
+ pfp: "img",
+ banner1_color: null,
+ banner2_color: null,
cic: 1,
rep: 2,
tdh: 3,
+ tdh_rate: undefined,
+ xtdh: undefined,
+ xtdh_rate: undefined,
level: 4,
archived: false,
- primary_address: '0x1',
+ primary_address: "0x1",
active_main_stage_submission_ids: undefined,
- winner_main_stage_drop_ids: []
+ winner_main_stage_drop_ids: undefined,
+ is_wave_creator: undefined,
+ artist_of_prevote_cards: undefined,
});
});
});
diff --git a/components/providers/MixpanelSetup.tsx b/components/providers/MixpanelSetup.tsx
index 836abdb38d..87afec57e1 100644
--- a/components/providers/MixpanelSetup.tsx
+++ b/components/providers/MixpanelSetup.tsx
@@ -2,6 +2,7 @@
import { useAuth } from "@/components/auth/Auth";
import { useCookieConsent } from "@/components/cookies/CookieConsentContext";
+import { getProfileViewerContext } from "@/helpers/ProfileHelpers";
import {
clearIdentity,
disableAnalytics,
@@ -13,10 +14,14 @@ import { classifyPageView } from "@/services/analytics/pageClassification";
import { usePathname, useSearchParams } from "next/navigation";
import { useEffect, useRef } from "react";
+const getProfileRouteTarget = (pathname: string): string | null => {
+ return pathname.split("/").find((segment) => segment.length > 0) ?? null;
+};
+
export default function MixpanelSetup() {
const pathname = usePathname();
const searchParams = useSearchParams();
- const { connectedProfile } = useAuth();
+ const { connectedProfile, fetchingProfile } = useAuth();
const { performanceConsent } = useCookieConsent();
const lastTrackedPageKeyRef = useRef(null);
const identifiedProfileIdRef = useRef(null);
@@ -25,6 +30,12 @@ export default function MixpanelSetup() {
pathname,
searchParams,
});
+ const profileViewerContext = pageView.logicalPage.startsWith("profile_")
+ ? getProfileViewerContext({
+ connectedProfile,
+ handleOrWallet: getProfileRouteTarget(pathname),
+ })
+ : null;
useEffect(() => {
if (!hasConsent) {
@@ -68,6 +79,10 @@ export default function MixpanelSetup() {
return;
}
+ if (pageView.logicalPage.startsWith("profile_") && fetchingProfile) {
+ return;
+ }
+
if (lastTrackedPageKeyRef.current === pageView.trackingKey) {
return;
}
@@ -78,9 +93,17 @@ export default function MixpanelSetup() {
connectedProfile?.id !== undefined && connectedProfile.id !== null,
logical_page: pageView.logicalPage,
page_group: pageView.pageGroup,
+ profile_viewer_context: profileViewerContext ?? undefined,
route_pattern: pageView.routePattern,
});
- }, [connectedProfile?.id, hasConsent, pageView, pathname]);
+ }, [
+ connectedProfile?.id,
+ fetchingProfile,
+ hasConsent,
+ pageView,
+ pathname,
+ profileViewerContext,
+ ]);
return null;
}
diff --git a/components/user/layout/UserPageTabs.tsx b/components/user/layout/UserPageTabs.tsx
index 51cde919ff..91d2fcfc20 100644
--- a/components/user/layout/UserPageTabs.tsx
+++ b/components/user/layout/UserPageTabs.tsx
@@ -1,7 +1,8 @@
"use client";
-import { AuthContext } from "@/components/auth/Auth";
+import { useAuth } from "@/components/auth/Auth";
import { useCookieConsent } from "@/components/cookies/CookieConsentContext";
+import { isOwnProfileRoute } from "@/helpers/ProfileHelpers";
import useCapacitor from "@/hooks/useCapacitor";
import {
faChevronLeft,
@@ -14,19 +15,12 @@ import {
useRouter,
useSearchParams,
} from "next/navigation";
-import {
- useCallback,
- useContext,
- useEffect,
- useMemo,
- useRef,
- useState,
-} from "react";
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import UserPageTab from "./UserPageTab";
import {
DEFAULT_USER_PAGE_TAB,
USER_PAGE_TABS,
- type UserPageTabConfig,
+ USER_PAGE_TAB_IDS,
type UserPageTabKey,
type UserPageVisibilityContext,
getUserPageTabByRoute,
@@ -72,11 +66,6 @@ const resolveTabFromPath = (pathname: string): UserPageTabKey => {
return match?.id ?? DEFAULT_TAB;
};
-const filterVisibleTabs = (
- tabs: readonly UserPageTabConfig[],
- context: UserPageVisibilityContext
-) => tabs.filter((tab) => (tab.isVisible ? tab.isVisible(context) : true));
-
export default function UserPageTabs() {
const pathname = usePathname() ?? "";
const router = useRouter();
@@ -86,16 +75,13 @@ export default function UserPageTabs() {
const searchString = searchParams?.toString() ?? "";
const capacitor = useCapacitor();
const { country } = useCookieConsent();
- const { showWaves, connectedProfile } = useContext(AuthContext);
+ const { showWaves, connectedProfile, fetchingProfile } = useAuth();
const isOwnProfile = useMemo(() => {
- if (!connectedProfile || !handleOrWallet) return false;
- const lower = handleOrWallet.toLowerCase();
- if (connectedProfile.normalised_handle === lower) return true;
- return (
- connectedProfile.wallets?.some((w) => w.wallet.toLowerCase() === lower) ??
- false
- );
+ return isOwnProfileRoute({
+ connectedProfile,
+ handleOrWallet,
+ });
}, [connectedProfile, handleOrWallet]);
const visibilityContext = useMemo(
@@ -119,9 +105,24 @@ export default function UserPageTabs() {
[pathname]
);
+ const preserveProxyTabWhileOwnershipLoads =
+ fetchingProfile &&
+ !connectedProfile &&
+ resolvedTabFromPath === USER_PAGE_TAB_IDS.PROXY;
+
const visibleTabs = useMemo(
- () => filterVisibleTabs(USER_PAGE_TABS, visibilityContext),
- [visibilityContext]
+ () =>
+ USER_PAGE_TABS.filter((tab) => {
+ if (
+ preserveProxyTabWhileOwnershipLoads &&
+ tab.id === USER_PAGE_TAB_IDS.PROXY
+ ) {
+ return true;
+ }
+
+ return tab.isVisible ? tab.isVisible(visibilityContext) : true;
+ }),
+ [preserveProxyTabWhileOwnershipLoads, visibilityContext]
);
const resolvedTabIsVisible = useMemo(
diff --git a/helpers/ProfileHelpers.ts b/helpers/ProfileHelpers.ts
index 3489fc2e91..374e6d936e 100644
--- a/helpers/ProfileHelpers.ts
+++ b/helpers/ProfileHelpers.ts
@@ -2,6 +2,72 @@ import { ProfileConnectedStatus } from "@/entities/IProfile";
import type { ProfileMinWithoutSubs } from "./ProfileTypes";
import type { ApiIdentity } from "@/generated/models/ApiIdentity";
import { getBannerColorValue } from "@/helpers/profile-banner.helpers";
+
+type ProfileIdentityForOwnership =
+ | Pick
+ | null
+ | undefined;
+
+type ProfileViewerContext = "self" | "other" | "anonymous";
+
+const normalizeProfileTarget = (
+ handleOrWallet: string | null | undefined
+): string | null => {
+ if (typeof handleOrWallet !== "string") {
+ return null;
+ }
+
+ const normalizedTarget = handleOrWallet.trim().toLowerCase();
+ return normalizedTarget.length > 0 ? normalizedTarget : null;
+};
+
+export const isOwnProfileRoute = ({
+ connectedProfile,
+ handleOrWallet,
+}: {
+ readonly connectedProfile: ProfileIdentityForOwnership;
+ readonly handleOrWallet: string | null | undefined;
+}): boolean => {
+ const normalizedTarget = normalizeProfileTarget(handleOrWallet);
+ if (!connectedProfile || !normalizedTarget) {
+ return false;
+ }
+
+ if (connectedProfile.normalised_handle?.toLowerCase() === normalizedTarget) {
+ return true;
+ }
+
+ return (
+ connectedProfile.wallets?.some(
+ (wallet) => wallet.wallet.toLowerCase() === normalizedTarget
+ ) ?? false
+ );
+};
+
+export const getProfileViewerContext = ({
+ connectedProfile,
+ handleOrWallet,
+}: {
+ readonly connectedProfile: ProfileIdentityForOwnership;
+ readonly handleOrWallet: string | null | undefined;
+}): ProfileViewerContext | null => {
+ const normalizedTarget = normalizeProfileTarget(handleOrWallet);
+ if (!normalizedTarget) {
+ return null;
+ }
+
+ if (!connectedProfile) {
+ return "anonymous";
+ }
+
+ return isOwnProfileRoute({
+ connectedProfile,
+ handleOrWallet: normalizedTarget,
+ })
+ ? "self"
+ : "other";
+};
+
export const getProfileConnectedStatus = ({
profile,
isProxy,