mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-07-11 16:18:55 +00:00
Merge branch 'master' into master-android
This commit is contained in:
@@ -561,6 +561,18 @@ func apiGroupMemberInfo(_ groupId: Int64, _ groupMemberId: Int64) throws -> (Gro
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiContactQueueInfo(_ contactId: Int64) async throws -> (RcvMsgInfo?, QueueInfo) {
|
||||
let r = await chatSendCmd(.apiContactQueueInfo(contactId: contactId))
|
||||
if case let .queueInfo(_, rcvMsgInfo, queueInfo) = r { return (rcvMsgInfo, queueInfo) }
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiGroupMemberQueueInfo(_ groupId: Int64, _ groupMemberId: Int64) async throws -> (RcvMsgInfo?, QueueInfo) {
|
||||
let r = await chatSendCmd(.apiGroupMemberQueueInfo(groupId: groupId, groupMemberId: groupMemberId))
|
||||
if case let .queueInfo(_, rcvMsgInfo, queueInfo) = r { return (rcvMsgInfo, queueInfo) }
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiSwitchContact(contactId: Int64) throws -> ConnectionStats {
|
||||
let r = chatSendCmdSync(.apiSwitchContact(contactId: contactId))
|
||||
if case let .contactSwitchStarted(_, _, connectionStats) = r { return connectionStats }
|
||||
@@ -1265,7 +1277,7 @@ func filterMembersToAdd(_ ms: [GMember]) -> [Contact] {
|
||||
let memberContactIds = ms.compactMap{ m in m.wrapped.memberCurrent ? m.wrapped.memberContactId : nil }
|
||||
return ChatModel.shared.chats
|
||||
.compactMap{ $0.chatInfo.contact }
|
||||
.filter{ c in c.ready && c.active && !memberContactIds.contains(c.apiId) }
|
||||
.filter{ c in c.sendMsgEnabled && !c.nextSendGrpInv && !memberContactIds.contains(c.apiId) }
|
||||
.sorted{ $0.displayName.lowercased() < $1.displayName.lowercased() }
|
||||
}
|
||||
|
||||
@@ -1835,7 +1847,7 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
}
|
||||
case let .sndFileCompleteXFTP(user, aChatItem, _):
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
case let .sndFileError(user, aChatItem, _):
|
||||
case let .sndFileError(user, aChatItem, _, _):
|
||||
if let aChatItem = aChatItem {
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
Task { cleanupFile(aChatItem) }
|
||||
|
||||
@@ -110,6 +110,7 @@ struct ChatInfoView: View {
|
||||
case switchAddressAlert
|
||||
case abortSwitchAddressAlert
|
||||
case syncConnectionForceAlert
|
||||
case queueInfo(info: String)
|
||||
case error(title: LocalizedStringKey, error: LocalizedStringKey = "")
|
||||
|
||||
var id: String {
|
||||
@@ -119,6 +120,7 @@ struct ChatInfoView: View {
|
||||
case .switchAddressAlert: return "switchAddressAlert"
|
||||
case .abortSwitchAddressAlert: return "abortSwitchAddressAlert"
|
||||
case .syncConnectionForceAlert: return "syncConnectionForceAlert"
|
||||
case let .queueInfo(info): return "queueInfo \(info)"
|
||||
case let .error(title, _): return "error \(title)"
|
||||
}
|
||||
}
|
||||
@@ -224,6 +226,18 @@ struct ChatInfoView: View {
|
||||
Section(header: Text("For console")) {
|
||||
infoRow("Local name", chat.chatInfo.localDisplayName)
|
||||
infoRow("Database ID", "\(chat.chatInfo.apiId)")
|
||||
Button ("Debug delivery") {
|
||||
Task {
|
||||
do {
|
||||
let info = queueInfoText(try await apiContactQueueInfo(chat.chatInfo.apiId))
|
||||
await MainActor.run { alert = .queueInfo(info: info) }
|
||||
} catch let e {
|
||||
logger.error("apiContactQueueInfo error: \(responseError(e))")
|
||||
let a = getErrorAlert(e, "Error")
|
||||
await MainActor.run { alert = .error(title: a.title, error: a.message) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,6 +257,7 @@ struct ChatInfoView: View {
|
||||
case .switchAddressAlert: return switchAddressAlert(switchContactAddress)
|
||||
case .abortSwitchAddressAlert: return abortSwitchAddressAlert(abortSwitchContactAddress)
|
||||
case .syncConnectionForceAlert: return syncConnectionForceAlert({ syncContactConnection(force: true) })
|
||||
case let .queueInfo(info): return queueInfoAlert(info)
|
||||
case let .error(title, error): return mkAlert(title: title, message: error)
|
||||
}
|
||||
}
|
||||
@@ -577,6 +592,22 @@ func syncConnectionForceAlert(_ syncConnectionForce: @escaping () -> Void) -> Al
|
||||
)
|
||||
}
|
||||
|
||||
func queueInfoText(_ info: (RcvMsgInfo?, QueueInfo)) -> String {
|
||||
let (rcvMsgInfo, qInfo) = info
|
||||
var msgInfo: String
|
||||
if let rcvMsgInfo { msgInfo = encodeJSON(rcvMsgInfo) } else { msgInfo = "none" }
|
||||
return String.localizedStringWithFormat(NSLocalizedString("server queue info: %@\n\nlast received msg: %@", comment: "queue info"), encodeJSON(qInfo), msgInfo)
|
||||
}
|
||||
|
||||
func queueInfoAlert(_ info: String) -> Alert {
|
||||
Alert(
|
||||
title: Text("Message queue info"),
|
||||
message: Text(info),
|
||||
primaryButton: .default(Text("Ok")),
|
||||
secondaryButton: .default(Text("Copy")) { UIPasteboard.general.string = info }
|
||||
)
|
||||
}
|
||||
|
||||
struct ChatInfoView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ChatInfoView(
|
||||
|
||||
@@ -54,7 +54,7 @@ struct CIFileView: View {
|
||||
switch (file.fileStatus) {
|
||||
case .sndStored: return file.fileProtocol == .local
|
||||
case .sndTransfer: return false
|
||||
case .sndComplete: return false
|
||||
case .sndComplete: return true
|
||||
case .sndCancelled: return false
|
||||
case .sndError: return false
|
||||
case .rcvInvitation: return true
|
||||
@@ -113,6 +113,11 @@ struct CIFileView: View {
|
||||
if file.fileProtocol == .local, let fileSource = getLoadedFileSource(file) {
|
||||
saveCryptoFile(fileSource)
|
||||
}
|
||||
case .sndComplete:
|
||||
logger.debug("CIFileView fileAction - in .sndComplete")
|
||||
if let fileSource = getLoadedFileSource(file) {
|
||||
saveCryptoFile(fileSource)
|
||||
}
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ struct CIInvalidJSONView: View {
|
||||
.cornerRadius(18)
|
||||
.textSelection(.disabled)
|
||||
.onTapGesture { showJSON = true }
|
||||
.sheet(isPresented: $showJSON) {
|
||||
.appSheet(isPresented: $showJSON) {
|
||||
invalidJSONView(json)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ struct ChatView: View {
|
||||
} label: {
|
||||
ChatInfoToolbar(chat: chat)
|
||||
}
|
||||
.sheet(isPresented: $showChatInfoSheet, onDismiss: {
|
||||
.appSheet(isPresented: $showChatInfoSheet, onDismiss: {
|
||||
connectionStats = nil
|
||||
customUserProfile = nil
|
||||
connectionCode = nil
|
||||
|
||||
@@ -35,6 +35,7 @@ struct GroupMemberInfoView: View {
|
||||
case abortSwitchAddressAlert
|
||||
case syncConnectionForceAlert
|
||||
case planAndConnectAlert(alert: PlanAndConnectAlert)
|
||||
case queueInfo(info: String)
|
||||
case error(title: LocalizedStringKey, error: LocalizedStringKey)
|
||||
|
||||
var id: String {
|
||||
@@ -49,6 +50,7 @@ struct GroupMemberInfoView: View {
|
||||
case .abortSwitchAddressAlert: return "abortSwitchAddressAlert"
|
||||
case .syncConnectionForceAlert: return "syncConnectionForceAlert"
|
||||
case let .planAndConnectAlert(alert): return "planAndConnectAlert \(alert.id)"
|
||||
case let .queueInfo(info): return "queueInfo \(info)"
|
||||
case let .error(title, _): return "error \(title)"
|
||||
}
|
||||
}
|
||||
@@ -178,6 +180,18 @@ struct GroupMemberInfoView: View {
|
||||
Section("For console") {
|
||||
infoRow("Local name", member.localDisplayName)
|
||||
infoRow("Database ID", "\(member.groupMemberId)")
|
||||
Button ("Debug delivery") {
|
||||
Task {
|
||||
do {
|
||||
let info = queueInfoText(try await apiGroupMemberQueueInfo(groupInfo.apiId, member.groupMemberId))
|
||||
await MainActor.run { alert = .queueInfo(info: info) }
|
||||
} catch let e {
|
||||
logger.error("apiContactQueueInfo error: \(responseError(e))")
|
||||
let a = getErrorAlert(e, "Error")
|
||||
await MainActor.run { alert = .error(title: a.title, error: a.message) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,6 +237,7 @@ struct GroupMemberInfoView: View {
|
||||
case .abortSwitchAddressAlert: return abortSwitchAddressAlert(abortSwitchMemberAddress)
|
||||
case .syncConnectionForceAlert: return syncConnectionForceAlert({ syncMemberConnection(force: true) })
|
||||
case let .planAndConnectAlert(alert): return planAndConnectAlert(alert, dismiss: true)
|
||||
case let .queueInfo(info): return queueInfoAlert(info)
|
||||
case let .error(title, error): return Alert(title: Text(title), message: Text(error))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,10 +349,12 @@ struct ChatListNavLink: View {
|
||||
.tint(.accentColor)
|
||||
}
|
||||
.frame(height: rowHeights[dynamicTypeSize])
|
||||
.sheet(isPresented: $showContactConnectionInfo) {
|
||||
if case let .contactConnection(contactConnection) = chat.chatInfo {
|
||||
ContactConnectionInfo(contactConnection: contactConnection)
|
||||
.environment(\EnvironmentValues.refresh as! WritableKeyPath<EnvironmentValues, RefreshAction?>, nil)
|
||||
.appSheet(isPresented: $showContactConnectionInfo) {
|
||||
Group {
|
||||
if case let .contactConnection(contactConnection) = chat.chatInfo {
|
||||
ContactConnectionInfo(contactConnection: contactConnection)
|
||||
.environment(\EnvironmentValues.refresh as! WritableKeyPath<EnvironmentValues, RefreshAction?>, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
@@ -467,7 +469,7 @@ struct ChatListNavLink: View {
|
||||
.padding(4)
|
||||
.frame(height: rowHeights[dynamicTypeSize])
|
||||
.onTapGesture { showInvalidJSON = true }
|
||||
.sheet(isPresented: $showInvalidJSON) {
|
||||
.appSheet(isPresented: $showInvalidJSON) {
|
||||
invalidJSONView(json)
|
||||
.environment(\EnvironmentValues.refresh as! WritableKeyPath<EnvironmentValues, RefreshAction?>, nil)
|
||||
}
|
||||
|
||||
@@ -406,7 +406,28 @@ private let versionDescriptions: [VersionDescription] = [
|
||||
description: "More reliable network connection."
|
||||
)
|
||||
]
|
||||
)
|
||||
),
|
||||
VersionDescription(
|
||||
version: "v5.8",
|
||||
post: URL(string: "https://simplex.chat/blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.html"),
|
||||
features: [
|
||||
FeatureDescription(
|
||||
icon: "arrow.forward",
|
||||
title: "Private message routing 🚀",
|
||||
description: "Protect your IP address from the messaging relays chosen by your contacts.\nEnable in *Network & servers* settings."
|
||||
),
|
||||
FeatureDescription(
|
||||
icon: "network.badge.shield.half.filled",
|
||||
title: "Safely receive files",
|
||||
description: "Confirm files from unknown servers."
|
||||
),
|
||||
FeatureDescription(
|
||||
icon: "battery.50",
|
||||
title: "Improved message delivery",
|
||||
description: "With reduced battery usage."
|
||||
)
|
||||
]
|
||||
),
|
||||
]
|
||||
|
||||
private let lastVersion = versionDescriptions.last!.version
|
||||
|
||||
@@ -123,7 +123,7 @@ struct UserProfilesView: View {
|
||||
deleteModeButton("Profile and server connections", true)
|
||||
deleteModeButton("Local profile data only", false)
|
||||
}
|
||||
.sheet(item: $selectedUser) { user in
|
||||
.appSheet(item: $selectedUser) { user in
|
||||
HiddenProfileView(user: user, profileHidden: $profileHidden)
|
||||
}
|
||||
.onChange(of: profileHidden) { _ in
|
||||
@@ -131,7 +131,7 @@ struct UserProfilesView: View {
|
||||
withAnimation { profileHidden = false }
|
||||
}
|
||||
}
|
||||
.sheet(item: $profileAction) { action in
|
||||
.appSheet(item: $profileAction) { action in
|
||||
profileActionView(action)
|
||||
}
|
||||
.alert(item: $alert) { alert in
|
||||
|
||||
@@ -1296,6 +1296,10 @@
|
||||
<target>Потвърди актуализаациите на базата данни</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
|
||||
<source>Confirm files from unknown servers.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm network settings" xml:space="preserve">
|
||||
<source>Confirm network settings</source>
|
||||
<target>Потвърди мрежовите настройки</target>
|
||||
@@ -4412,6 +4416,10 @@ Error: %@</source>
|
||||
<source>Private message routing</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
||||
<source>Private message routing 🚀</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private notes" xml:space="preserve">
|
||||
<source>Private notes</source>
|
||||
<target>Лични бележки</target>
|
||||
@@ -4510,6 +4518,11 @@ Error: %@</source>
|
||||
<target>Защити екрана на приложението</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings." xml:space="preserve">
|
||||
<source>Protect your IP address from the messaging relays chosen by your contacts.
|
||||
Enable in *Network & servers* settings.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
|
||||
<source>Protect your chat profiles with a password!</source>
|
||||
<target>Защитете чат профилите с парола!</target>
|
||||
@@ -4850,6 +4863,10 @@ Error: %@</source>
|
||||
<target>SMP сървъри</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safely receive files" xml:space="preserve">
|
||||
<source>Safely receive files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safer groups" xml:space="preserve">
|
||||
<source>Safer groups</source>
|
||||
<target>По-безопасни групи</target>
|
||||
|
||||
@@ -1250,6 +1250,10 @@
|
||||
<target>Potvrdit aktualizaci databáze</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
|
||||
<source>Confirm files from unknown servers.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm network settings" xml:space="preserve">
|
||||
<source>Confirm network settings</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -4234,6 +4238,10 @@ Error: %@</source>
|
||||
<source>Private message routing</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
||||
<source>Private message routing 🚀</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private notes" xml:space="preserve">
|
||||
<source>Private notes</source>
|
||||
<note>name of notes to self</note>
|
||||
@@ -4327,6 +4335,11 @@ Error: %@</source>
|
||||
<target>Ochrana obrazovky aplikace</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings." xml:space="preserve">
|
||||
<source>Protect your IP address from the messaging relays chosen by your contacts.
|
||||
Enable in *Network & servers* settings.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
|
||||
<source>Protect your chat profiles with a password!</source>
|
||||
<target>Chraňte své chat profily heslem!</target>
|
||||
@@ -4656,6 +4669,10 @@ Error: %@</source>
|
||||
<target>SMP servery</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safely receive files" xml:space="preserve">
|
||||
<source>Safely receive files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safer groups" xml:space="preserve">
|
||||
<source>Safer groups</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
|
||||
@@ -1296,6 +1296,10 @@
|
||||
<target>Datenbank-Aktualisierungen bestätigen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
|
||||
<source>Confirm files from unknown servers.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm network settings" xml:space="preserve">
|
||||
<source>Confirm network settings</source>
|
||||
<target>Bestätigen Sie die Netzwerkeinstellungen</target>
|
||||
@@ -4412,6 +4416,10 @@ Fehler: %@</target>
|
||||
<source>Private message routing</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
||||
<source>Private message routing 🚀</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private notes" xml:space="preserve">
|
||||
<source>Private notes</source>
|
||||
<target>Private Notizen</target>
|
||||
@@ -4510,6 +4518,11 @@ Fehler: %@</target>
|
||||
<target>App-Bildschirm schützen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings." xml:space="preserve">
|
||||
<source>Protect your IP address from the messaging relays chosen by your contacts.
|
||||
Enable in *Network & servers* settings.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
|
||||
<source>Protect your chat profiles with a password!</source>
|
||||
<target>Ihre Chat-Profile mit einem Passwort schützen!</target>
|
||||
@@ -4850,6 +4863,10 @@ Fehler: %@</target>
|
||||
<target>SMP-Server</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safely receive files" xml:space="preserve">
|
||||
<source>Safely receive files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safer groups" xml:space="preserve">
|
||||
<source>Safer groups</source>
|
||||
<target>Sicherere Gruppen</target>
|
||||
|
||||
@@ -1299,6 +1299,11 @@
|
||||
<target>Confirm database upgrades</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
|
||||
<source>Confirm files from unknown servers.</source>
|
||||
<target>Confirm files from unknown servers.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm network settings" xml:space="preserve">
|
||||
<source>Confirm network settings</source>
|
||||
<target>Confirm network settings</target>
|
||||
@@ -4428,6 +4433,11 @@ Error: %@</target>
|
||||
<target>Private message routing</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
||||
<source>Private message routing 🚀</source>
|
||||
<target>Private message routing 🚀</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private notes" xml:space="preserve">
|
||||
<source>Private notes</source>
|
||||
<target>Private notes</target>
|
||||
@@ -4528,6 +4538,13 @@ Error: %@</target>
|
||||
<target>Protect app screen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings." xml:space="preserve">
|
||||
<source>Protect your IP address from the messaging relays chosen by your contacts.
|
||||
Enable in *Network & servers* settings.</source>
|
||||
<target>Protect your IP address from the messaging relays chosen by your contacts.
|
||||
Enable in *Network & servers* settings.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
|
||||
<source>Protect your chat profiles with a password!</source>
|
||||
<target>Protect your chat profiles with a password!</target>
|
||||
@@ -4868,6 +4885,11 @@ Error: %@</target>
|
||||
<target>SMP servers</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safely receive files" xml:space="preserve">
|
||||
<source>Safely receive files</source>
|
||||
<target>Safely receive files</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safer groups" xml:space="preserve">
|
||||
<source>Safer groups</source>
|
||||
<target>Safer groups</target>
|
||||
|
||||
@@ -1296,6 +1296,10 @@
|
||||
<target>Confirmar actualizaciones de la bases de datos</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
|
||||
<source>Confirm files from unknown servers.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm network settings" xml:space="preserve">
|
||||
<source>Confirm network settings</source>
|
||||
<target>Confirmar configuración de red</target>
|
||||
@@ -4412,6 +4416,10 @@ Error: %@</target>
|
||||
<source>Private message routing</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
||||
<source>Private message routing 🚀</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private notes" xml:space="preserve">
|
||||
<source>Private notes</source>
|
||||
<target>Notas privadas</target>
|
||||
@@ -4510,6 +4518,11 @@ Error: %@</target>
|
||||
<target>Proteger la pantalla de la aplicación</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings." xml:space="preserve">
|
||||
<source>Protect your IP address from the messaging relays chosen by your contacts.
|
||||
Enable in *Network & servers* settings.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
|
||||
<source>Protect your chat profiles with a password!</source>
|
||||
<target>¡Protege tus perfiles con contraseña!</target>
|
||||
@@ -4850,6 +4863,10 @@ Error: %@</target>
|
||||
<target>Servidores SMP</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safely receive files" xml:space="preserve">
|
||||
<source>Safely receive files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safer groups" xml:space="preserve">
|
||||
<source>Safer groups</source>
|
||||
<target>Grupos más seguros</target>
|
||||
|
||||
@@ -1243,6 +1243,10 @@
|
||||
<target>Vahvista tietokannan päivitykset</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
|
||||
<source>Confirm files from unknown servers.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm network settings" xml:space="preserve">
|
||||
<source>Confirm network settings</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -4222,6 +4226,10 @@ Error: %@</source>
|
||||
<source>Private message routing</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
||||
<source>Private message routing 🚀</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private notes" xml:space="preserve">
|
||||
<source>Private notes</source>
|
||||
<note>name of notes to self</note>
|
||||
@@ -4315,6 +4323,11 @@ Error: %@</source>
|
||||
<target>Suojaa sovellusnäyttö</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings." xml:space="preserve">
|
||||
<source>Protect your IP address from the messaging relays chosen by your contacts.
|
||||
Enable in *Network & servers* settings.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
|
||||
<source>Protect your chat profiles with a password!</source>
|
||||
<target>Suojaa keskusteluprofiilisi salasanalla!</target>
|
||||
@@ -4644,6 +4657,10 @@ Error: %@</source>
|
||||
<target>SMP-palvelimet</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safely receive files" xml:space="preserve">
|
||||
<source>Safely receive files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safer groups" xml:space="preserve">
|
||||
<source>Safer groups</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
|
||||
@@ -1296,6 +1296,10 @@
|
||||
<target>Confirmer la mise à niveau de la base de données</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
|
||||
<source>Confirm files from unknown servers.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm network settings" xml:space="preserve">
|
||||
<source>Confirm network settings</source>
|
||||
<target>Confirmer les paramètres réseau</target>
|
||||
@@ -4412,6 +4416,10 @@ Erreur : %@</target>
|
||||
<source>Private message routing</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
||||
<source>Private message routing 🚀</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private notes" xml:space="preserve">
|
||||
<source>Private notes</source>
|
||||
<target>Notes privées</target>
|
||||
@@ -4510,6 +4518,11 @@ Erreur : %@</target>
|
||||
<target>Protéger l'écran de l'app</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings." xml:space="preserve">
|
||||
<source>Protect your IP address from the messaging relays chosen by your contacts.
|
||||
Enable in *Network & servers* settings.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
|
||||
<source>Protect your chat profiles with a password!</source>
|
||||
<target>Protégez vos profils de chat par un mot de passe !</target>
|
||||
@@ -4850,6 +4863,10 @@ Erreur : %@</target>
|
||||
<target>Serveurs SMP</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safely receive files" xml:space="preserve">
|
||||
<source>Safely receive files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safer groups" xml:space="preserve">
|
||||
<source>Safer groups</source>
|
||||
<target>Groupes plus sûrs</target>
|
||||
|
||||
@@ -1296,6 +1296,10 @@
|
||||
<target>Adatbázis frissítés megerősítése</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
|
||||
<source>Confirm files from unknown servers.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm network settings" xml:space="preserve">
|
||||
<source>Confirm network settings</source>
|
||||
<target>Hálózati beállítások megerősítése</target>
|
||||
@@ -4412,6 +4416,10 @@ Hiba: %@</target>
|
||||
<source>Private message routing</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
||||
<source>Private message routing 🚀</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private notes" xml:space="preserve">
|
||||
<source>Private notes</source>
|
||||
<target>Privát jegyzetek</target>
|
||||
@@ -4510,6 +4518,11 @@ Hiba: %@</target>
|
||||
<target>Alkalmazás képernyőjének védelme</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings." xml:space="preserve">
|
||||
<source>Protect your IP address from the messaging relays chosen by your contacts.
|
||||
Enable in *Network & servers* settings.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
|
||||
<source>Protect your chat profiles with a password!</source>
|
||||
<target>Csevegési profiljok védelme jelszóval!</target>
|
||||
@@ -4850,6 +4863,10 @@ Hiba: %@</target>
|
||||
<target>Üzenetküldő (SMP) kiszolgálók</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safely receive files" xml:space="preserve">
|
||||
<source>Safely receive files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safer groups" xml:space="preserve">
|
||||
<source>Safer groups</source>
|
||||
<target>Biztonságosabb csoportok</target>
|
||||
|
||||
@@ -1296,6 +1296,10 @@
|
||||
<target>Conferma aggiornamenti database</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
|
||||
<source>Confirm files from unknown servers.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm network settings" xml:space="preserve">
|
||||
<source>Confirm network settings</source>
|
||||
<target>Conferma le impostazioni di rete</target>
|
||||
@@ -4412,6 +4416,10 @@ Errore: %@</target>
|
||||
<source>Private message routing</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
||||
<source>Private message routing 🚀</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private notes" xml:space="preserve">
|
||||
<source>Private notes</source>
|
||||
<target>Note private</target>
|
||||
@@ -4510,6 +4518,11 @@ Errore: %@</target>
|
||||
<target>Proteggi la schermata dell'app</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings." xml:space="preserve">
|
||||
<source>Protect your IP address from the messaging relays chosen by your contacts.
|
||||
Enable in *Network & servers* settings.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
|
||||
<source>Protect your chat profiles with a password!</source>
|
||||
<target>Proteggi i tuoi profili di chat con una password!</target>
|
||||
@@ -4850,6 +4863,10 @@ Errore: %@</target>
|
||||
<target>Server SMP</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safely receive files" xml:space="preserve">
|
||||
<source>Safely receive files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safer groups" xml:space="preserve">
|
||||
<source>Safer groups</source>
|
||||
<target>Gruppi più sicuri</target>
|
||||
|
||||
@@ -1267,6 +1267,10 @@
|
||||
<target>データベースのアップグレードを確認</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
|
||||
<source>Confirm files from unknown servers.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm network settings" xml:space="preserve">
|
||||
<source>Confirm network settings</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -4248,6 +4252,10 @@ Error: %@</source>
|
||||
<source>Private message routing</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
||||
<source>Private message routing 🚀</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private notes" xml:space="preserve">
|
||||
<source>Private notes</source>
|
||||
<note>name of notes to self</note>
|
||||
@@ -4341,6 +4349,11 @@ Error: %@</source>
|
||||
<target>アプリ画面を守る</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings." xml:space="preserve">
|
||||
<source>Protect your IP address from the messaging relays chosen by your contacts.
|
||||
Enable in *Network & servers* settings.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
|
||||
<source>Protect your chat profiles with a password!</source>
|
||||
<target>チャットのプロフィールをパスワードで保護します!</target>
|
||||
@@ -4669,6 +4682,10 @@ Error: %@</source>
|
||||
<target>SMPサーバ</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safely receive files" xml:space="preserve">
|
||||
<source>Safely receive files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safer groups" xml:space="preserve">
|
||||
<source>Safer groups</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
|
||||
@@ -1296,6 +1296,10 @@
|
||||
<target>Bevestig database upgrades</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
|
||||
<source>Confirm files from unknown servers.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm network settings" xml:space="preserve">
|
||||
<source>Confirm network settings</source>
|
||||
<target>Bevestig netwerk instellingen</target>
|
||||
@@ -4412,6 +4416,10 @@ Fout: %@</target>
|
||||
<source>Private message routing</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
||||
<source>Private message routing 🚀</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private notes" xml:space="preserve">
|
||||
<source>Private notes</source>
|
||||
<target>Privé notities</target>
|
||||
@@ -4510,6 +4518,11 @@ Fout: %@</target>
|
||||
<target>App scherm verbergen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings." xml:space="preserve">
|
||||
<source>Protect your IP address from the messaging relays chosen by your contacts.
|
||||
Enable in *Network & servers* settings.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
|
||||
<source>Protect your chat profiles with a password!</source>
|
||||
<target>Bescherm je chat profielen met een wachtwoord!</target>
|
||||
@@ -4850,6 +4863,10 @@ Fout: %@</target>
|
||||
<target>SMP servers</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safely receive files" xml:space="preserve">
|
||||
<source>Safely receive files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safer groups" xml:space="preserve">
|
||||
<source>Safer groups</source>
|
||||
<target>Veiligere groepen</target>
|
||||
|
||||
@@ -1296,6 +1296,10 @@
|
||||
<target>Potwierdź aktualizacje bazy danych</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
|
||||
<source>Confirm files from unknown servers.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm network settings" xml:space="preserve">
|
||||
<source>Confirm network settings</source>
|
||||
<target>Potwierdź ustawienia sieciowe</target>
|
||||
@@ -4412,6 +4416,10 @@ Błąd: %@</target>
|
||||
<source>Private message routing</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
||||
<source>Private message routing 🚀</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private notes" xml:space="preserve">
|
||||
<source>Private notes</source>
|
||||
<target>Prywatne notatki</target>
|
||||
@@ -4510,6 +4518,11 @@ Błąd: %@</target>
|
||||
<target>Chroń ekran aplikacji</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings." xml:space="preserve">
|
||||
<source>Protect your IP address from the messaging relays chosen by your contacts.
|
||||
Enable in *Network & servers* settings.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
|
||||
<source>Protect your chat profiles with a password!</source>
|
||||
<target>Chroń swoje profile czatu hasłem!</target>
|
||||
@@ -4850,6 +4863,10 @@ Błąd: %@</target>
|
||||
<target>Serwery SMP</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safely receive files" xml:space="preserve">
|
||||
<source>Safely receive files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safer groups" xml:space="preserve">
|
||||
<source>Safer groups</source>
|
||||
<target>Bezpieczniejsze grupy</target>
|
||||
|
||||
@@ -1296,6 +1296,10 @@
|
||||
<target>Подтвердить обновление базы данных</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
|
||||
<source>Confirm files from unknown servers.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm network settings" xml:space="preserve">
|
||||
<source>Confirm network settings</source>
|
||||
<target>Подтвердите настройки сети</target>
|
||||
@@ -4412,6 +4416,10 @@ Error: %@</source>
|
||||
<source>Private message routing</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
||||
<source>Private message routing 🚀</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private notes" xml:space="preserve">
|
||||
<source>Private notes</source>
|
||||
<target>Личные заметки</target>
|
||||
@@ -4510,6 +4518,11 @@ Error: %@</source>
|
||||
<target>Защитить экран приложения</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings." xml:space="preserve">
|
||||
<source>Protect your IP address from the messaging relays chosen by your contacts.
|
||||
Enable in *Network & servers* settings.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
|
||||
<source>Protect your chat profiles with a password!</source>
|
||||
<target>Защитите Ваши профили чата паролем!</target>
|
||||
@@ -4850,6 +4863,10 @@ Error: %@</source>
|
||||
<target>SMP серверы</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safely receive files" xml:space="preserve">
|
||||
<source>Safely receive files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safer groups" xml:space="preserve">
|
||||
<source>Safer groups</source>
|
||||
<target>Более безопасные группы</target>
|
||||
|
||||
@@ -1235,6 +1235,10 @@
|
||||
<target>ยืนยันการอัพเกรดฐานข้อมูล</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
|
||||
<source>Confirm files from unknown servers.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm network settings" xml:space="preserve">
|
||||
<source>Confirm network settings</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -4203,6 +4207,10 @@ Error: %@</source>
|
||||
<source>Private message routing</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
||||
<source>Private message routing 🚀</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private notes" xml:space="preserve">
|
||||
<source>Private notes</source>
|
||||
<note>name of notes to self</note>
|
||||
@@ -4296,6 +4304,11 @@ Error: %@</source>
|
||||
<target>ปกป้องหน้าจอแอป</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings." xml:space="preserve">
|
||||
<source>Protect your IP address from the messaging relays chosen by your contacts.
|
||||
Enable in *Network & servers* settings.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
|
||||
<source>Protect your chat profiles with a password!</source>
|
||||
<target>ปกป้องโปรไฟล์การแชทของคุณด้วยรหัสผ่าน!</target>
|
||||
@@ -4623,6 +4636,10 @@ Error: %@</source>
|
||||
<target>เซิร์ฟเวอร์ SMP</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safely receive files" xml:space="preserve">
|
||||
<source>Safely receive files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safer groups" xml:space="preserve">
|
||||
<source>Safer groups</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
|
||||
@@ -1296,6 +1296,10 @@
|
||||
<target>Veritabanı geliştirmelerini onayla</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
|
||||
<source>Confirm files from unknown servers.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm network settings" xml:space="preserve">
|
||||
<source>Confirm network settings</source>
|
||||
<target>Ağ ayarlarını onaylayın</target>
|
||||
@@ -4412,6 +4416,10 @@ Hata: %@</target>
|
||||
<source>Private message routing</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
||||
<source>Private message routing 🚀</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private notes" xml:space="preserve">
|
||||
<source>Private notes</source>
|
||||
<target>Gizli notlar</target>
|
||||
@@ -4510,6 +4518,11 @@ Hata: %@</target>
|
||||
<target>Uygulama ekranını koru</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings." xml:space="preserve">
|
||||
<source>Protect your IP address from the messaging relays chosen by your contacts.
|
||||
Enable in *Network & servers* settings.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
|
||||
<source>Protect your chat profiles with a password!</source>
|
||||
<target>Bir parolayla birlikte sohbet profillerini koru!</target>
|
||||
@@ -4850,6 +4863,10 @@ Hata: %@</target>
|
||||
<target>SMP sunucuları</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safely receive files" xml:space="preserve">
|
||||
<source>Safely receive files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safer groups" xml:space="preserve">
|
||||
<source>Safer groups</source>
|
||||
<target>Daha güvenli gruplar</target>
|
||||
|
||||
@@ -1294,6 +1294,10 @@
|
||||
<target>Підтвердити оновлення бази даних</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
|
||||
<source>Confirm files from unknown servers.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm network settings" xml:space="preserve">
|
||||
<source>Confirm network settings</source>
|
||||
<target>Підтвердьте налаштування мережі</target>
|
||||
@@ -4395,6 +4399,10 @@ Error: %@</source>
|
||||
<source>Private message routing</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
||||
<source>Private message routing 🚀</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private notes" xml:space="preserve">
|
||||
<source>Private notes</source>
|
||||
<target>Приватні нотатки</target>
|
||||
@@ -4491,6 +4499,11 @@ Error: %@</source>
|
||||
<target>Захистіть екран програми</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings." xml:space="preserve">
|
||||
<source>Protect your IP address from the messaging relays chosen by your contacts.
|
||||
Enable in *Network & servers* settings.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
|
||||
<source>Protect your chat profiles with a password!</source>
|
||||
<target>Захистіть свої профілі чату паролем!</target>
|
||||
@@ -4830,6 +4843,10 @@ Error: %@</source>
|
||||
<target>Сервери SMP</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safely receive files" xml:space="preserve">
|
||||
<source>Safely receive files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safer groups" xml:space="preserve">
|
||||
<source>Safer groups</source>
|
||||
<target>Безпечніші групи</target>
|
||||
|
||||
@@ -1282,6 +1282,10 @@
|
||||
<target>确认数据库升级</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
|
||||
<source>Confirm files from unknown servers.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm network settings" xml:space="preserve">
|
||||
<source>Confirm network settings</source>
|
||||
<target>确认网络设置</target>
|
||||
@@ -4366,6 +4370,10 @@ Error: %@</source>
|
||||
<source>Private message routing</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
||||
<source>Private message routing 🚀</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private notes" xml:space="preserve">
|
||||
<source>Private notes</source>
|
||||
<target>私密笔记</target>
|
||||
@@ -4462,6 +4470,11 @@ Error: %@</source>
|
||||
<target>保护应用程序屏幕</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings." xml:space="preserve">
|
||||
<source>Protect your IP address from the messaging relays chosen by your contacts.
|
||||
Enable in *Network & servers* settings.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
|
||||
<source>Protect your chat profiles with a password!</source>
|
||||
<target>使用密码保护您的聊天资料!</target>
|
||||
@@ -4800,6 +4813,10 @@ Error: %@</source>
|
||||
<target>SMP 服务器</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safely receive files" xml:space="preserve">
|
||||
<source>Safely receive files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Safer groups" xml:space="preserve">
|
||||
<source>Safer groups</source>
|
||||
<target>更安全的群组</target>
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
5C029EAA283942EA004A9677 /* CallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C029EA9283942EA004A9677 /* CallController.swift */; };
|
||||
5C05DF532840AA1D00C683F9 /* CallSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C05DF522840AA1D00C683F9 /* CallSettings.swift */; };
|
||||
5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C063D2627A4564100AEC577 /* ChatPreviewView.swift */; };
|
||||
5C0EA1272C08ADAF00AD2E5E /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C0EA1222C08ADAF00AD2E5E /* libgmp.a */; };
|
||||
5C0EA1282C08ADAF00AD2E5E /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C0EA1232C08ADAF00AD2E5E /* libffi.a */; };
|
||||
5C0EA1292C08ADAF00AD2E5E /* libHSsimplex-chat-5.8.0.4-2QdMx3VWkTS77pHWzbDqa5-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C0EA1242C08ADAF00AD2E5E /* libHSsimplex-chat-5.8.0.4-2QdMx3VWkTS77pHWzbDqa5-ghc9.6.3.a */; };
|
||||
5C0EA12A2C08ADAF00AD2E5E /* libHSsimplex-chat-5.8.0.4-2QdMx3VWkTS77pHWzbDqa5.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C0EA1252C08ADAF00AD2E5E /* libHSsimplex-chat-5.8.0.4-2QdMx3VWkTS77pHWzbDqa5.a */; };
|
||||
5C0EA12B2C08ADAF00AD2E5E /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C0EA1262C08ADAF00AD2E5E /* libgmpxx.a */; };
|
||||
5C10D88828EED12E00E58BF0 /* ContactConnectionInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C10D88728EED12E00E58BF0 /* ContactConnectionInfo.swift */; };
|
||||
5C10D88A28F187F300E58BF0 /* FullScreenMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C10D88928F187F300E58BF0 /* FullScreenMediaView.swift */; };
|
||||
5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; };
|
||||
@@ -139,11 +144,6 @@
|
||||
5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; };
|
||||
5CEBD7462A5C0A8F00665FE2 /* KeyboardPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */; };
|
||||
5CEBD7482A5F115D00665FE2 /* SetDeliveryReceiptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */; };
|
||||
5CEE87942C024F4F00583B8A /* libHSsimplex-chat-5.8.0.3-3OkzEeUKATkEGDdUZR1g6G-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CEE878F2C024F4F00583B8A /* libHSsimplex-chat-5.8.0.3-3OkzEeUKATkEGDdUZR1g6G-ghc9.6.3.a */; };
|
||||
5CEE87952C024F4F00583B8A /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CEE87902C024F4F00583B8A /* libgmp.a */; };
|
||||
5CEE87962C024F4F00583B8A /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CEE87912C024F4F00583B8A /* libgmpxx.a */; };
|
||||
5CEE87972C024F4F00583B8A /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CEE87922C024F4F00583B8A /* libffi.a */; };
|
||||
5CEE87982C024F4F00583B8A /* libHSsimplex-chat-5.8.0.3-3OkzEeUKATkEGDdUZR1g6G.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CEE87932C024F4F00583B8A /* libHSsimplex-chat-5.8.0.3-3OkzEeUKATkEGDdUZR1g6G.a */; };
|
||||
5CF937202B24DE8C00E1D781 /* SharedFileSubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */; };
|
||||
5CF937232B2503D000E1D781 /* NSESubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF937212B25034A00E1D781 /* NSESubscriber.swift */; };
|
||||
5CFA59C42860BC6200863A68 /* MigrateToAppGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */; };
|
||||
@@ -278,6 +278,11 @@
|
||||
5C029EA9283942EA004A9677 /* CallController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallController.swift; sourceTree = "<group>"; };
|
||||
5C05DF522840AA1D00C683F9 /* CallSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallSettings.swift; sourceTree = "<group>"; };
|
||||
5C063D2627A4564100AEC577 /* ChatPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatPreviewView.swift; sourceTree = "<group>"; };
|
||||
5C0EA1222C08ADAF00AD2E5E /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5C0EA1232C08ADAF00AD2E5E /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5C0EA1242C08ADAF00AD2E5E /* libHSsimplex-chat-5.8.0.4-2QdMx3VWkTS77pHWzbDqa5-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.8.0.4-2QdMx3VWkTS77pHWzbDqa5-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
5C0EA1252C08ADAF00AD2E5E /* libHSsimplex-chat-5.8.0.4-2QdMx3VWkTS77pHWzbDqa5.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.8.0.4-2QdMx3VWkTS77pHWzbDqa5.a"; sourceTree = "<group>"; };
|
||||
5C0EA1262C08ADAF00AD2E5E /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5C10D88728EED12E00E58BF0 /* ContactConnectionInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactConnectionInfo.swift; sourceTree = "<group>"; };
|
||||
5C10D88928F187F300E58BF0 /* FullScreenMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenMediaView.swift; sourceTree = "<group>"; };
|
||||
5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactRequestView.swift; sourceTree = "<group>"; };
|
||||
@@ -440,11 +445,6 @@
|
||||
5CEACCEC27DEA495000BD591 /* MsgContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MsgContentView.swift; sourceTree = "<group>"; };
|
||||
5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardPadding.swift; sourceTree = "<group>"; };
|
||||
5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDeliveryReceiptsView.swift; sourceTree = "<group>"; };
|
||||
5CEE878F2C024F4F00583B8A /* libHSsimplex-chat-5.8.0.3-3OkzEeUKATkEGDdUZR1g6G-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.8.0.3-3OkzEeUKATkEGDdUZR1g6G-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
5CEE87902C024F4F00583B8A /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5CEE87912C024F4F00583B8A /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5CEE87922C024F4F00583B8A /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5CEE87932C024F4F00583B8A /* libHSsimplex-chat-5.8.0.3-3OkzEeUKATkEGDdUZR1g6G.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.8.0.3-3OkzEeUKATkEGDdUZR1g6G.a"; sourceTree = "<group>"; };
|
||||
5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedFileSubscriber.swift; sourceTree = "<group>"; };
|
||||
5CF937212B25034A00E1D781 /* NSESubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSESubscriber.swift; sourceTree = "<group>"; };
|
||||
5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToAppGroupView.swift; sourceTree = "<group>"; };
|
||||
@@ -539,13 +539,13 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5CEE87942C024F4F00583B8A /* libHSsimplex-chat-5.8.0.3-3OkzEeUKATkEGDdUZR1g6G-ghc9.6.3.a in Frameworks */,
|
||||
5CEE87972C024F4F00583B8A /* libffi.a in Frameworks */,
|
||||
5CEE87962C024F4F00583B8A /* libgmpxx.a in Frameworks */,
|
||||
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
|
||||
5CEE87952C024F4F00583B8A /* libgmp.a in Frameworks */,
|
||||
5C0EA1272C08ADAF00AD2E5E /* libgmp.a in Frameworks */,
|
||||
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
|
||||
5CEE87982C024F4F00583B8A /* libHSsimplex-chat-5.8.0.3-3OkzEeUKATkEGDdUZR1g6G.a in Frameworks */,
|
||||
5C0EA12A2C08ADAF00AD2E5E /* libHSsimplex-chat-5.8.0.4-2QdMx3VWkTS77pHWzbDqa5.a in Frameworks */,
|
||||
5C0EA1292C08ADAF00AD2E5E /* libHSsimplex-chat-5.8.0.4-2QdMx3VWkTS77pHWzbDqa5-ghc9.6.3.a in Frameworks */,
|
||||
5C0EA1282C08ADAF00AD2E5E /* libffi.a in Frameworks */,
|
||||
5C0EA12B2C08ADAF00AD2E5E /* libgmpxx.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -611,11 +611,11 @@
|
||||
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5CEE87922C024F4F00583B8A /* libffi.a */,
|
||||
5CEE87902C024F4F00583B8A /* libgmp.a */,
|
||||
5CEE87912C024F4F00583B8A /* libgmpxx.a */,
|
||||
5CEE878F2C024F4F00583B8A /* libHSsimplex-chat-5.8.0.3-3OkzEeUKATkEGDdUZR1g6G-ghc9.6.3.a */,
|
||||
5CEE87932C024F4F00583B8A /* libHSsimplex-chat-5.8.0.3-3OkzEeUKATkEGDdUZR1g6G.a */,
|
||||
5C0EA1232C08ADAF00AD2E5E /* libffi.a */,
|
||||
5C0EA1222C08ADAF00AD2E5E /* libgmp.a */,
|
||||
5C0EA1262C08ADAF00AD2E5E /* libgmpxx.a */,
|
||||
5C0EA1242C08ADAF00AD2E5E /* libHSsimplex-chat-5.8.0.4-2QdMx3VWkTS77pHWzbDqa5-ghc9.6.3.a */,
|
||||
5C0EA1252C08ADAF00AD2E5E /* libHSsimplex-chat-5.8.0.4-2QdMx3VWkTS77pHWzbDqa5.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 = 220;
|
||||
CURRENT_PROJECT_VERSION = 221;
|
||||
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 = 220;
|
||||
CURRENT_PROJECT_VERSION = 221;
|
||||
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 = 220;
|
||||
CURRENT_PROJECT_VERSION = 221;
|
||||
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 = 220;
|
||||
CURRENT_PROJECT_VERSION = 221;
|
||||
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 = 220;
|
||||
CURRENT_PROJECT_VERSION = 221;
|
||||
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 = 220;
|
||||
CURRENT_PROJECT_VERSION = 221;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
||||
@@ -82,6 +82,8 @@ public enum ChatCommand {
|
||||
case apiSetMemberSettings(groupId: Int64, groupMemberId: Int64, memberSettings: GroupMemberSettings)
|
||||
case apiContactInfo(contactId: Int64)
|
||||
case apiGroupMemberInfo(groupId: Int64, groupMemberId: Int64)
|
||||
case apiContactQueueInfo(contactId: Int64)
|
||||
case apiGroupMemberQueueInfo(groupId: Int64, groupMemberId: Int64)
|
||||
case apiSwitchContact(contactId: Int64)
|
||||
case apiSwitchGroupMember(groupId: Int64, groupMemberId: Int64)
|
||||
case apiAbortSwitchContact(contactId: Int64)
|
||||
@@ -228,6 +230,8 @@ public enum ChatCommand {
|
||||
case let .apiSetMemberSettings(groupId, groupMemberId, memberSettings): return "/_member settings #\(groupId) \(groupMemberId) \(encodeJSON(memberSettings))"
|
||||
case let .apiContactInfo(contactId): return "/_info @\(contactId)"
|
||||
case let .apiGroupMemberInfo(groupId, groupMemberId): return "/_info #\(groupId) \(groupMemberId)"
|
||||
case let .apiContactQueueInfo(contactId): return "/_queue info @\(contactId)"
|
||||
case let .apiGroupMemberQueueInfo(groupId, groupMemberId): return "/_queue info #\(groupId) \(groupMemberId)"
|
||||
case let .apiSwitchContact(contactId): return "/_switch @\(contactId)"
|
||||
case let .apiSwitchGroupMember(groupId, groupMemberId): return "/_switch #\(groupId) \(groupMemberId)"
|
||||
case let .apiAbortSwitchContact(contactId): return "/_abort switch @\(contactId)"
|
||||
@@ -375,6 +379,8 @@ public enum ChatCommand {
|
||||
case .apiSetMemberSettings: return "apiSetMemberSettings"
|
||||
case .apiContactInfo: return "apiContactInfo"
|
||||
case .apiGroupMemberInfo: return "apiGroupMemberInfo"
|
||||
case .apiContactQueueInfo: return "apiContactQueueInfo"
|
||||
case .apiGroupMemberQueueInfo: return "apiGroupMemberQueueInfo"
|
||||
case .apiSwitchContact: return "apiSwitchContact"
|
||||
case .apiSwitchGroupMember: return "apiSwitchGroupMember"
|
||||
case .apiAbortSwitchContact: return "apiAbortSwitchContact"
|
||||
@@ -516,6 +522,7 @@ public enum ChatResponse: Decodable, Error {
|
||||
case networkConfig(networkConfig: NetCfg)
|
||||
case contactInfo(user: UserRef, contact: Contact, connectionStats_: ConnectionStats?, customUserProfile: Profile?)
|
||||
case groupMemberInfo(user: UserRef, groupInfo: GroupInfo, member: GroupMember, connectionStats_: ConnectionStats?)
|
||||
case queueInfo(user: UserRef, rcvMsgInfo: RcvMsgInfo?, queueInfo: QueueInfo)
|
||||
case contactSwitchStarted(user: UserRef, contact: Contact, connectionStats: ConnectionStats)
|
||||
case groupMemberSwitchStarted(user: UserRef, groupInfo: GroupInfo, member: GroupMember, connectionStats: ConnectionStats)
|
||||
case contactSwitchAborted(user: UserRef, contact: Contact, connectionStats: ConnectionStats)
|
||||
@@ -628,7 +635,7 @@ public enum ChatResponse: Decodable, Error {
|
||||
case sndFileCompleteXFTP(user: UserRef, chatItem: AChatItem, fileTransferMeta: FileTransferMeta)
|
||||
case sndStandaloneFileComplete(user: UserRef, fileTransferMeta: FileTransferMeta, rcvURIs: [String])
|
||||
case sndFileCancelledXFTP(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta)
|
||||
case sndFileError(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta)
|
||||
case sndFileError(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, errorMessage: String)
|
||||
// call events
|
||||
case callInvitation(callInvitation: RcvCallInvitation)
|
||||
case callOffer(user: UserRef, contact: Contact, callType: CallType, offer: WebRTCSession, sharedKey: String?, askConfirmation: Bool)
|
||||
@@ -678,6 +685,7 @@ public enum ChatResponse: Decodable, Error {
|
||||
case .networkConfig: return "networkConfig"
|
||||
case .contactInfo: return "contactInfo"
|
||||
case .groupMemberInfo: return "groupMemberInfo"
|
||||
case .queueInfo: return "queueInfo"
|
||||
case .contactSwitchStarted: return "contactSwitchStarted"
|
||||
case .groupMemberSwitchStarted: return "groupMemberSwitchStarted"
|
||||
case .contactSwitchAborted: return "contactSwitchAborted"
|
||||
@@ -836,6 +844,9 @@ public enum ChatResponse: Decodable, Error {
|
||||
case let .networkConfig(networkConfig): return String(describing: networkConfig)
|
||||
case let .contactInfo(u, contact, connectionStats_, customUserProfile): return withUser(u, "contact: \(String(describing: contact))\nconnectionStats_: \(String(describing: connectionStats_))\ncustomUserProfile: \(String(describing: customUserProfile))")
|
||||
case let .groupMemberInfo(u, groupInfo, member, connectionStats_): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionStats_: \(String(describing: connectionStats_))")
|
||||
case let .queueInfo(u, rcvMsgInfo, queueInfo):
|
||||
let msgInfo = if let info = rcvMsgInfo { encodeJSON(rcvMsgInfo) } else { "none" }
|
||||
return withUser(u, "rcvMsgInfo: \(msgInfo)\nqueueInfo: \(encodeJSON(queueInfo))")
|
||||
case let .contactSwitchStarted(u, contact, connectionStats): return withUser(u, "contact: \(String(describing: contact))\nconnectionStats: \(String(describing: connectionStats))")
|
||||
case let .groupMemberSwitchStarted(u, groupInfo, member, connectionStats): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionStats: \(String(describing: connectionStats))")
|
||||
case let .contactSwitchAborted(u, contact, connectionStats): return withUser(u, "contact: \(String(describing: contact))\nconnectionStats: \(String(describing: connectionStats))")
|
||||
@@ -945,7 +956,7 @@ public enum ChatResponse: Decodable, Error {
|
||||
case let .sndFileCompleteXFTP(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .sndStandaloneFileComplete(u, _, rcvURIs): return withUser(u, String(rcvURIs.count))
|
||||
case let .sndFileCancelledXFTP(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .sndFileError(u, chatItem, _): return withUser(u, String(describing: chatItem))
|
||||
case let .sndFileError(u, chatItem, _, err): return withUser(u, "error: \(String(describing: err))\nchatItem: \(String(describing: chatItem))")
|
||||
case let .callInvitation(inv): return String(describing: inv)
|
||||
case let .callOffer(u, contact, callType, offer, sharedKey, askConfirmation): return withUser(u, "contact: \(contact.id)\ncallType: \(String(describing: callType))\nsharedKey: \(sharedKey ?? "")\naskConfirmation: \(askConfirmation)\noffer: \(String(describing: offer))")
|
||||
case let .callAnswer(u, contact, answer): return withUser(u, "contact: \(contact.id)\nanswer: \(String(describing: answer))")
|
||||
@@ -2170,3 +2181,42 @@ public enum UserNetworkType: String, Codable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct RcvMsgInfo: Codable {
|
||||
var msgId: Int64
|
||||
var msgDeliveryId: Int64
|
||||
var msgDeliveryStatus: String
|
||||
var agentMsgId: Int64
|
||||
var agentMsgMeta: String
|
||||
}
|
||||
|
||||
public struct QueueInfo: Codable {
|
||||
var qiSnd: Bool
|
||||
var qiNtf: Bool
|
||||
var qiSub: QSub?
|
||||
var qiSize: Int
|
||||
var qiMsg: MsgInfo?
|
||||
}
|
||||
|
||||
public struct QSub: Codable {
|
||||
var qSubThread: QSubThread
|
||||
var qDelivered: String?
|
||||
}
|
||||
|
||||
public enum QSubThread: String, Codable {
|
||||
case noSub
|
||||
case subPending
|
||||
case subThread
|
||||
case prohibitSub
|
||||
}
|
||||
|
||||
public struct MsgInfo: Codable {
|
||||
var msgId: String
|
||||
var msgTs: Date
|
||||
var msgType: MsgType
|
||||
}
|
||||
|
||||
public enum MsgType: String, Codable {
|
||||
case message
|
||||
case quota
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ android {
|
||||
"cs",
|
||||
"de",
|
||||
"es",
|
||||
"fa",
|
||||
"fi",
|
||||
"fr",
|
||||
"hu",
|
||||
|
||||
+72
@@ -931,6 +931,20 @@ object ChatController {
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiContactQueueInfo(rh: Long?, contactId: Long): Pair<RcvMsgInfo?, QueueInfo>? {
|
||||
val r = sendCmd(rh, CC.APIContactQueueInfo(contactId))
|
||||
if (r is CR.QueueInfoR) return Pair(r.rcvMsgInfo, r.queueInfo)
|
||||
apiErrorAlert("apiContactQueueInfo", generalGetString(MR.strings.error), r)
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiGroupMemberQueueInfo(rh: Long?, groupId: Long, groupMemberId: Long): Pair<RcvMsgInfo?, QueueInfo>? {
|
||||
val r = sendCmd(rh, CC.APIGroupMemberQueueInfo(groupId, groupMemberId))
|
||||
if (r is CR.QueueInfoR) return Pair(r.rcvMsgInfo, r.queueInfo)
|
||||
apiErrorAlert("apiGroupMemberQueueInfo", generalGetString(MR.strings.error), r)
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiSwitchContact(rh: Long?, contactId: Long): ConnectionStats? {
|
||||
val r = sendCmd(rh, CC.APISwitchContact(contactId))
|
||||
if (r is CR.ContactSwitchStarted) return r.connectionStats
|
||||
@@ -2507,6 +2521,8 @@ sealed class CC {
|
||||
class ApiSetMemberSettings(val groupId: Long, val groupMemberId: Long, val memberSettings: GroupMemberSettings): CC()
|
||||
class APIContactInfo(val contactId: Long): CC()
|
||||
class APIGroupMemberInfo(val groupId: Long, val groupMemberId: Long): CC()
|
||||
class APIContactQueueInfo(val contactId: Long): CC()
|
||||
class APIGroupMemberQueueInfo(val groupId: Long, val groupMemberId: Long): CC()
|
||||
class APISwitchContact(val contactId: Long): CC()
|
||||
class APISwitchGroupMember(val groupId: Long, val groupMemberId: Long): CC()
|
||||
class APIAbortSwitchContact(val contactId: Long): CC()
|
||||
@@ -2652,6 +2668,8 @@ sealed class CC {
|
||||
is ApiSetMemberSettings -> "/_member settings #$groupId $groupMemberId ${json.encodeToString(memberSettings)}"
|
||||
is APIContactInfo -> "/_info @$contactId"
|
||||
is APIGroupMemberInfo -> "/_info #$groupId $groupMemberId"
|
||||
is APIContactQueueInfo -> "/_queue info @$contactId"
|
||||
is APIGroupMemberQueueInfo -> "/_queue info #$groupId $groupMemberId"
|
||||
is APISwitchContact -> "/_switch @$contactId"
|
||||
is APISwitchGroupMember -> "/_switch #$groupId $groupMemberId"
|
||||
is APIAbortSwitchContact -> "/_abort switch @$contactId"
|
||||
@@ -2790,6 +2808,8 @@ sealed class CC {
|
||||
is ApiSetMemberSettings -> "apiSetMemberSettings"
|
||||
is APIContactInfo -> "apiContactInfo"
|
||||
is APIGroupMemberInfo -> "apiGroupMemberInfo"
|
||||
is APIContactQueueInfo -> "apiContactQueueInfo"
|
||||
is APIGroupMemberQueueInfo -> "apiGroupMemberQueueInfo"
|
||||
is APISwitchContact -> "apiSwitchContact"
|
||||
is APISwitchGroupMember -> "apiSwitchGroupMember"
|
||||
is APIAbortSwitchContact -> "apiAbortSwitchContact"
|
||||
@@ -4197,6 +4217,7 @@ sealed class CR {
|
||||
@Serializable @SerialName("networkConfig") class NetworkConfig(val networkConfig: NetCfg): CR()
|
||||
@Serializable @SerialName("contactInfo") class ContactInfo(val user: UserRef, val contact: Contact, val connectionStats_: ConnectionStats? = null, val customUserProfile: Profile? = null): CR()
|
||||
@Serializable @SerialName("groupMemberInfo") class GroupMemberInfo(val user: UserRef, val groupInfo: GroupInfo, val member: GroupMember, val connectionStats_: ConnectionStats? = null): CR()
|
||||
@Serializable @SerialName("queueInfo") class QueueInfoR(val user: UserRef, val rcvMsgInfo: RcvMsgInfo?, val queueInfo: QueueInfo): CR()
|
||||
@Serializable @SerialName("contactSwitchStarted") class ContactSwitchStarted(val user: UserRef, val contact: Contact, val connectionStats: ConnectionStats): CR()
|
||||
@Serializable @SerialName("groupMemberSwitchStarted") class GroupMemberSwitchStarted(val user: UserRef, val groupInfo: GroupInfo, val member: GroupMember, val connectionStats: ConnectionStats): CR()
|
||||
@Serializable @SerialName("contactSwitchAborted") class ContactSwitchAborted(val user: UserRef, val contact: Contact, val connectionStats: ConnectionStats): CR()
|
||||
@@ -4368,6 +4389,7 @@ sealed class CR {
|
||||
is NetworkConfig -> "networkConfig"
|
||||
is ContactInfo -> "contactInfo"
|
||||
is GroupMemberInfo -> "groupMemberInfo"
|
||||
is QueueInfoR -> "queueInfo"
|
||||
is ContactSwitchStarted -> "contactSwitchStarted"
|
||||
is GroupMemberSwitchStarted -> "groupMemberSwitchStarted"
|
||||
is ContactSwitchAborted -> "contactSwitchAborted"
|
||||
@@ -4529,6 +4551,7 @@ sealed class CR {
|
||||
is NetworkConfig -> json.encodeToString(networkConfig)
|
||||
is ContactInfo -> withUser(user, "contact: ${json.encodeToString(contact)}\nconnectionStats: ${json.encodeToString(connectionStats_)}")
|
||||
is GroupMemberInfo -> withUser(user, "group: ${json.encodeToString(groupInfo)}\nmember: ${json.encodeToString(member)}\nconnectionStats: ${json.encodeToString(connectionStats_)}")
|
||||
is QueueInfoR -> withUser(user, "rcvMsgInfo: ${json.encodeToString(rcvMsgInfo)}\nqueueInfo: ${json.encodeToString(queueInfo)}\n")
|
||||
is ContactSwitchStarted -> withUser(user, "contact: ${json.encodeToString(contact)}\nconnectionStats: ${json.encodeToString(connectionStats)}")
|
||||
is GroupMemberSwitchStarted -> withUser(user, "group: ${json.encodeToString(groupInfo)}\nmember: ${json.encodeToString(member)}\nconnectionStats: ${json.encodeToString(connectionStats)}")
|
||||
is ContactSwitchAborted -> withUser(user, "contact: ${json.encodeToString(contact)}\nconnectionStats: ${json.encodeToString(connectionStats)}")
|
||||
@@ -5786,3 +5809,52 @@ enum class UserNetworkType {
|
||||
OTHER -> generalGetString(MR.strings.network_type_other)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class RcvMsgInfo (
|
||||
val msgId: Long,
|
||||
val msgDeliveryId: Long,
|
||||
val msgDeliveryStatus: String,
|
||||
val agentMsgId: Long,
|
||||
val agentMsgMeta: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class QueueInfo (
|
||||
val qiSnd: Boolean,
|
||||
val qiNtf: Boolean,
|
||||
val qiSub: QSub? = null,
|
||||
val qiSize: Int,
|
||||
val qiMsg: MsgInfo? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class QSub (
|
||||
val qSubThread: QSubThread,
|
||||
val qDelivered: String? = null
|
||||
)
|
||||
|
||||
enum class QSubThread {
|
||||
@SerialName("noSub")
|
||||
NO_SUB,
|
||||
@SerialName("subPending")
|
||||
SUB_PENDING,
|
||||
@SerialName("subThread")
|
||||
SUB_THREAD,
|
||||
@SerialName("prohibitSub")
|
||||
PROHIBIT_SUB
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class MsgInfo (
|
||||
val msgId: String,
|
||||
val msgTs: Instant,
|
||||
val msgType: MsgType,
|
||||
)
|
||||
|
||||
enum class MsgType {
|
||||
@SerialName("message")
|
||||
MESSAGE,
|
||||
@SerialName("quota")
|
||||
QUOTA
|
||||
}
|
||||
|
||||
+20
@@ -42,6 +42,7 @@ import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.serialization.encodeToString
|
||||
import java.io.File
|
||||
|
||||
@Composable
|
||||
@@ -418,6 +419,19 @@ fun ChatInfoLayout(
|
||||
SectionView(title = stringResource(MR.strings.section_title_for_console)) {
|
||||
InfoRow(stringResource(MR.strings.info_row_local_name), chat.chatInfo.localDisplayName)
|
||||
InfoRow(stringResource(MR.strings.info_row_database_id), chat.chatInfo.apiId.toString())
|
||||
SectionItemView({
|
||||
withBGApi {
|
||||
val info = controller.apiContactQueueInfo(chat.remoteHostId, chat.chatInfo.apiId)
|
||||
if (info != null) {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(MR.strings.message_queue_info),
|
||||
text = queueInfoText(info)
|
||||
)
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Text(stringResource(MR.strings.info_row_debug_delivery))
|
||||
}
|
||||
}
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
@@ -798,6 +812,12 @@ fun showSyncConnectionForceAlert(syncConnectionForce: () -> Unit) {
|
||||
)
|
||||
}
|
||||
|
||||
fun queueInfoText(info: Pair<RcvMsgInfo?, QueueInfo>): String {
|
||||
val (rcvMsgInfo, qInfo) = info
|
||||
val msgInfo: String = if (rcvMsgInfo != null) json.encodeToString(rcvMsgInfo) else generalGetString(MR.strings.message_queue_info_none)
|
||||
return generalGetString(MR.strings.message_queue_info_server_info).format(json.encodeToString(qInfo), msgInfo)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewChatInfoLayout() {
|
||||
|
||||
+2
-1
@@ -12,6 +12,7 @@ import androidx.compose.runtime.saveable.mapSaver
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.*
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.draw.drawWithCache
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.platform.*
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
@@ -620,7 +621,7 @@ fun ChatLayout(
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colors.background)
|
||||
.then(if (wallpaperImage != null)
|
||||
Modifier.drawBehind { chatViewBackground(wallpaperImage, wallpaperType, backgroundColor, tintColor) }
|
||||
Modifier.drawWithCache { chatViewBackground(wallpaperImage, wallpaperType, backgroundColor, tintColor) }
|
||||
else
|
||||
Modifier)
|
||||
.padding(contentPadding)
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ fun getContactsToAdd(chatModel: ChatModel, search: String): List<Contact> {
|
||||
.map { it.chatInfo }
|
||||
.filterIsInstance<ChatInfo.Direct>()
|
||||
.map { it.contact }
|
||||
.filter { c -> c.ready && c.active && c.contactId !in memberContactIds && c.chatViewName.lowercase().contains(s) }
|
||||
.filter { c -> c.sendMsgEnabled && !c.nextSendGrpInv && c.contactId !in memberContactIds && c.chatViewName.lowercase().contains(s) }
|
||||
.sortedBy { it.displayName.lowercase() }
|
||||
.toList()
|
||||
}
|
||||
|
||||
+18
@@ -3,6 +3,7 @@ package chat.simplex.common.views.chat.group
|
||||
import InfoRow
|
||||
import SectionBottomSpacer
|
||||
import SectionDividerSpaced
|
||||
import SectionItemView
|
||||
import SectionSpacer
|
||||
import SectionTextFooter
|
||||
import SectionView
|
||||
@@ -27,6 +28,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.controller
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
@@ -58,6 +60,7 @@ fun GroupMemberInfoView(
|
||||
if (chat != null) {
|
||||
val newRole = remember { mutableStateOf(member.memberRole) }
|
||||
GroupMemberInfoLayout(
|
||||
rhId = rhId,
|
||||
groupInfo,
|
||||
member,
|
||||
connStats,
|
||||
@@ -219,6 +222,7 @@ fun removeMemberDialog(rhId: Long?, groupInfo: GroupInfo, member: GroupMember, c
|
||||
|
||||
@Composable
|
||||
fun GroupMemberInfoLayout(
|
||||
rhId: Long?,
|
||||
groupInfo: GroupInfo,
|
||||
member: GroupMember,
|
||||
connStats: MutableState<ConnectionStats?>,
|
||||
@@ -397,6 +401,19 @@ fun GroupMemberInfoLayout(
|
||||
SectionView(title = stringResource(MR.strings.section_title_for_console)) {
|
||||
InfoRow(stringResource(MR.strings.info_row_local_name), member.localDisplayName)
|
||||
InfoRow(stringResource(MR.strings.info_row_database_id), member.groupMemberId.toString())
|
||||
SectionItemView({
|
||||
withBGApi {
|
||||
val info = controller.apiGroupMemberQueueInfo(rhId, groupInfo.apiId, member.groupMemberId)
|
||||
if (info != null) {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(MR.strings.message_queue_info),
|
||||
text = queueInfoText(info)
|
||||
)
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Text(stringResource(MR.strings.info_row_debug_delivery))
|
||||
}
|
||||
}
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
@@ -644,6 +661,7 @@ fun blockMemberForAll(rhId: Long?, gInfo: GroupInfo, member: GroupMember, blocke
|
||||
fun PreviewGroupMemberInfoLayout() {
|
||||
SimpleXTheme {
|
||||
GroupMemberInfoLayout(
|
||||
rhId = null,
|
||||
groupInfo = GroupInfo.sampleData,
|
||||
member = GroupMember.sampleData,
|
||||
connStats = remember { mutableStateOf(null) },
|
||||
|
||||
+1
-7
@@ -154,13 +154,7 @@ fun CIFileView(
|
||||
FileProtocol.SMP -> progressIndicator()
|
||||
FileProtocol.LOCAL -> {}
|
||||
}
|
||||
is CIFileStatus.SndComplete -> {
|
||||
if ((file.forwardingAllowed() || (chatModel.connectedToRemote() && CIFile.cachedRemoteFileRequests[file.fileSource] == true))) {
|
||||
fileIcon()
|
||||
} else {
|
||||
fileIcon(innerIcon = painterResource(MR.images.ic_check_filled))
|
||||
}
|
||||
}
|
||||
is CIFileStatus.SndComplete -> fileIcon(innerIcon = painterResource(MR.images.ic_check_filled))
|
||||
is CIFileStatus.SndCancelled -> fileIcon(innerIcon = painterResource(MR.images.ic_close))
|
||||
is CIFileStatus.SndError -> fileIcon(innerIcon = painterResource(MR.images.ic_close))
|
||||
is CIFileStatus.RcvInvitation ->
|
||||
|
||||
+2
@@ -537,6 +537,7 @@ suspend fun exportChatArchive(
|
||||
if (!m.chatDbChanged.value) {
|
||||
controller.apiSaveAppSettings(AppSettings.current.prepareForExport())
|
||||
}
|
||||
wallpapersDir.mkdirs()
|
||||
m.controller.apiExportArchive(config)
|
||||
if (storagePath == null) {
|
||||
deleteOldArchive(m)
|
||||
@@ -592,6 +593,7 @@ private fun importArchive(
|
||||
withLongRunningApi {
|
||||
try {
|
||||
m.controller.apiDeleteStorage()
|
||||
wallpapersDir.mkdirs()
|
||||
try {
|
||||
val config = ArchiveConfig(archivePath, parentTempDirectory = databaseExportDir.toString())
|
||||
val archiveErrors = m.controller.apiImportArchive(config)
|
||||
|
||||
+75
-45
@@ -1,12 +1,13 @@
|
||||
package chat.simplex.common.views.helpers
|
||||
|
||||
import androidx.compose.ui.draw.CacheDrawScope
|
||||
import androidx.compose.ui.draw.DrawResult
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.clipRect
|
||||
import androidx.compose.ui.graphics.drawscope.*
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.*
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
@@ -352,9 +353,17 @@ sealed class WallpaperType {
|
||||
}
|
||||
}
|
||||
|
||||
fun DrawScope.chatViewBackground(image: ImageBitmap, imageType: WallpaperType, background: Color, tint: Color) = clipRect {
|
||||
val quality = FilterQuality.High
|
||||
fun repeat(imageScale: Float) {
|
||||
private fun drawToBitmap(image: ImageBitmap, imageScale: Float, tint: Color, size: Size, density: Float, layoutDirection: LayoutDirection): ImageBitmap {
|
||||
val quality = if (appPlatform.isAndroid) FilterQuality.High else FilterQuality.Low
|
||||
val drawScope = CanvasDrawScope()
|
||||
val bitmap = ImageBitmap(size.width.toInt(), size.height.toInt())
|
||||
val canvas = Canvas(bitmap)
|
||||
drawScope.draw(
|
||||
density = Density(density),
|
||||
layoutDirection = layoutDirection,
|
||||
canvas = canvas,
|
||||
size = size,
|
||||
) {
|
||||
val scale = imageScale * density
|
||||
for (h in 0..(size.height / image.height / scale).roundToInt()) {
|
||||
for (w in 0..(size.width / image.width / scale).roundToInt()) {
|
||||
@@ -368,50 +377,71 @@ fun DrawScope.chatViewBackground(image: ImageBitmap, imageType: WallpaperType, b
|
||||
}
|
||||
}
|
||||
}
|
||||
return bitmap
|
||||
}
|
||||
|
||||
drawRect(background)
|
||||
when (imageType) {
|
||||
is WallpaperType.Preset -> repeat((imageType.scale ?: 1f) * imageType.predefinedImageScale)
|
||||
is WallpaperType.Image -> when (val scaleType = imageType.scaleType ?: WallpaperScaleType.FILL) {
|
||||
WallpaperScaleType.REPEAT -> repeat(imageType.scale ?: 1f)
|
||||
WallpaperScaleType.FILL, WallpaperScaleType.FIT -> {
|
||||
val scale = scaleType.contentScale.computeScaleFactor(Size(image.width.toFloat(), image.height.toFloat()), Size(size.width, size.height))
|
||||
val scaledWidth = (image.width * scale.scaleX).roundToInt()
|
||||
val scaledHeight = (image.height * scale.scaleY).roundToInt()
|
||||
// Large image will cause freeze
|
||||
if (image.width > 4320 || image.height > 4320) return@clipRect
|
||||
fun CacheDrawScope.chatViewBackground(image: ImageBitmap, imageType: WallpaperType, background: Color, tint: Color): DrawResult {
|
||||
val imageScale = if (imageType is WallpaperType.Preset) {
|
||||
(imageType.scale ?: 1f) * imageType.predefinedImageScale
|
||||
} else if (imageType is WallpaperType.Image && imageType.scaleType == WallpaperScaleType.REPEAT) {
|
||||
imageType.scale ?: 1f
|
||||
} else {
|
||||
1f
|
||||
}
|
||||
val image = if (imageType is WallpaperType.Preset || (imageType is WallpaperType.Image && imageType.scaleType == WallpaperScaleType.REPEAT)) {
|
||||
drawToBitmap(image, imageScale, tint, size, density, layoutDirection)
|
||||
} else {
|
||||
image
|
||||
}
|
||||
|
||||
drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
if (scaleType == WallpaperScaleType.FIT) {
|
||||
if (scaledWidth < size.width) {
|
||||
// has black lines at left and right sides
|
||||
var x = (size.width - scaledWidth) / 2
|
||||
while (x > 0) {
|
||||
drawImage(image, dstOffset = IntOffset(x = (x - scaledWidth).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
x -= scaledWidth
|
||||
}
|
||||
x = size.width - (size.width - scaledWidth) / 2
|
||||
while (x < size.width) {
|
||||
drawImage(image, dstOffset = IntOffset(x = x.roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
x += scaledWidth
|
||||
}
|
||||
} else {
|
||||
// has black lines at top and bottom sides
|
||||
var y = (size.height - scaledHeight) / 2
|
||||
while (y > 0) {
|
||||
drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = (y - scaledHeight).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
y -= scaledHeight
|
||||
}
|
||||
y = size.height - (size.height - scaledHeight) / 2
|
||||
while (y < size.height) {
|
||||
drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = y.roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
y += scaledHeight
|
||||
return onDrawBehind {
|
||||
val quality = if (appPlatform.isAndroid) FilterQuality.High else FilterQuality.Low
|
||||
drawRect(background)
|
||||
when (imageType) {
|
||||
is WallpaperType.Preset -> drawImage(image)
|
||||
is WallpaperType.Image -> when (val scaleType = imageType.scaleType ?: WallpaperScaleType.FILL) {
|
||||
WallpaperScaleType.REPEAT -> drawImage(image)
|
||||
WallpaperScaleType.FILL, WallpaperScaleType.FIT -> {
|
||||
clipRect {
|
||||
val scale = scaleType.contentScale.computeScaleFactor(Size(image.width.toFloat(), image.height.toFloat()), Size(size.width, size.height))
|
||||
val scaledWidth = (image.width * scale.scaleX).roundToInt()
|
||||
val scaledHeight = (image.height * scale.scaleY).roundToInt()
|
||||
// Large image will cause freeze
|
||||
if (image.width > 4320 || image.height > 4320) return@clipRect
|
||||
|
||||
drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
if (scaleType == WallpaperScaleType.FIT) {
|
||||
if (scaledWidth < size.width) {
|
||||
// has black lines at left and right sides
|
||||
var x = (size.width - scaledWidth) / 2
|
||||
while (x > 0) {
|
||||
drawImage(image, dstOffset = IntOffset(x = (x - scaledWidth).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
x -= scaledWidth
|
||||
}
|
||||
x = size.width - (size.width - scaledWidth) / 2
|
||||
while (x < size.width) {
|
||||
drawImage(image, dstOffset = IntOffset(x = x.roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
x += scaledWidth
|
||||
}
|
||||
} else {
|
||||
// has black lines at top and bottom sides
|
||||
var y = (size.height - scaledHeight) / 2
|
||||
while (y > 0) {
|
||||
drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = (y - scaledHeight).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
y -= scaledHeight
|
||||
}
|
||||
y = size.height - (size.height - scaledHeight) / 2
|
||||
while (y < size.height) {
|
||||
drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = y.roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||
y += scaledHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
drawRect(tint)
|
||||
}
|
||||
drawRect(tint)
|
||||
}
|
||||
is WallpaperType.Empty -> {}
|
||||
}
|
||||
is WallpaperType.Empty -> {}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-1
@@ -1,6 +1,5 @@
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -13,12 +12,15 @@ import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.*
|
||||
import chat.simplex.common.model.NotificationsMode
|
||||
import chat.simplex.common.platform.onRightClick
|
||||
import chat.simplex.common.platform.windowWidth
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.onboarding.SelectableCard
|
||||
import chat.simplex.common.views.usersettings.SettingsActionItemWithContent
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
|
||||
@Composable
|
||||
fun SectionView(title: String? = null, padding: PaddingValues = PaddingValues(), content: (@Composable ColumnScope.() -> Unit)) {
|
||||
@@ -76,6 +78,26 @@ fun <T> SectionViewSelectable(
|
||||
SectionTextFooter(values.first { it.value == currentValue.value }.description)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T> SectionViewSelectableCards(
|
||||
title: String?,
|
||||
currentValue: State<T>,
|
||||
values: List<ValueTitleDesc<T>>,
|
||||
onSelected: (T) -> Unit,
|
||||
) {
|
||||
SectionView(title) {
|
||||
Column(Modifier.padding(horizontal = DEFAULT_PADDING)) {
|
||||
if (title != null) {
|
||||
Text(title, Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
|
||||
Spacer(Modifier.height(DEFAULT_PADDING * 2f))
|
||||
}
|
||||
values.forEach { item ->
|
||||
SelectableCard(currentValue, item.value, item.title, item.description, onSelected)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SectionItemView(
|
||||
click: (() -> Unit)? = null,
|
||||
|
||||
+1
@@ -594,6 +594,7 @@ private fun MutableState<MigrationToState?>.importArchive(archivePath: String, n
|
||||
chatInitControllerRemovingDatabases()
|
||||
}
|
||||
controller.apiDeleteStorage()
|
||||
wallpapersDir.mkdirs()
|
||||
try {
|
||||
val config = ArchiveConfig(archivePath, parentTempDirectory = databaseExportDir.toString())
|
||||
val archiveErrors = controller.apiImportArchive(config)
|
||||
|
||||
+17
-10
@@ -8,6 +8,7 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
@@ -35,9 +36,15 @@ fun SetNotificationsMode(m: ChatModel) {
|
||||
Column(Modifier.padding(horizontal = DEFAULT_PADDING * 1f)) {
|
||||
Text(stringResource(MR.strings.onboarding_notifications_mode_subtitle), Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
|
||||
Spacer(Modifier.height(DEFAULT_PADDING * 2f))
|
||||
NotificationButton(currentMode, NotificationsMode.OFF, MR.strings.onboarding_notifications_mode_off, MR.strings.onboarding_notifications_mode_off_desc)
|
||||
NotificationButton(currentMode, NotificationsMode.PERIODIC, MR.strings.onboarding_notifications_mode_periodic, MR.strings.onboarding_notifications_mode_periodic_desc)
|
||||
NotificationButton(currentMode, NotificationsMode.SERVICE, MR.strings.onboarding_notifications_mode_service, MR.strings.onboarding_notifications_mode_service_desc)
|
||||
SelectableCard(currentMode, NotificationsMode.OFF, stringResource(MR.strings.onboarding_notifications_mode_off), annotatedStringResource(MR.strings.onboarding_notifications_mode_off_desc)) {
|
||||
currentMode.value = NotificationsMode.OFF
|
||||
}
|
||||
SelectableCard(currentMode, NotificationsMode.PERIODIC, stringResource(MR.strings.onboarding_notifications_mode_periodic), annotatedStringResource(MR.strings.onboarding_notifications_mode_periodic_desc)){
|
||||
currentMode.value = NotificationsMode.PERIODIC
|
||||
}
|
||||
SelectableCard(currentMode, NotificationsMode.SERVICE, stringResource(MR.strings.onboarding_notifications_mode_service), annotatedStringResource(MR.strings.onboarding_notifications_mode_service_desc)){
|
||||
currentMode.value = NotificationsMode.SERVICE
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.fillMaxHeight().weight(1f))
|
||||
Box(Modifier.fillMaxWidth().padding(bottom = DEFAULT_PADDING_HALF), contentAlignment = Alignment.Center) {
|
||||
@@ -54,22 +61,22 @@ fun SetNotificationsMode(m: ChatModel) {
|
||||
expect fun SetNotificationsModeAdditions()
|
||||
|
||||
@Composable
|
||||
private fun NotificationButton(currentMode: MutableState<NotificationsMode>, mode: NotificationsMode, title: StringResource, description: StringResource) {
|
||||
fun <T> SelectableCard(currentValue: State<T>, newValue: T, title: String, description: AnnotatedString, onSelected: (T) -> Unit) {
|
||||
TextButton(
|
||||
onClick = { currentMode.value = mode },
|
||||
border = BorderStroke(1.dp, color = if (currentMode.value == mode) MaterialTheme.colors.primary else MaterialTheme.colors.secondary.copy(alpha = 0.5f)),
|
||||
onClick = { onSelected(newValue) },
|
||||
border = BorderStroke(1.dp, color = if (currentValue.value == newValue) MaterialTheme.colors.primary else MaterialTheme.colors.secondary.copy(alpha = 0.5f)),
|
||||
shape = RoundedCornerShape(35.dp),
|
||||
) {
|
||||
Column(Modifier.padding(horizontal = 10.dp).padding(top = 4.dp, bottom = 8.dp)) {
|
||||
Column(Modifier.padding(horizontal = 10.dp).padding(top = 4.dp, bottom = 8.dp).fillMaxWidth()) {
|
||||
Text(
|
||||
stringResource(title),
|
||||
title,
|
||||
style = MaterialTheme.typography.h3,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = if (currentMode.value == mode) MaterialTheme.colors.primary else MaterialTheme.colors.secondary,
|
||||
color = if (currentValue.value == newValue) MaterialTheme.colors.primary else MaterialTheme.colors.secondary,
|
||||
modifier = Modifier.padding(bottom = 8.dp).align(Alignment.CenterHorizontally),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Text(annotatedStringResource(description),
|
||||
Text(description,
|
||||
Modifier.align(Alignment.CenterHorizontally),
|
||||
fontSize = 15.sp,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
|
||||
+31
-7
@@ -271,7 +271,6 @@ private val versionDescriptions: List<VersionDescription> = listOf(
|
||||
icon = MR.images.ic_translate,
|
||||
titleId = MR.strings.v4_5_italian_interface,
|
||||
descrId = MR.strings.v4_5_italian_interface_descr,
|
||||
link = "https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat"
|
||||
)
|
||||
)
|
||||
),
|
||||
@@ -308,7 +307,6 @@ private val versionDescriptions: List<VersionDescription> = listOf(
|
||||
icon = MR.images.ic_translate,
|
||||
titleId = MR.strings.v4_6_chinese_spanish_interface,
|
||||
descrId = MR.strings.v4_6_chinese_spanish_interface_descr,
|
||||
link = "https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat"
|
||||
)
|
||||
)
|
||||
),
|
||||
@@ -330,7 +328,6 @@ private val versionDescriptions: List<VersionDescription> = listOf(
|
||||
icon = MR.images.ic_translate,
|
||||
titleId = MR.strings.v5_0_polish_interface,
|
||||
descrId = MR.strings.v5_0_polish_interface_descr,
|
||||
link = "https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat"
|
||||
)
|
||||
)
|
||||
),
|
||||
@@ -362,7 +359,6 @@ private val versionDescriptions: List<VersionDescription> = listOf(
|
||||
icon = MR.images.ic_translate,
|
||||
titleId = MR.strings.v5_1_japanese_portuguese_interface,
|
||||
descrId = MR.strings.whats_new_thanks_to_users_contribute_weblate,
|
||||
link = "https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat"
|
||||
)
|
||||
)
|
||||
),
|
||||
@@ -427,7 +423,6 @@ private val versionDescriptions: List<VersionDescription> = listOf(
|
||||
icon = MR.images.ic_translate,
|
||||
titleId = MR.strings.v5_3_new_interface_languages,
|
||||
descrId = MR.strings.v5_3_new_interface_languages_descr,
|
||||
link = "https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat"
|
||||
)
|
||||
)
|
||||
),
|
||||
@@ -491,7 +486,6 @@ private val versionDescriptions: List<VersionDescription> = listOf(
|
||||
icon = MR.images.ic_translate,
|
||||
titleId = MR.strings.v5_5_new_interface_languages,
|
||||
descrId = MR.strings.whats_new_thanks_to_users_contribute_weblate,
|
||||
link = "https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat"
|
||||
)
|
||||
)
|
||||
),
|
||||
@@ -554,7 +548,37 @@ private val versionDescriptions: List<VersionDescription> = listOf(
|
||||
icon = MR.images.ic_translate,
|
||||
titleId = MR.strings.v5_7_new_interface_languages,
|
||||
descrId = MR.strings.whats_new_thanks_to_users_contribute_weblate,
|
||||
link = "https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat"
|
||||
)
|
||||
)
|
||||
),
|
||||
VersionDescription(
|
||||
version = "v5.8",
|
||||
post = "https://simplex.chat/blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.html",
|
||||
features = listOf(
|
||||
FeatureDescription(
|
||||
icon = MR.images.ic_settings_ethernet,
|
||||
titleId = MR.strings.v5_8_private_routing,
|
||||
descrId = MR.strings.v5_8_private_routing_descr
|
||||
),
|
||||
FeatureDescription(
|
||||
icon = MR.images.ic_palette,
|
||||
titleId = MR.strings.v5_8_chat_themes,
|
||||
descrId = MR.strings.v5_8_chat_themes_descr
|
||||
),
|
||||
FeatureDescription(
|
||||
icon = MR.images.ic_security,
|
||||
titleId = MR.strings.v5_8_safe_files,
|
||||
descrId = MR.strings.v5_8_safe_files_descr
|
||||
),
|
||||
FeatureDescription(
|
||||
icon = MR.images.ic_battery_3_bar,
|
||||
titleId = MR.strings.v5_8_message_delivery,
|
||||
descrId = MR.strings.v5_8_message_delivery_descr
|
||||
),
|
||||
FeatureDescription(
|
||||
icon = MR.images.ic_translate,
|
||||
titleId = MR.strings.v5_8_persian_ui,
|
||||
descrId = MR.strings.whats_new_thanks_to_users_contribute_weblate
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
+5
-2
@@ -95,11 +95,13 @@ object AppearanceScope {
|
||||
val backgroundColor = backgroundColor ?: wallpaperType?.defaultBackgroundColor(theme, MaterialTheme.colors.background)
|
||||
val tintColor = tintColor ?: wallpaperType?.defaultTintColor(theme)
|
||||
Column(Modifier
|
||||
.drawBehind {
|
||||
.drawWithCache {
|
||||
if (wallpaperImage != null && wallpaperType != null && backgroundColor != null && tintColor != null) {
|
||||
chatViewBackground(wallpaperImage, wallpaperType, backgroundColor, tintColor)
|
||||
} else {
|
||||
drawRect(themeBackgroundColor)
|
||||
onDrawBehind {
|
||||
drawRect(themeBackgroundColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(DEFAULT_PADDING_HALF)
|
||||
@@ -887,6 +889,7 @@ object AppearanceScope {
|
||||
"cs" to "Čeština",
|
||||
"de" to "Deutsch",
|
||||
"es" to "Español",
|
||||
"fa" to "فارسی",
|
||||
"fi" to "Suomi",
|
||||
"fr" to "Français",
|
||||
"hu" to "Magyar",
|
||||
|
||||
+3
-2
@@ -7,6 +7,7 @@ import SectionItemView
|
||||
import SectionItemWithValue
|
||||
import SectionView
|
||||
import SectionViewSelectable
|
||||
import SectionViewSelectableCards
|
||||
import TextIconSpaced
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
@@ -555,7 +556,7 @@ private fun SMPProxyModePicker(
|
||||
Modifier.fillMaxWidth(),
|
||||
) {
|
||||
AppBarTitle(stringResource(MR.strings.network_smp_proxy_mode_private_routing))
|
||||
SectionViewSelectable(null, smpProxyMode, values, updateSMPProxyMode)
|
||||
SectionViewSelectableCards(null, smpProxyMode, values, updateSMPProxyMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -592,7 +593,7 @@ private fun SMPProxyFallbackPicker(
|
||||
Modifier.fillMaxWidth(),
|
||||
) {
|
||||
AppBarTitle(stringResource(MR.strings.network_smp_proxy_fallback_allow_downgrade))
|
||||
SectionViewSelectable(null, smpProxyFallback, values, updateSMPProxyFallback)
|
||||
SectionViewSelectableCards(null, smpProxyFallback, values, updateSMPProxyFallback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1401,6 +1401,7 @@
|
||||
<string name="section_title_for_console">FOR CONSOLE</string>
|
||||
<string name="info_row_local_name">Local name</string>
|
||||
<string name="info_row_database_id">Database ID</string>
|
||||
<string name="info_row_debug_delivery">Debug delivery</string>
|
||||
<string name="info_row_updated_at">Record updated at</string>
|
||||
<string name="info_row_sent_at">Sent at</string>
|
||||
<string name="info_row_created_at">Created at</string>
|
||||
@@ -1462,6 +1463,9 @@
|
||||
<string name="info_row_connection">Connection</string>
|
||||
<string name="conn_level_desc_direct">direct</string>
|
||||
<string name="conn_level_desc_indirect">indirect (%1$s)</string>
|
||||
<string name="message_queue_info">Message queue info</string>
|
||||
<string name="message_queue_info_none">none</string>
|
||||
<string name="message_queue_info_server_info">server queue info: %1$s\n\nlast received msg: %2$s</string>
|
||||
|
||||
<!-- GroupWelcomeView.kt -->
|
||||
<string name="group_welcome_title">Welcome message</string>
|
||||
@@ -1850,6 +1854,15 @@
|
||||
<string name="v5_7_network">Network management</string>
|
||||
<string name="v5_7_network_descr">More reliable network connection.</string>
|
||||
<string name="v5_7_new_interface_languages">Lithuanian UI</string>
|
||||
<string name="v5_8_private_routing">Private message routing 🚀</string>
|
||||
<string name="v5_8_private_routing_descr">Protect your IP address from the messaging relays chosen by your contacts.\nEnable in *Network & servers* settings.</string>
|
||||
<string name="v5_8_chat_themes">New chat themes</string>
|
||||
<string name="v5_8_chat_themes_descr">Make your chats look different!</string>
|
||||
<string name="v5_8_safe_files">Safely receive files</string>
|
||||
<string name="v5_8_safe_files_descr">Confirm files from unknown servers.</string>
|
||||
<string name="v5_8_message_delivery">Improved message delivery</string>
|
||||
<string name="v5_8_message_delivery_descr">With reduced battery usage.</string>
|
||||
<string name="v5_8_persian_ui">Persian UI</string>
|
||||
|
||||
<!-- CustomTimePicker -->
|
||||
<string name="custom_time_unit_seconds">seconds</string>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" fill="#5f6368">
|
||||
<path
|
||||
d="M480-85q-81 0-153.05-31.16-72.05-31.16-125.84-84.95-53.79-53.79-84.95-125.84Q85-399 85-480.01 85-564 116.75-636q31.75-72 86.5-125.25t128.25-83.5Q405-875 488.8-875q78.41 0 148.31 26.25Q707-822.5 759.99-776.37q52.99 46.13 84 110Q875-602.5 875-527q0 106.5-61.75 168t-164.66 61.5H574.5q-19 0-32.5 14.25T528.5-251q0 27.45 14.5 47.22 14.5 19.78 14.5 45.28 0 34-19.75 53.75T480-85Zm0-395Zm-231.5 24.5q19.7 0 34.1-14.4Q297-484.3 297-504q0-19.7-14.4-34.1-14.4-14.4-34.1-14.4-19.7 0-34.1 14.4Q200-523.7 200-504q0 19.7 14.4 34.1 14.4 14.4 34.1 14.4Zm125.5-169q19.7 0 34.1-14.4 14.4-14.4 14.4-34.1 0-19.7-14.4-34.1-14.4-14.4-34.1-14.4-19.7 0-34.1 14.4-14.4 14.4-14.4 34.1 0 19.7 14.4 34.1 14.4 14.4 34.1 14.4Zm212.5 0q19.7 0 34.1-14.4Q635-653.3 635-673q0-19.7-14.4-34.1-14.4-14.4-34.1-14.4-19.7 0-34.1 14.4Q538-692.7 538-673q0 19.7 14.4 34.1 14.4 14.4 34.1 14.4Zm130 169q19.7 0 34.1-14.4Q765-484.3 765-504q0-19.7-14.4-34.1-14.4-14.4-34.1-14.4-19.7 0-34.1 14.4Q668-523.7 668-504q0 19.7 14.4 34.1 14.4 14.4 34.1 14.4Zm-236.34 313q10.84 0 15.34-4.5t4.5-14.5q0-14-14.25-26.25T471.5-240.5q0-45.5 29.75-80T576.5-355h72q76 0 122.5-44.25T817.5-527q0-131-99.25-210.75T488.91-817.5q-144.91 0-245.66 98.05Q142.5-621.41 142.5-480q0 140 98.75 238.75t238.91 98.75Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -115,6 +115,7 @@ private fun ApplicationScope.AppWindow(closedByError: MutableState<Boolean>) {
|
||||
false
|
||||
}
|
||||
}, title = "SimpleX") {
|
||||
// val hardwareAccelerationDisabled = remember { listOf(GraphicsApi.SOFTWARE_FAST, GraphicsApi.SOFTWARE_COMPAT, GraphicsApi.UNKNOWN).contains(window.renderApi) }
|
||||
simplexWindowState.window = window
|
||||
AppScreen()
|
||||
if (simplexWindowState.openDialog.isAwaiting) {
|
||||
|
||||
@@ -3,7 +3,6 @@ package chat.simplex.desktop
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.AnimationVector1D
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.interaction.*
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
@@ -17,6 +16,8 @@ import kotlinx.coroutines.*
|
||||
import java.io.File
|
||||
|
||||
fun main() {
|
||||
// Disable hardware acceleration
|
||||
//System.setProperty("skiko.renderApi", "SOFTWARE")
|
||||
initHaskell()
|
||||
runMigrations()
|
||||
initApp()
|
||||
|
||||
@@ -26,11 +26,11 @@ android.enableJetifier=true
|
||||
kotlin.mpp.androidSourceSetLayoutVersion=2
|
||||
kotlin.jvm.target=11
|
||||
|
||||
android.version_name=5.8-beta.3
|
||||
android.version_code=214
|
||||
android.version_name=5.8-beta.4
|
||||
android.version_code=215
|
||||
|
||||
desktop.version_name=5.8-beta.3
|
||||
desktop.version_code=49
|
||||
desktop.version_name=5.8-beta.4
|
||||
desktop.version_code=50
|
||||
|
||||
kotlin.version=1.9.23
|
||||
gradle.plugin.version=8.2.0
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
layout: layouts/article.html
|
||||
title: "SimpleX network: private message routing, v5.8 released with IP address protection and chat themes"
|
||||
date: 2024-06-04
|
||||
# previewBody: blog_previews/20240426.html
|
||||
draft: true
|
||||
# image: images/20240426-profile.png
|
||||
# imageBottom: true
|
||||
permalink: "/blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.html"
|
||||
---
|
||||
|
||||
# SimpleX network: private message routing, v5.8 released with IP address protection and chat themes
|
||||
|
||||
**Published:** June 4, 2024
|
||||
|
||||
TODO
|
||||
|
||||
This is a permalink for release announcement.
|
||||
+1
-1
@@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/simplex-chat/simplexmq.git
|
||||
tag: 0f663bd569f5d282fc13cd969391f30d873d73a0
|
||||
tag: 2e4f507919dd3a08e5c4106fde193fee4414de3c
|
||||
|
||||
source-repository-package
|
||||
type: git
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
name: simplex-chat
|
||||
version: 5.8.0.4
|
||||
version: 5.8.0.5
|
||||
#synopsis:
|
||||
#description:
|
||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"https://github.com/simplex-chat/simplexmq.git"."0f663bd569f5d282fc13cd969391f30d873d73a0" = "0xv3fjq6acr25hkkpb4grmdj0hls9z5qnq0dx7h7lxck60q0v9w8";
|
||||
"https://github.com/simplex-chat/simplexmq.git"."2e4f507919dd3a08e5c4106fde193fee4414de3c" = "1s035v77rpk8xs8gcfqm2ddp1mf5n35zrkq68gxqr634r2r9vbaj";
|
||||
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
|
||||
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d";
|
||||
"https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ cabal-version: 1.12
|
||||
-- see: https://github.com/sol/hpack
|
||||
|
||||
name: simplex-chat
|
||||
version: 5.8.0.4
|
||||
version: 5.8.0.5
|
||||
category: Web, System, Services, Cryptography
|
||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||
author: simplex.chat
|
||||
|
||||
+47
-10
@@ -94,7 +94,7 @@ import qualified Simplex.FileTransfer.Description as FD
|
||||
import Simplex.FileTransfer.Protocol (FileParty (..), FilePartyI)
|
||||
import qualified Simplex.FileTransfer.Transport as XFTP
|
||||
import Simplex.Messaging.Agent as Agent
|
||||
import Simplex.Messaging.Agent.Client (AgentStatsKey (..), SubInfo (..), agentClientStore, getAgentWorkersDetails, getAgentWorkersSummary, ipAddressProtected, temporaryAgentError, withLockMap)
|
||||
import Simplex.Messaging.Agent.Client (AgentStatsKey (..), SubInfo (..), agentClientStore, getAgentQueuesInfo, getAgentWorkersDetails, getAgentWorkersSummary, getNetworkConfig', ipAddressProtected, temporaryAgentError, withLockMap)
|
||||
import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), InitialAgentServers (..), createAgentStore, defaultAgentConfig)
|
||||
import Simplex.Messaging.Agent.Lock (withLock)
|
||||
import Simplex.Messaging.Agent.Protocol
|
||||
@@ -103,7 +103,7 @@ import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), Migrati
|
||||
import Simplex.Messaging.Agent.Store.SQLite.DB (SlowQueryStats (..))
|
||||
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||
import qualified Simplex.Messaging.Agent.Store.SQLite.Migrations as Migrations
|
||||
import Simplex.Messaging.Client (ProxyClientError (..), defaultNetworkConfig)
|
||||
import Simplex.Messaging.Client (ProxyClientError (..), NetworkConfig (..), defaultNetworkConfig)
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..))
|
||||
import qualified Simplex.Messaging.Crypto.File as CF
|
||||
@@ -218,7 +218,7 @@ newChatController
|
||||
ChatDatabase {chatStore, agentStore}
|
||||
user
|
||||
cfg@ChatConfig {agentConfig = aCfg, defaultServers, inlineFiles, deviceNameForRemote}
|
||||
ChatOpts {coreOptions = CoreChatOpts {smpServers, xftpServers, networkConfig, logLevel, logConnections, logServerHosts, logFile, tbqSize, highlyAvailable}, deviceName, optFilesFolder, optTempDirectory, showReactions, allowInstantFiles, autoAcceptFileSize}
|
||||
ChatOpts {coreOptions = CoreChatOpts {smpServers, xftpServers, simpleNetCfg, logLevel, logConnections, logServerHosts, logFile, tbqSize, highlyAvailable}, deviceName, optFilesFolder, optTempDirectory, showReactions, allowInstantFiles, autoAcceptFileSize}
|
||||
backgroundMode = do
|
||||
let inlineFiles' = if allowInstantFiles || autoAcceptFileSize > 0 then inlineFiles else inlineFiles {sendChunks = 0, receiveInstant = False}
|
||||
config = cfg {logLevel, showReactions, tbqSize, subscriptionEvents = logConnections, hostEvents = logServerHosts, defaultServers = configServers, inlineFiles = inlineFiles', autoAcceptFileSize, highlyAvailable}
|
||||
@@ -303,7 +303,7 @@ newChatController
|
||||
let DefaultAgentServers {smp = defSmp, xftp = defXftp} = defaultServers
|
||||
smp' = fromMaybe defSmp (nonEmpty smpServers)
|
||||
xftp' = fromMaybe defXftp (nonEmpty xftpServers)
|
||||
in defaultServers {smp = smp', xftp = xftp', netCfg = networkConfig}
|
||||
in defaultServers {smp = smp', xftp = xftp', netCfg = updateNetworkConfig defaultNetworkConfig simpleNetCfg}
|
||||
agentServers :: ChatConfig -> IO InitialAgentServers
|
||||
agentServers config@ChatConfig {defaultServers = defServers@DefaultAgentServers {ntf, netCfg}} = do
|
||||
users <- withTransaction chatStore getUsers
|
||||
@@ -321,6 +321,13 @@ newChatController
|
||||
userServers :: User -> IO (NonEmpty (ProtoServerWithAuth p))
|
||||
userServers user' = activeAgentServers config protocol <$> withTransaction chatStore (`getProtocolServers` user')
|
||||
|
||||
updateNetworkConfig :: NetworkConfig -> SimpleNetCfg -> NetworkConfig
|
||||
updateNetworkConfig cfg SimpleNetCfg {socksProxy, smpProxyMode_, smpProxyFallback_, tcpTimeout_, logTLSErrors} =
|
||||
let cfg1 = maybe cfg (\smpProxyMode -> cfg {smpProxyMode}) smpProxyMode_
|
||||
cfg2 = maybe cfg1 (\smpProxyFallback -> cfg1 {smpProxyFallback}) smpProxyFallback_
|
||||
cfg3 = maybe cfg2 (\tcpTimeout -> cfg2 {tcpTimeout, tcpConnectTimeout = (tcpTimeout * 3) `div` 2}) tcpTimeout_
|
||||
in cfg3 {socksProxy, logTLSErrors}
|
||||
|
||||
withChatLock :: String -> CM a -> CM a
|
||||
withChatLock name action = asks chatLock >>= \l -> withLock l name action
|
||||
|
||||
@@ -1342,7 +1349,11 @@ processChatCommand' vr = \case
|
||||
processChatCommand $ APIGetChatItemTTL userId
|
||||
APISetNetworkConfig cfg -> withUser' $ \_ -> lift (withAgent' (`setNetworkConfig` cfg)) >> ok_
|
||||
APIGetNetworkConfig -> withUser' $ \_ ->
|
||||
lift $ CRNetworkConfig <$> withAgent' getNetworkConfig
|
||||
CRNetworkConfig <$> lift getNetworkConfig
|
||||
SetNetworkConfig netCfg -> do
|
||||
cfg <- lift getNetworkConfig
|
||||
void . processChatCommand $ APISetNetworkConfig $ updateNetworkConfig cfg netCfg
|
||||
pure $ CRNetworkConfig cfg
|
||||
APISetNetworkInfo info -> lift (withAgent' (`setUserNetworkInfo` info)) >> ok_
|
||||
ReconnectAllServers -> withUser' $ \_ -> lift (withAgent' reconnectAllServers) >> ok_
|
||||
APISetChatSettings (ChatRef cType chatId) chatSettings -> withUser $ \user -> case cType of
|
||||
@@ -1379,6 +1390,11 @@ processChatCommand' vr = \case
|
||||
forM customUserProfileId $ \profileId -> withStore (\db -> getProfileById db userId profileId)
|
||||
connectionStats <- mapM (withAgent . flip getConnectionServers) (contactConnId ct)
|
||||
pure $ CRContactInfo user ct connectionStats (fmap fromLocalProfile incognitoProfile)
|
||||
APIContactQueueInfo contactId -> withUser $ \user -> do
|
||||
ct@Contact {activeConn} <- withStore $ \db -> getContact db vr user contactId
|
||||
case activeConn of
|
||||
Just conn -> getConnQueueInfo user conn
|
||||
Nothing -> throwChatError $ CEContactNotActive ct
|
||||
APIGroupInfo gId -> withUser $ \user -> do
|
||||
(g, s) <- withStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> liftIO (getGroupSummary db user gId)
|
||||
pure $ CRGroupInfo user g s
|
||||
@@ -1386,6 +1402,11 @@ processChatCommand' vr = \case
|
||||
(g, m) <- withStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> getGroupMember db vr user gId gMemberId
|
||||
connectionStats <- mapM (withAgent . flip getConnectionServers) (memberConnId m)
|
||||
pure $ CRGroupMemberInfo user g m connectionStats
|
||||
APIGroupMemberQueueInfo gId gMemberId -> withUser $ \user -> do
|
||||
GroupMember {activeConn} <- withStore $ \db -> getGroupMember db vr user gId gMemberId
|
||||
case activeConn of
|
||||
Just conn -> getConnQueueInfo user conn
|
||||
Nothing -> throwChatError CEGroupMemberNotActive
|
||||
APISwitchContact contactId -> withUser $ \user -> do
|
||||
ct <- withStore $ \db -> getContact db vr user contactId
|
||||
case contactConnId ct of
|
||||
@@ -1497,6 +1518,8 @@ processChatCommand' vr = \case
|
||||
groupId <- withStore $ \db -> getGroupIdByName db user gName
|
||||
processChatCommand $ APIGroupInfo groupId
|
||||
GroupMemberInfo gName mName -> withMemberName gName mName APIGroupMemberInfo
|
||||
ContactQueueInfo cName -> withContactName cName APIContactQueueInfo
|
||||
GroupMemberQueueInfo gName mName -> withMemberName gName mName APIGroupMemberQueueInfo
|
||||
SwitchContact cName -> withContactName cName APISwitchContact
|
||||
SwitchGroupMember gName mName -> withMemberName gName mName APISwitchGroupMember
|
||||
AbortSwitchContact cName -> withContactName cName APIAbortSwitchContact
|
||||
@@ -2247,6 +2270,7 @@ processChatCommand' vr = \case
|
||||
SubInfo {server, subError = Just e} -> M.alter (Just . maybe [e] (e :)) server m
|
||||
_ -> m
|
||||
GetAgentSubsDetails -> lift $ CRAgentSubsDetails <$> withAgent' getAgentSubscriptions
|
||||
GetAgentQueuesInfo -> lift $ CRAgentQueuesInfo <$> withAgent' getAgentQueuesInfo
|
||||
-- CustomChatCommand is unsupported, it can be processed in preCmdHook
|
||||
-- in a modified CLI app or core - the hook should return Either ChatResponse ChatCommand
|
||||
CustomChatCommand _cmd -> withUser $ \user -> pure $ chatCmdError (Just user) "not supported"
|
||||
@@ -2794,6 +2818,9 @@ processChatCommand' vr = \case
|
||||
pure CIFile {fileId, fileName = takeFileName filePath, fileSize, fileSource = Just cf, fileStatus = CIFSSndStored, fileProtocol = FPLocal}
|
||||
let ci = mkChatItem cd ciId content ciFile_ Nothing Nothing itemForwarded Nothing False createdAt Nothing createdAt
|
||||
pure . CRNewChatItem user $ AChatItem SCTLocal SMDSnd (LocalChat nf) ci
|
||||
getConnQueueInfo user Connection {connId, agentConnId = AgentConnId acId} = do
|
||||
msgInfo <- withStore' (`getLastRcvMsgInfo` connId)
|
||||
CRQueueInfo user msgInfo <$> withAgent (`getConnectionQueueInfo` acId)
|
||||
|
||||
contactCITimed :: Contact -> CM (Maybe CITimed)
|
||||
contactCITimed ct = sndContactCITimed False ct Nothing
|
||||
@@ -3178,7 +3205,7 @@ receiveViaCompleteFD user fileId RcvFileDescr {fileDescrText, fileDescrComplete}
|
||||
pure $ filter (`notElem` knownSrvs) srvs
|
||||
ipProtectedForSrvs :: [XFTPServer] -> CM Bool
|
||||
ipProtectedForSrvs srvs = do
|
||||
netCfg <- lift $ withAgent' getNetworkConfig
|
||||
netCfg <- lift getNetworkConfig
|
||||
pure $ all (ipAddressProtected netCfg) srvs
|
||||
relaysNotApproved :: [XFTPServer] -> CM ()
|
||||
relaysNotApproved unknownSrvs = do
|
||||
@@ -3186,6 +3213,9 @@ receiveViaCompleteFD user fileId RcvFileDescr {fileDescrText, fileDescrComplete}
|
||||
forM_ aci_ $ \aci -> toView $ CRChatItemUpdated user aci
|
||||
throwChatError $ CEFileNotApproved fileId unknownSrvs
|
||||
|
||||
getNetworkConfig :: CM' NetworkConfig
|
||||
getNetworkConfig = withAgent' $ liftIO . getNetworkConfig'
|
||||
|
||||
resetRcvCIFileStatus :: User -> FileTransferId -> CIFileStatus 'MDRcv -> CM (Maybe AChatItem)
|
||||
resetRcvCIFileStatus user fileId ciFileStatus = do
|
||||
vr <- chatVersionRange
|
||||
@@ -7349,7 +7379,7 @@ chatCommandP =
|
||||
"/ttl" $> GetChatItemTTL,
|
||||
"/_network info " *> (APISetNetworkInfo <$> jsonP),
|
||||
"/_network " *> (APISetNetworkConfig <$> jsonP),
|
||||
("/network " <|> "/net ") *> (APISetNetworkConfig <$> netCfgP),
|
||||
("/network " <|> "/net ") *> (SetNetworkConfig <$> netCfgP),
|
||||
("/network" <|> "/net") $> APIGetNetworkConfig,
|
||||
"/reconnect" $> ReconnectAllServers,
|
||||
"/_settings " *> (APISetChatSettings <$> chatRefP <* A.space <*> jsonP),
|
||||
@@ -7360,6 +7390,10 @@ chatCommandP =
|
||||
("/info #" <|> "/i #") *> (GroupMemberInfo <$> displayName <* A.space <* char_ '@' <*> displayName),
|
||||
("/info #" <|> "/i #") *> (ShowGroupInfo <$> displayName),
|
||||
("/info " <|> "/i ") *> char_ '@' *> (ContactInfo <$> displayName),
|
||||
"/_queue info #" *> (APIGroupMemberQueueInfo <$> A.decimal <* A.space <*> A.decimal),
|
||||
"/_queue info @" *> (APIContactQueueInfo <$> A.decimal),
|
||||
("/queue info #" <|> "/qi #") *> (GroupMemberQueueInfo <$> displayName <* A.space <* char_ '@' <*> displayName),
|
||||
("/queue info " <|> "/qi ") *> char_ '@' *> (ContactQueueInfo <$> displayName),
|
||||
"/_switch #" *> (APISwitchGroupMember <$> A.decimal <* A.space <*> A.decimal),
|
||||
"/_switch @" *> (APISwitchContact <$> A.decimal),
|
||||
"/_abort switch #" *> (APIAbortSwitchGroupMember <$> A.decimal <* A.space <*> A.decimal),
|
||||
@@ -7536,6 +7570,7 @@ chatCommandP =
|
||||
"/get workers" $> GetAgentWorkers,
|
||||
"/get workers details" $> GetAgentWorkersDetails,
|
||||
"/get msgs" $> GetAgentMsgCounts,
|
||||
"/get queues" $> GetAgentQueuesInfo,
|
||||
"//" *> (CustomChatCommand <$> A.takeByteString)
|
||||
]
|
||||
where
|
||||
@@ -7657,10 +7692,12 @@ chatCommandP =
|
||||
<|> ("no" $> TMEDisableKeepTTL)
|
||||
netCfgP = do
|
||||
socksProxy <- "socks=" *> ("off" $> Nothing <|> "on" $> Just defaultSocksProxy <|> Just <$> strP)
|
||||
smpProxyMode_ <- optional $ " smp-proxy=" *> strP
|
||||
smpProxyFallback_ <- optional $ " smp-proxy-fallback=" *> strP
|
||||
t_ <- optional $ " timeout=" *> A.decimal
|
||||
logErrors <- " log=" *> onOffP <|> pure False
|
||||
let tcpTimeout = 1000000 * fromMaybe (maybe 5 (const 10) socksProxy) t_
|
||||
pure $ fullNetworkConfig socksProxy tcpTimeout logErrors
|
||||
logTLSErrors <- " log=" *> onOffP <|> pure False
|
||||
let tcpTimeout_ = (1000000 *) <$> t_
|
||||
pure $ SimpleNetCfg {socksProxy, smpProxyMode_, smpProxyFallback_, tcpTimeout_, logTLSErrors}
|
||||
dbKeyP = nonEmptyKey <$?> strP
|
||||
nonEmptyKey k@(DBEncryptionKey s) = if BA.null s then Left "empty key" else Right k
|
||||
dbEncryptionConfig currentKey newKey = DBEncryptionConfig {currentKey, newKey, keepKey = Just False}
|
||||
|
||||
@@ -68,13 +68,14 @@ import Simplex.Chat.Types.UITheme
|
||||
import Simplex.Chat.Util (liftIOEither)
|
||||
import Simplex.FileTransfer.Description (FileDescriptionURI)
|
||||
import Simplex.Messaging.Agent (AgentClient, SubscriptionsInfo)
|
||||
import Simplex.Messaging.Agent.Client (AgentLocks, AgentWorkersDetails (..), AgentWorkersSummary (..), ProtocolTestFailure, UserNetworkInfo)
|
||||
import Simplex.Messaging.Agent.Client (AgentLocks, AgentQueuesInfo (..), AgentWorkersDetails (..), AgentWorkersSummary (..), ProtocolTestFailure, UserNetworkInfo)
|
||||
import Simplex.Messaging.Agent.Env.SQLite (AgentConfig, NetworkConfig)
|
||||
import Simplex.Messaging.Agent.Lock
|
||||
import Simplex.Messaging.Agent.Protocol
|
||||
import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation, SQLiteStore, UpMigration, withTransaction)
|
||||
import Simplex.Messaging.Agent.Store.SQLite.DB (SlowQueryStats (..))
|
||||
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||
import Simplex.Messaging.Client (SMPProxyMode (..), SMPProxyFallback (..))
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Crypto.File (CryptoFile (..))
|
||||
import qualified Simplex.Messaging.Crypto.File as CF
|
||||
@@ -83,9 +84,10 @@ import Simplex.Messaging.Encoding.String
|
||||
import Simplex.Messaging.Notifications.Protocol (DeviceToken (..), NtfTknStatus)
|
||||
import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON, parseAll, parseString, sumTypeJSON)
|
||||
import Simplex.Messaging.Protocol (AProtoServerWithAuth, AProtocolType (..), CorrId, NtfServer, ProtoServerWithAuth, ProtocolTypeI, QueueId, SMPMsgMeta (..), SProtocolType, SubscriptionMode (..), UserProtocol, XFTPServer, XFTPServerWithAuth, userProtocol)
|
||||
import Simplex.Messaging.Server.QueueStore.QueueInfo
|
||||
import Simplex.Messaging.TMap (TMap)
|
||||
import Simplex.Messaging.Transport (TLS, simplexMQVersion)
|
||||
import Simplex.Messaging.Transport.Client (TransportHost)
|
||||
import Simplex.Messaging.Transport.Client (SocksProxy, TransportHost)
|
||||
import Simplex.Messaging.Util (allFinally, catchAllErrors, catchAllErrors', tryAllErrors, tryAllErrors', (<$$>))
|
||||
import Simplex.RemoteControl.Client
|
||||
import Simplex.RemoteControl.Invitation (RCSignedInvitation, RCVerifiedInvitation)
|
||||
@@ -350,6 +352,7 @@ data ChatCommand
|
||||
| GetChatItemTTL
|
||||
| APISetNetworkConfig NetworkConfig
|
||||
| APIGetNetworkConfig
|
||||
| SetNetworkConfig SimpleNetCfg
|
||||
| APISetNetworkInfo UserNetworkInfo
|
||||
| ReconnectAllServers
|
||||
| APISetChatSettings ChatRef ChatSettings
|
||||
@@ -357,6 +360,8 @@ data ChatCommand
|
||||
| APIContactInfo ContactId
|
||||
| APIGroupInfo GroupId
|
||||
| APIGroupMemberInfo GroupId GroupMemberId
|
||||
| APIContactQueueInfo ContactId
|
||||
| APIGroupMemberQueueInfo GroupId GroupMemberId
|
||||
| APISwitchContact ContactId
|
||||
| APISwitchGroupMember GroupId GroupMemberId
|
||||
| APIAbortSwitchContact ContactId
|
||||
@@ -375,6 +380,8 @@ data ChatCommand
|
||||
| ContactInfo ContactName
|
||||
| ShowGroupInfo GroupName
|
||||
| GroupMemberInfo GroupName ContactName
|
||||
| ContactQueueInfo ContactName
|
||||
| GroupMemberQueueInfo GroupName ContactName
|
||||
| SwitchContact ContactName
|
||||
| SwitchGroupMember GroupName ContactName
|
||||
| AbortSwitchContact ContactName
|
||||
@@ -504,6 +511,7 @@ data ChatCommand
|
||||
| GetAgentWorkers
|
||||
| GetAgentWorkersDetails
|
||||
| GetAgentMsgCounts
|
||||
| GetAgentQueuesInfo
|
||||
| -- The parser will return this command for strings that start from "//".
|
||||
-- This command should be processed in preCmdHook
|
||||
CustomChatCommand ByteString
|
||||
@@ -568,6 +576,7 @@ data ChatResponse
|
||||
| CRContactInfo {user :: User, contact :: Contact, connectionStats_ :: Maybe ConnectionStats, customUserProfile :: Maybe Profile}
|
||||
| CRGroupInfo {user :: User, groupInfo :: GroupInfo, groupSummary :: GroupSummary}
|
||||
| CRGroupMemberInfo {user :: User, groupInfo :: GroupInfo, member :: GroupMember, connectionStats_ :: Maybe ConnectionStats}
|
||||
| CRQueueInfo {user :: User, rcvMsgInfo :: Maybe RcvMsgInfo, queueInfo :: QueueInfo}
|
||||
| CRContactSwitchStarted {user :: User, contact :: Contact, connectionStats :: ConnectionStats}
|
||||
| CRGroupMemberSwitchStarted {user :: User, groupInfo :: GroupInfo, member :: GroupMember, connectionStats :: ConnectionStats}
|
||||
| CRContactSwitchAborted {user :: User, contact :: Contact, connectionStats :: ConnectionStats}
|
||||
@@ -750,6 +759,7 @@ data ChatResponse
|
||||
| CRAgentSubs {activeSubs :: Map Text Int, pendingSubs :: Map Text Int, removedSubs :: Map Text [String]}
|
||||
| CRAgentSubsDetails {agentSubs :: SubscriptionsInfo}
|
||||
| CRAgentMsgCounts {msgCounts :: [(Text, (Int, Int))]}
|
||||
| CRAgentQueuesInfo {agentQueuesInfo :: AgentQueuesInfo}
|
||||
| CRContactDisabled {user :: User, contact :: Contact}
|
||||
| CRConnectionDisabled {connectionEntity :: ConnectionEntity}
|
||||
| CRConnectionInactive {connectionEntity :: ConnectionEntity, inactive :: Bool}
|
||||
@@ -953,6 +963,18 @@ data AppFilePathsConfig = AppFilePathsConfig
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
data SimpleNetCfg = SimpleNetCfg
|
||||
{ socksProxy :: Maybe SocksProxy,
|
||||
smpProxyMode_ :: Maybe SMPProxyMode,
|
||||
smpProxyFallback_ :: Maybe SMPProxyFallback,
|
||||
tcpTimeout_ :: Maybe Int,
|
||||
logTLSErrors :: Bool
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
defaultSimpleNetCfg :: SimpleNetCfg
|
||||
defaultSimpleNetCfg = SimpleNetCfg Nothing Nothing Nothing Nothing False
|
||||
|
||||
data ContactSubStatus = ContactSubStatus
|
||||
{ contact :: Contact,
|
||||
contactError :: Maybe ChatError
|
||||
|
||||
@@ -962,6 +962,15 @@ data RcvMsgDelivery = RcvMsgDelivery
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
data RcvMsgInfo = RcvMsgInfo
|
||||
{ msgId :: Int64,
|
||||
msgDeliveryId :: Int64,
|
||||
msgDeliveryStatus :: Text,
|
||||
agentMsgId :: AgentMsgId,
|
||||
agentMsgMeta :: Text
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
data MsgMetaJSON = MsgMetaJSON
|
||||
{ integrity :: Text,
|
||||
rcvId :: Int64,
|
||||
@@ -1332,3 +1341,5 @@ $(JQ.deriveJSON defaultJSON ''MsgMetaJSON)
|
||||
|
||||
msgMetaJson :: MsgMeta -> Text
|
||||
msgMetaJson = decodeLatin1 . LB.toStrict . J.encode . msgMetaToJson
|
||||
|
||||
$(JQ.deriveJSON defaultJSON ''RcvMsgInfo)
|
||||
|
||||
@@ -48,7 +48,6 @@ import Simplex.Chat.Types
|
||||
import Simplex.Messaging.Agent.Client (agentClientStore)
|
||||
import Simplex.Messaging.Agent.Env.SQLite (createAgentStore)
|
||||
import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), MigrationError, closeSQLiteStore, reopenSQLiteStore)
|
||||
import Simplex.Messaging.Client (defaultNetworkConfig)
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, sumTypeJSON)
|
||||
@@ -192,7 +191,7 @@ mobileChatOpts dbFilePrefix =
|
||||
dbKey = "", -- for API database is already opened, and the key in options is not used
|
||||
smpServers = [],
|
||||
xftpServers = [],
|
||||
networkConfig = defaultNetworkConfig,
|
||||
simpleNetCfg = defaultSimpleNetCfg,
|
||||
logLevel = CLLImportant,
|
||||
logConnections = False,
|
||||
logServerHosts = True,
|
||||
|
||||
+29
-17
@@ -13,7 +13,6 @@ module Simplex.Chat.Options
|
||||
coreChatOptsP,
|
||||
getChatOpts,
|
||||
protocolServersP,
|
||||
fullNetworkConfig,
|
||||
)
|
||||
where
|
||||
|
||||
@@ -22,15 +21,17 @@ import qualified Data.Attoparsec.ByteString.Char8 as A
|
||||
import Data.ByteArray (ScrubbedBytes)
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import Data.Text.Encoding (encodeUtf8)
|
||||
import Numeric.Natural (Natural)
|
||||
import Options.Applicative
|
||||
import Simplex.Chat.Controller (ChatLogLevel (..), updateStr, versionNumber, versionString)
|
||||
import Simplex.Chat.Controller (ChatLogLevel (..), SimpleNetCfg (..), updateStr, versionNumber, versionString)
|
||||
import Simplex.FileTransfer.Description (mb)
|
||||
import Simplex.Messaging.Client (NetworkConfig (..), defaultNetworkConfig)
|
||||
import Simplex.Messaging.Client (SMPProxyMode (..), SMPProxyFallback (..))
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import Simplex.Messaging.Parsers (parseAll)
|
||||
import Simplex.Messaging.Protocol (ProtoServerWithAuth, ProtocolTypeI, SMPServerWithAuth, XFTPServerWithAuth)
|
||||
import Simplex.Messaging.Transport.Client (SocksProxy, defaultSocksProxy)
|
||||
import Simplex.Messaging.Transport.Client (defaultSocksProxy)
|
||||
import System.FilePath (combine)
|
||||
|
||||
data ChatOpts = ChatOpts
|
||||
@@ -55,7 +56,7 @@ data CoreChatOpts = CoreChatOpts
|
||||
dbKey :: ScrubbedBytes,
|
||||
smpServers :: [SMPServerWithAuth],
|
||||
xftpServers :: [XFTPServerWithAuth],
|
||||
networkConfig :: NetworkConfig,
|
||||
simpleNetCfg :: SimpleNetCfg,
|
||||
logLevel :: ChatLogLevel,
|
||||
logConnections :: Bool,
|
||||
logServerHosts :: Bool,
|
||||
@@ -123,18 +124,34 @@ coreChatOptsP appDir defaultDbFileName = do
|
||||
socksProxy <-
|
||||
flag' (Just defaultSocksProxy) (short 'x' <> help "Use local SOCKS5 proxy at :9050")
|
||||
<|> option
|
||||
parseSocksProxy
|
||||
strParse
|
||||
( long "socks-proxy"
|
||||
<> metavar "SOCKS5"
|
||||
<> help "Use SOCKS5 proxy at `ipv4:port` or `:port`"
|
||||
<> value Nothing
|
||||
)
|
||||
smpProxyMode_ <-
|
||||
optional $
|
||||
option
|
||||
strParse
|
||||
( long "smp-proxy"
|
||||
<> metavar "SMP_PROXY_MODE"
|
||||
<> help "Use private message routing: always, unknown, unprotected, never (default)"
|
||||
)
|
||||
smpProxyFallback_ <-
|
||||
optional $
|
||||
option
|
||||
strParse
|
||||
( long "smp-proxy-fallback"
|
||||
<> metavar "SMP_PROXY_FALLBACK_MODE"
|
||||
<> help "Allow downgrade and connect directly: no, [when IP address is] protected, yes (default)"
|
||||
)
|
||||
t <-
|
||||
option
|
||||
auto
|
||||
( long "tcp-timeout"
|
||||
<> metavar "TIMEOUT"
|
||||
<> help "TCP timeout, seconds (default: 5/10 without/with SOCKS5 proxy)"
|
||||
<> help "TCP timeout, seconds (default: 7/15 without/with SOCKS5 proxy)"
|
||||
<> value 0
|
||||
)
|
||||
logLevel <-
|
||||
@@ -149,7 +166,7 @@ coreChatOptsP appDir defaultDbFileName = do
|
||||
logTLSErrors <-
|
||||
switch
|
||||
( long "log-tls-errors"
|
||||
<> help "Log TLS errors (also enabled with `-l debug`)"
|
||||
<> help "Log TLS errors"
|
||||
)
|
||||
logConnections <-
|
||||
switch
|
||||
@@ -194,7 +211,7 @@ coreChatOptsP appDir defaultDbFileName = do
|
||||
dbKey,
|
||||
smpServers,
|
||||
xftpServers,
|
||||
networkConfig = fullNetworkConfig socksProxy (useTcpTimeout socksProxy t) (logTLSErrors || logLevel == CLLDebug),
|
||||
simpleNetCfg = SimpleNetCfg {socksProxy, smpProxyMode_, smpProxyFallback_, tcpTimeout_ = Just $ useTcpTimeout socksProxy t, logTLSErrors},
|
||||
logLevel,
|
||||
logConnections = logConnections || logLevel <= CLLInfo,
|
||||
logServerHosts = logServerHosts || logLevel <= CLLInfo,
|
||||
@@ -204,7 +221,7 @@ coreChatOptsP appDir defaultDbFileName = do
|
||||
highlyAvailable
|
||||
}
|
||||
where
|
||||
useTcpTimeout p t = 1000000 * if t > 0 then t else maybe 5 (const 10) p
|
||||
useTcpTimeout p t = 1000000 * if t > 0 then t else maybe 7 (const 15) p
|
||||
defaultDbFilePath = combine appDir defaultDbFileName
|
||||
|
||||
chatOptsP :: FilePath -> FilePath -> Parser ChatOpts
|
||||
@@ -321,16 +338,11 @@ chatOptsP appDir defaultDbFileName = do
|
||||
maintenance
|
||||
}
|
||||
|
||||
fullNetworkConfig :: Maybe SocksProxy -> Int -> Bool -> NetworkConfig
|
||||
fullNetworkConfig socksProxy tcpTimeout logTLSErrors =
|
||||
let tcpConnectTimeout = (tcpTimeout * 3) `div` 2
|
||||
in defaultNetworkConfig {socksProxy, tcpTimeout, tcpConnectTimeout, logTLSErrors}
|
||||
|
||||
parseProtocolServers :: ProtocolTypeI p => ReadM [ProtoServerWithAuth p]
|
||||
parseProtocolServers = eitherReader $ parseAll protocolServersP . B.pack
|
||||
|
||||
parseSocksProxy :: ReadM (Maybe SocksProxy)
|
||||
parseSocksProxy = eitherReader $ parseAll strP . B.pack
|
||||
strParse :: StrEncoding a => ReadM a
|
||||
strParse = eitherReader $ parseAll strP . encodeUtf8 . T.pack
|
||||
|
||||
parseServerPort :: ReadM (Maybe String)
|
||||
parseServerPort = eitherReader $ parseAll serverPortP . B.pack
|
||||
|
||||
@@ -72,11 +72,11 @@ import UnliftIO.Directory (copyFile, createDirectoryIfMissing, doesDirectoryExis
|
||||
|
||||
-- when acting as host
|
||||
minRemoteCtrlVersion :: AppVersion
|
||||
minRemoteCtrlVersion = AppVersion [5, 8, 0, 2]
|
||||
minRemoteCtrlVersion = AppVersion [5, 8, 0, 4]
|
||||
|
||||
-- when acting as controller
|
||||
minRemoteHostVersion :: AppVersion
|
||||
minRemoteHostVersion = AppVersion [5, 8, 0, 2]
|
||||
minRemoteHostVersion = AppVersion [5, 8, 0, 4]
|
||||
|
||||
currentAppVersion :: AppVersion
|
||||
currentAppVersion = AppVersion SC.version
|
||||
|
||||
@@ -23,6 +23,7 @@ module Simplex.Chat.Store.Messages
|
||||
createNewSndMessage,
|
||||
createSndMsgDelivery,
|
||||
createNewMessageAndRcvMsgDelivery,
|
||||
getLastRcvMsgInfo,
|
||||
createNewRcvMessage,
|
||||
updateSndMsgDeliveryStatus,
|
||||
createPendingGroupMessage,
|
||||
@@ -226,6 +227,23 @@ createNewMessageAndRcvMsgDelivery db connOrGroupId newMessage sharedMsgId_ RcvMs
|
||||
(msgId, connId, agentMsgId, msgMetaJson agentMsgMeta, snd $ broker agentMsgMeta, currentTs, currentTs, MDSRcvAgent)
|
||||
pure msg
|
||||
|
||||
getLastRcvMsgInfo :: DB.Connection -> Int64 -> IO (Maybe RcvMsgInfo)
|
||||
getLastRcvMsgInfo db connId =
|
||||
maybeFirstRow rcvMsgInfo $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT message_id, msg_delivery_id, delivery_status, agent_msg_id, agent_msg_meta
|
||||
FROM msg_deliveries
|
||||
WHERE connection_id = ? AND delivery_status IN (?, ?)
|
||||
ORDER BY created_at DESC, msg_delivery_id DESC
|
||||
LIMIT 1
|
||||
|]
|
||||
(connId, MDSRcvAgent, MDSRcvAcknowledged)
|
||||
where
|
||||
rcvMsgInfo (msgId, msgDeliveryId, msgDeliveryStatus, agentMsgId, agentMsgMeta) =
|
||||
RcvMsgInfo {msgId, msgDeliveryId, msgDeliveryStatus, agentMsgId, agentMsgMeta}
|
||||
|
||||
createNewRcvMessage :: forall e. MsgEncodingI e => DB.Connection -> ConnOrGroupId -> NewRcvMessage e -> Maybe SharedMsgId -> Maybe GroupMemberId -> Maybe GroupMemberId -> ExceptT StoreError IO RcvMessage
|
||||
createNewRcvMessage db connOrGroupId NewRcvMessage {chatMsgEvent, msgBody} sharedMsgId_ authorMember forwardedByMember =
|
||||
case connOrGroupId of
|
||||
|
||||
@@ -6,15 +6,16 @@ module Simplex.Chat.Terminal.Main where
|
||||
import Control.Concurrent (forkIO, threadDelay)
|
||||
import Control.Concurrent.STM
|
||||
import Control.Monad
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Time.Clock (getCurrentTime)
|
||||
import Data.Time.LocalTime (getCurrentTimeZone)
|
||||
import Network.Socket
|
||||
import Simplex.Chat.Controller (ChatConfig, ChatController (..), ChatResponse (..), currentRemoteHost, versionNumber, versionString)
|
||||
import Simplex.Chat.Controller (ChatConfig, ChatController (..), ChatResponse (..), SimpleNetCfg (..), currentRemoteHost, versionNumber, versionString)
|
||||
import Simplex.Chat.Core
|
||||
import Simplex.Chat.Options
|
||||
import Simplex.Chat.Terminal
|
||||
import Simplex.Chat.View (serializeChatResponse)
|
||||
import Simplex.Messaging.Client (NetworkConfig (..))
|
||||
import Simplex.Chat.View (serializeChatResponse, smpProxyModeStr)
|
||||
import Simplex.Messaging.Client (NetworkConfig (..), defaultNetworkConfig)
|
||||
import System.Directory (getAppUserDataDirectory)
|
||||
import System.Exit (exitFailure)
|
||||
import System.Terminal (withTerminal)
|
||||
@@ -51,7 +52,7 @@ simplexChatCLI cfg server_ = do
|
||||
putStrLn $ serializeChatResponse (rh, Just user) ts tz rh r
|
||||
|
||||
welcome :: ChatOpts -> IO ()
|
||||
welcome ChatOpts {coreOptions = CoreChatOpts {dbFilePrefix, networkConfig}} =
|
||||
welcome ChatOpts {coreOptions = CoreChatOpts {dbFilePrefix, simpleNetCfg = SimpleNetCfg {socksProxy, smpProxyMode_, smpProxyFallback_}}} =
|
||||
mapM_
|
||||
putStrLn
|
||||
[ versionString versionNumber,
|
||||
@@ -59,6 +60,9 @@ welcome ChatOpts {coreOptions = CoreChatOpts {dbFilePrefix, networkConfig}} =
|
||||
maybe
|
||||
"direct network connection - use `/network` command or `-x` CLI option to connect via SOCKS5 at :9050"
|
||||
(("using SOCKS5 proxy " <>) . show)
|
||||
(socksProxy networkConfig),
|
||||
socksProxy,
|
||||
smpProxyModeStr
|
||||
(fromMaybe (smpProxyMode defaultNetworkConfig) smpProxyMode_)
|
||||
(fromMaybe (smpProxyFallback defaultNetworkConfig) smpProxyFallback_),
|
||||
"type \"/help\" or \"/h\" for usage info"
|
||||
]
|
||||
|
||||
@@ -128,13 +128,16 @@ data UIColors = UIColors
|
||||
background :: Maybe UIColor,
|
||||
menus :: Maybe UIColor,
|
||||
title :: Maybe UIColor,
|
||||
accentVariant2 :: Maybe UIColor,
|
||||
sentMessage :: Maybe UIColor,
|
||||
receivedMessage :: Maybe UIColor
|
||||
sentReply :: Maybe UIColor,
|
||||
receivedMessage :: Maybe UIColor,
|
||||
receivedReply :: Maybe UIColor
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
defaultUIColors :: UIColors
|
||||
defaultUIColors = UIColors Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
|
||||
defaultUIColors = UIColors Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
|
||||
|
||||
newtype UIColor = UIColor String
|
||||
deriving (Eq, Show)
|
||||
|
||||
+38
-22
@@ -13,7 +13,6 @@ module Simplex.Chat.View where
|
||||
|
||||
import qualified Data.Aeson as J
|
||||
import qualified Data.Aeson.TH as JQ
|
||||
import Data.ByteString.Char8 (ByteString)
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
import qualified Data.ByteString.Lazy.Char8 as LB
|
||||
import Data.Char (isSpace, toUpper)
|
||||
@@ -56,6 +55,7 @@ import Simplex.Messaging.Agent.Client (ProtocolTestFailure (..), ProtocolTestSte
|
||||
import Simplex.Messaging.Agent.Env.SQLite (NetworkConfig (..))
|
||||
import Simplex.Messaging.Agent.Protocol
|
||||
import Simplex.Messaging.Agent.Store.SQLite.DB (SlowQueryStats (..))
|
||||
import Simplex.Messaging.Client (SMPProxyMode (..), SMPProxyFallback)
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..))
|
||||
import qualified Simplex.Messaging.Crypto.Ratchet as CR
|
||||
@@ -65,7 +65,7 @@ import Simplex.Messaging.Parsers (dropPrefix, taggedObjectJSON)
|
||||
import Simplex.Messaging.Protocol (AProtoServerWithAuth (..), AProtocolType, ProtoServerWithAuth, ProtocolServer (..), ProtocolTypeI, SProtocolType (..))
|
||||
import qualified Simplex.Messaging.Protocol as SMP
|
||||
import Simplex.Messaging.Transport.Client (TransportHost (..))
|
||||
import Simplex.Messaging.Util (bshow, tshow)
|
||||
import Simplex.Messaging.Util (bshow, safeDecodeUtf8, tshow)
|
||||
import Simplex.Messaging.Version hiding (version)
|
||||
import Simplex.RemoteControl.Types (RCCtrlAddress (..))
|
||||
import System.Console.ANSI.Types
|
||||
@@ -90,10 +90,10 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
||||
CRChatRunning -> ["chat is running"]
|
||||
CRChatStopped -> ["chat stopped"]
|
||||
CRChatSuspended -> ["chat suspended"]
|
||||
CRApiChats u chats -> ttyUser u $ if testView then testViewChats chats else [plain . bshow $ J.encode chats]
|
||||
CRApiChats u chats -> ttyUser u $ if testView then testViewChats chats else [viewJSON chats]
|
||||
CRChats chats -> viewChats ts tz chats
|
||||
CRApiChat u chat -> ttyUser u $ if testView then testViewChat chat else [plain . bshow $ J.encode chat]
|
||||
CRApiParsedMarkdown ft -> [plain . bshow $ J.encode ft]
|
||||
CRApiChat u chat -> ttyUser u $ if testView then testViewChat chat else [viewJSON chat]
|
||||
CRApiParsedMarkdown ft -> [viewJSON ft]
|
||||
CRUserProtoServers u userServers -> ttyUser u $ viewUserServers userServers testView
|
||||
CRServerTestResult u srv testFailure -> ttyUser u $ viewServerTestResult srv testFailure
|
||||
CRChatItemTTL u ttl -> ttyUser u $ viewChatItemTTL ttl
|
||||
@@ -101,6 +101,10 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
||||
CRContactInfo u ct cStats customUserProfile -> ttyUser u $ viewContactInfo ct cStats customUserProfile
|
||||
CRGroupInfo u g s -> ttyUser u $ viewGroupInfo g s
|
||||
CRGroupMemberInfo u g m cStats -> ttyUser u $ viewGroupMemberInfo g m cStats
|
||||
CRQueueInfo _ msgInfo qInfo ->
|
||||
[ "last received msg: " <> maybe "none" viewJSON msgInfo,
|
||||
"server queue info: " <> viewJSON qInfo
|
||||
]
|
||||
CRContactSwitchStarted {} -> ["switch started"]
|
||||
CRGroupMemberSwitchStarted {} -> ["switch started"]
|
||||
CRContactSwitchAborted {} -> ["switch aborted"]
|
||||
@@ -220,7 +224,7 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
||||
CRSndFileError u (Just ci) _ e -> ttyUser u $ uploadingFile "error" ci <> [plain e]
|
||||
CRSndFileRcvCancelled u _ ft@SndFileTransfer {recipientDisplayName = c} ->
|
||||
ttyUser u [ttyContact c <> " cancelled receiving " <> sndFile ft]
|
||||
CRStandaloneFileInfo info_ -> maybe ["no file information in URI"] (\j -> [plain . LB.toStrict $ J.encode j]) info_
|
||||
CRStandaloneFileInfo info_ -> maybe ["no file information in URI"] (\j -> [viewJSON j]) info_
|
||||
CRContactConnecting u _ -> ttyUser u []
|
||||
CRContactConnected u ct userCustomProfile -> ttyUser u $ viewContactConnected ct userCustomProfile testView
|
||||
CRContactAnotherClient u c -> ttyUser u [ttyContact' c <> ": contact is connected to another client"]
|
||||
@@ -322,7 +326,7 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
||||
]
|
||||
CRRemoteFileStored rhId (CryptoFile filePath cfArgs_) ->
|
||||
[plain $ "file " <> filePath <> " stored on remote host " <> show rhId]
|
||||
<> maybe [] ((: []) . plain . cryptoFileArgsStr testView) cfArgs_
|
||||
<> maybe [] ((: []) . cryptoFileArgsStr testView) cfArgs_
|
||||
CRRemoteCtrlList cs -> viewRemoteCtrls cs
|
||||
CRRemoteCtrlFound {remoteCtrl = RemoteCtrlInfo {remoteCtrlId, ctrlDeviceName}, ctrlAppInfo_, appVersion, compatible} ->
|
||||
[ ("remote controller " <> sShow remoteCtrlId <> " found: ")
|
||||
@@ -354,8 +358,8 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
||||
in ("Chat queries" : map viewQuery chatQueries) <> [""] <> ("Agent queries" : map viewQuery agentQueries)
|
||||
CRDebugLocks {chatLockName, chatEntityLocks, agentLocks} ->
|
||||
[ maybe "no chat lock" (("chat lock: " <>) . plain) chatLockName,
|
||||
plain $ "chat entity locks: " <> LB.unpack (J.encode chatEntityLocks),
|
||||
plain $ "agent locks: " <> LB.unpack (J.encode agentLocks)
|
||||
"chat entity locks: " <> viewJSON chatEntityLocks,
|
||||
"agent locks: " <> viewJSON agentLocks
|
||||
]
|
||||
CRAgentStats stats -> map (plain . intercalate ",") stats
|
||||
CRAgentSubs {activeSubs, pendingSubs, removedSubs} ->
|
||||
@@ -370,12 +374,16 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
||||
("active subscriptions:" : map sShow activeSubscriptions)
|
||||
<> ("pending subscriptions: " : map sShow pendingSubscriptions)
|
||||
<> ("removed subscriptions: " : map sShow removedSubscriptions)
|
||||
CRAgentWorkersSummary {agentWorkersSummary} -> ["agent workers summary: " <> plain (LB.unpack $ J.encode agentWorkersSummary)]
|
||||
CRAgentWorkersSummary {agentWorkersSummary} -> ["agent workers summary: " <> viewJSON agentWorkersSummary]
|
||||
CRAgentWorkersDetails {agentWorkersDetails} ->
|
||||
[ "agent workers details:",
|
||||
plain . LB.unpack $ J.encode agentWorkersDetails -- this would be huge, but copypastable when has its own line
|
||||
viewJSON agentWorkersDetails -- this would be huge, but copypastable when has its own line
|
||||
]
|
||||
CRAgentMsgCounts {msgCounts} -> ["received messages (total, duplicates):", viewJSON msgCounts]
|
||||
CRAgentQueuesInfo {agentQueuesInfo} ->
|
||||
[ "agent queues info:",
|
||||
plain . LB.unpack $ J.encode agentQueuesInfo
|
||||
]
|
||||
CRAgentMsgCounts {msgCounts} -> ["received messages (total, duplicates):", plain . LB.unpack $ J.encode msgCounts]
|
||||
CRContactDisabled u c -> ttyUser u ["[" <> ttyContact' c <> "] connection is disabled, to enable: " <> highlight ("/enable " <> viewContactName c) <> ", to delete: " <> highlight ("/d " <> viewContactName c)]
|
||||
CRConnectionDisabled entity -> viewConnectionEntityDisabled entity
|
||||
CRConnectionInactive entity inactive -> viewConnectionEntityInactive entity inactive
|
||||
@@ -393,7 +401,7 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
|
||||
CRChatError u e -> ttyUser' u $ viewChatError False logLevel testView e
|
||||
CRChatErrors u errs -> ttyUser' u $ concatMap (viewChatError False logLevel testView) errs
|
||||
CRArchiveImported archiveErrs -> if null archiveErrs then ["ok"] else ["archive import errors: " <> plain (show archiveErrs)]
|
||||
CRAppSettings as -> ["app settings: " <> plain (LB.unpack $ J.encode as)]
|
||||
CRAppSettings as -> ["app settings: " <> viewJSON as]
|
||||
CRTimedAction _ _ -> []
|
||||
CRCustomChatResponse u r -> ttyUser' u $ [plain r]
|
||||
where
|
||||
@@ -1206,12 +1214,17 @@ viewChatItemTTL = \case
|
||||
deletedAfter ttlStr = ["old messages are set to be deleted after: " <> ttlStr]
|
||||
|
||||
viewNetworkConfig :: NetworkConfig -> [StyledString]
|
||||
viewNetworkConfig NetworkConfig {socksProxy, tcpTimeout} =
|
||||
viewNetworkConfig NetworkConfig {socksProxy, tcpTimeout, smpProxyMode, smpProxyFallback} =
|
||||
[ plain $ maybe "direct network connection" (("using SOCKS5 proxy " <>) . show) socksProxy,
|
||||
"TCP timeout: " <> sShow tcpTimeout,
|
||||
"use " <> highlight' "/network socks=<on/off/[ipv4]:port>[ timeout=<seconds>]" <> " to change settings"
|
||||
plain $ smpProxyModeStr smpProxyMode smpProxyFallback,
|
||||
"use " <> highlight' "/network socks=<on/off/[ipv4]:port>[ timeout=<seconds>][ smp-proxy=always/unknown/unprotected/never][ smp-proxy-fallback=no/protected/yes]" <> " to change settings"
|
||||
]
|
||||
|
||||
smpProxyModeStr :: SMPProxyMode -> SMPProxyFallback -> String
|
||||
smpProxyModeStr SPMNever _ = "private message routing disabled."
|
||||
smpProxyModeStr mode fallback = T.unpack $ safeDecodeUtf8 $ "private message routing mode: " <> strEncode mode <> ", fallback: " <> strEncode fallback
|
||||
|
||||
viewContactInfo :: Contact -> Maybe ConnectionStats -> Maybe Profile -> [StyledString]
|
||||
viewContactInfo ct@Contact {contactId, profile = LocalProfile {localAlias, contactLink}, activeConn, uiThemes, customData} stats incognitoProfile =
|
||||
["contact ID: " <> sShow contactId]
|
||||
@@ -1237,10 +1250,10 @@ viewGroupInfo GroupInfo {groupId, uiThemes, customData} s =
|
||||
<> viewCustomData customData
|
||||
|
||||
viewUITheme :: Maybe UIThemeEntityOverrides -> [StyledString]
|
||||
viewUITheme = maybe [] (\uiThemes -> ["UI themes: " <> plain (LB.toStrict $ J.encode uiThemes)])
|
||||
viewUITheme = maybe [] (\uiThemes -> ["UI themes: " <> viewJSON uiThemes])
|
||||
|
||||
viewCustomData :: Maybe CustomData -> [StyledString]
|
||||
viewCustomData = maybe [] (\(CustomData v) -> ["custom data: " <> plain (LB.toStrict . J.encode $ J.Object v)])
|
||||
viewCustomData = maybe [] (\(CustomData v) -> ["custom data: " <> viewJSON (J.Object v)])
|
||||
|
||||
viewGroupMemberInfo :: GroupInfo -> GroupMember -> Maybe ConnectionStats -> [StyledString]
|
||||
viewGroupMemberInfo GroupInfo {groupId} m@GroupMember {groupMemberId, memberProfile = LocalProfile {localAlias, contactLink}, activeConn} stats =
|
||||
@@ -1682,7 +1695,7 @@ receivingFile_' :: (Maybe RemoteHostId, Maybe User) -> Bool -> String -> AChatIt
|
||||
receivingFile_' hu testView status (AChatItem _ _ chat ChatItem {file = Just CIFile {fileId, fileName, fileSource = Just f@(CryptoFile _ cfArgs_)}, chatDir}) =
|
||||
[plain status <> " receiving " <> fileTransferStr fileId fileName <> fileFrom chat chatDir] <> cfArgsStr cfArgs_ <> getRemoteFileStr
|
||||
where
|
||||
cfArgsStr (Just cfArgs) = [plain (cryptoFileArgsStr testView cfArgs) | status == "completed"]
|
||||
cfArgsStr (Just cfArgs) = [cryptoFileArgsStr testView cfArgs | status == "completed"]
|
||||
cfArgsStr _ = []
|
||||
getRemoteFileStr = case hu of
|
||||
(Just rhId, Just User {userId})
|
||||
@@ -1703,10 +1716,10 @@ viewLocalFile to CIFile {fileId, fileSource} ts tz = case fileSource of
|
||||
Just (CryptoFile fPath _) -> sentWithTime_ ts tz [to <> fileTransferStr fileId fPath]
|
||||
_ -> const []
|
||||
|
||||
cryptoFileArgsStr :: Bool -> CryptoFileArgs -> ByteString
|
||||
cryptoFileArgsStr :: Bool -> CryptoFileArgs -> StyledString
|
||||
cryptoFileArgsStr testView cfArgs@(CFArgs key nonce)
|
||||
| testView = LB.toStrict $ J.encode cfArgs
|
||||
| otherwise = "encryption key: " <> strEncode key <> ", nonce: " <> strEncode nonce
|
||||
| testView = viewJSON cfArgs
|
||||
| otherwise = plain $ "encryption key: " <> strEncode key <> ", nonce: " <> strEncode nonce
|
||||
|
||||
fileFrom :: ChatInfo c -> CIDirection c d -> StyledString
|
||||
fileFrom (DirectChat ct) CIDirectRcv = " from " <> ttyContact' ct
|
||||
@@ -1824,7 +1837,7 @@ viewCallAnswer ct WebRTCSession {rtcSession = answer, rtcIceCandidates = iceCand
|
||||
[ ttyContact' ct <> " continued the WebRTC call",
|
||||
"To connect, please paste the data below in your browser window you opened earlier and click Connect button",
|
||||
"",
|
||||
plain . LB.toStrict . J.encode $ WCCallAnswer {answer, iceCandidates}
|
||||
viewJSON WCCallAnswer {answer, iceCandidates}
|
||||
]
|
||||
|
||||
callMediaStr :: CallType -> StyledString
|
||||
@@ -2082,6 +2095,9 @@ viewConnectionEntityInactive entity inactive
|
||||
| inactive = ["[" <> connEntityLabel entity <> "] connection is marked as inactive"]
|
||||
| otherwise = ["[" <> connEntityLabel entity <> "] inactive connection is marked as active"]
|
||||
|
||||
viewJSON :: J.ToJSON a => a -> StyledString
|
||||
viewJSON = plain . LB.toStrict . J.encode
|
||||
|
||||
connEntityLabel :: ConnectionEntity -> StyledString
|
||||
connEntityLabel = \case
|
||||
RcvDirectMsgConnection _ (Just Contact {localDisplayName = c}) -> plain c
|
||||
|
||||
+7
-5
@@ -25,7 +25,7 @@ import Data.Maybe (isNothing)
|
||||
import qualified Data.Text as T
|
||||
import Network.Socket
|
||||
import Simplex.Chat
|
||||
import Simplex.Chat.Controller (ChatCommand (..), ChatConfig (..), ChatController (..), ChatDatabase (..), ChatLogLevel (..))
|
||||
import Simplex.Chat.Controller (ChatCommand (..), ChatConfig (..), ChatController (..), ChatDatabase (..), ChatLogLevel (..), defaultSimpleNetCfg)
|
||||
import Simplex.Chat.Core
|
||||
import Simplex.Chat.Options
|
||||
import Simplex.Chat.Protocol (currentChatVersion, pqEncryptionCompressionVersion)
|
||||
@@ -44,7 +44,7 @@ import Simplex.Messaging.Agent.Protocol (currentSMPAgentVersion, duplexHandshake
|
||||
import Simplex.Messaging.Agent.RetryInterval
|
||||
import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), closeSQLiteStore)
|
||||
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||
import Simplex.Messaging.Client (ProtocolClientConfig (..), defaultNetworkConfig)
|
||||
import Simplex.Messaging.Client (ProtocolClientConfig (..))
|
||||
import Simplex.Messaging.Client.Agent (defaultSMPClientAgentConfig)
|
||||
import Simplex.Messaging.Crypto.Ratchet (supportedE2EEncryptVRange)
|
||||
import qualified Simplex.Messaging.Crypto.Ratchet as CR
|
||||
@@ -94,7 +94,7 @@ testCoreOpts =
|
||||
-- dbKey = "this is a pass-phrase to encrypt the database",
|
||||
smpServers = ["smp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=:server_password@localhost:7001"],
|
||||
xftpServers = ["xftp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=:server_password@localhost:7002"],
|
||||
networkConfig = defaultNetworkConfig,
|
||||
simpleNetCfg = defaultSimpleNetCfg,
|
||||
logLevel = CLLImportant,
|
||||
logConnections = False,
|
||||
logServerHosts = False,
|
||||
@@ -432,7 +432,8 @@ smpServerCfg =
|
||||
controlPort = Nothing,
|
||||
smpAgentCfg = defaultSMPClientAgentConfig,
|
||||
allowSMPProxy = False,
|
||||
serverClientConcurrency = 16
|
||||
serverClientConcurrency = 16,
|
||||
information = Nothing
|
||||
}
|
||||
|
||||
withSmpServer :: IO () -> IO ()
|
||||
@@ -472,7 +473,8 @@ xftpServerConfig =
|
||||
serverStatsLogFile = "tests/tmp/xftp-server-stats.daily.log",
|
||||
serverStatsBackupFile = Nothing,
|
||||
controlPort = Nothing,
|
||||
transportConfig = defaultTransportServerConfig
|
||||
transportConfig = defaultTransportServerConfig,
|
||||
responseDelay = 0
|
||||
}
|
||||
|
||||
withXFTPServer :: IO () -> IO ()
|
||||
|
||||
Reference in New Issue
Block a user