Skip to content

Commit

Permalink
feat(blazorui): add WebOTP feature to BitOtpInput #9600 (#9603)
Browse files Browse the repository at this point in the history
  • Loading branch information
msynk authored Jan 3, 2025
1 parent 2eb39bb commit b0e48f4
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 13 deletions.
12 changes: 11 additions & 1 deletion src/BlazorUI/Bit.BlazorUI/Components/Inputs/BitInputBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Bit.BlazorUI;
/// integrates with an <see cref="Microsoft.AspNetCore.Components.Forms.EditContext"/>, which must be supplied
/// as a cascading parameter.
/// </summary>
public abstract class BitInputBase<TValue> : BitComponentBase, IDisposable
public abstract class BitInputBase<TValue> : BitComponentBase, IDisposable, IAsyncDisposable
{
protected bool IsDisposed;
protected bool ValueHasBeenSet;
Expand Down Expand Up @@ -476,5 +476,15 @@ public void Dispose()
GC.SuppressFinalize(this);
}

public ValueTask DisposeAsync()
{
if (IsDisposed) return ValueTask.CompletedTask;

Dispose();
return DisposeAsync(true);
}

protected virtual void Dispose(bool disposing) { }

protected virtual ValueTask DisposeAsync(bool disposing) { return ValueTask.CompletedTask; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Bit.BlazorUI;

public partial class BitOtpInput : BitInputBase<string?>, IDisposable
public partial class BitOtpInput : BitInputBase<string?>
{
private string _labelId = default!;
private string?[] _inputIds = default!;
Expand Down Expand Up @@ -116,14 +116,16 @@ public partial class BitOtpInput : BitInputBase<string?>, IDisposable
/// </summary>
public ValueTask FocusAsync(int index = 0) => _inputRefs[index].FocusAsync();

[JSInvokable]
public async Task SetPastedData(string pastedValue)


[JSInvokable("SetValue")]
public async Task _SetValue(string value)
{
if (IsEnabled is false || InvalidValueBinding()) return;
if (pastedValue.HasNoValue()) return;
if (Type is BitInputType.Number && int.TryParse(pastedValue, out _) is false) return;
if (value.HasNoValue()) return;
if (Type is BitInputType.Number && int.TryParse(value, out _) is false) return;

SetInputsValue(pastedValue);
SetInputsValue(value);

CurrentValueAsString = string.Join(string.Empty, _inputValues);

Expand Down Expand Up @@ -195,7 +197,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender)

foreach (var inputRef in _inputRefs)
{
await _js.BitOtpInputSetup(_dotnetObj, inputRef);
await _js.BitOtpInputSetup(_Id, _dotnetObj, inputRef);
}
}

Expand All @@ -206,11 +208,12 @@ protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(fa
return true;
}

protected override void Dispose(bool disposing)
protected override async ValueTask DisposeAsync(bool disposing)
{
if (disposing)
{
_dotnetObj?.Dispose();
await _js.BitOtpInputDispose(_Id);
}

base.Dispose(disposing);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,46 @@
namespace BitBlazorUI {
export class OtpInput {
public static setup(dotnetReference: DotNetObject, input: HTMLInputElement) {
private static abortControllers: { [key: string]: AbortController } = {};

public static setup(id: string, dotnetObj: DotNetObject, input: HTMLInputElement) {
input.addEventListener('focus', (e: any) => {
e.target?.select();
});

input.addEventListener('paste', async e => {
e.preventDefault();
let pastedValue = e.clipboardData?.getData('Text');
await dotnetReference.invokeMethodAsync("SetPastedData", pastedValue);
await dotnetObj.invokeMethodAsync("SetValue", pastedValue);
});

OtpInput.setupSmsAutofill(id, dotnetObj);
}

public static dispose(id: string) {
const ac = OtpInput.abortControllers[id];
if (!ac) return;

ac.abort();
delete OtpInput.abortControllers[id];
}

private static setupSmsAutofill(id: string, dotnetObj: DotNetObject) {
if (!('OTPCredential' in window)) return;

const abortCtrl = new AbortController();
OtpInput.abortControllers[id] = abortCtrl;

navigator.credentials.get({
otp: { transport: ['sms'] },
signal: abortCtrl.signal
} as any).then(async (otp: any) => {
await dotnetObj.invokeMethodAsync("SetValue", otp.code);
abortCtrl.abort();
delete OtpInput.abortControllers[id];
}).catch(async (err: any) => {
abortCtrl.abort();
delete OtpInput.abortControllers[id];
})
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@

internal static class BitOtpInputJsRuntimeExtensions
{
internal static ValueTask BitOtpInputSetup(this IJSRuntime jsRuntime, DotNetObjectReference<BitOtpInput> obj, ElementReference input)
internal static ValueTask BitOtpInputSetup(this IJSRuntime jsRuntime, string id, DotNetObjectReference<BitOtpInput> obj, ElementReference input)
{
return jsRuntime.InvokeVoid("BitBlazorUI.OtpInput.setup", obj, input);
return jsRuntime.InvokeVoid("BitBlazorUI.OtpInput.setup", id, obj, input);
}

internal static ValueTask BitOtpInputDispose(this IJSRuntime jsRuntime, string id)
{
return jsRuntime.InvokeVoid("BitBlazorUI.OtpInput.dispose", id);
}
}

0 comments on commit b0e48f4

Please sign in to comment.