ios: edit group profile (#853)
This commit is contained in:
committed by
GitHub
parent
0c58adff08
commit
de0f231c60
@@ -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 {
|
||||
|
||||
@@ -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: []))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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: [])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
140
apps/ios/Shared/Views/Chat/Group/GroupProfileView.swift
Normal file
140
apps/ios/Shared/Views/Chat/Group/GroupProfileView.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ struct UserProfile: View {
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
logger.error("UserProfile apiUpdateProfile error: \(error.localizedDescription)")
|
||||
logger.error("UserProfile apiUpdateProfile error: \(responseError(error))")
|
||||
}
|
||||
editProfile = false
|
||||
}
|
||||
|
||||
@@ -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 */,
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
enableAddressSanitizer = "YES"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user