diff --git a/bitchat/App/AppChromeModel.swift b/bitchat/App/AppChromeModel.swift index 227536ca..dda9f654 100644 --- a/bitchat/App/AppChromeModel.swift +++ b/bitchat/App/AppChromeModel.swift @@ -71,7 +71,13 @@ final class AppChromeModel: ObservableObject { showingFingerprintFor = nil } - func presentAppInfo() { + /// Presents the App Info sheet. Pass a pane when the launching control + /// promises one (the header gear is labeled "settings"); nil keeps the + /// sheet's own sticky last-used pane. + func presentAppInfo(pane: AppInfoView.Pane? = nil) { + if let pane { + AppInfoView.setSelectedPane(pane) + } isAppInfoPresented = true } diff --git a/bitchat/Localizable.xcstrings b/bitchat/Localizable.xcstrings index f34ed351..d4eb2335 100644 --- a/bitchat/Localizable.xcstrings +++ b/bitchat/Localizable.xcstrings @@ -76527,6 +76527,192 @@ "zh-Hans" : { "stringUnit" : { "state" : "needs_review", "value" : "用于草稿" } }, "zh-Hant" : { "stringUnit" : { "state" : "needs_review", "value" : "用於草稿" } } } + }, + "content.header.settings" : { + "comment" : "Accessibility label and tooltip for the header gear button that opens the settings/app info sheet", + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الإعدادات" + } + }, + "bn" : { + "stringUnit" : { + "state" : "translated", + "value" : "সেটিংস" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "einstellungen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "settings" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "ajustes" + } + }, + "fa" : { + "stringUnit" : { + "state" : "translated", + "value" : "تنظیمات" + } + }, + "fil" : { + "stringUnit" : { + "state" : "translated", + "value" : "settings" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "réglages" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הגדרות" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "सेटिंग्स" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "pengaturan" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "impostazioni" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "設定" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "설정" + } + }, + "ms" : { + "stringUnit" : { + "state" : "translated", + "value" : "tetapan" + } + }, + "ne" : { + "stringUnit" : { + "state" : "translated", + "value" : "सेटिङ" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "instellingen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "ustawienia" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "definições" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "ajustes" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "настройки" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "inställningar" + } + }, + "ta" : { + "stringUnit" : { + "state" : "translated", + "value" : "அமைப்புகள்" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ตั้งค่า" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ayarlar" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "налаштування" + } + }, + "ur" : { + "stringUnit" : { + "state" : "translated", + "value" : "سیٹنگز" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "cài đặt" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "设置" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "設定" + } + } + } } }, "version" : "1.1" diff --git a/bitchat/Views/AppInfoView.swift b/bitchat/Views/AppInfoView.swift index cb99fbac..7a56bd53 100644 --- a/bitchat/Views/AppInfoView.swift +++ b/bitchat/Views/AppInfoView.swift @@ -28,18 +28,28 @@ struct AppInfoView: View { @ObservedObject private var locationManager = LocationChannelManager.shared /// Sticky across opens: first-ever open lands on Info (the gentler /// introduction), and afterwards the sheet reopens wherever it was left. - @AppStorage("appInfo.selectedPane") private var selectedPane: Pane = .info + @AppStorage(AppInfoView.selectedPaneStorageKey) private var selectedPane: Pane = .info @State private var showPanicConfirmation = false @AppStorage(AppLanguageSettings.overrideKey) private var languageOverride = "" /// The override changed this session; localization resolves at process /// start, so surface the restart hint. @State private var showLanguageRestartNote = false - private enum Pane: String { + enum Pane: String { case settings case info } + private static let selectedPaneStorageKey = "appInfo.selectedPane" + + /// Overwrites the remembered pane so the next presentation opens there. + /// `selectedPane` is sticky by design; a caller whose control names a + /// specific pane (the header gear says "settings") uses this so the + /// sheet doesn't land first-time users on the Info default instead. + static func setSelectedPane(_ pane: Pane) { + UserDefaults.standard.set(pane.rawValue, forKey: selectedPaneStorageKey) + } + private var selectedTheme: AppTheme { AppTheme(rawValue: appThemeRawValue) ?? .matrix } diff --git a/bitchat/Views/ContentHeaderView.swift b/bitchat/Views/ContentHeaderView.swift index 96b548fb..a724f361 100644 --- a/bitchat/Views/ContentHeaderView.swift +++ b/bitchat/Views/ContentHeaderView.swift @@ -52,9 +52,10 @@ 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.) + // The gear in the icon cluster is the discoverable entry + // point to App Info; the logo tap stays as a shortcut but + // reads as static text, so surface it. (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") @@ -286,6 +287,26 @@ struct ContentHeaderView: View { ? 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")) ) + + // Settings entry point new users can actually find; the + // logo tap remains as a shortcut for those who know it. + // Opens on the settings pane explicitly: the sheet's pane + // memory defaults to info, and a button labeled "settings" + // must not land there. + Button(action: { appChromeModel.presentAppInfo(pane: .settings) }) { + Image(systemName: "gearshape") + .font(.bitchatSystem(size: 12)) + .foregroundColor(palette.secondary.opacity(0.9)) + .headerTapTarget() + } + .buttonStyle(.plain) + .padding(.leading, 4) + .accessibilityLabel( + String(localized: "content.header.settings", defaultValue: "settings", comment: "Accessibility label and tooltip for the header gear button that opens the settings/app info sheet") + ) + .help( + String(localized: "content.header.settings", defaultValue: "settings", comment: "Accessibility label and tooltip for the header gear button that opens the settings/app info sheet") + ) } .layoutPriority(3) .sheet(isPresented: $showVerifySheet) {