export type UserStatus = 'online' | 'away' | 'busy' | 'offline'; export type UserRole = 'host' | 'admin' | 'moderator' | 'member'; export type ChannelType = 'text' | 'voice'; export const DELETED_MESSAGE_CONTENT = '[Message deleted]'; export interface User { id: string; oderId: string; username: string; displayName: string; avatarUrl?: string; status: UserStatus; role: UserRole; joinedAt: number; peerId?: string; isOnline?: boolean; isAdmin?: boolean; isRoomOwner?: boolean; voiceState?: VoiceState; screenShareState?: ScreenShareState; } export interface RoomMember { id: string; oderId?: string; username: string; displayName: string; avatarUrl?: string; role: UserRole; joinedAt: number; lastSeenAt: number; } export interface Channel { id: string; name: string; type: ChannelType; position: number; } export interface Message { id: string; roomId: string; channelId?: string; senderId: string; senderName: string; content: string; timestamp: number; editedAt?: number; reactions: Reaction[]; isDeleted: boolean; replyToId?: string; } export interface Reaction { id: string; messageId: string; oderId: string; userId: string; emoji: string; timestamp: number; } export interface Room { id: string; name: string; description?: string; topic?: string; hostId: string; password?: string; isPrivate: boolean; createdAt: number; userCount: number; maxUsers?: number; icon?: string; iconUpdatedAt?: number; permissions?: RoomPermissions; channels?: Channel[]; members?: RoomMember[]; } export interface RoomSettings { name: string; description?: string; topic?: string; isPrivate: boolean; password?: string; maxUsers?: number; rules?: string[]; } export interface RoomPermissions { adminsManageRooms?: boolean; moderatorsManageRooms?: boolean; adminsManageIcon?: boolean; moderatorsManageIcon?: boolean; allowVoice?: boolean; allowScreenShare?: boolean; allowFileUploads?: boolean; slowModeInterval?: number; } export interface BanEntry { oderId: string; userId: string; roomId: string; bannedBy: string; displayName?: string; reason?: string; expiresAt?: number; timestamp: number; } export interface PeerConnection { peerId: string; userId: string; status: 'connecting' | 'connected' | 'disconnected' | 'failed'; dataChannel?: RTCDataChannel; connection?: RTCPeerConnection; } export interface VoiceState { isConnected: boolean; isMuted: boolean; isDeafened: boolean; isSpeaking: boolean; isMutedByAdmin?: boolean; volume?: number; roomId?: string; serverId?: string; } export interface ScreenShareState { isSharing: boolean; streamId?: string; sourceId?: string; sourceName?: string; } export type SignalingMessageType = | 'offer' | 'answer' | 'ice-candidate' | 'join' | 'leave' | 'chat' | 'state-sync' | 'kick' | 'ban' | 'host-change' | 'room-update'; export interface SignalingMessage { type: SignalingMessageType; from: string; to?: string; payload: unknown; timestamp: number; } export type ChatEventType = | 'message' | 'chat-message' | 'edit' | 'message-edited' | 'delete' | 'message-deleted' | 'reaction' | 'reaction-added' | 'reaction-removed' | 'kick' | 'ban' | 'room-deleted' | 'host-change' | 'room-settings-update' | 'voice-state' | 'chat-inventory-request' | 'chat-inventory' | 'chat-sync-request-ids' | 'chat-sync-batch' | 'chat-sync-summary' | 'chat-sync-request' | 'chat-sync-full' | 'file-announce' | 'file-chunk' | 'file-request' | 'file-cancel' | 'file-not-found' | 'member-roster-request' | 'member-roster' | 'member-leave' | 'voice-state-request' | 'state-request' | 'screen-state' | 'screen-share-request' | 'screen-share-stop' | 'role-change' | 'room-permissions-update' | 'server-icon-summary' | 'server-icon-request' | 'server-icon-full' | 'server-icon-update' | 'server-state-request' | 'server-state-full' | 'unban' | 'channels-update'; export interface ChatInventoryItem { id: string; ts: number; rc: number; ac?: number; } export interface ChatAttachmentAnnouncement { id: string; filename: string; size: number; mime: string; isImage: boolean; uploaderPeerId?: string; } export interface ChatAttachmentMeta extends ChatAttachmentAnnouncement { messageId: string; filePath?: string; savedPath?: string; } /** Optional fields depend on `type`. */ export interface ChatEvent { type: ChatEventType; fromPeerId?: string; messageId?: string; message?: Message; reaction?: Reaction; data?: string | Partial; timestamp?: number; targetUserId?: string; roomId?: string; items?: ChatInventoryItem[]; ids?: string[]; messages?: Message[]; attachments?: Record; total?: number; index?: number; count?: number; lastUpdated?: number; file?: ChatAttachmentAnnouncement; fileId?: string; hostId?: string; hostOderId?: string; previousHostId?: string; previousHostOderId?: string; kickedBy?: string; bannedBy?: string; content?: string; editedAt?: number; deletedAt?: number; deletedBy?: string; oderId?: string; displayName?: string; emoji?: string; reason?: string; settings?: RoomSettings; permissions?: Partial; voiceState?: Partial; isScreenSharing?: boolean; icon?: string; iconUpdatedAt?: number; role?: UserRole; room?: Room; channels?: Channel[]; members?: RoomMember[]; ban?: BanEntry; bans?: BanEntry[]; banOderId?: string; expiresAt?: number; } export interface ServerInfo { id: string; name: string; description?: string; topic?: string; hostName: string; ownerId?: string; ownerName?: string; ownerPublicKey?: string; userCount: number; maxUsers: number; isPrivate: boolean; tags?: string[]; createdAt: number; sourceId?: string; sourceName?: string; } export interface JoinRequest { roomId: string; userId: string; username: string; } export interface AppState { currentUser: User | null; currentRoom: Room | null; isConnecting: boolean; error: string | null; }