mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 00:25:19 +00:00
Screenshot privacy: warn on location sheet; gate screenshot broadcasts to chat only\n\n- Track sheet presentation in ChatViewModel\n- Show privacy alert when screenshot taken on location channels\n- Ignore screenshots for App Info and Location sheet for broadcast\n- ContentView wires sheet onAppear/onDisappear and presents alert\n- Fix location sheet subtitle wrapping: render as single Text to avoid orphaned bullets
This commit is contained in:
@@ -382,6 +382,11 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
|
|||||||
@Published var showBluetoothAlert = false
|
@Published var showBluetoothAlert = false
|
||||||
@Published var bluetoothAlertMessage = ""
|
@Published var bluetoothAlertMessage = ""
|
||||||
@Published var bluetoothState: CBManagerState = .unknown
|
@Published var bluetoothState: CBManagerState = .unknown
|
||||||
|
|
||||||
|
// Presentation state for privacy gating
|
||||||
|
@Published var isLocationChannelsSheetPresented: Bool = false
|
||||||
|
@Published var isAppInfoPresented: Bool = false
|
||||||
|
@Published var showScreenshotPrivacyWarning: Bool = false
|
||||||
|
|
||||||
// Messages are naturally ephemeral - no persistent storage
|
// Messages are naturally ephemeral - no persistent storage
|
||||||
// Persist mesh public timeline across channel switches
|
// Persist mesh public timeline across channel switches
|
||||||
@@ -2588,6 +2593,17 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
|
|||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
@objc private func userDidTakeScreenshot() {
|
@objc private func userDidTakeScreenshot() {
|
||||||
|
// Respect privacy: do not broadcast screenshots taken from non-chat sheets
|
||||||
|
if isLocationChannelsSheetPresented {
|
||||||
|
// Show a warning about sharing location screenshots publicly
|
||||||
|
showScreenshotPrivacyWarning = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if isAppInfoPresented {
|
||||||
|
// Silently ignore screenshots of app info
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Send screenshot notification based on current context
|
// Send screenshot notification based on current context
|
||||||
let screenshotMessage = "* \(nickname) took a screenshot *"
|
let screenshotMessage = "* \(nickname) took a screenshot *"
|
||||||
|
|
||||||
@@ -2607,7 +2623,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show local notification immediately as system message
|
// Show local notification immediately as system message (only in chat)
|
||||||
let localNotification = BitchatMessage(
|
let localNotification = BitchatMessage(
|
||||||
sender: "system",
|
sender: "system",
|
||||||
content: "you took a screenshot",
|
content: "you took a screenshot",
|
||||||
@@ -2658,7 +2674,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Show local notification immediately as system message
|
// Show local notification immediately as system message (only in chat)
|
||||||
let localNotification = BitchatMessage(
|
let localNotification = BitchatMessage(
|
||||||
sender: "system",
|
sender: "system",
|
||||||
content: "you took a screenshot",
|
content: "you took a screenshot",
|
||||||
|
|||||||
@@ -165,6 +165,8 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
.sheet(isPresented: $showAppInfo) {
|
.sheet(isPresented: $showAppInfo) {
|
||||||
AppInfoView()
|
AppInfoView()
|
||||||
|
.onAppear { viewModel.isAppInfoPresented = true }
|
||||||
|
.onDisappear { viewModel.isAppInfoPresented = false }
|
||||||
}
|
}
|
||||||
.sheet(isPresented: Binding(
|
.sheet(isPresented: Binding(
|
||||||
get: { viewModel.showingFingerprintFor != nil },
|
get: { viewModel.showingFingerprintFor != nil },
|
||||||
@@ -1182,6 +1184,13 @@ struct ContentView: View {
|
|||||||
.padding(.horizontal, 12)
|
.padding(.horizontal, 12)
|
||||||
.sheet(isPresented: $showLocationChannelsSheet) {
|
.sheet(isPresented: $showLocationChannelsSheet) {
|
||||||
LocationChannelsSheet(isPresented: $showLocationChannelsSheet)
|
LocationChannelsSheet(isPresented: $showLocationChannelsSheet)
|
||||||
|
.onAppear { viewModel.isLocationChannelsSheetPresented = true }
|
||||||
|
.onDisappear { viewModel.isLocationChannelsSheetPresented = false }
|
||||||
|
}
|
||||||
|
.alert("heads up", isPresented: $viewModel.showScreenshotPrivacyWarning) {
|
||||||
|
Button("ok", role: .cancel) {}
|
||||||
|
} message: {
|
||||||
|
Text("screenshots of location channels will reveal your location. think before sharing publicly.")
|
||||||
}
|
}
|
||||||
.background(backgroundColor.opacity(0.95))
|
.background(backgroundColor.opacity(0.95))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -304,22 +304,17 @@ struct LocationChannelsSheet: View {
|
|||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HStack(spacing: 0) {
|
let subtitleFull: String = {
|
||||||
Text(subtitlePrefix)
|
if let name = subtitleName, !name.isEmpty {
|
||||||
.font(.system(size: 12, design: .monospaced))
|
return subtitlePrefix + " • " + name
|
||||||
.foregroundColor(.secondary)
|
|
||||||
if let name = subtitleName {
|
|
||||||
Text(" • ")
|
|
||||||
.font(.system(size: 12, design: .monospaced))
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
Text(name)
|
|
||||||
.font(.system(size: 12, design: .monospaced))
|
|
||||||
.fontWeight(subtitleNameBold ? .bold : .regular)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
.lineLimit(1)
|
|
||||||
.truncationMode(.tail)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return subtitlePrefix
|
||||||
|
}()
|
||||||
|
Text(subtitleFull)
|
||||||
|
.font(.system(size: 12, design: .monospaced))
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.lineLimit(1)
|
||||||
|
.truncationMode(.tail)
|
||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
if isSelected {
|
if isSelected {
|
||||||
|
|||||||
Reference in New Issue
Block a user