@implements IAsyncDisposable
@using RobotNet10.ScriptEditor.Clients
@using RobotNet10.ScriptEditor.Models
@using RobotNet10.ScriptEditor.Dialogs
@using Microsoft.JSInterop
@using Microsoft.JSInterop.Implementation
@using Microsoft.AspNetCore.Components.Web
@using RobotNet10.ScriptEditor.Services
@using RobotNet10.ScriptEngine.Shared
@inject IJSRuntime JSRuntime
@inject IScriptEngineResource ScriptResource
@inject FileManagerHubClient FileManagerClient
@inject ScriptManagerHubClient ScriptManagerClient
@inject ScriptResourceResolver ResourceResolver
@inject ScriptWorkspace Workspace
@inject NavigationManager NavigationManager
@inject IDialogService DialogService
@using MudBlazor
@code {
private int _sidebarWidth = 250; // Chiều rộng mặc định của sidebar (px)
private IJSObjectReference? _jsModule;
protected override void OnInitialized()
{
// Subscribe to edit permission revoked event
FileManagerClient.EditPermissionRevoked += OnEditPermissionRevoked;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
try
{
_jsModule = await JSRuntime.InvokeAsync(
"import", "./_content/RobotNet10.ScriptEditor/scriptEditorResize.js");
if (_jsModule != null)
{
await _jsModule.InvokeVoidAsync("initializeLayout");
}
await FileManagerClient.StartAsync();
await ScriptManagerClient.StartAsync();
// Request edit permission before initializing workspace
// This will always succeed and force out any previous connection
try
{
await FileManagerClient.RequestEditPermissionAsync();
// Check if we actually have edit permission (depends on state)
var hasPermission = await FileManagerClient.HasEditPermissionAsync();
Workspace.IsReadOnly = !hasPermission;
}
catch (Exception)
{
// If request fails, set to read-only
Workspace.IsReadOnly = true;
}
var references = await ResourceResolver.GetMetadataReferences(ScriptResource.Modules.ToArray(), ScriptResource.DocModules.ToArray());
var rootFolder = await FileManagerClient.GetRootFolderAsync();
Workspace.Initialize(references, ScriptResource.UsingNamespaces.ToArray(), ScriptResource.AppGlobalType, rootFolder);
StateHasChanged();
}
catch (JSException)
{
// JavaScript module chưa được load, sẽ được xử lý sau
}
catch (Exception)
{
// Ignore other exceptions but don't crash the component
// Mono runtime assertions (e.g., debugger-agent) are non-fatal
}
}
}
private async void OnEditPermissionRevoked(string? userId)
{
// Lock the workspace when permission is revoked
Workspace.IsReadOnly = true;
StateHasChanged();
// Show dialog to inform user
// User can only close by clicking reload button, not by clicking outside or pressing Escape
var options = new DialogOptions
{
CloseButton = false,
CloseOnEscapeKey = false,
BackdropClick = false,
MaxWidth = MaxWidth.Small,
FullWidth = true
};
var parameters = new DialogParameters
{
{ x => x.OnReload, new Action(() => NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true)) }
};
await DialogService.ShowAsync("Edit Permission Revoked", parameters, options);
}
public async ValueTask DisposeAsync()
{
// Unsubscribe from events
FileManagerClient.EditPermissionRevoked -= OnEditPermissionRevoked;
if (_jsModule != null)
{
try
{
await _jsModule.InvokeVoidAsync("cleanupLayout");
await _jsModule.DisposeAsync();
// Revoke edit permission before stopping connection
if (FileManagerClient.IsConnected)
{
try
{
await FileManagerClient.RevokeEditPermissionAsync();
}
catch (Exception)
{
// Ignore errors when revoking permission during dispose
}
}
await FileManagerClient.StopAsync();
await ScriptManagerClient.StopAsync();
}
catch (JSDisconnectedException)
{
// Ignore khi JS context đã bị disconnect
}
}
}
}