@webinex/chatify 2.0.0-alpha10 → 2.0.0-alpha13

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.
Files changed (45) hide show
  1. package/dist/{Chatify-DGRV4QKP.js → Chatify-CwAfN1wu.js} +310 -117
  2. package/dist/Chatify-CwAfN1wu.js.map +1 -0
  3. package/dist/Chatify.css +54 -7
  4. package/dist/audit.js +2 -2
  5. package/dist/audit.js.map +1 -1
  6. package/dist/index.js +82 -75
  7. package/dist/types/audit/auditApi.d.ts +5 -1
  8. package/dist/types/core/api/api.d.ts +57 -17206
  9. package/dist/types/core/client.d.ts +4 -1
  10. package/dist/types/ui/ChatGroup/AutoReply/AutoReplyButton.d.ts +1 -0
  11. package/dist/types/ui/ChatGroup/AutoReply/AutoReplyForm.d.ts +12 -0
  12. package/dist/types/ui/ChatGroup/AutoReply/AutoReplyPanel.d.ts +1 -0
  13. package/dist/types/ui/ChatGroup/AutoReply/AutoReplyPeriodInput.d.ts +4 -0
  14. package/dist/types/ui/ChatGroup/AutoReply/AutoReplyTextInput.d.ts +4 -0
  15. package/dist/types/ui/ChatGroup/AutoReply/index.d.ts +17 -0
  16. package/dist/types/ui/ChatGroup/ChatGroupContext.d.ts +2 -1
  17. package/dist/types/ui/ChatGroup/ChatGroupPanel.d.ts +2 -1
  18. package/dist/types/ui/ChatGroup/Layout/ChatGroupActions.d.ts +1 -0
  19. package/dist/types/ui/ChatGroup/Layout/index.d.ts +3 -0
  20. package/dist/types/ui/ChatGroup/index.d.ts +1 -0
  21. package/dist/types/ui/common/Icon.d.ts +1 -0
  22. package/dist/types/ui/localizer.d.ts +15 -0
  23. package/package.json +1 -1
  24. package/src/audit/AuditSearch.tsx +1 -1
  25. package/src/core/api/api.ts +24 -4
  26. package/src/core/client.ts +13 -2
  27. package/src/styles/index.scss +65 -6
  28. package/src/ui/Chat/Members/ChatMembersPanel.tsx +2 -2
  29. package/src/ui/ChatGroup/AutoReply/AutoReplyButton.tsx +21 -0
  30. package/src/ui/ChatGroup/AutoReply/AutoReplyForm.tsx +50 -0
  31. package/src/ui/ChatGroup/AutoReply/AutoReplyPanel.tsx +94 -0
  32. package/src/ui/ChatGroup/AutoReply/AutoReplyPeriodInput.tsx +22 -0
  33. package/src/ui/ChatGroup/AutoReply/AutoReplyTextInput.tsx +22 -0
  34. package/src/ui/ChatGroup/AutoReply/index.ts +19 -0
  35. package/src/ui/ChatGroup/ChatGroupContext.tsx +12 -1
  36. package/src/ui/ChatGroup/ChatGroupPanel.tsx +2 -0
  37. package/src/ui/ChatGroup/Create/CreateChatMemberInput.tsx +1 -1
  38. package/src/ui/ChatGroup/Layout/ChatGroupActions.tsx +15 -0
  39. package/src/ui/ChatGroup/Layout/ChatGroupAside.tsx +2 -2
  40. package/src/ui/ChatGroup/Layout/ChatGroupMain.tsx +2 -0
  41. package/src/ui/ChatGroup/Layout/index.ts +3 -0
  42. package/src/ui/ChatGroup/index.ts +1 -0
  43. package/src/ui/common/Icon.tsx +2 -0
  44. package/src/ui/localizer.tsx +29 -0
  45. package/dist/Chatify-DGRV4QKP.js.map +0 -1
