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="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">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
@@ -15,13 +15,13 @@
|
||||
</NavLink>
|
||||
</div>
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="counter">
|
||||
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
|
||||
<NavLink class="nav-link" href="statistics">
|
||||
<span class="bi bi-graph-up-nav-menu" aria-hidden="true"></span> Discord Statistics
|
||||
</NavLink>
|
||||
</div>
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="weather">
|
||||
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
|
||||
<NavLink class="nav-link" href="new-year-countdown">
|
||||
<span class="bi bi-clock-nav-menu" aria-hidden="true"></span> New Year Countdown
|
||||
</NavLink>
|
||||
</div>
|
||||
</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");
|
||||
}
|
||||
|
||||
.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 {
|
||||
font-size: 0.9rem;
|
||||
padding-bottom: 0.5rem;
|
||||
@@ -55,15 +63,15 @@
|
||||
line-height: 3rem;
|
||||
}
|
||||
|
||||
.nav-item ::deep a.active {
|
||||
background-color: rgba(255,255,255,0.37);
|
||||
color: white;
|
||||
}
|
||||
.nav-item ::deep a.active {
|
||||
background-color: rgba(255,255,255,0.37);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-item ::deep a:hover {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.nav-item ::deep a:hover {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
@media (min-width: 641px) {
|
||||
.navbar-toggler {
|
||||
@@ -74,10 +82,10 @@
|
||||
/* Never collapse the sidebar for wide screens */
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
.nav-scrollable {
|
||||
/* Allow sidebar to scroll for tall menus */
|
||||
height: calc(100vh - 3.5rem);
|
||||
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