@mereb/app-messaging 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/gql.d.ts +1 -3
- package/dist/gql.js +1 -50
- package/dist/headless.d.ts +38 -0
- package/dist/headless.js +53 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/native.d.ts +1 -1
- package/dist/native.js +16 -28
- package/package.json +14 -4
- package/src/gql.ts +0 -53
- package/src/index.tsx +0 -1
- package/src/native.tsx +0 -47
- package/src/store.ts +0 -111
package/dist/gql.d.ts
CHANGED
|
@@ -1,3 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
export declare const MESSAGES: import("@apollo/client").DocumentNode;
|
|
3
|
-
export declare const SEND_MESSAGE: import("@apollo/client").DocumentNode;
|
|
1
|
+
export { ConversationsDocument as CONVERSATIONS, MessagesDocument as MESSAGES, SendMessageDocument as SEND_MESSAGE } from './headless.js';
|
package/dist/gql.js
CHANGED
|
@@ -1,50 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export const CONVERSATIONS = gql `
|
|
3
|
-
query Conversations {
|
|
4
|
-
conversations {
|
|
5
|
-
id
|
|
6
|
-
title
|
|
7
|
-
participantIds
|
|
8
|
-
unreadCount
|
|
9
|
-
updatedAt
|
|
10
|
-
lastMessage {
|
|
11
|
-
id
|
|
12
|
-
senderName
|
|
13
|
-
body
|
|
14
|
-
sentAt
|
|
15
|
-
conversationId
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
`;
|
|
20
|
-
export const MESSAGES = gql `
|
|
21
|
-
query ConversationMessages($conversationId: ID!, $after: String) {
|
|
22
|
-
messages(conversationId: $conversationId, after: $after, limit: 50) {
|
|
23
|
-
edges {
|
|
24
|
-
cursor
|
|
25
|
-
node {
|
|
26
|
-
id
|
|
27
|
-
conversationId
|
|
28
|
-
senderName
|
|
29
|
-
body
|
|
30
|
-
sentAt
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
pageInfo {
|
|
34
|
-
endCursor
|
|
35
|
-
hasNextPage
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
`;
|
|
40
|
-
export const SEND_MESSAGE = gql `
|
|
41
|
-
mutation SendMessage($conversationId: ID, $toUserId: ID, $body: String!) {
|
|
42
|
-
sendMessage(conversationId: $conversationId, toUserId: $toUserId, body: $body) {
|
|
43
|
-
id
|
|
44
|
-
conversationId
|
|
45
|
-
senderName
|
|
46
|
-
body
|
|
47
|
-
sentAt
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
`;
|
|
1
|
+
export { ConversationsDocument as CONVERSATIONS, MessagesDocument as MESSAGES, SendMessageDocument as SEND_MESSAGE } from './headless.js';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type MessagingMessage = {
|
|
2
|
+
id: string;
|
|
3
|
+
conversationId: string;
|
|
4
|
+
senderId: string;
|
|
5
|
+
senderName?: string | null;
|
|
6
|
+
body: string;
|
|
7
|
+
sentAt: string;
|
|
8
|
+
};
|
|
9
|
+
export type MessagingConversation = {
|
|
10
|
+
id: string;
|
|
11
|
+
title: string;
|
|
12
|
+
participantIds: string[];
|
|
13
|
+
lastMessage?: MessagingMessage | null;
|
|
14
|
+
unreadCount: number;
|
|
15
|
+
updatedAt: string;
|
|
16
|
+
};
|
|
17
|
+
export type MessagingMessagesConnection = {
|
|
18
|
+
edges: Array<{
|
|
19
|
+
cursor: string;
|
|
20
|
+
node: MessagingMessage;
|
|
21
|
+
}>;
|
|
22
|
+
pageInfo: {
|
|
23
|
+
endCursor: string | null;
|
|
24
|
+
hasNextPage: boolean;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export type ConversationsQueryResult = {
|
|
28
|
+
conversations: MessagingConversation[];
|
|
29
|
+
};
|
|
30
|
+
export type ConversationMessagesQueryResult = {
|
|
31
|
+
messages: MessagingMessagesConnection;
|
|
32
|
+
};
|
|
33
|
+
export type SendMessageMutationResult = {
|
|
34
|
+
sendMessage: MessagingMessage;
|
|
35
|
+
};
|
|
36
|
+
export declare const ConversationsDocument: import("@apollo/client").DocumentNode;
|
|
37
|
+
export declare const MessagesDocument: import("@apollo/client").DocumentNode;
|
|
38
|
+
export declare const SendMessageDocument: import("@apollo/client").DocumentNode;
|
package/dist/headless.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { gql } from '@apollo/client';
|
|
2
|
+
export const ConversationsDocument = gql `
|
|
3
|
+
query Conversations {
|
|
4
|
+
conversations {
|
|
5
|
+
id
|
|
6
|
+
title
|
|
7
|
+
participantIds
|
|
8
|
+
unreadCount
|
|
9
|
+
updatedAt
|
|
10
|
+
lastMessage {
|
|
11
|
+
id
|
|
12
|
+
senderId
|
|
13
|
+
senderName
|
|
14
|
+
body
|
|
15
|
+
sentAt
|
|
16
|
+
conversationId
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
`;
|
|
21
|
+
export const MessagesDocument = gql `
|
|
22
|
+
query ConversationMessages($conversationId: ID!, $after: String) {
|
|
23
|
+
messages(conversationId: $conversationId, after: $after, limit: 50) {
|
|
24
|
+
edges {
|
|
25
|
+
cursor
|
|
26
|
+
node {
|
|
27
|
+
id
|
|
28
|
+
conversationId
|
|
29
|
+
senderId
|
|
30
|
+
senderName
|
|
31
|
+
body
|
|
32
|
+
sentAt
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
pageInfo {
|
|
36
|
+
endCursor
|
|
37
|
+
hasNextPage
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
export const SendMessageDocument = gql `
|
|
43
|
+
mutation SendMessage($conversationId: ID, $toUserId: ID, $body: String!) {
|
|
44
|
+
sendMessage(conversationId: $conversationId, toUserId: $toUserId, body: $body) {
|
|
45
|
+
id
|
|
46
|
+
conversationId
|
|
47
|
+
senderId
|
|
48
|
+
senderName
|
|
49
|
+
body
|
|
50
|
+
sentAt
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
`;
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export * from './
|
|
1
|
+
export * from './headless.js';
|
|
2
|
+
export * from './gql.js';
|
package/dist/index.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export * from './
|
|
1
|
+
export * from './headless.js';
|
|
2
|
+
export * from './gql.js';
|
package/dist/native.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function MessagesScreen(): import("react").JSX.Element;
|
|
1
|
+
export declare function MessagesScreen(): import("react/jsx-runtime.js").JSX.Element;
|
package/dist/native.js
CHANGED
|
@@ -1,35 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
2
|
import { useMemo, useState } from 'react';
|
|
2
3
|
import { Text, View } from 'react-native';
|
|
3
|
-
import { createInMemoryMessagingStore } from './store';
|
|
4
|
+
import { createInMemoryMessagingStore } from './store.js';
|
|
4
5
|
export function MessagesScreen() {
|
|
5
6
|
const store = useMemo(() => createInMemoryMessagingStore(), []);
|
|
6
7
|
const [version, setVersion] = useState(0);
|
|
7
8
|
const conversations = store.getConversations();
|
|
8
|
-
return (
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
<Text onPress={() => {
|
|
23
|
-
store.sendMessage(conversations[0]?.id ?? 'conv-1', 'Sent from mobile preview', {
|
|
24
|
-
id: 'mobile-user',
|
|
25
|
-
name: 'You'
|
|
26
|
-
});
|
|
27
|
-
setVersion((v) => v + 1);
|
|
28
|
-
}} style={{ color: '#2563EB', fontWeight: '600' }}>
|
|
29
|
-
Send a quick test message
|
|
30
|
-
</Text>
|
|
31
|
-
<Text style={{ color: '#94A3B8', fontSize: 12 }}>
|
|
32
|
-
Version {version} • This view uses the shared in-memory store so it stays in sync with the web shell preview.
|
|
33
|
-
</Text>
|
|
34
|
-
</View>);
|
|
9
|
+
return (_jsxs(View, { style: { flex: 1, padding: 24, gap: 16 }, children: [_jsx(Text, { style: { fontWeight: '700', fontSize: 18, marginBottom: 8 }, children: "Messaging" }), conversations.map((conversation) => (_jsxs(View, { style: {
|
|
10
|
+
padding: 12,
|
|
11
|
+
borderRadius: 12,
|
|
12
|
+
borderWidth: 1,
|
|
13
|
+
borderColor: '#E2E8F0',
|
|
14
|
+
backgroundColor: '#FFFFFF',
|
|
15
|
+
gap: 4
|
|
16
|
+
}, children: [_jsx(Text, { style: { fontWeight: '600', fontSize: 16 }, children: conversation.title }), _jsx(Text, { style: { color: '#475569' }, children: conversation.lastMessage?.body ?? 'No messages yet' }), _jsxs(Text, { style: { color: '#94A3B8', fontSize: 12 }, children: ["Unread: ", conversation.unreadCount] })] }, conversation.id))), _jsx(Text, { onPress: () => {
|
|
17
|
+
store.sendMessage(conversations[0]?.id ?? 'conv-1', 'Sent from mobile preview', {
|
|
18
|
+
id: 'mobile-user',
|
|
19
|
+
name: 'You'
|
|
20
|
+
});
|
|
21
|
+
setVersion((v) => v + 1);
|
|
22
|
+
}, style: { color: '#2563EB', fontWeight: '600' }, children: "Send a quick test message" }), _jsxs(Text, { style: { color: '#94A3B8', fontSize: 12 }, children: ["Version ", version, " \u2022 This preview uses the package's local in-memory store. Web now uses the real messaging service."] })] }));
|
|
35
23
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mereb/app-messaging",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Messaging experience primitives for Mereb applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
"import": "./dist/store.js",
|
|
17
17
|
"default": "./dist/store.js"
|
|
18
18
|
},
|
|
19
|
+
"./headless": {
|
|
20
|
+
"types": "./dist/headless.d.ts",
|
|
21
|
+
"import": "./dist/headless.js",
|
|
22
|
+
"default": "./dist/headless.js"
|
|
23
|
+
},
|
|
19
24
|
"./native": {
|
|
20
25
|
"types": "./dist/native.d.ts",
|
|
21
26
|
"import": "./dist/native.js",
|
|
@@ -28,7 +33,6 @@
|
|
|
28
33
|
}
|
|
29
34
|
},
|
|
30
35
|
"files": [
|
|
31
|
-
"src",
|
|
32
36
|
"dist",
|
|
33
37
|
"package.json"
|
|
34
38
|
],
|
|
@@ -43,10 +47,16 @@
|
|
|
43
47
|
"@apollo/client": "^3.12.5",
|
|
44
48
|
"@types/react": "~18.2.79",
|
|
45
49
|
"graphql": "^16.11.0",
|
|
46
|
-
"
|
|
50
|
+
"husky": "^9.1.7",
|
|
51
|
+
"typescript": ">=5.3.3",
|
|
52
|
+
"vitest": "^3.2.4"
|
|
47
53
|
},
|
|
48
54
|
"scripts": {
|
|
55
|
+
"clean": "node -e \"const fs=require('node:fs'); fs.rmSync('dist',{recursive:true,force:true}); fs.rmSync('tsconfig.tsbuildinfo',{force:true});\"",
|
|
49
56
|
"typecheck": "tsc --noEmit --project tsconfig.json",
|
|
50
|
-
"
|
|
57
|
+
"test": "pnpm exec vitest run --config vitest.config.ts",
|
|
58
|
+
"test:watch": "pnpm exec vitest --config vitest.config.ts",
|
|
59
|
+
"build": "pnpm run clean && tsc --project tsconfig.json",
|
|
60
|
+
"version:bump": "node ./scripts/bump-version.mjs"
|
|
51
61
|
}
|
|
52
62
|
}
|
package/src/gql.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import {gql} from '@apollo/client'
|
|
2
|
-
|
|
3
|
-
export const CONVERSATIONS = gql`
|
|
4
|
-
query Conversations {
|
|
5
|
-
conversations {
|
|
6
|
-
id
|
|
7
|
-
title
|
|
8
|
-
participantIds
|
|
9
|
-
unreadCount
|
|
10
|
-
updatedAt
|
|
11
|
-
lastMessage {
|
|
12
|
-
id
|
|
13
|
-
senderName
|
|
14
|
-
body
|
|
15
|
-
sentAt
|
|
16
|
-
conversationId
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
`
|
|
21
|
-
|
|
22
|
-
export const MESSAGES = gql`
|
|
23
|
-
query ConversationMessages($conversationId: ID!, $after: String) {
|
|
24
|
-
messages(conversationId: $conversationId, after: $after, limit: 50) {
|
|
25
|
-
edges {
|
|
26
|
-
cursor
|
|
27
|
-
node {
|
|
28
|
-
id
|
|
29
|
-
conversationId
|
|
30
|
-
senderName
|
|
31
|
-
body
|
|
32
|
-
sentAt
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
pageInfo {
|
|
36
|
-
endCursor
|
|
37
|
-
hasNextPage
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
`
|
|
42
|
-
|
|
43
|
-
export const SEND_MESSAGE = gql`
|
|
44
|
-
mutation SendMessage($conversationId: ID, $toUserId: ID, $body: String!) {
|
|
45
|
-
sendMessage(conversationId: $conversationId, toUserId: $toUserId, body: $body) {
|
|
46
|
-
id
|
|
47
|
-
conversationId
|
|
48
|
-
senderName
|
|
49
|
-
body
|
|
50
|
-
sentAt
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
`
|
package/src/index.tsx
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './store'
|
package/src/native.tsx
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { useMemo, useState } from 'react'
|
|
2
|
-
import { Text, View } from 'react-native'
|
|
3
|
-
import { createInMemoryMessagingStore } from './store'
|
|
4
|
-
|
|
5
|
-
export function MessagesScreen() {
|
|
6
|
-
const store = useMemo(() => createInMemoryMessagingStore(), [])
|
|
7
|
-
const [version, setVersion] = useState(0)
|
|
8
|
-
const conversations = store.getConversations()
|
|
9
|
-
|
|
10
|
-
return (
|
|
11
|
-
<View style={{ flex: 1, padding: 24, gap: 16 }}>
|
|
12
|
-
<Text style={{ fontWeight: '700', fontSize: 18, marginBottom: 8 }}>Messaging</Text>
|
|
13
|
-
{conversations.map((conversation) => (
|
|
14
|
-
<View
|
|
15
|
-
key={conversation.id}
|
|
16
|
-
style={{
|
|
17
|
-
padding: 12,
|
|
18
|
-
borderRadius: 12,
|
|
19
|
-
borderWidth: 1,
|
|
20
|
-
borderColor: '#E2E8F0',
|
|
21
|
-
backgroundColor: '#FFFFFF',
|
|
22
|
-
gap: 4
|
|
23
|
-
}}
|
|
24
|
-
>
|
|
25
|
-
<Text style={{ fontWeight: '600', fontSize: 16 }}>{conversation.title}</Text>
|
|
26
|
-
<Text style={{ color: '#475569' }}>{conversation.lastMessage?.body ?? 'No messages yet'}</Text>
|
|
27
|
-
<Text style={{ color: '#94A3B8', fontSize: 12 }}>Unread: {conversation.unreadCount}</Text>
|
|
28
|
-
</View>
|
|
29
|
-
))}
|
|
30
|
-
<Text
|
|
31
|
-
onPress={() => {
|
|
32
|
-
store.sendMessage(conversations[0]?.id ?? 'conv-1', 'Sent from mobile preview', {
|
|
33
|
-
id: 'mobile-user',
|
|
34
|
-
name: 'You'
|
|
35
|
-
})
|
|
36
|
-
setVersion((v) => v + 1)
|
|
37
|
-
}}
|
|
38
|
-
style={{ color: '#2563EB', fontWeight: '600' }}
|
|
39
|
-
>
|
|
40
|
-
Send a quick test message
|
|
41
|
-
</Text>
|
|
42
|
-
<Text style={{ color: '#94A3B8', fontSize: 12 }}>
|
|
43
|
-
Version {version} • This view uses the shared in-memory store so it stays in sync with the web shell preview.
|
|
44
|
-
</Text>
|
|
45
|
-
</View>
|
|
46
|
-
)
|
|
47
|
-
}
|
package/src/store.ts
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
export type MessagingMessage = {
|
|
2
|
-
id: string
|
|
3
|
-
conversationId: string
|
|
4
|
-
senderId: string
|
|
5
|
-
senderName?: string
|
|
6
|
-
body: string
|
|
7
|
-
sentAt: string
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export type MessagingConversation = {
|
|
11
|
-
id: string
|
|
12
|
-
title: string
|
|
13
|
-
participantIds: string[]
|
|
14
|
-
lastMessage?: MessagingMessage
|
|
15
|
-
unreadCount: number
|
|
16
|
-
updatedAt: string
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export type MessagingStore = {
|
|
20
|
-
getConversations: () => MessagingConversation[]
|
|
21
|
-
getConversation: (id: string) => MessagingConversation | undefined
|
|
22
|
-
getMessages: (conversationId: string) => MessagingMessage[]
|
|
23
|
-
sendMessage: (
|
|
24
|
-
conversationId: string,
|
|
25
|
-
body: string,
|
|
26
|
-
sender?: { id: string; name?: string }
|
|
27
|
-
) => MessagingMessage
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const seedConversations: MessagingConversation[] = [
|
|
31
|
-
{
|
|
32
|
-
id: 'conv-1',
|
|
33
|
-
title: 'Platform Ops',
|
|
34
|
-
participantIds: ['u-platform', 'u-release', 'u-ops'],
|
|
35
|
-
unreadCount: 2,
|
|
36
|
-
updatedAt: new Date().toISOString(),
|
|
37
|
-
lastMessage: {
|
|
38
|
-
id: 'msg-1',
|
|
39
|
-
conversationId: 'conv-1',
|
|
40
|
-
senderId: 'u-release',
|
|
41
|
-
senderName: 'Release Manager',
|
|
42
|
-
body: 'Copy that. Will queue prod promotion after smoke.',
|
|
43
|
-
sentAt: new Date().toISOString()
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
id: 'conv-2',
|
|
48
|
-
title: 'Support triage',
|
|
49
|
-
participantIds: ['u-support', 'u-oncall', 'u-platform'],
|
|
50
|
-
unreadCount: 0,
|
|
51
|
-
updatedAt: new Date().toISOString(),
|
|
52
|
-
lastMessage: {
|
|
53
|
-
id: 'msg-2',
|
|
54
|
-
conversationId: 'conv-2',
|
|
55
|
-
senderId: 'u-oncall',
|
|
56
|
-
senderName: 'On-call',
|
|
57
|
-
body: 'Acknowledged. Looking at the router dashboards now.',
|
|
58
|
-
sentAt: new Date().toISOString()
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
]
|
|
62
|
-
|
|
63
|
-
export function createInMemoryMessagingStore(seed: MessagingConversation[] = seedConversations): MessagingStore {
|
|
64
|
-
let conversations: MessagingConversation[] = seed.map((conversation) => ({
|
|
65
|
-
...conversation,
|
|
66
|
-
lastMessage: conversation.lastMessage
|
|
67
|
-
? { ...conversation.lastMessage, sentAt: conversation.lastMessage.sentAt ?? new Date().toISOString() }
|
|
68
|
-
: undefined
|
|
69
|
-
}))
|
|
70
|
-
const messageLookup: Record<string, MessagingMessage[]> = {}
|
|
71
|
-
|
|
72
|
-
conversations.forEach((conversation) => {
|
|
73
|
-
messageLookup[conversation.id] = conversation.lastMessage ? [conversation.lastMessage] : []
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
const refreshConversation = (conversationId: string, message: MessagingMessage) => {
|
|
77
|
-
const idx = conversations.findIndex((c) => c.id === conversationId)
|
|
78
|
-
if (idx >= 0) {
|
|
79
|
-
conversations[idx] = {
|
|
80
|
-
...conversations[idx],
|
|
81
|
-
lastMessage: message,
|
|
82
|
-
updatedAt: message.sentAt,
|
|
83
|
-
unreadCount: Math.max(0, conversations[idx].unreadCount - 1)
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
getConversations: () => [...conversations].sort((a, b) => (a.updatedAt > b.updatedAt ? -1 : 1)),
|
|
90
|
-
getConversation: (id: string) => conversations.find((conversation) => conversation.id === id),
|
|
91
|
-
getMessages: (conversationId: string) => [...(messageLookup[conversationId] ?? [])],
|
|
92
|
-
sendMessage: (conversationId: string, body: string, sender?: { id: string; name?: string }) => {
|
|
93
|
-
const conversationExists = conversations.some((conversation) => conversation.id === conversationId)
|
|
94
|
-
if (!conversationExists) {
|
|
95
|
-
throw new Error('Conversation not found')
|
|
96
|
-
}
|
|
97
|
-
const message: MessagingMessage = {
|
|
98
|
-
id: `msg-${Math.random().toString(36).slice(2, 8)}`,
|
|
99
|
-
conversationId,
|
|
100
|
-
senderId: sender?.id ?? 'anonymous',
|
|
101
|
-
senderName: sender?.name ?? 'You',
|
|
102
|
-
body,
|
|
103
|
-
sentAt: new Date().toISOString()
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
messageLookup[conversationId] = [...(messageLookup[conversationId] ?? []), message]
|
|
107
|
-
refreshConversation(conversationId, message)
|
|
108
|
-
return message
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|