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
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ describe("UserPageBrainWrapper", () => {
},
identity: { id: "1" },
});
expect(routerPush).toHaveBeenCalledWith("/alice/identity");
expect(routerPush).toHaveBeenCalledWith("/alice");
});

it("shows drops when waves enabled", () => {
Expand Down
2 changes: 1 addition & 1 deletion __tests__/components/user/layout/UserPageTabs.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const renderTabs = (
country: string = "US"
) => {
(useRouter as jest.Mock).mockReturnValue({ push: jest.fn() });
(usePathname as jest.Mock).mockReturnValue("/[user]/identity");
(usePathname as jest.Mock).mockReturnValue("/[user]");
(useSearchParams as jest.Mock).mockReturnValue(new URLSearchParams());
(useParams as jest.Mock).mockReturnValue({ user: "testuser" });
capacitorMock.mockReturnValue({ isIos });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe('UserPageHeaderStats', () => {
/>
);
expect(screen.getByRole('link', { name: 'fmt-10 TDH' })).toHaveAttribute('href', '/bob/collected');
expect(screen.getByRole('link', { name: 'fmt-20 Rep' })).toHaveAttribute('href', '/bob/identity');
expect(screen.getByRole('link', { name: 'fmt-20 Rep' })).toHaveAttribute('href', '/bob');
expect(screen.getByRole('link', { name: 'fmt-3 TDH Rate' })).toHaveAttribute(
'href',
'/bob/stats?activity=tdh-history'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe("ProfileRatersTableItem", () => {
</table>
);
const link = screen.getByText("bob").closest("a")!;
expect(link).toHaveAttribute("href", "/bob/identity");
expect(link).toHaveAttribute("href", "/bob");
expect(screen.getByText("+5")).toBeInTheDocument();
expect(cicProps.level).toBe(3);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describe('UserSetUpProfileCta', () => {
useCtx.mockReturnValue({ address: '0xabc' });
renderWithProfile({ handle: null });
expect(screen.getByRole('button')).toBeInTheDocument();
expect(screen.getByRole('link')).toHaveAttribute('href', '/0xabc/identity');
expect(screen.getByRole('link')).toHaveAttribute('href', '/0xabc');
});

it('returns null when address missing or handle exists', () => {
Expand Down
4 changes: 2 additions & 2 deletions __tests__/pages/userPageRep.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function DummyRepTab({ profile }: { readonly profile: any }) {
}

const buildFactory = () =>
createUserTabPage({ subroute: "identity", metaLabel: "Identity", Tab: DummyRepTab });
createUserTabPage({ subroute: "", metaLabel: "Identity", Tab: DummyRepTab });

describe("rep page via createUserTabPage", () => {
beforeEach(() => {
Expand All @@ -79,7 +79,7 @@ describe("rep page via createUserTabPage", () => {
expect(userPageNeedsRedirect).toHaveBeenCalledWith({
profile: expect.any(Object),
req: { query: { user: "Alice", foo: "bar" } },
subroute: "identity",
subroute: "",
});
expect(redirectMock).not.toHaveBeenCalled();
});
Expand Down
21 changes: 21 additions & 0 deletions app/[user]/brain/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createUserTabPage } from "@/app/[user]/_lib/userTabPageFactory";
import UserPageBrainWrapper from "@/components/user/brain/UserPageBrainWrapper";
import {
USER_PAGE_TAB_IDS,
USER_PAGE_TAB_MAP,
} from "@/components/user/layout/userTabs.config";

const TAB_CONFIG = USER_PAGE_TAB_MAP[USER_PAGE_TAB_IDS.BRAIN];

const { Page, generateMetadata } = createUserTabPage({
subroute: TAB_CONFIG.route,
metaLabel: TAB_CONFIG.metaLabel,
Tab: ({ profile }) => (
<div className="tailwind-scope">
<UserPageBrainWrapper profile={profile} />
</div>
),
});

export default Page;
export { generateMetadata };
85 changes: 28 additions & 57 deletions app/[user]/identity/page.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,31 @@
import { createUserTabPage } from "@/app/[user]/_lib/userTabPageFactory";
import type { ActivityLogParams } from "@/components/profile-activity/ProfileActivityLogs";
import {
USER_PAGE_TAB_IDS,
USER_PAGE_TAB_MAP,
} from "@/components/user/layout/userTabs.config";
import UserPageRepWrapper from "@/components/user/rep/UserPageRepWrapper";
import type { ApiProfileRepRatesState } from "@/entities/IProfile";
import type { ApiIdentity } from "@/generated/models/ApiIdentity";
import { getProfileLogTypes } from "@/helpers/profile-logs.helpers";
import { ProfileActivityFilterTargetType } from "@/types/enums";

export interface UserPageRepPropsRepRates {
readonly ratings: ApiProfileRepRatesState;
readonly rater: string | null;
import { permanentRedirect } from "next/navigation";

type PageProps = {
readonly params?: Promise<{ user: string }> | undefined;
readonly searchParams?: Promise<Record<string, string | string[] | undefined>> | undefined;
};

function buildQueryString(
params: Record<string, string | string[] | undefined>
): string {
const query = new URLSearchParams();
for (const [key, value] of Object.entries(params)) {
if (value === undefined) continue;
const values = Array.isArray(value) ? value : [value];
values.forEach((v) => query.append(key, v));
}
return query.toString();
}

const getInitialActivityLogParams = (
handleOrWallet: string
): ActivityLogParams => ({
page: 1,
pageSize: 10,
logTypes: getProfileLogTypes({
logTypes: [],
}),
matter: null,
targetType: ProfileActivityFilterTargetType.ALL,
handleOrWallet,
groupId: null,
});

function RepTab({ profile }: { readonly profile: ApiIdentity }) {
const handleOrWallet = (
profile.handle ??
profile.wallets?.[0]?.wallet ??
""
).toLowerCase();

const initialActivityLogParams = getInitialActivityLogParams(handleOrWallet);

return (
<div className="tailwind-scope">
<UserPageRepWrapper
profile={profile}
initialActivityLogParams={initialActivityLogParams}
/>
</div>
);
export default async function IdentityRedirectPage({
params,
searchParams,
}: PageProps) {
const resolvedParams = params ? await params : undefined;
const user = resolvedParams?.user ?? "";
const resolvedSearchParams = searchParams ? await searchParams : undefined;

const qs = resolvedSearchParams ? buildQueryString(resolvedSearchParams) : "";
const destination = qs ? `/${user}?${qs}` : `/${user}`;
permanentRedirect(destination);
Comment thread
ragnep marked this conversation as resolved.
}

const TAB_CONFIG = USER_PAGE_TAB_MAP[USER_PAGE_TAB_IDS.REP];

const { Page, generateMetadata } = createUserTabPage({
subroute: TAB_CONFIG.route,
metaLabel: TAB_CONFIG.metaLabel,
Tab: RepTab,
});

export default Page;
export { generateMetadata };
53 changes: 46 additions & 7 deletions app/[user]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,59 @@
import { createUserTabPage } from "@/app/[user]/_lib/userTabPageFactory";
import UserPageBrainWrapper from "@/components/user/brain/UserPageBrainWrapper";
import type { ActivityLogParams } from "@/components/profile-activity/ProfileActivityLogs";
import {
USER_PAGE_TAB_IDS,
USER_PAGE_TAB_MAP,
} from "@/components/user/layout/userTabs.config";
import UserPageRepWrapper from "@/components/user/rep/UserPageRepWrapper";
import type { ApiProfileRepRatesState } from "@/entities/IProfile";
import type { ApiIdentity } from "@/generated/models/ApiIdentity";
import { getProfileLogTypes } from "@/helpers/profile-logs.helpers";
import { ProfileActivityFilterTargetType } from "@/types/enums";

const TAB_CONFIG = USER_PAGE_TAB_MAP[USER_PAGE_TAB_IDS.BRAIN];
export interface UserPageRepPropsRepRates {
readonly ratings: ApiProfileRepRatesState;
readonly rater: string | null;
}

const getInitialActivityLogParams = (
handleOrWallet: string
): ActivityLogParams => ({
page: 1,
pageSize: 10,
logTypes: getProfileLogTypes({
logTypes: [],
}),
matter: null,
targetType: ProfileActivityFilterTargetType.ALL,
handleOrWallet,
groupId: null,
});

function RepTab({ profile }: { readonly profile: ApiIdentity }) {
const handleOrWallet = (
profile.handle ??
profile.wallets?.[0]?.wallet ??
""
).toLowerCase();

const initialActivityLogParams = getInitialActivityLogParams(handleOrWallet);

return (
<div className="tailwind-scope">
<UserPageRepWrapper
profile={profile}
initialActivityLogParams={initialActivityLogParams}
/>
</div>
);
}

const TAB_CONFIG = USER_PAGE_TAB_MAP[USER_PAGE_TAB_IDS.REP];

const { Page, generateMetadata } = createUserTabPage({
subroute: TAB_CONFIG.route,
metaLabel: TAB_CONFIG.metaLabel,
Tab: ({ profile }) => (
<div className="tailwind-scope">
<UserPageBrainWrapper profile={profile} />
</div>
),
Tab: RepTab,
});

export default Page;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default function NotificationIdentityRating({
const myHandle = connectedProfile?.handle;
const getProfileLink = (): string | null => {
if (!myHandle) return null;
return `/${myHandle}/identity`;
return `/${myHandle}`;
};
const linkHref = getProfileLink();

Expand Down
5 changes: 3 additions & 2 deletions components/common/OverlappingAvatars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getScaledImageUri, ImageScale } from "@/helpers/image.helpers";
import { TOOLTIP_STYLES } from "@/helpers/tooltip.helpers";
import useIsTouchDevice from "@/hooks/useIsTouchDevice";
import Link from "next/link";
import type { MouseEvent } from "react";
import type { MouseEvent, ReactNode } from "react";
import { useId } from "react";
import { Tooltip } from "react-tooltip";

Expand All @@ -15,6 +15,7 @@ interface OverlappingAvatarItem {
readonly ariaLabel?: string;
readonly fallback?: string;
readonly title?: string;
readonly tooltipContent?: ReactNode;
}

interface OverlappingAvatarsProps {
Expand Down Expand Up @@ -116,7 +117,7 @@ export default function OverlappingAvatars({
delayShow={250}
style={TOOLTIP_STYLES}
>
{item.title}
{item.tooltipContent ?? item.title}
</Tooltip>
</span>
);
Expand Down
2 changes: 1 addition & 1 deletion components/react-query-wrapper/ReactQueryWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import type { UserPageRepPropsRepRates } from "@/app/[user]/identity/page";
import type { UserPageRepPropsRepRates } from "@/app/[user]/page";
import type {
ApiProfileRepRatesState,
ProfileActivityLog,
Expand Down
2 changes: 1 addition & 1 deletion components/user/brain/UserPageBrainWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default function UserPageBrainWrapper({
return;
}
if (connectedProfile || !address) {
router.push(`/${user}/identity`);
router.push(`/${user}`);
}
}, [connectedProfile, activeProfileProxy, address, showWaves]);

Expand Down
15 changes: 7 additions & 8 deletions components/user/layout/userTabs.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,22 @@ type UserPageTabDefinition = {
readonly badge?: string | undefined;
readonly isVisible?:
| ((context: UserPageVisibilityContext) => boolean)
| undefined
| undefined;
};

const TAB_DEFINITIONS = [
{
id: "rep",
title: "Identity",
route: "",
},
{
id: "brain",
title: "Brain",
route: "",
route: "brain",
isVisible: ({ showWaves }: UserPageVisibilityContext) => showWaves,
},
{
id: "rep",
title: "Identity",
route: "identity",
},
{
id: "collected",
title: "Collected",
route: "collected",
Expand Down Expand Up @@ -102,7 +101,7 @@ export const USER_PAGE_TAB_IDS = USER_PAGE_TABS.reduce((acc, tab) => {
return acc;
}, {} as { [K in Uppercase<UserPageTabKey>]: UserPageTabKey });

export const DEFAULT_USER_PAGE_TAB: UserPageTabKey = "collected";
export const DEFAULT_USER_PAGE_TAB: UserPageTabKey = "rep";

export function getUserPageTabByRoute(route: string) {
return USER_PAGE_TABS.find(
Expand Down
15 changes: 15 additions & 0 deletions components/user/rep/header/TopRaterAvatars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { ApiIdentity } from "@/generated/models/ApiIdentity";
import type { Page } from "@/helpers/Types";
import OverlappingAvatars from "@/components/common/OverlappingAvatars";
import { RateMatter } from "@/types/enums";
import { formatNumberWithCommas } from "@/helpers/Helpers";
import type { MouseEvent } from "react";

const STALE_TIME = 5 * 60 * 1000;
Expand Down Expand Up @@ -66,6 +67,10 @@ export default function TopRaterAvatars({

const raterHandles = ratersPage?.data.map((r) => r.handle) ?? [];

const ratingByHandle = new Map(
ratersPage?.data.map((r) => [r.handle.toLowerCase(), r.rating]) ?? []
);

const identityQueries = useQueries({
queries: raterHandles.map((handle) => ({
queryKey: [QueryKey.PROFILE, handle.toLowerCase()],
Expand All @@ -83,6 +88,15 @@ export default function TopRaterAvatars({
.map((q) => {
const identity = q.data!;
const target = identity.handle ?? identity.primary_wallet;
const rating = target
? ratingByHandle.get(target.toLowerCase())
: undefined;
const tooltipContent =
rating === undefined ? undefined : (
<span>
{target} &middot; {formatNumberWithCommas(rating)}
</span>
);
return {
key: target,
pfpUrl: identity.pfp ?? null,
Expand All @@ -92,6 +106,7 @@ export default function TopRaterAvatars({
? identity.handle.charAt(0).toUpperCase()
: "?",
title: target,
tooltipContent,
};
});

Expand Down
2 changes: 1 addition & 1 deletion components/user/rep/new-rep/UserPageRepNewRepSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ export default function UserPageRepNewRepSearch({
className="tw-hidden lg:tw-block tw-text-xs tw-font-medium tw-text-iron-500">
Grant Rep
</label>
<div className="tw-flex tw-items-center tw-gap-3 tw-text-xs tw-text-iron-500 tw-font-medium">
<div className="tw-flex tw-flex-wrap tw-items-center tw-gap-x-3 tw-gap-y-2 tw-text-xs tw-text-iron-500 tw-font-medium">
<span>
<span>Your available Rep:</span>
<span className="tw-ml-1 tw-font-semibold tw-text-iron-300">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export default function UserPageHeaderName({
</p>
</UserPageHeaderNameWrapper>
{profile?.handle && (
<div className="tw-flex tw-h-5 tw-w-5 tw-items-center tw-justify-center">
<div className="xl:tw-mt-1 tw-flex tw-h-5 tw-w-5 tw-items-center tw-justify-center">
<UserCICTypeIconWrapper profile={profile} />
</div>
)}
Expand Down
Loading