Merge pull request #1106 from ONLYOFFICE/feature/socket-newtag

Feature/socket newtag
This commit is contained in:
Ilya Oleshko 2022-12-07 18:02:39 +03:00 committed by GitHub
commit 0d814ae191
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 413 additions and 546 deletions

View File

@ -26,370 +26,41 @@
namespace ASC.Core.Notify.Socket;
[Scope]
public class ConfigureSocketServiceClient : IConfigureNamedOptions<SocketServiceClient>
{
internal readonly TenantManager _tenantManager;
internal readonly CoreSettings _coreSettings;
internal readonly MachinePseudoKeys _machinePseudoKeys;
internal readonly IConfiguration _configuration;
internal readonly ILogger<SocketServiceClient> logger;
internal readonly IHttpClientFactory _clientFactory;
public ConfigureSocketServiceClient(
TenantManager tenantManager,
CoreSettings coreSettings,
MachinePseudoKeys machinePseudoKeys,
IConfiguration configuration,
ILogger<SocketServiceClient> logger,
IHttpClientFactory clientFactory)
{
_tenantManager = tenantManager;
_coreSettings = coreSettings;
_machinePseudoKeys = machinePseudoKeys;
_configuration = configuration;
this.logger = logger;
_clientFactory = clientFactory;
}
public void Configure(string name, SocketServiceClient options)
{
options._logger = logger;
options._hub = name.Trim('/');
options._tenantManager = _tenantManager;
options._coreSettings = _coreSettings;
options._clientFactory = _clientFactory;
options._sKey = _machinePseudoKeys.GetMachineConstant();
options._url = _configuration["web:hub:internal"];
options.EnableSocket = !string.IsNullOrEmpty(options._url);
try
{
var replaceSetting = _configuration["jabber:replace-domain"];
if (!string.IsNullOrEmpty(replaceSetting))
{
options._jabberReplaceDomain = true;
var q =
replaceSetting.Split(new[] { "->" }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.Trim().ToLowerInvariant())
.ToList();
options._jabberReplaceFromDomain = q.ElementAt(0);
options._jabberReplaceToDomain = q.ElementAt(1);
}
}
catch (Exception) { }
}
public void Configure(SocketServiceClient options)
{
Configure("default", options);
}
}
[Scope(typeof(ConfigureSocketServiceClient))]
[Singletone]
public class SocketServiceClient
{
private static readonly TimeSpan _timeout = TimeSpan.FromSeconds(1);
internal ILogger<SocketServiceClient> _logger;
private static DateTime _lastErrorTime;
public bool EnableSocket { get; set; }
internal byte[] _sKey;
internal string _url;
internal bool _jabberReplaceDomain;
internal string _jabberReplaceFromDomain;
internal string _jabberReplaceToDomain;
private readonly TimeSpan _timeout = TimeSpan.FromSeconds(1);
private DateTime _lastErrorTime;
internal string _hub;
private readonly IHttpClientFactory _clientFactory;
private readonly ILogger<SocketServiceClient> _logger;
private readonly bool _enableSocket;
private readonly byte[] _sKey;
private readonly string _url;
internal TenantManager _tenantManager;
internal CoreSettings _coreSettings;
internal IHttpClientFactory _clientFactory;
public virtual string Hub { get => "default"; }
public SocketServiceClient() { }
public void SendMessage(string callerUserName, string calleeUserName, string messageText, int tenantId,
string domain)
public SocketServiceClient(
ILogger<SocketServiceClient> logger,
IHttpClientFactory clientFactory,
MachinePseudoKeys mashinePseudoKeys,
IConfiguration configuration)
{
try
{
domain = ReplaceDomain(domain);
var tenant = tenantId == -1
? _tenantManager.GetTenant(domain)
: _tenantManager.GetTenant(tenantId);
var isTenantUser = callerUserName.Length == 0;
var message = new MessageClass
{
UserName = isTenantUser ? tenant.GetTenantDomain(_coreSettings) : callerUserName,
Text = messageText
};
MakeRequest("send", new { tenantId = tenant.Id, callerUserName, calleeUserName, message, isTenantUser });
}
catch (Exception error)
{
ProcessError(error);
}
_logger = logger;
_clientFactory = clientFactory;
_sKey = mashinePseudoKeys.GetMachineConstant();
_url = configuration["web:hub:internal"];
_enableSocket = !string.IsNullOrEmpty(_url);
}
public void SendInvite(string chatRoomName, string calleeUserName, string domain)
{
try
{
domain = ReplaceDomain(domain);
var tenant = _tenantManager.GetTenant(domain);
var message = new MessageClass
{
UserName = tenant.GetTenantDomain(_coreSettings),
Text = chatRoomName
};
MakeRequest("sendInvite", new { tenantId = tenant.Id, calleeUserName, message });
}
catch (Exception error)
{
ProcessError(error);
}
}
public void SendState(string from, byte state, int tenantId, string domain)
{
try
{
domain = ReplaceDomain(domain);
if (tenantId == -1)
{
tenantId = _tenantManager.GetTenant(domain).Id;
}
MakeRequest("setState", new { tenantId, from, state });
}
catch (Exception error)
{
ProcessError(error);
}
}
public void SendOfflineMessages(string callerUserName, List<string> users, int tenantId)
{
try
{
MakeRequest("sendOfflineMessages", new { tenantId, callerUserName, users });
}
catch (Exception error)
{
ProcessError(error);
}
}
public void SendUnreadCounts(Dictionary<string, int> unreadCounts, string domain)
{
try
{
domain = ReplaceDomain(domain);
var tenant = _tenantManager.GetTenant(domain);
MakeRequest("sendUnreadCounts", new { tenantId = tenant.Id, unreadCounts });
}
catch (Exception error)
{
ProcessError(error);
}
}
public void SendUnreadUsers(Dictionary<int, Dictionary<Guid, int>> unreadUsers)
{
try
{
MakeRequest("sendUnreadUsers", unreadUsers);
}
catch (Exception error)
{
ProcessError(error);
}
}
public void SendUnreadUser(int tenant, string userId, int count)
{
try
{
MakeRequest("updateFolders", new { tenant, userId, count });
}
catch (Exception error)
{
ProcessError(error);
}
}
public void SendMailNotification(int tenant, string userId, MailNotificationState state)
{
try
{
MakeRequest("sendMailNotification", new { tenant, userId, state });
}
catch (Exception error)
{
ProcessError(error);
}
}
public void EnqueueCall(string numberId, string callId, string agent)
{
try
{
MakeRequest("enqueue", new { numberId, callId, agent });
}
catch (Exception error)
{
ProcessError(error);
}
}
public void IncomingCall(string callId, string agent)
{
try
{
MakeRequest("incoming", new { callId, agent });
}
catch (Exception error)
{
ProcessError(error);
}
}
public void MissCall(string numberId, string callId, string agent)
{
try
{
MakeRequest("miss", new { numberId, callId, agent });
}
catch (Exception error)
{
ProcessError(error);
}
}
public void Reload(string numberId, string agentId = null)
{
try
{
var numberRoom = _tenantManager.GetCurrentTenant().Id + numberId;
MakeRequest("reload", new { numberRoom, agentId });
}
catch (Exception error)
{
ProcessError(error);
}
}
public void StartEdit<T>(T fileId, string room)
{
try
{
MakeRequest("start-edit", new { room, fileId });
}
catch (Exception error)
{
ProcessError(error);
}
}
public void StopEdit<T>(T fileId, string room)
{
try
{
MakeRequest("stop-edit", new { room, fileId });
}
catch (Exception error)
{
ProcessError(error);
}
}
public void CreateFile<T>(T fileId, string room, string data)
{
try
{
MakeRequest("create-file", new { room, fileId, data });
}
catch (Exception error)
{
ProcessError(error);
}
}
public void UpdateFile<T>(T fileId, string room, string data)
{
try
{
MakeRequest("update-file", new { room, fileId, data });
}
catch (Exception error)
{
ProcessError(error);
}
}
public void DeleteFile<T>(T fileId, string room)
{
try
{
MakeRequest("delete-file", new { room, fileId });
}
catch (Exception error)
{
ProcessError(error);
}
}
public T GetAgent<T>(string numberId, List<Guid> contactsResponsibles)
{
try
{
return MakeRequest<T>("GetAgent", new { numberId, contactsResponsibles });
}
catch (Exception error)
{
ProcessError(error);
}
return default;
}
private string ReplaceDomain(string domain)
{
if (_jabberReplaceDomain && domain.EndsWith(_jabberReplaceFromDomain))
{
var place = domain.LastIndexOf(_jabberReplaceFromDomain);
if (place >= 0)
{
return domain.Remove(place, _jabberReplaceFromDomain.Length).Insert(place, _jabberReplaceToDomain);
}
}
return domain;
}
private void ProcessError(Exception e)
{
_logger.ErrorService(e);
if (e is HttpRequestException)
{
_lastErrorTime = DateTime.Now;
}
}
private string MakeRequest(string method, object data)
public async Task<string> MakeRequest(string method, object data)
{
if (!IsAvailable())
{
return string.Empty;
}
try
{
var request = new HttpRequestMessage();
request.Headers.Add("Authorization", CreateAuthToken());
request.Method = HttpMethod.Post;
@ -402,32 +73,45 @@ public class SocketServiceClient
var httpClient = _clientFactory.CreateClient();
using (var response = httpClient.Send(request))
using (var stream = response.Content.ReadAsStream())
//async
using (var response = await httpClient.SendAsync(request))
using (var stream = await response.Content.ReadAsStreamAsync())
using (var streamReader = new StreamReader(stream))
{
return streamReader.ReadToEnd();
return await streamReader.ReadToEndAsync();
}
}
catch (Exception e)
{
_logger.ErrorService(e);
if (e is HttpRequestException)
{
_lastErrorTime = DateTime.Now;
}
}
private T MakeRequest<T>(string method, object data)
return null;
}
public async Task<T> MakeRequest<T>(string method, object data)
{
var resultMakeRequest = MakeRequest(method, data);
var resultMakeRequest = await MakeRequest(method, data);
return JsonConvert.DeserializeObject<T>(resultMakeRequest);
}
private bool IsAvailable()
{
return EnableSocket && _lastErrorTime + _timeout < DateTime.Now;
return _enableSocket && _lastErrorTime + _timeout < DateTime.Now;
}
private string GetMethod(string method)
{
return $"{_url.TrimEnd('/')}/controller/{_hub}/{method}";
return $"{_url.TrimEnd('/')}/controller/{Hub}/{method}";
}
public string CreateAuthToken(string pkey = "socketio")
private string CreateAuthToken(string pkey = "socketio")
{
using var hasher = new HMACSHA1(_sKey);
var now = DateTime.UtcNow.ToString("yyyyMMddHHmmss");

View File

@ -26,5 +26,15 @@
res.end();
});
router.post("/markasnew-file", (req, res) => {
files.markAsNewFile(req.body);
res.end();
});
router.post("/markasnew-folder", (req, res) => {
files.markAsNewFolder(req.body);
res.end();
});
return router;
};

