@slorello
Frontends Without Javascript, The
Promise of Blazor
Steve Lorello
.NET Developer Advocate @Vonage
Twitter: @slorello
@slorello
1. What is Blazor?
2. Different Flavors of Blazor
3. Blazor Server - with Examples
4. Blazor WASM - with Examples
5. Combine Blazor, SignalR and Entity Framework
6. Some Fun Examples!
Agenda
@slorello
What is Blazor?
@slorello
● Blazor is a .NET web framework for building client UIs
● Build your apps in C# rather than JavaScript
● Share Server/Client application logic written for .NET
● Recycle Components between projects
● No Transpilation
● Use your favorite .NET libraries
What is Blazor?
@slorello
● Components are the backbone of Blazor
● Flexible UI elements with rendering logic
● Components are written in Razor markup (HTML + C#)
● Components written in .razor files
● Components Can be shared between projects or even
via NuGet
Components
@slorello
SignalR
@slorello
@slorello Source: https://anthonygiretti.com/
@slorello
Flavors of Blazor
@slorello
● Blazor Server
● Blazor WebAssembly
Flavors of Blazor
@slorello
Blazor Server
@slorello Source: https://docs.microsoft.com/
Blazor Server
@slorello
dotnet new blaozrserver -n BlazorServerHelloWorld
@slorello
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary"
@onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
Counter.razor
@slorello
Counter.razor
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary"
@onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
@slorello
Counter.razor
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary"
@onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
@slorello
Counter.razor
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary"
@onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
@slorello
Counter.razor
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary"
@onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
@slorello
Services
@slorello
WhatsApp Service
@slorello
dotnet add package Vonage
@slorello
public class WhatsAppService
{
private readonly IConfiguration _config;
public WhatsAppService(IConfiguration config)
{
_config = config;
}
}
WhatsAppService.cs
@slorello
public string SendWhatsAppMessage(string toNum, string fromNum, string text){
var appId = _config["APP_ID"];
var privateKey = _config["privateKeyPath"];
var creds = Credentials.FromAppIdAndPrivateKeyPath(appId, privateKey);
var content = new { type = "text", text };
var message = new { content };
var to = new { type = "whatsapp", number = toNum };
var from = new { type = "whatsapp", number = fromNum };
var request = new { to, from, message };
var uri = new Uri("https://messages-sandbox.nexmo.com/v0.1/messages");
var response = ApiRequest.DoRequestWithJsonContent<JObject>
("POST", uri, request, ApiRequest.AuthType.Bearer, creds);
return response["message_uuid"].ToString();
}
WhatsAppService.cs
@slorello
public string SendWhatsAppMessage(string toNum, string fromNum, string text){
var appId = _config["APP_ID"];
var privateKey = _config["privateKeyPath"];
var creds = Credentials.FromAppIdAndPrivateKeyPath(appId, privateKey);
var content = new { type = "text", text };
var message = new { content };
var to = new { type = "whatsapp", number = toNum };
var from = new { type = "whatsapp", number = fromNum };
var request = new { to, from, message };
var uri = new Uri("https://messages-sandbox.nexmo.com/v0.1/messages");
var response = ApiRequest.DoRequestWithJsonContent<JObject>
("POST", uri, request, ApiRequest.AuthType.Bearer, creds);
return response["message_uuid"].ToString();
}
WhatsAppService.cs
@slorello
public string SendWhatsAppMessage(string toNum, string fromNum, string text){
var appId = _config["APP_ID"];
var privateKey = _config["privateKeyPath"];
var creds = Credentials.FromAppIdAndPrivateKeyPath(appId, privateKey);
var content = new { type = "text", text };
var message = new { content };
var to = new { type = "whatsapp", number = toNum };
var from = new { type = "whatsapp", number = fromNum };
var request = new { to, from, message };
var uri = new Uri("https://messages-sandbox.nexmo.com/v0.1/messages");
var response = ApiRequest.DoRequestWithJsonContent<JObject>
("POST", uri, request, ApiRequest.AuthType.Bearer, creds);
return response["message_uuid"].ToString();
}
WhatsAppService.cs
@slorello
public string SendWhatsAppMessage(string toNum, string fromNum, string text){
var appId = _config["APP_ID"];
var privateKey = _config["privateKeyPath"];
var creds = Credentials.FromAppIdAndPrivateKeyPath(appId, privateKey);
var content = new { type = "text", text };
var message = new { content };
var to = new { type = "whatsapp", number = toNum };
var from = new { type = "whatsapp", number = fromNum };
var request = new { to, from, message };
var uri = new Uri("https://messages-sandbox.nexmo.com/v0.1/messages");
var response = ApiRequest.DoRequestWithJsonContent<JObject>
("POST", uri, request, ApiRequest.AuthType.Bearer, creds);
return response["message_uuid"].ToString();
}
WhatsAppService.cs
@slorello
public string SendWhatsAppMessage(string toNum, string fromNum, string text){
var appId = _config["APP_ID"];
var privateKey = _config["privateKeyPath"];
var creds = Credentials.FromAppIdAndPrivateKeyPath(appId, privateKey);
var content = new { type = "text", text };
var message = new { content };
var to = new { type = "whatsapp", number = toNum };
var from = new { type = "whatsapp", number = fromNum };
var request = new { to, from, message };
var uri = new Uri("https://messages-sandbox.nexmo.com/v0.1/messages");
var response = ApiRequest.DoRequestWithJsonContent<JObject>
("POST", uri, request, ApiRequest.AuthType.Bearer, creds);
return response["message_uuid"].ToString();
}
WhatsAppService.cs
@slorello
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
services.AddSingleton<WhatsAppService>();
}
Startup.cs
@slorello
@page "/"
@using BlazorServerHelloWorld.Data
@inject WhatsAppService WhatsappService
Index.razor
@slorello
<h1>Send a WhatsApp Message!</h1>
Welcome to your new app Fill out the below form to send a message.
<br />
to:
<input id="to" @bind="@To" class="input-group-text" />
from:
<input id="from" @bind="From" class="input-group-text" />
text:
<input id="text" @bind="Text" class="input-group-text" />
@if (!string.IsNullOrEmpty(MessageId))
{
<p>Message sent with id: @MessageId</p>
}
<button class="btn btn-primary" @onclick="SendWhatsApp">Send WhatsApp Message</button>
Index.razor
@slorello
@code{
private string To;
private string From;
private string Text;
private string MessageId;
private void SendWhatsApp()
{
MessageId = WhatsappService.SendWhatsAppMessage(To, From, Text);
To = "";
From = "";
Text = "";
}
}
Index.razor
@slorello
Demo
@slorello
Blazor WebAssembly
@slorello Source: https://docs.microsoft.com/
Blazor WebAssembly
@slorello
dotnet new blazorwasm -n BlazorWasmHelloWorld
@slorello
@slorello
@slorello
dotnet new blazorwasm -ho -n testBlazorHosted
@slorello
@slorello
@slorello
@slorello
@slorello
Receive WhatsApp Messages
@slorello
Build Model
@slorello
Add Entity Framework
@slorello
System.ComponentModel.Annotations
@slorello
public class WhatsAppMessage
{
[Key]
public string MessageId { get; set; }
public string To { get; set; }
public string From { get; set; }
public string Text { get; set; }
}
WhatsAppMessage.cs
@slorello
public class WhatsAppContext : DbContext
{
public DbSet<WhatsAppMessage> Messages { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite("Data Source=VonageWhatsApp.db");
}
WhatsAppContext.cs
@slorello
dotnet ef migrations add InitialCreate
dotnet ef database update
@slorello
services.AddDbContext<WhatsAppContext>();
@slorello
Build Controller
@slorello
[Route("api/[controller]")]
[ApiController]
public class WhatsAppController : ControllerBase
{
private readonly WhatsAppContext _db;
public WhatsAppController(WhatsAppContext db)
{
_db = db;
}
}
WhatsAppController.cs
@slorello
[Route("/webhooks/inbound-message")]
[HttpPost]
public ActionResult InboundWhatsAppMessage()
{
var inbound = WebhookParser.ParseWebhook<JObject>(Request.Body,
Request.ContentType);
var message = new WhatsAppMessage
{
MessageId = inbound["message_uuid"].ToString(),
From = inbound["from"]["number"].ToString(),
To = inbound["to"]["number"].ToString(),
Text = inbound["message"]["content"]["text"].ToString()
};
_db.Messages.Add(message);
_db.SaveChanges();
return Ok();
}
WhatsAppController.cs
@slorello
dashboard.nexmo.com
@slorello
[Route("getMessage")]
[HttpGet]
public ActionResult GetMessages()
{
return Ok(_db.Messages.Take(10));
}
WhatsAppController.cs
@slorello
Build Frontend
@slorello
@page "/"
@using WhatsAppBlazorInboundEntity.Shared
@inject HttpClient HttpClient
index.razor
@slorello
index.razor
<div class="x-display-table">
<h2>Inbound Messages</h2>
<table class="table">
<thead>
<tr>
<th>Message Id</th>
<th>From</th>
<th>To</th>
<th>Text</th>
</tr>
</thead>
<tbody>
@foreach (var message in _messages)
{
<tr>
<td>@message.MessageId</td>
<td>@message.From</td>
<td>@message.To</td>
<td>@message.Text</td>
</tr>
}
</tbody>
</table>
</div>
@slorello
<div class="x-display-table">
<h2>Inbound Messages</h2>
<table class="table">
<thead>
<tr>
<th>Message Id</th>
<th>From</th>
<th>To</th>
<th>Text</th>
</tr>
</thead>
<tbody>
@foreach (var message in _messages)
{
<tr>
<td>@message.MessageId</td>
<td>@message.From</td>
<td>@message.To</td>
<td>@message.Text</td>
</tr>
}
</tbody>
</table>
</div>
index.razor
@slorello
@code{
private List<WhatsAppMessage> _messages = new List<WhatsAppMessage>();
protected override async Task OnInitializedAsync()
{
_messages = await HttpClient.GetFromJsonAsync<List<WhatsAppMessage>>
("/api/WhatsApp/getMessages");
}
}
index.razor
@slorello
Demo
@slorello
Let’s build something Reactive with SignalR
@slorello
dotnet new blazorwasm -ho -n SpeechTranslatorBlazor
@slorello
● PSTN -> WebSockets
● Azure Cognitive Services
● Blazor WASM frontend
● SiganlR Connection
@slorello
public class Translation
{
public string UUID { get; set; }
public string Text { get; set; }
public string LanguageSpoken { get; set; }
public string LanguageTranslated { get; set; }
}
Translation.cs
@slorello
public class TranslationHub : Hub{}
TranslationHub.cs
@slorello
Configure Middleware
@slorello
endpoints.MapHub<Hubs.TranslationHub>("/TranslationHub");
Configure
services.AddSignalR();
Configure Services
Startup.cs
@slorello
var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(120),
ReceiveBufferSize = 640
};
app.UseWebSockets(webSocketOptions);
Startup.cs
@slorello
endpoints.Map("/ws", async (context) => {
if (context.WebSockets.IsWebSocketRequest)
{
var hub = (IHubContext<TranslationHub>)app.ApplicationServices.
GetService(typeof(IHubContext<TranslationHub>));
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
using (var engine = new TranslationEngine(Configuration, hub))
{
await engine.ReceiveAudioOnWebSocket(context, webSocket);
}
}
else
{
context.Response.StatusCode = 400;
}
});
Startup.cs
@slorello
endpoints.Map("/ws", async (context) => {
if (context.WebSockets.IsWebSocketRequest)
{
var hub = (IHubContext<TranslationHub>)app.ApplicationServices.
GetService(typeof(IHubContext<TranslationHub>));
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
using (var engine = new TranslationEngine(Configuration, hub))
{
await engine.ReceiveAudioOnWebSocket(context, webSocket);
}
}
else
{
context.Response.StatusCode = 400;
}
});
Startup.cs
@slorello
endpoints.Map("/ws", async (context) => {
if (context.WebSockets.IsWebSocketRequest)
{
var hub = (IHubContext<TranslationHub>)app.ApplicationServices.
GetService(typeof(IHubContext<TranslationHub>));
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
using (var engine = new TranslationEngine(Configuration, hub))
{
await engine.ReceiveAudioOnWebSocket(context, webSocket);
}
}
else
{
context.Response.StatusCode = 400;
}
});
Startup.cs
@slorello
endpoints.Map("/ws", async (context) => {
if (context.WebSockets.IsWebSocketRequest)
{
var hub = (IHubContext<TranslationHub>)app.ApplicationServices.
GetService(typeof(IHubContext<TranslationHub>));
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
using (var engine = new TranslationEngine(Configuration, hub))
{
await engine.ReceiveAudioOnWebSocket(context, webSocket);
}
}
else
{
context.Response.StatusCode = 400;
}
});
Startup.cs
@slorello
Voice Controller
@slorello
[Route("/webhooks/answer")]
[HttpGet]
public ActionResult Answer()
{
}
VoiceController.cs
@slorello
var host = Request.Host.ToString();
var webSocketAction = new ConnectAction()
{
Endpoint = new[]
{
new WebsocketEndpoint()
{
Uri = $"ws://{host}/ws",
ContentType="audio/l16;rate=16000",
Headers = new Translation
{
UUID = Request.Query["uuid"].ToString(),
LanguageSpoken = "en-US",
LanguageTranslated = "hi-IN"
}
}
}
};
var ncco = new Ncco(webSocketAction);
return Ok(ncco.ToString());
VoiceController.cs
@slorello
var host = Request.Host.ToString();
var webSocketAction = new ConnectAction()
{
Endpoint = new[]
{
new WebsocketEndpoint()
{
Uri = $"ws://{host}/ws",
ContentType="audio/l16;rate=16000",
Headers = new Translation
{
UUID = Request.Query["uuid"].ToString(),
LanguageSpoken = "en-US",
LanguageTranslated = "hi-IN"
}
}
}
};
var ncco = new Ncco(webSocketAction);
return Ok(ncco.ToString());
VoiceController.cs
@slorello
var host = Request.Host.ToString();
var webSocketAction = new ConnectAction()
{
Endpoint = new[]
{
new WebsocketEndpoint()
{
Uri = $"ws://{host}/ws",
ContentType="audio/l16;rate=16000",
Headers = new Translation
{
UUID = Request.Query["uuid"].ToString(),
LanguageSpoken = "en-US",
LanguageTranslated = "hi-IN"
}
}
}
};
var ncco = new Ncco(webSocketAction);
return Ok(ncco.ToString());
VoiceController.cs
@slorello
var host = Request.Host.ToString();
var webSocketAction = new ConnectAction()
{
Endpoint = new[]
{
new WebsocketEndpoint()
{
Uri = $"ws://{host}/ws",
ContentType="audio/l16;rate=16000",
Headers = new Translation
{
UUID = Request.Query["uuid"].ToString(),
LanguageSpoken = "en-US",
LanguageTranslated = "hi-IN"
}
}
}
};
var ncco = new Ncco(webSocketAction);
return Ok(ncco.ToString());
VoiceController.cs
@slorello
Translation Engine
@slorello
WebSocketReceiveResult result = await webSocket.ReceiveAsync
(new ArraySegment<byte>(buffer), CancellationToken.None);
var config = JsonConvert.DeserializeObject<Translation>
(System.Text.Encoding.Default.GetString(buffer));
_uuid = config.UUID;
await StartSpeechTranscriptionEngine(config.LanguageSpoken,
config.LanguageTranslated);
_languageSpoken = config.LanguageSpoken;
_languageTranslated = config.LanguageTranslated;
TranslationEngine.cs
@slorello
WebSocketReceiveResult result = await webSocket.ReceiveAsync
(new ArraySegment<byte>(buffer), CancellationToken.None);
var config = JsonConvert.DeserializeObject<Translation>
(System.Text.Encoding.Default.GetString(buffer));
_uuid = config.UUID;
await StartSpeechTranscriptionEngine(config.LanguageSpoken,
config.LanguageTranslated);
_languageSpoken = config.LanguageSpoken;
_languageTranslated = config.LanguageTranslated;
TranslationEngine.cs
@slorello
WebSocketReceiveResult result = await webSocket.ReceiveAsync
(new ArraySegment<byte>(buffer), CancellationToken.None);
var config = JsonConvert.DeserializeObject<Translation>
(System.Text.Encoding.Default.GetString(buffer));
_uuid = config.UUID;
await StartSpeechTranslationEngine(config.LanguageSpoken,
config.LanguageTranslated);
_languageSpoken = config.LanguageSpoken;
_languageTranslated = config.LanguageTranslated;
TranslationEngine.cs
@slorello
while (!result.CloseStatus.HasValue)
{
result = await webSocket.ReceiveAsync
(new ArraySegment<byte>(buffer), CancellationToken.None);
_inputStream.Write(buffer);
}
TranslationEngine.cs
@slorello
byte[] audio;
while (_audioToWrite.TryDequeue(out audio))
{
const int bufferSize = 640;
for (var i = 0; i + bufferSize < audio.Length; i += bufferSize)
{
var audioToSend = audio[i..(i + bufferSize)];
var endOfMessage = audio.Length > (bufferSize + i);
await webSocket.SendAsync(new ArraySegment<byte>(audioToSend, 0, bufferSize),
WebSocketMessageType.Binary, endOfMessage, CancellationToken.None);
}
}
TranslationEngine.cs
@slorello
private async Task StartSpeechTranslationEngine(string recognitionLanguage,
string targetLanguage)
{
_translationConfig.SpeechRecognitionLanguage = recognitionLanguage;
_translationConfig.AddTargetLanguage(targetLanguage);
_speechConfig.SpeechRecognitionLanguage = targetLanguage;
_speechConfig.SpeechSynthesisLanguage = targetLanguage;
_synthesizer = new SpeechSynthesizer(_speechConfig, _output);
_recognizer = new TranslationRecognizer(_translationConfig, _audioInput);
_recognizer.Recognized += RecognizerRecognized;
await _recognizer.StartContinuousRecognitionAsync();
}
TranslationEngine.cs
@slorello
private void RecognizerRecognized(object sender, TranslationRecognitionEventArgs e)
{
var translationLanguage = _languageTranslated.Split("-")[0];
var translation = e.Result.Translations[translationLanguage].ToString();
Trace.WriteLine("Recognized: " + translation);
var ttsAudio = _synthesizer.SpeakTextAsync(translation).Result.AudioData;
var translationResult = new Translation
{
LanguageSpoken = _languageSpoken,
LanguageTranslated = _languageTranslated,
Text = translation, UUID = _uuid
};
_hub.Clients.All.SendAsync("receiveTranslation", translationResult);
_audioToWrite.Enqueue(ttsAudio);
}
TranslationEngine.cs
@slorello
private void RecognizerRecognized(object sender, TranslationRecognitionEventArgs e)
{
var translationLanguage = _languageTranslated.Split("-")[0];
var translation = e.Result.Translations[translationLanguage].ToString();
Trace.WriteLine("Recognized: " + translation);
var ttsAudio = _synthesizer.SpeakTextAsync(translation).Result.AudioData;
var translationResult = new Translation
{
LanguageSpoken = _languageSpoken,
LanguageTranslated = _languageTranslated,
Text = translation, UUID = _uuid
};
_hub.Clients.All.SendAsync("receiveTranslation", translationResult);
_audioToWrite.Enqueue(ttsAudio);
}
TranslationEngine.cs
@slorello
private void RecognizerRecognized(object sender, TranslationRecognitionEventArgs e)
{
var translationLanguage = _languageTranslated.Split("-")[0];
var translation = e.Result.Translations[translationLanguage].ToString();
Trace.WriteLine("Recognized: " + translation);
var ttsAudio = _synthesizer.SpeakTextAsync(translation).Result.AudioData;
var translationResult = new Translation
{
LanguageSpoken = _languageSpoken,
LanguageTranslated = _languageTranslated,
Text = translation, UUID = _uuid
};
_hub.Clients.All.SendAsync("receiveTranslation", translationResult);
_audioToWrite.Enqueue(ttsAudio);
}
TranslationEngine.cs
@slorello
private void RecognizerRecognized(object sender, TranslationRecognitionEventArgs e)
{
var translationLanguage = _languageTranslated.Split("-")[0];
var translation = e.Result.Translations[translationLanguage].ToString();
Trace.WriteLine("Recognized: " + translation);
var ttsAudio = _synthesizer.SpeakTextAsync(translation).Result.AudioData;
var translationResult = new Translation
{
LanguageSpoken = _languageSpoken,
LanguageTranslated = _languageTranslated,
Text = translation, UUID = _uuid
};
_hub.Clients.All.SendAsync("receiveTranslation", translationResult);
_audioToWrite.Enqueue(ttsAudio);
}
TranslationEngine.cs
@slorello
private void RecognizerRecognized(object sender, TranslationRecognitionEventArgs e)
{
var translationLanguage = _languageTranslated.Split("-")[0];
var translation = e.Result.Translations[translationLanguage].ToString();
Trace.WriteLine("Recognized: " + translation);
var ttsAudio = _synthesizer.SpeakTextAsync(translation).Result.AudioData;
var translationResult = new Translation
{
LanguageSpoken = _languageSpoken,
LanguageTranslated = _languageTranslated,
Text = translation, UUID = _uuid
};
_hub.Clients.All.SendAsync("receiveTranslation", translationResult);
_audioToWrite.Enqueue(ttsAudio);
}
TranslationEngine.cs
@slorello
private void RecognizerRecognized(object sender, TranslationRecognitionEventArgs e)
{
var translationLanguage = _languageTranslated.Split("-")[0];
var translation = e.Result.Translations[translationLanguage].ToString();
Trace.WriteLine("Recognized: " + translation);
var ttsAudio = _synthesizer.SpeakTextAsync(translation).Result.AudioData;
var translationResult = new Translation
{
LanguageSpoken = _languageSpoken,
LanguageTranslated = _languageTranslated,
Text = translation, UUID = _uuid
};
_hub.Clients.All.SendAsync("receiveTranslation", translationResult);
_audioToWrite.Enqueue(ttsAudio);
}
TranslationEngine.cs
@slorello
Build the Frontend
@slorello
@using Microsoft.AspNetCore.SignalR.Client
@using SpeechTranslatorBlazor.Shared
@inject NavigationManager NavigationManager
@implements IDisposable
TranslationComponent.razor
@slorello
<h3>Translation</h3>
<table class="table">
<thead>
<tr>
<th>Uuid</th>
<th>Language Spoken</th>
<th>Language Translated To</th>
<th>Text</th>
</tr>
</thead>
<tbody>
@foreach (var translation in _translations.Values)
{
<tr>
<td>@translation.UUID</td>
<td>@translation.LanguageSpoken</td>
<td>@translation.LanguageTranslated</td>
<td>@translation.Text</td>
</tr>
}
</tbody>
</table>
TranslationComponent.razor
@slorello
protected override async Task OnInitializedAsync()
{
_hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/TranslationHub"))
.Build();
_hubConnection.On<Translation>("receiveTranslation", (translation) =>
{
if (_translations.ContainsKey(translation.UUID))
{
_translations[translation.UUID].Text += translation.Text;
}
else
{
_translations.Add(translation.UUID, translation);
}
StateHasChanged();
});
await _hubConnection.StartAsync();
}
TranslationComponent.razor
@slorello
protected override async Task OnInitializedAsync()
{
_hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/TranslationHub"))
.Build();
_hubConnection.On<Translation>("receiveTranslation", (translation) =>
{
if (_translations.ContainsKey(translation.UUID))
{
_translations[translation.UUID].Text += translation.Text;
}
else
{
_translations.Add(translation.UUID, translation);
}
StateHasChanged();
});
await _hubConnection.StartAsync();
}
TranslationComponent.razor
@slorello
protected override async Task OnInitializedAsync()
{
_hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/TranslationHub"))
.Build();
_hubConnection.On<Translation>("receiveTranslation", (translation) =>
{
if (_translations.ContainsKey(translation.UUID))
{
_translations[translation.UUID].Text += translation.Text;
}
else
{
_translations.Add(translation.UUID, translation);
}
StateHasChanged();
});
await _hubConnection.StartAsync();
}
TranslationComponent.razor
@slorello
protected override async Task OnInitializedAsync()
{
_hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/TranslationHub"))
.Build();
_hubConnection.On<Translation>("receiveTranslation", (translation) =>
{
if (_translations.ContainsKey(translation.UUID))
{
_translations[translation.UUID].Text += translation.Text;
}
else
{
_translations.Add(translation.UUID, translation);
}
StateHasChanged();
});
await _hubConnection.StartAsync();
}
TranslationComponent.razor
@slorello
protected override async Task OnInitializedAsync()
{
_hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/TranslationHub"))
.Build();
_hubConnection.On<Translation>("receiveTranslation", (translation) =>
{
if (_translations.ContainsKey(translation.UUID))
{
_translations[translation.UUID].Text += translation.Text;
}
else
{
_translations.Add(translation.UUID, translation);
}
StateHasChanged();
});
await _hubConnection.StartAsync();
}
TranslationComponent.razor
@slorello
protected override async Task OnInitializedAsync()
{
_hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/TranslationHub"))
.Build();
_hubConnection.On<Translation>("receiveTranslation", (translation) =>
{
if (_translations.ContainsKey(translation.UUID))
{
_translations[translation.UUID].Text += translation.Text;
}
else
{
_translations.Add(translation.UUID, translation);
}
StateHasChanged();
});
await _hubConnection.StartAsync();
}
TranslationComponent.razor
@slorello
@page "/"
<TranslationComponent></TranslationComponent>
index.razor
@slorello
Wrapping Up
@slorello
A Little More About Me
● .NET Developer & Software Engineer
● .NET Developer Advocate @Vonage
● Computer Science Graduate Student
@GeorgiaTech - specializing in Computer
Perception
● Blog posts: https://slorello.com
● Twitter: @slorello
@slorello
A Little More About Vonage
● Vonage provides a full suite of communications APIs
○ https://developer.nexmo.com
○ Coupon Code: 21DNGAZ €10
● Vonage Video API
○ https://www.vonage.com/communications-apis/video/
@slorello
https://github.com/slorello89/BlazorSpeechTranslator
https://github.com/slorello89/InboundWhatsAppBlazor
https://github.com/slorello89/SendWhatsAppMessageBlazor
https://docs.microsoft.com/en-us/aspnet/core/blazor
https://developer.nexmo.com
Resources
LinkedIn: https://www.linkedin.com/in/stephen-lorello-143086a9/
Twitter: @slorello
@slorello
An image
with some
text on the
side.
URL ATTRIBUTION GOES HERE
@slorello
An image with some text over it
Attribution if needed
@slorello
“ “
A really large quote would
go here so everyone can
read it.
Some Persons Name
https://website.com
@slorello
Code Snippet Examples
@slorello https://romannurik.github.io/SlidesCodeHighlighter/
const pluckDeep = key => obj => key.split('.').reduce((accum, key) => accum[key], obj)
const compose = (...fns) => res => fns.reduce((accum, next) => next(accum), res)
const unfold = (f, seed) => {
const go = (f, seed, acc) => {
const res = f(seed)
return res ? go(f, res[1], acc.concat([res[0]])) : acc
}
return go(f, seed, [])
}
@slorello https://carbon.now.sh/
@slorello
Example Web Page Slides
@slorello https://developer.nexmo.com
@slorello https://developer.nexmo.com

