Merge 72560f56f6010040c4c9c3d01bd224b387e1268c into 1f59e814f90c3f489f48d68262cb1bf640bf6181

This commit is contained in:
Taksh Kothari 2026-08-05 14:08:20 +00:00 committed by GitHub
commit 30e88a7a02
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 55059 additions and 53171 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,63 @@
//
// BridgeStatusSummary.swift
// bitchat
//
// This is free and unencumbered software released into the public domain.
// For more information, see <https://unlicense.org>
//
import Foundation
/// One-line mesh bridge status for the settings pane.
enum BridgeStatusSummary {
static func formatted(
enabled: Bool,
cell: String?,
bridgedCount: Int,
nearbyOnly: Bool
) -> String {
if !enabled {
return String(
localized: "app_info.settings.bridge.status.off",
defaultValue: "bridge off — only radio-range mesh traffic",
comment: "Bridge status line when the mesh bridge toggle is off"
)
}
let cellPart = cell.map {
String(
format: String(
localized: "app_info.settings.bridge.status.cell",
defaultValue: "rendezvous #%@",
comment: "Bridge status fragment showing rendezvous cell; %@ is geohash"
),
locale: .current,
$0
)
} ?? String(
localized: "app_info.settings.bridge.status.no_cell",
defaultValue: "no rendezvous cell",
comment: "Bridge status fragment when no cell is active"
)
let peoplePart = String(
format: String(
localized: "app_info.settings.bridge.status.people",
defaultValue: "%lld people via bridge",
comment: "Bridge status fragment counting bridged participants; %lld is count"
),
locale: .current,
bridgedCount
)
let composePart = nearbyOnly
? String(
localized: "app_info.settings.bridge.status.compose_nearby",
defaultValue: "compose: nearby only",
comment: "Bridge status fragment when outgoing mesh messages stay on radio"
)
: String(
localized: "app_info.settings.bridge.status.compose_bridged",
defaultValue: "compose: bridged",
comment: "Bridge status fragment when outgoing mesh messages cross the bridge"
)
return [cellPart, peoplePart, composePart].joined(separator: " · ")
}
}

View File

@ -78,6 +78,14 @@ struct AppInfoView: View {
)
}
static let bridgeNoCell = String(localized: "app_info.settings.bridge.no_cell", defaultValue: "no rendezvous cell yet — needs location access or a nearby bridge peer", comment: "Caption under the mesh bridge toggle when the bridge is on but has no geohash cell to meet on")
static func bridgeStatusSummary(enabled: Bool, cell: String?, bridgedCount: Int, nearbyOnly: Bool) -> String {
BridgeStatusSummary.formatted(
enabled: enabled,
cell: cell,
bridgedCount: bridgedCount,
nearbyOnly: nearbyOnly
)
}
// Moved from LocationChannelsSheet; keys unchanged. (The former
// internet-gateway toggle is gone: the bridge switch drives all
@ -440,6 +448,17 @@ struct AppInfoView: View {
.bitchatFont(size: 11)
.foregroundColor(secondaryTextColor)
}
Text(
verbatim: Strings.Settings.bridgeStatusSummary(
enabled: bridgeService.isEnabled,
cell: bridgeService.activeCell,
bridgedCount: bridgeService.bridgedPeerCount,
nearbyOnly: bridgeService.nearbyOnly
)
)
.bitchatFont(size: 11)
.foregroundColor(secondaryTextColor)
.fixedSize(horizontal: false, vertical: true)
}
settingsCard {

View File

@ -0,0 +1,28 @@
//
// BridgeStatusSummaryTests.swift
// bitchatTests
//
// This is free and unencumbered software released into the public domain.
// For more information, see <https://unlicense.org>
//
import Testing
@testable import bitchat
struct BridgeStatusSummaryTests {
@Test func offStateDoesNotMentionBridgePeople() {
let text = BridgeStatusSummary.formatted(enabled: false, cell: "u4pruy", bridgedCount: 3, nearbyOnly: false)
#expect(text.lowercased().contains("bridge off"))
}
@Test func enabledStateIncludesCellAndCount() {
let text = BridgeStatusSummary.formatted(enabled: true, cell: "u4pruy", bridgedCount: 2, nearbyOnly: true)
#expect(text.contains("u4pruy"))
#expect(text.contains("nearby"))
}
}
@Test func enabledWithZeroPeersMentionsBridgeCell() {
let text = BridgeStatusSummary.formatted(enabled: true, cell: "u4pruy", bridgedCount: 0, nearbyOnly: false)
#expect(text.localizedCaseInsensitiveContains("u4pruy") || text.localizedCaseInsensitiveContains("bridge"))
}

View File

@ -0,0 +1,10 @@
# Bridge status in settings
The mesh bridge settings card now includes a one-line **status summary**:
- Whether the bridge is on or off
- Active rendezvous cell (when known)
- Count of people visible via the bridge
- Current compose scope (nearby-only vs bridged)
This makes bridge behavior discoverable without opening the people sheet or guessing from header icons.

View File

@ -0,0 +1,53 @@
#!/usr/bin/env python3
"""Add localization keys to Localizable.xcstrings with en + needs_review placeholders."""
from __future__ import annotations
import json
import sys
from pathlib import Path
LOCALES = [
"ar", "bn", "de", "en", "es", "fa", "fil", "fr", "he", "hi", "id", "it",
"ja", "ko", "ms", "ne", "nl", "pl", "pt", "pt-BR", "ru", "sv", "ta", "th",
"tr", "uk", "ur", "vi", "zh-Hans", "zh-Hant",
]
XCSTRINGS = Path(__file__).resolve().parents[1] / "bitchat" / "Localizable.xcstrings"
def add_key(key: str, en_value: str, comment: str) -> None:
with XCSTRINGS.open(encoding="utf-8") as f:
data = json.load(f)
if key in data["strings"]:
print(f"skip existing: {key}", file=sys.stderr)
return
localizations = {}
for locale in LOCALES:
state = "translated" if locale == "en" else "needs_review"
localizations[locale] = {"stringUnit": {"state": state, "value": en_value}}
data["strings"][key] = {
"comment": comment,
"extractionState": "manual",
"localizations": localizations,
}
with XCSTRINGS.open("w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
f.write("\n")
print(f"added: {key}")
def main() -> None:
if len(sys.argv) < 3:
print("usage: add_localizable_key.py <key> <en_value> [comment]", file=sys.stderr)
sys.exit(1)
add_key(sys.argv[1], sys.argv[2], sys.argv[3] if len(sys.argv) > 3 else "")
if __name__ == "__main__":
main()