mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-26 03:45:21 +00:00
Give private DMs an unmistakable visual signature (#1357)
* Give private DMs an unmistakable visual signature An open DM renders identically to the public room — same view, same green-on-black surface, with a small header name and two orange icons as the only cues. For this audience the cost of misreading "am I in the encrypted DM or the public channel?" is severe: sensitive text typed into the wrong composer. Four presentation-layer cues; no formatter or cache changes: - The composer placeholder states the destination instead of a generic prompt: "message @jack — private" in a DM, "message #mesh — public, nearby" on mesh, "message #9q8yy — public" in a geohash channel. - A persistent lock caption sits above the DM composer. It reads "private · end-to-end encrypted" only once the Noise session is actually secured or verified, and "private conversation" before that — the caption must not overstate encryption mid-handshake. - The DM sheet header carries a faint orange wash (6%), extending the existing orange self-accent to the chrome. - Each private message row is prefixed with a small orange lock glyph (view-layer, hidden from VoiceOver — the caption carries the semantic; the cached AttributedString formatter is untouched). New strings are added source-language (en) only. * Fix geohash-DM caption and placeholder Two carve/review follow-ups: - The privacy caption showed "private conversation" for geohash DMs, implying they are not encrypted — but geohash DMs are NIP-17 gift-wrapped (always end-to-end encrypted), they just carry no Noise session status. Show the encrypted caption for geohash DMs and for secured Noise sessions; the pre-secured wording now applies only while a mesh handshake is still in progress. - The private-chat placeholder prepended "@" to the partner name, which for a geohash DM (whose display name is already "#geohash/@name") produced a doubled "@". The "@" is now added only for mesh nicknames. * Make the DM header orange wash visible in the matrix theme The 6% orange background was chained after .themedSurface(), so in the default matrix theme (whose themedSurface paints an opaque background) the wash sat behind the surface and never rendered — it was only visible in liquid glass. Apply the orange tint before .themedSurface() so it layers in front of the themed background. * Align DM lock glyph across text and media rows; keep header wash visible under glass Media rows in a private conversation now get the same leading lock glyph as text rows, so left edges line up instead of misaligning by the glyph's width. The DM header's orange wash gets a higher opacity under the liquid-glass theme, where themedSurface() adds no opaque backing and 6% orange disappears into the backdrop gradient. Also drops the dead sender != "system" guard in TextMessageView — system messages are routed to systemMessageRow before this view is built. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Remove orphaned content.input.message_placeholder from the string catalog The destination-stating placeholders replaced its last code reference; nothing on the branch resolves this key anymore. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: jack <212554440+jackjackbits@users.noreply.github.com> Co-authored-by: jack <jackjackbits@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
jack
jack
Claude Fable 5
parent
9ccff9cce4
commit
c8ceac1968
@@ -363,6 +363,12 @@ private struct ContentPrivateChatSheetView: View {
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.top, 10)
|
||||
.padding(.bottom, 12)
|
||||
// Orange tint before themedSurface so it layers in front of the
|
||||
// (opaque, in matrix) themed background rather than behind it.
|
||||
// Glass has no opaque backing — themedSurface is a no-op there,
|
||||
// so the wash needs more opacity to read over the backdrop
|
||||
// gradient's blue/purple glows.
|
||||
.background(Color.orange.opacity(theme.usesGlassChrome ? 0.14 : 0.06))
|
||||
.themedSurface()
|
||||
}
|
||||
|
||||
@@ -385,6 +391,8 @@ private struct ContentPrivateChatSheetView: View {
|
||||
Divider()
|
||||
}
|
||||
|
||||
privacyCaption
|
||||
|
||||
#if os(iOS)
|
||||
ContentComposerView(
|
||||
messageText: $messageText,
|
||||
@@ -421,6 +429,41 @@ private struct ContentPrivateChatSheetView: View {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Persistent one-line reminder that this composer feeds a private
|
||||
/// conversation — the DM sheet otherwise renders identically to the
|
||||
/// public timeline. Claims end-to-end encryption only once the session
|
||||
/// is actually secured.
|
||||
private var privacyCaption: some View {
|
||||
HStack(spacing: 5) {
|
||||
Image(systemName: "lock.fill")
|
||||
.font(.bitchatSystem(size: 9))
|
||||
Text(verbatim: privacyCaptionText)
|
||||
.bitchatFont(size: 11, weight: .medium)
|
||||
}
|
||||
.foregroundColor(Color.orange)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 4)
|
||||
.background(Color.orange.opacity(0.08))
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
|
||||
private var privacyCaptionText: String {
|
||||
// Geohash DMs are NIP-17 gift-wrapped — always end-to-end encrypted,
|
||||
// even though they carry no Noise session status. Mesh DMs earn the
|
||||
// "encrypted" claim only once the Noise handshake has secured.
|
||||
let isGeoDM = privateConversationModel.selectedPeerID?.isGeoDM == true
|
||||
let noiseSecured: Bool = {
|
||||
switch privateConversationModel.selectedHeaderState?.encryptionStatus {
|
||||
case .noiseSecured, .noiseVerified: return true
|
||||
default: return false
|
||||
}
|
||||
}()
|
||||
if isGeoDM || noiseSecured {
|
||||
return String(localized: "content.private.caption_encrypted", comment: "Caption above the private chat composer once the session is end-to-end encrypted")
|
||||
}
|
||||
return String(localized: "content.private.caption", comment: "Caption above the private chat composer before encryption is established")
|
||||
}
|
||||
}
|
||||
|
||||
private struct ContentPrivateHeaderInfoButton: View {
|
||||
|
||||
Reference in New Issue
Block a user