Add participant list in conversation info fragment

This commit is contained in:
Benoit Martins 2024-11-07 18:09:23 +01:00
parent 75588af0e2
commit a839c7d643
8 changed files with 304 additions and 30 deletions

View file

@ -1466,6 +1466,23 @@
}
}
},
"conversation_info_add_participants_label" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Add participants"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Ajouter des membres"
}
}
}
},
"conversation_info_confirm_start_group_call_dialog_message" : {
"extractionState" : "manual",
"localizations" : {
@ -1517,6 +1534,23 @@
}
}
},
"conversation_info_menu_add_to_contacts" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Add to contacts"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Ajouter aux contacts"
}
}
}
},
"conversation_info_menu_go_to_contact" : {
"extractionState" : "manual",
"localizations" : {
@ -1534,6 +1568,40 @@
}
}
},
"conversation_info_participant_is_admin_label" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Admin"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Administrateur"
}
}
}
},
"conversation_info_participants_list_title" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Group members"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Membres du groupe"
}
}
}
},
"conversation_invalid_participant_due_to_security_mode_toast" : {
"extractionState" : "manual",
"localizations" : {

View file

@ -37,6 +37,8 @@ struct CallView: View {
@ObservedObject var conversationViewModel: ConversationViewModel
@ObservedObject var conversationsListViewModel: ConversationsListViewModel
@ObservedObject var conversationForwardMessageViewModel: ConversationForwardMessageViewModel
@ObservedObject var contactViewModel: ContactViewModel
@ObservedObject var editContactViewModel: EditContactViewModel
@State private var addParticipantsViewModel: AddParticipantsViewModel?
@ -71,6 +73,9 @@ struct CallView: View {
@State var buttonSize = 60.0
@Binding var isShowEditContactFragment: Bool
@Binding var indexPage: Int
var body: some View {
GeometryReader { geo in
ZStack {
@ -200,8 +205,12 @@ struct CallView: View {
conversationViewModel: conversationViewModel,
conversationsListViewModel: conversationsListViewModel,
conversationForwardMessageViewModel: conversationForwardMessageViewModel,
contactViewModel: contactViewModel,
editContactViewModel: editContactViewModel,
isShowConversationFragment: $isShowConversationFragment,
isShowStartCallGroupPopup: $isShowStartCallGroupPopup
isShowStartCallGroupPopup: $isShowStartCallGroupPopup,
isShowEditContactFragment: $isShowEditContactFragment,
indexPage: $indexPage
)
.frame(maxWidth: .infinity)
.background(Color.gray100)
@ -2825,10 +2834,14 @@ struct PressedButtonStyle: ButtonStyle {
conversationViewModel: ConversationViewModel(),
conversationsListViewModel: ConversationsListViewModel(),
conversationForwardMessageViewModel: ConversationForwardMessageViewModel(),
contactViewModel: ContactViewModel(),
editContactViewModel: EditContactViewModel(),
fullscreenVideo: .constant(false),
isShowStartCallFragment: .constant(false),
isShowConversationFragment: .constant(false),
isShowStartCallGroupPopup: .constant(false)
isShowStartCallGroupPopup: .constant(false),
isShowEditContactFragment: .constant(false),
indexPage: .constant(0)
)
}
// swiftlint:enable type_body_length

View file

@ -21,7 +21,7 @@ import Foundation
import linphonesw
import Combine
class ContactAvatarModel: ObservableObject {
class ContactAvatarModel: ObservableObject, Identifiable {
let friend: Friend?

View file

@ -862,8 +862,12 @@ struct ContentView: View {
conversationViewModel: conversationViewModel,
conversationsListViewModel: conversationsListViewModel,
conversationForwardMessageViewModel: conversationForwardMessageViewModel,
contactViewModel: contactViewModel,
editContactViewModel: editContactViewModel,
isShowConversationFragment: $isShowConversationFragment,
isShowStartCallGroupPopup: $isShowStartCallGroupPopup
isShowStartCallGroupPopup: $isShowStartCallGroupPopup,
isShowEditContactFragment: $isShowEditContactFragment,
indexPage: $index
)
.frame(maxWidth: .infinity)
.background(Color.gray100)
@ -1200,10 +1204,14 @@ struct ContentView: View {
conversationViewModel: conversationViewModel,
conversationsListViewModel: conversationsListViewModel,
conversationForwardMessageViewModel: conversationForwardMessageViewModel,
contactViewModel: contactViewModel,
editContactViewModel: editContactViewModel,
fullscreenVideo: $fullscreenVideo,
isShowStartCallFragment: $isShowStartCallFragment,
isShowConversationFragment: $isShowConversationFragment,
isShowStartCallGroupPopup: $isShowStartCallGroupPopup
isShowStartCallGroupPopup: $isShowStartCallGroupPopup,
isShowEditContactFragment: $isShowEditContactFragment,
indexPage: $index
)
.zIndex(5)
.transition(.scale.combined(with: .move(edge: .top)))

View file

@ -33,6 +33,8 @@ struct ConversationFragment: View {
@ObservedObject var conversationViewModel: ConversationViewModel
@ObservedObject var conversationsListViewModel: ConversationsListViewModel
@ObservedObject var conversationForwardMessageViewModel: ConversationForwardMessageViewModel
@ObservedObject var contactViewModel: ContactViewModel
@ObservedObject var editContactViewModel: EditContactViewModel
@State var isMenuOpen = false
@State private var isMuted: Bool = false
@ -63,6 +65,9 @@ struct ConversationFragment: View {
@State private var selectedCategoryIndex = 0
@Binding var isShowEditContactFragment: Bool
@Binding var indexPage: Int
var body: some View {
NavigationView {
GeometryReader { geometry in
@ -977,10 +982,14 @@ struct ConversationFragment: View {
ConversationInfoFragment(
conversationViewModel: conversationViewModel,
conversationsListViewModel: conversationsListViewModel,
contactViewModel: contactViewModel,
editContactViewModel: editContactViewModel,
isMuted: $isMuted,
isShowEphemeralFragment: $isShowEphemeralFragment,
isShowStartCallGroupPopup: $isShowStartCallGroupPopup,
isShowInfoConversationFragment: $isShowInfoConversationFragment
isShowInfoConversationFragment: $isShowInfoConversationFragment,
isShowEditContactFragment: $isShowEditContactFragment,
indexPage: $indexPage
)
.zIndex(5)
.transition(.move(edge: .trailing))

View file

@ -22,15 +22,22 @@ import SwiftUI
struct ConversationInfoFragment: View {
@State private var orientation = UIDevice.current.orientation
@ObservedObject var contactsManager = ContactsManager.shared
@ObservedObject private var sharedMainViewModel = SharedMainViewModel.shared
@ObservedObject var conversationViewModel: ConversationViewModel
@ObservedObject var conversationsListViewModel: ConversationsListViewModel
@ObservedObject var contactViewModel: ContactViewModel
@ObservedObject var editContactViewModel: EditContactViewModel
@Binding var isMuted: Bool
@Binding var isShowEphemeralFragment: Bool
@Binding var isShowStartCallGroupPopup: Bool
@Binding var isShowInfoConversationFragment: Bool
@Binding var isShowEditContactFragment: Bool
@Binding var indexPage: Int
@State private var participantListIsOpen = true
var body: some View {
NavigationView {
@ -87,7 +94,7 @@ struct ConversationInfoFragment: View {
.frame(maxWidth: .infinity)
.padding(.top, 10)
Text(conversationViewModel.displayedConversation!.avatarModel.address)
Text(conversationViewModel.participantConversationModel.first?.address ?? "")
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
@ -115,12 +122,29 @@ struct ConversationInfoFragment: View {
Avatar(contactAvatarModel: conversationViewModel.displayedConversation!.avatarModel, avatarSize: 100)
.padding(.top, 4)
Text(conversationViewModel.displayedConversation!.avatarModel.name)
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 10)
HStack {
Text(conversationViewModel.displayedConversation!.avatarModel.name)
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
.padding(.top, 10)
if conversationViewModel.isUserAdmin {
Button(
action: {
},
label: {
Image("pencil-simple")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.orangeMain500)
.frame(width: 20, height: 20)
}
)
.padding(.top, 10)
}
}
.padding(.leading, conversationViewModel.isUserAdmin ? 20 : 0)
}
}
.frame(minHeight: 150)
@ -219,6 +243,91 @@ struct ConversationInfoFragment: View {
.background(Color.gray100)
}
if conversationViewModel.displayedConversation!.isGroup {
HStack(alignment: .center) {
Text("conversation_info_participants_list_title")
.default_text_style_800(styleSize: 18)
.frame(maxWidth: .infinity, alignment: .leading)
Spacer()
Image(participantListIsOpen ? "caret-up" : "caret-down")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.grayMain2c600)
.frame(width: 25, height: 25, alignment: .leading)
.padding(.all, 10)
}
.padding(.top, 30)
.padding(.bottom, 10)
.padding(.horizontal, 20)
.background(Color.gray100)
.onTapGesture {
withAnimation {
participantListIsOpen.toggle()
}
}
if participantListIsOpen {
VStack(spacing: 0) {
ForEach(conversationViewModel.participantConversationModel) { participantConversationModel in
HStack {
Avatar(contactAvatarModel: participantConversationModel, avatarSize: 50)
VStack {
Text(participantConversationModel.name)
.foregroundStyle(Color.grayMain2c700)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
if conversationViewModel.participantConversationModelAdmin != nil && participantConversationModel.address == conversationViewModel.participantConversationModelAdmin!.address {
Text("conversation_info_participant_is_admin_label")
.foregroundStyle(Color.grayMain2c400)
.default_text_style(styleSize: 12)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
}
}
}
.padding(.vertical, 15)
.padding(.horizontal, 20)
}
if conversationViewModel.isUserAdmin {
Button(
action: {
},
label: {
HStack {
Image("plus-circle")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.orangeMain500)
.frame(width: 20, height: 20)
Text("conversation_info_add_participants_label")
.default_text_style_orange_500(styleSize: 14)
.frame(height: 35)
}
}
)
.padding(.horizontal, 20)
.padding(.vertical, 5)
.background(Color.orangeMain100)
.cornerRadius(60)
.padding(.top, 10)
.padding(.bottom, 20)
}
}
.background(.white)
.cornerRadius(15)
.padding(.horizontal)
.zIndex(-1)
.transition(.move(edge: .top))
}
}
Text("contact_details_actions_title")
.default_text_style_800(styleSize: 18)
.frame(maxWidth: .infinity, alignment: .leading)
@ -230,20 +339,60 @@ struct ConversationInfoFragment: View {
if !conversationViewModel.displayedConversation!.isGroup {
Button(
action: {
if conversationViewModel.displayedConversation != nil {
let addressConv = conversationViewModel.participantConversationModel.first?.address ?? ""
let friendIndex = contactsManager.lastSearch.firstIndex(
where: {$0.friend!.addresses.contains(where: {$0.asStringUriOnly() == addressConv})})
if friendIndex != nil {
withAnimation {
conversationViewModel.displayedConversation = nil
indexPage = 0
contactViewModel.indexDisplayedFriend = friendIndex
}
} else {
withAnimation {
conversationViewModel.displayedConversation = nil
indexPage = 0
isShowEditContactFragment.toggle()
editContactViewModel.sipAddresses.removeAll()
editContactViewModel.sipAddresses.append(String(conversationViewModel.participantConversationModel.first?.address.dropFirst(4) ?? ""))
editContactViewModel.sipAddresses.append("")
}
}
}
},
label: {
HStack {
Image("address-book")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.grayMain2c600)
.frame(width: 25, height: 25)
Text("conversation_info_menu_go_to_contact")
.default_text_style(styleSize: 16)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
let addressConv = conversationViewModel.participantConversationModel.first?.address ?? ""
let friendIndex = contactsManager.lastSearch.firstIndex(
where: {$0.friend!.addresses.contains(where: {$0.asStringUriOnly() == addressConv})})
if friendIndex != nil {
Image("address-book")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.grayMain2c600)
.frame(width: 25, height: 25)
Text("conversation_info_menu_go_to_contact")
.default_text_style(styleSize: 16)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
} else {
Image("user-plus")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.grayMain2c600)
.frame(width: 25, height: 25)
Text("conversation_info_menu_add_to_contacts")
.default_text_style(styleSize: 16)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
}
}
}
)
@ -361,9 +510,13 @@ struct ConversationInfoFragment: View {
ConversationInfoFragment(
conversationViewModel: ConversationViewModel(),
conversationsListViewModel: ConversationsListViewModel(),
contactViewModel: ContactViewModel(),
editContactViewModel: EditContactViewModel(),
isMuted: .constant(false),
isShowEphemeralFragment: .constant(false),
isShowStartCallGroupPopup: .constant(false),
isShowInfoConversationFragment: .constant(true)
isShowInfoConversationFragment: .constant(true),
isShowEditContactFragment: .constant(false),
indexPage: .constant(0)
)
}

View file

@ -76,6 +76,8 @@ class ConversationViewModel: ObservableObject {
@Published var conversationMessagesSection: [MessagesSection] = []
@Published var participantConversationModel: [ContactAvatarModel] = []
@Published var participantConversationModelAdmin: ContactAvatarModel?
@Published var isUserAdmin: Bool = false
@Published var mediasToSend: [Attachment] = []
var maxMediaCount = 12
@ -310,12 +312,24 @@ class ConversationViewModel: ObservableObject {
func getParticipantConversationModel() {
coreContext.doOnCoreQueue { _ in
if self.displayedConversation != nil {
DispatchQueue.main.async {
self.isUserAdmin = false
self.participantConversationModelAdmin = nil
self.participantConversationModel.removeAll()
}
self.displayedConversation!.chatRoom.participants.forEach { participant in
if participant.address != nil {
ContactAvatarModel.getAvatarModelFromAddress(address: participant.address!) { avatarResult in
let avatarModelTmp = avatarResult
DispatchQueue.main.async {
self.participantConversationModel.append(avatarModelTmp)
if participant.isAdmin {
DispatchQueue.main.async {
self.participantConversationModelAdmin = avatarModelTmp
self.participantConversationModel.append(avatarModelTmp)
}
} else {
DispatchQueue.main.async {
self.participantConversationModel.append(avatarModelTmp)
}
}
}
}
@ -324,8 +338,16 @@ class ConversationViewModel: ObservableObject {
if self.displayedConversation!.chatRoom.me != nil {
ContactAvatarModel.getAvatarModelFromAddress(address: self.displayedConversation!.chatRoom.me!.address!) { avatarResult in
let avatarModelTmp = avatarResult
DispatchQueue.main.async {
self.participantConversationModel.append(avatarModelTmp)
if self.displayedConversation!.chatRoom.me!.isAdmin {
DispatchQueue.main.async {
self.isUserAdmin = true
self.participantConversationModelAdmin = avatarModelTmp
self.participantConversationModel.append(avatarModelTmp)
}
} else {
DispatchQueue.main.async {
self.participantConversationModel.append(avatarModelTmp)
}
}
}
}
@ -338,7 +360,9 @@ class ConversationViewModel: ObservableObject {
ContactAvatarModel.getAvatarModelFromAddress(address: address) { avatarResult in
let avatarModelTmp = avatarResult
DispatchQueue.main.async {
self.participantConversationModel.append(avatarModelTmp)
if self.participantConversationModel.first(where: {$0.address == avatarModelTmp.address}) == nil {
self.participantConversationModel.append(avatarModelTmp)
}
}
}
}

View file

@ -86,7 +86,6 @@ struct HistoryContactFragment: View {
let friendIndex = contactsManager.lastSearch.firstIndex(
where: {$0.friend!.addresses.contains(where: {$0.asStringUriOnly() == addressCall!.asStringUriOnly()})})
if friendIndex != nil {
withAnimation {
historyViewModel.displayedCall = nil
indexPage = 0