mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-10 07:27:23 +02:00
* fix: improve chat architecture robustness and error handling - Fix scroll-to-message index mismatch (was searching in wrong array) - Fix subscription memory leaks by tracking and cleaning up subscriptions - Add error handling for conversation resolution with retry UI - Add error handling for send message with toast notifications - Fix array mutation bugs in NIP-53 relay handling - Add type guards for LiveActivityMetadata - Fix RelaysDropdown O(n²) performance issue - Add loading state for send button * refactor: add stronger types and optimize message sorting - Add discriminated union types for ProtocolIdentifier (GroupIdentifier, LiveActivityIdentifier, DMIdentifier, NIP05Identifier, ChannelIdentifier) - Optimize message sorting using reverse() instead of full sort (O(n) vs O(n log n)) - Add type narrowing in adapter resolveConversation methods - Remove unused Observable import from ChatViewer --------- Co-authored-by: Claude <noreply@anthropic.com>
232 lines
5.1 KiB
TypeScript
232 lines
5.1 KiB
TypeScript
import type { NostrEvent } from "./nostr";
|
|
|
|
/**
|
|
* Chat protocol identifier
|
|
*/
|
|
export type ChatProtocol = "nip-c7" | "nip-17" | "nip-28" | "nip-29" | "nip-53";
|
|
|
|
/**
|
|
* Conversation type
|
|
*/
|
|
export type ConversationType = "dm" | "channel" | "group" | "live-chat";
|
|
|
|
/**
|
|
* Participant role in a conversation
|
|
*/
|
|
export type ParticipantRole = "admin" | "moderator" | "member" | "host";
|
|
|
|
/**
|
|
* Participant in a conversation
|
|
*/
|
|
export interface Participant {
|
|
pubkey: string;
|
|
role?: ParticipantRole;
|
|
permissions?: string[];
|
|
}
|
|
|
|
/**
|
|
* Live activity metadata for NIP-53
|
|
*/
|
|
export interface LiveActivityMetadata {
|
|
status: "planned" | "live" | "ended";
|
|
streaming?: string;
|
|
recording?: string;
|
|
starts?: number;
|
|
ends?: number;
|
|
hostPubkey: string;
|
|
currentParticipants?: number;
|
|
totalParticipants?: number;
|
|
hashtags: string[];
|
|
relays: string[];
|
|
}
|
|
|
|
/**
|
|
* Protocol-specific conversation metadata
|
|
*/
|
|
export interface ConversationMetadata {
|
|
// NIP-28 channel
|
|
channelEvent?: NostrEvent; // kind 40 creation event
|
|
|
|
// NIP-29 group
|
|
groupId?: string; // host'group-id format
|
|
relayUrl?: string; // Relay enforcing group rules
|
|
description?: string; // Group description
|
|
icon?: string; // Group icon/picture URL
|
|
|
|
// NIP-53 live chat
|
|
activityAddress?: {
|
|
kind: number;
|
|
pubkey: string;
|
|
identifier: string;
|
|
};
|
|
liveActivity?: LiveActivityMetadata;
|
|
|
|
// NIP-17 DM
|
|
encrypted?: boolean;
|
|
giftWrapped?: boolean;
|
|
}
|
|
|
|
/**
|
|
* Generic conversation abstraction
|
|
* Works across all messaging protocols
|
|
*/
|
|
export interface Conversation {
|
|
id: string; // Protocol-specific identifier
|
|
type: ConversationType;
|
|
protocol: ChatProtocol;
|
|
title: string;
|
|
participants: Participant[];
|
|
metadata?: ConversationMetadata;
|
|
lastMessage?: Message;
|
|
unreadCount: number;
|
|
}
|
|
|
|
/**
|
|
* Message metadata (reactions, zaps, encryption status, etc.)
|
|
*/
|
|
export interface MessageMetadata {
|
|
encrypted?: boolean;
|
|
reactions?: NostrEvent[];
|
|
zaps?: NostrEvent[];
|
|
deleted?: boolean;
|
|
hidden?: boolean; // NIP-28 channel hide
|
|
// Zap-specific metadata (for type: "zap" messages)
|
|
zapAmount?: number; // Amount in sats
|
|
zapRecipient?: string; // Pubkey of zap recipient
|
|
// NIP-61 nutzap-specific metadata
|
|
nutzapUnit?: string; // Unit for nutzap amount (sat, usd, eur, etc.)
|
|
}
|
|
|
|
/**
|
|
* Message type - system messages for events like join/leave, user messages for chat, zaps for stream tips
|
|
*/
|
|
export type MessageType = "user" | "system" | "zap";
|
|
|
|
/**
|
|
* Generic message abstraction
|
|
* Works across all messaging protocols
|
|
*/
|
|
export interface Message {
|
|
id: string;
|
|
conversationId: string;
|
|
author: string; // pubkey
|
|
content: string;
|
|
timestamp: number;
|
|
type?: MessageType; // Defaults to "user" if not specified
|
|
replyTo?: string; // Parent message ID
|
|
metadata?: MessageMetadata;
|
|
protocol: ChatProtocol;
|
|
event: NostrEvent; // Original Nostr event for verification
|
|
}
|
|
|
|
/**
|
|
* NIP-29 group identifier
|
|
*/
|
|
export interface GroupIdentifier {
|
|
type: "group";
|
|
/** Group ID (e.g., "bitcoin-dev") */
|
|
value: string;
|
|
/** Relay URL where the group is hosted (required for NIP-29) */
|
|
relays: string[];
|
|
}
|
|
|
|
/**
|
|
* NIP-53 live activity identifier
|
|
*/
|
|
export interface LiveActivityIdentifier {
|
|
type: "live-activity";
|
|
/** Address pointer for the live activity */
|
|
value: {
|
|
kind: 30311;
|
|
pubkey: string;
|
|
identifier: string;
|
|
};
|
|
/** Relay hints from naddr encoding */
|
|
relays?: string[];
|
|
}
|
|
|
|
/**
|
|
* NIP-C7/NIP-17 direct message identifier (resolved pubkey)
|
|
*/
|
|
export interface DMIdentifier {
|
|
type: "dm-recipient" | "chat-partner";
|
|
/** Recipient pubkey (hex) */
|
|
value: string;
|
|
/** Relay hints */
|
|
relays?: string[];
|
|
}
|
|
|
|
/**
|
|
* NIP-C7 NIP-05 identifier (needs resolution)
|
|
*/
|
|
export interface NIP05Identifier {
|
|
type: "chat-partner-nip05";
|
|
/** NIP-05 address to resolve */
|
|
value: string;
|
|
/** Relay hints */
|
|
relays?: string[];
|
|
}
|
|
|
|
/**
|
|
* NIP-28 channel identifier (future)
|
|
*/
|
|
export interface ChannelIdentifier {
|
|
type: "channel";
|
|
/** Channel creation event ID or address */
|
|
value: string;
|
|
/** Relay hints */
|
|
relays?: string[];
|
|
}
|
|
|
|
/**
|
|
* Protocol-specific identifier - discriminated union
|
|
* Returned by adapter parseIdentifier()
|
|
*/
|
|
export type ProtocolIdentifier =
|
|
| GroupIdentifier
|
|
| LiveActivityIdentifier
|
|
| DMIdentifier
|
|
| NIP05Identifier
|
|
| ChannelIdentifier;
|
|
|
|
/**
|
|
* Chat command parsing result
|
|
*/
|
|
export interface ChatCommandResult {
|
|
protocol: ChatProtocol;
|
|
identifier: ProtocolIdentifier;
|
|
adapter: any; // Will be ChatProtocolAdapter but avoiding circular dependency
|
|
}
|
|
|
|
/**
|
|
* Message loading options
|
|
*/
|
|
export interface LoadMessagesOptions {
|
|
limit?: number;
|
|
before?: number; // Unix timestamp
|
|
after?: number; // Unix timestamp
|
|
}
|
|
|
|
/**
|
|
* Conversation creation parameters
|
|
*/
|
|
export interface CreateConversationParams {
|
|
type: ConversationType;
|
|
title?: string;
|
|
participants: string[]; // pubkeys
|
|
metadata?: Record<string, any>;
|
|
}
|
|
|
|
/**
|
|
* Chat capabilities - what features a protocol supports
|
|
*/
|
|
export interface ChatCapabilities {
|
|
supportsEncryption: boolean;
|
|
supportsThreading: boolean;
|
|
supportsModeration: boolean;
|
|
supportsRoles: boolean;
|
|
supportsGroupManagement: boolean;
|
|
canCreateConversations: boolean;
|
|
requiresRelay: boolean;
|
|
}
|