Routed all notifications through the message bus instead of the snackbar

This commit is contained in:
Thorsten Sommer 2026-08-02 20:11:27 +02:00
parent 2a29aa3f33
commit 18dc8bf9b8
Signed by untrusted user who does not match committer: tsommer
GPG Key ID: 371BBA77A02C0108
13 changed files with 62 additions and 55 deletions

View File

@ -478,13 +478,13 @@ public partial class AssistantI18N : AssistantBaseCore<SettingsDialogI18N>
{ {
if (this.selectedLanguagePlugin is null) if (this.selectedLanguagePlugin is null)
{ {
this.Snackbar.Add(T("No language plugin selected."), Severity.Error); await this.MessageBus.SendError(new(Icons.Material.Filled.Translate, T("No language plugin selected.")));
return; return;
} }
if (this.finalLuaCode.Length == 0) if (this.finalLuaCode.Length == 0)
{ {
this.Snackbar.Add(T("No Lua code generated yet."), Severity.Error); await this.MessageBus.SendError(new(Icons.Material.Filled.Code, T("No Lua code generated yet.")));
return; return;
} }
@ -500,7 +500,7 @@ public partial class AssistantI18N : AssistantBaseCore<SettingsDialogI18N>
if (!File.Exists(pluginFilePath)) if (!File.Exists(pluginFilePath))
{ {
this.Logger.LogError("Plugin file not found: {PluginFilePath}.", pluginFilePath); this.Logger.LogError("Plugin file not found: {PluginFilePath}.", pluginFilePath);
this.Snackbar.Add(T("Plugin file not found."), Severity.Error); await this.MessageBus.SendError(new(Icons.Material.Filled.FindInPage, T("Plugin file not found.")));
return; return;
} }
@ -514,7 +514,7 @@ public partial class AssistantI18N : AssistantBaseCore<SettingsDialogI18N>
if (markerIndex == -1) if (markerIndex == -1)
{ {
this.Logger.LogError("Could not find 'UI_TEXT_CONTENT = {{}}' marker in plugin file: {PluginFilePath}", pluginFilePath); this.Logger.LogError("Could not find 'UI_TEXT_CONTENT = {{}}' marker in plugin file: {PluginFilePath}", pluginFilePath);
this.Snackbar.Add(T("Could not find 'UI_TEXT_CONTENT = {}' marker in plugin file."), Severity.Error); await this.MessageBus.SendError(new(Icons.Material.Filled.FindInPage, T("Could not find 'UI_TEXT_CONTENT = {}' marker in plugin file.")));
return; return;
} }
@ -524,12 +524,12 @@ public partial class AssistantI18N : AssistantBaseCore<SettingsDialogI18N>
// Write the updated content back to the file: // Write the updated content back to the file:
await File.WriteAllTextAsync(pluginFilePath, newContent); await File.WriteAllTextAsync(pluginFilePath, newContent);
this.Snackbar.Add(T("Successfully updated plugin file."), Severity.Success); await this.MessageBus.SendSuccess(new(Icons.Material.Filled.Translate, T("Successfully updated plugin file.")));
} }
catch (Exception ex) catch (Exception ex)
{ {
this.Logger.LogError(ex, "Error writing to plugin file."); this.Logger.LogError(ex, "Error writing to plugin file.");
this.Snackbar.Add(T("Error writing to plugin file."), Severity.Error); await this.MessageBus.SendError(new(Icons.Material.Filled.Translate, T("Error writing to plugin file.")));
} }
} }
#endif #endif

View File

