Compare commits

..

10 commits

Author SHA1 Message Date
spaced4ndy
3d22b738d8
core: fix change connection user (#5992)
* core: fix change connection user

* plans
2025-06-16 22:38:02 +01:00
Evgeny Poberezkin
c08189108e
6.3.6: ios 282, android 295, desktop 106 2025-06-14 20:12:19 +01:00
Evgeny Poberezkin
442d9afc4b
android: remove Contribute link from Android bundle 2025-06-14 19:26:46 +01:00
Evgeny Poberezkin
a593557c21
core: 6.3.6.0 (simplexmq 6.4.0.3.1) 2025-06-14 14:46:08 +01:00
Evgeny
07abe24e18
core: make decoding for short link data forward compatible (#5989)
* core: make decoding for short link data forward compatible

* simplexmq
2025-06-14 14:17:34 +01:00
Evgeny Poberezkin
5f6595dda9
6.3.5: ios 280, android 292, desktop 104 2025-06-09 09:32:32 +01:00
Evgeny Poberezkin
6fdd50efb9
core: 6.3.5.0 2025-06-08 18:28:26 +01:00
Evgeny
50dfda6c09
core: fix deletion queries for PostgreSQL client (#5969)
* core: fix deletion queries for PostgreSQL client

* disable test in posrgres

* plan
2025-06-08 18:27:42 +01:00
Evgeny Poberezkin
ea1a81fcac
core: 6.3.4.2 (simplexmq 6.4.0.3) 2025-06-06 12:26:46 +01:00
Evgeny
cf0639bf28
website: add Whonix to reviews (#5966) 2025-06-05 21:05:16 +01:00
166 changed files with 3221 additions and 11869 deletions

View file

@ -10,7 +10,7 @@
# SimpleX - the first messaging platform that has no user identifiers of any kind - 100% private by design!
[<img src="./images/trail-of-bits.jpg" height="100">](http://simplex.chat/blog/20221108-simplex-chat-v4.2-security-audit-new-website.html) &nbsp;&nbsp;&nbsp; [<img src="./images/privacy-guides.jpg" height="80">](https://www.privacyguides.org/en/real-time-communication/#simplex-chat) &nbsp;&nbsp;&nbsp; [<img src="./images/kuketz-blog.jpg" height="80">](https://www.kuketz-blog.de/simplex-eindruecke-vom-messenger-ohne-identifier/)
[<img src="./images/trail-of-bits.jpg" height="80">](http://simplex.chat/blog/20221108-simplex-chat-v4.2-security-audit-new-website.html) &nbsp;&nbsp;&nbsp; [<img src="./images/privacy-guides.jpg" height="64">](https://www.privacyguides.org/en/real-time-communication/#simplex-chat) &nbsp;&nbsp;&nbsp; [<img src="./images/whonix-logo.jpg" height="64">](https://www.whonix.org/wiki/Chat#Recommendation) &nbsp;&nbsp;&nbsp; [<img src="./images/kuketz-blog.jpg" height="64">](https://www.kuketz-blog.de/simplex-eindruecke-vom-messenger-ohne-identifier/)
## Welcome to SimpleX Chat!
@ -110,6 +110,15 @@ After you connect, you can [verify connection security code](./blog/20230103-sim
Read about the app features and settings in the new [User guide](./docs/guide/README.md).
## Contribute
We would love to have you join the development! You can help us with:
- [share the color theme](./docs/THEMES.md) you use in Android app!
- writing a tutorial or recipes about hosting servers, chat bot automations, etc.
- contributing to SimpleX Chat knowledge-base.
- developing features - please connect to us via chat so we can help you get started.
## Help translating SimpleX Chat
Thanks to our users and [Weblate](https://hosted.weblate.org/engage/simplex-chat/), SimpleX Chat apps, website and documents are translated to many other languages.
@ -141,15 +150,6 @@ Join our translators to help SimpleX grow!
Languages in progress: Arabic, Japanese, Korean, Portuguese and [others](https://hosted.weblate.org/projects/simplex-chat/#languages). We will be adding more languages as some of the already added are completed please suggest new languages, review the [translation guide](./docs/TRANSLATIONS.md) and get in touch with us!
## Contribute
We would love to have you join the development! You can help us with:
- [share the color theme](./docs/THEMES.md) you use in Android app!
- writing a tutorial or recipes about hosting servers, chat bot automations, etc.
- contributing to SimpleX Chat knowledge-base.
- developing features - please connect to us via chat so we can help you get started.
## Please support us with your donations
Huge thank you to everybody who donated to SimpleX Chat!

View file

@ -39,9 +39,9 @@ enum ChatCommand: ChatCmdProtocol {
case apiGetSettings(settings: AppSettings)
case apiGetChatTags(userId: Int64)
case apiGetChats(userId: Int64)
case apiGetChat(chatId: ChatId, scope: GroupChatScope?, contentTag: MsgContentTag?, pagination: ChatPagination, search: String)
case apiGetChatItemInfo(type: ChatType, id: Int64, scope: GroupChatScope?, itemId: Int64)
case apiSendMessages(type: ChatType, id: Int64, scope: GroupChatScope?, live: Bool, ttl: Int?, composedMessages: [ComposedMessage])
case apiGetChat(chatId: ChatId, pagination: ChatPagination, search: String)
case apiGetChatItemInfo(type: ChatType, id: Int64, itemId: Int64)
case apiSendMessages(type: ChatType, id: Int64, live: Bool, ttl: Int?, composedMessages: [ComposedMessage])
case apiCreateChatTag(tag: ChatTagData)
case apiSetChatTags(type: ChatType, id: Int64, tagIds: [Int64])
case apiDeleteChatTag(tagId: Int64)
@ -49,15 +49,15 @@ enum ChatCommand: ChatCmdProtocol {
case apiReorderChatTags(tagIds: [Int64])
case apiCreateChatItems(noteFolderId: Int64, composedMessages: [ComposedMessage])
case apiReportMessage(groupId: Int64, chatItemId: Int64, reportReason: ReportReason, reportText: String)
case apiUpdateChatItem(type: ChatType, id: Int64, scope: GroupChatScope?, itemId: Int64, updatedMessage: UpdatedMessage, live: Bool)
case apiDeleteChatItem(type: ChatType, id: Int64, scope: GroupChatScope?, itemIds: [Int64], mode: CIDeleteMode)
case apiUpdateChatItem(type: ChatType, id: Int64, itemId: Int64, updatedMessage: UpdatedMessage, live: Bool)
case apiDeleteChatItem(type: ChatType, id: Int64, itemIds: [Int64], mode: CIDeleteMode)
case apiDeleteMemberChatItem(groupId: Int64, itemIds: [Int64])
case apiArchiveReceivedReports(groupId: Int64)
case apiDeleteReceivedReports(groupId: Int64, itemIds: [Int64], mode: CIDeleteMode)
case apiChatItemReaction(type: ChatType, id: Int64, scope: GroupChatScope?, itemId: Int64, add: Bool, reaction: MsgReaction)
case apiChatItemReaction(type: ChatType, id: Int64, itemId: Int64, add: Bool, reaction: MsgReaction)
case apiGetReactionMembers(userId: Int64, groupId: Int64, itemId: Int64, reaction: MsgReaction)
case apiPlanForwardChatItems(fromChatType: ChatType, fromChatId: Int64, fromScope: GroupChatScope?, itemIds: [Int64])
case apiForwardChatItems(toChatType: ChatType, toChatId: Int64, toScope: GroupChatScope?, fromChatType: ChatType, fromChatId: Int64, fromScope: GroupChatScope?, itemIds: [Int64], ttl: Int?)
case apiPlanForwardChatItems(toChatType: ChatType, toChatId: Int64, itemIds: [Int64])
case apiForwardChatItems(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemIds: [Int64], ttl: Int?)
case apiGetNtfToken
case apiRegisterToken(token: DeviceToken, notificationMode: NotificationsMode)
case apiVerifyToken(token: DeviceToken, nonce: String, code: String)
@ -68,8 +68,6 @@ enum ChatCommand: ChatCmdProtocol {
case apiNewGroup(userId: Int64, incognito: Bool, groupProfile: GroupProfile)
case apiAddMember(groupId: Int64, contactId: Int64, memberRole: GroupMemberRole)
case apiJoinGroup(groupId: Int64)
case apiAcceptMember(groupId: Int64, groupMemberId: Int64, memberRole: GroupMemberRole)
case apiDeleteMemberSupportChat(groupId: Int64, groupMemberId: Int64)
case apiMembersRole(groupId: Int64, memberIds: [Int64], memberRole: GroupMemberRole)
case apiBlockMembersForAll(groupId: Int64, memberIds: [Int64], blocked: Bool)
case apiRemoveMembers(groupId: Int64, memberIds: [Int64], withMessages: Bool)
@ -149,8 +147,8 @@ enum ChatCommand: ChatCmdProtocol {
case apiCallStatus(contact: Contact, callStatus: WebRTCCallStatus)
// WebRTC calls /
case apiGetNetworkStatuses
case apiChatRead(type: ChatType, id: Int64, scope: GroupChatScope?)
case apiChatItemsRead(type: ChatType, id: Int64, scope: GroupChatScope?, itemIds: [Int64])
case apiChatRead(type: ChatType, id: Int64)
case apiChatItemsRead(type: ChatType, id: Int64, itemIds: [Int64])
case apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool)
case receiveFile(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool?, inline: Bool?)
case setFileToReceive(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool?)
@ -211,16 +209,15 @@ enum ChatCommand: ChatCmdProtocol {
case let .apiGetSettings(settings): return "/_get app settings \(encodeJSON(settings))"
case let .apiGetChatTags(userId): return "/_get tags \(userId)"
case let .apiGetChats(userId): return "/_get chats \(userId) pcc=on"
case let .apiGetChat(chatId, scope, contentTag, pagination, search):
let tag = contentTag != nil ? " content=\(contentTag!.rawValue)" : ""
return "/_get chat \(chatId)\(scopeRef(scope: scope))\(tag) \(pagination.cmdString)" + (search == "" ? "" : " search=\(search)")
case let .apiGetChatItemInfo(type, id, scope, itemId): return "/_get item info \(ref(type, id, scope: scope)) \(itemId)"
case let .apiSendMessages(type, id, scope, live, ttl, composedMessages):
case let .apiGetChat(chatId, pagination, search): return "/_get chat \(chatId) \(pagination.cmdString)" +
(search == "" ? "" : " search=\(search)")
case let .apiGetChatItemInfo(type, id, itemId): return "/_get item info \(ref(type, id)) \(itemId)"
case let .apiSendMessages(type, id, live, ttl, composedMessages):
let msgs = encodeJSON(composedMessages)
let ttlStr = ttl != nil ? "\(ttl!)" : "default"
return "/_send \(ref(type, id, scope: scope)) live=\(onOff(live)) ttl=\(ttlStr) json \(msgs)"
return "/_send \(ref(type, id)) live=\(onOff(live)) ttl=\(ttlStr) json \(msgs)"
case let .apiCreateChatTag(tag): return "/_create tag \(encodeJSON(tag))"
case let .apiSetChatTags(type, id, tagIds): return "/_tags \(ref(type, id, scope: nil)) \(tagIds.map({ "\($0)" }).joined(separator: ","))"
case let .apiSetChatTags(type, id, tagIds): return "/_tags \(ref(type, id)) \(tagIds.map({ "\($0)" }).joined(separator: ","))"
case let .apiDeleteChatTag(tagId): return "/_delete tag \(tagId)"
case let .apiUpdateChatTag(tagId, tagData): return "/_update tag \(tagId) \(encodeJSON(tagData))"
case let .apiReorderChatTags(tagIds): return "/_reorder tags \(tagIds.map({ "\($0)" }).joined(separator: ","))"
@ -229,17 +226,17 @@ enum ChatCommand: ChatCmdProtocol {
return "/_create *\(noteFolderId) json \(msgs)"
case let .apiReportMessage(groupId, chatItemId, reportReason, reportText):
return "/_report #\(groupId) \(chatItemId) reason=\(reportReason) \(reportText)"
case let .apiUpdateChatItem(type, id, scope, itemId, um, live): return "/_update item \(ref(type, id, scope: scope)) \(itemId) live=\(onOff(live)) \(um.cmdString)"
case let .apiDeleteChatItem(type, id, scope, itemIds, mode): return "/_delete item \(ref(type, id, scope: scope)) \(itemIds.map({ "\($0)" }).joined(separator: ",")) \(mode.rawValue)"
case let .apiUpdateChatItem(type, id, itemId, um, live): return "/_update item \(ref(type, id)) \(itemId) live=\(onOff(live)) \(um.cmdString)"
case let .apiDeleteChatItem(type, id, itemIds, mode): return "/_delete item \(ref(type, id)) \(itemIds.map({ "\($0)" }).joined(separator: ",")) \(mode.rawValue)"
case let .apiDeleteMemberChatItem(groupId, itemIds): return "/_delete member item #\(groupId) \(itemIds.map({ "\($0)" }).joined(separator: ","))"
case let .apiArchiveReceivedReports(groupId): return "/_archive reports #\(groupId)"
case let .apiDeleteReceivedReports(groupId, itemIds, mode): return "/_delete reports #\(groupId) \(itemIds.map({ "\($0)" }).joined(separator: ",")) \(mode.rawValue)"
case let .apiChatItemReaction(type, id, scope, itemId, add, reaction): return "/_reaction \(ref(type, id, scope: scope)) \(itemId) \(onOff(add)) \(encodeJSON(reaction))"
case let .apiChatItemReaction(type, id, itemId, add, reaction): return "/_reaction \(ref(type, id)) \(itemId) \(onOff(add)) \(encodeJSON(reaction))"
case let .apiGetReactionMembers(userId, groupId, itemId, reaction): return "/_reaction members \(userId) #\(groupId) \(itemId) \(encodeJSON(reaction))"
case let .apiPlanForwardChatItems(type, id, scope, itemIds): return "/_forward plan \(ref(type, id, scope: scope)) \(itemIds.map({ "\($0)" }).joined(separator: ","))"
case let .apiForwardChatItems(toChatType, toChatId, toScope, fromChatType, fromChatId, fromScope, itemIds, ttl):
case let .apiPlanForwardChatItems(type, id, itemIds): return "/_forward plan \(ref(type, id)) \(itemIds.map({ "\($0)" }).joined(separator: ","))"
case let .apiForwardChatItems(toChatType, toChatId, fromChatType, fromChatId, itemIds, ttl):
let ttlStr = ttl != nil ? "\(ttl!)" : "default"
return "/_forward \(ref(toChatType, toChatId, scope: toScope)) \(ref(fromChatType, fromChatId, scope: fromScope)) \(itemIds.map({ "\($0)" }).joined(separator: ",")) ttl=\(ttlStr)"
return "/_forward \(ref(toChatType, toChatId)) \(ref(fromChatType, fromChatId)) \(itemIds.map({ "\($0)" }).joined(separator: ",")) ttl=\(ttlStr)"
case .apiGetNtfToken: return "/_ntf get "
case let .apiRegisterToken(token, notificationMode): return "/_ntf register \(token.cmdString) \(notificationMode.rawValue)"
case let .apiVerifyToken(token, nonce, code): return "/_ntf verify \(token.cmdString) \(nonce) \(code)"
@ -250,8 +247,6 @@ enum ChatCommand: ChatCmdProtocol {
case let .apiNewGroup(userId, incognito, groupProfile): return "/_group \(userId) incognito=\(onOff(incognito)) \(encodeJSON(groupProfile))"
case let .apiAddMember(groupId, contactId, memberRole): return "/_add #\(groupId) \(contactId) \(memberRole)"
case let .apiJoinGroup(groupId): return "/_join #\(groupId)"
case let .apiAcceptMember(groupId, groupMemberId, memberRole): return "/_accept member #\(groupId) \(groupMemberId) \(memberRole.rawValue)"
case let .apiDeleteMemberSupportChat(groupId, groupMemberId): return "/_delete member chat #\(groupId) \(groupMemberId)"
case let .apiMembersRole(groupId, memberIds, memberRole): return "/_member role #\(groupId) \(memberIds.map({ "\($0)" }).joined(separator: ",")) \(memberRole.rawValue)"
case let .apiBlockMembersForAll(groupId, memberIds, blocked): return "/_block #\(groupId) \(memberIds.map({ "\($0)" }).joined(separator: ",")) blocked=\(onOff(blocked))"
case let .apiRemoveMembers(groupId, memberIds, withMessages): return "/_remove #\(groupId) \(memberIds.map({ "\($0)" }).joined(separator: ",")) messages=\(onOff(withMessages))"
@ -275,13 +270,13 @@ enum ChatCommand: ChatCmdProtocol {
case let .apiAcceptConditions(conditionsId, operatorIds): return "/_accept_conditions \(conditionsId) \(joinedIds(operatorIds))"
case let .apiSetChatItemTTL(userId, seconds): return "/_ttl \(userId) \(chatItemTTLStr(seconds: seconds))"
case let .apiGetChatItemTTL(userId): return "/_ttl \(userId)"
case let .apiSetChatTTL(userId, type, id, seconds): return "/_ttl \(userId) \(ref(type, id, scope: nil)) \(chatItemTTLStr(seconds: seconds))"
case let .apiSetChatTTL(userId, type, id, seconds): return "/_ttl \(userId) \(ref(type, id)) \(chatItemTTLStr(seconds: seconds))"
case let .apiSetNetworkConfig(networkConfig): return "/_network \(encodeJSON(networkConfig))"
case .apiGetNetworkConfig: return "/network"
case let .apiSetNetworkInfo(networkInfo): return "/_network info \(encodeJSON(networkInfo))"
case .reconnectAllServers: return "/reconnect"
case let .reconnectServer(userId, smpServer): return "/reconnect \(userId) \(smpServer)"
case let .apiSetChatSettings(type, id, chatSettings): return "/_settings \(ref(type, id, scope: nil)) \(encodeJSON(chatSettings))"
case let .apiSetChatSettings(type, id, chatSettings): return "/_settings \(ref(type, id)) \(encodeJSON(chatSettings))"
case let .apiSetMemberSettings(groupId, groupMemberId, memberSettings): return "/_member settings #\(groupId) \(groupMemberId) \(encodeJSON(memberSettings))"
case let .apiContactInfo(contactId): return "/_info @\(contactId)"
case let .apiGroupMemberInfo(groupId, groupMemberId): return "/_info #\(groupId) \(groupMemberId)"
@ -313,8 +308,8 @@ enum ChatCommand: ChatCmdProtocol {
case let .apiConnectPlan(userId, connLink): return "/_connect plan \(userId) \(connLink)"
case let .apiConnect(userId, incognito, connLink): return "/_connect \(userId) incognito=\(onOff(incognito)) \(connLink.connFullLink) \(connLink.connShortLink ?? "")"
case let .apiConnectContactViaAddress(userId, incognito, contactId): return "/_connect contact \(userId) incognito=\(onOff(incognito)) \(contactId)"
case let .apiDeleteChat(type, id, chatDeleteMode): return "/_delete \(ref(type, id, scope: nil)) \(chatDeleteMode.cmdString)"
case let .apiClearChat(type, id): return "/_clear chat \(ref(type, id, scope: nil))"
case let .apiDeleteChat(type, id, chatDeleteMode): return "/_delete \(ref(type, id)) \(chatDeleteMode.cmdString)"
case let .apiClearChat(type, id): return "/_clear chat \(ref(type, id))"
case let .apiListContacts(userId): return "/_contacts \(userId)"
case let .apiUpdateProfile(userId, profile): return "/_profile \(userId) \(encodeJSON(profile))"
case let .apiSetContactPrefs(contactId, preferences): return "/_set prefs @\(contactId) \(encodeJSON(preferences))"
@ -339,9 +334,9 @@ enum ChatCommand: ChatCmdProtocol {
case .apiGetCallInvitations: return "/_call get"
case let .apiCallStatus(contact, callStatus): return "/_call status @\(contact.apiId) \(callStatus.rawValue)"
case .apiGetNetworkStatuses: return "/_network_statuses"
case let .apiChatRead(type, id, scope): return "/_read chat \(ref(type, id, scope: scope))"
case let .apiChatItemsRead(type, id, scope, itemIds): return "/_read chat items \(ref(type, id, scope: scope)) \(joinedIds(itemIds))"
case let .apiChatUnread(type, id, unreadChat): return "/_unread chat \(ref(type, id, scope: nil)) \(onOff(unreadChat))"
case let .apiChatRead(type, id): return "/_read chat \(ref(type, id))"
case let .apiChatItemsRead(type, id, itemIds): return "/_read chat items \(ref(type, id)) \(joinedIds(itemIds))"
case let .apiChatUnread(type, id, unreadChat): return "/_unread chat \(ref(type, id)) \(onOff(unreadChat))"
case let .receiveFile(fileId, userApprovedRelays, encrypt, inline): return "/freceive \(fileId)\(onOffParam("approved_relays", userApprovedRelays))\(onOffParam("encrypt", encrypt))\(onOffParam("inline", inline))"
case let .setFileToReceive(fileId, userApprovedRelays, encrypt): return "/_set_file_to_receive \(fileId)\(onOffParam("approved_relays", userApprovedRelays))\(onOffParam("encrypt", encrypt))"
case let .cancelFile(fileId): return "/fcancel \(fileId)"
@ -426,8 +421,6 @@ enum ChatCommand: ChatCmdProtocol {
case .apiNewGroup: return "apiNewGroup"
case .apiAddMember: return "apiAddMember"
case .apiJoinGroup: return "apiJoinGroup"
case .apiAcceptMember: return "apiAcceptMember"
case .apiDeleteMemberSupportChat: return "apiDeleteMemberSupportChat"
case .apiMembersRole: return "apiMembersRole"
case .apiBlockMembersForAll: return "apiBlockMembersForAll"
case .apiRemoveMembers: return "apiRemoveMembers"
@ -530,20 +523,8 @@ enum ChatCommand: ChatCmdProtocol {
}
}
func ref(_ type: ChatType, _ id: Int64, scope: GroupChatScope?) -> String {
"\(type.rawValue)\(id)\(scopeRef(scope: scope))"
}
func scopeRef(scope: GroupChatScope?) -> String {
switch (scope) {
case .none: ""
case let .memberSupport(groupMemberId_):
if let groupMemberId = groupMemberId_ {
"(_support:\(groupMemberId))"
} else {
"(_support)"
}
}
func ref(_ type: ChatType, _ id: Int64) -> String {
"\(type.rawValue)\(id)"
}
func joinedIds(_ ids: [Int64]) -> String {
@ -730,7 +711,6 @@ enum ChatResponse1: Decodable, ChatAPIResult {
case contactDeleted(user: UserRef, contact: Contact)
case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection)
case groupDeletedUser(user: UserRef, groupInfo: GroupInfo)
case itemsReadForChat(user: UserRef, chatInfo: ChatInfo)
case chatCleared(user: UserRef, chatInfo: ChatInfo)
case userProfileNoChange(user: User)
case userProfileUpdated(user: User, fromProfile: Profile, toProfile: Profile, updateSummary: UserProfileUpdateSummary)
@ -769,7 +749,6 @@ enum ChatResponse1: Decodable, ChatAPIResult {
case .contactDeleted: "contactDeleted"
case .contactConnectionDeleted: "contactConnectionDeleted"
case .groupDeletedUser: "groupDeletedUser"
case .itemsReadForChat: "itemsReadForChat"
case .chatCleared: "chatCleared"
case .userProfileNoChange: "userProfileNoChange"
case .userProfileUpdated: "userProfileUpdated"
@ -802,7 +781,6 @@ enum ChatResponse1: Decodable, ChatAPIResult {
case let .contactDeleted(u, contact): return withUser(u, String(describing: contact))
case let .contactConnectionDeleted(u, connection): return withUser(u, String(describing: connection))
case let .groupDeletedUser(u, groupInfo): return withUser(u, String(describing: groupInfo))
case let .itemsReadForChat(u, chatInfo): return withUser(u, String(describing: chatInfo))
case let .chatCleared(u, chatInfo): return withUser(u, String(describing: chatInfo))
case .userProfileNoChange: return noDetails
case let .userProfileUpdated(u, _, toProfile, _): return withUser(u, String(describing: toProfile))
@ -853,8 +831,6 @@ enum ChatResponse2: Decodable, ChatAPIResult {
case userDeletedMembers(user: UserRef, groupInfo: GroupInfo, members: [GroupMember], withMessages: Bool)
case leftMemberUser(user: UserRef, groupInfo: GroupInfo)
case groupMembers(user: UserRef, group: SimpleXChat.Group)
case memberAccepted(user: UserRef, groupInfo: GroupInfo, member: GroupMember)
case memberSupportChatDeleted(user: UserRef, groupInfo: GroupInfo, member: GroupMember)
case membersRoleUser(user: UserRef, groupInfo: GroupInfo, members: [GroupMember], toRole: GroupMemberRole)
case membersBlockedForAllUser(user: UserRef, groupInfo: GroupInfo, members: [GroupMember], blocked: Bool)
case groupUpdated(user: UserRef, toGroup: GroupInfo)
@ -903,8 +879,6 @@ enum ChatResponse2: Decodable, ChatAPIResult {
case .userDeletedMembers: "userDeletedMembers"
case .leftMemberUser: "leftMemberUser"
case .groupMembers: "groupMembers"
case .memberAccepted: "memberAccepted"
case .memberSupportChatDeleted: "memberSupportChatDeleted"
case .membersRoleUser: "membersRoleUser"
case .membersBlockedForAllUser: "membersBlockedForAllUser"
case .groupUpdated: "groupUpdated"
@ -949,8 +923,6 @@ enum ChatResponse2: Decodable, ChatAPIResult {
case let .userDeletedMembers(u, groupInfo, members, withMessages): return withUser(u, "groupInfo: \(groupInfo)\nmembers: \(members)\nwithMessages: \(withMessages)")
case let .leftMemberUser(u, groupInfo): return withUser(u, String(describing: groupInfo))
case let .groupMembers(u, group): return withUser(u, String(describing: group))
case let .memberAccepted(u, groupInfo, member): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)")
case let .memberSupportChatDeleted(u, groupInfo, member): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)")
case let .membersRoleUser(u, groupInfo, members, toRole): return withUser(u, "groupInfo: \(groupInfo)\nmembers: \(members)\ntoRole: \(toRole)")
case let .membersBlockedForAllUser(u, groupInfo, members, blocked): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(members)\nblocked: \(blocked)")
case let .groupUpdated(u, toGroup): return withUser(u, String(describing: toGroup))
@ -1016,7 +988,6 @@ enum ChatEvent: Decodable, ChatAPIResult {
case groupLinkConnecting(user: UserRef, groupInfo: GroupInfo, hostMember: GroupMember)
case businessLinkConnecting(user: UserRef, groupInfo: GroupInfo, hostMember: GroupMember, fromContact: Contact)
case joinedGroupMemberConnecting(user: UserRef, groupInfo: GroupInfo, hostMember: GroupMember, member: GroupMember)
case memberAcceptedByOther(user: UserRef, groupInfo: GroupInfo, acceptingMember: GroupMember, member: GroupMember)
case memberRole(user: UserRef, groupInfo: GroupInfo, byMember: GroupMember, member: GroupMember, fromRole: GroupMemberRole, toRole: GroupMemberRole)
case memberBlockedForAll(user: UserRef, groupInfo: GroupInfo, byMember: GroupMember, member: GroupMember, blocked: Bool)
case deletedMemberUser(user: UserRef, groupInfo: GroupInfo, member: GroupMember, withMessages: Bool)
@ -1093,7 +1064,6 @@ enum ChatEvent: Decodable, ChatAPIResult {
case .groupLinkConnecting: "groupLinkConnecting"
case .businessLinkConnecting: "businessLinkConnecting"
case .joinedGroupMemberConnecting: "joinedGroupMemberConnecting"
case .memberAcceptedByOther: "memberAcceptedByOther"
case .memberRole: "memberRole"
case .memberBlockedForAll: "memberBlockedForAll"
case .deletedMemberUser: "deletedMemberUser"
@ -1174,7 +1144,6 @@ enum ChatEvent: Decodable, ChatAPIResult {
case let .groupLinkConnecting(u, groupInfo, hostMember): return withUser(u, "groupInfo: \(groupInfo)\nhostMember: \(String(describing: hostMember))")
case let .businessLinkConnecting(u, groupInfo, hostMember, fromContact): return withUser(u, "groupInfo: \(groupInfo)\nhostMember: \(String(describing: hostMember))\nfromContact: \(String(describing: fromContact))")
case let .joinedGroupMemberConnecting(u, groupInfo, hostMember, member): return withUser(u, "groupInfo: \(groupInfo)\nhostMember: \(hostMember)\nmember: \(member)")
case let .memberAcceptedByOther(u, groupInfo, acceptingMember, member): return withUser(u, "groupInfo: \(groupInfo)\nacceptingMember: \(acceptingMember)\nmember: \(member)")
case let .memberRole(u, groupInfo, byMember, member, fromRole, toRole): return withUser(u, "groupInfo: \(groupInfo)\nbyMember: \(byMember)\nmember: \(member)\nfromRole: \(fromRole)\ntoRole: \(toRole)")
case let .memberBlockedForAll(u, groupInfo, byMember, member, blocked): return withUser(u, "groupInfo: \(groupInfo)\nbyMember: \(byMember)\nmember: \(member)\nblocked: \(blocked)")
case let .deletedMemberUser(u, groupInfo, member, withMessages): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)\nwithMessages: \(withMessages)")

View file

@ -52,26 +52,8 @@ private func addTermItem(_ items: inout [TerminalItem], _ item: TerminalItem) {
items.append(item)
}
// analogue for SecondaryContextFilter in Kotlin
enum SecondaryItemsModelFilter {
case groupChatScopeContext(groupScopeInfo: GroupChatScopeInfo)
case msgContentTagContext(contentTag: MsgContentTag)
func descr() -> String {
switch self {
case let .groupChatScopeContext(groupScopeInfo):
return "groupChatScopeContext \(groupScopeInfo.toChatScope())"
case let .msgContentTagContext(contentTag):
return "msgContentTagContext \(contentTag.rawValue)"
}
}
}
// analogue for ChatsContext in Kotlin
class ItemsModel: ObservableObject {
static let shared = ItemsModel(secondaryIMFilter: nil)
public var secondaryIMFilter: SecondaryItemsModelFilter?
public var preloadState = PreloadState()
static let shared = ItemsModel()
private let publisher = ObservableObjectPublisher()
private var bag = Set<AnyCancellable>()
var reversedChatItems: [ChatItem] = [] {
@ -95,20 +77,13 @@ class ItemsModel: ObservableObject {
chatState.splits.isEmpty || chatState.splits.first != reversedChatItems.first?.id
}
init(secondaryIMFilter: SecondaryItemsModelFilter? = nil) {
self.secondaryIMFilter = secondaryIMFilter
init() {
publisher
.throttle(for: 0.2, scheduler: DispatchQueue.main, latest: true)
.sink { self.objectWillChange.send() }
.store(in: &bag)
}
static func loadSecondaryChat(_ chatId: ChatId, chatFilter: SecondaryItemsModelFilter, willNavigate: @escaping () -> Void = {}) {
let im = ItemsModel(secondaryIMFilter: chatFilter)
ChatModel.shared.secondaryIM = im
im.loadOpenChat(chatId, willNavigate: willNavigate)
}
func loadOpenChat(_ chatId: ChatId, willNavigate: @escaping () -> Void = {}) {
navigationTimeoutTask?.cancel()
loadChatTask?.cancel()
@ -124,7 +99,7 @@ class ItemsModel: ObservableObject {
loadChatTask = Task {
await MainActor.run { self.isLoading = true }
// try? await Task.sleep(nanoseconds: 1000_000000)
await loadChat(chatId: chatId, im: self)
await loadChat(chatId: chatId)
if !Task.isCancelled {
await MainActor.run {
self.isLoading = false
@ -139,7 +114,7 @@ class ItemsModel: ObservableObject {
loadChatTask?.cancel()
loadChatTask = Task {
// try? await Task.sleep(nanoseconds: 1000_000000)
await loadChat(chatId: chatId, im: self, openAroundItemId: openAroundItemId, clearItems: openAroundItemId == nil)
await loadChat(chatId: chatId, openAroundItemId: openAroundItemId, clearItems: openAroundItemId == nil)
if !Task.isCancelled {
await MainActor.run {
if openAroundItemId == nil {
@ -149,34 +124,6 @@ class ItemsModel: ObservableObject {
}
}
}
public var contentTag: MsgContentTag? {
switch secondaryIMFilter {
case nil: nil
case .groupChatScopeContext: nil
case let .msgContentTagContext(contentTag): contentTag
}
}
public var groupScopeInfo: GroupChatScopeInfo? {
switch secondaryIMFilter {
case nil: nil
case let .groupChatScopeContext(scopeInfo): scopeInfo
case .msgContentTagContext: nil
}
}
}
class PreloadState {
var prevFirstVisible: Int64 = Int64.min
var prevItemsCount: Int = 0
var preloading: Bool = false
func clear() {
prevFirstVisible = Int64.min
prevItemsCount = 0
preloading = false
}
}
class ChatTagsModel: ObservableObject {
@ -340,6 +287,7 @@ final class ChatModel: ObservableObject {
// current chat
@Published var chatId: String?
@Published var openAroundItemId: ChatItem.ID? = nil
var chatItemStatuses: Dictionary<Int64, CIStatus> = [:]
@Published var chatToTop: String?
@Published var groupMembers: [GMember] = []
@Published var groupMembersIndexes: Dictionary<Int64, Int> = [:] // groupMemberId to index in groupMembers list
@ -388,10 +336,6 @@ final class ChatModel: ObservableObject {
let im = ItemsModel.shared
// ItemsModel for secondary chat view (such as support scope chat), as opposed to ItemsModel.shared used for primary chat
@Published var secondaryIM: ItemsModel? = nil
@Published var secondaryPendingInviteeChatOpened = false
static var ok: Bool { ChatModel.shared.chatDbStatus == .ok }
let ntfEnableLocal = true
@ -449,7 +393,7 @@ final class ChatModel: ObservableObject {
func getGroupChat(_ groupId: Int64) -> Chat? {
chats.first { chat in
if case let .group(groupInfo, _) = chat.chatInfo {
if case let .group(groupInfo) = chat.chatInfo {
return groupInfo.groupId == groupId
} else {
return false
@ -502,11 +446,7 @@ final class ChatModel: ObservableObject {
func updateChatInfo(_ cInfo: ChatInfo) {
if let i = getChatIndex(cInfo.id) {
if case let .group(groupInfo, groupChatScope) = cInfo, groupChatScope != nil {
chats[i].chatInfo = .group(groupInfo: groupInfo, groupChatScope: nil)
} else {
chats[i].chatInfo = cInfo
}
chats[i].created = Date.now
}
}
@ -528,7 +468,7 @@ final class ChatModel: ObservableObject {
}
func updateGroup(_ groupInfo: GroupInfo) {
updateChat(.group(groupInfo: groupInfo, groupChatScope: nil))
updateChat(.group(groupInfo: groupInfo))
}
private func updateChat(_ cInfo: ChatInfo, addMissing: Bool = true) {
@ -571,18 +511,14 @@ final class ChatModel: ObservableObject {
// }
func addChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) {
// updates membersRequireAttention
updateChatInfo(cInfo)
// mark chat non deleted
if case let .direct(contact) = cInfo, contact.chatDeleted {
var updatedContact = contact
updatedContact.chatDeleted = false
updateContact(updatedContact)
}
// update chat list
// update previews
if let i = getChatIndex(cInfo.id) {
// update preview
if cInfo.groupChatScope() == nil || cInfo.groupInfo?.membership.memberPending ?? false {
chats[i].chatItems = switch cInfo {
case .group:
if let currentPreviewItem = chats[i].chatItems.first {
@ -600,45 +536,19 @@ final class ChatModel: ObservableObject {
if case .rcvNew = cItem.meta.itemStatus {
unreadCollector.changeUnreadCounter(cInfo.id, by: 1, unreadMentions: cItem.meta.userMention ? 1 : 0)
}
}
// pop chat
popChatCollector.throttlePopChat(cInfo.id, currentPosition: i)
} else {
if cInfo.groupChatScope() == nil {
addChat(Chat(chatInfo: cInfo, chatItems: [cItem]))
} else {
addChat(Chat(chatInfo: cInfo, chatItems: []))
}
}
// add to current scope
if let ciIM = getCIItemsModel(cInfo, cItem) {
_ = _upsertChatItem(ciIM, cInfo, cItem)
}
}
func getCIItemsModel(_ cInfo: ChatInfo, _ ci: ChatItem) -> ItemsModel? {
let cInfoScope = cInfo.groupChatScope()
if let cInfoScope = cInfoScope {
switch cInfoScope {
case .memberSupport:
switch secondaryIM?.secondaryIMFilter {
case .none:
return nil
case let .groupChatScopeContext(groupScopeInfo):
return (cInfo.id == chatId && sameChatScope(cInfoScope, groupScopeInfo.toChatScope())) ? secondaryIM : nil
case let .msgContentTagContext(contentTag):
return (cInfo.id == chatId && ci.isReport && contentTag == .report) ? secondaryIM : nil
}
}
} else {
return cInfo.id == chatId ? im : nil
// add to current chat
if chatId == cInfo.id {
_ = _upsertChatItem(cInfo, cItem)
}
}
func upsertChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) -> Bool {
// update chat list
var itemAdded: Bool = false
if cInfo.groupChatScope() == nil {
// update previews
var res: Bool
if let chat = getChat(cInfo.id) {
if let pItem = chat.chatItems.last {
if pItem.id == cItem.id || (chatId == cInfo.id && im.reversedChatItems.first(where: { $0.id == cItem.id }) == nil) {
@ -647,36 +557,31 @@ final class ChatModel: ObservableObject {
} else {
chat.chatItems = [cItem]
}
res = false
} else {
addChat(Chat(chatInfo: cInfo, chatItems: [cItem]))
itemAdded = true
res = true
}
if cItem.isDeletedContent || cItem.meta.itemDeleted != nil {
VoiceItemState.stopVoiceInChatView(cInfo, cItem)
}
}
// update current scope
if let ciIM = getCIItemsModel(cInfo, cItem) {
itemAdded = _upsertChatItem(ciIM, cInfo, cItem)
}
return itemAdded
// update current chat
return chatId == cInfo.id ? _upsertChatItem(cInfo, cItem) : res
}
private func _upsertChatItem(_ ciIM: ItemsModel, _ cInfo: ChatInfo, _ cItem: ChatItem) -> Bool {
if let i = getChatItemIndex(ciIM, cItem) {
let oldStatus = ciIM.reversedChatItems[i].meta.itemStatus
let newStatus = cItem.meta.itemStatus
var ci = cItem
if shouldKeepOldSndCIStatus(oldStatus: oldStatus, newStatus: newStatus) {
ci.meta.itemStatus = oldStatus
}
_updateChatItem(ciIM: ciIM, at: i, with: ci)
ChatItemDummyModel.shared.sendUpdate() // TODO [knocking] review what's this
private func _upsertChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) -> Bool {
if let i = getChatItemIndex(cItem) {
_updateChatItem(at: i, with: cItem)
ChatItemDummyModel.shared.sendUpdate()
return false
} else {
ciIM.reversedChatItems.insert(cItem, at: hasLiveDummy ? 1 : 0)
ciIM.chatState.itemAdded((cItem.id, cItem.isRcvNew), hasLiveDummy ? 1 : 0)
ciIM.itemAdded = true
var ci = cItem
if let status = chatItemStatuses.removeValue(forKey: ci.id), case .sndNew = ci.meta.itemStatus {
ci.meta.itemStatus = status
}
im.reversedChatItems.insert(ci, at: hasLiveDummy ? 1 : 0)
im.chatState.itemAdded((ci.id, ci.isRcvNew), hasLiveDummy ? 1 : 0)
im.itemAdded = true
ChatItemDummyModel.shared.sendUpdate()
return true
}
@ -690,26 +595,25 @@ final class ChatModel: ObservableObject {
}
func updateChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem, status: CIStatus? = nil) {
if let ciIM = getCIItemsModel(cInfo, cItem),
let i = getChatItemIndex(ciIM, cItem) {
if chatId == cInfo.id, let i = getChatItemIndex(cItem) {
withConditionalAnimation {
_updateChatItem(ciIM: ciIM, at: i, with: cItem)
_updateChatItem(at: i, with: cItem)
}
} else if let status = status {
chatItemStatuses.updateValue(status, forKey: cItem.id)
}
}
private func _updateChatItem(ciIM: ItemsModel, at i: Int, with cItem: ChatItem) {
ciIM.reversedChatItems[i] = cItem
ciIM.reversedChatItems[i].viewTimestamp = .now
private func _updateChatItem(at i: Int, with cItem: ChatItem) {
im.reversedChatItems[i] = cItem
im.reversedChatItems[i].viewTimestamp = .now
}
func getChatItemIndex(_ ciIM: ItemsModel, _ cItem: ChatItem) -> Int? {
ciIM.reversedChatItems.firstIndex(where: { $0.id == cItem.id })
func getChatItemIndex(_ cItem: ChatItem) -> Int? {
im.reversedChatItems.firstIndex(where: { $0.id == cItem.id })
}
func removeChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) {
// update chat list
if cInfo.groupChatScope() == nil {
if cItem.isRcvNew {
unreadCollector.changeUnreadCounter(cInfo.id, by: -1, unreadMentions: cItem.meta.userMention ? -1 : 0)
}
@ -719,13 +623,12 @@ final class ChatModel: ObservableObject {
chat.chatItems = [ChatItem.deletedItemDummy()]
}
}
}
// remove from current scope
if let ciIM = getCIItemsModel(cInfo, cItem) {
if let i = getChatItemIndex(ciIM, cItem) {
// remove from current chat
if chatId == cInfo.id {
if let i = getChatItemIndex(cItem) {
withAnimation {
let item = ciIM.reversedChatItems.remove(at: i)
ciIM.chatState.itemsRemoved([(item.id, i, item.isRcvNew)], im.reversedChatItems.reversed())
let item = im.reversedChatItems.remove(at: i)
im.chatState.itemsRemoved([(item.id, i, item.isRcvNew)], im.reversedChatItems.reversed())
}
}
}
@ -741,7 +644,7 @@ final class ChatModel: ObservableObject {
if chatId == groupInfo.id {
for i in 0..<im.reversedChatItems.count {
if let updatedItem = removedUpdatedItem(im.reversedChatItems[i]) {
_updateChatItem(ciIM: im, at: i, with: updatedItem) // TODO [knocking] review: use getCIItemsModel?
_updateChatItem(at: i, with: updatedItem)
}
}
} else if let chat = getChat(groupInfo.id),
@ -833,7 +736,7 @@ final class ChatModel: ObservableObject {
im.reversedChatItems.first?.isLiveDummy == true
}
func markAllChatItemsRead(_ chatIM: ItemsModel, _ cInfo: ChatInfo) {
func markAllChatItemsRead(_ cInfo: ChatInfo) {
// update preview
_updateChat(cInfo.id) { chat in
self.decreaseUnreadCounter(user: self.currentUser!, chat: chat)
@ -844,7 +747,7 @@ final class ChatModel: ObservableObject {
if chatId == cInfo.id {
var i = 0
while i < im.reversedChatItems.count {
markChatItemRead_(chatIM, i)
markChatItemRead_(i)
i += 1
}
im.chatState.itemsRead(nil, im.reversedChatItems.reversed())
@ -869,26 +772,27 @@ final class ChatModel: ObservableObject {
}
// clear current chat
if chatId == cInfo.id {
chatItemStatuses = [:]
im.reversedChatItems = []
im.chatState.clear()
}
}
func markChatItemsRead(_ chatIM: ItemsModel, _ cInfo: ChatInfo, _ itemIds: [ChatItem.ID], _ mentionsRead: Int) {
func markChatItemsRead(_ cInfo: ChatInfo, _ itemIds: [ChatItem.ID], _ mentionsRead: Int) {
if self.chatId == cInfo.id {
var unreadItemIds: Set<ChatItem.ID> = []
var i = 0
var ids = Set(itemIds)
while i < chatIM.reversedChatItems.count && !ids.isEmpty {
let item = chatIM.reversedChatItems[i]
while i < im.reversedChatItems.count && !ids.isEmpty {
let item = im.reversedChatItems[i]
if ids.contains(item.id) && item.isRcvNew {
markChatItemRead_(chatIM, i)
markChatItemRead_(i)
unreadItemIds.insert(item.id)
ids.remove(item.id)
}
i += 1
}
chatIM.chatState.itemsRead(unreadItemIds, chatIM.reversedChatItems.reversed())
im.chatState.itemsRead(unreadItemIds, im.reversedChatItems.reversed())
}
self.unreadCollector.changeUnreadCounter(cInfo.id, by: -itemIds.count, unreadMentions: -mentionsRead)
}
@ -984,13 +888,13 @@ final class ChatModel: ObservableObject {
}
}
private func markChatItemRead_(_ chatIM: ItemsModel, _ i: Int) {
let meta = chatIM.reversedChatItems[i].meta
private func markChatItemRead_(_ i: Int) {
let meta = im.reversedChatItems[i].meta
if case .rcvNew = meta.itemStatus {
chatIM.reversedChatItems[i].meta.itemStatus = .rcvRead
chatIM.reversedChatItems[i].viewTimestamp = .now
im.reversedChatItems[i].meta.itemStatus = .rcvRead
im.reversedChatItems[i].viewTimestamp = .now
if meta.itemLive != true, let ttl = meta.itemTimed?.ttl {
chatIM.reversedChatItems[i].meta.itemTimed?.deleteAt = .now + TimeInterval(ttl)
im.reversedChatItems[i].meta.itemTimed?.deleteAt = .now + TimeInterval(ttl)
}
}
}
@ -1069,7 +973,7 @@ final class ChatModel: ObservableObject {
var count = 0
var ns: [String] = []
if let ciCategory = chatItem.mergeCategory,
var i = getChatItemIndex(im, chatItem) { // TODO [knocking] review: use getCIItemsModel?
var i = getChatItemIndex(chatItem) {
while i < im.reversedChatItems.count {
let ci = im.reversedChatItems[i]
if ci.mergeCategory != ciCategory { break }
@ -1085,7 +989,7 @@ final class ChatModel: ObservableObject {
// returns the index of the passed item and the next item (it has smaller index)
func getNextChatItem(_ ci: ChatItem) -> (Int?, ChatItem?) {
if let i = getChatItemIndex(im, ci) { // TODO [knocking] review: use getCIItemsModel?
if let i = getChatItemIndex(ci) {
(i, i > 0 ? im.reversedChatItems[i - 1] : nil)
} else {
(nil, nil)
@ -1196,7 +1100,7 @@ final class ChatModel: ObservableObject {
func removeWallpaperFilesFromChat(_ chat: Chat) {
if case let .direct(contact) = chat.chatInfo {
removeWallpaperFilesFromTheme(contact.uiThemes)
} else if case let .group(groupInfo, _) = chat.chatInfo {
} else if case let .group(groupInfo) = chat.chatInfo {
removeWallpaperFilesFromTheme(groupInfo.uiThemes)
}
}
@ -1260,18 +1164,6 @@ final class Chat: ObservableObject, Identifiable, ChatLike {
var viewId: String { get { "\(chatInfo.id) \(created.timeIntervalSince1970)" } }
var supportUnreadCount: Int {
switch chatInfo {
case let .group(groupInfo, _):
if groupInfo.canModerate {
return groupInfo.membersRequireAttention
} else {
return groupInfo.membership.supportChat?.unread ?? 0
}
default: return 0
}
}
public static var sampleData: Chat = Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: [])
}

View file

@ -344,54 +344,43 @@ func apiGetChatTagsAsync() async throws -> [ChatTag] {
let loadItemsPerPage = 50
func apiGetChat(chatId: ChatId, scope: GroupChatScope?, contentTag: MsgContentTag? = nil, pagination: ChatPagination, search: String = "") async throws -> (Chat, NavigationInfo) {
let r: ChatResponse0 = try await chatSendCmd(.apiGetChat(chatId: chatId, scope: scope, contentTag: contentTag, pagination: pagination, search: search))
func apiGetChat(chatId: ChatId, pagination: ChatPagination, search: String = "") async throws -> (Chat, NavigationInfo) {
let r: ChatResponse0 = try await chatSendCmd(.apiGetChat(chatId: chatId, pagination: pagination, search: search))
if case let .apiChat(_, chat, navInfo) = r { return (Chat.init(chat), navInfo ?? NavigationInfo()) }
throw r.unexpected
}
func loadChat(chat: Chat, im: ItemsModel, search: String = "", clearItems: Bool = true) async {
await loadChat(chatId: chat.chatInfo.id, im: im, search: search, clearItems: clearItems)
func loadChat(chat: Chat, search: String = "", clearItems: Bool = true) async {
await loadChat(chatId: chat.chatInfo.id, search: search, clearItems: clearItems)
}
func loadChat(chatId: ChatId, im: ItemsModel, search: String = "", openAroundItemId: ChatItem.ID? = nil, clearItems: Bool = true) async {
func loadChat(chatId: ChatId, search: String = "", openAroundItemId: ChatItem.ID? = nil, clearItems: Bool = true) async {
let m = ChatModel.shared
let im = ItemsModel.shared
await MainActor.run {
m.chatItemStatuses = [:]
if clearItems {
im.reversedChatItems = []
im.chatState.clear()
ItemsModel.shared.chatState.clear()
}
}
await apiLoadMessages(
chatId,
im,
( // pagination
openAroundItemId != nil
? .around(chatItemId: openAroundItemId!, count: loadItemsPerPage)
: (
search == ""
? .initial(count: loadItemsPerPage) : .last(count: loadItemsPerPage)
)
),
search,
openAroundItemId,
{ 0...0 }
)
await apiLoadMessages(chatId, openAroundItemId != nil ? .around(chatItemId: openAroundItemId!, count: loadItemsPerPage) : (search == "" ? .initial(count: loadItemsPerPage) : .last(count: loadItemsPerPage)), im.chatState, search, openAroundItemId, { 0...0 })
}
func apiGetChatItemInfo(type: ChatType, id: Int64, scope: GroupChatScope?, itemId: Int64) async throws -> ChatItemInfo {
let r: ChatResponse0 = try await chatSendCmd(.apiGetChatItemInfo(type: type, id: id, scope: scope, itemId: itemId))
func apiGetChatItemInfo(type: ChatType, id: Int64, itemId: Int64) async throws -> ChatItemInfo {
let r: ChatResponse0 = try await chatSendCmd(.apiGetChatItemInfo(type: type, id: id, itemId: itemId))
if case let .chatItemInfo(_, _, chatItemInfo) = r { return chatItemInfo }
throw r.unexpected
}
func apiPlanForwardChatItems(type: ChatType, id: Int64, scope: GroupChatScope?, itemIds: [Int64]) async throws -> ([Int64], ForwardConfirmation?) {
let r: ChatResponse1 = try await chatSendCmd(.apiPlanForwardChatItems(fromChatType: type, fromChatId: id, fromScope: scope, itemIds: itemIds))
func apiPlanForwardChatItems(type: ChatType, id: Int64, itemIds: [Int64]) async throws -> ([Int64], ForwardConfirmation?) {
let r: ChatResponse1 = try await chatSendCmd(.apiPlanForwardChatItems(toChatType: type, toChatId: id, itemIds: itemIds))
if case let .forwardPlan(_, chatItemIds, forwardConfimation) = r { return (chatItemIds, forwardConfimation) }
throw r.unexpected
}
func apiForwardChatItems(toChatType: ChatType, toChatId: Int64, toScope: GroupChatScope?, fromChatType: ChatType, fromChatId: Int64, fromScope: GroupChatScope?, itemIds: [Int64], ttl: Int?) async -> [ChatItem]? {
let cmd: ChatCommand = .apiForwardChatItems(toChatType: toChatType, toChatId: toChatId, toScope: toScope, fromChatType: fromChatType, fromChatId: fromChatId, fromScope: fromScope, itemIds: itemIds, ttl: ttl)
func apiForwardChatItems(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemIds: [Int64], ttl: Int?) async -> [ChatItem]? {
let cmd: ChatCommand = .apiForwardChatItems(toChatType: toChatType, toChatId: toChatId, fromChatType: fromChatType, fromChatId: fromChatId, itemIds: itemIds, ttl: ttl)
return await processSendMessageCmd(toChatType: toChatType, cmd: cmd)
}
@ -423,8 +412,8 @@ func apiReorderChatTags(tagIds: [Int64]) async throws {
try await sendCommandOkResp(.apiReorderChatTags(tagIds: tagIds))
}
func apiSendMessages(type: ChatType, id: Int64, scope: GroupChatScope?, live: Bool = false, ttl: Int? = nil, composedMessages: [ComposedMessage]) async -> [ChatItem]? {
let cmd: ChatCommand = .apiSendMessages(type: type, id: id, scope: scope, live: live, ttl: ttl, composedMessages: composedMessages)
func apiSendMessages(type: ChatType, id: Int64, live: Bool = false, ttl: Int? = nil, composedMessages: [ComposedMessage]) async -> [ChatItem]? {
let cmd: ChatCommand = .apiSendMessages(type: type, id: id, live: live, ttl: ttl, composedMessages: composedMessages)
return await processSendMessageCmd(toChatType: type, cmd: cmd)
}
@ -501,8 +490,8 @@ private func createChatItemsErrorAlert(_ r: ChatError) {
)
}
func apiUpdateChatItem(type: ChatType, id: Int64, scope: GroupChatScope?, itemId: Int64, updatedMessage: UpdatedMessage, live: Bool = false) async throws -> ChatItem {
let r: ChatResponse1 = try await chatSendCmd(.apiUpdateChatItem(type: type, id: id, scope: scope, itemId: itemId, updatedMessage: updatedMessage, live: live), bgDelay: msgDelay)
func apiUpdateChatItem(type: ChatType, id: Int64, itemId: Int64, updatedMessage: UpdatedMessage, live: Bool = false) async throws -> ChatItem {
let r: ChatResponse1 = try await chatSendCmd(.apiUpdateChatItem(type: type, id: id, itemId: itemId, updatedMessage: updatedMessage, live: live), bgDelay: msgDelay)
switch r {
case let .chatItemUpdated(_, aChatItem): return aChatItem.chatItem
case let .chatItemNotChanged(_, aChatItem): return aChatItem.chatItem
@ -510,8 +499,8 @@ func apiUpdateChatItem(type: ChatType, id: Int64, scope: GroupChatScope?, itemId
}
}
func apiChatItemReaction(type: ChatType, id: Int64, scope: GroupChatScope?, itemId: Int64, add: Bool, reaction: MsgReaction) async throws -> ChatItem {
let r: ChatResponse1 = try await chatSendCmd(.apiChatItemReaction(type: type, id: id, scope: scope, itemId: itemId, add: add, reaction: reaction), bgDelay: msgDelay)
func apiChatItemReaction(type: ChatType, id: Int64, itemId: Int64, add: Bool, reaction: MsgReaction) async throws -> ChatItem {
let r: ChatResponse1 = try await chatSendCmd(.apiChatItemReaction(type: type, id: id, itemId: itemId, add: add, reaction: reaction), bgDelay: msgDelay)
if case let .chatItemReaction(_, _, reaction) = r { return reaction.chatReaction.chatItem }
throw r.unexpected
}
@ -523,8 +512,8 @@ func apiGetReactionMembers(groupId: Int64, itemId: Int64, reaction: MsgReaction)
throw r.unexpected
}
func apiDeleteChatItems(type: ChatType, id: Int64, scope: GroupChatScope?, itemIds: [Int64], mode: CIDeleteMode) async throws -> [ChatItemDeletion] {
let r: ChatResponse1 = try await chatSendCmd(.apiDeleteChatItem(type: type, id: id, scope: scope, itemIds: itemIds, mode: mode), bgDelay: msgDelay)
func apiDeleteChatItems(type: ChatType, id: Int64, itemIds: [Int64], mode: CIDeleteMode) async throws -> [ChatItemDeletion] {
let r: ChatResponse1 = try await chatSendCmd(.apiDeleteChatItem(type: type, id: id, itemIds: itemIds, mode: mode), bgDelay: msgDelay)
if case let .chatItemsDeleted(_, items, _) = r { return items }
throw r.unexpected
}
@ -1248,14 +1237,12 @@ func apiRejectContactRequest(contactReqId: Int64) async throws {
throw r.unexpected
}
func apiChatRead(type: ChatType, id: Int64, scope: GroupChatScope?) async throws {
try await sendCommandOkResp(.apiChatRead(type: type, id: id, scope: scope))
func apiChatRead(type: ChatType, id: Int64) async throws {
try await sendCommandOkResp(.apiChatRead(type: type, id: id))
}
func apiChatItemsRead(type: ChatType, id: Int64, scope: GroupChatScope?, itemIds: [Int64]) async throws -> ChatInfo {
let r: ChatResponse1 = try await chatSendCmd(.apiChatItemsRead(type: type, id: id, scope: scope, itemIds: itemIds))
if case let .itemsReadForChat(_, updatedChatInfo) = r { return updatedChatInfo }
throw r.unexpected
func apiChatItemsRead(type: ChatType, id: Int64, itemIds: [Int64]) async throws {
try await sendCommandOkResp(.apiChatItemsRead(type: type, id: id, itemIds: itemIds))
}
func apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool) async throws {
@ -1558,13 +1545,13 @@ func apiGetNetworkStatuses() throws -> [ConnNetworkStatus] {
throw r.unexpected
}
func markChatRead(_ im: ItemsModel, _ chat: Chat) async {
func markChatRead(_ chat: Chat) async {
do {
if chat.chatStats.unreadCount > 0 {
let cInfo = chat.chatInfo
try await apiChatRead(type: cInfo.chatType, id: cInfo.apiId, scope: cInfo.groupChatScope())
try await apiChatRead(type: cInfo.chatType, id: cInfo.apiId)
await MainActor.run {
withAnimation { ChatModel.shared.markAllChatItemsRead(im, cInfo) }
withAnimation { ChatModel.shared.markAllChatItemsRead(cInfo) }
}
}
if chat.chatStats.unreadChat {
@ -1587,12 +1574,11 @@ func markChatUnread(_ chat: Chat, unreadChat: Bool = true) async {
}
}
func apiMarkChatItemsRead(_ im: ItemsModel, _ cInfo: ChatInfo, _ itemIds: [ChatItem.ID], mentionsRead: Int) async {
func apiMarkChatItemsRead(_ cInfo: ChatInfo, _ itemIds: [ChatItem.ID], mentionsRead: Int) async {
do {
let updatedChatInfo = try await apiChatItemsRead(type: cInfo.chatType, id: cInfo.apiId, scope: cInfo.groupChatScope(), itemIds: itemIds)
await MainActor.run {
ChatModel.shared.updateChatInfo(updatedChatInfo)
ChatModel.shared.markChatItemsRead(im, cInfo, itemIds, mentionsRead)
try await apiChatItemsRead(type: cInfo.chatType, id: cInfo.apiId, itemIds: itemIds)
DispatchQueue.main.async {
ChatModel.shared.markChatItemsRead(cInfo, itemIds, mentionsRead)
}
} catch {
logger.error("apiChatItemsRead error: \(responseError(error))")
@ -1640,21 +1626,9 @@ func apiJoinGroup(_ groupId: Int64) async throws -> JoinGroupResult {
}
}
func apiAcceptMember(_ groupId: Int64, _ groupMemberId: Int64, _ memberRole: GroupMemberRole) async throws -> (GroupInfo, GroupMember) {
let r: ChatResponse2 = try await chatSendCmd(.apiAcceptMember(groupId: groupId, groupMemberId: groupMemberId, memberRole: memberRole))
if case let .memberAccepted(_, groupInfo, member) = r { return (groupInfo, member) }
throw r.unexpected
}
func apiDeleteMemberSupportChat(_ groupId: Int64, _ groupMemberId: Int64) async throws -> (GroupInfo, GroupMember) {
let r: ChatResponse2 = try await chatSendCmd(.apiDeleteMemberSupportChat(groupId: groupId, groupMemberId: groupMemberId))
if case let .memberSupportChatDeleted(_, groupInfo, member) = r { return (groupInfo, member) }
throw r.unexpected
}
func apiRemoveMembers(_ groupId: Int64, _ memberIds: [Int64], _ withMessages: Bool = false) async throws -> (GroupInfo, [GroupMember]) {
func apiRemoveMembers(_ groupId: Int64, _ memberIds: [Int64], _ withMessages: Bool = false) async throws -> [GroupMember] {
let r: ChatResponse2 = try await chatSendCmd(.apiRemoveMembers(groupId: groupId, memberIds: memberIds, withMessages: withMessages), bgTask: false)
if case let .userDeletedMembers(_, updatedGroupInfo, members, _withMessages) = r { return (updatedGroupInfo, members) }
if case let .userDeletedMembers(_, _, members, withMessages) = r { return members }
throw r.unexpected
}
@ -1695,8 +1669,8 @@ func apiListMembers(_ groupId: Int64) async -> [GroupMember] {
func filterMembersToAdd(_ ms: [GMember]) -> [Contact] {
let memberContactIds = ms.compactMap{ m in m.wrapped.memberCurrent ? m.wrapped.memberContactId : nil }
return ChatModel.shared.chats
.compactMap{ c in c.chatInfo.sendMsgEnabled ? c.chatInfo.contact : nil }
.filter{ c in !c.nextSendGrpInv && !memberContactIds.contains(c.apiId) }
.compactMap{ $0.chatInfo.contact }
.filter{ c in c.sendMsgEnabled && !c.nextSendGrpInv && !memberContactIds.contains(c.apiId) }
.sorted{ $0.displayName.lowercased() < $1.displayName.lowercased() }
}
@ -2158,7 +2132,7 @@ func processReceivedMsg(_ res: ChatEvent) async {
let cInfo = chatItem.chatInfo
let cItem = chatItem.chatItem
if !cItem.isDeletedContent && active(user) {
_ = await MainActor.run { m.upsertChatItem(cInfo, cItem) }
await MainActor.run { m.updateChatItem(cInfo, cItem, status: cItem.meta.itemStatus) }
}
if let endTask = m.messageDelivery[cItem.id] {
switch cItem.meta.itemStatus {
@ -2206,9 +2180,6 @@ func processReceivedMsg(_ res: ChatEvent) async {
m.decreaseGroupReportsCounter(item.deletedChatItem.chatInfo.id)
}
}
if let updatedChatInfo = items.last?.deletedChatItem.chatInfo {
m.updateChatInfo(updatedChatInfo)
}
}
case let .groupChatItemsDeleted(user, groupInfo, chatItemIDs, _, member_):
await groupChatItemsDeleted(user, groupInfo, chatItemIDs, member_)
@ -2257,13 +2228,6 @@ func processReceivedMsg(_ res: ChatEvent) async {
_ = m.upsertGroupMember(groupInfo, member)
}
}
case let .memberAcceptedByOther(user, groupInfo, _, member):
if active(user) {
await MainActor.run {
_ = m.upsertGroupMember(groupInfo, member)
m.updateGroup(groupInfo)
}
}
case let .deletedMemberUser(user, groupInfo, member, withMessages): // TODO update user member
if active(user) {
await MainActor.run {
@ -2276,7 +2240,6 @@ func processReceivedMsg(_ res: ChatEvent) async {
case let .deletedMember(user, groupInfo, byMember, deletedMember, withMessages):
if active(user) {
await MainActor.run {
m.updateGroup(groupInfo)
_ = m.upsertGroupMember(groupInfo, deletedMember)
if withMessages {
m.removeMemberItems(deletedMember, byMember: byMember, groupInfo)
@ -2286,7 +2249,6 @@ func processReceivedMsg(_ res: ChatEvent) async {
case let .leftMember(user, groupInfo, member):
if active(user) {
await MainActor.run {
m.updateGroup(groupInfo)
_ = m.upsertGroupMember(groupInfo, member)
}
}
@ -2301,12 +2263,6 @@ func processReceivedMsg(_ res: ChatEvent) async {
await MainActor.run {
m.updateGroup(groupInfo)
}
if m.chatId == groupInfo.id,
case .memberSupport(nil) = m.secondaryIM?.groupScopeInfo {
await MainActor.run {
m.secondaryPendingInviteeChatOpened = false
}
}
}
case let .joinedGroupMember(user, groupInfo, member):
if active(user) {
@ -2593,7 +2549,7 @@ func groupChatItemsDeleted(_ user: UserRef, _ groupInfo: GroupInfo, _ chatItemID
return
}
let im = ItemsModel.shared
let cInfo = ChatInfo.group(groupInfo: groupInfo, groupChatScope: nil)
let cInfo = ChatInfo.group(groupInfo: groupInfo)
await MainActor.run {
m.decreaseGroupReportsCounter(cInfo.id, by: chatItemIDs.count)
}

View file

@ -159,7 +159,7 @@ struct SimpleXApp: App {
if let id = chatModel.chatId,
let chat = chatModel.getChat(id),
!NtfManager.shared.navigatingToChat {
Task { await loadChat(chat: chat, im: ItemsModel.shared, clearItems: false) }
Task { await loadChat(chat: chat, clearItems: false) }
}
if let ncr = chatModel.ntfContactRequest {
await MainActor.run { chatModel.ntfContactRequest = nil }

View file

@ -22,28 +22,11 @@ struct ChatInfoToolbar: View {
Image(systemName: "theatermasks").frame(maxWidth: 24, maxHeight: 24, alignment: .center).foregroundColor(.indigo)
Spacer().frame(width: 16)
}
ZStack(alignment: .bottomTrailing) {
ChatInfoImage(
chat: chat,
size: imageSize,
color: Color(uiColor: .tertiaryLabel)
)
if chat.chatStats.reportsCount > 0 {
Image(systemName: "flag.circle.fill")
.resizable()
.scaledToFit()
.frame(width: 14, height: 14)
.symbolRenderingMode(.palette)
.foregroundStyle(.white, .red)
} else if chat.supportUnreadCount > 0 {
Image(systemName: "flag.circle.fill")
.resizable()
.scaledToFit()
.frame(width: 14, height: 14)
.symbolRenderingMode(.palette)
.foregroundStyle(.white, theme.colors.primary)
}
}
.padding(.trailing, 4)
let t = Text(cInfo.displayName).font(.headline)
(cInfo.contact?.verified == true ? contactVerifiedShield + t : t)

View file

@ -687,7 +687,7 @@ struct ChatTTLOption: View {
let m = ChatModel.shared
do {
try await setChatTTL(chatType: chat.chatInfo.chatType, id: chat.chatInfo.apiId, ttl)
await loadChat(chat: chat, im: ItemsModel.shared, clearItems: true)
await loadChat(chat: chat, clearItems: true)
await MainActor.run {
progressIndicator = false
currentChatItemTTL = chatItemTTL
@ -700,7 +700,7 @@ struct ChatTTLOption: View {
}
catch let error {
logger.error("setChatTTL error \(responseError(error))")
await loadChat(chat: chat, im: ItemsModel.shared, clearItems: true)
await loadChat(chat: chat, clearItems: true)
await MainActor.run {
chatItemTTL = currentChatItemTTL
progressIndicator = false
@ -938,7 +938,7 @@ struct ChatWallpaperEditorSheet: View {
self.chat = chat
self.themes = if case let ChatInfo.direct(contact) = chat.chatInfo, let uiThemes = contact.uiThemes {
uiThemes
} else if case let ChatInfo.group(groupInfo, _) = chat.chatInfo, let uiThemes = groupInfo.uiThemes {
} else if case let ChatInfo.group(groupInfo) = chat.chatInfo, let uiThemes = groupInfo.uiThemes {
uiThemes
} else {
ThemeModeOverrides()
@ -974,7 +974,7 @@ struct ChatWallpaperEditorSheet: View {
private func themesFromChat(_ chat: Chat) -> ThemeModeOverrides {
if case let ChatInfo.direct(contact) = chat.chatInfo, let uiThemes = contact.uiThemes {
uiThemes
} else if case let ChatInfo.group(groupInfo, _) = chat.chatInfo, let uiThemes = groupInfo.uiThemes {
} else if case let ChatInfo.group(groupInfo) = chat.chatInfo, let uiThemes = groupInfo.uiThemes {
uiThemes
} else {
ThemeModeOverrides()
@ -1052,12 +1052,12 @@ struct ChatWallpaperEditorSheet: View {
chat.wrappedValue = Chat.init(chatInfo: ChatInfo.direct(contact: contact))
themes = themesFromChat(chat.wrappedValue)
}
} else if case var ChatInfo.group(groupInfo, _) = chat.wrappedValue.chatInfo {
} else if case var ChatInfo.group(groupInfo) = chat.wrappedValue.chatInfo {
groupInfo.uiThemes = changedThemesConstant
await MainActor.run {
ChatModel.shared.updateChatInfo(ChatInfo.group(groupInfo: groupInfo, groupChatScope: nil))
chat.wrappedValue = Chat.init(chatInfo: ChatInfo.group(groupInfo: groupInfo, groupChatScope: nil))
ChatModel.shared.updateChatInfo(ChatInfo.group(groupInfo: groupInfo))
chat.wrappedValue = Chat.init(chatInfo: ChatInfo.group(groupInfo: groupInfo))
themes = themesFromChat(chat.wrappedValue)
}
}

View file

@ -12,8 +12,8 @@ import SimpleXChat
struct CIChatFeatureView: View {
@EnvironmentObject var m: ChatModel
@Environment(\.revealed) var revealed: Bool
@ObservedObject var im = ItemsModel.shared
@ObservedObject var chat: Chat
@ObservedObject var im: ItemsModel
@EnvironmentObject var theme: AppTheme
var chatItem: ChatItem
var feature: Feature
@ -53,7 +53,7 @@ struct CIChatFeatureView: View {
private func mergedFeatures() -> [FeatureInfo]? {
var fs: [FeatureInfo] = []
var icons: Set<String> = []
if var i = m.getChatItemIndex(im, chatItem) {
if var i = m.getChatItemIndex(chatItem) {
while i < im.reversedChatItems.count,
let f = featureInfo(im.reversedChatItems[i]) {
if !icons.contains(f.icon) {
@ -108,7 +108,6 @@ struct CIChatFeatureView_Previews: PreviewProvider {
let enabled = FeatureEnabled(forUser: false, forContact: false)
CIChatFeatureView(
chat: Chat.sampleData,
im: ItemsModel.shared,
chatItem: ChatItem.getChatFeatureSample(.fullDelete, enabled), feature: ChatFeature.fullDelete, iconColor: enabled.iconColor(.secondary)
).environment(\.revealed, true)
}

View file

@ -278,7 +278,6 @@ func showFileErrorAlert(_ err: FileError, temporary: Bool = false) {
struct CIFileView_Previews: PreviewProvider {
static var previews: some View {
let im = ItemsModel.shared
let sentFile: ChatItem = ChatItem(
chatDir: .directSnd,
meta: CIMeta.getSample(1, .now, "", .sndSent(sndProgress: .complete), itemEdited: true),
@ -294,16 +293,16 @@ struct CIFileView_Previews: PreviewProvider {
file: nil
)
Group {
ChatItemView(chat: Chat.sampleData, im: im, chatItem: sentFile, scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(fileName: "some_long_file_name_here", fileStatus: .rcvInvitation), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(fileStatus: .rcvAccepted), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(fileStatus: .rcvCancelled), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(fileSize: 1_000_000_000, fileStatus: .rcvInvitation), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(text: "Hello there", fileStatus: .rcvInvitation), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getFileMsgContentSample(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", fileStatus: .rcvInvitation), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: fileChatItemWtFile, scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, chatItem: sentFile, scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(fileName: "some_long_file_name_here", fileStatus: .rcvInvitation), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(fileStatus: .rcvAccepted), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(fileStatus: .rcvCancelled), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(fileSize: 1_000_000_000, fileStatus: .rcvInvitation), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(text: "Hello there", fileStatus: .rcvInvitation), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", fileStatus: .rcvInvitation), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: fileChatItemWtFile, scrollToItemId: { _ in })
}
.environment(\.revealed, false)
.previewLayout(.fixed(width: 360, height: 360))

View file

@ -12,7 +12,7 @@ import SimpleXChat
struct CIImageView: View {
@EnvironmentObject var m: ChatModel
let chatItem: ChatItem
var scrollToItem: ((ChatItem.ID) -> Void)? = nil
var scrollToItemId: ((ChatItem.ID) -> Void)? = nil
var preview: UIImage?
let maxWidth: CGFloat
var imgWidth: CGFloat?
@ -26,7 +26,7 @@ struct CIImageView: View {
if let uiImage = getLoadedImage(file) {
Group { if smallView { smallViewImageView(uiImage) } else { imageView(uiImage) } }
.fullScreenCover(isPresented: $showFullScreenImage) {
FullScreenMediaView(chatItem: chatItem, scrollToItem: scrollToItem, image: uiImage, showView: $showFullScreenImage)
FullScreenMediaView(chatItem: chatItem, scrollToItemId: scrollToItemId, image: uiImage, showView: $showFullScreenImage)
}
.if(!smallView) { view in
view.modifier(PrivacyBlur(blurred: $blurred))

View file

@ -45,7 +45,7 @@ struct CIRcvDecryptionError: View {
viewBody()
.onAppear {
// for direct chat ConnectionStats are populated on opening chat, see ChatView onAppear
if case let .group(groupInfo, _) = chat.chatInfo,
if case let .group(groupInfo) = chat.chatInfo,
case let .groupRcv(groupMember) = chatItem.chatDir {
do {
let (member, stats) = try apiGroupMemberInfoSync(groupInfo.apiId, groupMember.groupMemberId)
@ -83,7 +83,7 @@ struct CIRcvDecryptionError: View {
} else {
basicDecryptionErrorItem()
}
} else if case let .group(groupInfo, _) = chat.chatInfo,
} else if case let .group(groupInfo) = chat.chatInfo,
case let .groupRcv(groupMember) = chatItem.chatDir,
let mem = m.getGroupMember(groupMember.groupMemberId),
let memberStats = mem.wrapped.activeConn?.connectionStats {

View file

@ -435,7 +435,6 @@ class VoiceItemState {
struct CIVoiceView_Previews: PreviewProvider {
static var previews: some View {
let im = ItemsModel.shared
let sentVoiceMessage: ChatItem = ChatItem(
chatDir: .directSnd,
meta: CIMeta.getSample(1, .now, "", .sndSent(sndProgress: .complete), itemEdited: true),
@ -458,10 +457,10 @@ struct CIVoiceView_Previews: PreviewProvider {
duration: 30,
allowMenu: Binding.constant(true)
)
ChatItemView(chat: Chat.sampleData, im: im, chatItem: sentVoiceMessage, scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil), allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getVoiceMsgContentSample(), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil), allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getVoiceMsgContentSample(fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil), allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: voiceMessageWtFile, scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil), allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, chatItem: sentVoiceMessage, scrollToItemId: { _ in }, allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(), scrollToItemId: { _ in }, allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), scrollToItemId: { _ in }, allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, chatItem: voiceMessageWtFile, scrollToItemId: { _ in }, allowMenu: .constant(true))
}
.previewLayout(.fixed(width: 360, height: 360))
}

View file

@ -77,7 +77,6 @@ struct FramedCIVoiceView: View {
struct FramedCIVoiceView_Previews: PreviewProvider {
static var previews: some View {
let im = ItemsModel.shared
let sentVoiceMessage: ChatItem = ChatItem(
chatDir: .directSnd,
meta: CIMeta.getSample(1, .now, "", .sndSent(sndProgress: .complete), itemEdited: true),
@ -93,11 +92,11 @@ struct FramedCIVoiceView_Previews: PreviewProvider {
file: CIFile.getSample(fileStatus: .sndComplete)
)
Group {
ChatItemView(chat: Chat.sampleData, im: im, chatItem: sentVoiceMessage, scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getVoiceMsgContentSample(text: "Hello there"), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getVoiceMsgContentSample(text: "Hello there", fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getVoiceMsgContentSample(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: voiceMessageWithQuote, scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, chatItem: sentVoiceMessage, scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(text: "Hello there"), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(text: "Hello there", fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: voiceMessageWithQuote, scrollToItemId: { _ in })
}
.environment(\.revealed, false)
.previewLayout(.fixed(width: 360, height: 360))

File diff suppressed because one or more lines are too long

View file

@ -14,7 +14,7 @@ import AVKit
struct FullScreenMediaView: View {
@EnvironmentObject var m: ChatModel
@State var chatItem: ChatItem
var scrollToItem: ((ChatItem.ID) -> Void)?
var scrollToItemId: ((ChatItem.ID) -> Void)?
@State var image: UIImage?
@State var player: AVPlayer? = nil
@State var url: URL? = nil
@ -71,7 +71,7 @@ struct FullScreenMediaView: View {
let w = abs(t.width)
if t.height > 60 && t.height > w * 2 {
showView = false
scrollToItem?(chatItem.id)
scrollToItemId?(chatItem.id)
} else if w > 60 && w > abs(t.height) * 2 && !scrolling {
let previous = t.width > 0
scrolling = true

View file

@ -14,7 +14,6 @@ struct MarkedDeletedItemView: View {
@EnvironmentObject var theme: AppTheme
@Environment(\.revealed) var revealed: Bool
@ObservedObject var chat: Chat
@ObservedObject var im: ItemsModel
var chatItem: ChatItem
var body: some View {
@ -30,14 +29,14 @@ struct MarkedDeletedItemView: View {
var mergedMarkedDeletedText: LocalizedStringKey {
if !revealed,
let ciCategory = chatItem.mergeCategory,
var i = m.getChatItemIndex(im, chatItem) {
var i = m.getChatItemIndex(chatItem) {
var moderated = 0
var blocked = 0
var blockedByAdmin = 0
var deleted = 0
var moderatedBy: Set<String> = []
while i < im.reversedChatItems.count,
let ci = .some(im.reversedChatItems[i]),
while i < ItemsModel.shared.reversedChatItems.count,
let ci = .some(ItemsModel.shared.reversedChatItems[i]),
ci.mergeCategory == ciCategory,
let itemDeleted = ci.meta.itemDeleted {
switch itemDeleted {
@ -86,7 +85,6 @@ struct MarkedDeletedItemView_Previews: PreviewProvider {
Group {
MarkedDeletedItemView(
chat: Chat.sampleData,
im: ItemsModel.shared,
chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemDeleted: .deleted(deletedTs: .now))
).environment(\.revealed, true)
}

View file

@ -40,31 +40,25 @@ extension EnvironmentValues {
struct ChatItemView: View {
@ObservedObject var chat: Chat
@ObservedObject var im: ItemsModel
@EnvironmentObject var theme: AppTheme
@Environment(\.showTimestamp) var showTimestamp: Bool
@Environment(\.revealed) var revealed: Bool
var chatItem: ChatItem
var scrollToItem: (ChatItem.ID) -> Void
@Binding var scrollToItemId: ChatItem.ID?
var scrollToItemId: (ChatItem.ID) -> Void
var maxWidth: CGFloat = .infinity
@Binding var allowMenu: Bool
init(
chat: Chat,
im: ItemsModel,
chatItem: ChatItem,
scrollToItem: @escaping (ChatItem.ID) -> Void,
scrollToItemId: Binding<ChatItem.ID?> = .constant(nil),
scrollToItemId: @escaping (ChatItem.ID) -> Void,
showMember: Bool = false,
maxWidth: CGFloat = .infinity,
allowMenu: Binding<Bool> = .constant(false)
) {
self.chat = chat
self.im = im
self.chatItem = chatItem
self.scrollToItem = scrollToItem
_scrollToItemId = scrollToItemId
self.scrollToItemId = scrollToItemId
self.maxWidth = maxWidth
_allowMenu = allowMenu
}
@ -72,14 +66,14 @@ struct ChatItemView: View {
var body: some View {
let ci = chatItem
if chatItem.meta.itemDeleted != nil && (!revealed || chatItem.isDeletedContent) {
MarkedDeletedItemView(chat: chat, im: im, chatItem: chatItem)
MarkedDeletedItemView(chat: chat, chatItem: chatItem)
} else if ci.quotedItem == nil && ci.meta.itemForwarded == nil && ci.meta.itemDeleted == nil && !ci.meta.isLive {
if let mc = ci.content.msgContent, mc.isText && isShortEmoji(ci.content.text) {
EmojiItemView(chat: chat, chatItem: ci)
} else if ci.content.text.isEmpty, case let .voice(_, duration) = ci.content.msgContent {
CIVoiceView(chat: chat, chatItem: ci, recordingFile: ci.file, duration: duration, allowMenu: $allowMenu)
} else if ci.content.msgContent == nil {
ChatItemContentView(chat: chat, im: im, chatItem: chatItem, msgContentView: { Text(ci.text) }) // msgContent is unreachable branch in this case
ChatItemContentView(chat: chat, chatItem: chatItem, msgContentView: { Text(ci.text) }) // msgContent is unreachable branch in this case
} else {
framedItemView()
}
@ -107,10 +101,8 @@ struct ChatItemView: View {
}()
return FramedItemView(
chat: chat,
im: im,
chatItem: chatItem,
scrollToItem: scrollToItem,
scrollToItemId: $scrollToItemId,
scrollToItemId: scrollToItemId,
preview: preview,
maxWidth: maxWidth,
imgWidth: adjustedMaxWidth,
@ -125,7 +117,6 @@ struct ChatItemContentView<Content: View>: View {
@EnvironmentObject var theme: AppTheme
@Environment(\.revealed) var revealed: Bool
@ObservedObject var chat: Chat
@ObservedObject var im: ItemsModel
var chatItem: ChatItem
var msgContentView: () -> Content
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@ -149,9 +140,7 @@ struct ChatItemContentView<Content: View>: View {
case let .sndGroupInvitation(groupInvitation, memberRole): groupInvitationItemView(groupInvitation, memberRole)
case .rcvDirectEvent: eventItemView()
case .rcvGroupEvent(.memberCreatedContact): CIMemberCreatedContactView(chatItem: chatItem)
case .rcvGroupEvent(.newMemberPendingReview): CIEventView(eventText: pendingReviewEventItemText())
case .rcvGroupEvent: eventItemView()
case .sndGroupEvent(.userPendingReview): CIEventView(eventText: pendingReviewEventItemText())
case .sndGroupEvent: eventItemView()
case .rcvConnEvent: eventItemView()
case .sndConnEvent: eventItemView()
@ -160,7 +149,7 @@ struct ChatItemContentView<Content: View>: View {
case let .rcvChatPreference(feature, allowed, param):
CIFeaturePreferenceView(chat: chat, chatItem: chatItem, feature: feature, allowed: allowed, param: param)
case let .sndChatPreference(feature, _, _):
CIChatFeatureView(chat: chat, im: im, chatItem: chatItem, feature: feature, icon: feature.icon, iconColor: theme.colors.secondary)
CIChatFeatureView(chat: chat, chatItem: chatItem, feature: feature, icon: feature.icon, iconColor: theme.colors.secondary)
case let .rcvGroupFeature(feature, preference, _, role): chatFeatureView(feature, preference.enabled(role, for: chat.chatInfo.groupInfo?.membership).iconColor(theme.colors.secondary))
case let .sndGroupFeature(feature, preference, _, role): chatFeatureView(feature, preference.enabled(role, for: chat.chatInfo.groupInfo?.membership).iconColor(theme.colors.secondary))
case let .rcvChatFeatureRejected(feature): chatFeatureView(feature, .red)
@ -192,13 +181,6 @@ struct ChatItemContentView<Content: View>: View {
CIEventView(eventText: eventItemViewText(theme.colors.secondary))
}
private func pendingReviewEventItemText() -> Text {
Text(chatItem.content.text)
.font(.caption)
.foregroundColor(theme.colors.secondary)
.fontWeight(.bold)
}
private func eventItemViewText(_ secondaryColor: Color) -> Text {
if !revealed, let t = mergedGroupEventText {
return chatEventText(t + textSpace + chatItem.timestampText, secondaryColor)
@ -214,7 +196,7 @@ struct ChatItemContentView<Content: View>: View {
}
private func chatFeatureView(_ feature: Feature, _ iconColor: Color) -> some View {
CIChatFeatureView(chat: chat, im: im, chatItem: chatItem, feature: feature, iconColor: iconColor)
CIChatFeatureView(chat: chat, chatItem: chatItem, feature: feature, iconColor: iconColor)
}
private var mergedGroupEventText: Text? {
@ -274,17 +256,16 @@ func chatEventText(_ ci: ChatItem, _ secondaryColor: Color) -> Text {
struct ChatItemView_Previews: PreviewProvider {
static var previews: some View {
let im = ItemsModel.shared
Group{
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello"), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(2, .directRcv, .now, "hello there too"), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(1, .directSnd, .now, "🙂"), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(2, .directRcv, .now, "🙂🙂🙂🙂🙂"), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(2, .directRcv, .now, "🙂🙂🙂🙂🙂🙂"), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getDeletedContentSample(), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemDeleted: .deleted(deletedTs: .now)), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(1, .directSnd, .now, "🙂", .sndSent(sndProgress: .complete), itemLive: true), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil)).environment(\.revealed, true)
ChatItemView(chat: Chat.sampleData, im: im, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemLive: true), scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil)).environment(\.revealed, true)
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello"), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "hello there too"), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "🙂"), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "🙂🙂🙂🙂🙂"), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "🙂🙂🙂🙂🙂🙂"), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getDeletedContentSample(), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemDeleted: .deleted(deletedTs: .now)), scrollToItemId: { _ in })
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "🙂", .sndSent(sndProgress: .complete), itemLive: true), scrollToItemId: { _ in }).environment(\.revealed, true)
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemLive: true), scrollToItemId: { _ in }).environment(\.revealed, true)
}
.environment(\.revealed, false)
.previewLayout(.fixed(width: 360, height: 70))
@ -294,12 +275,10 @@ struct ChatItemView_Previews: PreviewProvider {
struct ChatItemView_NonMsgContentDeleted_Previews: PreviewProvider {
static var previews: some View {
let im = ItemsModel.shared
let ciFeatureContent = CIContent.rcvChatFeature(feature: .fullDelete, enabled: FeatureEnabled(forUser: false, forContact: false), param: nil)
Group{
ChatItemView(
chat: Chat.sampleData,
im: im,
chatItem: ChatItem(
chatDir: .directRcv,
meta: CIMeta.getSample(1, .now, "1 skipped message", .rcvRead, itemDeleted: .deleted(deletedTs: .now)),
@ -307,12 +286,10 @@ struct ChatItemView_NonMsgContentDeleted_Previews: PreviewProvider {
quotedItem: nil,
file: nil
),
scrollToItem: { _ in },
scrollToItemId: Binding.constant(nil)
scrollToItemId: { _ in }
)
ChatItemView(
chat: Chat.sampleData,
im: im,
chatItem: ChatItem(
chatDir: .directRcv,
meta: CIMeta.getSample(1, .now, "1 skipped message", .rcvRead),
@ -320,11 +297,10 @@ struct ChatItemView_NonMsgContentDeleted_Previews: PreviewProvider {
quotedItem: nil,
file: nil
),
scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil)
scrollToItemId: { _ in }
)
ChatItemView(
chat: Chat.sampleData,
im: im,
chatItem: ChatItem(
chatDir: .directRcv,
meta: CIMeta.getSample(1, .now, "received invitation to join group team as admin", .rcvRead, itemDeleted: .deleted(deletedTs: .now)),
@ -332,12 +308,10 @@ struct ChatItemView_NonMsgContentDeleted_Previews: PreviewProvider {
quotedItem: nil,
file: nil
),
scrollToItem: { _ in },
scrollToItemId: Binding.constant(nil)
scrollToItemId: { _ in }
)
ChatItemView(
chat: Chat.sampleData,
im: im,
chatItem: ChatItem(
chatDir: .directRcv,
meta: CIMeta.getSample(1, .now, "group event text", .rcvRead, itemDeleted: .deleted(deletedTs: .now)),
@ -345,12 +319,10 @@ struct ChatItemView_NonMsgContentDeleted_Previews: PreviewProvider {
quotedItem: nil,
file: nil
),
scrollToItem: { _ in },
scrollToItemId: Binding.constant(nil)
scrollToItemId: { _ in }
)
ChatItemView(
chat: Chat.sampleData,
im: im,
chatItem: ChatItem(
chatDir: .directRcv,
meta: CIMeta.getSample(1, .now, ciFeatureContent.text, .rcvRead, itemDeleted: .deleted(deletedTs: .now)),
@ -358,8 +330,7 @@ struct ChatItemView_NonMsgContentDeleted_Previews: PreviewProvider {
quotedItem: nil,
file: nil
),
scrollToItem: { _ in },
scrollToItemId: Binding.constant(nil)
scrollToItemId: { _ in }
)
}
.environment(\.revealed, true)

View file

@ -13,8 +13,8 @@ let TRIM_KEEP_COUNT = 200
func apiLoadMessages(
_ chatId: ChatId,
_ im: ItemsModel,
_ pagination: ChatPagination,
_ chatState: ActiveChatState,
_ search: String = "",
_ openAroundItemId: ChatItem.ID? = nil,
_ visibleItemIndexesNonReversed: @MainActor () -> ClosedRange<Int> = { 0 ... 0 }
@ -22,7 +22,7 @@ func apiLoadMessages(
let chat: Chat
let navInfo: NavigationInfo
do {
(chat, navInfo) = try await apiGetChat(chatId: chatId, scope: im.groupScopeInfo?.toChatScope(), contentTag: im.contentTag, pagination: pagination, search: search)
(chat, navInfo) = try await apiGetChat(chatId: chatId, pagination: pagination, search: search)
} catch let error {
logger.error("apiLoadMessages error: \(responseError(error))")
return
@ -38,31 +38,30 @@ func apiLoadMessages(
return
}
let unreadAfterItemId = im.chatState.unreadAfterItemId
let unreadAfterItemId = chatState.unreadAfterItemId
let oldItems = Array(im.reversedChatItems.reversed())
let oldItems = Array(ItemsModel.shared.reversedChatItems.reversed())
var newItems: [ChatItem] = []
switch pagination {
case .initial:
let newSplits: [Int64] = if !chat.chatItems.isEmpty && navInfo.afterTotal > 0 { [chat.chatItems.last!.id] } else { [] }
if im.secondaryIMFilter == nil && chatModel.getChat(chat.id) == nil {
if chatModel.getChat(chat.id) == nil {
chatModel.addChat(chat)
}
await MainActor.run {
im.reversedChatItems = chat.chatItems.reversed()
if im.secondaryIMFilter == nil {
chatModel.chatItemStatuses.removeAll()
ItemsModel.shared.reversedChatItems = chat.chatItems.reversed()
chatModel.updateChatInfo(chat.chatInfo)
}
im.chatState.splits = newSplits
chatState.splits = newSplits
if !chat.chatItems.isEmpty {
im.chatState.unreadAfterItemId = chat.chatItems.last!.id
chatState.unreadAfterItemId = chat.chatItems.last!.id
}
im.chatState.totalAfter = navInfo.afterTotal
im.chatState.unreadTotal = chat.chatStats.unreadCount
im.chatState.unreadAfter = navInfo.afterUnread
im.chatState.unreadAfterNewestLoaded = navInfo.afterUnread
chatState.totalAfter = navInfo.afterTotal
chatState.unreadTotal = chat.chatStats.unreadCount
chatState.unreadAfter = navInfo.afterUnread
chatState.unreadAfterNewestLoaded = navInfo.afterUnread
im.preloadState.clear()
PreloadState.shared.clear()
}
case let .before(paginationChatItemId, _):
newItems.append(contentsOf: oldItems)
@ -72,15 +71,15 @@ func apiLoadMessages(
let wasSize = newItems.count
let visibleItemIndexes = await MainActor.run { visibleItemIndexesNonReversed() }
let modifiedSplits = removeDuplicatesAndModifySplitsOnBeforePagination(
unreadAfterItemId, &newItems, newIds, im.chatState.splits, visibleItemIndexes
unreadAfterItemId, &newItems, newIds, chatState.splits, visibleItemIndexes
)
let insertAt = max((indexInCurrentItems - (wasSize - newItems.count) + modifiedSplits.trimmedIds.count), 0)
newItems.insert(contentsOf: chat.chatItems, at: insertAt)
let newReversed: [ChatItem] = newItems.reversed()
await MainActor.run {
im.reversedChatItems = newReversed
im.chatState.splits = modifiedSplits.newSplits
im.chatState.moveUnreadAfterItem(modifiedSplits.oldUnreadSplitIndex, modifiedSplits.newUnreadSplitIndex, oldItems)
ItemsModel.shared.reversedChatItems = newReversed
chatState.splits = modifiedSplits.newSplits
chatState.moveUnreadAfterItem(modifiedSplits.oldUnreadSplitIndex, modifiedSplits.newUnreadSplitIndex, oldItems)
}
case let .after(paginationChatItemId, _):
newItems.append(contentsOf: oldItems)
@ -90,7 +89,7 @@ func apiLoadMessages(
let mappedItems = mapItemsToIds(chat.chatItems)
let newIds = mappedItems.0
let (newSplits, unreadInLoaded) = removeDuplicatesAndModifySplitsOnAfterPagination(
mappedItems.1, paginationChatItemId, &newItems, newIds, chat, im.chatState.splits
mappedItems.1, paginationChatItemId, &newItems, newIds, chat, chatState.splits
)
let indexToAdd = min(indexInCurrentItems + 1, newItems.count)
let indexToAddIsLast = indexToAdd == newItems.count
@ -98,19 +97,19 @@ func apiLoadMessages(
let new: [ChatItem] = newItems
let newReversed: [ChatItem] = newItems.reversed()
await MainActor.run {
im.reversedChatItems = newReversed
im.chatState.splits = newSplits
im.chatState.moveUnreadAfterItem(im.chatState.splits.first ?? new.last!.id, new)
ItemsModel.shared.reversedChatItems = newReversed
chatState.splits = newSplits
chatState.moveUnreadAfterItem(chatState.splits.first ?? new.last!.id, new)
// loading clear bottom area, updating number of unread items after the newest loaded item
if indexToAddIsLast {
im.chatState.unreadAfterNewestLoaded -= unreadInLoaded
chatState.unreadAfterNewestLoaded -= unreadInLoaded
}
}
case .around:
var newSplits: [Int64]
if openAroundItemId == nil {
newItems.append(contentsOf: oldItems)
newSplits = await removeDuplicatesAndUpperSplits(&newItems, chat, im.chatState.splits, visibleItemIndexesNonReversed)
newSplits = await removeDuplicatesAndUpperSplits(&newItems, chat, chatState.splits, visibleItemIndexesNonReversed)
} else {
newSplits = []
}
@ -121,37 +120,33 @@ func apiLoadMessages(
let newReversed: [ChatItem] = newItems.reversed()
let orderedSplits = newSplits
await MainActor.run {
im.reversedChatItems = newReversed
im.chatState.splits = orderedSplits
im.chatState.unreadAfterItemId = chat.chatItems.last!.id
im.chatState.totalAfter = navInfo.afterTotal
im.chatState.unreadTotal = chat.chatStats.unreadCount
im.chatState.unreadAfter = navInfo.afterUnread
ItemsModel.shared.reversedChatItems = newReversed
chatState.splits = orderedSplits
chatState.unreadAfterItemId = chat.chatItems.last!.id
chatState.totalAfter = navInfo.afterTotal
chatState.unreadTotal = chat.chatStats.unreadCount
chatState.unreadAfter = navInfo.afterUnread
if let openAroundItemId {
im.chatState.unreadAfterNewestLoaded = navInfo.afterUnread
if im.secondaryIMFilter == nil {
ChatModel.shared.openAroundItemId = openAroundItemId // TODO [knocking] move openAroundItemId from ChatModel to ItemsModel?
ChatModel.shared.chatId = chat.id
}
chatState.unreadAfterNewestLoaded = navInfo.afterUnread
ChatModel.shared.openAroundItemId = openAroundItemId
ChatModel.shared.chatId = chatId
} else {
// no need to set it, count will be wrong
// chatState.unreadAfterNewestLoaded = navInfo.afterUnread
}
im.preloadState.clear()
PreloadState.shared.clear()
}
case .last:
newItems.append(contentsOf: oldItems)
let newSplits = await removeDuplicatesAndUnusedSplits(&newItems, chat, im.chatState.splits)
let newSplits = await removeDuplicatesAndUnusedSplits(&newItems, chat, chatState.splits)
newItems.append(contentsOf: chat.chatItems)
let items = newItems
await MainActor.run {
im.reversedChatItems = items.reversed()
im.chatState.splits = newSplits
if im.secondaryIMFilter == nil {
ItemsModel.shared.reversedChatItems = items.reversed()
chatState.splits = newSplits
chatModel.updateChatInfo(chat.chatInfo)
}
im.chatState.unreadAfterNewestLoaded = 0
chatState.unreadAfterNewestLoaded = 0
}
}
}

View file

@ -10,7 +10,6 @@ import SwiftUI
import SimpleXChat
struct MergedItems: Hashable, Equatable {
let im: ItemsModel
let items: [MergedItem]
let splits: [SplitRange]
// chat item id, index in list
@ -24,15 +23,15 @@ struct MergedItems: Hashable, Equatable {
hasher.combine("\(items.hashValue)")
}
static func create(_ im: ItemsModel, _ revealedItems: Set<Int64>) -> MergedItems {
if im.reversedChatItems.isEmpty {
return MergedItems(im: im, items: [], splits: [], indexInParentItems: [:])
static func create(_ items: [ChatItem], _ revealedItems: Set<Int64>, _ chatState: ActiveChatState) -> MergedItems {
if items.isEmpty {
return MergedItems(items: [], splits: [], indexInParentItems: [:])
}
let unreadCount = im.chatState.unreadTotal
let unreadCount = chatState.unreadTotal
let unreadAfterItemId = im.chatState.unreadAfterItemId
let itemSplits = im.chatState.splits
let unreadAfterItemId = chatState.unreadAfterItemId
let itemSplits = chatState.splits
var mergedItems: [MergedItem] = []
// Indexes of splits here will be related to reversedChatItems, not chatModel.chatItems
var splitRanges: [SplitRange] = []
@ -41,19 +40,19 @@ struct MergedItems: Hashable, Equatable {
var unclosedSplitIndex: Int? = nil
var unclosedSplitIndexInParent: Int? = nil
var visibleItemIndexInParent = -1
var unreadBefore = unreadCount - im.chatState.unreadAfterNewestLoaded
var unreadBefore = unreadCount - chatState.unreadAfterNewestLoaded
var lastRevealedIdsInMergedItems: BoxedValue<[Int64]>? = nil
var lastRangeInReversedForMergedItems: BoxedValue<ClosedRange<Int>>? = nil
var recent: MergedItem? = nil
while index < im.reversedChatItems.count {
let item = im.reversedChatItems[index]
let prev = index >= 1 ? im.reversedChatItems[index - 1] : nil
let next = index + 1 < im.reversedChatItems.count ? im.reversedChatItems[index + 1] : nil
while index < items.count {
let item = items[index]
let prev = index >= 1 ? items[index - 1] : nil
let next = index + 1 < items.count ? items[index + 1] : nil
let category = item.mergeCategory
let itemIsSplit = itemSplits.contains(item.id)
if item.id == unreadAfterItemId {
unreadBefore = unreadCount - im.chatState.unreadAfter
unreadBefore = unreadCount - chatState.unreadAfter
}
if item.isRcvNew {
unreadBefore -= 1
@ -107,19 +106,18 @@ struct MergedItems: Hashable, Equatable {
// found item that is considered as a split
if let unclosedSplitIndex, let unclosedSplitIndexInParent {
// it was at least second split in the list
splitRanges.append(SplitRange(itemId: im.reversedChatItems[unclosedSplitIndex].id, indexRangeInReversed: unclosedSplitIndex ... index - 1, indexRangeInParentItems: unclosedSplitIndexInParent ... visibleItemIndexInParent - 1))
splitRanges.append(SplitRange(itemId: items[unclosedSplitIndex].id, indexRangeInReversed: unclosedSplitIndex ... index - 1, indexRangeInParentItems: unclosedSplitIndexInParent ... visibleItemIndexInParent - 1))
}
unclosedSplitIndex = index
unclosedSplitIndexInParent = visibleItemIndexInParent
} else if index + 1 == im.reversedChatItems.count, let unclosedSplitIndex, let unclosedSplitIndexInParent {
} else if index + 1 == items.count, let unclosedSplitIndex, let unclosedSplitIndexInParent {
// just one split for the whole list, there will be no more, it's the end
splitRanges.append(SplitRange(itemId: im.reversedChatItems[unclosedSplitIndex].id, indexRangeInReversed: unclosedSplitIndex ... index, indexRangeInParentItems: unclosedSplitIndexInParent ... visibleItemIndexInParent))
splitRanges.append(SplitRange(itemId: items[unclosedSplitIndex].id, indexRangeInReversed: unclosedSplitIndex ... index, indexRangeInParentItems: unclosedSplitIndexInParent ... visibleItemIndexInParent))
}
indexInParentItems[item.id] = visibleItemIndexInParent
index += 1
}
return MergedItems(
im: im,
items: mergedItems,
splits: splitRanges,
indexInParentItems: indexInParentItems
@ -129,6 +127,7 @@ struct MergedItems: Hashable, Equatable {
// Use this check to ensure that mergedItems state based on currently actual state of global
// splits and reversedChatItems
func isActualState() -> Bool {
let im = ItemsModel.shared
// do not load anything if global splits state is different than in merged items because it
// will produce undefined results in terms of loading and placement of items.
// Same applies to reversedChatItems
@ -435,7 +434,7 @@ class BoxedValue<T: Hashable>: Equatable, Hashable {
}
@MainActor
func visibleItemIndexesNonReversed(_ im: ItemsModel, _ listState: EndlessScrollView<MergedItem>.ListState, _ mergedItems: MergedItems) -> ClosedRange<Int> {
func visibleItemIndexesNonReversed(_ listState: EndlessScrollView<MergedItem>.ListState, _ mergedItems: MergedItems) -> ClosedRange<Int> {
let zero = 0 ... 0
let items = mergedItems.items
if items.isEmpty {
@ -446,12 +445,12 @@ func visibleItemIndexesNonReversed(_ im: ItemsModel, _ listState: EndlessScrollV
guard let newest, let oldest else {
return zero
}
let size = im.reversedChatItems.count
let size = ItemsModel.shared.reversedChatItems.count
let range = size - oldest ... size - newest
if range.lowerBound < 0 || range.upperBound < 0 {
return zero
}
// visible items mapped to their underlying data structure which is im.reversedChatItems.reversed()
// visible items mapped to their underlying data structure which is ItemsModel.shared.reversedChatItems.reversed()
return range
}

View file

@ -9,7 +9,7 @@
import SwiftUI
import SimpleXChat
func loadLastItems(_ loadingMoreItems: Binding<Bool>, loadingBottomItems: Binding<Bool>, _ chat: Chat, _ im: ItemsModel) async {
func loadLastItems(_ loadingMoreItems: Binding<Bool>, loadingBottomItems: Binding<Bool>, _ chat: Chat) async {
await MainActor.run {
loadingMoreItems.wrappedValue = true
loadingBottomItems.wrappedValue = true
@ -22,15 +22,27 @@ func loadLastItems(_ loadingMoreItems: Binding<Bool>, loadingBottomItems: Bindin
}
return
}
await apiLoadMessages(chat.chatInfo.id, im, ChatPagination.last(count: 50))
await apiLoadMessages(chat.chatInfo.id, ChatPagination.last(count: 50), ItemsModel.shared.chatState)
await MainActor.run {
loadingMoreItems.wrappedValue = false
loadingBottomItems.wrappedValue = false
}
}
class PreloadState {
static let shared = PreloadState()
var prevFirstVisible: Int64 = Int64.min
var prevItemsCount: Int = 0
var preloading: Bool = false
func clear() {
prevFirstVisible = Int64.min
prevItemsCount = 0
preloading = false
}
}
func preloadIfNeeded(
_ im: ItemsModel,
_ allowLoadMoreItems: Binding<Bool>,
_ ignoreLoadingRequests: Binding<Int64?>,
_ listState: EndlessScrollView<MergedItem>.ListState,
@ -38,7 +50,7 @@ func preloadIfNeeded(
loadItems: @escaping (Bool, ChatPagination) async -> Bool,
loadLastItems: @escaping () async -> Void
) {
let state = im.preloadState
let state = PreloadState.shared
guard !listState.isScrolling && !listState.isAnimatedScrolling,
!state.preloading,
listState.totalItemsCount > 0
@ -51,7 +63,7 @@ func preloadIfNeeded(
Task {
defer { state.preloading = false }
var triedToLoad = true
await preloadItems(im, mergedItems.boxedValue, allowLoadMore, listState, ignoreLoadingRequests) { pagination in
await preloadItems(mergedItems.boxedValue, allowLoadMore, listState, ignoreLoadingRequests) { pagination in
triedToLoad = await loadItems(false, pagination)
return triedToLoad
}
@ -61,11 +73,11 @@ func preloadIfNeeded(
}
// it's important to ask last items when the view is fully covered with items. Otherwise, visible items from one
// split will be merged with last items and position of scroll will change unexpectedly.
if listState.itemsCanCoverScreen && !im.lastItemsLoaded {
if listState.itemsCanCoverScreen && !ItemsModel.shared.lastItemsLoaded {
await loadLastItems()
}
}
} else if listState.itemsCanCoverScreen && !im.lastItemsLoaded {
} else if listState.itemsCanCoverScreen && !ItemsModel.shared.lastItemsLoaded {
state.preloading = true
Task {
defer { state.preloading = false }
@ -75,7 +87,6 @@ func preloadIfNeeded(
}
func preloadItems(
_ im: ItemsModel,
_ mergedItems: MergedItems,
_ allowLoadMoreItems: Bool,
_ listState: EndlessScrollView<MergedItem>.ListState,
@ -94,7 +105,7 @@ async {
let splits = mergedItems.splits
let lastVisibleIndex = listState.lastVisibleItemIndex
var lastIndexToLoadFrom: Int? = findLastIndexToLoadFromInSplits(firstVisibleIndex, lastVisibleIndex, remaining, splits)
let items: [ChatItem] = im.reversedChatItems.reversed()
let items: [ChatItem] = ItemsModel.shared.reversedChatItems.reversed()
if splits.isEmpty && !items.isEmpty && lastVisibleIndex > mergedItems.items.count - remaining {
lastIndexToLoadFrom = items.count - 1
}
@ -111,7 +122,7 @@ async {
let sizeWas = items.count
let firstItemIdWas = items.first?.id
let triedToLoad = await loadItems(ChatPagination.before(chatItemId: loadFromItemId, count: ChatPagination.PRELOAD_COUNT))
if triedToLoad && sizeWas == im.reversedChatItems.count && firstItemIdWas == im.reversedChatItems.last?.id {
if triedToLoad && sizeWas == ItemsModel.shared.reversedChatItems.count && firstItemIdWas == ItemsModel.shared.reversedChatItems.last?.id {
ignoreLoadingRequests.wrappedValue = loadFromItemId
return false
}
@ -122,7 +133,7 @@ async {
let splits = mergedItems.splits
let split = splits.last(where: { $0.indexRangeInParentItems.contains(firstVisibleIndex) })
// we're inside a splitRange (top --- [end of the splitRange --- we're here --- start of the splitRange] --- bottom)
let reversedItems: [ChatItem] = im.reversedChatItems
let reversedItems: [ChatItem] = ItemsModel.shared.reversedChatItems
if let split, split.indexRangeInParentItems.lowerBound + remaining > firstVisibleIndex {
let index = split.indexRangeInReversed.lowerBound
if index >= 0 {

View file

@ -15,6 +15,8 @@ private let memberImageSize: CGFloat = 34
struct ChatView: View {
@EnvironmentObject var chatModel: ChatModel
@ObservedObject var im = ItemsModel.shared
@State var mergedItems: BoxedValue<MergedItems> = BoxedValue(MergedItems.create(ItemsModel.shared.reversedChatItems, [], ItemsModel.shared.chatState))
@State var revealedItems: Set<Int64> = Set()
@State var theme: AppTheme = buildTheme()
@Environment(\.dismiss) var dismiss
@ -22,10 +24,6 @@ struct ChatView: View {
@Environment(\.presentationMode) var presentationMode
@Environment(\.scenePhase) var scenePhase
@State @ObservedObject var chat: Chat
@ObservedObject var im: ItemsModel
@State var mergedItems: BoxedValue<MergedItems>
@State var floatingButtonModel: FloatingButtonModel
@Binding var scrollToItemId: ChatItem.ID?
@State private var showChatInfoSheet: Bool = false
@State private var showAddMembersSheet: Bool = false
@State private var composeState = ComposeState()
@ -57,14 +55,12 @@ struct ChatView: View {
@State private var allowLoadMoreItems: Bool = false
@State private var ignoreLoadingRequests: Int64? = nil
@State private var animatedScrollingInProgress: Bool = false
@State private var showUserSupportChatSheet = false
@State private var floatingButtonModel: FloatingButtonModel = FloatingButtonModel()
@State private var scrollView: EndlessScrollView<MergedItem> = EndlessScrollView(frame: .zero)
@AppStorage(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial
let userSupportScopeInfo: GroupChatScopeInfo = .memberSupport(groupMember_: nil)
var body: some View {
if #available(iOS 16.0, *) {
viewBody
@ -77,39 +73,25 @@ struct ChatView: View {
private var viewBody: some View {
let cInfo = chat.chatInfo
let memberSupportChat: (groupInfo: GroupInfo, member: GroupMember?)? =
if case let .group(groupInfo, .memberSupport(member)) = cInfo {
(groupInfo, member)
} else {
nil
}
let userMemberKnockingChat = memberSupportChat?.groupInfo.membership.memberPending == true
return ZStack {
let wallpaperImage = theme.wallpaper.type.image
let wallpaperType = theme.wallpaper.type
let backgroundColor = theme.wallpaper.background ?? wallpaperType.defaultBackgroundColor(theme.base, theme.colors.background)
let tintColor = theme.wallpaper.tint ?? wallpaperType.defaultTintColor(theme.base)
Color.clear.ignoresSafeArea(.all)
.if(wallpaperImage != nil && im.secondaryIMFilter == nil) { view in
.if(wallpaperImage != nil) { view in
view.modifier(
ChatViewBackground(image: wallpaperImage!, imageType: wallpaperType, background: backgroundColor, tint: tintColor)
)
}
VStack(spacing: 0) {
ZStack(alignment: .bottomTrailing) {
if userMemberKnockingChat {
ZStack(alignment: .top) {
chatItemsList()
userMemberKnockingTitleBar()
}
} else {
chatItemsList()
}
if let groupInfo = chat.chatInfo.groupInfo, !composeState.message.isEmpty {
GroupMentionsView(im: im, groupInfo: groupInfo, composeState: $composeState, selectedRange: $selectedRange, keyboardVisible: $keyboardVisible)
GroupMentionsView(groupInfo: groupInfo, composeState: $composeState, selectedRange: $selectedRange, keyboardVisible: $keyboardVisible)
}
FloatingButtons(im: im, theme: theme, scrollView: scrollView, chat: chat, loadingMoreItems: $loadingMoreItems, loadingTopItems: $loadingTopItems, requestedTopScroll: $requestedTopScroll, loadingBottomItems: $loadingBottomItems, requestedBottomScroll: $requestedBottomScroll, animatedScrollingInProgress: $animatedScrollingInProgress, listState: scrollView.listState, model: floatingButtonModel, reloadItems: {
mergedItems.boxedValue = MergedItems.create(im, revealedItems)
FloatingButtons(theme: theme, scrollView: scrollView, chat: chat, loadingMoreItems: $loadingMoreItems, loadingTopItems: $loadingTopItems, requestedTopScroll: $requestedTopScroll, loadingBottomItems: $loadingBottomItems, requestedBottomScroll: $requestedBottomScroll, animatedScrollingInProgress: $animatedScrollingInProgress, listState: scrollView.listState, model: floatingButtonModel, reloadItems: {
mergedItems.boxedValue = MergedItems.create(im.reversedChatItems, revealedItems, im.chatState)
scrollView.updateItems(mergedItems.boxedValue.items)
}
)
@ -119,7 +101,6 @@ struct ChatView: View {
let reason = chat.chatInfo.userCantSendReason
ComposeView(
chat: chat,
im: im,
composeState: $composeState,
keyboardVisible: $keyboardVisible,
keyboardHiddenDate: $keyboardHiddenDate,
@ -137,7 +118,7 @@ struct ChatView: View {
}
} else {
SelectedItemsBottomToolbar(
im: im,
chatItems: ItemsModel.shared.reversedChatItems,
selectedChatItems: $selectedChatItems,
chatInfo: chat.chatInfo,
deleteItems: { forAll in
@ -148,7 +129,7 @@ struct ChatView: View {
showArchiveSelectedReports = true
},
moderateItems: {
if case let .group(groupInfo, _) = chat.chatInfo {
if case let .group(groupInfo) = chat.chatInfo {
showModerateSelectedMessagesAlert(groupInfo)
}
},
@ -167,11 +148,7 @@ struct ChatView: View {
}
.background(ToolbarMaterial.material(toolbarMaterial))
}
.navigationTitle(
memberSupportChat == nil
? cInfo.chatViewName
: memberSupportChat?.member?.chatViewName ?? NSLocalizedString("Chat with admins", comment: "chat toolbar")
)
.navigationTitle(cInfo.chatViewName)
.background(theme.colors.background)
.navigationBarTitleDisplayMode(.inline)
.environmentObject(theme)
@ -195,7 +172,7 @@ struct ChatView: View {
archiveReports(chat.chatInfo, selected.sorted(), false, deletedSelectedMessages)
}
}
if case let ChatInfo.group(groupInfo, _) = chat.chatInfo, groupInfo.membership.memberActive {
if case let ChatInfo.group(groupInfo) = chat.chatInfo, groupInfo.membership.memberActive {
Button("For all moderators", role: .destructive) {
if let selected = selectedChatItems {
archiveReports(chat.chatInfo, selected.sorted(), true, deletedSelectedMessages)
@ -204,22 +181,25 @@ struct ChatView: View {
}
}
.appSheet(item: $selectedMember) { member in
if case let .group(groupInfo, _) = chat.chatInfo {
Group {
if case let .group(groupInfo) = chat.chatInfo {
GroupMemberInfoView(
groupInfo: groupInfo,
chat: chat,
groupMember: member,
scrollToItemId: $scrollToItemId,
navigation: true
)
}
}
}
// it should be presented on top level in order to prevent a bug in SwiftUI on iOS 16 related to .focused() modifier in AddGroupMembersView's search field
.appSheet(isPresented: $showAddMembersSheet) {
if case let .group(groupInfo, _) = cInfo {
Group {
if case let .group(groupInfo) = cInfo {
AddGroupMembersView(chat: chat, groupInfo: groupInfo)
}
}
}
.sheet(isPresented: Binding(
get: { !forwardedChatItems.isEmpty },
set: { isPresented in
@ -236,21 +216,6 @@ struct ChatView: View {
ChatItemForwardingView(chatItems: forwardedChatItems, fromChatInfo: chat.chatInfo, composeState: $composeState)
}
}
.appSheet(
isPresented: $showUserSupportChatSheet,
onDismiss: {
if chat.chatInfo.groupInfo?.membership.memberPending ?? false {
chatModel.chatId = nil
}
}
) {
if let groupInfo = cInfo.groupInfo {
SecondaryChatView(
chat: Chat(chatInfo: .group(groupInfo: groupInfo, groupChatScope: userSupportScopeInfo), chatItems: [], chatStats: ChatStats()),
scrollToItemId: $scrollToItemId
)
}
}
.onAppear {
scrollView.listState.onUpdateListener = onChatItemsUpdated
selectedChatItems = nil
@ -266,14 +231,6 @@ struct ChatView: View {
}
}
}
// if this is the main chat of the group with the pending member (knocking)
if case let .group(groupInfo, nil) = chat.chatInfo,
groupInfo.membership.memberPending {
ItemsModel.loadSecondaryChat(chat.id, chatFilter: .groupChatScopeContext(groupScopeInfo: userSupportScopeInfo)) {
showUserSupportChatSheet = true
chatModel.secondaryPendingInviteeChatOpened = true
}
}
}
.onChange(of: chatModel.chatId) { cId in
showChatInfoSheet = false
@ -288,7 +245,7 @@ struct ChatView: View {
initChatView()
theme = buildTheme()
closeSearch()
mergedItems.boxedValue = MergedItems.create(im, revealedItems)
mergedItems.boxedValue = MergedItems.create(im.reversedChatItems, revealedItems, im.chatState)
scrollView.updateItems(mergedItems.boxedValue.items)
if let openAround = chatModel.openAroundItemId, let index = mergedItems.boxedValue.indexInParentItems[openAround] {
@ -305,15 +262,10 @@ struct ChatView: View {
dismiss()
}
}
.onChange(of: chatModel.secondaryPendingInviteeChatOpened) { opened in
if im.secondaryIMFilter != nil && !opened {
dismiss()
}
}
.onChange(of: chatModel.openAroundItemId) { openAround in
if let openAround {
closeSearch()
mergedItems.boxedValue = MergedItems.create(im, revealedItems)
mergedItems.boxedValue = MergedItems.create(im.reversedChatItems, revealedItems, im.chatState)
scrollView.updateItems(mergedItems.boxedValue.items)
chatModel.openAroundItemId = nil
@ -336,8 +288,9 @@ struct ChatView: View {
if chatModel.chatId == cInfo.id && !presentationMode.wrappedValue.isPresented {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
if chatModel.chatId == nil {
im.reversedChatItems = []
im.chatState.clear()
chatModel.chatItemStatuses = [:]
ItemsModel.shared.reversedChatItems = []
ItemsModel.shared.chatState.clear()
chatModel.groupMembers = []
chatModel.groupMembersIndexes.removeAll()
chatModel.membersLoaded = false
@ -350,35 +303,6 @@ struct ChatView: View {
}
.toolbar {
ToolbarItem(placement: .principal) {
if im.secondaryIMFilter == nil {
primaryPrincipalToolbarContent()
} else if !userMemberKnockingChat { // no toolbar while knocking chat, it's unstable on sheet
secondaryPrincipalToolbarContent()
}
}
ToolbarItem(placement: .navigationBarTrailing) {
if im.secondaryIMFilter == nil {
primaryTrailingToolbarContent()
} else if !userMemberKnockingChat {
secondaryTrailingToolbarContent()
}
}
}
.if(im.secondaryIMFilter == nil) { v in
v.onChange(of: scrollToItemId) { itemId in
if let itemId = itemId {
dismissAllSheets(animated: false) {
scrollToItem(itemId)
scrollToItemId = nil
}
}
}
}
}
@inline(__always)
@ViewBuilder private func primaryPrincipalToolbarContent() -> some View {
let cInfo = chat.chatInfo
if selectedChatItems != nil {
SelectedItemsTopToolbar(selectedChatItems: $selectedChatItems)
} else if case let .direct(contact) = cInfo {
@ -399,7 +323,7 @@ struct ChatView: View {
onSearch: { focusSearch() }
)
}
} else if case let .group(groupInfo, _) = cInfo {
} else if case let .group(groupInfo) = cInfo {
Button {
Task { await chatModel.loadGroupMembers(groupInfo) { showChatInfoSheet = true } }
} label: {
@ -412,11 +336,10 @@ struct ChatView: View {
groupInfo: Binding(
get: { groupInfo },
set: { gInfo in
chat.chatInfo = .group(groupInfo: gInfo, groupChatScope: nil)
chat.chatInfo = .group(groupInfo: gInfo)
chat.created = Date.now
}
),
scrollToItemId: $scrollToItemId,
onSearch: { focusSearch() },
localAlias: groupInfo.localAlias
)
@ -425,10 +348,7 @@ struct ChatView: View {
ChatInfoToolbar(chat: chat)
}
}
@inline(__always)
@ViewBuilder private func primaryTrailingToolbarContent() -> some View {
let cInfo = chat.chatInfo
ToolbarItem(placement: .navigationBarTrailing) {
if selectedChatItems != nil {
Button {
withAnimation {
@ -466,7 +386,7 @@ struct ChatView: View {
Image(systemName: "ellipsis")
}
}
case let .group(groupInfo, _):
case let .group(groupInfo):
HStack {
if groupInfo.canAddMembers {
if (chat.chatInfo.incognito) {
@ -498,71 +418,8 @@ struct ChatView: View {
}
}
}
@inline(__always)
@ViewBuilder private func secondaryPrincipalToolbarContent() -> some View {
if selectedChatItems != nil {
SelectedItemsTopToolbar(selectedChatItems: $selectedChatItems)
} else {
switch im.secondaryIMFilter {
case let .groupChatScopeContext(groupScopeInfo):
switch groupScopeInfo {
case let .memberSupport(groupMember_):
if let groupMember = groupMember_ {
MemberSupportChatToolbar(groupMember: groupMember)
} else {
textChatToolbar("Chat with admins")
}
}
case let .msgContentTagContext(contentTag):
switch contentTag {
case .report:
textChatToolbar("Member reports")
default:
EmptyView()
}
case .none:
EmptyView()
}
}
}
@inline(__always)
@ViewBuilder private func secondaryTrailingToolbarContent() -> some View {
if selectedChatItems != nil {
Button {
withAnimation {
selectedChatItems = nil
}
} label: {
Text("Cancel")
}
} else {
searchButton()
}
}
@inline(__always)
private func userMemberKnockingTitleBar() -> some View {
VStack(spacing: 0) {
Text("Chat with admins")
.font(.headline)
.foregroundColor(theme.colors.onBackground)
.padding(.top, 8)
.padding(.bottom, 14)
.frame(maxWidth: .infinity)
.background(ToolbarMaterial.material(toolbarMaterial))
Divider()
}
}
func textChatToolbar(_ text: LocalizedStringKey) -> some View {
Text(text)
.font(.headline)
.lineLimit(1)
.foregroundColor(theme.colors.onBackground)
.frame(width: 220)
}
private func initChatView() {
let cInfo = chat.chatInfo
@ -594,19 +451,19 @@ struct ChatView: View {
floatingButtonModel.updateOnListChange(scrollView.listState)
}
private func scrollToItem(_ itemId: ChatItem.ID) {
private func scrollToItemId(_ itemId: ChatItem.ID) {
Task {
do {
var index = mergedItems.boxedValue.indexInParentItems[itemId]
if index == nil {
let pagination = ChatPagination.around(chatItemId: itemId, count: ChatPagination.PRELOAD_COUNT * 2)
let oldSize = im.reversedChatItems.count
let oldSize = ItemsModel.shared.reversedChatItems.count
let triedToLoad = await loadChatItems(chat, pagination)
if !triedToLoad {
return
}
var repeatsLeft = 50
while oldSize == im.reversedChatItems.count && repeatsLeft > 0 {
while oldSize == ItemsModel.shared.reversedChatItems.count && repeatsLeft > 0 {
try await Task.sleep(nanoseconds: 20_000000)
repeatsLeft -= 1
}
@ -616,7 +473,7 @@ struct ChatView: View {
closeKeyboardAndRun {
Task {
await MainActor.run { animatedScrollingInProgress = true }
await scrollView.scrollToItemAnimated(min(im.reversedChatItems.count - 1, index))
await scrollView.scrollToItemAnimated(min(ItemsModel.shared.reversedChatItems.count - 1, index))
await MainActor.run { animatedScrollingInProgress = false }
}
}
@ -691,13 +548,11 @@ struct ChatView: View {
? (g.size.width - 32)
: (g.size.width - 32) * 0.84
return ChatItemWithMenu(
im: im,
chat: $chat,
index: index,
isLastItem: index == mergedItems.boxedValue.items.count - 1,
chatItem: ci,
scrollToItem: scrollToItem,
scrollToItemId: $scrollToItemId,
scrollToItemId: scrollToItemId,
merged: mergedItem,
maxWidth: maxWidth,
composeState: $composeState,
@ -725,7 +580,7 @@ struct ChatView: View {
}
}
.onChange(of: im.reversedChatItems) { items in
mergedItems.boxedValue = MergedItems.create(im, revealedItems)
mergedItems.boxedValue = MergedItems.create(items, revealedItems, im.chatState)
scrollView.updateItems(mergedItems.boxedValue.items)
if im.itemAdded {
im.itemAdded = false
@ -737,7 +592,7 @@ struct ChatView: View {
}
}
.onChange(of: revealedItems) { revealed in
mergedItems.boxedValue = MergedItems.create(im, revealed)
mergedItems.boxedValue = MergedItems.create(im.reversedChatItems, revealed, im.chatState)
scrollView.updateItems(mergedItems.boxedValue.items)
}
.onChange(of: chat.id) { _ in
@ -772,7 +627,7 @@ struct ChatView: View {
private func updateWithInitiallyLoadedItems() {
if mergedItems.boxedValue.items.isEmpty {
mergedItems.boxedValue = MergedItems.create(im, revealedItems)
mergedItems.boxedValue = MergedItems.create(im.reversedChatItems, revealedItems, ItemsModel.shared.chatState)
}
let unreadIndex = mergedItems.boxedValue.items.lastIndex(where: { $0.hasUnread() })
let unreadItemId: Int64? = if let unreadIndex { mergedItems.boxedValue.items[unreadIndex].newest().item.id } else { nil }
@ -792,8 +647,8 @@ struct ChatView: View {
private func searchTextChanged(_ s: String) {
Task {
await loadChat(chat: chat, im: im, search: s)
mergedItems.boxedValue = MergedItems.create(im, revealedItems)
await loadChat(chat: chat, search: s)
mergedItems.boxedValue = MergedItems.create(im.reversedChatItems, revealedItems, im.chatState)
await MainActor.run {
scrollView.updateItems(mergedItems.boxedValue.items)
}
@ -808,8 +663,79 @@ struct ChatView: View {
}
}
class FloatingButtonModel: ObservableObject {
@Published var unreadAbove: Int = 0
@Published var unreadBelow: Int = 0
@Published var isNearBottom: Bool = true
@Published var date: Date? = nil
@Published var isDateVisible: Bool = false
var hideDateWorkItem: DispatchWorkItem? = nil
func updateOnListChange(_ listState: EndlessScrollView<MergedItem>.ListState) {
let lastVisibleItem = oldestPartiallyVisibleListItemInListStateOrNull(listState)
let unreadBelow = if let lastVisibleItem {
max(0, ItemsModel.shared.chatState.unreadTotal - lastVisibleItem.unreadBefore)
} else {
0
}
let unreadAbove = ItemsModel.shared.chatState.unreadTotal - unreadBelow
let date: Date? =
if let lastVisible = listState.visibleItems.last {
Calendar.current.startOfDay(for: lastVisible.item.oldest().item.meta.itemTs)
} else {
nil
}
// set the counters and date indicator
DispatchQueue.main.async { [weak self] in
guard let it = self else { return }
it.setDate(visibility: true)
it.unreadAbove = unreadAbove
it.unreadBelow = unreadBelow
it.date = date
}
// set floating button indication mode
let nearBottom = listState.firstVisibleItemIndex < 1
if nearBottom != self.isNearBottom {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) { [weak self] in
self?.isNearBottom = nearBottom
}
}
// hide Date indicator after 1 second of no scrolling
hideDateWorkItem?.cancel()
let workItem = DispatchWorkItem { [weak self] in
guard let it = self else { return }
it.setDate(visibility: false)
it.hideDateWorkItem = nil
}
DispatchQueue.main.async { [weak self] in
self?.hideDateWorkItem = workItem
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: workItem)
}
}
func resetDate() {
date = nil
isDateVisible = false
}
private func setDate(visibility isVisible: Bool) {
if isVisible {
if !isNearBottom,
!isDateVisible,
let date, !Calendar.current.isDateInToday(date) {
withAnimation { self.isDateVisible = true }
}
} else if isDateVisible {
withAnimation { self.isDateVisible = false }
}
}
}
private struct FloatingButtons: View {
@ObservedObject var im: ItemsModel
let theme: AppTheme
let scrollView: EndlessScrollView<MergedItem>
let chat: Chat
@ -854,7 +780,7 @@ struct ChatView: View {
.contextMenu {
Button {
Task {
await markChatRead(im, chat)
await markChatRead(chat)
}
} label: {
Label("Mark read", systemImage: "checkmark")
@ -879,7 +805,7 @@ struct ChatView: View {
}
}
.onTapGesture {
if loadingBottomItems || !im.lastItemsLoaded {
if loadingBottomItems || !ItemsModel.shared.lastItemsLoaded {
requestedTopScroll = false
requestedBottomScroll = true
} else {
@ -899,7 +825,7 @@ struct ChatView: View {
}
}
.onChange(of: loadingBottomItems) { loading in
if !loading && requestedBottomScroll && im.lastItemsLoaded {
if !loading && requestedBottomScroll && ItemsModel.shared.lastItemsLoaded {
requestedBottomScroll = false
scrollToBottom()
}
@ -909,9 +835,9 @@ struct ChatView: View {
private func scrollToTopUnread() {
Task {
if !im.chatState.splits.isEmpty {
if !ItemsModel.shared.chatState.splits.isEmpty {
await MainActor.run { loadingMoreItems = true }
await loadChat(chatId: chat.id, im: im, openAroundItemId: nil, clearItems: false)
await loadChat(chatId: chat.id, openAroundItemId: nil, clearItems: false)
await MainActor.run { reloadItems() }
if let index = listState.items.lastIndex(where: { $0.hasUnread() }) {
await MainActor.run { animatedScrollingInProgress = true }
@ -1021,7 +947,7 @@ struct ChatView: View {
private func addMembersButton() -> some View {
Button {
if case let .group(gInfo, _) = chat.chatInfo {
if case let .group(gInfo) = chat.chatInfo {
Task { await chatModel.loadGroupMembers(gInfo) { showAddMembersSheet = true } }
}
} label: {
@ -1031,7 +957,7 @@ struct ChatView: View {
private func groupLinkButton() -> some View {
Button {
if case let .group(gInfo, _) = chat.chatInfo {
if case let .group(gInfo) = chat.chatInfo {
Task {
do {
if let link = try apiGetGroupLink(gInfo.groupId) {
@ -1082,7 +1008,6 @@ struct ChatView: View {
let (validItems, confirmation) = try await apiPlanForwardChatItems(
type: chat.chatInfo.chatType,
id: chat.chatInfo.apiId,
scope: chat.chatInfo.groupChatScope(),
itemIds: Array(selectedChatItems)
)
if let confirmation {
@ -1172,6 +1097,7 @@ struct ChatView: View {
}
func openForwardingSheet(_ items: [Int64]) async {
let im = ItemsModel.shared
var items = Set(items)
var fci = [ChatItem]()
for reversedChatItem in im.reversedChatItems {
@ -1210,11 +1136,11 @@ struct ChatView: View {
private func loadChatItemsUnchecked(_ chat: Chat, _ pagination: ChatPagination) async -> Bool {
await apiLoadMessages(
chat.chatInfo.id,
im,
pagination,
im.chatState,
searchText,
nil,
{ visibleItemIndexesNonReversed(im, scrollView.listState, mergedItems.boxedValue) }
{ visibleItemIndexesNonReversed(scrollView.listState, mergedItems.boxedValue) }
)
return true
}
@ -1226,12 +1152,11 @@ struct ChatView: View {
func onChatItemsUpdated() {
if !mergedItems.boxedValue.isActualState() {
//logger.debug("Items are not actual, waiting for the next update: \(String(describing: mergedItems.boxedValue.splits)) \(im.chatState.splits), \(mergedItems.boxedValue.indexInParentItems.count) vs \(im.reversedChatItems.count)")
//logger.debug("Items are not actual, waiting for the next update: \(String(describing: mergedItems.boxedValue.splits)) \(ItemsModel.shared.chatState.splits), \(mergedItems.boxedValue.indexInParentItems.count) vs \(ItemsModel.shared.reversedChatItems.count)")
return
}
floatingButtonModel.updateOnListChange(scrollView.listState)
preloadIfNeeded(
im,
$allowLoadMoreItems,
$ignoreLoadingRequests,
scrollView.listState,
@ -1245,14 +1170,13 @@ struct ChatView: View {
},
loadLastItems: {
if !loadingMoreItems {
await loadLastItems($loadingMoreItems, loadingBottomItems: $loadingBottomItems, chat, im)
await loadLastItems($loadingMoreItems, loadingBottomItems: $loadingBottomItems, chat)
}
}
)
}
private struct ChatItemWithMenu: View {
@ObservedObject var im: ItemsModel
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var profileRadius = defaultProfileImageCorner
@ -1261,8 +1185,7 @@ struct ChatView: View {
let index: Int
let isLastItem: Bool
let chatItem: ChatItem
let scrollToItem: (ChatItem.ID) -> Void
@Binding var scrollToItemId: ChatItem.ID?
let scrollToItemId: (ChatItem.ID) -> Void
let merged: MergedItem
let maxWidth: CGFloat
@Binding var composeState: ComposeState
@ -1338,6 +1261,8 @@ struct ChatView: View {
}
var body: some View {
let im = ItemsModel.shared
let last = isLastItem ? im.reversedChatItems.last : nil
let listItem = merged.newest()
let item = listItem.item
@ -1381,12 +1306,12 @@ struct ChatView: View {
let (itemIds, unreadMentions) = unreadItemIds(range)
if !itemIds.isEmpty {
waitToMarkRead {
await apiMarkChatItemsRead(im, chat.chatInfo, itemIds, mentionsRead: unreadMentions)
await apiMarkChatItemsRead(chat.chatInfo, itemIds, mentionsRead: unreadMentions)
}
}
} else if chatItem.isRcvNew {
waitToMarkRead {
await apiMarkChatItemsRead(im, chat.chatInfo, [chatItem.id], mentionsRead: chatItem.meta.userMention ? 1 : 0)
await apiMarkChatItemsRead(chat.chatInfo, [chatItem.id], mentionsRead: chatItem.meta.userMention ? 1 : 0)
}
}
}
@ -1408,6 +1333,7 @@ struct ChatView: View {
}
private func unreadItemIds(_ range: ClosedRange<Int>) -> ([ChatItem.ID], Int) {
let im = ItemsModel.shared
var unreadItems: [ChatItem.ID] = []
var unreadMentions: Int = 0
@ -1620,10 +1546,8 @@ struct ChatView: View {
}
ChatItemView(
chat: chat,
im: im,
chatItem: ci,
scrollToItem: scrollToItem,
scrollToItemId: $scrollToItemId,
scrollToItemId: scrollToItemId,
maxWidth: maxWidth,
allowMenu: $allowMenu
)
@ -1663,7 +1587,7 @@ struct ChatView: View {
self.archivingReports = []
}
}
if case let ChatInfo.group(groupInfo, _) = chat.chatInfo, groupInfo.membership.memberActive {
if case let ChatInfo.group(groupInfo) = chat.chatInfo, groupInfo.membership.memberActive {
Button("For all moderators", role: .destructive) {
if let reports = self.archivingReports {
archiveReports(chat.chatInfo, reports.sorted(), true)
@ -1712,7 +1636,7 @@ struct ChatView: View {
})
}
switch chat.chatInfo {
case let .group(groupInfo, _):
case let .group(groupInfo):
v.contextMenu {
ReactionContextMenu(
groupInfo: groupInfo,
@ -1735,7 +1659,7 @@ struct ChatView: View {
@ViewBuilder
private func menu(_ ci: ChatItem, _ range: ClosedRange<Int>?, live: Bool) -> some View {
if case let .group(gInfo, _) = chat.chatInfo, ci.isReport, ci.meta.itemDeleted == nil {
if case let .group(gInfo) = chat.chatInfo, ci.isReport, ci.meta.itemDeleted == nil {
if ci.chatDir != .groupSnd, gInfo.membership.memberRole >= .moderator {
archiveReportButton(ci)
}
@ -1794,7 +1718,7 @@ struct ChatView: View {
if let (groupInfo, _) = ci.memberToModerate(chat.chatInfo) {
moderateButton(ci, groupInfo)
} else if ci.meta.itemDeleted == nil && chat.groupFeatureEnabled(.reports),
case let .group(gInfo, _) = chat.chatInfo,
case let .group(gInfo) = chat.chatInfo,
gInfo.membership.memberRole == .member
&& !live
&& composeState.voiceMessageRecordingState == .noRecording {
@ -1905,7 +1829,6 @@ struct ChatView: View {
let chatItem = try await apiChatItemReaction(
type: cInfo.chatType,
id: cInfo.apiId,
scope: cInfo.groupChatScope(),
itemId: ci.id,
add: add,
reaction: reaction
@ -2019,11 +1942,11 @@ struct ChatView: View {
Task {
do {
let cInfo = chat.chatInfo
let ciInfo = try await apiGetChatItemInfo(type: cInfo.chatType, id: cInfo.apiId, scope: cInfo.groupChatScope(), itemId: ci.id)
let ciInfo = try await apiGetChatItemInfo(type: cInfo.chatType, id: cInfo.apiId, itemId: ci.id)
await MainActor.run {
chatItemInfo = ciInfo
}
if case let .group(gInfo, _) = chat.chatInfo {
if case let .group(gInfo) = chat.chatInfo {
await m.loadGroupMembers(gInfo)
}
} catch let error {
@ -2077,13 +2000,13 @@ struct ChatView: View {
private func deleteButton(_ ci: ChatItem, label: LocalizedStringKey = "Delete") -> Button<some View> {
Button(role: .destructive) {
if !revealed,
let currIndex = m.getChatItemIndex(im, ci),
let currIndex = m.getChatItemIndex(ci),
let ciCategory = ci.mergeCategory {
let (prevHidden, _) = m.getPrevShownChatItem(currIndex, ciCategory)
if let range = itemsRange(currIndex, prevHidden) {
var itemIds: [Int64] = []
for i in range {
itemIds.append(im.reversedChatItems[i].id)
itemIds.append(ItemsModel.shared.reversedChatItems[i].id)
}
showDeleteMessages = true
deletingItems = itemIds
@ -2221,12 +2144,12 @@ struct ChatView: View {
selectedChatItems = selectedChatItems ?? []
var itemIds: [Int64] = []
if !revealed,
let currIndex = m.getChatItemIndex(im, ci),
let currIndex = m.getChatItemIndex(ci),
let ciCategory = ci.mergeCategory {
let (prevHidden, _) = m.getPrevShownChatItem(currIndex, ciCategory)
if let range = itemsRange(currIndex, prevHidden) {
for i in range {
itemIds.append(im.reversedChatItems[i].id)
itemIds.append(ItemsModel.shared.reversedChatItems[i].id)
}
} else {
itemIds.append(ci.id)
@ -2260,7 +2183,6 @@ struct ChatView: View {
try await apiDeleteChatItems(
type: chat.chatInfo.chatType,
id: chat.chatInfo.apiId,
scope: chat.chatInfo.groupChatScope(),
itemIds: [di.id],
mode: mode
)
@ -2277,7 +2199,6 @@ struct ChatView: View {
if deletedItem.isActiveReport {
m.decreaseGroupReportsCounter(chat.chatInfo.id)
}
m.updateChatInfo(itemDeletion.deletedChatItem.chatInfo)
}
}
}
@ -2316,14 +2237,14 @@ struct ChatView: View {
if searchIsNotBlank {
goToItemInnerButton(alignStart, "magnifyingglass", touchInProgress: touchInProgress) {
closeKeyboardAndRun {
im.loadOpenChatNoWait(chat.id, chatItem.id)
ItemsModel.shared.loadOpenChatNoWait(chat.id, chatItem.id)
}
}
} else if let chatTypeApiIdMsgId {
goToItemInnerButton(alignStart, "arrow.right", touchInProgress: touchInProgress) {
closeKeyboardAndRun {
let (chatType, apiId, msgId) = chatTypeApiIdMsgId
im.loadOpenChatNoWait("\(chatType.rawValue)\(apiId)", msgId)
ItemsModel.shared.loadOpenChatNoWait("\(chatType.rawValue)\(apiId)", msgId)
}
}
}
@ -2350,84 +2271,6 @@ struct ChatView: View {
}
}
class FloatingButtonModel: ObservableObject {
@ObservedObject var im: ItemsModel
public init(im: ItemsModel) {
self.im = im
}
@Published var unreadAbove: Int = 0
@Published var unreadBelow: Int = 0
@Published var isNearBottom: Bool = true
@Published var date: Date? = nil
@Published var isDateVisible: Bool = false
var hideDateWorkItem: DispatchWorkItem? = nil
func updateOnListChange(_ listState: EndlessScrollView<MergedItem>.ListState) {
let lastVisibleItem = oldestPartiallyVisibleListItemInListStateOrNull(listState)
let unreadBelow = if let lastVisibleItem {
max(0, im.chatState.unreadTotal - lastVisibleItem.unreadBefore)
} else {
0
}
let unreadAbove = im.chatState.unreadTotal - unreadBelow
let date: Date? =
if let lastVisible = listState.visibleItems.last {
Calendar.current.startOfDay(for: lastVisible.item.oldest().item.meta.itemTs)
} else {
nil
}
// set the counters and date indicator
DispatchQueue.main.async { [weak self] in
guard let it = self else { return }
it.setDate(visibility: true)
it.unreadAbove = unreadAbove
it.unreadBelow = unreadBelow
it.date = date
}
// set floating button indication mode
let nearBottom = listState.firstVisibleItemIndex < 1
if nearBottom != self.isNearBottom {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) { [weak self] in
self?.isNearBottom = nearBottom
}
}
// hide Date indicator after 1 second of no scrolling
hideDateWorkItem?.cancel()
let workItem = DispatchWorkItem { [weak self] in
guard let it = self else { return }
it.setDate(visibility: false)
it.hideDateWorkItem = nil
}
DispatchQueue.main.async { [weak self] in
self?.hideDateWorkItem = workItem
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: workItem)
}
}
func resetDate() {
date = nil
isDateVisible = false
}
private func setDate(visibility isVisible: Bool) {
if isVisible {
if !isNearBottom,
!isDateVisible,
let date, !Calendar.current.isDateInToday(date) {
withAnimation { self.isDateVisible = true }
}
} else if isDateVisible {
withAnimation { self.isDateVisible = false }
}
}
}
private func broadcastDeleteButtonText(_ chat: Chat) -> LocalizedStringKey {
chat.chatInfo.featureEnabled(.fullDelete) ? "Delete for everyone" : "Mark deleted for everyone"
}
@ -2449,7 +2292,6 @@ private func deleteMessages(_ chat: Chat, _ deletingItems: [Int64], _ mode: CIDe
try await apiDeleteChatItems(
type: chatInfo.chatType,
id: chatInfo.apiId,
scope: chatInfo.groupChatScope(),
itemIds: itemIds,
mode: mode
)
@ -2467,9 +2309,6 @@ private func deleteMessages(_ chat: Chat, _ deletingItems: [Int64], _ mode: CIDe
ChatModel.shared.decreaseGroupReportsCounter(chat.chatInfo.id)
}
}
if let updatedChatInfo = deletedItems.last?.deletedChatItem.chatInfo {
ChatModel.shared.updateChatInfo(updatedChatInfo)
}
}
await onSuccess()
} catch {
@ -2501,9 +2340,6 @@ func archiveReports(_ chatInfo: ChatInfo, _ itemIds: [Int64], _ forAll: Bool, _
ChatModel.shared.decreaseGroupReportsCounter(chatInfo.id)
}
}
if let updatedChatInfo = deleted.last?.deletedChatItem.chatInfo {
ChatModel.shared.updateChatInfo(updatedChatInfo)
}
}
await onSuccess()
} catch {
@ -2517,7 +2353,7 @@ private func buildTheme() -> AppTheme {
if let cId = ChatModel.shared.chatId, let chat = ChatModel.shared.getChat(cId) {
let perChatTheme = if case let .direct(contact) = chat.chatInfo {
contact.uiThemes?.preferredMode(!AppTheme.shared.colors.isLight)
} else if case let .group(groupInfo, _) = chat.chatInfo {
} else if case let .group(groupInfo) = chat.chatInfo {
groupInfo.uiThemes?.preferredMode(!AppTheme.shared.colors.isLight)
} else {
nil as ThemeModeOverride?
@ -2670,7 +2506,7 @@ func updateChatSettings(_ chat: Chat, chatSettings: ChatSettings) {
case var .direct(contact):
contact.chatSettings = chatSettings
ChatModel.shared.updateContact(contact)
case var .group(groupInfo, _):
case var .group(groupInfo):
groupInfo.chatSettings = chatSettings
ChatModel.shared.updateGroup(groupInfo)
default: ()
@ -2687,8 +2523,7 @@ struct ChatView_Previews: PreviewProvider {
static var previews: some View {
let chatModel = ChatModel()
chatModel.chatId = "@1"
let im = ItemsModel.shared
im.reversedChatItems = [
ItemsModel.shared.reversedChatItems = [
ChatItem.getSample(1, .directSnd, .now, "hello"),
ChatItem.getSample(2, .directRcv, .now, "hi"),
ChatItem.getSample(3, .directRcv, .now, "hi there"),
@ -2700,13 +2535,7 @@ struct ChatView_Previews: PreviewProvider {
ChatItem.getSample(9, .directSnd, .now, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
]
@State var showChatInfo = false
return ChatView(
chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []),
im: im,
mergedItems: BoxedValue(MergedItems.create(im, [])),
floatingButtonModel: FloatingButtonModel(im: im),
scrollToItemId: Binding.constant(nil)
)
return ChatView(chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []))
.environmentObject(chatModel)
}
}

View file

@ -323,7 +323,6 @@ struct ComposeView: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@ObservedObject var chat: Chat
@ObservedObject var im: ItemsModel
@Binding var composeState: ComposeState
@Binding var keyboardVisible: Bool
@Binding var keyboardHiddenDate: Date
@ -357,20 +356,6 @@ struct ComposeView: View {
var body: some View {
VStack(spacing: 0) {
Divider()
if let groupInfo = chat.chatInfo.groupInfo,
case let .groupChatScopeContext(groupScopeInfo) = im.secondaryIMFilter,
case let .memberSupport(member) = groupScopeInfo,
let member = member,
member.memberPending,
composeState.contextItem == .noContextItem,
composeState.noPreview {
ContextPendingMemberActionsView(
groupInfo: groupInfo,
member: member
)
Divider()
}
if chat.chatInfo.contact?.nextSendGrpInv ?? false {
ContextInvitingContactMemberView()
Divider()
@ -381,8 +366,8 @@ struct ComposeView: View {
Divider()
}
// preference checks should match checks in forwarding list
let simplexLinkProhibited = im.secondaryIMFilter == nil && hasSimplexLink && !chat.groupFeatureEnabled(.simplexLinks)
let fileProhibited = im.secondaryIMFilter == nil && composeState.attachmentPreview && !chat.groupFeatureEnabled(.files)
let simplexLinkProhibited = hasSimplexLink && !chat.groupFeatureEnabled(.simplexLinks)
let fileProhibited = composeState.attachmentPreview && !chat.groupFeatureEnabled(.files)
let voiceProhibited = composeState.voicePreview && !chat.chatInfo.featureEnabled(.voice)
if simplexLinkProhibited {
msgNotAllowedView("SimpleX links not allowed", icon: "link")
@ -412,8 +397,7 @@ struct ComposeView: View {
.padding(.bottom, 16)
.padding(.leading, 12)
.tint(theme.colors.primary)
if im.secondaryIMFilter == nil,
case let .group(g, _) = chat.chatInfo,
if case let .group(g) = chat.chatInfo,
!g.fullGroupPreferences.files.on(for: g.membership) {
b.disabled(true).onTapGesture {
AlertManager.shared.showAlertMsg(
@ -955,7 +939,6 @@ struct ComposeView: View {
let chatItem = try await apiUpdateChatItem(
type: chat.chatInfo.chatType,
id: chat.chatInfo.apiId,
scope: chat.chatInfo.groupChatScope(),
itemId: ei.id,
updatedMessage: UpdatedMessage(msgContent: mc, mentions: composeState.memberMentions),
live: live
@ -1018,9 +1001,9 @@ struct ComposeView: View {
reportReason: reportReason,
reportText: msgText
) {
if showReportsInSupportChatAlertDefault.get() {
await MainActor.run {
showReportsInSupportChatAlert()
for chatItem in chatItems {
chatModel.addChatItem(chat.chatInfo, chatItem)
}
}
return chatItems.first
@ -1029,26 +1012,6 @@ struct ComposeView: View {
return nil
}
func showReportsInSupportChatAlert() {
showAlert(
NSLocalizedString("Report sent to moderators", comment: "alert title"),
message: NSLocalizedString("You can view your reports in Chat with admins.", comment: "alert message"),
actions: {[
UIAlertAction(
title: NSLocalizedString("Don't show again", comment: "alert action"),
style: .default,
handler: { _ in
showReportsInSupportChatAlertDefault.set(false)
}
),
UIAlertAction(
title: NSLocalizedString("Ok", comment: "alert action"),
style: .default
)
]}
)
}
func send(_ mc: MsgContent, quoted: Int64?, file: CryptoFile? = nil, live: Bool = false, ttl: Int?, mentions: [String: Int64]) async -> ChatItem? {
await send(
[ComposedMessage(fileSource: file, quotedItemId: quoted, msgContent: mc, mentions: mentions)],
@ -1063,7 +1026,6 @@ struct ComposeView: View {
: await apiSendMessages(
type: chat.chatInfo.chatType,
id: chat.chatInfo.apiId,
scope: chat.chatInfo.groupChatScope(),
live: live,
ttl: ttl,
composedMessages: msgs
@ -1088,10 +1050,8 @@ struct ComposeView: View {
if let chatItems = await apiForwardChatItems(
toChatType: chat.chatInfo.chatType,
toChatId: chat.chatInfo.apiId,
toScope: chat.chatInfo.groupChatScope(),
fromChatType: fromChatInfo.chatType,
fromChatId: fromChatInfo.apiId,
fromScope: fromChatInfo.groupChatScope(),
itemIds: forwardedItems.map { $0.id },
ttl: ttl
) {
@ -1312,14 +1272,12 @@ struct ComposeView: View {
struct ComposeView_Previews: PreviewProvider {
static var previews: some View {
let chat = Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: [])
let im = ItemsModel.shared
@State var composeState = ComposeState(message: "hello")
@State var selectedRange = NSRange()
return Group {
ComposeView(
chat: chat,
im: im,
composeState: $composeState,
keyboardVisible: Binding.constant(true),
keyboardHiddenDate: Binding.constant(Date.now),
@ -1328,7 +1286,6 @@ struct ComposeView_Previews: PreviewProvider {
.environmentObject(ChatModel())
ComposeView(
chat: chat,
im: im,
composeState: $composeState,
keyboardVisible: Binding.constant(true),
keyboardHiddenDate: Binding.constant(Date.now),

View file

@ -1,108 +0,0 @@
//
// ContextPendingMemberActionsView.swift
// SimpleX (iOS)
//
// Created by spaced4ndy on 02.05.2025.
// Copyright © 2025 SimpleX Chat. All rights reserved.
//
import SwiftUI
import SimpleXChat
struct ContextPendingMemberActionsView: View {
@EnvironmentObject var theme: AppTheme
@Environment(\.dismiss) var dismiss
var groupInfo: GroupInfo
var member: GroupMember
var body: some View {
HStack(spacing: 0) {
ZStack {
Text("Reject")
.foregroundColor(.red)
}
.frame(maxWidth: .infinity)
.contentShape(Rectangle())
.onTapGesture {
showRejectMemberAlert(groupInfo, member, dismiss: dismiss)
}
ZStack {
Text("Accept")
.foregroundColor(theme.colors.primary)
}
.frame(maxWidth: .infinity)
.contentShape(Rectangle())
.onTapGesture {
showAcceptMemberAlert(groupInfo, member, dismiss: dismiss)
}
}
.frame(minHeight: 54)
.frame(maxWidth: .infinity)
.background(.thinMaterial)
}
}
func showRejectMemberAlert(_ groupInfo: GroupInfo, _ member: GroupMember, dismiss: DismissAction? = nil) {
showAlert(
title: NSLocalizedString("Reject member?", comment: "alert title"),
buttonTitle: "Reject",
buttonAction: { removeMember(groupInfo, member, dismiss: dismiss) },
cancelButton: true
)
}
func showAcceptMemberAlert(_ groupInfo: GroupInfo, _ member: GroupMember, dismiss: DismissAction? = nil) {
showAlert(
NSLocalizedString("Accept member", comment: "alert title"),
message: NSLocalizedString("Member will join the group, accept member?", comment: "alert message"),
actions: {[
UIAlertAction(
title: NSLocalizedString("Accept as member", comment: "alert action"),
style: .default,
handler: { _ in
acceptMember(groupInfo, member, .member, dismiss: dismiss)
}
),
UIAlertAction(
title: NSLocalizedString("Accept as observer", comment: "alert action"),
style: .default,
handler: { _ in
acceptMember(groupInfo, member, .observer, dismiss: dismiss)
}
),
UIAlertAction(
title: NSLocalizedString("Cancel", comment: "alert action"),
style: .default
)
]}
)
}
func acceptMember(_ groupInfo: GroupInfo, _ member: GroupMember, _ role: GroupMemberRole, dismiss: DismissAction? = nil) {
Task {
do {
let (gInfo, acceptedMember) = try await apiAcceptMember(groupInfo.groupId, member.groupMemberId, role)
await MainActor.run {
_ = ChatModel.shared.upsertGroupMember(gInfo, acceptedMember)
ChatModel.shared.updateGroup(gInfo)
dismiss?()
}
} catch let error {
logger.error("apiAcceptMember error: \(responseError(error))")
await MainActor.run {
showAlert(
NSLocalizedString("Error accepting member", comment: "alert title"),
message: responseError(error)
)
}
}
}
}
#Preview {
ContextPendingMemberActionsView(
groupInfo: GroupInfo.sampleData,
member: GroupMember.sampleData
)
}

View file

@ -78,12 +78,6 @@ struct AddGroupMembersViewCommon: View {
let count = selectedContacts.count
Section {
if creatingGroup {
MemberAdmissionButton(
groupInfo: $groupInfo,
admission: groupInfo.groupProfile.memberAdmission_,
currentAdmission: groupInfo.groupProfile.memberAdmission_,
creatingGroup: true
)
GroupPreferencesButton(
groupInfo: $groupInfo,
preferences: groupInfo.fullGroupPreferences,

View file

@ -17,7 +17,6 @@ struct GroupChatInfoView: View {
@Environment(\.dismiss) var dismiss: DismissAction
@ObservedObject var chat: Chat
@Binding var groupInfo: GroupInfo
@Binding var scrollToItemId: ChatItem.ID?
var onSearch: () -> Void
@State var localAlias: String
@FocusState private var aliasTextFieldFocused: Bool
@ -89,24 +88,6 @@ struct GroupChatInfoView: View {
.listRowSeparator(.hidden)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
Section {
if groupInfo.canAddMembers && groupInfo.businessChat == nil {
groupLinkButton()
}
if groupInfo.businessChat == nil && groupInfo.membership.memberRole >= .moderator {
memberSupportButton()
}
if groupInfo.canModerate {
GroupReportsChatNavLink(chat: chat, scrollToItemId: $scrollToItemId)
}
if groupInfo.membership.memberActive
&& (groupInfo.membership.memberRole < .moderator || groupInfo.membership.supportChat != nil) {
UserSupportChatNavLink(chat: chat, groupInfo: groupInfo, scrollToItemId: $scrollToItemId)
}
} header: {
Text("")
}
Section {
if groupInfo.isOwner && groupInfo.businessChat == nil {
editGroupButton()
@ -115,6 +96,19 @@ struct GroupChatInfoView: View {
addOrEditWelcomeMessage()
}
GroupPreferencesButton(groupInfo: $groupInfo, preferences: groupInfo.fullGroupPreferences, currentPreferences: groupInfo.fullGroupPreferences)
if members.filter({ $0.wrapped.memberCurrent }).count <= SMALL_GROUPS_RCPS_MEM_LIMIT {
sendReceiptsOption()
} else {
sendReceiptsOptionDisabled()
}
NavigationLink {
ChatWallpaperEditorSheet(chat: chat)
} label: {
Label("Chat theme", systemImage: "photo")
}
} header: {
Text("")
} footer: {
let label: LocalizedStringKey = (
groupInfo.businessChat == nil
@ -126,16 +120,6 @@ struct GroupChatInfoView: View {
}
Section {
if members.filter({ $0.wrapped.memberCurrent }).count <= SMALL_GROUPS_RCPS_MEM_LIMIT {
sendReceiptsOption()
} else {
sendReceiptsOptionDisabled()
}
NavigationLink {
ChatWallpaperEditorSheet(chat: chat)
} label: {
Label("Chat theme", systemImage: "photo")
}
ChatTTLOption(chat: chat, progressIndicator: $progressIndicator)
} footer: {
Text("Delete chat messages from your device.")
@ -143,6 +127,9 @@ struct GroupChatInfoView: View {
Section(header: Text("\(members.count + 1) members").foregroundColor(theme.colors.secondary)) {
if groupInfo.canAddMembers {
if groupInfo.businessChat == nil {
groupLinkButton()
}
if (chat.chatInfo.incognito) {
Label("Invite members", systemImage: "plus")
.foregroundColor(Color(uiColor: .tertiaryLabel))
@ -157,16 +144,9 @@ struct GroupChatInfoView: View {
let filteredMembers = s == ""
? members
: members.filter { $0.wrapped.localAliasAndFullName.localizedLowercase.contains(s) }
MemberRowView(
chat: chat,
groupInfo: groupInfo,
groupMember: GMember(groupInfo.membership),
scrollToItemId: $scrollToItemId,
user: true,
alert: $alert
)
MemberRowView(chat: chat, groupInfo: groupInfo, groupMember: GMember(groupInfo.membership), user: true, alert: $alert)
ForEach(filteredMembers) { member in
MemberRowView(chat: chat, groupInfo: groupInfo, groupMember: member, scrollToItemId: $scrollToItemId, alert: $alert)
MemberRowView(chat: chat, groupInfo: groupInfo, groupMember: member, alert: $alert)
}
}
@ -175,7 +155,7 @@ struct GroupChatInfoView: View {
if groupInfo.canDelete {
deleteGroupButton()
}
if groupInfo.membership.memberCurrentOrPending {
if groupInfo.membership.memberCurrent {
leaveGroupButton()
}
}
@ -373,7 +353,6 @@ struct GroupChatInfoView: View {
var chat: Chat
var groupInfo: GroupInfo
@ObservedObject var groupMember: GMember
@Binding var scrollToItemId: ChatItem.ID?
@EnvironmentObject var theme: AppTheme
var user: Bool = false
@Binding var alert: GroupChatInfoViewAlert?
@ -436,7 +415,7 @@ struct GroupChatInfoView: View {
}
private func memberInfoView() -> some View {
GroupMemberInfoView(groupInfo: groupInfo, chat: chat, groupMember: groupMember, scrollToItemId: $scrollToItemId)
GroupMemberInfoView(groupInfo: groupInfo, chat: chat, groupMember: groupMember)
.navigationBarHidden(false)
}
@ -544,89 +523,6 @@ struct GroupChatInfoView: View {
.navigationBarTitleDisplayMode(.large)
}
struct UserSupportChatNavLink: View {
@ObservedObject var chat: Chat
@EnvironmentObject var theme: AppTheme
var groupInfo: GroupInfo
@EnvironmentObject var chatModel: ChatModel
@Binding var scrollToItemId: ChatItem.ID?
@State private var navLinkActive = false
var body: some View {
let scopeInfo: GroupChatScopeInfo = .memberSupport(groupMember_: nil)
NavigationLink(isActive: $navLinkActive) {
SecondaryChatView(
chat: Chat(chatInfo: .group(groupInfo: groupInfo, groupChatScope: scopeInfo), chatItems: [], chatStats: ChatStats()),
scrollToItemId: $scrollToItemId
)
} label: {
HStack {
Label("Chat with admins", systemImage: chat.supportUnreadCount > 0 ? "flag.fill" : "flag")
Spacer()
if chat.supportUnreadCount > 0 {
UnreadBadge(count: chat.supportUnreadCount, color: theme.colors.primary)
}
}
}
.onChange(of: navLinkActive) { active in
if active {
ItemsModel.loadSecondaryChat(groupInfo.id, chatFilter: .groupChatScopeContext(groupScopeInfo: scopeInfo))
}
}
}
}
private func memberSupportButton() -> some View {
NavigationLink {
MemberSupportView(groupInfo: groupInfo, scrollToItemId: $scrollToItemId)
.navigationBarTitle("Chats with members")
.modifier(ThemedBackground())
.navigationBarTitleDisplayMode(.large)
} label: {
HStack {
Label(
"Chats with members",
systemImage: chat.supportUnreadCount > 0 ? "flag.fill" : "flag"
)
Spacer()
if chat.supportUnreadCount > 0 {
UnreadBadge(count: chat.supportUnreadCount, color: theme.colors.primary)
}
}
}
}
struct GroupReportsChatNavLink: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@State private var navLinkActive = false
@ObservedObject var chat: Chat
@Binding var scrollToItemId: ChatItem.ID?
var body: some View {
NavigationLink(isActive: $navLinkActive) {
SecondaryChatView(chat: chat, scrollToItemId: $scrollToItemId)
} label: {
HStack {
Label {
Text("Member reports")
} icon: {
Image(systemName: chat.chatStats.reportsCount > 0 ? "flag.fill" : "flag").foregroundColor(.red)
}
Spacer()
if chat.chatStats.reportsCount > 0 {
UnreadBadge(count: chat.chatStats.reportsCount, color: .red)
}
}
}
.onChange(of: navLinkActive) { active in
if active {
ItemsModel.loadSecondaryChat(chat.id, chatFilter: .msgContentTagContext(contentTag: .report))
}
}
}
}
private func editGroupButton() -> some View {
NavigationLink {
GroupProfileView(
@ -787,34 +683,24 @@ struct GroupChatInfoView: View {
title: Text("Remove member?"),
message: Text(messageLabel),
primaryButton: .destructive(Text("Remove")) {
removeMember(groupInfo, mem)
},
secondaryButton: .cancel()
)
}
}
func removeMember(_ groupInfo: GroupInfo, _ mem: GroupMember, dismiss: DismissAction? = nil) {
Task {
do {
let (updatedGroupInfo, updatedMembers) = try await apiRemoveMembers(groupInfo.groupId, [mem.groupMemberId])
let updatedMembers = try await apiRemoveMembers(groupInfo.groupId, [mem.groupMemberId])
await MainActor.run {
ChatModel.shared.updateGroup(updatedGroupInfo)
updatedMembers.forEach { updatedMember in
_ = ChatModel.shared.upsertGroupMember(updatedGroupInfo, updatedMember)
_ = chatModel.upsertGroupMember(groupInfo, updatedMember)
}
dismiss?()
}
} catch let error {
logger.error("apiRemoveMembers error: \(responseError(error))")
await MainActor.run {
showAlert(
NSLocalizedString("Error removing member", comment: "alert title"),
message: responseError(error)
let a = getErrorAlert(error, "Error removing member")
alert = .error(title: a.title, error: a.message)
}
}
},
secondaryButton: .cancel()
)
}
}
}
}
func deleteGroupAlertMessage(_ groupInfo: GroupInfo) -> Text {
@ -910,7 +796,6 @@ struct GroupChatInfoView_Previews: PreviewProvider {
GroupChatInfoView(
chat: Chat(chatInfo: ChatInfo.sampleData.group, chatItems: []),
groupInfo: Binding.constant(GroupInfo.sampleData),
scrollToItemId: Binding.constant(nil),
onSearch: {},
localAlias: ""
)

View file

@ -16,7 +16,6 @@ struct GroupMemberInfoView: View {
@State var groupInfo: GroupInfo
@ObservedObject var chat: Chat
@ObservedObject var groupMember: GMember
@Binding var scrollToItemId: ChatItem.ID?
var navigation: Bool = false
@State private var connectionStats: ConnectionStats? = nil
@State private var connectionCode: String? = nil
@ -104,10 +103,6 @@ struct GroupMemberInfoView: View {
if member.memberActive {
Section {
if groupInfo.membership.memberRole >= .moderator
&& (member.memberRole < .moderator || member.supportChat != nil) {
MemberInfoSupportChatNavLink(groupInfo: groupInfo, member: groupMember, scrollToItemId: $scrollToItemId)
}
if let code = connectionCode { verifyCodeButton(code) }
if let connStats = connectionStats,
connStats.ratchetSyncAllowed {
@ -283,7 +278,7 @@ struct GroupMemberInfoView: View {
}
}
.onChange(of: chat.chatInfo) { c in
if case let .group(gI, _) = chat.chatInfo {
if case let .group(gI) = chat.chatInfo {
groupInfo = gI
}
}
@ -479,31 +474,6 @@ struct GroupMemberInfoView: View {
.frame(maxWidth: .infinity, alignment: .center)
}
struct MemberInfoSupportChatNavLink: View {
@EnvironmentObject var theme: AppTheme
var groupInfo: GroupInfo
var member: GMember
@Binding var scrollToItemId: ChatItem.ID?
@State private var navLinkActive = false
var body: some View {
let scopeInfo: GroupChatScopeInfo = .memberSupport(groupMember_: member.wrapped)
NavigationLink(isActive: $navLinkActive) {
SecondaryChatView(
chat: Chat(chatInfo: .group(groupInfo: groupInfo, groupChatScope: scopeInfo), chatItems: [], chatStats: ChatStats()),
scrollToItemId: $scrollToItemId
)
} label: {
Label("Chat with member", systemImage: "flag")
}
.onChange(of: navLinkActive) { active in
if active {
ItemsModel.loadSecondaryChat(groupInfo.id, chatFilter: .groupChatScopeContext(groupScopeInfo: scopeInfo))
}
}
}
}
private func verifyCodeButton(_ code: String) -> some View {
let member = groupMember.wrapped
return NavigationLink {
@ -640,11 +610,10 @@ struct GroupMemberInfoView: View {
primaryButton: .destructive(Text("Remove")) {
Task {
do {
let (updatedGroupInfo, updatedMembers) = try await apiRemoveMembers(groupInfo.groupId, [mem.groupMemberId])
let updatedMembers = try await apiRemoveMembers(groupInfo.groupId, [mem.groupMemberId])
await MainActor.run {
chatModel.updateGroup(updatedGroupInfo)
updatedMembers.forEach { updatedMember in
_ = chatModel.upsertGroupMember(updatedGroupInfo, updatedMember)
_ = chatModel.upsertGroupMember(groupInfo, updatedMember)
}
dismiss()
}
@ -852,8 +821,7 @@ struct GroupMemberInfoView_Previews: PreviewProvider {
GroupMemberInfoView(
groupInfo: GroupInfo.sampleData,
chat: Chat.sampleData,
groupMember: GMember.sampleData,
scrollToItemId: Binding.constant(nil)
groupMember: GMember.sampleData
)
}
}

View file

@ -17,7 +17,6 @@ let MAX_VISIBLE_MEMBER_ROWS: CGFloat = 4.8
struct GroupMentionsView: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
var im: ItemsModel
var groupInfo: GroupInfo
@Binding var composeState: ComposeState
@Binding var selectedRange: NSRange
@ -95,30 +94,11 @@ struct GroupMentionsView: View {
}
}
func contextMemberFilter(_ member: GroupMember) -> Bool {
switch im.secondaryIMFilter {
case nil:
return true
case let .groupChatScopeContext(groupScopeInfo):
switch (groupScopeInfo) {
case let .memberSupport(groupMember_):
if let scopeMember = groupMember_ {
return member.memberRole >= .moderator || member.groupMemberId == scopeMember.groupMemberId
} else {
return member.memberRole >= .moderator
}
}
case .msgContentTagContext:
return false
}
}
private func filteredMembers() -> [GMember] {
let s = mentionName.lowercased()
return sortedMembers.filter {
contextMemberFilter($0.wrapped)
&& (s.isEmpty || $0.wrapped.localAliasAndFullName.localizedLowercase.contains(s))
}
return s.isEmpty
? sortedMembers
: sortedMembers.filter { $0.wrapped.localAliasAndFullName.localizedLowercase.contains(s) }
}
private func messageChanged(_ msg: String, _ parsedMsg: [FormattedText], _ range: NSRange) {

View file

@ -30,14 +30,6 @@ struct GroupPreferencesView: View {
let saveText: LocalizedStringKey = creatingGroup ? "Save" : "Save and notify group members"
VStack {
List {
Section {
MemberAdmissionButton(
groupInfo: $groupInfo,
admission: groupInfo.groupProfile.memberAdmission_,
currentAdmission: groupInfo.groupProfile.memberAdmission_,
creatingGroup: creatingGroup
)
}
featureSection(.timedMessages, $preferences.timedMessages.enable)
featureSection(.fullDelete, $preferences.fullDelete.enable)
featureSection(.directMessages, $preferences.directMessages.enable, $preferences.directMessages.role)
@ -148,66 +140,6 @@ struct GroupPreferencesView: View {
}
}
struct MemberAdmissionButton: View {
@Binding var groupInfo: GroupInfo
@State var admission: GroupMemberAdmission
@State var currentAdmission: GroupMemberAdmission
var creatingGroup: Bool = false
var body: some View {
NavigationLink {
MemberAdmissionView(
groupInfo: $groupInfo,
admission: $admission,
currentAdmission: currentAdmission,
creatingGroup: creatingGroup,
saveAdmission: saveAdmission
)
.navigationBarTitle("Member admission")
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(.large)
.onDisappear {
let saveText = NSLocalizedString(
creatingGroup ? "Save" : "Save and notify group members",
comment: "alert button"
)
if groupInfo.groupProfile.memberAdmission_ != admission {
showAlert(
title: NSLocalizedString("Save admission settings?", comment: "alert title"),
buttonTitle: saveText,
buttonAction: { saveAdmission() },
cancelButton: true
)
}
}
} label: {
if creatingGroup {
Text("Set member admission")
} else {
Label("Member admission", systemImage: "switch.2")
}
}
}
private func saveAdmission() {
Task {
do {
var gp = groupInfo.groupProfile
gp.memberAdmission = admission
let gInfo = try await apiUpdateGroup(groupInfo.groupId, gp)
await MainActor.run {
groupInfo = gInfo
ChatModel.shared.updateGroup(gInfo)
currentAdmission = admission
}
} catch {
logger.error("MemberAdmissionView apiUpdateGroup error: \(responseError(error))")
}
}
}
}
struct GroupPreferencesView_Previews: PreviewProvider {
static var previews: some View {
GroupPreferencesView(

View file

@ -1,93 +0,0 @@
//
// MemberAdmissionView.swift
// SimpleX (iOS)
//
// Created by spaced4ndy on 28.04.2025.
// Copyright © 2025 SimpleX Chat. All rights reserved.
//
import SwiftUI
import SimpleXChat
private let memberCriterias: [(criteria: MemberCriteria?, text: LocalizedStringKey)] = [
(nil, "off"),
(.all, "all")
]
struct MemberAdmissionView: View {
@Environment(\.dismiss) var dismiss: DismissAction
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@Binding var groupInfo: GroupInfo
@Binding var admission: GroupMemberAdmission
var currentAdmission: GroupMemberAdmission
let creatingGroup: Bool
let saveAdmission: () -> Void
@State private var showSaveDialogue = false
var body: some View {
let saveText: LocalizedStringKey = creatingGroup ? "Save" : "Save and notify group members"
VStack {
List {
admissionSection(
NSLocalizedString("Review members", comment: "admission stage"),
NSLocalizedString("Review members before admitting (\"knocking\").", comment: "admission stage description"),
$admission.review
)
if groupInfo.isOwner {
Section {
Button("Reset") { admission = currentAdmission }
Button(saveText) { saveAdmission() }
}
.disabled(currentAdmission == admission)
}
}
}
.modifier(BackButton(disabled: Binding.constant(false)) {
if currentAdmission == admission {
dismiss()
} else {
showSaveDialogue = true
}
})
.confirmationDialog("Save admission settings?", isPresented: $showSaveDialogue) {
Button(saveText) {
saveAdmission()
dismiss()
}
Button("Exit without saving") {
admission = currentAdmission
dismiss()
}
}
}
private func admissionSection(_ admissionStageStr: String, _ admissionStageDescrStr: String, _ memberCriteria: Binding<MemberCriteria?>) -> some View {
Section {
if groupInfo.isOwner {
Picker(admissionStageStr, selection: memberCriteria) {
ForEach(memberCriterias, id: \.criteria) { mc in
Text(mc.text)
}
}
.frame(height: 36)
} else {
infoRow(Text(admissionStageStr), memberCriteria.wrappedValue?.text ?? NSLocalizedString("off", comment: "member criteria value"))
}
} footer: {
Text(admissionStageDescrStr)
.foregroundColor(theme.colors.secondary)
}
}
}
#Preview {
MemberAdmissionView(
groupInfo: Binding.constant(GroupInfo.sampleData),
admission: Binding.constant(GroupMemberAdmission.sampleData),
currentAdmission: GroupMemberAdmission.sampleData,
creatingGroup: false,
saveAdmission: {}
)
}

View file

@ -1,44 +0,0 @@
//
// MemberSupportChatToolbar.swift
// SimpleX (iOS)
//
// Created by spaced4ndy on 01.05.2025.
// Copyright © 2025 SimpleX Chat. All rights reserved.
//
import SwiftUI
import SimpleXChat
struct MemberSupportChatToolbar: View {
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
var groupMember: GroupMember
var imageSize: CGFloat = 32
var body: some View {
return HStack {
MemberProfileImage(groupMember, size: imageSize)
.padding(.trailing, 4)
let t = Text(groupMember.chatViewName).font(.headline)
(groupMember.verified ? memberVerifiedShield + t : t)
.lineLimit(1)
}
.foregroundColor(theme.colors.onBackground)
.frame(width: 220)
}
private var memberVerifiedShield: Text {
(Text(Image(systemName: "checkmark.shield")) + textSpace)
.font(.caption)
.foregroundColor(theme.colors.secondary)
.baselineOffset(1)
.kerning(-2)
}
}
#Preview {
MemberSupportChatToolbar(
groupMember: GroupMember.sampleData
)
.environmentObject(CurrentColors.toAppTheme())
}

View file

@ -1,287 +0,0 @@
//
// MemberSupportView.swift
// SimpleX (iOS)
//
// Created by spaced4ndy on 28.04.2025.
// Copyright © 2025 SimpleX Chat. All rights reserved.
//
import SwiftUI
import SimpleXChat
struct MemberSupportView: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@State private var searchText: String = ""
@FocusState private var searchFocussed
var groupInfo: GroupInfo
@Binding var scrollToItemId: ChatItem.ID?
var body: some View {
viewBody()
.onAppear {
Task {
await chatModel.loadGroupMembers(groupInfo)
}
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
Task {
await chatModel.loadGroupMembers(groupInfo)
}
} label: {
Image(systemName: "arrow.clockwise")
}
}
}
}
@ViewBuilder private func viewBody() -> some View {
let membersWithChats = sortedMembersWithChats()
let s = searchText.trimmingCharacters(in: .whitespaces).localizedLowercase
let filteredMembersWithChats = s == ""
? membersWithChats
: membersWithChats.filter { $0.wrapped.localAliasAndFullName.localizedLowercase.contains(s) }
if membersWithChats.isEmpty {
Text("No chats with members")
.foregroundColor(.secondary)
} else {
List {
searchFieldView(text: $searchText, focussed: $searchFocussed, theme.colors.onBackground, theme.colors.secondary)
.padding(.leading, 8)
ForEach(filteredMembersWithChats) { memberWithChat in
MemberSupportChatNavLink(
groupInfo: groupInfo,
memberWithChat: memberWithChat,
scrollToItemId: $scrollToItemId
)
}
}
}
}
struct MemberSupportChatNavLink: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@State private var memberSupportChatNavLinkActive = false
var groupInfo: GroupInfo
var memberWithChat: GMember
@Binding var scrollToItemId: ChatItem.ID?
var body: some View {
ZStack {
let scopeInfo: GroupChatScopeInfo = .memberSupport(groupMember_: memberWithChat.wrapped)
Button {
ItemsModel.loadSecondaryChat(groupInfo.id, chatFilter: .groupChatScopeContext(groupScopeInfo: scopeInfo)) {
memberSupportChatNavLinkActive = true
}
} label: {
SupportChatRowView(groupMember: memberWithChat, groupInfo: groupInfo)
}
NavigationLink(isActive: $memberSupportChatNavLinkActive) {
SecondaryChatView(
chat: Chat(chatInfo: .group(groupInfo: groupInfo, groupChatScope: scopeInfo), chatItems: [], chatStats: ChatStats()),
scrollToItemId: $scrollToItemId
)
} label: {
EmptyView()
}
.frame(width: 1, height: 1)
.hidden()
}
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
if memberWithChat.wrapped.memberPending {
Button {
showAcceptMemberAlert(groupInfo, memberWithChat.wrapped)
} label: {
Label("Accept", systemImage: "checkmark")
}
.tint(theme.colors.primary)
} else {
Button {
showDeleteMemberSupportChatAlert(groupInfo, memberWithChat.wrapped)
} label: {
Label("Delete", systemImage: "trash")
}
.tint(.red)
}
}
}
}
func sortedMembersWithChats() -> [GMember] {
chatModel.groupMembers
.filter {
$0.wrapped.supportChat != nil &&
$0.wrapped.memberStatus != .memLeft &&
$0.wrapped.memberStatus != .memRemoved
}
.sorted { (m0: GMember, m1: GMember) -> Bool in
if m0.wrapped.memberPending != m1.wrapped.memberPending {
return m0.wrapped.memberPending
}
let mentions0 = (m0.wrapped.supportChat?.mentions ?? 0) > 0
let mentions1 = (m1.wrapped.supportChat?.mentions ?? 0) > 0
if mentions0 != mentions1 {
return mentions0
}
let attention0 = (m0.wrapped.supportChat?.memberAttention ?? 0) > 0
let attention1 = (m1.wrapped.supportChat?.memberAttention ?? 0) > 0
if attention0 != attention1 {
return attention0
}
let unread0 = (m0.wrapped.supportChat?.unread ?? 0) > 0
let unread1 = (m1.wrapped.supportChat?.unread ?? 0) > 0
if unread0 != unread1 {
return unread0
}
return (m0.wrapped.supportChat?.chatTs ?? .distantPast) > (m1.wrapped.supportChat?.chatTs ?? .distantPast)
}
}
private struct SupportChatRowView: View {
@EnvironmentObject var chatModel: ChatModel
@ObservedObject var groupMember: GMember
@EnvironmentObject var theme: AppTheme
@Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize
var groupInfo: GroupInfo
var dynamicChatInfoSize: CGFloat { dynamicSize(userFont).chatInfoSize }
var body: some View {
let member = groupMember.wrapped
HStack{
MemberProfileImage(member, size: 38)
.padding(.trailing, 2)
VStack(alignment: .leading) {
let t = Text(member.chatViewName).foregroundColor(theme.colors.onBackground)
(member.verified ? memberVerifiedShield + t : t)
.lineLimit(1)
Text(memberStatus(member))
.lineLimit(1)
.font(.caption)
.foregroundColor(theme.colors.secondary)
}
Spacer()
if member.memberPending {
Image(systemName: "flag.fill")
.resizable()
.scaledToFill()
.frame(width: dynamicChatInfoSize * 0.8, height: dynamicChatInfoSize * 0.8)
.foregroundColor(theme.colors.primary)
}
if let supportChat = member.supportChat {
SupportChatUnreadIndicator(supportChat: supportChat)
}
}
}
private func memberStatus(_ member: GroupMember) -> LocalizedStringKey {
if member.activeConn?.connDisabled ?? false {
return "disabled"
} else if member.activeConn?.connInactive ?? false {
return "inactive"
} else if member.memberPending {
return member.memberStatus.text
} else {
return LocalizedStringKey(member.memberRole.text)
}
}
struct SupportChatUnreadIndicator: View {
@EnvironmentObject var theme: AppTheme
@Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize
var supportChat: GroupSupportChat
var dynamicChatInfoSize: CGFloat { dynamicSize(userFont).chatInfoSize }
private var indicatorTint: Color {
if supportChat.mentions > 0 || supportChat.memberAttention > 0 {
return theme.colors.primary
} else {
return theme.colors.secondary
}
}
var body: some View {
HStack(alignment: .center, spacing: 2) {
if supportChat.unread > 0 || supportChat.mentions > 0 || supportChat.memberAttention > 0 {
if supportChat.mentions > 0 && supportChat.unread > 1 {
Text("\(MENTION_START)")
.font(userFont <= .xxxLarge ? .body : .callout)
.foregroundColor(indicatorTint)
.frame(minWidth: dynamicChatInfoSize, minHeight: dynamicChatInfoSize)
.cornerRadius(dynamicSize(userFont).unreadCorner)
.padding(.bottom, 1)
}
let singleUnreadIsMention = supportChat.mentions > 0 && supportChat.unread == 1
(singleUnreadIsMention ? Text("\(MENTION_START)") : unreadCountText(supportChat.unread))
.font(userFont <= .xxxLarge ? .caption : .caption2)
.foregroundColor(.white)
.padding(.horizontal, dynamicSize(userFont).unreadPadding)
.frame(minWidth: dynamicChatInfoSize, minHeight: dynamicChatInfoSize)
.background(indicatorTint)
.cornerRadius(dynamicSize(userFont).unreadCorner)
}
}
.frame(height: dynamicChatInfoSize)
.frame(minWidth: 22)
}
}
private var memberVerifiedShield: Text {
(Text(Image(systemName: "checkmark.shield")) + textSpace)
.font(.caption)
.baselineOffset(2)
.kerning(-2)
.foregroundColor(theme.colors.secondary)
}
}
}
func showDeleteMemberSupportChatAlert(_ groupInfo: GroupInfo, _ member: GroupMember) {
showAlert(
title: NSLocalizedString("Delete chat with member?", comment: "alert title"),
buttonTitle: "Delete",
buttonAction: { deleteMemberSupportChat(groupInfo, member) },
cancelButton: true
)
}
func deleteMemberSupportChat(_ groupInfo: GroupInfo, _ member: GroupMember) {
Task {
do {
let (gInfo, updatedMember) = try await apiDeleteMemberSupportChat(groupInfo.groupId, member.groupMemberId)
await MainActor.run {
_ = ChatModel.shared.upsertGroupMember(gInfo, updatedMember)
ChatModel.shared.updateGroup(gInfo)
}
// TODO member row doesn't get removed from list (upsertGroupMember correctly sets supportChat to nil) - this repopulates list to fix it
await ChatModel.shared.loadGroupMembers(gInfo)
} catch let error {
logger.error("apiDeleteMemberSupportChat error: \(responseError(error))")
await MainActor.run {
showAlert(
NSLocalizedString("Error deleting chat with member", comment: "alert title"),
message: responseError(error)
)
}
}
}
}
#Preview {
MemberSupportView(
groupInfo: GroupInfo.sampleData,
scrollToItemId: Binding.constant(nil)
)
}

View file

@ -1,42 +0,0 @@
//
// SecondaryChatView.swift
// SimpleX (iOS)
//
// Created by spaced4ndy on 29.04.2025.
// Copyright © 2025 SimpleX Chat. All rights reserved.
//
import SwiftUI
import SimpleXChat
struct SecondaryChatView: View {
@EnvironmentObject var chatModel: ChatModel
@ObservedObject var chat: Chat
@Binding var scrollToItemId: ChatItem.ID?
var body: some View {
if let im = chatModel.secondaryIM {
ChatView(
chat: chat,
im: im,
mergedItems: BoxedValue(MergedItems.create(im, [])),
floatingButtonModel: FloatingButtonModel(im: im),
scrollToItemId: $scrollToItemId
)
.onDisappear {
chatModel.secondaryIM = nil
}
}
}
}
#Preview {
SecondaryChatView(
chat: Chat(
chatInfo: .group(groupInfo: GroupInfo.sampleData, groupChatScope: .memberSupport(groupMember_: GroupMember.sampleData)),
chatItems: [],
chatStats: ChatStats()
),
scrollToItemId: Binding.constant(nil)
)
}

View file

@ -25,7 +25,7 @@ struct SelectedItemsTopToolbar: View {
struct SelectedItemsBottomToolbar: View {
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
let im: ItemsModel
let chatItems: [ChatItem]
@Binding var selectedChatItems: Set<Int64>?
var chatInfo: ChatInfo
// Bool - delete for everyone is possible
@ -75,9 +75,9 @@ struct SelectedItemsBottomToolbar: View {
.resizable()
.scaledToFit()
.frame(width: 20, height: 20, alignment: .center)
.foregroundColor(!moderateEnabled || deleteCountProhibited || im.secondaryIMFilter != nil ? theme.colors.secondary : .red)
.foregroundColor(!moderateEnabled || deleteCountProhibited ? theme.colors.secondary : .red)
}
.disabled(!moderateEnabled || deleteCountProhibited || im.secondaryIMFilter != nil)
.disabled(!moderateEnabled || deleteCountProhibited)
.opacity(canModerate ? 1 : 0)
Spacer()
@ -88,24 +88,24 @@ struct SelectedItemsBottomToolbar: View {
.resizable()
.scaledToFit()
.frame(width: 20, height: 20, alignment: .center)
.foregroundColor(!forwardEnabled || forwardCountProhibited || im.secondaryIMFilter != nil ? theme.colors.secondary : theme.colors.primary)
.foregroundColor(!forwardEnabled || forwardCountProhibited ? theme.colors.secondary : theme.colors.primary)
}
.disabled(!forwardEnabled || forwardCountProhibited || im.secondaryIMFilter != nil)
.disabled(!forwardEnabled || forwardCountProhibited)
}
.frame(maxHeight: .infinity)
.padding([.leading, .trailing], 12)
}
.onAppear {
recheckItems(chatInfo, im.reversedChatItems, selectedChatItems)
recheckItems(chatInfo, chatItems, selectedChatItems)
}
.onChange(of: chatInfo) { info in
recheckItems(info, im.reversedChatItems, selectedChatItems)
recheckItems(info, chatItems, selectedChatItems)
}
.onChange(of: im.reversedChatItems) { items in
.onChange(of: chatItems) { items in
recheckItems(chatInfo, items, selectedChatItems)
}
.onChange(of: selectedChatItems) { selected in
recheckItems(chatInfo, im.reversedChatItems, selected)
recheckItems(chatInfo, chatItems, selected)
}
.frame(height: 55.5)
.background(.thinMaterial)
@ -116,7 +116,7 @@ struct SelectedItemsBottomToolbar: View {
deleteCountProhibited = count == 0 || count > 200
forwardCountProhibited = count == 0 || count > 20
canModerate = possibleToModerate(chatInfo)
let groupInfo: GroupInfo? = if case let ChatInfo.group(groupInfo: info, _) = chatInfo {
let groupInfo: GroupInfo? = if case let ChatInfo.group(groupInfo: info) = chatInfo {
info
} else {
nil
@ -145,7 +145,7 @@ struct SelectedItemsBottomToolbar: View {
private func possibleToModerate(_ chatInfo: ChatInfo) -> Bool {
return switch chatInfo {
case let .group(groupInfo, _):
case let .group(groupInfo):
groupInfo.membership.memberRole >= .admin
default: false
}

View file

@ -66,7 +66,7 @@ struct ChatListNavLink: View {
switch chat.chatInfo {
case let .direct(contact):
contactNavLink(contact)
case let .group(groupInfo, _):
case let .group(groupInfo):
groupNavLink(groupInfo)
case let .local(noteFolder):
noteFolderNavLink(noteFolder)
@ -189,7 +189,7 @@ struct ChatListNavLink: View {
}
.swipeActions(edge: .trailing) {
tagChatButton(chat)
if (groupInfo.membership.memberCurrentOrPending) {
if (groupInfo.membership.memberCurrent) {
leaveGroupChatButton(groupInfo)
}
if groupInfo.canDelete {
@ -214,7 +214,7 @@ struct ChatListNavLink: View {
let showReportsButton = chat.chatStats.reportsCount > 0 && groupInfo.membership.memberRole >= .moderator
let showClearButton = !chat.chatItems.isEmpty
let showDeleteGroup = groupInfo.canDelete
let showLeaveGroup = groupInfo.membership.memberCurrentOrPending
let showLeaveGroup = groupInfo.membership.memberCurrent
let totalNumberOfButtons = 1 + (showReportsButton ? 1 : 0) + (showClearButton ? 1 : 0) + (showDeleteGroup ? 1 : 0) + (showLeaveGroup ? 1 : 0)
if showClearButton && totalNumberOfButtons <= 3 {
@ -276,7 +276,7 @@ struct ChatListNavLink: View {
@ViewBuilder private func markReadButton() -> some View {
if chat.chatStats.unreadCount > 0 || chat.chatStats.unreadChat {
Button {
Task { await markChatRead(ItemsModel.shared, chat) }
Task { await markChatRead(chat) }
} label: {
SwipeLabel(NSLocalizedString("Read", comment: "swipe action"), systemImage: "checkmark", inverted: oneHandUI)
}
@ -482,12 +482,14 @@ struct ChatListNavLink: View {
.tint(theme.colors.primary)
}
.appSheet(isPresented: $showContactConnectionInfo) {
Group {
if case let .contactConnection(contactConnection) = chat.chatInfo {
ContactConnectionInfo(contactConnection: contactConnection)
.environment(\EnvironmentValues.refresh as! WritableKeyPath<EnvironmentValues, RefreshAction?>, nil)
.modifier(ThemedBackground(grouped: true))
}
}
}
.contentShape(Rectangle())
.onTapGesture {
showContactConnectionInfo = true

View file

@ -148,7 +148,6 @@ struct ChatListView: View {
@State private var userPickerShown: Bool = false
@State private var sheet: SomeSheet<AnyView>? = nil
@StateObject private var chatTagsModel = ChatTagsModel.shared
@State private var scrollToItemId: ChatItem.ID? = nil
// iOS 15 is required it to show/hide toolbar while chat is hidden/visible
@State private var viewOnScreen = true
@ -447,14 +446,7 @@ struct ChatListView: View {
@ViewBuilder private func chatView() -> some View {
if let chatId = chatModel.chatId, let chat = chatModel.getChat(chatId) {
let im = ItemsModel.shared
ChatView(
chat: chat,
im: im,
mergedItems: BoxedValue(MergedItems.create(im, [])),
floatingButtonModel: FloatingButtonModel(im: im),
scrollToItemId: $scrollToItemId
)
ChatView(chat: chat)
}
}
@ -900,12 +892,12 @@ func presetTagMatchesChat(_ tag: PresetTag, _ chatInfo: ChatInfo, _ chatStats: C
case let .direct(contact): !(contact.activeConn == nil && contact.profile.contactLink != nil && contact.active) && !contact.chatDeleted
case .contactRequest: true
case .contactConnection: true
case let .group(groupInfo, _): groupInfo.businessChat?.chatType == .customer
case let .group(groupInfo): groupInfo.businessChat?.chatType == .customer
default: false
}
case .groups:
switch chatInfo {
case let .group(groupInfo, _): groupInfo.businessChat == nil
case let .group(groupInfo): groupInfo.businessChat == nil
default: false
}
case .business:

View file

@ -141,7 +141,7 @@ struct ChatPreviewView: View {
} else {
EmptyView()
}
case let .group(groupInfo, _):
case let .group(groupInfo):
switch (groupInfo.membership.memberStatus) {
case .memRejected: inactiveIcon()
case .memLeft: inactiveIcon()
@ -165,7 +165,7 @@ struct ChatPreviewView: View {
switch chat.chatInfo {
case let .direct(contact):
previewTitle(contact.verified == true ? verifiedIcon + t : t).foregroundColor(deleting ? Color.secondary : nil)
case let .group(groupInfo, _):
case let .group(groupInfo):
let v = previewTitle(t)
switch (groupInfo.membership.memberStatus) {
case .memInvited: v.foregroundColor(deleting ? theme.colors.secondary : chat.chatInfo.incognito ? .indigo : theme.colors.primary)
@ -341,12 +341,11 @@ struct ChatPreviewView: View {
chatPreviewInfoText("connecting…")
}
}
case let .group(groupInfo, _):
case let .group(groupInfo):
switch (groupInfo.membership.memberStatus) {
case .memRejected: chatPreviewInfoText("rejected")
case .memInvited: groupInvitationPreviewText(groupInfo)
case .memAccepted: chatPreviewInfoText("connecting…")
case .memPendingReview, .memPendingApproval: chatPreviewInfoText("reviewed by admins")
default: EmptyView()
}
default: EmptyView()
@ -440,11 +439,7 @@ struct ChatPreviewView: View {
if progressByTimeout {
ProgressView()
} else if chat.chatStats.reportsCount > 0 {
flagIcon(size: size * 0.8, color: .red)
} else if chat.supportUnreadCount > 0 {
flagIcon(size: size * 0.8, color: theme.colors.primary)
} else if chat.chatInfo.groupInfo?.membership.memberPending ?? false {
flagIcon(size: size * 0.8, color: theme.colors.secondary)
groupReportsIcon(size: size * 0.8)
} else {
incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary, size: size)
}
@ -490,12 +485,12 @@ struct ChatPreviewView: View {
}
}
func flagIcon(size: CGFloat, color: Color) -> some View {
func groupReportsIcon(size: CGFloat) -> some View {
Image(systemName: "flag")
.resizable()
.scaledToFit()
.frame(width: size, height: size)
.foregroundColor(color)
.foregroundColor(.red)
}
func smallContentPreview(size: CGFloat, _ view: @escaping () -> some View) -> some View {

View file

@ -124,7 +124,7 @@ struct UserPicker: View {
ZStack(alignment: .topTrailing) {
ProfileImage(imageStr: u.user.image, size: size, color: Color(uiColor: .tertiarySystemGroupedBackground))
if (u.unreadCount > 0) {
userUnreadBadge(u, theme: theme).offset(x: 4, y: -4)
UnreadBadge(userInfo: u).offset(x: 4, y: -4)
}
}
.padding(.trailing, 6)
@ -171,27 +171,19 @@ struct UserPicker: View {
}
}
@inline(__always)
func userUnreadBadge(_ userInfo: UserInfo, theme: AppTheme) -> some View {
UnreadBadge(
count: userInfo.unreadCount,
color: userInfo.user.showNtfs ? theme.colors.primary : theme.colors.secondary
)
}
struct UnreadBadge: View {
var userInfo: UserInfo
@EnvironmentObject var theme: AppTheme
@Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize
var count: Int
var color: Color
var body: some View {
let size = dynamicSize(userFont).chatInfoSize
unreadCountText(count)
unreadCountText(userInfo.unreadCount)
.font(userFont <= .xxxLarge ? .caption : .caption2)
.foregroundColor(.white)
.padding(.horizontal, dynamicSize(userFont).unreadPadding)
.frame(minWidth: size, minHeight: size)
.background(color)
.background(userInfo.user.showNtfs ? theme.colors.primary : theme.colors.secondary)
.cornerRadius(dynamicSize(userFont).unreadCorner)
}
}

View file

@ -33,7 +33,7 @@ extension View {
func appSheet<Content>(
isPresented: Binding<Bool>,
onDismiss: (() -> Void)? = nil,
@ViewBuilder content: @escaping () -> Content
content: @escaping () -> Content
) -> some View where Content: View {
sheet(isPresented: isPresented, onDismiss: onDismiss) {
content().modifier(PrivacySensitive())
@ -43,7 +43,7 @@ extension View {
func appSheet<T, Content>(
item: Binding<T?>,
onDismiss: (() -> Void)? = nil,
@ViewBuilder content: @escaping (T) -> Content
content: @escaping (T) -> Content
) -> some View where T: Identifiable, Content: View {
sheet(item: item, onDismiss: onDismiss) { it in
content(it).modifier(PrivacySensitive())

View file

@ -66,8 +66,6 @@ struct LocalAuthView: View {
m.chatId = nil
ItemsModel.shared.reversedChatItems = []
ItemsModel.shared.chatState.clear()
ChatModel.shared.secondaryIM?.reversedChatItems = []
ChatModel.shared.secondaryIM?.chatState.clear()
m.updateChats([])
m.users = []
_ = kcAppPassword.set(password)

View file

@ -193,7 +193,7 @@ struct AddGroupView: View {
Task {
await m.loadGroupMembers(gInfo)
}
let c = Chat(chatInfo: .group(groupInfo: gInfo, groupChatScope: nil), chatItems: [])
let c = Chat(chatInfo: .group(groupInfo: gInfo), chatItems: [])
m.addChat(c)
withAnimation {
groupInfo = gInfo

View file

@ -367,13 +367,13 @@ struct ChatThemePreview: View {
let alice = ChatItem.getSample(1, CIDirection.directRcv, Date.now, NSLocalizedString("Good afternoon!", comment: "message preview"))
let bob = ChatItem.getSample(2, CIDirection.directSnd, Date.now, NSLocalizedString("Good morning!", comment: "message preview"), quotedItem: CIQuote.getSample(alice.id, alice.meta.itemTs, alice.content.text, chatDir: alice.chatDir))
HStack {
ChatItemView(chat: Chat.sampleData, im: ItemsModel.shared, chatItem: alice, scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, chatItem: alice, scrollToItemId: { _ in })
.modifier(ChatItemClipped(alice, tailVisible: true))
Spacer()
}
HStack {
Spacer()
ChatItemView(chat: Chat.sampleData, im: ItemsModel.shared, chatItem: bob, scrollToItem: { _ in }, scrollToItemId: Binding.constant(nil))
ChatItemView(chat: Chat.sampleData, chatItem: bob, scrollToItemId: { _ in })
.modifier(ChatItemClipped(bob, tailVisible: true))
.frame(alignment: .trailing)
}

View file

@ -58,7 +58,6 @@ let DEFAULT_CONNECT_VIA_LINK_TAB = "connectViaLinkTab"
let DEFAULT_LIVE_MESSAGE_ALERT_SHOWN = "liveMessageAlertShown"
let DEFAULT_SHOW_HIDDEN_PROFILES_NOTICE = "showHiddenProfilesNotice"
let DEFAULT_SHOW_MUTE_PROFILE_ALERT = "showMuteProfileAlert"
let DEFAULT_SHOW_REPORTS_IN_SUPPORT_CHAT_ALERT = "showReportsInSupportChatAlert"
let DEFAULT_WHATS_NEW_VERSION = "defaultWhatsNewVersion"
let DEFAULT_ONBOARDING_STAGE = "onboardingStage"
let DEFAULT_MIGRATION_TO_STAGE = "migrationToStage"
@ -118,7 +117,6 @@ let appDefaults: [String: Any] = [
DEFAULT_LIVE_MESSAGE_ALERT_SHOWN: false,
DEFAULT_SHOW_HIDDEN_PROFILES_NOTICE: true,
DEFAULT_SHOW_MUTE_PROFILE_ALERT: true,
DEFAULT_SHOW_REPORTS_IN_SUPPORT_CHAT_ALERT: true,
DEFAULT_ONBOARDING_STAGE: OnboardingStage.onboardingComplete.rawValue,
DEFAULT_CUSTOM_DISAPPEARING_MESSAGE_TIME: 300,
DEFAULT_SHOW_UNREAD_AND_FAVORITES: false,
@ -146,7 +144,6 @@ let hintDefaults = [
DEFAULT_LIVE_MESSAGE_ALERT_SHOWN,
DEFAULT_SHOW_HIDDEN_PROFILES_NOTICE,
DEFAULT_SHOW_MUTE_PROFILE_ALERT,
DEFAULT_SHOW_REPORTS_IN_SUPPORT_CHAT_ALERT,
DEFAULT_SHOW_DELETE_CONVERSATION_NOTICE,
DEFAULT_SHOW_DELETE_CONTACT_NOTICE
]
@ -198,8 +195,6 @@ let customDisappearingMessageTimeDefault = IntDefault(defaults: UserDefaults.sta
let showDeleteConversationNoticeDefault = BoolDefault(defaults: UserDefaults.standard, forKey: DEFAULT_SHOW_DELETE_CONVERSATION_NOTICE)
let showDeleteContactNoticeDefault = BoolDefault(defaults: UserDefaults.standard, forKey: DEFAULT_SHOW_DELETE_CONTACT_NOTICE)
let showReportsInSupportChatAlertDefault = BoolDefault(defaults: UserDefaults.standard, forKey: DEFAULT_SHOW_REPORTS_IN_SUPPORT_CHAT_ALERT)
/// after importing new database, this flag will be set and unset only after importing app settings in `initializeChat` */
let shouldImportAppSettingsDefault = BoolDefault(defaults: UserDefaults.standard, forKey: DEFAULT_SHOULD_IMPORT_APP_SETTINGS)
let currentThemeDefault = StringDefault(defaults: UserDefaults.standard, forKey: DEFAULT_CURRENT_THEME, withDefault: DefaultTheme.SYSTEM_THEME_NAME)

View file

@ -350,7 +350,7 @@ struct UserProfilesView: View {
Image(systemName: "checkmark").foregroundColor(theme.colors.onBackground)
} else {
if userInfo.unreadCount > 0 {
userUnreadBadge(userInfo, theme: theme)
UnreadBadge(userInfo: userInfo)
}
if user.hidden {
Image(systemName: "lock").foregroundColor(theme.colors.secondary)

View file

@ -554,14 +554,6 @@ time interval</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
<source>Accept conditions</source>
<target>Приеми условията</target>
@ -583,10 +575,6 @@ swipe action</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
<source>Accepted conditions</source>
<target>Приети условия</target>
@ -1547,23 +1535,11 @@ set passcode view</note>
<source>Chat will be deleted for you - this cannot be undone!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Чатове</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
<source>Check messages every 20 min.</source>
<note>No comment provided by engineer.</note>
@ -2324,10 +2300,6 @@ swipe action</note>
<target>Изтриване на чат профила?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
<source>Delete chat?</source>
<note>No comment provided by engineer.</note>
@ -2726,7 +2698,7 @@ swipe action</note>
<trans-unit id="Don't show again" xml:space="preserve">
<source>Don't show again</source>
<target>Не показвай отново</target>
<note>alert action</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
@ -3034,10 +3006,6 @@ chat item action</note>
<target>Грешка при приемане на заявка за контакт</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>Грешка при добавяне на член(ове)</target>
@ -3126,10 +3094,6 @@ chat item action</note>
<target>Грешка при изтриване на базата данни</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
<source>Error deleting chat!</source>
<target>Грешка при изтриването на чата!</target>
@ -3232,7 +3196,7 @@ chat item action</note>
<trans-unit id="Error removing member" xml:space="preserve">
<source>Error removing member</source>
<target>Грешка при отстраняване на член</target>
<note>alert title</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reordering lists" xml:space="preserve">
<source>Error reordering lists</source>
@ -4544,10 +4508,6 @@ This is your link for group %@!</source>
<target>Член</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<note>item status text</note>
@ -4579,10 +4539,6 @@ This is your link for group %@!</source>
<target>Членът ще бъде премахнат от групата - това не може да бъде отменено!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<target>Членовете на групата могат да добавят реакции към съобщенията.</target>
@ -4975,10 +4931,6 @@ This is your link for group %@!</source>
<target>Нова членска роля</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
<source>New message</source>
<target>Ново съобщение</target>
@ -5015,10 +4967,6 @@ This is your link for group %@!</source>
<source>No chats in list %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>Няма избрани контакти</target>
@ -5192,8 +5140,7 @@ This is your link for group %@!</source>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<target>Ок</target>
<note>alert action
alert button</note>
<note>alert button</note>
</trans-unit>
<trans-unit id="Old database" xml:space="preserve">
<source>Old database</source>
@ -5584,10 +5531,6 @@ Error: %@</source>
<source>Please try to disable and re-enable notfications.</source>
<note>token info</note>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
<source>Please wait for token activation to complete.</source>
<note>token info</note>
@ -6007,10 +5950,6 @@ swipe action</note>
<target>Отхвърли заявката за контакт</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>Реле сървър се използва само ако е необходимо. Друга страна може да наблюдава вашия IP адрес.</target>
@ -6114,10 +6053,6 @@ swipe action</note>
<source>Report reason?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
<source>Report spam: only group moderators will see it.</source>
<note>report reason</note>
@ -6222,14 +6157,6 @@ swipe action</note>
<source>Review conditions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>Отзови</target>
@ -6283,10 +6210,6 @@ chat item action</note>
<target>Запази (и уведоми контактите)</target>
<note>alert button</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
<source>Save and notify contact</source>
<target>Запази и уведоми контакта</target>
@ -6766,10 +6689,6 @@ chat item action</note>
<target>Задайте го вместо системната идентификация.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
<source>Set message expiration in chats.</source>
<note>No comment provided by engineer.</note>
@ -8424,10 +8343,6 @@ Repeat join request?</source>
<target>Можете да видите отново линкът за покана в подробностите за връзката.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
<source>You can't send messages!</source>
<target>Не може да изпращате съобщения!</target>
@ -8720,10 +8635,6 @@ Repeat connection request?</source>
<target>по-горе, след това избери:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
<source>accepted call</source>
<target>обаждането прието</target>
@ -8733,10 +8644,6 @@ Repeat connection request?</source>
<source>accepted invitation</source>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
<source>admin</source>
<target>админ</target>
@ -8757,10 +8664,6 @@ Repeat connection request?</source>
<target>съгласуване на криптиране…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<target>всички членове</target>
@ -8844,10 +8747,6 @@ marked deleted chat item preview text</note>
<target>повикване…</target>
<note>call status</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
<source>cancelled %@</source>
<target>отменен %@</target>
@ -8953,14 +8852,6 @@ marked deleted chat item preview text</note>
<target>името на контакта %1$@ е променено на %2$@</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
<source>contact has e2e encryption</source>
<target>контактът има e2e криптиране</target>
@ -8971,10 +8862,6 @@ marked deleted chat item preview text</note>
<target>контактът няма e2e криптиране</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
<source>creator</source>
<target>създател</target>
@ -9143,10 +9030,6 @@ pref value</note>
<target>групата е изтрита</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
<source>group profile updated</source>
<target>профилът на групата е актуализиран</target>
@ -9270,10 +9153,6 @@ pref value</note>
<target>свързан</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<note>No comment provided by engineer.</note>
@ -9337,10 +9216,6 @@ pref value</note>
<target>няма текст</target>
<note>copied message info in history</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
<source>observer</source>
<target>наблюдател</target>
@ -9351,7 +9226,6 @@ pref value</note>
<target>изключено</target>
<note>enabled status
group pref value
member criteria value
time to disappear</note>
</trans-unit>
<trans-unit id="offered %@" xml:space="preserve">
@ -9400,10 +9274,6 @@ time to disappear</note>
<source>pending approval</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
<source>quantum resistant e2e encryption</source>
<target>квантово устойчиво e2e криптиране</target>
@ -9443,10 +9313,6 @@ time to disappear</note>
<target>премахнат адрес за контакт</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
<source>removed profile picture</source>
<target>премахната профилна снимка</target>
@ -9457,22 +9323,10 @@ time to disappear</note>
<target>ви острани</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
<source>requested to connect</source>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<target>запазено</target>
@ -9654,10 +9508,6 @@ last received msg: %2$@</source>
<target>вие</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>вие сте поканени в групата</target>

View file

@ -544,14 +544,6 @@ time interval</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
<source>Accept conditions</source>
<note>No comment provided by engineer.</note>
@ -572,10 +564,6 @@ swipe action</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
<source>Accepted conditions</source>
<note>No comment provided by engineer.</note>
@ -1468,23 +1456,11 @@ set passcode view</note>
<source>Chat will be deleted for you - this cannot be undone!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Chaty</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
<source>Check messages every 20 min.</source>
<note>No comment provided by engineer.</note>
@ -2217,10 +2193,6 @@ swipe action</note>
<target>Smazat chat profil?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
<source>Delete chat?</source>
<note>No comment provided by engineer.</note>
@ -2612,7 +2584,7 @@ swipe action</note>
<trans-unit id="Don't show again" xml:space="preserve">
<source>Don't show again</source>
<target>Znovu neukazuj</target>
<note>alert action</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
@ -2906,10 +2878,6 @@ chat item action</note>
<target>Chyba při přijímání žádosti o kontakt</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>Chyba přidávání člena(ů)</target>
@ -2997,10 +2965,6 @@ chat item action</note>
<target>Chyba při mazání databáze chatu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
<source>Error deleting chat!</source>
<target>Chyba při mazání chatu!</target>
@ -3101,7 +3065,7 @@ chat item action</note>
<trans-unit id="Error removing member" xml:space="preserve">
<source>Error removing member</source>
<target>Chyba při odebrání člena</target>
<note>alert title</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reordering lists" xml:space="preserve">
<source>Error reordering lists</source>
@ -4369,10 +4333,6 @@ This is your link for group %@!</source>
<target>Člen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<note>item status text</note>
@ -4404,10 +4364,6 @@ This is your link for group %@!</source>
<target>Člen bude odstraněn ze skupiny - toto nelze vzít zpět!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<target>Členové skupin mohou přidávat reakce na zprávy.</target>
@ -4783,10 +4739,6 @@ This is your link for group %@!</source>
<target>Nová role člena</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
<source>New message</source>
<target>Nová zpráva</target>
@ -4823,10 +4775,6 @@ This is your link for group %@!</source>
<source>No chats in list %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>Nebyl vybrán žádný kontakt</target>
@ -4997,8 +4945,7 @@ This is your link for group %@!</source>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<target>Ok</target>
<note>alert action
alert button</note>
<note>alert button</note>
</trans-unit>
<trans-unit id="Old database" xml:space="preserve">
<source>Old database</source>
@ -5373,10 +5320,6 @@ Error: %@</source>
<source>Please try to disable and re-enable notfications.</source>
<note>token info</note>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
<source>Please wait for token activation to complete.</source>
<note>token info</note>
@ -5788,10 +5731,6 @@ swipe action</note>
<target>Odmítnout žádost o kontakt</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>Přenosový server se používá pouze v případě potřeby. Jiná strana může sledovat vaši IP adresu.</target>
@ -5890,10 +5829,6 @@ swipe action</note>
<source>Report reason?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
<source>Report spam: only group moderators will see it.</source>
<note>report reason</note>
@ -5997,14 +5932,6 @@ swipe action</note>
<source>Review conditions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>Odvolat</target>
@ -6057,10 +5984,6 @@ chat item action</note>
<target>Uložit (a informovat kontakty)</target>
<note>alert button</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
<source>Save and notify contact</source>
<target>Uložit a upozornit kontakt</target>
@ -6532,10 +6455,6 @@ chat item action</note>
<target>Nastavte jej namísto ověřování systému.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
<source>Set message expiration in chats.</source>
<note>No comment provided by engineer.</note>
@ -8125,10 +8044,6 @@ Repeat join request?</source>
<source>You can view invitation link again in connection details.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
<source>You can't send messages!</source>
<target>Nemůžete posílat zprávy!</target>
@ -8415,10 +8330,6 @@ Repeat connection request?</source>
<target>výše, pak vyberte:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
<source>accepted call</source>
<target>přijatý hovor</target>
@ -8428,10 +8339,6 @@ Repeat connection request?</source>
<source>accepted invitation</source>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
<source>admin</source>
<target>správce</target>
@ -8451,10 +8358,6 @@ Repeat connection request?</source>
<target>povoluji šifrování…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
@ -8532,10 +8435,6 @@ marked deleted chat item preview text</note>
<target>volání…</target>
<note>call status</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
<source>cancelled %@</source>
<target>zrušeno %@</target>
@ -8640,14 +8539,6 @@ marked deleted chat item preview text</note>
<source>contact %1$@ changed to %2$@</source>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
<source>contact has e2e encryption</source>
<target>kontakt má šifrování e2e</target>
@ -8658,10 +8549,6 @@ marked deleted chat item preview text</note>
<target>kontakt nemá šifrování e2e</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
<source>creator</source>
<target>tvůrce</target>
@ -8828,10 +8715,6 @@ pref value</note>
<target>skupina smazána</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
<source>group profile updated</source>
<target>profil skupiny aktualizován</target>
@ -8954,10 +8837,6 @@ pref value</note>
<target>připojeno</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<note>No comment provided by engineer.</note>
@ -9021,10 +8900,6 @@ pref value</note>
<target>žádný text</target>
<note>copied message info in history</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
<source>observer</source>
<target>pozorovatel</target>
@ -9035,7 +8910,6 @@ pref value</note>
<target>vypnuto</target>
<note>enabled status
group pref value
member criteria value
time to disappear</note>
</trans-unit>
<trans-unit id="offered %@" xml:space="preserve">
@ -9083,10 +8957,6 @@ time to disappear</note>
<source>pending approval</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
<source>quantum resistant e2e encryption</source>
<note>chat item text</note>
@ -9124,10 +8994,6 @@ time to disappear</note>
<source>removed contact address</source>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
<source>removed profile picture</source>
<note>profile update event chat item</note>
@ -9137,22 +9003,10 @@ time to disappear</note>
<target>odstranil vás</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
<source>requested to connect</source>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
@ -9324,10 +9178,6 @@ last received msg: %2$@</source>
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>jste pozváni do skupiny</target>

View file

@ -565,16 +565,6 @@ time interval</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<target>Als Mitglied übernehmen</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<target>Als Beobachter übernehmen</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
<source>Accept conditions</source>
<target>Nutzungsbedingungen akzeptieren</target>
@ -596,11 +586,6 @@ swipe action</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<target>Mitglied übernehmen</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
<source>Accepted conditions</source>
<target>Akzeptierte Nutzungsbedingungen</target>
@ -1597,26 +1582,11 @@ set passcode view</note>
<target>Der Chat wird für Sie gelöscht. Dies kann nicht rückgängig gemacht werden!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<target>Chat mit Administratoren</target>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<target>Chat mit einem Mitglied</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Chats</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<target>Chats mit Mitgliedern</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
<source>Check messages every 20 min.</source>
<target>Alle 20min Nachrichten überprüfen.</target>
@ -2432,11 +2402,6 @@ swipe action</note>
<target>Chat-Profil löschen?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<target>Chat mit dem Mitglied löschen?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
<source>Delete chat?</source>
<target>Chat löschen?</target>
@ -2860,7 +2825,7 @@ swipe action</note>
<trans-unit id="Don't show again" xml:space="preserve">
<source>Don't show again</source>
<target>Nicht nochmals anzeigen</target>
<note>alert action</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
@ -3178,11 +3143,6 @@ chat item action</note>
<target>Fehler beim Annehmen der Kontaktanfrage</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<target>Fehler beim Übernehmen des Mitglieds</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>Fehler beim Hinzufügen von Mitgliedern</target>
@ -3278,11 +3238,6 @@ chat item action</note>
<target>Fehler beim Löschen der Chat-Datenbank</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<target>Fehler beim Löschen des Chats mit dem Mitglied</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
<source>Error deleting chat!</source>
<target>Fehler beim Löschen des Chats!</target>
@ -3391,7 +3346,7 @@ chat item action</note>
<trans-unit id="Error removing member" xml:space="preserve">
<source>Error removing member</source>
<target>Fehler beim Entfernen des Mitglieds</target>
<note>alert title</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reordering lists" xml:space="preserve">
<source>Error reordering lists</source>
@ -4775,11 +4730,6 @@ Das ist Ihr Link für die Gruppe %@!</target>
<target>Mitglied</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<target>Aufnahme von Mitgliedern</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<target>Mitglied inaktiv</target>
@ -4815,11 +4765,6 @@ Das ist Ihr Link für die Gruppe %@!</target>
<target>Das Mitglied wird aus der Gruppe entfernt. Dies kann nicht rückgängig gemacht werden!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<target>Ein Mitglied wird der Gruppe beitreten. Übernehmen?</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<target>Gruppenmitglieder können eine Reaktion auf Nachrichten geben.</target>
@ -5240,11 +5185,6 @@ Das ist Ihr Link für die Gruppe %@!</target>
<target>Neue Mitgliedsrolle</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<target>Ein neues Mitglied will der Gruppe beitreten.</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
<source>New message</source>
<target>Neue Nachricht</target>
@ -5285,11 +5225,6 @@ Das ist Ihr Link für die Gruppe %@!</target>
<target>Keine Chats in der Liste %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<target>Keine Chats mit Mitgliedern</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>Keine Kontakte ausgewählt</target>
@ -5482,8 +5417,7 @@ Das ist Ihr Link für die Gruppe %@!</target>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<target>Ok</target>
<note>alert action
alert button</note>
<note>alert button</note>
</trans-unit>
<trans-unit id="Old database" xml:space="preserve">
<source>Old database</source>
@ -5641,7 +5575,6 @@ Dies erfordert die Aktivierung eines VPNs.</target>
</trans-unit>
<trans-unit id="Open link?" xml:space="preserve">
<source>Open link?</source>
<target>Link öffnen?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Open migration to another device" xml:space="preserve">
@ -5895,11 +5828,6 @@ Fehler: %@</target>
<target>Bitte versuchen Sie, die Benachrichtigungen zu deaktivieren und wieder zu aktivieren.</target>
<note>token info</note>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<target>Bitte warten Sie auf die Überprüfung Ihrer Anfrage durch die Gruppen-Moderatoren, um der Gruppe beitreten zu können.</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
<source>Please wait for token activation to complete.</source>
<target>Bitte warten Sie, bis die Token-Aktivierung abgeschlossen ist.</target>
@ -6353,11 +6281,6 @@ swipe action</note>
<target>Kontaktanfrage ablehnen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<target>Mitglied ablehnen?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>Relais-Server werden nur genutzt, wenn sie benötigt werden. Ihre IP-Adresse kann von Anderen erfasst werden.</target>
@ -6468,11 +6391,6 @@ swipe action</note>
<target>Grund der Meldung?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<target>Meldung wurde an die Moderatoren gesendet</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
<source>Report spam: only group moderators will see it.</source>
<target>Spam melden: Nur Gruppenmoderatoren werden es sehen.</target>
@ -6588,16 +6506,6 @@ swipe action</note>
<target>Nutzungsbedingungen einsehen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<target>Überprüfung der Mitglieder</target>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<target>Überprüfung der Mitglieder vor der Aufnahme ("Anklopfen").</target>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>Widerrufen</target>
@ -6654,11 +6562,6 @@ chat item action</note>
<target>Speichern (und Kontakte benachrichtigen)</target>
<note>alert button</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<target>Speichern der Aufnahme-Einstellungen?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
<source>Save and notify contact</source>
<target>Speichern und Kontakt benachrichtigen</target>
@ -7174,11 +7077,6 @@ chat item action</note>
<target>Anstelle der System-Authentifizierung festlegen.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<target>Aufnahme von Mitgliedern festlegen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
<source>Set message expiration in chats.</source>
<target>Verfallsdatum von Nachrichten in Chats festlegen.</target>
@ -8941,11 +8839,6 @@ Verbindungsanfrage wiederholen?</target>
<target>Den Einladungslink können Sie in den Details der Verbindung nochmals sehen.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<target>Sie können Ihre Meldungen im Chat mit den Administratoren sehen.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
<source>You can't send messages!</source>
<target>Sie können keine Nachrichten versenden!</target>
@ -9185,7 +9078,7 @@ Verbindungsanfrage wiederholen?</target>
</trans-unit>
<trans-unit id="Your profile is stored on your device and only shared with your contacts." xml:space="preserve">
<source>Your profile is stored on your device and only shared with your contacts.</source>
<target>Ihr Profil wird auf Ihrem Gerät gespeichert und nur mit Ihren Kontakten geteilt.</target>
<target>Das Profil wird nur mit Ihren Kontakten geteilt.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." xml:space="preserve">
@ -9248,11 +9141,6 @@ Verbindungsanfrage wiederholen?</target>
<target>Danach die gewünschte Aktion auswählen:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<target>%@ übernommen</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
<source>accepted call</source>
<target>Anruf angenommen</target>
@ -9263,11 +9151,6 @@ Verbindungsanfrage wiederholen?</target>
<target>Einladung angenommen</target>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<target>hat Sie übernommen</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
<source>admin</source>
<target>Admin</target>
@ -9288,11 +9171,6 @@ Verbindungsanfrage wiederholen?</target>
<target>Verschlüsselung zustimmen…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<target>alle</target>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<target>Alle Mitglieder</target>
@ -9379,11 +9257,6 @@ marked deleted chat item preview text</note>
<target>Anrufen…</target>
<note>call status</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<target>Es können keine Nachrichten gesendet werden</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
<source>cancelled %@</source>
<target>abgebrochen %@</target>
@ -9489,16 +9362,6 @@ marked deleted chat item preview text</note>
<target>Der Kontaktname wurde von %1$@ auf %2$@ geändert</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<target>Kontakt gelöscht</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<target>Kontakt deaktiviert</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
<source>contact has e2e encryption</source>
<target>Kontakt nutzt E2E-Verschlüsselung</target>
@ -9509,11 +9372,6 @@ marked deleted chat item preview text</note>
<target>Kontakt nutzt keine E2E-Verschlüsselung</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<target>Kontakt nicht bereit</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
<source>creator</source>
<target>Ersteller</target>
@ -9685,11 +9543,6 @@ pref value</note>
<target>Gruppe gelöscht</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<target>Gruppe wird gelöscht</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
<source>group profile updated</source>
<target>Gruppenprofil aktualisiert</target>
@ -9815,11 +9668,6 @@ pref value</note>
<target>ist der Gruppe beigetreten</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<target>Das Mitglied hat eine alte App-Version</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<target>Nachricht</target>
@ -9885,11 +9733,6 @@ pref value</note>
<target>Kein Text</target>
<note>copied message info in history</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<target>Nicht synchronisiert</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
<source>observer</source>
<target>Beobachter</target>
@ -9900,7 +9743,6 @@ pref value</note>
<target>Aus</target>
<note>enabled status
group pref value
member criteria value
time to disappear</note>
</trans-unit>
<trans-unit id="offered %@" xml:space="preserve">
@ -9953,11 +9795,6 @@ time to disappear</note>
<target>ausstehende Genehmigung</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<target>Ausstehende Überprüfung</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
<source>quantum resistant e2e encryption</source>
<target>Quantum-resistente E2E-Verschlüsselung</target>
@ -9998,11 +9835,6 @@ time to disappear</note>
<target>Die Kontaktadresse wurde entfernt</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<target>Von der Gruppe entfernt</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
<source>removed profile picture</source>
<target>Das Profil-Bild wurde entfernt</target>
@ -10013,26 +9845,11 @@ time to disappear</note>
<target>hat Sie aus der Gruppe entfernt</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<target>Beitrittsanfrage abgelehnt</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
<source>requested to connect</source>
<target>Zur Verbindung aufgefordert</target>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<target>Überprüfung</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<target>Von Administratoren überprüft</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<target>abgespeichert</target>
@ -10222,11 +10039,6 @@ Zuletzt empfangene Nachricht: %2$@</target>
<target>Profil</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<target>Sie haben dieses Mitglied übernommen</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>Sie sind zu der Gruppe eingeladen</target>

View file

@ -565,16 +565,6 @@ time interval</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<target>Accept as member</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<target>Accept as observer</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
<source>Accept conditions</source>
<target>Accept conditions</target>
@ -596,11 +586,6 @@ swipe action</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<target>Accept member</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
<source>Accepted conditions</source>
<target>Accepted conditions</target>
@ -1597,26 +1582,11 @@ set passcode view</note>
<target>Chat will be deleted for you - this cannot be undone!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<target>Chat with admins</target>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<target>Chat with member</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Chats</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<target>Chats with members</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
<source>Check messages every 20 min.</source>
<target>Check messages every 20 min.</target>
@ -2432,11 +2402,6 @@ swipe action</note>
<target>Delete chat profile?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<target>Delete chat with member?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
<source>Delete chat?</source>
<target>Delete chat?</target>
@ -2860,7 +2825,7 @@ swipe action</note>
<trans-unit id="Don't show again" xml:space="preserve">
<source>Don't show again</source>
<target>Don't show again</target>
<note>alert action</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
@ -3178,11 +3143,6 @@ chat item action</note>
<target>Error accepting contact request</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<target>Error accepting member</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>Error adding member(s)</target>
@ -3278,11 +3238,6 @@ chat item action</note>
<target>Error deleting chat database</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<target>Error deleting chat with member</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
<source>Error deleting chat!</source>
<target>Error deleting chat!</target>
@ -3391,7 +3346,7 @@ chat item action</note>
<trans-unit id="Error removing member" xml:space="preserve">
<source>Error removing member</source>
<target>Error removing member</target>
<note>alert title</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reordering lists" xml:space="preserve">
<source>Error reordering lists</source>
@ -4775,11 +4730,6 @@ This is your link for group %@!</target>
<target>Member</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<target>Member admission</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<target>Member inactive</target>
@ -4815,11 +4765,6 @@ This is your link for group %@!</target>
<target>Member will be removed from group - this cannot be undone!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<target>Member will join the group, accept member?</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<target>Members can add message reactions.</target>
@ -5240,11 +5185,6 @@ This is your link for group %@!</target>
<target>New member role</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<target>New member wants to join the group.</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
<source>New message</source>
<target>New message</target>
@ -5285,11 +5225,6 @@ This is your link for group %@!</target>
<target>No chats in list %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<target>No chats with members</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>No contacts selected</target>
@ -5482,8 +5417,7 @@ This is your link for group %@!</target>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<target>Ok</target>
<note>alert action
alert button</note>
<note>alert button</note>
</trans-unit>
<trans-unit id="Old database" xml:space="preserve">
<source>Old database</source>
@ -5895,11 +5829,6 @@ Error: %@</target>
<target>Please try to disable and re-enable notfications.</target>
<note>token info</note>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<target>Please wait for group moderators to review your request to join the group.</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
<source>Please wait for token activation to complete.</source>
<target>Please wait for token activation to complete.</target>
@ -6353,11 +6282,6 @@ swipe action</note>
<target>Reject contact request</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<target>Reject member?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>Relay server is only used if necessary. Another party can observe your IP address.</target>
@ -6468,11 +6392,6 @@ swipe action</note>
<target>Report reason?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<target>Report sent to moderators</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
<source>Report spam: only group moderators will see it.</source>
<target>Report spam: only group moderators will see it.</target>
@ -6588,16 +6507,6 @@ swipe action</note>
<target>Review conditions</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<target>Review members</target>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<target>Review members before admitting ("knocking").</target>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>Revoke</target>
@ -6654,11 +6563,6 @@ chat item action</note>
<target>Save (and notify contacts)</target>
<note>alert button</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<target>Save admission settings?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
<source>Save and notify contact</source>
<target>Save and notify contact</target>
@ -7174,11 +7078,6 @@ chat item action</note>
<target>Set it instead of system authentication.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<target>Set member admission</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
<source>Set message expiration in chats.</source>
<target>Set message expiration in chats.</target>
@ -8941,11 +8840,6 @@ Repeat join request?</target>
<target>You can view invitation link again in connection details.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<target>You can view your reports in Chat with admins.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
<source>You can't send messages!</source>
<target>You can't send messages!</target>
@ -9248,11 +9142,6 @@ Repeat connection request?</target>
<target>above, then choose:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<target>accepted %@</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
<source>accepted call</source>
<target>accepted call</target>
@ -9263,11 +9152,6 @@ Repeat connection request?</target>
<target>accepted invitation</target>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<target>accepted you</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
<source>admin</source>
<target>admin</target>
@ -9288,11 +9172,6 @@ Repeat connection request?</target>
<target>agreeing encryption…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<target>all</target>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<target>all members</target>
@ -9379,11 +9258,6 @@ marked deleted chat item preview text</note>
<target>calling…</target>
<note>call status</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<target>can't send messages</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
<source>cancelled %@</source>
<target>cancelled %@</target>
@ -9489,16 +9363,6 @@ marked deleted chat item preview text</note>
<target>contact %1$@ changed to %2$@</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<target>contact deleted</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<target>contact disabled</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
<source>contact has e2e encryption</source>
<target>contact has e2e encryption</target>
@ -9509,11 +9373,6 @@ marked deleted chat item preview text</note>
<target>contact has no e2e encryption</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<target>contact not ready</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
<source>creator</source>
<target>creator</target>
@ -9685,11 +9544,6 @@ pref value</note>
<target>group deleted</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<target>group is deleted</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
<source>group profile updated</source>
<target>group profile updated</target>
@ -9815,11 +9669,6 @@ pref value</note>
<target>connected</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<target>member has old version</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<target>message</target>
@ -9885,11 +9734,6 @@ pref value</note>
<target>no text</target>
<note>copied message info in history</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<target>not synchronized</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
<source>observer</source>
<target>observer</target>
@ -9900,7 +9744,6 @@ pref value</note>
<target>off</target>
<note>enabled status
group pref value
member criteria value
time to disappear</note>
</trans-unit>
<trans-unit id="offered %@" xml:space="preserve">
@ -9953,11 +9796,6 @@ time to disappear</note>
<target>pending approval</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<target>pending review</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
<source>quantum resistant e2e encryption</source>
<target>quantum resistant e2e encryption</target>
@ -9998,11 +9836,6 @@ time to disappear</note>
<target>removed contact address</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<target>removed from group</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
<source>removed profile picture</source>
<target>removed profile picture</target>
@ -10013,26 +9846,11 @@ time to disappear</note>
<target>removed you</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<target>request to join rejected</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
<source>requested to connect</source>
<target>requested to connect</target>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<target>review</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<target>reviewed by admins</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<target>saved</target>
@ -10222,11 +10040,6 @@ last received msg: %2$@</target>
<target>you</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<target>you accepted this member</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>you are invited to group</target>

View file

@ -565,14 +565,6 @@ time interval</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
<source>Accept conditions</source>
<target>Aceptar condiciones</target>
@ -594,10 +586,6 @@ swipe action</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
<source>Accepted conditions</source>
<target>Condiciones aceptadas</target>
@ -1594,23 +1582,11 @@ set passcode view</note>
<target>El chat será eliminado para tí. ¡No puede deshacerse!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Chats</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
<source>Check messages every 20 min.</source>
<target>Comprobar mensajes cada 20 min.</target>
@ -2426,10 +2402,6 @@ swipe action</note>
<target>¿Eliminar perfil?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
<source>Delete chat?</source>
<target>¿Eliminar chat?</target>
@ -2853,7 +2825,7 @@ swipe action</note>
<trans-unit id="Don't show again" xml:space="preserve">
<source>Don't show again</source>
<target>No volver a mostrar</target>
<note>alert action</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
@ -3171,10 +3143,6 @@ chat item action</note>
<target>Error al aceptar solicitud del contacto</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>Error al añadir miembro(s)</target>
@ -3270,10 +3238,6 @@ chat item action</note>
<target>Error al eliminar base de datos</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
<source>Error deleting chat!</source>
<target>¡Error al eliminar chat!</target>
@ -3382,7 +3346,7 @@ chat item action</note>
<trans-unit id="Error removing member" xml:space="preserve">
<source>Error removing member</source>
<target>Error al expulsar miembro</target>
<note>alert title</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reordering lists" xml:space="preserve">
<source>Error reordering lists</source>
@ -4766,10 +4730,6 @@ This is your link for group %@!</source>
<target>Miembro</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<target>Miembro inactivo</target>
@ -4805,10 +4765,6 @@ This is your link for group %@!</source>
<target>El miembro será expulsado del grupo. ¡No puede deshacerse!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<target>Los miembros pueden añadir reacciones a los mensajes.</target>
@ -5229,10 +5185,6 @@ This is your link for group %@!</source>
<target>Nuevo rol de miembro</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
<source>New message</source>
<target>Mensaje nuevo</target>
@ -5273,10 +5225,6 @@ This is your link for group %@!</source>
<target>Sin chats en la lista %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>Ningún contacto seleccionado</target>
@ -5469,8 +5417,7 @@ This is your link for group %@!</source>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<target>Ok</target>
<note>alert action
alert button</note>
<note>alert button</note>
</trans-unit>
<trans-unit id="Old database" xml:space="preserve">
<source>Old database</source>
@ -5881,10 +5828,6 @@ Error: %@</target>
<target>Por favor, intenta desactivar y reactivar las notificaciones.</target>
<note>token info</note>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
<source>Please wait for token activation to complete.</source>
<target>Por favor, espera a que el token de activación se complete.</target>
@ -6338,10 +6281,6 @@ swipe action</note>
<target>Rechazar solicitud de contacto</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>El servidor de retransmisión sólo se usa en caso de necesidad. Un tercero podría ver tu IP.</target>
@ -6452,10 +6391,6 @@ swipe action</note>
<target>¿Motivo del informe?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
<source>Report spam: only group moderators will see it.</source>
<target>Informar de spam: sólo los moderadores del grupo lo verán.</target>
@ -6571,14 +6506,6 @@ swipe action</note>
<target>Revisar condiciones</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>Revocar</target>
@ -6635,10 +6562,6 @@ chat item action</note>
<target>Guardar (y notificar contactos)</target>
<note>alert button</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
<source>Save and notify contact</source>
<target>Guardar y notificar contacto</target>
@ -7154,10 +7077,6 @@ chat item action</note>
<target>Úsalo en lugar de la autenticación del sistema.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
<source>Set message expiration in chats.</source>
<target>Establece el vencimiento para los mensajes en los chats.</target>
@ -8920,10 +8839,6 @@ Repeat join request?</source>
<target>Podrás ver el enlace de invitación en detalles de conexión.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
<source>You can't send messages!</source>
<target>¡No puedes enviar mensajes!</target>
@ -9226,10 +9141,6 @@ Repeat connection request?</source>
<target>y después elige:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
<source>accepted call</source>
<target>llamada aceptada</target>
@ -9240,10 +9151,6 @@ Repeat connection request?</source>
<target>invitación aceptada</target>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
<source>admin</source>
<target>administrador</target>
@ -9264,10 +9171,6 @@ Repeat connection request?</source>
<target>acordando cifrado…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<target>todos los miembros</target>
@ -9354,10 +9257,6 @@ marked deleted chat item preview text</note>
<target>llamando…</target>
<note>call status</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
<source>cancelled %@</source>
<target>cancelado %@</target>
@ -9463,14 +9362,6 @@ marked deleted chat item preview text</note>
<target>el contacto %1$@ ha cambiado a %2$@</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
<source>contact has e2e encryption</source>
<target>el contacto dispone de cifrado de extremo a extremo</target>
@ -9481,10 +9372,6 @@ marked deleted chat item preview text</note>
<target>el contacto no dispone de cifrado de extremo a extremo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
<source>creator</source>
<target>creador</target>
@ -9656,10 +9543,6 @@ pref value</note>
<target>grupo eliminado</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
<source>group profile updated</source>
<target>perfil de grupo actualizado</target>
@ -9785,10 +9668,6 @@ pref value</note>
<target>conectado</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<target>mensaje</target>
@ -9854,10 +9733,6 @@ pref value</note>
<target>sin texto</target>
<note>copied message info in history</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
<source>observer</source>
<target>observador</target>
@ -9868,7 +9743,6 @@ pref value</note>
<target>desactivado</target>
<note>enabled status
group pref value
member criteria value
time to disappear</note>
</trans-unit>
<trans-unit id="offered %@" xml:space="preserve">
@ -9921,10 +9795,6 @@ time to disappear</note>
<target>pendiente de aprobación</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
<source>quantum resistant e2e encryption</source>
<target>cifrado e2e resistente a tecnología cuántica</target>
@ -9965,10 +9835,6 @@ time to disappear</note>
<target>dirección de contacto eliminada</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
<source>removed profile picture</source>
<target>ha eliminado la imagen del perfil</target>
@ -9979,23 +9845,11 @@ time to disappear</note>
<target>te ha expulsado</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
<source>requested to connect</source>
<target>solicitado para conectar</target>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<target>guardado</target>
@ -10185,10 +10039,6 @@ last received msg: %2$@</source>
<target>tu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>has sido invitado a un grupo</target>

View file

@ -527,14 +527,6 @@ time interval</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
<source>Accept conditions</source>
<note>No comment provided by engineer.</note>
@ -555,10 +547,6 @@ swipe action</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
<source>Accepted conditions</source>
<note>No comment provided by engineer.</note>
@ -1449,23 +1437,11 @@ set passcode view</note>
<source>Chat will be deleted for you - this cannot be undone!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Keskustelut</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
<source>Check messages every 20 min.</source>
<note>No comment provided by engineer.</note>
@ -2198,10 +2174,6 @@ swipe action</note>
<target>Poista keskusteluprofiili?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
<source>Delete chat?</source>
<note>No comment provided by engineer.</note>
@ -2593,7 +2565,7 @@ swipe action</note>
<trans-unit id="Don't show again" xml:space="preserve">
<source>Don't show again</source>
<target>Älä näytä uudelleen</target>
<note>alert action</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
@ -2886,10 +2858,6 @@ chat item action</note>
<target>Virhe kontaktipyynnön hyväksymisessä</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>Virhe lisättäessä jäseniä</target>
@ -2976,10 +2944,6 @@ chat item action</note>
<target>Virhe keskustelujen tietokannan poistamisessa</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
<source>Error deleting chat!</source>
<target>Virhe keskutelun poistamisessa!</target>
@ -3080,7 +3044,7 @@ chat item action</note>
<trans-unit id="Error removing member" xml:space="preserve">
<source>Error removing member</source>
<target>Virhe poistettaessa jäsentä</target>
<note>alert title</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reordering lists" xml:space="preserve">
<source>Error reordering lists</source>
@ -4347,10 +4311,6 @@ This is your link for group %@!</source>
<target>Jäsen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<note>item status text</note>
@ -4382,10 +4342,6 @@ This is your link for group %@!</source>
<target>Jäsen poistetaan ryhmästä - tätä ei voi perua!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<target>Ryhmän jäsenet voivat lisätä viestireaktioita.</target>
@ -4760,10 +4716,6 @@ This is your link for group %@!</source>
<target>Uusi jäsenrooli</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
<source>New message</source>
<target>Uusi viesti</target>
@ -4800,10 +4752,6 @@ This is your link for group %@!</source>
<source>No chats in list %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>Kontakteja ei ole valittu</target>
@ -4974,8 +4922,7 @@ This is your link for group %@!</source>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<target>Ok</target>
<note>alert action
alert button</note>
<note>alert button</note>
</trans-unit>
<trans-unit id="Old database" xml:space="preserve">
<source>Old database</source>
@ -5349,10 +5296,6 @@ Error: %@</source>
<source>Please try to disable and re-enable notfications.</source>
<note>token info</note>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
<source>Please wait for token activation to complete.</source>
<note>token info</note>
@ -5764,10 +5707,6 @@ swipe action</note>
<target>Hylkää yhteyspyyntö</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>Välityspalvelinta käytetään vain tarvittaessa. Toinen osapuoli voi tarkkailla IP-osoitettasi.</target>
@ -5866,10 +5805,6 @@ swipe action</note>
<source>Report reason?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
<source>Report spam: only group moderators will see it.</source>
<note>report reason</note>
@ -5973,14 +5908,6 @@ swipe action</note>
<source>Review conditions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>Peruuta</target>
@ -6033,10 +5960,6 @@ chat item action</note>
<target>Tallenna (ja ilmoita kontakteille)</target>
<note>alert button</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
<source>Save and notify contact</source>
<target>Tallenna ja ilmoita kontaktille</target>
@ -6507,10 +6430,6 @@ chat item action</note>
<target>Aseta se järjestelmän todennuksen sijaan.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
<source>Set message expiration in chats.</source>
<note>No comment provided by engineer.</note>
@ -8098,10 +8017,6 @@ Repeat join request?</source>
<source>You can view invitation link again in connection details.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
<source>You can't send messages!</source>
<target>Et voi lähettää viestejä!</target>
@ -8388,10 +8303,6 @@ Repeat connection request?</source>
<target>edellä, valitse sitten:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
<source>accepted call</source>
<target>hyväksytty puhelu</target>
@ -8401,10 +8312,6 @@ Repeat connection request?</source>
<source>accepted invitation</source>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
<source>admin</source>
<target>ylläpitäjä</target>
@ -8424,10 +8331,6 @@ Repeat connection request?</source>
<target>hyväksyy salausta…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
@ -8505,10 +8408,6 @@ marked deleted chat item preview text</note>
<target>soittaa…</target>
<note>call status</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
<source>cancelled %@</source>
<target>peruutettu %@</target>
@ -8612,14 +8511,6 @@ marked deleted chat item preview text</note>
<source>contact %1$@ changed to %2$@</source>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
<source>contact has e2e encryption</source>
<target>kontaktilla on e2e-salaus</target>
@ -8630,10 +8521,6 @@ marked deleted chat item preview text</note>
<target>kontaktilla ei ole e2e-salausta</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
<source>creator</source>
<target>luoja</target>
@ -8800,10 +8687,6 @@ pref value</note>
<target>ryhmä poistettu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
<source>group profile updated</source>
<target>ryhmäprofiili päivitetty</target>
@ -8926,10 +8809,6 @@ pref value</note>
<target>yhdistetty</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<note>No comment provided by engineer.</note>
@ -8993,10 +8872,6 @@ pref value</note>
<target>ei tekstiä</target>
<note>copied message info in history</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
<source>observer</source>
<target>tarkkailija</target>
@ -9007,7 +8882,6 @@ pref value</note>
<target>pois</target>
<note>enabled status
group pref value
member criteria value
time to disappear</note>
</trans-unit>
<trans-unit id="offered %@" xml:space="preserve">
@ -9055,10 +8929,6 @@ time to disappear</note>
<source>pending approval</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
<source>quantum resistant e2e encryption</source>
<note>chat item text</note>
@ -9096,10 +8966,6 @@ time to disappear</note>
<source>removed contact address</source>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
<source>removed profile picture</source>
<note>profile update event chat item</note>
@ -9109,22 +8975,10 @@ time to disappear</note>
<target>poisti sinut</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
<source>requested to connect</source>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
@ -9295,10 +9149,6 @@ last received msg: %2$@</source>
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>sinut on kutsuttu ryhmään</target>

View file

@ -565,14 +565,6 @@ time interval</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
<source>Accept conditions</source>
<target>Accepter les conditions</target>
@ -594,10 +586,6 @@ swipe action</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
<source>Accepted conditions</source>
<target>Conditions acceptées</target>
@ -1592,23 +1580,11 @@ set passcode view</note>
<target>Le discussion sera supprimé pour vous - il n'est pas possible de revenir en arrière!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Discussions</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
<source>Check messages every 20 min.</source>
<target>Consulter les messages toutes les 20 minutes.</target>
@ -2424,10 +2400,6 @@ swipe action</note>
<target>Supprimer le profil du chat ?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
<source>Delete chat?</source>
<target>Supprimer la discussion?</target>
@ -2851,7 +2823,7 @@ swipe action</note>
<trans-unit id="Don't show again" xml:space="preserve">
<source>Don't show again</source>
<target>Ne plus afficher</target>
<note>alert action</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
@ -3169,10 +3141,6 @@ chat item action</note>
<target>Erreur de validation de la demande de contact</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>Erreur lors de l'ajout de membre·s</target>
@ -3268,10 +3236,6 @@ chat item action</note>
<target>Erreur lors de la suppression de la base de données du chat</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
<source>Error deleting chat!</source>
<target>Erreur lors de la suppression du chat!</target>
@ -3380,7 +3344,7 @@ chat item action</note>
<trans-unit id="Error removing member" xml:space="preserve">
<source>Error removing member</source>
<target>Erreur lors de la suppression d'un membre</target>
<note>alert title</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reordering lists" xml:space="preserve">
<source>Error reordering lists</source>
@ -4748,10 +4712,6 @@ Voici votre lien pour le groupe %@ !</target>
<target>Membre</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<target>Membre inactif</target>
@ -4786,10 +4746,6 @@ Voici votre lien pour le groupe %@ !</target>
<target>Ce membre sera retiré du groupe - impossible de revenir en arrière!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<target>Les membres du groupe peuvent ajouter des réactions aux messages.</target>
@ -5204,10 +5160,6 @@ Voici votre lien pour le groupe %@ !</target>
<target>Nouveau rôle</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
<source>New message</source>
<target>Nouveau message</target>
@ -5245,10 +5197,6 @@ Voici votre lien pour le groupe %@ !</target>
<source>No chats in list %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>Aucun contact sélectionné</target>
@ -5435,8 +5383,7 @@ Voici votre lien pour le groupe %@ !</target>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<target>Ok</target>
<note>alert action
alert button</note>
<note>alert button</note>
</trans-unit>
<trans-unit id="Old database" xml:space="preserve">
<source>Old database</source>
@ -5843,10 +5790,6 @@ Erreur: %@</target>
<source>Please try to disable and re-enable notfications.</source>
<note>token info</note>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
<source>Please wait for token activation to complete.</source>
<note>token info</note>
@ -6291,10 +6234,6 @@ swipe action</note>
<target>Rejeter la demande de contact</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>Le serveur relais n'est utilisé que si nécessaire. Un tiers peut observer votre adresse IP.</target>
@ -6400,10 +6339,6 @@ swipe action</note>
<source>Report reason?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
<source>Report spam: only group moderators will see it.</source>
<note>report reason</note>
@ -6514,14 +6449,6 @@ swipe action</note>
<target>Vérifier les conditions</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>Révoquer</target>
@ -6578,10 +6505,6 @@ chat item action</note>
<target>Enregistrer (et en informer les contacts)</target>
<note>alert button</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
<source>Save and notify contact</source>
<target>Enregistrer et en informer le contact</target>
@ -7094,10 +7017,6 @@ chat item action</note>
<target>Il permet de remplacer l'authentification du système.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
<source>Set message expiration in chats.</source>
<note>No comment provided by engineer.</note>
@ -8843,10 +8762,6 @@ Répéter la demande d'adhésion ?</target>
<target>Vous pouvez à nouveau consulter le lien d'invitation dans les détails de la connexion.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
<source>You can't send messages!</source>
<target>Vous ne pouvez pas envoyer de messages !</target>
@ -9148,10 +9063,6 @@ Répéter la demande de connexion ?</target>
<target>ci-dessus, puis choisissez:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
<source>accepted call</source>
<target>appel accepté</target>
@ -9162,10 +9073,6 @@ Répéter la demande de connexion ?</target>
<target>invitation acceptée</target>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
<source>admin</source>
<target>admin</target>
@ -9186,10 +9093,6 @@ Répéter la demande de connexion ?</target>
<target>négociation du chiffrement…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<target>tous les membres</target>
@ -9275,10 +9178,6 @@ marked deleted chat item preview text</note>
<target>appel…</target>
<note>call status</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
<source>cancelled %@</source>
<target>annulé %@</target>
@ -9384,14 +9283,6 @@ marked deleted chat item preview text</note>
<target>le contact %1$@ est devenu %2$@</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
<source>contact has e2e encryption</source>
<target>Ce contact a le chiffrement de bout en bout</target>
@ -9402,10 +9293,6 @@ marked deleted chat item preview text</note>
<target>Ce contact n'a pas le chiffrement de bout en bout</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
<source>creator</source>
<target>créateur</target>
@ -9577,10 +9464,6 @@ pref value</note>
<target>groupe supprimé</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
<source>group profile updated</source>
<target>mise à jour du profil de groupe</target>
@ -9706,10 +9589,6 @@ pref value</note>
<target>est connecté·e</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<target>message</target>
@ -9774,10 +9653,6 @@ pref value</note>
<target>aucun texte</target>
<note>copied message info in history</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
<source>observer</source>
<target>observateur</target>
@ -9788,7 +9663,6 @@ pref value</note>
<target>off</target>
<note>enabled status
group pref value
member criteria value
time to disappear</note>
</trans-unit>
<trans-unit id="offered %@" xml:space="preserve">
@ -9839,10 +9713,6 @@ time to disappear</note>
<source>pending approval</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
<source>quantum resistant e2e encryption</source>
<target>chiffrement e2e résistant post-quantique</target>
@ -9882,10 +9752,6 @@ time to disappear</note>
<target>suppression de l'adresse de contact</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
<source>removed profile picture</source>
<target>suppression de la photo de profil</target>
@ -9896,23 +9762,11 @@ time to disappear</note>
<target>vous a retiré</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
<source>requested to connect</source>
<target>demande à se connecter</target>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<target>enregistré</target>
@ -10102,10 +9956,6 @@ dernier message reçu: %2$@</target>
<target>vous</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>vous êtes invité·e au groupe</target>

View file

@ -565,16 +565,6 @@ time interval</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<target>Befogadás tagként</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<target>Befogadás megfigyelőként</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
<source>Accept conditions</source>
<target>Feltételek elfogadása</target>
@ -596,11 +586,6 @@ swipe action</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<target>Tag befogadása</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
<source>Accepted conditions</source>
<target>Elfogadott feltételek</target>
@ -1597,26 +1582,11 @@ set passcode view</note>
<target>A csevegés törölve lesz az Ön számára ez a művelet nem vonható vissza!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<target>Csevegés az adminisztrátorokkal</target>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<target>Csevegés a taggal</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Csevegések</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<target>Csevegés a tagokkal</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
<source>Check messages every 20 min.</source>
<target>Üzenetek ellenőrzése 20 percenként.</target>
@ -2270,7 +2240,7 @@ Ez a saját egyszer használható meghívója!</target>
</trans-unit>
<trans-unit id="Database IDs and Transport isolation option." xml:space="preserve">
<source>Database IDs and Transport isolation option.</source>
<target>Adatbázis-azonosítók és átvitelelkülönítési beállítások.</target>
<target>Adatbázis-azonosítók és átvitel-izolációs beállítások.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Database downgrade" xml:space="preserve">
@ -2368,7 +2338,7 @@ Ez a saját egyszer használható meghívója!</target>
</trans-unit>
<trans-unit id="Decryption error" xml:space="preserve">
<source>Decryption error</source>
<target>Titkosításvisszafejtési hiba</target>
<target>Titkosítás visszafejtési hiba</target>
<note>message decrypt error item</note>
</trans-unit>
<trans-unit id="Delete" xml:space="preserve">
@ -2432,11 +2402,6 @@ swipe action</note>
<target>Törli a csevegési profilt?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<target>Törli a taggal való csevegést?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
<source>Delete chat?</source>
<target>Törli a csevegést?</target>
@ -2709,7 +2674,7 @@ swipe action</note>
</trans-unit>
<trans-unit id="Different names, avatars and transport isolation." xml:space="preserve">
<source>Different names, avatars and transport isolation.</source>
<target>Különböző nevek, profilképek és átvitelizoláció.</target>
<target>Különböző nevek, profilképek és átvitel-izoláció.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Direct messages" xml:space="preserve">
@ -2860,7 +2825,7 @@ swipe action</note>
<trans-unit id="Don't show again" xml:space="preserve">
<source>Don't show again</source>
<target>Ne mutasd újra</target>
<note>alert action</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
@ -3178,11 +3143,6 @@ chat item action</note>
<target>Hiba történt a meghívási kérés elfogadásakor</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<target>Hiba a tag befogadásakor</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>Hiba történt a tag(ok) hozzáadásakor</target>
@ -3278,11 +3238,6 @@ chat item action</note>
<target>Hiba történt a csevegési adatbázis törlésekor</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<target>Hiba a taggal való csevegés törlésekor</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
<source>Error deleting chat!</source>
<target>Hiba történt a csevegés törlésekor!</target>
@ -3310,7 +3265,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Error deleting user profile" xml:space="preserve">
<source>Error deleting user profile</source>
<target>Hiba történt a felhasználói profil törlésekor</target>
<target>Hiba történt a felhasználó-profil törlésekor</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error downloading the archive" xml:space="preserve">
@ -3391,7 +3346,7 @@ chat item action</note>
<trans-unit id="Error removing member" xml:space="preserve">
<source>Error removing member</source>
<target>Hiba történt a tag eltávolításakor</target>
<note>alert title</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reordering lists" xml:space="preserve">
<source>Error reordering lists</source>
@ -4494,7 +4449,7 @@ További fejlesztések hamarosan!</target>
</trans-unit>
<trans-unit id="It can happen when you or your connection used the old database backup." xml:space="preserve">
<source>It can happen when you or your connection used the old database backup.</source>
<target>Ez akkor fordulhat elő, ha Ön vagy a partnere egy régi adatbázis biztonsági mentését használta.</target>
<target>Ez akkor fordulhat elő, ha Ön vagy a partnere régi adatbázis biztonsági mentést használt.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It can happen when:&#10;1. The messages expired in the sending client after 2 days or on the server after 30 days.&#10;2. Message decryption failed, because you or your contact used old database backup.&#10;3. The connection was compromised." xml:space="preserve">
@ -4504,7 +4459,7 @@ További fejlesztések hamarosan!</target>
3. The connection was compromised.</source>
<target>Ez akkor fordulhat elő, ha:
1. Az üzenetek 2 nap után, vagy a kiszolgálón 30 nap után lejártak.
2. Nem sikerült az üzenetet visszafejteni, mert Ön, vagy a partnere egy régi adatbázis biztonsági mentését használta.
2. Nem sikerült az üzenetet visszafejteni, mert Ön, vagy a partnere régebbi adatbázis biztonsági mentést használt.
3. A kapcsolat sérült.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@ -4775,11 +4730,6 @@ Ez a saját hivatkozása a(z) %@ nevű csoporthoz!</target>
<target>Tag</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<target>Tagbefogadás</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<target>Inaktív tag</target>
@ -4815,11 +4765,6 @@ Ez a saját hivatkozása a(z) %@ nevű csoporthoz!</target>
<target>A tag el lesz távolítva a csoportból ez a művelet nem vonható vissza!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<target>A tag csatlakozni akar a csoporthoz, befogadja a tagot?</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<target>A tagok reakciókat adhatnak hozzá az üzenetekhez.</target>
@ -4997,12 +4942,12 @@ Ez a saját hivatkozása a(z) %@ nevű csoporthoz!</target>
</trans-unit>
<trans-unit id="Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." xml:space="preserve">
<source>Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery.</source>
<target>Az üzenetek, a fájlok és a hívások **végpontok közötti titkosítással**, kompromittálás előtti és utáni titkosságvédelemmel, illetve letagadhatósággal vannak védve.</target>
<target>Az üzenetek, a fájlok és a hívások **végpontok közötti titkosítással**, sérülés utáni titkosságvédelemmel és -helyreállítással, továbbá letagadhatósággal vannak védve.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery." xml:space="preserve">
<source>Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery.</source>
<target>Az üzenetek, a fájlok és a hívások **végpontok közötti kvantumbiztos titkosítással**, kompromittálás előtti és utáni titkosságvédelemmel, illetve letagadhatósággal vannak védve.</target>
<target>Az üzenetek, a fájlok és a hívások **végpontok közötti kvantumbiztos titkosítással**, sérülés utáni titkosságvédelemmel és -helyreállítással, továbbá letagadhatósággal vannak védve.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Migrate device" xml:space="preserve">
@ -5240,11 +5185,6 @@ Ez a saját hivatkozása a(z) %@ nevű csoporthoz!</target>
<target>Új tag szerepköre</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<target>Új tag szeretne csatlakozni a csoporthoz.</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
<source>New message</source>
<target>Új üzenet</target>
@ -5285,11 +5225,6 @@ Ez a saját hivatkozása a(z) %@ nevű csoporthoz!</target>
<target>Nincsenek csevegések a(z) %@ nevű listában</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<target>Nincsenek csevegések a tagokkal</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>Nincs partner kijelölve</target>
@ -5412,7 +5347,7 @@ Ez a saját hivatkozása a(z) %@ nevű csoporthoz!</target>
</trans-unit>
<trans-unit id="No user identifiers." xml:space="preserve">
<source>No user identifiers.</source>
<target>Nincsenek felhasználói azonosítók.</target>
<target>Nincsenek felhasználó-azonosítók.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Not compatible!" xml:space="preserve">
@ -5482,8 +5417,7 @@ Ez a saját hivatkozása a(z) %@ nevű csoporthoz!</target>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<target>Rendben</target>
<note>alert action
alert button</note>
<note>alert button</note>
</trans-unit>
<trans-unit id="Old database" xml:space="preserve">
<source>Old database</source>
@ -5641,7 +5575,6 @@ VPN engedélyezése szükséges.</target>
</trans-unit>
<trans-unit id="Open link?" xml:space="preserve">
<source>Open link?</source>
<target>Megnyitja a hivatkozást?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Open migration to another device" xml:space="preserve">
@ -5895,11 +5828,6 @@ Hiba: %@</target>
<target>Próbálja meg letiltani és újra engedélyezni az értesítéseket.</target>
<note>token info</note>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<target>Várja meg, amíg a csoport moderátorai áttekintik a csoporthoz való csatlakozási kérelmét.</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
<source>Please wait for token activation to complete.</source>
<target>Várjon, amíg a token aktiválása befejeződik.</target>
@ -6353,19 +6281,14 @@ swipe action</note>
<target>Meghívási kérés elutasítása</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<target>Elutasítja a tagot?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>A továbbítókiszolgáló csak szükség esetén lesz használva. Egy másik fél megfigyelheti az IP-címét.</target>
<target>A továbbítókiszolgáló csak szükség esetén lesz használva. Egy másik fél megfigyelheti az IP-címet.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Relay server protects your IP address, but it can observe the duration of the call." xml:space="preserve">
<source>Relay server protects your IP address, but it can observe the duration of the call.</source>
<target>A továbbítókiszolgáló megvédi az IP-címét, de megfigyelheti a hívás időtartamát.</target>
<target>A továbbítókiszolgáló megvédi az Ön IP-címét, de megfigyelheti a hívás időtartamát.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Remove" xml:space="preserve">
@ -6468,11 +6391,6 @@ swipe action</note>
<target>Jelentés indoklása?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<target>A jelentés el lett küldve a moderátoroknak</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
<source>Report spam: only group moderators will see it.</source>
<target>Kéretlen tartalom jelentése: csak a csoport moderátorai látják.</target>
@ -6588,16 +6506,6 @@ swipe action</note>
<target>Feltételek felülvizsgálata</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<target>Tagok áttekintése</target>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<target>Tagok áttekintése a befogadás előtt (kopogtatás).</target>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>Visszavonás</target>
@ -6654,11 +6562,6 @@ chat item action</note>
<target>Mentés (és a partnerek értesítése)</target>
<note>alert button</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<target>Elmenti a befogadási beállításokat?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
<source>Save and notify contact</source>
<target>Mentés és a partner értesítése</target>
@ -7174,11 +7077,6 @@ chat item action</note>
<target>Beállítás a rendszer-hitelesítés helyett.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<target>Tagbefogadás beállítása</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
<source>Set message expiration in chats.</source>
<target>Üzenetek eltűnési idejének módosítása a csevegésekben.</target>
@ -7437,7 +7335,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="SimpleX protocols reviewed by Trail of Bits." xml:space="preserve">
<source>SimpleX protocols reviewed by Trail of Bits.</source>
<target>A SimpleX-protokollokat a Trail of Bits auditálta.</target>
<target>A SimpleX Chat biztonsága a Trail of Bits által lett felülvizsgálva.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Simplified incognito mode" xml:space="preserve">
@ -8129,7 +8027,7 @@ A funkció bekapcsolása előtt a rendszer felszólítja a képernyőzár beáll
</trans-unit>
<trans-unit id="Transport isolation" xml:space="preserve">
<source>Transport isolation</source>
<target>Átvitelelkülönítés</target>
<target>Átvitel-izoláció</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Transport sessions" xml:space="preserve">
@ -8441,7 +8339,7 @@ A kapcsolódáshoz kérje meg a partnerét, hogy hozzon létre egy másik kapcso
</trans-unit>
<trans-unit id="Use private routing with unknown servers when IP address is not protected." xml:space="preserve">
<source>Use private routing with unknown servers when IP address is not protected.</source>
<target>Használjon privát útválasztást az ismeretlen kiszolgálókkal, ha az IP-cím nem védett.</target>
<target>Használjon privát útválasztást ismeretlen kiszolgálókkal, ha az IP-cím nem védett.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use private routing with unknown servers." xml:space="preserve">
@ -8726,12 +8624,12 @@ A kapcsolódáshoz kérje meg a partnerét, hogy hozzon létre egy másik kapcso
</trans-unit>
<trans-unit id="Without Tor or VPN, your IP address will be visible to file servers." xml:space="preserve">
<source>Without Tor or VPN, your IP address will be visible to file servers.</source>
<target>Tor vagy VPN nélkül az IP-címe láthatóvá válik a fájlkiszolgálók számára.</target>
<target>Tor vagy VPN nélkül az Ön IP-címe látható lesz a fájlkiszolgálók számára.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." xml:space="preserve">
<source>Without Tor or VPN, your IP address will be visible to these XFTP relays: %@.</source>
<target>Tor vagy VPN nélkül az IP-címe láthatóvá válik a következő XFTP-továbbítókiszolgálók számára: %@.</target>
<target>Tor vagy VPN nélkül az Ön IP-címe látható lesz a következő XFTP-továbbítókiszolgálók számára: %@.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Wrong database passphrase" xml:space="preserve">
@ -8941,11 +8839,6 @@ Megismétli a meghívási kérést?</target>
<target>A meghívási hivatkozást újra megtekintheti a kapcsolat részleteinél.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<target>A jelentéseket megtekintheti a „Csevegés az adminisztrátorokkal” menüben.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
<source>You can't send messages!</source>
<target>Nem lehet üzeneteket küldeni!</target>
@ -9185,7 +9078,7 @@ Megismétli a meghívási kérést?</target>
</trans-unit>
<trans-unit id="Your profile is stored on your device and only shared with your contacts." xml:space="preserve">
<source>Your profile is stored on your device and only shared with your contacts.</source>
<target>A profilja az eszközén van tárolva és csak a partnereivel van megosztva.</target>
<target>A profilja csak a partnereivel van megosztva.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." xml:space="preserve">
@ -9248,11 +9141,6 @@ Megismétli a meghívási kérést?</target>
<target>gombra fent, majd válassza ki:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<target>befogadta őt: %@</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
<source>accepted call</source>
<target>fogadott hívás</target>
@ -9263,11 +9151,6 @@ Megismétli a meghívási kérést?</target>
<target>elfogadott meghívó</target>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<target>befogadta Önt</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
<source>admin</source>
<target>adminisztrátor</target>
@ -9288,11 +9171,6 @@ Megismétli a meghívási kérést?</target>
<target>titkosítás elfogadása…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<target>összes</target>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<target>összes tag</target>
@ -9379,11 +9257,6 @@ marked deleted chat item preview text</note>
<target>hívás…</target>
<note>call status</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<target>nem lehet üzeneteket küldeni</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
<source>cancelled %@</source>
<target>%@ visszavonva</target>
@ -9489,16 +9362,6 @@ marked deleted chat item preview text</note>
<target>%1$@ a következőre módosította a nevét: %2$@</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<target>partner törölve</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<target>partner letiltva</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
<source>contact has e2e encryption</source>
<target>a partner e2e titkosítással rendelkezik</target>
@ -9509,11 +9372,6 @@ marked deleted chat item preview text</note>
<target>a partner nem rendelkezik e2e titkosítással</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<target>a kapcsolat nem áll készen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
<source>creator</source>
<target>készítő</target>
@ -9685,11 +9543,6 @@ pref value</note>
<target>a csoport törölve</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<target>csoport törölve</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
<source>group profile updated</source>
<target>csoportprofil frissítve</target>
@ -9815,11 +9668,6 @@ pref value</note>
<target>kapcsolódott</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<target>a tag régi verziót használ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<target>üzenet</target>
@ -9885,11 +9733,6 @@ pref value</note>
<target>nincs szöveg</target>
<note>copied message info in history</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<target>nincs szinkronizálva</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
<source>observer</source>
<target>megfigyelő</target>
@ -9900,7 +9743,6 @@ pref value</note>
<target>kikapcsolva</target>
<note>enabled status
group pref value
member criteria value
time to disappear</note>
</trans-unit>
<trans-unit id="offered %@" xml:space="preserve">
@ -9953,11 +9795,6 @@ time to disappear</note>
<target>jóváhagyásra vár</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<target>függőben lévő áttekintés</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
<source>quantum resistant e2e encryption</source>
<target>végpontok közötti kvantumbiztos titkosítás</target>
@ -9998,11 +9835,6 @@ time to disappear</note>
<target>eltávolította a kapcsolattartási címet</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<target>eltávolítva a csoportból</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
<source>removed profile picture</source>
<target>eltávolította a profilképét</target>
@ -10013,26 +9845,11 @@ time to disappear</note>
<target>eltávolította Önt</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<target>csatlakozási kérelem elutasítva</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
<source>requested to connect</source>
<target>Függőben lévő meghívási kérelem</target>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<target>áttekintés</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<target>áttekintve a moderátorok által</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<target>mentett</target>
@ -10222,11 +10039,6 @@ utoljára fogadott üzenet: %2$@</target>
<target>Ön</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<target>Ön befogadta ezt a tagot</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>Ön meghívást kapott a csoportba</target>

View file

@ -565,16 +565,6 @@ time interval</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<target>Accetta come membro</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<target>Accetta come osservatore</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
<source>Accept conditions</source>
<target>Accetta le condizioni</target>
@ -596,11 +586,6 @@ swipe action</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<target>Accetta membro</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
<source>Accepted conditions</source>
<target>Condizioni accettate</target>
@ -1597,26 +1582,11 @@ set passcode view</note>
<target>La chat verrà eliminata solo per te, non è reversibile!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<target>Chat con amministratori</target>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<target>Chatta con il membro</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Chat</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<target>Chat con membri</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
<source>Check messages every 20 min.</source>
<target>Controlla i messaggi ogni 20 min.</target>
@ -2432,11 +2402,6 @@ swipe action</note>
<target>Eliminare il profilo di chat?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<target>Eliminare la chat con il membro?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
<source>Delete chat?</source>
<target>Eliminare la chat?</target>
@ -2860,7 +2825,7 @@ swipe action</note>
<trans-unit id="Don't show again" xml:space="preserve">
<source>Don't show again</source>
<target>Non mostrare più</target>
<note>alert action</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
@ -3178,11 +3143,6 @@ chat item action</note>
<target>Errore nell'accettazione della richiesta di contatto</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<target>Errore di accettazione del membro</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>Errore di aggiunta membro/i</target>
@ -3278,11 +3238,6 @@ chat item action</note>
<target>Errore nell'eliminazione del database della chat</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<target>Errore di eliminazione della chat con il membro</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
<source>Error deleting chat!</source>
<target>Errore nell'eliminazione della chat!</target>
@ -3391,7 +3346,7 @@ chat item action</note>
<trans-unit id="Error removing member" xml:space="preserve">
<source>Error removing member</source>
<target>Errore nella rimozione del membro</target>
<note>alert title</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reordering lists" xml:space="preserve">
<source>Error reordering lists</source>
@ -4775,11 +4730,6 @@ Questo è il tuo link per il gruppo %@!</target>
<target>Membro</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<target>Ammissione del membro</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<target>Membro inattivo</target>
@ -4815,11 +4765,6 @@ Questo è il tuo link per il gruppo %@!</target>
<target>Il membro verrà rimosso dal gruppo, non è reversibile!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<target>Il membro entrerà nel gruppo, accettarlo?</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<target>I membri del gruppo possono aggiungere reazioni ai messaggi.</target>
@ -5240,11 +5185,6 @@ Questo è il tuo link per il gruppo %@!</target>
<target>Nuovo ruolo del membro</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<target>Un nuovo membro vuole entrare nel gruppo.</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
<source>New message</source>
<target>Nuovo messaggio</target>
@ -5285,11 +5225,6 @@ Questo è il tuo link per il gruppo %@!</target>
<target>Nessuna chat nell'elenco %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<target>Nessuna chat con membri</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>Nessun contatto selezionato</target>
@ -5482,8 +5417,7 @@ Questo è il tuo link per il gruppo %@!</target>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<target>Ok</target>
<note>alert action
alert button</note>
<note>alert button</note>
</trans-unit>
<trans-unit id="Old database" xml:space="preserve">
<source>Old database</source>
@ -5641,7 +5575,6 @@ Richiede l'attivazione della VPN.</target>
</trans-unit>
<trans-unit id="Open link?" xml:space="preserve">
<source>Open link?</source>
<target>Aprire il link?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Open migration to another device" xml:space="preserve">
@ -5895,11 +5828,6 @@ Errore: %@</target>
<target>Prova a disattivare e riattivare le notifiche.</target>
<note>token info</note>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<target>Attendi che i moderatori del gruppo revisionino la tua richiesta di entrare nel gruppo.</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
<source>Please wait for token activation to complete.</source>
<target>Attendi il completamento dell'attivazione del token.</target>
@ -6353,11 +6281,6 @@ swipe action</note>
<target>Rifiuta la richiesta di contatto</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<target>Rifiutare il membro?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>Il server relay viene usato solo se necessario. Un altro utente può osservare il tuo indirizzo IP.</target>
@ -6468,11 +6391,6 @@ swipe action</note>
<target>Motivo della segnalazione?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<target>Segnalazione inviata ai moderatori</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
<source>Report spam: only group moderators will see it.</source>
<target>Segnala spam: solo i moderatori del gruppo lo vedranno.</target>
@ -6588,16 +6506,6 @@ swipe action</note>
<target>Leggi le condizioni</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<target>Revisiona i membri</target>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<target>Revisiona i membri prima di ammetterli ("bussare").</target>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>Revoca</target>
@ -6654,11 +6562,6 @@ chat item action</note>
<target>Salva (e avvisa i contatti)</target>
<note>alert button</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<target>Salvare le impostazioni di ammissione?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
<source>Save and notify contact</source>
<target>Salva e avvisa il contatto</target>
@ -7174,11 +7077,6 @@ chat item action</note>
<target>Impostalo al posto dell'autenticazione di sistema.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<target>Imposta l'ammissione del membro</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
<source>Set message expiration in chats.</source>
<target>Imposta la scadenza dei messaggi nelle chat.</target>
@ -8941,11 +8839,6 @@ Ripetere la richiesta di ingresso?</target>
<target>Puoi vedere di nuovo il link di invito nei dettagli di connessione.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<target>Puoi vedere le tue segnalazioni nella chat con gli amministratori.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
<source>You can't send messages!</source>
<target>Non puoi inviare messaggi!</target>
@ -9248,11 +9141,6 @@ Ripetere la richiesta di connessione?</target>
<target>sopra, quindi scegli:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<target>%@ accettato</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
<source>accepted call</source>
<target>chiamata accettata</target>
@ -9263,11 +9151,6 @@ Ripetere la richiesta di connessione?</target>
<target>invito accettato</target>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<target>ti ha accettato/a</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
<source>admin</source>
<target>amministratore</target>
@ -9288,11 +9171,6 @@ Ripetere la richiesta di connessione?</target>
<target>concordando la crittografia…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<target>tutti</target>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<target>tutti i membri</target>
@ -9379,11 +9257,6 @@ marked deleted chat item preview text</note>
<target>chiamata…</target>
<note>call status</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<target>impossibile inviare messaggi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
<source>cancelled %@</source>
<target>annullato %@</target>
@ -9489,16 +9362,6 @@ marked deleted chat item preview text</note>
<target>contatto %1$@ cambiato in %2$@</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<target>contatto eliminato</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<target>contatto disattivato</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
<source>contact has e2e encryption</source>
<target>il contatto ha la crittografia e2e</target>
@ -9509,11 +9372,6 @@ marked deleted chat item preview text</note>
<target>il contatto non ha la crittografia e2e</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<target>contatto non pronto</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
<source>creator</source>
<target>creatore</target>
@ -9685,11 +9543,6 @@ pref value</note>
<target>gruppo eliminato</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<target>il gruppo è eliminato</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
<source>group profile updated</source>
<target>profilo del gruppo aggiornato</target>
@ -9815,11 +9668,6 @@ pref value</note>
<target>si è connesso/a</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<target>il membro ha una versione vecchia</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<target>messaggio</target>
@ -9885,11 +9733,6 @@ pref value</note>
<target>nessun testo</target>
<note>copied message info in history</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<target>non sincronizzato</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
<source>observer</source>
<target>osservatore</target>
@ -9900,7 +9743,6 @@ pref value</note>
<target>off</target>
<note>enabled status
group pref value
member criteria value
time to disappear</note>
</trans-unit>
<trans-unit id="offered %@" xml:space="preserve">
@ -9953,11 +9795,6 @@ time to disappear</note>
<target>in attesa di approvazione</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<target>in attesa di revisione</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
<source>quantum resistant e2e encryption</source>
<target>crittografia e2e resistente alla quantistica</target>
@ -9998,11 +9835,6 @@ time to disappear</note>
<target>indirizzo di contatto rimosso</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<target>rimosso dal gruppo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
<source>removed profile picture</source>
<target>immagine del profilo rimossa</target>
@ -10013,26 +9845,11 @@ time to disappear</note>
<target>ti ha rimosso/a</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<target>richiesta di entrare rifiutata</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
<source>requested to connect</source>
<target>richiesto di connettersi</target>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<target>revisiona</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<target>revisionato dagli amministratori</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<target>salvato</target>
@ -10222,11 +10039,6 @@ ultimo msg ricevuto: %2$@</target>
<target>tu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<target>hai accettato questo membro</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>sei stato/a invitato/a al gruppo</target>

View file

@ -561,14 +561,6 @@ time interval</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
<source>Accept conditions</source>
<note>No comment provided by engineer.</note>
@ -589,10 +581,6 @@ swipe action</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
<source>Accepted conditions</source>
<note>No comment provided by engineer.</note>
@ -1500,23 +1488,11 @@ set passcode view</note>
<source>Chat will be deleted for you - this cannot be undone!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>チャット</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
<source>Check messages every 20 min.</source>
<note>No comment provided by engineer.</note>
@ -2268,10 +2244,6 @@ swipe action</note>
<target>チャットのプロフィールを削除しますか?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
<source>Delete chat?</source>
<note>No comment provided by engineer.</note>
@ -2665,7 +2637,7 @@ swipe action</note>
<trans-unit id="Don't show again" xml:space="preserve">
<source>Don't show again</source>
<target>次から表示しない</target>
<note>alert action</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
@ -2959,10 +2931,6 @@ chat item action</note>
<target>連絡先リクエストの承諾にエラー発生</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>メンバー追加にエラー発生</target>
@ -3050,10 +3018,6 @@ chat item action</note>
<target>チャットデータベース削除にエラー発生</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
<source>Error deleting chat!</source>
<target>チャット削除にエラー発生!</target>
@ -3153,7 +3117,7 @@ chat item action</note>
<trans-unit id="Error removing member" xml:space="preserve">
<source>Error removing member</source>
<target>メンバー除名にエラー発生</target>
<note>alert title</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reordering lists" xml:space="preserve">
<source>Error reordering lists</source>
@ -4420,10 +4384,6 @@ This is your link for group %@!</source>
<target>メンバー</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<note>item status text</note>
@ -4455,10 +4415,6 @@ This is your link for group %@!</source>
<target>メンバーをグループから除名する (※元に戻せません※)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<target>グループメンバーはメッセージへのリアクションを追加できます。</target>
@ -4836,10 +4792,6 @@ This is your link for group %@!</source>
<target>新しいメンバーの役割</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
<source>New message</source>
<target>新しいメッセージ</target>
@ -4876,10 +4828,6 @@ This is your link for group %@!</source>
<source>No chats in list %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>連絡先が選択されてません</target>
@ -5050,8 +4998,7 @@ This is your link for group %@!</source>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<target>OK</target>
<note>alert action
alert button</note>
<note>alert button</note>
</trans-unit>
<trans-unit id="Old database" xml:space="preserve">
<source>Old database</source>
@ -5426,10 +5373,6 @@ Error: %@</source>
<source>Please try to disable and re-enable notfications.</source>
<note>token info</note>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
<source>Please wait for token activation to complete.</source>
<note>token info</note>
@ -5841,10 +5784,6 @@ swipe action</note>
<target>連絡要求を拒否する</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>中継サーバーは必要な場合にのみ使用されます。 別の当事者があなたの IP アドレスを監視できます。</target>
@ -5943,10 +5882,6 @@ swipe action</note>
<source>Report reason?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
<source>Report spam: only group moderators will see it.</source>
<note>report reason</note>
@ -6050,14 +5985,6 @@ swipe action</note>
<source>Review conditions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>取り消す</target>
@ -6110,10 +6037,6 @@ chat item action</note>
<target>保存(連絡先に通知)</target>
<note>alert button</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
<source>Save and notify contact</source>
<target>保存して、連絡先にに知らせる</target>
@ -6577,10 +6500,6 @@ chat item action</note>
<target>システム認証の代わりに設定します。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
<source>Set message expiration in chats.</source>
<note>No comment provided by engineer.</note>
@ -8169,10 +8088,6 @@ Repeat join request?</source>
<source>You can view invitation link again in connection details.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
<source>You can't send messages!</source>
<target>メッセージを送信できませんでした!</target>
@ -8459,10 +8374,6 @@ Repeat connection request?</source>
<target>上で選んでください:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
<source>accepted call</source>
<target>受けた通話</target>
@ -8472,10 +8383,6 @@ Repeat connection request?</source>
<source>accepted invitation</source>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
<source>admin</source>
<target>管理者</target>
@ -8495,10 +8402,6 @@ Repeat connection request?</source>
<target>暗号化に同意しています…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
@ -8576,10 +8479,6 @@ marked deleted chat item preview text</note>
<target>発信中…</target>
<note>call status</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
<source>cancelled %@</source>
<target>キャンセルされました %@</target>
@ -8683,14 +8582,6 @@ marked deleted chat item preview text</note>
<source>contact %1$@ changed to %2$@</source>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
<source>contact has e2e encryption</source>
<target>連絡先はエンドツーエンド暗号化があります</target>
@ -8701,10 +8592,6 @@ marked deleted chat item preview text</note>
<target>連絡先はエンドツーエンド暗号化がありません</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
<source>creator</source>
<target>作成者</target>
@ -8871,10 +8758,6 @@ pref value</note>
<target>グループ削除済み</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
<source>group profile updated</source>
<target>グループのプロフィールが更新されました</target>
@ -8997,10 +8880,6 @@ pref value</note>
<target>接続中</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<note>No comment provided by engineer.</note>
@ -9064,10 +8943,6 @@ pref value</note>
<target>テキストなし</target>
<note>copied message info in history</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
<source>observer</source>
<target>オブザーバー</target>
@ -9078,7 +8953,6 @@ pref value</note>
<target>オフ</target>
<note>enabled status
group pref value
member criteria value
time to disappear</note>
</trans-unit>
<trans-unit id="offered %@" xml:space="preserve">
@ -9126,10 +9000,6 @@ time to disappear</note>
<source>pending approval</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
<source>quantum resistant e2e encryption</source>
<note>chat item text</note>
@ -9167,10 +9037,6 @@ time to disappear</note>
<source>removed contact address</source>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
<source>removed profile picture</source>
<note>profile update event chat item</note>
@ -9180,22 +9046,10 @@ time to disappear</note>
<target>あなたを除名しました</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
<source>requested to connect</source>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
@ -9366,10 +9220,6 @@ last received msg: %2$@</source>
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>グループ招待が届きました</target>

View file

@ -565,14 +565,6 @@ time interval</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
<source>Accept conditions</source>
<target>Accepteer voorwaarden</target>
@ -594,10 +586,6 @@ swipe action</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
<source>Accepted conditions</source>
<target>Geaccepteerde voorwaarden</target>
@ -1593,23 +1581,11 @@ set passcode view</note>
<target>De chat wordt voor je verwijderd - dit kan niet ongedaan worden gemaakt!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Chats</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
<source>Check messages every 20 min.</source>
<target>Controleer uw berichten elke 20 minuten.</target>
@ -2425,10 +2401,6 @@ swipe action</note>
<target>Chatprofiel verwijderen?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
<source>Delete chat?</source>
<target>Chat verwijderen?</target>
@ -2852,7 +2824,7 @@ swipe action</note>
<trans-unit id="Don't show again" xml:space="preserve">
<source>Don't show again</source>
<target>Niet meer weergeven</target>
<note>alert action</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
@ -3170,10 +3142,6 @@ chat item action</note>
<target>Fout bij het accepteren van een contactverzoek</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>Fout bij het toevoegen van leden</target>
@ -3269,10 +3237,6 @@ chat item action</note>
<target>Fout bij het verwijderen van de chat database</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
<source>Error deleting chat!</source>
<target>Fout bij verwijderen gesprek!</target>
@ -3381,7 +3345,7 @@ chat item action</note>
<trans-unit id="Error removing member" xml:space="preserve">
<source>Error removing member</source>
<target>Fout bij verwijderen van lid</target>
<note>alert title</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reordering lists" xml:space="preserve">
<source>Error reordering lists</source>
@ -4765,10 +4729,6 @@ Dit is jouw link voor groep %@!</target>
<target>Lid</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<target>Lid inactief</target>
@ -4804,10 +4764,6 @@ Dit is jouw link voor groep %@!</target>
<target>Lid wordt uit de groep verwijderd, dit kan niet ongedaan worden gemaakt!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<target>Groepsleden kunnen bericht reacties toevoegen.</target>
@ -5228,10 +5184,6 @@ Dit is jouw link voor groep %@!</target>
<target>Nieuwe leden rol</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
<source>New message</source>
<target>nieuw bericht</target>
@ -5272,10 +5224,6 @@ Dit is jouw link voor groep %@!</target>
<target>Geen chats in lijst %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>Geen contacten geselecteerd</target>
@ -5468,8 +5416,7 @@ Dit is jouw link voor groep %@!</target>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<target>OK</target>
<note>alert action
alert button</note>
<note>alert button</note>
</trans-unit>
<trans-unit id="Old database" xml:space="preserve">
<source>Old database</source>
@ -5880,10 +5827,6 @@ Fout: %@</target>
<target>Probeer meldingen uit en weer in te schakelen.</target>
<note>token info</note>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
<source>Please wait for token activation to complete.</source>
<target>Wacht tot de tokenactivering voltooid is.</target>
@ -6337,10 +6280,6 @@ swipe action</note>
<target>Contactverzoek afwijzen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>Relay server wordt alleen gebruikt als dat nodig is. Een andere partij kan uw IP-adres zien.</target>
@ -6451,10 +6390,6 @@ swipe action</note>
<target>Reden melding?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
<source>Report spam: only group moderators will see it.</source>
<target>Spam melden: alleen groepsmoderators kunnen het zien.</target>
@ -6570,14 +6505,6 @@ swipe action</note>
<target>Voorwaarden bekijken</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>Intrekken</target>
@ -6634,10 +6561,6 @@ chat item action</note>
<target>Bewaar (en informeer contacten)</target>
<note>alert button</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
<source>Save and notify contact</source>
<target>Opslaan en Contact melden</target>
@ -7153,10 +7076,6 @@ chat item action</note>
<target>Stel het in in plaats van systeemverificatie.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
<source>Set message expiration in chats.</source>
<target>Stel de berichtvervaldatum in chats in.</target>
@ -8913,10 +8832,6 @@ Deelnameverzoek herhalen?</target>
<target>U kunt de uitnodigingslink opnieuw bekijken in de verbindingsdetails.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
<source>You can't send messages!</source>
<target>Je kunt geen berichten versturen!</target>
@ -9219,10 +9134,6 @@ Verbindingsverzoek herhalen?</target>
<target>hier boven, kies dan:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
<source>accepted call</source>
<target>geaccepteerde oproep</target>
@ -9233,10 +9144,6 @@ Verbindingsverzoek herhalen?</target>
<target>geaccepteerde uitnodiging</target>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
<source>admin</source>
<target>Beheerder</target>
@ -9257,10 +9164,6 @@ Verbindingsverzoek herhalen?</target>
<target>versleuteling overeenkomen…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<target>alle leden</target>
@ -9347,10 +9250,6 @@ marked deleted chat item preview text</note>
<target>bellen…</target>
<note>call status</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
<source>cancelled %@</source>
<target>geannuleerd %@</target>
@ -9456,14 +9355,6 @@ marked deleted chat item preview text</note>
<target>contactpersoon %1$@ gewijzigd in %2$@</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
<source>contact has e2e encryption</source>
<target>contact heeft e2e-codering</target>
@ -9474,10 +9365,6 @@ marked deleted chat item preview text</note>
<target>contact heeft geen e2e versleuteling</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
<source>creator</source>
<target>creator</target>
@ -9649,10 +9536,6 @@ pref value</note>
<target>groep verwijderd</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
<source>group profile updated</source>
<target>groep profiel bijgewerkt</target>
@ -9778,10 +9661,6 @@ pref value</note>
<target>is toegetreden</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<target>bericht</target>
@ -9847,10 +9726,6 @@ pref value</note>
<target>geen tekst</target>
<note>copied message info in history</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
<source>observer</source>
<target>Waarnemer</target>
@ -9861,7 +9736,6 @@ pref value</note>
<target>uit</target>
<note>enabled status
group pref value
member criteria value
time to disappear</note>
</trans-unit>
<trans-unit id="offered %@" xml:space="preserve">
@ -9914,10 +9788,6 @@ time to disappear</note>
<target>in afwachting van goedkeuring</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
<source>quantum resistant e2e encryption</source>
<target>quantum bestendige e2e-codering</target>
@ -9958,10 +9828,6 @@ time to disappear</note>
<target>contactadres verwijderd</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
<source>removed profile picture</source>
<target>profielfoto verwijderd</target>
@ -9972,23 +9838,11 @@ time to disappear</note>
<target>heeft je verwijderd</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
<source>requested to connect</source>
<target>verzocht om verbinding te maken</target>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<target>opgeslagen</target>
@ -10178,10 +10032,6 @@ laatst ontvangen bericht: %2$@</target>
<target>jij</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>je bent uitgenodigd voor de groep</target>

View file

@ -565,14 +565,6 @@ time interval</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
<source>Accept conditions</source>
<target>Zaakceptuj warunki</target>
@ -594,10 +586,6 @@ swipe action</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
<source>Accepted conditions</source>
<target>Zaakceptowano warunki</target>
@ -1587,23 +1575,11 @@ set passcode view</note>
<target>Czat zostanie usunięty dla Ciebie tej operacji nie można cofnąć!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Czaty</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
<source>Check messages every 20 min.</source>
<target>Sprawdzaj wiadomości co 20 min.</target>
@ -2398,10 +2374,6 @@ swipe action</note>
<target>Usunąć profil czatu?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
<source>Delete chat?</source>
<note>No comment provided by engineer.</note>
@ -2815,7 +2787,7 @@ swipe action</note>
<trans-unit id="Don't show again" xml:space="preserve">
<source>Don't show again</source>
<target>Nie pokazuj ponownie</target>
<note>alert action</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
@ -3128,10 +3100,6 @@ chat item action</note>
<target>Błąd przyjmowania prośby o kontakt</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>Błąd dodawania członka(ów)</target>
@ -3223,10 +3191,6 @@ chat item action</note>
<target>Błąd usuwania bazy danych czatu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
<source>Error deleting chat!</source>
<target>Błąd usuwania czatu!</target>
@ -3333,7 +3297,7 @@ chat item action</note>
<trans-unit id="Error removing member" xml:space="preserve">
<source>Error removing member</source>
<target>Błąd usuwania członka</target>
<note>alert title</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reordering lists" xml:space="preserve">
<source>Error reordering lists</source>
@ -4677,10 +4641,6 @@ To jest twój link do grupy %@!</target>
<target>Członek</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<target>Członek nieaktywny</target>
@ -4713,10 +4673,6 @@ To jest twój link do grupy %@!</target>
<target>Członek zostanie usunięty z grupy - nie można tego cofnąć!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<target>Członkowie grupy mogą dodawać reakcje wiadomości.</target>
@ -5127,10 +5083,6 @@ To jest twój link do grupy %@!</target>
<target>Nowa rola członka</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
<source>New message</source>
<target>Nowa wiadomość</target>
@ -5167,10 +5119,6 @@ To jest twój link do grupy %@!</target>
<source>No chats in list %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>Nie wybrano kontaktów</target>
@ -5350,8 +5298,7 @@ To jest twój link do grupy %@!</target>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<target>Ok</target>
<note>alert action
alert button</note>
<note>alert button</note>
</trans-unit>
<trans-unit id="Old database" xml:space="preserve">
<source>Old database</source>
@ -5751,10 +5698,6 @@ Błąd: %@</target>
<source>Please try to disable and re-enable notfications.</source>
<note>token info</note>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
<source>Please wait for token activation to complete.</source>
<note>token info</note>
@ -6197,10 +6140,6 @@ swipe action</note>
<target>Odrzuć prośbę kontaktu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>Serwer przekaźnikowy jest używany tylko w razie potrzeby. Inna strona może obserwować Twój adres IP.</target>
@ -6306,10 +6245,6 @@ swipe action</note>
<source>Report reason?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
<source>Report spam: only group moderators will see it.</source>
<note>report reason</note>
@ -6419,14 +6354,6 @@ swipe action</note>
<source>Review conditions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>Odwołaj</target>
@ -6483,10 +6410,6 @@ chat item action</note>
<target>Zapisz (i powiadom kontakty)</target>
<note>alert button</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
<source>Save and notify contact</source>
<target>Zapisz i powiadom kontakt</target>
@ -6995,10 +6918,6 @@ chat item action</note>
<target>Ustaw go zamiast uwierzytelniania systemowego.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
<source>Set message expiration in chats.</source>
<note>No comment provided by engineer.</note>
@ -8711,10 +8630,6 @@ Powtórzyć prośbę dołączenia?</target>
<target>Możesz zobaczyć link zaproszenia ponownie w szczegółach połączenia.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
<source>You can't send messages!</source>
<target>Nie możesz wysyłać wiadomości!</target>
@ -9015,10 +8930,6 @@ Powtórzyć prośbę połączenia?</target>
<target>powyżej, a następnie wybierz:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
<source>accepted call</source>
<target>zaakceptowane połączenie</target>
@ -9028,10 +8939,6 @@ Powtórzyć prośbę połączenia?</target>
<source>accepted invitation</source>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
<source>admin</source>
<target>administrator</target>
@ -9052,10 +8959,6 @@ Powtórzyć prośbę połączenia?</target>
<target>uzgadnianie szyfrowania…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<target>wszyscy członkowie</target>
@ -9141,10 +9044,6 @@ marked deleted chat item preview text</note>
<target>dzwonie…</target>
<note>call status</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
<source>cancelled %@</source>
<target>anulowany %@</target>
@ -9250,14 +9149,6 @@ marked deleted chat item preview text</note>
<target>kontakt %1$@ zmieniony na %2$@</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
<source>contact has e2e encryption</source>
<target>kontakt posiada szyfrowanie e2e</target>
@ -9268,10 +9159,6 @@ marked deleted chat item preview text</note>
<target>kontakt nie posiada szyfrowania e2e</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
<source>creator</source>
<target>twórca</target>
@ -9443,10 +9330,6 @@ pref value</note>
<target>grupa usunięta</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
<source>group profile updated</source>
<target>zaktualizowano profil grupy</target>
@ -9572,10 +9455,6 @@ pref value</note>
<target>połączony</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<target>wiadomość</target>
@ -9640,10 +9519,6 @@ pref value</note>
<target>brak tekstu</target>
<note>copied message info in history</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
<source>observer</source>
<target>obserwator</target>
@ -9654,7 +9529,6 @@ pref value</note>
<target>wyłączony</target>
<note>enabled status
group pref value
member criteria value
time to disappear</note>
</trans-unit>
<trans-unit id="offered %@" xml:space="preserve">
@ -9705,10 +9579,6 @@ time to disappear</note>
<source>pending approval</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
<source>quantum resistant e2e encryption</source>
<target>kwantowo odporne szyfrowanie e2e</target>
@ -9748,10 +9618,6 @@ time to disappear</note>
<target>usunięto adres kontaktu</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
<source>removed profile picture</source>
<target>usunięto zdjęcie profilu</target>
@ -9762,22 +9628,10 @@ time to disappear</note>
<target>usunął cię</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
<source>requested to connect</source>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<target>zapisane</target>
@ -9967,10 +9821,6 @@ ostatnia otrzymana wiadomość: %2$@</target>
<target>Ty</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>jesteś zaproszony do grupy</target>

View file

@ -167,7 +167,7 @@
</trans-unit>
<trans-unit id="%d hours" xml:space="preserve">
<source>%d hours</source>
<target>%d час.</target>
<target>%d ч.</target>
<note>time interval</note>
</trans-unit>
<trans-unit id="%d messages not forwarded" xml:space="preserve">
@ -565,16 +565,6 @@ time interval</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<target>Принять в группу</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<target>Принять как читателя</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
<source>Accept conditions</source>
<target>Принять условия</target>
@ -596,11 +586,6 @@ swipe action</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<target>Принять члена</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
<source>Accepted conditions</source>
<target>Принятые условия</target>
@ -813,7 +798,6 @@ swipe action</note>
</trans-unit>
<trans-unit id="All servers" xml:space="preserve">
<source>All servers</source>
<target>Все серверы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@ -1597,26 +1581,11 @@ set passcode view</note>
<target>Разговор будет удален для Вас - это действие нельзя отменить!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<target>Чат с админами</target>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<target>Чат с членом группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Чаты</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<target>Чаты с членами группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
<source>Check messages every 20 min.</source>
<target>Проверять сообщения каждые 20 минут.</target>
@ -2432,11 +2401,6 @@ swipe action</note>
<target>Удалить профиль?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<target>Удалить чат с членом группы?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
<source>Delete chat?</source>
<target>Удалить разговор?</target>
@ -2724,7 +2688,6 @@ swipe action</note>
</trans-unit>
<trans-unit id="Direct messages between members are prohibited." xml:space="preserve">
<source>Direct messages between members are prohibited.</source>
<target>Прямые сообщения между членами запрещены.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disable (keep overrides)" xml:space="preserve">
@ -2829,7 +2792,6 @@ swipe action</note>
</trans-unit>
<trans-unit id="Do not send history to new members." xml:space="preserve">
<source>Do not send history to new members.</source>
<target>Не отправлять историю новым членам.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do not use credentials with proxy." xml:space="preserve">
@ -2860,7 +2822,7 @@ swipe action</note>
<trans-unit id="Don't show again" xml:space="preserve">
<source>Don't show again</source>
<target>Не показывать</target>
<note>alert action</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
@ -2955,7 +2917,6 @@ chat item action</note>
</trans-unit>
<trans-unit id="Enable Flux in Network &amp; servers settings for better metadata privacy." xml:space="preserve">
<source>Enable Flux in Network &amp; servers settings for better metadata privacy.</source>
<target>Включите Flux в настройках Сеть и серверы для лучшей конфиденциальности метаданных.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Enable SimpleX Lock" xml:space="preserve">
@ -3178,14 +3139,8 @@ chat item action</note>
<target>Ошибка при принятии запроса на соединение</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<target>Ошибка вступления члена группы</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>Ошибка при добавлении членов группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error adding server" xml:space="preserve">
@ -3250,7 +3205,6 @@ chat item action</note>
</trans-unit>
<trans-unit id="Error creating member contact" xml:space="preserve">
<source>Error creating member contact</source>
<target>Ошибка при создании контакта</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error creating message" xml:space="preserve">
@ -3278,11 +3232,6 @@ chat item action</note>
<target>Ошибка при удалении данных чата</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<target>Ошибка при удалении чата с членом группы</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
<source>Error deleting chat!</source>
<target>Ошибка при удалении чата!</target>
@ -3390,8 +3339,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Error removing member" xml:space="preserve">
<source>Error removing member</source>
<target>Ошибка при удалении члена группы</target>
<note>alert title</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reordering lists" xml:space="preserve">
<source>Error reordering lists</source>
@ -3455,7 +3403,6 @@ chat item action</note>
</trans-unit>
<trans-unit id="Error sending member contact invitation" xml:space="preserve">
<source>Error sending member contact invitation</source>
<target>Ошибка при отправке приглашения члену</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error sending message" xml:space="preserve">
@ -3791,7 +3738,6 @@ snd error text</note>
</trans-unit>
<trans-unit id="Fix not supported by group member" xml:space="preserve">
<source>Fix not supported by group member</source>
<target>Починка не поддерживается членом группы.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="For all moderators" xml:space="preserve">
@ -3925,7 +3871,6 @@ Error: %2$@</source>
</trans-unit>
<trans-unit id="Fully decentralized visible only to members." xml:space="preserve">
<source>Fully decentralized visible only to members.</source>
<target>Группа полностью децентрализована она видна только членам.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Fully re-implemented - work in background!" xml:space="preserve">
@ -4035,7 +3980,6 @@ Error: %2$@</source>
</trans-unit>
<trans-unit id="Group profile is stored on members' devices, not on the servers." xml:space="preserve">
<source>Group profile is stored on members' devices, not on the servers.</source>
<target>Профиль группы хранится на устройствах членов, а не на серверах.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group welcome message" xml:space="preserve">
@ -4045,7 +3989,6 @@ Error: %2$@</source>
</trans-unit>
<trans-unit id="Group will be deleted for all members - this cannot be undone!" xml:space="preserve">
<source>Group will be deleted for all members - this cannot be undone!</source>
<target>Группа будет удалена для всех членов - это действие нельзя отменить!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group will be deleted for you - this cannot be undone!" xml:space="preserve">
@ -4110,7 +4053,6 @@ Error: %2$@</source>
</trans-unit>
<trans-unit id="History is not sent to new members." xml:space="preserve">
<source>History is not sent to new members.</source>
<target>История не отправляется новым членам.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="How SimpleX works" xml:space="preserve">
@ -4458,7 +4400,6 @@ More improvements are coming soon!</source>
</trans-unit>
<trans-unit id="Invite members" xml:space="preserve">
<source>Invite members</source>
<target>Пригласить членов группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Invite to chat" xml:space="preserve">
@ -4771,17 +4712,10 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="Member" xml:space="preserve">
<source>Member</source>
<target>Член группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<target>Приём членов в группу</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<target>Член неактивен</target>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member reports" xml:space="preserve">
@ -4796,72 +4730,54 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
<source>Member role will be changed to "%@". All group members will be notified.</source>
<target>Роль члена будет изменена на "%@". Все члены группы получат уведомление.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. The member will receive a new invitation." xml:space="preserve">
<source>Member role will be changed to "%@". The member will receive a new invitation.</source>
<target>Роль члена будет изменена на "%@". Будет отправлено новое приглашение.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will be removed from chat - this cannot be undone!" xml:space="preserve">
<source>Member will be removed from chat - this cannot be undone!</source>
<target>Член будет удален из разговора - это действие нельзя отменить!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will be removed from group - this cannot be undone!" xml:space="preserve">
<source>Member will be removed from group - this cannot be undone!</source>
<target>Член группы будет удален - это действие нельзя отменить!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<target>Участник хочет присоединиться к группе. Принять?</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<target>Члены могут добавлять реакции на сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Members can irreversibly delete sent messages. (24 hours)" xml:space="preserve">
<source>Members can irreversibly delete sent messages. (24 hours)</source>
<target>Члены могут необратимо удалять отправленные сообщения. (24 часа)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Members can report messsages to moderators." xml:space="preserve">
<source>Members can report messsages to moderators.</source>
<target>Члены группы могут пожаловаться модераторам.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Members can send SimpleX links." xml:space="preserve">
<source>Members can send SimpleX links.</source>
<target>Члены могут отправлять ссылки SimpleX.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Members can send direct messages." xml:space="preserve">
<source>Members can send direct messages.</source>
<target>Члены могут посылать прямые сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Members can send disappearing messages." xml:space="preserve">
<source>Members can send disappearing messages.</source>
<target>Члены могут посылать исчезающие сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Members can send files and media." xml:space="preserve">
<source>Members can send files and media.</source>
<target>Члены могут слать файлы и медиа.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Members can send voice messages." xml:space="preserve">
<source>Members can send voice messages.</source>
<target>Члены могут отправлять голосовые сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Mention members 👋" xml:space="preserve">
<source>Mention members 👋</source>
<target>Упоминайте участников 👋</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Menus" xml:space="preserve">
@ -4896,7 +4812,6 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<target>Сообщение может быть доставлено позже, если член группы станет активным.</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
@ -5236,14 +5151,8 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="New member role" xml:space="preserve">
<source>New member role</source>
<target>Роль члена группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<target>Новый участник хочет присоединиться к группе.</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
<source>New message</source>
<target>Новое сообщение</target>
@ -5284,11 +5193,6 @@ This is your link for group %@!</source>
<target>Нет чатов в списке %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<target>Нет чатов с членами группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>Контакты не выбраны</target>
@ -5463,9 +5367,6 @@ This is your link for group %@!</source>
<source>Now admins can:
- delete members' messages.
- disable members ("observer" role)</source>
<target>Теперь админы могут:
- удалять сообщения членов.
- приостанавливать членов (роль наблюдатель)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="OK" xml:space="preserve">
@ -5481,8 +5382,7 @@ This is your link for group %@!</source>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<target>Ок</target>
<note>alert action
alert button</note>
<note>alert button</note>
</trans-unit>
<trans-unit id="Old database" xml:space="preserve">
<source>Old database</source>
@ -5640,7 +5540,6 @@ Requires compatible VPN.</source>
</trans-unit>
<trans-unit id="Open link?" xml:space="preserve">
<source>Open link?</source>
<target>Открыть ссылку?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Open migration to another device" xml:space="preserve">
@ -5757,7 +5656,6 @@ Requires compatible VPN.</source>
</trans-unit>
<trans-unit id="Past member %@" xml:space="preserve">
<source>Past member %@</source>
<target>Бывший член %@</target>
<note>past/unknown group member</note>
</trans-unit>
<trans-unit id="Paste desktop address" xml:space="preserve">
@ -5894,11 +5792,6 @@ Error: %@</source>
<target>Попробуйте выключить и снова включить уведомления.</target>
<note>token info</note>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<target>Пожалуйста, подождите, пока модераторы группы рассмотрят ваш запрос на вступление.</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
<source>Please wait for token activation to complete.</source>
<target>Пожалуйста, дождитесь завершения активации токена.</target>
@ -6071,7 +5964,6 @@ Error: %@</source>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source>
<target>Запретить посылать прямые сообщения членам группы.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending disappearing messages." xml:space="preserve">
@ -6352,11 +6244,6 @@ swipe action</note>
<target>Отклонить запрос</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<target>Отклонить участника?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>Relay сервер используется только при необходимости. Другая сторона может видеть Ваш IP адрес.</target>
@ -6384,12 +6271,10 @@ swipe action</note>
</trans-unit>
<trans-unit id="Remove member" xml:space="preserve">
<source>Remove member</source>
<target>Удалить члена группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Remove member?" xml:space="preserve">
<source>Remove member?</source>
<target>Удалить члена группы?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Remove passphrase from keychain?" xml:space="preserve">
@ -6467,11 +6352,6 @@ swipe action</note>
<target>Причина сообщения?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<target>Жалоба отправлена модераторам</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
<source>Report spam: only group moderators will see it.</source>
<target>Пожаловаться на спам: увидят только модераторы группы.</target>
@ -6587,16 +6467,6 @@ swipe action</note>
<target>Посмотреть условия</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<target>Одобрять членов</target>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<target>Одобрять членов для вступления в группу.</target>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>Отозвать</target>
@ -6653,11 +6523,6 @@ chat item action</note>
<target>Сохранить (и уведомить контакты)</target>
<note>alert button</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<target>Сохранить настройки вступления?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
<source>Save and notify contact</source>
<target>Сохранить и уведомить контакт</target>
@ -6665,7 +6530,6 @@ chat item action</note>
</trans-unit>
<trans-unit id="Save and notify group members" xml:space="preserve">
<source>Save and notify group members</source>
<target>Сохранить и уведомить членов группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save and reconnect" xml:space="preserve">
@ -6950,7 +6814,6 @@ chat item action</note>
</trans-unit>
<trans-unit id="Send up to 100 last messages to new members." xml:space="preserve">
<source>Send up to 100 last messages to new members.</source>
<target>Отправить до 100 последних сообщений новым членам.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sender cancelled file transfer." xml:space="preserve">
@ -7173,11 +7036,6 @@ chat item action</note>
<target>Установите код вместо системной аутентификации.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<target>Приём членов в группу</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
<source>Set message expiration in chats.</source>
<target>Установите срок хранения сообщений в чатах.</target>
@ -7200,7 +7058,6 @@ chat item action</note>
</trans-unit>
<trans-unit id="Set the message shown to new members!" xml:space="preserve">
<source>Set the message shown to new members!</source>
<target>Установить сообщение для новых членов группы!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set timeouts for proxy/VPN" xml:space="preserve">
@ -7291,7 +7148,6 @@ chat item action</note>
</trans-unit>
<trans-unit id="Short link" xml:space="preserve">
<source>Short link</source>
<target>Короткая ссылка</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show QR code" xml:space="preserve">
@ -7396,7 +7252,6 @@ chat item action</note>
</trans-unit>
<trans-unit id="SimpleX channel link" xml:space="preserve">
<source>SimpleX channel link</source>
<target>SimpleX ссылка канала</target>
<note>simplex link type</note>
</trans-unit>
<trans-unit id="SimpleX contact address" xml:space="preserve">
@ -7841,22 +7696,18 @@ It can happen because of some bug or when the connection is compromised.</source
</trans-unit>
<trans-unit id="The message will be deleted for all members." xml:space="preserve">
<source>The message will be deleted for all members.</source>
<target>Сообщение будет удалено для всех членов группы.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The message will be marked as moderated for all members." xml:space="preserve">
<source>The message will be marked as moderated for all members.</source>
<target>Сообщение будет помечено как удаленное для всех членов группы.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The messages will be deleted for all members." xml:space="preserve">
<source>The messages will be deleted for all members.</source>
<target>Сообщения будут удалены для всех членов группы.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The messages will be marked as moderated for all members." xml:space="preserve">
<source>The messages will be marked as moderated for all members.</source>
<target>Сообщения будут помечены как удаленные для всех членов группы.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The old database was not removed during the migration, it can be deleted." xml:space="preserve">
@ -7966,7 +7817,6 @@ It can happen because of some bug or when the connection is compromised.</source
</trans-unit>
<trans-unit id="This group has over %lld members, delivery receipts are not sent." xml:space="preserve">
<source>This group has over %lld members, delivery receipts are not sent.</source>
<target>В этой группе более %lld членов, отчёты о доставке не отправляются.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="This group no longer exists." xml:space="preserve">
@ -7986,7 +7836,6 @@ It can happen because of some bug or when the connection is compromised.</source
</trans-unit>
<trans-unit id="This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link." xml:space="preserve">
<source>This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link.</source>
<target>Эта ссылка требует новую версию. Обновите приложение или попросите Ваш контакт прислать совместимую ссылку.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="This link was used with another mobile device, please create a new link on the desktop." xml:space="preserve">
@ -8178,17 +8027,14 @@ You will be prompted to complete authentication before this feature is enabled.<
</trans-unit>
<trans-unit id="Unblock member" xml:space="preserve">
<source>Unblock member</source>
<target>Разблокировать члена группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unblock member for all?" xml:space="preserve">
<source>Unblock member for all?</source>
<target>Разблокировать члена для всех?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unblock member?" xml:space="preserve">
<source>Unblock member?</source>
<target>Разблокировать члена группы?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Undelivered messages" xml:space="preserve">
@ -8290,12 +8136,10 @@ To connect, please ask your contact to create another connection link and check
</trans-unit>
<trans-unit id="Unsupported connection link" xml:space="preserve">
<source>Unsupported connection link</source>
<target>Ссылка не поддерживается</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Up to 100 last messages are sent to new members." xml:space="preserve">
<source>Up to 100 last messages are sent to new members.</source>
<target>До 100 последних сообщений отправляются новым членам.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update" xml:space="preserve">
@ -8390,7 +8234,6 @@ To connect, please ask your contact to create another connection link and check
</trans-unit>
<trans-unit id="Use TCP port 443 for preset servers only." xml:space="preserve">
<source>Use TCP port 443 for preset servers only.</source>
<target>Использовать TCP-порт 443 только для серверов по умолчанию.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use chat" xml:space="preserve">
@ -8460,7 +8303,6 @@ To connect, please ask your contact to create another connection link and check
</trans-unit>
<trans-unit id="Use short links (BETA)" xml:space="preserve">
<source>Use short links (BETA)</source>
<target>Короткие ссылки (БЕТА)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use the app while in the call." xml:space="preserve">
@ -8907,7 +8749,6 @@ Repeat join request?</source>
</trans-unit>
<trans-unit id="You can share a link or a QR code - anybody will be able to join the group. You won't lose members of the group if you later delete it." xml:space="preserve">
<source>You can share a link or a QR code - anybody will be able to join the group. You won't lose members of the group if you later delete it.</source>
<target>Вы можете поделиться ссылкой или QR кодом - через них можно присоединиться к группе. Вы сможете удалить ссылку, сохранив членов группы, которые через нее соединились.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can share this address with your contacts to let them connect with **%@**." xml:space="preserve">
@ -8940,11 +8781,6 @@ Repeat join request?</source>
<target>Вы можете увидеть ссылку-приглашение снова открыв соединение.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<target>Вы можете найти Ваши жалобы в Чате с админами.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
<source>You can't send messages!</source>
<target>Вы не можете отправлять сообщения!</target>
@ -8989,7 +8825,6 @@ Repeat connection request?</source>
</trans-unit>
<trans-unit id="You joined this group. Connecting to inviting group member." xml:space="preserve">
<source>You joined this group. Connecting to inviting group member.</source>
<target>Вы вступили в эту группу. Устанавливается соединение с пригласившим членом группы.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You may migrate the exported database." xml:space="preserve">
@ -9059,7 +8894,6 @@ Repeat connection request?</source>
</trans-unit>
<trans-unit id="You will connect to all group members." xml:space="preserve">
<source>You will connect to all group members.</source>
<target>Вы соединитесь со всеми членами группы.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You will still receive calls and notifications from muted profiles when they are active." xml:space="preserve">
@ -9247,11 +9081,6 @@ Repeat connection request?</source>
<target>наверху, затем выберите:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<target>принят %@</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
<source>accepted call</source>
<target>принятый звонок</target>
@ -9262,11 +9091,6 @@ Repeat connection request?</source>
<target>принятое приглашение</target>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<target>Вы приняты</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
<source>admin</source>
<target>админ</target>
@ -9287,14 +9111,8 @@ Repeat connection request?</source>
<target>шифрование согласовывается…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<target>все</target>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<target>все члены</target>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve">
@ -9378,11 +9196,6 @@ marked deleted chat item preview text</note>
<target>входящий звонок…</target>
<note>call status</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<target>нельзя отправлять</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
<source>cancelled %@</source>
<target>отменил(a) %@</target>
@ -9488,16 +9301,6 @@ marked deleted chat item preview text</note>
<target>контакт %1$@ изменён на %2$@</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<target>контакт удален</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<target>контакт выключен</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
<source>contact has e2e encryption</source>
<target>у контакта есть e2e шифрование</target>
@ -9508,11 +9311,6 @@ marked deleted chat item preview text</note>
<target>у контакта нет e2e шифрования</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<target>контакт не готов</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
<source>creator</source>
<target>создатель</target>
@ -9684,11 +9482,6 @@ pref value</note>
<target>группа удалена</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<target>группа удалена</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
<source>group profile updated</source>
<target>профиль группы обновлен</target>
@ -9801,12 +9594,10 @@ pref value</note>
</trans-unit>
<trans-unit id="member" xml:space="preserve">
<source>member</source>
<target>член группы</target>
<note>member role</note>
</trans-unit>
<trans-unit id="member %@ changed to %@" xml:space="preserve">
<source>member %1$@ changed to %2$@</source>
<target>член %1$@ изменился на %2$@</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="member connected" xml:space="preserve">
@ -9814,11 +9605,6 @@ pref value</note>
<target>соединен(а)</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<target>член имеет старую версию</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<target>написать</target>
@ -9884,11 +9670,6 @@ pref value</note>
<target>нет текста</target>
<note>copied message info in history</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<target>не синхронизирован</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
<source>observer</source>
<target>читатель</target>
@ -9899,7 +9680,6 @@ pref value</note>
<target>нет</target>
<note>enabled status
group pref value
member criteria value
time to disappear</note>
</trans-unit>
<trans-unit id="offered %@" xml:space="preserve">
@ -9952,11 +9732,6 @@ time to disappear</note>
<target>ожидает утверждения</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<target>ожидает одобрения</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
<source>quantum resistant e2e encryption</source>
<target>квантово-устойчивое e2e шифрование</target>
@ -9997,11 +9772,6 @@ time to disappear</note>
<target>удалён адрес контакта</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<target>удален из группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
<source>removed profile picture</source>
<target>удалена картинка профиля</target>
@ -10012,26 +9782,11 @@ time to disappear</note>
<target>удалил(а) Вас из группы</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<target>запрос на вступление отклонён</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
<source>requested to connect</source>
<target>запрошено соединение</target>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<target>рассмотрение</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<target>одобрен админами</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<target>сохранено</target>
@ -10221,11 +9976,6 @@ last received msg: %2$@</source>
<target>Вы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<target>Вы приняли этого члена</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>Вы приглашены в группу</target>
@ -10369,7 +10119,6 @@ last received msg: %2$@</source>
</trans-unit>
<trans-unit id="From %d chat(s)" xml:space="preserve">
<source>From %d chat(s)</source>
<target>Из %d чатов</target>
<note>notification body</note>
</trans-unit>
<trans-unit id="From: %@" xml:space="preserve">

View file

@ -520,14 +520,6 @@ time interval</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
<source>Accept conditions</source>
<note>No comment provided by engineer.</note>
@ -547,10 +539,6 @@ swipe action</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
<source>Accepted conditions</source>
<note>No comment provided by engineer.</note>
@ -1441,23 +1429,11 @@ set passcode view</note>
<source>Chat will be deleted for you - this cannot be undone!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>แชท</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
<source>Check messages every 20 min.</source>
<note>No comment provided by engineer.</note>
@ -2186,10 +2162,6 @@ swipe action</note>
<target>ลบโปรไฟล์แชทไหม?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
<source>Delete chat?</source>
<note>No comment provided by engineer.</note>
@ -2579,7 +2551,7 @@ swipe action</note>
<trans-unit id="Don't show again" xml:space="preserve">
<source>Don't show again</source>
<target>ไม่ต้องแสดงอีก</target>
<note>alert action</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
@ -2871,10 +2843,6 @@ chat item action</note>
<target>เกิดข้อผิดพลาดในการรับคำขอติดต่อ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>เกิดข้อผิดพลาดในการเพิ่มสมาชิก</target>
@ -2960,10 +2928,6 @@ chat item action</note>
<target>เกิดข้อผิดพลาดในการลบฐานข้อมูลแชท</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
<source>Error deleting chat!</source>
<target>เกิดข้อผิดพลาดในการลบแชท!</target>
@ -3064,7 +3028,7 @@ chat item action</note>
<trans-unit id="Error removing member" xml:space="preserve">
<source>Error removing member</source>
<target>เกิดข้อผิดพลาดในการลบสมาชิก</target>
<note>alert title</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reordering lists" xml:space="preserve">
<source>Error reordering lists</source>
@ -4329,10 +4293,6 @@ This is your link for group %@!</source>
<target>สมาชิก</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<note>item status text</note>
@ -4364,10 +4324,6 @@ This is your link for group %@!</source>
<target>สมาชิกจะถูกลบออกจากกลุ่ม - ไม่สามารถยกเลิกได้!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<target>สมาชิกกลุ่มสามารถเพิ่มการแสดงปฏิกิริยาต่อข้อความได้</target>
@ -4741,10 +4697,6 @@ This is your link for group %@!</source>
<target>บทบาทของสมาชิกใหม่</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
<source>New message</source>
<target>ข้อความใหม่</target>
@ -4781,10 +4733,6 @@ This is your link for group %@!</source>
<source>No chats in list %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>ไม่ได้เลือกผู้ติดต่อ</target>
@ -4954,8 +4902,7 @@ This is your link for group %@!</source>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<target>ตกลง</target>
<note>alert action
alert button</note>
<note>alert button</note>
</trans-unit>
<trans-unit id="Old database" xml:space="preserve">
<source>Old database</source>
@ -5327,10 +5274,6 @@ Error: %@</source>
<source>Please try to disable and re-enable notfications.</source>
<note>token info</note>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
<source>Please wait for token activation to complete.</source>
<note>token info</note>
@ -5740,10 +5683,6 @@ swipe action</note>
<target>ปฏิเสธคำขอติดต่อ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>ใช้เซิร์ฟเวอร์รีเลย์ในกรณีที่จำเป็นเท่านั้น บุคคลอื่นสามารถสังเกตที่อยู่ IP ของคุณได้</target>
@ -5842,10 +5781,6 @@ swipe action</note>
<source>Report reason?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
<source>Report spam: only group moderators will see it.</source>
<note>report reason</note>
@ -5949,14 +5884,6 @@ swipe action</note>
<source>Review conditions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>ถอน</target>
@ -6009,10 +5936,6 @@ chat item action</note>
<target>บันทึก (และแจ้งผู้ติดต่อ)</target>
<note>alert button</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
<source>Save and notify contact</source>
<target>บันทึกและแจ้งผู้ติดต่อ</target>
@ -6481,10 +6404,6 @@ chat item action</note>
<target>ตั้งแทนการรับรองความถูกต้องของระบบ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
<source>Set message expiration in chats.</source>
<note>No comment provided by engineer.</note>
@ -8067,10 +7986,6 @@ Repeat join request?</source>
<source>You can view invitation link again in connection details.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
<source>You can't send messages!</source>
<target>คุณไม่สามารถส่งข้อความได้!</target>
@ -8355,10 +8270,6 @@ Repeat connection request?</source>
<target>ด้านบน จากนั้นเลือก:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
<source>accepted call</source>
<target>รับสายแล้ว</target>
@ -8368,10 +8279,6 @@ Repeat connection request?</source>
<source>accepted invitation</source>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
<source>admin</source>
<target>ผู้ดูแลระบบ</target>
@ -8391,10 +8298,6 @@ Repeat connection request?</source>
<target>เห็นด้วยกับการ encryption…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
@ -8472,10 +8375,6 @@ marked deleted chat item preview text</note>
<target>กำลังโทร…</target>
<note>call status</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
<source>cancelled %@</source>
<target>ยกเลิก %@</target>
@ -8579,14 +8478,6 @@ marked deleted chat item preview text</note>
<source>contact %1$@ changed to %2$@</source>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
<source>contact has e2e encryption</source>
<target>ผู้ติดต่อมีการ encrypt จากต้นจนจบ</target>
@ -8597,10 +8488,6 @@ marked deleted chat item preview text</note>
<target>ผู้ติดต่อไม่มีการ encrypt จากต้นจนจบ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
<source>creator</source>
<target>ผู้สร้าง</target>
@ -8766,10 +8653,6 @@ pref value</note>
<target>ลบกลุ่มแล้ว</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
<source>group profile updated</source>
<target>อัปเดตโปรไฟล์กลุ่มแล้ว</target>
@ -8892,10 +8775,6 @@ pref value</note>
<target>เชื่อมต่อสำเร็จ</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<note>No comment provided by engineer.</note>
@ -8959,10 +8838,6 @@ pref value</note>
<target>ไม่มีข้อความ</target>
<note>copied message info in history</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
<source>observer</source>
<target>ผู้สังเกตการณ์</target>
@ -8973,7 +8848,6 @@ pref value</note>
<target>ปิด</target>
<note>enabled status
group pref value
member criteria value
time to disappear</note>
</trans-unit>
<trans-unit id="offered %@" xml:space="preserve">
@ -9021,10 +8895,6 @@ time to disappear</note>
<source>pending approval</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
<source>quantum resistant e2e encryption</source>
<note>chat item text</note>
@ -9062,10 +8932,6 @@ time to disappear</note>
<source>removed contact address</source>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
<source>removed profile picture</source>
<note>profile update event chat item</note>
@ -9075,22 +8941,10 @@ time to disappear</note>
<target>ลบคุณออกแล้ว</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
<source>requested to connect</source>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
@ -9261,10 +9115,6 @@ last received msg: %2$@</source>
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>คุณได้รับเชิญให้เข้าร่วมกลุ่ม</target>

View file

@ -563,14 +563,6 @@ time interval</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
<source>Accept conditions</source>
<target>Koşulları kabul et</target>
@ -592,10 +584,6 @@ swipe action</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
<source>Accepted conditions</source>
<target>Kabul edilmiş koşullar</target>
@ -1569,23 +1557,11 @@ set passcode view</note>
<target>Sohbet senden silinecek - bu geri alınamaz!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Sohbetler</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
<source>Check messages every 20 min.</source>
<target>Her 20 dakikada mesajları kontrol et.</target>
@ -2388,10 +2364,6 @@ swipe action</note>
<target>Sohbet profili silinsin mi?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
<source>Delete chat?</source>
<target>Sohbet silinsin mi?</target>
@ -2808,7 +2780,7 @@ swipe action</note>
<trans-unit id="Don't show again" xml:space="preserve">
<source>Don't show again</source>
<target>Yeniden gösterme</target>
<note>alert action</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
@ -3123,10 +3095,6 @@ chat item action</note>
<target>Bağlantı isteği kabul edilirken hata oluştu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>Üye(ler) eklenirken hata oluştu</target>
@ -3219,10 +3187,6 @@ chat item action</note>
<target>Sohbet veritabanı silinirken sorun oluştu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
<source>Error deleting chat!</source>
<target>Sohbet silinirken hata oluştu!</target>
@ -3330,7 +3294,7 @@ chat item action</note>
<trans-unit id="Error removing member" xml:space="preserve">
<source>Error removing member</source>
<target>Kişiyi silerken sorun oluştu</target>
<note>alert title</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reordering lists" xml:space="preserve">
<source>Error reordering lists</source>
@ -4689,10 +4653,6 @@ Bu senin grup için bağlantın %@!</target>
<target>Kişi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<target>Üye inaktif</target>
@ -4726,10 +4686,6 @@ Bu senin grup için bağlantın %@!</target>
<target>Üye gruptan çıkarılacaktır - bu geri alınamaz!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<target>Grup üyeleri mesaj tepkileri ekleyebilir.</target>
@ -5140,10 +5096,6 @@ Bu senin grup için bağlantın %@!</target>
<target>Yeni üye rolü</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
<source>New message</source>
<target>Yeni mesaj</target>
@ -5180,10 +5132,6 @@ Bu senin grup için bağlantın %@!</target>
<source>No chats in list %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>Hiçbir kişi seçilmedi</target>
@ -5363,8 +5311,7 @@ Bu senin grup için bağlantın %@!</target>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<target>Tamam</target>
<note>alert action
alert button</note>
<note>alert button</note>
</trans-unit>
<trans-unit id="Old database" xml:space="preserve">
<source>Old database</source>
@ -5764,10 +5711,6 @@ Hata: %@</target>
<source>Please try to disable and re-enable notfications.</source>
<note>token info</note>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
<source>Please wait for token activation to complete.</source>
<note>token info</note>
@ -6210,10 +6153,6 @@ swipe action</note>
<target>Bağlanma isteğini reddet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>Yönlendirici sunucusu yalnızca gerekli olduğunda kullanılır. Başka bir taraf IP adresinizi gözlemleyebilir.</target>
@ -6319,10 +6258,6 @@ swipe action</note>
<source>Report reason?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
<source>Report spam: only group moderators will see it.</source>
<note>report reason</note>
@ -6432,14 +6367,6 @@ swipe action</note>
<source>Review conditions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>İptal et</target>
@ -6496,10 +6423,6 @@ chat item action</note>
<target>Kaydet (ve kişilere bildir)</target>
<note>alert button</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
<source>Save and notify contact</source>
<target>Kaydet ve kişilere bildir</target>
@ -7008,10 +6931,6 @@ chat item action</note>
<target>Sistem kimlik doğrulaması yerine ayarla.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
<source>Set message expiration in chats.</source>
<note>No comment provided by engineer.</note>
@ -8727,10 +8646,6 @@ Katılma isteği tekrarlansın mı?</target>
<target>Bağlantı detaylarından davet bağlantısını yeniden görüntüleyebilirsin.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
<source>You can't send messages!</source>
<target>Mesajlar gönderemezsiniz!</target>
@ -9030,10 +8945,6 @@ Bağlantı isteği tekrarlansın mı?</target>
<target>yukarı çıkın, ardından seçin:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
<source>accepted call</source>
<target>kabul edilen arama</target>
@ -9043,10 +8954,6 @@ Bağlantı isteği tekrarlansın mı?</target>
<source>accepted invitation</source>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
<source>admin</source>
<target>yönetici</target>
@ -9067,10 +8974,6 @@ Bağlantı isteği tekrarlansın mı?</target>
<target>şifreleme kabul ediliyor…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<target>bütün üyeler</target>
@ -9156,10 +9059,6 @@ marked deleted chat item preview text</note>
<target>aranıyor…</target>
<note>call status</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
<source>cancelled %@</source>
<target>%@ iptal edildi</target>
@ -9265,14 +9164,6 @@ marked deleted chat item preview text</note>
<target>%1$@ kişisi %2$@ olarak değişti</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
<source>contact has e2e encryption</source>
<target>kişi uçtan uca şifrelemeye sahiptir</target>
@ -9283,10 +9174,6 @@ marked deleted chat item preview text</note>
<target>kişi uçtan uca şifrelemeye sahip değildir</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
<source>creator</source>
<target>oluşturan</target>
@ -9458,10 +9345,6 @@ pref value</note>
<target>grup silindi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
<source>group profile updated</source>
<target>grup profili güncellendi</target>
@ -9587,10 +9470,6 @@ pref value</note>
<target>bağlanıldı</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<target>mesaj</target>
@ -9655,10 +9534,6 @@ pref value</note>
<target>metin yok</target>
<note>copied message info in history</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
<source>observer</source>
<target>gözlemci</target>
@ -9669,7 +9544,6 @@ pref value</note>
<target>kapalı</target>
<note>enabled status
group pref value
member criteria value
time to disappear</note>
</trans-unit>
<trans-unit id="offered %@" xml:space="preserve">
@ -9720,10 +9594,6 @@ time to disappear</note>
<source>pending approval</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
<source>quantum resistant e2e encryption</source>
<target>kuantuma dayanıklı e2e şifreleme</target>
@ -9763,10 +9633,6 @@ time to disappear</note>
<target>kişi adresi silindi</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
<source>removed profile picture</source>
<target>profil fotoğrafı silindi</target>
@ -9777,22 +9643,10 @@ time to disappear</note>
<target>sen kaldırıldın</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
<source>requested to connect</source>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<target>kaydedildi</target>
@ -9982,10 +9836,6 @@ son alınan msj: %2$@</target>
<target>sen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>gruba davet edildiniz</target>

View file

@ -563,14 +563,6 @@ time interval</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
<source>Accept conditions</source>
<target>Прийняти умови</target>
@ -592,10 +584,6 @@ swipe action</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
<source>Accepted conditions</source>
<target>Прийняті умови</target>
@ -1569,23 +1557,11 @@ set passcode view</note>
<target>Чат буде видалено для вас - цю дію неможливо скасувати!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Чати</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
<source>Check messages every 20 min.</source>
<target>Перевіряйте повідомлення кожні 20 хв.</target>
@ -2388,10 +2364,6 @@ swipe action</note>
<target>Видалити профіль чату?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
<source>Delete chat?</source>
<target>Видалити чат?</target>
@ -2809,7 +2781,7 @@ swipe action</note>
<trans-unit id="Don't show again" xml:space="preserve">
<source>Don't show again</source>
<target>Більше не показувати</target>
<note>alert action</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
@ -3124,10 +3096,6 @@ chat item action</note>
<target>Помилка при прийнятті запиту на контакт</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>Помилка додавання користувача(ів)</target>
@ -3220,10 +3188,6 @@ chat item action</note>
<target>Помилка видалення бази даних чату</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
<source>Error deleting chat!</source>
<target>Помилка видалення чату!</target>
@ -3331,7 +3295,7 @@ chat item action</note>
<trans-unit id="Error removing member" xml:space="preserve">
<source>Error removing member</source>
<target>Помилка видалення учасника</target>
<note>alert title</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reordering lists" xml:space="preserve">
<source>Error reordering lists</source>
@ -4690,10 +4654,6 @@ This is your link for group %@!</source>
<target>Учасник</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<target>Користувач неактивний</target>
@ -4728,10 +4688,6 @@ This is your link for group %@!</source>
<target>Учасник буде видалений з групи - це неможливо скасувати!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<target>Учасники групи можуть додавати реакції на повідомлення.</target>
@ -5146,10 +5102,6 @@ This is your link for group %@!</source>
<target>Нова роль учасника</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
<source>New message</source>
<target>Нове повідомлення</target>
@ -5187,10 +5139,6 @@ This is your link for group %@!</source>
<source>No chats in list %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>Не вибрано жодного контакту</target>
@ -5377,8 +5325,7 @@ This is your link for group %@!</source>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<target>Гаразд</target>
<note>alert action
alert button</note>
<note>alert button</note>
</trans-unit>
<trans-unit id="Old database" xml:space="preserve">
<source>Old database</source>
@ -5785,10 +5732,6 @@ Error: %@</source>
<source>Please try to disable and re-enable notfications.</source>
<note>token info</note>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
<source>Please wait for token activation to complete.</source>
<note>token info</note>
@ -6233,10 +6176,6 @@ swipe action</note>
<target>Відхилити запит на контакт</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>Релейний сервер використовується тільки в разі потреби. Інша сторона може бачити вашу IP-адресу.</target>
@ -6342,10 +6281,6 @@ swipe action</note>
<source>Report reason?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
<source>Report spam: only group moderators will see it.</source>
<note>report reason</note>
@ -6456,14 +6391,6 @@ swipe action</note>
<target>Умови перегляду</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>Відкликати</target>
@ -6520,10 +6447,6 @@ chat item action</note>
<target>Зберегти (і повідомити контактам)</target>
<note>alert button</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
<source>Save and notify contact</source>
<target>Зберегти та повідомити контакт</target>
@ -7036,10 +6959,6 @@ chat item action</note>
<target>Встановіть його замість аутентифікації системи.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
<source>Set message expiration in chats.</source>
<note>No comment provided by engineer.</note>
@ -8785,10 +8704,6 @@ Repeat join request?</source>
<target>Ви можете переглянути посилання на запрошення ще раз у деталях підключення.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
<source>You can't send messages!</source>
<target>Ви не можете надсилати повідомлення!</target>
@ -9090,10 +9005,6 @@ Repeat connection request?</source>
<target>вище, а потім обирайте:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
<source>accepted call</source>
<target>прийнято виклик</target>
@ -9104,10 +9015,6 @@ Repeat connection request?</source>
<target>прийняте запрошення</target>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
<source>admin</source>
<target>адмін</target>
@ -9128,10 +9035,6 @@ Repeat connection request?</source>
<target>узгодження шифрування…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<target>всі учасники</target>
@ -9217,10 +9120,6 @@ marked deleted chat item preview text</note>
<target>дзвоніть…</target>
<note>call status</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
<source>cancelled %@</source>
<target>скасовано %@</target>
@ -9326,14 +9225,6 @@ marked deleted chat item preview text</note>
<target>контакт %1$@ змінено на %2$@</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
<source>contact has e2e encryption</source>
<target>контакт має шифрування e2e</target>
@ -9344,10 +9235,6 @@ marked deleted chat item preview text</note>
<target>контакт не має шифрування e2e</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
<source>creator</source>
<target>творець</target>
@ -9519,10 +9406,6 @@ pref value</note>
<target>групу видалено</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
<source>group profile updated</source>
<target>оновлено профіль групи</target>
@ -9648,10 +9531,6 @@ pref value</note>
<target>з'єднаний</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<target>повідомлення</target>
@ -9716,10 +9595,6 @@ pref value</note>
<target>без тексту</target>
<note>copied message info in history</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
<source>observer</source>
<target>спостерігач</target>
@ -9730,7 +9605,6 @@ pref value</note>
<target>вимкнено</target>
<note>enabled status
group pref value
member criteria value
time to disappear</note>
</trans-unit>
<trans-unit id="offered %@" xml:space="preserve">
@ -9781,10 +9655,6 @@ time to disappear</note>
<source>pending approval</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
<source>quantum resistant e2e encryption</source>
<target>квантово-стійке шифрування e2e</target>
@ -9824,10 +9694,6 @@ time to disappear</note>
<target>видалено контактну адресу</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
<source>removed profile picture</source>
<target>видалено зображення профілю</target>
@ -9838,23 +9704,11 @@ time to disappear</note>
<target>прибрали вас</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
<source>requested to connect</source>
<target>запит на підключення</target>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<target>збережено</target>
@ -10044,10 +9898,6 @@ last received msg: %2$@</source>
<target>ти</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>вас запрошують до групи</target>

View file

@ -565,14 +565,6 @@ time interval</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
<source>Accept conditions</source>
<target>接受条款</target>
@ -594,10 +586,6 @@ swipe action</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
<source>Accepted conditions</source>
<target>已接受的条款</target>
@ -1594,23 +1582,11 @@ set passcode view</note>
<target>将为你删除聊天 - 此操作无法撤销!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>聊天</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
<source>Check messages every 20 min.</source>
<target>每 20 分钟检查消息。</target>
@ -2425,10 +2401,6 @@ swipe action</note>
<target>删除聊天资料?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
<source>Delete chat?</source>
<target>删除聊天?</target>
@ -2852,7 +2824,7 @@ swipe action</note>
<trans-unit id="Don't show again" xml:space="preserve">
<source>Don't show again</source>
<target>不再显示</target>
<note>alert action</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
@ -3170,10 +3142,6 @@ chat item action</note>
<target>接受联系人请求错误</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>添加成员错误</target>
@ -3268,10 +3236,6 @@ chat item action</note>
<target>删除聊天数据库错误</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
<source>Error deleting chat!</source>
<target>删除聊天错误!</target>
@ -3380,7 +3344,7 @@ chat item action</note>
<trans-unit id="Error removing member" xml:space="preserve">
<source>Error removing member</source>
<target>删除成员错误</target>
<note>alert title</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reordering lists" xml:space="preserve">
<source>Error reordering lists</source>
@ -4764,10 +4728,6 @@ This is your link for group %@!</source>
<target>成员</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<target>成员不活跃</target>
@ -4803,10 +4763,6 @@ This is your link for group %@!</source>
<target>成员将被移出群组——此操作无法撤消!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<target>群组成员可以添加信息回应。</target>
@ -5227,10 +5183,6 @@ This is your link for group %@!</source>
<target>新成员角色</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
<source>New message</source>
<target>新消息</target>
@ -5271,10 +5223,6 @@ This is your link for group %@!</source>
<target>列表 %@ 中无聊天</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>未选择联系人</target>
@ -5467,8 +5415,7 @@ This is your link for group %@!</source>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<target>好的</target>
<note>alert action
alert button</note>
<note>alert button</note>
</trans-unit>
<trans-unit id="Old database" xml:space="preserve">
<source>Old database</source>
@ -5876,10 +5823,6 @@ Error: %@</source>
<source>Please try to disable and re-enable notfications.</source>
<note>token info</note>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
<source>Please wait for token activation to complete.</source>
<note>token info</note>
@ -6322,10 +6265,6 @@ swipe action</note>
<target>拒绝联系人请求</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>中继服务器仅在必要时使用。其他人可能会观察到您的IP地址。</target>
@ -6430,10 +6369,6 @@ swipe action</note>
<source>Report reason?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
<source>Report spam: only group moderators will see it.</source>
<note>report reason</note>
@ -6544,14 +6479,6 @@ swipe action</note>
<target>审阅条款</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>吊销</target>
@ -6607,10 +6534,6 @@ chat item action</note>
<target>保存(并通知联系人)</target>
<note>alert button</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
<source>Save and notify contact</source>
<target>保存并通知联系人</target>
@ -7118,10 +7041,6 @@ chat item action</note>
<target>设置它以代替系统身份验证。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
<source>Set message expiration in chats.</source>
<note>No comment provided by engineer.</note>
@ -8831,10 +8750,6 @@ Repeat join request?</source>
<target>您可以在连接详情中再次查看邀请链接。</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
<source>You can't send messages!</source>
<target>您无法发送消息!</target>
@ -9130,10 +9045,6 @@ Repeat connection request?</source>
<target>上面,然后选择:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
<source>accepted call</source>
<target>已接受通话</target>
@ -9143,10 +9054,6 @@ Repeat connection request?</source>
<source>accepted invitation</source>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
<source>admin</source>
<target>管理员</target>
@ -9167,10 +9074,6 @@ Repeat connection request?</source>
<target>同意加密…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<target>所有成员</target>
@ -9256,10 +9159,6 @@ marked deleted chat item preview text</note>
<target>呼叫中……</target>
<note>call status</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
<source>cancelled %@</source>
<target>已取消 %@</target>
@ -9365,14 +9264,6 @@ marked deleted chat item preview text</note>
<target>联系人 %1$@ 已更改为 %2$@</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
<source>contact has e2e encryption</source>
<target>联系人具有端到端加密</target>
@ -9383,10 +9274,6 @@ marked deleted chat item preview text</note>
<target>联系人没有端到端加密</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
<source>creator</source>
<target>创建者</target>
@ -9558,10 +9445,6 @@ pref value</note>
<target>群组已删除</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
<source>group profile updated</source>
<target>群组资料已更新</target>
@ -9687,10 +9570,6 @@ pref value</note>
<target>已连接</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<target>消息</target>
@ -9755,10 +9634,6 @@ pref value</note>
<target>无文本</target>
<note>copied message info in history</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
<source>observer</source>
<target>观察者</target>
@ -9769,7 +9644,6 @@ pref value</note>
<target>关闭</target>
<note>enabled status
group pref value
member criteria value
time to disappear</note>
</trans-unit>
<trans-unit id="offered %@" xml:space="preserve">
@ -9820,10 +9694,6 @@ time to disappear</note>
<source>pending approval</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
<source>quantum resistant e2e encryption</source>
<target>抗量子端到端加密</target>
@ -9863,10 +9733,6 @@ time to disappear</note>
<target>删除了联系地址</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
<source>removed profile picture</source>
<target>删除了资料图片</target>
@ -9877,22 +9743,10 @@ time to disappear</note>
<target>已将您移除</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
<source>requested to connect</source>
<note>chat list item title</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<target>已保存</target>
@ -10082,10 +9936,6 @@ last received msg: %2$@</source>
<target>您</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>您被邀请加入群组</target>

View file

@ -1,9 +1,6 @@
/* notification body */
"%d new events" = "%d новых сообщений";
/* notification body */
"From %d chat(s)" = "Из %d чатов";
/* notification body */
"From: %@" = "От: %@";

View file

@ -67,7 +67,6 @@ func apiSendMessages(
: SEChatCommand.apiSendMessages(
type: chatInfo.chatType,
id: chatInfo.apiId,
scope: chatInfo.groupChatScope(),
live: false,
ttl: nil,
composedMessages: composedMessages
@ -124,7 +123,7 @@ enum SEChatCommand: ChatCmdProtocol {
case apiSetEncryptLocalFiles(enable: Bool)
case apiGetChats(userId: Int64)
case apiCreateChatItems(noteFolderId: Int64, composedMessages: [ComposedMessage])
case apiSendMessages(type: ChatType, id: Int64, scope: GroupChatScope?, live: Bool, ttl: Int?, composedMessages: [ComposedMessage])
case apiSendMessages(type: ChatType, id: Int64, live: Bool, ttl: Int?, composedMessages: [ComposedMessage])
var cmdString: String {
switch self {
@ -140,27 +139,15 @@ enum SEChatCommand: ChatCmdProtocol {
case let .apiCreateChatItems(noteFolderId, composedMessages):
let msgs = encodeJSON(composedMessages)
return "/_create *\(noteFolderId) json \(msgs)"
case let .apiSendMessages(type, id, scope, live, ttl, composedMessages):
case let .apiSendMessages(type, id, live, ttl, composedMessages):
let msgs = encodeJSON(composedMessages)
let ttlStr = ttl != nil ? "\(ttl!)" : "default"
return "/_send \(ref(type, id, scope: scope)) live=\(onOff(live)) ttl=\(ttlStr) json \(msgs)"
return "/_send \(ref(type, id)) live=\(onOff(live)) ttl=\(ttlStr) json \(msgs)"
}
}
func ref(_ type: ChatType, _ id: Int64, scope: GroupChatScope?) -> String {
"\(type.rawValue)\(id)\(scopeRef(scope: scope))"
}
func scopeRef(scope: GroupChatScope?) -> String {
switch (scope) {
case .none: ""
case let .memberSupport(groupMemberId_):
if let groupMemberId = groupMemberId_ {
"(_support:\(groupMemberId))"
} else {
"(_support)"
}
}
func ref(_ type: ChatType, _ id: Int64) -> String {
"\(type.rawValue)\(id)"
}
}

View file

@ -168,19 +168,14 @@
648679AB2BC96A74006456E7 /* ChatItemForwardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */; };
649BCDA0280460FD00C3A862 /* ComposeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCD9F280460FD00C3A862 /* ComposeImageView.swift */; };
649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCDA12805D6EF00C3A862 /* CIImageView.swift */; };
64A779F62DBFB9F200FDEF2F /* MemberAdmissionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A779F52DBFB9F200FDEF2F /* MemberAdmissionView.swift */; };
64A779F82DBFDBF200FDEF2F /* MemberSupportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A779F72DBFDBF200FDEF2F /* MemberSupportView.swift */; };
64A779FC2DC1040000FDEF2F /* SecondaryChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A779FB2DC1040000FDEF2F /* SecondaryChatView.swift */; };
64A779FE2DC3AFF200FDEF2F /* MemberSupportChatToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A779FD2DC3AFF200FDEF2F /* MemberSupportChatToolbar.swift */; };
64A77A022DC4AD6100FDEF2F /* ContextPendingMemberActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A77A012DC4AD6100FDEF2F /* ContextPendingMemberActionsView.swift */; };
64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; };
64AA1C6C27F3537400AC7277 /* DeletedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */; };
64C06EB52A0A4A7C00792D4D /* ChatItemInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */; };
64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */; };
64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829982D54AEED006B9E89 /* libgmp.a */; };
64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829992D54AEEE006B9E89 /* libffi.a */; };
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F-ghc9.6.3.a */; };
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F.a */; };
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.6.0-64eNxtIoLF9BaOhAoPagss-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.6.0-64eNxtIoLF9BaOhAoPagss-ghc9.6.3.a */; };
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.3.6.0-64eNxtIoLF9BaOhAoPagss.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.6.0-64eNxtIoLF9BaOhAoPagss.a */; };
64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */; };
64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */; };
64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */; };
@ -532,19 +527,14 @@
6493D667280ED77F007A76FB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
649BCD9F280460FD00C3A862 /* ComposeImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeImageView.swift; sourceTree = "<group>"; };
649BCDA12805D6EF00C3A862 /* CIImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIImageView.swift; sourceTree = "<group>"; };
64A779F52DBFB9F200FDEF2F /* MemberAdmissionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemberAdmissionView.swift; sourceTree = "<group>"; };
64A779F72DBFDBF200FDEF2F /* MemberSupportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemberSupportView.swift; sourceTree = "<group>"; };
64A779FB2DC1040000FDEF2F /* SecondaryChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondaryChatView.swift; sourceTree = "<group>"; };
64A779FD2DC3AFF200FDEF2F /* MemberSupportChatToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemberSupportChatToolbar.swift; sourceTree = "<group>"; };
64A77A012DC4AD6100FDEF2F /* ContextPendingMemberActionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextPendingMemberActionsView.swift; sourceTree = "<group>"; };
64AA1C6827EE10C800AC7277 /* ContextItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextItemView.swift; sourceTree = "<group>"; };
64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedItemView.swift; sourceTree = "<group>"; };
64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemInfoView.swift; sourceTree = "<group>"; };
64C3B0202A0D359700E19930 /* CustomTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTimePicker.swift; sourceTree = "<group>"; };
64C829982D54AEED006B9E89 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
64C829992D54AEEE006B9E89 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F-ghc9.6.3.a"; sourceTree = "<group>"; };
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F.a"; sourceTree = "<group>"; };
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.6.0-64eNxtIoLF9BaOhAoPagss-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.3.6.0-64eNxtIoLF9BaOhAoPagss-ghc9.6.3.a"; sourceTree = "<group>"; };
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.6.0-64eNxtIoLF9BaOhAoPagss.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.3.6.0-64eNxtIoLF9BaOhAoPagss.a"; sourceTree = "<group>"; };
64C8299C2D54AEEE006B9E89 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
64D0C2BF29F9688300B38D5F /* UserAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressView.swift; sourceTree = "<group>"; };
64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressLearnMore.swift; sourceTree = "<group>"; };
@ -702,8 +692,8 @@
64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */,
64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */,
64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */,
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F-ghc9.6.3.a in Frameworks */,
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F.a in Frameworks */,
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.6.0-64eNxtIoLF9BaOhAoPagss-ghc9.6.3.a in Frameworks */,
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.3.6.0-64eNxtIoLF9BaOhAoPagss.a in Frameworks */,
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -788,8 +778,8 @@
64C829992D54AEEE006B9E89 /* libffi.a */,
64C829982D54AEED006B9E89 /* libgmp.a */,
64C8299C2D54AEEE006B9E89 /* libgmpxx.a */,
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F-ghc9.6.3.a */,
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F.a */,
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.6.0-64eNxtIoLF9BaOhAoPagss-ghc9.6.3.a */,
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.6.0-64eNxtIoLF9BaOhAoPagss.a */,
);
path = Libraries;
sourceTree = "<group>";
@ -1086,7 +1076,6 @@
644EFFDD292BCD9D00525D5B /* ComposeVoiceView.swift */,
D72A9087294BD7A70047C86D /* NativeTextEditor.swift */,
6419EC552AB8BC8B004A607A /* ContextInvitingContactMemberView.swift */,
64A77A012DC4AD6100FDEF2F /* ContextPendingMemberActionsView.swift */,
);
path = ComposeMessage;
sourceTree = "<group>";
@ -1128,10 +1117,6 @@
6448BBB528FA9D56000D2AB9 /* GroupLinkView.swift */,
1841516F0CE5992B0EDFB377 /* GroupWelcomeView.swift */,
B70CE9E52D4BE5930080F36D /* GroupMentions.swift */,
64A779F52DBFB9F200FDEF2F /* MemberAdmissionView.swift */,
64A779F72DBFDBF200FDEF2F /* MemberSupportView.swift */,
64A779FB2DC1040000FDEF2F /* SecondaryChatView.swift */,
64A779FD2DC3AFF200FDEF2F /* MemberSupportChatToolbar.swift */,
);
path = Group;
sourceTree = "<group>";
@ -1446,10 +1431,8 @@
640417CE2B29B8C200CCB412 /* NewChatView.swift in Sources */,
6440CA03288AECA70062C672 /* AddGroupMembersView.swift in Sources */,
640743612CD360E600158442 /* ChooseServerOperators.swift in Sources */,
64A779FE2DC3AFF200FDEF2F /* MemberSupportChatToolbar.swift in Sources */,
5C3F1D58284363C400EC8A82 /* PrivacySettings.swift in Sources */,
5C55A923283CEDE600C4E99E /* SoundPlayer.swift in Sources */,
64A779F82DBFDBF200FDEF2F /* MemberSupportView.swift in Sources */,
5C93292F29239A170090FFF9 /* ProtocolServersView.swift in Sources */,
5CB924D727A8563F00ACCCDD /* SettingsView.swift in Sources */,
B79ADAFF2CE4EF930083DFFD /* AddressCreationCard.swift in Sources */,
@ -1484,7 +1467,6 @@
5CB634AF29E4BB7D0066AD6B /* SetAppPasscodeView.swift in Sources */,
5C10D88828EED12E00E58BF0 /* ContactConnectionInfo.swift in Sources */,
5CBE6C12294487F7002D9531 /* VerifyCodeView.swift in Sources */,
64A779FC2DC1040000FDEF2F /* SecondaryChatView.swift in Sources */,
3CDBCF4227FAE51000354CDD /* ComposeLinkView.swift in Sources */,
648679AB2BC96A74006456E7 /* ChatItemForwardingView.swift in Sources */,
3CDBCF4827FF621E00354CDD /* CILinkView.swift in Sources */,
@ -1527,7 +1509,6 @@
5CB346E72868D76D001FD2EF /* NotificationsView.swift in Sources */,
647F090E288EA27B00644C40 /* GroupMemberInfoView.swift in Sources */,
646BB38E283FDB6D001CE359 /* LocalAuthenticationUtils.swift in Sources */,
64A77A022DC4AD6100FDEF2F /* ContextPendingMemberActionsView.swift in Sources */,
8C74C3EA2C1B90AF00039E77 /* ThemeManager.swift in Sources */,
5C7505A227B65FDB00BE3227 /* CIMetaView.swift in Sources */,
5C35CFC827B2782E00FB6C6D /* BGManager.swift in Sources */,
@ -1606,7 +1587,6 @@
1841560FD1CD447955474C1D /* UserProfilesView.swift in Sources */,
64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */,
8CC4ED902BD7B8530078AEE8 /* CallAudioDeviceManager.swift in Sources */,
64A779F62DBFB9F200FDEF2F /* MemberAdmissionView.swift in Sources */,
18415C6C56DBCEC2CBBD2F11 /* WebRTCClient.swift in Sources */,
8CB15EA02CFDA30600C28209 /* ChatItemsMerger.swift in Sources */,
184152CEF68D2336FC2EBCB0 /* CallViewRenderers.swift in Sources */,
@ -1991,7 +1971,7 @@
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 279;
CURRENT_PROJECT_VERSION = 282;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
@ -2016,7 +1996,7 @@
"@executable_path/Frameworks",
);
LLVM_LTO = YES_THIN;
MARKETING_VERSION = 6.4;
MARKETING_VERSION = 6.3.6;
OTHER_LDFLAGS = "-Wl,-stack_size,0x1000000";
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX;
@ -2041,7 +2021,7 @@
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 279;
CURRENT_PROJECT_VERSION = 282;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
@ -2066,7 +2046,7 @@
"@executable_path/Frameworks",
);
LLVM_LTO = YES;
MARKETING_VERSION = 6.4;
MARKETING_VERSION = 6.3.6;
OTHER_LDFLAGS = "-Wl,-stack_size,0x1000000";
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX;
@ -2083,11 +2063,11 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 279;
CURRENT_PROJECT_VERSION = 282;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 6.4;
MARKETING_VERSION = 6.3.6;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@ -2103,11 +2083,11 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 279;
CURRENT_PROJECT_VERSION = 282;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 6.4;
MARKETING_VERSION = 6.3.6;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@ -2128,7 +2108,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 279;
CURRENT_PROJECT_VERSION = 282;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GCC_OPTIMIZATION_LEVEL = s;
@ -2143,7 +2123,7 @@
"@executable_path/../../Frameworks",
);
LLVM_LTO = YES;
MARKETING_VERSION = 6.4;
MARKETING_VERSION = 6.3.6;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -2165,7 +2145,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 279;
CURRENT_PROJECT_VERSION = 282;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_CODE_COVERAGE = NO;
@ -2180,7 +2160,7 @@
"@executable_path/../../Frameworks",
);
LLVM_LTO = YES;
MARKETING_VERSION = 6.4;
MARKETING_VERSION = 6.3.6;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -2202,7 +2182,7 @@
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 279;
CURRENT_PROJECT_VERSION = 282;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@ -2228,7 +2208,7 @@
"$(PROJECT_DIR)/Libraries/sim",
);
LLVM_LTO = YES;
MARKETING_VERSION = 6.4;
MARKETING_VERSION = 6.3.6;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos;
@ -2253,7 +2233,7 @@
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 279;
CURRENT_PROJECT_VERSION = 282;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@ -2279,7 +2259,7 @@
"$(PROJECT_DIR)/Libraries/sim",
);
LLVM_LTO = YES;
MARKETING_VERSION = 6.4;
MARKETING_VERSION = 6.3.6;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos;
@ -2304,7 +2284,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 279;
CURRENT_PROJECT_VERSION = 282;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
@ -2319,7 +2299,7 @@
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 6.4;
MARKETING_VERSION = 6.3.6;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@ -2338,7 +2318,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 279;
CURRENT_PROJECT_VERSION = 282;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
@ -2353,7 +2333,7 @@
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 6.4;
MARKETING_VERSION = 6.3.6;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;

View file

@ -15,9 +15,6 @@ public let CREATE_MEMBER_CONTACT_VERSION = 2
// version to receive reports (MCReport)
public let REPORTS_VERSION = 12
// support group knocking (MsgScope)
public let GROUP_KNOCKING_VERSION = 15
public let contentModerationPostLink = URL(string: "https://simplex.chat/blog/20250114-simplex-network-large-groups-privacy-preserving-content-moderation.html#preventing-server-abuse-without-compromising-e2e-encryption")!
public struct User: Identifiable, Decodable, UserLike, NamedChat, Hashable {
@ -1200,7 +1197,7 @@ public enum GroupFeatureEnabled: String, Codable, Identifiable, Hashable {
public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
case direct(contact: Contact)
case group(groupInfo: GroupInfo, groupChatScope: GroupChatScopeInfo?)
case group(groupInfo: GroupInfo)
case local(noteFolder: NoteFolder)
case contactRequest(contactRequest: UserContactRequest)
case contactConnection(contactConnection: PendingContactConnection)
@ -1214,7 +1211,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
get {
switch self {
case let .direct(contact): return contact.localDisplayName
case let .group(groupInfo, _): return groupInfo.localDisplayName
case let .group(groupInfo): return groupInfo.localDisplayName
case .local: return ""
case let .contactRequest(contactRequest): return contactRequest.localDisplayName
case let .contactConnection(contactConnection): return contactConnection.localDisplayName
@ -1227,7 +1224,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
get {
switch self {
case let .direct(contact): return contact.displayName
case let .group(groupInfo, _): return groupInfo.displayName
case let .group(groupInfo): return groupInfo.displayName
case .local: return ChatInfo.privateNotesChatName
case let .contactRequest(contactRequest): return contactRequest.displayName
case let .contactConnection(contactConnection): return contactConnection.displayName
@ -1240,7 +1237,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
get {
switch self {
case let .direct(contact): return contact.fullName
case let .group(groupInfo, _): return groupInfo.fullName
case let .group(groupInfo): return groupInfo.fullName
case .local: return ""
case let .contactRequest(contactRequest): return contactRequest.fullName
case let .contactConnection(contactConnection): return contactConnection.fullName
@ -1253,7 +1250,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
get {
switch self {
case let .direct(contact): return contact.image
case let .group(groupInfo, _): return groupInfo.image
case let .group(groupInfo): return groupInfo.image
case .local: return nil
case let .contactRequest(contactRequest): return contactRequest.image
case let .contactConnection(contactConnection): return contactConnection.image
@ -1266,7 +1263,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
get {
switch self {
case let .direct(contact): return contact.localAlias
case let .group(groupInfo, _): return groupInfo.localAlias
case let .group(groupInfo): return groupInfo.localAlias
case .local: return ""
case let .contactRequest(contactRequest): return contactRequest.localAlias
case let .contactConnection(contactConnection): return contactConnection.localAlias
@ -1279,7 +1276,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
get {
switch self {
case let .direct(contact): return contact.id
case let .group(groupInfo, _): return groupInfo.id
case let .group(groupInfo): return groupInfo.id
case let .local(noteFolder): return noteFolder.id
case let .contactRequest(contactRequest): return contactRequest.id
case let .contactConnection(contactConnection): return contactConnection.id
@ -1305,7 +1302,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
get {
switch self {
case let .direct(contact): return contact.apiId
case let .group(groupInfo, _): return groupInfo.apiId
case let .group(groupInfo): return groupInfo.apiId
case let .local(noteFolder): return noteFolder.apiId
case let .contactRequest(contactRequest): return contactRequest.apiId
case let .contactConnection(contactConnection): return contactConnection.apiId
@ -1318,7 +1315,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
get {
switch self {
case let .direct(contact): return contact.ready
case let .group(groupInfo, _): return groupInfo.ready
case let .group(groupInfo): return groupInfo.ready
case let .local(noteFolder): return noteFolder.ready
case let .contactRequest(contactRequest): return contactRequest.ready
case let .contactConnection(contactConnection): return contactConnection.ready
@ -1339,57 +1336,34 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
public var userCantSendReason: (composeLabel: LocalizedStringKey, alertMessage: LocalizedStringKey?)? {
get {
switch self {
case let .direct(contact):
// TODO [short links] this will have additional statuses for pending contact requests before they are accepted
if contact.nextSendGrpInv { return nil }
if !contact.active { return ("contact deleted", nil) }
if !contact.sndReady { return ("contact not ready", nil) }
if contact.activeConn?.connectionStats?.ratchetSyncSendProhibited ?? false { return ("not synchronized", nil) }
if contact.activeConn?.connDisabled ?? true { return ("contact disabled", nil) }
return nil
case let .group(groupInfo, groupChatScope):
if groupInfo.membership.memberActive {
switch(groupChatScope) {
case .none:
if groupInfo.membership.memberPending { return ("reviewed by admins", "Please contact group admin.") }
if groupInfo.membership.memberRole == .observer { return ("you are observer", "Please contact group admin.") }
return nil
case let .some(.memberSupport(groupMember_: .some(supportMember))):
if supportMember.versionRange.maxVersion < GROUP_KNOCKING_VERSION && !supportMember.memberPending {
return ("member has old version", nil)
}
return nil
case .some(.memberSupport(groupMember_: .none)):
return nil
}
} else {
switch groupInfo.membership.memberStatus {
case .memRejected: return ("request to join rejected", nil)
case .memGroupDeleted: return ("group is deleted", nil)
case .memRemoved: return ("removed from group", nil)
case .memLeft: return ("you left", nil)
default: return ("can't send messages", nil)
}
}
case .local:
return nil
case .contactRequest:
return ("can't send messages", nil)
case .contactConnection:
return ("can't send messages", nil)
case .invalidJSON:
return ("can't send messages", nil)
case let .direct(contact): return contact.userCantSendReason
case let .group(groupInfo): return groupInfo.userCantSendReason
case let .local(noteFolder): return noteFolder.userCantSendReason
case let .contactRequest(contactRequest): return contactRequest.userCantSendReason
case let .contactConnection(contactConnection): return contactConnection.userCantSendReason
case .invalidJSON: return ("can't send messages", nil)
}
}
}
public var sendMsgEnabled: Bool { userCantSendReason == nil }
public var sendMsgEnabled: Bool {
get {
switch self {
case let .direct(contact): return contact.sendMsgEnabled
case let .group(groupInfo): return groupInfo.sendMsgEnabled
case let .local(noteFolder): return noteFolder.sendMsgEnabled
case let .contactRequest(contactRequest): return contactRequest.sendMsgEnabled
case let .contactConnection(contactConnection): return contactConnection.sendMsgEnabled
case .invalidJSON: return false
}
}
}
public var incognito: Bool {
get {
switch self {
case let .direct(contact): return contact.contactConnIncognito
case let .group(groupInfo, _): return groupInfo.membership.memberIncognito
case let .group(groupInfo): return groupInfo.membership.memberIncognito
case .local: return false
case .contactRequest: return false
case let .contactConnection(contactConnection): return contactConnection.incognito
@ -1414,7 +1388,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
public var groupInfo: GroupInfo? {
switch self {
case let .group(groupInfo, _): return groupInfo
case let .group(groupInfo): return groupInfo
default: return nil
}
}
@ -1431,7 +1405,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
case .voice: return cups.voice.enabled.forUser
case .calls: return cups.calls.enabled.forUser
}
case let .group(groupInfo, _):
case let .group(groupInfo):
let prefs = groupInfo.fullGroupPreferences
switch feature {
case .timedMessages: return prefs.timedMessages.on
@ -1454,7 +1428,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
case let .direct(contact):
let pref = contact.mergedPreferences.timedMessages
return pref.enabled.forUser ? pref.userPreference.preference.ttl : nil
case let .group(groupInfo, _):
case let .group(groupInfo):
let pref = groupInfo.fullGroupPreferences.timedMessages
return pref.on ? pref.ttl : nil
default:
@ -1479,7 +1453,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
} else {
return .other
}
case let .group(groupInfo, _):
case let .group(groupInfo):
if !groupInfo.fullGroupPreferences.voice.on(for: groupInfo.membership) {
return .groupOwnerCan
} else {
@ -1511,13 +1485,6 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
}
}
public func groupChatScope() -> GroupChatScope? {
switch self {
case let .group(_, groupChatScope): groupChatScope?.toChatScope()
default: nil
}
}
public func ntfsEnabled(chatItem: ChatItem) -> Bool {
ntfsEnabled(chatItem.meta.userMention)
}
@ -1533,7 +1500,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
public var chatSettings: ChatSettings? {
switch self {
case let .direct(contact): return contact.chatSettings
case let .group(groupInfo, _): return groupInfo.chatSettings
case let .group(groupInfo): return groupInfo.chatSettings
default: return nil
}
}
@ -1549,7 +1516,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
public var chatTags: [Int64]? {
switch self {
case let .direct(contact): return contact.chatTags
case let .group(groupInfo, _): return groupInfo.chatTags
case let .group(groupInfo): return groupInfo.chatTags
default: return nil
}
}
@ -1557,7 +1524,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
var createdAt: Date {
switch self {
case let .direct(contact): return contact.createdAt
case let .group(groupInfo, _): return groupInfo.createdAt
case let .group(groupInfo): return groupInfo.createdAt
case let .local(noteFolder): return noteFolder.createdAt
case let .contactRequest(contactRequest): return contactRequest.createdAt
case let .contactConnection(contactConnection): return contactConnection.createdAt
@ -1568,7 +1535,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
public var updatedAt: Date {
switch self {
case let .direct(contact): return contact.updatedAt
case let .group(groupInfo, _): return groupInfo.updatedAt
case let .group(groupInfo): return groupInfo.updatedAt
case let .local(noteFolder): return noteFolder.updatedAt
case let .contactRequest(contactRequest): return contactRequest.updatedAt
case let .contactConnection(contactConnection): return contactConnection.updatedAt
@ -1579,7 +1546,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
public var chatTs: Date {
switch self {
case let .direct(contact): return contact.chatTs ?? contact.updatedAt
case let .group(groupInfo, _): return groupInfo.chatTs ?? groupInfo.updatedAt
case let .group(groupInfo): return groupInfo.chatTs ?? groupInfo.updatedAt
case let .local(noteFolder): return noteFolder.chatTs
case let .contactRequest(contactRequest): return contactRequest.updatedAt
case let .contactConnection(contactConnection): return contactConnection.updatedAt
@ -1595,7 +1562,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
} else {
ChatTTL.userDefault(globalTTL)
}
case let .group(groupInfo, _):
case let .group(groupInfo):
return if let ciTTL = groupInfo.chatItemTTL {
ChatTTL.chat(ChatItemTTL(ciTTL))
} else {
@ -1615,7 +1582,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
public static var sampleData: ChatInfo.SampleData = SampleData(
direct: ChatInfo.direct(contact: Contact.sampleData),
group: ChatInfo.group(groupInfo: GroupInfo.sampleData, groupChatScope: nil),
group: ChatInfo.group(groupInfo: GroupInfo.sampleData),
local: ChatInfo.local(noteFolder: NoteFolder.sampleData),
contactRequest: ChatInfo.contactRequest(contactRequest: UserContactRequest.sampleData),
contactConnection: ChatInfo.contactConnection(contactConnection: PendingContactConnection.getSampleData())
@ -1645,13 +1612,7 @@ public struct ChatData: Decodable, Identifiable, Hashable, ChatLike {
}
public struct ChatStats: Decodable, Hashable {
public init(
unreadCount: Int = 0,
unreadMentions: Int = 0,
reportsCount: Int = 0,
minUnreadItemId: Int64 = 0,
unreadChat: Bool = false
) {
public init(unreadCount: Int = 0, unreadMentions: Int = 0, reportsCount: Int = 0, minUnreadItemId: Int64 = 0, unreadChat: Bool = false) {
self.unreadCount = unreadCount
self.unreadMentions = unreadMentions
self.reportsCount = reportsCount
@ -1668,32 +1629,6 @@ public struct ChatStats: Decodable, Hashable {
public var unreadChat: Bool = false
}
public enum GroupChatScope: Decodable {
case memberSupport(groupMemberId_: Int64?)
}
public func sameChatScope(_ scope1: GroupChatScope, _ scope2: GroupChatScope) -> Bool {
switch (scope1, scope2) {
case let (.memberSupport(groupMemberId1_), .memberSupport(groupMemberId2_)):
return groupMemberId1_ == groupMemberId2_
}
}
public enum GroupChatScopeInfo: Decodable, Hashable {
case memberSupport(groupMember_: GroupMember?)
public func toChatScope() -> GroupChatScope {
switch self {
case let .memberSupport(groupMember_):
if let groupMember = groupMember_ {
return .memberSupport(groupMemberId_: groupMember.groupMemberId)
} else {
return .memberSupport(groupMemberId_: nil)
}
}
}
}
public struct Contact: Identifiable, Decodable, NamedChat, Hashable {
public var contactId: Int64
var localDisplayName: ContactName
@ -1720,6 +1655,16 @@ public struct Contact: Identifiable, Decodable, NamedChat, Hashable {
public var ready: Bool { get { activeConn?.connStatus == .ready } }
public var sndReady: Bool { get { ready || activeConn?.connStatus == .sndReady } }
public var active: Bool { get { contactStatus == .active } }
public var userCantSendReason: (composeLabel: LocalizedStringKey, alertMessage: LocalizedStringKey?)? {
// TODO [short links] this will have additional statuses for pending contact requests before they are accepted
if nextSendGrpInv { return nil }
if !active { return ("contact deleted", nil) }
if !sndReady { return ("contact not ready", nil) }
if activeConn?.connectionStats?.ratchetSyncSendProhibited ?? false { return ("not synchronized", nil) }
if activeConn?.connDisabled ?? true { return ("contact disabled", nil) }
return nil
}
public var sendMsgEnabled: Bool { userCantSendReason == nil }
public var nextSendGrpInv: Bool { get { contactGroupMemberId != nil && !contactGrpInvSent } }
public var displayName: String { localAlias == "" ? profile.displayName : localAlias }
public var fullName: String { get { profile.fullName } }
@ -1898,6 +1843,8 @@ public struct UserContactRequest: Decodable, NamedChat, Hashable {
public var id: ChatId { get { "<@\(contactRequestId)" } }
public var apiId: Int64 { get { contactRequestId } }
var ready: Bool { get { true } }
public var userCantSendReason: (composeLabel: LocalizedStringKey, alertMessage: LocalizedStringKey?)? { ("can't send messages", nil) }
public var sendMsgEnabled: Bool { get { false } }
public var displayName: String { get { profile.displayName } }
public var fullName: String { get { profile.fullName } }
public var image: String? { get { profile.image } }
@ -1929,6 +1876,8 @@ public struct PendingContactConnection: Decodable, NamedChat, Hashable {
public var id: ChatId { get { ":\(pccConnId)" } }
public var apiId: Int64 { get { pccConnId } }
var ready: Bool { get { false } }
public var userCantSendReason: (composeLabel: LocalizedStringKey, alertMessage: LocalizedStringKey?)? { ("can't send messages", nil) }
public var sendMsgEnabled: Bool { get { false } }
var localDisplayName: String {
get { String.localizedStringWithFormat(NSLocalizedString("connection:%@", comment: "connection information"), pccConnId) }
}
@ -2053,11 +2002,24 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat, Hashable {
var updatedAt: Date
var chatTs: Date?
public var uiThemes: ThemeModeOverrides?
public var membersRequireAttention: Int
public var id: ChatId { get { "#\(groupId)" } }
public var apiId: Int64 { get { groupId } }
public var ready: Bool { get { true } }
public var userCantSendReason: (composeLabel: LocalizedStringKey, alertMessage: LocalizedStringKey?)? {
return if membership.memberActive {
membership.memberRole == .observer ? ("you are observer", "Please contact group admin.") : nil
} else {
switch membership.memberStatus {
case .memRejected: ("request to join rejected", nil)
case .memGroupDeleted: ("group is deleted", nil)
case .memRemoved: ("removed from group", nil)
case .memLeft: ("you left", nil)
default: ("can't send messages", nil)
}
}
}
public var sendMsgEnabled: Bool { userCantSendReason == nil }
public var displayName: String { localAlias == "" ? groupProfile.displayName : localAlias }
public var fullName: String { get { groupProfile.fullName } }
public var image: String? { get { groupProfile.image } }
@ -2070,17 +2032,13 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat, Hashable {
}
public var canDelete: Bool {
return membership.memberRole == .owner || !membership.memberCurrentOrPending
return membership.memberRole == .owner || !membership.memberCurrent
}
public var canAddMembers: Bool {
return membership.memberRole >= .admin && membership.memberActive
}
public var canModerate: Bool {
return membership.memberRole >= .moderator && membership.memberActive
}
public static let sampleData = GroupInfo(
groupId: 1,
localDisplayName: "team",
@ -2090,7 +2048,6 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat, Hashable {
chatSettings: ChatSettings.defaults,
createdAt: .now,
updatedAt: .now,
membersRequireAttention: 0,
chatTags: [],
localAlias: ""
)
@ -2102,20 +2059,12 @@ public struct GroupRef: Decodable, Hashable {
}
public struct GroupProfile: Codable, NamedChat, Hashable {
public init(
displayName: String,
fullName: String,
description: String? = nil,
image: String? = nil,
groupPreferences: GroupPreferences? = nil,
memberAdmission: GroupMemberAdmission? = nil
) {
public init(displayName: String, fullName: String, description: String? = nil, image: String? = nil, groupPreferences: GroupPreferences? = nil) {
self.displayName = displayName
self.fullName = fullName
self.description = description
self.image = image
self.groupPreferences = groupPreferences
self.memberAdmission = memberAdmission
}
public var displayName: String
@ -2123,48 +2072,14 @@ public struct GroupProfile: Codable, NamedChat, Hashable {
public var description: String?
public var image: String?
public var groupPreferences: GroupPreferences?
public var memberAdmission: GroupMemberAdmission?
public var localAlias: String { "" }
public var memberAdmission_: GroupMemberAdmission {
get { self.memberAdmission ?? GroupMemberAdmission() }
set { memberAdmission = newValue }
}
public static let sampleData = GroupProfile(
displayName: "team",
fullName: "My Team"
)
}
public struct GroupMemberAdmission: Codable, Hashable {
public var review: MemberCriteria?
public init(
review: MemberCriteria? = nil
) {
self.review = review
}
public static let sampleData = GroupMemberAdmission(
review: .all
)
}
public enum MemberCriteria: String, Codable, Identifiable, Hashable {
case all
public static var values: [MemberCriteria] { [.all] }
public var id: Self { self }
public var text: String {
switch self {
case .all: return NSLocalizedString("all", comment: "member criteria value")
}
}
}
public struct BusinessChatInfo: Decodable, Hashable {
public var chatType: BusinessChatType
public var businessId: String
@ -2191,7 +2106,6 @@ public struct GroupMember: Identifiable, Decodable, Hashable {
public var memberContactId: Int64?
public var memberContactProfileId: Int64
public var activeConn: Connection?
public var supportChat: GroupSupportChat?
public var memberChatVRange: VersionRange
public var id: String { "#\(groupId) @\(groupMemberId)" }
@ -2263,7 +2177,6 @@ public struct GroupMember: Identifiable, Decodable, Hashable {
case .memUnknown: return false
case .memInvited: return false
case .memPendingApproval: return true
case .memPendingReview: return true
case .memIntroduced: return false
case .memIntroInvited: return false
case .memAccepted: return false
@ -2283,7 +2196,6 @@ public struct GroupMember: Identifiable, Decodable, Hashable {
case .memUnknown: return false
case .memInvited: return false
case .memPendingApproval: return false
case .memPendingReview: return false
case .memIntroduced: return true
case .memIntroInvited: return true
case .memAccepted: return true
@ -2294,18 +2206,6 @@ public struct GroupMember: Identifiable, Decodable, Hashable {
}
}
public var memberPending: Bool {
switch memberStatus {
case .memPendingApproval: return true
case .memPendingReview: return true
default: return false
}
}
public var memberCurrentOrPending: Bool {
memberCurrent || memberPending
}
public func canBeRemoved(groupInfo: GroupInfo) -> Bool {
let userRole = groupInfo.membership.memberRole
return memberStatus != .memRemoved && memberStatus != .memLeft
@ -2359,13 +2259,6 @@ public struct GroupMember: Identifiable, Decodable, Hashable {
)
}
public struct GroupSupportChat: Codable, Hashable {
public var chatTs: Date
public var unread: Int
public var memberAttention: Int
public var mentions: Int
}
public struct GroupMemberSettings: Codable, Hashable {
public var showMessages: Bool
}
@ -2435,7 +2328,6 @@ public enum GroupMemberStatus: String, Decodable, Hashable {
case memUnknown = "unknown"
case memInvited = "invited"
case memPendingApproval = "pending_approval"
case memPendingReview = "pending_review"
case memIntroduced = "introduced"
case memIntroInvited = "intro-inv"
case memAccepted = "accepted"
@ -2453,7 +2345,6 @@ public enum GroupMemberStatus: String, Decodable, Hashable {
case .memUnknown: return "unknown status"
case .memInvited: return "invited"
case .memPendingApproval: return "pending approval"
case .memPendingReview: return "pending review"
case .memIntroduced: return "connecting (introduced)"
case .memIntroInvited: return "connecting (introduction invitation)"
case .memAccepted: return "connecting (accepted)"
@ -2473,7 +2364,6 @@ public enum GroupMemberStatus: String, Decodable, Hashable {
case .memUnknown: return "unknown"
case .memInvited: return "invited"
case .memPendingApproval: return "pending"
case .memPendingReview: return "review"
case .memIntroduced: return "connecting"
case .memIntroInvited: return "connecting"
case .memAccepted: return "connecting"
@ -2496,6 +2386,8 @@ public struct NoteFolder: Identifiable, Decodable, NamedChat, Hashable {
public var id: ChatId { get { "*\(noteFolderId)" } }
public var apiId: Int64 { get { noteFolderId } }
public var ready: Bool { get { true } }
public var userCantSendReason: (composeLabel: LocalizedStringKey, alertMessage: LocalizedStringKey?)? { nil }
public var sendMsgEnabled: Bool { get { true } }
public var displayName: String { get { ChatInfo.privateNotesChatName } }
public var fullName: String { get { "" } }
public var image: String? { get { nil } }
@ -2755,15 +2647,12 @@ public struct ChatItem: Identifiable, Decodable, Hashable {
case .userDeleted: nil
case .groupDeleted: nil
case .memberCreatedContact: nil
case .newMemberPendingReview: nil
default: .rcvGroupEvent
}
case let .sndGroupEvent(event):
switch event {
case .userRole: nil
case .userLeft: nil
case .memberAccepted: nil
case .userPendingReview: nil
default: .sndGroupEvent
}
default:
@ -2796,8 +2685,6 @@ public struct ChatItem: Identifiable, Decodable, Hashable {
switch rcvGroupEvent {
case .groupUpdated: return false
case .memberConnected: return false
case .memberAccepted: return false
case .userAccepted: return false
case .memberRole: return false
case .memberBlocked: return false
case .userRole: return true
@ -2809,7 +2696,6 @@ public struct ChatItem: Identifiable, Decodable, Hashable {
case .invitedViaGroupLink: return false
case .memberCreatedContact: return false
case .memberProfileUpdated: return false
case .newMemberPendingReview: return true
}
case .sndGroupEvent: return false
case .rcvConnEvent: return false
@ -2878,12 +2764,12 @@ public struct ChatItem: Identifiable, Decodable, Hashable {
public func memberToModerate(_ chatInfo: ChatInfo) -> (GroupInfo, GroupMember?)? {
switch (chatInfo, chatDir) {
case let (.group(groupInfo, _), .groupRcv(groupMember)):
case let (.group(groupInfo), .groupRcv(groupMember)):
let m = groupInfo.membership
return m.memberRole >= .admin && m.memberRole >= groupMember.memberRole && meta.itemDeleted == nil
? (groupInfo, groupMember)
: nil
case let (.group(groupInfo, _), .groupSnd):
case let (.group(groupInfo), .groupSnd):
let m = groupInfo.membership
return m.memberRole >= .admin ? (groupInfo, nil) : nil
default: return nil
@ -3265,21 +3151,6 @@ public enum CIStatus: Decodable, Hashable {
}
}
// as in corresponds to SENT response from agent, opposed to `sent` which means snd status
public var isSent: Bool {
switch self {
case .sndNew: false
case .sndSent: true
case .sndRcvd: false
case .sndErrorAuth: true
case .sndError: true
case .sndWarning: true
case .rcvNew: false
case .rcvRead: false
case .invalid: false
}
}
public func statusIcon(_ metaColor: Color, _ paleMetaColor: Color, _ primaryColor: Color = .accentColor) -> (Image, Color)? {
switch self {
case .sndNew: nil
@ -3333,17 +3204,6 @@ public enum CIStatus: Decodable, Hashable {
}
}
public func shouldKeepOldSndCIStatus(oldStatus: CIStatus, newStatus: CIStatus) -> Bool {
switch (oldStatus, newStatus) {
case (.sndRcvd, let new) where !new.isSndRcvd:
return true
case (let old, .sndNew) where old.isSent:
return true
default:
return false
}
}
public enum SndError: Decodable, Hashable {
case auth
case quota
@ -4243,16 +4103,6 @@ extension MsgContent: Encodable {
}
}
public enum MsgContentTag: String {
case text
case link
case image
case video
case voice
case file
case report
}
public struct FormattedText: Decodable, Hashable {
public var text: String
public var format: Format?
@ -4590,8 +4440,6 @@ public enum RcvDirectEvent: Decodable, Hashable {
public enum RcvGroupEvent: Decodable, Hashable {
case memberAdded(groupMemberId: Int64, profile: Profile)
case memberConnected
case memberAccepted(groupMemberId: Int64, profile: Profile)
case userAccepted
case memberLeft
case memberRole(groupMemberId: Int64, profile: Profile, role: GroupMemberRole)
case memberBlocked(groupMemberId: Int64, profile: Profile, blocked: Bool)
@ -4603,16 +4451,12 @@ public enum RcvGroupEvent: Decodable, Hashable {
case invitedViaGroupLink
case memberCreatedContact
case memberProfileUpdated(fromProfile: Profile, toProfile: Profile)
case newMemberPendingReview
var text: String {
switch self {
case let .memberAdded(_, profile):
return String.localizedStringWithFormat(NSLocalizedString("invited %@", comment: "rcv group event chat item"), profile.profileViewName)
case .memberConnected: return NSLocalizedString("member connected", comment: "rcv group event chat item")
case let .memberAccepted(_, profile):
return String.localizedStringWithFormat(NSLocalizedString("accepted %@", comment: "rcv group event chat item"), profile.profileViewName)
case .userAccepted: return NSLocalizedString("accepted you", comment: "rcv group event chat item")
case .memberLeft: return NSLocalizedString("left", comment: "rcv group event chat item")
case let .memberRole(_, profile, role):
return String.localizedStringWithFormat(NSLocalizedString("changed role of %@ to %@", comment: "rcv group event chat item"), profile.profileViewName, role.text)
@ -4632,7 +4476,6 @@ public enum RcvGroupEvent: Decodable, Hashable {
case .invitedViaGroupLink: return NSLocalizedString("invited via your group link", comment: "rcv group event chat item")
case .memberCreatedContact: return NSLocalizedString("connected directly", comment: "rcv group event chat item")
case let .memberProfileUpdated(fromProfile, toProfile): return profileUpdatedText(fromProfile, toProfile)
case .newMemberPendingReview: return NSLocalizedString("New member wants to join the group.", comment: "rcv group event chat item")
}
}
@ -4657,8 +4500,6 @@ public enum SndGroupEvent: Decodable, Hashable {
case memberDeleted(groupMemberId: Int64, profile: Profile)
case userLeft
case groupUpdated(groupProfile: GroupProfile)
case memberAccepted(groupMemberId: Int64, profile: Profile)
case userPendingReview
var text: String {
switch self {
@ -4676,9 +4517,6 @@ public enum SndGroupEvent: Decodable, Hashable {
return String.localizedStringWithFormat(NSLocalizedString("you removed %@", comment: "snd group event chat item"), profile.profileViewName)
case .userLeft: return NSLocalizedString("you left", comment: "snd group event chat item")
case .groupUpdated: return NSLocalizedString("group profile updated", comment: "snd group event chat item")
case .memberAccepted: return NSLocalizedString("you accepted this member", comment: "snd group event chat item")
case .userPendingReview:
return NSLocalizedString("Please wait for group moderators to review your request to join the group.", comment: "snd group event chat item")
}
}
}

View file

@ -16,7 +16,7 @@ public protocol ChatLike {
extension ChatLike {
public func groupFeatureEnabled(_ feature: GroupFeature) -> Bool {
if case let .group(groupInfo, _) = self.chatInfo {
if case let .group(groupInfo) = self.chatInfo {
let p = groupInfo.fullGroupPreferences
return switch feature {
case .timedMessages: p.timedMessages.on
@ -82,9 +82,9 @@ public func foundChat(_ chat: ChatLike, _ searchStr: String) -> Bool {
private func canForwardToChat(_ cInfo: ChatInfo) -> Bool {
switch cInfo {
case let .direct(contact): cInfo.sendMsgEnabled && !contact.nextSendGrpInv
case .group: cInfo.sendMsgEnabled
case .local: cInfo.sendMsgEnabled
case let .direct(contact): contact.sendMsgEnabled && !contact.nextSendGrpInv
case let .group(groupInfo): groupInfo.sendMsgEnabled
case let .local(noteFolder): noteFolder.sendMsgEnabled
case .contactRequest: false
case .contactConnection: false
case .invalidJSON: false
@ -94,7 +94,7 @@ private func canForwardToChat(_ cInfo: ChatInfo) -> Bool {
public func chatIconName(_ cInfo: ChatInfo) -> String {
switch cInfo {
case .direct: "person.crop.circle.fill"
case let .group(groupInfo, _):
case let .group(groupInfo):
switch groupInfo.businessChat?.chatType {
case .none: "person.2.circle.fill"
case .business: "briefcase.circle.fill"

View file

@ -62,7 +62,7 @@ public func createContactConnectedNtf(_ user: any UserLike, _ contact: Contact,
public func createMessageReceivedNtf(_ user: any UserLike, _ cInfo: ChatInfo, _ cItem: ChatItem, _ badgeCount: Int) -> UNMutableNotificationContent {
let previewMode = ntfPreviewModeGroupDefault.get()
var title: String
if case let .group(groupInfo, _) = cInfo, case let .groupRcv(groupMember) = cItem.chatDir {
if case let .group(groupInfo) = cInfo, case let .groupRcv(groupMember) = cItem.chatDir {
title = groupMsgNtfTitle(groupInfo, groupMember, hideContent: previewMode == .hidden)
} else {
title = previewMode == .hidden ? contactHidden : "\(cInfo.chatViewName):"

View file

@ -1485,7 +1485,7 @@ swipe action */
/* No comment provided by engineer. */
"Don't enable" = "Не активирай";
/* alert action */
/* No comment provided by engineer. */
"Don't show again" = "Не показвай отново";
/* No comment provided by engineer. */
@ -1777,7 +1777,7 @@ chat item action */
/* alert title */
"Error receiving file" = "Грешка при получаване на файл";
/* alert title */
/* No comment provided by engineer. */
"Error removing member" = "Грешка при отстраняване на член";
/* No comment provided by engineer. */
@ -2747,7 +2747,6 @@ snd error text */
/* enabled status
group pref value
member criteria value
time to disappear */
"off" = "изключено";
@ -2760,8 +2759,7 @@ time to disappear */
/* feature offered item */
"offered %@: %@" = "предлага %1$@: %2$@";
/* alert action
alert button */
/* alert button */
"Ok" = "Ок";
/* No comment provided by engineer. */
@ -3778,6 +3776,9 @@ chat item action */
/* No comment provided by engineer. */
"The old database was not removed during the migration, it can be deleted." = "Старата база данни не бе премахната по време на миграцията, тя може да бъде изтрита.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Профилът се споделя само с вашите контакти.";
/* No comment provided by engineer. */
"The second tick we missed! ✅" = "Втората отметка, която пропуснахме! ✅";
@ -4445,10 +4446,10 @@ chat item action */
"Your profile **%@** will be shared." = "Вашият профил **%@** ще бъде споделен.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Профилът се споделя само с вашите контакти.";
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Вашият профил се съхранява на вашето устройство и се споделя само с вашите контакти. SimpleX сървърите не могат да видят вашия профил.";
/* No comment provided by engineer. */
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Вашият профил се съхранява на вашето устройство и се споделя само с вашите контакти. SimpleX сървърите не могат да видят вашия профил.";
"Your profile, contacts and delivered messages are stored on your device." = "Вашият профил, контакти и доставени съобщения се съхраняват на вашето устройство.";
/* No comment provided by engineer. */
"Your random profile" = "Вашият автоматично генериран профил";

View file

@ -1127,7 +1127,7 @@ swipe action */
/* No comment provided by engineer. */
"Don't enable" = "Nepovolovat";
/* alert action */
/* No comment provided by engineer. */
"Don't show again" = "Znovu neukazuj";
/* No comment provided by engineer. */
@ -1367,7 +1367,7 @@ swipe action */
/* alert title */
"Error receiving file" = "Chyba při příjmu souboru";
/* alert title */
/* No comment provided by engineer. */
"Error removing member" = "Chyba při odebrání člena";
/* No comment provided by engineer. */
@ -2145,7 +2145,6 @@ snd error text */
/* enabled status
group pref value
member criteria value
time to disappear */
"off" = "vypnuto";
@ -2158,8 +2157,7 @@ time to disappear */
/* feature offered item */
"offered %@: %@" = "nabídl %1$@: %2$@";
/* alert action
alert button */
/* alert button */
"Ok" = "Ok";
/* No comment provided by engineer. */
@ -2990,6 +2988,9 @@ chat item action */
/* No comment provided by engineer. */
"The old database was not removed during the migration, it can be deleted." = "Stará databáze nebyla během přenášení odstraněna, lze ji smazat.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Profil je sdílen pouze s vašimi kontakty.";
/* No comment provided by engineer. */
"The second tick we missed! ✅" = "Druhé zaškrtnutí jsme přehlédli! ✅";
@ -3471,10 +3472,10 @@ chat item action */
"Your profile **%@** will be shared." = "Váš profil **%@** bude sdílen.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Profil je sdílen pouze s vašimi kontakty.";
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Váš profil je uložen ve vašem zařízení a sdílen pouze s vašimi kontakty. Servery SimpleX nevidí váš profil.";
/* No comment provided by engineer. */
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Váš profil je uložen ve vašem zařízení a sdílen pouze s vašimi kontakty. Servery SimpleX nevidí váš profil.";
"Your profile, contacts and delivered messages are stored on your device." = "Váš profil, kontakty a doručené zprávy jsou uloženy ve vašem zařízení.";
/* No comment provided by engineer. */
"Your random profile" = "Váš náhodný profil";

View file

@ -345,12 +345,6 @@ accept incoming call via notification
swipe action */
"Accept" = "Annehmen";
/* alert action */
"Accept as member" = "Als Mitglied übernehmen";
/* alert action */
"Accept as observer" = "Als Beobachter übernehmen";
/* No comment provided by engineer. */
"Accept conditions" = "Nutzungsbedingungen akzeptieren";
@ -364,12 +358,6 @@ swipe action */
swipe action */
"Accept incognito" = "Inkognito akzeptieren";
/* alert title */
"Accept member" = "Mitglied übernehmen";
/* rcv group event chat item */
"accepted %@" = "%@ übernommen";
/* call status */
"accepted call" = "Anruf angenommen";
@ -379,9 +367,6 @@ swipe action */
/* chat list item title */
"accepted invitation" = "Einladung angenommen";
/* rcv group event chat item */
"accepted you" = "hat Sie übernommen";
/* No comment provided by engineer. */
"Acknowledged" = "Bestätigt";
@ -478,9 +463,6 @@ swipe action */
/* chat item text */
"agreeing encryption…" = "Verschlüsselung zustimmen…";
/* member criteria value */
"all" = "alle";
/* No comment provided by engineer. */
"All" = "Alle";
@ -923,9 +905,6 @@ marked deleted chat item preview text */
/* No comment provided by engineer. */
"Can't message member" = "Mitglied kann nicht benachrichtigt werden";
/* No comment provided by engineer. */
"can't send messages" = "Es können keine Nachrichten gesendet werden";
/* alert action
alert button */
"Cancel" = "Abbrechen";
@ -1063,18 +1042,9 @@ set passcode view */
/* No comment provided by engineer. */
"Chat will be deleted for you - this cannot be undone!" = "Der Chat wird für Sie gelöscht. Dies kann nicht rückgängig gemacht werden!";
/* chat toolbar */
"Chat with admins" = "Chat mit Administratoren";
/* No comment provided by engineer. */
"Chat with member" = "Chat mit einem Mitglied";
/* No comment provided by engineer. */
"Chats" = "Chats";
/* No comment provided by engineer. */
"Chats with members" = "Chats mit Mitgliedern";
/* No comment provided by engineer. */
"Check messages every 20 min." = "Alle 20min Nachrichten überprüfen.";
@ -1363,15 +1333,9 @@ set passcode view */
/* No comment provided by engineer. */
"Contact already exists" = "Der Kontakt ist bereits vorhanden";
/* No comment provided by engineer. */
"contact deleted" = "Kontakt gelöscht";
/* No comment provided by engineer. */
"Contact deleted!" = "Kontakt gelöscht!";
/* No comment provided by engineer. */
"contact disabled" = "Kontakt deaktiviert";
/* No comment provided by engineer. */
"contact has e2e encryption" = "Kontakt nutzt E2E-Verschlüsselung";
@ -1390,9 +1354,6 @@ set passcode view */
/* No comment provided by engineer. */
"Contact name" = "Kontaktname";
/* No comment provided by engineer. */
"contact not ready" = "Kontakt nicht bereit";
/* No comment provided by engineer. */
"Contact preferences" = "Kontakt-Präferenzen";
@ -1641,9 +1602,6 @@ swipe action */
/* No comment provided by engineer. */
"Delete chat profile?" = "Chat-Profil löschen?";
/* alert title */
"Delete chat with member?" = "Chat mit dem Mitglied löschen?";
/* No comment provided by engineer. */
"Delete chat?" = "Chat löschen?";
@ -1914,7 +1872,7 @@ swipe action */
/* No comment provided by engineer. */
"Don't miss important messages." = "Verpassen Sie keine wichtigen Nachrichten.";
/* alert action */
/* No comment provided by engineer. */
"Don't show again" = "Nicht nochmals anzeigen";
/* No comment provided by engineer. */
@ -2158,9 +2116,6 @@ chat item action */
/* No comment provided by engineer. */
"Error accepting contact request" = "Fehler beim Annehmen der Kontaktanfrage";
/* alert title */
"Error accepting member" = "Fehler beim Übernehmen des Mitglieds";
/* No comment provided by engineer. */
"Error adding member(s)" = "Fehler beim Hinzufügen von Mitgliedern";
@ -2218,9 +2173,6 @@ chat item action */
/* No comment provided by engineer. */
"Error deleting chat database" = "Fehler beim Löschen der Chat-Datenbank";
/* alert title */
"Error deleting chat with member" = "Fehler beim Löschen des Chats mit dem Mitglied";
/* No comment provided by engineer. */
"Error deleting chat!" = "Fehler beim Löschen des Chats!";
@ -2284,7 +2236,7 @@ chat item action */
/* alert title */
"Error registering for notifications" = "Fehler beim Registrieren für Benachrichtigungen";
/* alert title */
/* No comment provided by engineer. */
"Error removing member" = "Fehler beim Entfernen des Mitglieds";
/* alert title */
@ -2658,9 +2610,6 @@ snd error text */
/* No comment provided by engineer. */
"Group invitation is no longer valid, it was removed by sender." = "Die Gruppeneinladung ist nicht mehr gültig, da sie vom Absender entfernt wurde.";
/* No comment provided by engineer. */
"group is deleted" = "Gruppe wird gelöscht";
/* No comment provided by engineer. */
"Group link" = "Gruppen-Link";
@ -3189,15 +3138,9 @@ snd error text */
/* profile update event chat item */
"member %@ changed to %@" = "Der Mitgliedsname von %1$@ wurde auf %2$@ geändert";
/* No comment provided by engineer. */
"Member admission" = "Aufnahme von Mitgliedern";
/* rcv group event chat item */
"member connected" = "ist der Gruppe beigetreten";
/* No comment provided by engineer. */
"member has old version" = "Das Mitglied hat eine alte App-Version";
/* item status text */
"Member inactive" = "Mitglied inaktiv";
@ -3219,9 +3162,6 @@ snd error text */
/* No comment provided by engineer. */
"Member will be removed from group - this cannot be undone!" = "Das Mitglied wird aus der Gruppe entfernt. Dies kann nicht rückgängig gemacht werden!";
/* alert message */
"Member will join the group, accept member?" = "Ein Mitglied wird der Gruppe beitreten. Übernehmen?";
/* No comment provided by engineer. */
"Members can add message reactions." = "Gruppenmitglieder können eine Reaktion auf Nachrichten geben.";
@ -3492,9 +3432,6 @@ snd error text */
/* No comment provided by engineer. */
"New member role" = "Neue Mitgliedsrolle";
/* rcv group event chat item */
"New member wants to join the group." = "Ein neues Mitglied will der Gruppe beitreten.";
/* notification */
"new message" = "Neue Nachricht";
@ -3534,9 +3471,6 @@ snd error text */
/* No comment provided by engineer. */
"No chats in list %@" = "Keine Chats in der Liste %@";
/* No comment provided by engineer. */
"No chats with members" = "Keine Chats mit Mitgliedern";
/* No comment provided by engineer. */
"No contacts selected" = "Keine Kontakte ausgewählt";
@ -3621,9 +3555,6 @@ snd error text */
/* No comment provided by engineer. */
"Not compatible!" = "Nicht kompatibel!";
/* No comment provided by engineer. */
"not synchronized" = "Nicht synchronisiert";
/* No comment provided by engineer. */
"Notes" = "Anmerkungen";
@ -3656,7 +3587,6 @@ snd error text */
/* enabled status
group pref value
member criteria value
time to disappear */
"off" = "Aus";
@ -3669,8 +3599,7 @@ time to disappear */
/* feature offered item */
"offered %@: %@" = "angeboten %1$@: %2$@";
/* alert action
alert button */
/* alert button */
"Ok" = "Ok";
/* No comment provided by engineer. */
@ -3766,9 +3695,6 @@ alert button */
/* No comment provided by engineer. */
"Open group" = "Gruppe öffnen";
/* alert title */
"Open link?" = "Link öffnen?";
/* authentication reason */
"Open migration to another device" = "Migration auf ein anderes Gerät öffnen";
@ -3871,9 +3797,6 @@ alert button */
/* No comment provided by engineer. */
"pending approval" = "ausstehende Genehmigung";
/* No comment provided by engineer. */
"pending review" = "Ausstehende Überprüfung";
/* No comment provided by engineer. */
"Periodic" = "Periodisch";
@ -3943,9 +3866,6 @@ alert button */
/* token info */
"Please try to disable and re-enable notfications." = "Bitte versuchen Sie, die Benachrichtigungen zu deaktivieren und wieder zu aktivieren.";
/* snd group event chat item */
"Please wait for group moderators to review your request to join the group." = "Bitte warten Sie auf die Überprüfung Ihrer Anfrage durch die Gruppen-Moderatoren, um der Gruppe beitreten zu können.";
/* token info */
"Please wait for token activation to complete." = "Bitte warten Sie, bis die Token-Aktivierung abgeschlossen ist.";
@ -4226,9 +4146,6 @@ swipe action */
/* No comment provided by engineer. */
"Reject contact request" = "Kontaktanfrage ablehnen";
/* alert title */
"Reject member?" = "Mitglied ablehnen?";
/* No comment provided by engineer. */
"rejected" = "abgelehnt";
@ -4268,9 +4185,6 @@ swipe action */
/* profile update event chat item */
"removed contact address" = "Die Kontaktadresse wurde entfernt";
/* No comment provided by engineer. */
"removed from group" = "Von der Gruppe entfernt";
/* profile update event chat item */
"removed profile picture" = "Das Profil-Bild wurde entfernt";
@ -4319,9 +4233,6 @@ swipe action */
/* No comment provided by engineer. */
"Report reason?" = "Grund der Meldung?";
/* alert title */
"Report sent to moderators" = "Meldung wurde an die Moderatoren gesendet";
/* report reason */
"Report spam: only group moderators will see it." = "Spam melden: Nur Gruppenmoderatoren werden es sehen.";
@ -4337,9 +4248,6 @@ swipe action */
/* No comment provided by engineer. */
"Reports" = "Meldungen";
/* No comment provided by engineer. */
"request to join rejected" = "Beitrittsanfrage abgelehnt";
/* chat list item title */
"requested to connect" = "Zur Verbindung aufgefordert";
@ -4394,21 +4302,9 @@ swipe action */
/* chat item action */
"Reveal" = "Aufdecken";
/* No comment provided by engineer. */
"review" = "Überprüfung";
/* No comment provided by engineer. */
"Review conditions" = "Nutzungsbedingungen einsehen";
/* admission stage */
"Review members" = "Überprüfung der Mitglieder";
/* admission stage description */
"Review members before admitting (\"knocking\")." = "Überprüfung der Mitglieder vor der Aufnahme (\"Anklopfen\").";
/* No comment provided by engineer. */
"reviewed by admins" = "Von Administratoren überprüft";
/* No comment provided by engineer. */
"Revoke" = "Widerrufen";
@ -4437,9 +4333,6 @@ chat item action */
/* alert button */
"Save (and notify contacts)" = "Speichern (und Kontakte benachrichtigen)";
/* alert title */
"Save admission settings?" = "Speichern der Aufnahme-Einstellungen?";
/* alert button */
"Save and notify contact" = "Speichern und Kontakt benachrichtigen";
@ -4776,9 +4669,6 @@ chat item action */
/* No comment provided by engineer. */
"Set it instead of system authentication." = "Anstelle der System-Authentifizierung festlegen.";
/* No comment provided by engineer. */
"Set member admission" = "Aufnahme von Mitgliedern festlegen";
/* No comment provided by engineer. */
"Set message expiration in chats." = "Verfallsdatum von Nachrichten in Chats festlegen.";
@ -5210,6 +5100,9 @@ report reason */
/* No comment provided by engineer. */
"The old database was not removed during the migration, it can be deleted." = "Die alte Datenbank wurde während der Migration nicht entfernt. Sie kann gelöscht werden.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Das Profil wird nur mit Ihren Kontakten geteilt.";
/* No comment provided by engineer. */
"The same conditions will apply to operator **%@**." = "Dieselben Nutzungsbedingungen gelten auch für den Betreiber **%@**.";
@ -5819,9 +5712,6 @@ report reason */
/* No comment provided by engineer. */
"You accepted connection" = "Sie haben die Verbindung akzeptiert";
/* snd group event chat item */
"you accepted this member" = "Sie haben dieses Mitglied übernommen";
/* No comment provided by engineer. */
"You allow" = "Sie erlauben";
@ -5933,9 +5823,6 @@ report reason */
/* alert message */
"You can view invitation link again in connection details." = "Den Einladungslink können Sie in den Details der Verbindung nochmals sehen.";
/* alert message */
"You can view your reports in Chat with admins." = "Sie können Ihre Meldungen im Chat mit den Administratoren sehen.";
/* No comment provided by engineer. */
"You can't send messages!" = "Sie können keine Nachrichten versenden!";
@ -6104,15 +5991,15 @@ report reason */
/* No comment provided by engineer. */
"Your profile **%@** will be shared." = "Ihr Profil **%@** wird geteilt.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Ihr Profil wird auf Ihrem Gerät gespeichert und nur mit Ihren Kontakten geteilt.";
/* No comment provided by engineer. */
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Ihr Profil wird auf Ihrem Gerät gespeichert und nur mit Ihren Kontakten geteilt. SimpleX-Server können Ihr Profil nicht einsehen.";
/* alert message */
"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Ihr Profil wurde geändert. Wenn Sie es speichern, wird das aktualisierte Profil an alle Ihre Kontakte gesendet.";
/* No comment provided by engineer. */
"Your profile, contacts and delivered messages are stored on your device." = "Ihr Profil, Ihre Kontakte und zugestellten Nachrichten werden auf Ihrem Gerät gespeichert.";
/* No comment provided by engineer. */
"Your random profile" = "Ihr Zufallsprofil";

View file

@ -1872,7 +1872,7 @@ swipe action */
/* No comment provided by engineer. */
"Don't miss important messages." = "No pierdas los mensajes importantes.";
/* alert action */
/* No comment provided by engineer. */
"Don't show again" = "No volver a mostrar";
/* No comment provided by engineer. */
@ -2236,7 +2236,7 @@ chat item action */
/* alert title */
"Error registering for notifications" = "Error al registrarse para notificaciones";
/* alert title */
/* No comment provided by engineer. */
"Error removing member" = "Error al expulsar miembro";
/* alert title */
@ -3587,7 +3587,6 @@ snd error text */
/* enabled status
group pref value
member criteria value
time to disappear */
"off" = "desactivado";
@ -3600,8 +3599,7 @@ time to disappear */
/* feature offered item */
"offered %@: %@" = "ofrecido %1$@: %2$@";
/* alert action
alert button */
/* alert button */
"Ok" = "Ok";
/* No comment provided by engineer. */
@ -5102,6 +5100,9 @@ report reason */
/* No comment provided by engineer. */
"The old database was not removed during the migration, it can be deleted." = "La base de datos antigua no se eliminó durante la migración, puede eliminarse.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "El perfil sólo se comparte con tus contactos.";
/* No comment provided by engineer. */
"The same conditions will apply to operator **%@**." = "Las mismas condiciones se aplicarán al operador **%@**.";
@ -5990,15 +5991,15 @@ report reason */
/* No comment provided by engineer. */
"Your profile **%@** will be shared." = "El perfil **%@** será compartido.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "El perfil sólo se comparte con tus contactos.";
/* No comment provided by engineer. */
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Tu perfil es almacenado en tu dispositivo y solamente se comparte con tus contactos. Los servidores SimpleX no pueden ver tu perfil.";
/* alert message */
"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Tu perfil ha sido modificado. Si lo guardas la actualización será enviada a todos tus contactos.";
/* No comment provided by engineer. */
"Your profile, contacts and delivered messages are stored on your device." = "Tu perfil, contactos y mensajes se almacenan en tu dispositivo.";
/* No comment provided by engineer. */
"Your random profile" = "Tu perfil aleatorio";

View file

@ -1073,7 +1073,7 @@ swipe action */
/* No comment provided by engineer. */
"Don't enable" = "Älä salli";
/* alert action */
/* No comment provided by engineer. */
"Don't show again" = "Älä näytä uudelleen";
/* No comment provided by engineer. */
@ -1307,7 +1307,7 @@ swipe action */
/* alert title */
"Error receiving file" = "Virhe tiedoston vastaanottamisessa";
/* alert title */
/* No comment provided by engineer. */
"Error removing member" = "Virhe poistettaessa jäsentä";
/* No comment provided by engineer. */
@ -2079,7 +2079,6 @@ snd error text */
/* enabled status
group pref value
member criteria value
time to disappear */
"off" = "pois";
@ -2092,8 +2091,7 @@ time to disappear */
/* feature offered item */
"offered %@: %@" = "tarjottu %1$@: %2$@";
/* alert action
alert button */
/* alert button */
"Ok" = "Ok";
/* No comment provided by engineer. */
@ -2912,6 +2910,9 @@ chat item action */
/* No comment provided by engineer. */
"The old database was not removed during the migration, it can be deleted." = "Vanhaa tietokantaa ei poistettu siirron aikana, se voidaan kuitenkin poistaa.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Profiili jaetaan vain kontaktiesi kanssa.";
/* No comment provided by engineer. */
"The second tick we missed! ✅" = "Toinen kuittaus, joka uupui! ✅";
@ -3390,10 +3391,10 @@ chat item action */
"Your profile **%@** will be shared." = "Profiilisi **%@** jaetaan.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Profiili jaetaan vain kontaktiesi kanssa.";
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Profiilisi tallennetaan laitteeseesi ja jaetaan vain yhteystietojesi kanssa. SimpleX-palvelimet eivät näe profiiliasi.";
/* No comment provided by engineer. */
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Profiilisi tallennetaan laitteeseesi ja jaetaan vain yhteystietojesi kanssa. SimpleX-palvelimet eivät näe profiiliasi.";
"Your profile, contacts and delivered messages are stored on your device." = "Profiilisi, kontaktisi ja toimitetut viestit tallennetaan laitteellesi.";
/* No comment provided by engineer. */
"Your random profile" = "Satunnainen profiilisi";

View file

@ -1863,7 +1863,7 @@ swipe action */
/* No comment provided by engineer. */
"Don't miss important messages." = "Ne manquez pas les messages importants.";
/* alert action */
/* No comment provided by engineer. */
"Don't show again" = "Ne plus afficher";
/* No comment provided by engineer. */
@ -2227,7 +2227,7 @@ chat item action */
/* alert title */
"Error registering for notifications" = "Erreur lors de l'inscription aux notifications";
/* alert title */
/* No comment provided by engineer. */
"Error removing member" = "Erreur lors de la suppression d'un membre";
/* alert title */
@ -3479,7 +3479,6 @@ snd error text */
/* enabled status
group pref value
member criteria value
time to disappear */
"off" = "off";
@ -3492,8 +3491,7 @@ time to disappear */
/* feature offered item */
"offered %@: %@" = "propose %1$@: %2$@";
/* alert action
alert button */
/* alert button */
"Ok" = "Ok";
/* No comment provided by engineer. */
@ -4885,6 +4883,9 @@ chat item action */
/* No comment provided by engineer. */
"The old database was not removed during the migration, it can be deleted." = "L'ancienne base de données n'a pas été supprimée lors de la migration, elle peut être supprimée.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Le profil n'est partagé qu'avec vos contacts.";
/* No comment provided by engineer. */
"The same conditions will apply to operator **%@**." = "Les mêmes conditions s'appliquent à l'opérateur **%@**.";
@ -5740,15 +5741,15 @@ chat item action */
/* No comment provided by engineer. */
"Your profile **%@** will be shared." = "Votre profil **%@** sera partagé.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Le profil n'est partagé qu'avec vos contacts.";
/* No comment provided by engineer. */
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Votre profil est stocké sur votre appareil et est seulement partagé avec vos contacts. Les serveurs SimpleX ne peuvent pas voir votre profil.";
/* alert message */
"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Votre profil a été modifié. Si vous l'enregistrez, le profil mis à jour sera envoyé à tous vos contacts.";
/* No comment provided by engineer. */
"Your profile, contacts and delivered messages are stored on your device." = "Votre profil, vos contacts et les messages reçus sont stockés sur votre appareil.";
/* No comment provided by engineer. */
"Your random profile" = "Votre profil aléatoire";

View file

@ -345,12 +345,6 @@ accept incoming call via notification
swipe action */
"Accept" = "Elfogadás";
/* alert action */
"Accept as member" = "Befogadás tagként";
/* alert action */
"Accept as observer" = "Befogadás megfigyelőként";
/* No comment provided by engineer. */
"Accept conditions" = "Feltételek elfogadása";
@ -364,12 +358,6 @@ swipe action */
swipe action */
"Accept incognito" = "Elfogadás inkognitóban";
/* alert title */
"Accept member" = "Tag befogadása";
/* rcv group event chat item */
"accepted %@" = "befogadta őt: %@";
/* call status */
"accepted call" = "fogadott hívás";
@ -379,9 +367,6 @@ swipe action */
/* chat list item title */
"accepted invitation" = "elfogadott meghívó";
/* rcv group event chat item */
"accepted you" = "befogadta Önt";
/* No comment provided by engineer. */
"Acknowledged" = "Visszaigazolt";
@ -478,9 +463,6 @@ swipe action */
/* chat item text */
"agreeing encryption…" = "titkosítás elfogadása…";
/* member criteria value */
"all" = "összes";
/* No comment provided by engineer. */
"All" = "Összes";
@ -923,9 +905,6 @@ marked deleted chat item preview text */
/* No comment provided by engineer. */
"Can't message member" = "Nem lehet üzenetet küldeni a tagnak";
/* No comment provided by engineer. */
"can't send messages" = "nem lehet üzeneteket küldeni";
/* alert action
alert button */
"Cancel" = "Mégse";
@ -1063,18 +1042,9 @@ set passcode view */
/* No comment provided by engineer. */
"Chat will be deleted for you - this cannot be undone!" = "A csevegés törölve lesz az Ön számára ez a művelet nem vonható vissza!";
/* chat toolbar */
"Chat with admins" = "Csevegés az adminisztrátorokkal";
/* No comment provided by engineer. */
"Chat with member" = "Csevegés a taggal";
/* No comment provided by engineer. */
"Chats" = "Csevegések";
/* No comment provided by engineer. */
"Chats with members" = "Csevegés a tagokkal";
/* No comment provided by engineer. */
"Check messages every 20 min." = "Üzenetek ellenőrzése 20 percenként.";
@ -1363,15 +1333,9 @@ set passcode view */
/* No comment provided by engineer. */
"Contact already exists" = "A partner már létezik";
/* No comment provided by engineer. */
"contact deleted" = "partner törölve";
/* No comment provided by engineer. */
"Contact deleted!" = "Partner törölve!";
/* No comment provided by engineer. */
"contact disabled" = "partner letiltva";
/* No comment provided by engineer. */
"contact has e2e encryption" = "a partner e2e titkosítással rendelkezik";
@ -1390,9 +1354,6 @@ set passcode view */
/* No comment provided by engineer. */
"Contact name" = "Csak név";
/* No comment provided by engineer. */
"contact not ready" = "a kapcsolat nem áll készen";
/* No comment provided by engineer. */
"Contact preferences" = "Partnerbeállítások";
@ -1544,7 +1505,7 @@ set passcode view */
"Database ID: %d" = "Adatbázis-azonosító: %d";
/* No comment provided by engineer. */
"Database IDs and Transport isolation option." = "Adatbázis-azonosítók és átvitelelkülönítési beállítások.";
"Database IDs and Transport isolation option." = "Adatbázis-azonosítók és átvitel-izolációs beállítások.";
/* No comment provided by engineer. */
"Database is encrypted using a random passphrase, you can change it." = "Az adatbázis egy véletlenszerű jelmondattal van titkosítva, amelyet szabadon módosíthat.";
@ -1589,7 +1550,7 @@ set passcode view */
"Decentralized" = "Decentralizált";
/* message decrypt error item */
"Decryption error" = "Titkosításvisszafejtési hiba";
"Decryption error" = "Titkosítás visszafejtési hiba";
/* No comment provided by engineer. */
"decryption errors" = "visszafejtési hibák";
@ -1641,9 +1602,6 @@ swipe action */
/* No comment provided by engineer. */
"Delete chat profile?" = "Törli a csevegési profilt?";
/* alert title */
"Delete chat with member?" = "Törli a taggal való csevegést?";
/* No comment provided by engineer. */
"Delete chat?" = "Törli a csevegést?";
@ -1819,7 +1777,7 @@ swipe action */
"different migration in the app/database: %@ / %@" = "különböző átköltöztetés az alkalmazásban/adatbázisban: %@ / %@";
/* No comment provided by engineer. */
"Different names, avatars and transport isolation." = "Különböző nevek, profilképek és átvitelizoláció.";
"Different names, avatars and transport isolation." = "Különböző nevek, profilképek és átvitel-izoláció.";
/* connection level description */
"direct" = "közvetlen";
@ -1914,7 +1872,7 @@ swipe action */
/* No comment provided by engineer. */
"Don't miss important messages." = "Ne maradjon le a fontos üzenetekről.";
/* alert action */
/* No comment provided by engineer. */
"Don't show again" = "Ne mutasd újra";
/* No comment provided by engineer. */
@ -2158,9 +2116,6 @@ chat item action */
/* No comment provided by engineer. */
"Error accepting contact request" = "Hiba történt a meghívási kérés elfogadásakor";
/* alert title */
"Error accepting member" = "Hiba a tag befogadásakor";
/* No comment provided by engineer. */
"Error adding member(s)" = "Hiba történt a tag(ok) hozzáadásakor";
@ -2218,9 +2173,6 @@ chat item action */
/* No comment provided by engineer. */
"Error deleting chat database" = "Hiba történt a csevegési adatbázis törlésekor";
/* alert title */
"Error deleting chat with member" = "Hiba a taggal való csevegés törlésekor";
/* No comment provided by engineer. */
"Error deleting chat!" = "Hiba történt a csevegés törlésekor!";
@ -2237,7 +2189,7 @@ chat item action */
"Error deleting token" = "Hiba történt a token törlésekor";
/* No comment provided by engineer. */
"Error deleting user profile" = "Hiba történt a felhasználói profil törlésekor";
"Error deleting user profile" = "Hiba történt a felhasználó-profil törlésekor";
/* No comment provided by engineer. */
"Error downloading the archive" = "Hiba történt az archívum letöltésekor";
@ -2284,7 +2236,7 @@ chat item action */
/* alert title */
"Error registering for notifications" = "Hiba történt az értesítések regisztrálásakor";
/* alert title */
/* No comment provided by engineer. */
"Error removing member" = "Hiba történt a tag eltávolításakor";
/* alert title */
@ -2658,9 +2610,6 @@ snd error text */
/* No comment provided by engineer. */
"Group invitation is no longer valid, it was removed by sender." = "A csoportmeghívó már nem érvényes, a küldője eltávolította.";
/* No comment provided by engineer. */
"group is deleted" = "csoport törölve";
/* No comment provided by engineer. */
"Group link" = "Csoporthivatkozás";
@ -3007,10 +2956,10 @@ snd error text */
"It allows having many anonymous connections without any shared data between them in a single chat profile." = "Lehetővé teszi, hogy egyetlen csevegési profilon belül több névtelen kapcsolat legyen, anélkül, hogy megosztott adatok lennének közöttük.";
/* No comment provided by engineer. */
"It can happen when you or your connection used the old database backup." = "Ez akkor fordulhat elő, ha Ön vagy a partnere egy régi adatbázis biztonsági mentését használta.";
"It can happen when you or your connection used the old database backup." = "Ez akkor fordulhat elő, ha Ön vagy a partnere régi adatbázis biztonsági mentést használt.";
/* No comment provided by engineer. */
"It can happen when:\n1. The messages expired in the sending client after 2 days or on the server after 30 days.\n2. Message decryption failed, because you or your contact used old database backup.\n3. The connection was compromised." = "Ez akkor fordulhat elő, ha:\n1. Az üzenetek 2 nap után, vagy a kiszolgálón 30 nap után lejártak.\n2. Nem sikerült az üzenetet visszafejteni, mert Ön, vagy a partnere egy régi adatbázis biztonsági mentését használta.\n3. A kapcsolat sérült.";
"It can happen when:\n1. The messages expired in the sending client after 2 days or on the server after 30 days.\n2. Message decryption failed, because you or your contact used old database backup.\n3. The connection was compromised." = "Ez akkor fordulhat elő, ha:\n1. Az üzenetek 2 nap után, vagy a kiszolgálón 30 nap után lejártak.\n2. Nem sikerült az üzenetet visszafejteni, mert Ön, vagy a partnere régebbi adatbázis biztonsági mentést használt.\n3. A kapcsolat sérült.";
/* No comment provided by engineer. */
"It protects your IP address and connections." = "Védi az IP-címét és a kapcsolatait.";
@ -3189,15 +3138,9 @@ snd error text */
/* profile update event chat item */
"member %@ changed to %@" = "%1$@ a következőre módosította a nevét: %2$@";
/* No comment provided by engineer. */
"Member admission" = "Tagbefogadás";
/* rcv group event chat item */
"member connected" = "kapcsolódott";
/* No comment provided by engineer. */
"member has old version" = "a tag régi verziót használ";
/* item status text */
"Member inactive" = "Inaktív tag";
@ -3219,9 +3162,6 @@ snd error text */
/* No comment provided by engineer. */
"Member will be removed from group - this cannot be undone!" = "A tag el lesz távolítva a csoportból ez a művelet nem vonható vissza!";
/* alert message */
"Member will join the group, accept member?" = "A tag csatlakozni akar a csoporthoz, befogadja a tagot?";
/* No comment provided by engineer. */
"Members can add message reactions." = "A tagok reakciókat adhatnak hozzá az üzenetekhez.";
@ -3334,10 +3274,10 @@ snd error text */
"Messages were deleted after you selected them." = "Az üzeneteket törölték miután kijelölte őket.";
/* No comment provided by engineer. */
"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Az üzenetek, a fájlok és a hívások **végpontok közötti titkosítással**, kompromittálás előtti és utáni titkosságvédelemmel, illetve letagadhatósággal vannak védve.";
"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Az üzenetek, a fájlok és a hívások **végpontok közötti titkosítással**, sérülés utáni titkosságvédelemmel és -helyreállítással, továbbá letagadhatósággal vannak védve.";
/* No comment provided by engineer. */
"Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Az üzenetek, a fájlok és a hívások **végpontok közötti kvantumbiztos titkosítással**, kompromittálás előtti és utáni titkosságvédelemmel, illetve letagadhatósággal vannak védve.";
"Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Az üzenetek, a fájlok és a hívások **végpontok közötti kvantumbiztos titkosítással**, sérülés utáni titkosságvédelemmel és -helyreállítással, továbbá letagadhatósággal vannak védve.";
/* No comment provided by engineer. */
"Migrate device" = "Eszköz átköltöztetése";
@ -3492,9 +3432,6 @@ snd error text */
/* No comment provided by engineer. */
"New member role" = "Új tag szerepköre";
/* rcv group event chat item */
"New member wants to join the group." = "Új tag szeretne csatlakozni a csoporthoz.";
/* notification */
"new message" = "új üzenet";
@ -3534,9 +3471,6 @@ snd error text */
/* No comment provided by engineer. */
"No chats in list %@" = "Nincsenek csevegések a(z) %@ nevű listában";
/* No comment provided by engineer. */
"No chats with members" = "Nincsenek csevegések a tagokkal";
/* No comment provided by engineer. */
"No contacts selected" = "Nincs partner kijelölve";
@ -3616,14 +3550,11 @@ snd error text */
"No unread chats" = "Nincsenek olvasatlan csevegések";
/* No comment provided by engineer. */
"No user identifiers." = "Nincsenek felhasználói azonosítók.";
"No user identifiers." = "Nincsenek felhasználó-azonosítók.";
/* No comment provided by engineer. */
"Not compatible!" = "Nem kompatibilis!";
/* No comment provided by engineer. */
"not synchronized" = "nincs szinkronizálva";
/* No comment provided by engineer. */
"Notes" = "Jegyzetek";
@ -3656,7 +3587,6 @@ snd error text */
/* enabled status
group pref value
member criteria value
time to disappear */
"off" = "kikapcsolva";
@ -3669,8 +3599,7 @@ time to disappear */
/* feature offered item */
"offered %@: %@" = "ajánlotta: %1$@, ekkor: %2$@";
/* alert action
alert button */
/* alert button */
"Ok" = "Rendben";
/* No comment provided by engineer. */
@ -3766,9 +3695,6 @@ alert button */
/* No comment provided by engineer. */
"Open group" = "Csoport megnyitása";
/* alert title */
"Open link?" = "Megnyitja a hivatkozást?";
/* authentication reason */
"Open migration to another device" = "Átköltöztetés indítása egy másik eszközre";
@ -3871,9 +3797,6 @@ alert button */
/* No comment provided by engineer. */
"pending approval" = "jóváhagyásra vár";
/* No comment provided by engineer. */
"pending review" = "függőben lévő áttekintés";
/* No comment provided by engineer. */
"Periodic" = "Időszakos";
@ -3943,9 +3866,6 @@ alert button */
/* token info */
"Please try to disable and re-enable notfications." = "Próbálja meg letiltani és újra engedélyezni az értesítéseket.";
/* snd group event chat item */
"Please wait for group moderators to review your request to join the group." = "Várja meg, amíg a csoport moderátorai áttekintik a csoporthoz való csatlakozási kérelmét.";
/* token info */
"Please wait for token activation to complete." = "Várjon, amíg a token aktiválása befejeződik.";
@ -4226,9 +4146,6 @@ swipe action */
/* No comment provided by engineer. */
"Reject contact request" = "Meghívási kérés elutasítása";
/* alert title */
"Reject member?" = "Elutasítja a tagot?";
/* No comment provided by engineer. */
"rejected" = "elutasítva";
@ -4236,10 +4153,10 @@ swipe action */
"rejected call" = "elutasított hívás";
/* No comment provided by engineer. */
"Relay server is only used if necessary. Another party can observe your IP address." = "A továbbítókiszolgáló csak szükség esetén lesz használva. Egy másik fél megfigyelheti az IP-címét.";
"Relay server is only used if necessary. Another party can observe your IP address." = "A továbbítókiszolgáló csak szükség esetén lesz használva. Egy másik fél megfigyelheti az IP-címet.";
/* No comment provided by engineer. */
"Relay server protects your IP address, but it can observe the duration of the call." = "A továbbítókiszolgáló megvédi az IP-címét, de megfigyelheti a hívás időtartamát.";
"Relay server protects your IP address, but it can observe the duration of the call." = "A továbbítókiszolgáló megvédi az Ön IP-címét, de megfigyelheti a hívás időtartamát.";
/* No comment provided by engineer. */
"Remove" = "Eltávolítás";
@ -4268,9 +4185,6 @@ swipe action */
/* profile update event chat item */
"removed contact address" = "eltávolította a kapcsolattartási címet";
/* No comment provided by engineer. */
"removed from group" = "eltávolítva a csoportból";
/* profile update event chat item */
"removed profile picture" = "eltávolította a profilképét";
@ -4319,9 +4233,6 @@ swipe action */
/* No comment provided by engineer. */
"Report reason?" = "Jelentés indoklása?";
/* alert title */
"Report sent to moderators" = "A jelentés el lett küldve a moderátoroknak";
/* report reason */
"Report spam: only group moderators will see it." = "Kéretlen tartalom jelentése: csak a csoport moderátorai látják.";
@ -4337,9 +4248,6 @@ swipe action */
/* No comment provided by engineer. */
"Reports" = "Jelentések";
/* No comment provided by engineer. */
"request to join rejected" = "csatlakozási kérelem elutasítva";
/* chat list item title */
"requested to connect" = "Függőben lévő meghívási kérelem";
@ -4394,21 +4302,9 @@ swipe action */
/* chat item action */
"Reveal" = "Felfedés";
/* No comment provided by engineer. */
"review" = "áttekintés";
/* No comment provided by engineer. */
"Review conditions" = "Feltételek felülvizsgálata";
/* admission stage */
"Review members" = "Tagok áttekintése";
/* admission stage description */
"Review members before admitting (\"knocking\")." = "Tagok áttekintése a befogadás előtt (kopogtatás).";
/* No comment provided by engineer. */
"reviewed by admins" = "áttekintve a moderátorok által";
/* No comment provided by engineer. */
"Revoke" = "Visszavonás";
@ -4437,9 +4333,6 @@ chat item action */
/* alert button */
"Save (and notify contacts)" = "Mentés (és a partnerek értesítése)";
/* alert title */
"Save admission settings?" = "Elmenti a befogadási beállításokat?";
/* alert button */
"Save and notify contact" = "Mentés és a partner értesítése";
@ -4776,9 +4669,6 @@ chat item action */
/* No comment provided by engineer. */
"Set it instead of system authentication." = "Beállítás a rendszer-hitelesítés helyett.";
/* No comment provided by engineer. */
"Set member admission" = "Tagbefogadás beállítása";
/* No comment provided by engineer. */
"Set message expiration in chats." = "Üzenetek eltűnési idejének módosítása a csevegésekben.";
@ -4940,7 +4830,7 @@ chat item action */
"SimpleX one-time invitation" = "Egyszer használható SimpleX-meghívó";
/* No comment provided by engineer. */
"SimpleX protocols reviewed by Trail of Bits." = "A SimpleX-protokollokat a Trail of Bits auditálta.";
"SimpleX protocols reviewed by Trail of Bits." = "A SimpleX Chat biztonsága a Trail of Bits által lett felülvizsgálva.";
/* No comment provided by engineer. */
"Simplified incognito mode" = "Egyszerűsített inkognitómód";
@ -5210,6 +5100,9 @@ report reason */
/* No comment provided by engineer. */
"The old database was not removed during the migration, it can be deleted." = "A régi adatbázis nem lett eltávolítva az átköltöztetéskor, ezért törölhető.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "A profilja csak a partnereivel van megosztva.";
/* No comment provided by engineer. */
"The same conditions will apply to operator **%@**." = "Ugyanezek a feltételek lesznek elfogadva a következő üzemeltető számára is: **%@**.";
@ -5370,7 +5263,7 @@ report reason */
"Total" = "Összes kapcsolat";
/* No comment provided by engineer. */
"Transport isolation" = "Átvitelelkülönítés";
"Transport isolation" = "Átvitel-izoláció";
/* No comment provided by engineer. */
"Transport sessions" = "Munkamenetek átvitele";
@ -5565,7 +5458,7 @@ report reason */
"Use only local notifications?" = "Csak helyi értesítések használata?";
/* No comment provided by engineer. */
"Use private routing with unknown servers when IP address is not protected." = "Használjon privát útválasztást az ismeretlen kiszolgálókkal, ha az IP-cím nem védett.";
"Use private routing with unknown servers when IP address is not protected." = "Használjon privát útválasztást ismeretlen kiszolgálókkal, ha az IP-cím nem védett.";
/* No comment provided by engineer. */
"Use private routing with unknown servers." = "Használjon privát útválasztást ismeretlen kiszolgálókkal.";
@ -5787,10 +5680,10 @@ report reason */
"With reduced battery usage." = "Csökkentett akkumulátor-használattal.";
/* No comment provided by engineer. */
"Without Tor or VPN, your IP address will be visible to file servers." = "Tor vagy VPN nélkül az IP-címe láthatóvá válik a fájlkiszolgálók számára.";
"Without Tor or VPN, your IP address will be visible to file servers." = "Tor vagy VPN nélkül az Ön IP-címe látható lesz a fájlkiszolgálók számára.";
/* alert message */
"Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." = "Tor vagy VPN nélkül az IP-címe láthatóvá válik a következő XFTP-továbbítókiszolgálók számára: %@.";
"Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." = "Tor vagy VPN nélkül az Ön IP-címe látható lesz a következő XFTP-továbbítókiszolgálók számára: %@.";
/* No comment provided by engineer. */
"Wrong database passphrase" = "Érvénytelen adatbázis-jelmondat";
@ -5819,9 +5712,6 @@ report reason */
/* No comment provided by engineer. */
"You accepted connection" = "Kapcsolat létrehozása";
/* snd group event chat item */
"you accepted this member" = "Ön befogadta ezt a tagot";
/* No comment provided by engineer. */
"You allow" = "Ön engedélyezi";
@ -5933,9 +5823,6 @@ report reason */
/* alert message */
"You can view invitation link again in connection details." = "A meghívási hivatkozást újra megtekintheti a kapcsolat részleteinél.";
/* alert message */
"You can view your reports in Chat with admins." = "A jelentéseket megtekintheti a „Csevegés az adminisztrátorokkal” menüben.";
/* No comment provided by engineer. */
"You can't send messages!" = "Nem lehet üzeneteket küldeni!";
@ -6104,15 +5991,15 @@ report reason */
/* No comment provided by engineer. */
"Your profile **%@** will be shared." = "A(z) **%@** nevű profilja meg lesz osztva.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "A profilja az eszközén van tárolva és csak a partnereivel van megosztva.";
/* No comment provided by engineer. */
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "A profilja az eszközén van tárolva és csak a partnereivel van megosztva. A SimpleX-kiszolgálók nem láthatják a profilját.";
/* alert message */
"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "A profilja módosult. Ha elmenti, a profilfrissítés el lesz küldve a partnerei számára.";
/* No comment provided by engineer. */
"Your profile, contacts and delivered messages are stored on your device." = "A profilja, a partnerei és az elküldött üzenetei a saját eszközén vannak tárolva.";
/* No comment provided by engineer. */
"Your random profile" = "Véletlenszerű profil";

View file

@ -345,12 +345,6 @@ accept incoming call via notification
swipe action */
"Accept" = "Accetta";
/* alert action */
"Accept as member" = "Accetta come membro";
/* alert action */
"Accept as observer" = "Accetta come osservatore";
/* No comment provided by engineer. */
"Accept conditions" = "Accetta le condizioni";
@ -364,12 +358,6 @@ swipe action */
swipe action */
"Accept incognito" = "Accetta in incognito";
/* alert title */
"Accept member" = "Accetta membro";
/* rcv group event chat item */
"accepted %@" = "%@ accettato";
/* call status */
"accepted call" = "chiamata accettata";
@ -379,9 +367,6 @@ swipe action */
/* chat list item title */
"accepted invitation" = "invito accettato";
/* rcv group event chat item */
"accepted you" = "ti ha accettato/a";
/* No comment provided by engineer. */
"Acknowledged" = "Riconosciuto";
@ -478,9 +463,6 @@ swipe action */
/* chat item text */
"agreeing encryption…" = "concordando la crittografia…";
/* member criteria value */
"all" = "tutti";
/* No comment provided by engineer. */
"All" = "Tutte";
@ -923,9 +905,6 @@ marked deleted chat item preview text */
/* No comment provided by engineer. */
"Can't message member" = "Impossibile inviare un messaggio al membro";
/* No comment provided by engineer. */
"can't send messages" = "impossibile inviare messaggi";
/* alert action
alert button */
"Cancel" = "Annulla";
@ -1063,18 +1042,9 @@ set passcode view */
/* No comment provided by engineer. */
"Chat will be deleted for you - this cannot be undone!" = "La chat verrà eliminata solo per te, non è reversibile!";
/* chat toolbar */
"Chat with admins" = "Chat con amministratori";
/* No comment provided by engineer. */
"Chat with member" = "Chatta con il membro";
/* No comment provided by engineer. */
"Chats" = "Chat";
/* No comment provided by engineer. */
"Chats with members" = "Chat con membri";
/* No comment provided by engineer. */
"Check messages every 20 min." = "Controlla i messaggi ogni 20 min.";
@ -1363,15 +1333,9 @@ set passcode view */
/* No comment provided by engineer. */
"Contact already exists" = "Il contatto esiste già";
/* No comment provided by engineer. */
"contact deleted" = "contatto eliminato";
/* No comment provided by engineer. */
"Contact deleted!" = "Contatto eliminato!";
/* No comment provided by engineer. */
"contact disabled" = "contatto disattivato";
/* No comment provided by engineer. */
"contact has e2e encryption" = "il contatto ha la crittografia e2e";
@ -1390,9 +1354,6 @@ set passcode view */
/* No comment provided by engineer. */
"Contact name" = "Nome del contatto";
/* No comment provided by engineer. */
"contact not ready" = "contatto non pronto";
/* No comment provided by engineer. */
"Contact preferences" = "Preferenze del contatto";
@ -1641,9 +1602,6 @@ swipe action */
/* No comment provided by engineer. */
"Delete chat profile?" = "Eliminare il profilo di chat?";
/* alert title */
"Delete chat with member?" = "Eliminare la chat con il membro?";
/* No comment provided by engineer. */
"Delete chat?" = "Eliminare la chat?";
@ -1914,7 +1872,7 @@ swipe action */
/* No comment provided by engineer. */
"Don't miss important messages." = "Non perdere messaggi importanti.";
/* alert action */
/* No comment provided by engineer. */
"Don't show again" = "Non mostrare più";
/* No comment provided by engineer. */
@ -2158,9 +2116,6 @@ chat item action */
/* No comment provided by engineer. */
"Error accepting contact request" = "Errore nell'accettazione della richiesta di contatto";
/* alert title */
"Error accepting member" = "Errore di accettazione del membro";
/* No comment provided by engineer. */
"Error adding member(s)" = "Errore di aggiunta membro/i";
@ -2218,9 +2173,6 @@ chat item action */
/* No comment provided by engineer. */
"Error deleting chat database" = "Errore nell'eliminazione del database della chat";
/* alert title */
"Error deleting chat with member" = "Errore di eliminazione della chat con il membro";
/* No comment provided by engineer. */
"Error deleting chat!" = "Errore nell'eliminazione della chat!";
@ -2284,7 +2236,7 @@ chat item action */
/* alert title */
"Error registering for notifications" = "Errore di registrazione per le notifiche";
/* alert title */
/* No comment provided by engineer. */
"Error removing member" = "Errore nella rimozione del membro";
/* alert title */
@ -2658,9 +2610,6 @@ snd error text */
/* No comment provided by engineer. */
"Group invitation is no longer valid, it was removed by sender." = "L'invito al gruppo non è più valido, è stato rimosso dal mittente.";
/* No comment provided by engineer. */
"group is deleted" = "il gruppo è eliminato";
/* No comment provided by engineer. */
"Group link" = "Link del gruppo";
@ -3189,15 +3138,9 @@ snd error text */
/* profile update event chat item */
"member %@ changed to %@" = "il membro %1$@ è diventato %2$@";
/* No comment provided by engineer. */
"Member admission" = "Ammissione del membro";
/* rcv group event chat item */
"member connected" = "si è connesso/a";
/* No comment provided by engineer. */
"member has old version" = "il membro ha una versione vecchia";
/* item status text */
"Member inactive" = "Membro inattivo";
@ -3219,9 +3162,6 @@ snd error text */
/* No comment provided by engineer. */
"Member will be removed from group - this cannot be undone!" = "Il membro verrà rimosso dal gruppo, non è reversibile!";
/* alert message */
"Member will join the group, accept member?" = "Il membro entrerà nel gruppo, accettarlo?";
/* No comment provided by engineer. */
"Members can add message reactions." = "I membri del gruppo possono aggiungere reazioni ai messaggi.";
@ -3492,9 +3432,6 @@ snd error text */
/* No comment provided by engineer. */
"New member role" = "Nuovo ruolo del membro";
/* rcv group event chat item */
"New member wants to join the group." = "Un nuovo membro vuole entrare nel gruppo.";
/* notification */
"new message" = "messaggio nuovo";
@ -3534,9 +3471,6 @@ snd error text */
/* No comment provided by engineer. */
"No chats in list %@" = "Nessuna chat nell'elenco %@";
/* No comment provided by engineer. */
"No chats with members" = "Nessuna chat con membri";
/* No comment provided by engineer. */
"No contacts selected" = "Nessun contatto selezionato";
@ -3621,9 +3555,6 @@ snd error text */
/* No comment provided by engineer. */
"Not compatible!" = "Non compatibile!";
/* No comment provided by engineer. */
"not synchronized" = "non sincronizzato";
/* No comment provided by engineer. */
"Notes" = "Note";
@ -3656,7 +3587,6 @@ snd error text */
/* enabled status
group pref value
member criteria value
time to disappear */
"off" = "off";
@ -3669,8 +3599,7 @@ time to disappear */
/* feature offered item */
"offered %@: %@" = "offerto %1$@: %2$@";
/* alert action
alert button */
/* alert button */
"Ok" = "Ok";
/* No comment provided by engineer. */
@ -3766,9 +3695,6 @@ alert button */
/* No comment provided by engineer. */
"Open group" = "Apri gruppo";
/* alert title */
"Open link?" = "Aprire il link?";
/* authentication reason */
"Open migration to another device" = "Apri migrazione ad un altro dispositivo";
@ -3871,9 +3797,6 @@ alert button */
/* No comment provided by engineer. */
"pending approval" = "in attesa di approvazione";
/* No comment provided by engineer. */
"pending review" = "in attesa di revisione";
/* No comment provided by engineer. */
"Periodic" = "Periodicamente";
@ -3943,9 +3866,6 @@ alert button */
/* token info */
"Please try to disable and re-enable notfications." = "Prova a disattivare e riattivare le notifiche.";
/* snd group event chat item */
"Please wait for group moderators to review your request to join the group." = "Attendi che i moderatori del gruppo revisionino la tua richiesta di entrare nel gruppo.";
/* token info */
"Please wait for token activation to complete." = "Attendi il completamento dell'attivazione del token.";
@ -4226,9 +4146,6 @@ swipe action */
/* No comment provided by engineer. */
"Reject contact request" = "Rifiuta la richiesta di contatto";
/* alert title */
"Reject member?" = "Rifiutare il membro?";
/* No comment provided by engineer. */
"rejected" = "rifiutato";
@ -4268,9 +4185,6 @@ swipe action */
/* profile update event chat item */
"removed contact address" = "indirizzo di contatto rimosso";
/* No comment provided by engineer. */
"removed from group" = "rimosso dal gruppo";
/* profile update event chat item */
"removed profile picture" = "immagine del profilo rimossa";
@ -4319,9 +4233,6 @@ swipe action */
/* No comment provided by engineer. */
"Report reason?" = "Motivo della segnalazione?";
/* alert title */
"Report sent to moderators" = "Segnalazione inviata ai moderatori";
/* report reason */
"Report spam: only group moderators will see it." = "Segnala spam: solo i moderatori del gruppo lo vedranno.";
@ -4337,9 +4248,6 @@ swipe action */
/* No comment provided by engineer. */
"Reports" = "Segnalazioni";
/* No comment provided by engineer. */
"request to join rejected" = "richiesta di entrare rifiutata";
/* chat list item title */
"requested to connect" = "richiesto di connettersi";
@ -4394,21 +4302,9 @@ swipe action */
/* chat item action */
"Reveal" = "Rivela";
/* No comment provided by engineer. */
"review" = "revisiona";
/* No comment provided by engineer. */
"Review conditions" = "Leggi le condizioni";
/* admission stage */
"Review members" = "Revisiona i membri";
/* admission stage description */
"Review members before admitting (\"knocking\")." = "Revisiona i membri prima di ammetterli (\"bussare\").";
/* No comment provided by engineer. */
"reviewed by admins" = "revisionato dagli amministratori";
/* No comment provided by engineer. */
"Revoke" = "Revoca";
@ -4437,9 +4333,6 @@ chat item action */
/* alert button */
"Save (and notify contacts)" = "Salva (e avvisa i contatti)";
/* alert title */
"Save admission settings?" = "Salvare le impostazioni di ammissione?";
/* alert button */
"Save and notify contact" = "Salva e avvisa il contatto";
@ -4776,9 +4669,6 @@ chat item action */
/* No comment provided by engineer. */
"Set it instead of system authentication." = "Impostalo al posto dell'autenticazione di sistema.";
/* No comment provided by engineer. */
"Set member admission" = "Imposta l'ammissione del membro";
/* No comment provided by engineer. */
"Set message expiration in chats." = "Imposta la scadenza dei messaggi nelle chat.";
@ -5210,6 +5100,9 @@ report reason */
/* No comment provided by engineer. */
"The old database was not removed during the migration, it can be deleted." = "Il database vecchio non è stato rimosso durante la migrazione, può essere eliminato.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Il profilo è condiviso solo con i tuoi contatti.";
/* No comment provided by engineer. */
"The same conditions will apply to operator **%@**." = "Le stesse condizioni si applicheranno all'operatore **%@**.";
@ -5819,9 +5712,6 @@ report reason */
/* No comment provided by engineer. */
"You accepted connection" = "Hai accettato la connessione";
/* snd group event chat item */
"you accepted this member" = "hai accettato questo membro";
/* No comment provided by engineer. */
"You allow" = "Lo consenti";
@ -5933,9 +5823,6 @@ report reason */
/* alert message */
"You can view invitation link again in connection details." = "Puoi vedere di nuovo il link di invito nei dettagli di connessione.";
/* alert message */
"You can view your reports in Chat with admins." = "Puoi vedere le tue segnalazioni nella chat con gli amministratori.";
/* No comment provided by engineer. */
"You can't send messages!" = "Non puoi inviare messaggi!";
@ -6104,15 +5991,15 @@ report reason */
/* No comment provided by engineer. */
"Your profile **%@** will be shared." = "Verrà condiviso il tuo profilo **%@**.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Il profilo è condiviso solo con i tuoi contatti.";
/* No comment provided by engineer. */
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Il tuo profilo è memorizzato sul tuo dispositivo e condiviso solo con i tuoi contatti. I server di SimpleX non possono vedere il tuo profilo.";
/* alert message */
"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Il tuo profilo è stato cambiato. Se lo salvi, il profilo aggiornato verrà inviato a tutti i tuoi contatti.";
/* No comment provided by engineer. */
"Your profile, contacts and delivered messages are stored on your device." = "Il tuo profilo, i contatti e i messaggi recapitati sono memorizzati sul tuo dispositivo.";
/* No comment provided by engineer. */
"Your random profile" = "Il tuo profilo casuale";

View file

@ -1277,7 +1277,7 @@ swipe action */
/* No comment provided by engineer. */
"Don't enable" = "有効にしない";
/* alert action */
/* No comment provided by engineer. */
"Don't show again" = "次から表示しない";
/* No comment provided by engineer. */
@ -1514,7 +1514,7 @@ swipe action */
/* alert title */
"Error receiving file" = "ファイル受信にエラー発生";
/* alert title */
/* No comment provided by engineer. */
"Error removing member" = "メンバー除名にエラー発生";
/* No comment provided by engineer. */
@ -2295,7 +2295,6 @@ snd error text */
/* enabled status
group pref value
member criteria value
time to disappear */
"off" = "オフ";
@ -2308,8 +2307,7 @@ time to disappear */
/* feature offered item */
"offered %@: %@" = "提供された %1$@: %2$@";
/* alert action
alert button */
/* alert button */
"Ok" = "OK";
/* No comment provided by engineer. */
@ -3113,6 +3111,9 @@ chat item action */
/* No comment provided by engineer. */
"The old database was not removed during the migration, it can be deleted." = "古いデータベースは移行時に削除されなかったので、削除することができます。";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "プロフィールは連絡先にしか共有されません。";
/* No comment provided by engineer. */
"The second tick we missed! ✅" = "長らくお待たせしました! ✅";
@ -3591,10 +3592,10 @@ chat item action */
"Your profile **%@** will be shared." = "あなたのプロファイル **%@** が共有されます。";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "プロフィールは連絡先にしか共有されません。";
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "プロフィールはデバイスに保存され、連絡先とのみ共有されます。 SimpleX サーバーはあなたのプロファイルを参照できません。";
/* No comment provided by engineer. */
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "プロフィールはデバイスに保存され、連絡先とのみ共有されます。 SimpleX サーバーはあなたのプロファイルを参照できません。";
"Your profile, contacts and delivered messages are stored on your device." = "あなたのプロフィール、連絡先、送信したメッセージがご自分の端末に保存されます。";
/* No comment provided by engineer. */
"Your random profile" = "あなたのランダム・プロフィール";

View file

@ -1869,7 +1869,7 @@ swipe action */
/* No comment provided by engineer. */
"Don't miss important messages." = "Mis geen belangrijke berichten.";
/* alert action */
/* No comment provided by engineer. */
"Don't show again" = "Niet meer weergeven";
/* No comment provided by engineer. */
@ -2233,7 +2233,7 @@ chat item action */
/* alert title */
"Error registering for notifications" = "Fout bij registreren voor meldingen";
/* alert title */
/* No comment provided by engineer. */
"Error removing member" = "Fout bij verwijderen van lid";
/* alert title */
@ -3584,7 +3584,6 @@ snd error text */
/* enabled status
group pref value
member criteria value
time to disappear */
"off" = "uit";
@ -3597,8 +3596,7 @@ time to disappear */
/* feature offered item */
"offered %@: %@" = "voorgesteld %1$@: %2$@";
/* alert action
alert button */
/* alert button */
"Ok" = "OK";
/* No comment provided by engineer. */
@ -5093,6 +5091,9 @@ report reason */
/* No comment provided by engineer. */
"The old database was not removed during the migration, it can be deleted." = "De oude database is niet verwijderd tijdens de migratie, deze kan worden verwijderd.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Het profiel wordt alleen gedeeld met uw contacten.";
/* No comment provided by engineer. */
"The same conditions will apply to operator **%@**." = "Dezelfde voorwaarden gelden voor operator **%@**.";
@ -5969,15 +5970,15 @@ report reason */
/* No comment provided by engineer. */
"Your profile **%@** will be shared." = "Uw profiel **%@** wordt gedeeld.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Het profiel wordt alleen gedeeld met uw contacten.";
/* No comment provided by engineer. */
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Uw profiel wordt op uw apparaat opgeslagen en alleen gedeeld met uw contacten. SimpleX servers kunnen uw profiel niet zien.";
/* alert message */
"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Je profiel is gewijzigd. Als je het opslaat, wordt het bijgewerkte profiel naar al je contacten verzonden.";
/* No comment provided by engineer. */
"Your profile, contacts and delivered messages are stored on your device." = "Uw profiel, contacten en afgeleverde berichten worden op uw apparaat opgeslagen.";
/* No comment provided by engineer. */
"Your random profile" = "Je willekeurige profiel";

View file

@ -1761,7 +1761,7 @@ swipe action */
/* No comment provided by engineer. */
"Don't enable" = "Nie włączaj";
/* alert action */
/* No comment provided by engineer. */
"Don't show again" = "Nie pokazuj ponownie";
/* No comment provided by engineer. */
@ -2092,7 +2092,7 @@ chat item action */
/* No comment provided by engineer. */
"Error reconnecting servers" = "Błąd ponownego łączenia serwerów";
/* alert title */
/* No comment provided by engineer. */
"Error removing member" = "Błąd usuwania członka";
/* No comment provided by engineer. */
@ -3236,7 +3236,6 @@ snd error text */
/* enabled status
group pref value
member criteria value
time to disappear */
"off" = "wyłączony";
@ -3249,8 +3248,7 @@ time to disappear */
/* feature offered item */
"offered %@: %@" = "zaoferował %1$@: %2$@";
/* alert action
alert button */
/* alert button */
"Ok" = "Ok";
/* No comment provided by engineer. */
@ -4558,6 +4556,9 @@ chat item action */
/* No comment provided by engineer. */
"The old database was not removed during the migration, it can be deleted." = "Stara baza danych nie została usunięta podczas migracji, można ją usunąć.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Profil jest udostępniany tylko Twoim kontaktom.";
/* No comment provided by engineer. */
"The second tick we missed! ✅" = "Drugi tik, który przegapiliśmy! ✅";
@ -5353,15 +5354,15 @@ chat item action */
/* No comment provided by engineer. */
"Your profile **%@** will be shared." = "Twój profil **%@** zostanie udostępniony.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Profil jest udostępniany tylko Twoim kontaktom.";
/* No comment provided by engineer. */
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Twój profil jest przechowywany na urządzeniu i udostępniany tylko Twoim kontaktom. Serwery SimpleX nie mogą zobaczyć Twojego profilu.";
/* alert message */
"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Twój profil został zmieniony. Jeśli go zapiszesz, zaktualizowany profil zostanie wysłany do wszystkich kontaktów.";
/* No comment provided by engineer. */
"Your profile, contacts and delivered messages are stored on your device." = "Twój profil, kontakty i dostarczone wiadomości są przechowywane na Twoim urządzeniu.";
/* No comment provided by engineer. */
"Your random profile" = "Twój losowy profil";

View file

@ -164,7 +164,7 @@
"%d file(s) were not downloaded." = "%d файлов не было загружено.";
/* time interval */
"%d hours" = "%d час.";
"%d hours" = "%d ч.";
/* alert title */
"%d messages not forwarded" = "%d сообщений не переслано";
@ -345,12 +345,6 @@ accept incoming call via notification
swipe action */
"Accept" = "Принять";
/* alert action */
"Accept as member" = "Принять в группу";
/* alert action */
"Accept as observer" = "Принять как читателя";
/* No comment provided by engineer. */
"Accept conditions" = "Принять условия";
@ -364,12 +358,6 @@ swipe action */
swipe action */
"Accept incognito" = "Принять инкогнито";
/* alert title */
"Accept member" = "Принять члена";
/* rcv group event chat item */
"accepted %@" = "принят %@";
/* call status */
"accepted call" = "принятый звонок";
@ -379,9 +367,6 @@ swipe action */
/* chat list item title */
"accepted invitation" = "принятое приглашение";
/* rcv group event chat item */
"accepted you" = "Вы приняты";
/* No comment provided by engineer. */
"Acknowledged" = "Подтверждено";
@ -478,9 +463,6 @@ swipe action */
/* chat item text */
"agreeing encryption…" = "шифрование согласовывается…";
/* member criteria value */
"all" = "все";
/* No comment provided by engineer. */
"All" = "Все";
@ -502,9 +484,6 @@ swipe action */
/* No comment provided by engineer. */
"All group members will remain connected." = "Все члены группы останутся соединены.";
/* feature role */
"all members" = "все члены";
/* No comment provided by engineer. */
"All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages." = "Все сообщения и файлы отправляются с **end-to-end шифрованием**, с постквантовой безопасностью в прямых разговорах.";
@ -523,9 +502,6 @@ swipe action */
/* No comment provided by engineer. */
"All reports will be archived for you." = "Все сообщения о нарушениях будут заархивированы для вас.";
/* No comment provided by engineer. */
"All servers" = "Все серверы";
/* No comment provided by engineer. */
"All your contacts will remain connected." = "Все контакты, которые соединились через этот адрес, сохранятся.";
@ -923,9 +899,6 @@ marked deleted chat item preview text */
/* No comment provided by engineer. */
"Can't message member" = "Не удаётся отправить сообщение члену группы";
/* No comment provided by engineer. */
"can't send messages" = "нельзя отправлять";
/* alert action
alert button */
"Cancel" = "Отменить";
@ -1063,18 +1036,9 @@ set passcode view */
/* No comment provided by engineer. */
"Chat will be deleted for you - this cannot be undone!" = "Разговор будет удален для Вас - это действие нельзя отменить!";
/* chat toolbar */
"Chat with admins" = "Чат с админами";
/* No comment provided by engineer. */
"Chat with member" = "Чат с членом группы";
/* No comment provided by engineer. */
"Chats" = "Чаты";
/* No comment provided by engineer. */
"Chats with members" = "Чаты с членами группы";
/* No comment provided by engineer. */
"Check messages every 20 min." = "Проверять сообщения каждые 20 минут.";
@ -1363,15 +1327,9 @@ set passcode view */
/* No comment provided by engineer. */
"Contact already exists" = "Существующий контакт";
/* No comment provided by engineer. */
"contact deleted" = "контакт удален";
/* No comment provided by engineer. */
"Contact deleted!" = "Контакт удален!";
/* No comment provided by engineer. */
"contact disabled" = "контакт выключен";
/* No comment provided by engineer. */
"contact has e2e encryption" = "у контакта есть e2e шифрование";
@ -1390,9 +1348,6 @@ set passcode view */
/* No comment provided by engineer. */
"Contact name" = "Имена контактов";
/* No comment provided by engineer. */
"contact not ready" = "контакт не готов";
/* No comment provided by engineer. */
"Contact preferences" = "Предпочтения контакта";
@ -1641,9 +1596,6 @@ swipe action */
/* No comment provided by engineer. */
"Delete chat profile?" = "Удалить профиль?";
/* alert title */
"Delete chat with member?" = "Удалить чат с членом группы?";
/* No comment provided by engineer. */
"Delete chat?" = "Удалить разговор?";
@ -1830,9 +1782,6 @@ swipe action */
/* No comment provided by engineer. */
"Direct messages between members are prohibited in this chat." = "Личные сообщения запрещены в этой группе.";
/* No comment provided by engineer. */
"Direct messages between members are prohibited." = "Прямые сообщения между членами запрещены.";
/* No comment provided by engineer. */
"Disable (keep overrides)" = "Выключить (кроме исключений)";
@ -1887,9 +1836,6 @@ swipe action */
/* No comment provided by engineer. */
"Do it later" = "Отложить";
/* No comment provided by engineer. */
"Do not send history to new members." = "Не отправлять историю новым членам.";
/* No comment provided by engineer. */
"Do NOT send messages directly, even if your or destination server does not support private routing." = "Не отправлять сообщения напрямую, даже если сервер получателя не поддерживает конфиденциальную доставку.";
@ -1914,7 +1860,7 @@ swipe action */
/* No comment provided by engineer. */
"Don't miss important messages." = "Не пропустите важные сообщения.";
/* alert action */
/* No comment provided by engineer. */
"Don't show again" = "Не показывать";
/* No comment provided by engineer. */
@ -1987,9 +1933,6 @@ chat item action */
/* No comment provided by engineer. */
"Enable camera access" = "Включить доступ к камере";
/* No comment provided by engineer. */
"Enable Flux in Network & servers settings for better metadata privacy." = "Включите Flux в настройках Сеть и серверы для лучшей конфиденциальности метаданных.";
/* No comment provided by engineer. */
"Enable for all" = "Включить для всех";
@ -2158,12 +2101,6 @@ chat item action */
/* No comment provided by engineer. */
"Error accepting contact request" = "Ошибка при принятии запроса на соединение";
/* alert title */
"Error accepting member" = "Ошибка вступления члена группы";
/* No comment provided by engineer. */
"Error adding member(s)" = "Ошибка при добавлении членов группы";
/* alert title */
"Error adding server" = "Ошибка добавления сервера";
@ -2200,9 +2137,6 @@ chat item action */
/* alert title */
"Error creating list" = "Ошибка создания списка";
/* No comment provided by engineer. */
"Error creating member contact" = "Ошибка при создании контакта";
/* No comment provided by engineer. */
"Error creating message" = "Ошибка создания сообщения";
@ -2218,9 +2152,6 @@ chat item action */
/* No comment provided by engineer. */
"Error deleting chat database" = "Ошибка при удалении данных чата";
/* alert title */
"Error deleting chat with member" = "Ошибка при удалении чата с членом группы";
/* No comment provided by engineer. */
"Error deleting chat!" = "Ошибка при удалении чата!";
@ -2284,9 +2215,6 @@ chat item action */
/* alert title */
"Error registering for notifications" = "Ошибка регистрации для уведомлений";
/* alert title */
"Error removing member" = "Ошибка при удалении члена группы";
/* alert title */
"Error reordering lists" = "Ошибка сортировки списков";
@ -2323,9 +2251,6 @@ chat item action */
/* No comment provided by engineer. */
"Error sending email" = "Ошибка отправки email";
/* No comment provided by engineer. */
"Error sending member contact invitation" = "Ошибка при отправке приглашения члену";
/* No comment provided by engineer. */
"Error sending message" = "Ошибка при отправке сообщения";
@ -2526,9 +2451,6 @@ snd error text */
/* No comment provided by engineer. */
"Fix not supported by contact" = "Починка не поддерживается контактом";
/* No comment provided by engineer. */
"Fix not supported by group member" = "Починка не поддерживается членом группы.";
/* No comment provided by engineer. */
"For all moderators" = "Для всех модераторов";
@ -2607,9 +2529,6 @@ snd error text */
/* No comment provided by engineer. */
"Full name (optional)" = "Полное имя (не обязательно)";
/* No comment provided by engineer. */
"Fully decentralized visible only to members." = "Группа полностью децентрализована она видна только членам.";
/* No comment provided by engineer. */
"Fully re-implemented - work in background!" = "Полностью обновлены - работают в фоне!";
@ -2658,9 +2577,6 @@ snd error text */
/* No comment provided by engineer. */
"Group invitation is no longer valid, it was removed by sender." = "Приглашение в группу больше не действительно, оно было удалено отправителем.";
/* No comment provided by engineer. */
"group is deleted" = "группа удалена";
/* No comment provided by engineer. */
"Group link" = "Ссылка группы";
@ -2679,18 +2595,12 @@ snd error text */
/* No comment provided by engineer. */
"Group profile" = "Профиль группы";
/* No comment provided by engineer. */
"Group profile is stored on members' devices, not on the servers." = "Профиль группы хранится на устройствах членов, а не на серверах.";
/* snd group event chat item */
"group profile updated" = "профиль группы обновлен";
/* No comment provided by engineer. */
"Group welcome message" = "Приветственное сообщение группы";
/* No comment provided by engineer. */
"Group will be deleted for all members - this cannot be undone!" = "Группа будет удалена для всех членов - это действие нельзя отменить!";
/* No comment provided by engineer. */
"Group will be deleted for you - this cannot be undone!" = "Группа будет удалена для Вас - это действие нельзя отменить!";
@ -2727,9 +2637,6 @@ snd error text */
/* No comment provided by engineer. */
"History" = "История";
/* No comment provided by engineer. */
"History is not sent to new members." = "История не отправляется новым членам.";
/* time unit */
"hours" = "часов";
@ -2964,9 +2871,6 @@ snd error text */
/* No comment provided by engineer. */
"Invite friends" = "Пригласить друзей";
/* No comment provided by engineer. */
"Invite members" = "Пригласить членов группы";
/* No comment provided by engineer. */
"Invite to chat" = "Пригласить в разговор";
@ -3180,75 +3084,15 @@ snd error text */
/* blur media */
"Medium" = "Среднее";
/* member role */
"member" = "член группы";
/* No comment provided by engineer. */
"Member" = "Член группы";
/* profile update event chat item */
"member %@ changed to %@" = "член %1$@ изменился на %2$@";
/* No comment provided by engineer. */
"Member admission" = "Приём членов в группу";
/* rcv group event chat item */
"member connected" = "соединен(а)";
/* No comment provided by engineer. */
"member has old version" = "член имеет старую версию";
/* item status text */
"Member inactive" = "Член неактивен";
/* chat feature */
"Member reports" = "Сообщения о нарушениях";
/* No comment provided by engineer. */
"Member role will be changed to \"%@\". All chat members will be notified." = "Роль участника будет изменена на \"%@\". Все участники разговора получат уведомление.";
/* No comment provided by engineer. */
"Member role will be changed to \"%@\". All group members will be notified." = "Роль члена будет изменена на \"%@\". Все члены группы получат уведомление.";
/* No comment provided by engineer. */
"Member role will be changed to \"%@\". The member will receive a new invitation." = "Роль члена будет изменена на \"%@\". Будет отправлено новое приглашение.";
/* No comment provided by engineer. */
"Member will be removed from chat - this cannot be undone!" = "Член будет удален из разговора - это действие нельзя отменить!";
/* No comment provided by engineer. */
"Member will be removed from group - this cannot be undone!" = "Член группы будет удален - это действие нельзя отменить!";
/* alert message */
"Member will join the group, accept member?" = "Участник хочет присоединиться к группе. Принять?";
/* No comment provided by engineer. */
"Members can add message reactions." = "Члены могут добавлять реакции на сообщения.";
/* No comment provided by engineer. */
"Members can irreversibly delete sent messages. (24 hours)" = "Члены могут необратимо удалять отправленные сообщения. (24 часа)";
/* No comment provided by engineer. */
"Members can report messsages to moderators." = "Члены группы могут пожаловаться модераторам.";
/* No comment provided by engineer. */
"Members can send direct messages." = "Члены могут посылать прямые сообщения.";
/* No comment provided by engineer. */
"Members can send disappearing messages." = "Члены могут посылать исчезающие сообщения.";
/* No comment provided by engineer. */
"Members can send files and media." = "Члены могут слать файлы и медиа.";
/* No comment provided by engineer. */
"Members can send SimpleX links." = "Члены могут отправлять ссылки SimpleX.";
/* No comment provided by engineer. */
"Members can send voice messages." = "Члены могут отправлять голосовые сообщения.";
/* No comment provided by engineer. */
"Mention members 👋" = "Упоминайте участников 👋";
/* No comment provided by engineer. */
"Menus" = "Меню";
@ -3270,9 +3114,6 @@ snd error text */
/* item status text */
"Message forwarded" = "Сообщение переслано";
/* item status description */
"Message may be delivered later if member becomes active." = "Сообщение может быть доставлено позже, если член группы станет активным.";
/* No comment provided by engineer. */
"Message queue info" = "Информация об очереди сообщений";
@ -3489,12 +3330,6 @@ snd error text */
/* No comment provided by engineer. */
"New media options" = "Новые медиа-опции";
/* No comment provided by engineer. */
"New member role" = "Роль члена группы";
/* rcv group event chat item */
"New member wants to join the group." = "Новый участник хочет присоединиться к группе.";
/* notification */
"new message" = "новое сообщение";
@ -3534,9 +3369,6 @@ snd error text */
/* No comment provided by engineer. */
"No chats in list %@" = "Нет чатов в списке %@";
/* No comment provided by engineer. */
"No chats with members" = "Нет чатов с членами группы";
/* No comment provided by engineer. */
"No contacts selected" = "Контакты не выбраны";
@ -3621,9 +3453,6 @@ snd error text */
/* No comment provided by engineer. */
"Not compatible!" = "Несовместимая версия!";
/* No comment provided by engineer. */
"not synchronized" = "не синхронизирован";
/* No comment provided by engineer. */
"Notes" = "Заметки";
@ -3648,15 +3477,11 @@ snd error text */
/* alert title */
"Notifications status" = "Статус уведомлений";
/* No comment provided by engineer. */
"Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "Теперь админы могут:\n- удалять сообщения членов.\n- приостанавливать членов (роль наблюдатель)";
/* member role */
"observer" = "читатель";
/* enabled status
group pref value
member criteria value
time to disappear */
"off" = "нет";
@ -3669,8 +3494,7 @@ time to disappear */
/* feature offered item */
"offered %@: %@" = "предложил(a) %1$@: %2$@";
/* alert action
alert button */
/* alert button */
"Ok" = "Ок";
/* No comment provided by engineer. */
@ -3766,9 +3590,6 @@ alert button */
/* No comment provided by engineer. */
"Open group" = "Открыть группу";
/* alert title */
"Open link?" = "Открыть ссылку?";
/* authentication reason */
"Open migration to another device" = "Открытие миграции на другое устройство";
@ -3844,9 +3665,6 @@ alert button */
/* No comment provided by engineer. */
"Password to show" = "Пароль чтобы раскрыть";
/* past/unknown group member */
"Past member %@" = "Бывший член %@";
/* No comment provided by engineer. */
"Paste desktop address" = "Вставить адрес компьютера";
@ -3871,9 +3689,6 @@ alert button */
/* No comment provided by engineer. */
"pending approval" = "ожидает утверждения";
/* No comment provided by engineer. */
"pending review" = "ожидает одобрения";
/* No comment provided by engineer. */
"Periodic" = "Периодически";
@ -3943,9 +3758,6 @@ alert button */
/* token info */
"Please try to disable and re-enable notfications." = "Попробуйте выключить и снова включить уведомления.";
/* snd group event chat item */
"Please wait for group moderators to review your request to join the group." = "Пожалуйста, подождите, пока модераторы группы рассмотрят ваш запрос на вступление.";
/* token info */
"Please wait for token activation to complete." = "Пожалуйста, дождитесь завершения активации токена.";
@ -4045,9 +3857,6 @@ alert button */
/* No comment provided by engineer. */
"Prohibit reporting messages to moderators." = "Запретить жаловаться модераторам группы.";
/* No comment provided by engineer. */
"Prohibit sending direct messages to members." = "Запретить посылать прямые сообщения членам группы.";
/* No comment provided by engineer. */
"Prohibit sending disappearing messages." = "Запретить посылать исчезающие сообщения.";
@ -4226,9 +4035,6 @@ swipe action */
/* No comment provided by engineer. */
"Reject contact request" = "Отклонить запрос";
/* alert title */
"Reject member?" = "Отклонить участника?";
/* No comment provided by engineer. */
"rejected" = "отклонён";
@ -4250,12 +4056,6 @@ swipe action */
/* No comment provided by engineer. */
"Remove image" = "Удалить изображение";
/* No comment provided by engineer. */
"Remove member" = "Удалить члена группы";
/* No comment provided by engineer. */
"Remove member?" = "Удалить члена группы?";
/* No comment provided by engineer. */
"Remove passphrase from keychain?" = "Удалить пароль из Keychain?";
@ -4268,9 +4068,6 @@ swipe action */
/* profile update event chat item */
"removed contact address" = "удалён адрес контакта";
/* No comment provided by engineer. */
"removed from group" = "удален из группы";
/* profile update event chat item */
"removed profile picture" = "удалена картинка профиля";
@ -4319,9 +4116,6 @@ swipe action */
/* No comment provided by engineer. */
"Report reason?" = "Причина сообщения?";
/* alert title */
"Report sent to moderators" = "Жалоба отправлена модераторам";
/* report reason */
"Report spam: only group moderators will see it." = "Пожаловаться на спам: увидят только модераторы группы.";
@ -4337,9 +4131,6 @@ swipe action */
/* No comment provided by engineer. */
"Reports" = "Сообщения о нарушениях";
/* No comment provided by engineer. */
"request to join rejected" = "запрос на вступление отклонён";
/* chat list item title */
"requested to connect" = "запрошено соединение";
@ -4394,21 +4185,9 @@ swipe action */
/* chat item action */
"Reveal" = "Показать";
/* No comment provided by engineer. */
"review" = "рассмотрение";
/* No comment provided by engineer. */
"Review conditions" = "Посмотреть условия";
/* admission stage */
"Review members" = "Одобрять членов";
/* admission stage description */
"Review members before admitting (\"knocking\")." = "Одобрять членов для вступления в группу.";
/* No comment provided by engineer. */
"reviewed by admins" = "одобрен админами";
/* No comment provided by engineer. */
"Revoke" = "Отозвать";
@ -4437,15 +4216,9 @@ chat item action */
/* alert button */
"Save (and notify contacts)" = "Сохранить (и уведомить контакты)";
/* alert title */
"Save admission settings?" = "Сохранить настройки вступления?";
/* alert button */
"Save and notify contact" = "Сохранить и уведомить контакт";
/* No comment provided by engineer. */
"Save and notify group members" = "Сохранить и уведомить членов группы";
/* No comment provided by engineer. */
"Save and reconnect" = "Сохранить и переподключиться";
@ -4638,9 +4411,6 @@ chat item action */
/* No comment provided by engineer. */
"Send them from gallery or custom keyboards." = "Отправьте из галереи или из дополнительных клавиатур.";
/* No comment provided by engineer. */
"Send up to 100 last messages to new members." = "Отправить до 100 последних сообщений новым членам.";
/* alert message */
"Sender cancelled file transfer." = "Отправитель отменил передачу файла.";
@ -4776,9 +4546,6 @@ chat item action */
/* No comment provided by engineer. */
"Set it instead of system authentication." = "Установите код вместо системной аутентификации.";
/* No comment provided by engineer. */
"Set member admission" = "Приём членов в группу";
/* No comment provided by engineer. */
"Set message expiration in chats." = "Установите срок хранения сообщений в чатах.";
@ -4797,9 +4564,6 @@ chat item action */
/* No comment provided by engineer. */
"Set passphrase to export" = "Установите пароль";
/* No comment provided by engineer. */
"Set the message shown to new members!" = "Установить сообщение для новых членов группы!";
/* No comment provided by engineer. */
"Set timeouts for proxy/VPN" = "Установить таймауты для прокси/VPN";
@ -4852,9 +4616,6 @@ chat item action */
/* No comment provided by engineer. */
"Share with contacts" = "Поделиться с контактами";
/* No comment provided by engineer. */
"Short link" = "Короткая ссылка";
/* No comment provided by engineer. */
"Show → on messages sent via private routing." = "Показать → на сообщениях доставленных конфиденциально.";
@ -4897,9 +4658,6 @@ chat item action */
/* No comment provided by engineer. */
"SimpleX address or 1-time link?" = "Адрес SimpleX или одноразовая ссылка?";
/* simplex link type */
"SimpleX channel link" = "SimpleX ссылка канала";
/* No comment provided by engineer. */
"SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app." = "SimpleX Chat и Flux заключили соглашение добавить серверы под управлением Flux в приложение.";
@ -5195,21 +4953,12 @@ report reason */
/* No comment provided by engineer. */
"The ID of the next message is incorrect (less or equal to the previous).\nIt can happen because of some bug or when the connection is compromised." = "Неправильный ID предыдущего сообщения (меньше или равен предыдущему).\nЭто может произойти из-за ошибки программы, или когда соединение компроментировано.";
/* No comment provided by engineer. */
"The message will be deleted for all members." = "Сообщение будет удалено для всех членов группы.";
/* No comment provided by engineer. */
"The message will be marked as moderated for all members." = "Сообщение будет помечено как удаленное для всех членов группы.";
/* No comment provided by engineer. */
"The messages will be deleted for all members." = "Сообщения будут удалены для всех членов группы.";
/* No comment provided by engineer. */
"The messages will be marked as moderated for all members." = "Сообщения будут помечены как удаленные для всех членов группы.";
/* No comment provided by engineer. */
"The old database was not removed during the migration, it can be deleted." = "Предыдущая версия данных чата не удалена при перемещении, её можно удалить.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Ваш профиль храниться на Вашем устройстве и отправляется только контактам.";
/* No comment provided by engineer. */
"The same conditions will apply to operator **%@**." = "Те же самые условия будут приняты для оператора **%@**.";
@ -5273,9 +5022,6 @@ report reason */
/* No comment provided by engineer. */
"This display name is invalid. Please choose another name." = "Ошибка имени профиля. Пожалуйста, выберите другое имя.";
/* No comment provided by engineer. */
"This group has over %lld members, delivery receipts are not sent." = "В этой группе более %lld членов, отчёты о доставке не отправляются.";
/* No comment provided by engineer. */
"This group no longer exists." = "Эта группа больше не существует.";
@ -5285,9 +5031,6 @@ report reason */
/* No comment provided by engineer. */
"This is your own SimpleX address!" = "Это ваш собственный адрес SimpleX!";
/* No comment provided by engineer. */
"This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link." = "Эта ссылка требует новую версию. Обновите приложение или попросите Ваш контакт прислать совместимую ссылку.";
/* No comment provided by engineer. */
"This link was used with another mobile device, please create a new link on the desktop." = "Эта ссылка была использована на другом мобильном, пожалуйста, создайте новую ссылку на компьютере.";
@ -5399,15 +5142,6 @@ report reason */
/* No comment provided by engineer. */
"Unblock for all" = "Разблокировать для всех";
/* No comment provided by engineer. */
"Unblock member" = "Разблокировать члена группы";
/* No comment provided by engineer. */
"Unblock member for all?" = "Разблокировать члена для всех?";
/* No comment provided by engineer. */
"Unblock member?" = "Разблокировать члена группы?";
/* rcv group event chat item */
"unblocked %@" = "%@ разблокирован";
@ -5480,12 +5214,6 @@ report reason */
/* swipe action */
"Unread" = "Не прочитано";
/* No comment provided by engineer. */
"Unsupported connection link" = "Ссылка не поддерживается";
/* No comment provided by engineer. */
"Up to 100 last messages are sent to new members." = "До 100 последних сообщений отправляются новым членам.";
/* No comment provided by engineer. */
"Update" = "Обновить";
@ -5576,9 +5304,6 @@ report reason */
/* No comment provided by engineer. */
"Use servers" = "Использовать серверы";
/* No comment provided by engineer. */
"Use short links (BETA)" = "Короткие ссылки (БЕТА)";
/* No comment provided by engineer. */
"Use SimpleX Chat servers?" = "Использовать серверы предосталенные SimpleX Chat?";
@ -5588,9 +5313,6 @@ report reason */
/* No comment provided by engineer. */
"Use TCP port %@ when no port is specified." = "Использовать TCP-порт %@, когда порт не указан.";
/* No comment provided by engineer. */
"Use TCP port 443 for preset servers only." = "Использовать TCP-порт 443 только для серверов по умолчанию.";
/* No comment provided by engineer. */
"Use the app while in the call." = "Используйте приложение во время звонка.";
@ -5819,9 +5541,6 @@ report reason */
/* No comment provided by engineer. */
"You accepted connection" = "Вы приняли приглашение соединиться";
/* snd group event chat item */
"you accepted this member" = "Вы приняли этого члена";
/* No comment provided by engineer. */
"You allow" = "Вы разрешаете";
@ -5912,9 +5631,6 @@ report reason */
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Вы можете установить просмотр уведомлений на экране блокировки в настройках.";
/* No comment provided by engineer. */
"You can share a link or a QR code - anybody will be able to join the group. You won't lose members of the group if you later delete it." = "Вы можете поделиться ссылкой или QR кодом - через них можно присоединиться к группе. Вы сможете удалить ссылку, сохранив членов группы, которые через нее соединились.";
/* No comment provided by engineer. */
"You can share this address with your contacts to let them connect with **%@**." = "Вы можете поделиться этим адресом с Вашими контактами, чтобы они могли соединиться с **%@**.";
@ -5933,9 +5649,6 @@ report reason */
/* alert message */
"You can view invitation link again in connection details." = "Вы можете увидеть ссылку-приглашение снова открыв соединение.";
/* alert message */
"You can view your reports in Chat with admins." = "Вы можете найти Ваши жалобы в Чате с админами.";
/* No comment provided by engineer. */
"You can't send messages!" = "Вы не можете отправлять сообщения!";
@ -5972,9 +5685,6 @@ report reason */
/* No comment provided by engineer. */
"You joined this group" = "Вы вступили в эту группу";
/* No comment provided by engineer. */
"You joined this group. Connecting to inviting group member." = "Вы вступили в эту группу. Устанавливается соединение с пригласившим членом группы.";
/* snd group event chat item */
"you left" = "Вы покинули группу";
@ -6029,9 +5739,6 @@ report reason */
/* No comment provided by engineer. */
"You will be required to authenticate when you start or resume the app after 30 seconds in background." = "Вы будете аутентифицированы при запуске и возобновлении приложения, которое было 30 секунд в фоновом режиме.";
/* No comment provided by engineer. */
"You will connect to all group members." = "Вы соединитесь со всеми членами группы.";
/* No comment provided by engineer. */
"You will still receive calls and notifications from muted profiles when they are active." = "Вы все равно получите звонки и уведомления в профилях без звука, когда они активные.";
@ -6104,15 +5811,15 @@ report reason */
/* No comment provided by engineer. */
"Your profile **%@** will be shared." = "Будет отправлен Ваш профиль **%@**.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Ваш профиль храниться на Вашем устройстве и отправляется только контактам.";
/* No comment provided by engineer. */
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Ваш профиль хранится на Вашем устройстве и отправляется только Вашим контактам. SimpleX серверы не могут получить доступ к Вашему профилю.";
/* alert message */
"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Ваш профиль был изменен. Если вы сохраните его, обновленный профиль будет отправлен всем вашим контактам.";
/* No comment provided by engineer. */
"Your profile, contacts and delivered messages are stored on your device." = "Ваш профиль, контакты и доставленные сообщения хранятся на Вашем устройстве.";
/* No comment provided by engineer. */
"Your random profile" = "Случайный профиль";
@ -6127,3 +5834,4 @@ report reason */
/* No comment provided by engineer. */
"Your SimpleX address" = "Ваш адрес SimpleX";

View file

@ -1028,7 +1028,7 @@ swipe action */
/* No comment provided by engineer. */
"Don't enable" = "อย่าเปิดใช้งาน";
/* alert action */
/* No comment provided by engineer. */
"Don't show again" = "ไม่ต้องแสดงอีก";
/* No comment provided by engineer. */
@ -1256,7 +1256,7 @@ swipe action */
/* alert title */
"Error receiving file" = "เกิดข้อผิดพลาดในการรับไฟล์";
/* alert title */
/* No comment provided by engineer. */
"Error removing member" = "เกิดข้อผิดพลาดในการลบสมาชิก";
/* No comment provided by engineer. */
@ -2016,7 +2016,6 @@ snd error text */
/* enabled status
group pref value
member criteria value
time to disappear */
"off" = "ปิด";
@ -2029,8 +2028,7 @@ time to disappear */
/* feature offered item */
"offered %@: %@" = "เสนอแล้ว %1$@: %2$@";
/* alert action
alert button */
/* alert button */
"Ok" = "ตกลง";
/* No comment provided by engineer. */
@ -2831,6 +2829,9 @@ chat item action */
/* No comment provided by engineer. */
"The old database was not removed during the migration, it can be deleted." = "ฐานข้อมูลเก่าไม่ได้ถูกลบในระหว่างการย้ายข้อมูล แต่สามารถลบได้";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "โปรไฟล์นี้แชร์กับผู้ติดต่อของคุณเท่านั้น";
/* No comment provided by engineer. */
"The second tick we missed! ✅" = "ขีดที่สองที่เราพลาด! ✅";
@ -3291,10 +3292,10 @@ chat item action */
"Your privacy" = "ความเป็นส่วนตัวของคุณ";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "โปรไฟล์นี้แชร์กับผู้ติดต่อของคุณเท่านั้น";
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "โปรไฟล์ของคุณจะถูกจัดเก็บไว้ในอุปกรณ์ของคุณและแชร์กับผู้ติดต่อของคุณเท่านั้น เซิร์ฟเวอร์ SimpleX ไม่สามารถดูโปรไฟล์ของคุณได้";
/* No comment provided by engineer. */
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "โปรไฟล์ของคุณจะถูกจัดเก็บไว้ในอุปกรณ์ของคุณและแชร์กับผู้ติดต่อของคุณเท่านั้น เซิร์ฟเวอร์ SimpleX ไม่สามารถดูโปรไฟล์ของคุณได้";
"Your profile, contacts and delivered messages are stored on your device." = "โปรไฟล์ รายชื่อผู้ติดต่อ และข้อความที่ส่งของคุณจะถูกจัดเก็บไว้ในอุปกรณ์ของคุณ";
/* No comment provided by engineer. */
"Your random profile" = "โปรไฟล์แบบสุ่มของคุณ";

View file

@ -1740,7 +1740,7 @@ swipe action */
/* No comment provided by engineer. */
"Don't enable" = "Etkinleştirme";
/* alert action */
/* No comment provided by engineer. */
"Don't show again" = "Yeniden gösterme";
/* No comment provided by engineer. */
@ -2083,7 +2083,7 @@ chat item action */
/* No comment provided by engineer. */
"Error reconnecting servers" = "Hata sunuculara yeniden bağlanılıyor";
/* alert title */
/* No comment provided by engineer. */
"Error removing member" = "Kişiyi silerken sorun oluştu";
/* No comment provided by engineer. */
@ -3272,7 +3272,6 @@ snd error text */
/* enabled status
group pref value
member criteria value
time to disappear */
"off" = "kapalı";
@ -3285,8 +3284,7 @@ time to disappear */
/* feature offered item */
"offered %@: %@" = "%1$@: %2$@ teklif etti";
/* alert action
alert button */
/* alert button */
"Ok" = "Tamam";
/* No comment provided by engineer. */
@ -4603,6 +4601,9 @@ chat item action */
/* No comment provided by engineer. */
"The old database was not removed during the migration, it can be deleted." = "Eski veritabanı geçiş sırasında kaldırılmadı, silinebilir.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Profil sadece kişilerinle paylaşılacak.";
/* No comment provided by engineer. */
"The second tick we missed! ✅" = "Özlediğimiz ikinci tik! ✅";
@ -5398,15 +5399,15 @@ chat item action */
/* No comment provided by engineer. */
"Your profile **%@** will be shared." = "Profiliniz **%@** paylaşılacaktır.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Profil sadece kişilerinle paylaşılacak.";
/* No comment provided by engineer. */
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Profiliniz cihazınızda saklanır ve sadece kişilerinizle paylaşılır. SimpleX sunucuları profilinizi göremez.";
/* alert message */
"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Profiliniz değiştirildi. Kaydederseniz, güncellenmiş profil tüm kişilerinize gönderilecektir.";
/* No comment provided by engineer. */
"Your profile, contacts and delivered messages are stored on your device." = "Profiliniz, kişileriniz ve gönderilmiş mesajlar cihazınızda saklanır.";
/* No comment provided by engineer. */
"Your random profile" = "Rasgele profiliniz";

View file

@ -1746,7 +1746,7 @@ swipe action */
/* No comment provided by engineer. */
"Don't enable" = "Не вмикати";
/* alert action */
/* No comment provided by engineer. */
"Don't show again" = "Більше не показувати";
/* No comment provided by engineer. */
@ -2089,7 +2089,7 @@ chat item action */
/* No comment provided by engineer. */
"Error reconnecting servers" = "Помилка перепідключення серверів";
/* alert title */
/* No comment provided by engineer. */
"Error removing member" = "Помилка видалення учасника";
/* No comment provided by engineer. */
@ -3317,7 +3317,6 @@ snd error text */
/* enabled status
group pref value
member criteria value
time to disappear */
"off" = "вимкнено";
@ -3330,8 +3329,7 @@ time to disappear */
/* feature offered item */
"offered %@: %@" = "запропонував %1$@: %2$@";
/* alert action
alert button */
/* alert button */
"Ok" = "Гаразд";
/* No comment provided by engineer. */
@ -4723,6 +4721,9 @@ chat item action */
/* No comment provided by engineer. */
"The old database was not removed during the migration, it can be deleted." = "Стара база даних не була видалена під час міграції, її можна видалити.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Профіль доступний лише вашим контактам.";
/* No comment provided by engineer. */
"The same conditions will apply to operator **%@**." = "Такі ж умови діятимуть і для оператора **%@**.";
@ -5578,15 +5579,15 @@ chat item action */
/* No comment provided by engineer. */
"Your profile **%@** will be shared." = "Ваш профіль **%@** буде опублікований.";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "Профіль доступний лише вашим контактам.";
/* No comment provided by engineer. */
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Ваш профіль зберігається на вашому пристрої і доступний лише вашим контактам. Сервери SimpleX не бачать ваш профіль.";
/* alert message */
"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Ваш профіль було змінено. Якщо ви збережете його, оновлений профіль буде надіслано всім вашим контактам.";
/* No comment provided by engineer. */
"Your profile, contacts and delivered messages are stored on your device." = "Ваш профіль, контакти та доставлені повідомлення зберігаються на вашому пристрої.";
/* No comment provided by engineer. */
"Your random profile" = "Ваш випадковий профіль";

View file

@ -1866,7 +1866,7 @@ swipe action */
/* No comment provided by engineer. */
"Don't miss important messages." = "不错过重要消息。";
/* alert action */
/* No comment provided by engineer. */
"Don't show again" = "不再显示";
/* No comment provided by engineer. */
@ -2227,7 +2227,7 @@ chat item action */
/* alert title */
"Error registering for notifications" = "注册消息推送出错";
/* alert title */
/* No comment provided by engineer. */
"Error removing member" = "删除成员错误";
/* alert title */
@ -3575,7 +3575,6 @@ snd error text */
/* enabled status
group pref value
member criteria value
time to disappear */
"off" = "关闭";
@ -3588,8 +3587,7 @@ time to disappear */
/* feature offered item */
"offered %@: %@" = "已提供 %1$@%2$@";
/* alert action
alert button */
/* alert button */
"Ok" = "好的";
/* No comment provided by engineer. */
@ -4924,6 +4922,9 @@ chat item action */
/* No comment provided by engineer. */
"The old database was not removed during the migration, it can be deleted." = "旧数据库在迁移过程中没有被移除,可以删除。";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "该资料仅与您的联系人共享。";
/* No comment provided by engineer. */
"The second tick we missed! ✅" = "我们错过的第二个\"√\"!✅";
@ -5696,10 +5697,10 @@ chat item action */
"Your profile **%@** will be shared." = "您的个人资料 **%@** 将被共享。";
/* No comment provided by engineer. */
"Your profile is stored on your device and only shared with your contacts." = "该资料仅与您的联系人共享。";
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "您的资料存储在您的设备上并仅与您的联系人共享。 SimpleX 服务器无法看到您的资料。";
/* No comment provided by engineer. */
"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "您的资料存储在您的设备上并仅与您的联系人共享。 SimpleX 服务器无法看到您的资料。";
"Your profile, contacts and delivered messages are stored on your device." = "您的资料、联系人和发送的消息存储在您的设备上。";
/* No comment provided by engineer. */
"Your random profile" = "您的随机资料";

View file

@ -155,6 +155,7 @@ buildConfig {
buildConfigField("String", "DESKTOP_VERSION_NAME", "\"${extra["desktop.version_name"]}\"")
buildConfigField("int", "DESKTOP_VERSION_CODE", "${extra["desktop.version_code"]}")
buildConfigField("String", "DATABASE_BACKEND", "\"${extra["database.backend"]}\"")
buildConfigField("Boolean", "ANDROID_BUNDLE", "${extra["android.bundle"]}")
}
}

View file

@ -91,7 +91,7 @@ fun UserPickerUserBox(
ProfileImageForActiveCall(size = USER_PICKER_IMAGE_SIZE, image = userInfo.user.profile.image, color = MaterialTheme.colors.secondaryVariant)
if (userInfo.unreadCount > 0 && !userInfo.user.activeUser) {
userUnreadBadge(userInfo.unreadCount, userInfo.user.showNtfs, false)
unreadBadge(userInfo.unreadCount, userInfo.user.showNtfs, false)
}
}
val user = userInfo.user

View file

@ -295,13 +295,14 @@ object ChatModel {
}
}
class ChatsContext(val secondaryContextFilter: SecondaryContextFilter?) {
class ChatsContext(val contentTag: MsgContentTag?) {
val chats = mutableStateOf(SnapshotStateList<Chat>())
/** if you modify the items by adding/removing them, use helpers methods like [addToChatItems], [removeLastChatItems], [removeAllAndNotify], [clearAndNotify] and so on.
* If some helper is missing, create it. Notify is needed to track state of items that we added manually (not via api call). See [apiLoadMessages].
* If you use api call to get the items, use just [add] instead of [addToChatItems].
* Never modify underlying list directly because it produces unexpected results in ChatView's LazyColumn (setting by index is ok) */
val chatItems = mutableStateOf(SnapshotStateList<ChatItem>())
val chatItemStatuses = mutableMapOf<Long, CIStatus>()
// set listener here that will be notified on every add/delete of a chat item
val chatState = ActiveChatState()
@ -309,26 +310,6 @@ object ChatModel {
fun getChat(id: String): Chat? = chats.value.firstOrNull { it.id == id }
private fun getChatIndex(rhId: Long?, id: String): Int = chats.value.indexOfFirst { it.id == id && it.remoteHostId == rhId }
val contentTag: MsgContentTag? =
when (secondaryContextFilter) {
null -> null
is SecondaryContextFilter.GroupChatScopeContext -> null
is SecondaryContextFilter.MsgContentTagContext -> secondaryContextFilter.contentTag
}
val groupScopeInfo: GroupChatScopeInfo? =
when (secondaryContextFilter) {
null -> null
is SecondaryContextFilter.GroupChatScopeContext -> secondaryContextFilter.groupScopeInfo
is SecondaryContextFilter.MsgContentTagContext -> null
}
val isUserSupportChat: Boolean =
when (groupScopeInfo) {
null -> false
is GroupChatScopeInfo.MemberSupport -> groupScopeInfo.groupMember_ == null
}
suspend fun addChat(chat: Chat) {
chats.add(index = 0, chat)
popChatCollector.throttlePopChat(chat.remoteHostId, chat.id, currentPosition = 0)
@ -361,8 +342,6 @@ object ChatModel {
)
)
}
} else if (currentCInfo is ChatInfo.Group && newCInfo is ChatInfo.Group && newCInfo.groupChatScope != null) {
newCInfo = newCInfo.copy(groupInfo = newCInfo.groupInfo, groupChatScope = null)
}
chats[i] = chats[i].copy(chatInfo = newCInfo)
}
@ -385,7 +364,7 @@ object ChatModel {
updateContact(rhId, updatedContact)
}
suspend fun updateGroup(rhId: Long?, groupInfo: GroupInfo) = updateChat(rhId, ChatInfo.Group(groupInfo, groupChatScope = null))
suspend fun updateGroup(rhId: Long?, groupInfo: GroupInfo) = updateChat(rhId, ChatInfo.Group(groupInfo))
private suspend fun updateChat(rhId: Long?, cInfo: ChatInfo, addMissing: Boolean = true) {
if (hasChat(rhId, cInfo.id)) {
@ -437,20 +416,16 @@ object ChatModel {
}
suspend fun addChatItem(rhId: Long?, cInfo: ChatInfo, cItem: ChatItem) {
// updates membersRequireAttention
updateChatInfo(rhId, cInfo)
// mark chat non deleted
if (cInfo is ChatInfo.Direct && cInfo.chatDeleted) {
val updatedContact = cInfo.contact.copy(chatDeleted = false)
updateContact(rhId, updatedContact)
}
// update chat list
// update previews
val i = getChatIndex(rhId, cInfo.id)
val chat: Chat
if (i >= 0) {
chat = chatsContext.chats[i]
// update preview (for chat from main scope to show new items for invitee in pending status)
if (cInfo.groupChatScope() == null || cInfo.groupInfo_?.membership?.memberPending == true) {
chat = chats[i]
val newPreviewItem = when (cInfo) {
is ChatInfo.Group -> {
val currentPreviewItem = chat.chatItems.firstOrNull()
@ -464,37 +439,32 @@ object ChatModel {
cItem
}
}
else -> cItem
}
val wasUnread = chat.unreadTag
chatsContext.chats[i] = chat.copy(
chats[i] = chat.copy(
chatItems = arrayListOf(newPreviewItem),
chatStats =
if (cItem.meta.itemStatus is CIStatus.RcvNew) {
increaseUnreadCounter(rhId, currentUser.value!!)
chat.chatStats.copy(unreadCount = chat.chatStats.unreadCount + 1, unreadMentions = if (cItem.meta.userMention) chat.chatStats.unreadMentions + 1 else chat.chatStats.unreadMentions)
} else
}
else
chat.chatStats
)
updateChatTagReadInPrimaryContext(chatsContext.chats[i], wasUnread)
}
// pop chat
updateChatTagReadNoContentTag(chats[i], wasUnread)
if (appPlatform.isDesktop && cItem.chatDir.sent) {
reorderChat(chatsContext.chats[i], 0)
reorderChat(chats[i], 0)
} else {
popChatCollector.throttlePopChat(chat.remoteHostId, chat.id, currentPosition = i)
}
} else {
if (cInfo.groupChatScope() == null) {
addChat(Chat(remoteHostId = rhId, chatInfo = cInfo, chatItems = arrayListOf(cItem)))
} else {
addChat(Chat(remoteHostId = rhId, chatInfo = cInfo, chatItems = emptyList()))
}
}
// add to current scope
withContext(Dispatchers.Main) {
if (chatItemBelongsToScope(cInfo, cItem)) {
// add to current chat
if (chatId.value == cInfo.id) {
// Prevent situation when chat item already in the list received from backend
if (chatItems.value.none { it.id == cItem.id }) {
if (chatItems.value.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
@ -507,28 +477,11 @@ object ChatModel {
}
}
private fun chatItemBelongsToScope(cInfo: ChatInfo, cItem: ChatItem): Boolean =
when (secondaryContextFilter) {
null ->
chatId.value == cInfo.id && cInfo.groupChatScope() == null
is SecondaryContextFilter.GroupChatScopeContext -> {
val cInfoScope = cInfo.groupChatScope()
if (cInfoScope != null) {
chatId.value == cInfo.id && sameChatScope(cInfoScope, secondaryContextFilter.groupScopeInfo.toChatScope())
} else {
false
}
}
is SecondaryContextFilter.MsgContentTagContext ->
chatId.value == cInfo.id && cItem.isReport
}
suspend fun upsertChatItem(rhId: Long?, cInfo: ChatInfo, cItem: ChatItem): Boolean {
var itemAdded = false
// update chat list
if (cInfo.groupChatScope() == null) {
// update previews
val i = getChatIndex(rhId, cInfo.id)
val chat: Chat
val res: Boolean
if (i >= 0) {
chat = chats[i]
val pItem = chat.chatItems.lastOrNull()
@ -536,59 +489,60 @@ object ChatModel {
chats[i] = chat.copy(chatItems = arrayListOf(cItem))
if (pItem.isRcvNew && !cItem.isRcvNew) {
// status changed from New to Read, update counter
decreaseCounterInPrimaryContext(rhId, cInfo.id)
decreaseCounterInChatNoContentTag(rhId, cInfo.id)
}
}
res = false
} else {
addChat(Chat(remoteHostId = rhId, chatInfo = cInfo, chatItems = arrayListOf(cItem)))
itemAdded = true
res = true
}
}
// update current scope
withContext(Dispatchers.Main) {
if (chatItemBelongsToScope(cInfo, cItem)) {
return withContext(Dispatchers.Main) {
// update current chat
if (chatId.value == cInfo.id) {
if (cItem.isDeletedContent || cItem.meta.itemDeleted != null) {
AudioPlayer.stop(cItem)
}
val items = chatItems.value
val itemIndex = items.indexOfFirst { it.id == cItem.id }
if (itemIndex >= 0) {
val oldStatus = items[itemIndex].meta.itemStatus
val newStatus = cItem.meta.itemStatus
val ci = if (shouldKeepOldSndCIStatus(oldStatus, newStatus)) {
cItem.copy(meta = cItem.meta.copy(itemStatus = oldStatus))
items[itemIndex] = cItem
false
} else {
val status = chatItemStatuses.remove(cItem.id)
val ci = if (status != null && cItem.meta.itemStatus is CIStatus.SndNew) {
cItem.copy(meta = cItem.meta.copy(itemStatus = status))
} else {
cItem
}
items[itemIndex] = ci
addToChatItems(ci)
true
}
} else {
addToChatItems(cItem)
itemAdded = true
res
}
}
}
return itemAdded
}
suspend fun updateChatItem(cInfo: ChatInfo, cItem: ChatItem, status: CIStatus? = null, atIndex: Int? = null) {
withContext(Dispatchers.Main) {
if (chatItemBelongsToScope(cInfo, cItem)) {
if (chatId.value == cInfo.id) {
val items = chatItems.value
val itemIndex = atIndex ?: items.indexOfFirst { it.id == cItem.id }
if (itemIndex >= 0) {
items[itemIndex] = cItem
}
} else if (status != null) {
chatItemStatuses[cItem.id] = status
}
}
}
fun removeChatItem(rhId: Long?, cInfo: ChatInfo, cItem: ChatItem) {
// update chat list
if (cInfo.groupChatScope() == null) {
if (cItem.isRcvNew) {
decreaseCounterInPrimaryContext(rhId, cInfo.id)
decreaseCounterInChatNoContentTag(rhId, cInfo.id)
}
// update preview
// update previews
val i = getChatIndex(rhId, cInfo.id)
val chat: Chat
if (i >= 0) {
@ -598,9 +552,8 @@ object ChatModel {
chats[i] = chat.copy(chatItems = arrayListOf(ChatItem.deletedItemDummy))
}
}
}
// remove from current scope
if (chatItemBelongsToScope(cInfo, cItem)) {
// remove from current chat
if (chatId.value == cInfo.id) {
chatItems.removeAllAndNotify {
// We delete taking into account meta.createdAt to make sure we will not be in situation when two items with the same id will be deleted
// (it can happen if already deleted chat item in backend still in the list and new one came with the same (re-used) chat item id)
@ -633,7 +586,7 @@ object ChatModel {
Log.d(TAG, "exiting removeMemberItems")
return
}
val cInfo = ChatInfo.Group(groupInfo, groupChatScope = null) // TODO [knocking] review
val cInfo = ChatInfo.Group(groupInfo)
if (chatId.value == groupInfo.id) {
for (i in 0 until chatItems.value.size) {
val updatedItem = removedUpdatedItem(chatItems.value[i])
@ -664,6 +617,7 @@ object ChatModel {
}
// clear current chat
if (chatId.value == cInfo.id) {
chatItemStatuses.clear()
chatItems.clearAndNotify()
}
}
@ -734,7 +688,7 @@ object ChatModel {
chats[chatIdx] = chat.copy(
chatStats = chat.chatStats.copy(unreadCount = unreadCount, unreadMentions = unreadMentions)
)
updateChatTagReadInPrimaryContext(chats[chatIdx], wasUnread)
updateChatTagReadNoContentTag(chats[chatIdx], wasUnread)
}
}
}
@ -775,9 +729,9 @@ object ChatModel {
return markedRead to mentionsMarkedRead
}
private fun decreaseCounterInPrimaryContext(rhId: Long?, chatId: ChatId) {
private fun decreaseCounterInChatNoContentTag(rhId: Long?, chatId: ChatId) {
// updates anything only in main ChatView, not GroupReportsView or anything else from the future
if (secondaryContextFilter != null) return
if (contentTag != null) return
val chatIndex = getChatIndex(rhId, chatId)
if (chatIndex == -1) return
@ -791,7 +745,7 @@ object ChatModel {
unreadCount = unreadCount,
)
)
updateChatTagReadInPrimaryContext(chats[chatIndex], wasUnread)
updateChatTagReadNoContentTag(chats[chatIndex], wasUnread)
}
fun removeChat(rhId: Long?, id: String) {
@ -860,16 +814,16 @@ object ChatModel {
}
fun increaseUnreadCounter(rhId: Long?, user: UserLike) {
changeUnreadCounterInPrimaryContext(rhId, user, 1)
changeUnreadCounterNoContentTag(rhId, user, 1)
}
fun decreaseUnreadCounter(rhId: Long?, user: UserLike, by: Int = 1) {
changeUnreadCounterInPrimaryContext(rhId, user, -by)
changeUnreadCounterNoContentTag(rhId, user, -by)
}
private fun changeUnreadCounterInPrimaryContext(rhId: Long?, user: UserLike, by: Int) {
private fun changeUnreadCounterNoContentTag(rhId: Long?, user: UserLike, by: Int) {
// updates anything only in main ChatView, not GroupReportsView or anything else from the future
if (secondaryContextFilter != null) return
if (contentTag != null) return
val i = users.indexOfFirst { it.user.userId == user.userId && it.user.remoteHostId == rhId }
if (i != -1) {
@ -877,9 +831,9 @@ object ChatModel {
}
}
fun updateChatTagReadInPrimaryContext(chat: Chat, wasUnread: Boolean) {
fun updateChatTagReadNoContentTag(chat: Chat, wasUnread: Boolean) {
// updates anything only in main ChatView, not GroupReportsView or anything else from the future
if (secondaryContextFilter != null) return
if (contentTag != null) return
val tags = chat.chatInfo.chatTags ?: return
val nowUnread = chat.unreadTag
@ -889,21 +843,21 @@ object ChatModel {
unreadTags[tag] = (unreadTags[tag] ?: 0) + 1
}
} else if (!nowUnread && wasUnread) {
markChatTagReadInPrimaryContext_(chat, tags)
markChatTagReadNoContentTag_(chat, tags)
}
}
fun markChatTagRead(chat: Chat) {
if (chat.unreadTag) {
chat.chatInfo.chatTags?.let { tags ->
markChatTagReadInPrimaryContext_(chat, tags)
markChatTagReadNoContentTag_(chat, tags)
}
}
}
private fun markChatTagReadInPrimaryContext_(chat: Chat, tags: List<Long>) {
private fun markChatTagReadNoContentTag_(chat: Chat, tags: List<Long>) {
// updates anything only in main ChatView, not GroupReportsView or anything else from the future
if (secondaryContextFilter != null) return
if (contentTag != null) return
for (tag in tags) {
val count = unreadTags[tag]
@ -935,12 +889,12 @@ object ChatModel {
val wasReportsCount = chat.chatStats.reportsCount
val nowReportsCount = chats[i].chatStats.reportsCount
val by = if (wasReportsCount == 0 && nowReportsCount > 0) 1 else if (wasReportsCount > 0 && nowReportsCount == 0) -1 else 0
changeGroupReportsTagInPrimaryContext(by)
changeGroupReportsTagNoContentTag(by)
}
}
private fun changeGroupReportsTagInPrimaryContext(by: Int = 0) {
if (by == 0 || secondaryContextFilter != null) return
private fun changeGroupReportsTagNoContentTag(by: Int = 0) {
if (by == 0 || contentTag != null) return
presetTags[PresetTagKind.GROUP_REPORTS] = kotlin.math.max(0, (presetTags[PresetTagKind.GROUP_REPORTS] ?: 0) + by)
clearActiveChatFilterIfNeeded()
}
@ -1145,28 +1099,6 @@ enum class ChatType(val type: String) {
ContactConnection(":");
}
sealed class GroupChatScope {
class MemberSupport(val groupMemberId_: Long?): GroupChatScope()
}
fun sameChatScope(scope1: GroupChatScope, scope2: GroupChatScope) =
scope1 is GroupChatScope.MemberSupport
&& scope2 is GroupChatScope.MemberSupport
&& scope1.groupMemberId_ == scope2.groupMemberId_
@Serializable
sealed class GroupChatScopeInfo {
@Serializable @SerialName("memberSupport") data class MemberSupport(val groupMember_: GroupMember?) : GroupChatScopeInfo()
fun toChatScope(): GroupChatScope =
when (this) {
is MemberSupport -> when (groupMember_) {
null -> GroupChatScope.MemberSupport(groupMemberId_ = null)
else -> GroupChatScope.MemberSupport(groupMemberId_ = groupMember_.groupMemberId)
}
}
}
@Serializable
data class User(
val remoteHostId: Long?,
@ -1272,6 +1204,8 @@ interface SomeChat {
val apiId: Long
val ready: Boolean
val chatDeleted: Boolean
val userCantSendReason: Pair<String, String?>?
val sendMsgEnabled: Boolean
val incognito: Boolean
fun featureEnabled(feature: ChatFeature): Boolean
val timedMessagesTTL: Int?
@ -1303,16 +1237,6 @@ data class Chat(
val id: String get() = chatInfo.id
val supportUnreadCount: Int get() = when (chatInfo) {
is ChatInfo.Group ->
if (chatInfo.groupInfo.canModerate) {
chatInfo.groupInfo.membersRequireAttention
} else {
chatInfo.groupInfo.membership.supportChat?.unread ?: 0
}
else -> 0
}
fun groupFeatureEnabled(feature: GroupFeature): Boolean =
if (chatInfo is ChatInfo.Group) {
chatInfo.groupInfo.groupFeatureEnabled(feature)
@ -1351,6 +1275,8 @@ sealed class ChatInfo: SomeChat, NamedChat {
override val apiId get() = contact.apiId
override val ready get() = contact.ready
override val chatDeleted get() = contact.chatDeleted
override val userCantSendReason get() = contact.userCantSendReason
override val sendMsgEnabled get() = contact.sendMsgEnabled
override val incognito get() = contact.incognito
override fun featureEnabled(feature: ChatFeature) = contact.featureEnabled(feature)
override val timedMessagesTTL: Int? get() = contact.timedMessagesTTL
@ -1368,13 +1294,15 @@ sealed class ChatInfo: SomeChat, NamedChat {
}
@Serializable @SerialName("group")
data class Group(val groupInfo: GroupInfo, val groupChatScope: GroupChatScopeInfo?): ChatInfo() {
data class Group(val groupInfo: GroupInfo): ChatInfo() {
override val chatType get() = ChatType.Group
override val localDisplayName get() = groupInfo.localDisplayName
override val id get() = groupInfo.id
override val apiId get() = groupInfo.apiId
override val ready get() = groupInfo.ready
override val chatDeleted get() = groupInfo.chatDeleted
override val userCantSendReason get() = groupInfo.userCantSendReason
override val sendMsgEnabled get() = groupInfo.sendMsgEnabled
override val incognito get() = groupInfo.incognito
override fun featureEnabled(feature: ChatFeature) = groupInfo.featureEnabled(feature)
override val timedMessagesTTL: Int? get() = groupInfo.timedMessagesTTL
@ -1386,7 +1314,7 @@ sealed class ChatInfo: SomeChat, NamedChat {
override val localAlias get() = groupInfo.localAlias
companion object {
val sampleData = Group(GroupInfo.sampleData, groupChatScope = null)
val sampleData = Group(GroupInfo.sampleData)
}
}
@ -1398,6 +1326,8 @@ sealed class ChatInfo: SomeChat, NamedChat {
override val apiId get() = noteFolder.apiId
override val ready get() = noteFolder.ready
override val chatDeleted get() = noteFolder.chatDeleted
override val userCantSendReason get() = noteFolder.userCantSendReason
override val sendMsgEnabled get() = noteFolder.sendMsgEnabled
override val incognito get() = noteFolder.incognito
override fun featureEnabled(feature: ChatFeature) = noteFolder.featureEnabled(feature)
override val timedMessagesTTL: Int? get() = noteFolder.timedMessagesTTL
@ -1421,6 +1351,8 @@ sealed class ChatInfo: SomeChat, NamedChat {
override val apiId get() = contactRequest.apiId
override val ready get() = contactRequest.ready
override val chatDeleted get() = contactRequest.chatDeleted
override val userCantSendReason get() = contactRequest.userCantSendReason
override val sendMsgEnabled get() = contactRequest.sendMsgEnabled
override val incognito get() = contactRequest.incognito
override fun featureEnabled(feature: ChatFeature) = contactRequest.featureEnabled(feature)
override val timedMessagesTTL: Int? get() = contactRequest.timedMessagesTTL
@ -1444,6 +1376,8 @@ sealed class ChatInfo: SomeChat, NamedChat {
override val apiId get() = contactConnection.apiId
override val ready get() = contactConnection.ready
override val chatDeleted get() = contactConnection.chatDeleted
override val userCantSendReason get() = contactConnection.userCantSendReason
override val sendMsgEnabled get() = contactConnection.sendMsgEnabled
override val incognito get() = contactConnection.incognito
override fun featureEnabled(feature: ChatFeature) = contactConnection.featureEnabled(feature)
override val timedMessagesTTL: Int? get() = contactConnection.timedMessagesTTL
@ -1472,6 +1406,8 @@ sealed class ChatInfo: SomeChat, NamedChat {
override val id get() = "?$apiId"
override val ready get() = false
override val chatDeleted get() = false
override val userCantSendReason get() = generalGetString(MR.strings.cant_send_message_generic) to null
override val sendMsgEnabled get() = false
override val incognito get() = false
override fun featureEnabled(feature: ChatFeature) = false
override val timedMessagesTTL: Int? get() = null
@ -1486,71 +1422,6 @@ sealed class ChatInfo: SomeChat, NamedChat {
}
}
val userCantSendReason: Pair<String, String?>?
get() {
when (this) {
is Direct -> {
// TODO [short links] this will have additional statuses for pending contact requests before they are accepted
if (contact.nextSendGrpInv) return null
if (!contact.active) return generalGetString(MR.strings.cant_send_message_contact_deleted) to null
if (!contact.sndReady) return generalGetString(MR.strings.cant_send_message_contact_not_ready) to null
if (contact.activeConn?.connectionStats?.ratchetSyncSendProhibited == true) return generalGetString(MR.strings.cant_send_message_contact_not_synchronized) to null
if (contact.activeConn?.connDisabled == true) return generalGetString(MR.strings.cant_send_message_contact_disabled) to null
return null
}
is Group -> {
if (groupInfo.membership.memberActive) {
when (groupChatScope) {
null -> {
if (groupInfo.membership.memberPending) {
return generalGetString(MR.strings.reviewed_by_admins) to generalGetString(MR.strings.observer_cant_send_message_desc)
}
if (groupInfo.membership.memberRole == GroupMemberRole.Observer) {
return generalGetString(MR.strings.observer_cant_send_message_title) to generalGetString(MR.strings.observer_cant_send_message_desc)
}
return null
}
is GroupChatScopeInfo.MemberSupport ->
if (groupChatScope.groupMember_ != null) {
if (
groupChatScope.groupMember_.versionRange.maxVersion < GROUP_KNOCKING_VERSION
&& !groupChatScope.groupMember_.memberPending
) {
return generalGetString(MR.strings.cant_send_message_member_has_old_version) to null
}
return null
} else {
return null
}
}
} else {
return when (groupInfo.membership.memberStatus) {
GroupMemberStatus.MemRejected -> generalGetString(MR.strings.cant_send_message_rejected) to null
GroupMemberStatus.MemGroupDeleted -> generalGetString(MR.strings.cant_send_message_group_deleted) to null
GroupMemberStatus.MemRemoved -> generalGetString(MR.strings.cant_send_message_mem_removed) to null
GroupMemberStatus.MemLeft -> generalGetString(MR.strings.cant_send_message_you_left) to null
else -> generalGetString(MR.strings.cant_send_message_generic) to null
}
}
}
is Local ->
return null
is ContactRequest ->
return generalGetString(MR.strings.cant_send_message_generic) to null
is ContactConnection ->
return generalGetString(MR.strings.cant_send_message_generic) to null
is InvalidJSON ->
return generalGetString(MR.strings.cant_send_message_generic) to null
}
}
val sendMsgEnabled get() = userCantSendReason == null
fun groupChatScope(): GroupChatScope? = when (this) {
is Group -> groupChatScope?.toChatScope()
else -> null
}
fun ntfsEnabled(ci: ChatItem): Boolean =
ntfsEnabled(ci.meta.userMention)
@ -1594,13 +1465,7 @@ sealed class ChatInfo: SomeChat, NamedChat {
is Direct -> contact.activeConn == null && contact.profile.contactLink != null && contact.active
else -> false
}
val groupInfo_: GroupInfo?
get() = when (this) {
is Group -> groupInfo
else -> null
}
}
@Serializable
sealed class NetworkStatus {
@ -1654,6 +1519,17 @@ data class Contact(
override val ready get() = activeConn?.connStatus == ConnStatus.Ready
val sndReady get() = ready || activeConn?.connStatus == ConnStatus.SndReady
val active get() = contactStatus == ContactStatus.Active
override val userCantSendReason: Pair<String, String?>?
get() {
// TODO [short links] this will have additional statuses for pending contact requests before they are accepted
if (nextSendGrpInv) return null
if (!active) return generalGetString(MR.strings.cant_send_message_contact_deleted) to null
if (!sndReady) return generalGetString(MR.strings.cant_send_message_contact_not_ready) to null
if (activeConn?.connectionStats?.ratchetSyncSendProhibited == true) return generalGetString(MR.strings.cant_send_message_contact_not_synchronized) to null
if (activeConn?.connDisabled == true) return generalGetString(MR.strings.cant_send_message_contact_disabled) to null
return null
}
override val sendMsgEnabled get() = userCantSendReason == null
val nextSendGrpInv get() = contactGroupMemberId != null && !contactGrpInvSent
override val incognito get() = contactConnIncognito
override fun featureEnabled(feature: ChatFeature) = when (feature) {
@ -1878,7 +1754,6 @@ data class GroupInfo (
override val updatedAt: Instant,
val chatTs: Instant?,
val uiThemes: ThemeModeOverrides? = null,
val membersRequireAttention: Int,
val chatTags: List<Long>,
val chatItemTTL: Long?,
override val localAlias: String,
@ -1888,6 +1763,23 @@ data class GroupInfo (
override val apiId get() = groupId
override val ready get() = membership.memberActive
override val chatDeleted get() = false
override val userCantSendReason: Pair<String, String?>? get() =
if (membership.memberActive) {
if (membership.memberRole == GroupMemberRole.Observer) {
generalGetString(MR.strings.observer_cant_send_message_title) to generalGetString(MR.strings.observer_cant_send_message_desc)
} else {
null
}
} else {
when (membership.memberStatus) {
GroupMemberStatus.MemRejected -> generalGetString(MR.strings.cant_send_message_rejected) to null
GroupMemberStatus.MemGroupDeleted -> generalGetString(MR.strings.cant_send_message_group_deleted) to null
GroupMemberStatus.MemRemoved -> generalGetString(MR.strings.cant_send_message_mem_removed) to null
GroupMemberStatus.MemLeft -> generalGetString(MR.strings.cant_send_message_you_left) to null
else -> generalGetString(MR.strings.cant_send_message_generic) to null
}
}
override val sendMsgEnabled get() = userCantSendReason == null
override val incognito get() = membership.memberIncognito
override fun featureEnabled(feature: ChatFeature) = when (feature) {
ChatFeature.TimedMessages -> fullGroupPreferences.timedMessages.on
@ -1905,7 +1797,7 @@ data class GroupInfo (
get() = membership.memberRole == GroupMemberRole.Owner && membership.memberCurrent
val canDelete: Boolean
get() = membership.memberRole == GroupMemberRole.Owner || !membership.memberCurrentOrPending
get() = membership.memberRole == GroupMemberRole.Owner || !membership.memberCurrent
val canAddMembers: Boolean
get() = membership.memberRole >= GroupMemberRole.Admin && membership.memberActive
@ -1940,7 +1832,6 @@ data class GroupInfo (
updatedAt = Clock.System.now(),
chatTs = Clock.System.now(),
uiThemes = null,
membersRequireAttention = 0,
chatTags = emptyList(),
localAlias = "",
chatItemTTL = null
@ -1958,8 +1849,7 @@ data class GroupProfile (
val description: String? = null,
override val image: String? = null,
override val localAlias: String = "",
val groupPreferences: GroupPreferences? = null,
val memberAdmission: GroupMemberAdmission? = null
val groupPreferences: GroupPreferences? = null
): NamedChat {
companion object {
val sampleData = GroupProfile(
@ -1969,27 +1859,6 @@ data class GroupProfile (
}
}
@Serializable
data class GroupMemberAdmission(
val review: MemberCriteria? = null,
) {
companion object {
val sampleData = GroupMemberAdmission(
review = null,
)
}
}
@Serializable
enum class MemberCriteria {
@SerialName("all") All;
val text: String
get() = when(this) {
MemberCriteria.All -> generalGetString(MR.strings.member_criteria_all)
}
}
@Serializable
data class BusinessChatInfo (
val chatType: BusinessChatType,
@ -2018,9 +1887,7 @@ data class GroupMember (
val memberProfile: LocalProfile,
val memberContactId: Long? = null,
val memberContactProfileId: Long,
var activeConn: Connection? = null,
val supportChat: GroupSupportChat? = null,
val memberChatVRange: VersionRange
var activeConn: Connection? = null
): NamedChat {
val id: String get() = "#$groupId @$groupMemberId"
val ready get() = activeConn?.connStatus == ConnStatus.Ready
@ -2079,7 +1946,6 @@ data class GroupMember (
GroupMemberStatus.MemUnknown -> false
GroupMemberStatus.MemInvited -> false
GroupMemberStatus.MemPendingApproval -> true
GroupMemberStatus.MemPendingReview -> true
GroupMemberStatus.MemIntroduced -> false
GroupMemberStatus.MemIntroInvited -> false
GroupMemberStatus.MemAccepted -> false
@ -2097,7 +1963,6 @@ data class GroupMember (
GroupMemberStatus.MemUnknown -> false
GroupMemberStatus.MemInvited -> false
GroupMemberStatus.MemPendingApproval -> false
GroupMemberStatus.MemPendingReview -> false
GroupMemberStatus.MemIntroduced -> true
GroupMemberStatus.MemIntroInvited -> true
GroupMemberStatus.MemAccepted -> true
@ -2107,15 +1972,6 @@ data class GroupMember (
GroupMemberStatus.MemCreator -> true
}
val memberPending: Boolean get() = when (this.memberStatus) {
GroupMemberStatus.MemPendingApproval -> true
GroupMemberStatus.MemPendingReview -> true
else -> false
}
val memberCurrentOrPending: Boolean get() =
memberCurrent || memberPending
fun canBeRemoved(groupInfo: GroupInfo): Boolean {
val userRole = groupInfo.membership.memberRole
return memberStatus != GroupMemberStatus.MemRemoved && memberStatus != GroupMemberStatus.MemLeft
@ -2134,8 +1990,6 @@ data class GroupMember (
&& userRole >= GroupMemberRole.Moderator && userRole >= memberRole && groupInfo.membership.memberActive
}
val versionRange: VersionRange = activeConn?.peerChatVRange ?: memberChatVRange
val memberIncognito = memberProfile.profileId != memberContactProfileId
companion object {
@ -2153,20 +2007,11 @@ data class GroupMember (
memberProfile = LocalProfile.sampleData,
memberContactId = 1,
memberContactProfileId = 1L,
activeConn = Connection.sampleData,
memberChatVRange = VersionRange(minVersion = 1, maxVersion = 15)
activeConn = Connection.sampleData
)
}
}
@Serializable
class GroupSupportChat (
val chatTs: Instant,
val unread: Int,
val memberAttention: Int,
val mentions: Int
)
@Serializable
data class GroupMemberSettings(val showMessages: Boolean) {}
@ -2223,7 +2068,6 @@ enum class GroupMemberStatus {
@SerialName("unknown") MemUnknown,
@SerialName("invited") MemInvited,
@SerialName("pending_approval") MemPendingApproval,
@SerialName("pending_review") MemPendingReview,
@SerialName("introduced") MemIntroduced,
@SerialName("intro-inv") MemIntroInvited,
@SerialName("accepted") MemAccepted,
@ -2240,7 +2084,6 @@ enum class GroupMemberStatus {
MemUnknown -> generalGetString(MR.strings.group_member_status_unknown)
MemInvited -> generalGetString(MR.strings.group_member_status_invited)
MemPendingApproval -> generalGetString(MR.strings.group_member_status_pending_approval)
MemPendingReview -> generalGetString(MR.strings.group_member_status_pending_review)
MemIntroduced -> generalGetString(MR.strings.group_member_status_introduced)
MemIntroInvited -> generalGetString(MR.strings.group_member_status_intro_invitation)
MemAccepted -> generalGetString(MR.strings.group_member_status_accepted)
@ -2258,7 +2101,6 @@ enum class GroupMemberStatus {
MemUnknown -> generalGetString(MR.strings.group_member_status_unknown_short)
MemInvited -> generalGetString(MR.strings.group_member_status_invited)
MemPendingApproval -> generalGetString(MR.strings.group_member_status_pending_approval_short)
MemPendingReview -> generalGetString(MR.strings.group_member_status_pending_review_short)
MemIntroduced -> generalGetString(MR.strings.group_member_status_connecting)
MemIntroInvited -> generalGetString(MR.strings.group_member_status_connecting)
MemAccepted -> generalGetString(MR.strings.group_member_status_connecting)
@ -2313,6 +2155,8 @@ class NoteFolder(
override val apiId get() = noteFolderId
override val chatDeleted get() = false
override val ready get() = true
override val userCantSendReason: Pair<String, String?>? = null
override val sendMsgEnabled get() = true
override val incognito get() = false
override fun featureEnabled(feature: ChatFeature) = feature == ChatFeature.Voice
override val timedMessagesTTL: Int? get() = null
@ -2348,6 +2192,8 @@ class UserContactRequest (
override val apiId get() = contactRequestId
override val chatDeleted get() = false
override val ready get() = true
override val userCantSendReason = generalGetString(MR.strings.cant_send_message_generic) to null
override val sendMsgEnabled get() = false
override val incognito get() = false
override fun featureEnabled(feature: ChatFeature) = false
override val timedMessagesTTL: Int? get() = null
@ -2386,6 +2232,8 @@ class PendingContactConnection(
override val apiId get() = pccConnId
override val chatDeleted get() = false
override val ready get() = false
override val userCantSendReason = generalGetString(MR.strings.cant_send_message_generic) to null
override val sendMsgEnabled get() = false
override val incognito get() = customUserProfileId != null
override fun featureEnabled(feature: ChatFeature) = false
override val timedMessagesTTL: Int? get() = null
@ -2603,16 +2451,11 @@ data class ChatItem (
is CIContent.RcvGroupFeature,
is CIContent.SndGroupFeature -> CIMergeCategory.ChatFeature
is CIContent.RcvGroupEventContent -> when (content.rcvGroupEvent) {
is RcvGroupEvent.UserRole,
is RcvGroupEvent.UserDeleted,
is RcvGroupEvent.GroupDeleted,
is RcvGroupEvent.MemberCreatedContact,
is RcvGroupEvent.NewMemberPendingReview ->
null
is RcvGroupEvent.UserRole, is RcvGroupEvent.UserDeleted, is RcvGroupEvent.GroupDeleted, is RcvGroupEvent.MemberCreatedContact -> null
else -> CIMergeCategory.RcvGroupEvent
}
is CIContent.SndGroupEventContent -> when (content.sndGroupEvent) {
is SndGroupEvent.UserRole, is SndGroupEvent.UserLeft, is SndGroupEvent.MemberAccepted, is SndGroupEvent.UserPendingReview -> null
is SndGroupEvent.UserRole, is SndGroupEvent.UserLeft -> null
else -> CIMergeCategory.SndGroupEvent
}
else -> {
@ -2683,8 +2526,6 @@ data class ChatItem (
is CIContent.RcvGroupEventContent -> when (content.rcvGroupEvent) {
is RcvGroupEvent.MemberAdded -> false
is RcvGroupEvent.MemberConnected -> false
is RcvGroupEvent.MemberAccepted -> false
is RcvGroupEvent.UserAccepted -> false
is RcvGroupEvent.MemberLeft -> false
is RcvGroupEvent.MemberRole -> false
is RcvGroupEvent.MemberBlocked -> false
@ -2696,7 +2537,6 @@ data class ChatItem (
is RcvGroupEvent.InvitedViaGroupLink -> false
is RcvGroupEvent.MemberCreatedContact -> false
is RcvGroupEvent.MemberProfileUpdated -> false
is RcvGroupEvent.NewMemberPendingReview -> true
}
is CIContent.SndGroupEventContent -> false
is CIContent.RcvConnEventContent -> false
@ -2878,11 +2718,6 @@ data class ChatItem (
}
}
sealed class SecondaryContextFilter {
class GroupChatScopeContext(val groupScopeInfo: GroupChatScopeInfo): SecondaryContextFilter()
class MsgContentTagContext(val contentTag: MsgContentTag): SecondaryContextFilter()
}
fun MutableState<SnapshotStateList<Chat>>.add(index: Int, elem: Chat) {
value = SnapshotStateList<Chat>().apply { addAll(value); add(index, elem) }
}
@ -3141,19 +2976,6 @@ sealed class CIStatus {
@Serializable @SerialName("rcvRead") class RcvRead: CIStatus()
@Serializable @SerialName("invalid") class Invalid(val text: String): CIStatus()
// as in corresponds to SENT response from agent
fun isSent(): Boolean = when(this) {
is SndNew -> false
is SndSent -> true
is SndRcvd -> false
is SndErrorAuth -> true
is CISSndError -> true
is SndWarning -> true
is RcvNew -> false
is RcvRead -> false
is Invalid -> false
}
fun statusIcon(
primaryColor: Color,
metaColor: Color = CurrentColors.value.colors.secondary,
@ -3193,13 +3015,6 @@ sealed class CIStatus {
}
}
fun shouldKeepOldSndCIStatus(oldStatus: CIStatus, newStatus: CIStatus): Boolean =
when {
oldStatus is CIStatus.SndRcvd && newStatus !is CIStatus.SndRcvd -> true
oldStatus.isSent() && newStatus is CIStatus.SndNew -> true
else -> false
}
@Serializable
sealed class SndError {
@Serializable @SerialName("auth") class Auth: SndError()
@ -4338,8 +4153,6 @@ sealed class RcvDirectEvent() {
sealed class RcvGroupEvent() {
@Serializable @SerialName("memberAdded") class MemberAdded(val groupMemberId: Long, val profile: Profile): RcvGroupEvent()
@Serializable @SerialName("memberConnected") class MemberConnected(): RcvGroupEvent()
@Serializable @SerialName("memberAccepted") class MemberAccepted(val groupMemberId: Long, val profile: Profile): RcvGroupEvent()
@Serializable @SerialName("userAccepted") class UserAccepted(): RcvGroupEvent()
@Serializable @SerialName("memberLeft") class MemberLeft(): RcvGroupEvent()
@Serializable @SerialName("memberRole") class MemberRole(val groupMemberId: Long, val profile: Profile, val role: GroupMemberRole): RcvGroupEvent()
@Serializable @SerialName("memberBlocked") class MemberBlocked(val groupMemberId: Long, val profile: Profile, val blocked: Boolean): RcvGroupEvent()
@ -4351,13 +4164,10 @@ sealed class RcvGroupEvent() {
@Serializable @SerialName("invitedViaGroupLink") class InvitedViaGroupLink(): RcvGroupEvent()
@Serializable @SerialName("memberCreatedContact") class MemberCreatedContact(): RcvGroupEvent()
@Serializable @SerialName("memberProfileUpdated") class MemberProfileUpdated(val fromProfile: Profile, val toProfile: Profile): RcvGroupEvent()
@Serializable @SerialName("newMemberPendingReview") class NewMemberPendingReview(): RcvGroupEvent()
val text: String get() = when (this) {
is MemberAdded -> String.format(generalGetString(MR.strings.rcv_group_event_member_added), profile.profileViewName)
is MemberConnected -> generalGetString(MR.strings.rcv_group_event_member_connected)
is MemberAccepted -> String.format(generalGetString(MR.strings.rcv_group_event_member_accepted), profile.profileViewName)
is UserAccepted -> generalGetString(MR.strings.rcv_group_event_user_accepted)
is MemberLeft -> generalGetString(MR.strings.rcv_group_event_member_left)
is MemberRole -> String.format(generalGetString(MR.strings.rcv_group_event_changed_member_role), profile.profileViewName, role.text)
is MemberBlocked -> if (blocked) {
@ -4373,7 +4183,6 @@ sealed class RcvGroupEvent() {
is InvitedViaGroupLink -> generalGetString(MR.strings.rcv_group_event_invited_via_your_group_link)
is MemberCreatedContact -> generalGetString(MR.strings.rcv_group_event_member_created_contact)
is MemberProfileUpdated -> profileUpdatedText(fromProfile, toProfile)
is NewMemberPendingReview -> generalGetString(MR.strings.rcv_group_event_new_member_pending_review)
}
private fun profileUpdatedText(from: Profile, to: Profile): String =
@ -4398,8 +4207,6 @@ sealed class SndGroupEvent() {
@Serializable @SerialName("memberDeleted") class MemberDeleted(val groupMemberId: Long, val profile: Profile): SndGroupEvent()
@Serializable @SerialName("userLeft") class UserLeft(): SndGroupEvent()
@Serializable @SerialName("groupUpdated") class GroupUpdated(val groupProfile: GroupProfile): SndGroupEvent()
@Serializable @SerialName("memberAccepted") class MemberAccepted(val groupMemberId: Long, val profile: Profile): SndGroupEvent()
@Serializable @SerialName("userPendingReview") class UserPendingReview(): SndGroupEvent()
val text: String get() = when (this) {
is MemberRole -> String.format(generalGetString(MR.strings.snd_group_event_changed_member_role), profile.profileViewName, role.text)
@ -4412,8 +4219,6 @@ sealed class SndGroupEvent() {
is MemberDeleted -> String.format(generalGetString(MR.strings.snd_group_event_member_deleted), profile.profileViewName)
is UserLeft -> generalGetString(MR.strings.snd_group_event_user_left)
is GroupUpdated -> generalGetString(MR.strings.snd_group_event_group_profile_updated)
is MemberAccepted -> generalGetString(MR.strings.snd_group_event_member_accepted)
is UserPendingReview -> generalGetString(MR.strings.snd_group_event_user_pending_review)
}
}

View file

@ -58,9 +58,6 @@ typealias ChatCtrl = Long
// version range that supports establishing direct connection with a group member (xGrpDirectInvVRange in core)
val CREATE_MEMBER_CONTACT_VERSION = 2
// support group knocking (MsgScope)
val GROUP_KNOCKING_VERSION = 15
enum class CallOnLockScreen {
DISABLE,
SHOW,
@ -166,7 +163,6 @@ class AppPreferences {
val oneHandUICardShown = mkBoolPreference(SHARED_PREFS_ONE_HAND_UI_CARD_SHOWN, false)
val addressCreationCardShown = mkBoolPreference(SHARED_PREFS_ADDRESS_CREATION_CARD_SHOWN, false)
val showMuteProfileAlert = mkBoolPreference(SHARED_PREFS_SHOW_MUTE_PROFILE_ALERT, true)
val showReportsInSupportChatAlert = mkBoolPreference(SHARED_PREFS_SHOW_REPORTS_IN_SUPPORT_CHAT_ALERT, true)
val appLanguage = mkStrPreference(SHARED_PREFS_APP_LANGUAGE, null)
val appUpdateChannel = mkEnumPreference(SHARED_PREFS_APP_UPDATE_CHANNEL, AppUpdatesChannel.DISABLED) { AppUpdatesChannel.entries.firstOrNull { it.name == this } }
val appSkippedUpdate = mkStrPreference(SHARED_PREFS_APP_SKIPPED_UPDATE, "")
@ -249,7 +245,6 @@ class AppPreferences {
liveMessageAlertShown to false,
showHiddenProfilesNotice to true,
showMuteProfileAlert to true,
showReportsInSupportChatAlert to true,
showDeleteConversationNotice to true,
showDeleteContactNotice to true,
)
@ -420,7 +415,6 @@ class AppPreferences {
private const val SHARED_PREFS_ONE_HAND_UI_CARD_SHOWN = "OneHandUICardShown"
private const val SHARED_PREFS_ADDRESS_CREATION_CARD_SHOWN = "AddressCreationCardShown"
private const val SHARED_PREFS_SHOW_MUTE_PROFILE_ALERT = "ShowMuteProfileAlert"
private const val SHARED_PREFS_SHOW_REPORTS_IN_SUPPORT_CHAT_ALERT = "ShowReportsInSupportChatAlert"
private const val SHARED_PREFS_STORE_DB_PASSPHRASE = "StoreDBPassphrase"
private const val SHARED_PREFS_INITIAL_RANDOM_DB_PASSPHRASE = "InitialRandomDBPassphrase"
private const val SHARED_PREFS_ENCRYPTED_DB_PASSPHRASE = "EncryptedDBPassphrase"
@ -888,8 +882,8 @@ object ChatController {
return null
}
suspend fun apiGetChat(rh: Long?, type: ChatType, id: Long, scope: GroupChatScope?, contentTag: MsgContentTag? = null, pagination: ChatPagination, search: String = ""): Pair<Chat, NavigationInfo>? {
val r = sendCmd(rh, CC.ApiGetChat(type, id, scope, contentTag, pagination, search))
suspend fun apiGetChat(rh: Long?, type: ChatType, id: Long, contentTag: MsgContentTag? = null, pagination: ChatPagination, search: String = ""): Pair<Chat, NavigationInfo>? {
val r = sendCmd(rh, CC.ApiGetChat(type, id, contentTag, pagination, search))
if (r is API.Result && r.res is CR.ApiChat) return if (rh == null) r.res.chat to r.res.navInfo else r.res.chat.copy(remoteHostId = rh) to r.res.navInfo
Log.e(TAG, "apiGetChat bad response: ${r.responseType} ${r.details}")
val e = (r as? API.Error)?.err
@ -923,8 +917,8 @@ object ChatController {
suspend fun apiReorderChatTags(rh: Long?, tagIds: List<Long>) = sendCommandOkResp(rh, CC.ApiReorderChatTags(tagIds))
suspend fun apiSendMessages(rh: Long?, type: ChatType, id: Long, scope: GroupChatScope?, live: Boolean = false, ttl: Int? = null, composedMessages: List<ComposedMessage>): List<AChatItem>? {
val cmd = CC.ApiSendMessages(type, id, scope, live, ttl, composedMessages)
suspend fun apiSendMessages(rh: Long?, type: ChatType, id: Long, live: Boolean = false, ttl: Int? = null, composedMessages: List<ComposedMessage>): List<AChatItem>? {
val cmd = CC.ApiSendMessages(type, id, live, ttl, composedMessages)
return processSendMessageCmd(rh, cmd)
}
@ -975,27 +969,27 @@ object ChatController {
return null
}
suspend fun apiGetChatItemInfo(rh: Long?, type: ChatType, id: Long, scope: GroupChatScope?, itemId: Long): ChatItemInfo? {
val r = sendCmd(rh, CC.ApiGetChatItemInfo(type, id, scope, itemId))
suspend fun apiGetChatItemInfo(rh: Long?, type: ChatType, id: Long, itemId: Long): ChatItemInfo? {
val r = sendCmd(rh, CC.ApiGetChatItemInfo(type, id, itemId))
if (r is API.Result && r.res is CR.ApiChatItemInfo) return r.res.chatItemInfo
apiErrorAlert("apiGetChatItemInfo", generalGetString(MR.strings.error_loading_details), r)
return null
}
suspend fun apiForwardChatItems(rh: Long?, toChatType: ChatType, toChatId: Long, toScope: GroupChatScope?, fromChatType: ChatType, fromChatId: Long, fromScope: GroupChatScope?, itemIds: List<Long>, ttl: Int?): List<ChatItem>? {
val cmd = CC.ApiForwardChatItems(toChatType, toChatId, toScope, fromChatType, fromChatId, fromScope, itemIds, ttl)
suspend fun apiForwardChatItems(rh: Long?, toChatType: ChatType, toChatId: Long, fromChatType: ChatType, fromChatId: Long, itemIds: List<Long>, ttl: Int?): List<ChatItem>? {
val cmd = CC.ApiForwardChatItems(toChatType, toChatId, fromChatType, fromChatId, itemIds, ttl)
return processSendMessageCmd(rh, cmd)?.map { it.chatItem }
}
suspend fun apiPlanForwardChatItems(rh: Long?, fromChatType: ChatType, fromChatId: Long, fromScope: GroupChatScope?, chatItemIds: List<Long>): CR.ForwardPlan? {
val r = sendCmd(rh, CC.ApiPlanForwardChatItems(fromChatType, fromChatId, fromScope, chatItemIds))
suspend fun apiPlanForwardChatItems(rh: Long?, fromChatType: ChatType, fromChatId: Long, chatItemIds: List<Long>): CR.ForwardPlan? {
val r = sendCmd(rh, CC.ApiPlanForwardChatItems(fromChatType, fromChatId, chatItemIds))
if (r is API.Result && r.res is CR.ForwardPlan) return r.res
apiErrorAlert("apiPlanForwardChatItems", generalGetString(MR.strings.error_forwarding_messages), r)
return null
}
suspend fun apiUpdateChatItem(rh: Long?, type: ChatType, id: Long, scope: GroupChatScope?, itemId: Long, updatedMessage: UpdatedMessage, live: Boolean = false): AChatItem? {
val r = sendCmd(rh, CC.ApiUpdateChatItem(type, id, scope, itemId, updatedMessage, live))
suspend fun apiUpdateChatItem(rh: Long?, type: ChatType, id: Long, itemId: Long, updatedMessage: UpdatedMessage, live: Boolean = false): AChatItem? {
val r = sendCmd(rh, CC.ApiUpdateChatItem(type, id, itemId, updatedMessage, live))
when {
r is API.Result && r.res is CR.ChatItemUpdated -> return r.res.chatItem
r is API.Result && r.res is CR.ChatItemNotChanged -> return r.res.chatItem
@ -1017,8 +1011,8 @@ object ChatController {
return null
}
suspend fun apiChatItemReaction(rh: Long?, type: ChatType, id: Long, scope: GroupChatScope?, itemId: Long, add: Boolean, reaction: MsgReaction): ChatItem? {
val r = sendCmd(rh, CC.ApiChatItemReaction(type, id, scope, itemId, add, reaction))
suspend fun apiChatItemReaction(rh: Long?, type: ChatType, id: Long, itemId: Long, add: Boolean, reaction: MsgReaction): ChatItem? {
val r = sendCmd(rh, CC.ApiChatItemReaction(type, id, itemId, add, reaction))
if (r is API.Result && r.res is CR.ChatItemReaction) return r.res.reaction.chatReaction.chatItem
Log.e(TAG, "apiUpdateChatItem bad response: ${r.responseType} ${r.details}")
return null
@ -1032,8 +1026,8 @@ object ChatController {
return null
}
suspend fun apiDeleteChatItems(rh: Long?, type: ChatType, id: Long, scope: GroupChatScope?, itemIds: List<Long>, mode: CIDeleteMode): List<ChatItemDeletion>? {
val r = sendCmd(rh, CC.ApiDeleteChatItem(type, id, scope, itemIds, mode))
suspend fun apiDeleteChatItems(rh: Long?, type: ChatType, id: Long, itemIds: List<Long>, mode: CIDeleteMode): List<ChatItemDeletion>? {
val r = sendCmd(rh, CC.ApiDeleteChatItem(type, id, itemIds, mode))
if (r is API.Result && r.res is CR.ChatItemsDeleted) return r.res.chatItemDeletions
Log.e(TAG, "apiDeleteChatItem bad response: ${r.responseType} ${r.details}")
return null
@ -1665,18 +1659,18 @@ object ChatController {
return null
}
suspend fun apiChatRead(rh: Long?, type: ChatType, id: Long, scope: GroupChatScope?): Boolean {
val r = sendCmd(rh, CC.ApiChatRead(type, id, scope))
suspend fun apiChatRead(rh: Long?, type: ChatType, id: Long): Boolean {
val r = sendCmd(rh, CC.ApiChatRead(type, id))
if (r.result is CR.CmdOk) return true
Log.e(TAG, "apiChatRead bad response: ${r.responseType} ${r.details}")
return false
}
suspend fun apiChatItemsRead(rh: Long?, type: ChatType, id: Long, scope: GroupChatScope?, itemIds: List<Long>): ChatInfo? {
val r = sendCmd(rh, CC.ApiChatItemsRead(type, id, scope, itemIds))
if (r is API.Result && r.res is CR.ItemsReadForChat) return r.res.chatInfo
suspend fun apiChatItemsRead(rh: Long?, type: ChatType, id: Long, itemIds: List<Long>): Boolean {
val r = sendCmd(rh, CC.ApiChatItemsRead(type, id, itemIds))
if (r.result is CR.CmdOk) return true
Log.e(TAG, "apiChatItemsRead bad response: ${r.responseType} ${r.details}")
return null
return false
}
suspend fun apiChatUnread(rh: Long?, type: ChatType, id: Long, unreadChat: Boolean): Boolean {
@ -1905,25 +1899,9 @@ object ChatController {
}
}
suspend fun apiAcceptMember(rh: Long?, groupId: Long, groupMemberId: Long, memberRole: GroupMemberRole): Pair<GroupInfo, GroupMember>? {
val r = sendCmd(rh, CC.ApiAcceptMember(groupId, groupMemberId, memberRole))
if (r is API.Result && r.res is CR.MemberAccepted) return r.res.groupInfo to r.res.member
if (!(networkErrorAlert(r))) {
apiErrorAlert("apiAcceptMember", generalGetString(MR.strings.error_accepting_member), r)
}
return null
}
suspend fun apiDeleteMemberSupportChat(rh: Long?, groupId: Long, groupMemberId: Long): Pair<GroupInfo, GroupMember>? {
val r = sendCmd(rh, CC.ApiDeleteMemberSupportChat(groupId, groupMemberId))
if (r is API.Result && r.res is CR.MemberSupportChatDeleted) return r.res.groupInfo to r.res.member
apiErrorAlert("apiDeleteMemberSupportChat", generalGetString(MR.strings.error_deleting_member_support_chat), r)
return null
}
suspend fun apiRemoveMembers(rh: Long?, groupId: Long, memberIds: List<Long>, withMessages: Boolean = false): Pair<GroupInfo, List<GroupMember>>? {
suspend fun apiRemoveMembers(rh: Long?, groupId: Long, memberIds: List<Long>, withMessages: Boolean = false): List<GroupMember>? {
val r = sendCmd(rh, CC.ApiRemoveMembers(groupId, memberIds, withMessages))
if (r is API.Result && r.res is CR.UserDeletedMembers) return r.res.groupInfo to r.res.members
if (r is API.Result && r.res is CR.UserDeletedMembers) return r.res.members
if (!(networkErrorAlert(r))) {
apiErrorAlert("apiRemoveMembers", generalGetString(MR.strings.error_removing_member), r)
}
@ -2442,8 +2420,10 @@ object ChatController {
}
}
withContext(Dispatchers.Main) {
if (cItem.isReport) {
chatModel.secondaryChatsContext.value?.addChatItem(rhId, cInfo, cItem)
}
}
} else if (cItem.isRcvNew && cInfo.ntfsEnabled(cItem)) {
withContext(Dispatchers.Main) {
chatModel.chatsContext.increaseUnreadCounter(rhId, r.user)
@ -2468,10 +2448,12 @@ object ChatController {
val cItem = chatItem.chatItem
if (!cItem.isDeletedContent && active(r.user)) {
withContext(Dispatchers.Main) {
chatModel.chatsContext.upsertChatItem(rhId, cInfo, cItem)
chatModel.chatsContext.updateChatItem(cInfo, cItem, status = cItem.meta.itemStatus)
}
withContext(Dispatchers.Main) {
chatModel.secondaryChatsContext.value?.upsertChatItem(rhId, cInfo, cItem)
if (cItem.isReport) {
chatModel.secondaryChatsContext.value?.updateChatItem(cInfo, cItem, status = cItem.meta.itemStatus)
}
}
}
}
@ -2483,10 +2465,12 @@ object ChatController {
chatModel.chatsContext.updateChatItem(r.reaction.chatInfo, r.reaction.chatReaction.chatItem)
}
withContext(Dispatchers.Main) {
if (r.reaction.chatReaction.chatItem.isReport) {
chatModel.secondaryChatsContext.value?.updateChatItem(r.reaction.chatInfo, r.reaction.chatReaction.chatItem)
}
}
}
}
is CR.ChatItemsDeleted -> {
if (!active(r.user)) {
r.chatItemDeletions.forEach { (deletedChatItem, toChatItem) ->
@ -2526,6 +2510,7 @@ object ChatController {
}
}
withContext(Dispatchers.Main) {
if (cItem.isReport) {
if (toChatItem == null) {
chatModel.secondaryChatsContext.value?.removeChatItem(rhId, cInfo, cItem)
} else {
@ -2533,10 +2518,6 @@ object ChatController {
}
}
}
r.chatItemDeletions.lastOrNull()?.deletedChatItem?.chatInfo?.let { updatedChatInfo ->
withContext(Dispatchers.Main) {
chatModel.chatsContext.updateChatInfo(rhId, updatedChatInfo)
}
}
}
is CR.GroupChatItemsDeleted -> {
@ -2594,13 +2575,6 @@ object ChatController {
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
}
}
is CR.MemberAcceptedByOther ->
if (active(r.user)) {
withContext(Dispatchers.Main) {
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
chatModel.chatsContext.updateGroup(rhId, r.groupInfo)
}
}
is CR.DeletedMemberUser -> // TODO update user member
if (active(r.user)) {
withContext(Dispatchers.Main) {
@ -2618,7 +2592,6 @@ object ChatController {
is CR.DeletedMember ->
if (active(r.user)) {
withContext(Dispatchers.Main) {
chatModel.chatsContext.updateGroup(rhId, r.groupInfo)
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.deletedMember)
if (r.withMessages) {
chatModel.chatsContext.removeMemberItems(rhId, r.deletedMember, byMember = r.byMember, r.groupInfo)
@ -2634,7 +2607,6 @@ object ChatController {
is CR.LeftMember ->
if (active(r.user)) {
withContext(Dispatchers.Main) {
chatModel.chatsContext.updateGroup(rhId, r.groupInfo)
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
}
withContext(Dispatchers.Main) {
@ -2683,16 +2655,6 @@ object ChatController {
withContext(Dispatchers.Main) {
chatModel.chatsContext.updateGroup(rhId, r.groupInfo)
}
if (
chatModel.chatId.value == r.groupInfo.id
&& ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)
&& chatModel.secondaryChatsContext.value?.secondaryContextFilter is SecondaryContextFilter.GroupChatScopeContext
) {
withContext(Dispatchers.Main) {
chatModel.secondaryChatsContext.value = null
}
ModalManager.end.closeModals()
}
}
is CR.JoinedGroupMember ->
if (active(r.user)) {
@ -3054,14 +3016,14 @@ object ChatController {
if (activeUser(rh, user)) {
val cInfo = aChatItem.chatInfo
val cItem = aChatItem.chatItem
withContext(Dispatchers.Main) { chatModel.chatsContext.upsertChatItem(rh, cInfo, cItem) }
withContext(Dispatchers.Main) {
chatModel.chatsContext.upsertChatItem(rh, cInfo, cItem)
}
withContext(Dispatchers.Main) {
if (cItem.isReport) {
chatModel.secondaryChatsContext.value?.upsertChatItem(rh, cInfo, cItem)
}
}
}
}
suspend fun groupChatItemsDeleted(rhId: Long?, r: CR.GroupChatItemsDeleted) {
if (!activeUser(rhId, r.user)) {
@ -3070,7 +3032,7 @@ object ChatController {
chatModel.users.addAll(users)
return
}
val cInfo = ChatInfo.Group(r.groupInfo, groupChatScope = null) // TODO [knocking] get scope from items?
val cInfo = ChatInfo.Group(r.groupInfo)
withContext(Dispatchers.Main) {
val chatsCtx = chatModel.chatsContext
r.chatItemIDs.forEach { itemId ->
@ -3125,12 +3087,12 @@ object ChatController {
if (!activeUser(rh, user)) {
notify()
} else {
val createdChat = withContext(Dispatchers.Main) {
chatModel.chatsContext.upsertChatItem(rh, cInfo, cItem)
}
val createdChat = withContext(Dispatchers.Main) { chatModel.chatsContext.upsertChatItem(rh, cInfo, cItem) }
withContext(Dispatchers.Main) {
if (cItem.content.msgContent is MsgContent.MCReport) {
chatModel.secondaryChatsContext.value?.upsertChatItem(rh, cInfo, cItem)
}
}
if (createdChat) {
notify()
} else if (cItem.content is CIContent.RcvCall && cItem.content.status == CICallStatus.Missed) {
@ -3334,9 +3296,9 @@ sealed class CC {
class ApiGetSettings(val settings: AppSettings): CC()
class ApiGetChatTags(val userId: Long): CC()
class ApiGetChats(val userId: Long): CC()
class ApiGetChat(val type: ChatType, val id: Long, val scope: GroupChatScope?, val contentTag: MsgContentTag?, val pagination: ChatPagination, val search: String = ""): CC()
class ApiGetChatItemInfo(val type: ChatType, val id: Long, val scope: GroupChatScope?, val itemId: Long): CC()
class ApiSendMessages(val type: ChatType, val id: Long, val scope: GroupChatScope?, val live: Boolean, val ttl: Int?, val composedMessages: List<ComposedMessage>): CC()
class ApiGetChat(val type: ChatType, val id: Long, val contentTag: MsgContentTag?, val pagination: ChatPagination, val search: String = ""): CC()
class ApiGetChatItemInfo(val type: ChatType, val id: Long, val itemId: Long): CC()
class ApiSendMessages(val type: ChatType, val id: Long, val live: Boolean, val ttl: Int?, val composedMessages: List<ComposedMessage>): CC()
class ApiCreateChatTag(val tag: ChatTagData): CC()
class ApiSetChatTags(val type: ChatType, val id: Long, val tagIds: List<Long>): CC()
class ApiDeleteChatTag(val tagId: Long): CC()
@ -3344,20 +3306,18 @@ sealed class CC {
class ApiReorderChatTags(val tagIds: List<Long>): CC()
class ApiCreateChatItems(val noteFolderId: Long, val composedMessages: List<ComposedMessage>): CC()
class ApiReportMessage(val groupId: Long, val chatItemId: Long, val reportReason: ReportReason, val reportText: String): CC()
class ApiUpdateChatItem(val type: ChatType, val id: Long, val scope: GroupChatScope?, val itemId: Long, val updatedMessage: UpdatedMessage, val live: Boolean): CC()
class ApiDeleteChatItem(val type: ChatType, val id: Long, val scope: GroupChatScope?, val itemIds: List<Long>, val mode: CIDeleteMode): CC()
class ApiUpdateChatItem(val type: ChatType, val id: Long, val itemId: Long, val updatedMessage: UpdatedMessage, val live: Boolean): CC()
class ApiDeleteChatItem(val type: ChatType, val id: Long, val itemIds: List<Long>, val mode: CIDeleteMode): CC()
class ApiDeleteMemberChatItem(val groupId: Long, val itemIds: List<Long>): CC()
class ApiArchiveReceivedReports(val groupId: Long): CC()
class ApiDeleteReceivedReports(val groupId: Long, val itemIds: List<Long>, val mode: CIDeleteMode): CC()
class ApiChatItemReaction(val type: ChatType, val id: Long, val scope: GroupChatScope?, val itemId: Long, val add: Boolean, val reaction: MsgReaction): CC()
class ApiChatItemReaction(val type: ChatType, val id: Long, val itemId: Long, val add: Boolean, val reaction: MsgReaction): CC()
class ApiGetReactionMembers(val userId: Long, val groupId: Long, val itemId: Long, val reaction: MsgReaction): CC()
class ApiPlanForwardChatItems(val fromChatType: ChatType, val fromChatId: Long, val fromScope: GroupChatScope?, val chatItemIds: List<Long>): CC()
class ApiForwardChatItems(val toChatType: ChatType, val toChatId: Long, val toScope: GroupChatScope?, val fromChatType: ChatType, val fromChatId: Long, val fromScope: GroupChatScope?, val itemIds: List<Long>, val ttl: Int?): CC()
class ApiPlanForwardChatItems(val fromChatType: ChatType, val fromChatId: Long, val chatItemIds: List<Long>): CC()
class ApiForwardChatItems(val toChatType: ChatType, val toChatId: Long, val fromChatType: ChatType, val fromChatId: Long, val itemIds: List<Long>, val ttl: Int?): CC()
class ApiNewGroup(val userId: Long, val incognito: Boolean, val groupProfile: GroupProfile): CC()
class ApiAddMember(val groupId: Long, val contactId: Long, val memberRole: GroupMemberRole): CC()
class ApiJoinGroup(val groupId: Long): CC()
class ApiAcceptMember(val groupId: Long, val groupMemberId: Long, val memberRole: GroupMemberRole): CC()
class ApiDeleteMemberSupportChat(val groupId: Long, val groupMemberId: Long): CC()
class ApiMembersRole(val groupId: Long, val memberIds: List<Long>, val memberRole: GroupMemberRole): CC()
class ApiBlockMembersForAll(val groupId: Long, val memberIds: List<Long>, val blocked: Boolean): CC()
class ApiRemoveMembers(val groupId: Long, val memberIds: List<Long>, val withMessages: Boolean): CC()
@ -3435,8 +3395,8 @@ sealed class CC {
class ApiGetNetworkStatuses(): CC()
class ApiAcceptContact(val incognito: Boolean, val contactReqId: Long): CC()
class ApiRejectContact(val contactReqId: Long): CC()
class ApiChatRead(val type: ChatType, val id: Long, val scope: GroupChatScope?): CC()
class ApiChatItemsRead(val type: ChatType, val id: Long, val scope: GroupChatScope?, val itemIds: List<Long>): CC()
class ApiChatRead(val type: ChatType, val id: Long): CC()
class ApiChatItemsRead(val type: ChatType, val id: Long, val itemIds: List<Long>): CC()
class ApiChatUnread(val type: ChatType, val id: Long, val unreadChat: Boolean): CC()
class ReceiveFile(val fileId: Long, val userApprovedRelays: Boolean, val encrypt: Boolean, val inline: Boolean?): CC()
class CancelFile(val fileId: Long): CC()
@ -3508,16 +3468,16 @@ sealed class CC {
} else {
" content=${contentTag.name.lowercase()}"
}
"/_get chat ${chatRef(type, id, scope)}$tag ${pagination.cmdString}" + (if (search == "") "" else " search=$search")
"/_get chat ${chatRef(type, id)}$tag ${pagination.cmdString}" + (if (search == "") "" else " search=$search")
}
is ApiGetChatItemInfo -> "/_get item info ${chatRef(type, id, scope)} $itemId"
is ApiGetChatItemInfo -> "/_get item info ${chatRef(type, id)} $itemId"
is ApiSendMessages -> {
val msgs = json.encodeToString(composedMessages)
val ttlStr = if (ttl != null) "$ttl" else "default"
"/_send ${chatRef(type, id, scope)} live=${onOff(live)} ttl=${ttlStr} json $msgs"
"/_send ${chatRef(type, id)} live=${onOff(live)} ttl=${ttlStr} json $msgs"
}
is ApiCreateChatTag -> "/_create tag ${json.encodeToString(tag)}"
is ApiSetChatTags -> "/_tags ${chatRef(type, id, scope = null)} ${tagIds.joinToString(",")}"
is ApiSetChatTags -> "/_tags ${chatRef(type, id)} ${tagIds.joinToString(",")}"
is ApiDeleteChatTag -> "/_delete tag $tagId"
is ApiUpdateChatTag -> "/_update tag $tagId ${json.encodeToString(tagData)}"
is ApiReorderChatTags -> "/_reorder tags ${tagIds.joinToString(",")}"
@ -3526,25 +3486,23 @@ sealed class CC {
"/_create *$noteFolderId json $msgs"
}
is ApiReportMessage -> "/_report #$groupId $chatItemId reason=${json.encodeToString(reportReason).trim('"')} $reportText"
is ApiUpdateChatItem -> "/_update item ${chatRef(type, id, scope)} $itemId live=${onOff(live)} ${updatedMessage.cmdString}"
is ApiDeleteChatItem -> "/_delete item ${chatRef(type, id, scope)} ${itemIds.joinToString(",")} ${mode.deleteMode}"
is ApiUpdateChatItem -> "/_update item ${chatRef(type, id)} $itemId live=${onOff(live)} ${updatedMessage.cmdString}"
is ApiDeleteChatItem -> "/_delete item ${chatRef(type, id)} ${itemIds.joinToString(",")} ${mode.deleteMode}"
is ApiDeleteMemberChatItem -> "/_delete member item #$groupId ${itemIds.joinToString(",")}"
is ApiArchiveReceivedReports -> "/_archive reports #$groupId"
is ApiDeleteReceivedReports -> "/_delete reports #$groupId ${itemIds.joinToString(",")} ${mode.deleteMode}"
is ApiChatItemReaction -> "/_reaction ${chatRef(type, id, scope)} $itemId ${onOff(add)} ${json.encodeToString(reaction)}"
is ApiChatItemReaction -> "/_reaction ${chatRef(type, id)} $itemId ${onOff(add)} ${json.encodeToString(reaction)}"
is ApiGetReactionMembers -> "/_reaction members $userId #$groupId $itemId ${json.encodeToString(reaction)}"
is ApiForwardChatItems -> {
val ttlStr = if (ttl != null) "$ttl" else "default"
"/_forward ${chatRef(toChatType, toChatId, toScope)} ${chatRef(fromChatType, fromChatId, fromScope)} ${itemIds.joinToString(",")} ttl=${ttlStr}"
"/_forward ${chatRef(toChatType, toChatId)} ${chatRef(fromChatType, fromChatId)} ${itemIds.joinToString(",")} ttl=${ttlStr}"
}
is ApiPlanForwardChatItems -> {
"/_forward plan ${chatRef(fromChatType, fromChatId, fromScope)} ${chatItemIds.joinToString(",")}"
"/_forward plan ${chatRef(fromChatType, fromChatId)} ${chatItemIds.joinToString(",")}"
}
is ApiNewGroup -> "/_group $userId incognito=${onOff(incognito)} ${json.encodeToString(groupProfile)}"
is ApiAddMember -> "/_add #$groupId $contactId ${memberRole.memberRole}"
is ApiJoinGroup -> "/_join #$groupId"
is ApiAcceptMember -> "/_accept member #$groupId $groupMemberId ${memberRole.memberRole}"
is ApiDeleteMemberSupportChat -> "/_delete member chat #$groupId $groupMemberId"
is ApiMembersRole -> "/_member role #$groupId ${memberIds.joinToString(",")} ${memberRole.memberRole}"
is ApiBlockMembersForAll -> "/_block #$groupId ${memberIds.joinToString(",")} blocked=${onOff(blocked)}"
is ApiRemoveMembers -> "/_remove #$groupId ${memberIds.joinToString(",")} messages=${onOff(withMessages)}"
@ -3568,13 +3526,13 @@ sealed class CC {
is ApiAcceptConditions -> "/_accept_conditions ${conditionsId} ${operatorIds.joinToString(",")}"
is APISetChatItemTTL -> "/_ttl $userId ${chatItemTTLStr(seconds)}"
is APIGetChatItemTTL -> "/_ttl $userId"
is APISetChatTTL -> "/_ttl $userId ${chatRef(chatType, id, scope = null)} ${chatItemTTLStr(seconds)}"
is APISetChatTTL -> "/_ttl $userId ${chatRef(chatType, id)} ${chatItemTTLStr(seconds)}"
is APISetNetworkConfig -> "/_network ${json.encodeToString(networkConfig)}"
is APIGetNetworkConfig -> "/network"
is APISetNetworkInfo -> "/_network info ${json.encodeToString(networkInfo)}"
is ReconnectServer -> "/reconnect $userId $server"
is ReconnectAllServers -> "/reconnect"
is APISetChatSettings -> "/_settings ${chatRef(type, id, scope = null)} ${json.encodeToString(chatSettings)}"
is APISetChatSettings -> "/_settings ${chatRef(type, id)} ${json.encodeToString(chatSettings)}"
is ApiSetMemberSettings -> "/_member settings #$groupId $groupMemberId ${json.encodeToString(memberSettings)}"
is APIContactInfo -> "/_info @$contactId"
is APIGroupMemberInfo -> "/_info #$groupId $groupMemberId"
@ -3596,8 +3554,8 @@ sealed class CC {
is APIConnectPlan -> "/_connect plan $userId $connLink"
is APIConnect -> "/_connect $userId incognito=${onOff(incognito)} ${connLink.connFullLink} ${connLink.connShortLink ?: ""}"
is ApiConnectContactViaAddress -> "/_connect contact $userId incognito=${onOff(incognito)} $contactId"
is ApiDeleteChat -> "/_delete ${chatRef(type, id, scope = null)} ${chatDeleteMode.cmdString}"
is ApiClearChat -> "/_clear chat ${chatRef(type, id, scope = null)}"
is ApiDeleteChat -> "/_delete ${chatRef(type, id)} ${chatDeleteMode.cmdString}"
is ApiClearChat -> "/_clear chat ${chatRef(type, id)}"
is ApiListContacts -> "/_contacts $userId"
is ApiUpdateProfile -> "/_profile $userId ${json.encodeToString(profile)}"
is ApiSetContactPrefs -> "/_set prefs @$contactId ${json.encodeToString(prefs)}"
@ -3622,9 +3580,9 @@ sealed class CC {
is ApiEndCall -> "/_call end @${contact.apiId}"
is ApiCallStatus -> "/_call status @${contact.apiId} ${callStatus.value}"
is ApiGetNetworkStatuses -> "/_network_statuses"
is ApiChatRead -> "/_read chat ${chatRef(type, id, scope)}"
is ApiChatItemsRead -> "/_read chat items ${chatRef(type, id, scope)} ${itemIds.joinToString(",")}"
is ApiChatUnread -> "/_unread chat ${chatRef(type, id, scope = null)} ${onOff(unreadChat)}"
is ApiChatRead -> "/_read chat ${chatRef(type, id)}"
is ApiChatItemsRead -> "/_read chat items ${chatRef(type, id)} ${itemIds.joinToString(",")}"
is ApiChatUnread -> "/_unread chat ${chatRef(type, id)} ${onOff(unreadChat)}"
is ReceiveFile ->
"/freceive $fileId" +
" approved_relays=${onOff(userApprovedRelays)}" +
@ -3708,8 +3666,6 @@ sealed class CC {
is ApiNewGroup -> "apiNewGroup"
is ApiAddMember -> "apiAddMember"
is ApiJoinGroup -> "apiJoinGroup"
is ApiAcceptMember -> "apiAcceptMember"
is ApiDeleteMemberSupportChat -> "apiDeleteMemberSupportChat"
is ApiMembersRole -> "apiMembersRole"
is ApiBlockMembersForAll -> "apiBlockMembersForAll"
is ApiRemoveMembers -> "apiRemoveMembers"
@ -3846,13 +3802,7 @@ sealed class CC {
private fun maybePwd(pwd: String?): String = if (pwd == "" || pwd == null) "" else " " + json.encodeToString(pwd)
companion object {
fun chatRef(chatType: ChatType, id: Long, scope: GroupChatScope?) = when (scope) {
null -> "${chatType.type}${id}"
is GroupChatScope.MemberSupport -> when (scope.groupMemberId_) {
null -> "${chatType.type}${id}(_support)"
else -> "${chatType.type}${id}(_support:${scope.groupMemberId_})"
}
}
fun chatRef(chatType: ChatType, id: Long) = "${chatType.type}${id}"
}
}
@ -5814,7 +5764,6 @@ sealed class CR {
@Serializable @SerialName("contactAlreadyExists") class ContactAlreadyExists(val user: UserRef, val contact: Contact): CR()
@Serializable @SerialName("contactDeleted") class ContactDeleted(val user: UserRef, val contact: Contact): CR()
@Serializable @SerialName("contactDeletedByContact") class ContactDeletedByContact(val user: UserRef, val contact: Contact): CR()
@Serializable @SerialName("itemsReadForChat") class ItemsReadForChat(val user: UserRef, val chatInfo: ChatInfo): CR()
@Serializable @SerialName("chatCleared") class ChatCleared(val user: UserRef, val chatInfo: ChatInfo): CR()
@Serializable @SerialName("userProfileNoChange") class UserProfileNoChange(val user: User): CR()
@Serializable @SerialName("userProfileUpdated") class UserProfileUpdated(val user: User, val fromProfile: Profile, val toProfile: Profile, val updateSummary: UserProfileUpdateSummary): CR()
@ -5863,9 +5812,6 @@ sealed class CR {
@Serializable @SerialName("receivedGroupInvitation") class ReceivedGroupInvitation(val user: UserRef, val groupInfo: GroupInfo, val contact: Contact, val memberRole: GroupMemberRole): CR()
@Serializable @SerialName("groupDeletedUser") class GroupDeletedUser(val user: UserRef, val groupInfo: GroupInfo): CR()
@Serializable @SerialName("joinedGroupMemberConnecting") class JoinedGroupMemberConnecting(val user: UserRef, val groupInfo: GroupInfo, val hostMember: GroupMember, val member: GroupMember): CR()
@Serializable @SerialName("memberAccepted") class MemberAccepted(val user: UserRef, val groupInfo: GroupInfo, val member: GroupMember): CR()
@Serializable @SerialName("memberSupportChatDeleted") class MemberSupportChatDeleted(val user: UserRef, val groupInfo: GroupInfo, val member: GroupMember): CR()
@Serializable @SerialName("memberAcceptedByOther") class MemberAcceptedByOther(val user: UserRef, val groupInfo: GroupInfo, val acceptingMember: GroupMember, val member: GroupMember): CR()
@Serializable @SerialName("memberRole") class MemberRole(val user: UserRef, val groupInfo: GroupInfo, val byMember: GroupMember, val member: GroupMember, val fromRole: GroupMemberRole, val toRole: GroupMemberRole): CR()
@Serializable @SerialName("membersRoleUser") class MembersRoleUser(val user: UserRef, val groupInfo: GroupInfo, val members: List<GroupMember>, val toRole: GroupMemberRole): CR()
@Serializable @SerialName("memberBlockedForAll") class MemberBlockedForAll(val user: UserRef, val groupInfo: GroupInfo, val byMember: GroupMember, val member: GroupMember, val blocked: Boolean): CR()
@ -5996,7 +5942,6 @@ sealed class CR {
is ContactAlreadyExists -> "contactAlreadyExists"
is ContactDeleted -> "contactDeleted"
is ContactDeletedByContact -> "contactDeletedByContact"
is ItemsReadForChat -> "itemsReadForChat"
is ChatCleared -> "chatCleared"
is UserProfileNoChange -> "userProfileNoChange"
is UserProfileUpdated -> "userProfileUpdated"
@ -6042,9 +5987,6 @@ sealed class CR {
is ReceivedGroupInvitation -> "receivedGroupInvitation"
is GroupDeletedUser -> "groupDeletedUser"
is JoinedGroupMemberConnecting -> "joinedGroupMemberConnecting"
is MemberAccepted -> "memberAccepted"
is MemberSupportChatDeleted -> "memberSupportChatDeleted"
is MemberAcceptedByOther -> "memberAcceptedByOther"
is MemberRole -> "memberRole"
is MembersRoleUser -> "membersRoleUser"
is MemberBlockedForAll -> "memberBlockedForAll"
@ -6168,7 +6110,6 @@ sealed class CR {
is ContactAlreadyExists -> withUser(user, json.encodeToString(contact))
is ContactDeleted -> withUser(user, json.encodeToString(contact))
is ContactDeletedByContact -> withUser(user, json.encodeToString(contact))
is ItemsReadForChat -> withUser(user, json.encodeToString(chatInfo))
is ChatCleared -> withUser(user, json.encodeToString(chatInfo))
is UserProfileNoChange -> withUser(user, noDetails())
is UserProfileUpdated -> withUser(user, json.encodeToString(toProfile))
@ -6214,9 +6155,6 @@ sealed class CR {
is ReceivedGroupInvitation -> withUser(user, "groupInfo: $groupInfo\ncontact: $contact\nmemberRole: $memberRole")
is GroupDeletedUser -> withUser(user, json.encodeToString(groupInfo))
is JoinedGroupMemberConnecting -> withUser(user, "groupInfo: $groupInfo\nhostMember: $hostMember\nmember: $member")
is MemberAccepted -> withUser(user, "groupInfo: $groupInfo\nmember: $member")
is MemberSupportChatDeleted -> withUser(user, "groupInfo: $groupInfo\nmember: $member")
is MemberAcceptedByOther -> withUser(user, "groupInfo: $groupInfo\nacceptingMember: $acceptingMember\nmember: $member")
is MemberRole -> withUser(user, "groupInfo: $groupInfo\nbyMember: $byMember\nmember: $member\nfromRole: $fromRole\ntoRole: $toRole")
is MembersRoleUser -> withUser(user, "groupInfo: $groupInfo\nmembers: $members\ntoRole: $toRole")
is MemberBlockedForAll -> withUser(user, "groupInfo: $groupInfo\nbyMember: $byMember\nmember: $member\nblocked: $blocked")

View file

@ -40,8 +40,9 @@ import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.usersettings.*
import chat.simplex.common.platform.*
import chat.simplex.common.views.chat.group.ChatTTLOption
import chat.simplex.common.views.chat.group.ChatTTLSection
import chat.simplex.common.views.chatlist.updateChatSettings
import chat.simplex.common.views.database.*
import chat.simplex.common.views.newchat.*
import chat.simplex.res.MR
import kotlinx.coroutines.*
@ -616,10 +617,7 @@ fun ChatInfoLayout(
}
SectionDividerSpaced(maxBottomPadding = false)
SectionView {
ChatTTLOption(chatItemTTL, setChatItemTTL, deletingItems)
SectionTextFooter(stringResource(MR.strings.chat_ttl_options_footer))
}
ChatTTLSection(chatItemTTL, setChatItemTTL, deletingItems)
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
val conn = contact.activeConn
@ -1386,7 +1384,7 @@ private fun setChatTTL(
private suspend fun afterSetChatTTL(chatsCtx: ChatModel.ChatsContext, rhId: Long?, chatInfo: ChatInfo, progressIndicator: MutableState<Boolean>) {
try {
val pagination = ChatPagination.Initial(ChatPagination.INITIAL_COUNT)
val (chat, navInfo) = controller.apiGetChat(rhId, chatInfo.chatType, chatInfo.apiId, scope = null, contentTag = null, pagination) ?: return
val (chat, navInfo) = controller.apiGetChat(rhId, chatInfo.chatType, chatInfo.apiId, null, pagination) ?: return
if (chat.chatItems.isEmpty()) {
// replacing old chat with the same old chat but without items. Less intrusive way of clearing a preview
withContext(Dispatchers.Main) {

View file

@ -17,7 +17,7 @@ suspend fun apiLoadSingleMessage(
apiId: Long,
itemId: Long
): ChatItem? = coroutineScope {
val (chat, _) = chatModel.controller.apiGetChat(rhId, chatType, apiId, chatsCtx.groupScopeInfo?.toChatScope(), chatsCtx.contentTag, ChatPagination.Around(itemId, 0), "") ?: return@coroutineScope null
val (chat, _) = chatModel.controller.apiGetChat(rhId, chatType, apiId, chatsCtx.contentTag, ChatPagination.Around(itemId, 0), "") ?: return@coroutineScope null
chat.chatItems.firstOrNull()
}
@ -31,7 +31,7 @@ suspend fun apiLoadMessages(
openAroundItemId: Long? = null,
visibleItemIndexesNonReversed: () -> IntRange = { 0 .. 0 }
) = coroutineScope {
val (chat, navInfo) = chatModel.controller.apiGetChat(rhId, chatType, apiId, chatsCtx.groupScopeInfo?.toChatScope(), chatsCtx.contentTag, pagination, search) ?: return@coroutineScope
val (chat, navInfo) = chatModel.controller.apiGetChat(rhId, chatType, apiId, chatsCtx.contentTag, pagination, search) ?: return@coroutineScope
// For .initial allow the chatItems to be empty as well as chatModel.chatId to not match this chat because these values become set after .initial finishes
/** When [openAroundItemId] is provided, chatId can be different too */
if (((chatModel.chatId.value != chat.id || chat.chatItems.isEmpty()) && pagination !is ChatPagination.Initial && pagination !is ChatPagination.Last && openAroundItemId == null)
@ -54,7 +54,7 @@ suspend fun processLoadedChat(
when (pagination) {
is ChatPagination.Initial -> {
val newSplits = if (chat.chatItems.isNotEmpty() && navInfo.afterTotal > 0) listOf(chat.chatItems.last().id) else emptyList()
if (chatsCtx.secondaryContextFilter == null) {
if (chatsCtx.contentTag == null) {
// update main chats, not content tagged
withContext(Dispatchers.Main) {
val oldChat = chatModel.chatsContext.getChat(chat.id)
@ -68,6 +68,7 @@ suspend fun processLoadedChat(
}
}
withContext(Dispatchers.Main) {
chatsCtx.chatItemStatuses.clear()
chatsCtx.chatItems.replaceAll(chat.chatItems)
chatModel.chatId.value = chat.id
splits.value = newSplits

View file

@ -63,23 +63,12 @@ fun ChatView(
onComposed: suspend (chatId: String) -> Unit
) {
val showSearch = rememberSaveable { mutableStateOf(false) }
val chat = chatModel.chats.value.firstOrNull { chat -> chat.chatInfo.id == staleChatId.value }
// They have their own iterator inside for a reason to prevent crash "Reading a state that was created after the snapshot..."
val remoteHostId = remember { derivedStateOf { chatModel.chats.value.firstOrNull { chat -> chat.chatInfo.id == staleChatId.value }?.remoteHostId } }
val activeChatInfo = remember { derivedStateOf {
var chatInfo = chatModel.chats.value.firstOrNull { chat -> chat.chatInfo.id == staleChatId.value }?.chatInfo
if (
chatsCtx.secondaryContextFilter is SecondaryContextFilter.GroupChatScopeContext
&& chatInfo is ChatInfo.Group
) {
val scopeInfo = chatsCtx.secondaryContextFilter.groupScopeInfo
chatInfo = chatInfo.copy(groupChatScope = scopeInfo)
}
chatInfo
} }
val activeChatInfo = remember { derivedStateOf { chatModel.chats.value.firstOrNull { chat -> chat.chatInfo.id == staleChatId.value }?.chatInfo } }
val user = chatModel.currentUser.value
val chatInfo = activeChatInfo.value
if (chat == null || chatInfo == null || user == null) {
if (chatInfo == null || user == null) {
LaunchedEffect(Unit) {
chatModel.chatId.value = null
ModalManager.end.closeModals()
@ -110,7 +99,7 @@ fun ChatView(
.distinctUntilChanged()
.filterNotNull()
.collect { chatId ->
if (chatsCtx.secondaryContextFilter == null) {
if (chatsCtx.contentTag == null) {
markUnreadChatAsRead(chatId)
}
showSearch.value = false
@ -119,18 +108,6 @@ fun ChatView(
}
}
}
if (chatsCtx.secondaryContextFilter == null && chatInfo is ChatInfo.Group && chatInfo.groupInfo.membership.memberPending) {
LaunchedEffect(Unit) {
val scopeInfo = GroupChatScopeInfo.MemberSupport(groupMember_ = null)
val supportChatInfo = ChatInfo.Group(chatInfo.groupInfo, groupChatScope = scopeInfo)
showMemberSupportChatView(
chatModel.chatId,
scrollToItemId,
supportChatInfo,
scopeInfo
)
}
}
val view = LocalMultiplatformView()
val chatRh = remoteHostId.value
// We need to have real unreadCount value for displaying it inside top right button
@ -140,7 +117,6 @@ fun ChatView(
chatsCtx.chats.value.firstOrNull { chat -> chat.chatInfo.id == staleChatId.value }?.chatStats?.unreadCount ?: 0
}
}
val reportsCount = reportsCount(chatInfo.id)
val clipboard = LocalClipboardManager.current
CompositionLocalProvider(
LocalAppBarHandler provides rememberAppBarHandler(chatInfo.id, keyboardCoversBar = false),
@ -157,7 +133,7 @@ fun ChatView(
val sameText = searchText.value == value
// showSearch can be false with empty text when it was closed manually after clicking on message from search to load .around it
// (required on Android to have this check to prevent call to search with old text)
val emptyAndClosedSearch = searchText.value.isEmpty() && !showSearch.value && chatsCtx.secondaryContextFilter == null
val emptyAndClosedSearch = searchText.value.isEmpty() && !showSearch.value && chatsCtx.contentTag == null
val c = chatModel.getChat(chatInfo.id)
if (sameText || emptyAndClosedSearch || c == null || chatModel.chatId.value != chatInfo.id) return@onSearchValueChanged
withBGApi {
@ -191,7 +167,7 @@ fun ChatView(
)
}
ComposeView(
rhId = remoteHostId.value, chatModel, chatsCtx, Chat(remoteHostId = chatRh, chatInfo = chatInfo, chatItems = emptyList()), composeState, attachmentOption,
chatModel, Chat(remoteHostId = chatRh, chatInfo = chatInfo, chatItems = emptyList()), composeState, attachmentOption,
showChooseAttachment = { scope.launch { attachmentBottomSheetState.show() } },
focusRequester = focusRequester
)
@ -244,7 +220,6 @@ fun ChatView(
rh = chatRh,
fromChatType = chatInfo.chatType,
fromChatId = chatInfo.apiId,
fromScope = chatInfo.groupChatScope(),
chatItemIds = chatItemIds
)
@ -342,7 +317,7 @@ fun ChatView(
}
}
},
showReports = {
showGroupReports = {
val info = activeChatInfo.value ?: return@ChatLayout
if (ModalManager.end.hasModalsOpen()) {
ModalManager.end.closeModals()
@ -353,38 +328,6 @@ fun ChatView(
showGroupReportsView(staleChatId, scrollToItemId, info)
}
},
showSupportChats = {
val info = activeChatInfo.value ?: return@ChatLayout
if (ModalManager.end.hasModalsOpen()) {
ModalManager.end.closeModals()
return@ChatLayout
}
hideKeyboard(view)
scope.launch {
if (info is ChatInfo.Group && info.groupInfo.membership.memberRole >= GroupMemberRole.Moderator) {
ModalManager.end.showCustomModal { close ->
MemberSupportView(
chatRh,
chat,
info.groupInfo,
scrollToItemId,
close
)
}
} else if (info is ChatInfo.Group) {
val scopeInfo = GroupChatScopeInfo.MemberSupport(groupMember_ = null)
val supportChatInfo = ChatInfo.Group(info.groupInfo, groupChatScope = scopeInfo)
scope.launch {
showMemberSupportChatView(
chatModel.chatId,
scrollToItemId = scrollToItemId,
supportChatInfo,
scopeInfo
)
}
}
}
},
showMemberInfo = { groupInfo: GroupInfo, member: GroupMember ->
hideKeyboard(view)
groupMembersJob.cancel()
@ -400,12 +343,12 @@ fun ChatView(
setGroupMembers(chatRh, groupInfo, chatModel)
if (!isActive) return@launch
if (chatsCtx.secondaryContextFilter == null) {
if (chatsCtx.contentTag == null) {
ModalManager.end.closeModals()
}
ModalManager.end.showModalCloseable(true) { close ->
remember { derivedStateOf { chatModel.getGroupMember(member.groupMemberId) } }.value?.let { mem ->
GroupMemberInfoView(chatRh, groupInfo, mem, scrollToItemId, stats, code, chatModel, close, close)
GroupMemberInfoView(chatRh, groupInfo, mem, stats, code, chatModel, close, close)
}
}
}
@ -436,7 +379,6 @@ fun ChatView(
chatRh,
type = chatInfo.chatType,
id = chatInfo.apiId,
scope = chatInfo.groupChatScope(),
itemIds = listOf(itemId),
mode = mode
)
@ -455,9 +397,9 @@ fun ChatView(
if (deletedItem.isActiveReport) {
chatModel.chatsContext.decreaseGroupReportsCounter(chatRh, chatInfo.id)
}
chatModel.chatsContext.updateChatInfo(chatRh, deleted.deletedChatItem.chatInfo)
}
withContext(Dispatchers.Main) {
if (deletedChatItem.isReport) {
if (toChatItem != null) {
chatModel.secondaryChatsContext.value?.upsertChatItem(chatRh, chatInfo, toChatItem)
} else {
@ -466,6 +408,7 @@ fun ChatView(
}
}
}
}
},
deleteMessages = { itemIds -> deleteMessages(chatRh, chatInfo, itemIds, false, moderate = false) },
archiveReports = { itemIds, forAll -> archiveReports(chatRh, chatInfo, itemIds, forAll) },
@ -569,7 +512,6 @@ fun ChatView(
rh = chatRh,
type = cInfo.chatType,
id = cInfo.apiId,
scope = cInfo.groupChatScope(),
itemId = cItem.id,
add = add,
reaction = reaction
@ -579,14 +521,16 @@ fun ChatView(
chatModel.chatsContext.updateChatItem(cInfo, updatedCI)
}
withContext(Dispatchers.Main) {
if (cItem.isReport) {
chatModel.secondaryChatsContext.value?.updateChatItem(cInfo, updatedCI)
}
}
}
}
},
showItemDetails = { cInfo, cItem ->
suspend fun loadChatItemInfo(): ChatItemInfo? = coroutineScope {
val ciInfo = chatModel.controller.apiGetChatItemInfo(chatRh, cInfo.chatType, cInfo.apiId, cInfo.groupChatScope(), cItem.id)
val ciInfo = chatModel.controller.apiGetChatItemInfo(chatRh, cInfo.chatType, cInfo.apiId, cItem.id)
if (ciInfo != null) {
if (chatInfo is ChatInfo.Group) {
setGroupMembers(chatRh, chatInfo.groupInfo, chatModel)
@ -635,16 +579,12 @@ fun ChatView(
withContext(Dispatchers.Main) {
chatModel.chatsContext.markChatItemsRead(chatRh, chatInfo.id, itemsIds)
ntfManager.cancelNotificationsForChat(chatInfo.id)
val updatedChatInfo = chatModel.controller.apiChatItemsRead(
chatModel.controller.apiChatItemsRead(
chatRh,
chatInfo.chatType,
chatInfo.apiId,
chatInfo.groupChatScope(),
itemsIds
)
if (updatedChatInfo != null) {
chatModel.chatsContext.updateChatInfo(chatRh, updatedChatInfo)
}
}
withContext(Dispatchers.Main) {
chatModel.secondaryChatsContext.value?.markChatItemsRead(chatRh, chatInfo.id, itemsIds)
@ -659,8 +599,7 @@ fun ChatView(
chatModel.controller.apiChatRead(
chatRh,
chatInfo.chatType,
chatInfo.apiId,
chatInfo.groupChatScope()
chatInfo.apiId
)
}
withContext(Dispatchers.Main) {
@ -743,8 +682,7 @@ fun ChatLayout(
selectedChatItems: MutableState<Set<Long>?>,
back: () -> Unit,
info: () -> Unit,
showReports: () -> Unit,
showSupportChats: () -> Unit,
showGroupReports: () -> Unit,
showMemberInfo: (GroupInfo, GroupMember) -> Unit,
loadMessages: suspend (ChatId, ChatPagination, visibleItemIndexesNonReversed: () -> IntRange) -> Unit,
deleteMessage: (Long, CIDeleteMode) -> Unit,
@ -807,7 +745,7 @@ fun ChatLayout(
sheetShape = RoundedCornerShape(topStart = 18.dp, topEnd = 18.dp)
) {
val composeViewHeight = remember { mutableStateOf(0.dp) }
Box(Modifier.fillMaxSize().chatViewBackgroundModifier(MaterialTheme.colors, MaterialTheme.wallpaper, LocalAppBarHandler.current?.backgroundGraphicsLayerSize, LocalAppBarHandler.current?.backgroundGraphicsLayer, drawWallpaper = chatsCtx.secondaryContextFilter == null)) {
Box(Modifier.fillMaxSize().chatViewBackgroundModifier(MaterialTheme.colors, MaterialTheme.wallpaper, LocalAppBarHandler.current?.backgroundGraphicsLayerSize, LocalAppBarHandler.current?.backgroundGraphicsLayer, drawWallpaper = chatsCtx.contentTag == null)) {
val remoteHostId = remember { remoteHostId }.value
val chatInfo = remember { chatInfo }.value
val oneHandUI = remember { appPrefs.oneHandUI.state }
@ -835,7 +773,6 @@ fun ChatLayout(
.padding(bottom = composeViewHeight.value)
) {
GroupMentions(
chatsCtx = chatsCtx,
rhId = remoteHostId,
composeState = composeState,
composeViewFocusRequester = composeViewFocusRequester,
@ -895,42 +832,16 @@ fun ChatLayout(
}
}
val reportsCount = reportsCount(chatInfo?.id)
val supportUnreadCount = supportUnreadCount(chatInfo?.id)
if (oneHandUI.value && chatBottomBar.value) {
if (
chatInfo is ChatInfo.Group
&& chatsCtx.secondaryContextFilter == null
&& (reportsCount > 0 || supportUnreadCount > 0)
) {
SupportChatsCountToolbar(chatInfo, reportsCount, supportUnreadCount, withStatusBar = true, showReports, showSupportChats)
if (chatInfo is ChatInfo.Group && chatInfo.groupInfo.canModerate && chatsCtx.contentTag == null && reportsCount > 0) {
ReportedCountToolbar(reportsCount, withStatusBar = true, showGroupReports)
} else {
StatusBarBackground()
}
} else {
NavigationBarBackground(true, oneHandUI.value, noAlpha = true)
}
when (chatsCtx.secondaryContextFilter) {
is SecondaryContextFilter.GroupChatScopeContext -> {
when (chatsCtx.secondaryContextFilter.groupScopeInfo) {
is GroupChatScopeInfo.MemberSupport -> {
if (oneHandUI.value) {
StatusBarBackground()
}
Column(if (oneHandUI.value) Modifier.align(Alignment.BottomStart).imePadding() else Modifier) {
Box {
if (selectedChatItems.value == null) {
MemberSupportChatAppBar(chatsCtx, chatsCtx.secondaryContextFilter.groupScopeInfo.groupMember_, { ModalManager.end.closeModal() }, onSearchValueChanged)
} else {
SelectedItemsCounterToolbar(selectedChatItems, !oneHandUI.value)
}
}
}
}
}
}
is SecondaryContextFilter.MsgContentTagContext -> {
when (chatsCtx.secondaryContextFilter.contentTag) {
MsgContentTag.Report -> {
if (chatsCtx.contentTag == MsgContentTag.Report) {
if (oneHandUI.value) {
StatusBarBackground()
}
@ -943,11 +854,7 @@ fun ChatLayout(
}
}
}
}
else -> TODO()
}
}
null -> {
} else {
Column(if (oneHandUI.value && chatBottomBar.value) Modifier.align(Alignment.BottomStart).imePadding() else Modifier) {
Box {
if (selectedChatItems.value == null) {
@ -958,13 +865,8 @@ fun ChatLayout(
SelectedItemsCounterToolbar(selectedChatItems, !oneHandUI.value || !chatBottomBar.value)
}
}
if (
chatInfo is ChatInfo.Group
&& (reportsCount > 0 || supportUnreadCount > 0)
&& (!oneHandUI.value || !chatBottomBar.value)
) {
SupportChatsCountToolbar(chatInfo, reportsCount, supportUnreadCount, withStatusBar = false, showReports, showSupportChats)
}
if (chatInfo is ChatInfo.Group && chatInfo.groupInfo.canModerate && chatsCtx.contentTag == null && reportsCount > 0 && (!oneHandUI.value || !chatBottomBar.value)) {
ReportedCountToolbar(reportsCount, withStatusBar = false, showGroupReports)
}
}
}
@ -998,7 +900,7 @@ fun BoxScope.ChatInfoToolbar(
showSearch.value = false
}
}
if (appPlatform.isAndroid && chatsCtx.secondaryContextFilter == null) {
if (appPlatform.isAndroid && chatsCtx.contentTag == null) {
BackHandler(onBack = onBackClicked)
}
val barButtons = arrayListOf<@Composable RowScope.() -> Unit>()
@ -1195,33 +1097,19 @@ fun ChatInfoToolbarTitle(cInfo: ChatInfo, imageSize: Dp = 40.dp, iconColor: Colo
}
@Composable
private fun SupportChatsCountToolbar(
chatInfo: ChatInfo,
private fun ReportedCountToolbar(
reportsCount: Int,
supportUnreadCount: Int,
withStatusBar: Boolean,
showReports: () -> Unit,
showSupportChats: () -> Unit
showGroupReports: () -> Unit
) {
Box {
val statusBarPadding = if (withStatusBar) WindowInsets.statusBars.asPaddingValues().calculateTopPadding() else 0.dp
Row(
Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly,
) {
if (
chatInfo is ChatInfo.Group
&& chatInfo.groupInfo.canModerate
&& reportsCount > 0
) {
Row(
Modifier
.fillMaxWidth()
.weight(1F)
.height(AppBarHeight * fontSizeSqrtMultiplier + statusBarPadding)
.background(MaterialTheme.colors.background)
.clickable(onClick = showReports)
.clickable(onClick = showGroupReports)
.padding(top = statusBarPadding),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
@ -1237,38 +1125,6 @@ private fun SupportChatsCountToolbar(
style = MaterialTheme.typography.button
)
}
}
if (supportUnreadCount > 0) {
Row(
Modifier
.fillMaxWidth()
.weight(1F)
.height(AppBarHeight * fontSizeSqrtMultiplier + statusBarPadding)
.background(MaterialTheme.colors.background)
.clickable(onClick = showSupportChats)
.padding(top = statusBarPadding),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Icon(painterResource(MR.images.ic_flag), null, Modifier.size(22.dp), tint = MaterialTheme.colors.primary)
Spacer(Modifier.width(4.dp))
Text(
if (chatInfo is ChatInfo.Group && chatInfo.groupInfo.canModerate) {
if (appPlatform.isAndroid)
stringResource(MR.strings.group_new_support_chats_short).format(supportUnreadCount)
else if (supportUnreadCount == 1)
stringResource(MR.strings.group_new_support_chat_one)
else
stringResource(MR.strings.group_new_support_chats).format(supportUnreadCount)
} else {
stringResource(MR.strings.group_new_support_messages).format(supportUnreadCount)
},
style = MaterialTheme.typography.button
)
}
}
}
Divider(Modifier.align(Alignment.BottomStart))
}
}
@ -1342,6 +1198,7 @@ fun BoxScope.ChatItemsList(
val searchValueIsNotBlank = remember { derivedStateOf { searchValue.value.isNotBlank() } }
val revealedItems = rememberSaveable(stateSaver = serializableSaver()) { mutableStateOf(setOf<Long>()) }
// not using reversedChatItems inside to prevent possible derivedState bug in Compose when one derived state access can cause crash asking another derived state
if (chatsCtx != null) {
val mergedItems = remember {
derivedStateOf {
MergedItems.create(chatsCtx.chatItems.value.asReversed(), unreadCount, revealedItems.value, chatsCtx.chatState)
@ -1349,10 +1206,9 @@ fun BoxScope.ChatItemsList(
}
val reversedChatItems = remember { derivedStateOf { chatsCtx.chatItems.value.asReversed() } }
val reportsCount = reportsCount(chatInfo.id)
val supportUnreadCount = supportUnreadCount(chatInfo.id)
val topPaddingToContent = topPaddingToContent(
chatView = chatsCtx.secondaryContextFilter == null,
additionalTopBar = chatsCtx.secondaryContextFilter == null && (reportsCount > 0 || supportUnreadCount > 0)
chatView = chatsCtx.contentTag == null,
additionalTopBar = chatsCtx.contentTag == null && reportsCount > 0
)
val topPaddingToContentPx = rememberUpdatedState(with(LocalDensity.current) { topPaddingToContent.roundToPx() })
val numberOfBottomAppBars = numberOfBottomAppBars()
@ -1417,10 +1273,12 @@ fun BoxScope.ChatItemsList(
val chatInfoUpdated = rememberUpdatedState(chatInfo)
val scope = rememberCoroutineScope()
val scrollToItem: (Long) -> Unit = remember {
// In group reports just set the itemId to scroll to so the main ChatView will handle scrolling
if (chatsCtx.contentTag == MsgContentTag.Report) return@remember { scrollToItemId.value = it }
scrollToItem(searchValue, loadingMoreItems, animatedScrollingInProgress, highlightedItems, chatInfoUpdated, maxHeight, scope, reversedChatItems, mergedItems, listState, loadMessages)
}
val scrollToQuotedItemFromItem: (Long) -> Unit = remember { findQuotedItemFromItem(chatsCtx, remoteHostIdUpdated, chatInfoUpdated, scope, scrollToItem, scrollToItemId) }
if (chatsCtx.secondaryContextFilter == null) {
val scrollToQuotedItemFromItem: (Long) -> Unit = remember { findQuotedItemFromItem(chatsCtx, remoteHostIdUpdated, chatInfoUpdated, scope, scrollToItem) }
if (chatsCtx.contentTag == null) {
LaunchedEffect(Unit) {
snapshotFlow { scrollToItemId.value }.filterNotNull().collect {
if (appPlatform.isAndroid) {
@ -1484,7 +1342,7 @@ fun BoxScope.ChatItemsList(
highlightedItems.value = setOf()
}
}
ChatItemView(chatsCtx, remoteHostId, chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, highlighted = highlighted, hoveredItemId = hoveredItemId, range = range, searchIsNotBlank = searchValueIsNotBlank, fillMaxWidth = fillMaxWidth, selectedChatItems = selectedChatItems, selectChatItem = { selectUnselectChatItem(true, cItem, revealed, selectedChatItems, reversedChatItems) }, deleteMessage = deleteMessage, deleteMessages = deleteMessages, archiveReports = archiveReports, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, forwardItem = forwardItem, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, scrollToItemId = scrollToItemId, scrollToQuotedItemFromItem = scrollToQuotedItemFromItem, setReaction = setReaction, showItemDetails = showItemDetails, reveal = reveal, showMemberInfo = showMemberInfo, showChatInfo = showChatInfo, developerTools = developerTools, showViaProxy = showViaProxy, itemSeparation = itemSeparation, showTimestamp = itemSeparation.timestamp)
ChatItemView(chatsCtx, remoteHostId, chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, highlighted = highlighted, hoveredItemId = hoveredItemId, range = range, searchIsNotBlank = searchValueIsNotBlank, fillMaxWidth = fillMaxWidth, selectedChatItems = selectedChatItems, selectChatItem = { selectUnselectChatItem(true, cItem, revealed, selectedChatItems, reversedChatItems) }, deleteMessage = deleteMessage, deleteMessages = deleteMessages, archiveReports = archiveReports, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, forwardItem = forwardItem, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, scrollToQuotedItemFromItem = scrollToQuotedItemFromItem, setReaction = setReaction, showItemDetails = showItemDetails, reveal = reveal, showMemberInfo = showMemberInfo, showChatInfo = showChatInfo, developerTools = developerTools, showViaProxy = showViaProxy, itemSeparation = itemSeparation, showTimestamp = itemSeparation.timestamp)
}
}
@ -1690,7 +1548,7 @@ fun BoxScope.ChatItemsList(
),
reverseLayout = true,
additionalBarOffset = composeViewHeight,
additionalTopBar = rememberUpdatedState(chatsCtx.secondaryContextFilter == null && (reportsCount > 0 || supportUnreadCount > 0)),
additionalTopBar = rememberUpdatedState(chatsCtx.contentTag == null && reportsCount > 0),
chatBottomBar = remember { appPrefs.chatBottomBar.state }
) {
val mergedItemsValue = mergedItems.value
@ -1770,6 +1628,7 @@ fun BoxScope.ChatItemsList(
}
}
}
}
}
private suspend fun loadLastItems(chatsCtx: ChatModel.ChatsContext, chatId: State<ChatId>, listState: State<LazyListState>, loadItems: State<suspend (ChatId, ChatPagination) -> Boolean>) {
@ -2375,15 +2234,6 @@ fun reportsCount(staleChatId: String?): Int {
}
}
@Composable
fun supportUnreadCount(staleChatId: String?): Int {
return if (staleChatId?.startsWith("#") != true) {
0
} else {
remember(staleChatId) { derivedStateOf { chatModel.chats.value.firstOrNull { chat -> chat.chatInfo.id == staleChatId } } }.value?.supportUnreadCount ?: 0
}
}
private fun reversedChatItemsStatic(chatsCtx: ChatModel.ChatsContext): List<ChatItem> =
chatsCtx.chatItems.value.asReversed()
@ -2465,8 +2315,7 @@ private fun findQuotedItemFromItem(
rhId: State<Long?>,
chatInfo: State<ChatInfo>,
scope: CoroutineScope,
scrollToItem: (Long) -> Unit,
scrollToItemId: MutableState<Long?>
scrollToItem: (Long) -> Unit
): (Long) -> Unit = { itemId: Long ->
scope.launch(Dispatchers.Default) {
val item = apiLoadSingleMessage(chatsCtx, rhId.value, chatInfo.value.chatType, chatInfo.value.apiId, itemId)
@ -2478,11 +2327,7 @@ private fun findQuotedItemFromItem(
chatModel.secondaryChatsContext.value?.updateChatItem(chatInfo.value, item)
}
if (item.quotedItem?.itemId != null) {
if (item.isReport && chatsCtx.secondaryContextFilter != null) {
scrollToItemId.value = item.quotedItem.itemId
} else {
scrollToItem(item.quotedItem.itemId)
}
} else {
showQuotedItemDoesNotExistAlert()
}
@ -2654,7 +2499,6 @@ private fun deleteMessages(chatRh: Long?, chatInfo: ChatInfo, itemIds: List<Long
chatRh,
type = chatInfo.chatType,
id = chatInfo.apiId,
scope = chatInfo.groupChatScope(),
itemIds = itemIds,
mode = if (forAll) CIDeleteMode.cidmBroadcast else CIDeleteMode.cidmInternal
)
@ -2673,12 +2517,10 @@ private fun deleteMessages(chatRh: Long?, chatInfo: ChatInfo, itemIds: List<Long
chatModel.chatsContext.decreaseGroupReportsCounter(chatRh, chatInfo.id)
}
}
deleted.lastOrNull()?.deletedChatItem?.chatInfo?.let { updatedChatInfo ->
chatModel.chatsContext.updateChatInfo(chatRh, updatedChatInfo)
}
}
withContext(Dispatchers.Main) {
for (di in deleted) {
if (di.deletedChatItem.chatItem.isReport) {
val toChatItem = di.toChatItem?.chatItem
if (toChatItem != null) {
chatModel.secondaryChatsContext.value?.upsertChatItem(chatRh, chatInfo, toChatItem)
@ -2687,6 +2529,7 @@ private fun deleteMessages(chatRh: Long?, chatInfo: ChatInfo, itemIds: List<Long
}
}
}
}
onSuccess()
}
}
@ -2716,12 +2559,10 @@ private fun archiveReports(chatRh: Long?, chatInfo: ChatInfo, itemIds: List<Long
chatModel.chatsContext.decreaseGroupReportsCounter(chatRh, chatInfo.id)
}
}
deleted.lastOrNull()?.deletedChatItem?.chatInfo?.let { updatedChatInfo ->
chatModel.chatsContext.updateChatInfo(chatRh, updatedChatInfo)
}
}
withContext(Dispatchers.Main) {
for (di in deleted) {
if (di.deletedChatItem.chatItem.isReport) {
val toChatItem = di.toChatItem?.chatItem
if (toChatItem != null) {
chatModel.secondaryChatsContext.value?.upsertChatItem(chatRh, chatInfo, toChatItem)
@ -2730,6 +2571,7 @@ private fun archiveReports(chatRh: Long?, chatInfo: ChatInfo, itemIds: List<Long
}
}
}
}
onSuccess()
}
}
@ -3078,7 +2920,7 @@ fun PreviewChatLayout() {
val unreadCount = remember { mutableStateOf(chatItems.count { it.isRcvNew }) }
val searchValue = remember { mutableStateOf("") }
ChatLayout(
chatsCtx = ChatModel.ChatsContext(secondaryContextFilter = null),
chatsCtx = ChatModel.ChatsContext(contentTag = null),
remoteHostId = remember { mutableStateOf(null) },
chatInfo = remember { mutableStateOf(ChatInfo.Direct.sampleData) },
unreadCount = unreadCount,
@ -3093,8 +2935,7 @@ fun PreviewChatLayout() {
selectedChatItems = remember { mutableStateOf(setOf()) },
back = {},
info = {},
showReports = {},
showSupportChats = {},
showGroupReports = {},
showMemberInfo = { _, _ -> },
loadMessages = { _, _, _ -> },
deleteMessage = { _, _ -> },
@ -3157,7 +2998,7 @@ fun PreviewGroupChatLayout() {
val unreadCount = remember { mutableStateOf(chatItems.count { it.isRcvNew }) }
val searchValue = remember { mutableStateOf("") }
ChatLayout(
chatsCtx = ChatModel.ChatsContext(secondaryContextFilter = null),
chatsCtx = ChatModel.ChatsContext(contentTag = null),
remoteHostId = remember { mutableStateOf(null) },
chatInfo = remember { mutableStateOf(ChatInfo.Direct.sampleData) },
unreadCount = unreadCount,
@ -3172,8 +3013,7 @@ fun PreviewGroupChatLayout() {
selectedChatItems = remember { mutableStateOf(setOf()) },
back = {},
info = {},
showReports = {},
showSupportChats = {},
showGroupReports = {},
showMemberInfo = { _, _ -> },
loadMessages = { _, _, _ -> },
deleteMessage = { _, _ -> },

View file

@ -1,126 +0,0 @@
package chat.simplex.common.views.chat
import SectionItemView
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
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.text.style.TextAlign
import androidx.compose.ui.unit.dp
import chat.simplex.common.model.*
import chat.simplex.common.platform.chatModel
import chat.simplex.common.views.chat.group.removeMember
import chat.simplex.common.views.chat.group.removeMemberDialog
import chat.simplex.common.views.helpers.*
import chat.simplex.res.MR
import dev.icerock.moko.resources.compose.stringResource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun ComposeContextPendingMemberActionsView(
rhId: Long?,
groupInfo: GroupInfo,
member: GroupMember
) {
Column(
Modifier
.height(60.dp)
.background(MaterialTheme.colors.surface)
) {
Divider()
Row(
Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly,
) {
Column(
Modifier
.fillMaxWidth()
.fillMaxHeight()
.weight(1F)
.clickable {
rejectMemberDialog(rhId, member, chatModel, close = { ModalManager.end.closeModal() })
},
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(stringResource(MR.strings.reject_pending_member_button), color = Color.Red)
}
Column(
Modifier
.fillMaxWidth()
.fillMaxHeight()
.weight(1F)
.clickable {
acceptMemberDialog(rhId, groupInfo, member, close = { ModalManager.end.closeModal() })
},
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(stringResource(MR.strings.accept_pending_member_button), color = MaterialTheme.colors.primary)
}
}
}
}
fun rejectMemberDialog(rhId: Long?, member: GroupMember, chatModel: ChatModel, close: (() -> Unit)? = null) {
AlertManager.shared.showAlertDialog(
title = generalGetString(MR.strings.reject_pending_member_alert_title),
confirmText = generalGetString(MR.strings.reject_pending_member_button),
onConfirm = {
removeMember(rhId, member, chatModel, close)
},
destructive = true,
)
}
fun acceptMemberDialog(rhId: Long?, groupInfo: GroupInfo, member: GroupMember, close: (() -> Unit)? = null) {
AlertManager.shared.showAlertDialogButtonsColumn(
title = generalGetString(MR.strings.accept_pending_member_alert_title),
text = generalGetString(MR.strings.accept_pending_member_alert_question),
buttons = {
Column {
// Accept as member
SectionItemView({
AlertManager.shared.hideAlert()
acceptMember(rhId, groupInfo, member, GroupMemberRole.Member, close)
}) {
Text(generalGetString(MR.strings.accept_pending_member_alert_confirmation_as_member), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
// Accept as observer
SectionItemView({
AlertManager.shared.hideAlert()
acceptMember(rhId, groupInfo, member, GroupMemberRole.Observer, close)
}) {
Text(generalGetString(MR.strings.accept_pending_member_alert_confirmation_as_observer), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
// Cancel
SectionItemView({
AlertManager.shared.hideAlert()
}) {
Text(stringResource(MR.strings.cancel_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
}
}
)
}
private fun acceptMember(rhId: Long?, groupInfo: GroupInfo, member: GroupMember, role: GroupMemberRole, close: (() -> Unit)?) {
withBGApi {
val r = chatModel.controller.apiAcceptMember(rhId, groupInfo.groupId, member.groupMemberId, role)
if (r != null) {
withContext(Dispatchers.Main) {
chatModel.chatsContext.upsertGroupMember(rhId, r.first, r.second)
chatModel.chatsContext.updateGroup(rhId, r.first)
}
}
close?.invoke()
}
}

View file

@ -333,9 +333,7 @@ suspend fun MutableState<ComposeState>.processPickedMedia(uris: List<URI>, text:
@Composable
fun ComposeView(
rhId: Long?,
chatModel: ChatModel,
chatsCtx: ChatModel.ChatsContext,
chat: Chat,
composeState: MutableState<ComposeState>,
attachmentOption: MutableState<AttachmentOption?>,
@ -468,7 +466,6 @@ fun ComposeView(
rh = chat.remoteHostId,
type = cInfo.chatType,
id = cInfo.apiId,
scope = cInfo.groupChatScope(),
live = live,
ttl = ttl,
composedMessages = listOf(ComposedMessage(file, quoted, mc, mentions))
@ -476,7 +473,7 @@ fun ComposeView(
if (!chatItems.isNullOrEmpty()) {
chatItems.forEach { aChatItem ->
withContext(Dispatchers.Main) {
chatsCtx.addChatItem(chat.remoteHostId, cInfo, aChatItem.chatItem)
chatModel.chatsContext.addChatItem(chat.remoteHostId, cInfo, aChatItem.chatItem)
}
}
return chatItems.first().chatItem
@ -501,17 +498,15 @@ fun ComposeView(
rh = rhId,
toChatType = chat.chatInfo.chatType,
toChatId = chat.chatInfo.apiId,
toScope = chat.chatInfo.groupChatScope(),
fromChatType = fromChatInfo.chatType,
fromChatId = fromChatInfo.apiId,
fromScope = fromChatInfo.groupChatScope(),
itemIds = forwardedItem.map { it.id },
ttl = ttl
)
withContext(Dispatchers.Main) {
chatItems?.forEach { chatItem ->
chatsCtx.addChatItem(rhId, chat.chatInfo, chatItem)
chatModel.chatsContext.addChatItem(rhId, chat.chatInfo, chatItem)
}
}
@ -568,21 +563,16 @@ fun ComposeView(
}
}
fun showReportsInSupportChatAlert() {
AlertManager.shared.showAlertDialog(
title = generalGetString(MR.strings.report_sent_alert_title),
text = generalGetString(MR.strings.report_sent_alert_msg_view_in_support_chat),
confirmText = generalGetString(MR.strings.ok),
dismissText = generalGetString(MR.strings.dont_show_again),
onDismiss = {
chatModel.controller.appPrefs.showReportsInSupportChatAlert.set(false)
},
)
}
suspend fun sendReport(reportReason: ReportReason, chatItemId: Long): List<ChatItem>? {
val cItems = chatModel.controller.apiReportMessage(chat.remoteHostId, chat.chatInfo.apiId, chatItemId, reportReason, msgText)
if (chatModel.controller.appPrefs.showReportsInSupportChatAlert.get()) showReportsInSupportChatAlert()
if (cItems != null) {
withContext(Dispatchers.Main) {
cItems.forEach { chatItem ->
chatModel.chatsContext.addChatItem(chat.remoteHostId, chat.chatInfo, chatItem.chatItem)
}
}
}
return cItems?.map { it.chatItem }
}
@ -591,7 +581,7 @@ fun ComposeView(
val contact = chatModel.controller.apiSendMemberContactInvitation(chat.remoteHostId, chat.chatInfo.apiId, mc)
if (contact != null) {
withContext(Dispatchers.Main) {
chatsCtx.updateContact(chat.remoteHostId, contact)
chatModel.chatsContext.updateContact(chat.remoteHostId, contact)
}
}
}
@ -604,14 +594,13 @@ fun ComposeView(
rh = chat.remoteHostId,
type = cInfo.chatType,
id = cInfo.apiId,
scope = cInfo.groupChatScope(),
itemId = ei.meta.itemId,
updatedMessage = UpdatedMessage(updateMsgContent(oldMsgContent), cs.memberMentions),
live = live
)
if (updatedItem != null) {
withContext(Dispatchers.Main) {
chatsCtx.upsertChatItem(chat.remoteHostId, cInfo, updatedItem.chatItem)
chatModel.chatsContext.upsertChatItem(chat.remoteHostId, cInfo, updatedItem.chatItem)
}
}
return updatedItem?.chatItem
@ -902,7 +891,7 @@ fun ComposeView(
fun editPrevMessage() {
if (composeState.value.contextItem != ComposeContextItem.NoContextItem || composeState.value.preview != ComposePreview.NoPreview) return
val lastEditable = chatsCtx.chatItems.value.findLast { it.meta.editable }
val lastEditable = chatModel.chatsContext.chatItems.value.findLast { it.meta.editable }
if (lastEditable != null) {
composeState.value = ComposeState(editingItem = lastEditable, useLinkPreviews = useLinkPreviews)
}
@ -1015,21 +1004,6 @@ fun ComposeView(
val nextSendGrpInv = rememberUpdatedState(chat.nextSendGrpInv)
Column {
if (
chat.chatInfo is ChatInfo.Group
&& chatsCtx.secondaryContextFilter is SecondaryContextFilter.GroupChatScopeContext
&& chatsCtx.secondaryContextFilter.groupScopeInfo is GroupChatScopeInfo.MemberSupport
&& chatsCtx.secondaryContextFilter.groupScopeInfo.groupMember_ != null
&& chatsCtx.secondaryContextFilter.groupScopeInfo.groupMember_.memberPending
&& composeState.value.contextItem == ComposeContextItem.NoContextItem
&& composeState.value.preview == ComposePreview.NoPreview
) {
ComposeContextPendingMemberActionsView(
rhId = rhId,
groupInfo = chat.chatInfo.groupInfo,
member = chatsCtx.secondaryContextFilter.groupScopeInfo.groupMember_
)
}
if (nextSendGrpInv.value) {
ComposeContextInvitingContactMemberView()
}
@ -1037,8 +1011,8 @@ fun ComposeView(
if (ctx is ComposeContextItem.ReportedItem) {
ReportReasonView(ctx.reason)
}
val simplexLinkProhibited = chatsCtx.secondaryContextFilter == null && hasSimplexLink.value && !chat.groupFeatureEnabled(GroupFeature.SimplexLinks)
val fileProhibited = chatsCtx.secondaryContextFilter == null && composeState.value.attachmentPreview && !chat.groupFeatureEnabled(GroupFeature.Files)
val simplexLinkProhibited = hasSimplexLink.value && !chat.groupFeatureEnabled(GroupFeature.SimplexLinks)
val fileProhibited = composeState.value.attachmentPreview && !chat.groupFeatureEnabled(GroupFeature.Files)
val voiceProhibited = composeState.value.preview is ComposePreview.VoicePreview && !chat.chatInfo.featureEnabled(ChatFeature.Voice)
if (composeState.value.preview !is ComposePreview.VoicePreview || composeState.value.editing) {
if (simplexLinkProhibited) {
@ -1067,10 +1041,7 @@ fun ComposeView(
Surface(color = MaterialTheme.colors.background, contentColor = MaterialTheme.colors.onBackground) {
Divider()
Row(Modifier.padding(end = 8.dp), verticalAlignment = Alignment.Bottom) {
val isGroupAndProhibitedFiles =
chatsCtx.secondaryContextFilter == null
&& chat.chatInfo is ChatInfo.Group
&& !chat.chatInfo.groupInfo.fullGroupPreferences.files.on(chat.chatInfo.groupInfo.membership)
val isGroupAndProhibitedFiles = chat.chatInfo is ChatInfo.Group && !chat.chatInfo.groupInfo.fullGroupPreferences.files.on(chat.chatInfo.groupInfo.membership)
val attachmentClicked = if (isGroupAndProhibitedFiles) {
{
AlertManager.shared.showAlertMsg(

View file

@ -77,7 +77,7 @@ fun SelectedItemsButtonsToolbar(
val forwardCountProhibited = remember { mutableStateOf(false) }
Box {
// It's hard to measure exact height of ComposeView with different fontSizes. Better to depend on actual ComposeView, even empty
ComposeView(rhId = null, chatModel = chatModel, chatModel.chatsContext, Chat.sampleData, remember { mutableStateOf(ComposeState(useLinkPreviews = false)) }, remember { mutableStateOf(null) }, {}, remember { FocusRequester() })
ComposeView(chatModel = chatModel, Chat.sampleData, remember { mutableStateOf(ComposeState(useLinkPreviews = false)) }, remember { mutableStateOf(null) }, {}, remember { FocusRequester() })
Row(
Modifier
.matchParentSize()
@ -101,21 +101,21 @@ fun SelectedItemsButtonsToolbar(
)
}
IconButton({ moderateItems() }, Modifier.alpha(if (canModerate.value) 1f else 0f), enabled = moderateEnabled.value && !deleteCountProhibited.value && chatsCtx.secondaryContextFilter == null) {
IconButton({ moderateItems() }, Modifier.alpha(if (canModerate.value) 1f else 0f), enabled = moderateEnabled.value && !deleteCountProhibited.value) {
Icon(
painterResource(MR.images.ic_flag),
null,
Modifier.size(22.dp),
tint = if (!moderateEnabled.value || deleteCountProhibited.value || chatsCtx.secondaryContextFilter != null) MaterialTheme.colors.secondary else MaterialTheme.colors.error
tint = if (!moderateEnabled.value || deleteCountProhibited.value) MaterialTheme.colors.secondary else MaterialTheme.colors.error
)
}
IconButton({ forwardItems() }, enabled = forwardEnabled.value && !forwardCountProhibited.value && chatsCtx.secondaryContextFilter == null) {
IconButton({ forwardItems() }, enabled = forwardEnabled.value && !forwardCountProhibited.value) {
Icon(
painterResource(MR.images.ic_forward),
null,
Modifier.size(22.dp),
tint = if (!forwardEnabled.value || forwardCountProhibited.value || chatsCtx.secondaryContextFilter != null) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
tint = if (!forwardEnabled.value || forwardCountProhibited.value) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
)
}
}

View file

@ -55,16 +55,6 @@ fun AddGroupMembersView(rhId: Long?, groupInfo: GroupInfo, creatingGroup: Boolea
GroupPreferencesView(chatModel, rhId, groupInfo.id, close)
}
},
openMemberAdmission = {
ModalManager.end.showCustomModal { close ->
MemberAdmissionView(
chat.simplex.common.platform.chatModel,
rhId,
groupInfo.id,
close
)
}
},
inviteMembers = {
allowModifyMembers = false
withLongRunningApi(slow = 120_000) {
@ -103,9 +93,8 @@ fun getContactsToAdd(chatModel: ChatModel, search: String): List<Contact> {
.asSequence()
.map { it.chatInfo }
.filterIsInstance<ChatInfo.Direct>()
.filter { it.sendMsgEnabled }
.map { it.contact }
.filter { c -> !c.nextSendGrpInv && c.contactId !in memberContactIds && c.anyNameContains(s)
.filter { c -> c.sendMsgEnabled && !c.nextSendGrpInv && c.contactId !in memberContactIds && c.anyNameContains(s)
}
.sortedBy { it.displayName.lowercase() }
.toList()
@ -121,7 +110,6 @@ fun AddGroupMembersLayout(
allowModifyMembers: Boolean,
searchText: MutableState<TextFieldValue>,
openPreferences: () -> Unit,
openMemberAdmission: () -> Unit,
inviteMembers: () -> Unit,
clearSelection: () -> Unit,
addContact: (Long) -> Unit,
@ -156,7 +144,7 @@ fun AddGroupMembersLayout(
horizontalArrangement = Arrangement.Center
) {
ChatInfoToolbarTitle(
ChatInfo.Group(groupInfo, groupChatScope = null),
ChatInfo.Group(groupInfo),
imageSize = 60.dp,
iconColor = if (isInDarkTheme()) GroupDark else SettingsSecondaryLight
)
@ -177,9 +165,6 @@ fun AddGroupMembersLayout(
} else {
SectionView {
if (creatingGroup) {
SectionItemView(openMemberAdmission) {
Text(stringResource(MR.strings.set_member_admission))
}
SectionItemView(openPreferences) {
Text(stringResource(MR.strings.set_group_preferences))
}
@ -391,7 +376,6 @@ fun PreviewAddGroupMembersLayout() {
allowModifyMembers = true,
searchText = remember { mutableStateOf(TextFieldValue("")) },
openPreferences = {},
openMemberAdmission = {},
inviteMembers = {},
clearSelection = {},
addContact = {},

View file

@ -74,9 +74,6 @@ fun ModalData.GroupChatInfoView(
val chatItemTTL = remember(groupInfo.id) { mutableStateOf(if (groupInfo.chatItemTTL != null) ChatItemTTL.fromSeconds(groupInfo.chatItemTTL) else null) }
val deletingItems = rememberSaveable(groupInfo.id) { mutableStateOf(false) }
val scope = rememberCoroutineScope()
val activeSortedMembers = remember { chatModel.groupMembers }.value
.filter { it.memberStatus != GroupMemberStatus.MemLeft && it.memberStatus != GroupMemberStatus.MemRemoved }
.sortedByDescending { it.memberRole }
GroupChatInfoLayout(
chat,
@ -98,7 +95,9 @@ fun ModalData.GroupChatInfoView(
setChatTTLAlert(chatsCtx, chat.remoteHostId, chat.chatInfo, chatItemTTL, previousChatTTL, deletingItems)
},
activeSortedMembers = activeSortedMembers,
activeSortedMembers = remember { chatModel.groupMembers }.value
.filter { it.memberStatus != GroupMemberStatus.MemLeft && it.memberStatus != GroupMemberStatus.MemRemoved }
.sortedByDescending { it.memberRole },
developerTools,
onLocalAliasChanged = { setGroupAlias(chat, it, chatModel) },
groupLink,
@ -127,7 +126,7 @@ fun ModalData.GroupChatInfoView(
}
ModalManager.end.showModalCloseable(true) { closeCurrent ->
remember { derivedStateOf { chatModel.getGroupMember(member.groupMemberId) } }.value?.let { mem ->
GroupMemberInfoView(rhId, groupInfo, mem, scrollToItemId, stats, code, chatModel, closeCurrent) {
GroupMemberInfoView(rhId, groupInfo, mem, stats, code, chatModel, closeCurrent) {
closeCurrent()
close()
}
@ -141,17 +140,6 @@ fun ModalData.GroupChatInfoView(
addOrEditWelcomeMessage = {
ModalManager.end.showCustomModal { close -> GroupWelcomeView(chatModel, rhId, groupInfo, close) }
},
openMemberSupport = {
ModalManager.end.showCustomModal { close ->
MemberSupportView(
rhId,
chat,
groupInfo,
scrollToItemId,
close
)
}
},
openPreferences = {
ModalManager.end.showCustomModal { close ->
GroupPreferencesView(
@ -329,40 +317,6 @@ fun AddGroupMembersButton(
)
}
@Composable
fun UserSupportChatButton(
chat: Chat,
groupInfo: GroupInfo,
scrollToItemId: MutableState<Long?>
) {
val scope = rememberCoroutineScope()
SettingsActionItemWithContent(
painterResource(if (chat.supportUnreadCount > 0) MR.images.ic_flag_filled else MR.images.ic_flag),
stringResource(MR.strings.button_support_chat),
click = {
val scopeInfo = GroupChatScopeInfo.MemberSupport(groupMember_ = null)
val supportChatInfo = ChatInfo.Group(groupInfo, groupChatScope = scopeInfo)
scope.launch {
showMemberSupportChatView(
chatModel.chatId,
scrollToItemId = scrollToItemId,
supportChatInfo,
scopeInfo
)
}
},
iconColor = (if (chat.supportUnreadCount > 0) MaterialTheme.colors.primary else MaterialTheme.colors.secondary),
) {
if (chat.supportUnreadCount > 0) {
UnreadBadge(
text = unreadCountStr(chat.supportUnreadCount),
backgroundColor = MaterialTheme.colors.primary
)
}
}
}
@Composable
fun ModalData.GroupChatInfoLayout(
chat: Chat,
@ -383,7 +337,6 @@ fun ModalData.GroupChatInfoLayout(
showMemberInfo: (GroupMember) -> Unit,
editGroupProfile: () -> Unit,
addOrEditWelcomeMessage: () -> Unit,
openMemberSupport: () -> Unit,
openPreferences: () -> Unit,
deleteGroup: () -> Unit,
clearChat: () -> Unit,
@ -469,40 +422,6 @@ fun ModalData.GroupChatInfoLayout(
SectionSpacer()
var anyTopSectionRowShow = false
SectionView {
if (groupInfo.canAddMembers && groupInfo.businessChat == null) {
anyTopSectionRowShow = true
if (groupLink == null) {
CreateGroupLinkButton(manageGroupLink)
} else {
GroupLinkButton(manageGroupLink)
}
}
if (groupInfo.businessChat == null && groupInfo.membership.memberRole >= GroupMemberRole.Moderator) {
anyTopSectionRowShow = true
MemberSupportButton(chat, openMemberSupport)
}
if (groupInfo.canModerate) {
anyTopSectionRowShow = true
GroupReportsButton(chat) {
scope.launch {
showGroupReportsView(chatModel.chatId, scrollToItemId, chat.chatInfo)
}
}
}
if (
groupInfo.membership.memberActive &&
(groupInfo.membership.memberRole < GroupMemberRole.Moderator || groupInfo.membership.supportChat != null)
) {
anyTopSectionRowShow = true
UserSupportChatButton(chat, groupInfo, scrollToItemId)
}
}
if (anyTopSectionRowShow) {
SectionDividerSpaced(maxBottomPadding = false)
}
SectionView {
if (groupInfo.isOwner && groupInfo.businessChat?.chatType == null) {
EditGroupProfileButton(editGroupProfile)
@ -512,17 +431,19 @@ fun ModalData.GroupChatInfoLayout(
}
val prefsTitleId = if (groupInfo.businessChat == null) MR.strings.group_preferences else MR.strings.chat_preferences
GroupPreferencesButton(prefsTitleId, openPreferences)
if (groupInfo.canModerate) {
GroupReportsButton {
scope.launch {
showGroupReportsView(chatModel.chatId, scrollToItemId, chat.chatInfo)
}
}
}
val footerId = if (groupInfo.businessChat == null) MR.strings.only_group_owners_can_change_prefs else MR.strings.only_chat_owners_can_change_prefs
SectionTextFooter(stringResource(footerId))
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
SectionView {
if (activeSortedMembers.filter { it.memberCurrent }.size <= SMALL_GROUPS_RCPS_MEM_LIMIT) {
SendReceiptsOption(currentUser, sendReceipts, setSendReceipts)
} else {
SendReceiptsOptionDisabled()
}
WallpaperButton {
ModalManager.end.showModal {
val chat = remember { derivedStateOf { chatModel.chats.value.firstOrNull { it.id == chat.id } } }
@ -532,13 +453,23 @@ fun ModalData.GroupChatInfoLayout(
}
}
}
ChatTTLOption(chatItemTTL, setChatItemTTL, deletingItems)
SectionTextFooter(stringResource(MR.strings.chat_ttl_options_footer))
}
val footerId = if (groupInfo.businessChat == null) MR.strings.only_group_owners_can_change_prefs else MR.strings.only_chat_owners_can_change_prefs
SectionTextFooter(stringResource(footerId))
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
ChatTTLSection(chatItemTTL, setChatItemTTL, deletingItems)
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = true)
SectionView(title = String.format(generalGetString(MR.strings.group_info_section_title_num_members), activeSortedMembers.count() + 1)) {
if (groupInfo.canAddMembers) {
if (groupInfo.businessChat == null) {
if (groupLink == null) {
CreateGroupLinkButton(manageGroupLink)
} else {
GroupLinkButton(manageGroupLink)
}
}
val onAddMembersClick = if (chat.chatInfo.incognito) ::cantInviteIncognitoAlert else addMembers
val tint = if (chat.chatInfo.incognito) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
val addMembersTitleId = when (groupInfo.businessChat?.chatType) {
@ -550,7 +481,7 @@ fun ModalData.GroupChatInfoLayout(
}
if (activeSortedMembers.size > 8) {
SectionItemView(padding = PaddingValues(start = 14.dp, end = DEFAULT_PADDING_HALF)) {
MemberListSearchRowView(searchText)
SearchRowView(searchText)
}
}
SectionItemView(minHeight = 54.dp, padding = PaddingValues(horizontal = DEFAULT_PADDING)) {
@ -596,7 +527,7 @@ fun ModalData.GroupChatInfoLayout(
val titleId = if (groupInfo.businessChat == null) MR.strings.button_delete_group else MR.strings.button_delete_chat
DeleteGroupButton(titleId, deleteGroup)
}
if (groupInfo.membership.memberCurrentOrPending) {
if (groupInfo.membership.memberCurrent) {
val titleId = if (groupInfo.businessChat == null) MR.strings.button_leave_group else MR.strings.button_leave_chat
LeaveGroupButton(titleId, leaveGroup)
}
@ -700,14 +631,17 @@ private fun SelectedItemsCounterToolbarSetter(
}
@Composable
fun ChatTTLOption(chatItemTTL: State<ChatItemTTL?>, setChatItemTTL: (ChatItemTTL?) -> Unit, deletingItems: State<Boolean>) {
fun ChatTTLSection(chatItemTTL: State<ChatItemTTL?>, setChatItemTTL: (ChatItemTTL?) -> Unit, deletingItems: State<Boolean>) {
Box {
SectionView {
TtlOptions(
chatItemTTL,
enabled = remember { derivedStateOf { !deletingItems.value } },
onSelected = setChatItemTTL,
default = chatModel.chatItemTTL
)
SectionTextFooter(stringResource(MR.strings.chat_ttl_options_footer))
}
if (deletingItems.value) {
Box(Modifier.matchParentSize()) {
ProgressIndicator()
@ -749,23 +683,6 @@ private fun GroupChatInfoHeader(cInfo: ChatInfo, groupInfo: GroupInfo) {
}
}
@Composable
private fun MemberSupportButton(chat: Chat, onClick: () -> Unit) {
SettingsActionItemWithContent(
painterResource(if (chat.supportUnreadCount > 0) MR.images.ic_flag_filled else MR.images.ic_flag),
stringResource(MR.strings.member_support),
click = onClick,
iconColor = (if (chat.supportUnreadCount > 0) MaterialTheme.colors.primary else MaterialTheme.colors.secondary)
) {
if (chat.supportUnreadCount > 0) {
UnreadBadge(
text = unreadCountStr(chat.supportUnreadCount),
backgroundColor = MaterialTheme.colors.primary
)
}
}
}
@Composable
private fun GroupPreferencesButton(titleId: StringResource, onClick: () -> Unit) {
SettingsActionItem(
@ -776,20 +693,12 @@ private fun GroupPreferencesButton(titleId: StringResource, onClick: () -> Unit)
}
@Composable
private fun GroupReportsButton(chat: Chat, onClick: () -> Unit) {
SettingsActionItemWithContent(
painterResource(if (chat.chatStats.reportsCount > 0) MR.images.ic_flag_filled else MR.images.ic_flag),
private fun GroupReportsButton(onClick: () -> Unit) {
SettingsActionItem(
painterResource(MR.images.ic_flag),
stringResource(MR.strings.group_reports_member_reports),
click = onClick,
iconColor = (if (chat.chatStats.reportsCount > 0) Color.Red else MaterialTheme.colors.secondary)
) {
if (chat.chatStats.reportsCount > 0) {
UnreadBadge(
text = unreadCountStr(chat.chatStats.reportsCount),
backgroundColor = Color.Red
click = onClick
)
}
}
}
@Composable
@ -911,7 +820,7 @@ fun MemberRow(member: GroupMember, user: Boolean = false, infoPage: Boolean = tr
}
@Composable
fun MemberVerifiedShield() {
private fun MemberVerifiedShield() {
Icon(painterResource(MR.images.ic_verified_user), null, Modifier.padding(end = 3.dp).size(16.dp), tint = MaterialTheme.colors.secondary)
}
@ -1032,7 +941,7 @@ private fun DeleteGroupButton(titleId: StringResource, onClick: () -> Unit) {
}
@Composable
fun MemberListSearchRowView(
private fun SearchRowView(
searchText: MutableState<TextFieldValue> = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue()) }
) {
Box(Modifier.width(36.dp), contentAlignment = Alignment.Center) {
@ -1055,18 +964,16 @@ private fun setGroupAlias(chat: Chat, localAlias: String, chatModel: ChatModel)
fun removeMembers(rhId: Long?, groupInfo: GroupInfo, memberIds: List<Long>, onSuccess: () -> Unit = {}) {
withBGApi {
val r = chatModel.controller.apiRemoveMembers(rhId, groupInfo.groupId, memberIds)
if (r != null) {
val (updatedGroupInfo, updatedMembers) = r
val updatedMembers = chatModel.controller.apiRemoveMembers(rhId, groupInfo.groupId, memberIds)
if (updatedMembers != null) {
withContext(Dispatchers.Main) {
chatModel.chatsContext.updateGroup(rhId, updatedGroupInfo)
updatedMembers.forEach { updatedMember ->
chatModel.chatsContext.upsertGroupMember(rhId, updatedGroupInfo, updatedMember)
chatModel.chatsContext.upsertGroupMember(rhId, groupInfo, updatedMember)
}
}
withContext(Dispatchers.Main) {
updatedMembers.forEach { updatedMember ->
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, updatedGroupInfo, updatedMember)
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, groupInfo, updatedMember)
}
}
onSuccess()
@ -1109,18 +1016,7 @@ fun PreviewGroupChatInfoLayout() {
selectedItems = remember { mutableStateOf(null) },
appBar = remember { mutableStateOf(null) },
scrollToItemId = remember { mutableStateOf(null) },
addMembers = {},
showMemberInfo = {},
editGroupProfile = {},
addOrEditWelcomeMessage = {},
openMemberSupport = {},
openPreferences = {},
deleteGroup = {},
clearChat = {},
leaveGroup = {},
manageGroupLink = {},
onSearchClicked = {},
deletingItems = remember { mutableStateOf(true) }
addMembers = {}, showMemberInfo = {}, editGroupProfile = {}, addOrEditWelcomeMessage = {}, openPreferences = {}, deleteGroup = {}, clearChat = {}, leaveGroup = {}, manageGroupLink = {}, onSearchClicked = {}, deletingItems = remember { mutableStateOf(true) }
)
}
}

View file

@ -45,7 +45,6 @@ fun GroupMemberInfoView(
rhId: Long?,
groupInfo: GroupInfo,
member: GroupMember,
scrollToItemId: MutableState<Long?>,
connectionStats: ConnectionStats?,
connectionCode: String?,
chatModel: ChatModel,
@ -80,7 +79,6 @@ fun GroupMemberInfoView(
rhId = rhId,
groupInfo,
member,
scrollToItemId,
connStats,
newRole,
developerTools,
@ -245,26 +243,25 @@ fun removeMemberDialog(rhId: Long?, groupInfo: GroupInfo, member: GroupMember, c
text = generalGetString(messageId),
confirmText = generalGetString(MR.strings.remove_member_confirmation),
onConfirm = {
removeMember(rhId, member, chatModel, close)
},
destructive = true,
)
}
fun removeMember(rhId: Long?, member: GroupMember, chatModel: ChatModel, close: (() -> Unit)? = null) {
withBGApi {
val r = chatModel.controller.apiRemoveMembers(rhId, member.groupId, listOf(member.groupMemberId))
if (r != null) {
val (updatedGroupInfo, removedMembers) = r
val removedMembers = chatModel.controller.apiRemoveMembers(rhId, member.groupId, listOf(member.groupMemberId))
if (removedMembers != null) {
withContext(Dispatchers.Main) {
chatModel.chatsContext.updateGroup(rhId, updatedGroupInfo)
removedMembers.forEach { removedMember ->
chatModel.chatsContext.upsertGroupMember(rhId, updatedGroupInfo, removedMember)
chatModel.chatsContext.upsertGroupMember(rhId, groupInfo, removedMember)
}
}
withContext(Dispatchers.Main) {
removedMembers.forEach { removedMember ->
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, groupInfo, removedMember)
}
}
}
close?.invoke()
}
},
destructive = true,
)
}
@Composable
@ -272,7 +269,6 @@ fun GroupMemberInfoLayout(
rhId: Long?,
groupInfo: GroupInfo,
member: GroupMember,
scrollToItemId: MutableState<Long?>,
connStats: MutableState<ConnectionStats?>,
newRole: MutableState<GroupMemberRole>,
developerTools: Boolean,
@ -303,29 +299,6 @@ fun GroupMemberInfoLayout(
}
}
@Composable
fun SupportChatButton() {
val scope = rememberCoroutineScope()
SettingsActionItem(
painterResource(MR.images.ic_flag),
stringResource(MR.strings.button_support_chat_member),
click = {
val scopeInfo = GroupChatScopeInfo.MemberSupport(groupMember_ = member)
val supportChatInfo = ChatInfo.Group(groupInfo, groupChatScope = scopeInfo)
scope.launch {
showMemberSupportChatView(
chatModel.chatId,
scrollToItemId = scrollToItemId,
supportChatInfo,
scopeInfo
)
}
},
iconColor = MaterialTheme.colors.secondary,
)
}
@Composable
fun ModeratorDestructiveSection() {
val canBlockForAll = member.canBlockForAll(groupInfo)
@ -440,12 +413,6 @@ fun GroupMemberInfoLayout(
if (member.memberActive) {
SectionView {
if (
groupInfo.membership.memberRole >= GroupMemberRole.Moderator &&
(member.memberRole < GroupMemberRole.Moderator || member.supportChat != null)
) {
SupportChatButton()
}
if (connectionCode != null) {
VerifyCodeButton(member.verified, verifyClicked)
}
@ -911,7 +878,6 @@ fun PreviewGroupMemberInfoLayout() {
rhId = null,
groupInfo = GroupInfo.sampleData,
member = GroupMember.sampleData,
scrollToItemId = remember { mutableStateOf(null) },
connStats = remember { mutableStateOf(null) },
newRole = remember { mutableStateOf(GroupMemberRole.Member) },
developerTools = false,

View file

@ -44,7 +44,7 @@ fun SelectedItemsMembersToolbar(
) {
// It's hard to measure exact height of ComposeView with different fontSizes. Better to depend on actual ComposeView, even empty
Box(Modifier.alpha(0f)) {
ComposeView(rhId = null, chatModel = chatModel, chatModel.chatsContext, Chat.sampleData, remember { mutableStateOf(ComposeState(useLinkPreviews = false)) }, remember { mutableStateOf(null) }, {}, remember { FocusRequester() })
ComposeView(chatModel = chatModel, Chat.sampleData, remember { mutableStateOf(ComposeState(useLinkPreviews = false)) }, remember { mutableStateOf(null) }, {}, remember { FocusRequester() })
}
Row(
Modifier

View file

@ -35,7 +35,6 @@ private val MAX_PICKER_HEIGHT = (PICKER_ROW_SIZE * 4) + (MEMBER_ROW_AVATAR_SIZE
@Composable
fun GroupMentions(
chatsCtx: ChatModel.ChatsContext,
rhId: Long?,
composeState: MutableState<ComposeState>,
composeViewFocusRequester: FocusRequester?,
@ -49,31 +48,12 @@ fun GroupMentions(
val mentionName = remember { mutableStateOf("") }
val mentionRange = remember { mutableStateOf<TextRange?>(null) }
val mentionMemberId = remember { mutableStateOf<String?>(null) }
fun contextMemberFilter(member: GroupMember): Boolean =
when (chatsCtx.secondaryContextFilter) {
null -> true
is SecondaryContextFilter.GroupChatScopeContext ->
when (chatsCtx.secondaryContextFilter.groupScopeInfo) {
is GroupChatScopeInfo.MemberSupport -> {
val scopeMember = chatsCtx.secondaryContextFilter.groupScopeInfo.groupMember_
if (scopeMember != null) {
member.memberRole >= GroupMemberRole.Moderator || member.groupMemberId == scopeMember.groupMemberId
} else {
member.memberRole >= GroupMemberRole.Moderator
}
}
}
is SecondaryContextFilter.MsgContentTagContext -> false
}
val filteredMembers = remember {
derivedStateOf {
val members = chatModel.groupMembers.value
.filter {
val status = it.memberStatus
status != GroupMemberStatus.MemLeft && status != GroupMemberStatus.MemRemoved && status != GroupMemberStatus.MemInvited
&& contextMemberFilter(it)
}
.sortedByDescending { it.memberRole }

View file

@ -17,9 +17,7 @@ import chat.simplex.common.views.usersettings.PreferenceToggleWithIcon
import chat.simplex.common.model.*
import chat.simplex.common.platform.ColumnWithScrollBar
import chat.simplex.common.platform.chatModel
import chat.simplex.common.views.usersettings.SettingsActionItem
import chat.simplex.res.MR
import dev.icerock.moko.resources.compose.painterResource
import kotlinx.coroutines.*
private val featureRoles: List<Pair<GroupMemberRole?, String>> = listOf(
@ -73,16 +71,6 @@ fun GroupPreferencesView(m: ChatModel, rhId: Long?, chatId: String, close: () ->
preferences = currentPreferences
},
savePrefs = ::savePrefs,
openMemberAdmission = {
ModalManager.end.showCustomModal { close ->
MemberAdmissionView(
chatModel,
rhId,
chatId,
close
)
}
}
)
}
}
@ -95,15 +83,10 @@ private fun GroupPreferencesLayout(
applyPrefs: (FullGroupPreferences) -> Unit,
reset: () -> Unit,
savePrefs: () -> Unit,
openMemberAdmission: () -> Unit,
) {
ColumnWithScrollBar {
val titleId = if (groupInfo.businessChat == null) MR.strings.group_preferences else MR.strings.chat_preferences
AppBarTitle(stringResource(titleId))
if (groupInfo.businessChat == null) {
MemberAdmissionButton(openMemberAdmission)
SectionDividerSpaced(maxBottomPadding = false)
}
val timedMessages = remember(preferences) { mutableStateOf(preferences.timedMessages.enable) }
val onTTLUpdated = { ttl: Int? ->
applyPrefs(preferences.copy(timedMessages = preferences.timedMessages.copy(ttl = ttl)))
@ -173,15 +156,6 @@ private fun GroupPreferencesLayout(
}
}
@Composable
private fun MemberAdmissionButton(onClick: () -> Unit) {
SettingsActionItem(
painterResource(MR.images.ic_toggle_on),
stringResource(MR.strings.member_admission),
click = onClick
)
}
@Composable
private fun FeatureSection(
feature: GroupFeature,

View file

@ -15,15 +15,7 @@ import dev.icerock.moko.resources.compose.stringResource
import kotlinx.coroutines.flow.*
@Composable
private fun GroupReportsView(
reportsChatsCtx: ChatModel.ChatsContext,
staleChatId: State<String?>,
scrollToItemId: MutableState<Long?>,
close: () -> Unit
) {
KeyChangeEffect(chatModel.chatId.value) {
close()
}
private fun GroupReportsView(reportsChatsCtx: ChatModel.ChatsContext, staleChatId: State<String?>, scrollToItemId: MutableState<Long?>) {
ChatView(reportsChatsCtx, staleChatId, scrollToItemId, onComposed = {})
}
@ -61,7 +53,7 @@ fun GroupReportsAppBar(
}
@Composable
fun ItemsReload(chatsCtx: ChatModel.ChatsContext,) {
private fun ItemsReload(chatsCtx: ChatModel.ChatsContext,) {
LaunchedEffect(Unit) {
snapshotFlow { chatModel.chatId.value }
.distinctUntilChanged()
@ -77,13 +69,13 @@ fun ItemsReload(chatsCtx: ChatModel.ChatsContext,) {
}
suspend fun showGroupReportsView(staleChatId: State<String?>, scrollToItemId: MutableState<Long?>, chatInfo: ChatInfo) {
val reportsChatsCtx = ChatModel.ChatsContext(secondaryContextFilter = SecondaryContextFilter.MsgContentTagContext(MsgContentTag.Report))
val reportsChatsCtx = ChatModel.ChatsContext(contentTag = MsgContentTag.Report)
openChat(secondaryChatsCtx = reportsChatsCtx, chatModel.remoteHostId(), chatInfo)
ModalManager.end.showCustomModal(true, id = ModalViewId.SECONDARY_CHAT) { close ->
ModalView({}, showAppBar = false) {
val chatInfo = remember { derivedStateOf { chatModel.chats.value.firstOrNull { it.id == chatModel.chatId.value }?.chatInfo } }.value
if (chatInfo is ChatInfo.Group && chatInfo.groupInfo.canModerate) {
GroupReportsView(reportsChatsCtx, staleChatId, scrollToItemId, close)
GroupReportsView(reportsChatsCtx, staleChatId, scrollToItemId)
} else {
LaunchedEffect(Unit) {
close()

View file

@ -1,152 +0,0 @@
package chat.simplex.common.views.chat.group
import InfoRow
import SectionBottomSpacer
import SectionDividerSpaced
import SectionItemView
import SectionTextFooter
import SectionView
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import dev.icerock.moko.resources.compose.stringResource
import chat.simplex.common.views.helpers.*
import chat.simplex.common.model.*
import chat.simplex.common.platform.ColumnWithScrollBar
import chat.simplex.common.platform.chatModel
import chat.simplex.res.MR
import dev.icerock.moko.resources.StringResource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun MemberAdmissionView(m: ChatModel, rhId: Long?, chatId: String, close: () -> Unit) {
val groupInfo = remember { derivedStateOf {
val ch = m.getChat(chatId)
val g = (ch?.chatInfo as? ChatInfo.Group)?.groupInfo
if (g == null || ch.remoteHostId != rhId) null else g
}}
val gInfo = groupInfo.value ?: return
var admission by rememberSaveable(gInfo, stateSaver = serializableSaver()) { mutableStateOf(gInfo.groupProfile.memberAdmission) }
var currentAdmission by rememberSaveable(gInfo, stateSaver = serializableSaver()) { mutableStateOf(admission) }
fun saveAdmission(afterSave: () -> Unit = {}) {
withBGApi {
val gp = gInfo.groupProfile.copy(memberAdmission = admission)
val g = m.controller.apiUpdateGroup(rhId, gInfo.groupId, gp)
if (g != null) {
withContext(Dispatchers.Main) {
chatModel.chatsContext.updateGroup(rhId, g)
currentAdmission = admission
}
}
afterSave()
}
}
ModalView(
close = {
if (admission == currentAdmission) close()
else showUnsavedChangesAlert({ saveAdmission(close) }, close)
},
) {
MemberAdmissionLayout(
admission,
currentAdmission,
gInfo,
applyAdmission = { admsn ->
admission = admsn
},
reset = {
admission = currentAdmission
},
saveAdmission = ::saveAdmission,
)
}
}
@Composable
private fun MemberAdmissionLayout(
admission: GroupMemberAdmission?,
currentAdmission: GroupMemberAdmission?,
groupInfo: GroupInfo,
applyAdmission: (GroupMemberAdmission) -> Unit,
reset: () -> Unit,
saveAdmission: () -> Unit,
) {
ColumnWithScrollBar {
AppBarTitle(stringResource(MR.strings.member_admission))
val review = remember(admission) { mutableStateOf(admission?.review) }
AdmissionSection(MR.strings.admission_stage_review, MR.strings.admission_stage_review_descr, review, groupInfo) { criteria ->
if (admission != null) {
applyAdmission(admission.copy(review = criteria))
} else {
applyAdmission(GroupMemberAdmission(review = criteria))
}
}
if (groupInfo.isOwner) {
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
ResetSaveButtons(
reset = reset,
save = saveAdmission,
disabled = admission == currentAdmission
)
}
SectionBottomSpacer()
}
}
private val memberCriterias: List<Pair<MemberCriteria?, String>> = listOf(
null to generalGetString(MR.strings.member_criteria_off),
MemberCriteria.All to generalGetString(MR.strings.member_criteria_all)
)
@Composable
private fun AdmissionSection(
admissionStageStrId: StringResource,
admissionStageDescrStrId: StringResource,
memberCriteria: State<MemberCriteria?>,
groupInfo: GroupInfo,
onSelected: (MemberCriteria?) -> Unit
) {
SectionView {
if (groupInfo.isOwner) {
ExposedDropDownSettingRow(
generalGetString(admissionStageStrId),
memberCriterias,
memberCriteria,
onSelected = { value ->
onSelected(value)
}
)
} else {
InfoRow(
stringResource(admissionStageStrId),
memberCriteria.value?.text ?: generalGetString(MR.strings.member_criteria_off)
)
}
}
SectionTextFooter(stringResource( admissionStageDescrStrId))
}
@Composable
private fun ResetSaveButtons(reset: () -> Unit, save: () -> Unit, disabled: Boolean) {
SectionView {
SectionItemView(reset, disabled = disabled) {
Text(stringResource(MR.strings.reset_verb), color = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary)
}
SectionItemView(save, disabled = disabled) {
Text(stringResource(MR.strings.save_and_notify_group_members), color = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary)
}
}
}
private fun showUnsavedChangesAlert(save: () -> Unit, revert: () -> Unit) {
AlertManager.shared.showAlertDialogStacked(
title = generalGetString(MR.strings.save_admission_question),
confirmText = generalGetString(MR.strings.save_and_notify_group_members),
dismissText = generalGetString(MR.strings.exit_without_saving),
onConfirm = save,
onDismiss = revert,
)
}

Some files were not shown because too many files have changed in this diff Show more