Files
decePubClient/Services/AppStatusService.cs
T
thepraandClaude Opus 5.5 b731d8c8c0
Build / Build (push) Successful in 1m3s
R3 two-factor in decePub: an authenticator app set up in Settings, its code asked for at sign-in
Completion program R3, with PrivaPub's TOTP routes of the same release.

- Settings, the login: TotpEditor turns two-factor sign-in on (the password, then the QR code, the secret and the
  otpauth:// link, then the app's first code; the ten recovery codes shown once with Copy), shows how many recovery
  codes are left (warned at three), and turns it off with the password and a code, asked first.
- Turning it on, like a password change, ends every other session: AuthService.ContinueSession takes the new JWT and
  exchanges the persona's token again.
- Sign-in: PrivaPub's 428 keeps the name and password and asks for the six digits (DOtp), or a recovery code instead;
  a wrong code says so; Back returns to the password.
- window.copyText (main.js) through AppStatusService.CopyText.
- 29 keys in both resx files; CLAUDE.md follows.

Verified: dotnet build decePubClient.csproj -c Release (0 errors, no new warnings).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpVCoDeM1qUTRp4fXQdfof
2026-10-17 03:33:46 +02:00

45 lines
1.9 KiB
C#

using Microsoft.AspNetCore.Components;
using decePubClient.Models;
using Microsoft.JSInterop;
namespace decePubClient.Services;
public class AppStatusService(IJSRuntime jsRuntime)
{
readonly IJSInProcessRuntime JsSyncRuntime = (IJSInProcessRuntime)jsRuntime;
public async ValueTask<bool> IsOnline(CancellationToken cancellationToken = default) =>
await jsRuntime.InvokeAsync<bool>("isOnline", cancellationToken);
public string Language() => JsSyncRuntime.Invoke<string>("getLanguage");
public void SetAppBadge(int counter) => JsSyncRuntime.InvokeVoid("setAppBadge", counter);
public void ClearAppBadge() => JsSyncRuntime.InvokeVoid("clearAppBadge");
// wwwroot/js/theme.js
public void ApplyTheme(PageSettings pageSettings) => JsSyncRuntime.InvokeVoid("neoTheme.apply", pageSettings);
public bool IsDarkTheme() => JsSyncRuntime.Invoke<bool>("neoTheme.isDark");
public bool IsMobileMedia() => JsSyncRuntime.Invoke<bool>("isMobileMedia");
// whether the navigation being decided came from the Back (or Forward) button
public bool TookBack() => JsSyncRuntime.Invoke<bool>("tookBack");
// whether the pointer can hover (a mouse); a touch screen can't, so what shows on hover there opens on a tap
public bool CanHover() => JsSyncRuntime.Invoke<bool>("canHover");
// read once: a card asks it for each gifv it shows
bool? reducedMotion;
public bool PrefersReducedMotion() => reducedMotion ??= JsSyncRuntime.Invoke<bool>("prefersReducedMotion");
// puts the text on the clipboard; false when the browser refused it, which main.js reports through logFromJs
public async ValueTask<bool> CopyText(string text, CancellationToken cancellationToken = default) =>
await jsRuntime.InvokeAsync<bool>("copyText", cancellationToken, text);
// a sideways-scrolling strip of tabs shows its selected tab
public void RevealActive(ElementReference strip) => JsSyncRuntime.InvokeVoid("revealActive", strip);
}