View File

@ -55,32 +55,12 @@
);
});
socket.on("subscribe", (roomParts) => {
if (!roomParts) return;
if (Array.isArray(roomParts)) {
const rooms = roomParts.map((p) => getRoom(p));
logger.info(`client ${socket.id} join rooms [${rooms.join(",")}]`);
socket.join(rooms);
} else {
const room = getRoom(roomParts);
logger.info(`client ${socket.id} join room ${room}`);
socket.join(room);
}
socket.on("subscribe", ({roomParts, individual}) => {
changeSubscription(roomParts, individual, subscribe);
});
socket.on("unsubscribe", (roomParts) => {
if (!roomParts) return;
if (Array.isArray(roomParts)) {
const rooms = roomParts.map((p) => getRoom(p));
logger.info(`client ${socket.id} leave rooms [${rooms.join(",")}]`);
socket.leave(rooms);
} else {
const room = getRoom(roomParts);
logger.info(`client ${socket.id} leave room ${room}`);
socket.leave(room);
}
socket.on("unsubscribe", ({roomParts, individual}) => {
changeSubscription(roomParts, individual, unsubscribe);
});
socket.on("refresh-folder", (folderId) => {
@ -94,6 +74,49 @@
logger.info(`restore backup in room ${room}`);
socket.to(room).emit("restore-backup");
});
function changeSubscription(roomParts, individual, changeFunc) {
if (!roomParts) return;
changeFunc(roomParts);
if(individual){
if (Array.isArray(roomParts)) {
changeFunc(roomParts.map((p) => `${p}-${userId}`));
} else {
changeFunc(`${roomParts}-${userId}`);
}
}
}
function subscribe(roomParts) {
if (!roomParts) return;
if (Array.isArray(roomParts)) {
const rooms = roomParts.map((p) => getRoom(p));
logger.info(`client ${socket.id} join rooms [${rooms.join(",")}]`);
socket.join(rooms);
} else {
const room = getRoom(roomParts);
logger.info(`client ${socket.id} join room ${room}`);
socket.join(room);
}
}
function unsubscribe(roomParts) {
if (!roomParts) return;
if (Array.isArray(roomParts)) {
const rooms = roomParts.map((p) => getRoom(p));
logger.info(`client ${socket.id} leave rooms [${rooms.join(",")}]`);
socket.leave(rooms);
} else {
const room = getRoom(roomParts);
logger.info(`client ${socket.id} leave room ${room}`);
socket.leave(room);
}
}
});
function startEdit({ fileId, room } = {}) {
@ -125,5 +148,15 @@
modifyFolder(room, "delete", fileId, "file");
}
return { startEdit, stopEdit, createFile, deleteFile, updateFile };
function markAsNewFile({ fileId, count, room } = {}) {
logger.info(`markAsNewFile ${fileId} in room ${room}:${count}`);
filesIO.to(room).emit("s:markasnew-file", { fileId, count });
}
function markAsNewFolder({ folderId, count, room } = {}) {
logger.info(`markAsNewFolder ${folderId} in room ${room}:${count}`);
filesIO.to(room).emit("s:markasnew-folder", { folderId, count });
}
return { startEdit, stopEdit, createFile, deleteFile, updateFile, markAsNewFile, markAsNewFolder };
};

View File

@ -200,7 +200,7 @@ public class FeedAggregatorService : FeedBaseService
}
}
_socketServiceClient.SendUnreadUsers(unreadUsers);
await _socketServiceClient.MakeRequest("sendUnreadUsers", unreadUsers);
_logger.DebugTimeCollectingNews(DateTime.UtcNow - start);
}

