Compare commits
13 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edf217111f | ||
|
|
f0e18c62fe | ||
|
|
a615dbec91 | ||
|
|
a8ef92a933 | ||
|
|
c1cca9385a | ||
|
|
267207cc15 | ||
|
|
012115b330 | ||
|
|
c4aa988fb3 | ||
|
|
c236a759d5 | ||
|
|
67323a41eb | ||
|
|
962166c2ef | ||
|
|
b0ed64533f | ||
|
|
bc7fe4ec75 |
@@ -11,8 +11,8 @@ android {
|
|||||||
applicationId "chat.simplex.app"
|
applicationId "chat.simplex.app"
|
||||||
minSdk 29
|
minSdk 29
|
||||||
targetSdk 32
|
targetSdk 32
|
||||||
versionCode 53
|
versionCode 54
|
||||||
versionName "4.0-beta.0"
|
versionName "4.0-beta.1"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
ndk {
|
ndk {
|
||||||
|
|||||||
@@ -1833,13 +1833,15 @@ sealed class ChatError {
|
|||||||
@Serializable
|
@Serializable
|
||||||
sealed class ChatErrorType {
|
sealed class ChatErrorType {
|
||||||
val string: String get() = when (this) {
|
val string: String get() = when (this) {
|
||||||
|
is NoActiveUser -> "noActiveUser"
|
||||||
is InvalidConnReq -> "invalidConnReq"
|
is InvalidConnReq -> "invalidConnReq"
|
||||||
is ContactGroups -> "groupNames $groupNames"
|
is ContactGroups -> "groupNames $groupNames"
|
||||||
is NoActiveUser -> "noActiveUser"
|
is СommandError -> "commandError $message"
|
||||||
}
|
}
|
||||||
@Serializable @SerialName("noActiveUser") class NoActiveUser: ChatErrorType()
|
@Serializable @SerialName("noActiveUser") class NoActiveUser: ChatErrorType()
|
||||||
@Serializable @SerialName("invalidConnReq") class InvalidConnReq: ChatErrorType()
|
@Serializable @SerialName("invalidConnReq") class InvalidConnReq: ChatErrorType()
|
||||||
@Serializable @SerialName("contactGroups") class ContactGroups(val contact: Contact, val groupNames: List<String>): ChatErrorType()
|
@Serializable @SerialName("contactGroups") class ContactGroups(val contact: Contact, val groupNames: List<String>): ChatErrorType()
|
||||||
|
@Serializable @SerialName("commandError") class СommandError(val message: String): ChatErrorType()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
|||||||
@@ -46,14 +46,7 @@ fun TerminalView(chatModel: ChatModel, close: () -> Unit) {
|
|||||||
TerminalLayout(
|
TerminalLayout(
|
||||||
chatModel.terminalItems,
|
chatModel.terminalItems,
|
||||||
composeState,
|
composeState,
|
||||||
sendCommand = {
|
sendCommand = { sendCommand(chatModel, composeState) },
|
||||||
withApi {
|
|
||||||
// show "in progress"
|
|
||||||
chatModel.controller.sendCmd(CC.Console(composeState.value.message))
|
|
||||||
composeState.value = ComposeState(useLinkPreviews = false)
|
|
||||||
// hide "in progress"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
close
|
close
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@@ -91,6 +84,25 @@ private fun runAuth(authorized: MutableState<Boolean>, context: Context) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun sendCommand(chatModel: ChatModel, composeState: MutableState<ComposeState>) {
|
||||||
|
val developerTools = chatModel.controller.appPrefs.developerTools.get()
|
||||||
|
val prefPerformLA = chatModel.controller.appPrefs.performLA.get()
|
||||||
|
val s = composeState.value.message
|
||||||
|
if (s.startsWith("/sql") && (!prefPerformLA || !developerTools)) {
|
||||||
|
val resp = CR.ChatCmdError(ChatError.ChatErrorChat(ChatErrorType.СommandError("Failed reading: empty")))
|
||||||
|
chatModel.terminalItems.add(TerminalItem.cmd(CC.Console(s)))
|
||||||
|
chatModel.terminalItems.add(TerminalItem.resp(resp))
|
||||||
|
composeState.value = ComposeState(useLinkPreviews = false)
|
||||||
|
} else {
|
||||||
|
withApi {
|
||||||
|
// show "in progress"
|
||||||
|
chatModel.controller.sendCmd(CC.Console(s))
|
||||||
|
composeState.value = ComposeState(useLinkPreviews = false)
|
||||||
|
// hide "in progress"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TerminalLayout(
|
fun TerminalLayout(
|
||||||
terminalItems: List<TerminalItem>,
|
terminalItems: List<TerminalItem>,
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ fun DatabaseView(
|
|||||||
importArchiveAlert(m, context, uri, progressIndicator)
|
importArchiveAlert(m, context, uri, progressIndicator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val appFilesCountAndSize = remember { mutableStateOf(directoryFileCountAndSize(getAppFilesDirectory(context))) }
|
||||||
LaunchedEffect(m.chatRunning) {
|
LaunchedEffect(m.chatRunning) {
|
||||||
runChat.value = m.chatRunning.value ?: true
|
runChat.value = m.chatRunning.value ?: true
|
||||||
}
|
}
|
||||||
@@ -78,10 +79,12 @@ fun DatabaseView(
|
|||||||
chatArchiveName,
|
chatArchiveName,
|
||||||
chatArchiveTime,
|
chatArchiveTime,
|
||||||
chatLastStart,
|
chatLastStart,
|
||||||
|
appFilesCountAndSize,
|
||||||
startChat = { startChat(m, runChat, chatLastStart, m.chatDbChanged) },
|
startChat = { startChat(m, runChat, chatLastStart, m.chatDbChanged) },
|
||||||
stopChatAlert = { stopChatAlert(m, runChat, context) },
|
stopChatAlert = { stopChatAlert(m, runChat, context) },
|
||||||
exportArchive = { exportArchive(context, m, progressIndicator, chatArchiveName, chatArchiveTime, chatArchiveFile, saveArchiveLauncher) },
|
exportArchive = { exportArchive(context, m, progressIndicator, chatArchiveName, chatArchiveTime, chatArchiveFile, saveArchiveLauncher) },
|
||||||
deleteChatAlert = { deleteChatAlert(m, progressIndicator) },
|
deleteChatAlert = { deleteChatAlert(m, progressIndicator) },
|
||||||
|
deleteAppFilesAndMedia = { deleteFilesAndMediaAlert(context, appFilesCountAndSize) },
|
||||||
showSettingsModal
|
showSettingsModal
|
||||||
)
|
)
|
||||||
if (progressIndicator.value) {
|
if (progressIndicator.value) {
|
||||||
@@ -112,10 +115,12 @@ fun DatabaseLayout(
|
|||||||
chatArchiveName: MutableState<String?>,
|
chatArchiveName: MutableState<String?>,
|
||||||
chatArchiveTime: MutableState<Instant?>,
|
chatArchiveTime: MutableState<Instant?>,
|
||||||
chatLastStart: MutableState<Instant?>,
|
chatLastStart: MutableState<Instant?>,
|
||||||
|
appFilesCountAndSize: MutableState<Pair<Int, Long>>,
|
||||||
startChat: () -> Unit,
|
startChat: () -> Unit,
|
||||||
stopChatAlert: () -> Unit,
|
stopChatAlert: () -> Unit,
|
||||||
exportArchive: () -> Unit,
|
exportArchive: () -> Unit,
|
||||||
deleteChatAlert: () -> Unit,
|
deleteChatAlert: () -> Unit,
|
||||||
|
deleteAppFilesAndMedia: () -> Unit,
|
||||||
showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit)
|
showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit)
|
||||||
) {
|
) {
|
||||||
val stopped = !runChat
|
val stopped = !runChat
|
||||||
@@ -196,6 +201,28 @@ fun DatabaseLayout(
|
|||||||
stringResource(R.string.stop_chat_to_enable_database_actions)
|
stringResource(R.string.stop_chat_to_enable_database_actions)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
SectionSpacer()
|
||||||
|
|
||||||
|
SectionView(stringResource(R.string.files_section)) {
|
||||||
|
val deleteFilesDisabled = operationsDisabled || appFilesCountAndSize.value.first == 0
|
||||||
|
SectionItemView(
|
||||||
|
deleteAppFilesAndMedia,
|
||||||
|
disabled = deleteFilesDisabled
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.delete_files_and_media),
|
||||||
|
color = if (deleteFilesDisabled) HighOrLowlight else Color.Red
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val (count, size) = appFilesCountAndSize.value
|
||||||
|
SectionTextFooter(
|
||||||
|
if (count == 0) {
|
||||||
|
stringResource(R.string.no_received_app_files)
|
||||||
|
} else {
|
||||||
|
String.format(stringResource(R.string.total_files_count_and_size), count, formatBytes(size))
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,6 +529,21 @@ private fun deleteChat(m: ChatModel, progressIndicator: MutableState<Boolean>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun deleteFilesAndMediaAlert(context: Context, appFilesCountAndSize: MutableState<Pair<Int, Long>>) {
|
||||||
|
AlertManager.shared.showAlertDialog(
|
||||||
|
title = generalGetString(R.string.delete_files_and_media_question),
|
||||||
|
text = generalGetString(R.string.delete_files_and_media_desc),
|
||||||
|
confirmText = generalGetString(R.string.delete_verb),
|
||||||
|
onConfirm = { deleteFiles(appFilesCountAndSize, context) },
|
||||||
|
destructive = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deleteFiles(appFilesCountAndSize: MutableState<Pair<Int, Long>>, context: Context) {
|
||||||
|
deleteAppFiles(context)
|
||||||
|
appFilesCountAndSize.value = directoryFileCountAndSize(getAppFilesDirectory(context))
|
||||||
|
}
|
||||||
|
|
||||||
private fun operationEnded(m: ChatModel, progressIndicator: MutableState<Boolean>, alert: () -> Unit) {
|
private fun operationEnded(m: ChatModel, progressIndicator: MutableState<Boolean>, alert: () -> Unit) {
|
||||||
m.chatDbChanged.value = true
|
m.chatDbChanged.value = true
|
||||||
progressIndicator.value = false
|
progressIndicator.value = false
|
||||||
@@ -527,10 +569,12 @@ fun PreviewDatabaseLayout() {
|
|||||||
chatArchiveName = remember { mutableStateOf("dummy_archive") },
|
chatArchiveName = remember { mutableStateOf("dummy_archive") },
|
||||||
chatArchiveTime = remember { mutableStateOf(Clock.System.now()) },
|
chatArchiveTime = remember { mutableStateOf(Clock.System.now()) },
|
||||||
chatLastStart = remember { mutableStateOf(Clock.System.now()) },
|
chatLastStart = remember { mutableStateOf(Clock.System.now()) },
|
||||||
|
appFilesCountAndSize = remember { mutableStateOf(0 to 0L) },
|
||||||
startChat = {},
|
startChat = {},
|
||||||
stopChatAlert = {},
|
stopChatAlert = {},
|
||||||
exportArchive = {},
|
exportArchive = {},
|
||||||
deleteChatAlert = {},
|
deleteChatAlert = {},
|
||||||
|
deleteAppFilesAndMedia = {},
|
||||||
showSettingsModal = { {} }
|
showSettingsModal = { {} }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ import androidx.compose.ui.text.style.TextDecoration
|
|||||||
import androidx.compose.ui.unit.*
|
import androidx.compose.ui.unit.*
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.core.text.HtmlCompat
|
import androidx.core.text.HtmlCompat
|
||||||
import chat.simplex.app.BuildConfig
|
import chat.simplex.app.*
|
||||||
import chat.simplex.app.SimplexApp
|
|
||||||
import chat.simplex.app.model.CIFile
|
import chat.simplex.app.model.CIFile
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.io.*
|
import java.io.*
|
||||||
@@ -405,6 +404,31 @@ fun removeFile(context: Context, fileName: String): Boolean {
|
|||||||
return fileDeleted
|
return fileDeleted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteAppFiles(context: Context) {
|
||||||
|
val dir = File(getAppFilesDirectory(context))
|
||||||
|
try {
|
||||||
|
dir.list()?.forEach {
|
||||||
|
removeFile(context, it)
|
||||||
|
}
|
||||||
|
} catch (e: java.lang.Exception) {
|
||||||
|
Log.e(TAG, "Util deleteAppFiles error: ${e.stackTraceToString()}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun directoryFileCountAndSize(dir: String): Pair<Int, Long> { // count, size in bytes
|
||||||
|
var fileCount = 0
|
||||||
|
var bytes = 0L
|
||||||
|
try {
|
||||||
|
File(dir).listFiles()?.forEach {
|
||||||
|
fileCount++
|
||||||
|
bytes += it.length()
|
||||||
|
}
|
||||||
|
} catch (e: java.lang.Exception) {
|
||||||
|
Log.e(TAG, "Util directoryFileCountAndSize error: ${e.stackTraceToString()}")
|
||||||
|
}
|
||||||
|
return fileCount to bytes
|
||||||
|
}
|
||||||
|
|
||||||
fun ByteArray.toBase64String() = Base64.encodeToString(this, Base64.DEFAULT)
|
fun ByteArray.toBase64String() = Base64.encodeToString(this, Base64.DEFAULT)
|
||||||
|
|
||||||
fun String.toByteArrayFromBase64() = Base64.decode(this, Base64.DEFAULT)
|
fun String.toByteArrayFromBase64() = Base64.decode(this, Base64.DEFAULT)
|
||||||
|
|||||||
@@ -556,6 +556,12 @@
|
|||||||
<string name="restart_the_app_to_create_a_new_chat_profile">Перезапустите приложение, чтобы создать новый профиль.</string>
|
<string name="restart_the_app_to_create_a_new_chat_profile">Перезапустите приложение, чтобы создать новый профиль.</string>
|
||||||
<string name="you_must_use_the_most_recent_version_of_database">Используйте самую последнюю версию архива чата и ТОЛЬКО на одном устройстве, иначе вы можете перестать получать сообщения от некоторых контактов.</string>
|
<string name="you_must_use_the_most_recent_version_of_database">Используйте самую последнюю версию архива чата и ТОЛЬКО на одном устройстве, иначе вы можете перестать получать сообщения от некоторых контактов.</string>
|
||||||
<string name="stop_chat_to_enable_database_actions">Остановите чат, чтобы разблокировать операции с архивом чата.</string>
|
<string name="stop_chat_to_enable_database_actions">Остановите чат, чтобы разблокировать операции с архивом чата.</string>
|
||||||
|
<string name="files_section">ФАЙЛЫ</string>
|
||||||
|
<string name="delete_files_and_media">Удалить файлы и медиа</string>
|
||||||
|
<string name="delete_files_and_media_question">Удалить файлы и медиа?</string>
|
||||||
|
<string name="delete_files_and_media_desc">Это действие нельзя отменить — все полученные и отправленные файлы будут удалены. Изображения останутся в низком разрешении.</string>
|
||||||
|
<string name="no_received_app_files">Нет полученных или отправленных файлов</string>
|
||||||
|
<string name="total_files_count_and_size">%d файл(ов) общим размером %s</string>
|
||||||
|
|
||||||
<!-- DatabaseEncryptionView.kt -->
|
<!-- DatabaseEncryptionView.kt -->
|
||||||
<string name="save_passphrase_in_keychain">Сохранить пароль в Keystore</string>
|
<string name="save_passphrase_in_keychain">Сохранить пароль в Keystore</string>
|
||||||
|
|||||||
@@ -557,6 +557,12 @@
|
|||||||
<string name="restart_the_app_to_create_a_new_chat_profile">Restart the app to create a new chat profile.</string>
|
<string name="restart_the_app_to_create_a_new_chat_profile">Restart the app to create a new chat profile.</string>
|
||||||
<string name="you_must_use_the_most_recent_version_of_database">You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts.</string>
|
<string name="you_must_use_the_most_recent_version_of_database">You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts.</string>
|
||||||
<string name="stop_chat_to_enable_database_actions">Stop chat to enable database actions.</string>
|
<string name="stop_chat_to_enable_database_actions">Stop chat to enable database actions.</string>
|
||||||
|
<string name="files_section">FILES</string>
|
||||||
|
<string name="delete_files_and_media">Delete files \& media</string>
|
||||||
|
<string name="delete_files_and_media_question">Delete files and media?</string>
|
||||||
|
<string name="delete_files_and_media_desc">This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain.</string>
|
||||||
|
<string name="no_received_app_files">No received or sent files</string>
|
||||||
|
<string name="total_files_count_and_size">%d file(s) with total size of %s</string>
|
||||||
|
|
||||||
<!-- DatabaseEncryptionView.kt -->
|
<!-- DatabaseEncryptionView.kt -->
|
||||||
<string name="save_passphrase_in_keychain">Save passphrase in Keystore</string>
|
<string name="save_passphrase_in_keychain">Save passphrase in Keystore</string>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ enum DatabaseAlert: Identifiable {
|
|||||||
case deleteChat
|
case deleteChat
|
||||||
case chatDeleted
|
case chatDeleted
|
||||||
case deleteLegacyDatabase
|
case deleteLegacyDatabase
|
||||||
|
case deleteFilesAndMedia
|
||||||
case error(title: LocalizedStringKey, error: String = "")
|
case error(title: LocalizedStringKey, error: String = "")
|
||||||
|
|
||||||
var id: String {
|
var id: String {
|
||||||
@@ -28,6 +29,7 @@ enum DatabaseAlert: Identifiable {
|
|||||||
case .deleteChat: return "deleteChat"
|
case .deleteChat: return "deleteChat"
|
||||||
case .chatDeleted: return "chatDeleted"
|
case .chatDeleted: return "chatDeleted"
|
||||||
case .deleteLegacyDatabase: return "deleteLegacyDatabase"
|
case .deleteLegacyDatabase: return "deleteLegacyDatabase"
|
||||||
|
case .deleteFilesAndMedia: return "deleteFilesAndMedia"
|
||||||
case let .error(title, _): return "error \(title)"
|
case let .error(title, _): return "error \(title)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,6 +48,7 @@ struct DatabaseView: View {
|
|||||||
@State private var dbContainer = dbContainerGroupDefault.get()
|
@State private var dbContainer = dbContainerGroupDefault.get()
|
||||||
@State private var legacyDatabase = hasLegacyDatabase()
|
@State private var legacyDatabase = hasLegacyDatabase()
|
||||||
@State private var useKeychain = storeDBPassphraseGroupDefault.get()
|
@State private var useKeychain = storeDBPassphraseGroupDefault.get()
|
||||||
|
@State private var appFilesCountAndSize: (Int, Int)?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
@@ -147,8 +150,28 @@ struct DatabaseView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
Button("Delete files & media", role: .destructive) {
|
||||||
|
alert = .deleteFilesAndMedia
|
||||||
|
}
|
||||||
|
} header: {
|
||||||
|
Text("Files")
|
||||||
|
} footer: {
|
||||||
|
if let (fileCount, size) = appFilesCountAndSize {
|
||||||
|
if fileCount == 0 {
|
||||||
|
Text("No received or sent files")
|
||||||
|
} else {
|
||||||
|
Text("\(fileCount) file(s) with total size of \(ByteCountFormatter().string(fromByteCount: Int64(size)))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.disabled(!stopped || appFilesCountAndSize?.0 == 0)
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
runChat = m.chatRunning ?? true
|
||||||
|
appFilesCountAndSize = directoryFileCountAndSize(getAppFilesDirectory())
|
||||||
}
|
}
|
||||||
.onAppear { runChat = m.chatRunning ?? true }
|
|
||||||
.alert(item: $alert) { item in databaseAlert(item) }
|
.alert(item: $alert) { item in databaseAlert(item) }
|
||||||
.fileImporter(
|
.fileImporter(
|
||||||
isPresented: $showFileImporter,
|
isPresented: $showFileImporter,
|
||||||
@@ -222,6 +245,15 @@ struct DatabaseView: View {
|
|||||||
},
|
},
|
||||||
secondaryButton: .cancel()
|
secondaryButton: .cancel()
|
||||||
)
|
)
|
||||||
|
case .deleteFilesAndMedia:
|
||||||
|
return Alert(
|
||||||
|
title: Text("Delete files and media?"),
|
||||||
|
message: Text("This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain."),
|
||||||
|
primaryButton: .destructive(Text("Delete")) {
|
||||||
|
deleteFiles()
|
||||||
|
},
|
||||||
|
secondaryButton: .cancel()
|
||||||
|
)
|
||||||
case let .error(title, error):
|
case let .error(title, error):
|
||||||
return Alert(title: Text(title), message: Text("\(error)"))
|
return Alert(title: Text(title), message: Text("\(error)"))
|
||||||
}
|
}
|
||||||
@@ -354,6 +386,11 @@ struct DatabaseView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func deleteFiles() {
|
||||||
|
deleteAppFiles()
|
||||||
|
appFilesCountAndSize = directoryFileCountAndSize(getAppFilesDirectory())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DatabaseView_Previews: PreviewProvider {
|
struct DatabaseView_Previews: PreviewProvider {
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ private let maxItemSize: Int = 50000
|
|||||||
|
|
||||||
struct TerminalView: View {
|
struct TerminalView: View {
|
||||||
@EnvironmentObject var chatModel: ChatModel
|
@EnvironmentObject var chatModel: ChatModel
|
||||||
|
@AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false
|
||||||
|
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
|
||||||
@State var composeState: ComposeState = ComposeState()
|
@State var composeState: ComposeState = ComposeState()
|
||||||
@FocusState private var keyboardVisible: Bool
|
@FocusState private var keyboardVisible: Bool
|
||||||
@State var authorized = !UserDefaults.standard.bool(forKey: DEFAULT_PERFORM_LA)
|
@State var authorized = !UserDefaults.standard.bool(forKey: DEFAULT_PERFORM_LA)
|
||||||
@@ -103,11 +105,19 @@ struct TerminalView: View {
|
|||||||
|
|
||||||
func sendMessage() {
|
func sendMessage() {
|
||||||
let cmd = ChatCommand.string(composeState.message)
|
let cmd = ChatCommand.string(composeState.message)
|
||||||
DispatchQueue.global().async {
|
if composeState.message.starts(with: "/sql") && (!prefPerformLA || !developerTools) {
|
||||||
Task {
|
let resp = ChatResponse.chatCmdError(chatError: ChatError.error(errorType: ChatErrorType.commandError(message: "Failed reading: empty")))
|
||||||
composeState.inProgress = true
|
DispatchQueue.main.async {
|
||||||
_ = await chatSendCmd(cmd)
|
ChatModel.shared.terminalItems.append(.cmd(.now, cmd))
|
||||||
composeState.inProgress = false
|
ChatModel.shared.terminalItems.append(.resp(.now, resp))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatchQueue.global().async {
|
||||||
|
Task {
|
||||||
|
composeState.inProgress = true
|
||||||
|
_ = await chatSendCmd(cmd)
|
||||||
|
composeState.inProgress = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
composeState = ComposeState()
|
composeState = ComposeState()
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd">
|
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd">
|
||||||
<file original="en.lproj/Localizable.strings" source-language="en" target-language="en" datatype="plaintext">
|
<file original="en.lproj/Localizable.strings" source-language="en" target-language="en" datatype="plaintext">
|
||||||
<header>
|
<header>
|
||||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="13.3" build-num="13E113"/>
|
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.0" build-num="14A309"/>
|
||||||
</header>
|
</header>
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id=" " xml:space="preserve">
|
<trans-unit id=" " xml:space="preserve">
|
||||||
@@ -82,6 +82,11 @@
|
|||||||
<target>%lld contact(s) selected</target>
|
<target>%lld contact(s) selected</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%lld file(s) with total size of %@" xml:space="preserve">
|
||||||
|
<source>%lld file(s) with total size of %@</source>
|
||||||
|
<target>%lld file(s) with total size of %@</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="%lld members" xml:space="preserve">
|
<trans-unit id="%lld members" xml:space="preserve">
|
||||||
<source>%lld members</source>
|
<source>%lld members</source>
|
||||||
<target>%lld members</target>
|
<target>%lld members</target>
|
||||||
@@ -701,6 +706,16 @@
|
|||||||
<target>Delete database</target>
|
<target>Delete database</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Delete files & media" xml:space="preserve">
|
||||||
|
<source>Delete files & media</source>
|
||||||
|
<target>Delete files & media</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Delete files and media?" xml:space="preserve">
|
||||||
|
<source>Delete files and media?</source>
|
||||||
|
<target>Delete files and media?</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Delete for everyone" xml:space="preserve">
|
<trans-unit id="Delete for everyone" xml:space="preserve">
|
||||||
<source>Delete for everyone</source>
|
<source>Delete for everyone</source>
|
||||||
<target>Delete for everyone</target>
|
<target>Delete for everyone</target>
|
||||||
@@ -1016,6 +1031,11 @@
|
|||||||
<target>File: %@</target>
|
<target>File: %@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Files" xml:space="preserve">
|
||||||
|
<source>Files</source>
|
||||||
|
<target>Files</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="For console" xml:space="preserve">
|
<trans-unit id="For console" xml:space="preserve">
|
||||||
<source>For console</source>
|
<source>For console</source>
|
||||||
<target>For console</target>
|
<target>For console</target>
|
||||||
@@ -1478,6 +1498,11 @@ We will be adding server redundancy to prevent lost messages.</target>
|
|||||||
<target>Group not found!</target>
|
<target>Group not found!</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="No received or sent files" xml:space="preserve">
|
||||||
|
<source>No received or sent files</source>
|
||||||
|
<target>No received or sent files</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Notifications" xml:space="preserve">
|
<trans-unit id="Notifications" xml:space="preserve">
|
||||||
<source>Notifications</source>
|
<source>Notifications</source>
|
||||||
<target>Notifications</target>
|
<target>Notifications</target>
|
||||||
@@ -2103,6 +2128,11 @@ We will be adding server redundancy to prevent lost messages.</target>
|
|||||||
<target>Theme</target>
|
<target>Theme</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." xml:space="preserve">
|
||||||
|
<source>This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain.</source>
|
||||||
|
<target>This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain.</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." xml:space="preserve">
|
<trans-unit id="This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." xml:space="preserve">
|
||||||
<source>This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost.</source>
|
<source>This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost.</source>
|
||||||
<target>This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost.</target>
|
<target>This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost.</target>
|
||||||
@@ -2995,7 +3025,7 @@ SimpleX servers cannot see your profile.</target>
|
|||||||
</file>
|
</file>
|
||||||
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="en" datatype="plaintext">
|
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="en" datatype="plaintext">
|
||||||
<header>
|
<header>
|
||||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="13.3" build-num="13E113"/>
|
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.0" build-num="14A309"/>
|
||||||
</header>
|
</header>
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="CFBundleName" xml:space="preserve">
|
<trans-unit id="CFBundleName" xml:space="preserve">
|
||||||
@@ -3027,7 +3057,7 @@ SimpleX servers cannot see your profile.</target>
|
|||||||
</file>
|
</file>
|
||||||
<file original="SimpleX NSE/en.lproj/InfoPlist.strings" source-language="en" target-language="en" datatype="plaintext">
|
<file original="SimpleX NSE/en.lproj/InfoPlist.strings" source-language="en" target-language="en" datatype="plaintext">
|
||||||
<header>
|
<header>
|
||||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="13.3" build-num="13E113"/>
|
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.0" build-num="14A309"/>
|
||||||
</header>
|
</header>
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="CFBundleDisplayName" xml:space="preserve">
|
<trans-unit id="CFBundleDisplayName" xml:space="preserve">
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
"project" : "SimpleX.xcodeproj",
|
"project" : "SimpleX.xcodeproj",
|
||||||
"targetLocale" : "en",
|
"targetLocale" : "en",
|
||||||
"toolInfo" : {
|
"toolInfo" : {
|
||||||
"toolBuildNumber" : "13E113",
|
"toolBuildNumber" : "14A309",
|
||||||
"toolID" : "com.apple.dt.xcode",
|
"toolID" : "com.apple.dt.xcode",
|
||||||
"toolName" : "Xcode",
|
"toolName" : "Xcode",
|
||||||
"toolVersion" : "13.3"
|
"toolVersion" : "14.0"
|
||||||
},
|
},
|
||||||
"version" : "1.0"
|
"version" : "1.0"
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd">
|
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd">
|
||||||
<file original="en.lproj/Localizable.strings" source-language="en" target-language="ru" datatype="plaintext">
|
<file original="en.lproj/Localizable.strings" source-language="en" target-language="ru" datatype="plaintext">
|
||||||
<header>
|
<header>
|
||||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="13.3" build-num="13E113"/>
|
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.0" build-num="14A309"/>
|
||||||
</header>
|
</header>
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id=" " xml:space="preserve">
|
<trans-unit id=" " xml:space="preserve">
|
||||||
@@ -82,6 +82,11 @@
|
|||||||
<target>Выбрано контактов: %lld</target>
|
<target>Выбрано контактов: %lld</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%lld file(s) with total size of %@" xml:space="preserve">
|
||||||
|
<source>%lld file(s) with total size of %@</source>
|
||||||
|
<target>%lld файл(ов) общим размером %@</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="%lld members" xml:space="preserve">
|
<trans-unit id="%lld members" xml:space="preserve">
|
||||||
<source>%lld members</source>
|
<source>%lld members</source>
|
||||||
<target>Членов группы: %lld</target>
|
<target>Членов группы: %lld</target>
|
||||||
@@ -701,6 +706,16 @@
|
|||||||
<target>Удалить данные чата</target>
|
<target>Удалить данные чата</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Delete files & media" xml:space="preserve">
|
||||||
|
<source>Delete files & media</source>
|
||||||
|
<target>Удалить файлы и медиа</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Delete files and media?" xml:space="preserve">
|
||||||
|
<source>Delete files and media?</source>
|
||||||
|
<target>Удалить файлы и медиа?</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Delete for everyone" xml:space="preserve">
|
<trans-unit id="Delete for everyone" xml:space="preserve">
|
||||||
<source>Delete for everyone</source>
|
<source>Delete for everyone</source>
|
||||||
<target>Удалить для всех</target>
|
<target>Удалить для всех</target>
|
||||||
@@ -1016,6 +1031,11 @@
|
|||||||
<target>Файл: %@</target>
|
<target>Файл: %@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Files" xml:space="preserve">
|
||||||
|
<source>Files</source>
|
||||||
|
<target>Файлы</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="For console" xml:space="preserve">
|
<trans-unit id="For console" xml:space="preserve">
|
||||||
<source>For console</source>
|
<source>For console</source>
|
||||||
<target>Для консоли</target>
|
<target>Для консоли</target>
|
||||||
@@ -1478,6 +1498,11 @@ We will be adding server redundancy to prevent lost messages.</source>
|
|||||||
<target>Группа не найдена!</target>
|
<target>Группа не найдена!</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="No received or sent files" xml:space="preserve">
|
||||||
|
<source>No received or sent files</source>
|
||||||
|
<target>Нет полученных или отправленных файлов</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Notifications" xml:space="preserve">
|
<trans-unit id="Notifications" xml:space="preserve">
|
||||||
<source>Notifications</source>
|
<source>Notifications</source>
|
||||||
<target>Уведомления</target>
|
<target>Уведомления</target>
|
||||||
@@ -2103,6 +2128,11 @@ We will be adding server redundancy to prevent lost messages.</source>
|
|||||||
<target>Тема</target>
|
<target>Тема</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." xml:space="preserve">
|
||||||
|
<source>This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain.</source>
|
||||||
|
<target>Это действие нельзя отменить — все полученные и отправленные файлы будут удалены. Изображения останутся в низком разрешении.</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." xml:space="preserve">
|
<trans-unit id="This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." xml:space="preserve">
|
||||||
<source>This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost.</source>
|
<source>This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost.</source>
|
||||||
<target>Это действие нельзя отменить — ваш профиль, контакты, сообщения и файлы будут безвозвратно утеряны.</target>
|
<target>Это действие нельзя отменить — ваш профиль, контакты, сообщения и файлы будут безвозвратно утеряны.</target>
|
||||||
@@ -2995,7 +3025,7 @@ SimpleX серверы не могут получить доступ к ваше
|
|||||||
</file>
|
</file>
|
||||||
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="ru" datatype="plaintext">
|
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="ru" datatype="plaintext">
|
||||||
<header>
|
<header>
|
||||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="13.3" build-num="13E113"/>
|
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.0" build-num="14A309"/>
|
||||||
</header>
|
</header>
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="CFBundleName" xml:space="preserve">
|
<trans-unit id="CFBundleName" xml:space="preserve">
|
||||||
@@ -3027,7 +3057,7 @@ SimpleX серверы не могут получить доступ к ваше
|
|||||||
</file>
|
</file>
|
||||||
<file original="SimpleX NSE/en.lproj/InfoPlist.strings" source-language="en" target-language="ru" datatype="plaintext">
|
<file original="SimpleX NSE/en.lproj/InfoPlist.strings" source-language="en" target-language="ru" datatype="plaintext">
|
||||||
<header>
|
<header>
|
||||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="13.3" build-num="13E113"/>
|
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.0" build-num="14A309"/>
|
||||||
</header>
|
</header>
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="CFBundleDisplayName" xml:space="preserve">
|
<trans-unit id="CFBundleDisplayName" xml:space="preserve">
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
"project" : "SimpleX.xcodeproj",
|
"project" : "SimpleX.xcodeproj",
|
||||||
"targetLocale" : "ru",
|
"targetLocale" : "ru",
|
||||||
"toolInfo" : {
|
"toolInfo" : {
|
||||||
"toolBuildNumber" : "13E113",
|
"toolBuildNumber" : "14A309",
|
||||||
"toolID" : "com.apple.dt.xcode",
|
"toolID" : "com.apple.dt.xcode",
|
||||||
"toolName" : "Xcode",
|
"toolName" : "Xcode",
|
||||||
"toolVersion" : "13.3"
|
"toolVersion" : "14.0"
|
||||||
},
|
},
|
||||||
"version" : "1.0"
|
"version" : "1.0"
|
||||||
}
|
}
|
||||||
@@ -1178,7 +1178,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 71;
|
CURRENT_PROJECT_VERSION = 73;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -1199,7 +1199,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = "4.0-beta.0";
|
MARKETING_VERSION = 4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||||
PRODUCT_NAME = SimpleX;
|
PRODUCT_NAME = SimpleX;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -1220,7 +1220,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 71;
|
CURRENT_PROJECT_VERSION = 73;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -1241,7 +1241,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = "4.0-beta.0";
|
MARKETING_VERSION = 4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||||
PRODUCT_NAME = SimpleX;
|
PRODUCT_NAME = SimpleX;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -1299,7 +1299,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 71;
|
CURRENT_PROJECT_VERSION = 73;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@@ -1312,7 +1312,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = "4.0-beta.0";
|
MARKETING_VERSION = 4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -1329,7 +1329,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 71;
|
CURRENT_PROJECT_VERSION = 73;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@@ -1342,7 +1342,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = "4.0-beta.0";
|
MARKETING_VERSION = 4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -1384,7 +1384,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Libraries/sim",
|
"$(PROJECT_DIR)/Libraries/sim",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = "4.0-beta.0";
|
MARKETING_VERSION = 4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -1430,7 +1430,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Libraries/sim",
|
"$(PROJECT_DIR)/Libraries/sim",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = "4.0-beta.0";
|
MARKETING_VERSION = 4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
|||||||
@@ -61,6 +61,46 @@ func fileModificationDate(_ path: String) -> Date? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func deleteAppFiles() {
|
||||||
|
let fm = FileManager.default
|
||||||
|
do {
|
||||||
|
let fileNames = try fm.contentsOfDirectory(atPath: getAppFilesDirectory().path)
|
||||||
|
for fileName in fileNames {
|
||||||
|
removeFile(fileName)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
logger.error("FileUtils deleteAppFiles error: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileSize(_ url: URL) -> Int? { // in bytes
|
||||||
|
do {
|
||||||
|
let val = try url.resourceValues(forKeys: [.totalFileAllocatedSizeKey, .fileAllocatedSizeKey])
|
||||||
|
return val.totalFileAllocatedSize ?? val.fileAllocatedSize
|
||||||
|
} catch {
|
||||||
|
logger.error("FileUtils fileSize error: \(error.localizedDescription)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func directoryFileCountAndSize(_ dir: URL) -> (Int, Int)? { // size in bytes
|
||||||
|
let fm = FileManager.default
|
||||||
|
if let enumerator = fm.enumerator(at: dir, includingPropertiesForKeys: [.totalFileAllocatedSizeKey, .fileAllocatedSizeKey], options: [], errorHandler: { (_, error) -> Bool in
|
||||||
|
logger.error("FileUtils directoryFileCountAndSize error: \(error.localizedDescription)")
|
||||||
|
return false
|
||||||
|
}) {
|
||||||
|
var fileCount = 0
|
||||||
|
var bytes = 0
|
||||||
|
for case let url as URL in enumerator {
|
||||||
|
fileCount += 1
|
||||||
|
bytes += fileSize(url) ?? 0
|
||||||
|
}
|
||||||
|
return (fileCount, bytes)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func hasBackup(newerThan date: Date) -> Bool {
|
public func hasBackup(newerThan date: Date) -> Bool {
|
||||||
let dbPath = getAppDatabasePath().path
|
let dbPath = getAppDatabasePath().path
|
||||||
return hasBackupFile(dbPath + AGENT_DB_BAK, newerThan: date)
|
return hasBackupFile(dbPath + AGENT_DB_BAK, newerThan: date)
|
||||||
|
|||||||
@@ -97,6 +97,9 @@
|
|||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"%lld contact(s) selected" = "Выбрано контактов: %lld";
|
"%lld contact(s) selected" = "Выбрано контактов: %lld";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"%lld file(s) with total size of %@" = "%lld файл(ов) общим размером %@";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"%lld members" = "Членов группы: %lld";
|
"%lld members" = "Членов группы: %lld";
|
||||||
|
|
||||||
@@ -500,6 +503,12 @@
|
|||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Delete database" = "Удалить данные чата";
|
"Delete database" = "Удалить данные чата";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Delete files & media" = "Удалить файлы и медиа";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Delete files and media?" = "Удалить файлы и медиа?";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Delete for everyone" = "Удалить для всех";
|
"Delete for everyone" = "Удалить для всех";
|
||||||
|
|
||||||
@@ -713,6 +722,9 @@
|
|||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"File: %@" = "Файл: %@";
|
"File: %@" = "Файл: %@";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Files" = "Файлы";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"For console" = "Для консоли";
|
"For console" = "Для консоли";
|
||||||
|
|
||||||
@@ -1043,6 +1055,9 @@
|
|||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"No group!" = "Группа не найдена!";
|
"No group!" = "Группа не найдена!";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"No received or sent files" = "Нет полученных или отправленных файлов";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Notifications" = "Уведомления";
|
"Notifications" = "Уведомления";
|
||||||
|
|
||||||
@@ -1457,6 +1472,9 @@
|
|||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Theme" = "Тема";
|
"Theme" = "Тема";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Это действие нельзя отменить — все полученные и отправленные файлы будут удалены. Изображения останутся в низком разрешении.";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." = "Это действие нельзя отменить — ваш профиль, контакты, сообщения и файлы будут безвозвратно утеряны.";
|
"This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." = "Это действие нельзя отменить — ваш профиль, контакты, сообщения и файлы будут безвозвратно утеряны.";
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ constraints: zip +disable-bzip2 +disable-zstd
|
|||||||
source-repository-package
|
source-repository-package
|
||||||
type: git
|
type: git
|
||||||
location: https://github.com/simplex-chat/simplexmq.git
|
location: https://github.com/simplex-chat/simplexmq.git
|
||||||
tag: a3f58fdc6b5b6bc135c6db4c3bc301869174854b
|
tag: 41d3c14157bb621b7e6a01eddd14419ef6c24de3
|
||||||
|
|
||||||
source-repository-package
|
source-repository-package
|
||||||
type: git
|
type: git
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: simplex-chat
|
name: simplex-chat
|
||||||
version: 3.2.1
|
version: 4.0.0
|
||||||
#synopsis:
|
#synopsis:
|
||||||
#description:
|
#description:
|
||||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"https://github.com/simplex-chat/simplexmq.git"."a3f58fdc6b5b6bc135c6db4c3bc301869174854b" = "045fj37vdwi31fda2g7y9dzc7jzqwil1x705c71mgg8pnzmsphr7";
|
"https://github.com/simplex-chat/simplexmq.git"."41d3c14157bb621b7e6a01eddd14419ef6c24de3" = "1phdb4y6vrja6ina3j2jks4a7xwb6sak9wg8kr3b1vp01bgdrjzg";
|
||||||
"https://github.com/simplex-chat/direct-sqlcipher.git"."34309410eb2069b029b8fc1872deb1e0db123294" = "0kwkmhyfsn2lixdlgl15smgr1h5gjk7fky6abzh8rng2h5ymnffd";
|
"https://github.com/simplex-chat/direct-sqlcipher.git"."34309410eb2069b029b8fc1872deb1e0db123294" = "0kwkmhyfsn2lixdlgl15smgr1h5gjk7fky6abzh8rng2h5ymnffd";
|
||||||
"https://github.com/simplex-chat/sqlcipher-simple.git"."5e154a2aeccc33ead6c243ec07195ab673137221" = "1d1gc5wax4vqg0801ajsmx1sbwvd9y7p7b8mmskvqsmpbwgbh0m0";
|
"https://github.com/simplex-chat/sqlcipher-simple.git"."5e154a2aeccc33ead6c243ec07195ab673137221" = "1d1gc5wax4vqg0801ajsmx1sbwvd9y7p7b8mmskvqsmpbwgbh0m0";
|
||||||
"https://github.com/simplex-chat/aeson.git"."3eb66f9a68f103b5f1489382aad89f5712a64db7" = "0kilkx59fl6c3qy3kjczqvm8c3f4n3p0bdk9biyflf51ljnzp4yp";
|
"https://github.com/simplex-chat/aeson.git"."3eb66f9a68f103b5f1489382aad89f5712a64db7" = "0kilkx59fl6c3qy3kjczqvm8c3f4n3p0bdk9biyflf51ljnzp4yp";
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ cabal-version: 1.12
|
|||||||
-- see: https://github.com/sol/hpack
|
-- see: https://github.com/sol/hpack
|
||||||
|
|
||||||
name: simplex-chat
|
name: simplex-chat
|
||||||
version: 3.2.1
|
version: 4.0.0
|
||||||
category: Web, System, Services, Cryptography
|
category: Web, System, Services, Cryptography
|
||||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||||
author: simplex.chat
|
author: simplex.chat
|
||||||
|
|||||||
@@ -259,6 +259,15 @@ processChatCommand = \case
|
|||||||
setActive $ ActiveC c
|
setActive $ ActiveC c
|
||||||
pure . CRNewChatItem $ AChatItem SCTDirect SMDSnd (DirectChat ct) ci
|
pure . CRNewChatItem $ AChatItem SCTDirect SMDSnd (DirectChat ct) ci
|
||||||
where
|
where
|
||||||
|
-- This method creates file invitation without connection request - it has to be accepted with x.acpt.file.inv message sent back to the contact
|
||||||
|
-- setupSndFileTransfer' :: Contact -> m (Maybe (FileInvitation, CIFile 'MDSnd))
|
||||||
|
-- setupSndFileTransfer' ct = forM file_ $ \file -> do
|
||||||
|
-- (fileSize, chSize) <- checkSndFile file
|
||||||
|
-- let fileName = takeFileName file
|
||||||
|
-- fileInvitation = FileInvitation {fileName, fileSize, fileConnReq = Nothing}
|
||||||
|
-- fileId <- withStore' $ \db -> createSndDirectFileTransfer db userId ct file fileInvitation chSize
|
||||||
|
-- let ciFile = CIFile {fileId, fileName, fileSize, filePath = Just file, fileStatus = CIFSSndStored}
|
||||||
|
-- pure (fileInvitation, ciFile)
|
||||||
setupSndFileTransfer :: Contact -> m (Maybe (FileInvitation, CIFile 'MDSnd))
|
setupSndFileTransfer :: Contact -> m (Maybe (FileInvitation, CIFile 'MDSnd))
|
||||||
setupSndFileTransfer ct = forM file_ $ \file -> do
|
setupSndFileTransfer ct = forM file_ $ \file -> do
|
||||||
(fileSize, chSize) <- checkSndFile file
|
(fileSize, chSize) <- checkSndFile file
|
||||||
@@ -1121,30 +1130,37 @@ acceptFileReceive user@User {userId} RcvFileTransfer {fileId, fileInvitation = F
|
|||||||
_ -> throwChatError $ CEFileAlreadyReceiving fName
|
_ -> throwChatError $ CEFileAlreadyReceiving fName
|
||||||
case fileConnReq of
|
case fileConnReq of
|
||||||
-- direct file protocol
|
-- direct file protocol
|
||||||
Just connReq ->
|
Just connReq -> do
|
||||||
-- [async agent commands] keep command synchronous, but process error
|
-- [async agent commands] keep command synchronous, but process error
|
||||||
tryError (withAgent $ \a -> joinConnection a True connReq . directMessage $ XFileAcpt fName) >>= \case
|
agentConnId <- withAgent $ \a -> joinConnection a True connReq . directMessage $ XFileAcpt fName
|
||||||
Right agentConnId -> do
|
filePath <- getRcvFilePath filePath_ fName
|
||||||
filePath <- getRcvFilePath filePath_ fName
|
withStore $ \db -> acceptRcvFileTransfer db user fileId agentConnId ConnJoined filePath
|
||||||
withStore $ \db -> acceptRcvFileTransfer db user fileId agentConnId ConnJoined filePath
|
-- group & direct file protocol
|
||||||
Left e -> throwError e
|
Nothing -> do
|
||||||
-- group file protocol
|
chatRef <- withStore $ \db -> getChatRefByFileId db user fileId
|
||||||
Nothing ->
|
case (chatRef, grpMemberId) of
|
||||||
case grpMemberId of
|
(ChatRef CTDirect contactId, Nothing) -> do
|
||||||
Nothing -> throwChatError $ CEFileInternal "group member not found for file transfer"
|
ct <- withStore $ \db -> getContact db userId contactId
|
||||||
Just memId -> do
|
(msg, ci) <- acceptFile
|
||||||
(GroupInfo {groupId}, GroupMember {activeConn}) <- withStore $ \db -> getGroupAndMember db user memId
|
void $ sendDirectContactMessage ct msg
|
||||||
|
pure ci
|
||||||
|
(ChatRef CTGroup groupId, Just memId) -> do
|
||||||
|
GroupMember {activeConn} <- withStore $ \db -> getGroupMember db user groupId memId
|
||||||
case activeConn of
|
case activeConn of
|
||||||
Just conn -> do
|
Just conn -> do
|
||||||
sharedMsgId <- withStore $ \db -> getSharedMsgIdByFileId db userId fileId
|
(msg, ci) <- acceptFile
|
||||||
-- [async agent commands] keep command synchronous, but process error
|
void $ sendDirectMessage conn msg $ GroupId groupId
|
||||||
(agentConnId, fileInvConnReq) <- withAgent $ \a -> createConnection a True SCMInvitation
|
|
||||||
filePath <- getRcvFilePath filePath_ fName
|
|
||||||
ci <- withStore $ \db -> acceptRcvFileTransfer db user fileId agentConnId ConnNew filePath
|
|
||||||
void $ sendDirectMessage conn (XFileAcptInv sharedMsgId fileInvConnReq fName) (GroupId groupId)
|
|
||||||
pure ci
|
pure ci
|
||||||
_ -> throwChatError $ CEFileInternal "member connection not active"
|
_ -> throwChatError $ CEFileInternal "member connection not active"
|
||||||
|
_ -> throwChatError $ CEFileInternal "invalid chat ref for file transfer"
|
||||||
where
|
where
|
||||||
|
acceptFile :: m (ChatMsgEvent, AChatItem)
|
||||||
|
acceptFile = do
|
||||||
|
sharedMsgId <- withStore $ \db -> getSharedMsgIdByFileId db userId fileId
|
||||||
|
(agentConnId, fileInvConnReq) <- withAgent $ \a -> createConnection a True SCMInvitation
|
||||||
|
filePath <- getRcvFilePath filePath_ fName
|
||||||
|
ci <- withStore (\db -> acceptRcvFileTransfer db user fileId agentConnId ConnNew filePath)
|
||||||
|
pure (XFileAcptInv sharedMsgId fileInvConnReq fName, ci)
|
||||||
getRcvFilePath :: Maybe FilePath -> String -> m FilePath
|
getRcvFilePath :: Maybe FilePath -> String -> m FilePath
|
||||||
getRcvFilePath fPath_ fn = case fPath_ of
|
getRcvFilePath fPath_ fn = case fPath_ of
|
||||||
Nothing ->
|
Nothing ->
|
||||||
@@ -1416,6 +1432,7 @@ processAgentMessage (Just user@User {userId, profile}) corrId agentConnId agentM
|
|||||||
-- TODO discontinue XFile
|
-- TODO discontinue XFile
|
||||||
XFile fInv -> processFileInvitation' ct fInv msg msgMeta
|
XFile fInv -> processFileInvitation' ct fInv msg msgMeta
|
||||||
XFileCancel sharedMsgId -> xFileCancel ct sharedMsgId msgMeta
|
XFileCancel sharedMsgId -> xFileCancel ct sharedMsgId msgMeta
|
||||||
|
XFileAcptInv sharedMsgId fileConnReq fName -> xFileAcptInv ct sharedMsgId fileConnReq fName msgMeta
|
||||||
XInfo p -> xInfo ct p
|
XInfo p -> xInfo ct p
|
||||||
XGrpInv gInv -> processGroupInvitation ct gInv msg msgMeta
|
XGrpInv gInv -> processGroupInvitation ct gInv msg msgMeta
|
||||||
XInfoProbe probe -> xInfoProbe ct probe
|
XInfoProbe probe -> xInfoProbe ct probe
|
||||||
@@ -1956,6 +1973,18 @@ processAgentMessage (Just user@User {userId, profile}) corrId agentConnId agentM
|
|||||||
cancelRcvFileTransfer user ft
|
cancelRcvFileTransfer user ft
|
||||||
toView $ CRRcvFileSndCancelled ft
|
toView $ CRRcvFileSndCancelled ft
|
||||||
|
|
||||||
|
xFileAcptInv :: Contact -> SharedMsgId -> ConnReqInvitation -> String -> MsgMeta -> m ()
|
||||||
|
xFileAcptInv ct sharedMsgId fileConnReq fName msgMeta = do
|
||||||
|
checkIntegrityCreateItem (CDDirectRcv ct) msgMeta
|
||||||
|
fileId <- withStore $ \db -> getDirectFileIdBySharedMsgId db user ct sharedMsgId
|
||||||
|
(FileTransferMeta {fileName, cancelled}, _) <- withStore (\db -> getSndFileTransfer db user fileId)
|
||||||
|
-- [async agent commands] no continuation needed, but command should be asynchronous for stability
|
||||||
|
if fName == fileName
|
||||||
|
then unless cancelled $ do
|
||||||
|
connIds <- joinAgentConnectionAsync user True fileConnReq $ directMessage XOk
|
||||||
|
withStore' $ \db -> createSndDirectFTConnection db user fileId connIds
|
||||||
|
else messageError "x.file.acpt.inv: fileName is different from expected"
|
||||||
|
|
||||||
xFileCancelGroup :: GroupInfo -> GroupMember -> SharedMsgId -> MsgMeta -> m ()
|
xFileCancelGroup :: GroupInfo -> GroupMember -> SharedMsgId -> MsgMeta -> m ()
|
||||||
xFileCancelGroup g@GroupInfo {groupId} mem@GroupMember {memberId} sharedMsgId msgMeta = do
|
xFileCancelGroup g@GroupInfo {groupId} mem@GroupMember {memberId} sharedMsgId msgMeta = do
|
||||||
checkIntegrityCreateItem (CDGroupRcv g mem) msgMeta
|
checkIntegrityCreateItem (CDGroupRcv g mem) msgMeta
|
||||||
@@ -1977,17 +2006,12 @@ processAgentMessage (Just user@User {userId, profile}) corrId agentConnId agentM
|
|||||||
checkIntegrityCreateItem (CDGroupRcv g m) msgMeta
|
checkIntegrityCreateItem (CDGroupRcv g m) msgMeta
|
||||||
fileId <- withStore $ \db -> getGroupFileIdBySharedMsgId db userId groupId sharedMsgId
|
fileId <- withStore $ \db -> getGroupFileIdBySharedMsgId db userId groupId sharedMsgId
|
||||||
(FileTransferMeta {fileName, cancelled}, _) <- withStore (\db -> getSndFileTransfer db user fileId)
|
(FileTransferMeta {fileName, cancelled}, _) <- withStore (\db -> getSndFileTransfer db user fileId)
|
||||||
unless cancelled $
|
if fName == fileName
|
||||||
if fName == fileName
|
then unless cancelled $ do
|
||||||
then
|
-- [async agent commands] no continuation needed, but command should be asynchronous for stability
|
||||||
tryError
|
connIds <- joinAgentConnectionAsync user True fileConnReq $ directMessage XOk
|
||||||
-- [async agent commands] no continuation needed, but command should be asynchronous for stability
|
withStore' $ \db -> createSndGroupFileTransferConnection db user fileId connIds m
|
||||||
(joinAgentConnectionAsync user True fileConnReq . directMessage $ XOk)
|
else messageError "x.file.acpt.inv: fileName is different from expected"
|
||||||
>>= \case
|
|
||||||
Right connIds ->
|
|
||||||
withStore' $ \db -> createSndGroupFileTransferConnection db user fileId connIds m
|
|
||||||
Left e -> throwError e
|
|
||||||
else messageError "x.file.acpt.inv: fileName is different from expected"
|
|
||||||
|
|
||||||
groupMsgToView :: GroupInfo -> GroupMember -> ChatItem 'CTGroup 'MDRcv -> MsgMeta -> m ()
|
groupMsgToView :: GroupInfo -> GroupMember -> ChatItem 'CTGroup 'MDRcv -> MsgMeta -> m ()
|
||||||
groupMsgToView gInfo m ci msgMeta = do
|
groupMsgToView gInfo m ci msgMeta = do
|
||||||
|
|||||||
@@ -106,6 +106,8 @@ module Simplex.Chat.Store
|
|||||||
matchSentProbe,
|
matchSentProbe,
|
||||||
mergeContactRecords,
|
mergeContactRecords,
|
||||||
createSndFileTransfer,
|
createSndFileTransfer,
|
||||||
|
createSndDirectFileTransfer,
|
||||||
|
createSndDirectFTConnection,
|
||||||
createSndGroupFileTransfer,
|
createSndGroupFileTransfer,
|
||||||
createSndGroupFileTransferConnection,
|
createSndGroupFileTransferConnection,
|
||||||
updateFileCancelled,
|
updateFileCancelled,
|
||||||
@@ -113,6 +115,7 @@ module Simplex.Chat.Store
|
|||||||
getSharedMsgIdByFileId,
|
getSharedMsgIdByFileId,
|
||||||
getFileIdBySharedMsgId,
|
getFileIdBySharedMsgId,
|
||||||
getGroupFileIdBySharedMsgId,
|
getGroupFileIdBySharedMsgId,
|
||||||
|
getDirectFileIdBySharedMsgId,
|
||||||
getChatRefByFileId,
|
getChatRefByFileId,
|
||||||
updateSndFileStatus,
|
updateSndFileStatus,
|
||||||
createSndFileChunk,
|
createSndFileChunk,
|
||||||
@@ -2001,6 +2004,25 @@ createSndFileTransfer db userId Contact {contactId} filePath FileInvitation {fil
|
|||||||
(fileId, fileStatus, connId, currentTs, currentTs)
|
(fileId, fileStatus, connId, currentTs, currentTs)
|
||||||
pure fileId
|
pure fileId
|
||||||
|
|
||||||
|
createSndDirectFileTransfer :: DB.Connection -> UserId -> Contact -> FilePath -> FileInvitation -> Integer -> IO Int64
|
||||||
|
createSndDirectFileTransfer db userId Contact {contactId} filePath FileInvitation {fileName, fileSize} chunkSize = do
|
||||||
|
currentTs <- getCurrentTime
|
||||||
|
DB.execute
|
||||||
|
db
|
||||||
|
"INSERT INTO files (user_id, contact_id, file_name, file_path, file_size, chunk_size, ci_file_status, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)"
|
||||||
|
(userId, contactId, fileName, filePath, fileSize, chunkSize, CIFSSndStored, currentTs, currentTs)
|
||||||
|
insertedRowId db
|
||||||
|
|
||||||
|
createSndDirectFTConnection :: DB.Connection -> User -> Int64 -> (CommandId, ConnId) -> IO ()
|
||||||
|
createSndDirectFTConnection db user@User {userId} fileId (cmdId, acId) = do
|
||||||
|
currentTs <- getCurrentTime
|
||||||
|
Connection {connId} <- createSndFileConnection_ db userId fileId acId
|
||||||
|
setCommandConnId db user cmdId connId
|
||||||
|
DB.execute
|
||||||
|
db
|
||||||
|
"INSERT INTO snd_files (file_id, file_status, connection_id, created_at, updated_at) VALUES (?,?,?,?,?)"
|
||||||
|
(fileId, FSAccepted, connId, currentTs, currentTs)
|
||||||
|
|
||||||
createSndGroupFileTransfer :: DB.Connection -> UserId -> GroupInfo -> FilePath -> FileInvitation -> Integer -> IO Int64
|
createSndGroupFileTransfer :: DB.Connection -> UserId -> GroupInfo -> FilePath -> FileInvitation -> Integer -> IO Int64
|
||||||
createSndGroupFileTransfer db userId GroupInfo {groupId} filePath FileInvitation {fileName, fileSize} chunkSize = do
|
createSndGroupFileTransfer db userId GroupInfo {groupId} filePath FileInvitation {fileName, fileSize} chunkSize = do
|
||||||
currentTs <- getCurrentTime
|
currentTs <- getCurrentTime
|
||||||
@@ -2069,6 +2091,19 @@ getGroupFileIdBySharedMsgId db userId groupId sharedMsgId =
|
|||||||
|]
|
|]
|
||||||
(userId, groupId, sharedMsgId)
|
(userId, groupId, sharedMsgId)
|
||||||
|
|
||||||
|
getDirectFileIdBySharedMsgId :: DB.Connection -> User -> Contact -> SharedMsgId -> ExceptT StoreError IO Int64
|
||||||
|
getDirectFileIdBySharedMsgId db User {userId} Contact {contactId} sharedMsgId =
|
||||||
|
ExceptT . firstRow fromOnly (SEFileIdNotFoundBySharedMsgId sharedMsgId) $
|
||||||
|
DB.query
|
||||||
|
db
|
||||||
|
[sql|
|
||||||
|
SELECT f.file_id
|
||||||
|
FROM files f
|
||||||
|
JOIN chat_items i ON i.chat_item_id = f.chat_item_id
|
||||||
|
WHERE i.user_id = ? AND i.contact_id = ? AND i.shared_msg_id = ?
|
||||||
|
|]
|
||||||
|
(userId, contactId, sharedMsgId)
|
||||||
|
|
||||||
getChatRefByFileId :: DB.Connection -> User -> Int64 -> ExceptT StoreError IO ChatRef
|
getChatRefByFileId :: DB.Connection -> User -> Int64 -> ExceptT StoreError IO ChatRef
|
||||||
getChatRefByFileId db User {userId} fileId =
|
getChatRefByFileId db User {userId} fileId =
|
||||||
liftIO getChatRef >>= \case
|
liftIO getChatRef >>= \case
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ extra-deps:
|
|||||||
# - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561
|
# - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561
|
||||||
# - ../simplexmq
|
# - ../simplexmq
|
||||||
- github: simplex-chat/simplexmq
|
- github: simplex-chat/simplexmq
|
||||||
commit: a3f58fdc6b5b6bc135c6db4c3bc301869174854b
|
commit: 41d3c14157bb621b7e6a01eddd14419ef6c24de3
|
||||||
# - ../direct-sqlcipher
|
# - ../direct-sqlcipher
|
||||||
- github: simplex-chat/direct-sqlcipher
|
- github: simplex-chat/direct-sqlcipher
|
||||||
commit: 34309410eb2069b029b8fc1872deb1e0db123294
|
commit: 34309410eb2069b029b8fc1872deb1e0db123294
|
||||||
|
|||||||
Reference in New Issue
Block a user