Skip to content

Commit 50b1338

Browse files
committed
Implemented FileSystemAccessOptions as a global config or optionally Options set by each call.
1 parent 91b1d50 commit 50b1338

19 files changed

Lines changed: 428 additions & 401 deletions

samples/KristofferStrube.Blazor.FileSystemAccess.ServerExample/Pages/IndexedDB.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ In this sample we store the references to files that we have previously opened i
4242
StoredFileHandles = (await Task.WhenAll(Enumerable
4343
.Range(0, length)
4444
.Select(async i => {
45-
var fileHandle = this.FileSystemAccessService.CreateFileHandle(JSRuntime, await entries.InvokeAsync<IJSObjectReference>("at", i));
45+
var fileHandle = FileSystemFileHandle.Create(JSRuntime, await entries.InvokeAsync<IJSObjectReference>("at", i));
4646
return (false, fileHandle, await fileHandle.GetNameAsync());
4747
})
4848
)).ToList();

samples/KristofferStrube.Blazor.FileSystemAccess.WasmExample/Pages/IndexedDB.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ In this sample we store the references to files that we have previously opened i
4141
StoredFileHandles = (await Task.WhenAll(Enumerable
4242
.Range(0, length)
4343
.Select(async i => (false,
44-
await this.FileSystemAccessService.CreateFileHandleInProcessAsync(
44+
await FileSystemFileHandleInProcess.CreateAsync(
4545
JSRuntime,
4646
await entries.InvokeAsync<IJSInProcessObjectReference>("at", i))))
4747
)).ToList();

samples/KristofferStrube.Blazor.FileSystemAccess.WasmExample/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
builder.Services.AddFileSystemAccessServiceInProcess(options =>
1616
{
1717
// The file at this path in this example is manually copied to wwwroot folder
18-
options.ScriptPath = $"/content/custom-path/{FileSystemAccessOptions.DefaultNamespace}.js";
18+
// options.ScriptPath = $"/content/custom-path/{FileSystemAccessOptions.DefaultNamespace}.js";
1919
});
2020

2121
builder.Services.AddURLServiceInProcess();
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
using KristofferStrube.Blazor.FileSystemAccess.Extensions;
2+
using Microsoft.JSInterop;
3+
4+
namespace KristofferStrube.Blazor.FileSystemAccess;
5+
internal abstract class BaseFileSystemAccessService<TFsFileHandle, TFsDirectoryHandle, TObjReference> : IFileSystemAccessService<TFsFileHandle, TFsDirectoryHandle, TObjReference>
6+
where TFsFileHandle : FileSystemFileHandle
7+
where TFsDirectoryHandle : FileSystemDirectoryHandle
8+
where TObjReference : IJSObjectReference
9+
{
10+
protected readonly Lazy<Task<IJSObjectReference>> helperTask;
11+
protected readonly IJSRuntime jSRuntime;
12+
13+
public BaseFileSystemAccessService(IJSRuntime jSRuntime)
14+
{
15+
helperTask = new(() => jSRuntime.GetHelperAsync(FileSystemAccessOptions.DefaultInstance));
16+
this.jSRuntime = jSRuntime;
17+
}
18+
19+
#region ShowOpenFilePickerAsync
20+
21+
/// <summary>
22+
/// <see href="https://wicg.github.io/file-system-access/#api-showopenfilepicker">showOpenFilePicker() browser specs</see>
23+
/// </summary>
24+
/// <param name="openFilePickerOptions"></param>
25+
/// <returns></returns>
26+
public async Task<TFsFileHandle[]> ShowOpenFilePickerAsync(OpenFilePickerOptionsStartInWellKnownDirectory? openFilePickerOptions)
27+
=> await this.InternalShowOpenFilePickerAsync(openFilePickerOptions?.Serializable());
28+
29+
/// <summary>
30+
/// <see href="https://wicg.github.io/file-system-access/#api-showopenfilepicker">showOpenFilePicker() browser specs</see>
31+
/// </summary>
32+
/// <param name="openFilePickerOptions"></param>
33+
/// <returns></returns>
34+
public async Task<TFsFileHandle[]> ShowOpenFilePickerAsync(OpenFilePickerOptionsStartInWellKnownDirectory? openFilePickerOptions, FileSystemAccessOptions fsaOptions)
35+
=> await InternalShowOpenFilePickerAsync(openFilePickerOptions?.Serializable(), fsaOptions);
36+
37+
/// <summary>
38+
/// <see href="https://wicg.github.io/file-system-access/#api-showopenfilepicker">showOpenFilePicker() browser specs</see>
39+
/// </summary>
40+
/// <param name="openFilePickerOptions"></param>
41+
/// <returns></returns>
42+
public async Task<TFsFileHandle[]> ShowOpenFilePickerAsync(OpenFilePickerOptionsStartInFileSystemHandle? openFilePickerOptions)
43+
=> await this.InternalShowOpenFilePickerAsync(openFilePickerOptions?.Serializable());
44+
45+
/// <summary>
46+
/// <see href="https://wicg.github.io/file-system-access/#api-showopenfilepicker">showOpenFilePicker() browser specs</see>
47+
/// </summary>
48+
/// <param name="openFilePickerOptions"></param>
49+
/// <returns></returns>
50+
public async Task<TFsFileHandle[]> ShowOpenFilePickerAsync(OpenFilePickerOptionsStartInFileSystemHandle? openFilePickerOptions, FileSystemAccessOptions fsaOptions)
51+
=> await this.InternalShowOpenFilePickerAsync(openFilePickerOptions?.Serializable(), fsaOptions);
52+
53+
/// <summary>
54+
/// <see href="https://wicg.github.io/file-system-access/#api-showopenfilepicker">showOpenFilePicker() browser specs</see>
55+
/// </summary>
56+
/// <returns></returns>
57+
public async Task<TFsFileHandle[]> ShowOpenFilePickerAsync()
58+
=> await InternalShowOpenFilePickerAsync(null);
59+
60+
/// <summary>
61+
/// <see href="https://wicg.github.io/file-system-access/#api-showopenfilepicker">showOpenFilePicker() browser specs</see>
62+
/// </summary>
63+
/// <returns></returns>
64+
public async Task<TFsFileHandle[]> ShowOpenFilePickerAsync(FileSystemAccessOptions fsaOptions)
65+
=> await InternalShowOpenFilePickerAsync(null, fsaOptions);
66+
67+
protected async Task<TFsFileHandle[]> InternalShowOpenFilePickerAsync(object? options)
68+
=> await InternalShowOpenFilePickerAsync(options, FileSystemAccessOptions.DefaultInstance);
69+
protected async Task<TFsFileHandle[]> InternalShowOpenFilePickerAsync(object? options, FileSystemAccessOptions fsaOptions)
70+
{
71+
var helper = await helperTask.Value;
72+
var jSFileHandles = await jSRuntime.InvokeAsync<TObjReference>("window.showOpenFilePicker", options);
73+
var length = await helper.InvokeAsync<int>("size", jSFileHandles);
74+
75+
return await Task.WhenAll(
76+
Enumerable
77+
.Range(0, length)
78+
.Select(async i =>
79+
await this.CreateFileHandleAsync(
80+
jSRuntime,
81+
await jSFileHandles.InvokeAsync<TObjReference>("at", i),
82+
fsaOptions)
83+
)
84+
.ToArray()
85+
);
86+
}
87+
88+
#endregion
89+
90+
#region ShowSaveFilePickerAsync
91+
92+
/// <summary>
93+
/// <see href="https://wicg.github.io/file-system-access/#api-showsavefilepicker">showSaveFilePicker() browser specs</see>
94+
/// </summary>
95+
/// <param name="saveFilePickerOptions"></param>
96+
/// <returns></returns>
97+
public async Task<TFsFileHandle> ShowSaveFilePickerAsync(SaveFilePickerOptionsStartInWellKnownDirectory? saveFilePickerOptions)
98+
=> await InternalShowSaveFilePickerAsync(saveFilePickerOptions?.Serializable());
99+
100+
/// <summary>
101+
/// <see href="https://wicg.github.io/file-system-access/#api-showsavefilepicker">showSaveFilePicker() browser specs</see>
102+
/// </summary>
103+
/// <param name="saveFilePickerOptions"></param>
104+
/// <returns></returns>
105+
public async Task<TFsFileHandle> ShowSaveFilePickerAsync(SaveFilePickerOptionsStartInWellKnownDirectory? saveFilePickerOptions, FileSystemAccessOptions fsaOptions)
106+
=> await InternalShowSaveFilePickerAsync(saveFilePickerOptions?.Serializable(), fsaOptions);
107+
108+
/// <summary>
109+
/// <see href="https://wicg.github.io/file-system-access/#api-showsavefilepicker">showSaveFilePicker() browser specs</see>
110+
/// </summary>
111+
/// <param name="saveFilePickerOptions"></param>
112+
/// <returns></returns>
113+
public async Task<TFsFileHandle> ShowSaveFilePickerAsync(SaveFilePickerOptionsStartInFileSystemHandle? saveFilePickerOptions)
114+
=> await InternalShowSaveFilePickerAsync(saveFilePickerOptions?.Serializable());
115+
116+
/// <summary>
117+
/// <see href="https://wicg.github.io/file-system-access/#api-showsavefilepicker">showSaveFilePicker() browser specs</see>
118+
/// </summary>
119+
/// <param name="saveFilePickerOptions"></param>
120+
/// <returns></returns>
121+
public async Task<TFsFileHandle> ShowSaveFilePickerAsync(SaveFilePickerOptionsStartInFileSystemHandle? saveFilePickerOptions, FileSystemAccessOptions fsaOptions)
122+
=> await InternalShowSaveFilePickerAsync(saveFilePickerOptions?.Serializable(), fsaOptions);
123+
124+
/// <summary>
125+
/// <see href="https://wicg.github.io/file-system-access/#api-showsavefilepicker">showSaveFilePicker() browser specs</see>
126+
/// </summary>
127+
/// <returns></returns>
128+
public async Task<TFsFileHandle> ShowSaveFilePickerAsync()
129+
=> await InternalShowSaveFilePickerAsync(null);
130+
131+
/// <summary>
132+
/// <see href="https://wicg.github.io/file-system-access/#api-showsavefilepicker">showSaveFilePicker() browser specs</see>
133+
/// </summary>
134+
/// <returns></returns>
135+
public async Task<TFsFileHandle> ShowSaveFilePickerAsync(FileSystemAccessOptions options)
136+
=> await InternalShowSaveFilePickerAsync(null, options);
137+
138+
protected async Task<TFsFileHandle> InternalShowSaveFilePickerAsync(object? options)
139+
=> await this.InternalShowSaveFilePickerAsync(options, FileSystemAccessOptions.DefaultInstance);
140+
141+
protected async Task<TFsFileHandle> InternalShowSaveFilePickerAsync(object? options, FileSystemAccessOptions fsaOptions)
142+
{
143+
var jSFileHandle = await jSRuntime.InvokeAsync<TObjReference>("window.showSaveFilePicker", options);
144+
return await this.CreateFileHandleAsync(jSRuntime, jSFileHandle, fsaOptions);
145+
}
146+
147+
#endregion
148+
149+
#region ShowDirectoryPickerAsync
150+
151+
/// <summary>
152+
/// <see href="https://wicg.github.io/file-system-access/#api-showdirectorypicker">showDirectoryPicker() browser specs</see>
153+
/// </summary>
154+
/// <param name="directoryPickerOptions"></param>
155+
/// <returns></returns>
156+
public async Task<TFsDirectoryHandle> ShowDirectoryPickerAsync(DirectoryPickerOptionsStartInWellKnownDirectory? directoryPickerOptions)
157+
=> await InternalShowDirectoryPickerAsync(directoryPickerOptions?.Serializable());
158+
159+
/// <summary>
160+
/// <see href="https://wicg.github.io/file-system-access/#api-showdirectorypicker">showDirectoryPicker() browser specs</see>
161+
/// </summary>
162+
/// <param name="directoryPickerOptions"></param>
163+
/// <returns></returns>
164+
public async Task<TFsDirectoryHandle> ShowDirectoryPickerAsync(DirectoryPickerOptionsStartInWellKnownDirectory? directoryPickerOptions, FileSystemAccessOptions fasOptions)
165+
=> await InternalShowDirectoryPickerAsync(directoryPickerOptions?.Serializable(), fasOptions);
166+
167+
/// <summary>
168+
/// <see href="https://wicg.github.io/file-system-access/#api-showdirectorypicker">showDirectoryPicker() browser specs</see>
169+
/// </summary>
170+
/// <param name="directoryPickerOptions"></param>
171+
/// <returns></returns>
172+
public async Task<TFsDirectoryHandle> ShowDirectoryPickerAsync(DirectoryPickerOptionsStartInFileSystemHandle? directoryPickerOptions)
173+
=> await InternalShowDirectoryPickerAsync(directoryPickerOptions?.Serializable());
174+
175+
/// <summary>
176+
/// <see href="https://wicg.github.io/file-system-access/#api-showdirectorypicker">showDirectoryPicker() browser specs</see>
177+
/// </summary>
178+
/// <param name="directoryPickerOptions"></param>
179+
/// <returns></returns>
180+
public async Task<TFsDirectoryHandle> ShowDirectoryPickerAsync(DirectoryPickerOptionsStartInFileSystemHandle? directoryPickerOptions, FileSystemAccessOptions fasOptions)
181+
=> await InternalShowDirectoryPickerAsync(directoryPickerOptions?.Serializable(), fasOptions);
182+
183+
/// <summary>
184+
/// <see href="https://wicg.github.io/file-system-access/#api-showdirectorypicker">showDirectoryPicker() browser specs</see>
185+
/// </summary>
186+
/// <param name="directoryPickerOptions"></param>
187+
/// <returns></returns>
188+
public async Task<TFsDirectoryHandle> ShowDirectoryPickerAsync()
189+
=> await InternalShowDirectoryPickerAsync(null);
190+
191+
/// <summary>
192+
/// <see href="https://wicg.github.io/file-system-access/#api-showdirectorypicker">showDirectoryPicker() browser specs</see>
193+
/// </summary>
194+
/// <param name="directoryPickerOptions"></param>
195+
/// <returns></returns>
196+
public async Task<TFsDirectoryHandle> ShowDirectoryPickerAsync(FileSystemAccessOptions fasOptions)
197+
=> await InternalShowDirectoryPickerAsync(null, fasOptions);
198+
199+
protected async Task<TFsDirectoryHandle> InternalShowDirectoryPickerAsync(object? options)
200+
=> await InternalShowDirectoryPickerAsync(options, FileSystemAccessOptions.DefaultInstance);
201+
202+
protected async Task<TFsDirectoryHandle> InternalShowDirectoryPickerAsync(object? options, FileSystemAccessOptions fasOptions)
203+
{
204+
var jSFileHandle = await jSRuntime.InvokeAsync<TObjReference>("window.showDirectoryPicker", options);
205+
return await this.CreateDirectoryHandleAsync(jSRuntime, jSFileHandle, fasOptions);
206+
}
207+
208+
#endregion
209+
210+
#region GetOriginPrivateDirectoryAsync
211+
212+
/// <summary>
213+
/// <see href="https://wicg.github.io/file-system-access/#dom-storagemanager-getdirectory">getDirectory() for StorageManager browser specs</see>
214+
/// </summary>
215+
/// <returns></returns>
216+
public async Task<TFsDirectoryHandle> GetOriginPrivateDirectoryAsync()
217+
=> await this.GetOriginPrivateDirectoryAsync(FileSystemAccessOptions.DefaultInstance);
218+
219+
/// <summary>
220+
/// <see href="https://wicg.github.io/file-system-access/#dom-storagemanager-getdirectory">getDirectory() for StorageManager browser specs</see>
221+
/// </summary>
222+
/// <returns></returns>
223+
public async Task<TFsDirectoryHandle> GetOriginPrivateDirectoryAsync(FileSystemAccessOptions fasOptions)
224+
{
225+
var jSFileHandle = await jSRuntime.InvokeAsync<TObjReference>("navigator.storage.getDirectory");
226+
return await CreateDirectoryHandleAsync(jSRuntime, jSFileHandle, fasOptions);
227+
}
228+
229+
#endregion
230+
231+
#region Common Handling Methods
232+
233+
protected abstract Task<TFsFileHandle> CreateFileHandleAsync(IJSRuntime jSRuntime, TObjReference jSReference, FileSystemAccessOptions options);
234+
protected abstract Task<TFsDirectoryHandle> CreateDirectoryHandleAsync(IJSRuntime jSRuntime, TObjReference jSReference, FileSystemAccessOptions options);
235+
236+
#endregion
237+
238+
/// <summary>
239+
/// Meta method for the wrapper that checks if the API is available in the current browser.
240+
/// </summary>
241+
/// <returns></returns>
242+
public async Task<bool> IsSupportedAsync()
243+
{
244+
return
245+
await jSRuntime.InvokeAsync<bool>("window.hasOwnProperty", "showOpenFilePicker") &
246+
await jSRuntime.InvokeAsync<bool>("window.hasOwnProperty", "showSaveFilePicker") &
247+
await jSRuntime.InvokeAsync<bool>("window.hasOwnProperty", "showDirectoryPicker");
248+
}
249+
250+
public async ValueTask DisposeAsync()
251+
{
252+
if (helperTask.IsValueCreated)
253+
{
254+
IJSObjectReference module = await helperTask.Value;
255+
await module.DisposeAsync();
256+
}
257+
GC.SuppressFinalize(this);
258+
}
259+
}

src/KristofferStrube.Blazor.FileSystemAccess/BaseJSWrapper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ public abstract class BaseJSWrapper : IAsyncDisposable
88
public readonly IJSObjectReference JSReference;
99
protected readonly Lazy<Task<IJSObjectReference>> helperTask;
1010
protected readonly IJSRuntime jSRuntime;
11-
protected readonly FileSystemAccessOptions? options;
11+
protected readonly FileSystemAccessOptions options;
1212

1313
/// <summary>
1414
/// Constructs a wrapper instance for an equivalent JS instance.
1515
/// </summary>
1616
/// <param name="jSRuntime">An <see cref="IJSRuntime"/> instance.</param>
1717
/// <param name="jSReference">A JS reference to an existing JS instance that should be wrapped.</param>
18-
internal BaseJSWrapper(IJSRuntime jSRuntime, IJSObjectReference jSReference, FileSystemAccessOptions? options)
18+
internal BaseJSWrapper(IJSRuntime jSRuntime, IJSObjectReference jSReference, FileSystemAccessOptions options)
1919
{
2020
this.options = options;
2121
helperTask = new(async () => await jSRuntime.GetHelperAsync(options));
Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
1-
using Microsoft.Extensions.Options;
2-
using Microsoft.JSInterop;
1+
using Microsoft.JSInterop;
32

43
namespace KristofferStrube.Blazor.FileSystemAccess.Extensions;
54

65
internal static class IJSRuntimeExtensions
76
{
8-
internal static async Task<IJSObjectReference> GetHelperAsync(this IJSRuntime jSRuntime, FileSystemAccessOptions? options)
7+
internal static async Task<IJSObjectReference> GetHelperAsync(this IJSRuntime jSRuntime, FileSystemAccessOptions options)
98
=> await GetHelperAsync<IJSObjectReference>(jSRuntime, options);
109

11-
internal static async Task<IJSInProcessObjectReference> GetInProcessHelperAsync(this IJSRuntime jSRuntime, FileSystemAccessOptions? options) =>
10+
internal static async Task<IJSInProcessObjectReference> GetInProcessHelperAsync(this IJSRuntime jSRuntime, FileSystemAccessOptions options) =>
1211
await GetHelperAsync<IJSInProcessObjectReference>(jSRuntime, options);
1312

14-
static async Task<T> GetHelperAsync<T>(IJSRuntime jSRuntime, FileSystemAccessOptions? options) =>
13+
static async Task<T> GetHelperAsync<T>(IJSRuntime jSRuntime, FileSystemAccessOptions options) =>
1514
await jSRuntime.InvokeAsync<T>("import", GetScriptPath(options));
1615

17-
static string GetScriptPath(FileSystemAccessOptions? options) => options?.ScriptPath ?? FileSystemAccessOptions.DefaultPath;
16+
static string GetScriptPath(FileSystemAccessOptions options) => options.FullScriptPath;
1817
}
Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using Microsoft.Extensions.DependencyInjection;
2-
using Microsoft.Extensions.Options;
32
using Microsoft.JSInterop;
3+
using System.Diagnostics;
44

55
namespace KristofferStrube.Blazor.FileSystemAccess;
66

@@ -13,10 +13,7 @@ public static IServiceCollection AddFileSystemAccessService(this IServiceCollect
1313

1414
public static IServiceCollection AddFileSystemAccessService(this IServiceCollection serviceCollection, Action<FileSystemAccessOptions>? configure)
1515
{
16-
if (configure is not null)
17-
{
18-
serviceCollection.Configure(configure);
19-
}
16+
ConfigureFsaOptions(serviceCollection, configure);
2017

2118
return serviceCollection.AddScoped<IFileSystemAccessService, FileSystemAccessService>();
2219
}
@@ -28,17 +25,23 @@ public static IServiceCollection AddFileSystemAccessServiceInProcess(this IServi
2825

2926
public static IServiceCollection AddFileSystemAccessServiceInProcess(this IServiceCollection serviceCollection, Action<FileSystemAccessOptions>? configure)
3027
{
31-
32-
if (configure is not null)
33-
{
34-
serviceCollection.Configure(configure);
35-
}
28+
ConfigureFsaOptions(serviceCollection, configure);
3629

3730
return serviceCollection
38-
.AddScoped<IFileSystemAccessServiceInProcess>(
39-
sp => new FileSystemAccessServiceInProcess(
40-
(IJSInProcessRuntime)sp.GetRequiredService<IJSRuntime>(),
41-
sp.GetRequiredService<IOptions<FileSystemAccessOptions>>()))
42-
.AddScoped<IFileSystemAccessService>(sp => sp.GetRequiredService<IFileSystemAccessServiceInProcess>());
31+
.AddScoped<IFileSystemAccessServiceInProcess, FileSystemAccessServiceInProcess>();
32+
//.AddScoped(sp =>
33+
//{
34+
// var service = sp.GetRequiredService<IFileSystemAccessServiceInProcess>();
35+
// return (IFileSystemAccessService)service;
36+
//});
37+
}
38+
39+
static void ConfigureFsaOptions(IServiceCollection services, Action<FileSystemAccessOptions>? configure)
40+
{
41+
if (configure is null) { return; }
42+
43+
services.Configure(configure);
44+
configure(FileSystemAccessOptions.DefaultInstance);
4345
}
46+
4447
}

0 commit comments

Comments
 (0)