View File

@ -269,7 +269,7 @@ export default function withFileActions(WrappedFileItem) {
selectTag,
selectOption,
onSelectItem,
setNewBadgeCount,
//setNewBadgeCount,
openFileAction,
uploadEmptyFolders,
} = filesActionsStore;
@ -356,7 +356,7 @@ export default function withFileActions(WrappedFileItem) {
isTrashFolder: isRecycleBinFolder,
getFolderInfo,
viewAs,
setNewBadgeCount,
//setNewBadgeCount,
isActive,
inProgress,
setBufferSelection,

View File

@ -202,7 +202,7 @@ const Shell = ({ items = [], page = "home", ...rest }) => {
useEffect(() => {
socketHelper.emit({
command: "subscribe",
data: "backup-restore",
data: { roomParts: "backup-restore" }
});
socketHelper.on("restore-backup", () => {
setPreparationPortalDialogVisible(true);

View File

@ -76,7 +76,7 @@ class NewFilesPanel extends React.Component {
this.props
.markAsRead(folderIds, fileIds)
.then(() => this.setNewBadgeCount())
//.then(() => this.setNewBadgeCount())
.then(() => {
const { hasNew, refreshFiles } = this.props;
@ -171,33 +171,33 @@ class NewFilesPanel extends React.Component {
}
};
setNewBadgeCount = () => {
const {
newFilesIds,
updateFoldersBadge,
updateFilesBadge,
updateRootBadge,
updateFolderBadge,
pathParts,
newFiles,
} = this.props;
// setNewBadgeCount = () => {
// const {
// newFilesIds,
// updateFoldersBadge,
// updateFilesBadge,
// updateRootBadge,
// updateFolderBadge,
// pathParts,
// newFiles,
// } = this.props;
const { readingFiles } = this.state;
// const { readingFiles } = this.state;
const filesCount = newFiles.filter(
(f) => !readingFiles.includes(f.id.toString())
).length;
updateRootBadge(+newFilesIds[0], filesCount);
// const filesCount = newFiles.filter(
// (f) => !readingFiles.includes(f.id.toString())
// ).length;
// updateRootBadge(+newFilesIds[0], filesCount);
if (newFilesIds.length <= 1) {
if (pathParts[0] === +newFilesIds[0]) {
updateFoldersBadge();
updateFilesBadge();
}
} else {
updateFolderBadge(newFilesIds[newFilesIds.length - 1], filesCount);
}
};
// if (newFilesIds.length <= 1) {
// if (pathParts[0] === +newFilesIds[0]) {
// updateFoldersBadge();
// updateFilesBadge();
// }
// } else {
// updateFolderBadge(newFilesIds[newFilesIds.length - 1], filesCount);
// }
//};
render() {
//console.log("NewFiles panel render");
@ -306,13 +306,13 @@ export default inject(
addFileToRecentlyViewed,
//setIsLoading,
isLoading,
updateFilesBadge,
updateFolderBadge,
updateFoldersBadge,
//updateFilesBadge,
//updateFolderBadge,
//updateFoldersBadge,
hasNew,
refreshFiles,
} = filesStore;
const { updateRootBadge } = treeFoldersStore;
//const { updateRootBadge } = treeFoldersStore;
const { setMediaViewerData } = mediaViewerDataStore;
const { getIcon, getFolderIcon } = settingsStore;
const { markAsRead } = filesActionsStore;
@ -341,10 +341,10 @@ export default inject(
getFolderIcon,
markAsRead,
setNewFilesPanelVisible,
updateRootBadge,
updateFolderBadge,
updateFoldersBadge,
updateFilesBadge,
// updateRootBadge,
// updateFolderBadge,
// updateFoldersBadge,
// updateFilesBadge,
theme: auth.settingsStore.theme,
hasNew,

View File

@ -196,24 +196,24 @@ class DialogsStore {
this.setNewFilesIds(newIds);
} else {
newFilesPanelVisible = false;
const {
getRootFolder,
updateRootBadge,
treeFolders,
} = this.treeFoldersStore;
const { updateFolderBadge, updateFoldersBadge } = this.filesStore;
// const {
// getRootFolder,
// updateRootBadge,
// treeFolders,
// } = this.treeFoldersStore;
// const { updateFolderBadge, updateFoldersBadge } = this.filesStore;
if (item) {
const { rootFolderType, id } = item;
const rootFolder = getRootFolder(rootFolderType);
updateRootBadge(rootFolder.id, item.new);
updateFolderBadge(id, item.new);
} else {
const rootFolder = treeFolders.find((x) => x.id === +newIds[0]);
updateRootBadge(rootFolder.id, rootFolder.new);
if (this.selectedFolderStore.id === rootFolder.id)
updateFoldersBadge();
}
// if (item) {
// const { rootFolderType, id } = item;
// const rootFolder = getRootFolder(rootFolderType);
// updateRootBadge(rootFolder.id, item.new);
// updateFolderBadge(id, item.new);
// } else {
// const rootFolder = treeFolders.find((x) => x.id === +newIds[0]);
// updateRootBadge(rootFolder.id, rootFolder.new);
// if (this.selectedFolderStore.id === rootFolder.id)
// updateFoldersBadge();
// }
}
} else {
this.setNewFilesIds(null);

View File

@ -1097,18 +1097,18 @@ class FilesActionStore {
setConnectItem({ ...provider, ...capability });
};
setNewBadgeCount = (item) => {
const { getRootFolder, updateRootBadge } = this.treeFoldersStore;
const { updateFileBadge, updateFolderBadge } = this.filesStore;
const { rootFolderType, fileExst, id } = item;
// setNewBadgeCount = (item) => {
// const { getRootFolder, updateRootBadge } = this.treeFoldersStore;
// const { updateFileBadge, updateFolderBadge } = this.filesStore;
// const { rootFolderType, fileExst, id } = item;
const count = item.new ? item.new : 1;
const rootFolder = getRootFolder(rootFolderType);
updateRootBadge(rootFolder.id, count);
// const count = item.new ? item.new : 1;
// const rootFolder = getRootFolder(rootFolderType);
// updateRootBadge(rootFolder.id, count);
if (fileExst) updateFileBadge(id);
else updateFolderBadge(id, item.new);
};
// if (fileExst) updateFileBadge(id);
// else updateFolderBadge(id, item.new);
// };
markAsRead = (folderIds, fileIds, item) => {
const {
@ -1132,7 +1132,7 @@ class FilesActionStore {
.then(() => {
if (!item) return;
this.setNewBadgeCount(item);
//this.setNewBadgeCount(item);
const { getFileIndex, updateFileStatus } = this.filesStore;

View File

@ -118,7 +118,7 @@ class FilesStore {
const { socketHelper, withPaging } = authStore.settingsStore;
socketHelper.on("s:modify-folder", async (opt) => {
//console.log("Call s:modify-folder", opt);
console.log("[WS] s:modify-folder", opt);
if (this.isLoading) return;
@ -130,7 +130,13 @@ class FilesStore {
const file = JSON.parse(opt?.data);
const newFiles = [file, ...this.files];
if (this.selectedFolderStore.id !== file.folderId) return;
const fileInfo = await api.files.getFileInfo(file.id);
console.log("[WS] create new file", fileInfo.id, fileInfo.title);
const newFiles = [fileInfo, ...this.files];
if (newFiles.length > this.filter.pageCount && withPaging) {
newFiles.pop(); // Remove last
@ -147,6 +153,8 @@ class FilesStore {
this.getFileInfo(file.id); //this.setFile(file);
console.log("[WS] update file", file.id, file.title);
if (this.selection) {
const foundIndex = this.selection?.findIndex(
(x) => x.id === file.id
@ -175,6 +183,12 @@ class FilesStore {
const foundIndex = this.files.findIndex((x) => x.id === opt?.id);
if (foundIndex == -1) return;
console.log(
"[WS] delete file",
this.files[foundIndex].id,
this.files[foundIndex].title
);
this.setFiles(
this.files.filter((_, index) => {
return index !== foundIndex;
@ -212,16 +226,46 @@ class FilesStore {
//);
if (this.selectedFolderStore.id == id) {
console.log("[WS] refresh-folder", id);
this.fetchFiles(id, this.filter);
}
});
socketHelper.on("s:markasnew-folder", ({ folderId, count }) => {
console.log(`[WS] markasnew-folder ${folderId}:${count}`);
const foundIndex =
folderId && this.folders.findIndex((x) => x.id === folderId);
if (foundIndex == -1) return;
runInAction(() => {
this.folders[foundIndex].new = count >= 0 ? count : 0;
this.treeFoldersStore.fetchTreeFolders();
});
});
socketHelper.on("s:markasnew-file", ({ fileId, count }) => {
console.log(`[WS] markasnew-file ${fileId}:${count}`);
const foundIndex = fileId && this.files.findIndex((x) => x.id === fileId);
if (foundIndex == -1) return;
this.updateFileStatus(
foundIndex,
count > 0
? this.files[foundIndex].fileStatus | FileStatus.IsNew
: this.files[foundIndex].fileStatus & ~FileStatus.IsNew
);
this.treeFoldersStore.fetchTreeFolders();
});
//WAIT FOR RESPONSES OF EDITING FILE
socketHelper.on("s:start-edit-file", (id) => {
//console.log(`Call s:start-edit-file (id=${id})`);
const foundIndex = this.files.findIndex((x) => x.id === id);
if (foundIndex == -1) return;
console.log(`[WS] s:start-edit-file`, id, this.files[foundIndex].title);
this.updateSelectionStatus(
id,
this.files[foundIndex].fileStatus | FileStatus.IsEditing,
@ -235,10 +279,11 @@ class FilesStore {
});
socketHelper.on("s:stop-edit-file", (id) => {
console.log(`Call s:stop-edit-file (id=${id})`);
const foundIndex = this.files.findIndex((x) => x.id === id);
if (foundIndex == -1) return;
console.log(`[WS] s:stop-edit-file`, id, this.files[foundIndex].title);
this.updateSelectionStatus(
id,
this.files[foundIndex].fileStatus & ~FileStatus.IsEditing,
@ -426,7 +471,10 @@ class FilesStore {
if (this.files?.length > 0) {
socketHelper.emit({
command: "unsubscribe",
data: this.files.map((f) => `FILE-${f.id}`),
data: {
roomParts: this.files.map((f) => `FILE-${f.id}`),
individual: true,
},
});
}
@ -435,14 +483,43 @@ class FilesStore {
if (this.files?.length > 0) {
socketHelper.emit({
command: "subscribe",
data: this.files.map((f) => `FILE-${f.id}`),
data: {
roomParts: this.files.map((f) => `FILE-${f.id}`),
individual: true,
},
});
this.files?.forEach((file) =>
console.log("[WS] subscribe to file's changes", file.id, file.title)
);
}
};
setFolders = (folders) => {
const { socketHelper } = this.authStore.settingsStore;
if (folders.length === 0 && this.folders.length === 0) return;
if (this.folders?.length > 0) {
socketHelper.emit({
command: "unsubscribe",
data: {
roomParts: this.folders.map((f) => `DIR-${f.id}`),
individual: true,
},
});
}
this.folders = folders;
if (this.folders?.length > 0) {
socketHelper.emit({
command: "subscribe",
data: {
roomParts: this.folders.map((f) => `DIR-${f.id}`),
individual: true,
},
});
}
};
getFileIndex = (id) => {
@ -1637,27 +1714,27 @@ class FilesStore {
if (folderIndex !== -1) this.folders[folderIndex] = folder;
};
updateFolderBadge = (id, count) => {
const folder = this.folders.find((x) => x.id === id);
if (folder) folder.new -= count;
};
// updateFolderBadge = (id, count) => {
// const folder = this.folders.find((x) => x.id === id);
// if (folder) folder.new -= count;
// };
updateFileBadge = (id) => {
const file = this.files.find((x) => x.id === id);
if (file) file.fileStatus = file.fileStatus & ~FileStatus.IsEditing;
};
// updateFileBadge = (id) => {
// const file = this.files.find((x) => x.id === id);
// if (file) file.fileStatus = file.fileStatus & ~FileStatus.IsEditing;
// };
updateFilesBadge = () => {
for (let file of this.files) {
file.fileStatus = file.fileStatus & ~FileStatus.IsEditing;
}
};
// updateFilesBadge = () => {
// for (let file of this.files) {
// file.fileStatus = file.fileStatus & ~FileStatus.IsEditing;
// }
// };
updateFoldersBadge = () => {
for (let folder of this.folders) {
folder.new = 0;
}
};
// updateFoldersBadge = () => {
// for (let folder of this.folders) {
// folder.new = 0;
// }
// };
updateRoomPin = (item) => {
const idx = this.folders.findIndex((folder) => folder.id === item);
@ -2613,9 +2690,15 @@ class FilesStore {
const { socketHelper } = this.authStore.settingsStore;
if (createdItem?.type == "file") {
console.log(
"[WS] subscribe to file's changes",
createdItem.id,
createdItem.title
);
socketHelper.emit({
command: "subscribe",
data: `FILE-${createdItem.id}`,
data: { roomParts: `FILE-${createdItem.id}`, individual: true },
});
}
};

View File

@ -74,13 +74,16 @@ class SelectedFolderStore {
const { socketHelper } = this.settingsStore;
if (this.id !== null) {
socketHelper.emit({ command: "unsubscribe", data: `DIR-${this.id}` });
socketHelper.emit({
command: "unsubscribe",
data: { roomParts: `DIR-${this.id}`, individual: true },
});
}
if (selectedFolder) {
socketHelper.emit({
command: "subscribe",
data: `DIR-${selectedFolder.id}`,
data: { roomParts: `DIR-${selectedFolder.id}`, individual: true },
});
}

View File

@ -56,16 +56,16 @@ class TreeFoldersStore {
this.expandedPanelKeys = expandedPanelKeys;
};
updateRootBadge = (id, count) => {
const index = this.treeFolders.findIndex((x) => x.id === id);
if (index < 0) return;
// updateRootBadge = (id, count) => {
// const index = this.treeFolders.findIndex((x) => x.id === id);
// if (index < 0) return;
this.treeFolders = this.treeFolders.map((f, i) => {
if (i !== index) return f;
f.newItems -= count;
return f;
});
};
// this.treeFolders = this.treeFolders.map((f, i) => {
// if (i !== index) return f;
// f.newItems -= count;
// return f;
// });
// };
isMy = (myType) => myType === FolderType.USER;
isCommon = (commonType) => commonType === FolderType.COMMON;

View File

@ -44,7 +44,7 @@ const withDialogs = (WrappedComponent) => {
const { socketHelper } = window.authStore.auth.settingsStore;
socketHelper.emit({
command: "subscribe",
data: "backup-restore",
data: { roomParts: "backup-restore" }
});
socketHelper.on("restore-backup", () => {
const message = t("Common:PreparationPortalTitle");

View File

@ -42,7 +42,7 @@ public enum TagType
}
[Serializable]
[DebuggerDisplay("{TagName} ({Id}) entry {EntryType} ({EntryId})")]
[DebuggerDisplay("{Name} ({Id}) entry {EntryType} ({EntryId})")]
public sealed class Tag : IMapFrom<DbFilesTag>
{
public string Name { get; set; }

View File

@ -203,12 +203,20 @@ public class FileStorageService<T> //: IFileStorageService
public async Task<Folder<T>> GetFolderAsync(T folderId)
{
var folderDao = GetFolderDao();
var tagDao = GetTagDao();
var folder = await folderDao.GetFolderAsync(folderId);
ErrorIf(folder == null, FilesCommonResource.ErrorMassage_FolderNotFound);
ErrorIf(!await _fileSecurity.CanReadAsync(folder), FilesCommonResource.ErrorMassage_SecurityException_ReadFolder);
await _entryStatusManager.SetIsFavoriteFolderAsync(folder);
var tag = await tagDao.GetNewTagsAsync(_authContext.CurrentAccount.ID, folder).FirstOrDefaultAsync();
if (tag != null)
{
folder.NewForMe = tag.Count;
}
return folder;
}
@ -968,7 +976,7 @@ public class FileStorageService<T> //: IFileStorageService
if (isFinish)
{
_fileTracker.Remove(id, tabId);
_socketManager.StopEdit(id);
await _socketManager.StopEdit(id);
}
else
{
@ -1018,7 +1026,7 @@ public class FileStorageService<T> //: IFileStorageService
if (!forcesave && _fileTracker.IsEditingAlone(fileId))
{
_fileTracker.Remove(fileId);
_socketManager.StopEdit(fileId);
await _socketManager.StopEdit(fileId);
}
var file = await _entryManager.SaveEditingAsync(fileId, fileExtension, fileuri, stream, doc, forcesave: forcesave ? ForcesaveType.User : ForcesaveType.None, keepLink: true);
@ -1043,7 +1051,7 @@ public class FileStorageService<T> //: IFileStorageService
if (!forcesave && _fileTracker.IsEditing(fileId))
{
_fileTracker.Remove(fileId);
_socketManager.StopEdit(fileId);
await _socketManager.StopEdit(fileId);
}
var file = await _entryManager.SaveEditingAsync(fileId,

View File

@ -220,7 +220,7 @@ public class DocumentServiceTrackerHelper
case TrackerStatus.NotFound:
case TrackerStatus.Closed:
_fileTracker.Remove(fileId);
_socketManager.StopEdit(fileId);
await _socketManager.StopEdit(fileId);
break;
@ -284,7 +284,6 @@ public class DocumentServiceTrackerHelper
_logger.InformationDocServiceUserIdIsNotGuid(user);
continue;
}
}
users.Remove(userId);
@ -314,7 +313,7 @@ public class DocumentServiceTrackerHelper
_fileTracker.Remove(fileId, userId: removeUserId);
}
_socketManager.StartEdit(fileId);
await _socketManager.StartEdit(fileId);
}
private async Task<TrackResponse> ProcessSaveAsync<T>(T fileId, TrackerData fileData)
@ -438,7 +437,7 @@ public class DocumentServiceTrackerHelper
if (!forcesave)
{
_fileTracker.Remove(fileId);
_socketManager.StopEdit(fileId);
await _socketManager.StopEdit(fileId);
}
if (file != null)

View File

@ -104,7 +104,6 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
await DeleteFilesAsync(Files, scope, true);
await DeleteFoldersAsync(Folders, scope, true);
}
}
private async Task DeleteFoldersAsync(IEnumerable<T> folderIds, IServiceScope scope, bool isNeedSendActions = false)
@ -279,7 +278,7 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
}
}
socketManager.DeleteFile(file);
await socketManager.DeleteFile(file);
}
else
{
@ -299,7 +298,7 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
filesMessageService.Send(file, MessageInitiator.AutoCleanUp, MessageAction.FileDeleted, file.Title);
}
socketManager.DeleteFile(file);
await socketManager.DeleteFile(file);
}
catch (Exception ex)
{

View File

@ -565,12 +565,12 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
await LinkDao.DeleteAllLinkAsync(file.Id.ToString());
}
if (Equals(toFolderId.ToString(), _daoFolderId))
if (Equals(toFolderId, _daoFolderId))
{
needToMark.Add(newFile);
}
socketManager.DeleteFile(file);
await socketManager.DeleteFile(file);
await socketManager.CreateFileAsync(newFile);
@ -665,7 +665,7 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
filesMessageService.Send(file, toFolder, _headers, MessageAction.FileMovedWithOverwriting, file.Title, parentFolder.Title, toFolder.Title);
socketManager.DeleteFile(file);
await socketManager.DeleteFile(file);
if (ProcessedFile(fileId))
{

View File

@ -55,7 +55,8 @@ public class FileMarkerHelper<T>
{
using var scope = _serviceProvider.CreateScope();
var fileMarker = scope.ServiceProvider.GetService<FileMarker>();
await fileMarker.ExecMarkFileAsNewAsync(obj);
var socketManager = scope.ServiceProvider.GetService<SocketManager>();
await fileMarker.ExecMarkFileAsNewAsync(obj, socketManager);
}
catch (Exception e)
{
@ -76,7 +77,6 @@ public class FileMarker
private readonly IDaoFactory _daoFactory;
private readonly GlobalFolder _globalFolder;
private readonly FileSecurity _fileSecurity;
private readonly CoreBaseSettings _coreBaseSettings;
private readonly AuthContext _authContext;
private readonly IServiceProvider _serviceProvider;
private readonly FilesSettingsHelper _filesSettingsHelper;
@ -87,7 +87,6 @@ public class FileMarker
IDaoFactory daoFactory,
GlobalFolder globalFolder,
FileSecurity fileSecurity,
CoreBaseSettings coreBaseSettings,
AuthContext authContext,
IServiceProvider serviceProvider,
FilesSettingsHelper filesSettingsHelper,
@ -98,14 +97,13 @@ public class FileMarker
_daoFactory = daoFactory;
_globalFolder = globalFolder;
_fileSecurity = fileSecurity;
_coreBaseSettings = coreBaseSettings;
_authContext = authContext;
_serviceProvider = serviceProvider;
_filesSettingsHelper = filesSettingsHelper;
this._cache = cache;
_cache = cache;
}
internal async Task ExecMarkFileAsNewAsync<T>(AsyncTaskData<T> obj)
internal async Task ExecMarkFileAsNewAsync<T>(AsyncTaskData<T> obj, SocketManager socketManager)
{
_tenantManager.SetCurrentTenant(obj.TenantID);
@ -277,6 +275,7 @@ public class FileMarker
else if (obj.FileEntry.RootFolderType == FolderType.VirtualRooms)
{
var virtualRoomsFolderId = await _globalFolder.GetFolderVirtualRoomsAsync(_daoFactory);
userIDs.ForEach(userID => RemoveFromCahce(virtualRoomsFolderId, userID));
if (obj.FileEntry.ProviderEntry)
{
@ -356,6 +355,8 @@ public class FileMarker
await GetNewTagsAsync(userID, entries.OfType<FileEntry<string>>().ToList());
}
if (updateTags.Count > 0)
{
await tagDao.UpdateNewTags(updateTags, obj.CurrentAccountId);
@ -366,6 +367,8 @@ public class FileMarker
await tagDao.SaveTags(newTags, obj.CurrentAccountId);
}
await Task.WhenAll(ExecMarkAsNewRequest(updateTags.Concat(newTags), socketManager));
async Task GetNewTagsAsync<T1>(Guid userID, List<FileEntry<T1>> entries)
{
var tagDao1 = _daoFactory.GetTagDao<T1>();
@ -571,6 +574,16 @@ public class FileMarker
await tagDao.RemoveTags(removeTags);
}
var socketManager = _serviceProvider.GetRequiredService<SocketManager>();
var toRemove = removeTags.Select(r => new Tag(r.Name, r.Type, r.Owner, 0)
{
EntryId = r.EntryId,
EntryType = r.EntryType
});
await Task.WhenAll(ExecMarkAsNewRequest(updateTags.Concat(toRemove), socketManager));
async Task UpdateRemoveTags<TFolder>(Folder<TFolder> folder)
{
var tagDao = _daoFactory.GetTagDao<TFolder>();
@ -813,6 +826,10 @@ public class FileMarker
parentFolderTag = Tag.New(_authContext.CurrentAccount.ID, parent, 0);
parentFolderTag.Id = -1;
}
else
{
((IFolder)parent).NewForMe = parentFolderTag.Count;
}
if (parent.FolderType != FolderType.VirtualRooms && parent.RootFolderType == FolderType.VirtualRooms && parent.ProviderEntry)
{
@ -942,6 +959,21 @@ public class FileMarker
var key = string.Format(CacheKeyFormat, userId, folderId);
_cache.Remove(key);
}
private IEnumerable<Task> ExecMarkAsNewRequest(IEnumerable<Tag> tags, SocketManager socketManager)
{
foreach (var t in tags)
{
if (t.EntryType == FileEntryType.File)
{
yield return socketManager.ExecMarkAsNewFile(t.EntryId, t.Count, t.Owner);
}
else if (t.EntryType == FileEntryType.Folder)
{
yield return socketManager.ExecMarkAsNewFolder(t.EntryId, t.Count, t.Owner);
}
}
}
}
[Transient]

View File

@ -245,7 +245,7 @@ public class FileTrackerHelper
if (await tracker.StartTrackAsync(fileId.ToString(), docKey))
{
_cache.Insert(Tracker + fileId, fileTracker, CacheTimeout, EvictionCallback(fileId, fileTracker));
socketManager.StartEdit(fileId);
await socketManager.StartEdit(fileId);
}
}
catch (Exception e)

View File

@ -26,34 +26,36 @@
namespace ASC.Web.Files.Utils;
[Scope]
public class SocketManager
public class SocketManager : SocketServiceClient
{
private readonly SocketServiceClient _socketServiceClient;
private readonly FileDtoHelper _filesWrapperHelper;
private readonly TenantManager _tenantManager;
public override string Hub => "files";
public SocketManager(
IOptionsSnapshot<SocketServiceClient> optionsSnapshot,
ILogger<SocketServiceClient> logger,
IHttpClientFactory clientFactory,
MachinePseudoKeys mashinePseudoKeys,
IConfiguration configuration,
FileDtoHelper filesWrapperHelper,
TenantManager tenantManager
)
) : base(logger, clientFactory, mashinePseudoKeys, configuration)
{
_socketServiceClient = optionsSnapshot.Get("files");
_filesWrapperHelper = filesWrapperHelper;
_tenantManager = tenantManager;
}
public void StartEdit<T>(T fileId)
public async Task StartEdit<T>(T fileId)
{
var room = GetFileRoom(fileId);
_socketServiceClient.StartEdit(fileId, room);
await MakeRequest("start-edit", new { room, fileId });
}
public void StopEdit<T>(T fileId)
public async Task StopEdit<T>(T fileId)
{
var room = GetFileRoom(fileId);
_socketServiceClient.StopEdit(fileId, room);
await MakeRequest("stop-edit", new { room, fileId });
}
public async Task CreateFileAsync<T>(File<T> file)
@ -69,7 +71,7 @@ public class SocketManager
serializerSettings.Converters.Add(new FileEntryWrapperConverter());
var data = JsonSerializer.Serialize(await _filesWrapperHelper.GetAsync(file), serializerSettings);
_socketServiceClient.CreateFile(file.Id, room, data);
await MakeRequest("create-file", new { room, fileId = file.Id, data });
}
public async Task UpdateFileAsync<T>(File<T> file)
@ -85,26 +87,40 @@ public class SocketManager
serializerSettings.Converters.Add(new FileEntryWrapperConverter());
var data = JsonSerializer.Serialize(await _filesWrapperHelper.GetAsync(file), serializerSettings);
_socketServiceClient.UpdateFile(file.Id, room, data);
await MakeRequest("update-file", new { room, fileId = file.Id, data });
}
public void DeleteFile<T>(File<T> file)
public async Task DeleteFile<T>(File<T> file)
{
var room = GetFolderRoom(file.ParentId);
_socketServiceClient.DeleteFile(file.Id, room);
await MakeRequest("delete-file", new { room, fileId = file.Id });
}
private string GetFileRoom<T>(T fileId)
public async Task ExecMarkAsNewFile(object fileId, int count, Guid owner)
{
var room = GetFileRoom(fileId, owner);
await MakeRequest("markasnew-file", new { room, fileId, count });
}
public async Task ExecMarkAsNewFolder(object folderId, int count, Guid owner)
{
var room = GetFolderRoom(folderId, owner);
await MakeRequest("markasnew-folder", new { room, folderId, count });
}
private string GetFileRoom<T>(T fileId, Guid? owner = null)
{
var tenantId = _tenantManager.GetCurrentTenant().Id;
var ownerData = owner.HasValue ? "-" + owner.Value : "";
return $"{tenantId}-FILE-{fileId}";
return $"{tenantId}-FILE-{fileId}{ownerData}";
}
private string GetFolderRoom<T>(T folderId)
private string GetFolderRoom<T>(T folderId, Guid? owner = null)
{
var tenantId = _tenantManager.GetCurrentTenant().Id;
var ownerData = owner.HasValue ? "-" + owner.Value : "";
return $"{tenantId}-DIR-{folderId}";
return $"{tenantId}-DIR-{folderId}{ownerData}";
}
}