mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 22:05:21 +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
@@ -31,6 +31,9 @@ struct LocationChannelsSheet: View {
|
||||
static let toggleOff: LocalizedStringKey = "common.toggle.off"
|
||||
|
||||
static let invalidGeohash = String(localized: "location_channels.error.invalid_geohash", comment: "Error shown when a custom geohash is invalid")
|
||||
static let switchChannelHint = String(localized: "location_channels.accessibility.switch_hint", comment: "Accessibility hint on a channel row explaining activation switches to it")
|
||||
static let addBookmark = String(localized: "location_channels.accessibility.add_bookmark", comment: "Accessibility action name for bookmarking a channel")
|
||||
static let removeBookmark = String(localized: "location_channels.accessibility.remove_bookmark", comment: "Accessibility action name for removing a channel bookmark")
|
||||
|
||||
static func meshTitle(_ count: Int) -> String {
|
||||
let label = String(localized: "location_channels.mesh_label", comment: "Label for the mesh channel row")
|
||||
@@ -103,7 +106,7 @@ struct LocationChannelsSheet: View {
|
||||
}
|
||||
Text(Strings.description)
|
||||
.bitchatFont(size: 12)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundColor(palette.secondary)
|
||||
|
||||
Group {
|
||||
switch locationChannelsModel.permissionState {
|
||||
@@ -122,7 +125,7 @@ struct LocationChannelsSheet: View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text(Strings.permissionDenied)
|
||||
.bitchatFont(size: 12)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundColor(palette.secondary)
|
||||
Button(Strings.openSettings, action: SystemSettings.location.open)
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
@@ -169,13 +172,7 @@ struct LocationChannelsSheet: View {
|
||||
}
|
||||
|
||||
private var closeButton: some View {
|
||||
Button(action: { isPresented = false }) {
|
||||
Image(systemName: "xmark")
|
||||
.bitchatFont(size: 13, weight: .semibold)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityLabel("Close")
|
||||
SheetCloseButton { isPresented = false }
|
||||
}
|
||||
|
||||
private var channelList: some View {
|
||||
@@ -210,7 +207,10 @@ struct LocationChannelsSheet: View {
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding(.leading, 8)
|
||||
}
|
||||
.accessibilityLabel(locationChannelsModel.isBookmarked(channel.geohash) ? Strings.removeBookmark : Strings.addBookmark)
|
||||
},
|
||||
accessoryActionTitle: locationChannelsModel.isBookmarked(channel.geohash) ? Strings.removeBookmark : Strings.addBookmark,
|
||||
accessoryAction: { locationChannelsModel.toggleBookmark(channel.geohash) }
|
||||
) {
|
||||
locationChannelsModel.markTeleported(for: channel.geohash, false)
|
||||
locationChannelsModel.select(ChannelID.location(channel))
|
||||
@@ -277,7 +277,7 @@ struct LocationChannelsSheet: View {
|
||||
HStack(spacing: 2) {
|
||||
Text(verbatim: "#")
|
||||
.bitchatFont(size: 14)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundColor(palette.secondary)
|
||||
TextField("geohash", text: $customGeohash)
|
||||
#if os(iOS)
|
||||
.textInputAutocapitalization(.never)
|
||||
@@ -319,7 +319,7 @@ struct LocationChannelsSheet: View {
|
||||
.bitchatFont(size: 14)
|
||||
.padding(.vertical, 6)
|
||||
.padding(.horizontal, 10)
|
||||
.background(Color.secondary.opacity(0.12))
|
||||
.background(palette.secondary.opacity(0.12))
|
||||
.cornerRadius(6)
|
||||
.opacity(isValid ? 1.0 : 0.4)
|
||||
.disabled(!isValid)
|
||||
@@ -336,7 +336,7 @@ struct LocationChannelsSheet: View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text(Strings.bookmarked)
|
||||
.bitchatFont(size: 12)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundColor(palette.secondary)
|
||||
LazyVStack(spacing: 0) {
|
||||
ForEach(Array(entries.enumerated()), id: \.offset) { index, gh in
|
||||
let level = levelForLength(gh.count)
|
||||
@@ -357,7 +357,10 @@ struct LocationChannelsSheet: View {
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding(.leading, 8)
|
||||
}
|
||||
.accessibilityLabel(locationChannelsModel.isBookmarked(gh) ? Strings.removeBookmark : Strings.addBookmark)
|
||||
},
|
||||
accessoryActionTitle: locationChannelsModel.isBookmarked(gh) ? Strings.removeBookmark : Strings.addBookmark,
|
||||
accessoryAction: { locationChannelsModel.toggleBookmark(gh) }
|
||||
) {
|
||||
let inRegional = locationChannelsModel.availableChannels.contains { $0.geohash == gh }
|
||||
if !inRegional && !locationChannelsModel.availableChannels.isEmpty {
|
||||
@@ -399,6 +402,8 @@ struct LocationChannelsSheet: View {
|
||||
titleColor: Color? = nil,
|
||||
titleBold: Bool = false,
|
||||
@ViewBuilder trailingAccessory: () -> some View = { EmptyView() },
|
||||
accessoryActionTitle: String? = nil,
|
||||
accessoryAction: (() -> Void)? = nil,
|
||||
action: @escaping () -> Void
|
||||
) -> some View {
|
||||
HStack(alignment: .center, spacing: 8) {
|
||||
@@ -409,17 +414,17 @@ struct LocationChannelsSheet: View {
|
||||
Text(parts.base)
|
||||
.bitchatFont(size: 14)
|
||||
.fontWeight(titleBold ? .bold : .regular)
|
||||
.foregroundColor(titleColor ?? Color.primary)
|
||||
.foregroundColor(titleColor ?? palette.primary)
|
||||
if let count = parts.countSuffix, !count.isEmpty {
|
||||
Text(count)
|
||||
.bitchatFont(size: 11)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundColor(palette.secondary)
|
||||
}
|
||||
}
|
||||
let subtitleFull = Strings.subtitle(prefix: subtitlePrefix, name: subtitleName)
|
||||
Text(subtitleFull)
|
||||
.bitchatFont(size: 12)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundColor(palette.secondary)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
}
|
||||
@@ -434,6 +439,19 @@ struct LocationChannelsSheet: View {
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture(perform: action)
|
||||
// The row is a plain HStack with a tap gesture, which VoiceOver reads
|
||||
// as disconnected static text. Expose it as one activatable button;
|
||||
// the visible bookmark accessory is mirrored as a named action.
|
||||
.accessibilityElement(children: .ignore)
|
||||
.accessibilityLabel(Text(verbatim: "\(title), \(Strings.subtitle(prefix: subtitlePrefix, name: subtitleName))"))
|
||||
.accessibilityAddTraits(isSelected ? [.isButton, .isSelected] : [.isButton])
|
||||
.accessibilityHint(Strings.switchChannelHint)
|
||||
.accessibilityAction(.default, action)
|
||||
.accessibilityActions {
|
||||
if let accessoryActionTitle, let accessoryAction {
|
||||
Button(accessoryActionTitle, action: accessoryAction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Split a title like "#mesh [3 people]" into base and suffix "[3 people]"
|
||||
@@ -477,16 +495,16 @@ extension LocationChannelsSheet {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(Strings.torTitle)
|
||||
.bitchatFont(size: 12, weight: .semibold)
|
||||
.foregroundColor(.primary)
|
||||
.foregroundColor(palette.primary)
|
||||
Text(Strings.torSubtitle)
|
||||
.bitchatFont(size: 11)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundColor(palette.secondary)
|
||||
}
|
||||
}
|
||||
.toggleStyle(IRCToggleStyle(accent: palette.accent, onLabel: Strings.toggleOn, offLabel: Strings.toggleOff))
|
||||
}
|
||||
.padding(12)
|
||||
.background(Color.secondary.opacity(0.12))
|
||||
.background(palette.secondary.opacity(0.12))
|
||||
.cornerRadius(8)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user