mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
Merge branch 'master' into master-android
This commit is contained in:
@@ -927,14 +927,19 @@ func standaloneFileInfo(url: String, ctrl: chat_ctrl? = nil) async -> MigrationF
|
||||
}
|
||||
}
|
||||
|
||||
func receiveFile(user: any UserLike, fileId: Int64, auto: Bool = false) async {
|
||||
if let chatItem = await apiReceiveFile(fileId: fileId, encrypted: privacyEncryptLocalFilesGroupDefault.get(), auto: auto) {
|
||||
func receiveFile(user: any UserLike, fileId: Int64, userApprovedRelays: Bool = false, auto: Bool = false) async {
|
||||
if let chatItem = await apiReceiveFile(
|
||||
fileId: fileId,
|
||||
userApprovedRelays: userApprovedRelays || !privacyAskToApproveRelaysGroupDefault.get(),
|
||||
encrypted: privacyEncryptLocalFilesGroupDefault.get(),
|
||||
auto: auto
|
||||
) {
|
||||
await chatItemSimpleUpdate(user, chatItem)
|
||||
}
|
||||
}
|
||||
|
||||
func apiReceiveFile(fileId: Int64, encrypted: Bool, inline: Bool? = nil, auto: Bool = false) async -> AChatItem? {
|
||||
let r = await chatSendCmd(.receiveFile(fileId: fileId, encrypted: encrypted, inline: inline))
|
||||
func apiReceiveFile(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool, inline: Bool? = nil, auto: Bool = false) async -> AChatItem? {
|
||||
let r = await chatSendCmd(.receiveFile(fileId: fileId, userApprovedRelays: userApprovedRelays, encrypted: encrypted, inline: inline))
|
||||
let am = AlertManager.shared
|
||||
if case let .rcvFileAccepted(_, chatItem) = r { return chatItem }
|
||||
if case .rcvFileAcceptedSndCancelled = r {
|
||||
@@ -947,19 +952,50 @@ func apiReceiveFile(fileId: Int64, encrypted: Bool, inline: Bool? = nil, auto: B
|
||||
}
|
||||
} else if let networkErrorAlert = networkErrorAlert(r) {
|
||||
logger.error("apiReceiveFile network error: \(String(describing: r))")
|
||||
am.showAlert(networkErrorAlert)
|
||||
if !auto {
|
||||
am.showAlert(networkErrorAlert)
|
||||
}
|
||||
} else {
|
||||
switch chatError(r) {
|
||||
case .fileCancelled:
|
||||
logger.debug("apiReceiveFile ignoring fileCancelled error")
|
||||
case .fileAlreadyReceiving:
|
||||
logger.debug("apiReceiveFile ignoring fileAlreadyReceiving error")
|
||||
case let .fileNotApproved(fileId, unknownServers):
|
||||
logger.debug("apiReceiveFile fileNotApproved error")
|
||||
if !auto {
|
||||
let srvs = unknownServers.map { s in
|
||||
if let srv = parseServerAddress(s), !srv.hostnames.isEmpty {
|
||||
srv.hostnames[0]
|
||||
} else {
|
||||
serverHost(s)
|
||||
}
|
||||
}
|
||||
am.showAlert(Alert(
|
||||
title: Text("Unknown servers!"),
|
||||
message: Text("Without Tor or VPN, your IP address will be visible to these XFTP relays: \(srvs.sorted().joined(separator: ", "))."),
|
||||
primaryButton: .default(
|
||||
Text("Download"),
|
||||
action: {
|
||||
Task {
|
||||
logger.debug("apiReceiveFile fileNotApproved alert - in Task")
|
||||
if let user = ChatModel.shared.currentUser {
|
||||
await receiveFile(user: user, fileId: fileId, userApprovedRelays: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
),
|
||||
secondaryButton: .cancel()
|
||||
))
|
||||
}
|
||||
default:
|
||||
logger.error("apiReceiveFile error: \(String(describing: r))")
|
||||
am.showAlertMsg(
|
||||
title: "Error receiving file",
|
||||
message: "Error: \(String(describing: r))"
|
||||
)
|
||||
if !auto {
|
||||
am.showAlertMsg(
|
||||
title: "Error receiving file",
|
||||
message: "Error: \(String(describing: r))"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -49,7 +49,7 @@ func localizedInfoRow(_ title: LocalizedStringKey, _ value: LocalizedStringKey)
|
||||
}
|
||||
}
|
||||
|
||||
private func serverHost(_ s: String) -> String {
|
||||
func serverHost(_ s: String) -> String {
|
||||
if let i = s.range(of: "@")?.lowerBound {
|
||||
return String(s[i...].dropFirst())
|
||||
} else {
|
||||
|
||||
@@ -60,6 +60,7 @@ struct CIFileView: View {
|
||||
case .rcvInvitation: return true
|
||||
case .rcvAccepted: return true
|
||||
case .rcvTransfer: return false
|
||||
case .rcvAborted: return true
|
||||
case .rcvComplete: return true
|
||||
case .rcvCancelled: return false
|
||||
case .rcvError: return false
|
||||
@@ -73,10 +74,10 @@ struct CIFileView: View {
|
||||
logger.debug("CIFileView fileAction")
|
||||
if let file = file {
|
||||
switch (file.fileStatus) {
|
||||
case .rcvInvitation:
|
||||
case .rcvInvitation, .rcvAborted:
|
||||
if fileSizeValid(file) {
|
||||
Task {
|
||||
logger.debug("CIFileView fileAction - in .rcvInvitation, in Task")
|
||||
logger.debug("CIFileView fileAction - in .rcvInvitation, .rcvAborted, in Task")
|
||||
if let user = m.currentUser {
|
||||
await receiveFile(user: user, fileId: file.fileId)
|
||||
}
|
||||
@@ -148,6 +149,8 @@ struct CIFileView: View {
|
||||
} else {
|
||||
progressView()
|
||||
}
|
||||
case .rcvAborted:
|
||||
fileIcon("doc.fill", color: .accentColor, innerIcon: "exclamationmark.arrow.circlepath", innerIconSize: 12)
|
||||
case .rcvComplete: fileIcon("doc.fill")
|
||||
case .rcvCancelled: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10)
|
||||
case .rcvError: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10)
|
||||
|
||||
@@ -17,7 +17,6 @@ struct CIImageView: View {
|
||||
let maxWidth: CGFloat
|
||||
@Binding var imgWidth: CGFloat?
|
||||
@State var scrollProxy: ScrollViewProxy?
|
||||
@State var metaColor: Color
|
||||
@State private var showFullScreenImage = false
|
||||
|
||||
var body: some View {
|
||||
@@ -38,7 +37,7 @@ struct CIImageView: View {
|
||||
.onTapGesture {
|
||||
if let file = file {
|
||||
switch file.fileStatus {
|
||||
case .rcvInvitation:
|
||||
case .rcvInvitation, .rcvAborted:
|
||||
Task {
|
||||
if let user = m.currentUser {
|
||||
await receiveFile(user: user, fileId: file.fileId)
|
||||
@@ -103,6 +102,7 @@ struct CIImageView: View {
|
||||
case .rcvInvitation: fileIcon("arrow.down", 10, 13)
|
||||
case .rcvAccepted: fileIcon("ellipsis", 14, 11)
|
||||
case .rcvTransfer: progressView()
|
||||
case .rcvAborted: fileIcon("exclamationmark.arrow.circlepath", 14, 11)
|
||||
case .rcvCancelled: fileIcon("xmark", 10, 13)
|
||||
case .rcvError: fileIcon("xmark", 10, 13)
|
||||
case .invalid: fileIcon("questionmark", 10, 13)
|
||||
@@ -116,7 +116,7 @@ struct CIImageView: View {
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: size, height: size)
|
||||
.foregroundColor(metaColor)
|
||||
.foregroundColor(.white)
|
||||
.padding(padding)
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ struct CIVideoView: View {
|
||||
.onTapGesture {
|
||||
if let file = file {
|
||||
switch file.fileStatus {
|
||||
case .rcvInvitation:
|
||||
case .rcvInvitation, .rcvAborted:
|
||||
receiveFileIfValidSize(file: file, receiveFile: receiveFile)
|
||||
case .rcvAccepted:
|
||||
switch file.fileProtocol {
|
||||
@@ -95,7 +95,7 @@ struct CIVideoView: View {
|
||||
}
|
||||
durationProgress()
|
||||
}
|
||||
if let file = file, case .rcvInvitation = file.fileStatus {
|
||||
if let file = file, showDownloadButton(file.fileStatus) {
|
||||
Button {
|
||||
receiveFileIfValidSize(file: file, receiveFile: receiveFile)
|
||||
} label: {
|
||||
@@ -105,6 +105,14 @@ struct CIVideoView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func showDownloadButton(_ fileStatus: CIFileStatus) -> Bool {
|
||||
switch fileStatus {
|
||||
case .rcvInvitation: true
|
||||
case .rcvAborted: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
private func videoViewEncrypted(_ file: CIFile, _ defaultPreview: UIImage, _ duration: Int) -> some View {
|
||||
return ZStack(alignment: .topTrailing) {
|
||||
ZStack(alignment: .center) {
|
||||
@@ -280,6 +288,7 @@ struct CIVideoView: View {
|
||||
} else {
|
||||
progressView()
|
||||
}
|
||||
case .rcvAborted: fileIcon("exclamationmark.arrow.circlepath", 14, 11)
|
||||
case .rcvCancelled: fileIcon("xmark", 10, 13)
|
||||
case .rcvError: fileIcon("xmark", 10, 13)
|
||||
case .invalid: fileIcon("questionmark", 10, 13)
|
||||
@@ -318,10 +327,10 @@ struct CIVideoView: View {
|
||||
}
|
||||
|
||||
// TODO encrypt: where file size is checked?
|
||||
private func receiveFileIfValidSize(file: CIFile, receiveFile: @escaping (User, Int64, Bool) async -> Void) {
|
||||
private func receiveFileIfValidSize(file: CIFile, receiveFile: @escaping (User, Int64, Bool, Bool) async -> Void) {
|
||||
Task {
|
||||
if let user = m.currentUser {
|
||||
await receiveFile(user, file.fileId, false)
|
||||
await receiveFile(user, file.fileId, false, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,9 +139,10 @@ struct VoiceMessagePlayer: View {
|
||||
case .sndComplete: playbackButton()
|
||||
case .sndCancelled: playbackButton()
|
||||
case .sndError: playbackButton()
|
||||
case .rcvInvitation: downloadButton(recordingFile)
|
||||
case .rcvInvitation: downloadButton(recordingFile, "play.fill")
|
||||
case .rcvAccepted: loadingIcon()
|
||||
case .rcvTransfer: loadingIcon()
|
||||
case .rcvAborted: downloadButton(recordingFile, "exclamationmark.arrow.circlepath")
|
||||
case .rcvComplete: playbackButton()
|
||||
case .rcvCancelled: playPauseIcon("play.fill", Color(uiColor: .tertiaryLabel))
|
||||
case .rcvError: playPauseIcon("play.fill", Color(uiColor: .tertiaryLabel))
|
||||
@@ -217,7 +218,7 @@ struct VoiceMessagePlayer: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func downloadButton(_ recordingFile: CIFile) -> some View {
|
||||
private func downloadButton(_ recordingFile: CIFile, _ icon: String) -> some View {
|
||||
Button {
|
||||
Task {
|
||||
if let user = chatModel.currentUser {
|
||||
@@ -225,7 +226,7 @@ struct VoiceMessagePlayer: View {
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
playPauseIcon("play.fill")
|
||||
playPauseIcon(icon)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ struct FramedItemView: View {
|
||||
} else {
|
||||
switch (chatItem.content.msgContent) {
|
||||
case let .image(text, image):
|
||||
CIImageView(chatItem: chatItem, image: image, maxWidth: maxWidth, imgWidth: $imgWidth, scrollProxy: scrollProxy, metaColor: metaColor)
|
||||
CIImageView(chatItem: chatItem, image: image, maxWidth: maxWidth, imgWidth: $imgWidth, scrollProxy: scrollProxy)
|
||||
.overlay(DetermineWidth())
|
||||
if text == "" && !chatItem.meta.isLive {
|
||||
Color.clear
|
||||
|
||||
@@ -23,6 +23,7 @@ extension AppSettings {
|
||||
setNetCfg(val)
|
||||
}
|
||||
if let val = privacyEncryptLocalFiles { privacyEncryptLocalFilesGroupDefault.set(val) }
|
||||
if let val = privacyAskToApproveRelays { privacyAskToApproveRelaysGroupDefault.set(val) }
|
||||
if let val = privacyAcceptImages {
|
||||
privacyAcceptImagesGroupDefault.set(val)
|
||||
def.setValue(val, forKey: DEFAULT_PRIVACY_ACCEPT_IMAGES)
|
||||
@@ -50,6 +51,7 @@ extension AppSettings {
|
||||
var c = AppSettings.defaults
|
||||
c.networkConfig = getNetCfg()
|
||||
c.privacyEncryptLocalFiles = privacyEncryptLocalFilesGroupDefault.get()
|
||||
c.privacyAskToApproveRelays = privacyAskToApproveRelaysGroupDefault.get()
|
||||
c.privacyAcceptImages = privacyAcceptImagesGroupDefault.get()
|
||||
c.privacyLinkPreviews = def.bool(forKey: DEFAULT_PRIVACY_LINK_PREVIEWS)
|
||||
c.privacyShowChatPreviews = def.bool(forKey: DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS)
|
||||
|
||||
@@ -16,6 +16,7 @@ struct PrivacySettings: View {
|
||||
@AppStorage(DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS) private var showChatPreviews = true
|
||||
@AppStorage(DEFAULT_PRIVACY_SAVE_LAST_DRAFT) private var saveLastDraft = true
|
||||
@AppStorage(GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES, store: groupDefaults) private var encryptLocalFiles = true
|
||||
@AppStorage(GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS, store: groupDefaults) private var askToApproveRelays = true
|
||||
@State private var simplexLinkMode = privacySimplexLinkModeDefault.get()
|
||||
@AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = false
|
||||
@AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false
|
||||
@@ -64,18 +65,6 @@ struct PrivacySettings: View {
|
||||
}
|
||||
|
||||
Section {
|
||||
settingsRow("lock.doc") {
|
||||
Toggle("Encrypt local files", isOn: $encryptLocalFiles)
|
||||
.onChange(of: encryptLocalFiles) {
|
||||
setEncryptLocalFiles($0)
|
||||
}
|
||||
}
|
||||
settingsRow("photo") {
|
||||
Toggle("Auto-accept images", isOn: $autoAcceptImages)
|
||||
.onChange(of: autoAcceptImages) {
|
||||
privacyAcceptImagesGroupDefault.set($0)
|
||||
}
|
||||
}
|
||||
settingsRow("network") {
|
||||
Toggle("Send link previews", isOn: $useLinkPreviews)
|
||||
}
|
||||
@@ -108,6 +97,32 @@ struct PrivacySettings: View {
|
||||
Text("Chats")
|
||||
}
|
||||
|
||||
Section {
|
||||
settingsRow("lock.doc") {
|
||||
Toggle("Encrypt local files", isOn: $encryptLocalFiles)
|
||||
.onChange(of: encryptLocalFiles) {
|
||||
setEncryptLocalFiles($0)
|
||||
}
|
||||
}
|
||||
settingsRow("photo") {
|
||||
Toggle("Auto-accept images", isOn: $autoAcceptImages)
|
||||
.onChange(of: autoAcceptImages) {
|
||||
privacyAcceptImagesGroupDefault.set($0)
|
||||
}
|
||||
}
|
||||
settingsRow("network.badge.shield.half.filled") {
|
||||
Toggle("Protect IP address", isOn: $askToApproveRelays)
|
||||
}
|
||||
} header: {
|
||||
Text("Files")
|
||||
} footer: {
|
||||
if askToApproveRelays {
|
||||
Text("The app will ask to confirm downloads from unknown file servers (except .onion).")
|
||||
} else {
|
||||
Text("Without Tor or VPN, your IP address will be visible to file servers.")
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
settingsRow("person") {
|
||||
Toggle("Contacts", isOn: $contactReceipts)
|
||||
|
||||
@@ -696,14 +696,16 @@ func apiGetNtfMessage(nonce: String, encNtfInfo: String) -> NtfMessages? {
|
||||
}
|
||||
|
||||
func apiReceiveFile(fileId: Int64, encrypted: Bool, inline: Bool? = nil) -> AChatItem? {
|
||||
let r = sendSimpleXCmd(.receiveFile(fileId: fileId, encrypted: encrypted, inline: inline))
|
||||
let userApprovedRelays = !privacyAskToApproveRelaysGroupDefault.get()
|
||||
let r = sendSimpleXCmd(.receiveFile(fileId: fileId, userApprovedRelays: userApprovedRelays, encrypted: encrypted, inline: inline))
|
||||
if case let .rcvFileAccepted(_, chatItem) = r { return chatItem }
|
||||
logger.error("receiveFile error: \(responseError(r))")
|
||||
return nil
|
||||
}
|
||||
|
||||
func apiSetFileToReceive(fileId: Int64, encrypted: Bool) {
|
||||
let r = sendSimpleXCmd(.setFileToReceive(fileId: fileId, encrypted: encrypted))
|
||||
let userApprovedRelays = !privacyAskToApproveRelaysGroupDefault.get()
|
||||
let r = sendSimpleXCmd(.setFileToReceive(fileId: fileId, userApprovedRelays: userApprovedRelays, encrypted: encrypted))
|
||||
if case .cmdOk = r { return }
|
||||
logger.error("setFileToReceive error: \(responseError(r))")
|
||||
}
|
||||
|
||||
@@ -76,6 +76,11 @@
|
||||
5C9CC7AD28C55D7800BEF955 /* DatabaseEncryptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9CC7AC28C55D7800BEF955 /* DatabaseEncryptionView.swift */; };
|
||||
5C9D13A3282187BB00AB8B43 /* WebRTC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9D13A2282187BB00AB8B43 /* WebRTC.swift */; };
|
||||
5C9D811A2AA8727A001D49FD /* CryptoFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9D81182AA7A4F1001D49FD /* CryptoFile.swift */; };
|
||||
5C9F3DCC2BF7A6900003B86B /* libHSsimplex-chat-5.8.0.1-BrjXjAnJqNV7yWXU89n05g.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9F3DC72BF7A6900003B86B /* libHSsimplex-chat-5.8.0.1-BrjXjAnJqNV7yWXU89n05g.a */; };
|
||||
5C9F3DCD2BF7A6900003B86B /* libHSsimplex-chat-5.8.0.1-BrjXjAnJqNV7yWXU89n05g-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9F3DC82BF7A6900003B86B /* libHSsimplex-chat-5.8.0.1-BrjXjAnJqNV7yWXU89n05g-ghc9.6.3.a */; };
|
||||
5C9F3DCE2BF7A6900003B86B /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9F3DC92BF7A6900003B86B /* libgmp.a */; };
|
||||
5C9F3DCF2BF7A6900003B86B /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9F3DCA2BF7A6900003B86B /* libgmpxx.a */; };
|
||||
5C9F3DD02BF7A6900003B86B /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9F3DCB2BF7A6900003B86B /* libffi.a */; };
|
||||
5C9FD96E27A5D6ED0075386C /* SendMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9FD96D27A5D6ED0075386C /* SendMessageView.swift */; };
|
||||
5CA059DC279559F40002BEB4 /* Tests_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059DB279559F40002BEB4 /* Tests_iOS.swift */; };
|
||||
5CA059DE279559F40002BEB4 /* Tests_iOSLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059DD279559F40002BEB4 /* Tests_iOSLaunchTests.swift */; };
|
||||
@@ -115,11 +120,6 @@
|
||||
5CD67B8F2B0E858A00C510B1 /* hs_init.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CD67B8D2B0E858A00C510B1 /* hs_init.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
5CD67B902B0E858A00C510B1 /* hs_init.c in Sources */ = {isa = PBXBuildFile; fileRef = 5CD67B8E2B0E858A00C510B1 /* hs_init.c */; };
|
||||
5CDCAD482818589900503DA2 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CDCAD472818589900503DA2 /* NotificationService.swift */; };
|
||||
5CE0E8B52BF2B9ED008D6E06 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE0E8B02BF2B9ED008D6E06 /* libgmp.a */; };
|
||||
5CE0E8B62BF2B9ED008D6E06 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE0E8B12BF2B9ED008D6E06 /* libgmpxx.a */; };
|
||||
5CE0E8B72BF2B9ED008D6E06 /* libHSsimplex-chat-5.8.0.0-4rkacYU1Nfn5xrDMCOexv9-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE0E8B22BF2B9ED008D6E06 /* libHSsimplex-chat-5.8.0.0-4rkacYU1Nfn5xrDMCOexv9-ghc9.6.3.a */; };
|
||||
5CE0E8B82BF2B9ED008D6E06 /* libHSsimplex-chat-5.8.0.0-4rkacYU1Nfn5xrDMCOexv9.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE0E8B32BF2B9ED008D6E06 /* libHSsimplex-chat-5.8.0.0-4rkacYU1Nfn5xrDMCOexv9.a */; };
|
||||
5CE0E8B92BF2B9ED008D6E06 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE0E8B42BF2B9ED008D6E06 /* libffi.a */; };
|
||||
5CE2BA702845308900EC33A6 /* SimpleXChat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; };
|
||||
5CE2BA712845308900EC33A6 /* SimpleXChat.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
5CE2BA77284530BF00EC33A6 /* SimpleXChat.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CE2BA76284530BF00EC33A6 /* SimpleXChat.h */; };
|
||||
@@ -359,6 +359,11 @@
|
||||
5C9CC7AC28C55D7800BEF955 /* DatabaseEncryptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseEncryptionView.swift; sourceTree = "<group>"; };
|
||||
5C9D13A2282187BB00AB8B43 /* WebRTC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRTC.swift; sourceTree = "<group>"; };
|
||||
5C9D81182AA7A4F1001D49FD /* CryptoFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoFile.swift; sourceTree = "<group>"; };
|
||||
5C9F3DC72BF7A6900003B86B /* libHSsimplex-chat-5.8.0.1-BrjXjAnJqNV7yWXU89n05g.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.8.0.1-BrjXjAnJqNV7yWXU89n05g.a"; sourceTree = "<group>"; };
|
||||
5C9F3DC82BF7A6900003B86B /* libHSsimplex-chat-5.8.0.1-BrjXjAnJqNV7yWXU89n05g-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.8.0.1-BrjXjAnJqNV7yWXU89n05g-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
5C9F3DC92BF7A6900003B86B /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5C9F3DCA2BF7A6900003B86B /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5C9F3DCB2BF7A6900003B86B /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5C9FD96A27A56D4D0075386C /* JSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSON.swift; sourceTree = "<group>"; };
|
||||
5C9FD96D27A5D6ED0075386C /* SendMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMessageView.swift; sourceTree = "<group>"; };
|
||||
5CA059C3279559F40002BEB4 /* SimpleXApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleXApp.swift; sourceTree = "<group>"; };
|
||||
@@ -425,11 +430,6 @@
|
||||
5CDCAD7428188D2900503DA2 /* APITypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APITypes.swift; sourceTree = "<group>"; };
|
||||
5CDCAD7D2818941F00503DA2 /* API.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = API.swift; sourceTree = "<group>"; };
|
||||
5CDCAD80281A7E2700503DA2 /* Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = "<group>"; };
|
||||
5CE0E8B02BF2B9ED008D6E06 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5CE0E8B12BF2B9ED008D6E06 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5CE0E8B22BF2B9ED008D6E06 /* libHSsimplex-chat-5.8.0.0-4rkacYU1Nfn5xrDMCOexv9-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.8.0.0-4rkacYU1Nfn5xrDMCOexv9-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
5CE0E8B32BF2B9ED008D6E06 /* libHSsimplex-chat-5.8.0.0-4rkacYU1Nfn5xrDMCOexv9.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.8.0.0-4rkacYU1Nfn5xrDMCOexv9.a"; sourceTree = "<group>"; };
|
||||
5CE0E8B42BF2B9ED008D6E06 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5CE1330328E118CC00FFFD8C /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = "de.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
5CE1330428E118CC00FFFD8C /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
5CE2BA682845308900EC33A6 /* SimpleXChat.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SimpleXChat.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -539,13 +539,13 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5CE0E8B72BF2B9ED008D6E06 /* libHSsimplex-chat-5.8.0.0-4rkacYU1Nfn5xrDMCOexv9-ghc9.6.3.a in Frameworks */,
|
||||
5CE0E8B62BF2B9ED008D6E06 /* libgmpxx.a in Frameworks */,
|
||||
5C9F3DCF2BF7A6900003B86B /* libgmpxx.a in Frameworks */,
|
||||
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
|
||||
5CE0E8B52BF2B9ED008D6E06 /* libgmp.a in Frameworks */,
|
||||
5CE0E8B92BF2B9ED008D6E06 /* libffi.a in Frameworks */,
|
||||
5C9F3DCE2BF7A6900003B86B /* libgmp.a in Frameworks */,
|
||||
5C9F3DCC2BF7A6900003B86B /* libHSsimplex-chat-5.8.0.1-BrjXjAnJqNV7yWXU89n05g.a in Frameworks */,
|
||||
5C9F3DD02BF7A6900003B86B /* libffi.a in Frameworks */,
|
||||
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
|
||||
5CE0E8B82BF2B9ED008D6E06 /* libHSsimplex-chat-5.8.0.0-4rkacYU1Nfn5xrDMCOexv9.a in Frameworks */,
|
||||
5C9F3DCD2BF7A6900003B86B /* libHSsimplex-chat-5.8.0.1-BrjXjAnJqNV7yWXU89n05g-ghc9.6.3.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -611,11 +611,11 @@
|
||||
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5CE0E8B42BF2B9ED008D6E06 /* libffi.a */,
|
||||
5CE0E8B02BF2B9ED008D6E06 /* libgmp.a */,
|
||||
5CE0E8B12BF2B9ED008D6E06 /* libgmpxx.a */,
|
||||
5CE0E8B22BF2B9ED008D6E06 /* libHSsimplex-chat-5.8.0.0-4rkacYU1Nfn5xrDMCOexv9-ghc9.6.3.a */,
|
||||
5CE0E8B32BF2B9ED008D6E06 /* libHSsimplex-chat-5.8.0.0-4rkacYU1Nfn5xrDMCOexv9.a */,
|
||||
5C9F3DCB2BF7A6900003B86B /* libffi.a */,
|
||||
5C9F3DC92BF7A6900003B86B /* libgmp.a */,
|
||||
5C9F3DCA2BF7A6900003B86B /* libgmpxx.a */,
|
||||
5C9F3DC82BF7A6900003B86B /* libHSsimplex-chat-5.8.0.1-BrjXjAnJqNV7yWXU89n05g-ghc9.6.3.a */,
|
||||
5C9F3DC72BF7A6900003B86B /* libHSsimplex-chat-5.8.0.1-BrjXjAnJqNV7yWXU89n05g.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
@@ -1562,7 +1562,7 @@
|
||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 216;
|
||||
CURRENT_PROJECT_VERSION = 217;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -1611,7 +1611,7 @@
|
||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 216;
|
||||
CURRENT_PROJECT_VERSION = 217;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -1697,7 +1697,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 216;
|
||||
CURRENT_PROJECT_VERSION = 217;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = s;
|
||||
@@ -1734,7 +1734,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 216;
|
||||
CURRENT_PROJECT_VERSION = 217;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_CODE_COVERAGE = NO;
|
||||
@@ -1771,7 +1771,7 @@
|
||||
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
|
||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 216;
|
||||
CURRENT_PROJECT_VERSION = 217;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
@@ -1822,7 +1822,7 @@
|
||||
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
|
||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 216;
|
||||
CURRENT_PROJECT_VERSION = 217;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
||||
@@ -123,8 +123,8 @@ public enum ChatCommand {
|
||||
case apiGetNetworkStatuses
|
||||
case apiChatRead(type: ChatType, id: Int64, itemRange: (Int64, Int64))
|
||||
case apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool)
|
||||
case receiveFile(fileId: Int64, encrypted: Bool?, inline: Bool?)
|
||||
case setFileToReceive(fileId: Int64, encrypted: Bool?)
|
||||
case receiveFile(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool?, inline: Bool?)
|
||||
case setFileToReceive(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool?)
|
||||
case cancelFile(fileId: Int64)
|
||||
// remote desktop commands
|
||||
case setLocalDeviceName(displayName: String)
|
||||
@@ -282,8 +282,8 @@ public enum ChatCommand {
|
||||
case .apiGetNetworkStatuses: return "/_network_statuses"
|
||||
case let .apiChatRead(type, id, itemRange: (from, to)): return "/_read chat \(ref(type, id)) from=\(from) to=\(to)"
|
||||
case let .apiChatUnread(type, id, unreadChat): return "/_unread chat \(ref(type, id)) \(onOff(unreadChat))"
|
||||
case let .receiveFile(fileId, encrypt, inline): return "/freceive \(fileId)\(onOffParam("encrypt", encrypt))\(onOffParam("inline", inline))"
|
||||
case let .setFileToReceive(fileId, encrypt): return "/_set_file_to_receive \(fileId)\(onOffParam("encrypt", encrypt))"
|
||||
case let .receiveFile(fileId, userApprovedRelays, encrypt, inline): return "/freceive \(fileId)\(onOffParam("approved_relays", userApprovedRelays))\(onOffParam("encrypt", encrypt))\(onOffParam("inline", inline))"
|
||||
case let .setFileToReceive(fileId, userApprovedRelays, encrypt): return "/_set_file_to_receive \(fileId)\(onOffParam("approved_relays", userApprovedRelays))\(onOffParam("encrypt", encrypt))"
|
||||
case let .cancelFile(fileId): return "/fcancel \(fileId)"
|
||||
case let .setLocalDeviceName(displayName): return "/set device name \(displayName)"
|
||||
case let .connectRemoteCtrl(xrcpInv): return "/connect remote ctrl \(xrcpInv)"
|
||||
@@ -1760,6 +1760,7 @@ public enum ChatErrorType: Decodable {
|
||||
case fileImageType(filePath: String)
|
||||
case fileImageSize(filePath: String)
|
||||
case fileNotReceived(fileId: Int64)
|
||||
case fileNotApproved(fileId: Int64, unknownServers: [String])
|
||||
// case xFTPRcvFile
|
||||
// case xFTPSndFile
|
||||
case fallbackToSMPProhibited(fileId: Int64)
|
||||
@@ -2038,6 +2039,7 @@ public struct MigrationFileLinkData: Codable {
|
||||
public struct AppSettings: Codable, Equatable {
|
||||
public var networkConfig: NetCfg? = nil
|
||||
public var privacyEncryptLocalFiles: Bool? = nil
|
||||
public var privacyAskToApproveRelays: Bool? = nil
|
||||
public var privacyAcceptImages: Bool? = nil
|
||||
public var privacyLinkPreviews: Bool? = nil
|
||||
public var privacyShowChatPreviews: Bool? = nil
|
||||
@@ -2061,6 +2063,7 @@ public struct AppSettings: Codable, Equatable {
|
||||
let def = AppSettings.defaults
|
||||
if networkConfig != def.networkConfig { empty.networkConfig = networkConfig }
|
||||
if privacyEncryptLocalFiles != def.privacyEncryptLocalFiles { empty.privacyEncryptLocalFiles = privacyEncryptLocalFiles }
|
||||
if privacyAskToApproveRelays != def.privacyAskToApproveRelays { empty.privacyAskToApproveRelays = privacyAskToApproveRelays }
|
||||
if privacyAcceptImages != def.privacyAcceptImages { empty.privacyAcceptImages = privacyAcceptImages }
|
||||
if privacyLinkPreviews != def.privacyLinkPreviews { empty.privacyLinkPreviews = privacyLinkPreviews }
|
||||
if privacyShowChatPreviews != def.privacyShowChatPreviews { empty.privacyShowChatPreviews = privacyShowChatPreviews }
|
||||
@@ -2085,6 +2088,7 @@ public struct AppSettings: Codable, Equatable {
|
||||
AppSettings (
|
||||
networkConfig: NetCfg.defaults,
|
||||
privacyEncryptLocalFiles: true,
|
||||
privacyAskToApproveRelays: true,
|
||||
privacyAcceptImages: true,
|
||||
privacyLinkPreviews: true,
|
||||
privacyShowChatPreviews: true,
|
||||
|
||||
@@ -23,6 +23,7 @@ public let GROUP_DEFAULT_NTF_ENABLE_PERIODIC = "ntfEnablePeriodic" // no longer
|
||||
let GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES = "privacyAcceptImages"
|
||||
public let GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE = "privacyTransferImagesInline" // no longer used
|
||||
public let GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES = "privacyEncryptLocalFiles"
|
||||
public let GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS = "privacyAskToApproveRelays"
|
||||
let GROUP_DEFAULT_NTF_BADGE_COUNT = "ntgBadgeCount"
|
||||
let GROUP_DEFAULT_NETWORK_USE_ONION_HOSTS = "networkUseOnionHosts"
|
||||
let GROUP_DEFAULT_NETWORK_SESSION_MODE = "networkSessionMode"
|
||||
@@ -73,6 +74,7 @@ public func registerGroupDefaults() {
|
||||
GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES: true,
|
||||
GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE: false,
|
||||
GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES: true,
|
||||
GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS: true,
|
||||
GROUP_DEFAULT_CONFIRM_DB_UPGRADES: false,
|
||||
GROUP_DEFAULT_CALL_KIT_ENABLED: true,
|
||||
GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED: false,
|
||||
@@ -181,6 +183,8 @@ public let privacyAcceptImagesGroupDefault = BoolDefault(defaults: groupDefaults
|
||||
|
||||
public let privacyEncryptLocalFilesGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES)
|
||||
|
||||
public let privacyAskToApproveRelaysGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS)
|
||||
|
||||
public let ntfBadgeCountGroupDefault = IntDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_NTF_BADGE_COUNT)
|
||||
|
||||
public let networkUseOnionHostsGroupDefault = EnumDefault<OnionHosts>(
|
||||
|
||||
@@ -3174,6 +3174,7 @@ public struct CIFile: Decodable {
|
||||
case .rcvInvitation: return false
|
||||
case .rcvAccepted: return false
|
||||
case .rcvTransfer: return false
|
||||
case .rcvAborted: return false
|
||||
case .rcvCancelled: return false
|
||||
case .rcvComplete: return true
|
||||
case .rcvError: return false
|
||||
@@ -3198,6 +3199,7 @@ public struct CIFile: Decodable {
|
||||
case .rcvInvitation: return nil
|
||||
case .rcvAccepted: return rcvCancelAction
|
||||
case .rcvTransfer: return rcvCancelAction
|
||||
case .rcvAborted: return nil
|
||||
case .rcvCancelled: return nil
|
||||
case .rcvComplete: return nil
|
||||
case .rcvError: return nil
|
||||
@@ -3312,6 +3314,7 @@ public enum CIFileStatus: Decodable, Equatable {
|
||||
case rcvInvitation
|
||||
case rcvAccepted
|
||||
case rcvTransfer(rcvProgress: Int64, rcvTotal: Int64)
|
||||
case rcvAborted
|
||||
case rcvComplete
|
||||
case rcvCancelled
|
||||
case rcvError
|
||||
@@ -3327,6 +3330,7 @@ public enum CIFileStatus: Decodable, Equatable {
|
||||
case .rcvInvitation: return "rcvInvitation"
|
||||
case .rcvAccepted: return "rcvAccepted"
|
||||
case let .rcvTransfer(rcvProgress, rcvTotal): return "rcvTransfer \(rcvProgress) \(rcvTotal)"
|
||||
case .rcvAborted: return "rcvAborted"
|
||||
case .rcvComplete: return "rcvComplete"
|
||||
case .rcvCancelled: return "rcvCancelled"
|
||||
case .rcvError: return "rcvError"
|
||||
|
||||
@@ -2644,6 +2644,7 @@ data class CIFile(
|
||||
is CIFileStatus.RcvInvitation -> false
|
||||
is CIFileStatus.RcvAccepted -> false
|
||||
is CIFileStatus.RcvTransfer -> false
|
||||
is CIFileStatus.RcvAborted -> false
|
||||
is CIFileStatus.RcvCancelled -> false
|
||||
is CIFileStatus.RcvComplete -> true
|
||||
is CIFileStatus.RcvError -> false
|
||||
@@ -2665,6 +2666,7 @@ data class CIFile(
|
||||
is CIFileStatus.RcvInvitation -> null
|
||||
is CIFileStatus.RcvAccepted -> rcvCancelAction
|
||||
is CIFileStatus.RcvTransfer -> rcvCancelAction
|
||||
is CIFileStatus.RcvAborted -> null
|
||||
is CIFileStatus.RcvCancelled -> null
|
||||
is CIFileStatus.RcvComplete -> null
|
||||
is CIFileStatus.RcvError -> null
|
||||
@@ -2845,6 +2847,7 @@ sealed class CIFileStatus {
|
||||
@Serializable @SerialName("rcvInvitation") object RcvInvitation: CIFileStatus()
|
||||
@Serializable @SerialName("rcvAccepted") object RcvAccepted: CIFileStatus()
|
||||
@Serializable @SerialName("rcvTransfer") class RcvTransfer(val rcvProgress: Long, val rcvTotal: Long): CIFileStatus()
|
||||
@Serializable @SerialName("rcvAborted") object RcvAborted: CIFileStatus()
|
||||
@Serializable @SerialName("rcvComplete") object RcvComplete: CIFileStatus()
|
||||
@Serializable @SerialName("rcvCancelled") object RcvCancelled: CIFileStatus()
|
||||
@Serializable @SerialName("rcvError") object RcvError: CIFileStatus()
|
||||
@@ -2859,6 +2862,7 @@ sealed class CIFileStatus {
|
||||
is RcvInvitation -> false
|
||||
is RcvAccepted -> false
|
||||
is RcvTransfer -> false
|
||||
is RcvAborted -> false
|
||||
is RcvComplete -> false
|
||||
is RcvCancelled -> false
|
||||
is RcvError -> false
|
||||
|
||||
+40
-8
@@ -12,6 +12,7 @@ import dev.icerock.moko.resources.compose.painterResource
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.call.*
|
||||
import chat.simplex.common.views.chat.group.toggleShowMemberMessages
|
||||
import chat.simplex.common.views.migration.MigrationFileLinkData
|
||||
import chat.simplex.common.views.onboarding.OnboardingStage
|
||||
import chat.simplex.common.views.usersettings.*
|
||||
@@ -106,6 +107,7 @@ class AppPreferences {
|
||||
val privacySaveLastDraft = mkBoolPreference(SHARED_PREFS_PRIVACY_SAVE_LAST_DRAFT, true)
|
||||
val privacyDeliveryReceiptsSet = mkBoolPreference(SHARED_PREFS_PRIVACY_DELIVERY_RECEIPTS_SET, false)
|
||||
val privacyEncryptLocalFiles = mkBoolPreference(SHARED_PREFS_PRIVACY_ENCRYPT_LOCAL_FILES, true)
|
||||
val privacyAskToApproveRelays = mkBoolPreference(SHARED_PREFS_PRIVACY_ASK_TO_APPROVE_RELAYS, true)
|
||||
val experimentalCalls = mkBoolPreference(SHARED_PREFS_EXPERIMENTAL_CALLS, false)
|
||||
val showUnreadAndFavorites = mkBoolPreference(SHARED_PREFS_SHOW_UNREAD_AND_FAVORITES, false)
|
||||
val chatArchiveName = mkStrPreference(SHARED_PREFS_CHAT_ARCHIVE_NAME, null)
|
||||
@@ -292,6 +294,7 @@ class AppPreferences {
|
||||
private const val SHARED_PREFS_PRIVACY_SAVE_LAST_DRAFT = "PrivacySaveLastDraft"
|
||||
private const val SHARED_PREFS_PRIVACY_DELIVERY_RECEIPTS_SET = "PrivacyDeliveryReceiptsSet"
|
||||
private const val SHARED_PREFS_PRIVACY_ENCRYPT_LOCAL_FILES = "PrivacyEncryptLocalFiles"
|
||||
private const val SHARED_PREFS_PRIVACY_ASK_TO_APPROVE_RELAYS = "PrivacyAskToApproveRelays"
|
||||
const val SHARED_PREFS_PRIVACY_FULL_BACKUP = "FullBackup"
|
||||
private const val SHARED_PREFS_EXPERIMENTAL_CALLS = "ExperimentalCalls"
|
||||
private const val SHARED_PREFS_SHOW_UNREAD_AND_FAVORITES = "ShowUnreadAndFavorites"
|
||||
@@ -1337,9 +1340,9 @@ object ChatController {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun apiReceiveFile(rh: Long?, fileId: Long, encrypted: Boolean, inline: Boolean? = null, auto: Boolean = false): AChatItem? {
|
||||
suspend fun apiReceiveFile(rh: Long?, fileId: Long, userApprovedRelays: Boolean, encrypted: Boolean, inline: Boolean? = null, auto: Boolean = false): AChatItem? {
|
||||
// -1 here is to override default behavior of providing current remote host id because file can be asked by local device while remote is connected
|
||||
val r = sendCmd(rh, CC.ReceiveFile(fileId, encrypted, inline))
|
||||
val r = sendCmd(rh, CC.ReceiveFile(fileId, userApprovedRelays = userApprovedRelays, encrypt = encrypted, inline = inline))
|
||||
return when (r) {
|
||||
is CR.RcvFileAccepted -> r.chatItem
|
||||
is CR.RcvFileAcceptedSndCancelled -> {
|
||||
@@ -1358,7 +1361,23 @@ object ChatController {
|
||||
val maybeChatError = chatError(r)
|
||||
if (maybeChatError is ChatErrorType.FileCancelled || maybeChatError is ChatErrorType.FileAlreadyReceiving) {
|
||||
Log.d(TAG, "apiReceiveFile ignoring FileCancelled or FileAlreadyReceiving error")
|
||||
} else {
|
||||
} else if (maybeChatError is ChatErrorType.FileNotApproved) {
|
||||
Log.d(TAG, "apiReceiveFile FileNotApproved error")
|
||||
if (!auto) {
|
||||
val srvs = maybeChatError.unknownServers.map{ serverHostname(it) }
|
||||
AlertManager.shared.showAlertDialog(
|
||||
title = generalGetString(MR.strings.file_not_approved_title),
|
||||
text = generalGetString(MR.strings.file_not_approved_descr).format(srvs.sorted().joinToString(separator = ", ")),
|
||||
confirmText = generalGetString(MR.strings.download_file),
|
||||
onConfirm = {
|
||||
val user = chatModel.currentUser.value
|
||||
if (user != null) {
|
||||
withBGApi { chatModel.controller.receiveFile(rh, user, fileId, userApprovedRelays = true) }
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
} else if (!auto) {
|
||||
apiErrorAlert("apiReceiveFile", generalGetString(MR.strings.error_receiving_file), r)
|
||||
}
|
||||
}
|
||||
@@ -2216,9 +2235,14 @@ object ChatController {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun receiveFile(rhId: Long?, user: UserLike, fileId: Long, auto: Boolean = false) {
|
||||
val encrypted = appPrefs.privacyEncryptLocalFiles.get()
|
||||
val chatItem = apiReceiveFile(rhId, fileId, encrypted = encrypted, auto = auto)
|
||||
suspend fun receiveFile(rhId: Long?, user: UserLike, fileId: Long, userApprovedRelays: Boolean = false, auto: Boolean = false) {
|
||||
val chatItem = apiReceiveFile(
|
||||
rhId,
|
||||
fileId,
|
||||
userApprovedRelays = userApprovedRelays || !appPrefs.privacyAskToApproveRelays.get(),
|
||||
encrypted = appPrefs.privacyEncryptLocalFiles.get(),
|
||||
auto = auto
|
||||
)
|
||||
if (chatItem != null) {
|
||||
chatItemSimpleUpdate(rhId, user, chatItem)
|
||||
}
|
||||
@@ -2501,7 +2525,7 @@ sealed class CC {
|
||||
class ApiRejectContact(val contactReqId: Long): CC()
|
||||
class ApiChatRead(val type: ChatType, val id: Long, val range: ItemRange): CC()
|
||||
class ApiChatUnread(val type: ChatType, val id: Long, val unreadChat: Boolean): CC()
|
||||
class ReceiveFile(val fileId: Long, val encrypt: Boolean, val inline: Boolean?): CC()
|
||||
class ReceiveFile(val fileId: Long, val userApprovedRelays: Boolean, val encrypt: Boolean, val inline: Boolean?): CC()
|
||||
class CancelFile(val fileId: Long): CC()
|
||||
// Remote control
|
||||
class SetLocalDeviceName(val displayName: String): CC()
|
||||
@@ -2652,6 +2676,7 @@ sealed class CC {
|
||||
is ApiChatUnread -> "/_unread chat ${chatRef(type, id)} ${onOff(unreadChat)}"
|
||||
is ReceiveFile ->
|
||||
"/freceive $fileId" +
|
||||
(" approved_relays=${onOff(userApprovedRelays)}") +
|
||||
(if (encrypt == null) "" else " encrypt=${onOff(encrypt)}") +
|
||||
(if (inline == null) "" else " inline=${onOff(inline)}")
|
||||
is CancelFile -> "/fcancel $fileId"
|
||||
@@ -4892,13 +4917,14 @@ sealed class ChatErrorType {
|
||||
is FileCancel -> "fileCancel"
|
||||
is FileAlreadyExists -> "fileAlreadyExists"
|
||||
is FileRead -> "fileRead"
|
||||
is FileWrite -> "fileWrite"
|
||||
is FileWrite -> "fileWrite $message"
|
||||
is FileSend -> "fileSend"
|
||||
is FileRcvChunk -> "fileRcvChunk"
|
||||
is FileInternal -> "fileInternal"
|
||||
is FileImageType -> "fileImageType"
|
||||
is FileImageSize -> "fileImageSize"
|
||||
is FileNotReceived -> "fileNotReceived"
|
||||
is FileNotApproved -> "fileNotApproved"
|
||||
// is XFTPRcvFile -> "xftpRcvFile"
|
||||
// is XFTPSndFile -> "xftpSndFile"
|
||||
is FallbackToSMPProhibited -> "fallbackToSMPProhibited"
|
||||
@@ -4978,6 +5004,7 @@ sealed class ChatErrorType {
|
||||
@Serializable @SerialName("fileImageType") class FileImageType(val filePath: String): ChatErrorType()
|
||||
@Serializable @SerialName("fileImageSize") class FileImageSize(val filePath: String): ChatErrorType()
|
||||
@Serializable @SerialName("fileNotReceived") class FileNotReceived(val fileId: Long): ChatErrorType()
|
||||
@Serializable @SerialName("fileNotApproved") class FileNotApproved(val fileId: Long, val unknownServers: List<String>): ChatErrorType()
|
||||
// @Serializable @SerialName("xFTPRcvFile") object XFTPRcvFile: ChatErrorType()
|
||||
// @Serializable @SerialName("xFTPSndFile") object XFTPSndFile: ChatErrorType()
|
||||
@Serializable @SerialName("fallbackToSMPProhibited") class FallbackToSMPProhibited(val fileId: Long): ChatErrorType()
|
||||
@@ -5476,6 +5503,7 @@ enum class NotificationsMode() {
|
||||
data class AppSettings(
|
||||
var networkConfig: NetCfg? = null,
|
||||
var privacyEncryptLocalFiles: Boolean? = null,
|
||||
var privacyAskToApproveRelays: Boolean? = null,
|
||||
var privacyAcceptImages: Boolean? = null,
|
||||
var privacyLinkPreviews: Boolean? = null,
|
||||
var privacyShowChatPreviews: Boolean? = null,
|
||||
@@ -5499,6 +5527,7 @@ data class AppSettings(
|
||||
val def = defaults
|
||||
if (networkConfig != def.networkConfig) { empty.networkConfig = networkConfig }
|
||||
if (privacyEncryptLocalFiles != def.privacyEncryptLocalFiles) { empty.privacyEncryptLocalFiles = privacyEncryptLocalFiles }
|
||||
if (privacyAskToApproveRelays != def.privacyAskToApproveRelays) { empty.privacyAskToApproveRelays = privacyAskToApproveRelays }
|
||||
if (privacyAcceptImages != def.privacyAcceptImages) { empty.privacyAcceptImages = privacyAcceptImages }
|
||||
if (privacyLinkPreviews != def.privacyLinkPreviews) { empty.privacyLinkPreviews = privacyLinkPreviews }
|
||||
if (privacyShowChatPreviews != def.privacyShowChatPreviews) { empty.privacyShowChatPreviews = privacyShowChatPreviews }
|
||||
@@ -5530,6 +5559,7 @@ data class AppSettings(
|
||||
setNetCfg(net)
|
||||
}
|
||||
privacyEncryptLocalFiles?.let { def.privacyEncryptLocalFiles.set(it) }
|
||||
privacyAskToApproveRelays?.let { def.privacyAskToApproveRelays.set(it) }
|
||||
privacyAcceptImages?.let { def.privacyAcceptImages.set(it) }
|
||||
privacyLinkPreviews?.let { def.privacyLinkPreviews.set(it) }
|
||||
privacyShowChatPreviews?.let { def.privacyShowChatPreviews.set(it) }
|
||||
@@ -5554,6 +5584,7 @@ data class AppSettings(
|
||||
get() = AppSettings(
|
||||
networkConfig = NetCfg.defaults,
|
||||
privacyEncryptLocalFiles = true,
|
||||
privacyAskToApproveRelays = true,
|
||||
privacyAcceptImages = true,
|
||||
privacyLinkPreviews = true,
|
||||
privacyShowChatPreviews = true,
|
||||
@@ -5579,6 +5610,7 @@ data class AppSettings(
|
||||
return defaults.copy(
|
||||
networkConfig = getNetCfg(),
|
||||
privacyEncryptLocalFiles = def.privacyEncryptLocalFiles.get(),
|
||||
privacyAskToApproveRelays = def.privacyAskToApproveRelays.get(),
|
||||
privacyAcceptImages = def.privacyAcceptImages.get(),
|
||||
privacyLinkPreviews = def.privacyLinkPreviews.get(),
|
||||
privacyShowChatPreviews = def.privacyShowChatPreviews.get(),
|
||||
|
||||
+3
-2
@@ -1,6 +1,5 @@
|
||||
package chat.simplex.common.views.chat.item
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CornerSize
|
||||
@@ -64,7 +63,7 @@ fun CIFileView(
|
||||
fun fileAction() {
|
||||
if (file != null) {
|
||||
when {
|
||||
file.fileStatus is CIFileStatus.RcvInvitation -> {
|
||||
file.fileStatus is CIFileStatus.RcvInvitation || file.fileStatus is CIFileStatus.RcvAborted -> {
|
||||
if (fileSizeValid(file)) {
|
||||
receiveFile(file.fileId)
|
||||
} else {
|
||||
@@ -176,6 +175,8 @@ fun CIFileView(
|
||||
} else {
|
||||
progressIndicator()
|
||||
}
|
||||
is CIFileStatus.RcvAborted ->
|
||||
fileIcon(innerIcon = painterResource(MR.images.ic_sync_problem), color = MaterialTheme.colors.primary)
|
||||
is CIFileStatus.RcvComplete -> fileIcon()
|
||||
is CIFileStatus.RcvCancelled -> fileIcon(innerIcon = painterResource(MR.images.ic_close))
|
||||
is CIFileStatus.RcvError -> fileIcon(innerIcon = painterResource(MR.images.ic_close))
|
||||
|
||||
+3
-7
@@ -21,17 +21,12 @@ import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.DEFAULT_MAX_IMAGE_WIDTH
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
|
||||
@Composable
|
||||
fun CIImageView(
|
||||
image: String,
|
||||
file: CIFile?,
|
||||
metaColor: Color,
|
||||
imageProvider: () -> ImageGalleryProvider,
|
||||
showMenu: MutableState<Boolean>,
|
||||
receiveFile: (Long) -> Unit
|
||||
@@ -51,7 +46,7 @@ fun CIImageView(
|
||||
icon,
|
||||
stringResource(stringId),
|
||||
Modifier.fillMaxSize(),
|
||||
tint = metaColor
|
||||
tint = Color.White
|
||||
)
|
||||
}
|
||||
|
||||
@@ -78,6 +73,7 @@ fun CIImageView(
|
||||
is CIFileStatus.RcvInvitation -> fileIcon(painterResource(MR.images.ic_arrow_downward), MR.strings.icon_descr_asked_to_receive)
|
||||
is CIFileStatus.RcvAccepted -> fileIcon(painterResource(MR.images.ic_more_horiz), MR.strings.icon_descr_waiting_for_image)
|
||||
is CIFileStatus.RcvTransfer -> progressIndicator()
|
||||
is CIFileStatus.RcvAborted -> fileIcon(painterResource(MR.images.ic_sync_problem), MR.strings.icon_descr_file)
|
||||
is CIFileStatus.RcvCancelled -> fileIcon(painterResource(MR.images.ic_close), MR.strings.icon_descr_file)
|
||||
is CIFileStatus.RcvError -> fileIcon(painterResource(MR.images.ic_close), MR.strings.icon_descr_file)
|
||||
is CIFileStatus.Invalid -> fileIcon(painterResource(MR.images.ic_question_mark), MR.strings.icon_descr_file)
|
||||
@@ -206,7 +202,7 @@ fun CIImageView(
|
||||
imageView(base64ToBitmap(image), onClick = {
|
||||
if (file != null) {
|
||||
when (file.fileStatus) {
|
||||
CIFileStatus.RcvInvitation ->
|
||||
CIFileStatus.RcvInvitation, CIFileStatus.RcvAborted ->
|
||||
if (fileSizeValid()) {
|
||||
receiveFile(file.fileId)
|
||||
} else {
|
||||
|
||||
+3
-2
@@ -73,7 +73,7 @@ fun CIVideoView(
|
||||
VideoPreviewImageView(preview, onClick = {
|
||||
if (file != null) {
|
||||
when (file.fileStatus) {
|
||||
CIFileStatus.RcvInvitation ->
|
||||
CIFileStatus.RcvInvitation, CIFileStatus.RcvAborted ->
|
||||
receiveFileIfValidSize(file, receiveFile)
|
||||
CIFileStatus.RcvAccepted ->
|
||||
when (file.fileProtocol) {
|
||||
@@ -102,7 +102,7 @@ fun CIVideoView(
|
||||
if (file != null) {
|
||||
DurationProgress(file, remember { mutableStateOf(false) }, remember { mutableStateOf(duration * 1000L) }, remember { mutableStateOf(0L) }/*, soundEnabled*/)
|
||||
}
|
||||
if (file?.fileStatus is CIFileStatus.RcvInvitation) {
|
||||
if (file?.fileStatus is CIFileStatus.RcvInvitation || file?.fileStatus is CIFileStatus.RcvAborted) {
|
||||
PlayButton(error = false, { showMenu.value = true }) { receiveFileIfValidSize(file, receiveFile) }
|
||||
}
|
||||
}
|
||||
@@ -396,6 +396,7 @@ private fun loadingIndicator(file: CIFile?) {
|
||||
} else {
|
||||
progressIndicator()
|
||||
}
|
||||
is CIFileStatus.RcvAborted -> fileIcon(painterResource(MR.images.ic_sync_problem), MR.strings.icon_descr_file)
|
||||
is CIFileStatus.RcvCancelled -> fileIcon(painterResource(MR.images.ic_close), MR.strings.icon_descr_file)
|
||||
is CIFileStatus.RcvError -> fileIcon(painterResource(MR.images.ic_close), MR.strings.icon_descr_file)
|
||||
is CIFileStatus.Invalid -> fileIcon(painterResource(MR.images.ic_question_mark), MR.strings.icon_descr_file)
|
||||
|
||||
+6
-2
@@ -22,6 +22,7 @@ import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.ImageResource
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
// TODO refactor https://github.com/simplex-chat/simplex-chat/pull/1451#discussion_r1033429901
|
||||
@@ -220,7 +221,8 @@ private fun PlayPauseButton(
|
||||
error: Boolean,
|
||||
play: () -> Unit,
|
||||
pause: () -> Unit,
|
||||
longClick: () -> Unit
|
||||
longClick: () -> Unit,
|
||||
icon: ImageResource = MR.images.ic_play_arrow_filled,
|
||||
) {
|
||||
val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage
|
||||
val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage
|
||||
@@ -241,7 +243,7 @@ private fun PlayPauseButton(
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
if (audioPlaying) painterResource(MR.images.ic_pause_filled) else painterResource(MR.images.ic_play_arrow_filled),
|
||||
if (audioPlaying) painterResource(MR.images.ic_pause_filled) else painterResource(icon),
|
||||
contentDescription = null,
|
||||
Modifier.size(36.dp),
|
||||
tint = if (error) WarningOrange else if (!enabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
|
||||
@@ -294,6 +296,8 @@ private fun VoiceMsgIndicator(
|
||||
) {
|
||||
ProgressIndicator()
|
||||
}
|
||||
} else if (file?.fileStatus is CIFileStatus.RcvAborted) {
|
||||
PlayPauseButton(audioPlaying, sent, 0f, strokeWidth, strokeColor, true, error, { receiveFile(file.fileId) }, {}, longClick = longClick, icon = MR.images.ic_sync_problem)
|
||||
} else {
|
||||
PlayPauseButton(audioPlaying, sent, 0f, strokeWidth, strokeColor, false, false, {}, {}, longClick)
|
||||
}
|
||||
|
||||
+1
-1
@@ -241,7 +241,7 @@ fun FramedItemView(
|
||||
} else {
|
||||
when (val mc = ci.content.msgContent) {
|
||||
is MsgContent.MCImage -> {
|
||||
CIImageView(image = mc.image, file = ci.file, metaColor = metaColor, imageProvider ?: return@PriorityLayout, showMenu, receiveFile)
|
||||
CIImageView(image = mc.image, file = ci.file, imageProvider ?: return@PriorityLayout, showMenu, receiveFile)
|
||||
if (mc.text == "" && !ci.meta.isLive) {
|
||||
metaColor = Color.White
|
||||
} else {
|
||||
|
||||
+18
-5
@@ -1,6 +1,7 @@
|
||||
package chat.simplex.common.views.usersettings
|
||||
|
||||
import SectionBottomSpacer
|
||||
import SectionCustomFooter
|
||||
import SectionDividerSpaced
|
||||
import SectionItemView
|
||||
import SectionTextFooter
|
||||
@@ -63,10 +64,6 @@ fun PrivacySettingsView(
|
||||
SectionDividerSpaced()
|
||||
|
||||
SectionView(stringResource(MR.strings.settings_section_title_chats)) {
|
||||
SettingsPreferenceItem(painterResource(MR.images.ic_lock), stringResource(MR.strings.encrypt_local_files), chatModel.controller.appPrefs.privacyEncryptLocalFiles, onChange = { enable ->
|
||||
withBGApi { chatModel.controller.apiSetEncryptLocalFiles(enable) }
|
||||
})
|
||||
SettingsPreferenceItem(painterResource(MR.images.ic_image), stringResource(MR.strings.auto_accept_images), chatModel.controller.appPrefs.privacyAcceptImages)
|
||||
SettingsPreferenceItem(painterResource(MR.images.ic_travel_explore), stringResource(MR.strings.send_link_previews), chatModel.controller.appPrefs.privacyLinkPreviews)
|
||||
SettingsPreferenceItem(
|
||||
painterResource(MR.images.ic_chat_bubble),
|
||||
@@ -91,6 +88,22 @@ fun PrivacySettingsView(
|
||||
chatModel.simplexLinkMode.value = it
|
||||
})
|
||||
}
|
||||
SectionDividerSpaced()
|
||||
|
||||
SectionView(stringResource(MR.strings.settings_section_title_files)) {
|
||||
SettingsPreferenceItem(painterResource(MR.images.ic_lock), stringResource(MR.strings.encrypt_local_files), chatModel.controller.appPrefs.privacyEncryptLocalFiles, onChange = { enable ->
|
||||
withBGApi { chatModel.controller.apiSetEncryptLocalFiles(enable) }
|
||||
})
|
||||
SettingsPreferenceItem(painterResource(MR.images.ic_image), stringResource(MR.strings.auto_accept_images), chatModel.controller.appPrefs.privacyAcceptImages)
|
||||
SettingsPreferenceItem(painterResource(MR.images.ic_security), stringResource(MR.strings.protect_ip_address), chatModel.controller.appPrefs.privacyAskToApproveRelays)
|
||||
}
|
||||
SectionCustomFooter {
|
||||
if (chatModel.controller.appPrefs.privacyAskToApproveRelays.state.value) {
|
||||
Text(stringResource(MR.strings.app_will_ask_to_confirm_unknown_file_servers))
|
||||
} else {
|
||||
Text(stringResource(MR.strings.without_tor_or_vpn_ip_address_will_be_visible_to_file_servers))
|
||||
}
|
||||
}
|
||||
|
||||
val currentUser = chatModel.currentUser.value
|
||||
if (currentUser != null) {
|
||||
@@ -141,7 +154,7 @@ fun PrivacySettingsView(
|
||||
}
|
||||
|
||||
if (!chatModel.desktopNoUserNoRemote) {
|
||||
SectionDividerSpaced()
|
||||
SectionDividerSpaced(maxTopPadding = true)
|
||||
DeliveryReceiptsSection(
|
||||
currentUser = currentUser,
|
||||
setOrAskSendReceiptsContacts = { enable ->
|
||||
|
||||
@@ -119,6 +119,8 @@
|
||||
<string name="error_joining_group">Error joining group</string>
|
||||
<string name="cannot_receive_file">Cannot receive file</string>
|
||||
<string name="sender_cancelled_file_transfer">Sender cancelled file transfer.</string>
|
||||
<string name="file_not_approved_title">Unknown servers!</string>
|
||||
<string name="file_not_approved_descr">Without Tor or VPN, your IP address will be visible to these XFTP relays:\n%1$s.</string>
|
||||
<string name="error_receiving_file">Error receiving file</string>
|
||||
<string name="error_creating_address">Error creating address</string>
|
||||
<string name="contact_already_exists">Contact already exists</string>
|
||||
@@ -992,6 +994,9 @@
|
||||
<string name="protect_app_screen">Protect app screen</string>
|
||||
<string name="encrypt_local_files">Encrypt local files</string>
|
||||
<string name="auto_accept_images">Auto-accept images</string>
|
||||
<string name="protect_ip_address">Protect IP address</string>
|
||||
<string name="app_will_ask_to_confirm_unknown_file_servers">The app will ask to confirm downloads from unknown file servers (except .onion or when SOCKS proxy is enabled).</string>
|
||||
<string name="without_tor_or_vpn_ip_address_will_be_visible_to_file_servers">Without Tor or VPN, your IP address will be visible to file servers.</string>
|
||||
<string name="send_link_previews">Send link previews</string>
|
||||
<string name="privacy_show_last_messages">Show last messages</string>
|
||||
<string name="privacy_message_draft">Message draft</string>
|
||||
@@ -1056,6 +1061,7 @@
|
||||
<string name="settings_section_title_app">APP</string>
|
||||
<string name="settings_section_title_device">DEVICE</string>
|
||||
<string name="settings_section_title_chats">CHATS</string>
|
||||
<string name="settings_section_title_files">FILES</string>
|
||||
<string name="settings_section_title_delivery_receipts">SEND DELIVERY RECEIPTS TO</string>
|
||||
<string name="settings_restart_app">Restart</string>
|
||||
<string name="settings_shutdown">Shutdown</string>
|
||||
|
||||
@@ -26,11 +26,11 @@ android.enableJetifier=true
|
||||
kotlin.mpp.androidSourceSetLayoutVersion=2
|
||||
kotlin.jvm.target=11
|
||||
|
||||
android.version_name=5.8-beta.0
|
||||
android.version_code=208
|
||||
android.version_name=5.8-beta.1
|
||||
android.version_code=209
|
||||
|
||||
desktop.version_name=5.8-beta.0
|
||||
desktop.version_code=45
|
||||
desktop.version_name=5.8-beta.1
|
||||
desktop.version_code=46
|
||||
|
||||
kotlin.version=1.9.23
|
||||
gradle.plugin.version=8.2.0
|
||||
|
||||
Reference in New Issue
Block a user