Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions PointingParty.Client.Tests/QrCodeTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using PointingParty.Client.Components;

namespace PointingParty.Client.Tests;

public class QrCodeTests : BunitContext
{
[Fact]
public void Renders_Qr_As_Image_DataUri()
{
// Arrange
var cut = Render<QrCode>(parameters => parameters.Add(p => p.GameId, "TestGame"));

// Act - look for an <img> with a data:image src
var imgElements = cut.FindAll("img");
var hasDataUri = imgElements.Any(i => (i.GetAttribute("src") ?? string.Empty).StartsWith("data:image/"));

// Assert - an image data URI should be rendered
Assert.True(hasDataUri, "Expected an <img> with a data:image/ src.");
}

[Fact]
public void Clicking_Qr_Opens_Modal_With_Larger_Image()
{
// Arrange
var cut = Render<QrCode>(parameters => parameters.Add(p => p.GameId, "TestGame"));

// Pre-check what's rendered initially
var initialImgs = cut.FindAll("img")
.Count(i => (i.GetAttribute("src") ?? string.Empty).StartsWith("data:image/"));

// Act - click the button that should show the modal
var button = cut.Find("button");
button.Click();

// After clicking, the modal should be present and contain at least one data-uri image.
var imgsAfter = cut.FindAll("img")
.Count(i => (i.GetAttribute("src") ?? string.Empty).StartsWith("data:image/"));

Assert.True(imgsAfter > initialImgs,
"Expected clicking the QR to open a modal containing a larger image data URI.");
}
}
6 changes: 4 additions & 2 deletions PointingParty.Client/Components/Pages/Game.razor
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ else
}

<div class="text-center text-xs mt-2 md:mt-4">
<span class="text-gray-500">To invite participants to the <strong>@GameId</strong> party, send them this link:</span><br/>
<span class="text-gray-500">To invite participants, send them this link:</span><br/>
<span class="font-medium">
<a href="https://pointingparty.com/Game/@Uri.EscapeDataString(GameId)">pointingparty.com/Game/@Uri.EscapeDataString(GameId)</a>
</span>
</span><br/>
<span class="text-gray-500">or share the QR code (click to enlarge)</span><br/>
<QrCode GameId="@GameId"/>
</div>

@code {
Expand Down
62 changes: 62 additions & 0 deletions PointingParty.Client/Components/QrCode.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
@using QRCoder
@using Microsoft.AspNetCore.Components
@using Microsoft.AspNetCore.Components.Web
@inject NavigationManager NavManager

@if (_qrCode is not null)
{
<div class="mx-auto">
<button class="p-0 bg-transparent border-0" @onclick="ShowModal" aria-label="Open QR code">
<img src="data:image/png;base64,@_qrCode" class="w-24" alt="QR code" />
</button>
</div>
}

@if (_showModal)
{
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50" tabindex="0"
@onclick="HideModal" @onkeydown="HandleKeyDown">
<div class="max-h-1/2 max-w-1/2 object-contain rounded shadow-lg cursor-zoom-out" @onclick="HideModal">
<img src="data:image/png;base64,@_qrCode" alt="QR code large" />
</div>
</div>
}

@code {

[Parameter]
public string GameId { get; set; } = "";

private readonly QRCodeGenerator _qrGenerator = new();
private string? _qrCode;
private bool _showModal;

protected override void OnParametersSet()
{
var generator = new PayloadGenerator.Url(NavManager.BaseUri.TrimEnd('/') + "/Game/" + Uri.EscapeDataString(GameId));
var payload = generator.ToString();

var qrCodeData = _qrGenerator.CreateQrCode(payload, QRCodeGenerator.ECCLevel.M);
var qrCode = new PngByteQRCode(qrCodeData);
_qrCode = Convert.ToBase64String(qrCode.GetGraphic(16));

base.OnParametersSet();
}

private void ShowModal()
{
_showModal = true;
}

private void HideModal()
{
_showModal = false;
}

private void HandleKeyDown(KeyboardEventArgs e)
{
if (e.Key == "Escape")
HideModal();
}

}
1 change: 1 addition & 0 deletions PointingParty.Client/PointingParty.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client.SourceGenerator" Version="7.0.0-preview.7.22376.6"/>
<PackageReference Include="QRCoder" Version="1.7.0" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion PointingParty/wwwroot/app.min.css

Large diffs are not rendered by default.