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