ios: edit group profile (#853)

This commit is contained in:
Evgeny Poberezkin
2022-07-30 13:03:44 +01:00
committed by GitHub
parent 0c58adff08
commit de0f231c60
12 changed files with 239 additions and 56 deletions

View File

@@ -621,6 +621,20 @@ func apiListMembers(_ groupId: Int64) async -> [GroupMember] {
return []
}
func filterMembersToAdd(_ ms: [GroupMember]) -> [Contact] {
let memberContactIds = ms.compactMap{ m in m.memberCurrent ? m.memberContactId : nil }
return ChatModel.shared.chats
.compactMap{ $0.chatInfo.contact }
.filter{ !memberContactIds.contains($0.apiId) }
.sorted{ $0.displayName.lowercased() < $1.displayName.lowercased() }
}
func apiUpdateGroup(_ groupId: Int64, _ groupProfile: GroupProfile) async throws -> GroupInfo {
let r = await chatSendCmd(.apiUpdateGroupProfile(groupId: groupId, groupProfile: groupProfile))
if case let .groupUpdated(toGroup) = r { return toGroup }
throw r
}
func initializeChat(start: Bool) throws {
logger.debug("initializeChat")
do {

View File

@@ -44,8 +44,8 @@ private func serverHost(_ s: String) -> String {
struct ChatInfoView: View {
@EnvironmentObject var chatModel: ChatModel
@Environment(\.dismiss) var dismiss: DismissAction
@ObservedObject var chat: Chat
@Binding var showSheet: Bool
@State private var alert: ChatInfoViewAlert? = nil
@State private var connectionStats: ConnectionStats?
@@ -172,7 +172,7 @@ struct ChatInfoView: View {
try await apiDeleteChat(type: chat.chatInfo.chatType, id: chat.chatInfo.apiId)
await MainActor.run {
chatModel.removeChat(chat.chatInfo.id)
showSheet = false
dismiss()
}
} catch let error {
logger.error("deleteContactAlert apiDeleteChat error: \(error.localizedDescription)")
@@ -190,9 +190,7 @@ struct ChatInfoView: View {
primaryButton: .destructive(Text("Clear")) {
Task {
await clearChat(chat)
await MainActor.run {
showSheet = false
}
await MainActor.run { dismiss() }
}
},
secondaryButton: .cancel()
@@ -209,7 +207,6 @@ struct ChatInfoView: View {
struct ChatInfoView_Previews: PreviewProvider {
static var previews: some View {
@State var showSheet = true
return ChatInfoView(chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []), showSheet: $showSheet)
ChatInfoView(chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []))
}
}

View File

@@ -17,6 +17,7 @@ struct ChatView: View {
@ObservedObject var chat: Chat
@State private var showChatInfoSheet: Bool = false
@State private var showAddMembersSheet: Bool = false
@State private var membersToAdd: [Contact] = []
@State private var composeState = ComposeState()
@State private var deletingItem: ChatItem? = nil
@FocusState private var keyboardVisible: Bool
@@ -105,25 +106,32 @@ struct ChatView: View {
ChatInfoToolbar(chat: chat)
}
.sheet(isPresented: $showChatInfoSheet) {
if case .direct = chat.chatInfo {
ChatInfoView(chat: chat, showSheet: $showChatInfoSheet)
} else if case let .group(groupInfo) = chat.chatInfo {
GroupChatInfoView(chat: chat, groupInfo: groupInfo, showSheet: $showChatInfoSheet)
switch cInfo {
case .direct:
ChatInfoView(chat: chat)
case let .group(groupInfo):
GroupChatInfoView(chat: chat, groupInfo: groupInfo)
default:
EmptyView()
}
}
}
ToolbarItem(placement: .navigationBarTrailing) {
if case let .direct(contact) = cInfo {
switch cInfo {
case let .direct(contact):
HStack {
callButton(contact, .audio, imageName: "phone")
callButton(contact, .video, imageName: "video")
}
} else if case let .group(groupInfo) = chat.chatInfo,
groupInfo.canAddMembers {
addMembersButton()
.sheet(isPresented: $showAddMembersSheet) {
AddGroupMembersView(chat: chat, groupInfo: groupInfo, showSheet: $showAddMembersSheet)
}
case let .group(groupInfo):
if groupInfo.canAddMembers {
addMembersButton()
.sheet(isPresented: $showAddMembersSheet) {
AddGroupMembersView(chat: chat, groupInfo: groupInfo, membersToAdd: membersToAdd)
}
}
default:
EmptyView()
}
}
}
@@ -140,7 +148,15 @@ struct ChatView: View {
private func addMembersButton() -> some View {
Button {
showAddMembersSheet = true
if case let .group(gInfo) = chat.chatInfo {
Task {
let ms = await apiListMembers(gInfo.apiId)
await MainActor.run {
membersToAdd = filterMembersToAdd(ms)
showAddMembersSheet = true
}
}
}
} label: {
Image(systemName: "person.crop.circle.badge.plus")
}

View File

@@ -11,10 +11,10 @@ import SimpleXChat
struct AddGroupMembersView: View {
@EnvironmentObject var chatModel: ChatModel
@Environment(\.dismiss) var dismiss: DismissAction
var chat: Chat
var groupInfo: GroupInfo
@Binding var showSheet: Bool
@State private var contactsToAdd: [Contact] = []
@State var membersToAdd: [Contact]
@State private var selectedContacts = Set<Int64>()
@State private var selectedRole: GroupMemberRole = .admin
@@ -26,7 +26,7 @@ struct AddGroupMembersView: View {
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
if (contactsToAdd.isEmpty) {
if (membersToAdd.isEmpty) {
Text("No contacts to add")
.foregroundColor(.secondary)
.padding()
@@ -52,7 +52,7 @@ struct AddGroupMembersView: View {
}
Section {
ForEach(contactsToAdd) { contact in
ForEach(membersToAdd) { contact in
contactCheckView(contact)
}
}
@@ -61,18 +61,6 @@ struct AddGroupMembersView: View {
.navigationBarHidden(true)
}
.frame(maxHeight: .infinity, alignment: .top)
.task {
contactsToAdd = await getContactsToAdd()
}
}
func getContactsToAdd() async -> [Contact] {
let ms = await apiListMembers(chat.chatInfo.apiId)
let memberContactIds = ms.compactMap{ m in m.memberCurrent ? m.memberContactId : nil }
return chatModel.chats
.compactMap{ $0.chatInfo.contact }
.filter{ !memberContactIds.contains($0.apiId) }
.sorted{ $0.displayName.lowercased() < $1.displayName.lowercased() }
}
func inviteMembersButton() -> some View {
@@ -81,7 +69,7 @@ struct AddGroupMembersView: View {
for contactId in selectedContacts {
await addMember(groupId: chat.chatInfo.apiId, contactId: contactId, memberRole: selectedRole)
}
showSheet = false
await MainActor.run { dismiss() }
}
} label: {
HStack {
@@ -128,7 +116,6 @@ struct AddGroupMembersView: View {
struct AddGroupMembersView_Previews: PreviewProvider {
static var previews: some View {
@State var showSheet = true
return AddGroupMembersView(chat: Chat(chatInfo: ChatInfo.sampleData.group), groupInfo: GroupInfo.sampleData, showSheet: $showSheet)
AddGroupMembersView(chat: Chat(chatInfo: ChatInfo.sampleData.group), groupInfo: GroupInfo.sampleData, membersToAdd: [])
}
}

View File

@@ -126,6 +126,6 @@ struct GroupMemberInfoView: View {
struct GroupMemberInfoView_Previews: PreviewProvider {
static var previews: some View {
return GroupMemberInfoView(groupInfo: GroupInfo.sampleData, member: GroupMember.sampleData)
GroupMemberInfoView(groupInfo: GroupInfo.sampleData, member: GroupMember.sampleData)
}
}

View File

@@ -0,0 +1,140 @@
//
// GroupProfileView.swift
// SimpleX (iOS)
//
// Created by Evgeny on 29/07/2022.
// Copyright © 2022 SimpleX Chat. All rights reserved.
//
import SwiftUI
import SimpleXChat
struct GroupProfileView: View {
@EnvironmentObject var chatModel: ChatModel
@Environment(\.dismiss) var dismiss: DismissAction
var groupId: Int64
@State var groupProfile: GroupProfile
@State private var showChooseSource = false
@State private var showImagePicker = false
@State private var showTakePhoto = false
@State private var chosenImage: UIImage? = nil
@State private var showSaveErrorAlert = false
@State private var saveGroupError: String? = nil
@FocusState private var focusDisplayName
var body: some View {
return VStack(alignment: .leading) {
Text("Group profile is stored on members devices.\nSimpleX servers cannot see group profile.")
.padding(.bottom)
ZStack(alignment: .center) {
ZStack(alignment: .topTrailing) {
profileImageView(groupProfile.image)
if groupProfile.image != nil {
Button {
groupProfile.image = nil
} label: {
Image(systemName: "multiply")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 12)
}
}
}
editImageButton { showChooseSource = true }
}
.frame(maxWidth: .infinity, alignment: .center)
VStack(alignment: .leading) {
ZStack(alignment: .leading) {
if !validDisplayName(groupProfile.displayName) {
Image(systemName: "exclamationmark.circle")
.foregroundColor(.red)
.padding(.bottom, 10)
}
profileNameTextEdit("Group display name", $groupProfile.displayName)
.focused($focusDisplayName)
}
profileNameTextEdit("Group full name (optional)", $groupProfile.fullName)
HStack(spacing: 20) {
Button("Cancel") { dismiss() }
Button("Save") { saveProfile() }
.disabled(groupProfile.displayName == "" || !validDisplayName(groupProfile.displayName))
}
}
.frame(maxWidth: .infinity, minHeight: 120, alignment: .leading)
}
.padding()
.frame(maxHeight: .infinity, alignment: .top)
.confirmationDialog("Group image", isPresented: $showChooseSource, titleVisibility: .visible) {
Button("Take picture") {
showTakePhoto = true
}
Button("Choose from library") {
showImagePicker = true
}
}
.fullScreenCover(isPresented: $showTakePhoto) {
ZStack {
Color.black.edgesIgnoringSafeArea(.all)
CameraImagePicker(image: $chosenImage)
}
}
.sheet(isPresented: $showImagePicker) {
LibraryImagePicker(image: $chosenImage) {
didSelectItem in showImagePicker = false
}
}
.onChange(of: chosenImage) { image in
if let image = image {
groupProfile.image = resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500)
} else {
groupProfile.image = nil
}
}
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
focusDisplayName = true
}
}
.alert(isPresented: $showSaveErrorAlert) {
Alert(
title: Text("Error saving group profile"),
message: Text("\(saveGroupError ?? "Unexpected error")")
)
}
}
func profileNameTextEdit(_ label: String, _ name: Binding<String>) -> some View {
TextField(label, text: name)
.textInputAutocapitalization(.never)
.disableAutocorrection(true)
.padding(.bottom)
.padding(.leading, 28)
}
func saveProfile() {
Task {
do {
let gInfo = try await apiUpdateGroup(groupId, groupProfile)
await MainActor.run {
chatModel.updateGroup(gInfo)
dismiss()
}
} catch let error {
let err = responseError(error)
saveGroupError = err
showSaveErrorAlert = true
logger.error("UserProfile apiUpdateProfile error: \(err)")
}
}
}
}
struct GroupProfileView_Previews: PreviewProvider {
static var previews: some View {
GroupProfileView(groupId: 1, groupProfile: GroupProfile.sampleData)
}
}

View File

@@ -11,14 +11,15 @@ import SimpleXChat
struct GroupChatInfoView: View {
@EnvironmentObject var chatModel: ChatModel
@ObservedObject var alertManager = AlertManager.shared
@Environment(\.dismiss) var dismiss: DismissAction
@ObservedObject var chat: Chat
var groupInfo: GroupInfo
@Binding var showSheet: Bool
@ObservedObject private var alertManager = AlertManager.shared
@State private var members: [GroupMember] = []
@State private var alert: GroupChatInfoViewAlert? = nil
@State private var showAddMembersSheet: Bool = false
@State private var selectedMember: GroupMember? = nil
@State private var showGroupProfile: Bool = false
enum GroupChatInfoViewAlert: Identifiable {
case deleteGroupAlert
@@ -34,7 +35,18 @@ struct GroupChatInfoView: View {
groupInfoHeader()
.listRowBackground(Color.clear)
Section(header: Text("\(members.count + 1) members")) {
Section {
Button {
showGroupProfile = true
} label: {
Label("Edit group profile", systemImage: "pencil")
}
}
.sheet(isPresented: $showGroupProfile) {
GroupProfileView(groupId: groupInfo.apiId, groupProfile: groupInfo.groupProfile)
}
Section("\(members.count + 1) members") {
if (groupInfo.canAddMembers) {
addMembersButton()
}
@@ -44,7 +56,7 @@ struct GroupChatInfoView: View {
}
}
.sheet(isPresented: $showAddMembersSheet) {
AddGroupMembersView(chat: chat, groupInfo: groupInfo, showSheet: $showAddMembersSheet)
AddGroupMembersView(chat: chat, groupInfo: groupInfo, membersToAdd: filterMembersToAdd(members))
}
.sheet(item: $selectedMember) { member in
GroupMemberInfoView(groupInfo: groupInfo, member: member)
@@ -76,8 +88,10 @@ struct GroupChatInfoView: View {
}
}
.task {
members = await apiListMembers(chat.chatInfo.apiId)
.sorted{ $0.displayName.lowercased() < $1.displayName.lowercased() } // TODO owner first
let ms = await apiListMembers(chat.chatInfo.apiId)
await MainActor.run {
members = ms.sorted { $0.displayName.lowercased() < $1.displayName.lowercased() }
}
}
}
@@ -178,7 +192,7 @@ struct GroupChatInfoView: View {
try await apiDeleteChat(type: chat.chatInfo.chatType, id: chat.chatInfo.apiId)
await MainActor.run {
chatModel.removeChat(chat.chatInfo.id)
showSheet = false
dismiss()
}
} catch let error {
logger.error("deleteGroupAlert apiDeleteChat error: \(error.localizedDescription)")
@@ -196,9 +210,7 @@ struct GroupChatInfoView: View {
primaryButton: .destructive(Text("Clear")) {
Task {
await clearChat(chat)
await MainActor.run {
showSheet = false
}
await MainActor.run { dismiss() }
}
},
secondaryButton: .cancel()
@@ -212,9 +224,7 @@ struct GroupChatInfoView: View {
primaryButton: .destructive(Text("Leave")) {
Task {
await leaveGroup(chat.chatInfo.apiId)
await MainActor.run {
showSheet = false
}
await MainActor.run { dismiss() }
}
},
secondaryButton: .cancel()
@@ -224,7 +234,6 @@ struct GroupChatInfoView: View {
struct GroupChatInfoView_Previews: PreviewProvider {
static var previews: some View {
@State var showSheet = true
return GroupChatInfoView(chat: Chat(chatInfo: ChatInfo.sampleData.group, chatItems: []), groupInfo: GroupInfo.sampleData, showSheet: $showSheet)
GroupChatInfoView(chat: Chat(chatInfo: ChatInfo.sampleData.group, chatItems: []), groupInfo: GroupInfo.sampleData)
}
}

View File

@@ -146,7 +146,7 @@ struct UserProfile: View {
}
}
} catch {
logger.error("UserProfile apiUpdateProfile error: \(error.localizedDescription)")
logger.error("UserProfile apiUpdateProfile error: \(responseError(error))")
}
editProfile = false
}

View File

@@ -55,6 +55,7 @@
5C9C2DA128929B6900CC63B1 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9C2D9C28929B6900CC63B1 /* libgmp.a */; };
5C9C2DA228929B6900CC63B1 /* libHSsimplex-chat-3.1.0-FNUbBjLYHjnDjt6ldpTolw-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9C2D9D28929B6900CC63B1 /* libHSsimplex-chat-3.1.0-FNUbBjLYHjnDjt6ldpTolw-ghc8.10.7.a */; };
5C9C2DA328929B6900CC63B1 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9C2D9E28929B6900CC63B1 /* libgmpxx.a */; };
5C9C2DA52894777E00CC63B1 /* GroupProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9C2DA42894777E00CC63B1 /* GroupProfileView.swift */; };
5C9D13A3282187BB00AB8B43 /* WebRTC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9D13A2282187BB00AB8B43 /* WebRTC.swift */; };
5C9FD96E27A5D6ED0075386C /* SendMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9FD96D27A5D6ED0075386C /* SendMessageView.swift */; };
5CA059DC279559F40002BEB4 /* Tests_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059DB279559F40002BEB4 /* Tests_iOS.swift */; };
@@ -234,6 +235,7 @@
5C9C2D9C28929B6900CC63B1 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5C9C2D9D28929B6900CC63B1 /* libHSsimplex-chat-3.1.0-FNUbBjLYHjnDjt6ldpTolw-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-3.1.0-FNUbBjLYHjnDjt6ldpTolw-ghc8.10.7.a"; sourceTree = "<group>"; };
5C9C2D9E28929B6900CC63B1 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5C9C2DA42894777E00CC63B1 /* GroupProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupProfileView.swift; sourceTree = "<group>"; };
5C9D13A2282187BB00AB8B43 /* WebRTC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRTC.swift; sourceTree = "<group>"; };
5C9FD96A27A56D4D0075386C /* JSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSON.swift; sourceTree = "<group>"; };
5C9FD96D27A5D6ED0075386C /* SendMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMessageView.swift; sourceTree = "<group>"; };
@@ -632,6 +634,7 @@
children = (
6440CA02288AECA70062C672 /* AddGroupMembersView.swift */,
647F090D288EA27B00644C40 /* GroupMemberInfoView.swift */,
5C9C2DA42894777E00CC63B1 /* GroupProfileView.swift */,
);
path = Group;
sourceTree = "<group>";
@@ -891,6 +894,7 @@
5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */,
649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */,
5CB346E52868AA7F001FD2EF /* SuspendChat.swift in Sources */,
5C9C2DA52894777E00CC63B1 /* GroupProfileView.swift in Sources */,
5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */,
5C764E89279CBCB3000C6508 /* ChatModel.swift in Sources */,
5C971E1D27AEBEF600C8A3CE /* ChatInfoView.swift in Sources */,

View File

@@ -44,6 +44,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableAddressSanitizer = "YES"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"

View File

@@ -40,6 +40,7 @@ public enum ChatCommand {
case apiRemoveMember(groupId: Int64, memberId: Int64)
case apiLeaveGroup(groupId: Int64)
case apiListMembers(groupId: Int64)
case apiUpdateGroupProfile(groupId: Int64, groupProfile: GroupProfile)
case getUserSMPServers
case setUserSMPServers(smpServers: [String])
case apiSetNetworkConfig(networkConfig: NetCfg)
@@ -101,6 +102,7 @@ public enum ChatCommand {
case let .apiRemoveMember(groupId, memberId): return "/_remove #\(groupId) \(memberId)"
case let .apiLeaveGroup(groupId): return "/_leave #\(groupId)"
case let .apiListMembers(groupId): return "/_members #\(groupId)"
case let .apiUpdateGroupProfile(groupId, groupProfile): return "/_group_profile #\(groupId) \(encodeJSON(groupProfile))"
case .getUserSMPServers: return "/smp_servers"
case let .setUserSMPServers(smpServers): return "/smp_servers \(smpServersStr(smpServers: smpServers))"
case let .apiSetNetworkConfig(networkConfig): return "/_network \(encodeJSON(networkConfig))"
@@ -162,6 +164,7 @@ public enum ChatCommand {
case .apiRemoveMember: return "apiRemoveMember"
case .apiLeaveGroup: return "apiLeaveGroup"
case .apiListMembers: return "apiListMembers"
case .apiUpdateGroupProfile: return "apiUpdateGroupProfile"
case .getUserSMPServers: return "getUserSMPServers"
case .setUserSMPServers: return "setUserSMPServers"
case .apiSetNetworkConfig: return "apiSetNetworkConfig"
@@ -270,6 +273,7 @@ public enum ChatResponse: Decodable, Error {
case joinedGroupMember(groupInfo: GroupInfo, member: GroupMember)
case connectedToGroupMember(groupInfo: GroupInfo, member: GroupMember)
case groupRemoved(groupInfo: GroupInfo)
case groupUpdated(toGroup: GroupInfo)
// receiving file events
case rcvFileAccepted(chatItem: AChatItem)
case rcvFileStart(chatItem: AChatItem)
@@ -359,6 +363,7 @@ public enum ChatResponse: Decodable, Error {
case .joinedGroupMember: return "joinedGroupMember"
case .connectedToGroupMember: return "connectedToGroupMember"
case .groupRemoved: return "groupRemoved"
case .groupUpdated: return "groupUpdated"
case .rcvFileAccepted: return "rcvFileAccepted"
case .rcvFileStart: return "rcvFileStart"
case .rcvFileComplete: return "rcvFileComplete"
@@ -449,6 +454,7 @@ public enum ChatResponse: Decodable, Error {
case let .joinedGroupMember(groupInfo, member): return "groupInfo: \(groupInfo)\nmember: \(member)"
case let .connectedToGroupMember(groupInfo, member): return "groupInfo: \(groupInfo)\nmember: \(member)"
case let .groupRemoved(groupInfo): return String(describing: groupInfo)
case let .groupUpdated(toGroup): return String(describing: toGroup)
case let .rcvFileAccepted(chatItem): return String(describing: chatItem)
case let .rcvFileStart(chatItem): return String(describing: chatItem)
case let .rcvFileComplete(chatItem): return String(describing: chatItem)

View File

@@ -421,12 +421,17 @@ public enum ConnStatus: String, Decodable {
public struct Group: Decodable {
public var groupInfo: GroupInfo
public var members: [GroupMember]
public init(groupInfo: GroupInfo, members: [GroupMember]) {
self.groupInfo = groupInfo
self.members = members
}
}
public struct GroupInfo: Identifiable, Decodable, NamedChat {
public var groupId: Int64
var localDisplayName: GroupName
var groupProfile: GroupProfile
public var groupProfile: GroupProfile
public var membership: GroupMember
var createdAt: Date
var updatedAt: Date
@@ -1261,6 +1266,7 @@ public enum RcvGroupEvent: Decodable {
case memberDeleted(groupMemberId: Int64, profile: Profile)
case userDeleted
case groupDeleted
case groupUpdated(groupProfile: GroupProfile)
var text: String {
switch self {
@@ -1272,6 +1278,7 @@ public enum RcvGroupEvent: Decodable {
return String.localizedStringWithFormat(NSLocalizedString("removed %@", comment: "rcv group event chat item"), profile.displayNameWithOptionalFullName)
case .userDeleted: return NSLocalizedString("removed you", comment: "rcv group event chat item")
case .groupDeleted: return NSLocalizedString("deleted group", comment: "rcv group event chat item")
case .groupUpdated: return NSLocalizedString("updated group profile", comment: "rcv group event chat item")
}
}
}
@@ -1279,12 +1286,14 @@ public enum RcvGroupEvent: Decodable {
public enum SndGroupEvent: Decodable {
case memberDeleted(groupMemberId: Int64, profile: Profile)
case userLeft
case groupUpdated(groupProfile: GroupProfile)
var text: String {
switch self {
case let .memberDeleted(_, profile):
return String.localizedStringWithFormat(NSLocalizedString("you removed %@", comment: "snd group event chat item"), profile.displayNameWithOptionalFullName)
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")
}
}
}