Permissions
Query the state of geolocation, notifications, camera, clipboard and any other browser permission from C# - without triggering a prompt.
@inject Bit.Butil.Permissions permissionsMDN reference
Returns true when the runtime exposes navigator.permissions. All evergreen browsers support it, but individual descriptor names still vary per engine.
@inject Bit.Butil.Permissions permissions
var isSupported = await permissions.IsSupported();Returns the current PermissionState for a descriptor name: Granted, Denied or Prompt. Querying never shows a prompt - it only reads the stored decision. Descriptor names the engine does not recognize come back as Unknown instead of throwing.
var state = await permissions.Query("geolocation");
if (state is PermissionState.Granted)
{
// safe to call the API without a prompt appearing
}Because Query is cheap and prompt-free, you can sweep a list of descriptors at startup to build a capability snapshot - handy for showing settings toggles in the right initial state.
string[] names = ["geolocation", "notifications", "camera", "microphone", "clipboard-read", "clipboard-write"];
foreach (var name in names)
{
var state = await permissions.Query(name);
// Granted / Denied / Prompt / Unknown
}A permission can change without your page doing anything - the user revokes camera access from the address bar, or grants notifications in site settings. SubscribeChange is the only way to notice that short of polling Query. It returns the state at subscription time alongside the subscription, so you don't have to call Query first and race the handler. An unrecognized descriptor yields Unknown with a null subscription - there is nothing to watch.
IAsyncDisposable
Bit.Butil.Permissions permissions
{
private ButilSubscription? _subscription;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender is false) return;
var (state, subscription) = await permissions.SubscribeChange("geolocation", newState =>
{
// the user granted or revoked it from browser UI
InvokeAsync(StateHasChanged);
});
if (subscription is null)
{
// no Permissions API, or this browser doesn't know the descriptor
return;
}
_subscription = subscription;
}
public async ValueTask DisposeAsync()
{
if (_subscription is not null) await _subscription.DisposeAsync();
}
}API reference
ValueTask<bool> IsSupported()Task<PermissionState> Query(string name)Task<(PermissionState State, ButilSubscription? Subscription)> SubscribeChange(string name, Action<PermissionState> handler)enum PermissionState { Granted, Denied, Prompt, Unknown }