Add discord statistics and new year countdown

This commit is contained in:
Stone_Red
2024-11-05 23:50:16 +01:00
parent d4538637f3
commit 0a33d7313b
8 changed files with 183 additions and 90 deletions
+5 -5
View File
@@ -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>
+16 -8
View File
@@ -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 {
+30
View File
@@ -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; }
}
+42
View File
@@ -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; }
}
-18
View File
@@ -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++;
}
}
+61
View File
@@ -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();
}
}
}
+27
View File
@@ -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";
}
}
-57
View File
@@ -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);
}
}