Frontends w ithout javascript

  • 1.
    @slorello Frontends Without Javascript,The Promise of Blazor Steve Lorello .NET Developer Advocate @Vonage Twitter: @slorello
  • 2.
    @slorello 1. What isBlazor? 2. Different Flavors of Blazor 3. Blazor Server - with Examples 4. Blazor WASM - with Examples 5. Combine Blazor, SignalR and Entity Framework 6. Some Fun Examples! Agenda
  • 3.
  • 4.
    @slorello ● Blazor isa .NET web framework for building client UIs ● Build your apps in C# rather than JavaScript ● Share Server/Client application logic written for .NET ● Recycle Components between projects ● No Transpilation ● Use your favorite .NET libraries What is Blazor?
  • 5.
    @slorello ● Components arethe backbone of Blazor ● Flexible UI elements with rendering logic ● Components are written in Razor markup (HTML + C#) ● Components written in .razor files ● Components Can be shared between projects or even via NuGet Components
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
    @slorello ● Blazor Server ●Blazor WebAssembly Flavors of Blazor
  • 11.
  • 12.
  • 13.
    @slorello dotnet new blaozrserver-n BlazorServerHelloWorld
  • 14.
    @slorello @page "/counter" <h1>Counter</h1> <p>Current count:@currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { private int currentCount = 0; private void IncrementCount() { currentCount++; } } Counter.razor
  • 15.
    @slorello Counter.razor @page "/counter" <h1>Counter</h1> <p>Current count:@currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { private int currentCount = 0; private void IncrementCount() { currentCount++; } }
  • 16.
    @slorello Counter.razor @page "/counter" <h1>Counter</h1> <p>Current count:@currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { private int currentCount = 0; private void IncrementCount() { currentCount++; } }
  • 17.
    @slorello Counter.razor @page "/counter" <h1>Counter</h1> <p>Current count:@currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { private int currentCount = 0; private void IncrementCount() { currentCount++; } }
  • 18.
    @slorello Counter.razor @page "/counter" <h1>Counter</h1> <p>Current count:@currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { private int currentCount = 0; private void IncrementCount() { currentCount++; } }
  • 19.
  • 20.
  • 21.
  • 22.
    @slorello public class WhatsAppService { privatereadonly IConfiguration _config; public WhatsAppService(IConfiguration config) { _config = config; } } WhatsAppService.cs
  • 23.
    @slorello public string SendWhatsAppMessage(stringtoNum, string fromNum, string text){ var appId = _config["APP_ID"]; var privateKey = _config["privateKeyPath"]; var creds = Credentials.FromAppIdAndPrivateKeyPath(appId, privateKey); var content = new { type = "text", text }; var message = new { content }; var to = new { type = "whatsapp", number = toNum }; var from = new { type = "whatsapp", number = fromNum }; var request = new { to, from, message }; var uri = new Uri("https://messages-sandbox.nexmo.com/v0.1/messages"); var response = ApiRequest.DoRequestWithJsonContent<JObject> ("POST", uri, request, ApiRequest.AuthType.Bearer, creds); return response["message_uuid"].ToString(); } WhatsAppService.cs
  • 24.
    @slorello public string SendWhatsAppMessage(stringtoNum, string fromNum, string text){ var appId = _config["APP_ID"]; var privateKey = _config["privateKeyPath"]; var creds = Credentials.FromAppIdAndPrivateKeyPath(appId, privateKey); var content = new { type = "text", text }; var message = new { content }; var to = new { type = "whatsapp", number = toNum }; var from = new { type = "whatsapp", number = fromNum }; var request = new { to, from, message }; var uri = new Uri("https://messages-sandbox.nexmo.com/v0.1/messages"); var response = ApiRequest.DoRequestWithJsonContent<JObject> ("POST", uri, request, ApiRequest.AuthType.Bearer, creds); return response["message_uuid"].ToString(); } WhatsAppService.cs
  • 25.
    @slorello public string SendWhatsAppMessage(stringtoNum, string fromNum, string text){ var appId = _config["APP_ID"]; var privateKey = _config["privateKeyPath"]; var creds = Credentials.FromAppIdAndPrivateKeyPath(appId, privateKey); var content = new { type = "text", text }; var message = new { content }; var to = new { type = "whatsapp", number = toNum }; var from = new { type = "whatsapp", number = fromNum }; var request = new { to, from, message }; var uri = new Uri("https://messages-sandbox.nexmo.com/v0.1/messages"); var response = ApiRequest.DoRequestWithJsonContent<JObject> ("POST", uri, request, ApiRequest.AuthType.Bearer, creds); return response["message_uuid"].ToString(); } WhatsAppService.cs
  • 26.
    @slorello public string SendWhatsAppMessage(stringtoNum, string fromNum, string text){ var appId = _config["APP_ID"]; var privateKey = _config["privateKeyPath"]; var creds = Credentials.FromAppIdAndPrivateKeyPath(appId, privateKey); var content = new { type = "text", text }; var message = new { content }; var to = new { type = "whatsapp", number = toNum }; var from = new { type = "whatsapp", number = fromNum }; var request = new { to, from, message }; var uri = new Uri("https://messages-sandbox.nexmo.com/v0.1/messages"); var response = ApiRequest.DoRequestWithJsonContent<JObject> ("POST", uri, request, ApiRequest.AuthType.Bearer, creds); return response["message_uuid"].ToString(); } WhatsAppService.cs
  • 27.
    @slorello public string SendWhatsAppMessage(stringtoNum, string fromNum, string text){ var appId = _config["APP_ID"]; var privateKey = _config["privateKeyPath"]; var creds = Credentials.FromAppIdAndPrivateKeyPath(appId, privateKey); var content = new { type = "text", text }; var message = new { content }; var to = new { type = "whatsapp", number = toNum }; var from = new { type = "whatsapp", number = fromNum }; var request = new { to, from, message }; var uri = new Uri("https://messages-sandbox.nexmo.com/v0.1/messages"); var response = ApiRequest.DoRequestWithJsonContent<JObject> ("POST", uri, request, ApiRequest.AuthType.Bearer, creds); return response["message_uuid"].ToString(); } WhatsAppService.cs
  • 28.
    @slorello public void ConfigureServices(IServiceCollectionservices) { services.AddRazorPages(); services.AddServerSideBlazor(); services.AddSingleton<WeatherForecastService>(); services.AddSingleton<WhatsAppService>(); } Startup.cs
  • 29.
    @slorello @page "/" @using BlazorServerHelloWorld.Data @injectWhatsAppService WhatsappService Index.razor
  • 30.
    @slorello <h1>Send a WhatsAppMessage!</h1> Welcome to your new app Fill out the below form to send a message. <br /> to: <input id="to" @bind="@To" class="input-group-text" /> from: <input id="from" @bind="From" class="input-group-text" /> text: <input id="text" @bind="Text" class="input-group-text" /> @if (!string.IsNullOrEmpty(MessageId)) { <p>Message sent with id: @MessageId</p> } <button class="btn btn-primary" @onclick="SendWhatsApp">Send WhatsApp Message</button> Index.razor
  • 31.
    @slorello @code{ private string To; privatestring From; private string Text; private string MessageId; private void SendWhatsApp() { MessageId = WhatsappService.SendWhatsAppMessage(To, From, Text); To = ""; From = ""; Text = ""; } } Index.razor
  • 32.
  • 33.
  • 34.
  • 35.
    @slorello dotnet new blazorwasm-n BlazorWasmHelloWorld
  • 36.
  • 37.
  • 38.
    @slorello dotnet new blazorwasm-ho -n testBlazorHosted
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
    @slorello public class WhatsAppMessage { [Key] publicstring MessageId { get; set; } public string To { get; set; } public string From { get; set; } public string Text { get; set; } } WhatsAppMessage.cs
  • 48.
    @slorello public class WhatsAppContext: DbContext { public DbSet<WhatsAppMessage> Messages { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseSqlite("Data Source=VonageWhatsApp.db"); } WhatsAppContext.cs
  • 49.
    @slorello dotnet ef migrationsadd InitialCreate dotnet ef database update
  • 50.
  • 51.
  • 52.
    @slorello [Route("api/[controller]")] [ApiController] public class WhatsAppController: ControllerBase { private readonly WhatsAppContext _db; public WhatsAppController(WhatsAppContext db) { _db = db; } } WhatsAppController.cs
  • 53.
    @slorello [Route("/webhooks/inbound-message")] [HttpPost] public ActionResult InboundWhatsAppMessage() { varinbound = WebhookParser.ParseWebhook<JObject>(Request.Body, Request.ContentType); var message = new WhatsAppMessage { MessageId = inbound["message_uuid"].ToString(), From = inbound["from"]["number"].ToString(), To = inbound["to"]["number"].ToString(), Text = inbound["message"]["content"]["text"].ToString() }; _db.Messages.Add(message); _db.SaveChanges(); return Ok(); } WhatsAppController.cs
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
    @slorello index.razor <div class="x-display-table"> <h2>Inbound Messages</h2> <tableclass="table"> <thead> <tr> <th>Message Id</th> <th>From</th> <th>To</th> <th>Text</th> </tr> </thead> <tbody> @foreach (var message in _messages) { <tr> <td>@message.MessageId</td> <td>@message.From</td> <td>@message.To</td> <td>@message.Text</td> </tr> } </tbody> </table> </div>
  • 59.
    @slorello <div class="x-display-table"> <h2>Inbound Messages</h2> <tableclass="table"> <thead> <tr> <th>Message Id</th> <th>From</th> <th>To</th> <th>Text</th> </tr> </thead> <tbody> @foreach (var message in _messages) { <tr> <td>@message.MessageId</td> <td>@message.From</td> <td>@message.To</td> <td>@message.Text</td> </tr> } </tbody> </table> </div> index.razor
  • 60.
    @slorello @code{ private List<WhatsAppMessage> _messages= new List<WhatsAppMessage>(); protected override async Task OnInitializedAsync() { _messages = await HttpClient.GetFromJsonAsync<List<WhatsAppMessage>> ("/api/WhatsApp/getMessages"); } } index.razor
  • 61.
  • 62.
    @slorello Let’s build somethingReactive with SignalR
  • 63.
    @slorello dotnet new blazorwasm-ho -n SpeechTranslatorBlazor
  • 64.
    @slorello ● PSTN ->WebSockets ● Azure Cognitive Services ● Blazor WASM frontend ● SiganlR Connection
  • 65.
    @slorello public class Translation { publicstring UUID { get; set; } public string Text { get; set; } public string LanguageSpoken { get; set; } public string LanguageTranslated { get; set; } } Translation.cs
  • 66.
    @slorello public class TranslationHub: Hub{} TranslationHub.cs
  • 67.
  • 68.
  • 69.
    @slorello var webSocketOptions =new WebSocketOptions() { KeepAliveInterval = TimeSpan.FromSeconds(120), ReceiveBufferSize = 640 }; app.UseWebSockets(webSocketOptions); Startup.cs
  • 70.
    @slorello endpoints.Map("/ws", async (context)=> { if (context.WebSockets.IsWebSocketRequest) { var hub = (IHubContext<TranslationHub>)app.ApplicationServices. GetService(typeof(IHubContext<TranslationHub>)); WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(); using (var engine = new TranslationEngine(Configuration, hub)) { await engine.ReceiveAudioOnWebSocket(context, webSocket); } } else { context.Response.StatusCode = 400; } }); Startup.cs
  • 71.
    @slorello endpoints.Map("/ws", async (context)=> { if (context.WebSockets.IsWebSocketRequest) { var hub = (IHubContext<TranslationHub>)app.ApplicationServices. GetService(typeof(IHubContext<TranslationHub>)); WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(); using (var engine = new TranslationEngine(Configuration, hub)) { await engine.ReceiveAudioOnWebSocket(context, webSocket); } } else { context.Response.StatusCode = 400; } }); Startup.cs
  • 72.
    @slorello endpoints.Map("/ws", async (context)=> { if (context.WebSockets.IsWebSocketRequest) { var hub = (IHubContext<TranslationHub>)app.ApplicationServices. GetService(typeof(IHubContext<TranslationHub>)); WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(); using (var engine = new TranslationEngine(Configuration, hub)) { await engine.ReceiveAudioOnWebSocket(context, webSocket); } } else { context.Response.StatusCode = 400; } }); Startup.cs
  • 73.
    @slorello endpoints.Map("/ws", async (context)=> { if (context.WebSockets.IsWebSocketRequest) { var hub = (IHubContext<TranslationHub>)app.ApplicationServices. GetService(typeof(IHubContext<TranslationHub>)); WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(); using (var engine = new TranslationEngine(Configuration, hub)) { await engine.ReceiveAudioOnWebSocket(context, webSocket); } } else { context.Response.StatusCode = 400; } }); Startup.cs
  • 74.
  • 75.
  • 76.
    @slorello var host =Request.Host.ToString(); var webSocketAction = new ConnectAction() { Endpoint = new[] { new WebsocketEndpoint() { Uri = $"ws://{host}/ws", ContentType="audio/l16;rate=16000", Headers = new Translation { UUID = Request.Query["uuid"].ToString(), LanguageSpoken = "en-US", LanguageTranslated = "hi-IN" } } } }; var ncco = new Ncco(webSocketAction); return Ok(ncco.ToString()); VoiceController.cs
  • 77.
    @slorello var host =Request.Host.ToString(); var webSocketAction = new ConnectAction() { Endpoint = new[] { new WebsocketEndpoint() { Uri = $"ws://{host}/ws", ContentType="audio/l16;rate=16000", Headers = new Translation { UUID = Request.Query["uuid"].ToString(), LanguageSpoken = "en-US", LanguageTranslated = "hi-IN" } } } }; var ncco = new Ncco(webSocketAction); return Ok(ncco.ToString()); VoiceController.cs
  • 78.
    @slorello var host =Request.Host.ToString(); var webSocketAction = new ConnectAction() { Endpoint = new[] { new WebsocketEndpoint() { Uri = $"ws://{host}/ws", ContentType="audio/l16;rate=16000", Headers = new Translation { UUID = Request.Query["uuid"].ToString(), LanguageSpoken = "en-US", LanguageTranslated = "hi-IN" } } } }; var ncco = new Ncco(webSocketAction); return Ok(ncco.ToString()); VoiceController.cs
  • 79.
    @slorello var host =Request.Host.ToString(); var webSocketAction = new ConnectAction() { Endpoint = new[] { new WebsocketEndpoint() { Uri = $"ws://{host}/ws", ContentType="audio/l16;rate=16000", Headers = new Translation { UUID = Request.Query["uuid"].ToString(), LanguageSpoken = "en-US", LanguageTranslated = "hi-IN" } } } }; var ncco = new Ncco(webSocketAction); return Ok(ncco.ToString()); VoiceController.cs
  • 80.
  • 81.
    @slorello WebSocketReceiveResult result =await webSocket.ReceiveAsync (new ArraySegment<byte>(buffer), CancellationToken.None); var config = JsonConvert.DeserializeObject<Translation> (System.Text.Encoding.Default.GetString(buffer)); _uuid = config.UUID; await StartSpeechTranscriptionEngine(config.LanguageSpoken, config.LanguageTranslated); _languageSpoken = config.LanguageSpoken; _languageTranslated = config.LanguageTranslated; TranslationEngine.cs
  • 82.
    @slorello WebSocketReceiveResult result =await webSocket.ReceiveAsync (new ArraySegment<byte>(buffer), CancellationToken.None); var config = JsonConvert.DeserializeObject<Translation> (System.Text.Encoding.Default.GetString(buffer)); _uuid = config.UUID; await StartSpeechTranscriptionEngine(config.LanguageSpoken, config.LanguageTranslated); _languageSpoken = config.LanguageSpoken; _languageTranslated = config.LanguageTranslated; TranslationEngine.cs
  • 83.
    @slorello WebSocketReceiveResult result =await webSocket.ReceiveAsync (new ArraySegment<byte>(buffer), CancellationToken.None); var config = JsonConvert.DeserializeObject<Translation> (System.Text.Encoding.Default.GetString(buffer)); _uuid = config.UUID; await StartSpeechTranslationEngine(config.LanguageSpoken, config.LanguageTranslated); _languageSpoken = config.LanguageSpoken; _languageTranslated = config.LanguageTranslated; TranslationEngine.cs
  • 84.
    @slorello while (!result.CloseStatus.HasValue) { result =await webSocket.ReceiveAsync (new ArraySegment<byte>(buffer), CancellationToken.None); _inputStream.Write(buffer); } TranslationEngine.cs
  • 85.
    @slorello byte[] audio; while (_audioToWrite.TryDequeue(outaudio)) { const int bufferSize = 640; for (var i = 0; i + bufferSize < audio.Length; i += bufferSize) { var audioToSend = audio[i..(i + bufferSize)]; var endOfMessage = audio.Length > (bufferSize + i); await webSocket.SendAsync(new ArraySegment<byte>(audioToSend, 0, bufferSize), WebSocketMessageType.Binary, endOfMessage, CancellationToken.None); } } TranslationEngine.cs
  • 86.
    @slorello private async TaskStartSpeechTranslationEngine(string recognitionLanguage, string targetLanguage) { _translationConfig.SpeechRecognitionLanguage = recognitionLanguage; _translationConfig.AddTargetLanguage(targetLanguage); _speechConfig.SpeechRecognitionLanguage = targetLanguage; _speechConfig.SpeechSynthesisLanguage = targetLanguage; _synthesizer = new SpeechSynthesizer(_speechConfig, _output); _recognizer = new TranslationRecognizer(_translationConfig, _audioInput); _recognizer.Recognized += RecognizerRecognized; await _recognizer.StartContinuousRecognitionAsync(); } TranslationEngine.cs
  • 87.
    @slorello private void RecognizerRecognized(objectsender, TranslationRecognitionEventArgs e) { var translationLanguage = _languageTranslated.Split("-")[0]; var translation = e.Result.Translations[translationLanguage].ToString(); Trace.WriteLine("Recognized: " + translation); var ttsAudio = _synthesizer.SpeakTextAsync(translation).Result.AudioData; var translationResult = new Translation { LanguageSpoken = _languageSpoken, LanguageTranslated = _languageTranslated, Text = translation, UUID = _uuid }; _hub.Clients.All.SendAsync("receiveTranslation", translationResult); _audioToWrite.Enqueue(ttsAudio); } TranslationEngine.cs
  • 88.
    @slorello private void RecognizerRecognized(objectsender, TranslationRecognitionEventArgs e) { var translationLanguage = _languageTranslated.Split("-")[0]; var translation = e.Result.Translations[translationLanguage].ToString(); Trace.WriteLine("Recognized: " + translation); var ttsAudio = _synthesizer.SpeakTextAsync(translation).Result.AudioData; var translationResult = new Translation { LanguageSpoken = _languageSpoken, LanguageTranslated = _languageTranslated, Text = translation, UUID = _uuid }; _hub.Clients.All.SendAsync("receiveTranslation", translationResult); _audioToWrite.Enqueue(ttsAudio); } TranslationEngine.cs
  • 89.
    @slorello private void RecognizerRecognized(objectsender, TranslationRecognitionEventArgs e) { var translationLanguage = _languageTranslated.Split("-")[0]; var translation = e.Result.Translations[translationLanguage].ToString(); Trace.WriteLine("Recognized: " + translation); var ttsAudio = _synthesizer.SpeakTextAsync(translation).Result.AudioData; var translationResult = new Translation { LanguageSpoken = _languageSpoken, LanguageTranslated = _languageTranslated, Text = translation, UUID = _uuid }; _hub.Clients.All.SendAsync("receiveTranslation", translationResult); _audioToWrite.Enqueue(ttsAudio); } TranslationEngine.cs
  • 90.
    @slorello private void RecognizerRecognized(objectsender, TranslationRecognitionEventArgs e) { var translationLanguage = _languageTranslated.Split("-")[0]; var translation = e.Result.Translations[translationLanguage].ToString(); Trace.WriteLine("Recognized: " + translation); var ttsAudio = _synthesizer.SpeakTextAsync(translation).Result.AudioData; var translationResult = new Translation { LanguageSpoken = _languageSpoken, LanguageTranslated = _languageTranslated, Text = translation, UUID = _uuid }; _hub.Clients.All.SendAsync("receiveTranslation", translationResult); _audioToWrite.Enqueue(ttsAudio); } TranslationEngine.cs
  • 91.
    @slorello private void RecognizerRecognized(objectsender, TranslationRecognitionEventArgs e) { var translationLanguage = _languageTranslated.Split("-")[0]; var translation = e.Result.Translations[translationLanguage].ToString(); Trace.WriteLine("Recognized: " + translation); var ttsAudio = _synthesizer.SpeakTextAsync(translation).Result.AudioData; var translationResult = new Translation { LanguageSpoken = _languageSpoken, LanguageTranslated = _languageTranslated, Text = translation, UUID = _uuid }; _hub.Clients.All.SendAsync("receiveTranslation", translationResult); _audioToWrite.Enqueue(ttsAudio); } TranslationEngine.cs
  • 92.
    @slorello private void RecognizerRecognized(objectsender, TranslationRecognitionEventArgs e) { var translationLanguage = _languageTranslated.Split("-")[0]; var translation = e.Result.Translations[translationLanguage].ToString(); Trace.WriteLine("Recognized: " + translation); var ttsAudio = _synthesizer.SpeakTextAsync(translation).Result.AudioData; var translationResult = new Translation { LanguageSpoken = _languageSpoken, LanguageTranslated = _languageTranslated, Text = translation, UUID = _uuid }; _hub.Clients.All.SendAsync("receiveTranslation", translationResult); _audioToWrite.Enqueue(ttsAudio); } TranslationEngine.cs
  • 93.
  • 94.
    @slorello @using Microsoft.AspNetCore.SignalR.Client @using SpeechTranslatorBlazor.Shared @injectNavigationManager NavigationManager @implements IDisposable TranslationComponent.razor
  • 95.
    @slorello <h3>Translation</h3> <table class="table"> <thead> <tr> <th>Uuid</th> <th>Language Spoken</th> <th>LanguageTranslated To</th> <th>Text</th> </tr> </thead> <tbody> @foreach (var translation in _translations.Values) { <tr> <td>@translation.UUID</td> <td>@translation.LanguageSpoken</td> <td>@translation.LanguageTranslated</td> <td>@translation.Text</td> </tr> } </tbody> </table> TranslationComponent.razor
  • 96.
    @slorello protected override asyncTask OnInitializedAsync() { _hubConnection = new HubConnectionBuilder() .WithUrl(NavigationManager.ToAbsoluteUri("/TranslationHub")) .Build(); _hubConnection.On<Translation>("receiveTranslation", (translation) => { if (_translations.ContainsKey(translation.UUID)) { _translations[translation.UUID].Text += translation.Text; } else { _translations.Add(translation.UUID, translation); } StateHasChanged(); }); await _hubConnection.StartAsync(); } TranslationComponent.razor
  • 97.
    @slorello protected override asyncTask OnInitializedAsync() { _hubConnection = new HubConnectionBuilder() .WithUrl(NavigationManager.ToAbsoluteUri("/TranslationHub")) .Build(); _hubConnection.On<Translation>("receiveTranslation", (translation) => { if (_translations.ContainsKey(translation.UUID)) { _translations[translation.UUID].Text += translation.Text; } else { _translations.Add(translation.UUID, translation); } StateHasChanged(); }); await _hubConnection.StartAsync(); } TranslationComponent.razor
  • 98.
    @slorello protected override asyncTask OnInitializedAsync() { _hubConnection = new HubConnectionBuilder() .WithUrl(NavigationManager.ToAbsoluteUri("/TranslationHub")) .Build(); _hubConnection.On<Translation>("receiveTranslation", (translation) => { if (_translations.ContainsKey(translation.UUID)) { _translations[translation.UUID].Text += translation.Text; } else { _translations.Add(translation.UUID, translation); } StateHasChanged(); }); await _hubConnection.StartAsync(); } TranslationComponent.razor
  • 99.
    @slorello protected override asyncTask OnInitializedAsync() { _hubConnection = new HubConnectionBuilder() .WithUrl(NavigationManager.ToAbsoluteUri("/TranslationHub")) .Build(); _hubConnection.On<Translation>("receiveTranslation", (translation) => { if (_translations.ContainsKey(translation.UUID)) { _translations[translation.UUID].Text += translation.Text; } else { _translations.Add(translation.UUID, translation); } StateHasChanged(); }); await _hubConnection.StartAsync(); } TranslationComponent.razor
  • 100.
    @slorello protected override asyncTask OnInitializedAsync() { _hubConnection = new HubConnectionBuilder() .WithUrl(NavigationManager.ToAbsoluteUri("/TranslationHub")) .Build(); _hubConnection.On<Translation>("receiveTranslation", (translation) => { if (_translations.ContainsKey(translation.UUID)) { _translations[translation.UUID].Text += translation.Text; } else { _translations.Add(translation.UUID, translation); } StateHasChanged(); }); await _hubConnection.StartAsync(); } TranslationComponent.razor
  • 101.
    @slorello protected override asyncTask OnInitializedAsync() { _hubConnection = new HubConnectionBuilder() .WithUrl(NavigationManager.ToAbsoluteUri("/TranslationHub")) .Build(); _hubConnection.On<Translation>("receiveTranslation", (translation) => { if (_translations.ContainsKey(translation.UUID)) { _translations[translation.UUID].Text += translation.Text; } else { _translations.Add(translation.UUID, translation); } StateHasChanged(); }); await _hubConnection.StartAsync(); } TranslationComponent.razor
  • 102.
  • 103.
  • 104.
    @slorello A Little MoreAbout Me ● .NET Developer & Software Engineer ● .NET Developer Advocate @Vonage ● Computer Science Graduate Student @GeorgiaTech - specializing in Computer Perception ● Blog posts: https://slorello.com ● Twitter: @slorello
  • 105.
    @slorello A Little MoreAbout Vonage ● Vonage provides a full suite of communications APIs ○ https://developer.nexmo.com ○ Coupon Code: 21DNGAZ €10 ● Vonage Video API ○ https://www.vonage.com/communications-apis/video/
  • 106.
  • 107.
    @slorello An image with some texton the side. URL ATTRIBUTION GOES HERE
  • 108.
    @slorello An image withsome text over it Attribution if needed
  • 109.
    @slorello “ “ A reallylarge quote would go here so everyone can read it. Some Persons Name https://website.com
  • 110.
  • 111.
    @slorello https://romannurik.github.io/SlidesCodeHighlighter/ const pluckDeep= key => obj => key.split('.').reduce((accum, key) => accum[key], obj) const compose = (...fns) => res => fns.reduce((accum, next) => next(accum), res) const unfold = (f, seed) => { const go = (f, seed, acc) => { const res = f(seed) return res ? go(f, res[1], acc.concat([res[0]])) : acc } return go(f, seed, []) }
  • 112.
  • 113.
  • 114.
  • 115.