Regression: Fix presence status (#19474)
Co-authored-by: Guilherme Gazzo <guilherme@gazzo.xyz>pull/19508/head^2
parent
0f09b1ba14
commit
4b68fb9cbe
@ -1,80 +0,0 @@ |
||||
import { Emitter } from '@rocket.chat/emitter'; |
||||
|
||||
import { APIClient } from '../../app/utils/client'; |
||||
|
||||
export const Presence = new Emitter(); |
||||
|
||||
const Statuses = new Map(); |
||||
|
||||
const getPresence = (() => { |
||||
const uids = new Set(); |
||||
|
||||
let timer; |
||||
const fetch = () => { |
||||
timer && clearTimeout(timer); |
||||
timer = setTimeout(async () => { |
||||
const params = { |
||||
ids: [...uids], |
||||
}; |
||||
|
||||
const { |
||||
users, |
||||
} = await APIClient.v1.get('users.presence', params); |
||||
|
||||
users.forEach((user) => { |
||||
Presence.emit(user._id, user); |
||||
uids.delete(user._id); |
||||
}); |
||||
|
||||
[...uids].forEach((uid) => { |
||||
Presence.emit(uid, { uid }); |
||||
}); |
||||
|
||||
uids.clear(); |
||||
}, 50); |
||||
}; |
||||
|
||||
const get = async (uid) => { |
||||
uids.add(uid); |
||||
fetch(); |
||||
}; |
||||
|
||||
Presence.on('remove', (uid) => { |
||||
if (Presence.has(uid)) { |
||||
return; |
||||
} |
||||
Statuses.delete(uid); |
||||
}); |
||||
|
||||
Presence.on('reset', () => { |
||||
Presence.once('restart', () => Presence.events().filter((e) => Boolean(e) && !['reset', 'restart', 'remove'].includes(e) && typeof e === 'string').forEach(get)); |
||||
}); |
||||
|
||||
return get; |
||||
})(); |
||||
|
||||
|
||||
const update = ({ _id: uid, status }) => { |
||||
Statuses.set(uid, status); |
||||
}; |
||||
|
||||
Presence.listen = async (uid, handle) => { |
||||
Presence.on(uid, handle); |
||||
Presence.on(uid, update); |
||||
Presence.on('reset', handle); |
||||
if (Statuses.has(uid)) { |
||||
return handle({ status: Statuses.get(uid) }); |
||||
} |
||||
getPresence(uid); |
||||
}; |
||||
|
||||
Presence.stop = (uid, handle) => { |
||||
Presence.off(uid, handle); |
||||
Presence.off('reset', handle); |
||||
Presence.emit('remove', uid); |
||||
}; |
||||
|
||||
Presence.reset = () => { |
||||
Presence.emit('reset', { status: 'offline' }); |
||||
Statuses.clear(); |
||||
}; |
||||
@ -0,0 +1,133 @@ |
||||
import { Emitter, EventType, Handler } from '@rocket.chat/emitter'; |
||||
|
||||
import { APIClient } from '../../app/utils/client'; |
||||
import { IUser } from '../../definition/IUser'; |
||||
|
||||
const emitter = new Emitter(); |
||||
const statuses = new Map(); |
||||
|
||||
type User = Pick<IUser, '_id' | 'username' | 'name' | 'status' | 'utcOffset' | 'statusText' | 'avatarETag'>; |
||||
|
||||
type UsersPresencePayload = { |
||||
users: User[]; |
||||
full: boolean; |
||||
}; |
||||
|
||||
const isUid = (eventType: EventType): eventType is User['_id'] => |
||||
Boolean(eventType) && typeof eventType === 'string' && !['reset', 'restart', 'remove'].includes(eventType); |
||||
|
||||
const uids = new Set<User['_id']>(); |
||||
const getPresence = ((): ((uid: User['_id']) => void) => { |
||||
let timer: ReturnType<typeof setTimeout>; |
||||
|
||||
const fetch = (delay = 250): void => { |
||||
timer && clearTimeout(timer); |
||||
timer = setTimeout(async () => { |
||||
const currentUids = new Set(uids); |
||||
uids.clear(); |
||||
try { |
||||
const params = { |
||||
ids: [...currentUids], |
||||
}; |
||||
|
||||
const { users } = await APIClient.v1.get('users.presence', params) as UsersPresencePayload; |
||||
|
||||
users.forEach((user) => { |
||||
if (!statuses.has(user._id)) { |
||||
emitter.emit(user._id, user); |
||||
} |
||||
currentUids.delete(user._id); |
||||
}); |
||||
|
||||
currentUids.forEach((uid) => { |
||||
emitter.emit(uid, { uid, status: 'offline' }); |
||||
}); |
||||
|
||||
currentUids.clear(); |
||||
} catch { |
||||
fetch(delay + delay); |
||||
} finally { |
||||
currentUids.forEach((item) => uids.add(item)); |
||||
} |
||||
}, delay); |
||||
}; |
||||
|
||||
const get = (uid: User['_id']): void => { |
||||
uids.add(uid); |
||||
fetch(); |
||||
}; |
||||
|
||||
emitter.on('remove', (uid) => { |
||||
if (emitter.has(uid)) { |
||||
return; |
||||
} |
||||
|
||||
statuses.delete(uid); |
||||
}); |
||||
|
||||
emitter.on('reset', () => { |
||||
statuses.clear(); |
||||
emitter.once('restart', () => { |
||||
emitter.events() |
||||
.filter(isUid) |
||||
.forEach(get); |
||||
}); |
||||
}); |
||||
|
||||
return get; |
||||
})(); |
||||
|
||||
type PresenceUpdate = Partial<Pick<User, '_id' | 'username' | 'status' | 'statusText'>>; |
||||
|
||||
const update: Handler<PresenceUpdate> = (update) => { |
||||
if (update?._id) { |
||||
statuses.set(update._id, update.status); |
||||
uids.delete(update._id); |
||||
} |
||||
}; |
||||
|
||||
const listen = (uid: User['_id'], handler: Handler<PresenceUpdate>): void => { |
||||
emitter.on(uid, handler); |
||||
emitter.on(uid, update); |
||||
emitter.on('reset', handler); |
||||
|
||||
if (statuses.has(uid)) { |
||||
return handler({ status: statuses.get(uid) }); |
||||
} |
||||
|
||||
getPresence(uid); |
||||
}; |
||||
|
||||
const stop = (uid: User['_id'], handler: Handler<PresenceUpdate>): void => { |
||||
emitter.off(uid, handler); |
||||
emitter.off(uid, update); |
||||
emitter.off('reset', handler); |
||||
emitter.emit('remove', uid); |
||||
}; |
||||
|
||||
const reset = (): void => { |
||||
emitter.emit('reset', {}); |
||||
statuses.clear(); |
||||
}; |
||||
|
||||
const restart = (): void => { |
||||
emitter.emit('restart'); |
||||
}; |
||||
|
||||
const notify = (update: PresenceUpdate): void => { |
||||
if (update._id) { |
||||
emitter.emit(update._id, update); |
||||
} |
||||
|
||||
if (update.username) { |
||||
emitter.emit(update.username, update); |
||||
} |
||||
}; |
||||
|
||||
export const Presence = { |
||||
listen, |
||||
stop, |
||||
reset, |
||||
restart, |
||||
notify, |
||||
}; |
||||
Loading…
Reference in new issue