@webinex/chatify 2.0.1-alpha2 → 2.0.2-alpha2

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.
@@ -2,6 +2,7 @@
2
2
  import { File } from '../../../core';
3
3
  export interface InputBoxFileProps {
4
4
  file: File;
5
+ disabled?: boolean;
5
6
  onDelete: (file: File) => void;
6
7
  }
7
8
  export declare const InputBoxFile: import("../../customize").Customizable<import("react").ComponentType<InputBoxFileProps>>;
@@ -2,6 +2,7 @@
2
2
  import { File } from '../../../core';
3
3
  export interface InputBoxFileListProps {
4
4
  files: File[];
5
+ disabled?: boolean;
5
6
  onDelete: (file: File) => void;
6
7
  }
7
8
  export declare const InputBoxFileList: import("../../customize").Customizable<import("react").ComponentType<InputBoxFileListProps>>;
@@ -4,6 +4,7 @@ export interface InputFilesBoxProps {
4
4
  value: File[];
5
5
  accept?: string;
6
6
  loading?: boolean;
7
+ disabled?: boolean;
7
8
  onAddedClick?: () => void;
8
9
  onUpload: (value: globalThis.File[]) => void;
9
10
  }
@@ -2,5 +2,6 @@
2
2
  export interface InputSubmitButtonBoxProps {
3
3
  onSubmit: () => void;
4
4
  disabled?: boolean;
5
+ loading?: boolean;
5
6
  }
6
7
  export declare const InputSubmitButtonBox: import("../../customize").Customizable<import("react").ComponentType<InputSubmitButtonBoxProps>>;
@@ -3,6 +3,7 @@ export interface TextInputBoxProps {
3
3
  value: string;
4
4
  onChange: (value: string) => void;
5
5
  onSend: (value: string) => void;
6
+ disabled?: boolean;
6
7
  inputRef?: RefObject<HTMLTextAreaElement>;
7
8
  }
8
9
  export declare const InputTextBox: import("../../customize").Customizable<import("react").ComponentType<TextInputBoxProps>>;
package/dist/index.d.ts CHANGED
@@ -13313,12 +13313,14 @@ declare const InputBox: Customizable<react.ComponentType<unknown>>;
13313
13313
 
13314
13314
  interface InputBoxFileProps {
13315
13315
  file: File;
13316
+ disabled?: boolean;
13316
13317
  onDelete: (file: File) => void;
13317
13318
  }
13318
13319
  declare const InputBoxFile: Customizable<react.ComponentType<InputBoxFileProps>>;
13319
13320
 
13320
13321
  interface InputBoxFileListProps {
13321
13322
  files: File[];
13323
+ disabled?: boolean;
13322
13324
  onDelete: (file: File) => void;
13323
13325
  }
13324
13326
  declare const InputBoxFileList: Customizable<react.ComponentType<InputBoxFileListProps>>;
@@ -13327,6 +13329,7 @@ interface InputFilesBoxProps {
13327
13329
  value: File[];
13328
13330
  accept?: string;
13329
13331
  loading?: boolean;
13332
+ disabled?: boolean;
13330
13333
  onAddedClick?: () => void;
13331
13334
  onUpload: (value: globalThis.File[]) => void;
13332
13335
  }
@@ -13336,6 +13339,7 @@ declare const InputFilesBox: Customizable<react.ComponentType<InputFilesBoxProps
13336
13339
  interface InputSubmitButtonBoxProps {
13337
13340
  onSubmit: () => void;
13338
13341
  disabled?: boolean;
13342
+ loading?: boolean;
13339
13343
  }
13340
13344
  declare const InputSubmitButtonBox: Customizable<react.ComponentType<InputSubmitButtonBoxProps>>;
13341
13345
 
@@ -13343,6 +13347,7 @@ interface TextInputBoxProps {
13343
13347
  value: string;
13344
13348
  onChange: (value: string) => void;
13345
13349
  onSend: (value: string) => void;
13350
+ disabled?: boolean;
13346
13351
  inputRef?: RefObject<HTMLTextAreaElement>;
13347
13352
  }
