Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
cc85c81
refactor: replace ReactiveVar in VideoRecorder with Emitter + hook
ggazzo Apr 22, 2026
7a69bff
refactor: drop meteor/meteor from AudioEncoder
ggazzo Apr 22, 2026
fc3fce8
refactor: drop Meteor.startup wrapper from autotranslate actionButton
ggazzo Apr 22, 2026
9b50262
refactor: drop meteor/meteor type import from join slash command
ggazzo Apr 22, 2026
c1416a1
refactor: drop meteor/meteor from getConfig
ggazzo Apr 22, 2026
9eac0bd
refactor: drop meteor/tracker from timeAgo
ggazzo Apr 22, 2026
3e5d960
refactor: replace setReaction client method stub with explicit optimi…
ggazzo Apr 23, 2026
534b711
refactor: replace sendMessage client method stub with explicit optimi…
ggazzo Apr 23, 2026
b127ed2
chore: mark Meteor.absoluteUrl rootUrl override for removal with DDP
ggazzo Apr 23, 2026
477d4d6
refactor: replace Tracker.autorun with settings.observe in queueManager
ggazzo Apr 23, 2026
486038c
refactor: drop meteor/meteor and meteor/tracker from autotranslate mo…
ggazzo Apr 23, 2026
29317d1
refactor: replace ReactiveVar/Tracker in RoomHistoryManager with Emit…
ggazzo Apr 23, 2026
3611b87
chore: mark Meteor.absoluteUrl/OAuth overrides for removal with DDP
ggazzo Apr 23, 2026
427a515
refactor: drop meteor/meteor type import from useIframe
ggazzo Apr 23, 2026
0074c27
refactor: drop meteor/meteor from roomCoordinator
ggazzo Apr 23, 2026
970a498
refactor: drop meteor/accounts-base from RoomE2EESetup
ggazzo Apr 23, 2026
c2d9ff8
refactor: drop meteor/tracker from GameCenterInvitePlayersModal
ggazzo Apr 23, 2026
39b9b4f
refactor: replace meteor/session with in-memory Emitter in SessionPro…
ggazzo Apr 23, 2026
9daf2bc
refactor: drop meteor/meteor type import from IOAuthProvider
ggazzo Apr 23, 2026
10b9547
refactor: drop dead Tracker.autorun from IndexRoute
ggazzo Apr 24, 2026
0ead054
refactor: drop Meteor.startup wrapper from incomingMessages
ggazzo Apr 24, 2026
e2bb140
refactor: drop meteor/* from roles startup
ggazzo Apr 24, 2026
aedb0a3
refactor: drop meteor/* from autotranslate stream startup
ggazzo Apr 24, 2026
e120cab
chore: mark Meteor.autorun userId sync in userAndUsers for removal wi…
ggazzo Apr 24, 2026
a759ffd
refactor: drop meteor/* from client/startup/startup.ts
ggazzo Apr 24, 2026
e92bd0a
refactor: drop useReactiveValue from useIsSingleBusinessHours
ggazzo Apr 24, 2026
562c4b1
refactor: drop useReactiveValue from readOnly-aware hooks/components
ggazzo Apr 24, 2026
01525f2
refactor: drop useReactiveValue from MessageBox.canSend
ggazzo Apr 24, 2026
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
6 changes: 1 addition & 5 deletions apps/meteor/app/autotranslate/client/lib/actionButton.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
import { Meteor } from 'meteor/meteor';

import { AutoTranslate } from './autotranslate';

Meteor.startup(() => {
AutoTranslate.init();
});
AutoTranslate.init();
56 changes: 33 additions & 23 deletions apps/meteor/app/autotranslate/client/lib/autotranslate.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import type { IRoom, ISubscription, ISupportedLanguage, ITranslatedMessage, MessageAttachmentDefault } from '@rocket.chat/core-typings';
import { isTranslatedMessageAttachment } from '@rocket.chat/core-typings';
import mem from 'mem';
import { Meteor } from 'meteor/meteor';
import { Tracker } from 'meteor/tracker';

import { PermissionsCachedStore } from '../../../../client/cachedStores';
import { settings } from '../../../../client/lib/settings';
import { getUserId } from '../../../../client/lib/user';
import { watchUser, watchUserId } from '../../../../client/meteor/user';
import { Messages, Subscriptions } from '../../../../client/stores';
import { getUserId, userIdStore } from '../../../../client/lib/user';
import { Messages, Subscriptions, Users } from '../../../../client/stores';
import {
hasTranslationLanguageInAttachments,
hasTranslationLanguageInMessage,
Expand All @@ -18,15 +16,16 @@ import { sdk } from '../../../utils/client/lib/SDKClient';
let userLanguage = 'en';
let username = '';

Meteor.startup(() => {
Tracker.autorun(() => {
const user = watchUser();
if (!user) return;

userLanguage = user.language || 'en';
username = user.username || '';
});
});
const refreshUserCache = () => {
const uid = userIdStore.getState();
const user = uid ? Users.use.getState().get(uid) : undefined;
if (!user) return;
userLanguage = user.language || 'en';
username = user.username || '';
};
refreshUserCache();
userIdStore.subscribe(refreshUserCache);
Users.use.subscribe(refreshUserCache);

export const AutoTranslate = {
initialized: false,
Expand Down Expand Up @@ -84,14 +83,7 @@ export const AutoTranslate = {
return;
}

Tracker.autorun(async (c) => {
const uid = watchUserId();
if (!settings.watch('AutoTranslate_Enabled') || !uid || !hasPermission('auto-translate')) {
return;
}

c.stop();

const loadProviders = async () => {
try {
[this.providersMetadata, this.supportedLanguages] = await Promise.all([
sdk.call('autoTranslate.getProviderUiMetadata'),
Expand All @@ -101,7 +93,25 @@ export const AutoTranslate = {
// Avoid unwanted error message on UI when autotranslate is disabled while fetching data
console.error((e as Error).message);
}
});
};

let loaded = false;
const unsubs: Array<() => void> = [];
const tryLoad = async () => {
if (loaded) return;
if (!settings.peek('AutoTranslate_Enabled') || !userIdStore.getState() || !hasPermission('auto-translate')) {
return;
}
loaded = true;
unsubs.splice(0).forEach((unsubscribe) => unsubscribe());
await loadProviders();
};

unsubs.push(userIdStore.subscribe(() => void tryLoad()));
unsubs.push(settings.observe('AutoTranslate_Enabled', () => void tryLoad()));
unsubs.push(PermissionsCachedStore.useReady.subscribe(() => void tryLoad()));

void tryLoad();

Subscriptions.use.subscribe(() => {
mem.clear(this.findSubscriptionByRid);
Expand Down
1 change: 0 additions & 1 deletion apps/meteor/app/lib/client/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
import '../lib/MessageTypes';
import './OAuthProxy';
import './methods/sendMessage';
69 changes: 34 additions & 35 deletions apps/meteor/app/lib/client/methods/sendMessage.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import type { IMessage } from '@rocket.chat/core-typings';
import type { ServerMethods } from '@rocket.chat/ddp-client';
import { clientCallbacks } from '@rocket.chat/ui-client';
import { Meteor } from 'meteor/meteor';

import { onClientMessageReceived } from '../../../../client/lib/onClientMessageReceived';
import { settings } from '../../../../client/lib/settings';
Expand All @@ -11,40 +9,41 @@ import { Messages, Rooms } from '../../../../client/stores';
import { trim } from '../../../../lib/utils/stringUtils';
import { t } from '../../../utils/lib/i18n';

Meteor.methods<ServerMethods>({
async sendMessage(message) {
const uid = getUserId();
if (!uid || trim(message.msg) === '') {
return false;
}
const messageAlreadyExists = message._id && Messages.state.get(message._id);
if (messageAlreadyExists) {
return dispatchToastMessage({ type: 'error', message: t('Message_Already_Sent') });
}
const user = getUser();
if (!user?.username) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'sendMessage' });
}
message.ts = new Date();
message.u = {
export const runOptimisticSendMessage = async (
message: Partial<IMessage> & { rid: IMessage['rid']; msg: IMessage['msg'] },
): Promise<void> => {
const uid = getUserId();
if (!uid || trim(message.msg) === '') {
return;
}
const messageAlreadyExists = message._id && Messages.state.get(message._id);
if (messageAlreadyExists) {
dispatchToastMessage({ type: 'error', message: t('Message_Already_Sent') });
return;
}
const user = getUser();
if (!user?.username) {
return;
}

const room = Rooms.state.get(message.rid);
if (room?.federated) {
return;
}

const optimistic: IMessage = {
...(message as IMessage),
ts: new Date(),
u: {
_id: uid,
username: user.username,
name: user.name || '',
};
message.temp = true;
if (settings.peek('Message_Read_Receipt_Enabled')) {
message.unread = true;
}

// If the room is federated, send the message to matrix only
const room = Rooms.state.get(message.rid);
if (room?.federated) {
return;
}
},
temp: true,
...(settings.peek('Message_Read_Receipt_Enabled') ? { unread: true } : {}),
};

await onClientMessageReceived(message as IMessage).then((message) => {
Messages.state.store(message);
return clientCallbacks.run('afterSaveMessage', message, { room, user });
});
},
});
const processed = await onClientMessageReceived(optimistic);
Messages.state.store(processed);
await clientCallbacks.run('afterSaveMessage', processed, { room, user });
};
15 changes: 9 additions & 6 deletions apps/meteor/app/livechat/client/lib/stream/queueManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
type IOmnichannelAgent,
type Serialized,
} from '@rocket.chat/core-typings';
import { Tracker } from 'meteor/tracker';

import { useLivechatInquiryStore } from '../../../../../client/hooks/useLivechatInquiryStore';
import { queryClient } from '../../../../../client/lib/queryClient';
Expand Down Expand Up @@ -64,8 +63,10 @@ const removeInquiry = async (inquiry: ILivechatInquiryRecord) => {
return queryClient.invalidateQueries({ queryKey: ['rooms', { reference: inquiry.rid, type: 'l' }] });
};

const INQUIRY_COUNT_SETTING = 'Livechat_guest_pool_max_number_incoming_livechats_displayed';

const getInquiriesFromAPI = async () => {
const count = settings.peek('Livechat_guest_pool_max_number_incoming_livechats_displayed') ?? 0;
const count = settings.peek<number>(INQUIRY_COUNT_SETTING) ?? 0;
const { inquiries } = await sdk.rest.get('/v1/livechat/inquiries.queuedForUser', { count });
return inquiries;
};
Expand Down Expand Up @@ -140,10 +141,12 @@ const subscribe = async (userId: IOmnichannelAgent['_id']) => {
const cleanDepartmentListeners = addListenerForeachDepartment(agentDepartments);
const globalCleanup = addGlobalListener();

const computation = Tracker.autorun(async () => {
const inquiriesFromAPI = await getInquiriesFromAPI();
const refetchInquiries = async () => updateInquiries(await getInquiriesFromAPI());

await refetchInquiries();

await updateInquiries(inquiriesFromAPI);
const unobserveInquiryCount = settings.observe(INQUIRY_COUNT_SETTING, () => {
void refetchInquiries();
});

return () => {
Expand All @@ -152,8 +155,8 @@ const subscribe = async (userId: IOmnichannelAgent['_id']) => {
cleanAgentListener?.();
cleanDepartmentListeners?.();
globalCleanup?.();
unobserveInquiryCount();
departments.clear();
computation.stop();
};
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
Expand Down
1 change: 0 additions & 1 deletion apps/meteor/app/reactions/client/index.ts

This file was deleted.

123 changes: 60 additions & 63 deletions apps/meteor/app/reactions/client/methods/setReaction.ts
Original file line number Diff line number Diff line change
@@ -1,84 +1,81 @@
import type { IMessage } from '@rocket.chat/core-typings';
import type { ServerMethods } from '@rocket.chat/ddp-client';
import { Meteor } from 'meteor/meteor';

import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator';
import { getUser, getUserId } from '../../../../client/lib/user';
import { Rooms, Subscriptions, Messages } from '../../../../client/stores';
import { emoji } from '../../../emoji/client';

Meteor.methods<ServerMethods>({
async setReaction(reaction, messageId) {
if (!getUserId()) {
throw new Meteor.Error(203, 'User_logged_out');
}

const user = getUser();
export const runOptimisticSetReaction = (reaction: string, messageId: IMessage['_id']): void => {
if (!getUserId()) {
return;
}

if (!user?.username) {
return false;
}
const user = getUser();
if (!user?.username) {
return;
}

const message: IMessage | undefined = Messages.state.get(messageId);
if (!message) {
return false;
}
const message: IMessage | undefined = Messages.state.get(messageId);
if (!message) {
return;
}

const room = Rooms.state.get(message.rid);
if (!room) {
return false;
}

if (message.private) {
return false;
}
const room = Rooms.state.get(message.rid);
if (!room) {
return;
}

if (!emoji.list[reaction]) {
return false;
}
if (message.private) {
return;
}

if (roomCoordinator.readOnly(room, user)) {
return false;
}
if (!emoji.list[reaction]) {
return;
}

if (!Subscriptions.state.find(({ rid }) => rid === message.rid)) {
return false;
}
if (roomCoordinator.readOnly(room, user)) {
return;
}

if (message.reactions?.[reaction] && message.reactions[reaction].usernames.indexOf(user.username) !== -1) {
message.reactions[reaction].usernames.splice(message.reactions[reaction].usernames.indexOf(user.username), 1);
if (!Subscriptions.state.find(({ rid }) => rid === message.rid)) {
return;
}

if (message.reactions[reaction].usernames.length === 0) {
delete message.reactions[reaction];
}
if (message.reactions?.[reaction] && message.reactions[reaction].usernames.indexOf(user.username) !== -1) {
message.reactions[reaction].usernames.splice(message.reactions[reaction].usernames.indexOf(user.username), 1);

if (!message.reactions || typeof message.reactions !== 'object' || Object.keys(message.reactions).length === 0) {
delete message.reactions;
Messages.state.update(
(record) => record._id === messageId,
({ reactions: _, ...record }) => record,
);
} else {
Messages.state.update(
(record) => record._id === messageId,
(record) => ({ ...record, reactions: message.reactions }),
);
}
} else {
if (!message.reactions) {
message.reactions = {};
}
if (!message.reactions[reaction]) {
message.reactions[reaction] = {
usernames: [],
};
}
message.reactions[reaction].usernames.push(user.username);
if (message.reactions[reaction].usernames.length === 0) {
delete message.reactions[reaction];
}

if (!message.reactions || typeof message.reactions !== 'object' || Object.keys(message.reactions).length === 0) {
delete message.reactions;
Messages.state.update(
(record) => record._id === messageId,
(record) => ({ ...record, reactions: message.reactions }),
({ reactions: _, ...record }) => record,
);
return;
}
},
});

Messages.state.update(
(record) => record._id === messageId,
(record) => ({ ...record, reactions: message.reactions }),
);
return;
}

if (!message.reactions) {
message.reactions = {};
}
if (!message.reactions[reaction]) {
message.reactions[reaction] = {
usernames: [],
};
}
message.reactions[reaction].usernames.push(user.username);

Messages.state.update(
(record) => record._id === messageId,
(record) => ({ ...record, reactions: message.reactions }),
);
};
4 changes: 1 addition & 3 deletions apps/meteor/app/slashcommands-join/client/client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { Meteor } from 'meteor/meteor';

import { slashCommands } from '../../utils/client/slashCommand';

slashCommands.add({
Expand All @@ -10,7 +8,7 @@ slashCommands.add({
permission: 'view-c-room',
},
result(err, _result: unknown, params: Record<string, any>) {
if ((err as Meteor.Error).error === 'error-user-already-in-room') {
if ((err as { error?: string } | undefined)?.error === 'error-user-already-in-room') {
params.cmd = 'open';
params.msg.msg = params.msg.msg.replace('join', 'open');
return void slashCommands.run({ command: 'open', params: params.params, message: params.msg, triggerId: '', userId: params.userId });
Expand Down
Loading
Loading