🚫 Disable all ws for frontend (#324)
This commit is contained in:
@@ -6,20 +6,11 @@ import CheckCircleIcon from '@material-ui/icons/CheckCircle';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import { ConnectedState } from '@features/users/state/atoms/connected';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import WebSocket, { WebsocketEvents } from '@features/global/types/websocket-types';
|
||||
// import WebSocket, { WebsocketEvents } from '@features/global/types/websocket-types';
|
||||
|
||||
export default () => {
|
||||
const [{ connected, reconnecting }, setState] = useRecoilState(ConnectedState);
|
||||
|
||||
useEffect(() => {
|
||||
WebSocket.get().on(WebsocketEvents.Disconnected, () => {
|
||||
setState({ connected: false, reconnecting: false });
|
||||
});
|
||||
WebSocket.get().on(WebsocketEvents.Connected, () => {
|
||||
setState({ connected: true, reconnecting: false });
|
||||
});
|
||||
}, [setState]);
|
||||
|
||||
return (
|
||||
<div className={'connection_indicator ' + (connected === false ? 'visible' : '')}>
|
||||
{connected === false && reconnecting !== true && (
|
||||
|
||||
+1
-9
@@ -1,28 +1,20 @@
|
||||
import Api from '../../global/framework/api-service';
|
||||
import { TdriveService } from '../../global/framework/registry-decorator-service';
|
||||
import { Application } from 'app/features/applications/types/application';
|
||||
import { WebsocketRoom } from '../../global/types/websocket-types';
|
||||
|
||||
const PREFIX = '/internal/services/applications/v1/companies';
|
||||
|
||||
@TdriveService('CompanyApplicationsAPIClientService')
|
||||
class CompanyApplicationsAPIClient {
|
||||
private realtime: Map<string, WebsocketRoom[]> = new Map();
|
||||
|
||||
websockets(companyId: string): WebsocketRoom[] {
|
||||
return this.realtime.get(companyId) || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all applications for a company
|
||||
*
|
||||
* @param companyId
|
||||
*/
|
||||
async list(companyId: string): Promise<Application[]> {
|
||||
return Api.get<{ resources: Application[]; websockets: WebsocketRoom[] }>(
|
||||
return Api.get<{ resources: Application[] }>(
|
||||
`${PREFIX}/${companyId}/applications`,
|
||||
).then(result => {
|
||||
this.realtime.set(companyId, result.websockets);
|
||||
return result.resources && result.resources.length ? result.resources : [];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
} from '../state/company-applications';
|
||||
import CompanyApplicationsAPIClient from 'app/features/applications/api/company-applications-api-client';
|
||||
import { useCurrentCompany } from '../../companies/hooks/use-companies';
|
||||
import { useRealtimeRoom } from 'app/features/global/hooks/use-realtime';
|
||||
import { Application } from 'app/features/applications/types/application';
|
||||
import { LoadingState } from 'app/features/global/state/atoms/Loading';
|
||||
import useRouterWorkspace from 'app/features/router/hooks/use-router-workspace';
|
||||
@@ -102,18 +101,6 @@ export function useCompanyApplications(companyId = '') {
|
||||
};
|
||||
}
|
||||
|
||||
export const useCompanyApplicationsRealtime = (companyId = '') => {
|
||||
const { company } = useCurrentCompany();
|
||||
companyId = companyId || company?.id || '';
|
||||
|
||||
const { refresh } = useCompanyApplications(companyId);
|
||||
|
||||
const room = CompanyApplicationsAPIClient.websockets(companyId || '')[0];
|
||||
useRealtimeRoom<Application[]>(room, 'useCompanyApplications', () => {
|
||||
refresh();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Use a single application
|
||||
* @param applicationId
|
||||
|
||||
@@ -4,7 +4,6 @@ import AccessRightsService from 'app/features/workspace-members/services/workspa
|
||||
import CurrentUser from 'app/deprecated/user/CurrentUser';
|
||||
import JWT from 'app/features/auth/jwt-storage-service';
|
||||
import LocalStorage from '../../global/framework/local-storage-service';
|
||||
import WebSocket from '../../global/types/websocket-types';
|
||||
|
||||
class Application {
|
||||
private logger: Logger.Logger;
|
||||
@@ -25,9 +24,6 @@ class Application {
|
||||
this.started = true;
|
||||
this.logger.info('Starting application for user', user);
|
||||
|
||||
const ws = WebSocket.get();
|
||||
ws.connect();
|
||||
|
||||
AccessRightsService.resetLevels();
|
||||
CurrentUser.start();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { CompanyType } from '@features/companies/types/company';
|
||||
import { WorkspaceType } from '@features/workspaces/types/workspace';
|
||||
import Api from '../../global/framework/api-service';
|
||||
import { TdriveService } from '../../global/framework/registry-decorator-service';
|
||||
import { WebsocketRoom } from '../../global/types/websocket-types';
|
||||
|
||||
const PREFIX = '/internal/services/users/v1';
|
||||
|
||||
@@ -14,12 +13,6 @@ export type UpdateWorkspaceBody = {
|
||||
|
||||
@TdriveService('CompanyAPIClientService')
|
||||
class CompanyAPIClient {
|
||||
private realtime: Map<string, WebsocketRoom> = new Map();
|
||||
|
||||
websocket(companyId: string): WebsocketRoom {
|
||||
return this.realtime.get(companyId) || { room: '', token: '' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of companies for a user, only common companies with current user are returned.
|
||||
|
||||
@@ -37,13 +30,12 @@ class CompanyAPIClient {
|
||||
* @param companyId
|
||||
*/
|
||||
async get(companyId: string, disableJWTAuthentication = false): Promise<CompanyType> {
|
||||
return Api.get<{ resource: CompanyType; websocket: WebsocketRoom }>(
|
||||
return Api.get<{ resource: CompanyType; }>(
|
||||
`${PREFIX}/companies/${companyId}`,
|
||||
undefined,
|
||||
false,
|
||||
{ disableJWTAuthentication },
|
||||
).then(result => {
|
||||
result.resource?.id && this.realtime.set(result.resource.id, result.websocket);
|
||||
return result.resource;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import WorkspacesService from '@deprecated/workspaces/workspaces.jsx';
|
||||
import AccessRightsService from '@features/workspace-members/services/workspace-members-access-rights-service';
|
||||
import LoginService from '@features/auth/login-service';
|
||||
import UserAPIClient from '@features/users/api/user-api-client';
|
||||
import { useRealtimeRoom } from '@features/global/hooks/use-realtime';
|
||||
import CompanyAPIClient from '@features/companies/api/company-api-client';
|
||||
|
||||
/**
|
||||
@@ -86,16 +85,6 @@ export const useCurrentCompany = () => {
|
||||
|
||||
return { company: company as CompanyType, refresh };
|
||||
};
|
||||
|
||||
export const useCurrentCompanyRealtime = () => {
|
||||
const { company, refresh } = useCurrentCompany();
|
||||
|
||||
const room = CompanyAPIClient.websocket(company?.id || '');
|
||||
useRealtimeRoom<CompanyType>(room, 'useCurrentCompany', () => {
|
||||
refresh();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Company priority:
|
||||
* 1. Router company id
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { useRealtimeRoom } from '@features/global/hooks/use-realtime';
|
||||
import { useDriveActions } from './use-drive-actions';
|
||||
import { useDriveItem } from './use-drive-item';
|
||||
|
||||
export const useDriveRealtime = (id: string) => {
|
||||
const { refresh } = useDriveActions();
|
||||
const { websockets } = useDriveItem(id);
|
||||
const room = websockets?.[0];
|
||||
useRealtimeRoom(room as { room: string; token: string }, 'useDriveRealtime-' + id, () => {
|
||||
refresh(id);
|
||||
});
|
||||
};
|
||||
|
||||
export const DriveRealtimeObject = ({ id }: { id: string }) => {
|
||||
useDriveRealtime(id);
|
||||
return <></>;
|
||||
};
|
||||
@@ -1,96 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import useWebSocket from '@features/global/hooks/use-websocket';
|
||||
import Logger from '@features/global/framework/logger-service';
|
||||
import {
|
||||
RealtimeBaseAction,
|
||||
RealtimeBaseEvent,
|
||||
RealtimeResourceEvent,
|
||||
} from '../types/realtime-types';
|
||||
import { WebsocketRoom } from '@features/global/types/websocket-types';
|
||||
|
||||
const logger = Logger.getLogger('useRealtimeRoom');
|
||||
|
||||
export type RealtimeRoomService<T> = {
|
||||
lastEvent: T;
|
||||
send: (data: T) => void;
|
||||
emit: (event: string, data: T) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Subscribe to a room using websocket channel.
|
||||
*
|
||||
* Note: It will subscribe only once, even if the component using it re renders. If you need to unsubscribe and subscribe again, call unsubscribe on the returned object.
|
||||
*
|
||||
* @param roomConf
|
||||
* @param tagName
|
||||
* @param onEvent
|
||||
* @returns
|
||||
*/
|
||||
const useRealtimeRoom = <T>(
|
||||
roomConf: WebsocketRoom,
|
||||
tagName: string,
|
||||
onEvent: (action: RealtimeBaseAction, event: T) => void,
|
||||
) => {
|
||||
const { websocket } = useWebSocket();
|
||||
const [lastEvent, setLastEvent] = useState<{ action: RealtimeBaseAction; payload: T }>();
|
||||
const [room, setRoom] = useState(roomConf);
|
||||
const [tag] = useState(tagName);
|
||||
// subscribe once
|
||||
const subscribed = useRef(false);
|
||||
|
||||
const newEvent = useCallback(
|
||||
(event: { action: RealtimeBaseAction; payload: T }) => {
|
||||
if (event) {
|
||||
setLastEvent(event);
|
||||
onEvent(event.action, event.payload);
|
||||
}
|
||||
},
|
||||
[onEvent],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (room !== roomConf) {
|
||||
setRoom({ ...roomConf });
|
||||
if (room && subscribed.current && websocket) {
|
||||
websocket.leave(room.room, tag);
|
||||
subscribed.current = false;
|
||||
}
|
||||
}
|
||||
}, [roomConf?.room, roomConf?.token, tagName]);
|
||||
|
||||
useEffect(() => {
|
||||
if (room && room.room && websocket && !subscribed.current) {
|
||||
websocket.join(room.room, room.token, tag, (type: string, event: RealtimeBaseEvent) => {
|
||||
logger.debug('Received WebSocket event', type, event);
|
||||
if (type === 'realtime:resource') {
|
||||
newEvent({
|
||||
action: (event as RealtimeResourceEvent<T>).action,
|
||||
payload: {
|
||||
...(event as RealtimeResourceEvent<T>).resource,
|
||||
_type: (event as RealtimeResourceEvent<T>).type,
|
||||
},
|
||||
});
|
||||
} else if (type === 'realtime:event') {
|
||||
newEvent({ action: 'event', payload: event.data });
|
||||
} else if (type === 'realtime:join:success') {
|
||||
logger.debug(`Room ${room} has been joined`);
|
||||
} else {
|
||||
logger.debug('Event type is not supported', type);
|
||||
}
|
||||
});
|
||||
subscribed.current = true;
|
||||
}
|
||||
}, [websocket, tag, room, onEvent]);
|
||||
|
||||
return {
|
||||
lastEvent,
|
||||
send: (data: any) => websocket?.send(room.room, room.token, data),
|
||||
unsubscribe: () => {
|
||||
subscribed.current = false;
|
||||
websocket?.leave(room.room, tagName);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export { useRealtimeRoom };
|
||||
@@ -1,34 +0,0 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import WebSocketFactory, { WebsocketEvents } from '@features/global/types/websocket-types';
|
||||
import WebSocketService from '@features/global/services/websocket-service';
|
||||
|
||||
const useWebSocket = () => {
|
||||
const wsRef = useRef<WebSocketService>();
|
||||
// having this will allow consumers to be updated with the io instance once connected
|
||||
const [connected, setConnected] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
wsRef.current = WebSocketFactory.get();
|
||||
|
||||
if (wsRef.current.isConnected()) {
|
||||
setConnected(() => true);
|
||||
}
|
||||
|
||||
wsRef.current.on(WebsocketEvents.Connected, () => {
|
||||
setConnected(() => true);
|
||||
});
|
||||
|
||||
wsRef.current.on(WebsocketEvents.Disconnected, () => {
|
||||
setConnected(() => false);
|
||||
});
|
||||
|
||||
return () => undefined;
|
||||
}, []);
|
||||
|
||||
return {
|
||||
websocket: wsRef.current,
|
||||
connected,
|
||||
};
|
||||
};
|
||||
|
||||
export default useWebSocket;
|
||||
@@ -1,44 +0,0 @@
|
||||
import Globals from './globals-tdrive-app-service';
|
||||
import Logger from '../framework/logger-service';
|
||||
import JWT from '../../auth/jwt-storage-service';
|
||||
import WebSocketService, { WebSocketOptions } from './websocket-service';
|
||||
|
||||
class WebSocketFactory {
|
||||
private logger: Logger.Logger;
|
||||
private instance!: WebSocketService;
|
||||
|
||||
constructor() {
|
||||
this.logger = Logger.getLogger('WebSocketFactory');
|
||||
}
|
||||
|
||||
get(): WebSocketService {
|
||||
if (!this.instance) {
|
||||
this.instance = new WebSocketService(this.getOptions());
|
||||
}
|
||||
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
private getOptions(): WebSocketOptions {
|
||||
return {
|
||||
url: Globals.environment.websocket_url,
|
||||
authenticateHandler: async () => {
|
||||
let token = JWT.getJWT();
|
||||
|
||||
if (JWT.isAccessExpired()) {
|
||||
try {
|
||||
token = (await JWT.renew()).value;
|
||||
} catch (err) {
|
||||
this.logger.error('Can not get a new JWT token for WS collection', err);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
token,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default new WebSocketFactory();
|
||||
@@ -1,261 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import io from 'socket.io-client';
|
||||
import { EventEmitter } from 'events';
|
||||
import Logger from '@features/global/framework/logger-service';
|
||||
import { WebsocketEvents, WebSocketListener, WebsocketRoomActions } from '../types/websocket-types';
|
||||
import { Maybe } from '@features/global/types/global-types';
|
||||
|
||||
export type WebSocketOptions = {
|
||||
url: string;
|
||||
authenticateHandler: () => Promise<any>;
|
||||
};
|
||||
|
||||
const CONNECT_TIMEOUT = 30000;
|
||||
|
||||
class WebSocketService extends EventEmitter {
|
||||
private logger: Logger.Logger;
|
||||
private lastConnection = 0;
|
||||
private wsListeners: {
|
||||
[path: string]: { [tag: string]: WebSocketListener };
|
||||
} = {};
|
||||
private socket: SocketIOClient.Socket | null = null;
|
||||
private connectTimeout?: ReturnType<typeof setTimeout>;
|
||||
|
||||
constructor(private options: WebSocketOptions) {
|
||||
super();
|
||||
this.logger = Logger.getLogger('WebsocketService');
|
||||
this.addEventListeners();
|
||||
}
|
||||
|
||||
private addEventListeners(): void {
|
||||
const reconnectWhenNeeded = () => {
|
||||
if (!this.isConnected()) {
|
||||
this.connect();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
reconnectWhenNeeded();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('focus', () => {
|
||||
reconnectWhenNeeded();
|
||||
});
|
||||
|
||||
setInterval(() => {
|
||||
if (new Date().getTime() - this.lastConnection > CONNECT_TIMEOUT) {
|
||||
this.lastConnection = new Date().getTime();
|
||||
reconnectWhenNeeded();
|
||||
}
|
||||
}, CONNECT_TIMEOUT);
|
||||
}
|
||||
|
||||
isConnected(): boolean {
|
||||
return this.socket?.connected || false;
|
||||
}
|
||||
|
||||
async connect(): Promise<boolean> {
|
||||
let connected: (value: boolean) => void;
|
||||
const promise = new Promise<boolean>(resolve => {
|
||||
connected = resolve;
|
||||
});
|
||||
|
||||
if (!this.options.url) {
|
||||
this.logger.info('Skipping connect to empty URL');
|
||||
return false;
|
||||
}
|
||||
|
||||
this.logger.debug('Connecting to websocket', this.options.url);
|
||||
|
||||
if (this.socket) {
|
||||
if (this.socket.connected) {
|
||||
this.logger.debug('Already connected to', this.options.url);
|
||||
return false;
|
||||
} else {
|
||||
this.socket?.close();
|
||||
this.socket = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.options.authenticateHandler) {
|
||||
this.logger.error('Cannot connect without an authentication method');
|
||||
return false;
|
||||
}
|
||||
|
||||
this.socket = io.connect(this.options.url || '', {
|
||||
path: '/socket',
|
||||
reconnectionDelayMax: 10000,
|
||||
reconnectionDelay: 2000,
|
||||
});
|
||||
|
||||
this.socket.on('disconnect', () => {
|
||||
this.logger.debug('Disconnected from websocket, socket.io will reconnect');
|
||||
});
|
||||
|
||||
this.socket.on('connect', async () => {
|
||||
this.logger.debug('Connected to websocket, authenticating...');
|
||||
if (this.connectTimeout) {
|
||||
clearTimeout(this.connectTimeout);
|
||||
}
|
||||
|
||||
this.socket
|
||||
?.emit('authenticate', (await this.options.authenticateHandler()) || {})
|
||||
.on('authenticated', () => {
|
||||
this.logger.debug('Authenticated');
|
||||
this.rejoinAll(true);
|
||||
connected(true);
|
||||
this.emit(WebsocketEvents.Connected, { url: this.options.url });
|
||||
})
|
||||
.on('unauthorized', (err: any) => {
|
||||
this.logger.warn('Websocket is not authorized', err);
|
||||
this.socket?.close();
|
||||
this.socket = null;
|
||||
//Retry and expect new jwt
|
||||
this.connectTimeout = setTimeout(async () => {
|
||||
const isConnected = await this.connect();
|
||||
if (isConnected) {
|
||||
connected(true);
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
this.socket?.on(WebsocketEvents.JoinSuccess, (event: any) => {
|
||||
this.logger.debug('Websocket join success', event.name);
|
||||
event.name && this.notify(event.name, WebsocketEvents.JoinSuccess, event);
|
||||
});
|
||||
|
||||
this.socket?.on(WebsocketEvents.JoinError, (event: any) => {
|
||||
this.logger.debug('Websocket join error', event.name);
|
||||
event.name && this.notify(event.name, WebsocketEvents.JoinError, event);
|
||||
});
|
||||
|
||||
this.socket?.on(WebsocketEvents.Resource, (event: any) => {
|
||||
this.logger.debug('Received resource on room', event.room, event);
|
||||
event.room && this.notify(event.room, WebsocketEvents.Resource, event);
|
||||
});
|
||||
|
||||
this.socket?.on(WebsocketEvents.Event, (event: any) => {
|
||||
this.logger.debug('New Websocket event', event.name);
|
||||
event.name && this.notify(event.name, WebsocketEvents.Event, event);
|
||||
});
|
||||
|
||||
this.socket?.on('disconnect', () => {
|
||||
this.emit(WebsocketEvents.Disconnected, { url: this.options.url });
|
||||
});
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
private rejoinAll(newlyConnected = false) {
|
||||
Object.keys(this.wsListeners).forEach(key => {
|
||||
Object.keys(this.wsListeners[key]).forEach(tag => {
|
||||
if (this.wsListeners[key][tag]) {
|
||||
newlyConnected && this.wsListeners[key][tag].callback(WebsocketEvents.Connected, {});
|
||||
this.join(
|
||||
key,
|
||||
this.wsListeners[key][tag].token,
|
||||
tag,
|
||||
this.wsListeners[key][tag].callback,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private notify(path: string, type: WebsocketEvents, event: any) {
|
||||
if (this.wsListeners[path]) {
|
||||
Object.values(this.wsListeners[path]).forEach(callback => callback.callback?.(type, event));
|
||||
}
|
||||
}
|
||||
|
||||
public getSocket(): SocketIOClient.Socket | null {
|
||||
return this.socket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join a room. callback will be called when a message is received on the given room
|
||||
*
|
||||
* @param path
|
||||
* @param tag
|
||||
* @param callback
|
||||
*/
|
||||
public join(
|
||||
path: string,
|
||||
token: string,
|
||||
tag: string,
|
||||
callback: (type: WebsocketEvents, event: any) => void,
|
||||
) {
|
||||
const name = path.replace(/\/$/, '');
|
||||
|
||||
this.logger.debug(`Join room with name='${name}' and tag='${tag}'`);
|
||||
|
||||
if (this.socket) {
|
||||
this.socket.emit(WebsocketRoomActions.Join, { name, token });
|
||||
}
|
||||
|
||||
this.wsListeners[name] = this.wsListeners[name] || {};
|
||||
this.wsListeners[name][tag] = { token, callback };
|
||||
}
|
||||
|
||||
/**
|
||||
* Leave a room
|
||||
*
|
||||
* @param path
|
||||
* @param tag
|
||||
*/
|
||||
public leave(path: string, tag: string) {
|
||||
const name = path.replace(/\/$/, '');
|
||||
|
||||
this.wsListeners[name] = this.wsListeners[name] || {};
|
||||
delete this.wsListeners[name][tag];
|
||||
|
||||
if (Object.keys(this.wsListeners[name]).length === 0) {
|
||||
if (this.socket) {
|
||||
this.logger.debug(`Leave room with name='${name}' and tag='${tag}'`);
|
||||
this.socket.emit(WebsocketRoomActions.Leave, { name });
|
||||
}
|
||||
delete this.wsListeners[name];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send data as {name: path, data} in the realtime:event topic.
|
||||
*
|
||||
* @param path
|
||||
* @param data
|
||||
*/
|
||||
public send<T>(path: string, token: string, data: T): void {
|
||||
const name = path.replace(/\/$/, '');
|
||||
this.logger.debug(`Send realtime:event with name='${name}'`);
|
||||
|
||||
if (this.socket) {
|
||||
this.socket.emit('realtime:event', { name, data, token });
|
||||
}
|
||||
}
|
||||
|
||||
public async get<Request, Response>(
|
||||
route: string,
|
||||
request: Request,
|
||||
callback?: (response: Response) => void,
|
||||
): Promise<Maybe<Response>> {
|
||||
this.logger.debug(`Get ${route}`);
|
||||
|
||||
return new Promise<Maybe<Response>>(resolve => {
|
||||
if (this.socket) {
|
||||
this.socket.emit(route, { data: request }, (response: { data: Response }) => {
|
||||
this.logger.trace('Got a socket ack');
|
||||
const result = response.data;
|
||||
callback && callback(result);
|
||||
resolve(result);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default WebSocketService;
|
||||
@@ -1,41 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
export type WebSocketResource = {
|
||||
room: string;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
export type RealtimeResources<T> = {
|
||||
resources: T[];
|
||||
websockets: WebSocketResource[];
|
||||
};
|
||||
|
||||
export type RealtimeApplicationEventAction = 'configure' | 'close_configure';
|
||||
export type RealtimeEventAction = 'saved' | 'updated' | 'deleted' | 'event';
|
||||
|
||||
export type RealtimeBaseAction = RealtimeEventAction | RealtimeApplicationEventAction;
|
||||
|
||||
export interface RealtimeBaseEvent {
|
||||
action: RealtimeEventAction;
|
||||
data?: any;
|
||||
}
|
||||
|
||||
export interface RealtimeResourceEvent<T> extends RealtimeBaseEvent {
|
||||
resource: T;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
export interface RealtimeEvent<T, U> extends RealtimeBaseEvent {
|
||||
room: string;
|
||||
path: string;
|
||||
type: U;
|
||||
resource: T;
|
||||
}
|
||||
|
||||
export interface RealtimeApplicationEvent {
|
||||
action: 'configure' | 'close_configure';
|
||||
connection_id: string;
|
||||
application: unknown;
|
||||
form: unknown;
|
||||
hidden_data: unknown;
|
||||
configurator_id: string;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import WebSocketFactory from '../services/websocket-factory-service';
|
||||
|
||||
export type WebsocketRoom = {
|
||||
room: string;
|
||||
token: string;
|
||||
};
|
||||
|
||||
export enum WebsocketRoomActions {
|
||||
Join = 'realtime:join',
|
||||
Leave = 'realtime:leave',
|
||||
}
|
||||
|
||||
export enum WebsocketEvents {
|
||||
Connected = 'connected',
|
||||
Connecting = 'connecting',
|
||||
Disconnected = 'disconnected',
|
||||
JoinSuccess = 'realtime:join:success',
|
||||
JoinError = 'realtime:join:error',
|
||||
Resource = 'realtime:resource',
|
||||
Event = 'realtime:event',
|
||||
}
|
||||
|
||||
export type WebSocketListener = {
|
||||
token: string;
|
||||
callback: <T>(type: WebsocketEvents, event: T) => void;
|
||||
};
|
||||
|
||||
export default WebSocketFactory;
|
||||
@@ -1,47 +0,0 @@
|
||||
import WebSocketService from '../../global/services/websocket-service';
|
||||
import Logger from '../../global/framework/logger-service';
|
||||
import UsersService from '@features/users/services/current-user-service';
|
||||
const logger = Logger.getLogger('OnlineUsersRealtimeAPI');
|
||||
|
||||
export type GetUserRequestType = string;
|
||||
export type GetUsersRequestType = Array<GetUserRequestType>;
|
||||
export type GetUserResponseType = [string, boolean];
|
||||
export type GetUsersResponseType = Array<GetUserResponseType>;
|
||||
export type RealtimeUpdateMessageType = GetUsersResponseType;
|
||||
|
||||
export type OnlineUserRealtimeAPIType = {
|
||||
getUserStatus: (id: GetUserRequestType) => Promise<GetUserResponseType>;
|
||||
getUsersStatus: (ids: GetUsersRequestType) => Promise<GetUsersResponseType>;
|
||||
setMyStatus: () => void;
|
||||
};
|
||||
|
||||
export const ONLINE_ROOM = (companyId: string) => `/users/online/${companyId}`;
|
||||
|
||||
export const OnlineUserRealtimeAPI = (websocket: WebSocketService): OnlineUserRealtimeAPIType => {
|
||||
const getUserStatus = (id: GetUserRequestType): Promise<GetUserResponseType> => {
|
||||
logger.debug(`Get user online status ${id}`);
|
||||
return getUsersStatus([id]).then(response => response?.[0] || [id, false]);
|
||||
};
|
||||
|
||||
const getUsersStatus = (ids: GetUsersRequestType = []): Promise<GetUsersResponseType> => {
|
||||
logger.debug(`Get users statuses ${ids.join(',')}`);
|
||||
return websocket
|
||||
.get<GetUsersRequestType, GetUsersResponseType>('online:get', ids)
|
||||
.then(result => result || []);
|
||||
};
|
||||
|
||||
const setMyStatus = (): void => {
|
||||
const id = UsersService.getCurrentUserId();
|
||||
logger.debug(`Set user online status ${id}`);
|
||||
|
||||
websocket
|
||||
.get<GetUsersRequestType, GetUsersResponseType>('online:set', [id])
|
||||
.then(result => result || []);
|
||||
};
|
||||
|
||||
return {
|
||||
getUserStatus,
|
||||
getUsersStatus,
|
||||
setMyStatus,
|
||||
};
|
||||
};
|
||||
@@ -3,7 +3,6 @@ import { CompanyType } from '@features/companies/types/company';
|
||||
import { UserPreferencesType, UserType } from '@features/users/types/user';
|
||||
import Api from '../../global/framework/api-service';
|
||||
import { TdriveService } from '../../global/framework/registry-decorator-service';
|
||||
import { WebsocketRoom } from '../../global/types/websocket-types';
|
||||
import WorkspaceAPIClient from '../../workspaces/api/workspace-api-client';
|
||||
import CurrentUser from '../../../deprecated/user/CurrentUser';
|
||||
import { setUserList } from '../hooks/use-user-list';
|
||||
@@ -23,13 +22,8 @@ type SearchUserApiResponse<T> = {
|
||||
@TdriveService('UserAPIClientService')
|
||||
class UserAPIClientService {
|
||||
private readonly prefixUrl: string = '/internal/services/users/v1';
|
||||
private realtime: Map<string, WebsocketRoom> = new Map();
|
||||
private logger = Logger.getLogger('UserAPIClientService');
|
||||
|
||||
websocket(userId: string): WebsocketRoom {
|
||||
return this.realtime.get(userId) || { room: '', token: '' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get users from their ID
|
||||
*
|
||||
@@ -101,7 +95,6 @@ class UserAPIClientService {
|
||||
companyId: string,
|
||||
options?: { bufferize?: boolean; callback?: (res: UserType[]) => void },
|
||||
): Promise<UserType[]> {
|
||||
|
||||
const res = await new Promise<UserType[]>(resolve =>
|
||||
Api.post(
|
||||
`/internal/services/users/v1/users/${companyId}/all`,
|
||||
@@ -135,7 +128,7 @@ class UserAPIClientService {
|
||||
}
|
||||
|
||||
async getCurrent(disableJWTAuthentication = false): Promise<UserType> {
|
||||
return Api.get<{ resource: UserType; websocket: WebsocketRoom }>(
|
||||
return Api.get<{ resource: UserType }>(
|
||||
'/internal/services/users/v1/users/me',
|
||||
undefined,
|
||||
false,
|
||||
@@ -144,7 +137,6 @@ class UserAPIClientService {
|
||||
if (!result?.resource?.id) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
result.resource.id && this.realtime.set(result.resource.id, result.websocket);
|
||||
return result.resource;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,29 +1,20 @@
|
||||
import Api from '../../global/framework/api-service';
|
||||
import { NotificationType } from '@features/users/types/notification-types';
|
||||
import { TdriveService } from '../../global/framework/registry-decorator-service';
|
||||
import { WebsocketRoom } from '@features/global/types/websocket-types';
|
||||
|
||||
@TdriveService('UserNotificationAPIClientService')
|
||||
class UserNotificationAPIClient {
|
||||
private realtime: WebsocketRoom = { room: '', token: '' };
|
||||
|
||||
websocket(): WebsocketRoom {
|
||||
return this.realtime;
|
||||
}
|
||||
|
||||
async getAllCompaniesBadges(): Promise<NotificationType[]> {
|
||||
const response = await Api.get<{ resources: NotificationType[]; websockets: WebsocketRoom[] }>(
|
||||
const response = await Api.get<{ resources: NotificationType[] }>(
|
||||
'/internal/services/notifications/v1/badges?limit=1000&websockets=1&all_companies=true',
|
||||
);
|
||||
if (response.websockets) this.realtime = response.websockets[0];
|
||||
return response.resources ? response.resources : [];
|
||||
}
|
||||
|
||||
async getCompanyBadges(companyId: string): Promise<NotificationType[]> {
|
||||
const response = await Api.get<{ resources: NotificationType[]; websockets: WebsocketRoom[] }>(
|
||||
const response = await Api.get<{ resources: NotificationType[] }>(
|
||||
'/internal/services/notifications/v1/badges?limit=1000&websockets=1&company_id=' + companyId,
|
||||
);
|
||||
if (response.websockets) this.realtime = response.websockets[0];
|
||||
return response.resources ? response.resources : [];
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import LoginService from '@features/auth/login-service';
|
||||
import UserAPIClient from '@features/users/api/user-api-client';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { CurrentUserState } from '../state/atoms/current-user';
|
||||
import { useRealtimeRoom } from '@features/global/hooks/use-realtime';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import { useSetUserList } from './use-user-list';
|
||||
import { getPublicLinkToken } from 'app/features/drive/api-client/api-client';
|
||||
@@ -46,23 +45,3 @@ export const useCurrentUser = () => {
|
||||
|
||||
return { user, refresh };
|
||||
};
|
||||
|
||||
export const useCurrentUserRealtime = () => {
|
||||
const { user, refresh } = useCurrentUser();
|
||||
const room = UserAPIClient.websocket(user?.id || '');
|
||||
|
||||
const timeout = useRef(0);
|
||||
|
||||
useRealtimeRoom<any>(room, 'useCurrentUser', async (action, resource) => {
|
||||
switch (resource._type) {
|
||||
case 'user':
|
||||
clearTimeout(timeout.current); //
|
||||
timeout.current = setTimeout(() => {
|
||||
refresh();
|
||||
}, 1000) as any;
|
||||
break;
|
||||
default:
|
||||
console.error('Unknown resource type');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { useGlobalEffect } from '@features/global/hooks/use-global-effect';
|
||||
import { useRealtimeRoom } from '@features/global/hooks/use-realtime';
|
||||
import useRouterCompany from '@features/router/hooks/use-router-company';
|
||||
import _ from 'lodash';
|
||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||
import userNotificationApiClient from '../api/user-notification-api-client';
|
||||
import {
|
||||
NotificationsBadgesState,
|
||||
NotificationsChannelBadgesSelector,
|
||||
NotificationsCompanyBadgesSelector,
|
||||
NotificationsOtherCompanyBadgesSelector,
|
||||
NotificationsWorkspaceBadgesSelector,
|
||||
} from '../state/atoms/notifications';
|
||||
import { NotificationType } from '../types/notification-types';
|
||||
import ElectronService from '@features/global/framework/electron-service';
|
||||
import windowState from '@features/global/utils/window';
|
||||
import RouterService, { ClientStateType } from '../../router/services/router-service';
|
||||
import { pushDesktopNotification } from '../services/push-desktop-notification';
|
||||
import { RouterState } from '@features/router/state/atoms/router';
|
||||
import { useCurrentUser } from '@features/users/hooks/use-current-user';
|
||||
|
||||
export let removeBadgesNow: (type: 'company' | 'workspace' | 'channel', id: string) => void = () =>
|
||||
undefined;
|
||||
|
||||
const executeRequest = () => {
|
||||
if ('Notification' in window && window.Notification.requestPermission) {
|
||||
const request = window.Notification.requestPermission();
|
||||
if (request && request.then) {
|
||||
request
|
||||
.then(() => undefined)
|
||||
.finally(() => {
|
||||
window.removeEventListener('click', executeRequest);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
window.addEventListener('click', executeRequest);
|
||||
executeRequest();
|
||||
|
||||
export const useNotifications = () => {
|
||||
const [badges, setBadges] = useRecoilState(NotificationsBadgesState);
|
||||
const companyId = useRouterCompany();
|
||||
|
||||
const addBadges = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(newBadges: NotificationType[]) => {
|
||||
const badges = snapshot.getLoadable(NotificationsBadgesState).getValue();
|
||||
const list = _.uniqBy([...badges, ...newBadges], 'id');
|
||||
setBadges(list);
|
||||
},
|
||||
[setBadges, badges],
|
||||
);
|
||||
|
||||
const removeBadges = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(removedBadges: NotificationType[]) => {
|
||||
const badges = snapshot.getLoadable(NotificationsBadgesState).getValue();
|
||||
const list = _.differenceBy(badges, removedBadges, 'id');
|
||||
setBadges(list);
|
||||
},
|
||||
[setBadges, badges],
|
||||
);
|
||||
|
||||
const removeObjectBadges = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(type: 'channel' | 'workspace' | 'company', id: string) => {
|
||||
const badges = snapshot.getLoadable(NotificationsBadgesState).getValue();
|
||||
const list = badges.filter(
|
||||
b =>
|
||||
(type === 'channel' && b.channel_id !== id) ||
|
||||
(type === 'workspace' && b.workspace_id !== id) ||
|
||||
(type === 'company' && b.company_id !== id),
|
||||
);
|
||||
setBadges(list);
|
||||
},
|
||||
[setBadges, badges],
|
||||
);
|
||||
removeBadgesNow = removeObjectBadges;
|
||||
|
||||
const setCompanyBadges = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(newBadges: NotificationType[], companyId: string) => {
|
||||
const badges = snapshot.getLoadable(NotificationsBadgesState).getValue();
|
||||
const list = _.uniqBy(
|
||||
[...badges.filter(b => b.company_id !== companyId), ...newBadges],
|
||||
'id',
|
||||
);
|
||||
setBadges(list);
|
||||
},
|
||||
[setBadges, badges],
|
||||
);
|
||||
|
||||
useGlobalEffect(
|
||||
'useCompanyNotifications',
|
||||
async () => {
|
||||
if (companyId) {
|
||||
const updatedBadges = await userNotificationApiClient.getCompanyBadges(companyId);
|
||||
const updatedOtherCompaniesBadges = await userNotificationApiClient.getAllCompaniesBadges();
|
||||
setCompanyBadges([...updatedOtherCompaniesBadges, ...updatedBadges], companyId);
|
||||
}
|
||||
},
|
||||
[companyId],
|
||||
);
|
||||
|
||||
useGlobalEffect(
|
||||
'useNotificationsSetWindowBadges',
|
||||
() => {
|
||||
let notifications = 0;
|
||||
const state = RouterService.getStateFromRoute();
|
||||
const ignore: any = [];
|
||||
for (const notification of badges) {
|
||||
if (
|
||||
ignore.indexOf(notification.company_id) >= 0 ||
|
||||
ignore.indexOf(notification.workspace_id) >= 0
|
||||
)
|
||||
return;
|
||||
|
||||
if (
|
||||
notification.company_id !== state.companyId ||
|
||||
(notification.workspace_id !== state.workspaceId &&
|
||||
notification.workspace_id !== 'direct')
|
||||
) {
|
||||
notifications++;
|
||||
ignore.push(notification.company_id);
|
||||
ignore.push(notification.workspace_id);
|
||||
} else {
|
||||
notifications++;
|
||||
}
|
||||
}
|
||||
|
||||
windowState.setPrefix(notifications);
|
||||
if (notifications > 0) {
|
||||
ElectronService.setBadge('' + notifications);
|
||||
} else {
|
||||
ElectronService.setBadge('');
|
||||
}
|
||||
},
|
||||
[badges],
|
||||
);
|
||||
|
||||
const { user } = useCurrentUser();
|
||||
const soundType =
|
||||
user?.preference?.notifications?.[0]?.preferences?.notification_sound || 'default';
|
||||
const realtimeEvent = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async (action: string, resource: any) => {
|
||||
const routerState = snapshot.getLoadable(RouterState).valueMaybe() as ClientStateType;
|
||||
|
||||
if (action === 'event' && resource._type === 'notification:desktop') {
|
||||
pushDesktopNotification({ ...resource, routerState }, soundType);
|
||||
userNotificationApiClient.acknowledge(resource);
|
||||
}
|
||||
if (action === 'saved') addBadges([resource]);
|
||||
if (action === 'deleted') removeBadges([resource]);
|
||||
},
|
||||
[addBadges, removeBadges],
|
||||
);
|
||||
const room = userNotificationApiClient.websocket();
|
||||
useRealtimeRoom<any>(room, 'useNotifications', realtimeEvent);
|
||||
|
||||
return {
|
||||
badges,
|
||||
};
|
||||
};
|
||||
|
||||
export const useCompanyNotifications = (id: string) => {
|
||||
const badges = useRecoilValue(NotificationsCompanyBadgesSelector(id));
|
||||
return {
|
||||
badges,
|
||||
};
|
||||
};
|
||||
|
||||
export const useOtherCompanyNotifications = (id: string) => {
|
||||
const badges = useRecoilValue(NotificationsOtherCompanyBadgesSelector(id));
|
||||
return {
|
||||
badges,
|
||||
};
|
||||
};
|
||||
|
||||
export const useWorkspaceNotifications = (id: string) => {
|
||||
const badges = useRecoilValue(NotificationsWorkspaceBadgesSelector(id));
|
||||
return {
|
||||
badges,
|
||||
};
|
||||
};
|
||||
|
||||
export const useChannelNotifications = (id: string) => {
|
||||
const badges = useRecoilValue(NotificationsChannelBadgesSelector(id));
|
||||
return {
|
||||
badges,
|
||||
};
|
||||
};
|
||||
@@ -1,95 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import Logger from '@features/global/framework/logger-service';
|
||||
import { useRealtimeRoom } from '@features/global/hooks/use-realtime';
|
||||
import { OnlineUsersState, OnlineUserType } from '../state/atoms/online-users';
|
||||
import useWebSocket from '../../global/hooks/use-websocket';
|
||||
import {
|
||||
OnlineUserRealtimeAPI,
|
||||
ONLINE_ROOM,
|
||||
RealtimeUpdateMessageType,
|
||||
} from '../../../features/users/api/online-user-realtime-api-client';
|
||||
import JWTStorage from '../../auth/jwt-storage-service';
|
||||
import useRouterCompany from '@features/router/hooks/use-router-company';
|
||||
|
||||
const logger = Logger.getLogger('useOnlineUsers');
|
||||
|
||||
const GET_INTERVAL = 30000; //30 seconds
|
||||
const SET_INTERVAL = 600000; //10 minutes
|
||||
let getIntervalId = setTimeout(() => {}, 0);
|
||||
let setIntervalId = setTimeout(() => {}, 0);
|
||||
|
||||
export const useOnlineUsers = (): void => {
|
||||
logger.trace('Running online hook');
|
||||
const { websocket } = useWebSocket();
|
||||
const companyId = useRouterCompany();
|
||||
const [onlineUsers, setOnlineUsersState] = useRecoilState(OnlineUsersState);
|
||||
|
||||
const updateOnline = useCallback((statuses: Array<[string, boolean]> = []): void => {
|
||||
logger.debug(`Update online status for ${statuses.length} users`, statuses);
|
||||
const lastSeen = Date.now();
|
||||
setOnlineUsersState(users => {
|
||||
if (!statuses.length) {
|
||||
return users;
|
||||
}
|
||||
|
||||
const previousStateMap = new Map<string, OnlineUserType>(users.map(u => [u.id!, u]));
|
||||
|
||||
for (const statusTuple of statuses) {
|
||||
previousStateMap.set(statusTuple[0], {
|
||||
id: statusTuple[0],
|
||||
connected: statusTuple[1],
|
||||
lastSeen: statusTuple[1] ? lastSeen : previousStateMap.get(statusTuple[0])?.lastSeen || 0,
|
||||
initialized: true,
|
||||
});
|
||||
}
|
||||
|
||||
return [...previousStateMap.entries()].map(entry => ({
|
||||
id: entry[0],
|
||||
connected: entry[1].connected,
|
||||
lastSeen: entry[1].lastSeen,
|
||||
initialized: true,
|
||||
}));
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (websocket) {
|
||||
// got some local users, ask if there are still online
|
||||
const api = OnlineUserRealtimeAPI(websocket);
|
||||
clearInterval(getIntervalId);
|
||||
getIntervalId = setInterval(async () => {
|
||||
const users = onlineUsers.map(u => u.id).filter(<T>(n?: T): n is T => Boolean(n));
|
||||
const status = await api.getUsersStatus(users);
|
||||
updateOnline(status);
|
||||
}, GET_INTERVAL);
|
||||
|
||||
clearInterval(setIntervalId);
|
||||
setIntervalId = setInterval(async () => await api.setMyStatus(), SET_INTERVAL);
|
||||
}
|
||||
|
||||
return () => {
|
||||
getIntervalId && clearInterval(getIntervalId);
|
||||
setIntervalId && clearInterval(setIntervalId);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [websocket, onlineUsers]);
|
||||
|
||||
// listen to room events in which online events are pushed
|
||||
useRealtimeRoom<RealtimeUpdateMessageType>(
|
||||
{ room: ONLINE_ROOM(companyId), token: JWTStorage.getJWT() },
|
||||
'useOnlineUsers' + companyId,
|
||||
(action, resource: any) => {
|
||||
delete resource['_type'];
|
||||
resource = Object.values(resource?.online || resource);
|
||||
if (action === 'event' && resource?.length) {
|
||||
updateOnline(resource.map((u: any) => [u.user_id, u.is_online]));
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useGlobalEffect } from '@features/global/hooks/use-global-effect';
|
||||
import { useRealtimeRoom } from '@features/global/hooks/use-realtime';
|
||||
import { LoadingState } from '@features/global/state/atoms/Loading';
|
||||
import { UserType } from '@features/users/types/user';
|
||||
import UserAPIClient from '../api/user-api-client';
|
||||
@@ -34,10 +33,5 @@ export const useUser = (userId: string): UserType | undefined => {
|
||||
[],
|
||||
);
|
||||
|
||||
const room = UserAPIClient.websocket(userId);
|
||||
useRealtimeRoom<UserType>(room, 'useUser', () => {
|
||||
refresh();
|
||||
});
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
import React from 'react';
|
||||
import { useOnlineUsers } from '@features/users/hooks/use-online-users';
|
||||
|
||||
/**
|
||||
* This hook will be global to application
|
||||
*/
|
||||
export default (): JSX.Element => {
|
||||
useOnlineUsers();
|
||||
return <></>;
|
||||
};
|
||||
|
||||
@@ -2,7 +2,6 @@ import Api from '../../global/framework/api-service';
|
||||
import { CompanyType } from '@features/companies/types/company';
|
||||
import { WorkspaceType } from '@features/workspaces/types/workspace';
|
||||
import { TdriveService } from '../../global/framework/registry-decorator-service';
|
||||
import { WebsocketRoom } from '../../global/types/websocket-types';
|
||||
import _ from 'lodash';
|
||||
|
||||
const PREFIX = '/internal/services/workspaces/v1/companies';
|
||||
@@ -23,22 +22,15 @@ type UpdateWorkspaceInviteDomainResponse = {
|
||||
|
||||
@TdriveService('WorkspaceAPIClientService')
|
||||
class WorkspaceAPIClient {
|
||||
private realtime: Map<string, WebsocketRoom[]> = new Map();
|
||||
|
||||
websockets(companyId: string): WebsocketRoom[] {
|
||||
return this.realtime.get(companyId) || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all workspaces for a company
|
||||
*
|
||||
* @param companyId
|
||||
*/
|
||||
async list(companyId: string): Promise<WorkspaceType[]> {
|
||||
return Api.get<{ resources: WorkspaceType[]; websockets: WebsocketRoom[] }>(
|
||||
return Api.get<{ resources: WorkspaceType[] }>(
|
||||
`${PREFIX}/${companyId}/workspaces?websockets=1`,
|
||||
).then(result => {
|
||||
this.realtime.set(companyId, result.websockets);
|
||||
return result.resources && result.resources.length ? result.resources : [];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,15 +2,10 @@ import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { WorkspaceType } from '@features/workspaces/types/workspace';
|
||||
import { WorkspaceListStateFamily } from '../state/workspace-list';
|
||||
import Collections from '@deprecated/CollectionsV1/Collections/Collections';
|
||||
import { useRealtimeRoom } from '@features/global/hooks/use-realtime';
|
||||
import useRouterWorkspace from '@features/router/hooks/use-router-workspace';
|
||||
import RouterService from '@features/router/services/router-service';
|
||||
import _ from 'lodash';
|
||||
import WorkspacesService from '@deprecated/workspaces/workspaces.jsx';
|
||||
import AccessRightsService, {
|
||||
RightsOrNone,
|
||||
} from '@features/workspace-members/services/workspace-members-access-rights-service';
|
||||
import LocalStorage from '@features/global/framework/local-storage-service';
|
||||
import useRouterCompany from '@features/router/hooks/use-router-company';
|
||||
import WorkspaceAPIClient from '@features/workspaces/api/workspace-api-client';
|
||||
@@ -47,22 +42,6 @@ export const useWorkspacesCommons = (companyId = '') => {
|
||||
return { workspaces, loading, refresh };
|
||||
};
|
||||
|
||||
export function useWorkspaces(companyId = '') {
|
||||
const { workspaces, loading, refresh } = useWorkspacesCommons(companyId);
|
||||
|
||||
useRealtimeRoom<WorkspaceType>(
|
||||
WorkspaceAPIClient.websockets(companyId)[0],
|
||||
'useWorkspaces',
|
||||
action => {
|
||||
if (action === 'saved') {
|
||||
refresh();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return { workspaces, loading, refresh };
|
||||
}
|
||||
|
||||
export function useWorkspaceLoader(companyId: string) {
|
||||
const loading = useRecoilValue(LoadingState(`workspaces-${companyId}`));
|
||||
return { loading };
|
||||
|
||||
@@ -6,7 +6,6 @@ import { getFilesTree } from '@components/uploads/file-tree-utils';
|
||||
import UploadZone from '@components/uploads/upload-zone';
|
||||
import { setTdriveTabToken } from '@features/drive/api-client/api-client';
|
||||
import { useDriveItem } from '@features/drive/hooks/use-drive-item';
|
||||
import { DriveRealtimeObject } from '@features/drive/hooks/use-drive-realtime';
|
||||
import { useDriveUpload } from '@features/drive/hooks/use-drive-upload';
|
||||
import { DriveItemSelectedList } from '@features/drive/state/store';
|
||||
import { formatBytes } from '@features/drive/utils';
|
||||
@@ -257,7 +256,6 @@ export default memo(
|
||||
onDrop={handleDrop}
|
||||
|
||||
>
|
||||
<DriveRealtimeObject id={parentId} key={parentId} />
|
||||
{role == "admin" && <UsersModal />}
|
||||
<VersionsModal />
|
||||
<AccessModal />
|
||||
|
||||
@@ -9,7 +9,7 @@ import ModalComponent from '@components/modal/modal-component';
|
||||
import PopupService from '@deprecated/popupManager/popupManager.js';
|
||||
import useUsetiful from '@features/global/hooks/use-usetiful';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import { useCurrentUser, useCurrentUserRealtime } from '@features/users/hooks/use-current-user';
|
||||
import { useCurrentUser } from '@features/users/hooks/use-current-user';
|
||||
import UserContext from '@features/users/state/integration/user-context';
|
||||
import Viewer from '@views/client/viewer/viewer';
|
||||
import ConnectionIndicator from 'components/connection-indicator/connection-indicator';
|
||||
@@ -26,7 +26,6 @@ import SideBar from './side-bar';
|
||||
export default React.memo((): JSX.Element => {
|
||||
const [menuIsOpen, setMenuIsOpen] = useState(false);
|
||||
const { user } = useCurrentUser();
|
||||
useCurrentUserRealtime();
|
||||
const { FeatureToggles, activeFeatureNames } = useFeatureToggles();
|
||||
|
||||
useUsetiful();
|
||||
|
||||
Reference in New Issue
Block a user