@@ -52,7 +52,10 @@ export declare class ChatifyClient {
52
52
  subscribe: <TMethod extends ChatifySignalRMethod>(method: TMethod, subscriber: (args: ChatifySignalRArgsByMethod[TMethod]) => void) => () => ((args: any[]) => void)[];
53
53
  subscribeReconnect: (subscriber: () => any) => () => (() => void)[];
54
54
  get axios(): AxiosInstance;
55
- accounts: () => Promise<Account[]>;
55
+ accounts: (args: {
56
+ ids?: string[];
57
+ }) => Promise<Account[]>;
58
+ currentUserAccount: () => Promise<Account>;
56
59
  updateAccount: (request: UpdateAccountRequest) => Promise<void>;
57
60
  chats: () => Promise<ChatListItem[]>;
58
61
  chat: (id: string) => Promise<Chat>;
@@ -0,0 +1 @@
1
+ export declare const AutoReplyButton: import('../../../index.ts').Customizable<import('react').ComponentType<unknown>>;
@@ -0,0 +1,12 @@
1
+ import { Dayjs } from 'dayjs';
2
+ export interface AutoReplyFormValue {
3
+ period: [Dayjs, Dayjs];
4
+ text: string;
5
+ }
6
+ export interface AutoReplyFormProps {
7
+ value: AutoReplyFormValue;
8
+ onSubmit: (value: AutoReplyFormValue) => void;
9
+ busy?: boolean;
10
+ }
11
+ export declare const AUTO_REPLY_FORM_INITIAL_VALUE: AutoReplyFormValue;
12
+ export declare const AutoReplyForm: import('../../../index.ts').Customizable<import('react').ComponentType<AutoReplyFormProps>>;
@@ -0,0 +1 @@
1
+ export declare const AutoReplyPanel: import('../../../index.ts').Customizable<import('react').ComponentType<unknown>>;
@@ -0,0 +1,4 @@
1
+ export interface AutoReplyPeriodInputProps {
2
+ name: string;
3
+ }
4
+ export declare const AutoReplyPeriodInput: import('../../../index.ts').Customizable<import('react').ComponentType<AutoReplyPeriodInputProps>>;
@@ -0,0 +1,4 @@
1
+ export interface AutoReplyTextInputProps {
2
+ name: string;
3
+ }
4
+ export declare const AutoReplyTextInput: import('../../../index.ts').Customizable<import('react').ComponentType<AutoReplyTextInputProps>>;
@@ -0,0 +1,17 @@
1
+ import { AutoReplyButton } from './AutoReplyButton';
2
+ import { AutoReplyForm } from './AutoReplyForm';
3
+ import { AutoReplyPanel } from './AutoReplyPanel';
4
+ import { AutoReplyPeriodInput } from './AutoReplyPeriodInput';
5
+ import { AutoReplyTextInput } from './AutoReplyTextInput';
6
+ export * from './AutoReplyButton';
7
+ export * from './AutoReplyForm';
8
+ export * from './AutoReplyPanel';
9
+ export * from './AutoReplyPeriodInput';
10
+ export * from './AutoReplyTextInput';
11
+ export interface AutoReplyCustomizeValue {
12
+ AutoReplyButton?: typeof AutoReplyButton.Component | null;
13
+ AutoReplyForm?: typeof AutoReplyForm.Component | null;
14
+ AutoReplyPanel?: typeof AutoReplyPanel.Component | null;
15
+ AutoReplyPeriodInput?: typeof AutoReplyPeriodInput.Component | null;
16
+ AutoReplyTextInput?: typeof AutoReplyTextInput.Component | null;
17
+ }
@@ -1,12 +1,13 @@
1
1
  import { PropsWithChildren } from 'react';
2
2
  export interface ChatGroupContext {
3
- view: 'chat' | 'new-chat' | null;
3
+ view: 'chat' | 'new-chat' | 'auto-reply' | null;
4
4
  chatId: string | null;
5
5
  showMembers: boolean;
6
6
  none(): void;
7
7
  openChat(chatId: string): void;
8
8
  toggleNewChat(): void;
9
9
  toggleMembers(): void;
10
+ toggleAutoReply(): void;
10
11
  }
11
12
  export declare function useChatGroupContext(): ChatGroupContext;
12
13
  export declare function ChatGroupContext(props: PropsWithChildren<{}>): import("react/jsx-runtime").JSX.Element;
@@ -4,7 +4,8 @@ import { CreateChatPanelCustomizeValue } from './Create';
4
4
  import { ChatGroupLayoutCustomizeValue } from './Layout';
5
5
  import { ChatListPanelCustomizeValue } from './List';
6
6
  import { ChatViewCustomizeValue } from '../Chat';
7
- export interface ChatGroupCustomizeValue extends ChatViewCustomizeValue, CreateChatPanelCustomizeValue, ChatListPanelCustomizeValue, ChatGroupLayoutCustomizeValue {
7
+ import { AutoReplyCustomizeValue } from './AutoReply';
8
+ export interface ChatGroupCustomizeValue extends ChatViewCustomizeValue, AutoReplyCustomizeValue, CreateChatPanelCustomizeValue, ChatListPanelCustomizeValue, ChatGroupLayoutCustomizeValue {
8
9
  }
9
10
  export interface ChatifyProps {
10
11
  className?: string;
@@ -0,0 +1 @@
1
+ export declare const ChatGroupActions: import('../../../index.ts').Customizable<import('react').ComponentType<unknown>>;
@@ -1,14 +1,17 @@
1
+ import { ChatGroupActions } from './ChatGroupActions';
1
2
  import { ChatGroupAside } from './ChatGroupAside';
2
3
  import { ChatGroupHeader } from './ChatGroupHeader';
3
4
  import { ChatGroupFooter } from './ChatGroupFooter';
4
5
  import { ChatGroupMain } from './ChatGroupMain';
5
6
  import { ChatGroupBody } from './ChatGroupBody';
7
+ export * from './ChatGroupActions';
6
8
  export * from './ChatGroupAside';
7
9
  export * from './ChatGroupHeader';
8
10
  export * from './ChatGroupFooter';
9
11
  export * from './ChatGroupMain';
10
12
  export * from './ChatGroupBody';
11
13
  export interface ChatGroupLayoutCustomizeValue {
14
+ ChatGroupActions?: typeof ChatGroupActions.Component | null;
12
15
  ChatGroupAside?: typeof ChatGroupAside.Component | null;
13
16
  ChatGroupHeader?: typeof ChatGroupHeader.Component | null;
14
17
  ChatGroupFooter?: typeof ChatGroupFooter.Component | null;
@@ -1,3 +1,4 @@
1
+ export * from './AutoReply';
1
2
  export * from './Create';
2
3
  export * from './Layout';
3
4
  export * from './List';
@@ -17,5 +17,6 @@ export declare const ICONS: {
17
17
  unwatch: import('react').ForwardRefExoticComponent<Omit<import('@ant-design/icons/lib/components/AntdIcon').AntdIconProps, "ref"> & import('react').RefAttributes<HTMLSpanElement>>;
18
18
  'delete-file': import('react').ForwardRefExoticComponent<Omit<import('@ant-design/icons/lib/components/AntdIcon').AntdIconProps, "ref"> & import('react').RefAttributes<HTMLSpanElement>>;
19
19
  'open-file': import('react').ForwardRefExoticComponent<Omit<import('@ant-design/icons/lib/components/AntdIcon').AntdIconProps, "ref"> & import('react').RefAttributes<HTMLSpanElement>>;
20
+ settings: import('react').ForwardRefExoticComponent<Omit<import('@ant-design/icons/lib/components/AntdIcon').AntdIconProps, "ref"> & import('react').RefAttributes<HTMLSpanElement>>;
20
21
  };
21
22
  export declare const Icon: import('../../index.ts').Customizable<import('react').ComponentType<IconProps>>;
@@ -37,6 +37,21 @@ export interface LocalizerBase {
37
37
  chatList: {
38
38
  topShown: (count: number) => string;
39
39
  };
40
+ autoReply: {
41
+ buttonTitle: () => string;
42
+ title: () => React.ReactNode;
43
+ period: {
44
+ label: () => React.ReactNode;
45
+ required: () => string;
46
+ format: () => string;
47
+ };
48
+ text: {
49
+ label: () => React.ReactNode;
50
+ required: () => string;
51
+ };
52
+ submitBtn: () => React.ReactNode;
53
+ clearBtn: () => React.ReactNode;
54
+ };
40
55
  }
41
56
  export interface Localizer extends LocalizerBase {
42
57
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@webinex/chatify",
3
3
  "description": "Webinex Chatify",
4
4
  "private": false,
5
- "version": "2.0.0-alpha10",
5
+ "version": "2.0.0-alpha13",
6
6
  "author": "Webinex Dev",
7
7
  "license": "Apache-2.0",
8
8
  "type": "module",
@@ -42,7 +42,7 @@ export const AuditSearch = customize('AuditSearch', (props: AuditSearchProps) =>
42
42
  const { className, onSearch } = props;
43
43
  const [searchValue, setSearchValue] = useState('');
44
44
  const [values, setValues] = useState<Value[]>([]);
45
- const { data: accounts, isLoading } = chatifyApi.useGetAccountListQuery();
45
+ const { data: accounts, isLoading } = chatifyApi.useGetAccountListQuery({});
46
46
  const options = useOptions(accounts, searchValue);
47
47
  const localizer = useLocalizer();
48
48
 
@@ -1,4 +1,4 @@
1
- import { DEFAULT_MESSAGE_LIST_TAKE, __baseApi, __settings } from './baseApi';
1
+ import { ChatifyApiSettings, DEFAULT_MESSAGE_LIST_TAKE, __baseApi, __settings } from './baseApi';
2
2
  import { useSelector } from 'react-redux';
3
3
  import {
4
4
  Account,
@@ -12,6 +12,7 @@ import {
12
12
  Thread,
13
13
  ThreadMessage,
14
14
  ThreadWatchListItem,
15
+ UpdateAccountRequest,
15
16
  calcThreadListUnreadCount,
16
17
  chatId,
17
18
  parseThreadMessageId,
@@ -50,11 +51,24 @@ const __chatifyApi = __baseApi.injectEndpoints({
50
51
  // ============== ACCOUNT ======================
51
52
  // ====================================================
52
53
 
53
- getAccountList: builder.query<Account[], void>({
54
- queryFn: async () => ({ data: await __settings.client.accounts() }),
54
+ getAccountList: builder.query<Account[], { ids?: string[] }>({
55
+ queryFn: async (args) => ({ data: await __settings.client.accounts(args) }),
55
56
  providesTags: ['account'],
56
57
  }),
57
58
 
59
+ geCurrentUserAccount: builder.query<Account | null, void>({
60
+ queryFn: async () => ({ data: await __settings.client.currentUserAccount() }),
61
+ providesTags: ['account'],
62
+ }),
63
+
64
+ updateAccount: builder.mutation<void, UpdateAccountRequest>({
65
+ queryFn: async (request) => {
66
+ await __settings.client.updateAccount(request);
67
+ return { data: null! };
68
+ },
69
+ invalidatesTags: ['account'],
70
+ }),
71
+
58
72
  // ====================================================
59
73
  // ================ CHAT =======================
60
74
  // ====================================================
@@ -648,7 +662,13 @@ export function useUnreadThreadMessageCount() {
648
662
  return [count, state] as const;
649
663
  }
650
664
 
651
- export const chatifyApi = Object.assign(__chatifyApi, {
665
+ type ChatifyApi = typeof __chatifyApi & {
666
+ settings: ChatifyApiSettings;
667
+ useChatListItem: typeof useChatListItem;
668
+ useUnreadThreadMessageCount: typeof useUnreadThreadMessageCount;
669
+ };
670
+
671
+ export const chatifyApi: ChatifyApi = Object.assign(__chatifyApi, {
652
672
  settings: __settings,
653
673
  useChatListItem,
654
674
  useUnreadThreadMessageCount,
@@ -164,8 +164,19 @@ export class ChatifyClient {
164
164
  return this._config.axios;
165
165
  }
166
166
 
167
- public accounts = async () => {
168
- const { data } = await this.axios.get<Account[]>('account');
167
+ public accounts = async (args: { ids?: string[] }) => {
168
+ const searchParams = new URLSearchParams();
169
+
170
+ args?.ids?.forEach((id) => {
171
+ searchParams.append('ids', encodeURIComponent(id));
172
+ });
173
+
174
+ const { data } = await this.axios.get<Account[]>(`account?${searchParams.toString()}`);
175
+ return data;
176
+ };
177
+
178
+ public currentUserAccount = async () => {
179
+ const { data } = await this.axios.get<Account>('account/me');
169
180
  return data;
170
181
  };
171
182
 
@@ -83,18 +83,51 @@
83
83
  padding-inline: 0.5rem;
84
84
  margin-bottom: 1rem;
85
85
 
86
- .wxchtf-add-btn {
86
+ .wxchtf-add-btn,
87
+ .wxchtf-add-btn-settings {
87
88
  border: none;
88
89
  background-color: #e4f7f7;
89
90
  height: 50px;
90
- padding: 0;
91
91
  line-height: 50px;
92
- min-width: 150px;
93
- border-radius: 50px;
94
92
  font-weight: 600;
95
93
  opacity: 0.9;
96
94
  box-shadow: none;
97
95
  }
96
+
97
+ > .wxchtf-add-btn {
98
+ padding: 0 24px;
99
+ min-width: 150px;
100
+ border-radius: 50px;
101
+ }
102
+
103
+ > .wxchtf-add-btn-settings {
104
+ padding: 0 16px;
105
+ min-width: 50px;
106
+ border-radius: 50px;
107
+ }
108
+
109
+ > .wxchtf-add-btn-group {
110
+ display: inline-flex;
111
+
112
+ .wxchtf-add-btn-wrapper {
113
+ width: auto;
114
+ padding-inline: 0;
115
+ margin-bottom: 0;
116
+ }
117
+
118
+ .wxchtf-add-btn {
119
+ padding: 0 18px;
120
+ min-width: 120px;
121
+ border-radius: 50px 0 0 50px;
122
+ }
123
+
124
+ .wxchtf-add-btn-settings {
125
+ padding: 0 14px;
126
+ min-width: 44px;
127
+ border-radius: 0 50px 50px 0;
128
+ border-left: 1px solid rgba(0, 0, 0, 0.06);
129
+ }
130
+ }
98
131
  }
99
132
  }
100
133
 
@@ -108,7 +141,8 @@
108
141
  z-index: 2;
109
142
  min-width: 0;
110
143
 
111
- .wxchtf-create-chat-panel {
144
+ .wxchtf-create-chat-panel,
145
+ .wxchtf-auto-reply-panel {
112
146
  display: flex;
113
147
  flex-flow: column nowrap;
114
148
  justify-content: center;
@@ -116,7 +150,8 @@
116
150
  height: 100%;
117
151
  position: relative;
118
152
 
119
- .wxchtf-create-chat-panel-close {
153
+ .wxchtf-create-chat-panel-close,
154
+ .wxchtf-auto-reply-panel-close {
120
155
  position: absolute;
121
156
  right: 25px;
122
157
  top: 25px;
@@ -126,7 +161,31 @@
126
161
  padding: 2rem;
127
162
  max-width: 700px;
128
163
  width: 100%;
164
+ }
165
+ }
129
166
 
167
+ .wxchtf-auto-reply-panel {
168
+ .wxchtf-auto-reply-panel-header {
169
+ width: 100%;
170
+ max-width: 700px;
171
+ padding: 0 2rem;
172
+ margin-bottom: 0.5rem;
173
+ display: flex;
174
+ align-items: center;
175
+ justify-content: space-between;
176
+
177
+ h3 {
178
+ margin: 0;
179
+ }
180
+
181
+ .wxchtf-auto-reply-panel-clear {
182
+ padding: 0;
183
+ }
184
+ }
185
+ }
186
+
187
+ .wxchtf-create-chat-panel {
188
+ form {
130
189
  .wxchtf-create-chat-member-input-list {
131
190
  max-height: 15rem;
132
191
  overflow-y: auto;
@@ -32,7 +32,7 @@ function useRemove(chatId: string, accountId: string, deleteHistory: boolean) {
32
32
  const ChatMemberListItem = customize('ChatMemberListItem', (props: ChatMemberListItemProps) => {
33
33
  const { chatId } = props;
34
34
  const { data: chat } = chatifyApi.useGetChatQuery({ id: chatId });
35
- const { data: accounts } = chatifyApi.useGetAccountListQuery();
35
+ const { data: accounts } = chatifyApi.useGetAccountListQuery({});
36
36
  const account = accounts!.find((x) => x.id === props.id)!;
37
37
  const isMember = chat!.members.some((x) => x.id === account.id);
38
38
  const [onAddWithoutHistory] = useAdd(chatId, account.id, false);
@@ -82,7 +82,7 @@ const ChatMemberListItem = customize('ChatMemberListItem', (props: ChatMemberLis
82
82
  export const ChatMembersPanel = customize('ChatMembersPanel', () => {
83
83
  const { id } = useChatContext();
84
84
  const { data: chat } = chatifyApi.useGetChatQuery({ id });
85
- const { data: accounts } = chatifyApi.useGetAccountListQuery();
85
+ const { data: accounts } = chatifyApi.useGetAccountListQuery({});
86
86
  const ordered = useMemo(
87
87
  () =>
88
88
  accounts
@@ -0,0 +1,21 @@
1
+ import { Button } from 'antd';
2
+ import { customize } from '../../customize';
3
+ import { useChatGroupContext } from '../ChatGroupContext';
4
+ import { useLocalizer } from '../../localizer';
5
+ import { Icon } from '../../common/Icon';
6
+
7
+ export const AutoReplyButton = customize('AutoReplyButton', () => {
8
+ const { toggleAutoReply } = useChatGroupContext();
9
+ const localizer = useLocalizer();
10
+ const titleText = localizer.autoReply.buttonTitle();
11
+
12
+ return (
13
+ <Button
14
+ className="wxchtf-add-btn-settings"
15
+ onClick={toggleAutoReply}
16
+ icon={<Icon type="settings" />}
17
+ title={titleText}
18
+ aria-label={titleText}
19
+ />
20
+ );
21
+ });
@@ -0,0 +1,50 @@
1
+ import { Button, Form } from 'antd';
2
+ import { Dayjs } from 'dayjs';
3
+ import { customize } from '../../customize';
4
+ import { useLocalizer } from '../../localizer';
5
+ import { AutoReplyPeriodInput } from './AutoReplyPeriodInput';
6
+ import { AutoReplyTextInput } from './AutoReplyTextInput';
7
+ import { useEffect } from 'react';
8
+
9
+ export interface AutoReplyFormValue {
10
+ period: [Dayjs, Dayjs];
11
+ text: string;
12
+ }
13
+
14
+ export interface AutoReplyFormProps {
15
+ value: AutoReplyFormValue;
16
+ onSubmit: (value: AutoReplyFormValue) => void;
17
+ busy?: boolean;
18
+ }
19
+
20
+ export const AUTO_REPLY_FORM_INITIAL_VALUE: AutoReplyFormValue = {
21
+ period: [] as unknown as [Dayjs, Dayjs],
22
+ text: '',
23
+ };
24
+
25
+ export const AutoReplyForm = customize('AutoReplyForm', (props: AutoReplyFormProps) => {
26
+ const { value, busy = false, onSubmit } = props;
27
+ const localizer = useLocalizer();
28
+
29
+ const [form] = Form.useForm<AutoReplyFormValue>();
30
+
31
+ useEffect(() => {
32
+ form.setFieldsValue(value);
33
+ }, [value, form]);
34
+
35
+ return (
36
+ <Form<AutoReplyFormValue>
37
+ form={form}
38
+ layout="vertical"
39
+ onFinish={onSubmit}
40
+ disabled={busy}
41
+ initialValues={value}
42
+ >
43
+ <AutoReplyPeriodInput name="period" />
44
+ <AutoReplyTextInput name="text" />
45
+ <Button loading={busy} type="primary" block htmlType="submit">
46
+ {localizer.autoReply.submitBtn()}
47
+ </Button>
48
+ </Form>
49
+ );
50
+ });
@@ -0,0 +1,94 @@
1
+ import dayjs from 'dayjs';
2
+ import { useCallback, useMemo } from 'react';
3
+ import { Button } from 'antd';
4
+ import { customize } from '../../customize';
5
+ import { useLocalizer } from '../../localizer';
6
+ import { useChatGroupContext } from '../ChatGroupContext';
7
+ import { Icon } from '../../common/Icon';
8
+ import { AutoReply } from '../../../core';
9
+ import { chatifyApi } from '../../../core';
10
+ import { AUTO_REPLY_FORM_INITIAL_VALUE, AutoReplyForm, AutoReplyFormValue } from './AutoReplyForm';
11
+
12
+ function mapToFormValue(autoReply?: AutoReply | null): AutoReplyFormValue {
13
+ if (!autoReply) {
14
+ return AUTO_REPLY_FORM_INITIAL_VALUE;
15
+ }
16
+
17
+ return {
18
+ period: [dayjs(autoReply.period.start), dayjs(autoReply.period.end)],
19
+ text: autoReply.text,
20
+ };
21
+ }
22
+
23
+ function useValue() {
24
+ const { data: me } = chatifyApi.useGeCurrentUserAccountQuery();
25
+
26
+ const value = useMemo<AutoReplyFormValue>(() => {
27
+ return mapToFormValue(me?.autoReply);
28
+ }, [me]);
29
+
30
+ return value;
31
+ }
32
+
33
+ function useAutoReplyActions() {
34
+ const meId = chatifyApi.settings.me();
35
+ const [updateAccount, { isLoading }] = chatifyApi.useUpdateAccountMutation();
36
+
37
+ const onSubmit = useCallback(
38
+ (value: AutoReplyFormValue) =>
39
+ updateAccount({
40
+ id: meId,
41
+ autoReply: {
42
+ hasValue: true,
43
+ value: {
44
+ text: value.text,
45
+ period: {
46
+ start: value.period[0].toISOString(),
47
+ end: value.period[1].toISOString(),
48
+ },
49
+ },
50
+ },
51
+ }).unwrap(),
52
+ [updateAccount, meId],
53
+ );
54
+
55
+ const onClear = useCallback(
56
+ () =>
57
+ updateAccount({
58
+ id: meId,
59
+ autoReply: {
60
+ hasValue: true,
61
+ value: null,
62
+ },
63
+ }).unwrap(),
64
+ [updateAccount, meId],
65
+ );
66
+
67
+ return [onSubmit, isLoading, onClear] as const;
68
+ }
69
+
70
+ export const AutoReplyPanel = customize('AutoReplyPanel', () => {
71
+ const localizer = useLocalizer();
72
+ const { toggleAutoReply } = useChatGroupContext();
73
+ const value = useValue();
74
+ const [onSubmit, submitting, onClear] = useAutoReplyActions();
75
+
76
+ return (
77
+ <div className="wxchtf-auto-reply-panel">
78
+ <Button
79
+ onClick={toggleAutoReply}
80
+ className="wxchtf-auto-reply-panel-close"
81
+ type="link"
82
+ icon={<Icon type="close" />}
83
+ />
84
+ <div className="wxchtf-auto-reply-panel-header">
85
+ <h3>{localizer.autoReply.title()}</h3>
86
+ <Button type="link" onClick={onClear} disabled={submitting} className="wxchtf-auto-reply-panel-clear">
87
+ {localizer.autoReply.clearBtn()}
88
+ </Button>
89
+ </div>
90
+
91
+ <AutoReplyForm value={value} busy={submitting ?? false} onSubmit={onSubmit} />
92
+ </div>
93
+ );
94
+ });
@@ -0,0 +1,22 @@
1
+ import { DatePicker, Form } from 'antd';
2
+ import { customize } from '../../customize';
3
+ import { useLocalizer } from '../../localizer';
4
+
5
+ export interface AutoReplyPeriodInputProps {
6
+ name: string;
7
+ }
8
+
9
+ export const AutoReplyPeriodInput = customize('AutoReplyPeriodInput', (props: AutoReplyPeriodInputProps) => {
10
+ const { name } = props;
11
+ const localizer = useLocalizer();
12
+
13
+ return (
14
+ <Form.Item
15
+ label={localizer.autoReply.period.label()}
16
+ name={name}
17
+ rules={[{ required: true, message: localizer.autoReply.period.required() }]}
18
+ >
19
+ <DatePicker.RangePicker format={localizer.autoReply.period.format()} />
20
+ </Form.Item>
21
+ );
22
+ });
@@ -0,0 +1,22 @@
1
+ import { Form, Input } from 'antd';
2
+ import { customize } from '../../customize';
3
+ import { useLocalizer } from '../../localizer';
4
+
5
+ export interface AutoReplyTextInputProps {
6
+ name: string;
7
+ }
8
+
9
+ export const AutoReplyTextInput = customize('AutoReplyTextInput', (props: AutoReplyTextInputProps) => {
10
+ const { name } = props;
11
+ const localizer = useLocalizer();
12
+
13
+ return (
14
+ <Form.Item
15
+ label={localizer.autoReply.text.label()}
16
+ name={name}
17
+ rules={[{ required: true, message: localizer.autoReply.text.required() }]}
18
+ >
19
+ <Input.TextArea rows={4} />
20
+ </Form.Item>
21
+ );
22
+ });
@@ -0,0 +1,19 @@
1
+ import { AutoReplyButton } from './AutoReplyButton';
2
+ import { AutoReplyForm } from './AutoReplyForm';
3
+ import { AutoReplyPanel } from './AutoReplyPanel';
4
+ import { AutoReplyPeriodInput } from './AutoReplyPeriodInput';
5
+ import { AutoReplyTextInput } from './AutoReplyTextInput';
6
+
7
+ export * from './AutoReplyButton';
8
+ export * from './AutoReplyForm';
9
+ export * from './AutoReplyPanel';
10
+ export * from './AutoReplyPeriodInput';
11
+ export * from './AutoReplyTextInput';
12
+
13
+ export interface AutoReplyCustomizeValue {
14
+ AutoReplyButton?: typeof AutoReplyButton.Component | null;
15
+ AutoReplyForm?: typeof AutoReplyForm.Component | null;
16
+ AutoReplyPanel?: typeof AutoReplyPanel.Component | null;
17
+ AutoReplyPeriodInput?: typeof AutoReplyPeriodInput.Component | null;
18
+ AutoReplyTextInput?: typeof AutoReplyTextInput.Component | null;
19
+ }
@@ -1,7 +1,7 @@
1
1
  import React, { PropsWithChildren, useContext, useMemo, useState } from 'react';
2
2
 
3
3
  export interface ChatGroupContext {
4
- view: 'chat' | 'new-chat' | null;
4
+ view: 'chat' | 'new-chat' | 'auto-reply' | null;
5
5
  chatId: string | null;
6
6
  showMembers: boolean;
7
7
 
@@ -9,6 +9,7 @@ export interface ChatGroupContext {
9
9
  openChat(chatId: string): void;
10
10
  toggleNewChat(): void;
11
11
  toggleMembers(): void;
12
+ toggleAutoReply(): void;
12
13
  }
13
14
 
14
15
  const ChatGroupReactContext = React.createContext<ChatGroupContext>(null!);
@@ -42,6 +43,16 @@ export function ChatGroupContext(props: PropsWithChildren<{}>) {
42
43
  return { ...prev, view: 'new-chat' };
43
44
  }
44
45
  }),
46
+ toggleAutoReply: () =>
47
+ setState((prev) => {
48
+ if (prev.view === 'auto-reply' && prev.chatId) {
49
+ return { ...prev, view: 'chat' };
50
+ } else if (prev.view === 'auto-reply' && !prev.chatId) {
51
+ return { ...prev, view: null };
52
+ } else {
53
+ return { ...prev, view: 'auto-reply' };
54
+ }
55
+ }),
45
56
  }),
46
57
  [state],
47
58
  );
@@ -7,9 +7,11 @@ import { ChatGroupContext, useChatGroupContext } from './ChatGroupContext';
7
7
  import { chatifyApi } from '../../core';
8
8
  import { CustomizeContext } from '../customize';
9
9
  import { ChatViewCustomizeValue } from '../Chat';
10
+ import { AutoReplyCustomizeValue } from './AutoReply';
10
11
 
11
12
  export interface ChatGroupCustomizeValue
12
13
  extends ChatViewCustomizeValue,
14
+ AutoReplyCustomizeValue,
13
15
  CreateChatPanelCustomizeValue,
14
16
  ChatListPanelCustomizeValue,
15
17
  ChatGroupLayoutCustomizeValue {}
@@ -16,7 +16,7 @@ interface CheckboxListProps {
16
16
 
17
17
  function CheckboxList(props: CheckboxListProps) {
18
18
  const { value, onChange } = props;
19
- const { data: accounts, isFetching } = chatifyApi.useGetAccountListQuery();
19
+ const { data: accounts, isFetching } = chatifyApi.useGetAccountListQuery({});
20
20
  const me = chatifyApi.settings.me();
21
21
  const dataSource = useMemo(() => accounts?.filter((x) => x.id !== me) ?? [], [accounts, me]);
22
22