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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
/test-results/
/playwright-report/
/playwright/.cache/
/.playwright-mcp/
/storageState.json
/playwright/.auth/
storageState.json
Expand Down
9 changes: 0 additions & 9 deletions .playwright-mcp/page-2026-03-31T06-27-58-535Z.yml

This file was deleted.

Binary file removed .playwright-mcp/page-2026-03-31T06-28-02-990Z.png
Binary file not shown.
4 changes: 0 additions & 4 deletions .playwright-mcp/page-2026-03-31T13-04-45-965Z.yml

This file was deleted.

4 changes: 0 additions & 4 deletions .playwright-mcp/page-2026-03-31T13-15-19-347Z.yml

This file was deleted.

2 changes: 1 addition & 1 deletion components/brain/my-stream/MyStreamWaveLeaderboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ const MyStreamWaveLeaderboard: React.FC<MyStreamWaveLeaderboardProps> = ({
</div>

{/* Content section */}
<div className="tw-min-w-0">
<div className="tw-min-w-0 tw-pb-[calc(env(safe-area-inset-bottom,0px)+1.5rem)]">
<AnimatePresence>
{showToggleableDropInput && (
<motion.div
Expand Down
91 changes: 91 additions & 0 deletions components/waves/drops/identity/IdentityProfileSupplement.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import type { ApiProfileRepCategorySummary } from "@/generated/models/ApiProfileRepCategorySummary";
import { formatNumberWithCommas } from "@/helpers/Helpers";

type IdentityProfileSupplementProfile = {
readonly bio?: string | null | undefined;
readonly top_rep_categories?:
| readonly ApiProfileRepCategorySummary[]
| null
| undefined;
};

type IdentityProfileSupplementVariant = "default" | "chat" | "compact";

interface IdentityProfileSupplementProps {
readonly profile: IdentityProfileSupplementProfile;
readonly variant: IdentityProfileSupplementVariant;
readonly maxRepCategories?: number | undefined;
}

const normalizeBio = (bio: string | null | undefined): string | null => {
const trimmedBio = bio?.trim();
return trimmedBio && trimmedBio.length > 0 ? trimmedBio : null;
};

const getRepCategories = (
categories: readonly ApiProfileRepCategorySummary[] | null | undefined
): ApiProfileRepCategorySummary[] =>
(categories ?? []).filter((category) => category.category.trim().length > 0);

const formatSignedRep = (rep: number): string => {
const formattedValue = formatNumberWithCommas(Math.abs(rep));

if (rep > 0) {
return `+${formattedValue}`;
}

if (rep < 0) {
return `-${formattedValue}`;
}

return formattedValue;
};

export default function IdentityProfileSupplement({
profile,
variant,
maxRepCategories,
}: IdentityProfileSupplementProps) {
const bio = normalizeBio(profile.bio);
const repCategories = getRepCategories(profile.top_rep_categories);
const visibleRepCategories =
maxRepCategories === undefined
? repCategories
: repCategories.slice(0, maxRepCategories);

if (!bio && visibleRepCategories.length === 0) {
return null;
}

return (
<div>
{bio && (
<p
className={`tw-mb-3 tw-line-clamp-2 tw-whitespace-pre-line tw-break-words tw-leading-relaxed tw-text-iron-300 ${
variant === "default"
? "tw-text-sm tw-font-medium tw-leading-6"
: "tw-text-[13px] tw-font-medium tw-leading-5"
}`}
>
{bio}
</p>
)}

{visibleRepCategories.length > 0 && (
<div className="tw-flex tw-flex-wrap tw-items-center tw-gap-1.5">
{visibleRepCategories.map((category) => (
<span
key={`${category.category}-${category.rep}`}
className="tw-inline-flex tw-items-center tw-gap-x-1 tw-rounded-lg tw-bg-black/50 tw-px-2 tw-py-1.5 tw-text-[10px] tw-font-medium tw-uppercase tw-leading-4 tw-tracking-widest tw-text-iron-200 tw-shadow-inner"
>
<span> {category.category}</span>
<span className="tw-text-iron-400">
{formatSignedRep(category.rep)}
</span>
</span>
))}
</div>
)}
</div>
);
}
43 changes: 27 additions & 16 deletions components/waves/drops/identity/WaveDropIdentity.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ParticipationIdentityProfileCard, {
type ParticipationIdentityProfileCardVariant,
} from "@/components/waves/drops/participation/ParticipationIdentityProfileCard";
import IdentityProfileSupplement from "@/components/waves/drops/identity/IdentityProfileSupplement";
import type { ApiDropMetadataResponse } from "@/generated/models/ApiDropMetadataResponse";
import type { ApiWaveMin } from "@/generated/models/ApiWaveMin";
import Link from "next/link";
Expand Down Expand Up @@ -104,24 +105,34 @@ export function WaveDropIdentity({
<div className={className}>
<div
data-testid={`${testIdPrefix}-compact`}
className="tw-flex tw-min-w-0 tw-items-center tw-gap-2 tw-rounded-lg tw-border tw-border-iron-800/50 tw-bg-iron-900/40 tw-px-2.5 tw-py-2"
className="tw-flex tw-min-w-0 tw-flex-col tw-gap-2 tw-rounded-lg tw-border tw-border-iron-800/50 tw-bg-iron-900/40 tw-px-3 tw-py-2.5"
>
<span className="tw-flex-shrink-0 tw-text-[10px] tw-font-medium tw-uppercase tw-tracking-[0.14em] tw-text-iron-500">
Identity
</span>
{identityProfile ? (
<Link
href={getIdentityHref(displayLabel)}
prefetch={false}
onClick={(event) => event.stopPropagation()}
className="tw-min-w-0 tw-truncate tw-text-sm tw-font-semibold tw-text-iron-100 tw-no-underline tw-transition tw-duration-300 tw-ease-out desktop-hover:hover:tw-text-iron-300"
>
{displayLabel}
</Link>
) : (
<span className="tw-min-w-0 tw-break-all tw-text-sm tw-font-semibold tw-text-iron-100">
{displayLabel}
<div className="tw-flex tw-min-w-0 tw-items-center tw-gap-2">
<span className="tw-flex-shrink-0 tw-text-xs tw-font-medium tw-uppercase tw-tracking-wide tw-text-iron-500">
Identity
</span>
{identityProfile ? (
<Link
href={getIdentityHref(displayLabel)}
prefetch={false}
onClick={(event) => event.stopPropagation()}
className="tw-min-w-0 tw-truncate tw-text-sm tw-font-semibold tw-text-iron-100 tw-no-underline tw-transition tw-duration-300 tw-ease-out desktop-hover:hover:tw-text-iron-300"
>
{displayLabel}
</Link>
) : (
<span className="tw-min-w-0 tw-break-all tw-text-sm tw-font-semibold tw-text-iron-100">
{displayLabel}
</span>
)}
</div>

{identityProfile && (
<IdentityProfileSupplement
profile={identityProfile}
variant="compact"
maxRepCategories={3}
/>
)}
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions components/waves/drops/identityDisplay.helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ApiDropMetadataResponse } from "@/generated/models/ApiDropMetadataResponse";
import type { ApiProfileMin } from "@/generated/models/ApiProfileMin";
import type { ApiDropResolvedIdentityProfile } from "@/generated/models/ApiDropResolvedIdentityProfile";
import type { ApiWaveMin } from "@/generated/models/ApiWaveMin";
import { ApiWaveParticipationSubmissionStrategyType } from "@/generated/models/ApiWaveParticipationSubmissionStrategyType";
import {
Expand Down Expand Up @@ -33,7 +33,7 @@ export const getDropIdentityProfile = ({
}: {
readonly wave: Pick<ApiWaveMin, "submission_type"> | null | undefined;
readonly metadata: readonly ApiDropMetadataResponse[] | null | undefined;
}): ApiProfileMin | null => {
}): ApiDropResolvedIdentityProfile | null => {
if (!isIdentitySubmissionWave(wave)) {
return null;
}
Expand Down
10 changes: 9 additions & 1 deletion components/waves/drops/participation/EndedParticipationDrop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import UserCICAndLevel, {
} from "@/components/user/utils/UserCICAndLevel";
import type { ApiDrop } from "@/generated/models/ApiDrop";
import { getTimeAgoShort } from "@/helpers/Helpers";
import { areSameProfileIdentity } from "@/helpers/ProfileHelpers";
import { getWaveRoute } from "@/helpers/navigation.helpers";
import type { ExtendedDrop } from "@/helpers/waves/drop.helpers";
import useIsMobileDevice from "@/hooks/isMobileDevice";
Expand Down Expand Up @@ -59,6 +60,12 @@ export default function EndedParticipationDrop({
wave: drop.wave,
metadata: drop.metadata,
});
const isSelfNominee = identityProfile
? areSameProfileIdentity({
left: drop.author,
right: identityProfile,
})
: false;

const [activePartIndex, setActivePartIndex] = useState(0);
const [longPressTriggered, setLongPressTriggered] = useState(false);
Expand Down Expand Up @@ -200,11 +207,12 @@ export default function EndedParticipationDrop({
</div>

{identityProfile && (
<div className="sm:tw-ml-[3.25rem]">
<div className="tw-ml-[3.25rem]">
<ParticipationIdentityProfileCard
profile={identityProfile}
contextId={drop.id}
variant="chat"
showIdentityHeader={!isSelfNominee}
/>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { MobileVotingModal, VotingModal } from "@/components/voting";
import VotingModalButton from "@/components/voting/VotingModalButton";
import type { ExtendedDrop } from "@/helpers/waves/drop.helpers";
import { areSameProfileIdentity } from "@/helpers/ProfileHelpers";
import type { ActiveDropState } from "@/types/dropInteractionTypes";
import type { ApiDrop } from "@/generated/models/ApiDrop";
import { useCallback, useState } from "react";
Expand Down Expand Up @@ -59,6 +60,12 @@ export default function OngoingParticipationDrop({
wave: drop.wave,
metadata: drop.metadata,
});
const isSelfNominee = identityProfile
? areSameProfileIdentity({
left: drop.author,
right: identityProfile,
})
: false;

const [activePartIndex, setActivePartIndex] = useState(0);
const [longPressTriggered, setLongPressTriggered] = useState(false);
Expand Down Expand Up @@ -117,11 +124,12 @@ export default function OngoingParticipationDrop({

<div className="tw-flex tw-w-full tw-flex-col">
{identityProfile && (
<div className="tw-px-4 sm:tw-ml-[3.25rem]">
<div className="tw-ml-[3.25rem] tw-px-4">
<ParticipationIdentityProfileCard
profile={identityProfile}
contextId={drop.id}
variant="chat"
showIdentityHeader={!isSelfNominee}
/>
</div>
)}
Expand Down
Loading
Loading