snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
178
apps/meteor/client/lib/UserAction.ts
Normal file
178
apps/meteor/client/lib/UserAction.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
import type { IExtras, IRoomActivity, IUser } from '@rocket.chat/core-typings';
|
||||
import { Emitter } from '@rocket.chat/emitter';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import { sdk } from './SDKClient';
|
||||
import { settings } from './settings';
|
||||
import { getUser, getUserId } from './user';
|
||||
import { Users } from '../stores';
|
||||
|
||||
const TIMEOUT = 15000;
|
||||
const RENEW = TIMEOUT / 3;
|
||||
|
||||
const USER_ACTIVITY = 'user-activity';
|
||||
|
||||
export const USER_ACTIVITIES = {
|
||||
USER_RECORDING: 'user-recording',
|
||||
USER_TYPING: 'user-typing',
|
||||
USER_UPLOADING: 'user-uploading',
|
||||
USER_PLAYING: 'user-playing',
|
||||
};
|
||||
|
||||
const activityTimeouts = new Map();
|
||||
const activityRenews = new Map();
|
||||
const continuingIntervals = new Map();
|
||||
const roomActivities = new Map<string, Set<string>>();
|
||||
const rooms = new Map<string, (username: string, activityType: string[], extras?: object) => void>();
|
||||
|
||||
const performingUsers = new Map<string, IRoomActivity>();
|
||||
const performingUsersEmitter = new Emitter<{ changed: void }>();
|
||||
|
||||
const shownName = function (user: IUser | null | undefined): string | undefined {
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
if (settings.peek('UI_Use_Real_Name')) {
|
||||
return user.name;
|
||||
}
|
||||
return user.username;
|
||||
};
|
||||
|
||||
const emitActivities = debounce(async (rid: string, extras: IExtras): Promise<void> => {
|
||||
const activities = roomActivities.get(extras?.tmid || rid) || new Set();
|
||||
sdk.publish('notify-room', [`${rid}/${USER_ACTIVITY}`, shownName(getUser()), [...activities], extras]);
|
||||
}, 500);
|
||||
|
||||
function handleStreamAction(rid: string, username: string, activityTypes: string[], extras?: IExtras): void {
|
||||
rid = extras?.tmid || rid;
|
||||
const roomActivities = { ...performingUsers.get(rid) };
|
||||
|
||||
for (const [, activity] of Object.entries(USER_ACTIVITIES)) {
|
||||
roomActivities[activity] = roomActivities[activity] || new Map();
|
||||
const users = roomActivities[activity];
|
||||
const timeout = users[username];
|
||||
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
|
||||
if (activityTypes.includes(activity)) {
|
||||
activityTypes.splice(activityTypes.indexOf(activity), 1);
|
||||
users[username] = setTimeout(() => handleStreamAction(rid, username, activityTypes, extras), TIMEOUT);
|
||||
} else {
|
||||
delete users[username];
|
||||
}
|
||||
}
|
||||
|
||||
performingUsers.set(rid, roomActivities);
|
||||
performingUsersEmitter.emit('changed');
|
||||
}
|
||||
export const UserAction = new (class {
|
||||
addStream(rid: string): () => void {
|
||||
if (rooms.get(rid)) {
|
||||
throw new Error('UserAction - addStream should only be called once per room');
|
||||
}
|
||||
|
||||
const handler = function (username: string, activityType: string[], extras?: object): void {
|
||||
const uid = getUserId();
|
||||
const user = uid ? Users.state.get(uid) : undefined;
|
||||
|
||||
if (username === shownName(user)) {
|
||||
return;
|
||||
}
|
||||
handleStreamAction(rid, username, activityType, extras);
|
||||
};
|
||||
rooms.set(rid, handler);
|
||||
|
||||
const { stop } = sdk.stream('notify-room', [`${rid}/${USER_ACTIVITY}`], handler);
|
||||
return () => {
|
||||
if (!rooms.get(rid)) {
|
||||
return;
|
||||
}
|
||||
stop();
|
||||
rooms.delete(rid);
|
||||
};
|
||||
}
|
||||
|
||||
performContinuously(rid: string, activityType: string, extras: IExtras = {}): void {
|
||||
const trid = extras?.tmid || rid;
|
||||
const key = `${activityType}-${trid}`;
|
||||
|
||||
if (continuingIntervals.get(key)) {
|
||||
return;
|
||||
}
|
||||
this.start(rid, activityType, extras);
|
||||
|
||||
continuingIntervals.set(
|
||||
key,
|
||||
setInterval(() => {
|
||||
this.start(rid, activityType, extras);
|
||||
}, RENEW),
|
||||
);
|
||||
}
|
||||
|
||||
start(rid: string, activityType: string, extras: IExtras = {}): void {
|
||||
const trid = extras?.tmid || rid;
|
||||
const key = `${activityType}-${trid}`;
|
||||
|
||||
if (activityRenews.get(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
activityRenews.set(
|
||||
key,
|
||||
setTimeout(() => {
|
||||
clearTimeout(activityRenews.get(key));
|
||||
activityRenews.delete(key);
|
||||
}, RENEW),
|
||||
);
|
||||
|
||||
const activities = roomActivities.get(trid) || new Set();
|
||||
activities.add(activityType);
|
||||
roomActivities.set(trid, activities);
|
||||
|
||||
void emitActivities(rid, extras);
|
||||
|
||||
if (activityTimeouts.get(key)) {
|
||||
clearTimeout(activityTimeouts.get(key));
|
||||
activityTimeouts.delete(key);
|
||||
}
|
||||
|
||||
activityTimeouts.set(
|
||||
key,
|
||||
setTimeout(() => this.stop(trid, activityType, extras), TIMEOUT),
|
||||
);
|
||||
activityTimeouts.get(key);
|
||||
}
|
||||
|
||||
stop(rid: string, activityType: string, extras: IExtras): void {
|
||||
const trid = extras?.tmid || rid;
|
||||
const key = `${activityType}-${trid}`;
|
||||
|
||||
if (activityTimeouts.get(key)) {
|
||||
clearTimeout(activityTimeouts.get(key));
|
||||
activityTimeouts.delete(key);
|
||||
}
|
||||
if (activityRenews.get(key)) {
|
||||
clearTimeout(activityRenews.get(key));
|
||||
activityRenews.delete(key);
|
||||
}
|
||||
if (continuingIntervals.get(key)) {
|
||||
clearInterval(continuingIntervals.get(key));
|
||||
continuingIntervals.delete(key);
|
||||
}
|
||||
|
||||
const activities = roomActivities.get(trid) || new Set();
|
||||
activities.delete(activityType);
|
||||
roomActivities.set(trid, activities);
|
||||
void emitActivities(rid, extras);
|
||||
}
|
||||
|
||||
get(roomId: string): IRoomActivity | undefined {
|
||||
return performingUsers.get(roomId);
|
||||
}
|
||||
|
||||
subscribe(onChanged: () => void): () => void {
|
||||
return performingUsersEmitter.on('changed', onChanged);
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user