Improved dialog handling

This commit is contained in:
Thorsten Sommer 2026-06-05 13:56:36 +02:00
parent 3918d7d674
commit 6d8b59bb02
Signed by untrusted user who does not match committer: tsommer
GPG Key ID: 371BBA77A02C0108
6 changed files with 100 additions and 46 deletions

View File

@ -101,7 +101,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
// Apply the filters for the message bus: // Apply the filters for the message bus:
this.ApplyFilters([], [ Event.HAS_CHAT_UNSAVED_CHANGES, Event.RESET_CHAT_STATE, Event.CHAT_STREAMING_DONE, Event.AI_JOB_CHANGED, Event.AI_JOB_FINISHED, Event.CHAT_GENERATION_CHANGED ]); this.ApplyFilters([], [ Event.HAS_CHAT_UNSAVED_CHANGES, Event.RESET_CHAT_STATE, Event.CHAT_STREAMING_DONE, Event.AI_JOB_CHANGED, Event.AI_JOB_FINISHED, Event.CHAT_GENERATION_CHANGED, Event.WORKSPACE_RENAMED ]);
// Configure the spellchecking for the user input: // Configure the spellchecking for the user input:
this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES); this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES);
@ -377,6 +377,29 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
this.WorkspaceName(this.currentWorkspaceName); this.WorkspaceName(this.currentWorkspaceName);
} }
private async Task RefreshRenamedWorkspaceHeaderAsync(Guid workspaceId)
{
var currentChatThread = this.ChatThread;
if (currentChatThread is null || currentChatThread.WorkspaceId != workspaceId)
return;
var syncVersion = Interlocked.Increment(ref this.workspaceHeaderSyncVersion);
var chatThreadId = currentChatThread.ChatId;
var loadedWorkspaceName = await WorkspaceBehaviour.LoadWorkspaceNameAsync(workspaceId);
if (syncVersion != this.workspaceHeaderSyncVersion)
return;
if (this.ChatThread is null
|| this.ChatThread.ChatId != chatThreadId
|| this.ChatThread.WorkspaceId != workspaceId)
return;
this.currentChatThreadId = chatThreadId;
this.currentWorkspaceId = workspaceId;
this.PublishWorkspaceNameIfChanged(loadedWorkspaceName);
}
private async Task SyncForegroundChatAsync() private async Task SyncForegroundChatAsync()
{ {
var nextForegroundChatId = this.ChatThread?.ChatId ?? Guid.Empty; var nextForegroundChatId = this.ChatThread?.ChatId ?? Guid.Empty;
@ -864,7 +887,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
{ x => x.ConfirmText, T("Move chat") }, { x => x.ConfirmText, T("Move chat") },
}; };
var dialogReference = await this.DialogService.ShowAsync<WorkspaceSelectionDialog>(T("Move Chat to Workspace"), dialogParameters, DialogOptions.FULLSCREEN); var dialogReference = await this.DialogService.ShowAsync<WorkspaceSelectionDialog>(T("Move Chat to Workspace"), dialogParameters, DialogOptions.FULLSCREEN_MANUAL_ESCAPE);
var dialogResult = await dialogReference.Result; var dialogResult = await dialogReference.Result;
if (dialogResult is null || dialogResult.Canceled) if (dialogResult is null || dialogResult.Canceled)
return; return;
@ -1047,6 +1070,11 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
await this.SaveThread(); await this.SaveThread();
break; break;
case Event.WORKSPACE_RENAMED:
if (data is Guid workspaceId)
await this.RefreshRenamedWorkspaceHeaderAsync(workspaceId);
break;
case Event.AI_JOB_CHANGED: case Event.AI_JOB_CHANGED:
case Event.AI_JOB_FINISHED: case Event.AI_JOB_FINISHED:
case Event.CHAT_GENERATION_CHANGED: case Event.CHAT_GENERATION_CHANGED:

View File

@ -752,6 +752,7 @@ public partial class Workspaces : MSGComponentBase
if (!await WorkspaceBehaviour.RenameWorkspaceAsync(workspaceId, alteredWorkspaceName)) if (!await WorkspaceBehaviour.RenameWorkspaceAsync(workspaceId, alteredWorkspaceName))
return; return;
await this.SendMessage(Event.WORKSPACE_RENAMED, workspaceId);
await this.LoadTreeItemsAsync(startPrefetch: false); await this.LoadTreeItemsAsync(startPrefetch: false);
} }
@ -822,7 +823,7 @@ public partial class Workspaces : MSGComponentBase
{ x => x.ConfirmText, T("Move chat") }, { x => x.ConfirmText, T("Move chat") },
}; };
var dialogReference = await this.DialogService.ShowAsync<WorkspaceSelectionDialog>(T("Move Chat to Workspace"), dialogParameters, DialogOptions.FULLSCREEN); var dialogReference = await this.DialogService.ShowAsync<WorkspaceSelectionDialog>(T("Move Chat to Workspace"), dialogParameters, DialogOptions.FULLSCREEN_MANUAL_ESCAPE);
var dialogResult = await dialogReference.Result; var dialogResult = await dialogReference.Result;
if (dialogResult is null || dialogResult.Canceled) if (dialogResult is null || dialogResult.Canceled)
return; return;

View File

