mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 05:45:18 +00:00
* Centralize UI theme colors into semantic ThemePalette tokens Introduce AppTheme/ThemePalette (Utils/Theme.swift) with an environment key and @ThemedPalette property wrapper, persisted via @AppStorage. Views now resolve background/primary/secondary/accentBlue/alertRed/ divider tokens from the environment instead of computing colors inline, removing the backgroundColor/textColor/secondaryTextColor prop-drilling through the header, composer, and people-sheet hierarchy. Matrix theme output is pixel-identical; this is groundwork for a user-selectable theme switcher. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Add liquid glass theme with in-app appearance switcher Add a .liquidGlass AppTheme alongside the matrix terminal theme, selectable from a one-line APPEARANCE row in the app info sheet (persisted via @AppStorage, applies live). Liquid glass renders system fonts and colors over a subtle gradient backdrop, with the header and composer floating as Liquid Glass panels (real .glassEffect() on iOS/macOS 26, compiler-gated with an ultraThinMaterial fallback for older SDKs). The message list scrolls underneath the chrome via safe-area insets, and all sheets share the same backdrop and surface language. The matrix theme is unchanged. Details: - Theme is threaded through ChatMessageFormatter so message AttributedStrings switch font design per theme; the per-message format cache gains a variant key so themes never serve each other's cached strings - New palette tokens: accent (interactive tint) and locationAccent (geohash green), replacing scattered hardcoded greens/blues in the voice note, waveform, verification, and people-sheet views - Header controls get full-height tap targets and the people count becomes a real Button (previously a tap gesture on the cluster) - CommandSuggestionsView renders nothing when empty instead of a zero-height view that pushed the composer input off-center - New appearance strings localized for all 29 catalog languages 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>
90 lines
2.9 KiB
Swift
90 lines
2.9 KiB
Swift
//
|
|
// CommandSuggestionsView.swift
|
|
// bitchat
|
|
//
|
|
// Created by Islam on 29/10/2025.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
struct CommandSuggestionsView: View {
|
|
@EnvironmentObject private var privateConversationModel: PrivateConversationModel
|
|
@EnvironmentObject private var locationChannelsModel: LocationChannelsModel
|
|
@ThemedPalette private var palette
|
|
|
|
@Binding var messageText: String
|
|
|
|
private var filteredCommands: [CommandInfo] {
|
|
guard messageText.hasPrefix("/") && !messageText.contains(" ") else { return [] }
|
|
let isGeoPublic = locationChannelsModel.selectedChannel.isLocation
|
|
let isGeoDM = privateConversationModel.selectedPeerID?.isGeoDM == true
|
|
return CommandInfo.all(isGeoPublic: isGeoPublic, isGeoDM: isGeoDM).filter { command in
|
|
command.alias.starts(with: messageText.lowercased())
|
|
}
|
|
}
|
|
|
|
var body: some View {
|
|
// Render nothing when there are no matches: a zero-height view would
|
|
// still receive the composer VStack's spacing and push the input row
|
|
// off-center.
|
|
if !filteredCommands.isEmpty {
|
|
VStack(alignment: .leading, spacing: 0) {
|
|
ForEach(filteredCommands) { command in
|
|
Button {
|
|
messageText = command.alias + " "
|
|
} label: {
|
|
buttonRow(for: command)
|
|
}
|
|
.buttonStyle(.plain)
|
|
.background(Color.gray.opacity(0.1))
|
|
}
|
|
}
|
|
.themedOverlayPanel()
|
|
}
|
|
}
|
|
|
|
private func buttonRow(for command: CommandInfo) -> some View {
|
|
HStack {
|
|
Text(command.alias)
|
|
.bitchatFont(size: 11)
|
|
.foregroundColor(palette.primary)
|
|
.fontWeight(.medium)
|
|
|
|
if let placeholder = command.placeholder {
|
|
Text(placeholder)
|
|
.bitchatFont(size: 10)
|
|
.foregroundColor(palette.secondary.opacity(0.8))
|
|
}
|
|
|
|
Spacer()
|
|
|
|
Text(command.description)
|
|
.bitchatFont(size: 10)
|
|
.foregroundColor(palette.secondary)
|
|
}
|
|
.padding(.horizontal, 12)
|
|
.padding(.vertical, 3)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
}
|
|
}
|
|
|
|
@available(iOS 17, macOS 14, *)
|
|
#Preview {
|
|
@Previewable @State var messageText: String = "/"
|
|
let keychain = KeychainManager()
|
|
let viewModel = ChatViewModel(
|
|
keychain: keychain,
|
|
idBridge: NostrIdentityBridge(),
|
|
identityManager: SecureIdentityStateManager(keychain)
|
|
)
|
|
let privateConversationModel = PrivateConversationModel(
|
|
chatViewModel: viewModel,
|
|
conversations: viewModel.conversations
|
|
)
|
|
let locationChannelsModel = LocationChannelsModel()
|
|
|
|
CommandSuggestionsView(messageText: $messageText)
|
|
.environmentObject(privateConversationModel)
|
|
.environmentObject(locationChannelsModel)
|
|
}
|