13348
13353
  declare const InputTextBox: Customizable<react.ComponentType<TextInputBoxProps>>;
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.1-alpha2",
5
+ "version": "2.0.2-alpha2",
6
6
  "author": "Webinex Dev",
7
7
  "license": "Apache-2.0",
8
8
  "main": "src/index.ts",
@@ -11,6 +11,7 @@ import { InputBoxFileList } from './InputBoxFileList';
11
11
  function useFormState() {
12
12
  const { onSend } = useConversation();
13
13
  const [text, setText] = useState('');
14
+ const [sending, setSending] = useState(false);
14
15
  const { onUpload, files, loading, setFiles, showFileBox, onDeleteFile } = useFlippoInputFileBox();
15
16
 
16
17
  const onSubmit = useCallback(() => {
@@ -18,16 +19,17 @@ function useFormState() {
18
19
  return;
19
20
  }
20
21
 
21
- setText('');
22
- setFiles([]);
23
- Promise.resolve(onSend({ text, files })).catch(() => {
24
- setText(text);
25
- setFiles(files);
26
- });
22
+ setSending(true);
23
+ Promise.resolve(onSend({ text, files }))
24
+ .finally(() => setSending(false))
25
+ .then(() => {
26
+ setFiles([]);
27
+ setText('');
28
+ });
27
29
  // eslint-disable-next-line react-hooks/exhaustive-deps
28
30
  }, [text, onSend, files, setFiles, loading]);
29
31
 
30
- return [onSubmit, { text, setText, files, onUpload, loading, showFileBox, onDeleteFile }] as const;
32
+ return [onSubmit, { text, setText, files, onUpload, loading, showFileBox, onDeleteFile, sending }] as const;
31
33
  }
32
34
 
