@thrillee/aegischat 0.1.0
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/index.d.mts +452 -0
- package/dist/index.d.ts +452 -0
- package/dist/index.js +1065 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1024 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +60 -0
- package/src/hooks/index.ts +27 -0
- package/src/hooks/useAutoRead.ts +78 -0
- package/src/hooks/useChannels.ts +99 -0
- package/src/hooks/useChat.ts +731 -0
- package/src/hooks/useFileUpload.ts +25 -0
- package/src/hooks/useMentions.ts +36 -0
- package/src/hooks/useMessages.ts +37 -0
- package/src/hooks/useReactions.ts +28 -0
- package/src/hooks/useTypingIndicator.ts +28 -0
- package/src/index.ts +68 -0
- package/src/services/api.ts +370 -0
- package/src/types/index.ts +373 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// AegisChat React SDK - useFileUpload Hook
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
import { useState } from 'react';
|
|
6
|
+
import type { FileAttachment, UploadProgress } from '../types';
|
|
7
|
+
|
|
8
|
+
export interface UseFileUploadOptions {
|
|
9
|
+
channelId: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface UseFileUploadReturn {
|
|
13
|
+
uploadProgress: UploadProgress[];
|
|
14
|
+
upload: (file: File) => Promise<FileAttachment | null>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function useFileUpload(_options: UseFileUploadOptions): UseFileUploadReturn {
|
|
18
|
+
const [uploadProgress, setUploadProgress] = useState<UploadProgress[]>([]);
|
|
19
|
+
|
|
20
|
+
const upload = async (_file: File): Promise<FileAttachment | null> => null;
|
|
21
|
+
|
|
22
|
+
return { uploadProgress, upload };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default useFileUpload;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// AegisChat React SDK - useMentions Hook
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
export interface UseMentionsOptions {}
|
|
6
|
+
|
|
7
|
+
export interface UseMentionsReturn {
|
|
8
|
+
parseMentions: (content: string) => string[];
|
|
9
|
+
highlightMentions: (content: string) => string;
|
|
10
|
+
isUserMentioned: (content: string, userId: string) => boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function useMentions(_options: UseMentionsOptions = {}): UseMentionsReturn {
|
|
14
|
+
const parseMentions = (content: string): string[] => {
|
|
15
|
+
const mentionRegex = /@\[([^\]]+)\]\(([^)]+)\)/g;
|
|
16
|
+
const mentions: string[] = [];
|
|
17
|
+
let match;
|
|
18
|
+
while ((match = mentionRegex.exec(content)) !== null) {
|
|
19
|
+
mentions.push(match[2]);
|
|
20
|
+
}
|
|
21
|
+
return mentions;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const highlightMentions = (content: string): string => {
|
|
25
|
+
return content.replace(/@\[([^\]]+)\]\(([^)]+)\)/g, '<span class="mention">@$1</span>');
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const isUserMentioned = (content: string, userId: string): boolean => {
|
|
29
|
+
const mentions = parseMentions(content);
|
|
30
|
+
return mentions.includes(userId);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return { parseMentions, highlightMentions, isUserMentioned };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default useMentions;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// AegisChat React SDK - useMessages Hook
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
import { useCallback, useState } from 'react';
|
|
6
|
+
import { messagesApi } from '../services/api';
|
|
7
|
+
import type { Message, MessagesResponse } from '../types';
|
|
8
|
+
|
|
9
|
+
export interface UseMessagesOptions {
|
|
10
|
+
channelId: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface UseMessagesReturn {
|
|
14
|
+
messages: Message[];
|
|
15
|
+
isLoading: boolean;
|
|
16
|
+
hasMore: boolean;
|
|
17
|
+
sendMessage: (params: { content: string; type?: string; metadata?: Record<string, unknown> }) => Promise<void>;
|
|
18
|
+
loadMore: () => Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useMessages(_options: UseMessagesOptions): UseMessagesReturn {
|
|
22
|
+
const [messages, setMessages] = useState<Message[]>([]);
|
|
23
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
24
|
+
const [hasMore, setHasMore] = useState(true);
|
|
25
|
+
|
|
26
|
+
const sendMessage = useCallback(async (params: { content: string; type?: string; metadata?: Record<string, unknown> }) => {
|
|
27
|
+
// Implementation would go here
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
30
|
+
const loadMore = useCallback(async () => {
|
|
31
|
+
// Implementation would go here
|
|
32
|
+
}, []);
|
|
33
|
+
|
|
34
|
+
return { messages, isLoading, hasMore, sendMessage, loadMore };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default useMessages;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// AegisChat React SDK - useReactions Hook
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
import { useState } from 'react';
|
|
6
|
+
import type { ReactionSummary } from '../types';
|
|
7
|
+
|
|
8
|
+
export interface UseReactionsOptions {
|
|
9
|
+
channelId: string;
|
|
10
|
+
messageId: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface UseReactionsReturn {
|
|
14
|
+
reactions: ReactionSummary[];
|
|
15
|
+
addReaction: (emoji: string) => Promise<void>;
|
|
16
|
+
removeReaction: (emoji: string) => Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function useReactions(_options: UseReactionsOptions): UseReactionsReturn {
|
|
20
|
+
const [reactions, setReactions] = useState<ReactionSummary[]>([]);
|
|
21
|
+
|
|
22
|
+
const addReaction = async (_emoji: string) => {};
|
|
23
|
+
const removeReaction = async (_emoji: string) => {};
|
|
24
|
+
|
|
25
|
+
return { reactions, addReaction, removeReaction };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default useReactions;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// AegisChat React SDK - useTypingIndicator Hook
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
import { useCallback, useState } from 'react';
|
|
6
|
+
import type { TypingUser } from '../types';
|
|
7
|
+
|
|
8
|
+
export interface UseTypingIndicatorOptions {
|
|
9
|
+
channelId: string;
|
|
10
|
+
ws?: WebSocket | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface UseTypingIndicatorReturn {
|
|
14
|
+
typingUsers: TypingUser[];
|
|
15
|
+
startTyping: () => void;
|
|
16
|
+
stopTyping: () => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function useTypingIndicator(_options: UseTypingIndicatorOptions): UseTypingIndicatorReturn {
|
|
20
|
+
const [typingUsers, setTypingUsers] = useState<TypingUser[]>([]);
|
|
21
|
+
|
|
22
|
+
const startTyping = useCallback(() => {}, []);
|
|
23
|
+
const stopTyping = useCallback(() => {}, []);
|
|
24
|
+
|
|
25
|
+
return { typingUsers, startTyping, stopTyping };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default useTypingIndicator;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// AegisChat React SDK - Main Entry Point
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
export { useChat } from './hooks/useChat';
|
|
6
|
+
export type { UseChatOptions, UseChatReturn } from './hooks/useChat';
|
|
7
|
+
|
|
8
|
+
export { useAutoRead } from './hooks/useAutoRead';
|
|
9
|
+
export type { UseAutoReadOptions, UseAutoReadReturn } from './hooks/useAutoRead';
|
|
10
|
+
|
|
11
|
+
export { useChannels } from './hooks/useChannels';
|
|
12
|
+
export type { UseChannelsOptions, UseChannelsReturn } from './hooks/useChannels';
|
|
13
|
+
|
|
14
|
+
export { useMessages } from './hooks/useMessages';
|
|
15
|
+
export type { UseMessagesOptions, UseMessagesReturn } from './hooks/useMessages';
|
|
16
|
+
|
|
17
|
+
export { useTypingIndicator } from './hooks/useTypingIndicator';
|
|
18
|
+
export type { UseTypingIndicatorOptions } from './hooks/useTypingIndicator';
|
|
19
|
+
|
|
20
|
+
export { useReactions } from './hooks/useReactions';
|
|
21
|
+
export type { UseReactionsOptions } from './hooks/useReactions';
|
|
22
|
+
|
|
23
|
+
export { useFileUpload } from './hooks/useFileUpload';
|
|
24
|
+
export type { UseFileUploadOptions } from './hooks/useFileUpload';
|
|
25
|
+
|
|
26
|
+
export { useMentions } from './hooks/useMentions';
|
|
27
|
+
export type { UseMentionsOptions } from './hooks/useMentions';
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
chatApi,
|
|
31
|
+
channelsApi,
|
|
32
|
+
messagesApi,
|
|
33
|
+
reactionsApi,
|
|
34
|
+
filesApi,
|
|
35
|
+
usersApi,
|
|
36
|
+
configureApiClient,
|
|
37
|
+
} from './services/api';
|
|
38
|
+
|
|
39
|
+
export type {
|
|
40
|
+
AegisConfig,
|
|
41
|
+
ChatSession,
|
|
42
|
+
ChatConnectParams,
|
|
43
|
+
UserSummary,
|
|
44
|
+
UserStatus,
|
|
45
|
+
Channel,
|
|
46
|
+
ChannelListItem,
|
|
47
|
+
ChannelType,
|
|
48
|
+
Message,
|
|
49
|
+
MessageSummary,
|
|
50
|
+
MessageType,
|
|
51
|
+
MessageStatus,
|
|
52
|
+
MessageMetadata,
|
|
53
|
+
FileAttachment,
|
|
54
|
+
TypingUser,
|
|
55
|
+
TypingEvent,
|
|
56
|
+
ReactionSummary,
|
|
57
|
+
ReactionEvent,
|
|
58
|
+
UploadProgress,
|
|
59
|
+
WebSocketMessage,
|
|
60
|
+
WebSocketStatus,
|
|
61
|
+
ApiResponse,
|
|
62
|
+
ApiError,
|
|
63
|
+
PaginationParams,
|
|
64
|
+
PaginationMeta,
|
|
65
|
+
PaginatedResponse,
|
|
66
|
+
MessagesResponse,
|
|
67
|
+
ChannelsResponse,
|
|
68
|
+
} from './types';
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// AegisChat React SDK - API Service
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
ApiResponse,
|
|
7
|
+
ChatSession,
|
|
8
|
+
ChatConnectParams,
|
|
9
|
+
Channel,
|
|
10
|
+
ChannelListItem,
|
|
11
|
+
Message,
|
|
12
|
+
MessagesResponse,
|
|
13
|
+
UserSummary,
|
|
14
|
+
ReactionSummary,
|
|
15
|
+
FileAttachment,
|
|
16
|
+
UploadUrlResponse,
|
|
17
|
+
} from '../types';
|
|
18
|
+
|
|
19
|
+
let baseUrl = '';
|
|
20
|
+
let getAccessToken: () => Promise<string> | string = () => '';
|
|
21
|
+
let onUnauthorized: (() => void) | undefined;
|
|
22
|
+
|
|
23
|
+
export function configureApiClient(config: {
|
|
24
|
+
baseUrl: string;
|
|
25
|
+
getAccessToken: () => Promise<string> | string;
|
|
26
|
+
onUnauthorized?: () => void;
|
|
27
|
+
}): void {
|
|
28
|
+
baseUrl = config.baseUrl;
|
|
29
|
+
getAccessToken = config.getAccessToken;
|
|
30
|
+
onUnauthorized = config.onUnauthorized;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function fetchWithAuth<T>(
|
|
34
|
+
path: string,
|
|
35
|
+
options: RequestInit = {}
|
|
36
|
+
): Promise<T> {
|
|
37
|
+
const token = await (typeof getAccessToken === 'function'
|
|
38
|
+
? getAccessToken()
|
|
39
|
+
: getAccessToken);
|
|
40
|
+
|
|
41
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
42
|
+
...options,
|
|
43
|
+
headers: {
|
|
44
|
+
'Content-Type': 'application/json',
|
|
45
|
+
Authorization: `Bearer ${token}`,
|
|
46
|
+
...options.headers,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (response.status === 401) {
|
|
51
|
+
onUnauthorized?.();
|
|
52
|
+
throw new Error('Unauthorized');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
const error = await response.json().catch(() => ({}));
|
|
57
|
+
throw new Error(error.message || `HTTP ${response.status}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return response.json();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const chatApi = {
|
|
64
|
+
/**
|
|
65
|
+
* Connect to chat session
|
|
66
|
+
*/
|
|
67
|
+
async connect(
|
|
68
|
+
params: ChatConnectParams,
|
|
69
|
+
signal?: AbortSignal
|
|
70
|
+
): Promise<ApiResponse<ChatSession>> {
|
|
71
|
+
return fetchWithAuth('/api/v1/chat/connect', {
|
|
72
|
+
method: 'POST',
|
|
73
|
+
body: JSON.stringify(params),
|
|
74
|
+
signal,
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Refresh access token
|
|
80
|
+
*/
|
|
81
|
+
async refreshToken(
|
|
82
|
+
refreshToken: string,
|
|
83
|
+
signal?: AbortSignal
|
|
84
|
+
): Promise<ApiResponse<{ access_token: string; expires_in: number }>> {
|
|
85
|
+
return fetchWithAuth('/api/v1/chat/refresh', {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
body: JSON.stringify({ refresh_token: refreshToken }),
|
|
88
|
+
signal,
|
|
89
|
+
});
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const channelsApi = {
|
|
94
|
+
/**
|
|
95
|
+
* List channels
|
|
96
|
+
*/
|
|
97
|
+
async list(
|
|
98
|
+
options: { type?: string; limit?: number } = {},
|
|
99
|
+
signal?: AbortSignal
|
|
100
|
+
): Promise<ApiResponse<{ channels: ChannelListItem[] }>> {
|
|
101
|
+
const params = new URLSearchParams();
|
|
102
|
+
if (options.type) params.append('type', options.type);
|
|
103
|
+
if (options.limit) params.append('limit', String(options.limit));
|
|
104
|
+
const query = params.toString() ? `?${params.toString()}` : '';
|
|
105
|
+
return fetchWithAuth(`/api/v1/channels${query}`, { signal });
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get channel by ID
|
|
110
|
+
*/
|
|
111
|
+
async get(channelId: string, signal?: AbortSignal): Promise<ApiResponse<Channel>> {
|
|
112
|
+
return fetchWithAuth(`/api/v1/channels/${channelId}`, { signal });
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get or create DM channel
|
|
117
|
+
*/
|
|
118
|
+
async getOrCreateDM(
|
|
119
|
+
userId: string,
|
|
120
|
+
signal?: AbortSignal
|
|
121
|
+
): Promise<ApiResponse<Channel>> {
|
|
122
|
+
return fetchWithAuth('/api/v1/channels/dm', {
|
|
123
|
+
method: 'POST',
|
|
124
|
+
body: JSON.stringify({ user_id: userId }),
|
|
125
|
+
signal,
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Create channel
|
|
131
|
+
*/
|
|
132
|
+
async create(
|
|
133
|
+
data: { name: string; type?: string; description?: string; metadata?: Record<string, unknown> },
|
|
134
|
+
signal?: AbortSignal
|
|
135
|
+
): Promise<ApiResponse<Channel>> {
|
|
136
|
+
return fetchWithAuth('/api/v1/channels', {
|
|
137
|
+
method: 'POST',
|
|
138
|
+
body: JSON.stringify(data),
|
|
139
|
+
signal,
|
|
140
|
+
});
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Mark channel as read
|
|
145
|
+
*/
|
|
146
|
+
async markAsRead(
|
|
147
|
+
channelId: string,
|
|
148
|
+
signal?: AbortSignal
|
|
149
|
+
): Promise<ApiResponse<{ unread_count: number }>> {
|
|
150
|
+
return fetchWithAuth(`/api/v1/channels/${channelId}/read`, {
|
|
151
|
+
method: 'POST',
|
|
152
|
+
signal,
|
|
153
|
+
});
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Get channel members
|
|
158
|
+
*/
|
|
159
|
+
async getMembers(
|
|
160
|
+
channelId: string,
|
|
161
|
+
signal?: AbortSignal
|
|
162
|
+
): Promise<ApiResponse<{ members: UserSummary[] }>> {
|
|
163
|
+
return fetchWithAuth(`/api/v1/channels/${channelId}/members`, { signal });
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Update channel
|
|
168
|
+
*/
|
|
169
|
+
async update(
|
|
170
|
+
channelId: string,
|
|
171
|
+
data: { name?: string; description?: string; metadata?: Record<string, unknown> },
|
|
172
|
+
signal?: AbortSignal
|
|
173
|
+
): Promise<ApiResponse<Channel>> {
|
|
174
|
+
return fetchWithAuth(`/api/v1/channels/${channelId}`, {
|
|
175
|
+
method: 'PATCH',
|
|
176
|
+
body: JSON.stringify(data),
|
|
177
|
+
signal,
|
|
178
|
+
});
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
export const messagesApi = {
|
|
183
|
+
/**
|
|
184
|
+
* List messages in a channel
|
|
185
|
+
*/
|
|
186
|
+
async list(
|
|
187
|
+
channelId: string,
|
|
188
|
+
options: { limit?: number; before?: string } = {},
|
|
189
|
+
signal?: AbortSignal
|
|
190
|
+
): Promise<ApiResponse<MessagesResponse>> {
|
|
191
|
+
const params = new URLSearchParams();
|
|
192
|
+
if (options.limit) params.append('limit', String(options.limit));
|
|
193
|
+
if (options.before) params.append('before', options.before);
|
|
194
|
+
const query = params.toString() ? `?${params.toString()}` : '';
|
|
195
|
+
return fetchWithAuth(`/api/v1/channels/${channelId}/messages${query}`, { signal });
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Send a message
|
|
200
|
+
*/
|
|
201
|
+
async send(
|
|
202
|
+
channelId: string,
|
|
203
|
+
data: { content: string; type?: string; parent_id?: string; metadata?: Record<string, unknown>; file_ids?: string[] },
|
|
204
|
+
signal?: AbortSignal
|
|
205
|
+
): Promise<ApiResponse<Message>> {
|
|
206
|
+
return fetchWithAuth(`/api/v1/channels/${channelId}/messages`, {
|
|
207
|
+
method: 'POST',
|
|
208
|
+
body: JSON.stringify(data),
|
|
209
|
+
signal,
|
|
210
|
+
});
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Update a message
|
|
215
|
+
*/
|
|
216
|
+
async update(
|
|
217
|
+
channelId: string,
|
|
218
|
+
messageId: string,
|
|
219
|
+
data: { content?: string; metadata?: Record<string, unknown> },
|
|
220
|
+
signal?: AbortSignal
|
|
221
|
+
): Promise<ApiResponse<Message>> {
|
|
222
|
+
return fetchWithAuth(`/api/v1/channels/${channelId}/messages/${messageId}`, {
|
|
223
|
+
method: 'PATCH',
|
|
224
|
+
body: JSON.stringify(data),
|
|
225
|
+
signal,
|
|
226
|
+
});
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Delete a message
|
|
231
|
+
*/
|
|
232
|
+
async delete(
|
|
233
|
+
channelId: string,
|
|
234
|
+
messageId: string,
|
|
235
|
+
signal?: AbortSignal
|
|
236
|
+
): Promise<ApiResponse<{ success: boolean }>> {
|
|
237
|
+
return fetchWithAuth(`/api/v1/channels/${channelId}/messages/${messageId}`, {
|
|
238
|
+
method: 'DELETE',
|
|
239
|
+
signal,
|
|
240
|
+
});
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Mark messages as delivered
|
|
245
|
+
*/
|
|
246
|
+
async markDelivered(
|
|
247
|
+
channelId: string,
|
|
248
|
+
signal?: AbortSignal
|
|
249
|
+
): Promise<ApiResponse<{ success: boolean }>> {
|
|
250
|
+
return fetchWithAuth(`/api/v1/channels/${channelId}/messages/delivered`, {
|
|
251
|
+
method: 'POST',
|
|
252
|
+
signal,
|
|
253
|
+
});
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Mark messages as read
|
|
258
|
+
*/
|
|
259
|
+
async markRead(
|
|
260
|
+
channelId: string,
|
|
261
|
+
signal?: AbortSignal
|
|
262
|
+
): Promise<ApiResponse<{ success: boolean }>> {
|
|
263
|
+
return fetchWithAuth(`/api/v1/channels/${channelId}/messages/read`, {
|
|
264
|
+
method: 'POST',
|
|
265
|
+
signal,
|
|
266
|
+
});
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
export const reactionsApi = {
|
|
271
|
+
/**
|
|
272
|
+
* Add reaction to a message
|
|
273
|
+
*/
|
|
274
|
+
async add(
|
|
275
|
+
channelId: string,
|
|
276
|
+
messageId: string,
|
|
277
|
+
emoji: string,
|
|
278
|
+
signal?: AbortSignal
|
|
279
|
+
): Promise<ApiResponse<{ reactions: ReactionSummary[] }>> {
|
|
280
|
+
return fetchWithAuth(`/api/v1/channels/${channelId}/messages/${messageId}/reactions`, {
|
|
281
|
+
method: 'POST',
|
|
282
|
+
body: JSON.stringify({ emoji }),
|
|
283
|
+
signal,
|
|
284
|
+
});
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Remove reaction from a message
|
|
289
|
+
*/
|
|
290
|
+
async remove(
|
|
291
|
+
channelId: string,
|
|
292
|
+
messageId: string,
|
|
293
|
+
emoji: string,
|
|
294
|
+
signal?: AbortSignal
|
|
295
|
+
): Promise<ApiResponse<{ reactions: ReactionSummary[] }>> {
|
|
296
|
+
return fetchWithAuth(
|
|
297
|
+
`/api/v1/channels/${channelId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}`,
|
|
298
|
+
{ method: 'DELETE', signal }
|
|
299
|
+
);
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
export const filesApi = {
|
|
304
|
+
/**
|
|
305
|
+
* Get upload URL
|
|
306
|
+
*/
|
|
307
|
+
async getUploadUrl(
|
|
308
|
+
data: { file_name: string; file_type: string; file_size: number },
|
|
309
|
+
signal?: AbortSignal
|
|
310
|
+
): Promise<ApiResponse<UploadUrlResponse>> {
|
|
311
|
+
return fetchWithAuth('/api/v1/files/upload-url', {
|
|
312
|
+
method: 'POST',
|
|
313
|
+
body: JSON.stringify(data),
|
|
314
|
+
signal,
|
|
315
|
+
});
|
|
316
|
+
},
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Confirm file upload
|
|
320
|
+
*/
|
|
321
|
+
async confirm(
|
|
322
|
+
fileId: string,
|
|
323
|
+
signal?: AbortSignal
|
|
324
|
+
): Promise<ApiResponse<{ file: FileAttachment }>> {
|
|
325
|
+
return fetchWithAuth('/api/v1/files', {
|
|
326
|
+
method: 'POST',
|
|
327
|
+
body: JSON.stringify({ file_id: fileId }),
|
|
328
|
+
signal,
|
|
329
|
+
});
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Get download URL
|
|
334
|
+
*/
|
|
335
|
+
async getDownloadUrl(
|
|
336
|
+
fileId: string,
|
|
337
|
+
signal?: AbortSignal
|
|
338
|
+
): Promise<ApiResponse<{ url: string; expires_at: string }>> {
|
|
339
|
+
return fetchWithAuth(`/api/v1/files/${fileId}/download`, { signal });
|
|
340
|
+
},
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
export const usersApi = {
|
|
344
|
+
/**
|
|
345
|
+
* Search users
|
|
346
|
+
*/
|
|
347
|
+
async search(
|
|
348
|
+
query: string,
|
|
349
|
+
signal?: AbortSignal
|
|
350
|
+
): Promise<ApiResponse<{ users: UserSummary[] }>> {
|
|
351
|
+
return fetchWithAuth(`/api/v1/users/search?q=${encodeURIComponent(query)}`, { signal });
|
|
352
|
+
},
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Get user by ID
|
|
356
|
+
*/
|
|
357
|
+
async get(userId: string, signal?: AbortSignal): Promise<ApiResponse<UserSummary>> {
|
|
358
|
+
return fetchWithAuth(`/api/v1/users/${userId}`, { signal });
|
|
359
|
+
},
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
export default {
|
|
363
|
+
chatApi,
|
|
364
|
+
channelsApi,
|
|
365
|
+
messagesApi,
|
|
366
|
+
reactionsApi,
|
|
367
|
+
filesApi,
|
|
368
|
+
usersApi,
|
|
369
|
+
configureApiClient,
|
|
370
|
+
};
|