Lens Account List Item

PreviousNext

A list item for a Lens account displaying the avatar, name, and handle.

Docs
lens-blockscomponent

Preview

Loading preview…
registry/new-york/components/account/lens-account-list-item.tsx
"use client";

import { Account, AuthenticatedUser } from "@lens-protocol/react";
import { Avatar, AvatarFallback, AvatarImage } from "@/registry/new-york/ui/avatar";
import { CheckCircle2Icon, ChevronRight, Loader, UserCircle2 } from "lucide-react";
import { truncateAddress } from "@/registry/new-york/lib/lens-utils";
import { ReactNode } from "react";
import { isResult, Result } from "@/registry/new-york/lib/result";
import { LensAccountListItemSkeleton } from "@/registry/new-york/components/account/lens-account-list-item-skeleton";

type LensAccountListItemProps = {
  account: Account | Result<Account>;
  selectedAccount?: Account | null;
  onAccountSelected?: (account: Account) => void;
  authenticatedUser?: AuthenticatedUser | null;
  showChevron?: boolean;
  renderDivider?: () => ReactNode;
};

export const LensAccountListItem = ({
  account: accountRes,
  selectedAccount,
  onAccountSelected,
  authenticatedUser,
  showChevron = true,
  renderDivider,
}: LensAccountListItemProps) => {
  const isAccountResult = isResult(accountRes);
  const account = isAccountResult ? accountRes.data : accountRes;

  const isAuthenticated =
    authenticatedUser && authenticatedUser?.address.toLowerCase() === account?.address.toLowerCase();
  const isSelected = (onAccountSelected && isAuthenticated) ?? false;

  const Divider = renderDivider;

  if (isAccountResult && accountRes.error) {
    return null; // show error state
  }

  if (!account || (isAccountResult && accountRes.loading)) {
    return (
      <div className="w-full flex items-center text-start">
        <LensAccountListItemSkeleton showChevron={showChevron} />
      </div>
    );
  }

  return (
    <div>
      <button
        type="button"
        disabled={selectedAccount?.address !== undefined || isSelected}
        onClick={() => onAccountSelected?.(account)}
        className={`w-full flex items-center text-start p-2 gap-2 enabled:cursor-pointer enabled:hover:bg-neutral-100 enabled:focus:bg-neutral-100 focus:outline-0 rounded-sm ${isSelected ? "border" : "disabled:opacity-45"}`}
      >
        <Avatar className="flex-none w-10 h-10">
          <AvatarImage src={account.metadata?.picture} alt="Account avatar" />
          <AvatarFallback>
            <UserCircle2 className="w-10 h-10 opacity-45" />
          </AvatarFallback>
        </Avatar>
        <div className="flex-grow flex flex-col w-full min-w-0">
          <div className="flex gap-1 items-center">
            <span className="text-sm font-semibold">
              {account.metadata?.name ?? account.username?.localName ?? "[anonymous]"}
            </span>
          </div>
          <span className="text-xs opacity-65 truncate">
            {account.metadata?.name ? "@" + account.username?.localName : truncateAddress(account.address)}
          </span>
        </div>
        {selectedAccount?.address == account.address ? (
          <Loader className="animate-spin flex-none opacity-45 w-4 h-4" />
        ) : showChevron && isSelected ? (
          <CheckCircle2Icon className="flex-none opacity-45 w-4 h-4 text-accent-foreground" />
        ) : (
          showChevron && <ChevronRight className="flex-none opacity-45 w-4 h-4" />
        )}
      </button>
      {Divider && <Divider />}
    </div>
  );
};

Installation

npx shadcn@latest add @lens-blocks/account-list-item

Usage

import { AccountListItem } from "@/components/account-list-item"
<AccountListItem />