Add Ntfy message box

This commit is contained in:
pancakes 2025-09-13 02:12:31 +10:00
parent ca997d10b1
commit 82dc4f227e
Signed by: pancakes
SSH key fingerprint: SHA256:yrp4c4hhaPoPG07fb4QyQIgAdlbUdsJvUAydJEWnfTw
4 changed files with 160 additions and 4 deletions

View file

@ -1,12 +1,15 @@
@page "/" @page "/"
@using System.ComponentModel.DataAnnotations
@using System.Text.Json
@using PancakesWeb.Data @using PancakesWeb.Data
@using PancakesWeb.Schema
<PageTitle>Home</PageTitle> <PageTitle>Home</PageTitle>
<main class="container"> <main class="container">
<h1 id="pancakes">pancakes</h1> <h1 id="pancakes">pancakes</h1>
<p>I'm a cat <abbr title="non-binary">enby</abbr> (<abbr <p>I'm a cat <abbr title="non-binary">enby</abbr> (<abbr
title="they/them/their/theirs/themself or it/it/its/its/itself in English">they/it</abbr>) from Australia title="they/them/their/theirs/themself or it/it/its/its/itself in English">they/it</abbr>) from Australia
that likes cats, Linux, and programming. You can find my links, projects, and other pages here.</p> that likes cats, Linux, and programming. You can find my links, projects, and other pages here.</p>
<h2 id="accounts">Accounts</h2> <h2 id="accounts">Accounts</h2>
@ -121,4 +124,41 @@
</li> </li>
} }
</ul> </ul>
<h2 id="notify">Notify</h2>
<p>Send me a push notification. This will be a silent notification on my phone with no popup, vibration, or sound.</p>
<EditForm Model="Ntfy" OnValidSubmit="SubmitNtfy" FormName="Ntfy">
<DataAnnotationsValidator/>
<InputText @bind-Value="Ntfy.Message" maxlength="128" placeholder="Message"/>
<button type="submit">Send</button>
<ValidationSummary/>
</EditForm>
</main> </main>
@code {
[Inject] private HttpClient Http { get; set; } = null!;
[Inject] private IConfiguration Config { get; set; } = null!;
[SupplyParameterFromForm] private NtfyModel Ntfy { get; set; } = new NtfyModel();
private async Task SubmitNtfy()
{
var ntfy = new NtfyRequest
{
Topic = Config["Ntfy:Topic"]!,
Message = Ntfy.Message,
Title = "pancakes.gay",
Priority = 2,
Tags = ["black_cat"]
};
await Http.PostAsJsonAsync(Config["Ntfy:Url"], ntfy, JsonSerializerOptions.Web);
}
public class NtfyModel
{
[StringLength(128)]
public string Message { get; set; } = "";
}
}

View file

@ -5,6 +5,14 @@ var builder = WebApplication.CreateBuilder(args);
// Add services to the container. // Add services to the container.
builder.Services.AddRazorComponents() builder.Services.AddRazorComponents()
.AddInteractiveServerComponents(); .AddInteractiveServerComponents();
builder.Services.AddHttpClient();
builder.Configuration.AddEnvironmentVariables();
if (builder.Configuration["Ntfy:Url"] == null)
throw new Exception("Ntfy:Url is unset");
if (builder.Configuration["Ntfy:Topic"] == null)
throw new Exception("Ntfy:Topic is unset");
var app = builder.Build(); var app = builder.Build();

View file

@ -0,0 +1,88 @@
using System.Text.Json.Serialization;
namespace PancakesWeb.Schema;
public class NtfyRequest
{
public required string Topic { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Message { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Title { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public List<string>? Tags { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public int? Priority { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public List<NtfyAction>? Actions { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Click { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Attach { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public bool? Markdown { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Icon { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Filename { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Delay { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Email { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Call { get; set; }
}
[JsonDerivedType(typeof(NtfyViewAction), "view")]
[JsonDerivedType(typeof(NtfyBroadcastAction), "broadcast")]
[JsonDerivedType(typeof(NtfyHttpAction), "http")]
[JsonPolymorphic(TypeDiscriminatorPropertyName = "action",
UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)]
public abstract class NtfyAction
{
public required string Label { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public bool? Clear { get; set; }
}
public class NtfyViewAction : NtfyAction
{
public required string Url { get; set; }
}
public class NtfyBroadcastAction : NtfyAction
{
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Intent { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public Dictionary<string, string>? Extras { get; set; }
}
public class NtfyHttpAction : NtfyAction
{
public required string Url { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Method { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public Dictionary<string, string>? Headers { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Body { get; set; }
}

View file

@ -109,7 +109,7 @@ pre code {
text-wrap: nowrap; text-wrap: nowrap;
} }
button { button, input[type=submit] {
display: inline-block; display: inline-block;
padding: 0.5em 1em; padding: 0.5em 1em;
@ -121,12 +121,28 @@ button {
border: none; border: none;
border-radius: var(--radius); border-radius: var(--radius);
cursor: pointer; cursor: pointer;
transition: 0.2s background-color;
} }
button:hover, button:focus { button:hover, button:focus, input[type=submit]:hover, input[type=submit]:focus {
background-color: color-mix(in srgb, var(--accent) 80%, var(--background)); background-color: color-mix(in srgb, var(--accent) 80%, var(--background));
} }
input, textarea {
padding: 0.5rem;
outline: none;
background-color: var(--foreground);
color: var(--text);
border: 1px solid var(--border-color);
border-radius: var(--radius);
transition: 0.2s border;
}
input, textarea:focus {
border-color: var(--accent);
}
.copy-code-button { .copy-code-button {
position: absolute; position: absolute;
top: 0; top: 0;
@ -166,6 +182,10 @@ pre code:hover + .copy-code-button, .copy-code-button:hover {
text-decoration: none; text-decoration: none;
} }
.validation-errors {
color: var(--error);
}
@keyframes title-fade-in { @keyframes title-fade-in {
0% { 0% {
margin-top: 0; margin-top: 0;