@ -35,9 +35,6 @@ public partial class AssistantLogViewer : MSGComponentBase
[Inject] [Inject]
private RustService RustService { get; init; } = null!; private RustService RustService { get; init; } = null!;
[Inject]
private ISnackbar Snackbar { get; init; } = null!;
[Inject] [Inject]
private NavigationManager NavigationManager { get; init; } = null!; private NavigationManager NavigationManager { get; init; } = null!;
@ -215,11 +212,7 @@ public partial class AssistantLogViewer : MSGComponentBase
var path = this.CurrentLogPath; var path = this.CurrentLogPath;
if (string.IsNullOrWhiteSpace(path)) if (string.IsNullOrWhiteSpace(path))
{ {
this.Snackbar.Add(T("The log file path is not available yet."), Severity.Warning, config => await this.MessageBus.SendWarning(new(Icons.Material.Filled.Folder, T("The log file path is not available yet.")));
{
config.Icon = Icons.Material.Filled.Folder;
config.IconSize = Size.Large;
});
return; return;
} }
@ -231,30 +224,18 @@ public partial class AssistantLogViewer : MSGComponentBase
catch (Exception e) catch (Exception e)
{ {
this.Logger.LogWarning(e, "Could not open the log file location in the file manager."); this.Logger.LogWarning(e, "Could not open the log file location in the file manager.");
this.Snackbar.Add(T("Could not open the log file location."), Severity.Error, config => await this.MessageBus.SendError(new(Icons.Material.Filled.Folder, T("Could not open the log file location.")));
{
config.Icon = Icons.Material.Filled.Folder;
config.IconSize = Size.Large;
});
return; return;
} }
if (response.Success) if (response.Success)
{ {
this.Snackbar.Add(T("Opened the log file location."), Severity.Success, config => await this.MessageBus.SendSuccess(new(Icons.Material.Filled.FolderOpen, T("Opened the log file location.")));
{
config.Icon = Icons.Material.Filled.FolderOpen;
config.IconSize = Size.Large;
});
return; return;
} }
var issue = string.IsNullOrWhiteSpace(response.Issue) ? T("Unknown error") : response.Issue; var issue = string.IsNullOrWhiteSpace(response.Issue) ? T("Unknown error") : response.Issue;
this.Snackbar.Add(string.Format(T("Could not open the log file location: {0}"), issue), Severity.Error, config => await this.MessageBus.SendError(new(Icons.Material.Filled.Folder, string.Format(T("Could not open the log file location: {0}"), issue)));
{
config.Icon = Icons.Material.Filled.Folder;
config.IconSize = Size.Large;
});
} }
private void ClearFilters() private void ClearFilters()

View File

