diff --git a/apps/android/app/build.gradle b/apps/android/app/build.gradle index b68ca2fb50..e078820830 100644 --- a/apps/android/app/build.gradle +++ b/apps/android/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "chat.simplex.app" minSdk 29 targetSdk 32 - versionCode 90 - versionName "4.4.4-beta.1" + versionCode 91 + versionName "4.4.4" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" ndk { diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt index 4384c4257b..311b14536e 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt @@ -1057,6 +1057,16 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a } } + suspend fun apiGetVersion(): CoreVersionInfo? { + val r = sendCmd(CC.ShowVersion()) + return if (r is CR.VersionInfo) { + r.versionInfo + } else { + Log.e(TAG, "apiGetVersion bad response: ${r.responseType} ${r.details}") + null + } + } + private fun networkErrorAlert(r: CR): Boolean { return when { r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorAgent @@ -1677,6 +1687,7 @@ sealed class CC { class ApiChatRead(val type: ChatType, val id: Long, val range: ItemRange): CC() class ApiChatUnread(val type: ChatType, val id: Long, val unreadChat: Boolean): CC() class ReceiveFile(val fileId: Long, val inline: Boolean): CC() + class ShowVersion(): CC() val cmdString: String get() = when (this) { is Console -> cmd @@ -1748,6 +1759,7 @@ sealed class CC { is ApiChatRead -> "/_read chat ${chatRef(type, id)} from=${range.from} to=${range.to}" is ApiChatUnread -> "/_unread chat ${chatRef(type, id)} ${onOff(unreadChat)}" is ReceiveFile -> "/freceive $fileId inline=${onOff(inline)}" + is ShowVersion -> "/version" } val cmdType: String get() = when (this) { @@ -1820,6 +1832,7 @@ sealed class CC { is ApiChatRead -> "apiChatRead" is ApiChatUnread -> "apiChatUnread" is ReceiveFile -> "receiveFile" + is ShowVersion -> "showVersion" } class ItemRange(val from: Long, val to: Long) @@ -2815,6 +2828,7 @@ sealed class CR { @Serializable @SerialName("callEnded") class CallEnded(val contact: Contact): CR() @Serializable @SerialName("newContactConnection") class NewContactConnection(val connection: PendingContactConnection): CR() @Serializable @SerialName("contactConnectionDeleted") class ContactConnectionDeleted(val connection: PendingContactConnection): CR() + @Serializable @SerialName("versionInfo") class VersionInfo(val versionInfo: CoreVersionInfo): CR() @Serializable @SerialName("cmdOk") class CmdOk: CR() @Serializable @SerialName("chatCmdError") class ChatCmdError(val chatError: ChatError): CR() @Serializable @SerialName("chatError") class ChatRespError(val chatError: ChatError): CR() @@ -2913,6 +2927,7 @@ sealed class CR { is CallEnded -> "callEnded" is NewContactConnection -> "newContactConnection" is ContactConnectionDeleted -> "contactConnectionDeleted" + is VersionInfo -> "versionInfo" is CmdOk -> "cmdOk" is ChatCmdError -> "chatCmdError" is ChatRespError -> "chatError" @@ -3012,6 +3027,7 @@ sealed class CR { is CallEnded -> "contact: ${contact.id}" is NewContactConnection -> json.encodeToString(connection) is ContactConnectionDeleted -> json.encodeToString(connection) + is VersionInfo -> json.encodeToString(versionInfo) is CmdOk -> noDetails() is ChatCmdError -> chatError.string is ChatRespError -> chatError.string @@ -3069,6 +3085,13 @@ class AutoAccept(val acceptIncognito: Boolean, val autoReply: MsgContent?) { } } +@Serializable +data class CoreVersionInfo( + val version: String, + val buildTimestamp: String, + val simplexmqVersion: String, + val simplexmqCommit: String +) @Serializable sealed class ChatError { diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt index c64bb12b11..f2c9446187 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt @@ -5,6 +5,7 @@ import SectionItemView import SectionSpacer import SectionView import android.content.res.Configuration +import android.icu.util.VersionInfo import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.material.* @@ -57,7 +58,14 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit) { showSettingsModal = { modalView -> { ModalManager.shared.showModal(true) { modalView(chatModel) } } }, showCustomModal = { modalView -> { ModalManager.shared.showCustomModal { close -> modalView(chatModel, close) } } }, showTerminal = { ModalManager.shared.showCustomModal { close -> TerminalView(chatModel, close) } }, -// showVideoChatPrototype = { ModalManager.shared.showCustomModal { close -> CallViewDebug(close) } }, + showVersion = { + withApi { + val info = chatModel.controller.apiGetVersion() + if (info != null) { + ModalManager.shared.showModal { VersionInfoView(info) } + } + } + } ) } } @@ -89,7 +97,7 @@ fun SettingsLayout( showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit), showCustomModal: (@Composable (ChatModel, () -> Unit) -> Unit) -> (() -> Unit), showTerminal: () -> Unit, -// showVideoChatPrototype: () -> Unit + showVersion: () -> Unit ) { val uriHandler = LocalUriHandler.current Surface(Modifier.fillMaxSize().verticalScroll(rememberScrollState())) { @@ -170,7 +178,7 @@ fun SettingsLayout( } // SettingsActionItem(Icons.Outlined.Science, stringResource(R.string.settings_experimental_features), showSettingsModal { ExperimentalFeaturesView(it, enableCalls) }) // SectionDivider() - AppVersionItem() + AppVersionItem(showVersion) } } } @@ -345,8 +353,8 @@ fun MaintainIncognitoState(chatModel: ChatModel) { } } -@Composable private fun AppVersionItem() { - SectionItemView() { +@Composable private fun AppVersionItem(showVersion: () -> Unit) { + SectionItemView(showVersion) { Text("v${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})") } } @@ -491,7 +499,7 @@ fun PreviewSettingsLayout() { showSettingsModal = { {} }, showCustomModal = { {} }, showTerminal = {}, -// showVideoChatPrototype = {} + showVersion = {} ) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/VersionInfoView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/VersionInfoView.kt new file mode 100644 index 0000000000..80a32c0858 --- /dev/null +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/VersionInfoView.kt @@ -0,0 +1,29 @@ +package chat.simplex.app.views.usersettings + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import chat.simplex.app.BuildConfig +import chat.simplex.app.R +import chat.simplex.app.model.CoreVersionInfo +import chat.simplex.app.ui.theme.DEFAULT_PADDING +import chat.simplex.app.views.helpers.AppBarTitle + +@Composable +fun VersionInfoView(info: CoreVersionInfo) { + Column( + Modifier.padding(horizontal = DEFAULT_PADDING), + horizontalAlignment = Alignment.Start + ) { + AppBarTitle(stringResource(R.string.app_version_title), false) + Text(String.format(stringResource(R.string.app_version_name), BuildConfig.VERSION_NAME)) + Text(String.format(stringResource(R.string.app_version_code), BuildConfig.VERSION_CODE)) + Text(String.format(stringResource(R.string.core_version), info.version)) + Text(String.format(stringResource(R.string.core_build_timestamp), info.buildTimestamp)) + Text(String.format(stringResource(R.string.core_simplexmq_version), info.simplexmqVersion, info.simplexmqCommit.substring(startIndex = 0, endIndex = 7))) + } +} diff --git a/apps/android/app/src/main/res/values/strings.xml b/apps/android/app/src/main/res/values/strings.xml index 26e00bca21..b00c1364e2 100644 --- a/apps/android/app/src/main/res/values/strings.xml +++ b/apps/android/app/src/main/res/values/strings.xml @@ -485,6 +485,12 @@ A separate TCP connection (and SOCKS credential) will be used for each contact and group member.\nPlease note: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail. Update transport isolation mode? Appearance + App version + App version: v%s + App build: %s + Core version: v%s + Core built at: %s + simplexmq: v%s (%2s) Create address diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 89dedfa2cb..4026b7c2c1 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -892,6 +892,12 @@ func apiGetGroupLink(_ groupId: Int64) throws -> String? { } } +func apiGetVersion() throws -> CoreVersionInfo { + let r = chatSendCmdSync(.showVersion) + if case let .versionInfo(info) = r { return info } + throw r +} + func initializeChat(start: Bool, dbKey: String? = nil) throws { logger.debug("initializeChat") let m = ChatModel.shared diff --git a/apps/ios/Shared/Views/UserSettings/SettingsView.swift b/apps/ios/Shared/Views/UserSettings/SettingsView.swift index 9a57df8d1f..d133edad3f 100644 --- a/apps/ios/Shared/Views/UserSettings/SettingsView.swift +++ b/apps/ios/Shared/Views/UserSettings/SettingsView.swift @@ -278,7 +278,12 @@ struct SettingsView: View { // } label: { // settingsRow("gauge") { Text("Experimental features") } // } - Text("v\(appVersion ?? "?") (\(appBuild ?? "?"))") + NavigationLink { + VersionView() + .navigationBarTitle("App version") + } label: { + Text("v\(appVersion ?? "?") (\(appBuild ?? "?"))") + } } } .navigationTitle("Your settings") diff --git a/apps/ios/Shared/Views/UserSettings/VersionView.swift b/apps/ios/Shared/Views/UserSettings/VersionView.swift new file mode 100644 index 0000000000..9f13e4a95b --- /dev/null +++ b/apps/ios/Shared/Views/UserSettings/VersionView.swift @@ -0,0 +1,43 @@ +// +// VersionView.swift +// SimpleXChat +// +// Created by Evgeny on 22/01/2023. +// Copyright © 2023 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +struct VersionView: View { + @State var versionInfo: CoreVersionInfo? + + var body: some View { + VStack(alignment: .leading) { + Text("App version: v\(appVersion ?? "?")") + Text("App build: \(appBuild ?? "?")") + if let info = versionInfo { + Text("Core version: v\(info.version)") + Text("Core built at: \(info.buildTimestamp)") + if let v = try? AttributedString(markdown: "simplexmq: v\(info.simplexmqVersion) ([\(info.simplexmqCommit.prefix(7))](https://github.com/simplex-chat/simplexmq/commit/\(info.simplexmqCommit)))") { + Text(v) + } + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .padding() + .onAppear { + do { + versionInfo = try apiGetVersion() + } catch let error { + logger.error("apiGetVersion error: \(responseError(error))") + } + } + } +} + +struct VersionView_Previews: PreviewProvider { + static var previews: some View { + VersionView() + } +} diff --git a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff index f359101580..fb5e7947dc 100644 --- a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff +++ b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff @@ -463,11 +463,23 @@ Anruf annehmen No comment provided by engineer. + + App build: %@ + No comment provided by engineer. + App icon App Icon No comment provided by engineer. + + App version + No comment provided by engineer. + + + App version: v%@ + No comment provided by engineer. + Appearance Design @@ -828,6 +840,14 @@ Kopieren chat item action + + Core built at: %@ + No comment provided by engineer. + + + Core version: v%@ + No comment provided by engineer. + Create Erstellen diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff index 847b3e2878..1e707f082e 100644 --- a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff +++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff @@ -463,11 +463,26 @@ Answer call No comment provided by engineer. + + App build: %@ + App build: %@ + No comment provided by engineer. + App icon App icon No comment provided by engineer. + + App version + App version + No comment provided by engineer. + + + App version: v%@ + App version: v%@ + No comment provided by engineer. + Appearance Appearance @@ -828,6 +843,16 @@ Copy chat item action + + Core built at: %@ + Core built at: %@ + No comment provided by engineer. + + + Core version: v%@ + Core version: v%@ + No comment provided by engineer. + Create Create diff --git a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff index f35a16ed3a..573377773c 100644 --- a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff @@ -463,11 +463,23 @@ Répondre à l'appel No comment provided by engineer. + + App build: %@ + No comment provided by engineer. + App icon Icône de l'app No comment provided by engineer. + + App version + No comment provided by engineer. + + + App version: v%@ + No comment provided by engineer. + Appearance Apparence @@ -828,6 +840,14 @@ Copier chat item action + + Core built at: %@ + No comment provided by engineer. + + + Core version: v%@ + No comment provided by engineer. + Create Créer diff --git a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff index 5841619864..290beb8a9a 100644 --- a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff +++ b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff @@ -463,11 +463,23 @@ Rispondi alla chiamata No comment provided by engineer. + + App build: %@ + No comment provided by engineer. + App icon Icona app No comment provided by engineer. + + App version + No comment provided by engineer. + + + App version: v%@ + No comment provided by engineer. + Appearance Aspetto @@ -828,6 +840,14 @@ Copia chat item action + + Core built at: %@ + No comment provided by engineer. + + + Core version: v%@ + No comment provided by engineer. + Create Crea diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff index af7aa75f75..133021d01c 100644 --- a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff @@ -463,11 +463,23 @@ Принять звонок No comment provided by engineer. + + App build: %@ + No comment provided by engineer. + App icon Иконка No comment provided by engineer. + + App version + No comment provided by engineer. + + + App version: v%@ + No comment provided by engineer. + Appearance Интерфейс @@ -828,6 +840,14 @@ Скопировать chat item action + + Core built at: %@ + No comment provided by engineer. + + + Core version: v%@ + No comment provided by engineer. + Create Создать @@ -2275,7 +2295,6 @@ We will be adding server redundancy to prevent lost messages. PING count - Количество PING No comment provided by engineer. diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 40ac25f10c..4853819fb7 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -41,6 +41,11 @@ 5C3F1D5A2844B4DE00EC8A82 /* ExperimentalFeaturesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3F1D592844B4DE00EC8A82 /* ExperimentalFeaturesView.swift */; }; 5C4B3B0A285FB130003915F2 /* DatabaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C4B3B09285FB130003915F2 /* DatabaseView.swift */; }; 5C5346A827B59A6A004DF848 /* ChatHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5346A727B59A6A004DF848 /* ChatHelp.swift */; }; + 5C54F6F2297DF8A40054C4E2 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C54F6ED297DF8A40054C4E2 /* libffi.a */; }; + 5C54F6F3297DF8A40054C4E2 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C54F6EE297DF8A40054C4E2 /* libgmp.a */; }; + 5C54F6F4297DF8A40054C4E2 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C54F6EF297DF8A40054C4E2 /* libgmpxx.a */; }; + 5C54F6F5297DF8A40054C4E2 /* libHSsimplex-chat-4.4.4-696z0wvJHN7Hvtog9MKb6r-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C54F6F0297DF8A40054C4E2 /* libHSsimplex-chat-4.4.4-696z0wvJHN7Hvtog9MKb6r-ghc8.10.7.a */; }; + 5C54F6F6297DF8A40054C4E2 /* libHSsimplex-chat-4.4.4-696z0wvJHN7Hvtog9MKb6r.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C54F6F1297DF8A40054C4E2 /* libHSsimplex-chat-4.4.4-696z0wvJHN7Hvtog9MKb6r.a */; }; 5C55A91F283AD0E400C4E99E /* CallManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C55A91E283AD0E400C4E99E /* CallManager.swift */; }; 5C55A921283CCCB700C4E99E /* IncomingCallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C55A920283CCCB700C4E99E /* IncomingCallView.swift */; }; 5C55A923283CEDE600C4E99E /* SoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C55A922283CEDE600C4E99E /* SoundPlayer.swift */; }; @@ -51,6 +56,7 @@ 5C5E5D3B2824468B00B0488A /* ActiveCallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5E5D3A2824468B00B0488A /* ActiveCallView.swift */; }; 5C5F2B6D27EBC3FE006A9D5F /* ImagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5F2B6C27EBC3FE006A9D5F /* ImagePicker.swift */; }; 5C5F2B7027EBC704006A9D5F /* ProfileImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5F2B6F27EBC704006A9D5F /* ProfileImage.swift */; }; + 5C65F343297D45E100B67AF3 /* VersionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C65F341297D3F3600B67AF3 /* VersionView.swift */; }; 5C6AD81327A834E300348BD7 /* NewChatButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C6AD81227A834E300348BD7 /* NewChatButton.swift */; }; 5C6BA667289BD954009B8ECC /* DismissSheets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C6BA666289BD954009B8ECC /* DismissSheets.swift */; }; 5C7031162953C97F00150A12 /* CIFeaturePreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7031152953C97F00150A12 /* CIFeaturePreferenceView.swift */; }; @@ -131,11 +137,6 @@ 5CE4407927ADB701007B033A /* EmojiItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE4407827ADB701007B033A /* EmojiItemView.swift */; }; 5CEACCE327DE9246000BD591 /* ComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCE227DE9246000BD591 /* ComposeView.swift */; }; 5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; }; - 5CF283BA297ABFB000A8CCB5 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF283B5297ABFB000A8CCB5 /* libgmp.a */; }; - 5CF283BB297ABFB000A8CCB5 /* libHSsimplex-chat-4.4.2-BwepkrkiRWZ1ABwc5I53xG-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF283B6297ABFB000A8CCB5 /* libHSsimplex-chat-4.4.2-BwepkrkiRWZ1ABwc5I53xG-ghc8.10.7.a */; }; - 5CF283BC297ABFB000A8CCB5 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF283B7297ABFB000A8CCB5 /* libgmpxx.a */; }; - 5CF283BD297ABFB000A8CCB5 /* libHSsimplex-chat-4.4.2-BwepkrkiRWZ1ABwc5I53xG.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF283B8297ABFB000A8CCB5 /* libHSsimplex-chat-4.4.2-BwepkrkiRWZ1ABwc5I53xG.a */; }; - 5CF283BE297ABFB000A8CCB5 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF283B9297ABFB000A8CCB5 /* libffi.a */; }; 5CFA59C42860BC6200863A68 /* MigrateToAppGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */; }; 5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59CF286477B400863A68 /* ChatArchiveView.swift */; }; 5CFE0921282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; }; @@ -259,6 +260,11 @@ 5C422A7C27A9A6FA0097A1E1 /* SimpleX (iOS).entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "SimpleX (iOS).entitlements"; sourceTree = ""; }; 5C4B3B09285FB130003915F2 /* DatabaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseView.swift; sourceTree = ""; }; 5C5346A727B59A6A004DF848 /* ChatHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatHelp.swift; sourceTree = ""; }; + 5C54F6ED297DF8A40054C4E2 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; + 5C54F6EE297DF8A40054C4E2 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; + 5C54F6EF297DF8A40054C4E2 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; + 5C54F6F0297DF8A40054C4E2 /* libHSsimplex-chat-4.4.4-696z0wvJHN7Hvtog9MKb6r-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.4.4-696z0wvJHN7Hvtog9MKb6r-ghc8.10.7.a"; sourceTree = ""; }; + 5C54F6F1297DF8A40054C4E2 /* libHSsimplex-chat-4.4.4-696z0wvJHN7Hvtog9MKb6r.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.4.4-696z0wvJHN7Hvtog9MKb6r.a"; sourceTree = ""; }; 5C55A91E283AD0E400C4E99E /* CallManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallManager.swift; sourceTree = ""; }; 5C55A920283CCCB700C4E99E /* IncomingCallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncomingCallView.swift; sourceTree = ""; }; 5C55A922283CEDE600C4E99E /* SoundPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundPlayer.swift; sourceTree = ""; }; @@ -270,6 +276,7 @@ 5C5E5D3C282447AB00B0488A /* CallTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallTypes.swift; sourceTree = ""; }; 5C5F2B6C27EBC3FE006A9D5F /* ImagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePicker.swift; sourceTree = ""; }; 5C5F2B6F27EBC704006A9D5F /* ProfileImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileImage.swift; sourceTree = ""; }; + 5C65F341297D3F3600B67AF3 /* VersionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionView.swift; sourceTree = ""; }; 5C6AD81227A834E300348BD7 /* NewChatButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewChatButton.swift; sourceTree = ""; }; 5C6BA666289BD954009B8ECC /* DismissSheets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DismissSheets.swift; sourceTree = ""; }; 5C7031152953C97F00150A12 /* CIFeaturePreferenceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIFeaturePreferenceView.swift; sourceTree = ""; }; @@ -362,11 +369,6 @@ 5CE4407827ADB701007B033A /* EmojiItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiItemView.swift; sourceTree = ""; }; 5CEACCE227DE9246000BD591 /* ComposeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeView.swift; sourceTree = ""; }; 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MsgContentView.swift; sourceTree = ""; }; - 5CF283B5297ABFB000A8CCB5 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; - 5CF283B6297ABFB000A8CCB5 /* libHSsimplex-chat-4.4.2-BwepkrkiRWZ1ABwc5I53xG-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.4.2-BwepkrkiRWZ1ABwc5I53xG-ghc8.10.7.a"; sourceTree = ""; }; - 5CF283B7297ABFB000A8CCB5 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; - 5CF283B8297ABFB000A8CCB5 /* libHSsimplex-chat-4.4.2-BwepkrkiRWZ1ABwc5I53xG.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.4.2-BwepkrkiRWZ1ABwc5I53xG.a"; sourceTree = ""; }; - 5CF283B9297ABFB000A8CCB5 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; 5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToAppGroupView.swift; sourceTree = ""; }; 5CFA59CF286477B400863A68 /* ChatArchiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatArchiveView.swift; sourceTree = ""; }; 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ZoomableScrollView.swift; path = Shared/Views/ZoomableScrollView.swift; sourceTree = SOURCE_ROOT; }; @@ -429,12 +431,12 @@ buildActionMask = 2147483647; files = ( 5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */, - 5CF283BC297ABFB000A8CCB5 /* libgmpxx.a in Frameworks */, - 5CF283BA297ABFB000A8CCB5 /* libgmp.a in Frameworks */, - 5CF283BB297ABFB000A8CCB5 /* libHSsimplex-chat-4.4.2-BwepkrkiRWZ1ABwc5I53xG-ghc8.10.7.a in Frameworks */, - 5CF283BE297ABFB000A8CCB5 /* libffi.a in Frameworks */, + 5C54F6F6297DF8A40054C4E2 /* libHSsimplex-chat-4.4.4-696z0wvJHN7Hvtog9MKb6r.a in Frameworks */, + 5C54F6F4297DF8A40054C4E2 /* libgmpxx.a in Frameworks */, + 5C54F6F2297DF8A40054C4E2 /* libffi.a in Frameworks */, + 5C54F6F5297DF8A40054C4E2 /* libHSsimplex-chat-4.4.4-696z0wvJHN7Hvtog9MKb6r-ghc8.10.7.a in Frameworks */, + 5C54F6F3297DF8A40054C4E2 /* libgmp.a in Frameworks */, 5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */, - 5CF283BD297ABFB000A8CCB5 /* libHSsimplex-chat-4.4.2-BwepkrkiRWZ1ABwc5I53xG.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -492,11 +494,11 @@ 5C764E5C279C70B7000C6508 /* Libraries */ = { isa = PBXGroup; children = ( - 5CF283B9297ABFB000A8CCB5 /* libffi.a */, - 5CF283B5297ABFB000A8CCB5 /* libgmp.a */, - 5CF283B7297ABFB000A8CCB5 /* libgmpxx.a */, - 5CF283B6297ABFB000A8CCB5 /* libHSsimplex-chat-4.4.2-BwepkrkiRWZ1ABwc5I53xG-ghc8.10.7.a */, - 5CF283B8297ABFB000A8CCB5 /* libHSsimplex-chat-4.4.2-BwepkrkiRWZ1ABwc5I53xG.a */, + 5C54F6ED297DF8A40054C4E2 /* libffi.a */, + 5C54F6EE297DF8A40054C4E2 /* libgmp.a */, + 5C54F6EF297DF8A40054C4E2 /* libgmpxx.a */, + 5C54F6F0297DF8A40054C4E2 /* libHSsimplex-chat-4.4.4-696z0wvJHN7Hvtog9MKb6r-ghc8.10.7.a */, + 5C54F6F1297DF8A40054C4E2 /* libHSsimplex-chat-4.4.4-696z0wvJHN7Hvtog9MKb6r.a */, ); path = Libraries; sourceTree = ""; @@ -647,6 +649,7 @@ 5C3F1D592844B4DE00EC8A82 /* ExperimentalFeaturesView.swift */, 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */, 18415845648CA4F5A8BCA272 /* UserProfilesView.swift */, + 5C65F341297D3F3600B67AF3 /* VersionView.swift */, ); path = UserSettings; sourceTree = ""; @@ -999,6 +1002,7 @@ 5C00164428A26FBC0094D739 /* ContextMenu.swift in Sources */, 5C3A88D127DF57800060F1C2 /* FramedItemView.swift in Sources */, 5CB924E427A8683A00ACCCDD /* UserAddress.swift in Sources */, + 5C65F343297D45E100B67AF3 /* VersionView.swift in Sources */, 64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */, 5CB0BA90282713D900B3292C /* SimpleXInfo.swift in Sources */, 5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */, @@ -1322,7 +1326,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 116; + CURRENT_PROJECT_VERSION = 117; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; ENABLE_PREVIEWS = YES; @@ -1364,7 +1368,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 116; + CURRENT_PROJECT_VERSION = 117; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; ENABLE_PREVIEWS = YES; @@ -1443,7 +1447,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 116; + CURRENT_PROJECT_VERSION = 117; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; GENERATE_INFOPLIST_FILE = YES; @@ -1473,7 +1477,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 116; + CURRENT_PROJECT_VERSION = 117; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; GENERATE_INFOPLIST_FILE = YES; diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index f652c1229b..d1b3dbab0d 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -92,6 +92,7 @@ public enum ChatCommand { case apiChatRead(type: ChatType, id: Int64, itemRange: (Int64, Int64)) case apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool) case receiveFile(fileId: Int64, inline: Bool) + case showVersion case string(String) public var cmdString: String { @@ -180,6 +181,7 @@ public enum ChatCommand { case let .apiChatRead(type, id, itemRange: (from, to)): return "/_read chat \(ref(type, id)) from=\(from) to=\(to)" case let .apiChatUnread(type, id, unreadChat): return "/_unread chat \(ref(type, id)) \(onOff(unreadChat))" case let .receiveFile(fileId, inline): return "/freceive \(fileId) inline=\(onOff(inline))" + case .showVersion: return "/version" case let .string(str): return str } } @@ -266,6 +268,7 @@ public enum ChatCommand { case .apiChatRead: return "apiChatRead" case .apiChatUnread: return "apiChatUnread" case .receiveFile: return "receiveFile" + case .showVersion: return "showVersion" case .string: return "console command" } } @@ -409,6 +412,7 @@ public enum ChatResponse: Decodable, Error { case ntfMessages(user: User, connEntity: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo]) case newContactConnection(user: User, connection: PendingContactConnection) case contactConnectionDeleted(user: User, connection: PendingContactConnection) + case versionInfo(versionInfo: CoreVersionInfo) case cmdOk(user: User?) case chatCmdError(user: User?, chatError: ChatError) case chatError(user: User?, chatError: ChatError) @@ -513,6 +517,7 @@ public enum ChatResponse: Decodable, Error { case .ntfMessages: return "ntfMessages" case .newContactConnection: return "newContactConnection" case .contactConnectionDeleted: return "contactConnectionDeleted" + case .versionInfo: return "versionInfo" case .cmdOk: return "cmdOk" case .chatCmdError: return "chatCmdError" case .chatError: return "chatError" @@ -620,6 +625,7 @@ public enum ChatResponse: Decodable, Error { case let .ntfMessages(u, connEntity, msgTs, ntfMessages): return withUser(u, "connEntity: \(String(describing: connEntity))\nmsgTs: \(String(describing: msgTs))\nntfMessages: \(String(describing: ntfMessages))") case let .newContactConnection(u, connection): return withUser(u, String(describing: connection)) case let .contactConnectionDeleted(u, connection): return withUser(u, String(describing: connection)) + case let .versionInfo(versionInfo): return String(describing: versionInfo) case .cmdOk: return noDetails case let .chatCmdError(u, chatError): return withUser(u, String(describing: chatError)) case let .chatError(u, chatError): return withUser(u, String(describing: chatError)) @@ -1039,6 +1045,13 @@ public enum NotificationPreviewMode: String, SelectableItem { public static var values: [NotificationPreviewMode] = [.message, .contact, .hidden] } +public struct CoreVersionInfo: Decodable { + public var version: String + public var buildTimestamp: String + public var simplexmqVersion: String + public var simplexmqCommit: String +} + public func decodeJSON(_ json: String) -> T? { if let data = json.data(using: .utf8) { return try? jsonDecoder.decode(T.self, from: data) diff --git a/apps/simplex-chat/Main.hs b/apps/simplex-chat/Main.hs index 371a83411e..0b4a7bbe55 100644 --- a/apps/simplex-chat/Main.hs +++ b/apps/simplex-chat/Main.hs @@ -5,7 +5,7 @@ module Main where import Control.Concurrent (threadDelay) import Data.Time.Clock (getCurrentTime) import Server -import Simplex.Chat.Controller (versionNumber) +import Simplex.Chat.Controller (versionNumber, versionString) import Simplex.Chat.Core import Simplex.Chat.Options import Simplex.Chat.Terminal @@ -36,7 +36,7 @@ welcome :: ChatOpts -> IO () welcome ChatOpts {dbFilePrefix, networkConfig} = mapM_ putStrLn - [ "SimpleX Chat v" ++ versionNumber, + [ versionString versionNumber, "db: " <> dbFilePrefix <> "_chat.db, " <> dbFilePrefix <> "_agent.db", maybe "direct network connection - use `/network` command or `-x` CLI option to connect via SOCKS5 at :9050" diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 89e2b4a735..1329d0cab6 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -1310,7 +1310,7 @@ processChatCommand = \case updateGroupProfileByName gName $ \p -> p {groupPreferences = Just . setGroupPreference' SGFTimedMessages pref $ groupPreferences p} QuitChat -> liftIO exitSuccess - ShowVersion -> pure $ CRVersionInfo versionNumber CoreVersionInfo {buildTimestamp = $(buildTimestampQ)} + ShowVersion -> pure $ CRVersionInfo $ coreVersionInfo $(buildTimestampQ) $(simplexmqCommitQ) DebugLocks -> do chatLockName <- atomically . tryReadTMVar =<< asks chatLock agentLocks <- withAgent debugAgentLocks diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 0cfea16348..85b0b0316d 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -55,6 +55,7 @@ import Simplex.Messaging.Notifications.Protocol (DeviceToken (..), NtfTknStatus) import Simplex.Messaging.Parsers (dropPrefix, enumJSON, parseAll, parseString, sumTypeJSON) import Simplex.Messaging.Protocol (AProtocolType, CorrId, MsgFlags, NtfServer) import Simplex.Messaging.TMap (TMap) +import Simplex.Messaging.Transport (simplexMQVersion) import Simplex.Messaging.Transport.Client (TransportHost) import System.IO (Handle) import System.Mem.Weak (Weak) @@ -63,8 +64,8 @@ import UnliftIO.STM versionNumber :: String versionNumber = showVersion SC.version -versionStr :: String -versionStr = "SimpleX Chat v" <> versionNumber +versionString :: String -> String +versionString version = "SimpleX Chat v" <> version updateStr :: String updateStr = "To update run: curl -o- https://raw.githubusercontent.com/simplex-chat/simplex-chat/master/install.sh | bash" @@ -74,6 +75,29 @@ buildTimestampQ = do s <- formatTime defaultTimeLocale (iso8601DateFormat $ Just "%H:%M:%S") <$> runIO getCurrentTime [|fromString s|] +simplexmqCommitQ :: Q Exp +simplexmqCommitQ = do + s <- either error B.unpack . A.parseOnly commitHashP <$> runIO (B.readFile "./cabal.project") + [|fromString s|] + where + commitHashP :: A.Parser ByteString + commitHashP = + A.manyTill' A.anyChar "location: https://github.com/simplex-chat/simplexmq.git" + *> A.takeWhile (== ' ') + *> A.endOfLine + *> A.takeWhile (== ' ') + *> "tag: " + *> A.takeWhile (A.notInClass " \r\n") + +coreVersionInfo :: String -> String -> CoreVersionInfo +coreVersionInfo buildTimestamp simplexmqCommit = + CoreVersionInfo + { version = versionNumber, + buildTimestamp, + simplexmqVersion = simplexMQVersion, + simplexmqCommit + } + data ChatConfig = ChatConfig { agentConfig :: AgentConfig, yesToMigrations :: Bool, @@ -368,7 +392,7 @@ data ChatResponse | CRFileTransferStatus User (FileTransfer, [Integer]) -- TODO refactor this type to FileTransferStatus | CRUserProfile {user :: User, profile :: Profile} | CRUserProfileNoChange {user :: User} - | CRVersionInfo {version :: String, versionInfo :: CoreVersionInfo} + | CRVersionInfo {versionInfo :: CoreVersionInfo} | CRInvitation {user :: User, connReqInvitation :: ConnReqInvitation} | CRSentConfirmation {user :: User} | CRSentInvitation {user :: User, customUserProfile :: Maybe Profile} @@ -581,7 +605,10 @@ data ChatLogLevel = CLLDebug | CLLInfo | CLLWarning | CLLError | CLLImportant deriving (Eq, Ord, Show) data CoreVersionInfo = CoreVersionInfo - { buildTimestamp :: String + { version :: String, + buildTimestamp :: String, + simplexmqVersion :: String, + simplexmqCommit :: String } deriving (Show, Generic) diff --git a/src/Simplex/Chat/Options.hs b/src/Simplex/Chat/Options.hs index 39d0731049..9c2135f7b5 100644 --- a/src/Simplex/Chat/Options.hs +++ b/src/Simplex/Chat/Options.hs @@ -17,7 +17,7 @@ import qualified Data.Attoparsec.ByteString.Char8 as A import qualified Data.ByteString.Char8 as B import Numeric.Natural (Natural) import Options.Applicative -import Simplex.Chat.Controller (ChatLogLevel (..), updateStr, versionStr) +import Simplex.Chat.Controller (ChatLogLevel (..), updateStr, versionNumber, versionString) import Simplex.Messaging.Client (NetworkConfig (..), defaultNetworkConfig) import Simplex.Messaging.Encoding.String import Simplex.Messaging.Parsers (parseAll) @@ -232,5 +232,6 @@ getChatOpts appDir defaultDbFileName = (helper <*> versionOption <*> chatOpts appDir defaultDbFileName) (header versionStr <> fullDesc <> progDesc "Start chat with DB_FILE file and use SERVER as SMP server") where + versionStr = versionString versionNumber versionOption = infoOption versionAndUpdate (long "version" <> short 'v' <> help "Show version") versionAndUpdate = versionStr <> "\n" <> updateStr diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index bf008bb7ca..654b81d09f 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -114,7 +114,7 @@ responseToView user_ ChatConfig {logLevel, testView} liveItems ts = \case CRFileTransferStatus u ftStatus -> ttyUser u $ viewFileTransferStatus ftStatus CRUserProfile u p -> ttyUser u $ viewUserProfile p CRUserProfileNoChange u -> ttyUser u ["user profile did not change"] - CRVersionInfo _ _ -> [plain versionStr, plain updateStr] + CRVersionInfo info -> viewVersionInfo logLevel info CRInvitation u cReq -> ttyUser u $ viewConnReqInvitation cReq CRSentConfirmation u -> ttyUser u ["confirmation sent!"] CRSentInvitation u customUserProfile -> ttyUser u $ viewSentInvitation customUserProfile testView @@ -1161,6 +1161,15 @@ instance ToJSON WCallCommand where toEncoding = J.genericToEncoding . taggedObjectJSON $ dropPrefix "WCCall" toJSON = J.genericToJSON . taggedObjectJSON $ dropPrefix "WCCall" +viewVersionInfo :: ChatLogLevel -> CoreVersionInfo -> [StyledString] +viewVersionInfo logLevel CoreVersionInfo {version, buildTimestamp, simplexmqVersion, simplexmqCommit} = + map plain $ + if logLevel <= CLLInfo + then [versionString version <> parens buildTimestamp, updateStr, "simplexmq: " <> simplexmqVersion <> parens simplexmqCommit] + else [versionString version, updateStr] + where + parens s = " (" <> s <> ")" + viewChatError :: ChatLogLevel -> ChatError -> [StyledString] viewChatError logLevel = \case ChatError err -> case err of