feat: sending and receiving messages with files, editing text messages
This commit is contained in:
@@ -40,6 +40,7 @@ export type { BillPaymentStatus } from './models/BillPaymentStatus';
|
|||||||
export type { BillStatusUpdateRequest } from './models/BillStatusUpdateRequest';
|
export type { BillStatusUpdateRequest } from './models/BillStatusUpdateRequest';
|
||||||
export type { BoardSchema } from './models/BoardSchema';
|
export type { BoardSchema } from './models/BoardSchema';
|
||||||
export type { Body_parse_deals_excel } from './models/Body_parse_deals_excel';
|
export type { Body_parse_deals_excel } from './models/Body_parse_deals_excel';
|
||||||
|
export type { Body_send_messages_with_files } from './models/Body_send_messages_with_files';
|
||||||
export type { Body_upload_passport_image } from './models/Body_upload_passport_image';
|
export type { Body_upload_passport_image } from './models/Body_upload_passport_image';
|
||||||
export type { Body_upload_product_barcode_image } from './models/Body_upload_product_barcode_image';
|
export type { Body_upload_product_barcode_image } from './models/Body_upload_product_barcode_image';
|
||||||
export type { Body_upload_product_image } from './models/Body_upload_product_image';
|
export type { Body_upload_product_image } from './models/Body_upload_product_image';
|
||||||
@@ -216,6 +217,9 @@ export type { DepartmentSchema } from './models/DepartmentSchema';
|
|||||||
export type { DepartmentSectionBaseSchema } from './models/DepartmentSectionBaseSchema';
|
export type { DepartmentSectionBaseSchema } from './models/DepartmentSectionBaseSchema';
|
||||||
export type { DepartmentSectionBriefSchema } from './models/DepartmentSectionBriefSchema';
|
export type { DepartmentSectionBriefSchema } from './models/DepartmentSectionBriefSchema';
|
||||||
export type { DepartmentSectionSchema } from './models/DepartmentSectionSchema';
|
export type { DepartmentSectionSchema } from './models/DepartmentSectionSchema';
|
||||||
|
export type { EditMessageRequest } from './models/EditMessageRequest';
|
||||||
|
export type { EditMessageResponse } from './models/EditMessageResponse';
|
||||||
|
export type { EditMessageSchema } from './models/EditMessageSchema';
|
||||||
export type { FinishPauseByShiftIdResponse } from './models/FinishPauseByShiftIdResponse';
|
export type { FinishPauseByShiftIdResponse } from './models/FinishPauseByShiftIdResponse';
|
||||||
export type { FinishPauseByUserIdResponse } from './models/FinishPauseByUserIdResponse';
|
export type { FinishPauseByUserIdResponse } from './models/FinishPauseByUserIdResponse';
|
||||||
export type { FinishShiftByIdResponse } from './models/FinishShiftByIdResponse';
|
export type { FinishShiftByIdResponse } from './models/FinishShiftByIdResponse';
|
||||||
@@ -279,12 +283,14 @@ export type { GetWorkShiftsPlanningDataRequest } from './models/GetWorkShiftsPla
|
|||||||
export type { GetWorkShiftsResponse } from './models/GetWorkShiftsResponse';
|
export type { GetWorkShiftsResponse } from './models/GetWorkShiftsResponse';
|
||||||
export type { GroupBillRequestSchema } from './models/GroupBillRequestSchema';
|
export type { GroupBillRequestSchema } from './models/GroupBillRequestSchema';
|
||||||
export type { HTTPValidationError } from './models/HTTPValidationError';
|
export type { HTTPValidationError } from './models/HTTPValidationError';
|
||||||
|
export type { LoadMessagesResponse } from './models/LoadMessagesResponse';
|
||||||
export type { LoadReceiptRequest } from './models/LoadReceiptRequest';
|
export type { LoadReceiptRequest } from './models/LoadReceiptRequest';
|
||||||
export type { LoadReceiptResponse } from './models/LoadReceiptResponse';
|
export type { LoadReceiptResponse } from './models/LoadReceiptResponse';
|
||||||
export type { ManageEmployeeRequest } from './models/ManageEmployeeRequest';
|
export type { ManageEmployeeRequest } from './models/ManageEmployeeRequest';
|
||||||
export type { ManageEmployeeResponse } from './models/ManageEmployeeResponse';
|
export type { ManageEmployeeResponse } from './models/ManageEmployeeResponse';
|
||||||
export type { MarketplaceCreateSchema } from './models/MarketplaceCreateSchema';
|
export type { MarketplaceCreateSchema } from './models/MarketplaceCreateSchema';
|
||||||
export type { MarketplaceSchema } from './models/MarketplaceSchema';
|
export type { MarketplaceSchema } from './models/MarketplaceSchema';
|
||||||
|
export type { MessageFileSchema } from './models/MessageFileSchema';
|
||||||
export type { MessageSchema } from './models/MessageSchema';
|
export type { MessageSchema } from './models/MessageSchema';
|
||||||
export type { ModuleSchema } from './models/ModuleSchema';
|
export type { ModuleSchema } from './models/ModuleSchema';
|
||||||
export type { NotificationChannel } from './models/NotificationChannel';
|
export type { NotificationChannel } from './models/NotificationChannel';
|
||||||
@@ -334,12 +340,15 @@ export type { ProjectGeneralInfoSchema } from './models/ProjectGeneralInfoSchema
|
|||||||
export type { ProjectSchema } from './models/ProjectSchema';
|
export type { ProjectSchema } from './models/ProjectSchema';
|
||||||
export type { ReceiptBoxSchema } from './models/ReceiptBoxSchema';
|
export type { ReceiptBoxSchema } from './models/ReceiptBoxSchema';
|
||||||
export type { ReceiptPalletSchema } from './models/ReceiptPalletSchema';
|
export type { ReceiptPalletSchema } from './models/ReceiptPalletSchema';
|
||||||
|
export type { RepeatSendingMessageSchema } from './models/RepeatSendingMessageSchema';
|
||||||
|
export type { RepeatSendingTextMessageRequest } from './models/RepeatSendingTextMessageRequest';
|
||||||
|
export type { RepeatSendingTextMessageResponse } from './models/RepeatSendingTextMessageResponse';
|
||||||
export type { ResidualBoxSchema } from './models/ResidualBoxSchema';
|
export type { ResidualBoxSchema } from './models/ResidualBoxSchema';
|
||||||
export type { ResidualPalletSchema } from './models/ResidualPalletSchema';
|
export type { ResidualPalletSchema } from './models/ResidualPalletSchema';
|
||||||
export type { ResidualProductSchema } from './models/ResidualProductSchema';
|
export type { ResidualProductSchema } from './models/ResidualProductSchema';
|
||||||
export type { RoleSchema } from './models/RoleSchema';
|
export type { RoleSchema } from './models/RoleSchema';
|
||||||
export type { SendMessageRequest } from './models/SendMessageRequest';
|
export type { SendTextMessageRequest } from './models/SendTextMessageRequest';
|
||||||
export type { SendMessageResponse } from './models/SendMessageResponse';
|
export type { SendTextMessageResponse } from './models/SendTextMessageResponse';
|
||||||
export type { ServiceCategoryReorderRequest } from './models/ServiceCategoryReorderRequest';
|
export type { ServiceCategoryReorderRequest } from './models/ServiceCategoryReorderRequest';
|
||||||
export type { ServiceCategoryReorderResponse } from './models/ServiceCategoryReorderResponse';
|
export type { ServiceCategoryReorderResponse } from './models/ServiceCategoryReorderResponse';
|
||||||
export type { ServiceCategorySchema } from './models/ServiceCategorySchema';
|
export type { ServiceCategorySchema } from './models/ServiceCategorySchema';
|
||||||
|
|||||||
8
src/client/models/Body_send_messages_with_files.ts
Normal file
8
src/client/models/Body_send_messages_with_files.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type Body_send_messages_with_files = {
|
||||||
|
files: Array<Blob>;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/EditMessageRequest.ts
Normal file
9
src/client/models/EditMessageRequest.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { EditMessageSchema } from './EditMessageSchema';
|
||||||
|
export type EditMessageRequest = {
|
||||||
|
message: EditMessageSchema;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
/* istanbul ignore file */
|
/* istanbul ignore file */
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export type SendMessageResponse = {
|
export type EditMessageResponse = {
|
||||||
ok: boolean;
|
ok: boolean;
|
||||||
message: string;
|
message: string;
|
||||||
};
|
};
|
||||||
10
src/client/models/EditMessageSchema.ts
Normal file
10
src/client/models/EditMessageSchema.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type EditMessageSchema = {
|
||||||
|
text: string;
|
||||||
|
chatId: number;
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/LoadMessagesResponse.ts
Normal file
9
src/client/models/LoadMessagesResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type LoadMessagesResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
12
src/client/models/MessageFileSchema.ts
Normal file
12
src/client/models/MessageFileSchema.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type MessageFileSchema = {
|
||||||
|
id: number;
|
||||||
|
filePath: string;
|
||||||
|
type: string;
|
||||||
|
fileName: string;
|
||||||
|
fileSize: number;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
/* istanbul ignore file */
|
/* istanbul ignore file */
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
import type { MessageFileSchema } from './MessageFileSchema';
|
||||||
import type { TgUserSchema } from './TgUserSchema';
|
import type { TgUserSchema } from './TgUserSchema';
|
||||||
export type MessageSchema = {
|
export type MessageSchema = {
|
||||||
text: string;
|
text: string;
|
||||||
@@ -10,5 +11,7 @@ export type MessageSchema = {
|
|||||||
createdAt: string;
|
createdAt: string;
|
||||||
tgSender: (TgUserSchema | null);
|
tgSender: (TgUserSchema | null);
|
||||||
status: string;
|
status: string;
|
||||||
|
isEdited: boolean;
|
||||||
|
file?: (MessageFileSchema | null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
10
src/client/models/RepeatSendingMessageSchema.ts
Normal file
10
src/client/models/RepeatSendingMessageSchema.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type RepeatSendingMessageSchema = {
|
||||||
|
text: string;
|
||||||
|
chatId: number;
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/RepeatSendingTextMessageRequest.ts
Normal file
9
src/client/models/RepeatSendingTextMessageRequest.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { RepeatSendingMessageSchema } from './RepeatSendingMessageSchema';
|
||||||
|
export type RepeatSendingTextMessageRequest = {
|
||||||
|
message: RepeatSendingMessageSchema;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/RepeatSendingTextMessageResponse.ts
Normal file
9
src/client/models/RepeatSendingTextMessageResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type RepeatSendingTextMessageResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import type { BaseMessageSchema } from './BaseMessageSchema';
|
import type { BaseMessageSchema } from './BaseMessageSchema';
|
||||||
export type SendMessageRequest = {
|
export type SendTextMessageRequest = {
|
||||||
message: BaseMessageSchema;
|
message: BaseMessageSchema;
|
||||||
};
|
};
|
||||||
|
|
||||||
9
src/client/models/SendTextMessageResponse.ts
Normal file
9
src/client/models/SendTextMessageResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type SendTextMessageResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -2,29 +2,35 @@
|
|||||||
/* istanbul ignore file */
|
/* istanbul ignore file */
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
import type { Body_send_messages_with_files } from '../models/Body_send_messages_with_files';
|
||||||
import type { CreateChatRequest } from '../models/CreateChatRequest';
|
import type { CreateChatRequest } from '../models/CreateChatRequest';
|
||||||
import type { CreateChatResponse } from '../models/CreateChatResponse';
|
import type { CreateChatResponse } from '../models/CreateChatResponse';
|
||||||
import type { DeleteMessageResponse } from '../models/DeleteMessageResponse';
|
import type { DeleteMessageResponse } from '../models/DeleteMessageResponse';
|
||||||
|
import type { EditMessageRequest } from '../models/EditMessageRequest';
|
||||||
|
import type { EditMessageResponse } from '../models/EditMessageResponse';
|
||||||
import type { GetChatRequest } from '../models/GetChatRequest';
|
import type { GetChatRequest } from '../models/GetChatRequest';
|
||||||
import type { GetChatResponse } from '../models/GetChatResponse';
|
import type { GetChatResponse } from '../models/GetChatResponse';
|
||||||
import type { GetMessagesRequest } from '../models/GetMessagesRequest';
|
import type { GetMessagesRequest } from '../models/GetMessagesRequest';
|
||||||
import type { GetMessagesResponse } from '../models/GetMessagesResponse';
|
import type { GetMessagesResponse } from '../models/GetMessagesResponse';
|
||||||
import type { SendMessageRequest } from '../models/SendMessageRequest';
|
import type { LoadMessagesResponse } from '../models/LoadMessagesResponse';
|
||||||
import type { SendMessageResponse } from '../models/SendMessageResponse';
|
import type { RepeatSendingTextMessageRequest } from '../models/RepeatSendingTextMessageRequest';
|
||||||
|
import type { RepeatSendingTextMessageResponse } from '../models/RepeatSendingTextMessageResponse';
|
||||||
|
import type { SendTextMessageRequest } from '../models/SendTextMessageRequest';
|
||||||
|
import type { SendTextMessageResponse } from '../models/SendTextMessageResponse';
|
||||||
import type { CancelablePromise } from '../core/CancelablePromise';
|
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||||
import { OpenAPI } from '../core/OpenAPI';
|
import { OpenAPI } from '../core/OpenAPI';
|
||||||
import { request as __request } from '../core/request';
|
import { request as __request } from '../core/request';
|
||||||
export class ChatService {
|
export class ChatService {
|
||||||
/**
|
/**
|
||||||
* Send Message
|
* Send Text Message
|
||||||
* @returns SendMessageResponse Successful Response
|
* @returns SendTextMessageResponse Successful Response
|
||||||
* @throws ApiError
|
* @throws ApiError
|
||||||
*/
|
*/
|
||||||
public static sendMessage({
|
public static sendTextMessage({
|
||||||
requestBody,
|
requestBody,
|
||||||
}: {
|
}: {
|
||||||
requestBody: SendMessageRequest,
|
requestBody: SendTextMessageRequest,
|
||||||
}): CancelablePromise<SendMessageResponse> {
|
}): CancelablePromise<SendTextMessageResponse> {
|
||||||
return __request(OpenAPI, {
|
return __request(OpenAPI, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '/chat/message',
|
url: '/chat/message',
|
||||||
@@ -35,6 +41,74 @@ export class ChatService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Edit Message
|
||||||
|
* @returns EditMessageResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static editMessage({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: EditMessageRequest,
|
||||||
|
}): CancelablePromise<EditMessageResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'PATCH',
|
||||||
|
url: '/chat/message',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Repeat Sending Text Message
|
||||||
|
* @returns RepeatSendingTextMessageResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static repeatSendingTextMessage({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: RepeatSendingTextMessageRequest,
|
||||||
|
}): CancelablePromise<RepeatSendingTextMessageResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/chat/message/repeat',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Send Messages With Files
|
||||||
|
* @returns LoadMessagesResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static sendMessagesWithFiles({
|
||||||
|
chatId,
|
||||||
|
caption,
|
||||||
|
formData,
|
||||||
|
}: {
|
||||||
|
chatId: number,
|
||||||
|
caption: string,
|
||||||
|
formData: Body_send_messages_with_files,
|
||||||
|
}): CancelablePromise<LoadMessagesResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/chat/message/files',
|
||||||
|
query: {
|
||||||
|
'chat_id': chatId,
|
||||||
|
'caption': caption,
|
||||||
|
},
|
||||||
|
formData: formData,
|
||||||
|
mediaType: 'multipart/form-data',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Delete Message
|
* Delete Message
|
||||||
* @returns DeleteMessageResponse Successful Response
|
* @returns DeleteMessageResponse Successful Response
|
||||||
@@ -116,4 +190,25 @@ export class ChatService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Get Tg File
|
||||||
|
* @returns any Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static getTgFileChatTgFileFileIdGet({
|
||||||
|
fileId,
|
||||||
|
}: {
|
||||||
|
fileId: number,
|
||||||
|
}): CancelablePromise<any> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/chat/tg-file/{file_id}',
|
||||||
|
path: {
|
||||||
|
'file_id': fileId,
|
||||||
|
},
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,17 +15,13 @@ export const ActionIconCopy: FC<Props> = ({ onCopiedLabel, value }) => {
|
|||||||
<Tooltip
|
<Tooltip
|
||||||
label={onCopiedLabel}
|
label={onCopiedLabel}
|
||||||
offset={5}
|
offset={5}
|
||||||
position="bottom"
|
|
||||||
radius="xl"
|
radius="xl"
|
||||||
transitionProps={{ duration: 100, transition: "slide-down" }}
|
transitionProps={{ duration: 100, transition: "slide-down" }}
|
||||||
opened={clipboard.copied}>
|
opened={clipboard.copied}>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
variant={"default"}
|
variant={"default"}
|
||||||
size="lg"
|
size="lg"
|
||||||
onClick={() => {
|
onClick={() => clipboard.copy(value)}
|
||||||
console.log("AKLSKLSKSM");
|
|
||||||
return clipboard.copy(value);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{clipboard.copied ? (
|
{clipboard.copied ? (
|
||||||
<IconCheck
|
<IconCheck
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ const Chat = () => {
|
|||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
elements.push((
|
elements.push(
|
||||||
<Message key={currMessage.id + "msg"} message={currMessage} />
|
<Message key={currMessage.id + "msg"} message={currMessage} />
|
||||||
));
|
);
|
||||||
prevMessage = currMessage;
|
prevMessage = currMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,12 +38,13 @@ const Chat = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack h={"96vh"}>
|
||||||
<ScrollArea
|
<ScrollArea
|
||||||
|
h={"100%"}
|
||||||
viewportRef={scrollRef}
|
viewportRef={scrollRef}
|
||||||
onScrollPositionChange={onScrollPositionChange}
|
onScrollPositionChange={onScrollPositionChange}
|
||||||
>
|
>
|
||||||
<Stack h={"91vh"} pr={"md"} gap={"sm"}>
|
<Stack pr={"md"} gap={"sm"}>
|
||||||
{getChatElements()}
|
{getChatElements()}
|
||||||
</Stack>
|
</Stack>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
|||||||
15
src/components/Chat/components/ChatFile/ChatFile.module.css
Normal file
15
src/components/Chat/components/ChatFile/ChatFile.module.css
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
.file-circe {
|
||||||
|
@mixin light {
|
||||||
|
background-color: var(--mantine-color-gray-0);
|
||||||
|
}
|
||||||
|
@mixin dark {
|
||||||
|
background-color: var(--mantine-color-dark-4);
|
||||||
|
}
|
||||||
|
border-radius: 50%;
|
||||||
|
align-items: center;
|
||||||
|
justify-items: center;
|
||||||
|
padding: 0.8rem;
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
21
src/components/Chat/components/ChatFile/ChatFile.tsx
Normal file
21
src/components/Chat/components/ChatFile/ChatFile.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { MessageFileSchema } from "../../../../client";
|
||||||
|
import ChatDocument from "./components/ChatDocument.tsx";
|
||||||
|
import ChatPhoto from "./components/ChatPhoto.tsx";
|
||||||
|
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
file: MessageFileSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChatFile = ({ file }: Props) => {
|
||||||
|
if (file.type === "photo") {
|
||||||
|
return (
|
||||||
|
<ChatPhoto file={file} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ChatDocument file={file} />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChatFile;
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { Center, Group, rem, Stack, Text } from "@mantine/core";
|
||||||
|
import { IconFileFilled } from "@tabler/icons-react";
|
||||||
|
import { MessageFileSchema } from "../../../../../client";
|
||||||
|
import styles from "../ChatFile.module.css";
|
||||||
|
import classNames from "classnames";
|
||||||
|
import downloadFile from "../utils/downloadFile.ts";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
file: MessageFileSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChatDocument = ({ file }: Props) => {
|
||||||
|
const round = (value: number) => Math.round(value * 10) / 10;
|
||||||
|
|
||||||
|
const getFileSize = () => {
|
||||||
|
const BYTES_IN_KB = 1_024;
|
||||||
|
const BYTES_IN_MB = 1_048_576;
|
||||||
|
const BYTES_IN_GB = 1_073_741_824;
|
||||||
|
|
||||||
|
if (file.fileSize < BYTES_IN_KB) {
|
||||||
|
return `${file.fileSize} B`;
|
||||||
|
}
|
||||||
|
if (file.fileSize < BYTES_IN_MB) {
|
||||||
|
return `${round(file.fileSize / BYTES_IN_KB)} KB`;
|
||||||
|
}
|
||||||
|
if (file.fileSize < BYTES_IN_GB) {
|
||||||
|
return `${round(file.fileSize / BYTES_IN_MB)} MB`;
|
||||||
|
}
|
||||||
|
return `${round(file.fileSize / BYTES_IN_GB)} GB`;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group justify={"center"} my={"sm"}>
|
||||||
|
<Center
|
||||||
|
className={classNames(styles["file-circe"])}
|
||||||
|
onClick={() => downloadFile(file)}
|
||||||
|
>
|
||||||
|
<IconFileFilled />
|
||||||
|
</Center>
|
||||||
|
<Stack gap={rem(1)} my={rem(1)}>
|
||||||
|
<Text>{file.fileName}</Text>
|
||||||
|
<Text>{getFileSize()}</Text>
|
||||||
|
</Stack>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChatDocument;
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { MessageFileSchema } from "../../../../../client";
|
||||||
|
import { Box, Image, Modal } from "@mantine/core";
|
||||||
|
import getDocLink from "../utils/getDocLink.ts";
|
||||||
|
import { useDisclosure } from "@mantine/hooks";
|
||||||
|
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
file: MessageFileSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChatPhoto = ({ file }: Props) => {
|
||||||
|
const [opened, { open, close }] = useDisclosure(false);
|
||||||
|
|
||||||
|
const image = (
|
||||||
|
<Image
|
||||||
|
src={getDocLink(file.id)}
|
||||||
|
radius={"md"}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
opened={opened}
|
||||||
|
onClose={close}
|
||||||
|
size={"auto"}
|
||||||
|
withCloseButton={false}
|
||||||
|
>
|
||||||
|
<Box mah={"90vh"} maw={"90vw"}>
|
||||||
|
{image}
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
<Box
|
||||||
|
my={"sm"}
|
||||||
|
onClick={open}
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
>
|
||||||
|
{image}
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChatPhoto;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { MessageFileSchema } from "../../../../../client";
|
||||||
|
import getDocLink from "./getDocLink.ts";
|
||||||
|
|
||||||
|
|
||||||
|
const downloadFile = (file: MessageFileSchema) => {
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = getDocLink(file.id);
|
||||||
|
link.download = file.fileName;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default downloadFile;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
const getDocLink = (fileId: number) => {
|
||||||
|
return `${import.meta.env.VITE_API_URL}/chat/tg-file/${fileId}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getDocLink;
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
import { MessageSchema } from "../../../../client";
|
import { MessageSchema } from "../../../../client";
|
||||||
import { Box, Center, em, Flex, Group, rem } from "@mantine/core";
|
import { Box, Center, em, Flex, Group, rem } from "@mantine/core";
|
||||||
import { formatDateTime } from "../../../../types/utils.ts";
|
import { formatDateTime } from "../../../../types/utils.ts";
|
||||||
import { IconAlertCircle, IconCheck, IconClock, IconTrash } from "@tabler/icons-react";
|
import { IconAlertCircle, IconBrandTelegram, IconCheck, IconClock, IconEdit, IconTrash } from "@tabler/icons-react";
|
||||||
import { useContextMenu } from "mantine-contextmenu";
|
import { useContextMenu } from "mantine-contextmenu";
|
||||||
import useMessage from "./hooks/useMessage.tsx";
|
import useMessage from "./hooks/useMessage.tsx";
|
||||||
import styles from "../../Chat.module.css";
|
import styles from "../../Chat.module.css";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import ChatFile from "../ChatFile/ChatFile.tsx";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
message: MessageSchema;
|
message: MessageSchema;
|
||||||
@@ -20,18 +21,47 @@ enum MessageStatuses {
|
|||||||
const Message = ({ message }: Props) => {
|
const Message = ({ message }: Props) => {
|
||||||
const isMine = !message.tgSender;
|
const isMine = !message.tgSender;
|
||||||
const isSuccess = message.status == MessageStatuses.SUCCESS;
|
const isSuccess = message.status == MessageStatuses.SUCCESS;
|
||||||
const { onDeleteMessageClick } = useMessage();
|
const isError = message.status == MessageStatuses.ERROR;
|
||||||
|
|
||||||
|
const {
|
||||||
|
onDeleteMessageClick,
|
||||||
|
onEditMessageClick,
|
||||||
|
onRepeatSendingClick,
|
||||||
|
} = useMessage();
|
||||||
|
|
||||||
const { showContextMenu } = useContextMenu();
|
const { showContextMenu } = useContextMenu();
|
||||||
|
|
||||||
const contextMenu = () => showContextMenu([
|
const contextMenuSuccessMsg = () => showContextMenu([
|
||||||
{
|
{
|
||||||
key: "delete",
|
key: "delete",
|
||||||
onClick: () => onDeleteMessageClick(message),
|
onClick: () => onDeleteMessageClick(message),
|
||||||
title: "Удалить",
|
title: "Удалить",
|
||||||
icon: <IconTrash />,
|
icon: <IconTrash />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "edit",
|
||||||
|
onClick: () => onEditMessageClick(message),
|
||||||
|
title: "Редактировать",
|
||||||
|
icon: <IconEdit />,
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const contextMenuErrorMsg = () => showContextMenu([
|
||||||
|
{
|
||||||
|
key: "repeatSending",
|
||||||
|
onClick: () => onRepeatSendingClick(message),
|
||||||
|
title: "Повторить отправку",
|
||||||
|
icon: <IconBrandTelegram />,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const getContext = () => {
|
||||||
|
if (!isMine) return;
|
||||||
|
|
||||||
|
if (isSuccess) return contextMenuSuccessMsg();
|
||||||
|
if (isError) return contextMenuErrorMsg();
|
||||||
|
}
|
||||||
|
|
||||||
const getStatusIcon = () => {
|
const getStatusIcon = () => {
|
||||||
const size = em(18);
|
const size = em(18);
|
||||||
if (message.status == MessageStatuses.ERROR) {
|
if (message.status == MessageStatuses.ERROR) {
|
||||||
@@ -49,11 +79,15 @@ const Message = ({ message }: Props) => {
|
|||||||
py={rem(5)}
|
py={rem(5)}
|
||||||
px={rem(15)}
|
px={rem(15)}
|
||||||
className={classNames(isMine ? styles["message"] : styles["other-message"])}
|
className={classNames(isMine ? styles["message"] : styles["other-message"])}
|
||||||
onContextMenu={isMine && isSuccess ? contextMenu() : undefined}
|
onContextMenu={getContext()}
|
||||||
|
maw={em(600)}
|
||||||
>
|
>
|
||||||
{!isMine && (
|
{!isMine && (
|
||||||
<div>{message.tgSender!.lastName} {message.tgSender!.firstName}</div>
|
<div>{message.tgSender!.lastName} {message.tgSender!.firstName}</div>
|
||||||
)}
|
)}
|
||||||
|
{message.file && (
|
||||||
|
<ChatFile file={message.file} />
|
||||||
|
)}
|
||||||
<div>{message.text}</div>
|
<div>{message.text}</div>
|
||||||
<Group
|
<Group
|
||||||
gap={em(5)}
|
gap={em(5)}
|
||||||
@@ -61,6 +95,7 @@ const Message = ({ message }: Props) => {
|
|||||||
align={"center"}
|
align={"center"}
|
||||||
wrap={"nowrap"}
|
wrap={"nowrap"}
|
||||||
>
|
>
|
||||||
|
{message.isEdited && "ред."}
|
||||||
<Center>
|
<Center>
|
||||||
{formatDateTime(message.createdAt).substring(11, 16)}
|
{formatDateTime(message.createdAt).substring(11, 16)}
|
||||||
</Center>
|
</Center>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { ChatService, MessageSchema } from "../../../../../client";
|
import { ChatService, MessageSchema } from "../../../../../client";
|
||||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||||
|
import { useChatContext } from "../../../../../pages/ClientsPage/contexts/ChatContext.tsx";
|
||||||
|
|
||||||
const useMessage = () => {
|
const useMessage = () => {
|
||||||
|
const { form } = useChatContext();
|
||||||
|
|
||||||
const onDeleteMessageClick = (message: MessageSchema) => {
|
const onDeleteMessageClick = (message: MessageSchema) => {
|
||||||
ChatService.deleteMessage({
|
ChatService.deleteMessage({
|
||||||
@@ -15,8 +17,31 @@ const useMessage = () => {
|
|||||||
.catch(err => console.log(err));
|
.catch(err => console.log(err));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onEditMessageClick = (message: MessageSchema) => {
|
||||||
|
form.setValues({
|
||||||
|
messageId: message.id,
|
||||||
|
message: message.text,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRepeatSendingClick = (message: MessageSchema) => {
|
||||||
|
ChatService.repeatSendingTextMessage({
|
||||||
|
requestBody: {
|
||||||
|
message,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(({ ok, message }) => {
|
||||||
|
if (!ok) {
|
||||||
|
notifications.error({ message });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
onDeleteMessageClick,
|
onDeleteMessageClick,
|
||||||
|
onEditMessageClick,
|
||||||
|
onRepeatSendingClick,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,43 +1,71 @@
|
|||||||
import { Button, Group, TextInput, Tooltip } from "@mantine/core";
|
import { ActionIcon, Button, Divider, Group, Stack, TextInput, Tooltip } from "@mantine/core";
|
||||||
import { IconSend2 } from "@tabler/icons-react";
|
import { IconCheck, IconPaperclip, IconSend2, IconX } from "@tabler/icons-react";
|
||||||
import { useChatContext } from "../../../../pages/ClientsPage/contexts/ChatContext.tsx";
|
import { useChatContext } from "../../../../pages/ClientsPage/contexts/ChatContext.tsx";
|
||||||
import { useForm } from "@mantine/form";
|
|
||||||
import ActionIconCopy from "../../../ActionIconCopy/ActionIconCopy.tsx";
|
import ActionIconCopy from "../../../ActionIconCopy/ActionIconCopy.tsx";
|
||||||
|
import SelectedFile from "../SelectedFile/SelectedFile.tsx";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
|
||||||
export type MessageForm = {
|
|
||||||
message: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const MessageInput = () => {
|
const MessageInput = () => {
|
||||||
const { sendMessage, chat } = useChatContext();
|
const {
|
||||||
const form = useForm<MessageForm>({
|
submitMessage,
|
||||||
initialValues: {
|
chat,
|
||||||
message: "",
|
form,
|
||||||
},
|
files,
|
||||||
});
|
fileDialog,
|
||||||
|
} = useChatContext();
|
||||||
|
|
||||||
|
const getFiles = useMemo(() => {
|
||||||
|
return files.map(file => (
|
||||||
|
<SelectedFile key={file.name} file={file} />
|
||||||
|
));
|
||||||
|
}, [files]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={form.onSubmit(values => sendMessage(values, form))}>
|
<form onSubmit={form.onSubmit(values => submitMessage(values))}>
|
||||||
<Group wrap={"nowrap"} align={"center"}>
|
<Stack gap={"xs"}>
|
||||||
{chat?.tgGroup?.tgInviteLink && (
|
<Divider />
|
||||||
<Tooltip label={"Ссылка-приглашение"}>
|
{getFiles}
|
||||||
|
<Group wrap={"nowrap"} align={"center"}>
|
||||||
|
{chat?.tgGroup?.tgInviteLink && (
|
||||||
<ActionIconCopy
|
<ActionIconCopy
|
||||||
onCopiedLabel={"Ссылка скопирована в буфер обмена"}
|
onCopiedLabel={"Ссылка на чат скопирована в буфер обмена"}
|
||||||
value={chat.tgGroup.tgInviteLink}
|
value={chat.tgGroup.tgInviteLink}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
<Tooltip label={"Прикрепить файлы"}>
|
||||||
|
<ActionIcon
|
||||||
|
variant={"default"}
|
||||||
|
onClick={fileDialog.open}
|
||||||
|
size="lg"
|
||||||
|
>
|
||||||
|
<IconPaperclip />
|
||||||
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
<TextInput
|
||||||
<TextInput
|
{...form.getInputProps("message")}
|
||||||
{...form.getInputProps("message")}
|
w={"100%"}
|
||||||
w={"100%"}
|
/>
|
||||||
/>
|
{form.values.messageId && (
|
||||||
<Button
|
<Button
|
||||||
variant={"default"}
|
variant={"default"}
|
||||||
type="submit"
|
onClick={form.reset}
|
||||||
>
|
>
|
||||||
<IconSend2 />
|
<IconX />
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
)}
|
||||||
|
<Button
|
||||||
|
variant={"default"}
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
{form.values.messageId ? (
|
||||||
|
<IconCheck />
|
||||||
|
) : (
|
||||||
|
<IconSend2 />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
38
src/components/Chat/components/SelectedFile/SelectedFile.tsx
Normal file
38
src/components/Chat/components/SelectedFile/SelectedFile.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { Box, Group, Text } from "@mantine/core";
|
||||||
|
import { IconX } from "@tabler/icons-react";
|
||||||
|
import { useChatContext } from "../../../../pages/ClientsPage/contexts/ChatContext.tsx";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
file: File;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SelectedFile = ({ file }: Props) => {
|
||||||
|
const { files, filesHandlers } = useChatContext();
|
||||||
|
|
||||||
|
const onCancelFileClick = () => {
|
||||||
|
const idx = files.findIndex(f => f.name === file.name && f.type === file.type);
|
||||||
|
|
||||||
|
if (idx < 0) return;
|
||||||
|
filesHandlers.remove(idx);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Group justify={"space-between"}>
|
||||||
|
<Text>
|
||||||
|
{file.name}
|
||||||
|
</Text>
|
||||||
|
<Box
|
||||||
|
onClick={onCancelFileClick}
|
||||||
|
style={{
|
||||||
|
cursor: "pointer",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconX />
|
||||||
|
</Box>
|
||||||
|
</Group>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelectedFile;
|
||||||
@@ -3,13 +3,16 @@ import CreateServiceCategoryModal from "../pages/ServicesPage/modals/CreateServi
|
|||||||
import CreateServiceModal from "../pages/ServicesPage/modals/CreateServiceModal.tsx";
|
import CreateServiceModal from "../pages/ServicesPage/modals/CreateServiceModal.tsx";
|
||||||
import createProductModal from "../pages/ProductsPage/modals/CreateProductModal/CreateProductModal.tsx";
|
import createProductModal from "../pages/ProductsPage/modals/CreateProductModal/CreateProductModal.tsx";
|
||||||
import ProductFormModal from "../pages/ClientsPage/modals/ClientFormModal/ClientFormModal.tsx";
|
import ProductFormModal from "../pages/ClientsPage/modals/ClientFormModal/ClientFormModal.tsx";
|
||||||
import AddCardServiceModal from "../modules/cardModules/cardEditorTabs/ProductAndServiceTab/modals/AddCardServiceModal.tsx";
|
import AddCardServiceModal
|
||||||
import AddCardProductModal from "../modules/cardModules/cardEditorTabs/ProductAndServiceTab/modals/AddCardProductModal.tsx";
|
from "../modules/cardModules/cardEditorTabs/ProductAndServiceTab/modals/AddCardServiceModal.tsx";
|
||||||
|
import AddCardProductModal
|
||||||
|
from "../modules/cardModules/cardEditorTabs/ProductAndServiceTab/modals/AddCardProductModal.tsx";
|
||||||
import PrintBarcodeModal from "./PrintBarcodeModal/PrintBarcodeModal.tsx";
|
import PrintBarcodeModal from "./PrintBarcodeModal/PrintBarcodeModal.tsx";
|
||||||
import AddBarcodeModal from "./AddBarcodeModal/AddBarcodeModal.tsx";
|
import AddBarcodeModal from "./AddBarcodeModal/AddBarcodeModal.tsx";
|
||||||
import BarcodeTemplateFormModal
|
import BarcodeTemplateFormModal
|
||||||
from "../pages/BarcodePage/modals/BarcodeTemplateFormModal/BarcodeTemplateFormModal.tsx";
|
from "../pages/BarcodePage/modals/BarcodeTemplateFormModal/BarcodeTemplateFormModal.tsx";
|
||||||
import ProductServiceFormModal from "../modules/cardModules/cardEditorTabs/ProductAndServiceTab/modals/ProductServiceFormModal.tsx";
|
import ProductServiceFormModal
|
||||||
|
from "../modules/cardModules/cardEditorTabs/ProductAndServiceTab/modals/ProductServiceFormModal.tsx";
|
||||||
import UserFormModal from "../pages/AdminPage/modals/UserFormModal/UserFormModal.tsx";
|
import UserFormModal from "../pages/AdminPage/modals/UserFormModal/UserFormModal.tsx";
|
||||||
import EmployeeSelectModal from "./EmployeeSelectModal/EmployeeSelectModal.tsx";
|
import EmployeeSelectModal from "./EmployeeSelectModal/EmployeeSelectModal.tsx";
|
||||||
import EmployeeTableModal from "./EmployeeTableModal/EmployeeTableModal.tsx";
|
import EmployeeTableModal from "./EmployeeTableModal/EmployeeTableModal.tsx";
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ const generateRows = (modules: Module[]) => {
|
|||||||
};
|
};
|
||||||
const tsxContent = template(data);
|
const tsxContent = template(data);
|
||||||
fs.writeFileSync(OUTPUT_PATH, tsxContent);
|
fs.writeFileSync(OUTPUT_PATH, tsxContent);
|
||||||
console.log("File successfully generated.");
|
console.log("ChatFile successfully generated.");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,31 @@
|
|||||||
import React, { createContext, FC, MutableRefObject, useContext, useEffect, useRef, useState } from "react";
|
import React, { createContext, FC, MutableRefObject, useContext, useEffect, useRef, useState } from "react";
|
||||||
import { ChatSchema, ChatService, MessageSchema } from "../../../client";
|
import { ChatSchema, ChatService, MessageSchema } from "../../../client";
|
||||||
import { notifications } from "../../../shared/lib/notifications.ts";
|
import { notifications } from "../../../shared/lib/notifications.ts";
|
||||||
import { MessageForm } from "../../../components/Chat/components/MessageInput/MessageInput.tsx";
|
import { useForm, UseFormReturnType } from "@mantine/form";
|
||||||
import { UseFormReturnType } from "@mantine/form";
|
import { useDebouncedState, useFileDialog, useListState, UseListStateHandlers } from "@mantine/hooks";
|
||||||
import { useDebouncedState } from "@mantine/hooks";
|
|
||||||
|
export type MessageForm = {
|
||||||
|
message: string;
|
||||||
|
messageId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileDialog = {
|
||||||
|
files: FileList | null;
|
||||||
|
open: () => void;
|
||||||
|
reset: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
type ChatContextState = {
|
type ChatContextState = {
|
||||||
chat: ChatSchema | null;
|
chat: ChatSchema | null;
|
||||||
setChat: (chat: ChatSchema | null) => void;
|
setChat: (chat: ChatSchema | null) => void;
|
||||||
messages: MessageSchema[];
|
messages: MessageSchema[];
|
||||||
|
form: UseFormReturnType<MessageForm>;
|
||||||
onScrollPositionChange: (values: { x: number, y: number }) => void;
|
onScrollPositionChange: (values: { x: number, y: number }) => void;
|
||||||
scrollRef: MutableRefObject<HTMLDivElement | null>;
|
scrollRef: MutableRefObject<HTMLDivElement | null>;
|
||||||
sendMessage: (values: MessageForm, form: UseFormReturnType<MessageForm>) => void;
|
submitMessage: (values: MessageForm) => void;
|
||||||
|
files: Array<File>;
|
||||||
|
filesHandlers: UseListStateHandlers<File>;
|
||||||
|
fileDialog: FileDialog;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ChatContext = createContext<ChatContextState | undefined>(undefined);
|
const ChatContext = createContext<ChatContextState | undefined>(undefined);
|
||||||
@@ -26,17 +40,30 @@ const useChatContextState = () => {
|
|||||||
const [isScrollToBottom, setIsScrollToBottom] = useState(true);
|
const [isScrollToBottom, setIsScrollToBottom] = useState(true);
|
||||||
const [scrollPosition, setScrollPosition] = useDebouncedState<{ x: number, y: number }>({ x: 0, y: 0 }, 400);
|
const [scrollPosition, setScrollPosition] = useDebouncedState<{ x: number, y: number }>({ x: 0, y: 0 }, 400);
|
||||||
|
|
||||||
|
const [files, filesHandlers] = useListState<File>([]);
|
||||||
|
const fileDialog = useFileDialog();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
filesHandlers.setState(Array.from(fileDialog.files ?? []));
|
||||||
|
}, [fileDialog.files]);
|
||||||
|
|
||||||
|
const form = useForm<MessageForm>({
|
||||||
|
initialValues: {
|
||||||
|
message: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const setChatValue = (chat: ChatSchema | null) => {
|
const setChatValue = (chat: ChatSchema | null) => {
|
||||||
if (chat) {
|
if (chat) {
|
||||||
setOffset(0);
|
setOffset(0);
|
||||||
setHasMore(true);
|
setHasMore(true);
|
||||||
setScrollPosition({ x: 0, y: 0 });
|
setScrollPosition({ x: 0, y: 0 });
|
||||||
|
filesHandlers.setState([]);
|
||||||
|
form.reset();
|
||||||
}
|
}
|
||||||
setChat(chat);
|
setChat(chat);
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(`has more = ${hasMore}`);
|
|
||||||
|
|
||||||
const fetchMessages = () => {
|
const fetchMessages = () => {
|
||||||
if (!chat) return;
|
if (!chat) return;
|
||||||
|
|
||||||
@@ -71,9 +98,7 @@ const useChatContextState = () => {
|
|||||||
setMessages((prev) => [...prev, ...newMessages]);
|
setMessages((prev) => [...prev, ...newMessages]);
|
||||||
setOffset((prev) => prev + limit);
|
setOffset((prev) => prev + limit);
|
||||||
if (scrollRef.current) {
|
if (scrollRef.current) {
|
||||||
console.log("FETCH ON SCROLL")
|
|
||||||
const prevPosition = limit / offset * scrollRef.current.scrollHeight;
|
const prevPosition = limit / offset * scrollRef.current.scrollHeight;
|
||||||
console.log(`PREV POSITION = ${prevPosition}`)
|
|
||||||
scrollRef.current.scrollTo({ top: prevPosition, behavior: "instant" });
|
scrollRef.current.scrollTo({ top: prevPosition, behavior: "instant" });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -91,25 +116,42 @@ const useChatContextState = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (scrollRef.current && isScrollToBottom && chat) {
|
if (scrollRef.current && isScrollToBottom && chat) {
|
||||||
setIsScrollToBottom(false);
|
setIsScrollToBottom(false);
|
||||||
console.log("Scroll to the bottom");
|
|
||||||
console.log(`scrollHeight = ${scrollRef.current.scrollHeight}`);
|
|
||||||
scrollRef.current.scrollTo({ top: scrollRef.current.scrollHeight, behavior: "smooth" });
|
scrollRef.current.scrollTo({ top: scrollRef.current.scrollHeight, behavior: "smooth" });
|
||||||
}
|
}
|
||||||
}, [messages, isScrollToBottom]);
|
}, [messages, isScrollToBottom]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!chat) return;
|
if (!chat) return;
|
||||||
console.log(`handleScroll ${Math.abs(scrollPosition.y - 200)}`);
|
|
||||||
if (Math.abs(scrollPosition.y - 200) <= 200 && hasMore) {
|
if (Math.abs(scrollPosition.y - 200) <= 200 && hasMore) {
|
||||||
console.log("handleScroll WORK");
|
|
||||||
fetchMessagesOnScroll();
|
fetchMessagesOnScroll();
|
||||||
}
|
}
|
||||||
}, [scrollPosition]);
|
}, [scrollPosition]);
|
||||||
|
|
||||||
const sendMessage = (values: MessageForm, form: UseFormReturnType<MessageForm>) => {
|
const sendMessageWithFiles = (values: MessageForm) => {
|
||||||
if (!chat) return;
|
if (!chat) return;
|
||||||
|
|
||||||
ChatService.sendMessage({
|
ChatService.sendMessagesWithFiles({
|
||||||
|
formData: {
|
||||||
|
files,
|
||||||
|
},
|
||||||
|
chatId: chat.id,
|
||||||
|
caption: values.message,
|
||||||
|
})
|
||||||
|
.then(({ ok, message }) => {
|
||||||
|
if (!ok) {
|
||||||
|
notifications.error({ message });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
filesHandlers.setState([]);
|
||||||
|
form.reset();
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendTextMessage = (values: MessageForm) => {
|
||||||
|
if (!chat) return;
|
||||||
|
|
||||||
|
ChatService.sendTextMessage({
|
||||||
requestBody: {
|
requestBody: {
|
||||||
message: {
|
message: {
|
||||||
text: values.message,
|
text: values.message,
|
||||||
@@ -127,13 +169,49 @@ const useChatContextState = () => {
|
|||||||
.catch(err => console.log(err));
|
.catch(err => console.log(err));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const editMessage = (values: MessageForm) => {
|
||||||
|
if (!chat) return;
|
||||||
|
|
||||||
|
ChatService.editMessage({
|
||||||
|
requestBody: {
|
||||||
|
message: {
|
||||||
|
id: values.messageId!,
|
||||||
|
text: values.message,
|
||||||
|
chatId: chat.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(({ ok, message }) => {
|
||||||
|
if (!ok) {
|
||||||
|
notifications.error({ message });
|
||||||
|
}
|
||||||
|
form.reset();
|
||||||
|
setIsScrollToBottom(true);
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitMessage = (values: MessageForm) => {
|
||||||
|
if (values.messageId) {
|
||||||
|
editMessage(values);
|
||||||
|
} else if (files.length === 0) {
|
||||||
|
sendTextMessage(values);
|
||||||
|
} else {
|
||||||
|
sendMessageWithFiles(values);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
chat,
|
chat,
|
||||||
setChat: setChatValue,
|
setChat: setChatValue,
|
||||||
messages,
|
messages,
|
||||||
|
form,
|
||||||
onScrollPositionChange: setScrollPosition,
|
onScrollPositionChange: setScrollPosition,
|
||||||
scrollRef,
|
scrollRef,
|
||||||
sendMessage,
|
submitMessage,
|
||||||
|
files,
|
||||||
|
filesHandlers,
|
||||||
|
fileDialog,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user