@ -562,7 +562,7 @@ public partial class AssistantPromptOptimizer : AssistantBaseCore<SettingsDialog
this.currentCustomPromptGuidePath = selected.FilePath; this.currentCustomPromptGuidePath = selected.FilePath;
if (files.Count > 1 || replacedPrevious) if (files.Count > 1 || replacedPrevious)
this.Snackbar.Add(T("Replaced the previously selected custom prompt guide file."), Severity.Info); await this.MessageBus.SendInfo(new(Icons.Material.Filled.SwapHoriz, T("Replaced the previously selected custom prompt guide file.")));
await this.LoadCustomPromptGuidelineContentAsync(selected); await this.LoadCustomPromptGuidelineContentAsync(selected);
} }
@ -572,7 +572,7 @@ public partial class AssistantPromptOptimizer : AssistantBaseCore<SettingsDialog
if (!fileAttachment.Exists) if (!fileAttachment.Exists)
{ {
this.customPromptingGuidelineContent = string.Empty; this.customPromptingGuidelineContent = string.Empty;
this.Snackbar.Add(T("The selected custom prompt guide file could not be found."), Severity.Warning); await this.MessageBus.SendWarning(new(Icons.Material.Filled.FindInPage, T("The selected custom prompt guide file could not be found.")));
return; return;
} }
@ -581,12 +581,12 @@ public partial class AssistantPromptOptimizer : AssistantBaseCore<SettingsDialog
this.isLoadingCustomPromptGuide = true; this.isLoadingCustomPromptGuide = true;
this.customPromptingGuidelineContent = await UserFile.LoadFileData(fileAttachment.FilePath, this.RustService, this.DialogService); this.customPromptingGuidelineContent = await UserFile.LoadFileData(fileAttachment.FilePath, this.RustService, this.DialogService);
if (string.IsNullOrWhiteSpace(this.customPromptingGuidelineContent)) if (string.IsNullOrWhiteSpace(this.customPromptingGuidelineContent))
this.Snackbar.Add(T("The custom prompt guide file is empty or could not be read."), Severity.Warning); await this.MessageBus.SendWarning(new(Icons.Material.Filled.Description, T("The custom prompt guide file is empty or could not be read.")));
} }
catch catch
{ {
this.customPromptingGuidelineContent = string.Empty; this.customPromptingGuidelineContent = string.Empty;
this.Snackbar.Add(T("Failed to load custom prompt guide content."), Severity.Error); await this.MessageBus.SendError(new(Icons.Material.Filled.Description, T("Failed to load custom prompt guide content.")));
} }
finally finally
{ {
@ -600,7 +600,7 @@ public partial class AssistantPromptOptimizer : AssistantBaseCore<SettingsDialog
var promptingGuideline = await ReadPromptingGuidelineAsync(); var promptingGuideline = await ReadPromptingGuidelineAsync();
if (string.IsNullOrWhiteSpace(promptingGuideline)) if (string.IsNullOrWhiteSpace(promptingGuideline))
{ {
this.Snackbar.Add(T("The prompting guideline file could not be loaded."), Severity.Warning); await this.MessageBus.SendWarning(new(Icons.Material.Filled.MenuBook, T("The prompting guideline file could not be loaded.")));
return; return;
} }

View File

@ -89,7 +89,7 @@ public partial class VisualBriefingAssistant
terminalIssue = result.Issue; terminalIssue = result.Issue;
if (terminalStatus is not AssistantSessionStatus.CANCELED) if (terminalStatus is not AssistantSessionStatus.CANCELED)
this.Snackbar.Add(result.Issue, Severity.Error); await this.MessageBus.SendError(new(Icons.Material.Filled.AutoAwesome, result.Issue));
return; return;
} }
@ -107,7 +107,7 @@ public partial class VisualBriefingAssistant
this.UpdateProject(latest); this.UpdateProject(latest);
} }
this.Snackbar.Add(successMessage, Severity.Success); await this.MessageBus.SendSuccess(new(Icons.Material.Filled.AutoAwesome, successMessage));
terminalStatus = AssistantSessionStatus.COMPLETED; terminalStatus = AssistantSessionStatus.COMPLETED;
} }
catch (OperationCanceledException) catch (OperationCanceledException)
@ -119,7 +119,7 @@ public partial class VisualBriefingAssistant
{ {
terminalIssue = unexpectedFailureMessage; terminalIssue = unexpectedFailureMessage;
this.Logger.LogError("Unexpected visual briefing UI failure. BriefingId={BriefingId} Mode={Mode} ExceptionType={ExceptionType}", briefingId, mode, exception.GetType().Name); this.Logger.LogError("Unexpected visual briefing UI failure. BriefingId={BriefingId} Mode={Mode} ExceptionType={ExceptionType}", briefingId, mode, exception.GetType().Name);
this.Snackbar.Add(terminalIssue, Severity.Error); await this.MessageBus.SendError(new(Icons.Material.Filled.AutoAwesome, terminalIssue));
} }
finally finally
{ {

View File

@ -154,7 +154,7 @@ public partial class VisualBriefingAssistant
var path = await this.Store.GetProjectDirectoryPathAsync(this.selectedProject.BriefingId); var path = await this.Store.GetProjectDirectoryPathAsync(this.selectedProject.BriefingId);
if (string.IsNullOrWhiteSpace(path)) if (string.IsNullOrWhiteSpace(path))
{ {
this.Snackbar.Add(T("The visual briefing project folder is not available."), Severity.Warning); await this.MessageBus.SendWarning(new(Icons.Material.Filled.Folder, T("The visual briefing project folder is not available.")));
return; return;
} }
@ -166,18 +166,18 @@ public partial class VisualBriefingAssistant
catch (Exception exception) catch (Exception exception)
{ {
this.Logger.LogWarning(exception, "Could not open the visual briefing project folder. BriefingId={BriefingId}", this.selectedProject.BriefingId); this.Logger.LogWarning(exception, "Could not open the visual briefing project folder. BriefingId={BriefingId}", this.selectedProject.BriefingId);
this.Snackbar.Add(T("Could not open the visual briefing project folder."), Severity.Error); await this.MessageBus.SendError(new(Icons.Material.Filled.Folder, T("Could not open the visual briefing project folder.")));
return; return;
} }
if (response.Success) if (response.Success)
{ {
this.Snackbar.Add(T("Opened the visual briefing project folder."), Severity.Success); await this.MessageBus.SendSuccess(new(Icons.Material.Filled.Folder, T("Opened the visual briefing project folder.")));
return; return;
} }
var issue = string.IsNullOrWhiteSpace(response.Issue) ? T("Unknown error") : response.Issue; var issue = string.IsNullOrWhiteSpace(response.Issue) ? T("Unknown error") : response.Issue;
this.Snackbar.Add(string.Format(T("Could not open the visual briefing project folder: {0}"), issue), Severity.Error); await this.MessageBus.SendError(new(Icons.Material.Filled.Folder, string.Format(T("Could not open the visual briefing project folder: {0}"), issue)));
} }
/// <summary> /// <summary>

