1
0
Fork 0

feat: various cards

fix-1
Paul Makles 2023-07-27 12:28:02 +01:00
parent ef53ec696f
commit b45973482d
No known key found for this signature in database
GPG Key ID: 5059F398521BB0F6
7 changed files with 284 additions and 130 deletions

View File

@ -0,0 +1,43 @@
import { Channel } from "revolt-api";
import { Card, CardDescription, CardHeader, CardTitle } from "../ui/card";
import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
import Link from "next/link";
export function ChannelCard({
channel,
subtitle,
}: {
channel: Channel;
subtitle: string;
}) {
if (channel.channel_type === "SavedMessages")
return <div>Refusing to render.</div>;
const name =
channel.channel_type === "DirectMessage" ? "Direct Message" : channel.name;
return (
<Card className="shadow-none">
<CardHeader>
<CardTitle>
<Avatar>
{channel.channel_type !== "DirectMessage" && (
<AvatarImage
src={`https://autumn.revolt.chat/icons/${channel.icon?._id}`}
/>
)}
<AvatarFallback>
{name
.split(" ")
.slice(0, 2)
.map((x) => String.fromCodePoint(x.codePointAt(0) ?? 32) ?? "")
.join("")}
</AvatarFallback>
</Avatar>
{name}
</CardTitle>
<CardDescription>{subtitle}</CardDescription>
</CardHeader>
</Card>
);
}

View File