33
35
  function useFlippoInputFileBox() {
@@ -64,26 +66,26 @@ function useFlippoInputFileBox() {
64
66
  }
65
67
 
66
68
  export const InputBox = customize('InputBox', () => {
67
- const [onSend, { text, setText, files, loading, onUpload, showFileBox, onDeleteFile }] = useFormState();
69
+ const [onSend, { text, setText, files, loading, onUpload, showFileBox, onDeleteFile, sending }] =
70
+ useFormState();
68
71
  const inputRef = useRef<HTMLTextAreaElement>(null);
69
- const [showFilesModal, setShowFilesModal] = useState(false);
70
72
  const [showFileList, setShowFileList] = useState(false);
71
73
  const toggleShowFileList = useCallback(() => setShowFileList((prev) => !prev), []);
72
74
 
73
75
  useLayoutEffect(() => inputRef.current?.focus(), []);
74
76
 
75
77
  useEffect(() => {
76
- if (files.length === 0 && showFilesModal) {
77
- setShowFilesModal(false);
78
+ if (files.length === 0 && showFileList) {
79
+ setShowFileList(false);
78
80
  }
79
- }, [files, showFilesModal]);
81
+ }, [files, showFileList]);
80
82
 
81
83
  return (
82
84
  <div className="wxchtf-input-form">
83
85
  <div className="wxchtf-input-box">
84
86
  {showFileList && (
85
87
  <div className="wxchtf-file-list-box">
86
- <InputBoxFileList files={files} onDelete={onDeleteFile} />
88
+ <InputBoxFileList files={files} onDelete={onDeleteFile} disabled={sending} />
87
89
  </div>
88
90
  )}
89
91
  <div className="wxchtf-input-box-row">
@@ -93,10 +95,17 @@ export const InputBox = customize('InputBox', () => {
93
95
  onUpload={onUpload}
94
96
  value={files}
95
97
  loading={loading}
98
+ disabled={sending}
96
99
  />
97
100
  )}
98
- <InputTextBox value={text} onSend={onSend} onChange={setText} inputRef={inputRef} />
99
- <InputSubmitButtonBox onSubmit={onSend} disabled={loading} />
101
+ <InputTextBox
102
+ value={text}
103
+ onSend={onSend}
104
+ onChange={setText}
105
+ disabled={sending}
106
+ inputRef={inputRef}
107
+ />
108
+ <InputSubmitButtonBox onSubmit={onSend} disabled={loading} loading={sending} />
100
109
  </div>
101
110
  </div>
102
111
  </div>
@@ -7,11 +7,12 @@ import { customize } from '../../customize';
7
7
 
8
8
  export interface InputBoxFileProps {
9
9
  file: File;
10
+ disabled?: boolean;
10
11
  onDelete: (file: File) => void;
11
12
  }
12
13
 
13
14
  export const InputBoxFile = customize('InputBoxFile', (props: InputBoxFileProps) => {
14
- const { file, onDelete } = props;
15
+ const { file, disabled, onDelete } = props;
15
16
  const formatter = useFormatter();
16
17
  const onDeleteClick = useCallback(() => onDelete(file), [file, onDelete]);
17
18
 
@@ -25,6 +26,7 @@ export const InputBoxFile = customize('InputBoxFile', (props: InputBoxFileProps)
25
26
  </div>
26
27
  <div className="wxchtf-file-actions">
27
28
  <Button
29
+ disabled={disabled}
28
30
  className="wxchtf-delete"
29
31
  type="link"
30
32
  icon={<Icon type="delete-file" />}
@@ -4,15 +4,17 @@ import { InputBoxFile } from './InputBoxFile';
4
4
 
5
5
  export interface InputBoxFileListProps {
6
6
  files: File[];
7
+ disabled?: boolean;
7
8
  onDelete: (file: File) => void;
8
9
  }
9
10
 
10
11
  export const InputBoxFileList = customize('InputBoxFileList', (props: InputBoxFileListProps) => {
11
- const { files, onDelete } = props;
12
+ const { files, onDelete, disabled } = props;
13
+
12
14
  return (
13
15
  <div className="wxchtf-file-list">
14
16
  {files.map((file) => (
15
- <InputBoxFile key={file.ref} file={file} onDelete={onDelete} />
17
+ <InputBoxFile key={file.ref} file={file} onDelete={onDelete} disabled={disabled} />
16
18
  ))}
17
19
  </div>
18
20
  );
@@ -8,12 +8,13 @@ export interface InputFilesBoxProps {
8
8
  value: File[];
9
9
  accept?: string;
10
10
  loading?: boolean;
11
+ disabled?: boolean;
11
12
  onAddedClick?: () => void;
12
13
  onUpload: (value: globalThis.File[]) => void;
13
14
  }
14
15
 
15
16
  export const InputFileButton = customize('InputFileButton', (props: InputFilesBoxProps) => {
16
- const { onUpload, onAddedClick, accept, loading, value } = props;
17
+ const { onUpload, onAddedClick, accept, loading, value, disabled } = props;
17
18
  const inputRef = useRef<HTMLInputElement | null>(null);
18
19
  const onClick = useCallback(() => inputRef.current?.click(), []);
19
20
  const countButtonRef = useRef<HTMLButtonElement | null>(null);
@@ -40,6 +41,7 @@ export const InputFileButton = customize('InputFileButton', (props: InputFilesBo
40
41
  loading={loading}
41
42
  onClick={onClick}
42
43
  type="link"
44
+ disabled={disabled}
43
45
  icon={<Icon type="attach" />}
44
46
  className="wxchtf-add-files-btn"
45
47
  />
@@ -50,6 +52,7 @@ export const InputFileButton = customize('InputFileButton', (props: InputFilesBo
50
52
  onAnimationIteration={onCountButtonAnimationEnd}
51
53
  onClick={onAddedClick}
52
54
  type="link"
55
+ disabled={disabled}
53
56
  icon={<>+{value.length}</>}
54
57
  />
55
58
  )}
@@ -5,10 +5,11 @@ import { Icon } from '../../common/Icon';
5
5
  export interface InputSubmitButtonBoxProps {
6
6
  onSubmit: () => void;
7
7
  disabled?: boolean;
8
+ loading?: boolean;
8
9
  }
9
10
 
10
11
  export const InputSubmitButtonBox = customize('InputSubmitButtonBox', (props: InputSubmitButtonBoxProps) => {
11
- const { onSubmit, disabled } = props;
12
+ const { onSubmit, disabled, loading } = props;
12
13
 
13
14
  return (
14
15
  <div className="wxchtf-submit-btn-box">
@@ -18,6 +19,7 @@ export const InputSubmitButtonBox = customize('InputSubmitButtonBox', (props: In
18
19
  icon={<Icon type="send" />}
19
20
  className="wxchtf-submit-btn"
20
21
  disabled={disabled}
22
+ loading={loading}
21
23
  />
22
24
  </div>
23
25
  );
@@ -9,6 +9,7 @@ export interface TextInputBoxProps {
9
9
  value: string;
10
10
  onChange: (value: string) => void;
11
11
  onSend: (value: string) => void;
12
+ disabled?: boolean;
12
13
  inputRef?: RefObject<HTMLTextAreaElement>;
13
14
  }
14
15
 
@@ -19,7 +20,7 @@ const AUTO_SIZE: TextAreaProps['autoSize'] = { maxRows: 5, minRows: 1 };
19
20
  export const InputTextBox = customize(
20
21
  'InputTextBox',
21
22
  memo((props: TextInputBoxProps) => {
22
- const { value, onChange, onSend, inputRef } = props;
23
+ const { value, onChange, onSend, disabled, inputRef } = props;
23
24
  const localizer = useLocalizer();
24
25
 
25
26
  const onInputChange = useCallback(
@@ -46,6 +47,7 @@ export const InputTextBox = customize(
46
47
  className="wxchtf-text-input-textarea"
47
48
  placeholder={localizer.input.placeholder()}
48
49
  onKeyDown={onKeyDown}
50
+ disabled={disabled}
49
51
  />
50
52
  </div>
51
53
  );
@@ -4,7 +4,7 @@ import { customize, useCustomizeContext } from '../../customize';
4
4
  import { useFormatter } from '../../useFormatter';
5
5
  import { Icon } from '../../common';
6
6
  import { ConversationCustomizeValue } from '../Conversation';
7
- import { useCallback } from 'react';
7
+ import { useCallback, useState } from 'react';
8
8
 
9
9
  function open(url: string) {
10
10
  const w = window.open(url, '_blank');
@@ -15,17 +15,33 @@ export interface MessageFileProps {
15
15
  file: File;
16
16
  }
17
17
 
18
- export const MessageFile = customize('MessageFile', (props: MessageFileProps) => {
18
+ function useOpen(props: MessageFileProps) {
19
19
  const { file } = props;
20
20
  const { flippo } = useCustomizeContext<ConversationCustomizeValue>();
21
- const formatter = useFormatter();
21
+ const [isOpenFetching, setIsOpenFetching] = useState(false);
22
22
 
23
- const onOpen = useCallback(() => flippo?.getSasUrl(file.ref).then(open), [flippo, file.ref]);
23
+ const onOpen = useCallback(
24
+ () =>
25
+ flippo &&
26
+ Promise.resolve(setIsOpenFetching(true))
27
+ .then(() => flippo!.getSasUrl(file.ref))
28
+ .then(open)
29
+ .finally(() => setIsOpenFetching(false)),
30
+ [flippo, file.ref],
31
+ );
32
+
33
+ return [onOpen, { isOpenFetching }] as const;
34
+ }
35
+
36
+ export const MessageFile = customize('MessageFile', (props: MessageFileProps) => {
37
+ const { file } = props;
38
+ const formatter = useFormatter();
39
+ const [onOpen, { isOpenFetching }] = useOpen(props);
24
40
 
25
41
  return (
26
42
  <div className="wxchtf-file">
27
43
  <div className="wxchtf-icon">
28
- <Button onClick={onOpen} type="link" icon={<Icon type="open-file" />} />
44
+ <Button loading={isOpenFetching} onClick={onOpen} type="link" icon={<Icon type="open-file" />} />
29
45
  </div>
30
46
  <div className="wxchtf-file-info">
31
47
  <div className="wxchtf-name">{file.name}</div>