diff --git a/Linphone/Assets.xcassets/video-conference.imageset/Contents.json b/Linphone/Assets.xcassets/video-conference.imageset/Contents.json new file mode 100644 index 000000000..6ec75ca93 --- /dev/null +++ b/Linphone/Assets.xcassets/video-conference.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "video-conference.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/video-conference.imageset/video-conference.svg b/Linphone/Assets.xcassets/video-conference.imageset/video-conference.svg new file mode 100644 index 000000000..5f7f30001 --- /dev/null +++ b/Linphone/Assets.xcassets/video-conference.imageset/video-conference.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/UI/Main/ContentView.swift b/Linphone/UI/Main/ContentView.swift index 5e76f7dcc..38802c523 100644 --- a/Linphone/UI/Main/ContentView.swift +++ b/Linphone/UI/Main/ContentView.swift @@ -241,7 +241,7 @@ struct ContentView: View { conversationViewModel.displayedConversation = nil }, label: { VStack { - Image("meetings") + Image("video-conference") .renderingMode(.template) .resizable() .foregroundStyle(self.index == 3 ? Color.orangeMain500 : Color.grayMain2c600) @@ -669,7 +669,7 @@ struct ContentView: View { conversationViewModel.displayedConversation = nil }, label: { VStack { - Image("meetings") + Image("video-conference") .renderingMode(.template) .resizable() .foregroundStyle(self.index == 3 ? Color.orangeMain500 : Color.grayMain2c600) @@ -698,7 +698,9 @@ struct ContentView: View { } } - if contactViewModel.indexDisplayedFriend != nil || historyViewModel.displayedCall != nil || conversationViewModel.displayedConversation != nil { + if contactViewModel.indexDisplayedFriend != nil || historyViewModel.displayedCall != nil || conversationViewModel.displayedConversation != nil || + scheduleMeetingViewModel.displayedMeeting != nil + { HStack(spacing: 0) { Spacer() .frame(maxWidth: @@ -739,7 +741,13 @@ struct ContentView: View { .frame(maxWidth: .infinity) .background(Color.gray100) .ignoresSafeArea(.keyboard) + } else if self.index == 3 { + MeetingFragment(scheduleMeetingViewModel: scheduleMeetingViewModel, meetingsListViewModel: meetingsListViewModel, isShowScheduleMeetingFragment: $isShowScheduleMeetingFragment) + .frame(maxWidth: .infinity) + .background(Color.gray100) + .ignoresSafeArea(.keyboard) } + } .onAppear { if !(orientation == .landscapeLeft diff --git a/Linphone/UI/Main/Meetings/Fragments/MeetingFragment.swift b/Linphone/UI/Main/Meetings/Fragments/MeetingFragment.swift index cce87d668..e1d58118b 100644 --- a/Linphone/UI/Main/Meetings/Fragments/MeetingFragment.swift +++ b/Linphone/UI/Main/Meetings/Fragments/MeetingFragment.swift @@ -17,19 +17,254 @@ * along with this program. If not, see . */ +// swiftlint:disable line_length import SwiftUI +import linphonesw struct MeetingFragment: View { - @ObservedObject var meetingViewModel: MeetingViewModel + @Environment(\.dismiss) var dismiss + + private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom } + @State private var orientation = UIDevice.current.orientation + + @ObservedObject var scheduleMeetingViewModel: ScheduleMeetingViewModel + @ObservedObject var meetingsListViewModel: MeetingsListViewModel + + @State private var showDatePicker = false + @State private var showTimePicker = false + + @State var selectedDate = Date.now + @State var setFromDate: Bool = true + @State var selectedHours: Int = 0 + @State var selectedMinutes: Int = 0 + + @State var addParticipantsViewModel = AddParticipantsViewModel() + @Binding var isShowScheduleMeetingFragment: Bool + + @ViewBuilder + func getParticipantLine(participant: SelectedAddressModel) -> some View { + HStack(spacing: 0) { + Avatar(contactAvatarModel: participant.avatarModel, avatarSize: 50) + .padding(.leading, 10) + + Text(participant.avatarModel.name) + .default_text_style(styleSize: 14) + .padding(.leading, 10) + .padding(.trailing, 40) + + Text("Organizer") + .font(Font.custom("NotoSans-Light", size: 12)) + .foregroundStyle(Color.grayMain2c600) + .opacity(participant.isOrganizer ? 1 : 0) + }.padding(.bottom, 5) + } var body: some View { - ZStack { - Text("TODO") + NavigationView { + ZStack(alignment: .bottomTrailing) { + VStack(spacing: 0) { + if #available(iOS 16.0, *) { + Rectangle() + .foregroundColor(Color.orangeMain500) + .edgesIgnoringSafeArea(.top) + .frame(height: 0) + } else if idiom != .pad && !(orientation == .landscapeLeft || orientation == .landscapeRight + || UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) { + Rectangle() + .foregroundColor(Color.orangeMain500) + .edgesIgnoringSafeArea(.top) + .frame(height: 1) + } + + HStack { + Image("caret-left") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.orangeMain500) + .frame(width: 25, height: 25, alignment: .leading) + .padding(.all, 10) + .padding(.leading, -10) + .onTapGesture { + withAnimation { + scheduleMeetingViewModel.displayedMeeting = nil + } + } + Spacer() + if scheduleMeetingViewModel.myself != nil && scheduleMeetingViewModel.myself!.isOrganizer { + Image("pencil-simple") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.orangeMain500) + .frame(width: 25, height: 25, alignment: .leading) + .padding(.trailing, 5) + .onTapGesture { + withAnimation { + isShowScheduleMeetingFragment.toggle() + } + } + } + Image("dots-three-vertical") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.orangeMain500) + .frame(width: 25, height: 25, alignment: .leading) + .onTapGesture { + } + } + .frame(maxWidth: .infinity) + .frame(height: 50) + .padding(.horizontal) + .padding(.bottom, 5) + .background(.white) + + ScrollView(.vertical) { + HStack(alignment: .center, spacing: 10) { + Image("video-conference") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c800) + .frame(width: 24, height: 24) + .padding(.leading, 15) + Text(scheduleMeetingViewModel.subject) + .fontWeight(.bold) + .default_text_style(styleSize: 20) + .frame(height: 29, alignment: .leading) + Spacer() + }.padding(.bottom, 5) + + Rectangle() + .foregroundStyle(.clear) + .frame(height: 1) + .background(Color.gray200) + + HStack(alignment: .center, spacing: 10) { + Image("video-camera") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c800) + .frame(width: 24, height: 24) + .padding(.leading, 15) + Text(scheduleMeetingViewModel.conferenceUri) + .underline() + .default_text_style(styleSize: 14) + Spacer() + + Image("share-network") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c800) + .frame(width: 25, height: 25) + .padding(.trailing, 15) + } + + HStack(alignment: .center, spacing: 10) { + Image("clock") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c800) + .frame(width: 24, height: 24) + .padding(.leading, 15) + Text(scheduleMeetingViewModel.getFullDateString()) + .default_text_style(styleSize: 14) + Spacer() + } + + HStack(alignment: .center, spacing: 10) { + Image("earth") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c800) + .frame(width: 24, height: 24) + .padding(.leading, 15) + Text("TODO : timezone") + .default_text_style(styleSize: 14) + Spacer() + } + + Rectangle() + .foregroundStyle(.clear) + .frame(height: 1) + .background(Color.gray200) + + HStack(alignment: .top, spacing: 10) { + Image("note") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c600) + .frame(width: 24, height: 24) + .padding(.leading, 15) + + Text(scheduleMeetingViewModel.description) + .default_text_style(styleSize: 14) + Spacer() + }.padding(.top, 10) + .padding(.bottom, 10) + + Rectangle() + .foregroundStyle(.clear) + .frame(height: 1) + .background(Color.gray200) + + HStack(alignment: .top, spacing: 10) { + Image("users") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c600) + .frame(width: 24, height: 24) + .padding(.leading, 15) + + ScrollView { + VStack(alignment: .leading, spacing: 0) { + if scheduleMeetingViewModel.myself != nil { + getParticipantLine(participant: scheduleMeetingViewModel.myself!) + } + ForEach(0.. some View { return Text(model.monthStr) .fontWeight(.bold) @@ -24,6 +25,7 @@ struct MeetingsFragment: View { .default_text_style_500(styleSize: 22) } + @ViewBuilder func createWeekLine(model: MeetingsListItemModel) -> some View { return Text(model.weekStr) .padding(.leading, 43) @@ -31,7 +33,7 @@ struct MeetingsFragment: View { .padding(.bottom, 3) .default_text_style_500(styleSize: 14) } - + @ViewBuilder func createMeetingLine(model: MeetingsListItemModel) -> some View { return VStack(alignment: .leading) { if model.isToday { @@ -40,7 +42,7 @@ struct MeetingsFragment: View { .default_text_style_500(styleSize: 15) } else { HStack(alignment: .center) { - Image("meetings") + Image("video-conference") .renderingMode(.template) .resizable() .foregroundStyle(Color.grayMain2c600) @@ -64,12 +66,10 @@ struct MeetingsFragment: View { .clipShape(RoundedRectangle(cornerRadius: 20)) .shadow(color: .black.opacity(0.2), radius: 4) .onTapGesture { - do { - let meetingAddress = try Factory.Instance.createAddress(addr: model.model?.address ?? "") - TelecomManager.shared.meetingWaitingRoomDisplayed = true - TelecomManager.shared.meetingWaitingRoomSelected = meetingAddress - } catch { - Log.error("[MeetingsFragment] Couldn't create address from \(model.model?.address ?? "")") + withAnimation { + if let meetingModel = model.model { + scheduleMeetingViewModel.loadExistingMeeting(meeting: meetingModel) + } } } } diff --git a/Linphone/UI/Main/Meetings/Fragments/ScheduleMeetingFragment.swift b/Linphone/UI/Main/Meetings/Fragments/ScheduleMeetingFragment.swift index b72a803ba..3e3a65890 100644 --- a/Linphone/UI/Main/Meetings/Fragments/ScheduleMeetingFragment.swift +++ b/Linphone/UI/Main/Meetings/Fragments/ScheduleMeetingFragment.swift @@ -74,11 +74,15 @@ struct ScheduleMeetingFragment: View { .padding(.leading, -10) .onTapGesture { withAnimation { + if let meeting = scheduleMeetingViewModel.displayedMeeting { + // reload meeting to cancel change from edit + scheduleMeetingViewModel.loadExistingMeeting(meeting: meeting) + } isShowScheduleMeetingFragment.toggle() } } - Text("New meeting" ) + Text("\(scheduleMeetingViewModel.displayedMeeting != nil ? "Edit" : "New") meeting" ) .multilineTextAlignment(.leading) .default_text_style_orange_800(styleSize: 16) @@ -143,10 +147,10 @@ struct ScheduleMeetingFragment: View { } */ HStack(alignment: .center, spacing: 8) { - Image("users-three") + Image("video-conference") .renderingMode(.template) .resizable() - .foregroundStyle(Color.grayMain2c600) + .foregroundStyle(Color.grayMain2c800) .frame(width: 24, height: 24) .padding(.leading, 16) TextField("Subject", text: $scheduleMeetingViewModel.subject) diff --git a/Linphone/UI/Main/Meetings/Models/MeetingModel.swift b/Linphone/UI/Main/Meetings/Models/MeetingModel.swift index fd7abac7a..ba89195dd 100644 --- a/Linphone/UI/Main/Meetings/Models/MeetingModel.swift +++ b/Linphone/UI/Main/Meetings/Models/MeetingModel.swift @@ -9,9 +9,10 @@ import linphonesw class MeetingModel: ObservableObject { - private var confInfo: ConferenceInfo + var confInfo: ConferenceInfo var id: String var meetingDate: Date + var endDate: Date var isToday: Bool var isAfterToday: Bool @@ -24,7 +25,6 @@ class MeetingModel: ObservableObject { @Published var isBroadcast: Bool @Published var subject: String @Published var address: String - @Published var firstMeetingOfTheDay: Bool = false init(conferenceInfo: ConferenceInfo) { confInfo = conferenceInfo @@ -34,7 +34,7 @@ class MeetingModel: ObservableObject { let formatter = DateFormatter() formatter.dateFormat = Locale.current.identifier == "fr_FR" ? "HH:mm" : "h:mm a" startTime = formatter.string(from: meetingDate) - let endDate = Calendar.current.date(byAdding: .minute, value: Int(confInfo.duration), to: meetingDate)! + endDate = Calendar.current.date(byAdding: .minute, value: Int(confInfo.duration), to: meetingDate)! endTime = formatter.string(from: endDate) time = "\(startTime) - \(endTime)" diff --git a/Linphone/UI/Main/Meetings/ViewModel/MeetingViewModel.swift b/Linphone/UI/Main/Meetings/ViewModel/MeetingViewModel.swift index 12e16a223..681bef225 100644 --- a/Linphone/UI/Main/Meetings/ViewModel/MeetingViewModel.swift +++ b/Linphone/UI/Main/Meetings/ViewModel/MeetingViewModel.swift @@ -22,7 +22,7 @@ import linphonesw class MeetingViewModel: ObservableObject { static let TAG = "[Meeting ViewModel]" - + /* private var coreContext = CoreContext.shared @Published var showBackbutton: Bool = false @@ -40,20 +40,20 @@ class MeetingViewModel: ObservableObject { @Published var participants: [ParticipantModel] = [] @Published var conferenceInfoFoundEvent: Bool = false - var conferenceInfo: ConferenceInfo? + var meetingModel: MeetingModel - init() { - + init(model: MeetingModel) { + meetingModel = model } func findConferenceInfo(uri: String) { coreContext.doOnCoreQueue { core in var confInfoFound = false if let address = try? Factory.Instance.createAddress(addr: uri) { - let foundConfInfo = core.findConferenceInformationFromUri(uri: address) - if foundConfInfo != nil { + + if let confInfo = core.findConferenceInformationFromUri(uri: address) { Log.info("\(MeetingViewModel.TAG) Conference info with SIP URI \(uri) was found") - self.conferenceInfo = foundConfInfo + self.meetingModel.confInfo = confInfo self.configureConferenceInfo(core: core) confInfoFound = true } else { @@ -71,68 +71,65 @@ class MeetingViewModel: ObservableObject { } private func configureConferenceInfo(core: Core) { - if let confInfo = self.conferenceInfo { + /* + timezone.postValue( + AppUtils.getFormattedString( + R.string.meeting_schedule_timezone_title, + TimeZone.getDefault().displayName + ) + .replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() } + ) + */ + + var isEditable = false + + if let organizerAddress = meetingModel.confInfo.organizer { + let localAccount = core.accountList.first(where: { + if let address = $0.params?.identityAddress { + return organizerAddress.weakEqual(address2: address) + } else { + return false + } + }) - /* - timezone.postValue( - AppUtils.getFormattedString( - R.string.meeting_schedule_timezone_title, - TimeZone.getDefault().displayName - ) - .replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() } - ) - */ - - var isEditable = false - - if let organizerAddress = confInfo.organizer { - let localAccount = core.accountList.first(where: { - if let address = $0.params?.identityAddress { - return organizerAddress.weakEqual(address2: address) - } else { - return false - } - }) - - isEditable = localAccount != nil - } else { - Log.error("\(MeetingViewModel.TAG) No organizer SIP URI found for: \(confInfo.uri?.asStringUriOnly() ?? "(empty)")") - } - - let startDate = Date(timeIntervalSince1970: TimeInterval(confInfo.dateTime)) - let endDate = Calendar.current.date(byAdding: .minute, value: Int(confInfo.duration), to: startDate)! - - let formatter = DateFormatter() - formatter.dateFormat = Locale.current.identifier == "fr_FR" ? "HH:mm" : "h:mm a" - let startTime = formatter.string(from: startDate) - let endTime = formatter.string(from: endDate) - let dateTime = "\(startTime) - \(endTime)" - - DispatchQueue.main.sync { - self.subject = confInfo.subject ?? "" - self.sipUri = confInfo.uri?.asStringUriOnly() ?? "" - self.description = confInfo.description - self.startDate = startDate - self.endDate = endDate - self.dateTime = dateTime - self.isEditable = isEditable - } - - self.computeParticipantsList(core: core, confInfo: confInfo) + isEditable = localAccount != nil + } else { + Log.error("\(MeetingViewModel.TAG) No organizer SIP URI found for: \(meetingModel.confInfo.uri?.asStringUriOnly() ?? "(empty)")") } + + let startDate = Date(timeIntervalSince1970: TimeInterval(meetingModel.confInfo.dateTime)) + let endDate = Calendar.current.date(byAdding: .minute, value: Int(meetingModel.confInfo.duration), to: startDate)! + + let formatter = DateFormatter() + formatter.dateFormat = Locale.current.identifier == "fr_FR" ? "HH:mm" : "h:mm a" + let startTime = formatter.string(from: startDate) + let endTime = formatter.string(from: endDate) + let dateTime = "\(startTime) - \(endTime)" + + DispatchQueue.main.async { + self.subject = self.meetingModel.confInfo.subject ?? "" + self.sipUri = self.meetingModel.confInfo.uri?.asStringUriOnly() ?? "" + self.description = self.meetingModel.confInfo.description + self.startDate = startDate + self.endDate = endDate + self.dateTime = dateTime + self.isEditable = isEditable + } + + self.computeParticipantsList() } - private func computeParticipantsList(core: Core, confInfo: ConferenceInfo) { + private func computeParticipantsList() { var speakersList: [ParticipantModel] = [] var participantsList: [ParticipantModel] = [] var allSpeaker = true - let organizer = confInfo.organizer + let organizer = meetingModel.confInfo.organizer var organizerFound = false - for pInfo in confInfo.participantInfos { + for pInfo in meetingModel.confInfo.participantInfos { if let participantAddress = pInfo.address { let isOrganizer = organizer != nil && organizer!.weakEqual(address2: participantAddress) - Log.info("\(MeetingViewModel.TAG) Conference \(confInfo.subject)[${conferenceInfo.subject}] \(isOrganizer ? "organizer: " : "participant: ") \(participantAddress.asStringUriOnly()) is a \(pInfo.role)") + Log.info("\(MeetingViewModel.TAG) Conference \(meetingModel.confInfo.subject)[${conferenceInfo.subject}] \(isOrganizer ? "organizer: " : "participant: ") \(participantAddress.asStringUriOnly()) is a \(pInfo.role)") if isOrganizer { organizerFound = true } @@ -156,10 +153,11 @@ class MeetingViewModel: ObservableObject { participantsList.append(ParticipantModel(address: organizerAddress)) } - DispatchQueue.main.sync { + DispatchQueue.main.async { self.isBroadcast = !allSpeaker - speakers = speakersList - participants = participantsList + self.speakers = speakersList + self.participants = participantsList } } + */ } diff --git a/Linphone/UI/Main/Meetings/ViewModel/ScheduleMeetingViewModel.swift b/Linphone/UI/Main/Meetings/ViewModel/ScheduleMeetingViewModel.swift index 0a43e13fc..579b34a56 100644 --- a/Linphone/UI/Main/Meetings/ViewModel/ScheduleMeetingViewModel.swift +++ b/Linphone/UI/Main/Meetings/ViewModel/ScheduleMeetingViewModel.swift @@ -38,13 +38,16 @@ class ScheduleMeetingViewModel: ObservableObject { @Published var participants: [SelectedAddressModel] = [] @Published var operationInProgress: Bool = false @Published var conferenceCreatedEvent: Bool = false + @Published var conferenceUri: String = "" var conferenceScheduler: ConferenceScheduler? private var mSchedulerSubscriptions = Set() var conferenceInfoToEdit: ConferenceInfo? - + @Published var displayedMeeting: MeetingModel? // if nil, then we are currently creating a new meeting + @Published var myself: SelectedAddressModel? @Published var fromDate: Date @Published var toDate: Date + @Published var errorMsg: String = "" init() { fromDate = Calendar.current.date(byAdding: .hour, value: 1, to: Date.now)! @@ -65,7 +68,7 @@ class ScheduleMeetingViewModel: ObservableObject { participants = [] operationInProgress = false conferenceCreatedEvent = false - + conferenceUri = "" fromDate = Calendar.current.date(byAdding: .hour, value: 1, to: Date.now)! toDate = Calendar.current.date(byAdding: .hour, value: 2, to: Date.now)! computeDateLabels() @@ -94,6 +97,14 @@ class ScheduleMeetingViewModel: ObservableObject { toTime = formatter.string(from: toDate) } + func getFullDateString() -> String { + var day = fromDate.formatted(Date.FormatStyle().weekday(.abbreviated)) + var dayNumber = fromDate.formatted(Date.FormatStyle().day(.twoDigits)) + var month = fromDate.formatted(Date.FormatStyle().month(.wide)) + var year = fromDate.formatted(Date.FormatStyle().year(.defaultDigits)) + return "\(day). \(dayNumber) \(month) \(year) | \(allDayMeeting ? "All day" : "\(fromTime) - \(toTime)")" + } + private func updateTimezone() { // TODO } @@ -143,6 +154,8 @@ class ScheduleMeetingViewModel: ObservableObject { if cbVal.state == ConferenceScheduler.State.Error { DispatchQueue.main.async { self.operationInProgress = false + + self.errorMsg = (self.displayedMeeting != nil) ? "Could not edit conference" : "Could not create conference" // TODO: show error toast } } else if cbVal.state == ConferenceScheduler.State.Ready { @@ -207,11 +220,9 @@ class ScheduleMeetingViewModel: ObservableObject { CoreContext.shared.doOnCoreQueue { core in Log.info("\(ScheduleMeetingViewModel.TAG) Scheduling \(self.isBroadcastSelected ? "broadcast" : "meeting")") - let localAccount = core.defaultAccount - let localAddress = localAccount?.params?.identityAddress - - if let conferenceInfo = try? Factory.Instance.createConferenceInfo() { - conferenceInfo.organizer = localAddress + if let conferenceInfo = self.displayedMeeting != nil ? self.displayedMeeting!.confInfo : try? Factory.Instance.createConferenceInfo() { + let localAccount = core.defaultAccount + conferenceInfo.organizer = localAccount?.params?.identityAddress self.fillConferenceInfo(confInfo: conferenceInfo) if self.conferenceScheduler == nil { self.initConferenceSchedulerAndListeners(core: core) @@ -243,6 +254,45 @@ class ScheduleMeetingViewModel: ObservableObject { } } + func loadExistingMeeting(meeting: MeetingModel) { + DispatchQueue.main.async { + self.resetViewModelData() + self.subject = meeting.confInfo.subject ?? "" + self.description = meeting.confInfo.description ?? "" + self.fromDate = meeting.meetingDate + self.toDate = meeting.endDate + self.participants = [] + + let organizer = meeting.confInfo.organizer + CoreContext.shared.doOnCoreQueue { core in + if let myAddr = core.defaultAccount?.contactAddress { + ContactAvatarModel.getAvatarModelFromAddress(address: myAddr) { avatarResult in + DispatchQueue.main.async { + let isOrganizer = (organizer != nil) ? myAddr.weakEqual(address2: organizer!) : false + self.myself = SelectedAddressModel(addr: myAddr, avModel: avatarResult, isOrg: isOrganizer) + } + } + } + } + + for pInfo in meeting.confInfo.participantInfos { + if let addr = pInfo.address { + ContactAvatarModel.getAvatarModelFromAddress(address: addr) { avatarResult in + DispatchQueue.main.async { + let isOrganizer = (organizer != nil) ? addr.weakEqual(address2: organizer!) : false + self.participants.append(SelectedAddressModel(addr: addr, avModel: avatarResult, isOrg:isOrganizer)) + } + } + } + } + self.conferenceUri = meeting.confInfo.uri?.asStringUriOnly() ?? "" + self.computeDateLabels() + self.computeTimeLabels() + self.updateTimezone() + self.displayedMeeting = meeting + } + } + func loadExistingConferenceInfoFromUri(conferenceUri: String) { CoreContext.shared.doOnCoreQueue { core in if let conferenceAddress = core.interpretUrl(url: conferenceUri, applyInternationalPrefix: false) { diff --git a/Linphone/UI/Main/Viewmodel/AddParticipantsViewModel.swift b/Linphone/UI/Main/Viewmodel/AddParticipantsViewModel.swift index 3db4b0bc7..6e1f2d647 100644 --- a/Linphone/UI/Main/Viewmodel/AddParticipantsViewModel.swift +++ b/Linphone/UI/Main/Viewmodel/AddParticipantsViewModel.swift @@ -12,10 +12,12 @@ import Combine class SelectedAddressModel: ObservableObject { var address: Address var avatarModel: ContactAvatarModel + var isOrganizer: Bool = false - init (addr: Address, avModel: ContactAvatarModel) { + init (addr: Address, avModel: ContactAvatarModel, isOrg: Bool = false) { address = addr avatarModel = avModel + isOrganizer = isOrg } }