@ -0,0 +1,124 @@
"use client";
import { Message, User } from "revolt-api";
import { Avatar, AvatarImage } from "../ui/avatar";
import { Image as ImageIcon, Pencil } from "lucide-react";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "../ui/alert-dialog";
import dayjs from "dayjs";
import { decodeTime } from "ulid";
import calendarPlugin from "dayjs/plugin/calendar";
import Link from "next/link";
dayjs.extend(calendarPlugin);
export function CompactMessage({
message,
users,
hideUser,
}: {
message: Message;
users?: Record<string, User>;
hideUser?: boolean;
}) {
const user = users?.[message.author];
return (
<AlertDialog>
<AlertDialogTrigger className="w-full">
<div
className="flex gap-2"
onMouseUp={(e) =>
e.button === 1 &&
window.open(`/panel/inspect/message/${message._id}`, "_blank")
}
>
<div
className="flex-shrink-0 w-12 text-center"
title={dayjs(decodeTime(message._id)).calendar()}
>
{dayjs(decodeTime(message._id)).format("HH:mm")}
</div>
{!hideUser && (
<div
className="flex-1 min-w-0 overflow-ellipsis overflow-hidden text-right"
title={`${user?.username}#${user?.discriminator}`}
>
{user?.avatar && (
<Avatar className="w-4 h-4 inline-block align-middle mr-1">
<AvatarImage
src={`https://autumn.revolt.chat/avatars/${user.avatar._id}`}
/>
</Avatar>
)}
{user?.username}{" "}
{message.edited && (
<Pencil size={12} className="inline align-middle" />
)}
</div>
)}
<div className="flex-[2] min-w-0 overflow-ellipsis overflow-hidden text-left">
{(message.attachments || message.embeds) && (
<ImageIcon size={12} className="inline align-middle" />
)}{" "}
{message.content ?? <span className="text-gray-500">No text.</span>}
</div>
</div>
</AlertDialogTrigger>
<AlertDialogContent className="max-h-[100vh] overflow-y-auto">
<AlertDialogHeader className="min-w-0">
<AlertDialogTitle>
{user?.avatar && (
<Avatar className="inline-block align-middle mr-1">
<AvatarImage
src={`https://autumn.revolt.chat/avatars/${user.avatar._id}`}
/>
</Avatar>
)}{" "}
{user?.username}#{user?.discriminator}
</AlertDialogTitle>
<AlertDialogDescription className="flex flex-col gap-2 max-w-full">
{message.content && <span>{message.content}</span>}
{message.attachments?.map((attachment) =>
attachment.metadata.type === "Image" ? (
<a
target="_blank"
className="w-fit"
key={attachment._id}
href={`https://autumn.revolt.chat/attachments/${attachment._id}`}
>
<img
className="max-h-[240px] object-contain"
src={`https://autumn.revolt.chat/attachments/${attachment._id}`}
/>
</a>
) : (
"unsupported"
)
)}
{message.embeds?.map((embed, index) => (
<pre key={index} className="overflow-auto max-w-full">
<code>{JSON.stringify(embed, null, 2)}</code>
</pre>
))}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<Link href={`/panel/inspect/message/${message._id}`}>
<AlertDialogCancel>Inspect</AlertDialogCancel>
</Link>
<AlertDialogAction>Close</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}

View File

@ -0,0 +1,35 @@
"use client";
import { useState } from "react";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "../ui/card";
import { Button } from "../ui/button";
export function JsonCard({ obj }: { obj: any }) {
const [shown, setShown] = useState(false);
return (
<Card className="shadow-none">
<CardHeader>
<CardTitle>Document</CardTitle>
<CardDescription>Raw JSON representation</CardDescription>
</CardHeader>
<CardContent>
{shown ? (
<pre>
<code>{JSON.stringify(obj, null, 2)}</code>
</pre>
) : (
<Button variant="secondary" onClick={() => setShown(true)}>
Show
</Button>
)}
</CardContent>
</Card>
);
}

View File

@ -1,4 +1,4 @@
import { Message, SnapshotContent, User } from "revolt-api";
import { SnapshotContent } from "revolt-api";
import {
Card,
CardContent,
@ -7,100 +7,7 @@ import {
CardTitle,
} from "../ui/card";
import { fetchUsersById } from "@/lib/db";
import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
import { Image as ImageIcon } from "lucide-react";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "../ui/alert-dialog";
function Message({
message,
users,
}: {
message: Message;
users?: Record<string, User>;
}) {
const user = users?.[message.author];
return (
<AlertDialog>
<AlertDialogTrigger className="w-full">
<div className="flex gap-2">
<div
className="flex-1 min-w-0 overflow-ellipsis overflow-hidden text-right"
title={`${user?.username}#${user?.discriminator}`}
>
{user?.avatar && (
<Avatar className="w-4 h-4 inline-block align-middle mr-1">
<AvatarImage
src={`https://autumn.revolt.chat/avatars/${user.avatar._id}`}
/>
</Avatar>
)}
{user?.username}
</div>
<div className="flex-[2] min-w-0 overflow-ellipsis overflow-hidden text-left">
{(message.attachments || message.embeds) && (
<ImageIcon size={16} className="inline align-middle" />
)}{" "}
{message.content ?? <span className="text-gray-500">No text.</span>}
</div>
</div>
</AlertDialogTrigger>
<AlertDialogContent className="max-h-[100vh] overflow-y-auto">
<AlertDialogHeader className="min-w-0">
<AlertDialogTitle>
{user?.avatar && (
<Avatar className="inline-block align-middle mr-1">
<AvatarImage
src={`https://autumn.revolt.chat/avatars/${user.avatar._id}`}
/>
</Avatar>
)}{" "}
{user?.username}#{user?.discriminator}
</AlertDialogTitle>
<AlertDialogDescription className="flex flex-col gap-2 max-w-full">
{message.content && <span>{message.content}</span>}
{message.attachments?.map((attachment) =>
attachment.metadata.type === "Image" ? (
<a
target="_blank"
className="w-fit"
key={attachment._id}
href={`https://autumn.revolt.chat/attachments/${attachment._id}`}
>
<img
className="max-h-[240px] object-contain"
src={`https://autumn.revolt.chat/attachments/${attachment._id}`}
/>
</a>
) : (
"unsupported"
)
)}
{message.embeds?.map((embed, index) => (
<pre key={index} className="overflow-auto max-w-full">
<code>{JSON.stringify(embed, null, 2)}</code>
</pre>
))}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Delete</AlertDialogCancel>
<AlertDialogAction>Close</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}
import { CompactMessage } from "./CompactMessage";
export async function MessageContextCard({
snapshot,
@ -133,13 +40,13 @@ export async function MessageContextCard({
<CardContent>
<div className="opacity-40">
{[...(snapshot._prior_context ?? [])].reverse()?.map((message) => (
<Message key={message._id} message={message} users={users} />
<CompactMessage key={message._id} message={message} users={users} />
))}
</div>
<Message message={snapshot} users={users} />
<CompactMessage message={snapshot} users={users} />
<div className="opacity-40">
{snapshot._leading_context?.map((message) => (
<Message key={message._id} message={message} users={users} />
<CompactMessage key={message._id} message={message} users={users} />
))}
</div>
</CardContent>

View File

@ -2,13 +2,22 @@ import Link from "next/link";
import { Report } from "revolt-api";
import { Card, CardDescription, CardHeader, CardTitle } from "../ui/card";
import { Badge } from "../ui/badge";
import dayjs from "dayjs";
import { decodeTime } from "ulid";
import relativeTime from "dayjs/plugin/relativeTime";
dayjs.extend(relativeTime);
export function ReportCard({ report }: { report: Report }) {
return (
<Link href={`/panel/reports/${report._id}`}>
<Card className="transition-all hover:-translate-y-1 hover:shadow-md">
<CardHeader>
<CardTitle className="overflow-ellipsis whitespace-nowrap overflow-x-clip">
<CardTitle
className={`overflow-ellipsis whitespace-nowrap overflow-x-clip ${
report.status !== "Created" ? "text-gray-500" : ""
}`}
>
{report.content.report_reason.includes("Illegal") && (
<Badge className="align-middle" variant="destructive">
Urgent
@ -18,7 +27,8 @@ export function ReportCard({ report }: { report: Report }) {
</CardTitle>
<CardDescription>
{report._id.toString().substring(20, 26)} &middot;{" "}
{report.content.report_reason} &middot; {report.content.type}
{report.content.report_reason} &middot; {report.content.type}{" "}
&middot; {dayjs(decodeTime(report._id)).fromNow()}
</CardDescription>
</CardHeader>
</Card>

View File

@ -0,0 +1,35 @@
import { Server } from "revolt-api";
import { Card, CardDescription, CardHeader, CardTitle } from "../ui/card";
import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
import Link from "next/link";
export function ServerCard({
server,
subtitle,
}: {
server: Server;
subtitle: string;
}) {
return (
<Card className="shadow-none">
<CardHeader>
<CardTitle>
<Avatar>
<AvatarImage
src={`https://autumn.revolt.chat/icons/${server.icon?._id}`}
/>
<AvatarFallback>
{server.name
.split(" ")
.slice(0, 2)
.map((x) => String.fromCodePoint(x.codePointAt(0) ?? 32) ?? "")
.join("")}
</AvatarFallback>
</Avatar>
{server.name}
</CardTitle>
<CardDescription>{subtitle}</CardDescription>
</CardHeader>
</Card>
);
}

View File

@ -1,38 +1,38 @@
import { User } from "revolt-api";
import { Card, CardDescription, CardHeader, CardTitle } from "../ui/card";
import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
import Link from "next/link";
import { Badge } from "../ui/badge";
export function UserCard({ user, subtitle }: { user: User; subtitle: string }) {
return (
<Link href={`/panel/inspect/${user._id}`}>
<Card
className="shadow-none bg-no-repeat bg-right text-left"
style={{
backgroundImage: user.profile?.background
? `linear-gradient(to right, white, rgba(255,0,0,0)), url('https://autumn.revolt.chat/backgrounds/${user.profile.background._id}')`
: "",
backgroundSize: "75%",
}}
>
<CardHeader>
<CardTitle>
<Avatar>
<AvatarImage
src={`https://autumn.revolt.chat/avatars/${user.avatar?._id}`}
/>
<AvatarFallback>
{(user.display_name ?? user.username)
.split(" ")
.slice(0, 2)
.join("")}
</AvatarFallback>
</Avatar>
{user.username}#{user.discriminator} {user.display_name}
</CardTitle>
<CardDescription>{subtitle}</CardDescription>
</CardHeader>
</Card>
</Link>
<Card
className="shadow-none bg-no-repeat bg-right text-left"
style={{
backgroundImage: user.profile?.background
? `linear-gradient(to right, white, rgba(255,0,0,0)), url('https://autumn.revolt.chat/backgrounds/${user.profile.background._id}')`
: "",
backgroundSize: "75%",
}}
>
<CardHeader>
<CardTitle className="overflow-hidden overflow-ellipsis whitespace-nowrap">
<Avatar>
<AvatarImage
src={`https://autumn.revolt.chat/avatars/${user.avatar?._id}`}
/>
<AvatarFallback className="overflow-hidden overflow-ellipsis whitespace-nowrap">
{(user.display_name ?? user.username)
.split(" ")
.slice(0, 2)
.map((x) => String.fromCodePoint(x.codePointAt(0) ?? 32) ?? "")
.join("")}
</AvatarFallback>
</Avatar>
{user.bot && <Badge className="align-middle">Bot</Badge>}{" "}
{user.username}#{user.discriminator} {user.display_name}
</CardTitle>
<CardDescription>{subtitle}</CardDescription>
</CardHeader>
</Card>
);
}