Implemented DMService
This commit is contained in:
@@ -14,4 +14,14 @@ export interface PublicUserData {
|
||||
displayName: string
|
||||
username: string
|
||||
userid: string
|
||||
}
|
||||
|
||||
export interface Attachment {
|
||||
fileId: string
|
||||
fileName: string
|
||||
format: string
|
||||
type: string
|
||||
path: string
|
||||
height: number
|
||||
width: number
|
||||
}
|
||||
112
src/domain/dmService.schema.ts
Normal file
112
src/domain/dmService.schema.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import {Attachment, TimeStamp} from "./common.schema";
|
||||
|
||||
export interface GetMessageReq {
|
||||
from: number
|
||||
chatid: string
|
||||
}
|
||||
|
||||
export interface GetMessagePosReq {
|
||||
messageId: string
|
||||
chatid: string
|
||||
}
|
||||
|
||||
export interface GetPinnedMessagesReq {
|
||||
chatid: string
|
||||
}
|
||||
|
||||
export interface EditMessageReq {
|
||||
message: string
|
||||
messageId: string
|
||||
chatid: string
|
||||
userid: string
|
||||
}
|
||||
|
||||
export interface FinishMessageReq {
|
||||
uploadId: string | null
|
||||
message: string
|
||||
replyTo: string
|
||||
replyToMessage: string
|
||||
chatid: string
|
||||
userid: string
|
||||
}
|
||||
|
||||
export interface ReadMessagesReq {
|
||||
chatid: string
|
||||
userid: string
|
||||
}
|
||||
|
||||
export interface PinMessageReq {
|
||||
chatid: string
|
||||
messageId: string
|
||||
userid: string
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface UnpinMessageReq {
|
||||
chatid: string,
|
||||
messageId: string,
|
||||
userid: string
|
||||
}
|
||||
|
||||
export interface DeleteMessagesReq {
|
||||
messageIds: string[]
|
||||
chatid: string
|
||||
userid: string
|
||||
}
|
||||
|
||||
export interface JoinWsRoomReq {
|
||||
connId: string
|
||||
chatid: string
|
||||
userid: string
|
||||
}
|
||||
|
||||
// Response schemas
|
||||
export interface GetMessagePosResp {
|
||||
messagePos: number
|
||||
}
|
||||
|
||||
// Types
|
||||
export interface Message {
|
||||
msgid: string
|
||||
author: string
|
||||
message: string
|
||||
sent_at: TimeStamp
|
||||
isEdited: boolean
|
||||
chatid: string
|
||||
files: Attachment[]
|
||||
seen: boolean
|
||||
replyTo: string
|
||||
replyToId: string
|
||||
forwardedFrom: string
|
||||
forwardedFromName: string
|
||||
}
|
||||
|
||||
export interface PinnedMessage {
|
||||
message: string
|
||||
messageId: string
|
||||
}
|
||||
|
||||
// WebSocket payloads
|
||||
export interface WSMessageDeletedPayload {
|
||||
messageId: string
|
||||
}
|
||||
|
||||
export interface WSMessageEditedPayload {
|
||||
messageId: string
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface WSMessagePinnedPayload {
|
||||
chatid: string
|
||||
messageId: string
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface WSMessagesReadPayload {
|
||||
userid: string
|
||||
}
|
||||
|
||||
export interface WSMessageUnpinnedPayload {
|
||||
chatid: string
|
||||
messageId: string
|
||||
}
|
||||
31
src/mocks/handlers/dm.http.ts
Normal file
31
src/mocks/handlers/dm.http.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import {http, HttpResponse} from "msw";
|
||||
import {Chat} from "../../domain/chatService.schema";
|
||||
import {FinishMessageReq, GetMessagePosResp, Message, PinnedMessage} from "../../domain/dmService.schema";
|
||||
import {CreateNetworkReq, Network} from "../../domain/networkService.schema";
|
||||
|
||||
export const dmHandlers = [
|
||||
http.get('*/chat/dm/messages', () => {
|
||||
return HttpResponse.json(<Message[]>[{
|
||||
message: "This is a message",
|
||||
}])
|
||||
}),
|
||||
|
||||
http.get('*/chat/dm/getMessagePosition', () => {
|
||||
return HttpResponse.json(<GetMessagePosResp>{
|
||||
messagePos: 5000
|
||||
})
|
||||
}),
|
||||
|
||||
http.get('*/chat/dm/pinnedMessages', () => {
|
||||
return HttpResponse.json(<PinnedMessage[]>[{
|
||||
message: "This is a pinned message",
|
||||
}])
|
||||
}),
|
||||
|
||||
http.post('*/chat/dm/finishMessage', async ({request}) => {
|
||||
const body = await request.json() as FinishMessageReq
|
||||
return HttpResponse.json(<Message>{
|
||||
message: body.message,
|
||||
})
|
||||
}),
|
||||
]
|
||||
@@ -4,6 +4,7 @@ import {pictureHandlers} from "./handlers/picture.http";
|
||||
import {callHandlers} from "./handlers/call.http";
|
||||
import {fileUploadHandlers} from "./handlers/fUpl.http";
|
||||
import {chatHandlers} from "./handlers/chat.http";
|
||||
import {dmHandlers} from "./handlers/dm.http";
|
||||
|
||||
export const allHandlers = [
|
||||
...authHandlers,
|
||||
@@ -11,5 +12,6 @@ export const allHandlers = [
|
||||
...pictureHandlers,
|
||||
...callHandlers,
|
||||
...fileUploadHandlers,
|
||||
...chatHandlers
|
||||
...chatHandlers,
|
||||
...dmHandlers
|
||||
]
|
||||
29
src/services/dm.http.test.ts
Normal file
29
src/services/dm.http.test.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import {describe, expect, it} from "vitest";
|
||||
import {DMService} from "./dmService";
|
||||
import {DatabaseMock} from "../mocks/storage/database";
|
||||
import {faker} from "@faker-js/faker/locale/en";
|
||||
|
||||
describe("DmService", () => {
|
||||
const service = new DMService("", "", "", new DatabaseMock(), () => {})
|
||||
|
||||
it("should get messages", async () => {
|
||||
const messages = await service.get()
|
||||
expect(messages[0].message).toBe("This is a message")
|
||||
})
|
||||
|
||||
it('should get message position', async () => {
|
||||
const pos = await service.getMessagePos("")
|
||||
expect(pos).toBe(5000)
|
||||
});
|
||||
|
||||
it('should get pinned messages', async () => {
|
||||
const pinnedMessages = await service.getPinnedMessages()
|
||||
expect(pinnedMessages[0].message).toBe("This is a pinned message")
|
||||
});
|
||||
|
||||
it('should send a new message', async () => {
|
||||
const message = faker.internet.displayName()
|
||||
const newMessage = await service.sendMessage(message)
|
||||
expect(newMessage.message).toBe(message)
|
||||
});
|
||||
})
|
||||
247
src/services/dmService.ts
Normal file
247
src/services/dmService.ts
Normal file
@@ -0,0 +1,247 @@
|
||||
import {DatabaseAPI} from "../storage/database";
|
||||
import {AxiosInstance, isAxiosError} from "axios";
|
||||
import {MessageListener} from "../domain/websocket.schema";
|
||||
import {getClient} from "../core/http";
|
||||
import {WebSocketHandler} from "../core/webSocketHandler";
|
||||
import {
|
||||
DeleteMessagesReq,
|
||||
EditMessageReq,
|
||||
FinishMessageReq,
|
||||
GetMessagePosResp, JoinWsRoomReq,
|
||||
Message, PinMessageReq,
|
||||
PinnedMessage, ReadMessagesReq, UnpinMessageReq
|
||||
} from "../domain/dmService.schema";
|
||||
import {NetworkInvite} from "../domain/networkService.schema";
|
||||
import {GenericErrorBody} from "../domain/http.schema";
|
||||
import {FileData} from "../domain/fileUploadService.schema";
|
||||
import {FileUploadService} from "./fileUploadService";
|
||||
|
||||
export class DMService {
|
||||
userid: string;
|
||||
chatid: string;
|
||||
token: string;
|
||||
database: DatabaseAPI;
|
||||
client: AxiosInstance
|
||||
|
||||
constructor(userid: string, token: string, chatid: string, database: DatabaseAPI, wsMessageListener: MessageListener) {
|
||||
this.userid = userid;
|
||||
this.chatid = chatid;
|
||||
this.database = database;
|
||||
this.token = token;
|
||||
this.client = getClient(false).create({
|
||||
headers: {
|
||||
"Authorization": token,
|
||||
"X-WS-ID": WebSocketHandler.getInstance().connId
|
||||
}
|
||||
})
|
||||
WebSocketHandler.getInstance().registerService({
|
||||
identifier: chatid,
|
||||
onNewConnId: this.onNewConnId,
|
||||
onNewMessage: wsMessageListener,
|
||||
})
|
||||
}
|
||||
|
||||
private onNewConnId(newConnId: string) {
|
||||
console.log("NetworkService: New connection id")
|
||||
this.client.defaults.headers["X-WS-ID"] = newConnId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches all messages in the chat
|
||||
* @param from
|
||||
*/
|
||||
async get(from: number = 0): Promise<Message[]> {
|
||||
try {
|
||||
const resp = await this.client.get<Message[]>(`chat/dm/messages?chatid=${this.chatid}&userid=${this.userid}&from=${from}`);
|
||||
return resp.data
|
||||
} catch (e) {
|
||||
if (isAxiosError<GenericErrorBody>(e)) {
|
||||
throw e;
|
||||
}
|
||||
throw new Error("Unexpected error")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the position of the specified message which can be used in get() to fetch an old message with it's surrounding messages
|
||||
* @param messageId
|
||||
*/
|
||||
async getMessagePos(messageId: string): Promise<number> {
|
||||
try {
|
||||
const resp = await this.client.get<GetMessagePosResp>(`chat/dm/getMessagePosition?chatid=${this.chatid}&userid=${this.userid}&messageId=${messageId}`);
|
||||
return resp.data.messagePos
|
||||
} catch (e) {
|
||||
if (isAxiosError<GenericErrorBody>(e)) {
|
||||
throw e;
|
||||
}
|
||||
throw new Error("Unexpected error")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all messages pinned in the chat
|
||||
*/
|
||||
async getPinnedMessages(): Promise<PinnedMessage[]> {
|
||||
try {
|
||||
const resp = await this.client.get<PinnedMessage[]>(`chat/dm/pinnedMessages?chatid=${this.chatid}&userid=${this.userid}`);
|
||||
return resp.data
|
||||
} catch (e) {
|
||||
if (isAxiosError<GenericErrorBody>(e)) {
|
||||
throw e;
|
||||
}
|
||||
throw new Error("Unexpected error")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits the specified message
|
||||
* @param messageId
|
||||
* @param newMessage
|
||||
*/
|
||||
async editMessage(messageId: string, newMessage: string): Promise<void> {
|
||||
try {
|
||||
const resp = await this.client.patch("chat/dm/editMessage", <EditMessageReq>{
|
||||
messageId: messageId,
|
||||
chatid: this.chatid,
|
||||
userid: this.userid,
|
||||
message: newMessage
|
||||
});
|
||||
return resp.data
|
||||
} catch (e) {
|
||||
if (isAxiosError<GenericErrorBody>(e)) {
|
||||
throw e;
|
||||
}
|
||||
throw new Error("Unexpected error")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a new message to the chat
|
||||
* @param message
|
||||
* @param replyTo
|
||||
* @param replyToMessage
|
||||
* @param attachments
|
||||
*/
|
||||
async sendMessage(message: string, replyTo: string | null = null, replyToMessage: string | null = null, attachments: FileData[] | null = null): Promise<Message> {
|
||||
let uploadId = ""
|
||||
if (attachments) {
|
||||
const uploader = new FileUploadService(this.token)
|
||||
uploadId = await uploader.uploadFiles(this.chatid, this.userid, attachments)
|
||||
}
|
||||
try {
|
||||
const resp = await this.client.post<Message>("chat/dm/finishMessage", <FinishMessageReq>{
|
||||
message: message,
|
||||
chatid: this.chatid,
|
||||
replyTo: replyTo,
|
||||
replyToMessage: replyToMessage,
|
||||
userid: this.userid,
|
||||
uploadId: uploadId,
|
||||
});
|
||||
return resp.data
|
||||
} catch (e) {
|
||||
if (isAxiosError<GenericErrorBody>(e)) {
|
||||
throw e;
|
||||
}
|
||||
throw new Error("Unexpected error")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all messages sent to you in the chat
|
||||
*/
|
||||
async readMessages(): Promise<void> {
|
||||
try {
|
||||
await this.client.patch("chat/dm/readMessages", <ReadMessagesReq>{
|
||||
chatid: this.chatid,
|
||||
userid: this.userid,
|
||||
});
|
||||
return
|
||||
} catch (e) {
|
||||
if (isAxiosError<GenericErrorBody>(e)) {
|
||||
throw e;
|
||||
}
|
||||
throw new Error("Unexpected error")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pins the specified message
|
||||
* @param messageId
|
||||
* @param message
|
||||
*/
|
||||
async pinMessage(messageId: string, message: string): Promise<void> {
|
||||
try {
|
||||
const resp = await this.client.patch("chat/dm/pinMessage", <PinMessageReq>{
|
||||
messageId: messageId,
|
||||
chatid: this.chatid,
|
||||
userid: this.userid,
|
||||
message: message
|
||||
});
|
||||
return
|
||||
} catch (e) {
|
||||
if (isAxiosError<GenericErrorBody>(e)) {
|
||||
throw e;
|
||||
}
|
||||
throw new Error("Unexpected error")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpins the specified message
|
||||
* @param messageId
|
||||
*/
|
||||
async unpinMessage(messageId: string): Promise<void> {
|
||||
try {
|
||||
const resp = await this.client.patch("chat/dm/unpinMessage", <UnpinMessageReq>{
|
||||
messageId: messageId,
|
||||
chatid: this.chatid,
|
||||
userid: this.userid,
|
||||
});
|
||||
return
|
||||
} catch (e) {
|
||||
if (isAxiosError<GenericErrorBody>(e)) {
|
||||
throw e;
|
||||
}
|
||||
throw new Error("Unexpected error")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the message(s)
|
||||
* @param messageIds
|
||||
*/
|
||||
async deleteMessages(messageIds: string[]): Promise<void> {
|
||||
try {
|
||||
const resp = await this.client.patch("chat/dm/deleteMessages", <DeleteMessagesReq>{
|
||||
chatid: this.chatid,
|
||||
userid: this.userid,
|
||||
messageIds: messageIds
|
||||
});
|
||||
return
|
||||
} catch (e) {
|
||||
if (isAxiosError<GenericErrorBody>(e)) {
|
||||
throw e;
|
||||
}
|
||||
throw new Error("Unexpected error")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins the WebSocket room to start receiving realtime messages
|
||||
*/
|
||||
async joinWebSocketRoom(): Promise<void> {
|
||||
try {
|
||||
const resp = await this.client.patch("chat/dm/joinWebSocketRoom", <JoinWsRoomReq>{
|
||||
chatid: this.chatid,
|
||||
userid: this.userid,
|
||||
connId: WebSocketHandler.getInstance().connId,
|
||||
});
|
||||
return
|
||||
} catch (e) {
|
||||
if (isAxiosError<GenericErrorBody>(e)) {
|
||||
throw e;
|
||||
}
|
||||
throw new Error("Unexpected error")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user