House rules: numbers through one formatter, no made-up server errors, and the rules fail the build
Build / Build (push) Successful in 33s
Build / Build (push) Successful in 33s
- Every number shown on a page goes through NumbersExtensions: counts, percentages, the nerd readouts and the values written into a style. - A failure the client builds itself (offline, timeout, retries run out, an answer it can't read) is a 400 that names its kind; only a server's own 5XX passes through. - Login, sign-up, recovery and password confirmations leave autocomplete to the browser, so it can offer the saved login. - The Daisy components start with empty lists and attributes, and a decorative image still renders alt="". - Arasaka.HouseRules.Analyzers checks all of this in every build, together with PrivaPub.ClientModels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W4YtEJrgxG12Pc39Fai8Bd
This commit is contained in:
1 parent
697b2c8897
commit
3827d903a3
37 files changed
+165
-111
No files matched your search
@@ -279,7 +279,7 @@ namespace decePubClient.Services
|
||||
|
||||
var account = await response.MastodonReadFromJsonAsync<Account>(cancellationToken);
|
||||
return account?.Id is null
|
||||
? new WebResult().Invalidate(localizer["The server sent an invalid response."], (int)HttpStatusCode.BadGateway)
|
||||
? new WebResult().Invalidate(localizer["The server sent an invalid response."], (int)HttpStatusCode.BadRequest)
|
||||
: new WebResult { Data = account };
|
||||
}
|
||||
|
||||
@@ -293,7 +293,7 @@ namespace decePubClient.Services
|
||||
|
||||
var account = await response.MastodonReadFromJsonAsync<Account>(cancellationToken);
|
||||
return account?.Id is null
|
||||
? new WebResult().Invalidate(localizer["The server sent an invalid response."], (int)HttpStatusCode.BadGateway)
|
||||
? new WebResult().Invalidate(localizer["The server sent an invalid response."], (int)HttpStatusCode.BadRequest)
|
||||
: new WebResult { Data = account };
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace decePubClient.Services
|
||||
|
||||
var jwtUser = await response.DefaultReadFromJsonAsync<JwtUser>(cancellationToken);
|
||||
if (string.IsNullOrEmpty(jwtUser?.Token))
|
||||
return new WebResult().Invalidate(localizer["The server sent an invalid response."], (int)HttpStatusCode.BadGateway);
|
||||
return new WebResult().Invalidate(localizer["The server sent an invalid response."], (int)HttpStatusCode.BadRequest);
|
||||
|
||||
await authStateProvider.SetSession(jwtUser, cancellationToken);
|
||||
return new WebResult { Data = jwtUser };
|
||||
@@ -262,7 +262,7 @@ namespace decePubClient.Services
|
||||
|
||||
var setup = await response.DefaultReadFromJsonAsync<ViewTotpSetup>(cancellationToken);
|
||||
if (string.IsNullOrEmpty(setup?.Secret))
|
||||
return new WebResult().Invalidate(localizer["The server sent an invalid response."], (int)HttpStatusCode.BadGateway);
|
||||
return new WebResult().Invalidate(localizer["The server sent an invalid response."], (int)HttpStatusCode.BadRequest);
|
||||
|
||||
return new WebResult { Data = setup };
|
||||
}
|
||||
@@ -282,7 +282,7 @@ namespace decePubClient.Services
|
||||
|
||||
var enabled = await response.DefaultReadFromJsonAsync<ViewTotpEnabled>(cancellationToken);
|
||||
if (string.IsNullOrEmpty(enabled?.Session?.Token))
|
||||
return new WebResult().Invalidate(localizer["The server sent an invalid response."], (int)HttpStatusCode.BadGateway);
|
||||
return new WebResult().Invalidate(localizer["The server sent an invalid response."], (int)HttpStatusCode.BadRequest);
|
||||
|
||||
await ContinueSession(enabled.Session, nameof(TotpEnable), cancellationToken);
|
||||
return new WebResult { Data = enabled.RecoveryCodes };
|
||||
|
||||
@@ -159,7 +159,7 @@ namespace decePubClient.Services
|
||||
if (string.IsNullOrEmpty(token?.AccessToken))
|
||||
{
|
||||
response.Dispose();
|
||||
return (SUtility.ClassifiedFailure(FailureCodes.ServerError, localizer["The server sent an invalid response."], HttpStatusCode.BadGateway), null);
|
||||
return (SUtility.ClassifiedFailure(FailureCodes.ServerError, localizer["The server sent an invalid response."], HttpStatusCode.BadRequest), null);
|
||||
}
|
||||
|
||||
return (response, token.AccessToken);
|
||||
@@ -205,7 +205,7 @@ namespace decePubClient.Services
|
||||
{
|
||||
await logger.ProcessError(ex, where, cancellationToken);
|
||||
return SUtility.ClassifiedFailure(FailureCodes.ClientError,
|
||||
localizer["The request could not be prepared. Reload the page and try again."], HttpStatusCode.ServiceUnavailable);
|
||||
localizer["The request could not be prepared. Reload the page and try again."], HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
var delay = attempt < RetryRampUpDelaysMs.Length ? RetryRampUpDelaysMs[attempt] : RetryIndefiniteDelayMs;
|
||||
@@ -216,7 +216,7 @@ namespace decePubClient.Services
|
||||
if (transportException is not null)
|
||||
return await DescribeUnreachable(transportException, attempt + 1, cancellationToken);
|
||||
return SUtility.ClassifiedFailure(FailureCodes.RetriesExhausted,
|
||||
localizer["The server is still not answering after {0} attempts. Try again later.", attempt + 1], HttpStatusCode.ServiceUnavailable);
|
||||
localizer["The server is still not answering after {0} attempts. Try again later.", attempt + 1], HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
await logger.ProcessWarning($"Attempt {attempt + 1} failed, retrying in {delay}ms. {failure}", where, cancellationToken);
|
||||
@@ -245,18 +245,18 @@ namespace decePubClient.Services
|
||||
{
|
||||
if (!await statusService.IsOnline(cancellationToken))
|
||||
return SUtility.ClassifiedFailure(FailureCodes.DeviceOffline,
|
||||
localizer["This device is offline. Check the connection and try again."], HttpStatusCode.ServiceUnavailable);
|
||||
localizer["This device is offline. Check the connection and try again."], HttpStatusCode.BadRequest);
|
||||
|
||||
if (exception is TaskCanceledException { InnerException: TimeoutException })
|
||||
return SUtility.ClassifiedFailure(FailureCodes.Timeout,
|
||||
localizer["The server took too long to answer. Try again later."], HttpStatusCode.ServiceUnavailable);
|
||||
localizer["The server took too long to answer. Try again later."], HttpStatusCode.BadRequest);
|
||||
|
||||
if (attempts > 1)
|
||||
return SUtility.ClassifiedFailure(FailureCodes.RetriesExhausted,
|
||||
localizer["The server is still not answering after {0} attempts. Try again later.", attempts], HttpStatusCode.ServiceUnavailable);
|
||||
localizer["The server is still not answering after {0} attempts. Try again later.", attempts], HttpStatusCode.BadRequest);
|
||||
|
||||
return SUtility.ClassifiedFailure(FailureCodes.ApiUnreachable,
|
||||
localizer["The server can't be reached. Try again later."], HttpStatusCode.ServiceUnavailable);
|
||||
localizer["The server can't be reached. Try again later."], HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
Task<HttpResponseMessage> SendAnonymousRequest(Func<HttpRequestMessage> requestFactory, CancellationToken cancellationToken) =>
|
||||
@@ -403,7 +403,7 @@ namespace decePubClient.Services
|
||||
using var timeout = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
timeout.CancelAfter(request.Content is MultipartContent ? UploadTimeout : RequestTimeout);
|
||||
var started = Stopwatch.GetTimestamp();
|
||||
HttpResponseMessage response;
|
||||
var response = default(HttpResponseMessage);
|
||||
try
|
||||
{
|
||||
response = await httpClient.SendAsync(request, timeout.Token);
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace decePubClient.Services
|
||||
if (!current.IsSuccessStatusCode)
|
||||
return await current.Content.ReadWebResult(current.StatusCode, localizer, cancellationToken);
|
||||
if (await current.DefaultReadFromJsonAsync<ViewServerSettings>(cancellationToken) is not { } latest)
|
||||
return new WebResult().Invalidate(localizer["The server sent an invalid response."], (int)HttpStatusCode.BadGateway);
|
||||
return new WebResult().Invalidate(localizer["The server sent an invalid response."], (int)HttpStatusCode.BadRequest);
|
||||
|
||||
var form = FormOf(latest);
|
||||
change(form);
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace decePubClient.Services
|
||||
{
|
||||
var attachment = await response.MastodonReadFromJsonAsync<MediaAttachment>(cancellationToken);
|
||||
return attachment?.Id is null
|
||||
? new WebResult().Invalidate(localizer["The server sent an invalid response."], (int)HttpStatusCode.BadGateway)
|
||||
? new WebResult().Invalidate(localizer["The server sent an invalid response."], (int)HttpStatusCode.BadRequest)
|
||||
: new WebResult { Data = attachment };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace decePubClient.Services
|
||||
|
||||
public async Task CloseTop(CancellationToken cancellationToken = default)
|
||||
{
|
||||
Entry top;
|
||||
var top = default(Entry);
|
||||
lock (open)
|
||||
top = open.LastOrDefault();
|
||||
if (top is null)
|
||||
@@ -40,7 +40,7 @@ namespace decePubClient.Services
|
||||
|
||||
public async Task CloseAll(CancellationToken cancellationToken = default)
|
||||
{
|
||||
Entry[] all;
|
||||
var all = default(Entry[]);
|
||||
lock (open)
|
||||
{
|
||||
all = [.. open];
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace decePubClient.Services
|
||||
if (personaIds.Count > 0 && key is null)
|
||||
{
|
||||
await Save(device, cancellationToken);
|
||||
return result.Invalidate(localizer["This server does not offer push notifications."], (int)HttpStatusCode.NotImplemented);
|
||||
return result.Invalidate(localizer["This server does not offer push notifications."], (int)HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
foreach (var personaId in personaIds)
|
||||
@@ -252,7 +252,7 @@ namespace decePubClient.Services
|
||||
{
|
||||
if (isNew)
|
||||
await Js("unsubscribe", false, [entry.Slot], cancellationToken);
|
||||
return new WebResult().Invalidate(localizer["This browser did not subscribe: {0}", browser?.Error ?? "?"], (int)HttpStatusCode.ServiceUnavailable);
|
||||
return new WebResult().Invalidate(localizer["This browser did not subscribe: {0}", browser?.Error ?? "?"], (int)HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
var result = await Tell(device, entry.PersonaId, browser, cancellationToken);
|
||||
|
||||
@@ -332,7 +332,7 @@ namespace decePubClient.Services
|
||||
|
||||
var status = await response.MastodonReadFromJsonAsync<Status>(cancellationToken);
|
||||
return status is null
|
||||
? new WebResult().Invalidate(localizer["The server sent an invalid response."], (int)HttpStatusCode.BadGateway)
|
||||
? new WebResult().Invalidate(localizer["The server sent an invalid response."], (int)HttpStatusCode.BadRequest)
|
||||
: new WebResult { Data = status };
|
||||
}
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ namespace decePubClient.Services
|
||||
while (socket.State == WebSocketState.Open)
|
||||
{
|
||||
var text = new StringBuilder();
|
||||
WebSocketReceiveResult received;
|
||||
var received = default(WebSocketReceiveResult);
|
||||
do
|
||||
{
|
||||
received = await socket.ReceiveAsync(buffer, cancellationToken);
|
||||
@@ -229,7 +229,7 @@ namespace decePubClient.Services
|
||||
|
||||
/// <summary>Closes the socket; the page polls from then on. The announcements stay asked for: the next Follow opens
|
||||
/// the socket for them too.</summary>
|
||||
public async Task Stop()
|
||||
public async Task Stop(CancellationToken cancellationToken = default)
|
||||
{
|
||||
current = null;
|
||||
if (running is not null)
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace decePubClient.Services
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.ProcessError(ex, $"{nameof(TimelineService)}.{nameof(Load)}:{query.Type}", cancellationToken);
|
||||
return new WebResult().Invalidate(localizer["The feed could not be read."], (int)HttpStatusCode.BadGateway, exception: ex);
|
||||
return new WebResult().Invalidate(localizer["The feed could not be read."], (int)HttpStatusCode.BadRequest, exception: ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user