2024-12-03 15:32:38 +01:00
using AIStudio.Settings ;
2025-04-12 21:13:33 +02:00
using AIStudio.Tools.PluginSystem ;
2026-08-25 18:53:09 +02:00
using AIStudio.Tools.Services ;
2024-12-03 15:32:38 +01:00
2024-06-30 15:26:28 +02:00
using Microsoft.AspNetCore.Components ;
2024-07-14 21:46:17 +02:00
namespace AIStudio.Components ;
2024-06-30 15:26:28 +02:00
2026-08-23 21:15:37 +02:00
public abstract class MSGComponentBase : ComponentBase , IDisposable , IAsyncDisposable , IMessageBusReceiver , ILang
2024-06-30 15:26:28 +02:00
{
2024-12-03 15:32:38 +01:00
[Inject]
protected SettingsManager SettingsManager { get ; init ; } = null !;
2024-06-30 15:26:28 +02:00
[Inject]
protected MessageBus MessageBus { get ; init ; } = null !;
2026-08-25 18:53:09 +02:00
/// <summary>
/// The circuit this component lives in. Use it before any JS interop: while its connection is down,
/// the browser is unreachable, although the component itself keeps working.
/// </summary>
[Inject]
protected CircuitStateService CircuitState { get ; init ; } = null !;
2025-04-12 21:13:33 +02:00
private ILanguagePlugin Lang { get ; set ; } = PluginFactory . BaseLanguage ;
2024-06-30 15:26:28 +02:00
#region Overrides of ComponentBase
2025-04-12 21:13:33 +02:00
protected override async Task OnInitializedAsync ()
2024-06-30 15:26:28 +02:00
{
2025-04-12 21:13:33 +02:00
this . Lang = await this . SettingsManager . GetActiveLanguagePlugin ();
2026-08-25 18:53:09 +02:00
this . MessageBus . RegisterComponent ( this , this . CircuitState );
2025-04-12 21:13:33 +02:00
await base . OnInitializedAsync ();
}
#endregion
#region Implementation of ILang
/// <inheritdoc />
2025-04-24 13:50:14 +02:00
public string T ( string fallbackEN ) => this . GetText ( this . Lang , fallbackEN );
2025-04-27 16:13:15 +02:00
/// <inheritdoc />
public string T ( string fallbackEN , string? typeNamespace , string? typeName ) => this . GetText ( this . Lang , fallbackEN , typeNamespace , typeName );
2024-06-30 15:26:28 +02:00
#endregion
#region Implementation of IMessageBusReceiver
2025-04-12 21:13:33 +02:00
public async Task ProcessMessage < T >( ComponentBase ? sendingComponent , Event triggeredEvent , T ? data )
2024-09-15 12:30:07 +02:00
{
2025-06-02 20:08:25 +02:00
await this . InvokeAsync ( async () =>
2024-09-15 12:30:07 +02:00
{
2025-06-02 20:08:25 +02:00
switch ( triggeredEvent )
{
case Event . COLOR_THEME_CHANGED :
this . StateHasChanged ();
break ;
2024-09-15 12:30:07 +02:00
2025-06-02 20:08:25 +02:00
case Event . PLUGINS_RELOADED :
this . Lang = await this . SettingsManager . GetActiveLanguagePlugin ();
await this . InvokeAsync ( this . StateHasChanged );
break ;
}
2025-04-24 13:50:14 +02:00
2025-06-02 20:08:25 +02:00
await this . ProcessIncomingMessage ( sendingComponent , triggeredEvent , data );
});
2024-09-15 12:30:07 +02:00
}
2025-04-12 21:13:33 +02:00
public async Task < TResult ?> ProcessMessageWithResult < TPayload , TResult >( ComponentBase ? sendingComponent , Event triggeredEvent , TPayload ? data )
{
return await this . ProcessIncomingMessageWithResult < TPayload , TResult >( sendingComponent , triggeredEvent , data );
}
2024-06-30 15:26:28 +02:00
#endregion
2025-04-12 21:13:33 +02:00
protected virtual Task ProcessIncomingMessage < T >( ComponentBase ? sendingComponent , Event triggeredEvent , T ? data )
{
return Task . CompletedTask ;
}
protected virtual Task < TResult ?> ProcessIncomingMessageWithResult < TPayload , TResult >( ComponentBase ? sendingComponent , Event triggeredEvent , TPayload ? data )
{
return Task . FromResult < TResult ?>( default );
}
2024-06-30 15:26:28 +02:00
protected async Task SendMessage < T >( Event triggeredEvent , T ? data = default )
{
await this . MessageBus . SendMessage ( this , triggeredEvent , data );
}
2024-07-13 10:37:57 +02:00
protected async Task < TResult ?> SendMessageWithResult < TPayload , TResult >( Event triggeredEvent , TPayload ? data )
{
return await this . MessageBus . SendMessageUseFirstResult < TPayload , TResult >( this , triggeredEvent , data );
}
2025-04-24 13:50:14 +02:00
/// <summary>
/// Define for which components and events you want to receive messages.
/// </summary>
/// <param name="filterComponents">A list of components for which you want to receive messages. Use an empty list to receive messages from all components.</param>
/// <param name="events">A list of events for which you want to receive messages.</param>
protected void ApplyFilters ( ComponentBase [] filterComponents , Event [] events )
2024-06-30 15:26:28 +02:00
{
2024-09-15 12:30:07 +02:00
// Append the color theme changed event to the list of events:
var eventsList = new List < Event >( events )
{
2025-04-12 21:13:33 +02:00
Event . COLOR_THEME_CHANGED ,
Event . PLUGINS_RELOADED ,
2024-09-15 12:30:07 +02:00
};
2026-03-23 13:57:26 +01:00
this . MessageBus . ApplyFilters ( this , filterComponents , eventsList . ToHashSet ());
2024-06-30 15:26:28 +02:00
}
2025-04-27 16:13:15 +02:00
2026-08-23 21:15:37 +02:00
/// <summary>
/// Releases what this component has acquired. Override this instead of implementing
/// IDisposable again, so the deregistration from the message bus cannot be lost.
/// </summary>
2025-04-27 16:13:15 +02:00
protected virtual void DisposeResources ()
{
}
2026-08-23 21:15:37 +02:00
/// <summary>
/// Releases what this component has acquired and needs an await to release. Override this
/// instead of implementing IAsyncDisposable, see the remarks on DisposeAsync below.
/// </summary>
protected virtual ValueTask DisposeResourcesAsync () => ValueTask . CompletedTask ;
2025-04-27 16:13:15 +02:00
#region Implementation of IDisposable
public void Dispose ()
{
this . MessageBus . Unregister ( this );
this . DisposeResources ();
}
#endregion
2026-08-23 21:15:37 +02:00
#region Implementation of IAsyncDisposable
/// <summary>
/// Releases this component asynchronously.
/// </summary>
/// <remarks>
/// This base class implements both ways of disposing on purpose. Blazor calls only DisposeAsync
/// when a component offers both, so a derived component which implements IAsyncDisposable on
/// its own would silently skip everything Dispose does — above all the deregistration from the
/// message bus, which holds a strong reference to every receiver. Deriving components override
/// DisposeResources or DisposeResourcesAsync instead, and this stays the one place which knows
/// about both.
/// </remarks>
public async ValueTask DisposeAsync ()
{
await this . DisposeResourcesAsync ();
this . Dispose ();
}
#endregion
2024-06-30 15:26:28 +02:00
}