Compare commits

..

6 Commits

Author SHA1 Message Date
Evgeny Poberezkin
98417dafc4 4.4.1: ios 113, Android 87 2023-01-11 17:54:24 +00:00
Evgeny Poberezkin
e8374be19c mobile: set defaults consistently (protected screen: iOS off/Android on, accept images: on, faster image transfer: on) (#1724)
* ios: set defaults consistently (protected screen: off, accept images: on, faster image transfer: on)

* android: transfer images faster by default
2023-01-11 17:09:17 +00:00
Stanislav Dmitrenko
62a2f61751 android: Fix non-unique chat item id in listState (#1723)
* android: Fix non-unique chat item id in listState

* Test

* Revert "Test"

This reverts commit 6625bce138.
2023-01-11 16:41:34 +00:00
Evgeny Poberezkin
2d47175f94 ios: disable reply/edit actions and deletion of live item in live mode (#1722) 2023-01-11 17:29:09 +04:00
Evgeny Poberezkin
a6d7604d21 mobile: send live message when there is any content (#1721)
* ios: send live message when there is any content

* android: improve live message logic

* fix, refactor

* prohibit live messages with quotes
2023-01-11 12:01:02 +00:00
JRoberts
9e3573fc76 android: fix send button being disabled on images, files & voice messages (#1720)
* android: fix send button being disabled on images, files & voice messages

* rename view

* format
2023-01-11 08:59:04 +00:00
17 changed files with 132 additions and 117 deletions

View File

@@ -11,8 +11,8 @@ android {
applicationId "chat.simplex.app"
minSdk 29
targetSdk 32
versionCode 86
versionName "4.4.1-beta.1"
versionCode 87
versionName "4.4.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
ndk {

View File

@@ -17,6 +17,7 @@ import chat.simplex.app.views.helpers.*
import chat.simplex.app.views.onboarding.OnboardingStage
import chat.simplex.app.views.usersettings.NotificationPreviewMode
import chat.simplex.app.views.usersettings.NotificationsMode
import kotlinx.coroutines.*
import kotlinx.datetime.*
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
@@ -180,10 +181,12 @@ class ChatModel(val controller: ChatController) {
}
// add to current chat
if (chatId.value == cInfo.id) {
if (chatItems.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
chatItems.add(kotlin.math.max(0, chatItems.lastIndex), cItem)
} else {
chatItems.add(cItem)
runBlocking(Dispatchers.Main) {
if (chatItems.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
chatItems.add(kotlin.math.max(0, chatItems.lastIndex), cItem)
} else {
chatItems.add(cItem)
}
}
}
}
@@ -215,7 +218,9 @@ class ChatModel(val controller: ChatController) {
chatItems[itemIndex] = cItem
return false
} else {
chatItems.add(cItem)
runBlocking(Dispatchers.Main) {
chatItems.add(cItem)
}
return true
}
} else {
@@ -259,16 +264,15 @@ class ChatModel(val controller: ChatController) {
}
}
fun addLiveChatItemDummy(quotedCItem: ChatItem?, chatInfo: ChatInfo): ChatItem {
val quoted = if (quotedCItem?.content?.msgContent != null) {
CIQuote(chatDir = quotedCItem.chatDir, itemId = quotedCItem.id, sentAt = quotedCItem.meta.createdAt, content = quotedCItem.content.msgContent!!)
} else null
val cItem = ChatItem.liveChatItemDummy(chatInfo is ChatInfo.Direct, quoted)
chatItems.add(cItem)
fun addLiveDummy(chatInfo: ChatInfo): ChatItem {
val cItem = ChatItem.liveDummy(chatInfo is ChatInfo.Direct)
runBlocking(Dispatchers.Main) {
chatItems.add(cItem)
}
return cItem
}
fun removeLiveChatItemDummy() {
fun removeLiveDummy() {
if (chatItems.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
chatItems.removeLast()
}
@@ -1320,7 +1324,7 @@ data class ChatItem (
file = null
)
fun liveChatItemDummy(direct: Boolean, quoted: CIQuote?): ChatItem = ChatItem(
fun liveDummy(direct: Boolean): ChatItem = ChatItem(
chatDir = if (direct) CIDirection.DirectSnd() else CIDirection.GroupSnd(),
meta = CIMeta(
itemId = TEMP_LIVE_CHAT_ITEM_ID,
@@ -1336,7 +1340,7 @@ data class ChatItem (
editable = false
),
content = CIContent.SndMsgContent(MsgContent.MCText("")),
quotedItem = quoted,
quotedItem = null,
file = null
)

View File

@@ -90,7 +90,7 @@ class AppPreferences(val context: Context) {
val webrtcIceServers = mkStrPreference(SHARED_PREFS_WEBRTC_ICE_SERVERS, null)
val privacyProtectScreen = mkBoolPreference(SHARED_PREFS_PRIVACY_PROTECT_SCREEN, true)
val privacyAcceptImages = mkBoolPreference(SHARED_PREFS_PRIVACY_ACCEPT_IMAGES, true)
val privacyTransferImagesInline = mkBoolPreference(SHARED_PREFS_PRIVACY_TRANSFER_IMAGES_INLINE, false)
val privacyTransferImagesInline = mkBoolPreference(SHARED_PREFS_PRIVACY_TRANSFER_IMAGES_INLINE, true)
val privacyLinkPreviews = mkBoolPreference(SHARED_PREFS_PRIVACY_LINK_PREVIEWS, true)
private val _simplexLinkMode = mkStrPreference(SHARED_PREFS_PRIVACY_SIMPLEX_LINK_MODE, SimplexLinkMode.default.name)
val simplexLinkMode: SharedPreference<SimplexLinkMode> = SharedPreference(

View File

@@ -41,7 +41,6 @@ import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.views.chat.item.*
import chat.simplex.app.views.helpers.*
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Serializable
@@ -105,6 +104,9 @@ data class ComposeState(
}
hasContent && !inProgress
}
val endLiveDisabled: Boolean
get() = liveMessage != null && message.isEmpty() && preview is ComposePreview.NoPreview && contextItem is ComposeContextItem.NoContextItem
val linkPreviewAllowed: Boolean
get() =
when (preview) {
@@ -354,7 +356,7 @@ fun ComposeView(
chosenContent.value = emptyList()
chosenAudio.value = null
chosenFile.value = null
chatModel.removeLiveChatItemDummy()
chatModel.removeLiveDummy()
}
suspend fun send(cInfo: ChatInfo, mc: MsgContent, quoted: Long?, file: String? = null, live: Boolean = false): ChatItem? {
@@ -572,16 +574,16 @@ fun ComposeView(
}
suspend fun sendLiveMessage() {
val typedMsg = composeState.value.message
val sentMsg = truncateToWords(typedMsg)
if (sentMsg.isNotEmpty() && (composeState.value.liveMessage == null || composeState.value.liveMessage?.sent == false)) {
val ci = sendMessageAsync(sentMsg, live = true)
val cs = composeState.value
val typedMsg = cs.message
if ((cs.sendEnabled() || cs.contextItem is ComposeContextItem.QuotedItem) && (cs.liveMessage == null || !cs.liveMessage?.sent)) {
val ci = sendMessageAsync(typedMsg, live = true)
if (ci != null) {
composeState.value = composeState.value.copy(liveMessage = LiveMessage(ci, typedMsg = typedMsg, sentMsg = sentMsg, sent = true))
composeState.value = composeState.value.copy(liveMessage = LiveMessage(ci, typedMsg = typedMsg, sentMsg = typedMsg, sent = true))
}
} else if (composeState.value.liveMessage == null) {
val cItem = chatModel.addLiveChatItemDummy((composeState.value.contextItem as? ComposeContextItem.QuotedItem)?.chatItem, chat.chatInfo)
composeState.value = composeState.value.copy(liveMessage = LiveMessage(cItem, typedMsg = typedMsg, sentMsg = sentMsg, sent = false))
} else if (cs.liveMessage == null) {
val cItem = chatModel.addLiveDummy(chat.chatInfo)
composeState.value = composeState.value.copy(liveMessage = LiveMessage(cItem, typedMsg = typedMsg, sentMsg = typedMsg, sent = false))
}
}
@@ -702,16 +704,6 @@ fun ComposeView(
}
}
}
LaunchedEffect(Unit) {
snapshotFlow { composeState.value.contextItem }
.distinctUntilChanged()
.collect {
if (composeState.value.liveMessage?.sent == false) {
chatModel.removeLiveChatItemDummy()
chatModel.addLiveChatItemDummy((it as? ComposeContextItem.QuotedItem)?.chatItem, chat.chatInfo)
}
}
}
val activity = LocalContext.current as Activity
DisposableEffect(Unit) {
@@ -723,7 +715,7 @@ fun ComposeView(
sendMessage()
resetLinkPreview()
}
chatModel.removeLiveChatItemDummy()
chatModel.removeLiveDummy()
}
}
}
@@ -745,7 +737,7 @@ fun ComposeView(
updateLiveMessage = ::updateLiveMessage,
cancelLiveMessage = {
composeState.value = composeState.value.copy(liveMessage = null)
chatModel.removeLiveChatItemDummy()
chatModel.removeLiveDummy()
},
onMessageChange = ::onMessageChange,
textStyle = textStyle

View File

@@ -62,7 +62,7 @@ fun SendMsgView(
allowedVoiceByPrefs: Boolean,
allowVoiceToContact: () -> Unit,
sendMessage: () -> Unit,
sendLiveMessage: ( suspend () -> Unit)? = null,
sendLiveMessage: (suspend () -> Unit)? = null,
updateLiveMessage: (suspend () -> Unit)? = null,
cancelLiveMessage: (() -> Unit)? = null,
onMessageChange: (String) -> Unit,
@@ -70,7 +70,7 @@ fun SendMsgView(
) {
Box(Modifier.padding(vertical = 8.dp)) {
val cs = composeState.value
val showProgress = cs.inProgress && (cs.preview is ComposePreview.ImagePreview || cs.preview is ComposePreview.FilePreview)
val showProgress = cs.inProgress && (cs.preview is ComposePreview.ImagePreview || cs.preview is ComposePreview.FilePreview)
val showVoiceButton = cs.message.isEmpty() && showVoiceRecordIcon && !composeState.value.editing &&
cs.liveMessage == null && (cs.preview is ComposePreview.NoPreview || recState.value is RecordingState.Started)
NativeKeyboard(composeState, textStyle, onMessageChange)
@@ -109,7 +109,10 @@ fun SendMsgView(
else ->
RecordVoiceView(recState, stopRecOnNextClick)
}
if (sendLiveMessage != null && updateLiveMessage != null && (cs.preview !is ComposePreview.VoicePreview || !stopRecOnNextClick.value)) {
if (sendLiveMessage != null
&& updateLiveMessage != null
&& (cs.preview !is ComposePreview.VoicePreview || !stopRecOnNextClick.value)
&& cs.contextItem is ComposeContextItem.NoContextItem) {
Spacer(Modifier.width(10.dp))
StartLiveMessageButton {
if (composeState.value.preview is ComposePreview.NoPreview) {
@@ -125,14 +128,18 @@ fun SendMsgView(
}
}
else -> {
val cs = composeState.value
val icon = if (cs.editing || cs.liveMessage != null) Icons.Filled.Check else Icons.Outlined.ArrowUpward
val color = if (cs.sendEnabled() && cs.message.isNotEmpty()) MaterialTheme.colors.primary else HighOrLowlight
if (composeState.value.liveMessage == null &&
val disabled = !cs.sendEnabled() ||
(!allowedVoiceByPrefs && cs.preview is ComposePreview.VoicePreview) ||
cs.endLiveDisabled
if (cs.liveMessage == null &&
cs.preview !is ComposePreview.VoicePreview && !cs.editing &&
cs.contextItem is ComposeContextItem.NoContextItem &&
sendLiveMessage != null && updateLiveMessage != null
) {
var showDropdown by rememberSaveable { mutableStateOf(false) }
SendTextButton(icon, color, sendButtonSize, sendButtonAlpha, cs.sendEnabled(), sendMessage) { showDropdown = true }
SendMsgButton(icon, sendButtonSize, sendButtonAlpha, !disabled, sendMessage) { showDropdown = true }
DropdownMenu(
expanded = showDropdown,
@@ -149,7 +156,7 @@ fun SendMsgView(
)
}
} else {
SendTextButton(icon, color, sendButtonSize, sendButtonAlpha, cs.sendEnabled() && cs.message.isNotEmpty(), sendMessage)
SendMsgButton(icon, sendButtonSize, sendButtonAlpha, !disabled, sendMessage)
}
}
}
@@ -171,7 +178,6 @@ private fun NativeKeyboard(
val paddingTop = with(LocalDensity.current) { 7.dp.roundToPx() }
val paddingEnd = with(LocalDensity.current) { 45.dp.roundToPx() }
val paddingBottom = with(LocalDensity.current) { 7.dp.roundToPx() }
var showKeyboard by remember { mutableStateOf(false) }
LaunchedEffect(cs.contextItem) {
if (cs.contextItem is ComposeContextItem.QuotedItem) {
@@ -192,6 +198,7 @@ private fun NativeKeyboard(
) {
super.setOnReceiveContentListener(mimeTypes, listener)
}
override fun onCreateInputConnection(editorInfo: EditorInfo): InputConnection {
val connection = super.onCreateInputConnection(editorInfo)
EditorInfoCompat.setContentMimeTypes(editorInfo, arrayOf("image/*"))
@@ -344,7 +351,6 @@ private fun LockToCurrentOrientationUntilDispose() {
}
}
@Composable
private fun StopRecordButton(onClick: () -> Unit) {
IconButton(onClick, Modifier.size(36.dp)) {
@@ -395,9 +401,8 @@ private fun CancelLiveMessageButton(
}
@Composable
private fun SendTextButton(
private fun SendMsgButton(
icon: ImageVector,
backgroundColor: Color,
sizeDp: Animatable<Float, AnimationVector1D>,
alpha: Animatable<Float, AnimationVector1D>,
enabled: Boolean,
@@ -426,7 +431,7 @@ private fun SendTextButton(
.padding(4.dp)
.alpha(alpha.value)
.clip(CircleShape)
.background(backgroundColor)
.background(if (enabled) MaterialTheme.colors.primary else HighOrLowlight)
.padding(3.dp)
)
}
@@ -573,7 +578,7 @@ fun PreviewSendMsgViewEditing() {
SendMsgView(
composeState = remember { mutableStateOf(composeStateEditing) },
showVoiceRecordIcon = false,
recState = remember { mutableStateOf(RecordingState.NotStarted) },
recState = remember { mutableStateOf(RecordingState.NotStarted) },
isDirectChat = true,
liveMessageAlertShown = SharedPreference(get = { true }, set = { }),
needToAllowVoiceToContact = false,

View File

@@ -54,6 +54,7 @@ fun ChatItemView(
val fullDeleteAllowed = remember(cInfo) { cInfo.featureEnabled(ChatFeature.FullDelete) }
val saveFileLauncher = rememberSaveFileLauncher(cxt = context, ciFile = cItem.file)
val onLinkLongClick = { _: String -> showMenu.value = true }
val live = composeState.value.liveMessage != null
Box(
modifier = Modifier
@@ -97,7 +98,7 @@ fun ChatItemView(
onDismissRequest = { showMenu.value = false },
Modifier.width(220.dp)
) {
if (!cItem.meta.itemDeleted) {
if (!cItem.meta.itemDeleted && !live) {
ItemAction(stringResource(R.string.reply_verb), Icons.Outlined.Reply, onClick = {
if (composeState.value.editing) {
composeState.value = ComposeState(contextItem = ComposeContextItem.QuotedItem(cItem), useLinkPreviews = useLinkPreviews)
@@ -133,7 +134,7 @@ fun ChatItemView(
})
}
}
if (cItem.meta.editable && cItem.content.msgContent !is MsgContent.MCVoice) {
if (cItem.meta.editable && cItem.content.msgContent !is MsgContent.MCVoice && !live) {
ItemAction(stringResource(R.string.edit_verb), Icons.Filled.Edit, onClick = {
composeState.value = ComposeState(editingItem = cItem, useLinkPreviews = useLinkPreviews)
showMenu.value = false
@@ -149,7 +150,9 @@ fun ChatItemView(
}
)
}
DeleteItemAction(cItem, showMenu, questionText = deleteMessageQuestionText(), deleteMessage)
if (!(live && cItem.meta.isLive)) {
DeleteItemAction(cItem, showMenu, questionText = deleteMessageQuestionText(), deleteMessage)
}
}
}

View File

@@ -17,7 +17,7 @@ struct ContentView: View {
@AppStorage(DEFAULT_SHOW_LA_NOTICE) private var prefShowLANotice = false
@AppStorage(DEFAULT_LA_NOTICE_SHOWN) private var prefLANoticeShown = false
@AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false
@AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = true
@AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = false
@AppStorage(DEFAULT_NOTIFICATION_ALERT_SHOWN) private var notificationAlertShown = false
@State private var showWhatsNew = false

View File

@@ -283,12 +283,8 @@ final class ChatModel: ObservableObject {
return nil
}
func addLiveDummy(_ quotedCItem: ChatItem?, _ chatInfo: ChatInfo) -> ChatItem {
var quoted: CIQuote? = nil
if let quotedItem = quotedCItem, let msgContent = quotedItem.content.msgContent {
quoted = CIQuote.getSampleWithMsgContent(itemId: quotedItem.id, sentAt: quotedItem.meta.updatedAt, msgContent: msgContent, chatDir: quotedItem.chatDir)
}
let cItem = ChatItem.liveChatItemDummy(chatInfo.chatType, quoted)
func addLiveDummy(_ chatInfo: ChatInfo) -> ChatItem {
let cItem = ChatItem.liveDummy(chatInfo.chatType)
withAnimation {
reversedChatItems.insert(cItem, at: 0)
}

View File

@@ -442,9 +442,13 @@ struct ChatView: View {
var body: some View {
let alignment: Alignment = ci.chatDir.sent ? .trailing : .leading
let uiMenu: Binding<UIMenu> = Binding(
get: { UIMenu(title: "", children: menu(live: composeState.liveMessage != nil)) },
set: { _ in }
)
ChatItemView(chatInfo: chat.chatInfo, chatItem: ci, showMember: showMember, maxWidth: maxWidth, scrollProxy: scrollProxy, revealed: $revealed)
.uiKitContextMenu(actions: menu())
.uiKitContextMenu(menu: uiMenu)
.confirmationDialog("Delete message?", isPresented: $showDeleteMessage, titleVisibility: .visible) {
Button("Delete for me", role: .destructive) {
deleteMessage(.cidmInternal)
@@ -459,10 +463,10 @@ struct ChatView: View {
.frame(minWidth: 0, maxWidth: .infinity, alignment: alignment)
}
private func menu() -> [UIAction] {
private func menu(live: Bool) -> [UIAction] {
var menu: [UIAction] = []
if let mc = ci.content.msgContent, !ci.meta.itemDeleted || revealed {
if !ci.meta.itemDeleted && !ci.isLiveDummy && composeState.liveMessage == nil {
if !ci.meta.itemDeleted && !ci.isLiveDummy && !live {
menu.append(replyUIAction())
}
menu.append(shareUIAction())
@@ -478,13 +482,15 @@ struct ChatView: View {
menu.append(saveFileAction(filePath))
}
}
if ci.meta.editable && !mc.isVoice {
if ci.meta.editable && !mc.isVoice && !live {
menu.append(editAction())
}
if revealed {
menu.append(hideUIAction())
}
menu.append(deleteUIAction())
if !live || !ci.meta.isLive {
menu.append(deleteUIAction())
}
} else if ci.meta.itemDeleted {
menu.append(revealUIAction())
menu.append(deleteUIAction())

View File

@@ -96,6 +96,13 @@ struct ComposeState {
}
}
var quoting: Bool {
switch contextItem {
case .quotedItem: return true
default: return false
}
}
var sendEnabled: Bool {
switch preview {
case .imagePreviews: return true
@@ -105,6 +112,10 @@ struct ComposeState {
}
}
var endLiveDisabled: Bool {
liveMessage != nil && message.isEmpty && noPreview && !quoting
}
var linkPreviewAllowed: Bool {
switch preview {
case .imagePreviews: return false
@@ -400,18 +411,15 @@ struct ComposeView: View {
private func sendLiveMessage() async {
let typedMsg = composeState.message
let sentMsg = truncateToWords(typedMsg)
if !sentMsg.isEmpty && (composeState.liveMessage == nil || composeState.liveMessage?.sentMsg == nil),
let ci = await sendMessageAsync(sentMsg, live: true) {
let lm = composeState.liveMessage
if (composeState.sendEnabled || composeState.quoting)
&& (lm == nil || lm?.sentMsg == nil),
let ci = await sendMessageAsync(typedMsg, live: true) {
await MainActor.run {
composeState = composeState.copy(liveMessage: LiveMessage(chatItem: ci, typedMsg: typedMsg, sentMsg: sentMsg))
composeState = composeState.copy(liveMessage: LiveMessage(chatItem: ci, typedMsg: typedMsg, sentMsg: typedMsg))
}
} else if composeState.liveMessage == nil {
var quoted: ChatItem? = nil
if case let .quotedItem(item) = composeState.contextItem {
quoted = item
}
let cItem = chatModel.addLiveDummy(quoted, chat.chatInfo)
} else if lm == nil {
let cItem = chatModel.addLiveDummy(chat.chatInfo)
await MainActor.run {
composeState = composeState.copy(liveMessage: LiveMessage(chatItem: cItem, typedMsg: typedMsg, sentMsg: nil))
}

View File

@@ -100,7 +100,9 @@ struct SendMessageView: View {
} else {
voiceMessageNotAllowedButton()
}
if let send = sendLiveMessage, let update = updateLiveMessage {
if let send = sendLiveMessage,
let update = updateLiveMessage,
case .noContextItem = composeState.contextItem {
startLiveMessageButton(send: send, update: update)
}
}
@@ -137,11 +139,12 @@ struct SendMessageView: View {
!composeState.sendEnabled ||
composeState.disabled ||
(!voiceMessageAllowed && composeState.voicePreview) ||
(composeState.liveMessage != nil && composeState.message.isEmpty)
composeState.endLiveDisabled
)
.frame(width: 29, height: 29)
if composeState.liveMessage == nil,
case .noContextItem = composeState.contextItem,
!composeState.voicePreview && !composeState.editing,
let send = sendLiveMessage,
let update = updateLiveMessage {

View File

@@ -35,7 +35,7 @@ private struct SheetForItem<T, C>: ViewModifier where T: Identifiable, C: View {
}
private struct PrivacySensitive: ViewModifier {
@AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = true
@AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = false
@Environment(\.scenePhase) var scenePhase
func body(content: Content) -> some View {

View File

@@ -11,10 +11,10 @@ import UIKit
import SwiftUI
extension View {
func uiKitContextMenu(title: String = "", actions: [UIAction]) -> some View {
func uiKitContextMenu(menu: Binding<UIMenu>) -> some View {
self.overlay(Color(uiColor: .systemBackground))
.overlay(
InteractionView(content: self, menu: UIMenu(title: title, children: actions))
InteractionView(content: self, menu: menu)
)
}
}
@@ -26,7 +26,7 @@ private struct InteractionConfig<Content: View> {
private struct InteractionView<Content: View>: UIViewRepresentable {
let content: Content
let menu: UIMenu
@Binding var menu: UIMenu
func makeUIView(context: Context) -> UIView {
let view = UIView()

View File

@@ -13,9 +13,9 @@ struct PrivacySettings: View {
@AppStorage(DEFAULT_PRIVACY_ACCEPT_IMAGES) private var autoAcceptImages = true
@AppStorage(DEFAULT_PRIVACY_LINK_PREVIEWS) private var useLinkPreviews = true
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@AppStorage(GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE, store: groupDefaults) private var transferImagesInline = false
@AppStorage(GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE, store: groupDefaults) private var transferImagesInline = true
@State private var simplexLinkMode = privacySimplexLinkModeDefault.get()
@AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = true
@AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = false
var body: some View {
VStack {

View File

@@ -76,11 +76,11 @@
5CA059ED279559F40002BEB4 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059C4279559F40002BEB4 /* ContentView.swift */; };
5CA059EF279559F40002BEB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5CA059C5279559F40002BEB4 /* Assets.xcassets */; };
5CA7DFC329302AF000F7FDDE /* AppSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA7DFC229302AF000F7FDDE /* AppSheet.swift */; };
5CA85CFB29661DF10095AF72 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA85CF629661DF00095AF72 /* libffi.a */; };
5CA85CFC29661DF10095AF72 /* libHSsimplex-chat-4.4.0-IXklDMWJZrrISWcSAPS4si.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA85CF729661DF00095AF72 /* libHSsimplex-chat-4.4.0-IXklDMWJZrrISWcSAPS4si.a */; };
5CA85CFD29661DF10095AF72 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA85CF829661DF00095AF72 /* libgmp.a */; };
5CA85CFE29661DF10095AF72 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA85CF929661DF10095AF72 /* libgmpxx.a */; };
5CA85CFF29661DF10095AF72 /* libHSsimplex-chat-4.4.0-IXklDMWJZrrISWcSAPS4si-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA85CFA29661DF10095AF72 /* libHSsimplex-chat-4.4.0-IXklDMWJZrrISWcSAPS4si-ghc8.10.7.a */; };
5CA85D05296F151E0095AF72 /* libHSsimplex-chat-4.4.1-5apk6NMi0TsBQXmy6Jpqfi-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA85D00296F151E0095AF72 /* libHSsimplex-chat-4.4.1-5apk6NMi0TsBQXmy6Jpqfi-ghc8.10.7.a */; };
5CA85D06296F151E0095AF72 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA85D01296F151E0095AF72 /* libffi.a */; };
5CA85D07296F151E0095AF72 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA85D02296F151E0095AF72 /* libgmpxx.a */; };
5CA85D08296F151E0095AF72 /* libHSsimplex-chat-4.4.1-5apk6NMi0TsBQXmy6Jpqfi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA85D03296F151E0095AF72 /* libHSsimplex-chat-4.4.1-5apk6NMi0TsBQXmy6Jpqfi.a */; };
5CA85D09296F151E0095AF72 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA85D04296F151E0095AF72 /* libgmp.a */; };
5CADE79A29211BB900072E13 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CADE79929211BB900072E13 /* PreferencesView.swift */; };
5CADE79C292131E900072E13 /* ContactPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CADE79B292131E900072E13 /* ContactPreferencesView.swift */; };
5CB0BA882826CB3A00B3292C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CB0BA862826CB3A00B3292C /* InfoPlist.strings */; };
@@ -296,11 +296,11 @@
5CA059DB279559F40002BEB4 /* Tests_iOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_iOS.swift; sourceTree = "<group>"; };
5CA059DD279559F40002BEB4 /* Tests_iOSLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_iOSLaunchTests.swift; sourceTree = "<group>"; };
5CA7DFC229302AF000F7FDDE /* AppSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSheet.swift; sourceTree = "<group>"; };
5CA85CF629661DF00095AF72 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5CA85CF729661DF00095AF72 /* libHSsimplex-chat-4.4.0-IXklDMWJZrrISWcSAPS4si.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.4.0-IXklDMWJZrrISWcSAPS4si.a"; sourceTree = "<group>"; };
5CA85CF829661DF00095AF72 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5CA85CF929661DF10095AF72 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5CA85CFA29661DF10095AF72 /* libHSsimplex-chat-4.4.0-IXklDMWJZrrISWcSAPS4si-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.4.0-IXklDMWJZrrISWcSAPS4si-ghc8.10.7.a"; sourceTree = "<group>"; };
5CA85D00296F151E0095AF72 /* libHSsimplex-chat-4.4.1-5apk6NMi0TsBQXmy6Jpqfi-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.4.1-5apk6NMi0TsBQXmy6Jpqfi-ghc8.10.7.a"; sourceTree = "<group>"; };
5CA85D01296F151E0095AF72 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5CA85D02296F151E0095AF72 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5CA85D03296F151E0095AF72 /* libHSsimplex-chat-4.4.1-5apk6NMi0TsBQXmy6Jpqfi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.4.1-5apk6NMi0TsBQXmy6Jpqfi.a"; sourceTree = "<group>"; };
5CA85D04296F151E0095AF72 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5CADE79929211BB900072E13 /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = "<group>"; };
5CADE79B292131E900072E13 /* ContactPreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactPreferencesView.swift; sourceTree = "<group>"; };
5CB0BA872826CB3A00B3292C /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@@ -420,13 +420,13 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5CA85CFE29661DF10095AF72 /* libgmpxx.a in Frameworks */,
5CA85D08296F151E0095AF72 /* libHSsimplex-chat-4.4.1-5apk6NMi0TsBQXmy6Jpqfi.a in Frameworks */,
5CA85D09296F151E0095AF72 /* libgmp.a in Frameworks */,
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
5CA85CFD29661DF10095AF72 /* libgmp.a in Frameworks */,
5CA85CFB29661DF10095AF72 /* libffi.a in Frameworks */,
5CA85D07296F151E0095AF72 /* libgmpxx.a in Frameworks */,
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
5CA85CFC29661DF10095AF72 /* libHSsimplex-chat-4.4.0-IXklDMWJZrrISWcSAPS4si.a in Frameworks */,
5CA85CFF29661DF10095AF72 /* libHSsimplex-chat-4.4.0-IXklDMWJZrrISWcSAPS4si-ghc8.10.7.a in Frameworks */,
5CA85D05296F151E0095AF72 /* libHSsimplex-chat-4.4.1-5apk6NMi0TsBQXmy6Jpqfi-ghc8.10.7.a in Frameworks */,
5CA85D06296F151E0095AF72 /* libffi.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -484,11 +484,11 @@
5C764E5C279C70B7000C6508 /* Libraries */ = {
isa = PBXGroup;
children = (
5CA85CF629661DF00095AF72 /* libffi.a */,
5CA85CF829661DF00095AF72 /* libgmp.a */,
5CA85CF929661DF10095AF72 /* libgmpxx.a */,
5CA85CFA29661DF10095AF72 /* libHSsimplex-chat-4.4.0-IXklDMWJZrrISWcSAPS4si-ghc8.10.7.a */,
5CA85CF729661DF00095AF72 /* libHSsimplex-chat-4.4.0-IXklDMWJZrrISWcSAPS4si.a */,
5CA85D01296F151E0095AF72 /* libffi.a */,
5CA85D04296F151E0095AF72 /* libgmp.a */,
5CA85D02296F151E0095AF72 /* libgmpxx.a */,
5CA85D00296F151E0095AF72 /* libHSsimplex-chat-4.4.1-5apk6NMi0TsBQXmy6Jpqfi-ghc8.10.7.a */,
5CA85D03296F151E0095AF72 /* libHSsimplex-chat-4.4.1-5apk6NMi0TsBQXmy6Jpqfi.a */,
);
path = Libraries;
sourceTree = "<group>";
@@ -1305,7 +1305,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 112;
CURRENT_PROJECT_VERSION = 113;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES;
@@ -1347,7 +1347,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 112;
CURRENT_PROJECT_VERSION = 113;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES;
@@ -1426,7 +1426,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 112;
CURRENT_PROJECT_VERSION = 113;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GENERATE_INFOPLIST_FILE = YES;
@@ -1456,7 +1456,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 112;
CURRENT_PROJECT_VERSION = 113;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GENERATE_INFOPLIST_FILE = YES;

View File

@@ -44,7 +44,9 @@ public func registerGroupDefaults() {
GROUP_DEFAULT_NETWORK_TCP_KEEP_CNT: KeepAliveOpts.defaults.keepCnt,
GROUP_DEFAULT_INCOGNITO: false,
GROUP_DEFAULT_STORE_DB_PASSPHRASE: true,
GROUP_DEFAULT_INITIAL_RANDOM_DB_PASSPHRASE: false
GROUP_DEFAULT_INITIAL_RANDOM_DB_PASSPHRASE: false,
GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES: true,
GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE: true
])
}

View File

@@ -1863,7 +1863,7 @@ public struct ChatItem: Identifiable, Decodable {
)
}
public static func liveChatItemDummy(_ chatType: ChatType, _ quoted: CIQuote?) -> ChatItem {
public static func liveDummy(_ chatType: ChatType) -> ChatItem {
var item = ChatItem(
chatDir: chatType == ChatType.direct ? CIDirection.directSnd : CIDirection.groupSnd,
meta: CIMeta(
@@ -1879,7 +1879,7 @@ public struct ChatItem: Identifiable, Decodable {
editable: false
),
content: .sndMsgContent(msgContent: .text("")),
quotedItem: quoted,
quotedItem: nil,
file: nil
)
item.isLiveDummy = true
@@ -2135,10 +2135,6 @@ public struct CIQuote: Decodable, ItemContent {
}
return CIQuote(chatDir: chatDir, itemId: itemId, sentAt: sentAt, content: mc)
}
public static func getSampleWithMsgContent(itemId: Int64?, sentAt: Date, msgContent: MsgContent, chatDir: CIDirection) -> CIQuote {
return CIQuote(chatDir: chatDir, itemId: itemId, sentAt: sentAt, content: msgContent)
}
}
public struct CIFile: Decodable {