mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 16:05:19 +00:00
ux-fixes: lock alignment, caption band, wrapping, tap targets, VoiceOver, theme sweep (#1366)
* Fix lock glyph alignment, privacy-caption band, and empty-state wrapping - Message-row locks: align to first text baseline instead of a hardcoded top padding that left the lock ~4pt below the line's visual center - Header/caption locks: 1pt optical lift (lock.fill ink is bottom-heavy; geometric centering reads low); seal badge stays untouched - DM privacy caption: sit on the themed surface like the rest of the bottom chrome instead of painting its own orange band - Empty-state lines: non-breaking spaces so the closing * can't orphan and 'bitchat/ for help' can't break right after the slash Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Unify sheet close buttons, widen tiny tap targets, handle long nicknames - New SheetCloseButton component: one glyph size/weight (13 semibold), 32pt visual box, 44pt hit target; adopted by all 7 sheets (sizes had drifted across 12/13/14pt, two had no frame at all) - Favorite star buttons get real tap targets (peer list + DM header) - DM header nickname: single line with middle truncation instead of wrapping into the fixed-height header; peer-list names truncate tail - Geohash people rows: leading glyph 12 -> 10 to match mesh rows - Sidebar lock glyphs get the same optical lift as the DM header Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Make channel switching, voice notes, and header actions work under VoiceOver - Channel rows in the location sheet are now single activatable buttons (label + selected trait + switch hint) with the bookmark toggle mirrored as a named accessibility action; bookmark buttons labeled - Voice-note mic: press-and-hold drag gestures can't be activated by VoiceOver, so the default accessibility action now toggles start/stop-and-send; announces 'recording' state; localized labels - Attachment button: camera (long-press) path exposed as a named action; labels localized instead of hardcoded English - People-count button announces connected vs no-one-reachable (was color-only); verification QR button gains a spoken name (.help is only a hint on iOS); bitchat/ logo exposes its tap-for-app-info as a button (panic triple-tap stays undiscoverable on purpose) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Theme-correctness sweep: palette colors everywhere, AX-size header growth - Fingerprint/verification sheet cards: palette-tinted boxes instead of fixed gray bands that ignored matrix green and occluded glass - Voice-note card: palette background (translucent) instead of opaque white/black; waveform + payment chips + image placeholder follow suit - .secondary/.primary/Color.blue swapped for palette.secondary/primary/ accentBlue across location sheets, people sheets, message captions, and the header count (system gray read wrong under matrix green) - Autocomplete/command rows: dropped the uniform gray wash that dulled the themed overlay panel - 'tap to reveal' caption follows the theme font instead of hardcoding monospaced - Headers use minHeight so two-line accessibility text sizes grow the bar instead of clipping inside it Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Fix main header expanding to fill the screen The header bar's fixed height was load-bearing: its children fill the bar with .frame(maxHeight: .infinity) tap targets, so switching to an open-ended minHeight let the header expand to swallow all available vertical space, centering the title mid-screen and crushing the timeline into the composer. Restore the fixed height — headerHeight is a @ScaledMetric, so it already grows with Dynamic Type. Reproduced and verified both layouts with an offscreen render harness. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * DM header: floating glass panel instead of muddy orange wash under glass Orange at 14% over the backdrop gradient reads as a gray-beige band, not a privacy signature. Under liquid glass the DM header now uses the same floating chrome panel as the main header; the private signature is already carried by the orange lock, caption, and composer accents. Matrix keeps its orange wash over the opaque themed surface, unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.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
Claude Fable 5
parent
8296630cf3
commit
d285c6ad53
@@ -33,6 +33,16 @@ struct ContentHeaderView: View {
|
||||
.onTapGesture(count: 1) {
|
||||
appChromeModel.presentAppInfo()
|
||||
}
|
||||
// This is the only entry point to App Info, but it reads as
|
||||
// static text; surface the tap. (The triple-tap panic wipe
|
||||
// stays undiscoverable on purpose — it's destructive.)
|
||||
.accessibilityAddTraits(.isButton)
|
||||
.accessibilityHint(
|
||||
String(localized: "content.accessibility.app_info_hint", comment: "Accessibility hint on the bitchat/ logo explaining a tap opens app info")
|
||||
)
|
||||
.accessibilityAction {
|
||||
appChromeModel.presentAppInfo()
|
||||
}
|
||||
|
||||
HStack(spacing: 0) {
|
||||
Text(verbatim: "@")
|
||||
@@ -183,6 +193,13 @@ struct ContentHeaderView: View {
|
||||
headerOtherPeersCount
|
||||
)
|
||||
)
|
||||
// Connected-vs-nobody is otherwise encoded only in the icon's
|
||||
// color; say it.
|
||||
.accessibilityValue(
|
||||
headerPeersReachable
|
||||
? String(localized: "content.accessibility.peers_connected", comment: "Accessibility value when peers are reachable")
|
||||
: String(localized: "content.accessibility.peers_none", comment: "Accessibility value when no peers are reachable")
|
||||
)
|
||||
}
|
||||
.layoutPriority(3)
|
||||
.sheet(isPresented: $showVerifySheet) {
|
||||
@@ -190,6 +207,11 @@ struct ContentHeaderView: View {
|
||||
.environmentObject(verificationModel)
|
||||
}
|
||||
}
|
||||
// Fixed height is load-bearing: children fill the bar with
|
||||
// .frame(maxHeight: .infinity) tap targets, so an open-ended
|
||||
// minHeight lets the header expand to swallow the whole screen.
|
||||
// headerHeight is a @ScaledMetric, so it still grows with Dynamic
|
||||
// Type.
|
||||
.frame(height: headerHeight)
|
||||
.padding(.horizontal, 12)
|
||||
.sheet(isPresented: $appChromeModel.isLocationChannelsSheetPresented) {
|
||||
@@ -266,14 +288,25 @@ private extension ContentHeaderView {
|
||||
dynamicTypeSize.isAccessibilitySize ? 2 : 1
|
||||
}
|
||||
|
||||
/// Whether anyone is actually reachable on the current channel — the
|
||||
/// state the count icon's color encodes visually.
|
||||
var headerPeersReachable: Bool {
|
||||
switch locationChannelsModel.selectedChannel {
|
||||
case .location:
|
||||
return peerListModel.visibleGeohashPeerCount > 0
|
||||
case .mesh:
|
||||
return peerListModel.connectedMeshPeerCount > 0
|
||||
}
|
||||
}
|
||||
|
||||
func channelPeopleCountAndColor() -> (Int, Color) {
|
||||
switch locationChannelsModel.selectedChannel {
|
||||
case .location:
|
||||
let count = peerListModel.visibleGeohashPeerCount
|
||||
return (count, count > 0 ? palette.locationAccent : Color.secondary)
|
||||
return (count, count > 0 ? palette.locationAccent : palette.secondary)
|
||||
case .mesh:
|
||||
let meshBlue = Color(hue: 0.60, saturation: 0.85, brightness: 0.82)
|
||||
let color: Color = peerListModel.connectedMeshPeerCount > 0 ? meshBlue : Color.secondary
|
||||
let color: Color = peerListModel.connectedMeshPeerCount > 0 ? meshBlue : palette.secondary
|
||||
return (peerListModel.reachableMeshPeerCount, color)
|
||||
}
|
||||
}
|
||||
@@ -293,16 +326,10 @@ private struct ContentLocationNotesUnavailableView: View {
|
||||
Text("content.notes.title")
|
||||
.bitchatFont(size: 16, weight: .bold)
|
||||
Spacer()
|
||||
Button(action: { showLocationNotes = false }) {
|
||||
Image(systemName: "xmark")
|
||||
.bitchatFont(size: 13, weight: .semibold)
|
||||
.foregroundColor(palette.primary)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityLabel(String(localized: "common.close", comment: "Accessibility label for close buttons"))
|
||||
SheetCloseButton { showLocationNotes = false }
|
||||
.foregroundColor(palette.primary)
|
||||
}
|
||||
.frame(height: headerHeight)
|
||||
.frame(minHeight: headerHeight)
|
||||
.padding(.horizontal, 12)
|
||||
.themedChromePanel(edge: .top)
|
||||
Text("content.notes.location_unavailable")
|
||||
|
||||
Reference in New Issue
Block a user