feat: add messaging-ui domain types
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { isOwnMessage } from './types';
|
||||
import type { Message } from './types';
|
||||
|
||||
const base: Message = {
|
||||
id: 'm1',
|
||||
actorId: 'actor_a',
|
||||
text: 'hello',
|
||||
at: '2026-07-17T10:00:00.000Z',
|
||||
};
|
||||
|
||||
describe('isOwnMessage', () => {
|
||||
it('is true when the message actor matches the current actor', () => {
|
||||
expect(isOwnMessage(base, 'actor_a')).toBe(true);
|
||||
});
|
||||
|
||||
it('is false when the actors differ', () => {
|
||||
expect(isOwnMessage(base, 'actor_b')).toBe(false);
|
||||
});
|
||||
|
||||
it('is false when the current actor is unknown', () => {
|
||||
expect(isOwnMessage(base, null)).toBe(false);
|
||||
});
|
||||
|
||||
it('is false when the message has no actor', () => {
|
||||
expect(isOwnMessage({ ...base, actorId: null }, 'actor_a')).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
// Domain types for the messaging UI. Zero imports on purpose: this file must never
|
||||
// reach for a transport package. Adapters map their own DTOs onto these.
|
||||
|
||||
export type Membership = 'dm' | 'group';
|
||||
|
||||
export interface Person {
|
||||
id: string;
|
||||
name: string;
|
||||
kind: 'staff' | 'customer';
|
||||
}
|
||||
|
||||
export interface Conversation {
|
||||
threadId: string;
|
||||
title: string;
|
||||
subject: string | null;
|
||||
membership: Membership | null;
|
||||
participants: string[];
|
||||
unread: number;
|
||||
lastMessage?: string;
|
||||
lastAt?: string;
|
||||
}
|
||||
|
||||
export interface Reaction {
|
||||
emoji: string;
|
||||
count: number;
|
||||
mine: boolean;
|
||||
}
|
||||
|
||||
export interface Attachment {
|
||||
url: string;
|
||||
mime: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface Message {
|
||||
id: string;
|
||||
actorId: string | null;
|
||||
text: string;
|
||||
at: string;
|
||||
parentInteractionId?: string | null;
|
||||
reactions?: Reaction[];
|
||||
attachment?: Attachment;
|
||||
/** True only when the transport is optimistic-local and not yet acknowledged. */
|
||||
pending?: boolean;
|
||||
}
|
||||
|
||||
export interface SendOpts {
|
||||
parentInteractionId?: string;
|
||||
attachment?: Attachment;
|
||||
}
|
||||
|
||||
export type MessageEvent =
|
||||
| { kind: 'message'; message: Message }
|
||||
| { kind: 'typing'; userId: string }
|
||||
| { kind: 'receipt'; messageId: string; actorId: string }
|
||||
| { kind: 'reaction'; messageId: string; reactions: Reaction[] };
|
||||
|
||||
export type Unsubscribe = () => void;
|
||||
|
||||
/** Ownership is a pure function of explicit identity — never inferred from history.
|
||||
* See the spec: inferring it is the bug this SDK exists partly to kill. */
|
||||
export function isOwnMessage(message: Message, currentActorId: string | null): boolean {
|
||||
return currentActorId !== null && message.actorId !== null && message.actorId === currentActorId;
|
||||
}
|
||||
Reference in New Issue
Block a user