@ -8,6 +8,12 @@ public static class DialogOptions
FullWidth = true, MaxWidth = MaxWidth.Medium, FullWidth = true, MaxWidth = MaxWidth.Medium,
}; };
public static readonly MudBlazor.DialogOptions FULLSCREEN_MANUAL_ESCAPE = new()
{
CloseOnEscapeKey = false,
FullWidth = true, MaxWidth = MaxWidth.Medium,
};
public static readonly MudBlazor.DialogOptions FULLSCREEN_NO_HEADER = new() public static readonly MudBlazor.DialogOptions FULLSCREEN_NO_HEADER = new()
{ {
NoHeader = true, NoHeader = true,

View File

@ -1,59 +1,63 @@
@inherits MSGComponentBase @inherits MSGComponentBase
<MudDialog> <MudDialog>
<DialogContent> <DialogContent>
<MudText Typo="Typo.body1"> <div @onkeydown="@this.HandleDialogKeyDown">
@this.Message <MudText Typo="Typo.body1">
</MudText> @this.Message
<MudList T="Guid" @bind-SelectedValue="@this.selectedWorkspace"> </MudText>
@foreach (var workspace in this.workspaces) <MudList T="Guid" @bind-SelectedValue="@this.selectedWorkspace">
{ @foreach (var workspace in this.workspaces)
<MudListItem Text="@workspace.Name" Icon="@Icons.Material.Filled.Description" Value="@workspace.WorkspaceId" /> {
} <MudListItem Text="@workspace.Name" Icon="@Icons.Material.Filled.Description" Value="@workspace.WorkspaceId" />
</MudList> }
</MudList>
</div>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<MudStack Style="width: 100%;" Spacing="2"> <div style="width: 100%;" @onkeydown="@this.HandleDialogKeyDown">
<MudDivider/> <MudStack Style="width: 100%;" Spacing="2">
<MudDivider/>
@if (this.showCreateWorkspaceForm)
{
<MudForm @ref="this.createWorkspaceForm">
<MudTextField T="string"
@bind-Text="@this.newWorkspaceName"
Variant="Variant.Outlined"
AutoGrow="@false"
Lines="1"
Label="@T("Workspace name")"
Immediate="@true"
Disabled="@this.isCreatingWorkspace"
OnKeyDown="@this.HandleNewWorkspaceNameKeyDown"
Validation="@this.ValidateNewWorkspaceName" />
</MudForm>
}
else
{
<MudButton StartIcon="@Icons.Material.Filled.LibraryAdd" Variant="Variant.Filled" OnClick="@this.ShowCreateWorkspaceForm">
@T("Create new workspace")
</MudButton>
}
<MudStack Row="@true" Justify="Justify.FlexEnd" AlignItems="AlignItems.Center" Wrap="Wrap.NoWrap" Spacing="2">
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
@T("Cancel")
</MudButton>
@if (this.showCreateWorkspaceForm) @if (this.showCreateWorkspaceForm)
{ {
<MudButton OnClick="@this.CreateWorkspaceAsync" Variant="Variant.Filled" Color="Color.Info" Disabled="@this.isCreatingWorkspace"> <MudForm @ref="this.createWorkspaceForm">
@T("Add workspace") <MudTextField T="string"
</MudButton> @bind-Text="@this.newWorkspaceName"
Variant="Variant.Outlined"
AutoGrow="@false"
Lines="1"
Label="@T("Workspace name")"
Immediate="@true"
Disabled="@this.isCreatingWorkspace"
OnKeyDown="@this.HandleNewWorkspaceNameKeyDown"
Validation="@this.ValidateNewWorkspaceName" />
</MudForm>
} }
else else
{ {
<MudButton OnClick="@this.Confirm" Variant="Variant.Filled" Color="Color.Info" Disabled="@(this.selectedWorkspace == Guid.Empty)"> <MudButton StartIcon="@Icons.Material.Filled.LibraryAdd" Variant="Variant.Filled" OnClick="@this.ShowCreateWorkspaceForm">
@this.ConfirmText @T("Create new workspace")
</MudButton> </MudButton>
} }
<MudStack Row="@true" Justify="Justify.FlexEnd" AlignItems="AlignItems.Center" Wrap="Wrap.NoWrap" Spacing="2">
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
@T("Cancel")
</MudButton>
@if (this.showCreateWorkspaceForm)
{
<MudButton OnClick="@this.CreateWorkspaceAsync" Variant="Variant.Filled" Color="Color.Info" Disabled="@this.isCreatingWorkspace">
@T("Add workspace")
</MudButton>
}
else
{
<MudButton OnClick="@this.Confirm" Variant="Variant.Filled" Color="Color.Info" Disabled="@(this.selectedWorkspace == Guid.Empty)">
@this.ConfirmText
</MudButton>
}
</MudStack>
</MudStack> </MudStack>
</MudStack> </div>
</DialogActions> </DialogActions>
</MudDialog> </MudDialog>

View File

@ -80,6 +80,16 @@ public partial class WorkspaceSelectionDialog : MSGComponentBase
await this.CreateWorkspaceAsync(); await this.CreateWorkspaceAsync();
} }
private void HandleDialogKeyDown(KeyboardEventArgs keyEvent)
{
var key = keyEvent.Key.ToLowerInvariant();
var code = keyEvent.Code.ToLowerInvariant();
if (key is not "escape" && code is not "escape")
return;
this.Cancel();
}
private void ShowCreateWorkspaceForm() private void ShowCreateWorkspaceForm()
{ {
this.createWorkspaceError = null; this.createWorkspaceError = null;

View File

@ -156,6 +156,11 @@ public enum Event
/// </summary> /// </summary>
WORKSPACE_TOGGLE_OVERLAY, WORKSPACE_TOGGLE_OVERLAY,
/// <summary>
/// Notifies receivers that a workspace was renamed.
/// </summary>
WORKSPACE_RENAMED,