Merge branch 'master' into master-ios
This commit is contained in:
@@ -695,6 +695,13 @@ sealed class ChatInfo: SomeChat, NamedChat {
|
||||
private val invalidChatName = generalGetString(R.string.invalid_chat)
|
||||
}
|
||||
}
|
||||
|
||||
val chatSettings
|
||||
get() = when(this) {
|
||||
is Direct -> contact.chatSettings
|
||||
is Group -> groupInfo.chatSettings
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@@ -783,7 +790,7 @@ data class Contact(
|
||||
profile = LocalProfile.sampleData,
|
||||
activeConn = Connection.sampleData,
|
||||
contactUsed = true,
|
||||
chatSettings = ChatSettings(true),
|
||||
chatSettings = ChatSettings(true, false),
|
||||
userPreferences = ChatPreferences.sampleData,
|
||||
mergedPreferences = ContactUserPreferences.sampleData,
|
||||
createdAt = Clock.System.now(),
|
||||
@@ -931,7 +938,7 @@ data class GroupInfo (
|
||||
fullGroupPreferences = FullGroupPreferences.sampleData,
|
||||
membership = GroupMember.sampleData,
|
||||
hostConnCustomUserProfileId = null,
|
||||
chatSettings = ChatSettings(true),
|
||||
chatSettings = ChatSettings(true, false),
|
||||
createdAt = Clock.System.now(),
|
||||
updatedAt = Clock.System.now()
|
||||
)
|
||||
|
||||
@@ -106,6 +106,7 @@ class AppPreferences(val context: Context) {
|
||||
)
|
||||
val privacyFullBackup = mkBoolPreference(SHARED_PREFS_PRIVACY_FULL_BACKUP, false)
|
||||
val experimentalCalls = mkBoolPreference(SHARED_PREFS_EXPERIMENTAL_CALLS, false)
|
||||
val showUnreadAndFavorites = mkBoolPreference(SHARED_PREFS_SHOW_UNREAD_AND_FAVORITES, false)
|
||||
val chatArchiveName = mkStrPreference(SHARED_PREFS_CHAT_ARCHIVE_NAME, null)
|
||||
val chatArchiveTime = mkDatePreference(SHARED_PREFS_CHAT_ARCHIVE_TIME, null)
|
||||
val chatLastStart = mkDatePreference(SHARED_PREFS_CHAT_LAST_START, null)
|
||||
@@ -249,6 +250,7 @@ class AppPreferences(val context: Context) {
|
||||
private const val SHARED_PREFS_PRIVACY_SIMPLEX_LINK_MODE = "PrivacySimplexLinkMode"
|
||||
internal const val SHARED_PREFS_PRIVACY_FULL_BACKUP = "FullBackup"
|
||||
private const val SHARED_PREFS_EXPERIMENTAL_CALLS = "ExperimentalCalls"
|
||||
private const val SHARED_PREFS_SHOW_UNREAD_AND_FAVORITES = "ShowUnreadAndFavorites"
|
||||
private const val SHARED_PREFS_CHAT_ARCHIVE_NAME = "ChatArchiveName"
|
||||
private const val SHARED_PREFS_CHAT_ARCHIVE_TIME = "ChatArchiveTime"
|
||||
private const val SHARED_PREFS_APP_LANGUAGE = "AppLanguage"
|
||||
@@ -2519,8 +2521,13 @@ data class KeepAliveOpts(
|
||||
|
||||
@Serializable
|
||||
data class ChatSettings(
|
||||
val enableNtfs: Boolean
|
||||
)
|
||||
val enableNtfs: Boolean,
|
||||
val favorite: Boolean
|
||||
) {
|
||||
companion object {
|
||||
val defaults: ChatSettings = ChatSettings(enableNtfs = true, favorite = false)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class FullChatPreferences(
|
||||
@@ -2930,7 +2937,8 @@ enum class GroupFeature: Feature {
|
||||
@SerialName("directMessages") DirectMessages,
|
||||
@SerialName("fullDelete") FullDelete,
|
||||
@SerialName("reactions") Reactions,
|
||||
@SerialName("voice") Voice;
|
||||
@SerialName("voice") Voice,
|
||||
@SerialName("files") Files;
|
||||
|
||||
override val hasParam: Boolean get() = when(this) {
|
||||
TimedMessages -> true
|
||||
@@ -2944,6 +2952,7 @@ enum class GroupFeature: Feature {
|
||||
FullDelete -> generalGetString(R.string.full_deletion)
|
||||
Reactions -> generalGetString(R.string.message_reactions)
|
||||
Voice -> generalGetString(R.string.voice_messages)
|
||||
Files -> generalGetString(R.string.files_and_media)
|
||||
}
|
||||
|
||||
val icon: Painter
|
||||
@@ -2953,6 +2962,7 @@ enum class GroupFeature: Feature {
|
||||
FullDelete -> painterResource(R.drawable.ic_delete_forever)
|
||||
Reactions -> painterResource(R.drawable.ic_add_reaction)
|
||||
Voice -> painterResource(R.drawable.ic_keyboard_voice)
|
||||
Files -> painterResource(R.drawable.ic_draft)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -2962,6 +2972,7 @@ enum class GroupFeature: Feature {
|
||||
FullDelete -> painterResource(R.drawable.ic_delete_forever_filled)
|
||||
Reactions -> painterResource(R.drawable.ic_add_reaction_filled)
|
||||
Voice -> painterResource(R.drawable.ic_keyboard_voice_filled)
|
||||
Files -> painterResource(R.drawable.ic_draft_filled)
|
||||
}
|
||||
|
||||
fun enableDescription(enabled: GroupFeatureEnabled, canEdit: Boolean): String =
|
||||
@@ -2987,6 +2998,10 @@ enum class GroupFeature: Feature {
|
||||
GroupFeatureEnabled.ON -> generalGetString(R.string.allow_to_send_voice)
|
||||
GroupFeatureEnabled.OFF -> generalGetString(R.string.prohibit_sending_voice)
|
||||
}
|
||||
Files -> when(enabled) {
|
||||
GroupFeatureEnabled.ON -> generalGetString(R.string.allow_to_send_files)
|
||||
GroupFeatureEnabled.OFF -> generalGetString(R.string.prohibit_sending_files)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
when(this) {
|
||||
@@ -3010,6 +3025,10 @@ enum class GroupFeature: Feature {
|
||||
GroupFeatureEnabled.ON -> generalGetString(R.string.group_members_can_send_voice)
|
||||
GroupFeatureEnabled.OFF -> generalGetString(R.string.voice_messages_are_prohibited)
|
||||
}
|
||||
Files -> when(enabled) {
|
||||
GroupFeatureEnabled.ON -> generalGetString(R.string.group_members_can_send_files)
|
||||
GroupFeatureEnabled.OFF -> generalGetString(R.string.files_are_prohibited_in_group)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3122,7 +3141,8 @@ data class FullGroupPreferences(
|
||||
val directMessages: GroupPreference,
|
||||
val fullDelete: GroupPreference,
|
||||
val reactions: GroupPreference,
|
||||
val voice: GroupPreference
|
||||
val voice: GroupPreference,
|
||||
val files: GroupPreference,
|
||||
) {
|
||||
fun toGroupPreferences(): GroupPreferences =
|
||||
GroupPreferences(
|
||||
@@ -3130,7 +3150,8 @@ data class FullGroupPreferences(
|
||||
directMessages = directMessages,
|
||||
fullDelete = fullDelete,
|
||||
reactions = reactions,
|
||||
voice = voice
|
||||
voice = voice,
|
||||
files = files,
|
||||
)
|
||||
|
||||
companion object {
|
||||
@@ -3139,7 +3160,8 @@ data class FullGroupPreferences(
|
||||
directMessages = GroupPreference(GroupFeatureEnabled.OFF),
|
||||
fullDelete = GroupPreference(GroupFeatureEnabled.OFF),
|
||||
reactions = GroupPreference(GroupFeatureEnabled.ON),
|
||||
voice = GroupPreference(GroupFeatureEnabled.ON)
|
||||
voice = GroupPreference(GroupFeatureEnabled.ON),
|
||||
files = GroupPreference(GroupFeatureEnabled.ON),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -3150,7 +3172,8 @@ data class GroupPreferences(
|
||||
val directMessages: GroupPreference?,
|
||||
val fullDelete: GroupPreference?,
|
||||
val reactions: GroupPreference?,
|
||||
val voice: GroupPreference?
|
||||
val voice: GroupPreference?,
|
||||
val files: GroupPreference?,
|
||||
) {
|
||||
companion object {
|
||||
val sampleData = GroupPreferences(
|
||||
@@ -3158,7 +3181,8 @@ data class GroupPreferences(
|
||||
directMessages = GroupPreference(GroupFeatureEnabled.OFF),
|
||||
fullDelete = GroupPreference(GroupFeatureEnabled.OFF),
|
||||
reactions = GroupPreference(GroupFeatureEnabled.ON),
|
||||
voice = GroupPreference(GroupFeatureEnabled.ON)
|
||||
voice = GroupPreference(GroupFeatureEnabled.ON),
|
||||
files = GroupPreference(GroupFeatureEnabled.ON),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: () -> Unit) {
|
||||
)
|
||||
}
|
||||
},
|
||||
changeNtfsState = { enabled, currentValue -> changeNtfsStatePerChat(enabled, currentValue, chat, chatModel) },
|
||||
changeNtfsState = { enabled, currentValue -> toggleNotifications(chat, enabled, chatModel, currentValue) },
|
||||
onSearchValueChanged = { value ->
|
||||
if (searchText.value == value) return@ChatLayout
|
||||
val c = chatModel.getChat(chat.chatInfo.id) ?: return@ChatLayout
|
||||
|
||||
@@ -16,6 +16,7 @@ import android.webkit.MimeTypeMap
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContract
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.*
|
||||
@@ -713,11 +714,22 @@ fun ComposeView(
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
) {
|
||||
IconButton(showChooseAttachment, enabled = !composeState.value.attachmentDisabled && rememberUpdatedState(chat.userCanSend).value) {
|
||||
val isGroupAndProhibitedFiles = chat.chatInfo is ChatInfo.Group && !chat.chatInfo.groupInfo.fullGroupPreferences.files.on
|
||||
val attachmentClicked = if (isGroupAndProhibitedFiles) {
|
||||
{
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(R.string.files_and_media_prohibited),
|
||||
text = generalGetString(R.string.only_owners_can_enable_files_and_media)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
showChooseAttachment
|
||||
}
|
||||
IconButton(attachmentClicked, enabled = !composeState.value.attachmentDisabled && rememberUpdatedState(chat.userCanSend).value) {
|
||||
Icon(
|
||||
painterResource(R.drawable.ic_attach_file_filled_500),
|
||||
contentDescription = stringResource(R.string.attach),
|
||||
tint = if (!composeState.value.attachmentDisabled && userCanSend.value) MaterialTheme.colors.primary else MaterialTheme.colors.secondary,
|
||||
tint = if (!composeState.value.attachmentDisabled && userCanSend.value && !isGroupAndProhibitedFiles) MaterialTheme.colors.primary else MaterialTheme.colors.secondary,
|
||||
modifier = Modifier
|
||||
.size(28.dp)
|
||||
.clip(CircleShape)
|
||||
|
||||
@@ -11,12 +11,16 @@ import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -34,15 +38,17 @@ fun AddGroupMembersView(groupInfo: GroupInfo, creatingGroup: Boolean = false, ch
|
||||
val selectedContacts = remember { mutableStateListOf<Long>() }
|
||||
val selectedRole = remember { mutableStateOf(GroupMemberRole.Member) }
|
||||
var allowModifyMembers by remember { mutableStateOf(true) }
|
||||
val searchText = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) }
|
||||
BackHandler(onBack = close)
|
||||
AddGroupMembersLayout(
|
||||
chatModel.incognito.value,
|
||||
groupInfo = groupInfo,
|
||||
creatingGroup = creatingGroup,
|
||||
contactsToAdd = getContactsToAdd(chatModel),
|
||||
contactsToAdd = getContactsToAdd(chatModel, searchText.value.text),
|
||||
selectedContacts = selectedContacts,
|
||||
selectedRole = selectedRole,
|
||||
allowModifyMembers = allowModifyMembers,
|
||||
searchText,
|
||||
openPreferences = {
|
||||
ModalManager.shared.showCustomModal { close ->
|
||||
GroupPreferencesView(chatModel, groupInfo.id, close)
|
||||
@@ -69,7 +75,8 @@ fun AddGroupMembersView(groupInfo: GroupInfo, creatingGroup: Boolean = false, ch
|
||||
)
|
||||
}
|
||||
|
||||
fun getContactsToAdd(chatModel: ChatModel): List<Contact> {
|
||||
fun getContactsToAdd(chatModel: ChatModel, search: String): List<Contact> {
|
||||
val s = search.trim().lowercase()
|
||||
val memberContactIds = chatModel.groupMembers
|
||||
.filter { it.memberCurrent }
|
||||
.mapNotNull { it.memberContactId }
|
||||
@@ -78,7 +85,7 @@ fun getContactsToAdd(chatModel: ChatModel): List<Contact> {
|
||||
.map { it.chatInfo }
|
||||
.filterIsInstance<ChatInfo.Direct>()
|
||||
.map { it.contact }
|
||||
.filter { it.contactId !in memberContactIds }
|
||||
.filter { it.contactId !in memberContactIds && it.chatViewName.lowercase().contains(s) }
|
||||
.sortedBy { it.displayName.lowercase() }
|
||||
.toList()
|
||||
}
|
||||
@@ -92,6 +99,7 @@ fun AddGroupMembersLayout(
|
||||
selectedContacts: List<Long>,
|
||||
selectedRole: MutableState<GroupMemberRole>,
|
||||
allowModifyMembers: Boolean,
|
||||
searchText: MutableState<TextFieldValue>,
|
||||
openPreferences: () -> Unit,
|
||||
inviteMembers: () -> Unit,
|
||||
clearSelection: () -> Unit,
|
||||
@@ -125,7 +133,7 @@ fun AddGroupMembersLayout(
|
||||
}
|
||||
SectionSpacer()
|
||||
|
||||
if (contactsToAdd.isEmpty()) {
|
||||
if (contactsToAdd.isEmpty() && searchText.value.text.isEmpty()) {
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
@@ -154,8 +162,10 @@ fun AddGroupMembersLayout(
|
||||
InviteSectionFooter(selectedContactsCount = selectedContacts.size, allowModifyMembers, clearSelection)
|
||||
}
|
||||
SectionDividerSpaced(maxTopPadding = true)
|
||||
|
||||
SectionView(stringResource(R.string.select_contacts)) {
|
||||
SectionItemView(padding = PaddingValues(start = DEFAULT_PADDING, end = DEFAULT_PADDING_HALF)) {
|
||||
SearchRowView(searchText, selectedContacts.size)
|
||||
}
|
||||
ContactList(contacts = contactsToAdd, selectedContacts, groupInfo, allowModifyMembers, addContact, removeContact)
|
||||
}
|
||||
}
|
||||
@@ -163,6 +173,25 @@ fun AddGroupMembersLayout(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SearchRowView(
|
||||
searchText: MutableState<TextFieldValue> = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue()) },
|
||||
selectedContactsSize: Int
|
||||
) {
|
||||
Box(Modifier.width(36.dp), contentAlignment = Alignment.Center) {
|
||||
Icon(painterResource(R.drawable.ic_search), stringResource(android.R.string.search_go), tint = MaterialTheme.colors.secondary)
|
||||
}
|
||||
Spacer(Modifier.width(DEFAULT_SPACE_AFTER_ICON))
|
||||
SearchTextField(Modifier.fillMaxWidth(), searchText = searchText, alwaysVisible = true) {
|
||||
searchText.value = searchText.value.copy(it)
|
||||
}
|
||||
val view = LocalView.current
|
||||
LaunchedEffect(selectedContactsSize) {
|
||||
searchText.value = searchText.value.copy("")
|
||||
hideKeyboard(view)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RoleSelectionRow(groupInfo: GroupInfo, selectedRole: MutableState<GroupMemberRole>, enabled: Boolean) {
|
||||
Row(
|
||||
@@ -325,6 +354,7 @@ fun PreviewAddGroupMembersLayout() {
|
||||
selectedContacts = remember { mutableStateListOf() },
|
||||
selectedRole = remember { mutableStateOf(GroupMemberRole.Admin) },
|
||||
allowModifyMembers = true,
|
||||
searchText = remember { mutableStateOf(TextFieldValue("")) },
|
||||
openPreferences = {},
|
||||
inviteMembers = {},
|
||||
clearSelection = {},
|
||||
|
||||
@@ -13,12 +13,14 @@ import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -196,10 +198,17 @@ fun GroupChatInfoLayout(
|
||||
val tint = if (chat.chatInfo.incognito) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
|
||||
AddMembersButton(tint, onAddMembersClick)
|
||||
}
|
||||
val searchText = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue()) }
|
||||
val filteredMembers = derivedStateOf { members.filter { it.chatViewName.lowercase().contains(searchText.value.text.trim()) } }
|
||||
if (members.size > 8) {
|
||||
SectionItemView(padding = PaddingValues(start = 14.dp, end = DEFAULT_PADDING_HALF)) {
|
||||
SearchRowView(searchText)
|
||||
}
|
||||
}
|
||||
SectionItemView(minHeight = 54.dp) {
|
||||
MemberRow(groupInfo.membership, user = true)
|
||||
}
|
||||
MembersList(members, showMemberInfo)
|
||||
MembersList(filteredMembers.value, showMemberInfo)
|
||||
}
|
||||
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
|
||||
SectionView {
|
||||
@@ -393,6 +402,19 @@ private fun DeleteGroupButton(onClick: () -> Unit) {
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SearchRowView(
|
||||
searchText: MutableState<TextFieldValue> = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue()) }
|
||||
) {
|
||||
Box(Modifier.width(36.dp), contentAlignment = Alignment.Center) {
|
||||
Icon(painterResource(R.drawable.ic_search), stringResource(android.R.string.search_go), tint = MaterialTheme.colors.secondary)
|
||||
}
|
||||
Spacer(Modifier.width(14.dp))
|
||||
SearchTextField(Modifier.fillMaxWidth(), searchText = searchText, alwaysVisible = true) {
|
||||
searchText.value = searchText.value.copy(it)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewGroupChatInfoLayout() {
|
||||
|
||||
@@ -103,6 +103,12 @@ private fun GroupPreferencesLayout(
|
||||
FeatureSection(GroupFeature.Voice, allowVoice, groupInfo, preferences, onTTLUpdated) {
|
||||
applyPrefs(preferences.copy(voice = GroupPreference(enable = it)))
|
||||
}
|
||||
// TODO uncomment in 5.3
|
||||
// SectionDividerSpaced(true, maxBottomPadding = false)
|
||||
// val allowFiles = remember(preferences) { mutableStateOf(preferences.files.enable) }
|
||||
// FeatureSection(GroupFeature.Files, allowFiles, groupInfo, preferences, onTTLUpdated) {
|
||||
// applyPrefs(preferences.copy(files = GroupPreference(enable = it)))
|
||||
// }
|
||||
if (groupInfo.canEdit) {
|
||||
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
|
||||
ResetSaveButtons(
|
||||
|
||||
@@ -145,6 +145,7 @@ fun ContactMenuItems(chat: Chat, chatModel: ChatModel, showMenu: MutableState<Bo
|
||||
} else {
|
||||
MarkUnreadChatAction(chat, chatModel, showMenu)
|
||||
}
|
||||
ToggleFavoritesChatAction(chat, chatModel, chat.chatInfo.chatSettings?.favorite == true, showMenu)
|
||||
ToggleNotificationsChatAction(chat, chatModel, chat.chatInfo.ntfsEnabled, showMenu)
|
||||
ClearChatAction(chat, chatModel, showMenu)
|
||||
DeleteContactAction(chat, chatModel, showMenu)
|
||||
@@ -173,6 +174,7 @@ fun GroupMenuItems(chat: Chat, groupInfo: GroupInfo, chatModel: ChatModel, showM
|
||||
} else {
|
||||
MarkUnreadChatAction(chat, chatModel, showMenu)
|
||||
}
|
||||
ToggleFavoritesChatAction(chat, chatModel, chat.chatInfo.chatSettings?.favorite == true, showMenu)
|
||||
ToggleNotificationsChatAction(chat, chatModel, chat.chatInfo.ntfsEnabled, showMenu)
|
||||
ClearChatAction(chat, chatModel, showMenu)
|
||||
if (groupInfo.membership.memberCurrent) {
|
||||
@@ -210,13 +212,25 @@ fun MarkUnreadChatAction(chat: Chat, chatModel: ChatModel, showMenu: MutableStat
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ToggleFavoritesChatAction(chat: Chat, chatModel: ChatModel, favorite: Boolean, showMenu: MutableState<Boolean>) {
|
||||
ItemAction(
|
||||
if (favorite) stringResource(R.string.unfavorite_chat) else stringResource(R.string.favorite_chat),
|
||||
if (favorite) painterResource(R.drawable.ic_star_off) else painterResource(R.drawable.ic_star),
|
||||
onClick = {
|
||||
toggleChatFavorite(chat, !favorite, chatModel)
|
||||
showMenu.value = false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ToggleNotificationsChatAction(chat: Chat, chatModel: ChatModel, ntfsEnabled: Boolean, showMenu: MutableState<Boolean>) {
|
||||
ItemAction(
|
||||
if (ntfsEnabled) stringResource(R.string.mute_chat) else stringResource(R.string.unmute_chat),
|
||||
if (ntfsEnabled) painterResource(R.drawable.ic_notifications_off) else painterResource(R.drawable.ic_notifications),
|
||||
onClick = {
|
||||
changeNtfsStatePerChat(!ntfsEnabled, mutableStateOf(ntfsEnabled), chat, chatModel)
|
||||
toggleNotifications(chat, !ntfsEnabled, chatModel)
|
||||
showMenu.value = false
|
||||
}
|
||||
)
|
||||
@@ -535,13 +549,23 @@ fun groupInvitationAcceptedAlert() {
|
||||
)
|
||||
}
|
||||
|
||||
fun changeNtfsStatePerChat(enabled: Boolean, currentState: MutableState<Boolean>, chat: Chat, chatModel: ChatModel) {
|
||||
fun toggleNotifications(chat: Chat, enableNtfs: Boolean, chatModel: ChatModel, currentState: MutableState<Boolean>? = null) {
|
||||
val chatSettings = (chat.chatInfo.chatSettings ?: ChatSettings.defaults).copy(enableNtfs = enableNtfs)
|
||||
updateChatSettings(chat, chatSettings, chatModel, currentState)
|
||||
}
|
||||
|
||||
fun toggleChatFavorite(chat: Chat, favorite: Boolean, chatModel: ChatModel) {
|
||||
val chatSettings = (chat.chatInfo.chatSettings ?: ChatSettings.defaults).copy(favorite = favorite)
|
||||
updateChatSettings(chat, chatSettings, chatModel)
|
||||
}
|
||||
|
||||
fun updateChatSettings(chat: Chat, chatSettings: ChatSettings, chatModel: ChatModel, currentState: MutableState<Boolean>? = null) {
|
||||
val newChatInfo = when(chat.chatInfo) {
|
||||
is ChatInfo.Direct -> with (chat.chatInfo) {
|
||||
ChatInfo.Direct(contact.copy(chatSettings = contact.chatSettings.copy(enableNtfs = enabled)))
|
||||
ChatInfo.Direct(contact.copy(chatSettings = chatSettings))
|
||||
}
|
||||
is ChatInfo.Group -> with(chat.chatInfo) {
|
||||
ChatInfo.Group(groupInfo.copy(chatSettings = groupInfo.chatSettings.copy(enableNtfs = enabled)))
|
||||
ChatInfo.Group(groupInfo.copy(chatSettings = chatSettings))
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
@@ -557,10 +581,13 @@ fun changeNtfsStatePerChat(enabled: Boolean, currentState: MutableState<Boolean>
|
||||
}
|
||||
if (res && newChatInfo != null) {
|
||||
chatModel.updateChatInfo(newChatInfo)
|
||||
if (!enabled) {
|
||||
if (!chatSettings.enableNtfs) {
|
||||
chatModel.controller.ntfManager.cancelNotificationsForChat(chat.id)
|
||||
}
|
||||
currentState.value = enabled
|
||||
val current = currentState?.value
|
||||
if (current != null) {
|
||||
currentState.value = !current
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.capitalize
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.*
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import chat.simplex.app.*
|
||||
@@ -222,11 +223,6 @@ private fun ChatListToolbar(chatModel: ChatModel, drawerState: DrawerState, user
|
||||
},
|
||||
title = {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
stringResource(R.string.your_chats),
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
if (chatModel.incognito.value) {
|
||||
Icon(
|
||||
painterResource(R.drawable.ic_theater_comedy_filled),
|
||||
@@ -235,6 +231,14 @@ private fun ChatListToolbar(chatModel: ChatModel, drawerState: DrawerState, user
|
||||
modifier = Modifier.padding(10.dp).size(26.dp)
|
||||
)
|
||||
}
|
||||
Text(
|
||||
stringResource(R.string.your_chats),
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
if (chatModel.chats.size > 0) {
|
||||
ToggleFilterButton()
|
||||
}
|
||||
}
|
||||
},
|
||||
onTitleClick = null,
|
||||
@@ -275,6 +279,24 @@ private fun BoxScope.unreadBadge(text: String? = "") {
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ToggleFilterButton() {
|
||||
val pref = remember { SimplexApp.context.chatModel.controller.appPrefs.showUnreadAndFavorites }
|
||||
IconButton(onClick = { pref.set(!pref.get()) }) {
|
||||
Icon(
|
||||
painterResource(R.drawable.ic_filter_list),
|
||||
null,
|
||||
tint = if (pref.state.value) MaterialTheme.colors.background else MaterialTheme.colors.primary,
|
||||
modifier = Modifier
|
||||
.padding(3.dp)
|
||||
.background(color = if (pref.state.value) MaterialTheme.colors.primary else MaterialTheme.colors.background, shape = RoundedCornerShape(50))
|
||||
.border(width = 1.dp, color = MaterialTheme.colors.primary, shape = RoundedCornerShape(50))
|
||||
.padding(3.dp)
|
||||
.size(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ProgressIndicator() {
|
||||
CircularProgressIndicator(
|
||||
@@ -290,14 +312,12 @@ private var lazyListState = 0 to 0
|
||||
|
||||
@Composable
|
||||
private fun ChatList(chatModel: ChatModel, search: String) {
|
||||
val filter: (Chat) -> Boolean = { chat: Chat ->
|
||||
chat.chatInfo.chatViewName.lowercase().contains(search.lowercase())
|
||||
}
|
||||
val listState = rememberLazyListState(lazyListState.first, lazyListState.second)
|
||||
DisposableEffect(Unit) {
|
||||
onDispose { lazyListState = listState.firstVisibleItemIndex to listState.firstVisibleItemScrollOffset }
|
||||
}
|
||||
val chats by remember(search) { derivedStateOf { if (search.isEmpty()) chatModel.chats else chatModel.chats.filter(filter) } }
|
||||
val showUnreadAndFavorites = remember { chatModel.controller.appPrefs.showUnreadAndFavorites.state }.value
|
||||
val chats by remember(search, showUnreadAndFavorites) { derivedStateOf { filteredChats(showUnreadAndFavorites, search) } }
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
listState
|
||||
@@ -306,4 +326,44 @@ private fun ChatList(chatModel: ChatModel, search: String) {
|
||||
ChatListNavLinkView(chat, chatModel)
|
||||
}
|
||||
}
|
||||
if (chats.isEmpty() && !chatModel.chats.isEmpty()) {
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text(generalGetString(R.string.no_filtered_chats), color = MaterialTheme.colors.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun filteredChats(showUnreadAndFavorites: Boolean, searchText: String): List<Chat> {
|
||||
val chatModel = SimplexApp.context.chatModel
|
||||
val s = searchText.trim().lowercase()
|
||||
return if (s.isEmpty() && !showUnreadAndFavorites)
|
||||
chatModel.chats
|
||||
else {
|
||||
chatModel.chats.filter { chat ->
|
||||
when (val cInfo = chat.chatInfo) {
|
||||
is ChatInfo.Direct -> if (s.isEmpty()) {
|
||||
filtered(chat)
|
||||
} else {
|
||||
(viewNameContains(cInfo, s) ||
|
||||
cInfo.contact.profile.displayName.lowercase().contains(s) ||
|
||||
cInfo.contact.fullName.lowercase().contains(s))
|
||||
}
|
||||
is ChatInfo.Group -> if (s.isEmpty()) {
|
||||
(filtered(chat) || cInfo.groupInfo.membership.memberStatus == GroupMemberStatus.MemInvited)
|
||||
} else {
|
||||
viewNameContains(cInfo, s)
|
||||
}
|
||||
is ChatInfo.ContactRequest -> s.isEmpty() || viewNameContains(cInfo, s)
|
||||
is ChatInfo.ContactConnection -> s.isNotEmpty() && cInfo.contactConnection.localAlias.lowercase().contains(s)
|
||||
is ChatInfo.InvalidJSON -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun filtered(chat: Chat): Boolean =
|
||||
(chat.chatInfo.chatSettings?.favorite ?: false) || chat.chatStats.unreadCount > 0 || chat.chatStats.unreadChat
|
||||
|
||||
private fun viewNameContains(cInfo: ChatInfo, s: String): Boolean =
|
||||
cInfo.chatViewName.lowercase().contains(s.lowercase())
|
||||
|
||||
|
||||
@@ -245,6 +245,21 @@ fun ChatPreviewView(
|
||||
.size(17.dp)
|
||||
)
|
||||
}
|
||||
} else if (chat.chatInfo.chatSettings?.favorite == true) {
|
||||
Box(
|
||||
Modifier.padding(top = 24.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.ic_star_filled),
|
||||
contentDescription = generalGetString(R.string.favorite_chat),
|
||||
tint = MaterialTheme.colors.secondary,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 3.dp)
|
||||
.padding(vertical = 1.dp)
|
||||
.size(17.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (cInfo is ChatInfo.Direct) {
|
||||
Box(
|
||||
|
||||
@@ -33,7 +33,7 @@ fun DefaultTopAppBar(
|
||||
if (!showSearch) {
|
||||
title?.invoke()
|
||||
} else {
|
||||
SearchTextField(Modifier.fillMaxWidth(), stringResource(android.R.string.search_go), alwaysVisible = false, onSearchValueChanged)
|
||||
SearchTextField(Modifier.fillMaxWidth(), alwaysVisible = false, onValueChange = onSearchValueChanged)
|
||||
}
|
||||
},
|
||||
backgroundColor = if (isInDarkTheme()) ToolbarDark else ToolbarLight,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package chat.simplex.app.views.helpers
|
||||
|
||||
import android.R
|
||||
import android.util.Log
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.*
|
||||
@@ -8,8 +9,13 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.capitalize
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import chat.simplex.app.TAG
|
||||
import chat.simplex.app.ui.theme.isInDarkTheme
|
||||
import chat.simplex.app.ui.theme.themedBackground
|
||||
|
||||
@@ -29,8 +29,13 @@ import kotlinx.coroutines.delay
|
||||
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
fun SearchTextField(modifier: Modifier, placeholder: String, alwaysVisible: Boolean, onValueChange: (String) -> Unit) {
|
||||
var searchText by rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) }
|
||||
fun SearchTextField(
|
||||
modifier: Modifier,
|
||||
alwaysVisible: Boolean,
|
||||
searchText: MutableState<TextFieldValue> = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) },
|
||||
placeholder: String = stringResource(android.R.string.search_go),
|
||||
onValueChange: (String) -> Unit
|
||||
) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val focusManager = LocalFocusManager.current
|
||||
val keyboard = LocalSoftwareKeyboardController.current
|
||||
@@ -45,7 +50,7 @@ fun SearchTextField(modifier: Modifier, placeholder: String, alwaysVisible: Bool
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
if (searchText.text.isNotEmpty()) onValueChange("")
|
||||
if (searchText.value.text.isNotEmpty()) onValueChange("")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +64,7 @@ fun SearchTextField(modifier: Modifier, placeholder: String, alwaysVisible: Bool
|
||||
val shape = MaterialTheme.shapes.small.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize)
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
BasicTextField(
|
||||
value = searchText,
|
||||
value = searchText.value,
|
||||
modifier = modifier
|
||||
.background(colors.backgroundColor(enabled).value, shape)
|
||||
.indicatorLine(enabled, false, interactionSource, colors)
|
||||
@@ -69,7 +74,7 @@ fun SearchTextField(modifier: Modifier, placeholder: String, alwaysVisible: Bool
|
||||
minHeight = TextFieldDefaults.MinHeight
|
||||
),
|
||||
onValueChange = {
|
||||
searchText = it
|
||||
searchText.value = it
|
||||
onValueChange(it.text)
|
||||
},
|
||||
cursorBrush = SolidColor(colors.cursorColor(false).value),
|
||||
@@ -84,18 +89,18 @@ fun SearchTextField(modifier: Modifier, placeholder: String, alwaysVisible: Bool
|
||||
interactionSource = interactionSource,
|
||||
decorationBox = @Composable { innerTextField ->
|
||||
TextFieldDefaults.TextFieldDecorationBox(
|
||||
value = searchText.text,
|
||||
value = searchText.value.text,
|
||||
innerTextField = innerTextField,
|
||||
placeholder = {
|
||||
Text(placeholder)
|
||||
},
|
||||
trailingIcon = if (searchText.text.isNotEmpty()) {{
|
||||
trailingIcon = if (searchText.value.text.isNotEmpty()) {{
|
||||
IconButton({
|
||||
if (alwaysVisible) {
|
||||
keyboard?.hide()
|
||||
focusManager.clearFocus()
|
||||
}
|
||||
searchText = TextFieldValue("");
|
||||
searchText.value = TextFieldValue("");
|
||||
onValueChange("")
|
||||
}) {
|
||||
Icon(painterResource(R.drawable.ic_close), stringResource(R.string.icon_descr_close_button), tint = MaterialTheme.colors.primary,)
|
||||
|
||||
@@ -62,7 +62,7 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean, FragmentActivity)
|
||||
ModalView(
|
||||
{ close() },
|
||||
endButtons = {
|
||||
SearchTextField(Modifier.fillMaxWidth(), stringResource(android.R.string.search_go), alwaysVisible = true) { search.value = it }
|
||||
SearchTextField(Modifier.fillMaxWidth(), alwaysVisible = true) { search.value = it }
|
||||
},
|
||||
content = { modalView(chatModel, search) })
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<vector android:height="24dp" android:viewportHeight="960"
|
||||
android:viewportWidth="960" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M430.5,707.5q-12.25,0 -20.63,-8.18 -8.38,-8.18 -8.38,-20.5 0,-12.32 8.38,-20.58T430.5,650h99q12.25,0 20.63,8.43 8.38,8.43 8.38,20.75 0,12.32 -8.38,20.33t-20.63,8h-99ZM152.5,300q-12.25,0 -20.63,-8.18 -8.38,-8.18 -8.38,-20.5 0,-12.32 8.38,-20.58t20.63,-8.25h655q12.25,0 20.63,8.43 8.38,8.43 8.38,20.75 0,12.32 -8.38,20.33t-20.63,8h-655ZM271.5,504q-12.25,0 -20.63,-8.43 -8.38,-8.43 -8.38,-20.75 0,-11.82 8.38,-20.08t20.63,-8.25h417q11.75,0 20.13,8.43 8.38,8.43 8.38,20.25 0,12.32 -8.38,20.58T688.5,504h-417Z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M480,729.5 L293.5,842q-8,5 -16.75,4.5T261.5,841q-6.5,-5 -10,-12.5t-1,-17.5l49,-212.5L135,455q-7.5,-6.5 -9.25,-14.5t0.25,-16q2,-8 9,-13.25t16.5,-6.25L369,386l84.5,-200.5q3.5,-8.5 11,-13T480,168q8,0 15.5,4.5t11.5,13L591.5,386 809,405q9,1 16,6.25t9,13.25q2,8 0.25,16T825,455L660.5,598.5 710,811q2,10 -1.5,17.5t-10,12.5q-6.5,5 -15.25,5.5T667,842L480,729.5Z"/>
|
||||
</vector>
|
||||
9
apps/android/app/src/main/res/drawable/ic_star_off.xml
Normal file
9
apps/android/app/src/main/res/drawable/ic_star_off.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector android:height="24dp" android:viewportHeight="960"
|
||||
android:viewportWidth="960" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<group>
|
||||
<clip-path android:pathData="m124.92,168.01v678.55h710.16v-678.55zM398.32,304.53c2.09,2.21 4.19,4.36 6.29,6.56 4.15,4.34 8.26,8.76 12.34,13.16 4.31,4.63 8.58,9.28 12.85,13.95 3.91,4.31 7.83,8.63 11.76,12.93 3.61,4.02 7.24,8.02 10.86,12.03l1.72,1.88 -5.43,4.92c2.66,2.71 5.31,5.44 7.97,8.16L437.5,396.84c3.42,3.27 6.86,6.52 10.27,9.8l2.93,2.85 -14.45,15 -2.93,-2.81c-6,-5.77 -12,-11.58 -18.05,-17.3 -5.52,-5.18 -11.09,-10.28 -16.76,-15.31 -5.86,-5.27 -11.73,-10.56 -17.5,-15.94 -3.88,-3.66 -7.77,-7.34 -11.45,-11.21 -1.01,-1.05 -2,-2.11 -3.05,-3.13l7.5,-7.89c-1.27,-1.33 -2.53,-2.67 -3.83,-3.98 -1.76,-1.8 -3.55,-3.6 -5.31,-5.39L372.07,335c-0.69,-0.73 -1.39,-1.45 -2.07,-2.19zM622.85,527.89c0.98,1.11 1.94,2.25 2.93,3.36 2.92,3.29 5.93,6.49 8.91,9.73 3.89,4.24 7.78,8.47 11.68,12.7 3.7,4.02 7.4,8.03 11.13,12.03 3.37,3.66 6.82,7.32 10.23,10.94 3.28,3.41 6.54,6.81 9.8,10.23 3.57,3.78 7.15,7.57 10.7,11.37 2.84,3.02 5.68,6 8.52,9.02l-12.77,12.03c1.03,1.1 2.06,2.19 3.09,3.28l-3.95,3.71c0.52,0.7 1.04,1.4 1.13,1.52 2.1,2.62 4.28,5.17 6.45,7.73l-1.56,1.29c1.82,2.14 3.71,4.2 5.59,6.29 0.87,0.95 1.78,1.87 2.66,2.81l4.18,-3.36c3.86,4.81 7.71,9.63 11.6,14.41 3.65,4.49 7.31,8.94 11.13,13.28 3.45,4.01 6.9,8.04 10.27,12.11 2.75,3.32 5.52,6.62 8.2,10 3.03,3.94 6.03,7.92 7.73,12.66 -28.44,22.74 -22.95,32.05 -37.19,16.8 -3.88,-4.21 -7.78,-8.42 -11.68,-12.62 -5.85,-6.31 -11.68,-12.6 -17.58,-18.87 -6.38,-6.81 -12.79,-13.6 -19.1,-20.47 -3.99,-4.44 -8.09,-8.87 -11.45,-13.83l0.47,-0.27c-4.95,-4.81 -9.89,-9.64 -14.84,-14.45 -3.9,-3.83 -7.85,-7.63 -11.6,-11.6l5.66,-5.16c-0.58,-0.57 -1.17,-1.15 -1.76,-1.72 -6.97,-6.69 -14,-13.33 -21.13,-19.84l-0.66,0.66c-2.91,-2.91 -5.78,-5.84 -8.67,-8.75 -1.04,-0.87 -2.01,-1.83 -3.09,-2.66l0.2,-0.27c-2.67,-2.7 -5.36,-5.37 -8.01,-8.09 -6.04,-6.23 -11.98,-12.56 -17.73,-19.06 -2,-2.24 -4.01,-4.52 -6.05,-6.72l29.22,-27.3c2.27,2.44 4.5,4.93 6.72,7.42 0.94,1.06 1.92,2.09 2.89,3.13z"/>
|
||||
<path android:fillColor="#FF000000" android:pathData="m321,757.5 l159,-95 159,96 -42.5,-180 140,-121.5L552,440.5l-72,-170L408.5,440 224,456l140,121 -43,180.5ZM480,729.5L293.5,842q-8,5 -16.75,4.5T261.5,841q-6.5,-5 -10,-12.5t-1,-17.5l49,-212.5L135,455q-7.5,-6.5 -9.25,-14.5t0.25,-16q2,-8 9,-13.25t16.5,-6.25L369,386l84.5,-200.5q3.5,-8.5 11,-13T480,168q8,0 15.5,4.5t11.5,13L591.5,386 809,405q9,1 16,6.25t9,13.25q2,8 0.25,16T825,455L660.5,598.5 710,811q2,10 -1.5,17.5t-10,12.5q-6.5,5 -15.25,5.5T667,842L480,729.5ZM480,524.5Z"/>
|
||||
</group>
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M639.94,703.24 L132.69,196.96c-5.82,-5.5 -8.73,-11.88 -8.73,-19.16 0,-7.27 2.83,-13.82 8.49,-19.64 5.66,-5.82 12.12,-8.73 19.4,-8.73 7.27,0 13.82,2.75 19.64,8.24l657.97,658.94c5.56,5.56 8.26,12.05 8.09,19.47 -0.16,7.43 -2.83,13.9 -8,19.41 -5.81,5.5 -12.36,8.24 -19.63,8.24 -7.28,0 -13.67,-2.76 -19.19,-8.27L640.02,704.39c0,0 -0.66,-1.96 -0.09,-1.15z" android:strokeWidth="0.969876"/>
|
||||
</vector>
|
||||
@@ -951,7 +951,7 @@
|
||||
<string name="you_will_be_connected_when_your_contacts_device_is_online">Budete připojeni, jakmile bude zařízení vašeho kontaktu online, vyčkejte prosím nebo se podívejte později!</string>
|
||||
<string name="your_chat_profile_will_be_sent_to_your_contact">Váš chat profil bude odeslán
|
||||
\nvašemu kontaktu</string>
|
||||
<string name="your_chats">Vaše konverzace</string>
|
||||
<string name="your_chats">Konverzace</string>
|
||||
<string name="paste_connection_link_below_to_connect">Do níže uvedeného pole vložte odkaz, který jste obdrželi pro spojení s kontaktem.</string>
|
||||
<string name="share_invitation_link">Sdílet jednorázovou pozvánku</string>
|
||||
<string name="status_e2e_encrypted">koncově šifrované</string>
|
||||
|
||||
@@ -170,7 +170,7 @@
|
||||
<string name="personal_welcome">Willkommen <xliff:g>%1$s</xliff:g>!</string>
|
||||
<string name="welcome">Willkommen!</string>
|
||||
<string name="this_text_is_available_in_settings">Dieser Text ist in den Einstellungen verfügbar.</string>
|
||||
<string name="your_chats">Meine Chats</string>
|
||||
<string name="your_chats">Chats</string>
|
||||
<string name="contact_connection_pending">verbinde …</string>
|
||||
<string name="group_preview_you_are_invited">Sie sind zu der Gruppe eingeladen</string>
|
||||
<string name="group_preview_join_as">Beitreten als %s</string>
|
||||
|
||||
@@ -118,7 +118,7 @@
|
||||
<string name="allow_verb">Autoriser</string>
|
||||
<string name="delete_message__question">Supprimer le message \?</string>
|
||||
<string name="for_me_only">Supprimer pour moi</string>
|
||||
<string name="your_chats">Vos chats</string>
|
||||
<string name="your_chats">Chats</string>
|
||||
<string name="notification_preview_mode_message">Texte du message</string>
|
||||
<string name="notification_preview_mode_hidden">Caché</string>
|
||||
<string name="la_notice_to_protect_your_information_turn_on_simplex_lock_you_will_be_prompted_to_complete_authentication_before_this_feature_is_enabled">Pour protéger vos informations, activez la fonction SimpleX Lock.
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
<string name="personal_welcome">Benvenuto/a <xliff:g>%1$s</xliff:g>!</string>
|
||||
<string name="welcome">Benvenuto/a!</string>
|
||||
<string name="this_text_is_available_in_settings">Questo testo è disponibile nelle impostazioni</string>
|
||||
<string name="your_chats">Le tue chat</string>
|
||||
<string name="your_chats">Сhat</string>
|
||||
<string name="group_preview_you_are_invited">sei stato invitato in un gruppo</string>
|
||||
<string name="group_preview_join_as">entra come %s</string>
|
||||
<string name="group_connection_pending">in connessione…</string>
|
||||
|
||||
@@ -1220,7 +1220,7 @@
|
||||
<string name="group_main_profile_sent">פרופיל הצ׳אט שלך יישלח לחברי הקבוצה</string>
|
||||
<string name="database_is_not_encrypted">מסד הנתונים שלך אינו מוצפן – יש להגדיר סיסמה כדי להגן עליו.</string>
|
||||
<string name="v4_3_irreversible_message_deletion_desc">אנשי הקשר שלך יכולים לאפשר מחיקת הודעות מלאה.</string>
|
||||
<string name="your_chats">הצ׳אטים שלך</string>
|
||||
<string name="your_chats">הצ׳אטים</string>
|
||||
<string name="contact_sent_large_file">איש הקשר שלך שלח קובץ גדול יותר מהגודל המרבי הנתמך כעת (<xliff:g id="maxFileSize">%1$s</xliff:g>).</string>
|
||||
<string name="alert_text_connection_pending_they_need_to_be_online_can_delete_and_retry">איש הקשר שלך צריך להיות מקוון כדי שהחיבור יושלם.
|
||||
\nניתן לבטל חיבור זה ולהסיר את איש הקשר (ולנסות מאוחר יותר עם קישור חדש).</string>
|
||||
|
||||
@@ -704,7 +704,7 @@
|
||||
<string name="images_limit_title">画像数の上限を超えてます!</string>
|
||||
<string name="welcome">ようこそ!</string>
|
||||
<string name="group_preview_you_are_invited">グループ招待が届きました</string>
|
||||
<string name="your_chats">あなたのチャット</string>
|
||||
<string name="your_chats">チャット</string>
|
||||
<string name="text_field_set_contact_placeholder">連絡先を設定…</string>
|
||||
<string name="waiting_for_file">ファイル待ち</string>
|
||||
<string name="switch_receiving_address_question">受信アドレスを変えますか?</string>
|
||||
|
||||
@@ -347,7 +347,7 @@
|
||||
<string name="you_will_stop_receiving_messages_from_this_group_chat_history_will_be_preserved">Jūs nustosite gauti žinutes iš šios grupės. Pokalbio istorija bus išsaugota.</string>
|
||||
<string name="group_info_member_you">jūs: <xliff:g id="group_info_you">%1$s</xliff:g></string>
|
||||
<string name="personal_welcome">Sveiki, <xliff:g>%1$s</xliff:g>!</string>
|
||||
<string name="your_chats">Jūsų pokalbiai</string>
|
||||
<string name="your_chats">Pokalbiai</string>
|
||||
<string name="wrong_passphrase">Neteisinga duomenų bazės slaptafrazė</string>
|
||||
<string name="icon_descr_video_snd_complete">Vaizdo įrašas išsiųstas</string>
|
||||
<string name="voice_message">Balso žinutė</string>
|
||||
|
||||
@@ -662,7 +662,7 @@
|
||||
<string name="welcome">Welkom!</string>
|
||||
<string name="group_preview_you_are_invited">je bent uitgenodigd voor de groep</string>
|
||||
<string name="you_have_no_chats">Je hebt geen gesprekken</string>
|
||||
<string name="your_chats">Jouw gesprekken</string>
|
||||
<string name="your_chats">Gesprekken</string>
|
||||
<string name="share_file">Deel bestand…</string>
|
||||
<string name="share_image">Afbeelding delen…</string>
|
||||
<string name="icon_descr_waiting_for_image">Wachten op afbeelding</string>
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
<string name="personal_welcome">Witaj <xliff:g>%1$s</xliff:g>!</string>
|
||||
<string name="group_preview_you_are_invited">jesteś zaproszony do grupy</string>
|
||||
<string name="you_have_no_chats">Nie masz czatów</string>
|
||||
<string name="your_chats">Twoje czaty</string>
|
||||
<string name="your_chats">Czaty</string>
|
||||
<string name="icon_descr_asked_to_receive">Poproszony o odbiór obrazu</string>
|
||||
<string name="icon_descr_video_asked_to_receive">Poproszony o odbiór filmu</string>
|
||||
<string name="image_decoding_exception_title">Błąd dekodowania</string>
|
||||
|
||||
@@ -573,7 +573,7 @@
|
||||
<string name="notifications_mode_off">Executa quando o aplicativo está aberto</string>
|
||||
<string name="icon_descr_sent_msg_status_sent">enviado</string>
|
||||
<string name="icon_descr_sent_msg_status_send_failed">o envio falhou</string>
|
||||
<string name="your_chats">Seus chats</string>
|
||||
<string name="your_chats">Chats</string>
|
||||
<string name="your_profile_will_be_sent">Seu perfil de chat será enviado para seu contato</string>
|
||||
<string name="paste_button">Colar</string>
|
||||
<string name="one_time_link">Link de convite de uso único</string>
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
<string name="personal_welcome">Здравствуйте <xliff:g>%1$s</xliff:g>!</string>
|
||||
<string name="welcome">Здравствуйте!</string>
|
||||
<string name="this_text_is_available_in_settings">Этот текст можно найти в Настройках</string>
|
||||
<string name="your_chats">Ваши чаты</string>
|
||||
<string name="your_chats">Чаты</string>
|
||||
<string name="contact_connection_pending">соединяется…</string>
|
||||
<string name="group_preview_you_are_invited">Вы приглашены в группу</string>
|
||||
<string name="group_preview_join_as">вступить как %s</string>
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
<string name="personal_welcome">ยินดีต้อนรับ <xliff:g>%1$s</xliff:g>!</string>
|
||||
<string name="group_preview_you_are_invited">คุณได้รับเชิญให้เข้าร่วมกลุ่ม</string>
|
||||
<string name="you_have_no_chats">คุณไม่มีการแชท</string>
|
||||
<string name="your_chats">แชทของคุณ</string>
|
||||
<string name="your_chats">แชท</string>
|
||||
<string name="you_are_observer">คุณเป็นผู้สังเกตการณ์</string>
|
||||
<string name="image_decoding_exception_desc">ไม่สามารถถอดรหัสภาพได้ โปรดลองใช้ภาพอื่นหรือติดต่อผู้พัฒนาแอป</string>
|
||||
<string name="observer_cant_send_message_title">คุณไม่สามารถส่งข้อความได้!</string>
|
||||
|
||||
@@ -331,7 +331,7 @@
|
||||
<string name="auth_stop_chat">Зупинити чат</string>
|
||||
<string name="auth_open_chat_console">Відкрийте консоль чату</string>
|
||||
<string name="moderate_message_will_be_marked_warning">Повідомлення буде позначено як модероване для всіх учасників.</string>
|
||||
<string name="your_chats">Ваші чати</string>
|
||||
<string name="your_chats">Чати</string>
|
||||
<string name="contact_connection_pending">підключення…</string>
|
||||
<string name="group_connection_pending">підключення…</string>
|
||||
<string name="tap_to_start_new_chat">Натисніть, щоб почати новий чат</string>
|
||||
|
||||
@@ -495,7 +495,7 @@
|
||||
<string name="notification_preview_mode_contact_desc">只显示联系人</string>
|
||||
<string name="la_notice_to_protect_your_information_turn_on_simplex_lock_you_will_be_prompted_to_complete_authentication_before_this_feature_is_enabled">为保护您的信息,请打开 SimpleX 锁定。
|
||||
\n在启用此功能之前,系统将提示您完成身份验证。</string>
|
||||
<string name="your_chats">您的聊天</string>
|
||||
<string name="your_chats">聊天</string>
|
||||
<string name="share_file">分享文件……</string>
|
||||
<string name="share_image">分享媒体……</string>
|
||||
<string name="share_message">分享消息……</string>
|
||||
|
||||
@@ -253,7 +253,7 @@
|
||||
<string name="personal_welcome">Welcome <xliff:g>%1$s</xliff:g>!</string>
|
||||
<string name="welcome">Welcome!</string>
|
||||
<string name="this_text_is_available_in_settings">This text is available in settings</string>
|
||||
<string name="your_chats">Your chats</string>
|
||||
<string name="your_chats">Chats</string>
|
||||
<string name="contact_connection_pending">connecting…</string>
|
||||
<string name="group_preview_you_are_invited">you are invited to group</string>
|
||||
<string name="group_preview_join_as">join as %s</string>
|
||||
@@ -261,6 +261,7 @@
|
||||
<string name="tap_to_start_new_chat">Tap to start a new chat</string>
|
||||
<string name="chat_with_developers">Chat with the developers</string>
|
||||
<string name="you_have_no_chats">You have no chats</string>
|
||||
<string name="no_filtered_chats">No filtered chats</string>
|
||||
|
||||
<!-- ShareListView.kt -->
|
||||
<string name="share_message">Share message…</string>
|
||||
@@ -281,6 +282,8 @@
|
||||
<string name="you_are_observer">you are observer</string>
|
||||
<string name="observer_cant_send_message_title">You can\'t send messages!</string>
|
||||
<string name="observer_cant_send_message_desc">Please contact group admin.</string>
|
||||
<string name="files_and_media_prohibited">Files and media prohibited!</string>
|
||||
<string name="only_owners_can_enable_files_and_media">Only group owners can enable files and media.</string>
|
||||
|
||||
<!-- Images - chat.simplex.app.views.chat.item.CIImageView.kt -->
|
||||
<string name="image_descr">Image</string>
|
||||
@@ -422,6 +425,9 @@
|
||||
<!-- Actions - ChatListNavLinkView.kt -->
|
||||
<string name="mute_chat">Mute</string>
|
||||
<string name="unmute_chat">Unmute</string>
|
||||
<string name="favorite_chat">Favorite</string>
|
||||
<string name="unfavorite_chat">Unfavorite</string>
|
||||
|
||||
|
||||
<!-- Pending contact connection alert dialogues -->
|
||||
<string name="you_invited_your_contact">You invited your contact</string>
|
||||
@@ -1290,6 +1296,7 @@
|
||||
<string name="full_deletion">Delete for everyone</string>
|
||||
<string name="message_reactions">Message reactions</string>
|
||||
<string name="voice_messages">Voice messages</string>
|
||||
<string name="files_and_media">Files and media</string>
|
||||
<string name="audio_video_calls">Audio/video calls</string>
|
||||
<string name="available_in_v51">\nAvailable in v5.1</string>
|
||||
<string name="feature_enabled">enabled</string>
|
||||
@@ -1344,6 +1351,8 @@
|
||||
<string name="prohibit_sending_voice">Prohibit sending voice messages.</string>
|
||||
<string name="allow_message_reactions">Allow message reactions.</string>
|
||||
<string name="prohibit_message_reactions_group">Prohibit messages reactions.</string>
|
||||
<string name="allow_to_send_files">Allow to send files and media.</string>
|
||||
<string name="prohibit_sending_files">Prohibit sending files and media.</string>
|
||||
<string name="group_members_can_send_disappearing">Group members can send disappearing messages.</string>
|
||||
<string name="disappearing_messages_are_prohibited">Disappearing messages are prohibited in this group.</string>
|
||||
<string name="group_members_can_send_dms">Group members can send direct messages.</string>
|
||||
@@ -1354,6 +1363,8 @@
|
||||
<string name="voice_messages_are_prohibited">Voice messages are prohibited in this group.</string>
|
||||
<string name="group_members_can_add_message_reactions">Group members can add message reactions.</string>
|
||||
<string name="message_reactions_are_prohibited">Message reactions are prohibited in this group.</string>
|
||||
<string name="group_members_can_send_files">Group members can send files and media.</string>
|
||||
<string name="files_are_prohibited_in_group">Files and media are prohibited in this group.</string>
|
||||
<string name="delete_after">Delete after</string>
|
||||
<string name="ttl_sec">%d sec</string>
|
||||
<string name="ttl_s">%ds</string>
|
||||
|
||||
@@ -264,7 +264,7 @@ struct ComposeView: View {
|
||||
default: previewView()
|
||||
}
|
||||
HStack (alignment: .bottom) {
|
||||
Button {
|
||||
let b = Button {
|
||||
showChooseSource = true
|
||||
} label: {
|
||||
Image(systemName: "paperclip")
|
||||
@@ -274,6 +274,17 @@ struct ComposeView: View {
|
||||
.frame(width: 25, height: 25)
|
||||
.padding(.bottom, 12)
|
||||
.padding(.leading, 12)
|
||||
if case let .group(g) = chat.chatInfo,
|
||||
!g.fullGroupPreferences.files.on {
|
||||
b.disabled(true).onTapGesture {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title: "Files and media prohibited!",
|
||||
message: "Only group owners can enable files and media."
|
||||
)
|
||||
}
|
||||
} else {
|
||||
b
|
||||
}
|
||||
ZStack(alignment: .leading) {
|
||||
SendMessageView(
|
||||
composeState: $composeState,
|
||||
|
||||
@@ -27,6 +27,8 @@ struct GroupPreferencesView: View {
|
||||
featureSection(.directMessages, $preferences.directMessages.enable)
|
||||
featureSection(.reactions, $preferences.reactions.enable)
|
||||
featureSection(.voice, $preferences.voice.enable)
|
||||
// TODO uncomment in 5.3
|
||||
// featureSection(.files, $preferences.files.enable)
|
||||
|
||||
if groupInfo.canEdit {
|
||||
Section {
|
||||
|
||||
@@ -117,18 +117,24 @@ struct ChatListView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private var chatList: some View {
|
||||
List {
|
||||
ForEach(filteredChats(), id: \.viewId) { chat in
|
||||
ChatListNavLink(chat: chat)
|
||||
.padding(.trailing, -16)
|
||||
.disabled(chatModel.chatRunning != true)
|
||||
@ViewBuilder private var chatList: some View {
|
||||
let cs = filteredChats()
|
||||
ZStack {
|
||||
List {
|
||||
ForEach(cs, id: \.viewId) { chat in
|
||||
ChatListNavLink(chat: chat)
|
||||
.padding(.trailing, -16)
|
||||
.disabled(chatModel.chatRunning != true)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: chatModel.chatId) { _ in
|
||||
if chatModel.chatId == nil, let chatId = chatModel.chatToTop {
|
||||
chatModel.chatToTop = nil
|
||||
chatModel.popChat(chatId)
|
||||
.onChange(of: chatModel.chatId) { _ in
|
||||
if chatModel.chatId == nil, let chatId = chatModel.chatToTop {
|
||||
chatModel.chatToTop = nil
|
||||
chatModel.popChat(chatId)
|
||||
}
|
||||
}
|
||||
if cs.isEmpty && !chatModel.chats.isEmpty {
|
||||
Text("No filtered chats").foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,18 +201,34 @@ struct ChatListView: View {
|
||||
return s == "" && !showUnreadAndFavorites
|
||||
? chatModel.chats
|
||||
: chatModel.chats.filter { chat in
|
||||
let contains = s == ""
|
||||
? ((chat.chatInfo.chatSettings?.favorite ?? false) || chat.chatStats.unreadCount > 0 || chat.chatStats.unreadChat)
|
||||
: chat.chatInfo.chatViewName.localizedLowercase.contains(s)
|
||||
switch chat.chatInfo {
|
||||
let cInfo = chat.chatInfo
|
||||
switch cInfo {
|
||||
case let .direct(contact):
|
||||
return contains
|
||||
|| contact.profile.displayName.localizedLowercase.contains(s)
|
||||
|| contact.fullName.localizedLowercase.contains(s)
|
||||
case .contactConnection: return false
|
||||
default: return contains
|
||||
return s == ""
|
||||
? filtered(chat)
|
||||
: (viewNameContains(cInfo, s) ||
|
||||
contact.profile.displayName.localizedLowercase.contains(s) ||
|
||||
contact.fullName.localizedLowercase.contains(s))
|
||||
case let .group(gInfo):
|
||||
return s == ""
|
||||
? (filtered(chat) || gInfo.membership.memberStatus == .memInvited)
|
||||
: viewNameContains(cInfo, s)
|
||||
case .contactRequest:
|
||||
return s == "" || viewNameContains(cInfo, s)
|
||||
case let .contactConnection(conn):
|
||||
return s != "" && conn.localAlias.localizedLowercase.contains(s)
|
||||
case .invalidJSON:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func filtered(_ chat: Chat) -> Bool {
|
||||
(chat.chatInfo.chatSettings?.favorite ?? false) || chat.chatStats.unreadCount > 0 || chat.chatStats.unreadChat
|
||||
}
|
||||
|
||||
func viewNameContains(_ cInfo: ChatInfo, _ s: String) -> Bool {
|
||||
cInfo.chatViewName.localizedLowercase.contains(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,11 @@
|
||||
5CC2C0FC2809BF11000C35E3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FA2809BF11000C35E3 /* Localizable.strings */; };
|
||||
5CC2C0FF2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FD2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings */; };
|
||||
5CC868F329EB540C0017BBFD /* CIRcvDecryptionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC868F229EB540C0017BBFD /* CIRcvDecryptionError.swift */; };
|
||||
5CCAA6D52A4B38F700BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCAA6D02A4B38F600BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ.a */; };
|
||||
5CCAA6D62A4B38F700BAF93B /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCAA6D12A4B38F600BAF93B /* libgmp.a */; };
|
||||
5CCAA6D72A4B38F700BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCAA6D22A4B38F600BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ-ghc8.10.7.a */; };
|
||||
5CCAA6D82A4B38F700BAF93B /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCAA6D32A4B38F600BAF93B /* libgmpxx.a */; };
|
||||
5CCAA6D92A4B38F700BAF93B /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCAA6D42A4B38F600BAF93B /* libffi.a */; };
|
||||
5CCB939C297EFCB100399E78 /* NavStackCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCB939B297EFCB100399E78 /* NavStackCompat.swift */; };
|
||||
5CCD403427A5F6DF00368C90 /* AddContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* AddContactView.swift */; };
|
||||
5CCD403727A5F9A200368C90 /* ScanToConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */; };
|
||||
@@ -147,11 +152,6 @@
|
||||
5CFE0922282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; };
|
||||
6407BA83295DA85D0082BA18 /* CIInvalidJSONView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6407BA82295DA85D0082BA18 /* CIInvalidJSONView.swift */; };
|
||||
6432857C2925443C00FBE5C8 /* GroupPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6432857B2925443C00FBE5C8 /* GroupPreferencesView.swift */; };
|
||||
643EE9682A372E8700678085 /* libHSsimplex-chat-5.1.3.0-7OTLHAmjBPvIoz7MIh3bdt.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 643EE9632A372E8700678085 /* libHSsimplex-chat-5.1.3.0-7OTLHAmjBPvIoz7MIh3bdt.a */; };
|
||||
643EE9692A372E8700678085 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 643EE9642A372E8700678085 /* libffi.a */; };
|
||||
643EE96A2A372E8700678085 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 643EE9652A372E8700678085 /* libgmpxx.a */; };
|
||||
643EE96B2A372E8700678085 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 643EE9662A372E8700678085 /* libgmp.a */; };
|
||||
643EE96C2A372E8700678085 /* libHSsimplex-chat-5.1.3.0-7OTLHAmjBPvIoz7MIh3bdt-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 643EE9672A372E8700678085 /* libHSsimplex-chat-5.1.3.0-7OTLHAmjBPvIoz7MIh3bdt-ghc8.10.7.a */; };
|
||||
6440CA00288857A10062C672 /* CIEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6440C9FF288857A10062C672 /* CIEventView.swift */; };
|
||||
6440CA03288AECA70062C672 /* AddGroupMembersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6440CA02288AECA70062C672 /* AddGroupMembersView.swift */; };
|
||||
6442E0BA287F169300CEC0F9 /* AddGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6442E0B9287F169300CEC0F9 /* AddGroupView.swift */; };
|
||||
@@ -392,6 +392,11 @@
|
||||
5CC2C0FB2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
5CC2C0FE2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = "ru.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
5CC868F229EB540C0017BBFD /* CIRcvDecryptionError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIRcvDecryptionError.swift; sourceTree = "<group>"; };
|
||||
5CCAA6D02A4B38F600BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ.a"; sourceTree = "<group>"; };
|
||||
5CCAA6D12A4B38F600BAF93B /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5CCAA6D22A4B38F600BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ-ghc8.10.7.a"; sourceTree = "<group>"; };
|
||||
5CCAA6D32A4B38F600BAF93B /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5CCAA6D42A4B38F600BAF93B /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5CCB939B297EFCB100399E78 /* NavStackCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavStackCompat.swift; sourceTree = "<group>"; };
|
||||
5CCD403327A5F6DF00368C90 /* AddContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactView.swift; sourceTree = "<group>"; };
|
||||
5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanToConnectView.swift; sourceTree = "<group>"; };
|
||||
@@ -422,11 +427,6 @@
|
||||
5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ZoomableScrollView.swift; path = Shared/Views/ZoomableScrollView.swift; sourceTree = SOURCE_ROOT; };
|
||||
6407BA82295DA85D0082BA18 /* CIInvalidJSONView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIInvalidJSONView.swift; sourceTree = "<group>"; };
|
||||
6432857B2925443C00FBE5C8 /* GroupPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupPreferencesView.swift; sourceTree = "<group>"; };
|
||||
643EE9632A372E8700678085 /* libHSsimplex-chat-5.1.3.0-7OTLHAmjBPvIoz7MIh3bdt.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.1.3.0-7OTLHAmjBPvIoz7MIh3bdt.a"; sourceTree = "<group>"; };
|
||||
643EE9642A372E8700678085 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
643EE9652A372E8700678085 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
643EE9662A372E8700678085 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
643EE9672A372E8700678085 /* libHSsimplex-chat-5.1.3.0-7OTLHAmjBPvIoz7MIh3bdt-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.1.3.0-7OTLHAmjBPvIoz7MIh3bdt-ghc8.10.7.a"; sourceTree = "<group>"; };
|
||||
6440C9FF288857A10062C672 /* CIEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIEventView.swift; sourceTree = "<group>"; };
|
||||
6440CA02288AECA70062C672 /* AddGroupMembersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddGroupMembersView.swift; sourceTree = "<group>"; };
|
||||
6442E0B9287F169300CEC0F9 /* AddGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddGroupView.swift; sourceTree = "<group>"; };
|
||||
@@ -497,13 +497,13 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5CCAA6D82A4B38F700BAF93B /* libgmpxx.a in Frameworks */,
|
||||
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
|
||||
643EE96A2A372E8700678085 /* libgmpxx.a in Frameworks */,
|
||||
5CCAA6D72A4B38F700BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ-ghc8.10.7.a in Frameworks */,
|
||||
5CCAA6D52A4B38F700BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ.a in Frameworks */,
|
||||
5CCAA6D92A4B38F700BAF93B /* libffi.a in Frameworks */,
|
||||
5CCAA6D62A4B38F700BAF93B /* libgmp.a in Frameworks */,
|
||||
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
|
||||
643EE9682A372E8700678085 /* libHSsimplex-chat-5.1.3.0-7OTLHAmjBPvIoz7MIh3bdt.a in Frameworks */,
|
||||
643EE96B2A372E8700678085 /* libgmp.a in Frameworks */,
|
||||
643EE9692A372E8700678085 /* libffi.a in Frameworks */,
|
||||
643EE96C2A372E8700678085 /* libHSsimplex-chat-5.1.3.0-7OTLHAmjBPvIoz7MIh3bdt-ghc8.10.7.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -564,11 +564,11 @@
|
||||
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
643EE9642A372E8700678085 /* libffi.a */,
|
||||
643EE9662A372E8700678085 /* libgmp.a */,
|
||||
643EE9652A372E8700678085 /* libgmpxx.a */,
|
||||
643EE9672A372E8700678085 /* libHSsimplex-chat-5.1.3.0-7OTLHAmjBPvIoz7MIh3bdt-ghc8.10.7.a */,
|
||||
643EE9632A372E8700678085 /* libHSsimplex-chat-5.1.3.0-7OTLHAmjBPvIoz7MIh3bdt.a */,
|
||||
5CCAA6D42A4B38F600BAF93B /* libffi.a */,
|
||||
5CCAA6D12A4B38F600BAF93B /* libgmp.a */,
|
||||
5CCAA6D32A4B38F600BAF93B /* libgmpxx.a */,
|
||||
5CCAA6D22A4B38F600BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ-ghc8.10.7.a */,
|
||||
5CCAA6D02A4B38F600BAF93B /* libHSsimplex-chat-5.1.3.0-5OWMsZbPutPDzQF8wFktLQ.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
|
||||
@@ -1090,9 +1090,9 @@ public struct KeepAliveOpts: Codable, Equatable {
|
||||
|
||||
public struct ChatSettings: Codable {
|
||||
public var enableNtfs: Bool
|
||||
public var favorite: Bool? = false
|
||||
public var favorite: Bool
|
||||
|
||||
public init(enableNtfs: Bool, favorite: Bool?) {
|
||||
public init(enableNtfs: Bool, favorite: Bool) {
|
||||
self.enableNtfs = enableNtfs
|
||||
self.favorite = favorite
|
||||
}
|
||||
|
||||
@@ -667,10 +667,11 @@ public enum ChatFeature: String, Decodable, Feature {
|
||||
|
||||
public enum GroupFeature: String, Decodable, Feature {
|
||||
case timedMessages
|
||||
case directMessages
|
||||
case fullDelete
|
||||
case reactions
|
||||
case voice
|
||||
case directMessages
|
||||
case files
|
||||
|
||||
public var id: Self { self }
|
||||
|
||||
@@ -688,6 +689,7 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
case .fullDelete: return NSLocalizedString("Delete for everyone", comment: "chat feature")
|
||||
case .reactions: return NSLocalizedString("Message reactions", comment: "chat feature")
|
||||
case .voice: return NSLocalizedString("Voice messages", comment: "chat feature")
|
||||
case .files: return NSLocalizedString("Files and media", comment: "chat feature")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,6 +700,7 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
case .fullDelete: return "trash.slash"
|
||||
case .reactions: return "face.smiling"
|
||||
case .voice: return "mic"
|
||||
case .files: return "doc"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -708,6 +711,7 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
case .fullDelete: return "trash.slash.fill"
|
||||
case .reactions: return "face.smiling.fill"
|
||||
case .voice: return "mic.fill"
|
||||
case .files: return "doc.fill"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -746,6 +750,11 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
case .on: return "Allow to send voice messages."
|
||||
case .off: return "Prohibit sending voice messages."
|
||||
}
|
||||
case .files:
|
||||
switch enabled {
|
||||
case .on: return "Allow to send files and media."
|
||||
case .off: return "Prohibit sending files and media."
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch self {
|
||||
@@ -774,6 +783,11 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
case .on: return "Group members can send voice messages."
|
||||
case .off: return "Voice messages are prohibited in this group."
|
||||
}
|
||||
case .files:
|
||||
switch enabled {
|
||||
case .on: return "Group members can send files and media."
|
||||
case .off: return "Files and media are prohibited in this group."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -912,19 +926,22 @@ public struct FullGroupPreferences: Decodable, Equatable {
|
||||
public var fullDelete: GroupPreference
|
||||
public var reactions: GroupPreference
|
||||
public var voice: GroupPreference
|
||||
public var files: GroupPreference
|
||||
|
||||
public init(
|
||||
timedMessages: TimedMessagesGroupPreference,
|
||||
directMessages: GroupPreference,
|
||||
fullDelete: GroupPreference,
|
||||
reactions: GroupPreference,
|
||||
voice: GroupPreference
|
||||
voice: GroupPreference,
|
||||
files: GroupPreference
|
||||
) {
|
||||
self.timedMessages = timedMessages
|
||||
self.directMessages = directMessages
|
||||
self.fullDelete = fullDelete
|
||||
self.reactions = reactions
|
||||
self.voice = voice
|
||||
self.files = files
|
||||
}
|
||||
|
||||
public static let sampleData = FullGroupPreferences(
|
||||
@@ -932,7 +949,8 @@ public struct FullGroupPreferences: Decodable, Equatable {
|
||||
directMessages: GroupPreference(enable: .off),
|
||||
fullDelete: GroupPreference(enable: .off),
|
||||
reactions: GroupPreference(enable: .on),
|
||||
voice: GroupPreference(enable: .on)
|
||||
voice: GroupPreference(enable: .on),
|
||||
files: GroupPreference(enable: .on)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -942,19 +960,22 @@ public struct GroupPreferences: Codable {
|
||||
public var fullDelete: GroupPreference?
|
||||
public var reactions: GroupPreference?
|
||||
public var voice: GroupPreference?
|
||||
public var files: GroupPreference?
|
||||
|
||||
public init(
|
||||
timedMessages: TimedMessagesGroupPreference?,
|
||||
directMessages: GroupPreference?,
|
||||
fullDelete: GroupPreference?,
|
||||
reactions: GroupPreference?,
|
||||
voice: GroupPreference?
|
||||
voice: GroupPreference?,
|
||||
files: GroupPreference?
|
||||
) {
|
||||
self.timedMessages = timedMessages
|
||||
self.directMessages = directMessages
|
||||
self.fullDelete = fullDelete
|
||||
self.reactions = reactions
|
||||
self.voice = voice
|
||||
self.files = files
|
||||
}
|
||||
|
||||
public static let sampleData = GroupPreferences(
|
||||
@@ -962,7 +983,8 @@ public struct GroupPreferences: Codable {
|
||||
directMessages: GroupPreference(enable: .off),
|
||||
fullDelete: GroupPreference(enable: .off),
|
||||
reactions: GroupPreference(enable: .on),
|
||||
voice: GroupPreference(enable: .on)
|
||||
voice: GroupPreference(enable: .on),
|
||||
files: GroupPreference(enable: .on)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -972,7 +994,8 @@ public func toGroupPreferences(_ fullPreferences: FullGroupPreferences) -> Group
|
||||
directMessages: fullPreferences.directMessages,
|
||||
fullDelete: fullPreferences.fullDelete,
|
||||
reactions: fullPreferences.reactions,
|
||||
voice: fullPreferences.voice
|
||||
voice: fullPreferences.voice,
|
||||
files: fullPreferences.files
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: simplex-chat
|
||||
version: 5.1.3.0
|
||||
version: 5.2.0.0
|
||||
#synopsis:
|
||||
#description:
|
||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||
|
||||
@@ -5,7 +5,7 @@ cabal-version: 1.12
|
||||
-- see: https://github.com/sol/hpack
|
||||
|
||||
name: simplex-chat
|
||||
version: 5.1.3.0
|
||||
version: 5.2.0.0
|
||||
category: Web, System, Services, Cryptography
|
||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||
author: simplex.chat
|
||||
|
||||
Reference in New Issue
Block a user