Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to combine attachments and message text in a single activity #5084

Closed
wants to merge 32 commits into from

Conversation

ms-jb
Copy link
Contributor

@ms-jb ms-jb commented Mar 22, 2024

Fixes #5083

Changelog Entry

  • Resolves #5083. Added sendAttachmentOn style option to send attachments and text in a single activity, by @ms-jb

Description

Added a new styleOption that enables attachments and message text to be sent in a single activity. If enabled, uploaded files are retained until the Send button is pressed to allow for message text to be optionally included. Standalone text works as before.

webchat-new-icons

Design

Sample implementation:

<script>
  window.WebChat.renderWebChat(
    {
      styleOptions: {
        sendAttachmentOn: 'send' // Default
      }
    },
    document.getElementById('webchat')
  );
</script>

Specific Changes

  • Added combineAttachmentsAndText style option to send attachments and text in a single activity
  • I have added tests and executed them locally
  • I have updated CHANGELOG.md
  • I have updated documentation

Review Checklist

This section is for contributors to review your work.

  • Accessibility reviewed (tab order, content readability, alt text, color contrast)
  • Browser and platform compatibilities reviewed
  • CSS styles reviewed (minimal rules, no z-index)
  • Documents reviewed (docs, samples, live demo)
  • Internationalization reviewed (strings, unit formatting)
  • package.json and package-lock.json reviewed
  • Security reviewed (no data URIs, check for nonce leak)
  • Tests reviewed (coverage, legitimacy)

@compulim
Copy link
Contributor

Please get sign off from designer.

@ms-jb
Copy link
Contributor Author

ms-jb commented Mar 29, 2024

Will update the icons to match the new design:
image

@ms-jb
Copy link
Contributor Author

ms-jb commented Apr 1, 2024

setFiles(files);

if (combineAttachmentsAndText) {
current.blur();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't just blur the focus. It will impact accessibility.

const focus = useFocus();
const localize = useLocalizer();
const scrollToEnd = useScrollToEnd();
const styleToEmotionObject = useStyleToEmotionObject();
const submitErrorMessageId = useUniqueId('webchat__send-box__error-message-id');
const timeoutRef = useRef<readonly [Timeout, Timeout] | undefined>(undefined);
const [{ combineAttachmentsAndText }] = useStyleOptions();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sort.

sendFiles(files, sendBoxValue);
setFiles([]);
setSendBoxValue('');
if (uploadButtonRef?.current) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't expose DOM element using hooks.

setFiles([]);
setSendBoxValue('');
if (uploadButtonRef?.current) {
uploadButtonRef.current.value = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why set the upload file value to null when setFiles([]) remove all the files?

: undefined
: // If combineAttachments is enabled, allow sending if either there is a message or files
// Otherwise, require message text only
(combineAttachmentsAndText && (sendBoxValue || files.length)) || (!combineAttachmentsAndText && sendBoxValue)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic seems too complicated.

@@ -1,9 +1,9 @@
const SEND_FILES = 'WEB_CHAT/SEND_FILES';

export default function sendFiles(files) {
export default function sendFiles(files, text) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please convert this file to TypeScript.

role="button"
tabIndex={-1}
type="file"
/>
<IconButton alt={uploadFileString} aria-label={uploadFileString} disabled={disabled} onClick={handleClick}>
<AttachmentIcon />
{/* When a file is attached, overlay the check icon */}
{uploadButtonRef.current?.value && <CheckIcon />}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Build an icon, don't overlay.

const apiSubmitSendBox = useSubmitSendBox();
const [{ files, setFiles }] = useFiles();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sort.

apiSubmitSendBox,
clearTimeout,
combineAttachmentsAndText,
files,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any changes to files should not recreate the callback.

Use ref.

@@ -270,6 +270,10 @@ type StyleOptions = {
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#multiple
*/
uploadMultiple?: boolean;
/**
* Send the attachments and message text together as a single activity
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Send the attachments and message text together as a single activity
* Send the attachments and message text together as a single activity.

role="button"
tabIndex={-1}
type="file"
/>
<IconButton alt={uploadFileString} aria-label={uploadFileString} disabled={disabled} onClick={handleClick}>
<AttachmentIcon />
{/* When a file is attached, overlay the check icon */}
{uploadButtonRef.current?.value && <CheckIcon />}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using uploadButtonRef.current?.value to check if a file is pending upload, is probably the reason why you need to set uploadButtonRef.current.value to null after send.

It seems useFiles hook was added much later and not really used.

/**
* This is a hook that returns the files and setFiles from the context.
*/
export default function useFiles(): [{ files: File[]; setFiles: (files: File[]) => void }] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useFiles should follow the pattern of other state hooks:

useFiles(): [readonly File[], (readonly File[]) => void]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to useSendBoxAttachments.

import useWebChatAPIContext from './internal/useWebChatAPIContext';

/**
* This is a hook that returns the files and setFiles from the context.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add more context.

/**
* This is a hook that returns the files and setFiles from the context.
*/
export default function useFiles(): [{ files: File[]; setFiles: (files: File[]) => void }] {
Copy link
Contributor

@compulim compulim Apr 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name "file" is very vague. It could be files from an incoming activity. Or files to send out.

focus('sendBox');
return;
}

sendFiles(files);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use the SendBoxComposer.useSubmit() hook to send files. This will simplify lots of code.

@@ -116,10 +132,33 @@ const SendBoxComposer = ({ children }: PropsWithChildren<{}>) => {
]) as readonly [Timeout, Timeout];
} else {
scrollToEndRef.current?.();
apiSubmitSendBox();
if (combineAttachmentsAndText && files.length) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old logic use Redux to send, while the new logic use the hook directly. This is inconsistent.

@@ -116,10 +132,33 @@ const SendBoxComposer = ({ children }: PropsWithChildren<{}>) => {
]) as readonly [Timeout, Timeout];
} else {
scrollToEndRef.current?.();
apiSubmitSendBox();
if (combineAttachmentsAndText && files.length) {
Copy link
Contributor

@compulim compulim Apr 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few lines above, it already checked if there are any errors. One type of error is "no files attached".

The check here (files.length) is meaningless.

@@ -540,6 +540,12 @@ const ComposerCore = ({
[scrollToEndButtonMiddleware]
);

// For useFiles hook
const [files, setFiles] = useState<File[]>([]);
Copy link
Contributor

@compulim compulim Apr 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using useState here, instead of Redux, will complicate the code later.

In your latter code, sometimes, it call useSendFiles, and in other times, it call, useAPISubmitSendBox. This complicate testing.

If the attachment list is maintained inside Redux, the latter code will not need to call useSendFiles.

We could move away from Redux too. But we should select one path. Doing 1 thing with 2 ways will slowly introduce branching/versioning issue.

@@ -12,14 +12,14 @@ type PostActivityFile = {
url: string;
};

export default function useSendFiles(): (files: PostActivityFile[]) => void {
export default function useSendFiles(): (files: PostActivityFile[], text?: string) => void {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When text argument is introduced to useSendFiles, it almost act the same as useSendMessage.

Instead of adding text argument to useSendFiles, should add attachments to useSendMessage instead. Then, useSendFiles can be deprecated.

Copy link
Contributor

@compulim compulim Apr 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added deprecation notes. Still need a warn once.

@compulim
Copy link
Contributor

compulim commented Apr 8, 2024

Obsoleted by PR #5123.

@compulim compulim closed this Apr 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add ability to combine attachments and message text in a single activity
2 participants