Implement conference details view with edit option.

This commit is contained in:
QuentinArguillere 2024-06-20 17:15:52 +02:00
parent 0aeef2f022
commit 570007c2c6
10 changed files with 409 additions and 90 deletions

View file

@ -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
}
}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40Zm0,80H168V56h48ZM40,56H152V200H40ZM216,200H168V136h48v64ZM180,88a12,12,0,1,1,12,12A12,12,0,0,1,180,88Zm24,80a12,12,0,1,1-12-12A12,12,0,0,1,204,168Zm-68.25-2a39.76,39.76,0,0,0-17.19-23.34,32,32,0,1,0-45.12,0A39.84,39.84,0,0,0,56.25,166a8,8,0,0,0,15.5,4c2.64-10.25,13.06-18,24.25-18s21.62,7.73,24.25,18a8,8,0,1,0,15.5-4ZM80,120a16,16,0,1,1,16,16A16,16,0,0,1,80,120Z"></path></svg>

After

Width:  |  Height:  |  Size: 582 B

View file

@ -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

View file

@ -17,19 +17,254 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// 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..<scheduleMeetingViewModel.participants.count, id: \.self) { index in
getParticipantLine(participant: scheduleMeetingViewModel.participants[index])
}
}
}.frame(maxHeight: 170)
Spacer()
}.padding(.top, 10)
Rectangle()
.foregroundStyle(.clear)
.frame(height: 1)
.background(Color.gray200)
}
.background(.white)
}.frame(maxHeight: .infinity)
Spacer()
Button(action: {
TelecomManager.shared.meetingWaitingRoomSelected = try? Factory.Instance.createAddress(addr: scheduleMeetingViewModel.displayedMeeting?.address ?? "")
TelecomManager.shared.meetingWaitingRoomDisplayed = true
}, label: {
Text("Join the meeting now")
.bold()
.default_text_style_white_500(styleSize: 16)
.frame(maxWidth: .infinity, maxHeight: 47, alignment: .center)
.frame(height: 47)
.background(Color.orangeMain500)
.clipShape(RoundedRectangle(cornerRadius: 48))
.padding(.leading, 15)
.padding(.trailing, 15)
})
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
#Preview {
MeetingFragment(meetingViewModel: MeetingViewModel())
let model = ScheduleMeetingViewModel()
model.subject = "Meeting subject"
model.conferenceUri = "linphone.com/lalalal.fr"
model.description = "description du meeting ça va être la bringue wesh wesh gros bien ou bien ça roule"
return MeetingFragment(scheduleMeetingViewModel: model
, meetingsListViewModel: MeetingsListViewModel()
, isShowScheduleMeetingFragment: .constant(true))
}
// swiftlint:enable line_length

View file

@ -17,6 +17,7 @@ struct MeetingsFragment: View {
@State var showingSheet: Bool = false
@ViewBuilder
func createMonthLine(model: MeetingsListItemModel) -> 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)
}
}
}
}

View file

@ -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)

View file

@ -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)"

View file

@ -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
}
}
*/
}

View file

@ -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<AnyCancellable?>()
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) {

View file

@ -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
}
}