View File

@ -118,14 +118,14 @@ public partial class VisualBriefingAssistant
if (PathComparer().Equals(Path.GetFullPath(sourcePath), Path.GetFullPath(response.SaveFilePath))) if (PathComparer().Equals(Path.GetFullPath(sourcePath), Path.GetFullPath(response.SaveFilePath)))
{ {
this.Snackbar.Add(T("Choose a different export location so the immutable briefing version is not overwritten."), Severity.Error); await this.MessageBus.SendError(new(Icons.Material.Filled.SaveAs, T("Choose a different export location so the immutable briefing version is not overwritten.")));
return; return;
} }
var verified = await this.Store.OpenIntegrityCheckedVersionAsync(this.selectedBriefing.BriefingId, this.selectedRevisionId); var verified = await this.Store.OpenIntegrityCheckedVersionAsync(this.selectedBriefing.BriefingId, this.selectedRevisionId);
if (verified is null) if (verified is null)
{ {
this.Snackbar.Add(T("The selected briefing version failed its integrity check and cannot be exported."), Severity.Error); await this.MessageBus.SendError(new(Icons.Material.Filled.GppBad, T("The selected briefing version failed its integrity check and cannot be exported.")));
return; return;
} }
@ -146,7 +146,7 @@ public partial class VisualBriefingAssistant
exportedVersion.DocumentHash, exportedVersion.DocumentHash,
source.Length); source.Length);
this.Snackbar.Add(T("The visual briefing was exported."), Severity.Success); await this.MessageBus.SendSuccess(new(Icons.Material.Filled.FileDownload, T("The visual briefing was exported.")));
} }
/// <summary> /// <summary>
@ -176,7 +176,7 @@ public partial class VisualBriefingAssistant
if (!imported.Success) if (!imported.Success)
{ {
this.Snackbar.Add(imported.Issue, Severity.Error); await this.MessageBus.SendError(new(Icons.Material.Filled.FileUpload, imported.Issue));
return; return;
} }
@ -190,7 +190,7 @@ public partial class VisualBriefingAssistant
imported.RevisionId, imported.RevisionId,
imported.WasDeduplicated); imported.WasDeduplicated);
this.Snackbar.Add(imported.WasDeduplicated ? T("This briefing revision was already imported.") : T("The visual briefing was imported."), Severity.Success); await this.MessageBus.SendSuccess(new(Icons.Material.Filled.FileUpload, imported.WasDeduplicated ? T("This briefing revision was already imported.") : T("The visual briefing was imported.")));
} }
/// <summary> /// <summary>

View File

@ -217,7 +217,7 @@ public partial class VisualBriefingAssistant : MSGComponentBase
"Could not auto-save visual briefing. BriefingId={BriefingId} ExceptionType={ExceptionType}", "Could not auto-save visual briefing. BriefingId={BriefingId} ExceptionType={ExceptionType}",
this.selectedBriefing.BriefingId, this.selectedBriefing.BriefingId,
exception.GetType().Name); exception.GetType().Name);
this.Snackbar.Add(T("The visual briefing settings could not be saved."), Severity.Error); await this.MessageBus.SendError(new(Icons.Material.Filled.SaveAs, T("The visual briefing settings could not be saved.")));
} }
} }

View File

@ -77,12 +77,9 @@ public partial class MudCopyClipboardButton : ComponentBase
break; break;
default: default:
this.Snackbar.Add(TB("Cannot copy this content type to clipboard."), Severity.Error, config => // This component is no MSGComponentBase, so it uses the shared bus instance the same
{ // way FileExtensionValidation does:
config.Icon = Icons.Material.Filled.ContentCopy; await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.ContentCopy, TB("Cannot copy this content type to clipboard.")));
config.IconSize = Size.Large;
config.IconColor = Color.Error;
});
break; break;
} }
} }

