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

Chore: Refactor Message composer using React #27482

Merged
merged 26 commits into from
Dec 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
02d0da4
Chore: Restrict `ChatMessages` API - Phase 2 (#27283)
tassoevan Nov 29, 2022
1536f94
Missing audio and video record
ggazzo Nov 30, 2022
8f8eb22
WIP
ggazzo Dec 1, 2022
a391df7
..
ggazzo Dec 1, 2022
079d4cf
,,
ggazzo Dec 1, 2022
aa33ba4
Merge remote-tracking branch 'origin/develop' into feat/composer
ggazzo Dec 7, 2022
00f34cf
small fixes
ggazzo Dec 7, 2022
420df2b
Merge branch 'develop' into feat/composer
ggazzo Dec 7, 2022
20ffdd0
lint
ggazzo Dec 7, 2022
9cb1f9e
Merge branch 'feat/composer' of github.com:RocketChat/Rocket.Chat int…
ggazzo Dec 7, 2022
aa79a69
lint
ggazzo Dec 8, 2022
d62bd0b
TS
ggazzo Dec 8, 2022
ce29bf4
add is_playing key
ggazzo Dec 8, 2022
4aa7898
Improve toolbox semantic and solve e2e tests
ggazzo Dec 8, 2022
cf39d3c
fix role selector
ggazzo Dec 8, 2022
afeb693
Merge remote-tracking branch 'origin/develop' into feat/composer
ggazzo Dec 8, 2022
28703b7
Merge remote-tracking branch 'origin/develop' into feat/composer
ggazzo Dec 8, 2022
45e9158
fix selector
ggazzo Dec 9, 2022
9b51499
Merge branch 'develop' into feat/composer
ggazzo Dec 9, 2022
e0533e7
fix emoji picker focus issue
ggazzo Dec 10, 2022
4e2d4c2
fix autofocus missing
ggazzo Dec 12, 2022
9ec9a21
fix function name
ggazzo Dec 12, 2022
8fc0b70
data-qa-id="menu-more-actions"
ggazzo Dec 12, 2022
f312a2f
lint
ggazzo Dec 12, 2022
9e55993
Merge remote-tracking branch 'origin/develop' into feat/composer
ggazzo Dec 16, 2022
d487b08
Merge branch 'feat/composer' of github.com:RocketChat/Rocket.Chat int…
ggazzo Dec 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions apps/meteor/app/emoji/client/lib/EmojiPicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,9 @@ export const EmojiPicker = {
this.source.focus();
},
pickEmoji(emoji) {
this.pickCallback(emoji);

this.close();
this.addRecent(emoji);
this.pickCallback(emoji);
},
addRecent(_emoji) {
const pos = this.recent.indexOf(_emoji);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
&__container {
display: flex;

padding: 0.75rem 0;
padding: 0.5rem 0.75rem;

cursor: text;

Expand Down Expand Up @@ -305,6 +305,7 @@
border-width: 0;
border-top-width: 1px;
flex-wrap: wrap;
justify-content: space-between;
}

& [data-desktop] {
Expand All @@ -318,7 +319,7 @@
&__textarea {
flex: 1 0 100%;

margin-bottom: 10px;
margin-block-end: 8px;
order: 1;
}

Expand Down Expand Up @@ -357,6 +358,10 @@
font-size: 20px;
order: 5;
}

[role='toolbar'] {
order: 6;
}
}

.js-message-action {
Expand Down
11 changes: 1 addition & 10 deletions apps/meteor/app/threads/client/flextab/thread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,7 @@ Template.thread.helpers({
subscription,
rid,
tmid,
onSend: async (
_event: Event,
{
value: text,
tshow,
}: {
value: string;
tshow?: boolean;
},
) => {
onSend: async ({ value: text, tshow }: { value: string; tshow?: boolean }) => {
instance.sendToBottom();
if (alsoSendPreferenceState === 'default') {
instance.state.set('sendToChannel', false);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Session } from 'meteor/session';
import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui';
import type { TranslationKey } from '@rocket.chat/ui-contexts';

import { Rooms } from '../../../models/client';
import { messageBox } from '../../../ui-utils/client';
Expand All @@ -14,7 +15,7 @@ const APP_GROUP = 'Create_new';

export const onAdded = (button: IUIActionButton): void =>
// eslint-disable-next-line no-void
void messageBox.actions.add(APP_GROUP, t(Utilities.getI18nKeyForApp(button.labelI18n, button.appId)), {
void messageBox.actions.add(APP_GROUP, t(Utilities.getI18nKeyForApp(button.labelI18n, button.appId)) as TranslationKey, {
id: getIdForActionButton(button),
// icon: button.icon || '',
condition() {
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/ui-message/client/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import './message';
import './messageBox/messageBox.ts';
// import './messageBox/messageBox.ts';
import './popup/customMessagePopups';
import './popup/messagePopup';
import './popup/messagePopupChannel';
Expand Down
194 changes: 194 additions & 0 deletions apps/meteor/app/ui-message/client/messageBox/createComposerAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import { Meteor } from 'meteor/meteor';
import type { IMessage } from '@rocket.chat/core-typings';
import { Emitter } from '@rocket.chat/emitter';
import $ from 'jquery';

import { withDebouncing } from '../../../../lib/utils/highOrderFunctions';
import type { ComposerAPI } from '../../../../client/lib/chats/ChatAPI';
import './messageBoxActions';
import './messageBoxReplyPreview.ts';
import './userActionIndicator.ts';

export const createComposerAPI = (input: HTMLTextAreaElement, storageID: string): ComposerAPI => {
const triggerEvent = (input: HTMLTextAreaElement, evt: string): void => {
$(input).trigger(evt);

const event = new Event(evt, { bubbles: true });
// TODO: Remove this hack for react to trigger onChange
const tracker = (input as any)._valueTracker;
if (tracker) {
tracker.setValue(new Date().toString());
}
input.dispatchEvent(event);
};

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

let _quotedMessages: IMessage[] = [];

const persist = withDebouncing({ wait: 1000 })(() => {
if (input.value) {
Meteor._localStorage.setItem(storageID, input.value);
return;
}

Meteor._localStorage.removeItem(storageID);
});

const notifyQuotedMessagesUpdate = (): void => {
emitter.emit('quotedMessagesUpdate');
};

input.addEventListener('input', persist);

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

const setText = (
text: string,
{
selection,
}: {
selection?:
| { readonly start?: number; readonly end?: number }
| ((previous: { readonly start: number; readonly end: number }) => { readonly start?: number; readonly end?: number });
} = {},
): void => {
focus();

const { selectionStart, selectionEnd } = input;
const textAreaTxt = input.value;

if (typeof selection === 'function') {
selection = selection({ start: selectionStart, end: selectionEnd });
}

if (selection) {
if (!document.execCommand || !document.execCommand('insertText', false, text)) {
input.value = textAreaTxt.substring(0, selectionStart) + text + textAreaTxt.substring(selectionStart);
focus();
}
input.setSelectionRange(selection.start ?? 0, selection.end ?? text.length);
}

if (!selection) {
input.value = text;
}

persist();

triggerEvent(input, 'input');
triggerEvent(input, 'change');

focus();
};

const insertText = (text: string): void => {
setText(text, {
selection: ({ start, end }) => ({
start: start + text.length,
end: end + text.length,
}),
});
};

const clear = (): void => {
setText('');
};

const focus = (): void => {
input.focus();
};

const replyWith = async (text: string): Promise<void> => {
if (input) {
input.value = text;
input.focus();
}
};

const quoteMessage = async (message: IMessage): Promise<void> => {
_quotedMessages = [..._quotedMessages.filter((_message) => _message._id !== message._id), message];
notifyQuotedMessagesUpdate();
input.focus();
};

const dismissQuotedMessage = async (mid: IMessage['_id']): Promise<void> => {
_quotedMessages = _quotedMessages.filter((message) => message._id !== mid);
notifyQuotedMessagesUpdate();
};

const dismissAllQuotedMessages = async (): Promise<void> => {
_quotedMessages = [];
notifyQuotedMessagesUpdate();
};

const quotedMessages = {
get: () => _quotedMessages,
subscribe: (callback: () => void) => emitter.on('quotedMessagesUpdate', callback),
};

const [editing, setEditing] = (() => {
let editing = false;

return [
{
get: () => editing,
subscribe: (callback: () => void) => emitter.on('editing', callback),
},
(value: boolean) => {
editing = value;
emitter.emit('editing');
},
];
})();

const [recording, setRecordingMode] = (() => {
let recording = false;

return [
{
get: () => recording,
subscribe: (callback: () => void) => emitter.on('recording', callback),
},
(value: boolean) => {
recording = value;
emitter.emit('recording');
},
];
})();

const setEditingMode = (editing: boolean): void => {
setEditing(editing);
};

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

return {
release,
get text(): string {
return input.value;
},
get selection(): { start: number; end: number } {
return {
start: input.selectionStart,
end: input.selectionEnd,
};
},

editing,
setEditingMode,
recording,
setRecordingMode,
insertText,
setText,
clear,
focus,
replyWith,
quoteMessage,
dismissQuotedMessage,
dismissAllQuotedMessages,
quotedMessages,
};
};
Loading