LatestUsedAppsWidget.tsx raw

   1  import dayjs from "dayjs";
   2  import { ChevronRightIcon } from "lucide-react";
   3  import { Link } from "react-router";
   4  import AppAvatar from "src/components/AppAvatar";
   5  import { FormattedBitcoinAmount } from "src/components/FormattedBitcoinAmount";
   6  import {
   7    Card,
   8    CardContent,
   9    CardHeader,
  10    CardTitle,
  11  } from "src/components/ui/card";
  12  import { LinkButton } from "src/components/ui/custom/link-button";
  13  import { useApps } from "src/hooks/useApps";
  14  import { useTransactions } from "src/hooks/useTransactions";
  15  import { cn, getAppDisplayName } from "src/lib/utils";
  16  import { App } from "src/types";
  17  
  18  export function LatestUsedAppsWidget() {
  19    const { data: appsData } = useApps(
  20      3,
  21      undefined,
  22      undefined,
  23      "last_settled_transaction"
  24    );
  25    const apps = appsData?.apps;
  26    const usedApps = apps?.filter((x) => x.lastSettledTransactionAt);
  27  
  28    if (!usedApps?.length) {
  29      return null;
  30    }
  31  
  32    return (
  33      <Card>
  34        <CardHeader>
  35          <CardTitle className="flex items-center justify-between">
  36            <div>Recently Used Apps</div>
  37            <LinkButton to="/apps?tab=connected-apps" variant="ghost" size="sm">
  38              See All
  39            </LinkButton>
  40          </CardTitle>
  41        </CardHeader>
  42        <CardContent className="grid grid-cols-1 gap-4">
  43          {usedApps
  44            .sort(
  45              (a, b) =>
  46                new Date(b.lastSettledTransactionAt ?? 0).getTime() -
  47                new Date(a.lastSettledTransactionAt ?? 0).getTime()
  48            )
  49            .map((app) => (
  50              <RecentlyUsedAppRow key={app.id} app={app} />
  51            ))}
  52        </CardContent>
  53      </Card>
  54    );
  55  }
  56  
  57  function RecentlyUsedAppRow({ app }: { app: App }) {
  58    const { data: transactionsData } = useTransactions(app.id, false, 1, 1);
  59    const latestTransaction = transactionsData?.transactions[0];
  60  
  61    return (
  62      <Link to={`/apps/${app.id}`} className="group">
  63        <div className="flex items-center w-full gap-4">
  64          <AppAvatar app={app} className="w-12 h-12 rounded-lg" />
  65          <p className="text-sm font-medium flex-1 truncate">
  66            {getAppDisplayName(app.name)}
  67          </p>
  68          <div className="flex flex-col items-end">
  69            {latestTransaction && (
  70              <span
  71                className={cn(
  72                  "text-sm font-medium",
  73                  latestTransaction.type === "incoming" &&
  74                    "text-green-600 dark:text-emerald-500"
  75                )}
  76              >
  77                {latestTransaction.type === "outgoing" ? "-" : "+"}
  78                <FormattedBitcoinAmount
  79                  amountMsat={latestTransaction.amountMsat}
  80                />
  81              </span>
  82            )}
  83            <span className="text-xs text-muted-foreground">
  84              {app.lastSettledTransactionAt
  85                ? dayjs(app.lastSettledTransactionAt).fromNow()
  86                : "never"}
  87            </span>
  88          </div>
  89          <ChevronRightIcon className="size-4 shrink-0 text-muted-foreground transition-transform group-hover:translate-x-0.5" />
  90        </div>
  91      </Link>
  92    );
  93  }
  94