mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
mobile: settings for auto-accepting images, link previews, spinner for link previews; privacy settings (#708)
* ios: settings for auto-accepting images, link previews, spinner for link previews * android: settings for auto-accepting images, link previews, spinner for link previews, privacy settings * update translation Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com> * translation Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com> * translation Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com> Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
7c1d573a17
commit
29e2c00811
@@ -78,6 +78,8 @@ class AppPreferences(val context: Context) {
|
||||
)
|
||||
val performLA = mkBoolPreference(SHARED_PREFS_PERFORM_LA, false)
|
||||
val laNoticeShown = mkBoolPreference(SHARED_PREFS_LA_NOTICE_SHOWN, false)
|
||||
val privacyAcceptImages = mkBoolPreference(SHARED_PREFS_PRIVACY_ACCEPT_IMAGES, true)
|
||||
val privacyLinkPreviews = mkBoolPreference(SHARED_PREFS_PRIVACY_LINK_PREVIEWS, true)
|
||||
|
||||
private fun mkIntPreference(prefName: String, default: Int) =
|
||||
Preference(
|
||||
@@ -107,6 +109,8 @@ class AppPreferences(val context: Context) {
|
||||
private const val SHARED_PREFS_WEBRTC_CALLS_ON_LOCK_SCREEN = "CallsOnLockScreen"
|
||||
private const val SHARED_PREFS_PERFORM_LA = "PerformLA"
|
||||
private const val SHARED_PREFS_LA_NOTICE_SHOWN = "LANoticeShown"
|
||||
private const val SHARED_PREFS_PRIVACY_ACCEPT_IMAGES = "PrivacyAcceptImages"
|
||||
private const val SHARED_PREFS_PRIVACY_LINK_PREVIEWS = "PrivacyLinkPreviews"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -510,13 +514,8 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
|
||||
val cItem = r.chatItem.chatItem
|
||||
chatModel.addChatItem(cInfo, cItem)
|
||||
val file = cItem.file
|
||||
if (cItem.content.msgContent is MsgContent.MCImage && file != null && file.fileSize <= MAX_IMAGE_SIZE) {
|
||||
withApi {
|
||||
val chatItem = apiReceiveFile(file.fileId)
|
||||
if (chatItem != null) {
|
||||
chatItemSimpleUpdate(chatItem)
|
||||
}
|
||||
}
|
||||
if (cItem.content.msgContent is MsgContent.MCImage && file != null && file.fileSize <= MAX_IMAGE_SIZE && appPrefs.privacyAcceptImages.get()) {
|
||||
withApi { receiveFile(file.fileId) }
|
||||
}
|
||||
if (!cItem.isCall && (!isAppOnForeground(appContext) || chatModel.chatId.value != cInfo.id)) {
|
||||
ntfManager.notifyMessageReceived(cInfo, cItem)
|
||||
@@ -615,6 +614,13 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun receiveFile(fileId: Long) {
|
||||
val chatItem = apiReceiveFile(fileId)
|
||||
if (chatItem != null) {
|
||||
chatItemSimpleUpdate(chatItem)
|
||||
}
|
||||
}
|
||||
|
||||
private fun chatItemSimpleUpdate(aChatItem: AChatItem) {
|
||||
val cInfo = aChatItem.chatInfo
|
||||
val cItem = aChatItem.chatItem
|
||||
|
||||
@@ -26,7 +26,7 @@ import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun TerminalView(chatModel: ChatModel, close: () -> Unit) {
|
||||
val composeState = remember { mutableStateOf(ComposeState()) }
|
||||
val composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = false)) }
|
||||
BackHandler(onBack = close)
|
||||
TerminalLayout(
|
||||
chatModel.terminalItems,
|
||||
@@ -35,7 +35,7 @@ fun TerminalView(chatModel: ChatModel, close: () -> Unit) {
|
||||
withApi {
|
||||
// show "in progress"
|
||||
chatModel.controller.sendCmd(CC.Console(composeState.value.message))
|
||||
composeState.value = ComposeState()
|
||||
composeState.value = ComposeState(useLinkPreviews = false)
|
||||
// hide "in progress"
|
||||
}
|
||||
},
|
||||
@@ -120,7 +120,7 @@ fun PreviewTerminalLayout() {
|
||||
SimpleXTheme {
|
||||
TerminalLayout(
|
||||
terminalItems = TerminalItem.sampleData,
|
||||
composeState = remember { mutableStateOf(ComposeState()) },
|
||||
composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = false)) },
|
||||
sendCommand = {},
|
||||
close = {}
|
||||
)
|
||||
|
||||
@@ -44,7 +44,8 @@ import kotlinx.datetime.Clock
|
||||
fun ChatView(chatModel: ChatModel) {
|
||||
val chat: Chat? = chatModel.chats.firstOrNull { chat -> chat.chatInfo.id == chatModel.chatId.value }
|
||||
val user = chatModel.currentUser.value
|
||||
val composeState = remember { mutableStateOf(ComposeState()) }
|
||||
val useLinkPreviews = chatModel.controller.appPrefs.privacyLinkPreviews.get()
|
||||
val composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = useLinkPreviews)) }
|
||||
val attachmentOption = remember { mutableStateOf<AttachmentOption?>(null) }
|
||||
val attachmentBottomSheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden)
|
||||
val scope = rememberCoroutineScope()
|
||||
@@ -83,6 +84,7 @@ fun ChatView(chatModel: ChatModel) {
|
||||
scope,
|
||||
attachmentBottomSheetState,
|
||||
chatModel.chatItems,
|
||||
useLinkPreviews = useLinkPreviews,
|
||||
back = { chatModel.chatId.value = null },
|
||||
info = { ModalManager.shared.showCustomModal { close -> ChatInfoView(chatModel, close) } },
|
||||
openDirectChat = { contactId ->
|
||||
@@ -104,14 +106,7 @@ fun ChatView(chatModel: ChatModel) {
|
||||
}
|
||||
},
|
||||
receiveFile = { fileId ->
|
||||
withApi {
|
||||
val chatItem = chatModel.controller.apiReceiveFile(fileId)
|
||||
if (chatItem != null) {
|
||||
val cInfo = chatItem.chatInfo
|
||||
val cItem = chatItem.chatItem
|
||||
chatModel.upsertChatItem(cInfo, cItem)
|
||||
}
|
||||
}
|
||||
withApi { chatModel.controller.receiveFile(fileId) }
|
||||
},
|
||||
startCall = { media ->
|
||||
val cInfo = chat.chatInfo
|
||||
@@ -143,6 +138,7 @@ fun ChatLayout(
|
||||
scope: CoroutineScope,
|
||||
attachmentBottomSheetState: ModalBottomSheetState,
|
||||
chatItems: List<ChatItem>,
|
||||
useLinkPreviews: Boolean,
|
||||
back: () -> Unit,
|
||||
info: () -> Unit,
|
||||
openDirectChat: (Long) -> Unit,
|
||||
@@ -175,7 +171,7 @@ fun ChatLayout(
|
||||
modifier = Modifier.navigationBarsWithImePadding()
|
||||
) { contentPadding ->
|
||||
Box(Modifier.padding(contentPadding)) {
|
||||
ChatItemsList(user, chat, composeState, chatItems, openDirectChat, deleteMessage, receiveFile, acceptCall)
|
||||
ChatItemsList(user, chat, composeState, chatItems, useLinkPreviews, openDirectChat, deleteMessage, receiveFile, acceptCall)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -261,6 +257,7 @@ fun ChatItemsList(
|
||||
chat: Chat,
|
||||
composeState: MutableState<ComposeState>,
|
||||
chatItems: List<ChatItem>,
|
||||
useLinkPreviews: Boolean,
|
||||
openDirectChat: (Long) -> Unit,
|
||||
deleteMessage: (Long, CIDeleteMode) -> Unit,
|
||||
receiveFile: (Long) -> Unit,
|
||||
@@ -302,11 +299,11 @@ fun ChatItemsList(
|
||||
} else {
|
||||
Spacer(Modifier.size(42.dp))
|
||||
}
|
||||
ChatItemView(user, chat.chatInfo, cItem, composeState, cxt, uriHandler, showMember = showMember, deleteMessage = deleteMessage, receiveFile = receiveFile, acceptCall = acceptCall)
|
||||
ChatItemView(user, chat.chatInfo, cItem, composeState, cxt, uriHandler, showMember = showMember, useLinkPreviews = useLinkPreviews, deleteMessage = deleteMessage, receiveFile = receiveFile, acceptCall = acceptCall)
|
||||
}
|
||||
} else {
|
||||
Box(Modifier.padding(start = 86.dp, end = 12.dp)) {
|
||||
ChatItemView(user, chat.chatInfo, cItem, composeState, cxt, uriHandler, deleteMessage = deleteMessage, receiveFile = receiveFile, acceptCall = acceptCall)
|
||||
ChatItemView(user, chat.chatInfo, cItem, composeState, cxt, uriHandler, useLinkPreviews = useLinkPreviews, deleteMessage = deleteMessage, receiveFile = receiveFile, acceptCall = acceptCall)
|
||||
}
|
||||
}
|
||||
} else { // direct message
|
||||
@@ -317,7 +314,7 @@ fun ChatItemsList(
|
||||
end = if (sent) 12.dp else 76.dp,
|
||||
)
|
||||
) {
|
||||
ChatItemView(user, chat.chatInfo, cItem, composeState, cxt, uriHandler, deleteMessage = deleteMessage, receiveFile = receiveFile, acceptCall = acceptCall)
|
||||
ChatItemView(user, chat.chatInfo, cItem, composeState, cxt, uriHandler, useLinkPreviews = useLinkPreviews, deleteMessage = deleteMessage, receiveFile = receiveFile, acceptCall = acceptCall)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -375,12 +372,13 @@ fun PreviewChatLayout() {
|
||||
chatItems = chatItems,
|
||||
chatStats = Chat.ChatStats()
|
||||
),
|
||||
composeState = remember { mutableStateOf(ComposeState()) },
|
||||
composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = true)) },
|
||||
composeView = {},
|
||||
attachmentOption = remember { mutableStateOf<AttachmentOption?>(null) },
|
||||
scope = rememberCoroutineScope(),
|
||||
attachmentBottomSheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden),
|
||||
chatItems = chatItems,
|
||||
useLinkPreviews = true,
|
||||
back = {},
|
||||
info = {},
|
||||
openDirectChat = {},
|
||||
@@ -421,12 +419,13 @@ fun PreviewGroupChatLayout() {
|
||||
chatItems = chatItems,
|
||||
chatStats = Chat.ChatStats()
|
||||
),
|
||||
composeState = remember { mutableStateOf(ComposeState()) },
|
||||
composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = true)) },
|
||||
composeView = {},
|
||||
attachmentOption = remember { mutableStateOf<AttachmentOption?>(null) },
|
||||
scope = rememberCoroutineScope(),
|
||||
attachmentBottomSheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden),
|
||||
chatItems = chatItems,
|
||||
useLinkPreviews = true,
|
||||
back = {},
|
||||
info = {},
|
||||
openDirectChat = {},
|
||||
|
||||
@@ -45,7 +45,7 @@ import java.io.File
|
||||
|
||||
sealed class ComposePreview {
|
||||
object NoPreview: ComposePreview()
|
||||
class CLinkPreview(val linkPreview: LinkPreview): ComposePreview()
|
||||
class CLinkPreview(val linkPreview: LinkPreview?): ComposePreview()
|
||||
class ImagePreview(val image: String): ComposePreview()
|
||||
class FilePreview(val fileName: String): ComposePreview()
|
||||
}
|
||||
@@ -60,12 +60,14 @@ data class ComposeState(
|
||||
val message: String = "",
|
||||
val preview: ComposePreview = ComposePreview.NoPreview,
|
||||
val contextItem: ComposeContextItem = ComposeContextItem.NoContextItem,
|
||||
val inProgress: Boolean = false
|
||||
val inProgress: Boolean = false,
|
||||
val useLinkPreviews: Boolean
|
||||
) {
|
||||
constructor(editingItem: ChatItem): this(
|
||||
constructor(editingItem: ChatItem, useLinkPreviews: Boolean): this (
|
||||
editingItem.content.text,
|
||||
chatItemPreview(editingItem),
|
||||
ComposeContextItem.EditingItem(editingItem)
|
||||
ComposeContextItem.EditingItem(editingItem),
|
||||
useLinkPreviews = useLinkPreviews
|
||||
)
|
||||
|
||||
val editing: Boolean
|
||||
@@ -88,7 +90,7 @@ data class ComposeState(
|
||||
when (preview) {
|
||||
is ComposePreview.ImagePreview -> false
|
||||
is ComposePreview.FilePreview -> false
|
||||
else -> true
|
||||
else -> useLinkPreviews
|
||||
}
|
||||
val linkPreview: LinkPreview?
|
||||
get() =
|
||||
@@ -124,6 +126,7 @@ fun ComposeView(
|
||||
val prevLinkUrl = remember { mutableStateOf<String?>(null) }
|
||||
val pendingLinkUrl = remember { mutableStateOf<String?>(null) }
|
||||
val cancelledLinks = remember { mutableSetOf<String>() }
|
||||
val useLinkPreviews = chatModel.controller.appPrefs.privacyLinkPreviews.get()
|
||||
val smallFont = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onBackground)
|
||||
val textStyle = remember { mutableStateOf(smallFont) }
|
||||
// attachments
|
||||
@@ -239,6 +242,7 @@ fun ComposeView(
|
||||
|
||||
fun loadLinkPreview(url: String, wait: Long? = null) {
|
||||
if (pendingLinkUrl.value == url) {
|
||||
composeState.value = composeState.value.copy(preview = ComposePreview.CLinkPreview(null))
|
||||
withApi {
|
||||
if (wait != null) delay(wait)
|
||||
val lp = getLinkPreview(url)
|
||||
@@ -277,7 +281,7 @@ fun ComposeView(
|
||||
is ComposePreview.CLinkPreview -> {
|
||||
val url = parseMessage(cs.message)
|
||||
val lp = composePreview.linkPreview
|
||||
if (url == lp.uri) {
|
||||
if (lp != null && url == lp.uri) {
|
||||
MsgContent.MCLink(cs.message, preview = lp)
|
||||
} else {
|
||||
MsgContent.MCText(cs.message)
|
||||
@@ -299,7 +303,7 @@ fun ComposeView(
|
||||
}
|
||||
|
||||
fun clearState() {
|
||||
composeState.value = ComposeState()
|
||||
composeState.value = ComposeState(useLinkPreviews = useLinkPreviews)
|
||||
textStyle.value = smallFont
|
||||
chosenImage.value = null
|
||||
chosenFile.value = null
|
||||
@@ -397,6 +401,7 @@ fun ComposeView(
|
||||
if (uri != null) {
|
||||
cancelledLinks.add(uri)
|
||||
}
|
||||
pendingLinkUrl.value = null
|
||||
composeState.value = composeState.value.copy(preview = ComposePreview.NoPreview)
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ fun PreviewSendMsgView() {
|
||||
val textStyle = remember { mutableStateOf(smallFont) }
|
||||
SimpleXTheme {
|
||||
SendMsgView(
|
||||
composeState = remember { mutableStateOf(ComposeState()) },
|
||||
composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = true)) },
|
||||
sendMessage = {},
|
||||
onMessageChange = { _ -> },
|
||||
textStyle = textStyle
|
||||
@@ -129,7 +129,7 @@ fun PreviewSendMsgView() {
|
||||
fun PreviewSendMsgViewEditing() {
|
||||
val smallFont = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onBackground)
|
||||
val textStyle = remember { mutableStateOf(smallFont) }
|
||||
val composeStateEditing = ComposeState(editingItem = ChatItem.getSampleData())
|
||||
val composeStateEditing = ComposeState(editingItem = ChatItem.getSampleData(), useLinkPreviews = true)
|
||||
SimpleXTheme {
|
||||
SendMsgView(
|
||||
composeState = remember { mutableStateOf(composeStateEditing) },
|
||||
@@ -150,7 +150,7 @@ fun PreviewSendMsgViewEditing() {
|
||||
fun PreviewSendMsgViewInProgress() {
|
||||
val smallFont = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onBackground)
|
||||
val textStyle = remember { mutableStateOf(smallFont) }
|
||||
val composeStateInProgress = ComposeState(preview = ComposePreview.FilePreview("test.txt"), inProgress = true)
|
||||
val composeStateInProgress = ComposeState(preview = ComposePreview.FilePreview("test.txt"), inProgress = true, useLinkPreviews = true)
|
||||
SimpleXTheme {
|
||||
SendMsgView(
|
||||
composeState = remember { mutableStateOf(composeStateInProgress) },
|
||||
|
||||
@@ -26,7 +26,8 @@ import chat.simplex.app.views.helpers.*
|
||||
fun CIImageView(
|
||||
image: String,
|
||||
file: CIFile?,
|
||||
showMenu: MutableState<Boolean>
|
||||
showMenu: MutableState<Boolean>,
|
||||
receiveFile: (Long) -> Unit
|
||||
) {
|
||||
@Composable
|
||||
fun loadingIndicator() {
|
||||
@@ -98,11 +99,21 @@ fun CIImageView(
|
||||
})
|
||||
} else {
|
||||
imageView(base64ToBitmap(image), onClick = {
|
||||
if (file != null && file.fileStatus == CIFileStatus.RcvAccepted)
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(R.string.waiting_for_image),
|
||||
generalGetString(R.string.image_will_be_received_when_contact_is_online)
|
||||
)
|
||||
if (file != null) {
|
||||
when (file.fileStatus) {
|
||||
CIFileStatus.RcvInvitation ->
|
||||
receiveFile(file.fileId)
|
||||
CIFileStatus.RcvAccepted ->
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(R.string.waiting_for_image),
|
||||
generalGetString(R.string.image_will_be_received_when_contact_is_online)
|
||||
)
|
||||
CIFileStatus.RcvTransfer -> {} // ?
|
||||
CIFileStatus.RcvComplete -> {} // ?
|
||||
CIFileStatus.RcvCancelled -> {} // TODO
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
loadingIndicator()
|
||||
|
||||
@@ -36,6 +36,7 @@ fun ChatItemView(
|
||||
cxt: Context,
|
||||
uriHandler: UriHandler? = null,
|
||||
showMember: Boolean = false,
|
||||
useLinkPreviews: Boolean,
|
||||
deleteMessage: (Long, CIDeleteMode) -> Unit,
|
||||
receiveFile: (Long) -> Unit,
|
||||
acceptCall: (Contact) -> Unit
|
||||
@@ -69,7 +70,7 @@ fun ChatItemView(
|
||||
) {
|
||||
ItemAction(stringResource(R.string.reply_verb), Icons.Outlined.Reply, onClick = {
|
||||
if (composeState.value.editing) {
|
||||
composeState.value = ComposeState(contextItem = ComposeContextItem.QuotedItem(cItem))
|
||||
composeState.value = ComposeState(contextItem = ComposeContextItem.QuotedItem(cItem), useLinkPreviews = useLinkPreviews)
|
||||
} else {
|
||||
composeState.value = composeState.value.copy(contextItem = ComposeContextItem.QuotedItem(cItem))
|
||||
}
|
||||
@@ -98,7 +99,7 @@ fun ChatItemView(
|
||||
}
|
||||
if (cItem.meta.editable) {
|
||||
ItemAction(stringResource(R.string.edit_verb), Icons.Filled.Edit, onClick = {
|
||||
composeState.value = ComposeState(editingItem = cItem)
|
||||
composeState.value = ComposeState(editingItem = cItem, useLinkPreviews = useLinkPreviews)
|
||||
showMenu.value = false
|
||||
})
|
||||
}
|
||||
@@ -203,7 +204,8 @@ fun PreviewChatItemView() {
|
||||
ChatItem.getSampleData(
|
||||
1, CIDirection.DirectSnd(), Clock.System.now(), "hello"
|
||||
),
|
||||
composeState = remember { mutableStateOf(ComposeState()) },
|
||||
useLinkPreviews = true,
|
||||
composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = true)) },
|
||||
cxt = LocalContext.current,
|
||||
deleteMessage = { _, _ -> },
|
||||
receiveFile = {},
|
||||
@@ -220,7 +222,8 @@ fun PreviewChatItemViewDeletedContent() {
|
||||
User.sampleData,
|
||||
ChatInfo.Direct.sampleData,
|
||||
ChatItem.getDeletedContentSampleData(),
|
||||
composeState = remember { mutableStateOf(ComposeState()) },
|
||||
useLinkPreviews = true,
|
||||
composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = true)) },
|
||||
cxt = LocalContext.current,
|
||||
deleteMessage = { _, _ -> },
|
||||
receiveFile = {},
|
||||
|
||||
@@ -121,7 +121,7 @@ fun FramedItemView(
|
||||
Column(Modifier.fillMaxWidth()) {
|
||||
when (val mc = ci.content.msgContent) {
|
||||
is MsgContent.MCImage -> {
|
||||
CIImageView(image = mc.image, file = ci.file, showMenu)
|
||||
CIImageView(image = mc.image, file = ci.file, showMenu, receiveFile)
|
||||
if (mc.text == "") {
|
||||
metaColor = Color.White
|
||||
} else {
|
||||
|
||||
@@ -11,6 +11,7 @@ import androidx.compose.material.icons.outlined.Close
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -66,23 +67,36 @@ suspend fun getLinkPreview(url: String): LinkPreview? {
|
||||
|
||||
|
||||
@Composable
|
||||
fun ComposeLinkView(linkPreview: LinkPreview, cancelPreview: () -> Unit) {
|
||||
fun ComposeLinkView(linkPreview: LinkPreview?, cancelPreview: () -> Unit) {
|
||||
Row(
|
||||
Modifier.fillMaxWidth().padding(top = 8.dp).background(SentColorLight),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
val imageBitmap = base64ToBitmap(linkPreview.image).asImageBitmap()
|
||||
Image(
|
||||
imageBitmap,
|
||||
stringResource(R.string.image_descr_link_preview),
|
||||
modifier = Modifier.width(80.dp).height(60.dp).padding(end = 8.dp)
|
||||
)
|
||||
Column(Modifier.fillMaxWidth().weight(1F)) {
|
||||
Text(linkPreview.title, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
Text(
|
||||
linkPreview.uri, maxLines = 1, overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.body2
|
||||
if (linkPreview == null) {
|
||||
Box(
|
||||
Modifier.fillMaxWidth().weight(1f).height(60.dp).padding(start = 16.dp),
|
||||
contentAlignment = Alignment.CenterStart
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
Modifier.size(16.dp),
|
||||
color = HighOrLowlight,
|
||||
strokeWidth = 2.dp
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val imageBitmap = base64ToBitmap(linkPreview.image).asImageBitmap()
|
||||
Image(
|
||||
imageBitmap,
|
||||
stringResource(R.string.image_descr_link_preview),
|
||||
modifier = Modifier.width(80.dp).height(60.dp).padding(end = 8.dp)
|
||||
)
|
||||
Column(Modifier.fillMaxWidth().weight(1F)) {
|
||||
Text(linkPreview.title, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
Text(
|
||||
linkPreview.uri, maxLines = 1, overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.body2
|
||||
)
|
||||
}
|
||||
}
|
||||
IconButton(onClick = cancelPreview, modifier = Modifier.padding(0.dp)) {
|
||||
Icon(
|
||||
@@ -139,4 +153,12 @@ fun PreviewComposeLinkView() {
|
||||
SimpleXTheme {
|
||||
ComposeLinkView(LinkPreview.sampleData) { -> }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun PreviewComposeLinkViewLoading() {
|
||||
SimpleXTheme {
|
||||
ComposeLinkView(null) { -> }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ fun CallSettingsLayout(
|
||||
) {
|
||||
val lockCallState = remember { mutableStateOf(callOnLockScreen.get()) }
|
||||
Text(
|
||||
stringResource(R.string.call_settings),
|
||||
stringResource(R.string.your_calls),
|
||||
Modifier.padding(bottom = 24.dp),
|
||||
style = MaterialTheme.typography.h1
|
||||
)
|
||||
@@ -71,6 +71,7 @@ fun SharedPreferenceToggle(
|
||||
checkedThumbColor = MaterialTheme.colors.primary,
|
||||
uncheckedThumbColor = HighOrLowlight
|
||||
),
|
||||
modifier = Modifier.padding(end = 6.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package chat.simplex.app.views.usersettings
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
|
||||
@Composable
|
||||
fun PrivacySettingsView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit) {
|
||||
@Composable fun divider() = Divider(Modifier.padding(horizontal = 8.dp))
|
||||
Column(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.your_privacy),
|
||||
style = MaterialTheme.typography.h1,
|
||||
modifier = Modifier.padding(start = 8.dp, bottom = 24.dp)
|
||||
)
|
||||
ChatLockSection(chatModel.performLA, setPerformLA)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
|
||||
AutoAcceptImagesSection(chatModel.controller.appPrefs.privacyAcceptImages)
|
||||
divider()
|
||||
LinkPreviewsSection(chatModel.controller.appPrefs.privacyLinkPreviews)
|
||||
divider()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable private fun AutoAcceptImagesSection(prefAcceptImages: Preference<Boolean>) {
|
||||
SettingsSectionView() {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
Icons.Outlined.Image,
|
||||
contentDescription = stringResource(R.string.auto_accept_images),
|
||||
tint = HighOrLowlight,
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
SharedPreferenceToggle(stringResource(R.string.auto_accept_images), prefAcceptImages)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable private fun LinkPreviewsSection(prefLinkPreviews: Preference<Boolean>) {
|
||||
SettingsSectionView() {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
Icons.Outlined.TravelExplore,
|
||||
contentDescription = stringResource(R.string.send_link_previews),
|
||||
tint = HighOrLowlight,
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
SharedPreferenceToggle(stringResource(R.string.send_link_previews), prefLinkPreviews)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,6 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit) {
|
||||
profile = user.profile,
|
||||
runServiceInBackground = chatModel.runServiceInBackground,
|
||||
setRunServiceInBackground = ::setRunServiceInBackground,
|
||||
performLA = chatModel.performLA,
|
||||
setPerformLA = setPerformLA,
|
||||
showModal = { modalView -> { ModalManager.shared.showModal { modalView(chatModel) } } },
|
||||
showCustomModal = { modalView -> { ModalManager.shared.showCustomModal { close -> modalView(chatModel, close) } } },
|
||||
@@ -65,7 +64,6 @@ fun SettingsLayout(
|
||||
profile: Profile,
|
||||
runServiceInBackground: MutableState<Boolean>,
|
||||
setRunServiceInBackground: (Boolean) -> Unit,
|
||||
performLA: MutableState<Boolean>,
|
||||
setPerformLA: (Boolean) -> Unit,
|
||||
showModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
|
||||
showCustomModal: (@Composable (ChatModel, () -> Unit) -> Unit) -> (() -> Unit),
|
||||
@@ -103,7 +101,7 @@ fun SettingsLayout(
|
||||
|
||||
CallSettingsSection(showModal)
|
||||
divider()
|
||||
ChatLockSection(performLA, setPerformLA)
|
||||
PrivacySettingsSection(showModal, setPerformLA)
|
||||
divider()
|
||||
PrivateNotificationsSection(runServiceInBackground, setRunServiceInBackground)
|
||||
divider()
|
||||
@@ -154,6 +152,18 @@ fun SettingsLayout(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable private fun PrivacySettingsSection(showModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit), setPerformLA: (Boolean) -> Unit) {
|
||||
SettingsSectionView(showModal { PrivacySettingsView(it, setPerformLA) }) {
|
||||
Icon(
|
||||
Icons.Outlined.Lock,
|
||||
contentDescription = stringResource(R.string.privacy_and_security),
|
||||
tint = HighOrLowlight,
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(stringResource(R.string.privacy_and_security))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable private fun HelpViewSection(showModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit)) {
|
||||
SettingsSectionView(showModal { HelpView(it) }) {
|
||||
Icon(
|
||||
@@ -258,13 +268,13 @@ fun SettingsLayout(
|
||||
checkedThumbColor = MaterialTheme.colors.primary,
|
||||
uncheckedThumbColor = HighOrLowlight
|
||||
),
|
||||
modifier = Modifier.padding(end = 8.dp)
|
||||
modifier = Modifier.padding(end = 6.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable private fun ChatLockSection(performLA: MutableState<Boolean>, setPerformLA: (Boolean) -> Unit) {
|
||||
@Composable fun ChatLockSection(performLA: MutableState<Boolean>, setPerformLA: (Boolean) -> Unit) {
|
||||
SettingsSectionView() {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
@@ -286,7 +296,7 @@ fun SettingsLayout(
|
||||
checkedThumbColor = MaterialTheme.colors.primary,
|
||||
uncheckedThumbColor = HighOrLowlight
|
||||
),
|
||||
modifier = Modifier.padding(end = 8.dp)
|
||||
modifier = Modifier.padding(end = 6.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -362,7 +372,6 @@ fun PreviewSettingsLayout() {
|
||||
profile = Profile.sampleData,
|
||||
runServiceInBackground = remember { mutableStateOf(true) },
|
||||
setRunServiceInBackground = {},
|
||||
performLA = remember { mutableStateOf(false) },
|
||||
setPerformLA = {},
|
||||
showModal = { {} },
|
||||
showCustomModal = { {} },
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
<string name="toast_permission_denied">Разрешение не получено!</string>
|
||||
<string name="use_camera_button">Камера</string>
|
||||
<string name="from_gallery_button">Галерея</string>
|
||||
<string name="choose_file">Файлы\n(v2.0)</string>
|
||||
<string name="choose_file">Файлы</string>
|
||||
|
||||
<!-- help - ChatHelpView.kt -->
|
||||
<string name="thank_you_for_installing_simplex">Спасибо, что установили <xliff:g id="appNameFull">SimpleX Chat</xliff:g>!</string>
|
||||
@@ -379,7 +379,8 @@
|
||||
<string name="icon_descr_audio_call">аудиозвонок</string>
|
||||
|
||||
<!-- Call settings -->
|
||||
<string name="call_settings">Настройки звонков</string>
|
||||
<string name="call_settings">Аудио- и видеозвонки</string>
|
||||
<string name="your_calls">Ваши звонки</string>
|
||||
<string name="connect_calls_via_relay">Соединяться через сервер (relay)</string>
|
||||
<string name="call_on_lock_screen">Звонки на экране блокировки:</string>
|
||||
<string name="accept_call_on_lock_screen">Принимать</string>
|
||||
@@ -421,4 +422,10 @@
|
||||
<string name="integrity_msg_duplicate">повторное сообщение</string>
|
||||
<string name="alert_title_skipped_messages">Пропущенные сообщения</string>
|
||||
<string name="alert_text_skipped_messages_it_can_happen_when">Это может случится, когда:\n1. Сервер удалил сообщения, если они не были доставлены в течение 30 дней.\n2. Сервер, через который вы получаете сообщения от контакта, был обновлён и перезапущен.\n3. Соединение компроментировано.\nПожалуйста, соединитесь с девелоперами через Настройки, чтобы получать уведомления о серверах.\nМы планируем добавить избыточную доставку сообщений, чтобы не терять сообщения.</string>
|
||||
|
||||
<!-- Privacy settings -->
|
||||
<string name="privacy_and_security">Конфиденциальность</string>
|
||||
<string name="your_privacy">Конфиденциальность</string>
|
||||
<string name="auto_accept_images">Автоприем изображений</string>
|
||||
<string name="send_link_previews">Отправлять картинки ссылок</string>
|
||||
</resources>
|
||||
|
||||
@@ -169,7 +169,7 @@
|
||||
<string name="toast_permission_denied">Permission Denied!</string>
|
||||
<string name="use_camera_button">Use Camera</string>
|
||||
<string name="from_gallery_button">From Gallery</string>
|
||||
<string name="choose_file">Choose file\n(new in v2.0)</string>
|
||||
<string name="choose_file">Choose file</string>
|
||||
|
||||
<!-- help - ChatHelpView.kt -->
|
||||
<string name="thank_you_for_installing_simplex">Thank you for installing <xliff:g id="appNameFull">SimpleX Chat</xliff:g>!</string>
|
||||
@@ -381,7 +381,8 @@
|
||||
<string name="icon_descr_audio_call">audio call</string>
|
||||
|
||||
<!-- Call settings -->
|
||||
<string name="call_settings">Call settings</string>
|
||||
<string name="call_settings">Audio & video calls</string>
|
||||
<string name="your_calls">Your calls</string>
|
||||
<string name="connect_calls_via_relay">Connect via relay</string>
|
||||
<string name="call_on_lock_screen">Calls on lock screen:</string>
|
||||
<string name="accept_call_on_lock_screen">Accept</string>
|
||||
@@ -423,4 +424,10 @@
|
||||
<string name="integrity_msg_duplicate">duplicate message</string>
|
||||
<string name="alert_title_skipped_messages">Skipped messages</string>
|
||||
<string name="alert_text_skipped_messages_it_can_happen_when">It can happen when:\n1. The messages expire on the server if they were not received for 30 days,\n2. The server you use to receive the messages from this contact was updated and restarted.\n3. The connection is compromised.\nPlease connect to the developers via Settings to receive the updates about the servers.\nWe will be adding server redundancy to prevent lost messages.</string>
|
||||
|
||||
<!-- Privacy settings -->
|
||||
<string name="privacy_and_security">Privacy & security</string>
|
||||
<string name="your_privacy">Your privacy</string>
|
||||
<string name="auto_accept_images">Auto-accept images</string>
|
||||
<string name="send_link_previews">Send link previews</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user