mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-05-20 13:52:15 +00:00
The dialog for file attachments will only open once.
This commit is contained in:
parent
09258c7548
commit
3d03d36f0c
@ -21,6 +21,7 @@
|
||||
<MudTooltip Text="@T("Number of attachments")" Placement="Placement.Bottom">
|
||||
<MudBadge Content="@this.Content.FileAttachments.Count" Color="Color.Primary" Overlap="true" BadgeClass="sources-card-header">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.AttachFile"
|
||||
Disabled="@this.isOpeningAttachmentsDialog"
|
||||
OnClick="@this.OpenAttachmentsDialog"/>
|
||||
</MudBadge>
|
||||
</MudTooltip>
|
||||
@ -149,4 +150,4 @@
|
||||
}
|
||||
}
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
</MudCard>
|
||||
@ -102,6 +102,7 @@ public partial class ContentBlockComponent : MSGComponentBase, IAsyncDisposable
|
||||
private ElementReference mathContentContainer;
|
||||
private string lastMathRenderSignature = string.Empty;
|
||||
private bool hasActiveMathContainer;
|
||||
private bool isOpeningAttachmentsDialog;
|
||||
private bool isDisposed;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
@ -600,8 +601,20 @@ public partial class ContentBlockComponent : MSGComponentBase, IAsyncDisposable
|
||||
|
||||
private async Task OpenAttachmentsDialog()
|
||||
{
|
||||
var result = await ReviewAttachmentsDialog.OpenDialogAsync(this.DialogService, this.Content.FileAttachments.ToHashSet());
|
||||
this.Content.FileAttachments = result.ToList();
|
||||
if (this.isOpeningAttachmentsDialog)
|
||||
return;
|
||||
|
||||
this.isOpeningAttachmentsDialog = true;
|
||||
|
||||
try
|
||||
{
|
||||
var result = await ReviewAttachmentsDialog.OpenDialogAsync(this.DialogService, this.Content.FileAttachments.ToHashSet());
|
||||
this.Content.FileAttachments = result.ToList();
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.isOpeningAttachmentsDialog = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
@ -613,4 +626,4 @@ public partial class ContentBlockComponent : MSGComponentBase, IAsyncDisposable
|
||||
await this.DisposeMathContainerIfNeededAsync();
|
||||
this.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,11 +6,11 @@
|
||||
@if (this.isDraggingOver)
|
||||
{
|
||||
<MudBadge
|
||||
Content="@this.DocumentPaths.Count"
|
||||
Color="Color.Primary"
|
||||
Overlap="true"
|
||||
Class="cursor-pointer"
|
||||
OnClick="@this.OpenAttachmentsDialog">
|
||||
Content="@this.DocumentPaths.Count"
|
||||
Color="Color.Primary"
|
||||
Overlap="true"
|
||||
Class="cursor-pointer"
|
||||
OnClick="@this.OpenAttachmentsDialog">
|
||||
<MudLink OnClick="@this.AddFilesManually" Style="text-decoration: none;">
|
||||
<MudTextField T="string"
|
||||
Text="@DROP_FILES_HERE_TEXT"
|
||||
@ -19,6 +19,7 @@
|
||||
Typo="Typo.body2"
|
||||
Variant="Variant.Outlined"
|
||||
ReadOnly="true"
|
||||
Disabled="@(this.isOpeningFileDialog || this.isOpeningAttachmentsDialog)"
|
||||
/>
|
||||
</MudLink>
|
||||
</MudBadge>
|
||||
@ -35,6 +36,7 @@
|
||||
<MudIconButton
|
||||
Icon="@Icons.Material.Filled.AttachFile"
|
||||
Color="Color.Default"
|
||||
Disabled="@(this.isOpeningFileDialog || this.isOpeningAttachmentsDialog)"
|
||||
OnClick="@this.AddFilesManually"/>
|
||||
</MudBadge>
|
||||
</MudTooltip>
|
||||
@ -45,6 +47,7 @@
|
||||
<MudIconButton
|
||||
Icon="@Icons.Material.Filled.AttachFile"
|
||||
Color="Color.Default"
|
||||
Disabled="@(this.isOpeningFileDialog || this.isOpeningAttachmentsDialog)"
|
||||
OnClick="@this.AddFilesManually"/>
|
||||
</MudTooltip>
|
||||
}
|
||||
@ -60,6 +63,7 @@ else
|
||||
Variant="Variant.Filled"
|
||||
StartIcon="@Icons.Material.Filled.Add"
|
||||
Color="Color.Primary"
|
||||
Disabled="@(this.isOpeningFileDialog || this.isOpeningAttachmentsDialog)"
|
||||
OnClick="@(() => this.AddFilesManually())"
|
||||
Style="vertical-align: top; margin-top: -2px;"
|
||||
Size="Size.Small">
|
||||
|
||||
@ -78,6 +78,8 @@ public partial class AttachDocuments : MSGComponentBase
|
||||
private uint numDropAreasAboveThis;
|
||||
private bool isComponentHovered;
|
||||
private bool isDraggingOver;
|
||||
private bool isOpeningFileDialog;
|
||||
private bool isOpeningAttachmentsDialog;
|
||||
|
||||
#region Overrides of MSGComponentBase
|
||||
|
||||
@ -202,40 +204,64 @@ public partial class AttachDocuments : MSGComponentBase
|
||||
|
||||
private async Task AddFilesManually()
|
||||
{
|
||||
if (this.isOpeningFileDialog)
|
||||
return;
|
||||
|
||||
this.isOpeningFileDialog = true;
|
||||
|
||||
try
|
||||
{
|
||||
// Ensure that Pandoc is installed and ready:
|
||||
var pandocState = await this.PandocAvailabilityService.EnsureAvailabilityAsync(
|
||||
showSuccessMessage: false,
|
||||
showDialog: true);
|
||||
var pandocState = await this.PandocAvailabilityService.EnsureAvailabilityAsync(
|
||||
showSuccessMessage: false,
|
||||
showDialog: true);
|
||||
|
||||
// If Pandoc is not available (user cancelled installation), abort file selection:
|
||||
if (!pandocState.IsAvailable)
|
||||
{
|
||||
this.Logger.LogWarning("The user cancelled the Pandoc installation or Pandoc is not available. Aborting file selection.");
|
||||
return;
|
||||
if (!pandocState.IsAvailable)
|
||||
{
|
||||
this.Logger.LogWarning("The user cancelled the Pandoc installation or Pandoc is not available. Aborting file selection.");
|
||||
return;
|
||||
}
|
||||
|
||||
var selectFiles = await this.RustService.SelectFiles(T("Select files to attach"));
|
||||
if (selectFiles.UserCancelled)
|
||||
return;
|
||||
|
||||
foreach (var selectedFilePath in selectFiles.SelectedFilePaths)
|
||||
{
|
||||
if (!File.Exists(selectedFilePath))
|
||||
continue;
|
||||
|
||||
if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(FileExtensionValidation.UseCase.ATTACHING_CONTENT, selectedFilePath, this.ValidateMediaFileTypes, this.Provider))
|
||||
continue;
|
||||
|
||||
this.DocumentPaths.Add(FileAttachment.FromPath(selectedFilePath));
|
||||
}
|
||||
|
||||
await this.DocumentPathsChanged.InvokeAsync(this.DocumentPaths);
|
||||
await this.OnChange(this.DocumentPaths);
|
||||
}
|
||||
|
||||
var selectFiles = await this.RustService.SelectFiles(T("Select files to attach"));
|
||||
if (selectFiles.UserCancelled)
|
||||
return;
|
||||
|
||||
foreach (var selectedFilePath in selectFiles.SelectedFilePaths)
|
||||
finally
|
||||
{
|
||||
if (!File.Exists(selectedFilePath))
|
||||
continue;
|
||||
|
||||
if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(FileExtensionValidation.UseCase.ATTACHING_CONTENT, selectedFilePath, this.ValidateMediaFileTypes, this.Provider))
|
||||
continue;
|
||||
|
||||
this.DocumentPaths.Add(FileAttachment.FromPath(selectedFilePath));
|
||||
this.isOpeningFileDialog = false;
|
||||
}
|
||||
|
||||
await this.DocumentPathsChanged.InvokeAsync(this.DocumentPaths);
|
||||
await this.OnChange(this.DocumentPaths);
|
||||
}
|
||||
|
||||
private async Task OpenAttachmentsDialog()
|
||||
{
|
||||
this.DocumentPaths = await ReviewAttachmentsDialog.OpenDialogAsync(this.DialogService, this.DocumentPaths);
|
||||
if (this.isOpeningAttachmentsDialog)
|
||||
return;
|
||||
|
||||
this.isOpeningAttachmentsDialog = true;
|
||||
|
||||
try
|
||||
{
|
||||
this.DocumentPaths = await ReviewAttachmentsDialog.OpenDialogAsync(this.DialogService, this.DocumentPaths);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.isOpeningAttachmentsDialog = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ClearAllFiles()
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
@inherits MSGComponentBase
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Description" OnClick="@(async () => await this.SelectFile())" Variant="Variant.Filled" Class="mb-3" Disabled="@this.Disabled">
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Description" OnClick="@(async () => await this.SelectFile())" Variant="Variant.Filled" Class="mb-3" Disabled="@(this.Disabled || this.isSelectingFile)">
|
||||
@if (string.IsNullOrWhiteSpace(this.Text))
|
||||
{
|
||||
@T("Use file content as input")
|
||||
@ -8,4 +8,4 @@
|
||||
{
|
||||
@this.Text
|
||||
}
|
||||
</MudButton>
|
||||
</MudButton>
|
||||
@ -30,53 +30,64 @@ public partial class ReadFileContent : MSGComponentBase
|
||||
|
||||
[Inject]
|
||||
private PandocAvailabilityService PandocAvailabilityService { get; init; } = null!;
|
||||
|
||||
private bool isSelectingFile;
|
||||
|
||||
private async Task SelectFile()
|
||||
{
|
||||
if (this.Disabled)
|
||||
if (this.Disabled || this.isSelectingFile)
|
||||
return;
|
||||
|
||||
// Ensure that Pandoc is installed and ready:
|
||||
var pandocState = await this.PandocAvailabilityService.EnsureAvailabilityAsync(
|
||||
showSuccessMessage: false,
|
||||
showDialog: true);
|
||||
|
||||
// Check if Pandoc is available after the check / installation:
|
||||
if (!pandocState.IsAvailable)
|
||||
{
|
||||
this.Logger.LogWarning("The user cancelled the Pandoc installation or Pandoc is not available. Aborting file selection.");
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedFile = await this.RustService.SelectFile(T("Select file to read its content"));
|
||||
if (selectedFile.UserCancelled)
|
||||
{
|
||||
this.Logger.LogInformation("User cancelled the file selection");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!File.Exists(selectedFile.SelectedFilePath))
|
||||
{
|
||||
this.Logger.LogWarning("Selected file does not exist: '{FilePath}'", selectedFile.SelectedFilePath);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(FileExtensionValidation.UseCase.DIRECTLY_LOADING_CONTENT, selectedFile.SelectedFilePath))
|
||||
{
|
||||
this.Logger.LogWarning("User attempted to load unsupported file: {FilePath}", selectedFile.SelectedFilePath);
|
||||
return;
|
||||
}
|
||||
this.isSelectingFile = true;
|
||||
|
||||
try
|
||||
{
|
||||
var fileContent = await UserFile.LoadFileData(selectedFile.SelectedFilePath, this.RustService, this.DialogService);
|
||||
await this.FileContentChanged.InvokeAsync(fileContent);
|
||||
this.Logger.LogInformation("Successfully loaded file content: {FilePath}", selectedFile.SelectedFilePath);
|
||||
// Ensure that Pandoc is installed and ready:
|
||||
var pandocState = await this.PandocAvailabilityService.EnsureAvailabilityAsync(
|
||||
showSuccessMessage: false,
|
||||
showDialog: true);
|
||||
|
||||
// Check if Pandoc is available after the check / installation:
|
||||
if (!pandocState.IsAvailable)
|
||||
{
|
||||
this.Logger.LogWarning("The user cancelled the Pandoc installation or Pandoc is not available. Aborting file selection.");
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedFile = await this.RustService.SelectFile(T("Select file to read its content"));
|
||||
if (selectedFile.UserCancelled)
|
||||
{
|
||||
this.Logger.LogInformation("User cancelled the file selection");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!File.Exists(selectedFile.SelectedFilePath))
|
||||
{
|
||||
this.Logger.LogWarning("Selected file does not exist: '{FilePath}'", selectedFile.SelectedFilePath);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(FileExtensionValidation.UseCase.DIRECTLY_LOADING_CONTENT, selectedFile.SelectedFilePath))
|
||||
{
|
||||
this.Logger.LogWarning("User attempted to load unsupported file: {FilePath}", selectedFile.SelectedFilePath);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var fileContent = await UserFile.LoadFileData(selectedFile.SelectedFilePath, this.RustService, this.DialogService);
|
||||
await this.FileContentChanged.InvokeAsync(fileContent);
|
||||
this.Logger.LogInformation("Successfully loaded file content: {FilePath}", selectedFile.SelectedFilePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.Logger.LogError(ex, "Failed to load file content: {FilePath}", selectedFile.SelectedFilePath);
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Error, T("Failed to load file content")));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
finally
|
||||
{
|
||||
this.Logger.LogError(ex, "Failed to load file content: {FilePath}", selectedFile.SelectedFilePath);
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Error, T("Failed to load file content")));
|
||||
this.isSelectingFile = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -13,7 +13,7 @@
|
||||
Variant="Variant.Outlined"
|
||||
/>
|
||||
|
||||
<MudButton StartIcon="@Icons.Material.Filled.FolderOpen" Variant="Variant.Outlined" Color="Color.Primary" Disabled="this.Disabled" OnClick="@this.OpenFileDialog">
|
||||
<MudButton StartIcon="@Icons.Material.Filled.FolderOpen" Variant="Variant.Outlined" Color="Color.Primary" Disabled="@(this.Disabled || this.isOpeningFileDialog)" OnClick="@this.OpenFileDialog">
|
||||
@T("Choose File")
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
@ -35,6 +35,7 @@ public partial class SelectFile : MSGComponentBase
|
||||
protected ILogger<SelectFile> Logger { get; init; } = null!;
|
||||
|
||||
private static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new();
|
||||
private bool isOpeningFileDialog;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
@ -55,10 +56,22 @@ public partial class SelectFile : MSGComponentBase
|
||||
|
||||
private async Task OpenFileDialog()
|
||||
{
|
||||
var response = await this.RustService.SelectFile(this.FileDialogTitle, this.Filter, string.IsNullOrWhiteSpace(this.File) ? null : this.File);
|
||||
this.Logger.LogInformation($"The user selected the file '{response.SelectedFilePath}'.");
|
||||
if (this.Disabled || this.isOpeningFileDialog)
|
||||
return;
|
||||
|
||||
if (!response.UserCancelled)
|
||||
this.InternalFileChanged(response.SelectedFilePath);
|
||||
this.isOpeningFileDialog = true;
|
||||
|
||||
try
|
||||
{
|
||||
var response = await this.RustService.SelectFile(this.FileDialogTitle, this.Filter, string.IsNullOrWhiteSpace(this.File) ? null : this.File);
|
||||
this.Logger.LogInformation($"The user selected the file '{response.SelectedFilePath}'.");
|
||||
|
||||
if (!response.UserCancelled)
|
||||
this.InternalFileChanged(response.SelectedFilePath);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.isOpeningFileDialog = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user