chore: cleanup

This commit is contained in:
Alejandro Gómez
2026-03-04 17:12:35 +01:00
parent 26bb0713ac
commit 498007401d
8 changed files with 97 additions and 190 deletions

View File

@@ -4,7 +4,7 @@
* Compact log of relay operations for debugging and introspection.
*/
import { useState, useCallback, useMemo } from "react";
import { useState, useMemo } from "react";
import {
Check,
X,
@@ -45,7 +45,6 @@ import { EventErrorBoundary } from "./EventErrorBoundary";
type TabFilter = "all" | "publish" | "connect" | "auth" | "notice";
/** Map tab values to the EventLogType(s) they filter */
const TAB_TYPE_MAP: Record<TabFilter, EventLogType[] | undefined> = {
all: undefined,
publish: ["PUBLISH"],
@@ -62,6 +61,17 @@ const TAB_FILTERS: { value: TabFilter; label: string }[] = [
{ value: "notice", label: "Notice" },
];
// ============================================================================
// Constants
// ============================================================================
const AUTH_STATUS_TOOLTIP: Record<string, string> = {
challenge: "Auth challenge",
success: "Auth success",
failed: "Auth failed",
rejected: "Auth rejected",
};
// ============================================================================
// Shared row layout
// ============================================================================
@@ -284,13 +294,6 @@ function ConnectEntry({ entry }: { entry: ConnectLogEntry }) {
function AuthEntry({ entry }: { entry: AuthLogEntry }) {
const [expanded, setExpanded] = useState(false);
const statusTooltip: Record<string, string> = {
challenge: "Auth challenge",
success: "Auth success",
failed: "Auth failed",
rejected: "Auth rejected",
};
return (
<EntryRow
icon={
@@ -304,7 +307,7 @@ function AuthEntry({ entry }: { entry: AuthLogEntry }) {
<ShieldAlert className="size-3.5 text-muted-foreground" />
)
}
tooltip={statusTooltip[entry.status] ?? "Auth"}
tooltip={AUTH_STATUS_TOOLTIP[entry.status] ?? "Auth"}
timestamp={entry.timestamp}
expanded={expanded}
onToggle={() => setExpanded(!expanded)}
@@ -389,6 +392,16 @@ function LogEntryRenderer({
// Main Component
// ============================================================================
function getTabCount(
tab: TabFilter,
totalCount: number,
typeCounts: Record<string, number>,
): number {
const types = TAB_TYPE_MAP[tab];
if (!types) return totalCount;
return types.reduce((sum, t) => sum + (typeCounts[t] || 0), 0);
}
export function EventLogViewer() {
const [activeTab, setActiveTab] = useState<TabFilter>("all");
@@ -400,33 +413,7 @@ export function EventLogViewer() {
retryRelay,
totalCount,
typeCounts,
} = useEventLog({
types: filterTypes,
});
/** Get count for a tab filter */
const getTabCount = useCallback(
(tab: TabFilter): number => {
const types = TAB_TYPE_MAP[tab];
if (!types) return totalCount;
return types.reduce((sum, t) => sum + (typeCounts[t] || 0), 0);
},
[totalCount, typeCounts],
);
const handleRetry = useCallback(
async (entryId: string) => {
await retryFailedRelays(entryId);
},
[retryFailedRelays],
);
const handleRetryRelay = useCallback(
async (entryId: string, relay: string) => {
await retryRelay(entryId, relay);
},
[retryRelay],
);
} = useEventLog({ types: filterTypes });
return (
<div className="h-full flex flex-col">
@@ -437,20 +424,23 @@ export function EventLogViewer() {
onValueChange={(v) => setActiveTab(v as TabFilter)}
>
<TabsList className="h-7">
{TAB_FILTERS.map((tab) => (
<TabsTrigger
key={tab.value}
value={tab.value}
className="text-xs px-1.5 h-5 gap-1"
>
{tab.label}
{getTabCount(tab.value) > 0 && (
<span className="text-[10px] tabular-nums text-muted-foreground">
{getTabCount(tab.value)}
</span>
)}
</TabsTrigger>
))}
{TAB_FILTERS.map((tab) => {
const count = getTabCount(tab.value, totalCount, typeCounts);
return (
<TabsTrigger
key={tab.value}
value={tab.value}
className="text-xs px-1.5 h-5 gap-1"
>
{tab.label}
{count > 0 && (
<span className="text-[10px] tabular-nums text-muted-foreground">
{count}
</span>
)}
</TabsTrigger>
);
})}
</TabsList>
</Tabs>
@@ -476,8 +466,8 @@ export function EventLogViewer() {
<LogEntryRenderer
key={entry.id}
entry={entry}
onRetry={handleRetry}
onRetryRelay={handleRetryRelay}
onRetry={retryFailedRelays}
onRetryRelay={retryRelay}
/>
))
)}

View File

@@ -66,6 +66,7 @@ export function EmojiPickerDialog({
const [searchResults, setSearchResults] = useState<EmojiSearchResult[]>([]);
const [selectedIndex, setSelectedIndex] = useState(0);
const virtuosoRef = useRef<VirtuosoHandle>(null);
const searchInputRef = useRef<HTMLInputElement>(null);
// Use the same emoji search hook as chat autocomplete
const { service } = useEmojiSearch();
@@ -247,7 +248,13 @@ export function EmojiPickerDialog({
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-xs p-0 gap-0 overflow-hidden">
<DialogContent
className="max-w-xs p-0 gap-0 overflow-hidden"
onOpenAutoFocus={(e) => {
e.preventDefault();
searchInputRef.current?.focus();
}}
>
{/* Top emojis — recently used quick-picks.
This section also provides natural spacing for the dialog close (X) button,
which is absolutely positioned at top-right of the dialog. */}
@@ -289,13 +296,13 @@ export function EmojiPickerDialog({
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" />
<Input
ref={searchInputRef}
type="text"
placeholder="Search emojis..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onKeyDown={handleKeyDown}
className="pl-9"
autoFocus
/>
</div>
</div>