AppOfTheDayWidget.tsx raw

   1  import { ChevronRightIcon } from "lucide-react";
   2  import { Link } from "react-router";
   3  import {
   4    appStoreApps,
   5    getAppStoreUrl,
   6  } from "src/components/connections/SuggestedAppData";
   7  import {
   8    Card,
   9    CardContent,
  10    CardDescription,
  11    CardHeader,
  12    CardTitle,
  13  } from "src/components/ui/card";
  14  
  15  export function AppOfTheDayWidget() {
  16    function seededRandom(seed: number) {
  17      const x = Math.sin(seed) * 10000;
  18      return x - Math.floor(x);
  19    }
  20  
  21    // filter out apps which already have a widget
  22    const excludedAppIds = ["alby-go", "zapplanner"];
  23    const apps = appStoreApps.filter((a) => !excludedAppIds.includes(a.id));
  24  
  25    // eslint-disable-next-line react-hooks/purity
  26    const daysSinceEpoch = Math.floor(Date.now() / (1000 * 60 * 60 * 24));
  27    const todayIndex = Math.floor(seededRandom(daysSinceEpoch) * apps.length);
  28    const app = apps[todayIndex];
  29  
  30    return (
  31      <Card>
  32        <CardHeader className="px-6 pb-0">
  33          <CardTitle className="text-base font-semibold">
  34            App of the Day
  35          </CardTitle>
  36        </CardHeader>
  37        <CardContent className="px-6 pt-0">
  38          <Link
  39            to={getAppStoreUrl(app)}
  40            className="group flex items-center gap-4 rounded-md"
  41          >
  42            <img
  43              src={app.logo}
  44              alt={`${app.title} logo`}
  45              className="size-15 rounded-lg object-cover"
  46            />
  47            <div className="min-w-0 flex-1">
  48              <p className="truncate text-base font-semibold text-foreground">
  49                {app.title}
  50              </p>
  51              <CardDescription className="line-clamp-2 text-sm leading-5 text-muted-foreground">
  52                {app.description}
  53              </CardDescription>
  54            </div>
  55            <ChevronRightIcon className="size-4 shrink-0 text-muted-foreground transition-transform group-hover:translate-x-0.5" />
  56          </Link>
  57        </CardContent>
  58      </Card>
  59    );
  60  }
  61