[NEW] Stream to get individual presence updates (#3606)
Co-authored-by: Gerzon Z <gerzonzcanario@gmail.com>
This commit is contained in:
parent
7d23385555
commit
ab9d568528
|
@ -58,7 +58,7 @@ export const SNIPPETED_MESSAGES = createRequestTypes('SNIPPETED_MESSAGES', ['OPE
|
||||||
export const DEEP_LINKING = createRequestTypes('DEEP_LINKING', ['OPEN']);
|
export const DEEP_LINKING = createRequestTypes('DEEP_LINKING', ['OPEN']);
|
||||||
export const SORT_PREFERENCES = createRequestTypes('SORT_PREFERENCES', ['SET_ALL', 'SET']);
|
export const SORT_PREFERENCES = createRequestTypes('SORT_PREFERENCES', ['SET_ALL', 'SET']);
|
||||||
export const SET_CUSTOM_EMOJIS = 'SET_CUSTOM_EMOJIS';
|
export const SET_CUSTOM_EMOJIS = 'SET_CUSTOM_EMOJIS';
|
||||||
export const SET_ACTIVE_USERS = 'SET_ACTIVE_USERS';
|
export const ACTIVE_USERS = createRequestTypes('ACTIVE_USERS', ['SET', 'CLEAR']);
|
||||||
export const USERS_TYPING = createRequestTypes('USERS_TYPING', ['ADD', 'REMOVE', 'CLEAR']);
|
export const USERS_TYPING = createRequestTypes('USERS_TYPING', ['ADD', 'REMOVE', 'CLEAR']);
|
||||||
export const INVITE_LINKS = createRequestTypes('INVITE_LINKS', [
|
export const INVITE_LINKS = createRequestTypes('INVITE_LINKS', [
|
||||||
'SET_TOKEN',
|
'SET_TOKEN',
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Action } from 'redux';
|
import { Action } from 'redux';
|
||||||
|
|
||||||
import { IActiveUsers } from '../reducers/activeUsers';
|
import { IActiveUsers } from '../reducers/activeUsers';
|
||||||
import { SET_ACTIVE_USERS } from './actionsTypes';
|
import { ACTIVE_USERS } from './actionsTypes';
|
||||||
|
|
||||||
interface ISetActiveUsers extends Action {
|
interface ISetActiveUsers extends Action {
|
||||||
activeUsers: IActiveUsers;
|
activeUsers: IActiveUsers;
|
||||||
|
@ -10,6 +10,10 @@ interface ISetActiveUsers extends Action {
|
||||||
export type TActionActiveUsers = ISetActiveUsers;
|
export type TActionActiveUsers = ISetActiveUsers;
|
||||||
|
|
||||||
export const setActiveUsers = (activeUsers: IActiveUsers): ISetActiveUsers => ({
|
export const setActiveUsers = (activeUsers: IActiveUsers): ISetActiveUsers => ({
|
||||||
type: SET_ACTIVE_USERS,
|
type: ACTIVE_USERS.SET,
|
||||||
activeUsers
|
activeUsers
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const clearActiveUsers = (): Action => ({
|
||||||
|
type: ACTIVE_USERS.CLEAR
|
||||||
|
});
|
||||||
|
|
|
@ -19,7 +19,7 @@ export function subscribeUsersPresence() {
|
||||||
this.activeUsersSubTimeout = setTimeout(() => {
|
this.activeUsersSubTimeout = setTimeout(() => {
|
||||||
this.sdk.subscribe('activeUsers');
|
this.sdk.subscribe('activeUsers');
|
||||||
}, 5000);
|
}, 5000);
|
||||||
} else {
|
} else if (compareServerVersion(serverVersion, 'lowerThan', '4.1.0')) {
|
||||||
this.sdk.subscribe('stream-notify-logged', 'user-status');
|
this.sdk.subscribe('stream-notify-logged', 'user-status');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +52,11 @@ export default async function getUsersPresence() {
|
||||||
try {
|
try {
|
||||||
// RC 1.1.0
|
// RC 1.1.0
|
||||||
const result = await this.sdk.get('users.presence', params);
|
const result = await this.sdk.get('users.presence', params);
|
||||||
|
|
||||||
|
if (compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '4.1.0')) {
|
||||||
|
this.sdk.subscribeRaw('stream-user-presence', ['', { added: ids }]);
|
||||||
|
}
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
const { users } = result;
|
const { users } = result;
|
||||||
|
|
||||||
|
@ -100,13 +105,9 @@ export default async function getUsersPresence() {
|
||||||
|
|
||||||
let usersTimer = null;
|
let usersTimer = null;
|
||||||
export function getUserPresence(uid) {
|
export function getUserPresence(uid) {
|
||||||
const auth = reduxStore.getState().login.isAuthenticated;
|
|
||||||
|
|
||||||
if (!usersTimer) {
|
if (!usersTimer) {
|
||||||
usersTimer = setTimeout(() => {
|
usersTimer = setTimeout(() => {
|
||||||
if (auth && ids.length) {
|
|
||||||
getUsersPresence.call(this);
|
getUsersPresence.call(this);
|
||||||
}
|
|
||||||
usersTimer = null;
|
usersTimer = null;
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ import { compareServerVersion } from './utils';
|
||||||
import reduxStore from './createStore';
|
import reduxStore from './createStore';
|
||||||
import database from './database';
|
import database from './database';
|
||||||
import subscribeRooms from './methods/subscriptions/rooms';
|
import subscribeRooms from './methods/subscriptions/rooms';
|
||||||
import getUsersPresence, { getUserPresence, subscribeUsersPresence } from './methods/getUsersPresence';
|
import { getUserPresence, subscribeUsersPresence } from './methods/getUsersPresence';
|
||||||
import protectedFunction from './methods/helpers/protectedFunction';
|
import protectedFunction from './methods/helpers/protectedFunction';
|
||||||
import readMessages from './methods/readMessages';
|
import readMessages from './methods/readMessages';
|
||||||
import getSettings, { getLoginSettings, setSettings, subscribeSettings } from './methods/getSettings';
|
import getSettings, { getLoginSettings, setSettings, subscribeSettings } from './methods/getSettings';
|
||||||
|
@ -308,10 +308,26 @@ const RocketChat = {
|
||||||
protectedFunction(ddpMessage => onRolesChanged(ddpMessage))
|
protectedFunction(ddpMessage => onRolesChanged(ddpMessage))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// RC 4.1
|
||||||
|
this.sdk.onStreamData('stream-user-presence', ddpMessage => {
|
||||||
|
const userStatus = ddpMessage.fields.args[0];
|
||||||
|
const { uid } = ddpMessage.fields;
|
||||||
|
const [, status, statusText] = userStatus;
|
||||||
|
const newStatus = { status: STATUSES[status], statusText };
|
||||||
|
reduxStore.dispatch(setActiveUsers({ [uid]: newStatus }));
|
||||||
|
|
||||||
|
const { user: loggedUser } = reduxStore.getState().login;
|
||||||
|
if (loggedUser && loggedUser.id === uid) {
|
||||||
|
reduxStore.dispatch(setUser(newStatus));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.notifyLoggedListener = this.sdk.onStreamData(
|
this.notifyLoggedListener = this.sdk.onStreamData(
|
||||||
'stream-notify-logged',
|
'stream-notify-logged',
|
||||||
protectedFunction(async ddpMessage => {
|
protectedFunction(async ddpMessage => {
|
||||||
const { eventName } = ddpMessage.fields;
|
const { eventName } = ddpMessage.fields;
|
||||||
|
|
||||||
|
// `user-status` event is deprecated after RC 4.1 in favor of `stream-user-presence/${uid}`
|
||||||
if (/user-status/.test(eventName)) {
|
if (/user-status/.test(eventName)) {
|
||||||
this.activeUsers = this.activeUsers || {};
|
this.activeUsers = this.activeUsers || {};
|
||||||
if (!this._setUserTimer) {
|
if (!this._setUserTimer) {
|
||||||
|
@ -1633,6 +1649,8 @@ const RocketChat = {
|
||||||
reduxStore.dispatch(setUser({ status: { status: 'offline' } }));
|
reduxStore.dispatch(setUser({ status: { status: 'offline' } }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const serverVersion = reduxStore.getState().server.version;
|
||||||
|
if (compareServerVersion(serverVersion, 'lowerThan', '4.1.0')) {
|
||||||
if (!this._setUserTimer) {
|
if (!this._setUserTimer) {
|
||||||
this._setUserTimer = setTimeout(() => {
|
this._setUserTimer = setTimeout(() => {
|
||||||
const activeUsersBatch = this.activeUsers;
|
const activeUsersBatch = this.activeUsers;
|
||||||
|
@ -1643,6 +1661,7 @@ const RocketChat = {
|
||||||
return (this.activeUsers = {});
|
return (this.activeUsers = {});
|
||||||
}, 10000);
|
}, 10000);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!ddpMessage.fields) {
|
if (!ddpMessage.fields) {
|
||||||
this.activeUsers[ddpMessage.id] = { status: 'offline' };
|
this.activeUsers[ddpMessage.id] = { status: 'offline' };
|
||||||
|
@ -1650,7 +1669,6 @@ const RocketChat = {
|
||||||
this.activeUsers[ddpMessage.id] = { status: ddpMessage.fields.status };
|
this.activeUsers[ddpMessage.id] = { status: ddpMessage.fields.status };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getUsersPresence,
|
|
||||||
getUserPresence,
|
getUserPresence,
|
||||||
subscribeUsersPresence,
|
subscribeUsersPresence,
|
||||||
getDirectory({ query, count, offset, sort }) {
|
getDirectory({ query, count, offset, sort }) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { setActiveUsers } from '../actions/activeUsers';
|
import { clearActiveUsers, setActiveUsers } from '../actions/activeUsers';
|
||||||
import { IActiveUsers, initialState } from './activeUsers';
|
import { IActiveUsers, initialState } from './activeUsers';
|
||||||
import { mockedStore } from './mockedStore';
|
import { mockedStore } from './mockedStore';
|
||||||
|
|
||||||
|
@ -13,4 +13,11 @@ describe('test reducer', () => {
|
||||||
const state = mockedStore.getState().activeUsers;
|
const state = mockedStore.getState().activeUsers;
|
||||||
expect(state).toEqual({ ...activeUsers });
|
expect(state).toEqual({ ...activeUsers });
|
||||||
});
|
});
|
||||||
|
it('should return initial state after dispatching clear', () => {
|
||||||
|
const previousState = mockedStore.getState().activeUsers;
|
||||||
|
expect(previousState).not.toBe(initialState);
|
||||||
|
mockedStore.dispatch(clearActiveUsers());
|
||||||
|
const state = mockedStore.getState().activeUsers;
|
||||||
|
expect(state).toEqual(initialState);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { TApplicationActions } from '../definitions';
|
import { TApplicationActions } from '../definitions';
|
||||||
import { SET_ACTIVE_USERS } from '../actions/actionsTypes';
|
import { ACTIVE_USERS } from '../actions/actionsTypes';
|
||||||
|
|
||||||
type TUserStatus = 'online' | 'offline';
|
type TUserStatus = 'online' | 'offline' | 'away' | 'busy';
|
||||||
export interface IActiveUser {
|
export interface IActiveUser {
|
||||||
status: TUserStatus;
|
status: TUserStatus;
|
||||||
statusText: string;
|
statusText: string;
|
||||||
|
@ -15,11 +15,13 @@ export const initialState: IActiveUsers = {};
|
||||||
|
|
||||||
export default function activeUsers(state = initialState, action: TApplicationActions): IActiveUsers {
|
export default function activeUsers(state = initialState, action: TApplicationActions): IActiveUsers {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case SET_ACTIVE_USERS:
|
case ACTIVE_USERS.SET:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
...action.activeUsers
|
...action.activeUsers
|
||||||
};
|
};
|
||||||
|
case ACTIVE_USERS.CLEAR:
|
||||||
|
return initialState;
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ import EventEmitter from '../utils/events';
|
||||||
import { inviteLinksRequest } from '../actions/inviteLinks';
|
import { inviteLinksRequest } from '../actions/inviteLinks';
|
||||||
import { showErrorAlert } from '../utils/info';
|
import { showErrorAlert } from '../utils/info';
|
||||||
import { localAuthenticate } from '../utils/localAuthentication';
|
import { localAuthenticate } from '../utils/localAuthentication';
|
||||||
import { setActiveUsers } from '../actions/activeUsers';
|
|
||||||
import { encryptionInit, encryptionStop } from '../actions/encryption';
|
import { encryptionInit, encryptionStop } from '../actions/encryption';
|
||||||
import UserPreferences from '../lib/userPreferences';
|
import UserPreferences from '../lib/userPreferences';
|
||||||
import { inquiryRequest, inquiryReset } from '../ee/omnichannel/actions/inquiry';
|
import { inquiryRequest, inquiryReset } from '../ee/omnichannel/actions/inquiry';
|
||||||
|
@ -100,7 +99,6 @@ const registerPushToken = function* registerPushToken() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchUsersPresence = function* fetchUserPresence() {
|
const fetchUsersPresence = function* fetchUserPresence() {
|
||||||
yield RocketChat.getUsersPresence();
|
|
||||||
RocketChat.subscribeUsersPresence();
|
RocketChat.subscribeUsersPresence();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -222,11 +220,6 @@ const handleLogout = function* handleLogout({ forcedByServer }) {
|
||||||
const handleSetUser = function* handleSetUser({ user }) {
|
const handleSetUser = function* handleSetUser({ user }) {
|
||||||
setLanguage(user?.language);
|
setLanguage(user?.language);
|
||||||
|
|
||||||
if (user && user.status) {
|
|
||||||
const userId = yield select(state => state.login.user.id);
|
|
||||||
yield put(setActiveUsers({ [userId]: user }));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user?.statusLivechat && RocketChat.isOmnichannelModuleAvailable()) {
|
if (user?.statusLivechat && RocketChat.isOmnichannelModuleAvailable()) {
|
||||||
if (isOmnichannelStatusAvailable(user)) {
|
if (isOmnichannelStatusAvailable(user)) {
|
||||||
yield put(inquiryRequest());
|
yield put(inquiryRequest());
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { SERVER } from '../actions/actionsTypes';
|
||||||
import { selectServerFailure, selectServerRequest, selectServerSuccess, serverFailure } from '../actions/server';
|
import { selectServerFailure, selectServerRequest, selectServerSuccess, serverFailure } from '../actions/server';
|
||||||
import { clearSettings } from '../actions/settings';
|
import { clearSettings } from '../actions/settings';
|
||||||
import { setUser } from '../actions/login';
|
import { setUser } from '../actions/login';
|
||||||
|
import { clearActiveUsers } from '../actions/activeUsers';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import database from '../lib/database';
|
import database from '../lib/database';
|
||||||
import log, { logServerVersion } from '../utils/log';
|
import log, { logServerVersion } from '../utils/log';
|
||||||
|
@ -73,6 +74,7 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
|
||||||
|
|
||||||
yield put(inquiryReset());
|
yield put(inquiryReset());
|
||||||
yield put(encryptionStop());
|
yield put(encryptionStop());
|
||||||
|
yield put(clearActiveUsers());
|
||||||
const serversDB = database.servers;
|
const serversDB = database.servers;
|
||||||
yield UserPreferences.setStringAsync(RocketChat.CURRENT_SERVER, server);
|
yield UserPreferences.setStringAsync(RocketChat.CURRENT_SERVER, server);
|
||||||
const userId = yield UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${server}`);
|
const userId = yield UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${server}`);
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
"@react-navigation/stack": "5.14.5",
|
"@react-navigation/stack": "5.14.5",
|
||||||
"@rocket.chat/message-parser": "0.30.0",
|
"@rocket.chat/message-parser": "0.30.0",
|
||||||
"@rocket.chat/react-native-fast-image": "^8.2.0",
|
"@rocket.chat/react-native-fast-image": "^8.2.0",
|
||||||
"@rocket.chat/sdk": "RocketChat/Rocket.Chat.js.SDK#mobile",
|
"@rocket.chat/sdk": "RocketChat/Rocket.Chat.js.SDK#subscribe-raw",
|
||||||
"@rocket.chat/ui-kit": "0.13.0",
|
"@rocket.chat/ui-kit": "0.13.0",
|
||||||
"bytebuffer": "^5.0.1",
|
"bytebuffer": "^5.0.1",
|
||||||
"color2k": "1.2.4",
|
"color2k": "1.2.4",
|
||||||
|
|
|
@ -3758,9 +3758,9 @@
|
||||||
resolved "https://registry.yarnpkg.com/@rocket.chat/react-native-fast-image/-/react-native-fast-image-8.2.0.tgz#4f48858f95f40afcb10b39cee9b1239c150d6c51"
|
resolved "https://registry.yarnpkg.com/@rocket.chat/react-native-fast-image/-/react-native-fast-image-8.2.0.tgz#4f48858f95f40afcb10b39cee9b1239c150d6c51"
|
||||||
integrity sha512-NF5KlFt642ZucP/KHnYGBNYLD6O7bcrZMKfRQlH5Y3/1xpnPX1g4wuygtiV7XArMU1FopQT+qmCUPPj8IMDTcw==
|
integrity sha512-NF5KlFt642ZucP/KHnYGBNYLD6O7bcrZMKfRQlH5Y3/1xpnPX1g4wuygtiV7XArMU1FopQT+qmCUPPj8IMDTcw==
|
||||||
|
|
||||||
"@rocket.chat/sdk@RocketChat/Rocket.Chat.js.SDK#mobile":
|
"@rocket.chat/sdk@RocketChat/Rocket.Chat.js.SDK#subscribe-raw":
|
||||||
version "1.1.0-mobile"
|
version "1.2.0-mobile"
|
||||||
resolved "https://codeload.github.com/RocketChat/Rocket.Chat.js.SDK/tar.gz/c64e69ea22514ae3bbe24e36ca77868fdae76157"
|
resolved "https://codeload.github.com/RocketChat/Rocket.Chat.js.SDK/tar.gz/6eb97d265d2e2054eced23fc78145ab179e3a1f3"
|
||||||
dependencies:
|
dependencies:
|
||||||
js-sha256 "^0.9.0"
|
js-sha256 "^0.9.0"
|
||||||
lru-cache "^4.1.1"
|
lru-cache "^4.1.1"
|
||||||
|
|
Loading…
Reference in New Issue