mirror of
https://github.com/Stone-Red-Code/TPH-Blazor-Spotlight-Example.git
synced 2026-09-04 00:56:28 +02:00
Add discord statistics and new year countdown
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
<div class="top-row ps-3 navbar navbar-dark">
|
<div class="top-row ps-3 navbar navbar-dark">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a class="navbar-brand" href="">TphBlazorSpotlightExample</a>
|
<a class="navbar-brand" href="">TPH Blazor Spotlight</a>
|
||||||
<button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu">
|
<button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
@@ -15,13 +15,13 @@
|
|||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-item px-3">
|
<div class="nav-item px-3">
|
||||||
<NavLink class="nav-link" href="counter">
|
<NavLink class="nav-link" href="statistics">
|
||||||
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
|
<span class="bi bi-graph-up-nav-menu" aria-hidden="true"></span> Discord Statistics
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-item px-3">
|
<div class="nav-item px-3">
|
||||||
<NavLink class="nav-link" href="weather">
|
<NavLink class="nav-link" href="new-year-countdown">
|
||||||
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
|
<span class="bi bi-clock-nav-menu" aria-hidden="true"></span> New Year Countdown
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -33,6 +33,14 @@
|
|||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bi-clock-nav-menu {
|
||||||
|
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0id2hpdGUiIGNsYXNzPSJiaSBiaS1jbG9jayIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBkPSJNOCAzLjVhLjUuNSAwIDAgMC0xIDBWOWEuNS41IDAgMCAwIC4yNTIuNDM0bDMuNSAyYS41LjUgMCAwIDAgLjQ5Ni0uODY4TDggOC43MXoiLz4KICA8cGF0aCBkPSJNOCAxNkE4IDggMCAxIDAgOCAwYTggOCAwIDAgMCAwIDE2bTctOEE3IDcgMCAxIDEgMSA4YTcgNyAwIDAgMSAxNCAwIi8+Cjwvc3ZnPg==");
|
||||||
|
}
|
||||||
|
|
||||||
|
.bi-graph-up-nav-menu {
|
||||||
|
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0id2hpdGUiIGNsYXNzPSJiaSBiaS1ncmFwaC11cCIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0wIDBoMXYxNWgxNXYxSDB6bTE0LjgxNyAzLjExM2EuNS41IDAgMCAxIC4wNy43MDRsLTQuNSA1LjVhLjUuNSAwIDAgMS0uNzQuMDM3TDcuMDYgNi43NjdsLTMuNjU2IDUuMDI3YS41LjUgMCAwIDEtLjgwOC0uNTg4bDQtNS41YS41LjUgMCAwIDEgLjc1OC0uMDZsMi42MDkgMi42MSA0LjE1LTUuMDczYS41LjUgMCAwIDEgLjcwNC0uMDciLz4KPC9zdmc+ ");
|
||||||
|
}
|
||||||
|
|
||||||
.nav-item {
|
.nav-item {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
@@ -55,15 +63,15 @@
|
|||||||
line-height: 3rem;
|
line-height: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item ::deep a.active {
|
.nav-item ::deep a.active {
|
||||||
background-color: rgba(255,255,255,0.37);
|
background-color: rgba(255,255,255,0.37);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item ::deep a:hover {
|
.nav-item ::deep a:hover {
|
||||||
background-color: rgba(255,255,255,0.1);
|
background-color: rgba(255,255,255,0.1);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 641px) {
|
@media (min-width: 641px) {
|
||||||
.navbar-toggler {
|
.navbar-toggler {
|
||||||
@@ -74,10 +82,10 @@
|
|||||||
/* Never collapse the sidebar for wide screens */
|
/* Never collapse the sidebar for wide screens */
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-scrollable {
|
.nav-scrollable {
|
||||||
/* Allow sidebar to scroll for tall menus */
|
/* Allow sidebar to scroll for tall menus */
|
||||||
height: calc(100vh - 3.5rem);
|
height: calc(100vh - 3.5rem);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace TphBlazorSpotlightExample.Models;
|
||||||
|
|
||||||
|
public class DiscordInvite
|
||||||
|
{
|
||||||
|
[JsonPropertyName("type")]
|
||||||
|
public int Type { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("code")]
|
||||||
|
public string Code { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("expires_at")]
|
||||||
|
public object ExpiresAt { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("flags")]
|
||||||
|
public int Flags { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("guild")]
|
||||||
|
public Guild Guild { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("guild_id")]
|
||||||
|
public string GuildId { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("approximate_member_count")]
|
||||||
|
public int ApproximateMemberCount { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("approximate_presence_count")]
|
||||||
|
public int ApproximatePresenceCount { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace TphBlazorSpotlightExample.Models;
|
||||||
|
|
||||||
|
public class Guild
|
||||||
|
{
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("splash")]
|
||||||
|
public string Splash { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("banner")]
|
||||||
|
public string Banner { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("description")]
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("icon")]
|
||||||
|
public string Icon { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("features")]
|
||||||
|
public List<string> Features { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("verification_level")]
|
||||||
|
public int VerificationLevel { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("vanity_url_code")]
|
||||||
|
public string VanityUrlCode { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("nsfw_level")]
|
||||||
|
public int NsfwLevel { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("nsfw")]
|
||||||
|
public bool Nsfw { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("premium_subscription_count")]
|
||||||
|
public int PremiumSubscriptionCount { get; set; }
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
@page "/counter"
|
|
||||||
|
|
||||||
<PageTitle>Counter</PageTitle>
|
|
||||||
|
|
||||||
<h1>Counter</h1>
|
|
||||||
|
|
||||||
<p role="status">Current count: @currentCount</p>
|
|
||||||
|
|
||||||
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
|
|
||||||
|
|
||||||
@code {
|
|
||||||
private int currentCount = 0;
|
|
||||||
|
|
||||||
private void IncrementCount()
|
|
||||||
{
|
|
||||||
currentCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
@page "/statistics"
|
||||||
|
|
||||||
|
@inject HttpClient HttpClient
|
||||||
|
|
||||||
|
<PageTitle>Discord Statistics</PageTitle>
|
||||||
|
|
||||||
|
<h1>Discord Statistics</h1>
|
||||||
|
|
||||||
|
<p class="mt-5">Discord invite link</p>
|
||||||
|
|
||||||
|
<InputText class="input-group-text mb-3" style="width: 20rem" @bind-Value="discordInviteLink" />
|
||||||
|
|
||||||
|
<button class="btn btn-primary mx-2 mb-3" style="width: fit-content" @onclick="Fetch">Fetch</button>
|
||||||
|
|
||||||
|
<p class="text-danger">
|
||||||
|
@errorMessage
|
||||||
|
</p>
|
||||||
|
|
||||||
|
@if (discordInvite != null)
|
||||||
|
{
|
||||||
|
<div class="d-flex row gap-3 justify-content-center text-center">
|
||||||
|
<div class="card bg-dark" style="max-width: 50rem">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">
|
||||||
|
@discordInvite.Guild.Name
|
||||||
|
</h5>
|
||||||
|
<h6 class="card-subtitle mb-4 text-muted">
|
||||||
|
@discordInvite.Guild.Description
|
||||||
|
</h6>
|
||||||
|
<p class="card-text m-1">Invite Code: <a href="https://discord.gg/@discordInvite.Code">discord.gg/@discordInvite.Code</a></p>
|
||||||
|
<p class="card-text m-1">Total Members: @discordInvite.ApproximateMemberCount</p>
|
||||||
|
<p class="card-text m-1">Total Online Members: @discordInvite.ApproximatePresenceCount</p>
|
||||||
|
<p class="card-text m-1">Boosts: @discordInvite.Guild.PremiumSubscriptionCount</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private string discordInviteLink = "https://discord.gg/programming";
|
||||||
|
|
||||||
|
private string errorMessage = string.Empty;
|
||||||
|
private DiscordInvite? discordInvite = null;
|
||||||
|
|
||||||
|
private async Task Fetch()
|
||||||
|
{
|
||||||
|
var response = await HttpClient.GetAsync($"https://discord.com/api/invites/{discordInviteLink.Split("/").LastOrDefault()}?with_counts=true");
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
discordInvite = await response.Content.ReadFromJsonAsync<DiscordInvite>();
|
||||||
|
|
||||||
|
errorMessage = string.Empty;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errorMessage = await response.Content.ReadAsStringAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
@page "/new-year-countdown"
|
||||||
|
|
||||||
|
<PageTitle>New Year Countdown</PageTitle>
|
||||||
|
|
||||||
|
<h1>New Year Countdown</h1>
|
||||||
|
|
||||||
|
<p class="mt-5">Time left until the new year @newYear.Year</p>
|
||||||
|
|
||||||
|
<h3>@GetTimeLeft()</h3>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private DateTime newYear = new DateTime(DateTime.Now.Year + 1, 1, 1, 0, 0, 0);
|
||||||
|
|
||||||
|
override protected void OnInitialized()
|
||||||
|
{
|
||||||
|
System.Timers.Timer timer = new(1000);
|
||||||
|
timer.Elapsed += (sender, e) => InvokeAsync(StateHasChanged);
|
||||||
|
timer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetTimeLeft()
|
||||||
|
{
|
||||||
|
var timeLeft = newYear - DateTime.Now;
|
||||||
|
|
||||||
|
return $"{timeLeft.Days} days, {timeLeft.Hours} hours, {timeLeft.Minutes} minutes, {timeLeft.Seconds} seconds";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
@page "/weather"
|
|
||||||
@inject HttpClient Http
|
|
||||||
|
|
||||||
<PageTitle>Weather</PageTitle>
|
|
||||||
|
|
||||||
<h1>Weather</h1>
|
|
||||||
|
|
||||||
<p>This component demonstrates fetching data from the server.</p>
|
|
||||||
|
|
||||||
@if (forecasts == null)
|
|
||||||
{
|
|
||||||
<p><em>Loading...</em></p>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Date</th>
|
|
||||||
<th>Temp. (C)</th>
|
|
||||||
<th>Temp. (F)</th>
|
|
||||||
<th>Summary</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@foreach (var forecast in forecasts)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>@forecast.Date.ToShortDateString()</td>
|
|
||||||
<td>@forecast.TemperatureC</td>
|
|
||||||
<td>@forecast.TemperatureF</td>
|
|
||||||
<td>@forecast.Summary</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
}
|
|
||||||
|
|
||||||
@code {
|
|
||||||
private WeatherForecast[]? forecasts;
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json");
|
|
||||||
}
|
|
||||||
|
|
||||||
public class WeatherForecast
|
|
||||||
{
|
|
||||||
public DateOnly Date { get; set; }
|
|
||||||
|
|
||||||
public int TemperatureC { get; set; }
|
|
||||||
|
|
||||||
public string? Summary { get; set; }
|
|
||||||
|
|
||||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user