ios: block member for all (#3708)

* ios: block for all

* member info

* change conditions

* update ui

* blockedByAdmin

* fix

* rework

* fix, comment

* comment

* fix

* fix

* fixes for default

* fix text

* reorder

* fix

* fix

* secondary

* old buttons (revert this)

* blocked by admin text

* revise notification
This commit is contained in:
spaced4ndy 2024-01-20 14:33:36 +04:00 committed by GitHub
parent f188118643
commit 7d2f6a3609
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 249 additions and 36 deletions

View File

@ -1141,6 +1141,12 @@ func apiMemberRole(_ groupId: Int64, _ memberId: Int64, _ memberRole: GroupMembe
throw r
}
func apiBlockMemberForAll(_ groupId: Int64, _ memberId: Int64, _ blocked: Bool) async throws -> GroupMember {
let r = await chatSendCmd(.apiBlockMemberForAll(groupId: groupId, memberId: memberId, blocked: blocked), bgTask: false)
if case let .memberBlockedForAllUser(_, _, member, _) = r { return member }
throw r
}
func leaveGroup(_ groupId: Int64) async {
do {
let groupInfo = try await apiLeaveGroup(groupId)
@ -1680,6 +1686,13 @@ func processReceivedMsg(_ res: ChatResponse) async {
_ = m.upsertGroupMember(groupInfo, member)
}
}
case let .memberBlockedForAll(user, groupInfo, byMember: _, member: member, blocked: _):
if active(user) {
await MainActor.run {
m.updateGroup(groupInfo)
_ = m.upsertGroupMember(groupInfo, member)
}
}
case let .newMemberContactReceivedInv(user, contact, _, _):
if active(user) {
await MainActor.run {

View File

@ -46,7 +46,9 @@ struct FramedItemView: View {
framedItemHeader(icon: "flag", caption: Text("moderated by \(byGroupMember.displayName)").italic())
case .blocked:
framedItemHeader(icon: "hand.raised", caption: Text("blocked").italic())
default:
case .blockedByAdmin:
framedItemHeader(icon: "hand.raised", caption: Text("blocked by admin").italic())
case .deleted:
framedItemHeader(icon: "trash", caption: Text("marked deleted").italic())
}
} else if chatItem.meta.isLive {

View File

@ -33,6 +33,7 @@ struct MarkedDeletedItemView: View {
var i = m.getChatItemIndex(chatItem) {
var moderated = 0
var blocked = 0
var blockedByAdmin = 0
var deleted = 0
var moderatedBy: Set<String> = []
while i < m.reversedChatItems.count,
@ -44,16 +45,19 @@ struct MarkedDeletedItemView: View {
moderated += 1
moderatedBy.insert(byGroupMember.displayName)
case .blocked: blocked += 1
case .blockedByAdmin: blockedByAdmin += 1
case .deleted: deleted += 1
}
i += 1
}
let total = moderated + blocked + deleted
let total = moderated + blocked + blockedByAdmin + deleted
return total <= 1
? markedDeletedText
: total == moderated
? "\(total) messages moderated by \(moderatedBy.joined(separator: ", "))"
: total == blocked
: total == blockedByAdmin
? "\(total) messages blocked by admin"
: total == blocked + blockedByAdmin
? "\(total) messages blocked"
: "\(total) messages marked deleted"
} else {
@ -65,7 +69,8 @@ struct MarkedDeletedItemView: View {
switch chatItem.meta.itemDeleted {
case let .moderated(_, byGroupMember): "moderated by \(byGroupMember.displayName)"
case .blocked: "blocked"
default: "marked deleted"
case .blockedByAdmin: "blocked by admin"
case .deleted, nil: "marked deleted"
}
}
}

View File

@ -109,6 +109,7 @@ struct ChatItemContentView<Content: View>: View {
case let .rcvGroupFeatureRejected(feature): chatFeatureView(feature, .red)
case .sndModerated: deletedItemView()
case .rcvModerated: deletedItemView()
case .rcvBlocked: deletedItemView()
case let .invalidJSON(json): CIInvalidJSONView(json: json)
}
}

View File

@ -36,6 +36,8 @@ struct GroupChatInfoView: View {
case largeGroupReceiptsDisabled
case blockMemberAlert(mem: GroupMember)
case unblockMemberAlert(mem: GroupMember)
case blockForAllAlert(mem: GroupMember)
case unblockForAllAlert(mem: GroupMember)
case removeMemberAlert(mem: GroupMember)
case error(title: LocalizedStringKey, error: LocalizedStringKey)
@ -48,6 +50,8 @@ struct GroupChatInfoView: View {
case .largeGroupReceiptsDisabled: return "largeGroupReceiptsDisabled"
case let .blockMemberAlert(mem): return "blockMemberAlert \(mem.groupMemberId)"
case let .unblockMemberAlert(mem): return "unblockMemberAlert \(mem.groupMemberId)"
case let .blockForAllAlert(mem): return "blockForAllAlert \(mem.groupMemberId)"
case let .unblockForAllAlert(mem): return "unblockForAllAlert \(mem.groupMemberId)"
case let .removeMemberAlert(mem): return "removeMemberAlert \(mem.groupMemberId)"
case let .error(title, _): return "error \(title)"
}
@ -143,6 +147,8 @@ struct GroupChatInfoView: View {
case .largeGroupReceiptsDisabled: return largeGroupReceiptsDisabledAlert()
case let .blockMemberAlert(mem): return blockMemberAlert(groupInfo, mem)
case let .unblockMemberAlert(mem): return unblockMemberAlert(groupInfo, mem)
case let .blockForAllAlert(mem): return blockForAllAlert(groupInfo, mem)
case let .unblockForAllAlert(mem): return unblockForAllAlert(groupInfo, mem)
case let .removeMemberAlert(mem): return removeMemberAlert(mem)
case let .error(title, error): return Alert(title: Text(title), message: Text(error))
}
@ -226,13 +232,10 @@ struct GroupChatInfoView: View {
.foregroundColor(.secondary)
}
Spacer()
let role = member.memberRole
if [.owner, .admin, .observer].contains(role) {
Text(member.memberRole.text)
.foregroundColor(.secondary)
}
memberInfo(member)
}
// revert from this:
if user {
v
} else if member.canBeRemoved(groupInfo: groupInfo) {
@ -240,6 +243,43 @@ struct GroupChatInfoView: View {
} else {
blockSwipe(member, v)
}
// revert to this: vvv
// if user {
// v
// } else if groupInfo.membership.memberRole >= .admin {
// // TODO if there are more actions, refactor with lists of swipeActions
// let canBlockForAll = member.canBlockForAll(groupInfo: groupInfo)
// let canRemove = member.canBeRemoved(groupInfo: groupInfo)
// if canBlockForAll && canRemove {
// removeSwipe(member, blockForAllSwipe(member, v))
// } else if canBlockForAll {
// blockForAllSwipe(member, v)
// } else if canRemove {
// removeSwipe(member, v)
// } else {
// v
// }
// } else {
// if !member.blockedByAdmin {
// blockSwipe(member, v)
// } else {
// v
// }
// }
// ^^^
}
@ViewBuilder private func memberInfo(_ member: GroupMember) -> some View {
if member.blocked {
Text("blocked")
.foregroundColor(.secondary)
} else {
let role = member.memberRole
if [.owner, .admin, .observer].contains(role) {
Text(member.memberRole.text)
.foregroundColor(.secondary)
}
}
}
private func blockSwipe<V: View>(_ member: GroupMember, _ v: V) -> some View {
@ -260,6 +300,24 @@ struct GroupChatInfoView: View {
}
}
private func blockForAllSwipe<V: View>(_ member: GroupMember, _ v: V) -> some View {
v.swipeActions(edge: .leading) {
if member.blockedByAdmin {
Button {
alert = .unblockForAllAlert(mem: member)
} label: {
Label("Unblock for all", systemImage: "hand.raised.slash").foregroundColor(.accentColor)
}
} else {
Button {
alert = .blockForAllAlert(mem: member)
} label: {
Label("Block for all", systemImage: "hand.raised").foregroundColor(.secondary)
}
}
}
}
private func removeSwipe<V: View>(_ member: GroupMember, _ v: V) -> some View {
v.swipeActions(edge: .trailing) {
Button(role: .destructive) {

View File

@ -27,6 +27,8 @@ struct GroupMemberInfoView: View {
enum GroupMemberInfoViewAlert: Identifiable {
case blockMemberAlert(mem: GroupMember)
case unblockMemberAlert(mem: GroupMember)
case blockForAllAlert(mem: GroupMember)
case unblockForAllAlert(mem: GroupMember)
case removeMemberAlert(mem: GroupMember)
case changeMemberRoleAlert(mem: GroupMember, role: GroupMemberRole)
case switchAddressAlert
@ -39,6 +41,8 @@ struct GroupMemberInfoView: View {
switch self {
case let .blockMemberAlert(mem): return "blockMemberAlert \(mem.groupMemberId)"
case let .unblockMemberAlert(mem): return "unblockMemberAlert \(mem.groupMemberId)"
case let .blockForAllAlert(mem): return "blockForAllAlert \(mem.groupMemberId)"
case let .unblockForAllAlert(mem): return "unblockForAllAlert \(mem.groupMemberId)"
case let .removeMemberAlert(mem): return "removeMemberAlert \(mem.groupMemberId)"
case let .changeMemberRoleAlert(mem, role): return "changeMemberRoleAlert \(mem.groupMemberId) \(role.rawValue)"
case .switchAddressAlert: return "switchAddressAlert"
@ -164,6 +168,7 @@ struct GroupMemberInfoView: View {
}
}
// revert from this:
Section {
if member.memberSettings.showMessages {
blockMemberButton(member)
@ -174,6 +179,13 @@ struct GroupMemberInfoView: View {
removeMemberButton(member)
}
}
// revert to this: vvv
// if groupInfo.membership.memberRole >= .admin {
// adminDestructiveSection(member)
// } else {
// nonAdminBlockSection(member)
// }
// ^^^
if developerTools {
Section("For console") {
@ -216,6 +228,8 @@ struct GroupMemberInfoView: View {
switch(alertItem) {
case let .blockMemberAlert(mem): return blockMemberAlert(groupInfo, mem)
case let .unblockMemberAlert(mem): return unblockMemberAlert(groupInfo, mem)
case let .blockForAllAlert(mem): return blockForAllAlert(groupInfo, mem)
case let .unblockForAllAlert(mem): return unblockForAllAlert(groupInfo, mem)
case let .removeMemberAlert(mem): return removeMemberAlert(mem)
case let .changeMemberRoleAlert(mem, _): return changeMemberRoleAlert(mem)
case .switchAddressAlert: return switchAddressAlert(switchMemberAddress)
@ -385,6 +399,55 @@ struct GroupMemberInfoView: View {
}
}
@ViewBuilder private func adminDestructiveSection(_ mem: GroupMember) -> some View {
let canBlockForAll = mem.canBlockForAll(groupInfo: groupInfo)
let canRemove = mem.canBeRemoved(groupInfo: groupInfo)
if canBlockForAll || canRemove {
Section {
if canBlockForAll {
if mem.blockedByAdmin {
unblockForAllButton(mem)
} else {
blockForAllButton(mem)
}
}
if canRemove {
removeMemberButton(mem)
}
}
}
}
private func nonAdminBlockSection(_ mem: GroupMember) -> some View {
Section {
if mem.blockedByAdmin {
Label("Blocked by admin", systemImage: "hand.raised")
.foregroundColor(.secondary)
} else if mem.memberSettings.showMessages {
blockMemberButton(mem)
} else {
unblockMemberButton(mem)
}
}
}
private func blockForAllButton(_ mem: GroupMember) -> some View {
Button(role: .destructive) {
alert = .blockForAllAlert(mem: mem)
} label: {
Label("Block for all", systemImage: "hand.raised")
.foregroundColor(.red)
}
}
private func unblockForAllButton(_ mem: GroupMember) -> some View {
Button {
alert = .unblockForAllAlert(mem: mem)
} label: {
Label("Unblock for all", systemImage: "hand.raised.slash")
}
}
private func blockMemberButton(_ mem: GroupMember) -> some View {
Button(role: .destructive) {
alert = .blockMemberAlert(mem: mem)
@ -560,6 +623,41 @@ func updateMemberSettings(_ gInfo: GroupInfo, _ member: GroupMember, _ memberSet
}
}
func blockForAllAlert(_ gInfo: GroupInfo, _ mem: GroupMember) -> Alert {
Alert(
title: Text("Block member for all?"),
message: Text("All new messages from \(mem.chatViewName) will be hidden!"),
primaryButton: .destructive(Text("Block for all")) {
blockMemberForAll(gInfo, mem, true)
},
secondaryButton: .cancel()
)
}
func unblockForAllAlert(_ gInfo: GroupInfo, _ mem: GroupMember) -> Alert {
Alert(
title: Text("Unblock member for all?"),
message: Text("Messages from \(mem.chatViewName) will be shown!"),
primaryButton: .default(Text("Unblock for all")) {
blockMemberForAll(gInfo, mem, false)
},
secondaryButton: .cancel()
)
}
func blockMemberForAll(_ gInfo: GroupInfo, _ member: GroupMember, _ blocked: Bool) {
Task {
do {
let updatedMember = try await apiBlockMemberForAll(gInfo.groupId, member.groupMemberId, blocked)
await MainActor.run {
_ = ChatModel.shared.upsertGroupMember(gInfo, updatedMember)
}
} catch let error {
logger.error("apiBlockMemberForAll error: \(responseError(error))")
}
}
}
struct GroupMemberInfoView_Previews: PreviewProvider {
static var previews: some View {
GroupMemberInfoView(

View File

@ -55,6 +55,7 @@ public enum ChatCommand {
case apiAddMember(groupId: Int64, contactId: Int64, memberRole: GroupMemberRole)
case apiJoinGroup(groupId: Int64)
case apiMemberRole(groupId: Int64, memberId: Int64, memberRole: GroupMemberRole)
case apiBlockMemberForAll(groupId: Int64, memberId: Int64, blocked: Bool)
case apiRemoveMember(groupId: Int64, memberId: Int64)
case apiLeaveGroup(groupId: Int64)
case apiListMembers(groupId: Int64)
@ -195,6 +196,7 @@ public enum ChatCommand {
case let .apiAddMember(groupId, contactId, memberRole): return "/_add #\(groupId) \(contactId) \(memberRole)"
case let .apiJoinGroup(groupId): return "/_join #\(groupId)"
case let .apiMemberRole(groupId, memberId, memberRole): return "/_member role #\(groupId) \(memberId) \(memberRole.rawValue)"
case let .apiBlockMemberForAll(groupId, memberId, blocked): return "/_block #\(groupId) \(memberId) blocked=\(onOff(blocked))"
case let .apiRemoveMember(groupId, memberId): return "/_remove #\(groupId) \(memberId)"
case let .apiLeaveGroup(groupId): return "/_leave #\(groupId)"
case let .apiListMembers(groupId): return "/_members #\(groupId)"
@ -334,6 +336,7 @@ public enum ChatCommand {
case .apiAddMember: return "apiAddMember"
case .apiJoinGroup: return "apiJoinGroup"
case .apiMemberRole: return "apiMemberRole"
case .apiBlockMemberForAll: return "apiBlockMemberForAll"
case .apiRemoveMember: return "apiRemoveMember"
case .apiLeaveGroup: return "apiLeaveGroup"
case .apiListMembers: return "apiListMembers"
@ -566,6 +569,8 @@ public enum ChatResponse: Decodable, Error {
case joinedGroupMemberConnecting(user: UserRef, groupInfo: GroupInfo, hostMember: GroupMember, member: GroupMember)
case memberRole(user: UserRef, groupInfo: GroupInfo, byMember: GroupMember, member: GroupMember, fromRole: GroupMemberRole, toRole: GroupMemberRole)
case memberRoleUser(user: UserRef, groupInfo: GroupInfo, member: GroupMember, fromRole: GroupMemberRole, toRole: GroupMemberRole)
case memberBlockedForAll(user: UserRef, groupInfo: GroupInfo, byMember: GroupMember, member: GroupMember, blocked: Bool)
case memberBlockedForAllUser(user: UserRef, groupInfo: GroupInfo, member: GroupMember, blocked: Bool)
case deletedMemberUser(user: UserRef, groupInfo: GroupInfo, member: GroupMember)
case deletedMember(user: UserRef, groupInfo: GroupInfo, byMember: GroupMember, deletedMember: GroupMember)
case leftMember(user: UserRef, groupInfo: GroupInfo, member: GroupMember)
@ -716,6 +721,8 @@ public enum ChatResponse: Decodable, Error {
case .joinedGroupMemberConnecting: return "joinedGroupMemberConnecting"
case .memberRole: return "memberRole"
case .memberRoleUser: return "memberRoleUser"
case .memberBlockedForAll: return "memberBlockedForAll"
case .memberBlockedForAllUser: return "memberBlockedForAllUser"
case .deletedMemberUser: return "deletedMemberUser"
case .deletedMember: return "deletedMember"
case .leftMember: return "leftMember"
@ -864,6 +871,8 @@ public enum ChatResponse: Decodable, Error {
case let .joinedGroupMemberConnecting(u, groupInfo, hostMember, member): return withUser(u, "groupInfo: \(groupInfo)\nhostMember: \(hostMember)\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 .memberRoleUser(u, groupInfo, member, fromRole, toRole): return withUser(u, "groupInfo: \(groupInfo)\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 .memberBlockedForAllUser(u, groupInfo, member, blocked): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)\nblocked: \(blocked)")
case let .deletedMemberUser(u, groupInfo, member): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)")
case let .deletedMember(u, groupInfo, byMember, deletedMember): return withUser(u, "groupInfo: \(groupInfo)\nbyMember: \(byMember)\ndeletedMember: \(deletedMember)")
case let .leftMember(u, groupInfo, member): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)")

View File

@ -1814,6 +1814,7 @@ public struct GroupMember: Identifiable, Decodable {
public var memberCategory: GroupMemberCategory
public var memberStatus: GroupMemberStatus
public var memberSettings: GroupMemberSettings
public var blockedByAdmin: Bool
public var invitedBy: InvitedBy
public var localDisplayName: ContactName
public var memberProfile: LocalProfile
@ -1833,6 +1834,7 @@ public struct GroupMember: Identifiable, Decodable {
public var image: String? { get { memberProfile.image } }
public var contactLink: String? { get { memberProfile.contactLink } }
public var verified: Bool { activeConn?.connectionCode != nil }
public var blocked: Bool { blockedByAdmin || !memberSettings.showMessages }
var directChatId: ChatId? {
get {
@ -1899,7 +1901,7 @@ public struct GroupMember: Identifiable, Decodable {
public func canBeRemoved(groupInfo: GroupInfo) -> Bool {
let userRole = groupInfo.membership.memberRole
return memberStatus != .memRemoved && memberStatus != .memLeft
&& userRole >= .admin && userRole >= memberRole && groupInfo.membership.memberCurrent
&& userRole >= .admin && userRole >= memberRole && groupInfo.membership.memberActive
}
public func canChangeRoleTo(groupInfo: GroupInfo) -> [GroupMemberRole]? {
@ -1908,6 +1910,12 @@ public struct GroupMember: Identifiable, Decodable {
return GroupMemberRole.allCases.filter { $0 <= userRole && $0 != .author }
}
public func canBlockForAll(groupInfo: GroupInfo) -> Bool {
let userRole = groupInfo.membership.memberRole
return memberStatus != .memRemoved && memberStatus != .memLeft && memberRole < .admin
&& userRole >= .admin && userRole >= memberRole && groupInfo.membership.memberActive
}
public var memberIncognito: Bool {
memberProfile.profileId != memberContactProfileId
}
@ -1920,6 +1928,7 @@ public struct GroupMember: Identifiable, Decodable {
memberCategory: .inviteeMember,
memberStatus: .memComplete,
memberSettings: GroupMemberSettings(showMessages: true),
blockedByAdmin: false,
invitedBy: .user,
localDisplayName: "alice",
memberProfile: LocalProfile.sampleData,
@ -2184,6 +2193,7 @@ public struct ChatItem: Identifiable, Decodable {
case .rcvDeleted: return true
case .sndModerated: return true
case .rcvModerated: return true
case .rcvBlocked: return true
default: return false
}
}
@ -2228,22 +2238,18 @@ public struct ChatItem: Identifiable, Decodable {
}
}
private var showNtfDir: Bool {
return !chatDir.sent
}
public var showNotification: Bool {
switch content {
case .sndMsgContent: return showNtfDir
case .rcvMsgContent: return showNtfDir
case .sndDeleted: return showNtfDir
case .rcvDeleted: return showNtfDir
case .sndCall: return showNtfDir
case .sndMsgContent: return false
case .rcvMsgContent: return meta.itemDeleted == nil
case .sndDeleted: return false
case .rcvDeleted: return false
case .sndCall: return false
case .rcvCall: return false // notification is shown on .callInvitation instead
case .rcvIntegrityError: return showNtfDir
case .rcvDecryptionError: return showNtfDir
case .rcvGroupInvitation: return showNtfDir
case .sndGroupInvitation: return showNtfDir
case .rcvIntegrityError: return false
case .rcvDecryptionError: return false
case .rcvGroupInvitation: return true
case .sndGroupInvitation: return false
case .rcvDirectEvent(rcvDirectEvent: let rcvDirectEvent):
switch rcvDirectEvent {
case .contactDeleted: return false
@ -2254,9 +2260,10 @@ public struct ChatItem: Identifiable, Decodable {
case .groupUpdated: return false
case .memberConnected: return false
case .memberRole: return false
case .userRole: return showNtfDir
case .userDeleted: return showNtfDir
case .groupDeleted: return showNtfDir
case .memberBlocked: return false
case .userRole: return true
case .userDeleted: return true
case .groupDeleted: return true
case .memberAdded: return false
case .memberLeft: return false
case .memberDeleted: return false
@ -2264,19 +2271,20 @@ public struct ChatItem: Identifiable, Decodable {
case .memberCreatedContact: return false
case .memberProfileUpdated: return false
}
case .sndGroupEvent: return showNtfDir
case .sndGroupEvent: return false
case .rcvConnEvent: return false
case .sndConnEvent: return showNtfDir
case .sndConnEvent: return false
case .rcvChatFeature: return false
case .sndChatFeature: return showNtfDir
case .sndChatFeature: return false
case .rcvChatPreference: return false
case .sndChatPreference: return showNtfDir
case .sndChatPreference: return false
case .rcvGroupFeature: return false
case .sndGroupFeature: return showNtfDir
case .rcvChatFeatureRejected: return showNtfDir
case .rcvGroupFeatureRejected: return showNtfDir
case .sndModerated: return true
case .rcvModerated: return true
case .sndGroupFeature: return false
case .rcvChatFeatureRejected: return true
case .rcvGroupFeatureRejected: return false
case .sndModerated: return false
case .rcvModerated: return false
case .rcvBlocked: return false
case .invalidJSON: return false
}
}
@ -2663,12 +2671,14 @@ public enum SndCIStatusProgress: String, Decodable {
public enum CIDeleted: Decodable {
case deleted(deletedTs: Date?)
case blocked(deletedTs: Date?)
case blockedByAdmin(deletedTs: Date?)
case moderated(deletedTs: Date?, byGroupMember: GroupMember)
var id: String {
switch self {
case .deleted: return "deleted"
case .blocked: return "blocked"
case .blockedByAdmin: return "blocked by admin"
case .moderated: return "moderated"
}
}
@ -2709,6 +2719,7 @@ public enum CIContent: Decodable, ItemContent {
case rcvGroupFeatureRejected(groupFeature: GroupFeature)
case sndModerated
case rcvModerated
case rcvBlocked
case invalidJSON(json: String)
public var text: String {
@ -2739,6 +2750,7 @@ public enum CIContent: Decodable, ItemContent {
case let .rcvGroupFeatureRejected(groupFeature): return String.localizedStringWithFormat("%@: received, prohibited", groupFeature.text)
case .sndModerated: return NSLocalizedString("moderated", comment: "moderated chat item")
case .rcvModerated: return NSLocalizedString("moderated", comment: "moderated chat item")
case .rcvBlocked: return NSLocalizedString("blocked by admin", comment: "blocked chat item")
case .invalidJSON: return NSLocalizedString("invalid data", comment: "invalid chat item")
}
}
@ -2777,6 +2789,7 @@ public enum CIContent: Decodable, ItemContent {
case .rcvDecryptionError: return true
case .rcvGroupInvitation: return true
case .rcvModerated: return true
case .rcvBlocked: return true
case .invalidJSON: return true
default: return false
}
@ -3463,6 +3476,7 @@ public enum RcvGroupEvent: Decodable {
case memberConnected
case memberLeft
case memberRole(groupMemberId: Int64, profile: Profile, role: GroupMemberRole)
case memberBlocked(groupMemberId: Int64, profile: Profile, blocked: Bool)
case userRole(role: GroupMemberRole)
case memberDeleted(groupMemberId: Int64, profile: Profile)
case userDeleted
@ -3480,6 +3494,12 @@ public enum RcvGroupEvent: Decodable {
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)
case let .memberBlocked(_, profile, blocked):
if blocked {
return String.localizedStringWithFormat(NSLocalizedString("blocked %@", comment: "rcv group event chat item"), profile.profileViewName)
} else {
return String.localizedStringWithFormat(NSLocalizedString("unblocked %@", comment: "rcv group event chat item"), profile.profileViewName)
}
case let .userRole(role):
return String.localizedStringWithFormat(NSLocalizedString("changed your role to %@", comment: "rcv group event chat item"), role.text)
case let .memberDeleted(_, profile):
@ -3510,6 +3530,7 @@ public enum RcvGroupEvent: Decodable {
public enum SndGroupEvent: Decodable {
case memberRole(groupMemberId: Int64, profile: Profile, role: GroupMemberRole)
case userRole(role: GroupMemberRole)
case memberBlocked(groupMemberId: Int64, profile: Profile, blocked: Bool)
case memberDeleted(groupMemberId: Int64, profile: Profile)
case userLeft
case groupUpdated(groupProfile: GroupProfile)
@ -3520,6 +3541,12 @@ public enum SndGroupEvent: Decodable {
return String.localizedStringWithFormat(NSLocalizedString("you changed role of %@ to %@", comment: "snd group event chat item"), profile.profileViewName, role.text)
case let .userRole(role):
return String.localizedStringWithFormat(NSLocalizedString("you changed role for yourself to %@", comment: "snd group event chat item"), role.text)
case let .memberBlocked(_, profile, blocked):
if blocked {
return String.localizedStringWithFormat(NSLocalizedString("you blocked %@", comment: "snd group event chat item"), profile.profileViewName)
} else {
return String.localizedStringWithFormat(NSLocalizedString("you unblocked %@", comment: "snd group event chat item"), profile.profileViewName)
}
case let .memberDeleted(_, profile):
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")