feat(shared): reorder AppError constructor to (message, code, status), add singleton logger, retry and TTL cache utilities
This commit is contained in:
@@ -1,12 +1 @@
|
||||
export interface Guild {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: string | null;
|
||||
}
|
||||
|
||||
export interface Channel {
|
||||
id: string;
|
||||
name: string;
|
||||
type?: string;
|
||||
parentId?: string | null;
|
||||
}
|
||||
export { Guild, Channel } from "../../shared/api/client";
|
||||
|
||||
@@ -1,17 +1 @@
|
||||
export type MediaMode = "music" | "screen";
|
||||
|
||||
export interface MediaItem {
|
||||
id?: string;
|
||||
source: string;
|
||||
title: string;
|
||||
mode?: MediaMode;
|
||||
durationMs?: number | null;
|
||||
thumbnailUrl?: string | null;
|
||||
}
|
||||
|
||||
export interface MediaState {
|
||||
playing: boolean;
|
||||
musicVolume: number;
|
||||
current: MediaItem | null;
|
||||
queue: MediaItem[];
|
||||
}
|
||||
export { MediaMode, MediaItem, MediaState } from "../../shared/api/client";
|
||||
|
||||
@@ -14,6 +14,15 @@ export interface MessageMetadata {
|
||||
embeds?: Array<{ title?: string; image?: string; thumbnail?: string }>;
|
||||
}
|
||||
|
||||
export function parseMetadata(value: string | null): MessageMetadata {
|
||||
if (!value) return {};
|
||||
try {
|
||||
return JSON.parse(value) as MessageMetadata;
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export interface MessageRecord {
|
||||
id: string;
|
||||
guild_id: string;
|
||||
|
||||
@@ -1,14 +1 @@
|
||||
export type DashboardTab = "live" | "messages" | "analytics";
|
||||
|
||||
export interface UIState {
|
||||
selectedGuild?: string;
|
||||
selectedVoiceGuild?: string;
|
||||
selectedVoiceChannel?: string;
|
||||
selectedTextGuild?: string;
|
||||
selectedTextChannel?: string;
|
||||
selectedAnalyticsGuild?: string;
|
||||
selectedAnalyticsChannel?: string;
|
||||
activeTab?: DashboardTab;
|
||||
isListening?: boolean;
|
||||
isStreaming?: boolean;
|
||||
}
|
||||
export { DashboardTab, UIState } from "../../shared/api/client";
|
||||
|
||||
@@ -1,14 +1 @@
|
||||
export interface VoiceStatus {
|
||||
connected: boolean;
|
||||
activeGuildId?: string | null;
|
||||
activeChannelId?: string | null;
|
||||
activeChannelName?: string | null;
|
||||
}
|
||||
|
||||
export interface ActiveSpeaker {
|
||||
id?: string;
|
||||
userId?: string;
|
||||
username: string;
|
||||
avatar: string;
|
||||
speaking: boolean;
|
||||
}
|
||||
export { VoiceStatus, ActiveSpeaker } from "../../shared/api/client";
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { Download, Mic } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Badge, Button, Skeleton } from "../../../shared/ui";
|
||||
import { formatBytes, formatDate } from "../../../shared/lib/utils";
|
||||
|
||||
interface VoiceRecording {
|
||||
id: string;
|
||||
@@ -21,16 +22,6 @@ interface VoiceRecording {
|
||||
uploaded_at: number | null;
|
||||
}
|
||||
|
||||
function formatDate(value: number): string {
|
||||
return new Date(value).toLocaleString();
|
||||
}
|
||||
|
||||
function formatBytes(value: number): string {
|
||||
if (value < 1024) return `${value} B`;
|
||||
if (value < 1024 * 1024) return `${(value / 1024).toFixed(1)} KB`;
|
||||
return `${(value / 1024 / 1024).toFixed(1)} MB`;
|
||||
}
|
||||
|
||||
export function RecordingsSubPanel() {
|
||||
const [recordings, setRecordings] = useState<VoiceRecording[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import type { MessageRecord } from "../../../shared/api/client";
|
||||
|
||||
interface MessageMetadata {
|
||||
stickers?: Array<{ name?: string; url?: string }>;
|
||||
attachments?: Array<{ name: string; url: string; contentType?: string }>;
|
||||
embeds?: Array<{ title?: string; image?: string; thumbnail?: string }>;
|
||||
}
|
||||
import { parseMetadata } from "../../../entities/message/types";
|
||||
|
||||
interface ImageItem {
|
||||
url: string;
|
||||
@@ -13,15 +8,6 @@ interface ImageItem {
|
||||
message: MessageRecord;
|
||||
}
|
||||
|
||||
function parseMetadata(value: string | null): MessageMetadata {
|
||||
if (!value) return {};
|
||||
try {
|
||||
return JSON.parse(value) as MessageMetadata;
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export function ImageGrid({ messages }: { messages: MessageRecord[] }) {
|
||||
const images: ImageItem[] = [];
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { Fragment, useMemo, useState } from "react";
|
||||
import type { MessageRecord } from "../../../shared/api/client";
|
||||
import { moderateMessage } from "../../../shared/api/client";
|
||||
import { Badge, Button, Skeleton } from "../../../shared/ui";
|
||||
import { parseMetadata } from "../../../entities/message/types";
|
||||
|
||||
const CUSTOM_EMOJI_REGEX = /<(a)?:([a-zA-Z0-9_]+):(\d+)>/g;
|
||||
|
||||
@@ -71,21 +72,6 @@ interface MessageCardProps {
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
interface MessageMetadata {
|
||||
stickers?: Array<{ name?: string; url?: string }>;
|
||||
attachments?: Array<{ name: string; url: string; contentType?: string }>;
|
||||
embeds?: Array<{ title?: string; image?: string; thumbnail?: string }>;
|
||||
}
|
||||
|
||||
function parseMetadata(value: string | null): MessageMetadata {
|
||||
if (!value) return {};
|
||||
try {
|
||||
return JSON.parse(value) as MessageMetadata;
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function parseStringList(value?: string | null): string[] {
|
||||
if (!value) return [];
|
||||
try {
|
||||
|
||||
@@ -112,6 +112,8 @@ export interface ActiveSpeaker {
|
||||
speaking: boolean;
|
||||
}
|
||||
|
||||
export type MediaMode = "music" | "screen";
|
||||
|
||||
export interface MediaItem {
|
||||
id?: string;
|
||||
source: string;
|
||||
|
||||
@@ -4,3 +4,15 @@ import { twMerge } from "tailwind-merge";
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
export function formatBytes(bytes: number): string {
|
||||
if (bytes === 0) return "0 Bytes";
|
||||
const k = 1024;
|
||||
const sizes = ["Bytes", "KB", "MB", "GB"];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return `${Math.round((bytes / Math.pow(k, i)) * 100) / 100} ${sizes[i]}`;
|
||||
}
|
||||
|
||||
export function formatDate(value: number): string {
|
||||
return new Date(value).toLocaleString();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user