mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-11 03:41:12 +00:00
listen notification
optimize code Update user-api.js optimize code optimize code Update lib-content-view.js Update lib-content-view.js remove-userless-code update settings optimize cur code add max number of reconnections
This commit is contained in:
@@ -32,7 +32,7 @@ import DirColumnView from '../../components/dir-view-mode/dir-column-view';
|
|||||||
import SelectedDirentsToolbar from '../../components/toolbar/selected-dirents-toolbar';
|
import SelectedDirentsToolbar from '../../components/toolbar/selected-dirents-toolbar';
|
||||||
import MetadataPathToolbar from '../../components/toolbar/metadata-path-toolbar';
|
import MetadataPathToolbar from '../../components/toolbar/metadata-path-toolbar';
|
||||||
import { eventBus } from '../../components/common/event-bus';
|
import { eventBus } from '../../components/common/event-bus';
|
||||||
|
import WebSocketClient from '../../utils/websocket-service';
|
||||||
import '../../css/lib-content-view.css';
|
import '../../css/lib-content-view.css';
|
||||||
|
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
@@ -49,7 +49,6 @@ class LibContentView extends React.Component {
|
|||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
let isTreePanelShown = true;
|
let isTreePanelShown = true;
|
||||||
const storedTreePanelState = localStorage.getItem(TREE_PANEL_STATE_KEY);
|
const storedTreePanelState = localStorage.getItem(TREE_PANEL_STATE_KEY);
|
||||||
if (storedTreePanelState != undefined) {
|
if (storedTreePanelState != undefined) {
|
||||||
@@ -59,6 +58,7 @@ class LibContentView extends React.Component {
|
|||||||
const storedDirentDetailShowState = localStorage.getItem(DIRENT_DETAIL_SHOW_KEY);
|
const storedDirentDetailShowState = localStorage.getItem(DIRENT_DETAIL_SHOW_KEY);
|
||||||
const isDirentDetailShow = storedDirentDetailShowState === 'true';
|
const isDirentDetailShow = storedDirentDetailShowState === 'true';
|
||||||
|
|
||||||
|
this.socket = new WebSocketClient(this.onMessageCallback, this.props.repoID);
|
||||||
this.state = {
|
this.state = {
|
||||||
currentMode: cookie.load('seafile_view_mode') || LIST_MODE,
|
currentMode: cookie.load('seafile_view_mode') || LIST_MODE,
|
||||||
isTreePanelShown: isTreePanelShown, // display the 'dirent tree' side panel
|
isTreePanelShown: isTreePanelShown, // display the 'dirent tree' side panel
|
||||||
@@ -161,6 +161,38 @@ class LibContentView extends React.Component {
|
|||||||
this.calculatePara(this.props);
|
this.calculatePara(this.props);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMessageCallback = (data) => {
|
||||||
|
if (data.type === 'file-lock-changed') {
|
||||||
|
const currentUrl = window.location.href;
|
||||||
|
const parsedUrl = new URL(currentUrl);
|
||||||
|
const pathParts = parsedUrl.pathname.split('/').filter(part => part.length > 0);
|
||||||
|
const dirRouter = decodeURIComponent(pathParts.slice(3).join('/'));
|
||||||
|
let notiUrlIndex = '';
|
||||||
|
if (data.content.path.includes('/')) {
|
||||||
|
notiUrlIndex = data.content.path.lastIndexOf('/');
|
||||||
|
}
|
||||||
|
const notifRouter = data.content.path.slice(0, notiUrlIndex);
|
||||||
|
if (dirRouter === notifRouter) {
|
||||||
|
const dirent = { name: data.content.path.split('/').pop() };
|
||||||
|
if (data.content.change_event === 'locked') {
|
||||||
|
if (data.content.expire === -1) {
|
||||||
|
this.updateDirent(dirent, 'is_freezed', true);
|
||||||
|
} else {
|
||||||
|
this.updateDirent(dirent, 'is_freezed', false);
|
||||||
|
}
|
||||||
|
this.updateDirent(dirent, 'is_locked', true);
|
||||||
|
this.updateDirent(dirent, 'locked_by_me', true);
|
||||||
|
let lockName = data.content.lock_user.split('@');
|
||||||
|
this.updateDirent(dirent, 'lock_owner_name', lockName[0]);
|
||||||
|
} else if (data.content.change_event === 'unlocked') {
|
||||||
|
this.updateDirent(dirent, 'is_locked', false);
|
||||||
|
this.updateDirent(dirent, 'locked_by_me', false);
|
||||||
|
this.updateDirent(dirent, 'lock_owner_name', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||||
if (nextProps.repoID !== this.props.repoID) {
|
if (nextProps.repoID !== this.props.repoID) {
|
||||||
this.setState({ path: '/', viewId: '', tagID: '', currentMode: cookie.load('seafile_view_mode') || LIST_MODE }, () => {
|
this.setState({ path: '/', viewId: '', tagID: '', currentMode: cookie.load('seafile_view_mode') || LIST_MODE }, () => {
|
||||||
@@ -280,6 +312,7 @@ class LibContentView extends React.Component {
|
|||||||
isLibView: false,
|
isLibView: false,
|
||||||
currentRepoInfo: null,
|
currentRepoInfo: null,
|
||||||
});
|
});
|
||||||
|
this.socket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
@@ -405,6 +438,7 @@ class LibContentView extends React.Component {
|
|||||||
|
|
||||||
// load data
|
// load data
|
||||||
loadDirData = (path) => {
|
loadDirData = (path) => {
|
||||||
|
|
||||||
// list used FileTags
|
// list used FileTags
|
||||||
this.updateUsedRepoTags();
|
this.updateUsedRepoTags();
|
||||||
|
|
||||||
|
@@ -31,6 +31,7 @@ export const fileServerRoot = window.app.config.fileServerRoot;
|
|||||||
export const useGoFileserver = window.app.config.useGoFileserver;
|
export const useGoFileserver = window.app.config.useGoFileserver;
|
||||||
export const seafileVersion = window.app.config.seafileVersion;
|
export const seafileVersion = window.app.config.seafileVersion;
|
||||||
export const serviceURL = window.app.config.serviceURL;
|
export const serviceURL = window.app.config.serviceURL;
|
||||||
|
export const notificationServerUrl = window.app.config.notificationServerUrl;
|
||||||
export const appAvatarURL = window.app.config.avatarURL;
|
export const appAvatarURL = window.app.config.avatarURL;
|
||||||
export const faviconPath = window.app.config.faviconPath;
|
export const faviconPath = window.app.config.faviconPath;
|
||||||
export const loginBGPath = window.app.config.loginBGPath;
|
export const loginBGPath = window.app.config.loginBGPath;
|
||||||
|
@@ -75,6 +75,12 @@ class UserAPI {
|
|||||||
form.append('suite_id', suiteID);
|
form.append('suite_id', suiteID);
|
||||||
return this.req.put(url, form);
|
return this.req.put(url, form);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNotificationToken(repoID) {
|
||||||
|
const url = this.server + '/api/v2.1/repos/' + repoID + '/repo-notification-jwt-token/';
|
||||||
|
return this.req.get(url);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let userAPI = new UserAPI();
|
let userAPI = new UserAPI();
|
||||||
|
116
frontend/src/utils/websocket-service.js
Normal file
116
frontend/src/utils/websocket-service.js
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
import { userAPI } from './user-api';
|
||||||
|
import { notificationServerUrl } from './constants';
|
||||||
|
|
||||||
|
|
||||||
|
class WebSocketClient {
|
||||||
|
constructor(onMessageCallback, repoId) {
|
||||||
|
|
||||||
|
this.url = notificationServerUrl; // WebSocket address;
|
||||||
|
this.repoId = repoId;
|
||||||
|
this.socket = null;
|
||||||
|
this.shouldReconnect = true;
|
||||||
|
this.reconnectAttempts = 0;
|
||||||
|
this.maxReconnectAttempts = 5;
|
||||||
|
this.onMessageCallback = onMessageCallback;
|
||||||
|
if (notificationServerUrl !== '') {
|
||||||
|
this.connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async connect() {
|
||||||
|
this.socket = new WebSocket(this.url);
|
||||||
|
|
||||||
|
this.socket.onopen = async () => {
|
||||||
|
const msg = await this.formatSubscriptionMsg();
|
||||||
|
this.socket.send(JSON.stringify(msg));
|
||||||
|
};
|
||||||
|
|
||||||
|
// listen message from WebSocket server
|
||||||
|
this.socket.onmessage = async (event) => {
|
||||||
|
const parsedData = JSON.parse(event.data);
|
||||||
|
// jwt-expire reconnect
|
||||||
|
if (parsedData.type === 'jwt-expired') {
|
||||||
|
const msg = await this.formatSubscriptionMsg();
|
||||||
|
this.socket.send(JSON.stringify(msg));
|
||||||
|
} else {
|
||||||
|
this.onMessageCallback(parsedData);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.socket.onerror = (error) => {
|
||||||
|
return error;
|
||||||
|
};
|
||||||
|
|
||||||
|
// reconnect WebSocket
|
||||||
|
this.socket.onclose = () => {
|
||||||
|
if (this.shouldReconnect) {
|
||||||
|
this.reconnect();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRepoJwtToken() {
|
||||||
|
const response = await userAPI.getNotificationToken(this.repoId).then(res => {
|
||||||
|
return res.data;
|
||||||
|
}).catch(err => {
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
return response.token;
|
||||||
|
}
|
||||||
|
|
||||||
|
async formatSubscriptionMsg() {
|
||||||
|
const repoToken = await this.getRepoJwtToken();
|
||||||
|
const jsonData = {
|
||||||
|
type: 'subscribe',
|
||||||
|
content: {
|
||||||
|
repos: [
|
||||||
|
{
|
||||||
|
id: this.repoId,
|
||||||
|
jwt_token: repoToken,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return jsonData;
|
||||||
|
}
|
||||||
|
|
||||||
|
formatUnSubscriptionMsg() {
|
||||||
|
const jsonData = {
|
||||||
|
type: 'unsubscribe',
|
||||||
|
content: {
|
||||||
|
repos: [
|
||||||
|
{
|
||||||
|
id: this.repoId
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return jsonData;
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.shouldReconnect = false;
|
||||||
|
if (this.socket) {
|
||||||
|
if (this.socket.readyState === WebSocket.OPEN) {
|
||||||
|
const msg = this.formatUnSubscriptionMsg();
|
||||||
|
this.socket.send(JSON.stringify(msg));
|
||||||
|
}
|
||||||
|
this.socket.close();
|
||||||
|
this.socket = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
reconnect() {
|
||||||
|
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
|
||||||
|
setTimeout(() => {
|
||||||
|
this.reconnectAttempts++;
|
||||||
|
this.connect();
|
||||||
|
}, delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WebSocketClient;
|
@@ -182,6 +182,7 @@ def base(request):
|
|||||||
'enable_seafile_ai': ENABLE_SEAFILE_AI,
|
'enable_seafile_ai': ENABLE_SEAFILE_AI,
|
||||||
'enable_whiteboard': ENABLE_WHITEBOARD,
|
'enable_whiteboard': ENABLE_WHITEBOARD,
|
||||||
'enable_excalidraw': ENABLE_EXCALIDRAW,
|
'enable_excalidraw': ENABLE_EXCALIDRAW,
|
||||||
|
'notification_server_url': os.environ.get('NOTIFICATION_SERVER_URL', ''),
|
||||||
}
|
}
|
||||||
|
|
||||||
if request.user.is_staff:
|
if request.user.is_staff:
|
||||||
|
@@ -941,7 +941,6 @@ SEND_EMAIL_ON_ADDING_SYSTEM_MEMBER = True # Whether to send email when a system
|
|||||||
SEND_EMAIL_ON_RESETTING_USER_PASSWD = True # Whether to send email when a system staff resetting user's password.
|
SEND_EMAIL_ON_RESETTING_USER_PASSWD = True # Whether to send email when a system staff resetting user's password.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##########################
|
##########################
|
||||||
# Settings for seadoc #
|
# Settings for seadoc #
|
||||||
##########################
|
##########################
|
||||||
|
@@ -58,6 +58,7 @@
|
|||||||
cloudMode: {% if cloud_mode %} true {% else %} false {% endif %},
|
cloudMode: {% if cloud_mode %} true {% else %} false {% endif %},
|
||||||
isOrgContext: {% if org is not None %} true {% else %} false {% endif %},
|
isOrgContext: {% if org is not None %} true {% else %} false {% endif %},
|
||||||
enableSeafileAI: {% if enable_seafile_ai %} true {% else %} false {% endif %},
|
enableSeafileAI: {% if enable_seafile_ai %} true {% else %} false {% endif %},
|
||||||
|
notificationServerUrl: '{{ notification_server_url }}'
|
||||||
},
|
},
|
||||||
pageOptions: {
|
pageOptions: {
|
||||||
csrfToken: "{{ csrf_token }}",
|
csrfToken: "{{ csrf_token }}",
|
||||||
|
Reference in New Issue
Block a user