Teams-style chat list, contact name resolution, and fixes
UI:
- Rework the conversation list to a Teams/Outlook-style row: single-line
preview, right-aligned timestamp, unread dot, and a neutral elevated
rounded fill (drawn by a Border that wraps the whole row) for hover/select.
- Smooth the Chats/Calls/Stories rail indicator by suppressing the content
frame's transition, which competed with the indicator animation.
Contact names:
- Fetch + decrypt Signal profile names (ProfileCipher, ProfileService,
ProfileNameStore, GET /v1/profile) when a peer has no synced name.
- Capture each contact's profileKey from the link'n'sync backup and the
contacts sync (un-reserve ContactDetails.profileKey) so names can be
resolved; fall back to the phone number before a raw ACI.
Other:
- Report the device to Signal as "Wingnal: {machine name}".
- Resolve build warnings: target Wingnal.Service at net8.0-windows (CA1416),
initialize zkgroup credential fields (CS8618), hoist a stackalloc (CA2014).
- Skip the live chat-connect diagnostic test by default (it logged in as the
real account and disconnected the app).
- Bump app version 1.0.1.0 -> 1.0.2.0.
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
namespace Wingnal.Service.Net;
|
||||
|
||||
/// <summary>The subset of <c>GET /v1/profile/{id}</c> we consume: the encrypted display <c>name</c>
|
||||
/// (base64 of <c>nonce || ciphertext || tag</c>, decryptable with the recipient's profile key). Other
|
||||
/// fields (about, avatar, capabilities, …) are ignored.</summary>
|
||||
public sealed record ProfileResponse
|
||||
{
|
||||
public string? Name { get; init; }
|
||||
public string? About { get; init; }
|
||||
}
|
||||
@@ -74,6 +74,19 @@ public sealed class SignalRestClient : IDisposable
|
||||
?? throw new InvalidOperationException("empty prekey response");
|
||||
}
|
||||
|
||||
/// <summary>Fetches a recipient's profile (GET /v1/profile/{serviceId}). The <c>name</c> field is
|
||||
/// base64 of a ProfileCipher blob encrypted under the recipient's profile key. Returns null when the
|
||||
/// profile isn't accessible (e.g. 401/403/404) so callers can fall back to a placeholder name rather
|
||||
/// than surfacing an error. <paramref name="authToken"/> is Basic {aci.deviceId:password}.</summary>
|
||||
public async Task<ProfileResponse?> GetProfileAsync(string serviceId, string authToken, CancellationToken ct)
|
||||
{
|
||||
using var msg = new HttpRequestMessage(HttpMethod.Get, $"/v1/profile/{serviceId}");
|
||||
msg.Headers.Authorization = new AuthenticationHeaderValue("Basic", authToken);
|
||||
using HttpResponseMessage response = await _http.SendAsync(msg, ct).ConfigureAwait(false);
|
||||
if (!response.IsSuccessStatusCode) return null;
|
||||
return await response.Content.ReadFromJsonAsync<ProfileResponse>(Json, ct).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>Sends a list of per-device encrypted messages to a destination. Returns the raw body on
|
||||
/// a device-mismatch (409) / stale-devices (410) so the caller can react; throws on other failures.</summary>
|
||||
public async Task<(bool Ok, HttpStatusCode Status, string Body)> SendMessagesAsync(
|
||||
|
||||
Reference in New Issue
Block a user