Skip to content

Commit

Permalink
Chore: New Composer (#27547)
Browse files Browse the repository at this point in the history
Co-authored-by: Tasso Evangelista <tasso.evangelista@rocket.chat>
  • Loading branch information
ggazzo and tassoevan authored Dec 16, 2022
1 parent 4c20c7e commit fdbde60
Show file tree
Hide file tree
Showing 31 changed files with 1,168 additions and 79 deletions.
3 changes: 3 additions & 0 deletions apps/meteor/app/lib/server/startup/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1263,6 +1263,9 @@ settingsRegistry.addGroup('Message', function () {
type: 'boolean',
public: true,
});
/**
* @deprecated
*/
this.add('Message_ShowFormattingTips', true, {
type: 'boolean',
public: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
position: relative;

width: 100%;
padding: 24px;
padding: 0 24px;

font-size: var(--message-box-text-size);

Expand Down
41 changes: 20 additions & 21 deletions apps/meteor/app/threads/client/flextab/thread.html
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
<template name="thread">
<section class="contextual-bar__content flex-tab threads dropzone {{dragAndDrop}} {{hideUsername}}">
<div class="dropzone-overlay {{isDropzoneDisabled}} background-transparent-darkest color-content-background-color">{{_ dragAndDropLabel}}</div>
<div class="dropzone-overlay {{isDropzoneDisabled}} background-transparent-darkest color-content-background-color">
{{_ dragAndDropLabel}}
</div>
<div class="thread-list js-scroll-thread">
<ul class="thread">
{{#with _messageContext}}
{{#if isLoading}}
<li class="load-more">
{{> loading}}
</li>
{{else}}
{{#if mainMessage }}
{{> message groupable=false hideRoles=true msg=mainMessage room=room subscription=subscription settings=settings templatePrefix='thread-' customClass=customClassMain u=u ignored=false shouldCollapseReplies=true chatContext=chatContext messageContext=messageContext}}
{{/if}}
{{#each msg in messages}}
{{> message hideRoles=true msg=msg room=room shouldCollapseReplies=true subscription=subscription settings=settings templatePrefix='thread-' customClass=(customClass msg) u=u context="threads" chatContext=chatContext messageContext=messageContext}}
{{/each}}
{{/if}}
{{/with}}
{{#with _messageContext}} {{#if isLoading}}
<li class="load-more">{{> loading}}</li>
{{else}} {{#if mainMessage }} {{> message groupable=false hideRoles=true msg=mainMessage room=room subscription=subscription
settings=settings templatePrefix='thread-' customClass=customClassMain u=u ignored=false shouldCollapseReplies=true
chatContext=chatContext messageContext=messageContext}} {{/if}} {{#each msg in messages}} {{> message hideRoles=true msg=msg
room=room shouldCollapseReplies=true subscription=subscription settings=settings templatePrefix='thread-' customClass=(customClass
msg) u=u context="threads" chatContext=chatContext messageContext=messageContext}} {{/each}} {{/if}} {{/with}}
</ul>
</div>
{{> messageBox messageBoxData}}
<footer class="thread-footer">
{{#with checkboxData }}
<div style="display: flex;">
{{> Checkbox . }}
<div class="thread-footer__wrapper">
<div class="thread-footer__row">
{{#with checkboxData }}
<div style="display: flex; flex-direction: row">
<div>{{> Checkbox . }}</div>
<label for="sendAlso" class="thread-footer__text">{{ _ "Also_send_to_channel" }}</label>
</div>
{{/with}}
</div>
{{/with}}
<label for="sendAlso" class="thread-footer__text">{{ _ "Also_send_to_channel" }}</label>
<div class="thread-footer__row">{{> messageBox messageBoxData}}</div>
</div>
</footer>
</section>
</template>
3 changes: 3 additions & 0 deletions apps/meteor/app/threads/client/flextab/thread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export type ThreadTemplateInstance = Blaze.TemplateInstance<{
subscription: ISubscription;
jump: unknown;
following: boolean;
readOnly: boolean;
rid: IRoom['_id'];
tabBar: {
openRoomInfo: (username: string) => void;
Expand Down Expand Up @@ -124,6 +125,7 @@ Template.thread.helpers({
mainMessage: { rid, _id: tmid },
subscription,
chatContext,
readOnly,
} = Template.currentData() as ThreadTemplateInstance['data'];

if (!chatContext) {
Expand All @@ -140,6 +142,7 @@ Template.thread.helpers({
subscription,
rid,
tmid,
readOnly,
onSend: async ({ value: text, tshow }: { value: string; tshow?: boolean }) => {
instance.sendToBottom();
if (alsoSendPreferenceState === 'default') {
Expand Down
18 changes: 17 additions & 1 deletion apps/meteor/app/threads/client/threads.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,25 @@
.thread-footer {
display: flex;

padding: 0 1.5rem 1rem;
padding: 0 1.5rem;

align-items: center;

flex-direction: column;
align-items: stretch;

&__wrapper {
margin-block: -4px;
}

&__row {
display: flex;
flex-direction: column;

&:not(:first-child) {
margin-block-end: 4px;
}
}
}

@media (width < 500px) {
Expand Down
78 changes: 73 additions & 5 deletions apps/meteor/app/ui-message/client/messageBox/createComposerAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import type { ComposerAPI } from '../../../../client/lib/chats/ChatAPI';
import './messageBoxActions';
import './messageBoxReplyPreview.ts';
import './userActionIndicator.ts';
import type { FormattingButton } from './messageBoxFormatting';
import { formattingButtons } from './messageBoxFormatting';

export const createComposerAPI = (input: HTMLTextAreaElement, storageID: string): ComposerAPI => {
const triggerEvent = (input: HTMLTextAreaElement, evt: string): void => {
Expand All @@ -22,7 +24,7 @@ export const createComposerAPI = (input: HTMLTextAreaElement, storageID: string)
input.dispatchEvent(event);
};

const emitter = new Emitter<{ quotedMessagesUpdate: void; editing: void; recording: void }>();
const emitter = new Emitter<{ quotedMessagesUpdate: void; editing: void; recording: void; formatting: void }>();

let _quotedMessages: IMessage[] = [];

Expand All @@ -41,10 +43,6 @@ export const createComposerAPI = (input: HTMLTextAreaElement, storageID: string)

input.addEventListener('input', persist);

const release = (): void => {
input.removeEventListener('input', persist);
};

const setText = (
text: string,
{
Expand Down Expand Up @@ -165,8 +163,77 @@ export const createComposerAPI = (input: HTMLTextAreaElement, storageID: string)

setText(Meteor._localStorage.getItem(storageID) ?? '');

const [formatters, stopFormatterTracker] = (() => {
let actions: FormattingButton[] = [];

const c = Tracker.autorun(() => {
actions = formattingButtons.filter(({ condition }) => !condition || condition());
emitter.emit('formatting');
});

return [
{
get: () => actions,
subscribe: (callback: () => void) => emitter.on('formatting', callback),
},
c,
];
})();

const release = (): void => {
input.removeEventListener('input', persist);
stopFormatterTracker.stop();
};

const wrapSelection = (pattern: string): void => {
const { selectionEnd = input.value.length, selectionStart = 0 } = input;
const initText = input.value.slice(0, selectionStart);
const selectedText = input.value.slice(selectionStart, selectionEnd);
const finalText = input.value.slice(selectionEnd, input.value.length);

focus();

const startPattern = pattern.slice(0, pattern.indexOf('{{text}}'));
const startPatternFound = [...startPattern].reverse().every((char, index) => input.value.slice(selectionStart - index - 1, 1) === char);

if (startPatternFound) {
const endPattern = pattern.slice(pattern.indexOf('{{text}}') + '{{text}}'.length);
const endPatternFound = [...endPattern].every((char, index) => input.value.slice(selectionEnd + index, 1) === char);

if (endPatternFound) {
insertText(selectedText);
input.selectionStart = selectionStart - startPattern.length;
input.selectionEnd = selectionEnd + endPattern.length;

if (!document.execCommand || !document.execCommand('insertText', false, selectedText)) {
input.value = initText.slice(0, initText.length - startPattern.length) + selectedText + finalText.slice(endPattern.length);
}

input.selectionStart = selectionStart - startPattern.length;
input.selectionEnd = input.selectionStart + selectedText.length;
triggerEvent(input, 'input');
triggerEvent(input, 'change');

focus();
return;
}
}

if (!document.execCommand || !document.execCommand('insertText', false, pattern.replace('{{text}}', selectedText))) {
input.value = initText + pattern.replace('{{text}}', selectedText) + finalText;
}

input.selectionStart = selectionStart + pattern.indexOf('{{text}}');
input.selectionEnd = input.selectionStart + selectedText.length;
triggerEvent(input, 'input');
triggerEvent(input, 'change');

focus();
};

return {
release,
wrapSelection,
get text(): string {
return input.value;
},
Expand All @@ -190,5 +257,6 @@ export const createComposerAPI = (input: HTMLTextAreaElement, storageID: string)
dismissQuotedMessage,
dismissAllQuotedMessages,
quotedMessages,
formatters,
};
};
2 changes: 2 additions & 0 deletions apps/meteor/app/ui-message/client/messageBox/messageBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ import './messageBoxReplyPreview.ts';
export type MessageBoxTemplateInstance = Blaze.TemplateInstance<{
rid: IRoom['_id'];
tmid?: IMessage['_id'];
readOnly: boolean;
onSend?: (params: { value: string; tshow?: boolean }) => Promise<void>;
onResize?: () => void;
onTyping?: () => void;
onEscape?: () => void;
onNavigateToPreviousMessage?: () => void;
onNavigateToNextMessage?: () => void;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import type { Icon } from '@rocket.chat/fuselage';
import type { TranslationKey } from '@rocket.chat/ui-contexts';
import type { ComponentProps } from 'react';

import { Markdown } from '../../../markdown/client';
import { settings } from '../../../settings/client';

type FormattingButton = {
label: string;
icon?: string;
pattern?: string;
text?: () => string | undefined;
link?: string;
command?: string;
condition?: () => boolean;
};
export type FormattingButton =
| {
label: TranslationKey;
icon: ComponentProps<typeof Icon>['name'];
pattern: string;
// text?: () => string | undefined;
command?: string;
condition: () => boolean;
}
| {
label: TranslationKey;
text: () => string | undefined;
link: string;
condition: () => boolean;
};

export const formattingButtons: ReadonlyArray<FormattingButton> = [
{
Expand Down Expand Up @@ -58,7 +68,7 @@ export const formattingButtons: ReadonlyArray<FormattingButton> = [
condition: () => Markdown && settings.get('Markdown_Parser') !== 'disabled',
},
{
label: 'KaTeX',
label: 'KaTeX' as TranslationKey,
text: () => {
if (!settings.get('Katex_Enabled')) {
return;
Expand All @@ -75,6 +85,9 @@ export const formattingButtons: ReadonlyArray<FormattingButton> = [
},
] as const;

/**
* @deprecated
*/
export function applyFormatting(pattern: string, input: HTMLTextAreaElement) {
const { selectionEnd = input.value.length, selectionStart = 0 } = input;
const initText = input.value.slice(0, selectionStart);
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/ui-utils/client/lib/popover.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ Template.popover.onDestroyed(function () {
Template.popover.events({
'click .js-action'(e, instance) {
e.stopPropagation();
!this.action || this.action.call(this, e, instance.data.data);
!this.action || this.action.call(this, { event: e, ...instance.data.data });
popover.close();
},
'click .js-close'() {
Expand Down
13 changes: 13 additions & 0 deletions apps/meteor/app/ui/client/lib/ChatMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { processMessageEditing } from '../../../../client/lib/chats/flows/proces
import { processTooLongMessage } from '../../../../client/lib/chats/flows/processTooLongMessage';
import { processSetReaction } from '../../../../client/lib/chats/flows/processSetReaction';
import { sendMessage } from '../../../../client/lib/chats/flows/sendMessage';
import { UserAction } from '..';

export class ChatMessages implements ChatAPI {
private currentEditingMID?: string;
Expand Down Expand Up @@ -98,6 +99,18 @@ export class ChatMessages implements ChatAPI {
processMessageEditing: processMessageEditing.bind(null, this),
processSetReaction: processSetReaction.bind(null, this),
requestMessageDeletion: requestMessageDeletion.bind(this, this),

action: {
start: async (action: 'typing') => {
UserAction.start(params.rid, `user-${action}`, { tmid: params.tmid });
},
performContinuously: async (action: 'recording' | 'uploading' | 'playing') => {
UserAction.performContinuously(params.rid, `user-${action}`, { tmid: params.tmid });
},
stop: async (action: 'typing' | 'recording' | 'uploading' | 'playing') => {
UserAction.stop(params.rid, `user-${action}`, { tmid: params.tmid });
},
},
};
}

Expand Down
3 changes: 3 additions & 0 deletions apps/meteor/app/ui/client/lib/UserAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ export const USER_ACTIVITIES = {
USER_RECORDING: 'user-recording',
USER_TYPING: 'user-typing',
USER_UPLOADING: 'user-uploading',
USER_PLAYING: 'user-playing',
};

export type USER_ACTIVITIES_TYPES = 'user-recording' | 'user-typing' | 'user-uploading' | 'user-playing';

const activityTimeouts = new Map();
const activityRenews = new Map();
const continuingIntervals = new Map();
Expand Down
Loading

0 comments on commit fdbde60

Please sign in to comment.