View File

@ -47,7 +47,7 @@ public abstract class SettingsPanelProviderBase : SettingsPanelBase
else else
{ {
// No encryption secret available - inform the user: // No encryption secret available - inform the user:
this.Snackbar.Add(TB("Cannot export the encrypted API key: No enterprise encryption secret is configured."), Severity.Warning); await this.MessageBus.SendWarning(new(Icons.Material.Filled.Key, TB("Cannot export the encrypted API key: No enterprise encryption secret is configured.")));
} }
} }
} }

View File

@ -112,7 +112,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
this.MessageBus.ApplyFilters(this, [], this.MessageBus.ApplyFilters(this, [],
[ [
Event.UPDATE_AVAILABLE, Event.CONFIGURATION_CHANGED, Event.COLOR_THEME_CHANGED, Event.SHOW_ERROR, Event.UPDATE_AVAILABLE, Event.CONFIGURATION_CHANGED, Event.COLOR_THEME_CHANGED, Event.SHOW_ERROR,
Event.SHOW_WARNING, Event.SHOW_SUCCESS, Event.STARTUP_PLUGIN_SYSTEM, Event.PLUGINS_RELOADED, Event.SHOW_WARNING, Event.SHOW_SUCCESS, Event.SHOW_INFO, Event.STARTUP_PLUGIN_SYSTEM, Event.PLUGINS_RELOADED,
Event.INSTALL_UPDATE, Event.STARTUP_COMPLETED, Event.AI_JOB_CHANGED, Event.AI_JOB_FINISHED, Event.INSTALL_UPDATE, Event.STARTUP_COMPLETED, Event.AI_JOB_CHANGED, Event.AI_JOB_FINISHED,
Event.CHAT_GENERATION_CHANGED, Event.ASSISTANT_SESSION_CHANGED, Event.ASSISTANT_SESSION_FINISHED, Event.CHAT_GENERATION_CHANGED, Event.ASSISTANT_SESSION_CHANGED, Event.ASSISTANT_SESSION_FINISHED,
]); ]);
@ -266,6 +266,12 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
break; break;
case Event.SHOW_INFO:
if (data is DataInfoMessage info)
info.Show(this.Snackbar);
break;
case Event.STARTUP_PLUGIN_SYSTEM: case Event.STARTUP_PLUGIN_SYSTEM:
_ = Task.Run(async () => _ = Task.Run(async () =>
{ {

View File

@ -0,0 +1,16 @@
namespace AIStudio.Tools;
public readonly record struct DataInfoMessage(string Icon, string Message)
{
public void Show(ISnackbar snackbar)
{
var icon = this.Icon;
snackbar.Add(this.Message, Severity.Info, config =>
{
config.Icon = icon;
config.IconSize = Size.Large;
config.HideTransitionDuration = 600;
config.VisibleStateDuration = 10_000;
});
}
}

View File

@ -73,6 +73,11 @@ public enum Event
/// </summary> /// </summary>
SHOW_SUCCESS, SHOW_SUCCESS,
/// <summary>
/// Requests display of an informational notification.
/// </summary>
SHOW_INFO,
/// <summary> /// <summary>
/// Carries an event received from the Tauri runtime. /// Carries an event received from the Tauri runtime.
/// </summary> /// </summary>

View File

@ -91,6 +91,8 @@ public sealed class MessageBus
public Task SendSuccess(DataSuccessMessage dataSuccessMessage) => this.SendMessage(null, Event.SHOW_SUCCESS, dataSuccessMessage); public Task SendSuccess(DataSuccessMessage dataSuccessMessage) => this.SendMessage(null, Event.SHOW_SUCCESS, dataSuccessMessage);
public Task SendInfo(DataInfoMessage dataInfoMessage) => this.SendMessage(null, Event.SHOW_INFO, dataInfoMessage);
public void DeferMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data = default) public void DeferMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data = default)
{ {
if (this.deferredMessages.TryGetValue(triggeredEvent, out var queue)) if (this.deferredMessages.TryGetValue(triggeredEvent, out var queue))