mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-17 11:08:06 +00:00
Rename "ScheduleMeetingViewModel" to "MeetingViewModel"
This commit is contained in:
parent
570007c2c6
commit
e74b2dd4f3
13 changed files with 479 additions and 574 deletions
|
|
@ -12,7 +12,6 @@
|
|||
6613A0AE2BAEB7DF008923A4 /* MeetingFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6613A0AD2BAEB7DF008923A4 /* MeetingFragment.swift */; };
|
||||
6613A0B02BAEB7F4008923A4 /* MeetingsListFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6613A0AF2BAEB7F4008923A4 /* MeetingsListFragment.swift */; };
|
||||
6613A0B42BAEBE3F008923A4 /* MeetingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6613A0B32BAEBE3F008923A4 /* MeetingViewModel.swift */; };
|
||||
6613A0B62BAEBE5C008923A4 /* ScheduleMeetingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6613A0B52BAEBE5C008923A4 /* ScheduleMeetingViewModel.swift */; };
|
||||
66162A202BDFC2F900DCE913 /* AddParticipantsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66162A1F2BDFC2F900DCE913 /* AddParticipantsViewModel.swift */; };
|
||||
662B69D92B25DE18007118BF /* TelecomManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 662B69D82B25DE18007118BF /* TelecomManager.swift */; };
|
||||
662B69DB2B25DE25007118BF /* ProviderDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 662B69DA2B25DE25007118BF /* ProviderDelegate.swift */; };
|
||||
|
|
@ -192,7 +191,6 @@
|
|||
6613A0AD2BAEB7DF008923A4 /* MeetingFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeetingFragment.swift; sourceTree = "<group>"; };
|
||||
6613A0AF2BAEB7F4008923A4 /* MeetingsListFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeetingsListFragment.swift; sourceTree = "<group>"; };
|
||||
6613A0B32BAEBE3F008923A4 /* MeetingViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MeetingViewModel.swift; sourceTree = "<group>"; };
|
||||
6613A0B52BAEBE5C008923A4 /* ScheduleMeetingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleMeetingViewModel.swift; sourceTree = "<group>"; };
|
||||
66162A1F2BDFC2F900DCE913 /* AddParticipantsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddParticipantsViewModel.swift; sourceTree = "<group>"; };
|
||||
662B69D82B25DE18007118BF /* TelecomManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelecomManager.swift; sourceTree = "<group>"; };
|
||||
662B69DA2B25DE25007118BF /* ProviderDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderDelegate.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -423,7 +421,6 @@
|
|||
children = (
|
||||
66E56BC82BA4A6D7006CE56F /* MeetingsListViewModel.swift */,
|
||||
6613A0B32BAEBE3F008923A4 /* MeetingViewModel.swift */,
|
||||
6613A0B52BAEBE5C008923A4 /* ScheduleMeetingViewModel.swift */,
|
||||
);
|
||||
path = ViewModel;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1109,7 +1106,6 @@
|
|||
66C492012B24DB6900CEA16D /* Log.swift in Sources */,
|
||||
C6A5A9432C10B5ED0070FEA4 /* DecodableExtension.swift in Sources */,
|
||||
D714035B2BE11E00004BD8CA /* CallMediaEncryptionModel.swift in Sources */,
|
||||
6613A0B62BAEBE5C008923A4 /* ScheduleMeetingViewModel.swift in Sources */,
|
||||
D748BF2C2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift in Sources */,
|
||||
D7CEE0382B7A214F00FD79B7 /* ConversationsListViewModel.swift in Sources */,
|
||||
D74C9CF82ACACECE0021626A /* WelcomePage1Fragment.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ struct LinphoneApp: App {
|
|||
@State private var conversationsListViewModel: ConversationsListViewModel?
|
||||
@State private var conversationViewModel: ConversationViewModel?
|
||||
@State private var meetingsListViewModel: MeetingsListViewModel?
|
||||
@State private var scheduleMeetingViewModel: ScheduleMeetingViewModel?
|
||||
@State private var meetingViewModel: MeetingViewModel?
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
|
|
@ -111,7 +111,7 @@ struct LinphoneApp: App {
|
|||
&& conversationsListViewModel != nil
|
||||
&& conversationViewModel != nil
|
||||
&& meetingsListViewModel != nil
|
||||
&& scheduleMeetingViewModel != nil {
|
||||
&& meetingViewModel != nil {
|
||||
ContentView(
|
||||
contactViewModel: contactViewModel!,
|
||||
editContactViewModel: editContactViewModel!,
|
||||
|
|
@ -123,7 +123,7 @@ struct LinphoneApp: App {
|
|||
conversationsListViewModel: conversationsListViewModel!,
|
||||
conversationViewModel: conversationViewModel!,
|
||||
meetingsListViewModel: meetingsListViewModel!,
|
||||
scheduleMeetingViewModel: scheduleMeetingViewModel!
|
||||
meetingViewModel: meetingViewModel!
|
||||
).onOpenURL { url in
|
||||
URIHandler.handleURL(url: url)
|
||||
}
|
||||
|
|
@ -145,7 +145,7 @@ struct LinphoneApp: App {
|
|||
conversationsListViewModel = ConversationsListViewModel()
|
||||
conversationViewModel = ConversationViewModel()
|
||||
meetingsListViewModel = MeetingsListViewModel()
|
||||
scheduleMeetingViewModel = ScheduleMeetingViewModel()
|
||||
meetingViewModel = MeetingViewModel()
|
||||
}.onOpenURL { url in
|
||||
URIHandler.handleURL(url: url)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ struct ContentView: View {
|
|||
@ObservedObject var conversationsListViewModel: ConversationsListViewModel
|
||||
@ObservedObject var conversationViewModel: ConversationViewModel
|
||||
@ObservedObject var meetingsListViewModel: MeetingsListViewModel
|
||||
@ObservedObject var scheduleMeetingViewModel: ScheduleMeetingViewModel
|
||||
@ObservedObject var meetingViewModel: MeetingViewModel
|
||||
|
||||
@State var index = 0
|
||||
@State private var orientation = UIDevice.current.orientation
|
||||
|
|
@ -509,7 +509,7 @@ struct ContentView: View {
|
|||
} else if self.index == 3 {
|
||||
MeetingsView(
|
||||
meetingsListViewModel: meetingsListViewModel,
|
||||
scheduleMeetingViewModel: scheduleMeetingViewModel,
|
||||
meetingViewModel: meetingViewModel,
|
||||
isShowScheduleMeetingFragment: $isShowScheduleMeetingFragment
|
||||
)
|
||||
}
|
||||
|
|
@ -699,7 +699,7 @@ struct ContentView: View {
|
|||
}
|
||||
|
||||
if contactViewModel.indexDisplayedFriend != nil || historyViewModel.displayedCall != nil || conversationViewModel.displayedConversation != nil ||
|
||||
scheduleMeetingViewModel.displayedMeeting != nil
|
||||
meetingViewModel.displayedMeeting != nil
|
||||
{
|
||||
HStack(spacing: 0) {
|
||||
Spacer()
|
||||
|
|
@ -742,7 +742,7 @@ struct ContentView: View {
|
|||
.background(Color.gray100)
|
||||
.ignoresSafeArea(.keyboard)
|
||||
} else if self.index == 3 {
|
||||
MeetingFragment(scheduleMeetingViewModel: scheduleMeetingViewModel, meetingsListViewModel: meetingsListViewModel, isShowScheduleMeetingFragment: $isShowScheduleMeetingFragment)
|
||||
MeetingFragment(meetingViewModel: meetingViewModel, meetingsListViewModel: meetingsListViewModel, isShowScheduleMeetingFragment: $isShowScheduleMeetingFragment)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(Color.gray100)
|
||||
.ignoresSafeArea(.keyboard)
|
||||
|
|
@ -951,7 +951,7 @@ struct ContentView: View {
|
|||
|
||||
if isShowScheduleMeetingFragment {
|
||||
ScheduleMeetingFragment(
|
||||
scheduleMeetingViewModel: scheduleMeetingViewModel,
|
||||
meetingViewModel: meetingViewModel,
|
||||
meetingsListViewModel: meetingsListViewModel,
|
||||
isShowScheduleMeetingFragment: $isShowScheduleMeetingFragment
|
||||
)
|
||||
|
|
@ -1038,7 +1038,7 @@ struct ContentView: View {
|
|||
conversationsListViewModel: ConversationsListViewModel(),
|
||||
conversationViewModel: ConversationViewModel(),
|
||||
meetingsListViewModel: MeetingsListViewModel(),
|
||||
scheduleMeetingViewModel: ScheduleMeetingViewModel()
|
||||
meetingViewModel: MeetingViewModel()
|
||||
)
|
||||
}
|
||||
// swiftlint:enable type_body_length
|
||||
|
|
|
|||
|
|
@ -1,9 +1,21 @@
|
|||
//
|
||||
// ParticipantsListFragment.swift
|
||||
// Linphone
|
||||
//
|
||||
// Created by QuentinArguillere on 16/04/2024.
|
||||
//
|
||||
/*
|
||||
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
import Foundation
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ struct MeetingFragment: View {
|
|||
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
|
||||
@State private var orientation = UIDevice.current.orientation
|
||||
|
||||
@ObservedObject var scheduleMeetingViewModel: ScheduleMeetingViewModel
|
||||
@ObservedObject var meetingViewModel: MeetingViewModel
|
||||
@ObservedObject var meetingsListViewModel: MeetingsListViewModel
|
||||
|
||||
@State private var showDatePicker = false
|
||||
|
|
@ -87,11 +87,11 @@ struct MeetingFragment: View {
|
|||
.padding(.leading, -10)
|
||||
.onTapGesture {
|
||||
withAnimation {
|
||||
scheduleMeetingViewModel.displayedMeeting = nil
|
||||
meetingViewModel.displayedMeeting = nil
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
if scheduleMeetingViewModel.myself != nil && scheduleMeetingViewModel.myself!.isOrganizer {
|
||||
if meetingViewModel.myself != nil && meetingViewModel.myself!.isOrganizer {
|
||||
Image("pencil-simple")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
|
|
@ -126,7 +126,7 @@ struct MeetingFragment: View {
|
|||
.foregroundStyle(Color.grayMain2c800)
|
||||
.frame(width: 24, height: 24)
|
||||
.padding(.leading, 15)
|
||||
Text(scheduleMeetingViewModel.subject)
|
||||
Text(meetingViewModel.subject)
|
||||
.fontWeight(.bold)
|
||||
.default_text_style(styleSize: 20)
|
||||
.frame(height: 29, alignment: .leading)
|
||||
|
|
@ -145,7 +145,7 @@ struct MeetingFragment: View {
|
|||
.foregroundStyle(Color.grayMain2c800)
|
||||
.frame(width: 24, height: 24)
|
||||
.padding(.leading, 15)
|
||||
Text(scheduleMeetingViewModel.conferenceUri)
|
||||
Text(meetingViewModel.conferenceUri)
|
||||
.underline()
|
||||
.default_text_style(styleSize: 14)
|
||||
Spacer()
|
||||
|
|
@ -165,7 +165,7 @@ struct MeetingFragment: View {
|
|||
.foregroundStyle(Color.grayMain2c800)
|
||||
.frame(width: 24, height: 24)
|
||||
.padding(.leading, 15)
|
||||
Text(scheduleMeetingViewModel.getFullDateString())
|
||||
Text(meetingViewModel.getFullDateString())
|
||||
.default_text_style(styleSize: 14)
|
||||
Spacer()
|
||||
}
|
||||
|
|
@ -195,7 +195,7 @@ struct MeetingFragment: View {
|
|||
.frame(width: 24, height: 24)
|
||||
.padding(.leading, 15)
|
||||
|
||||
Text(scheduleMeetingViewModel.description)
|
||||
Text(meetingViewModel.description)
|
||||
.default_text_style(styleSize: 14)
|
||||
Spacer()
|
||||
}.padding(.top, 10)
|
||||
|
|
@ -216,11 +216,11 @@ struct MeetingFragment: View {
|
|||
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
if scheduleMeetingViewModel.myself != nil {
|
||||
getParticipantLine(participant: scheduleMeetingViewModel.myself!)
|
||||
if meetingViewModel.myself != nil {
|
||||
getParticipantLine(participant: meetingViewModel.myself!)
|
||||
}
|
||||
ForEach(0..<scheduleMeetingViewModel.participants.count, id: \.self) { index in
|
||||
getParticipantLine(participant: scheduleMeetingViewModel.participants[index])
|
||||
ForEach(0..<meetingViewModel.participants.count, id: \.self) { index in
|
||||
getParticipantLine(participant: meetingViewModel.participants[index])
|
||||
}
|
||||
}
|
||||
}.frame(maxHeight: 170)
|
||||
|
|
@ -238,7 +238,7 @@ struct MeetingFragment: View {
|
|||
Spacer()
|
||||
|
||||
Button(action: {
|
||||
TelecomManager.shared.meetingWaitingRoomSelected = try? Factory.Instance.createAddress(addr: scheduleMeetingViewModel.displayedMeeting?.address ?? "")
|
||||
TelecomManager.shared.meetingWaitingRoomSelected = try? Factory.Instance.createAddress(addr: meetingViewModel.displayedMeeting?.address ?? "")
|
||||
TelecomManager.shared.meetingWaitingRoomDisplayed = true
|
||||
}, label: {
|
||||
Text("Join the meeting now")
|
||||
|
|
@ -258,11 +258,11 @@ struct MeetingFragment: View {
|
|||
}
|
||||
|
||||
#Preview {
|
||||
let model = ScheduleMeetingViewModel()
|
||||
let model = MeetingViewModel()
|
||||
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
|
||||
return MeetingFragment(meetingViewModel: model
|
||||
, meetingsListViewModel: MeetingsListViewModel()
|
||||
, isShowScheduleMeetingFragment: .constant(true))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,21 @@
|
|||
//
|
||||
// MeetingsFragment.swift
|
||||
// Linphone
|
||||
//
|
||||
// Created by QuentinArguillere on 18/04/2024.
|
||||
//
|
||||
/*
|
||||
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
import linphonesw
|
||||
|
|
@ -11,7 +23,7 @@ import linphonesw
|
|||
struct MeetingsFragment: View {
|
||||
|
||||
@ObservedObject var meetingsListViewModel: MeetingsListViewModel
|
||||
@ObservedObject var scheduleMeetingViewModel: ScheduleMeetingViewModel
|
||||
@ObservedObject var meetingViewModel: MeetingViewModel
|
||||
|
||||
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
|
||||
|
||||
|
|
@ -68,7 +80,7 @@ struct MeetingsFragment: View {
|
|||
.onTapGesture {
|
||||
withAnimation {
|
||||
if let meetingModel = model.model {
|
||||
scheduleMeetingViewModel.loadExistingMeeting(meeting: meetingModel)
|
||||
meetingViewModel.loadExistingMeeting(meeting: meetingModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -172,5 +184,5 @@ struct MeetingsFragment: View {
|
|||
}
|
||||
|
||||
#Preview {
|
||||
MeetingsFragment(meetingsListViewModel: MeetingsListViewModel(), scheduleMeetingViewModel: ScheduleMeetingViewModel())
|
||||
MeetingsFragment(meetingsListViewModel: MeetingsListViewModel(), meetingViewModel: MeetingViewModel())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ struct ScheduleMeetingFragment: View {
|
|||
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
|
||||
@State private var orientation = UIDevice.current.orientation
|
||||
|
||||
@ObservedObject var scheduleMeetingViewModel: ScheduleMeetingViewModel
|
||||
@ObservedObject var meetingViewModel: MeetingViewModel
|
||||
@ObservedObject var meetingsListViewModel: MeetingsListViewModel
|
||||
|
||||
@State private var delayedColor = Color.white
|
||||
|
|
@ -74,15 +74,15 @@ struct ScheduleMeetingFragment: View {
|
|||
.padding(.leading, -10)
|
||||
.onTapGesture {
|
||||
withAnimation {
|
||||
if let meeting = scheduleMeetingViewModel.displayedMeeting {
|
||||
if let meeting = meetingViewModel.displayedMeeting {
|
||||
// reload meeting to cancel change from edit
|
||||
scheduleMeetingViewModel.loadExistingMeeting(meeting: meeting)
|
||||
meetingViewModel.loadExistingMeeting(meeting: meeting)
|
||||
}
|
||||
isShowScheduleMeetingFragment.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
Text("\(scheduleMeetingViewModel.displayedMeeting != nil ? "Edit" : "New") meeting" )
|
||||
Text("\(meetingViewModel.displayedMeeting != nil ? "Edit" : "New") meeting" )
|
||||
.multilineTextAlignment(.leading)
|
||||
.default_text_style_orange_800(styleSize: 16)
|
||||
|
||||
|
|
@ -98,17 +98,17 @@ struct ScheduleMeetingFragment: View {
|
|||
Spacer()
|
||||
HStack(alignment: .center) {
|
||||
Button(action: {
|
||||
scheduleMeetingViewModel.isBroadcastSelected.toggle()
|
||||
meetingViewModel.isBroadcastSelected.toggle()
|
||||
}, label: {
|
||||
Image("users-three")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(scheduleMeetingViewModel.isBroadcastSelected ? .white : Color.orangeMain500)
|
||||
.foregroundStyle(meetingViewModel.isBroadcastSelected ? .white : Color.orangeMain500)
|
||||
.frame(width: 25, height: 25)
|
||||
})
|
||||
Text("Meeting")
|
||||
.default_text_style_orange_500( styleSize: 15)
|
||||
.foregroundStyle(scheduleMeetingViewModel.isBroadcastSelected ? .white : Color.orangeMain500)
|
||||
.foregroundStyle(meetingViewModel.isBroadcastSelected ? .white : Color.orangeMain500)
|
||||
}
|
||||
.padding(.horizontal, 40)
|
||||
.padding(.vertical, 10)
|
||||
|
|
@ -117,13 +117,13 @@ struct ScheduleMeetingFragment: View {
|
|||
RoundedRectangle(cornerRadius: 60)
|
||||
.inset(by: 0.5)
|
||||
.stroke(Color.orangeMain500, lineWidth: 1)
|
||||
.background(scheduleMeetingViewModel.isBroadcastSelected ? Color.orangeMain500 : Color.white)
|
||||
.background(meetingViewModel.isBroadcastSelected ? Color.orangeMain500 : Color.white)
|
||||
)
|
||||
Spacer()
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Button(action: {
|
||||
scheduleMeetingViewModel.isBroadcastSelected.toggle()
|
||||
meetingViewModel.isBroadcastSelected.toggle()
|
||||
}, label: {
|
||||
Image("slideshow")
|
||||
.renderingMode(.template)
|
||||
|
|
@ -153,7 +153,7 @@ struct ScheduleMeetingFragment: View {
|
|||
.foregroundStyle(Color.grayMain2c800)
|
||||
.frame(width: 24, height: 24)
|
||||
.padding(.leading, 16)
|
||||
TextField("Subject", text: $scheduleMeetingViewModel.subject)
|
||||
TextField("Subject", text: $meetingViewModel.subject)
|
||||
.default_text_style_700(styleSize: 20)
|
||||
.frame(height: 29, alignment: .leading)
|
||||
Spacer()
|
||||
|
|
@ -171,43 +171,43 @@ struct ScheduleMeetingFragment: View {
|
|||
.foregroundStyle(Color.grayMain2c800)
|
||||
.frame(width: 24, height: 24)
|
||||
.padding(.leading, 16)
|
||||
Text(scheduleMeetingViewModel.fromDateStr)
|
||||
Text(meetingViewModel.fromDateStr)
|
||||
.fontWeight(.bold)
|
||||
.default_text_style_500(styleSize: 16)
|
||||
.onTapGesture {
|
||||
setFromDate = true
|
||||
selectedDate = scheduleMeetingViewModel.fromDate
|
||||
selectedDate = meetingViewModel.fromDate
|
||||
showDatePicker.toggle()
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
|
||||
if !scheduleMeetingViewModel.allDayMeeting {
|
||||
if !meetingViewModel.allDayMeeting {
|
||||
HStack(spacing: 8) {
|
||||
Text(scheduleMeetingViewModel.fromTime)
|
||||
Text(meetingViewModel.fromTime)
|
||||
.fontWeight(.bold)
|
||||
.padding(.leading, 48)
|
||||
.frame(height: 29, alignment: .leading)
|
||||
.default_text_style_500(styleSize: 16)
|
||||
.opacity(scheduleMeetingViewModel.allDayMeeting ? 0 : 1)
|
||||
.opacity(meetingViewModel.allDayMeeting ? 0 : 1)
|
||||
.onTapGesture {
|
||||
setFromDate = true
|
||||
selectedDate = scheduleMeetingViewModel.fromDate
|
||||
selectedDate = meetingViewModel.fromDate
|
||||
showTimePicker.toggle()
|
||||
}
|
||||
Text(scheduleMeetingViewModel.toTime)
|
||||
Text(meetingViewModel.toTime)
|
||||
.fontWeight(.bold)
|
||||
.padding(.leading, 8)
|
||||
.frame(height: 29, alignment: .leading)
|
||||
.default_text_style_500(styleSize: 16)
|
||||
.opacity(scheduleMeetingViewModel.allDayMeeting ? 0 : 1)
|
||||
.opacity(meetingViewModel.allDayMeeting ? 0 : 1)
|
||||
.onTapGesture {
|
||||
setFromDate = false
|
||||
selectedDate = scheduleMeetingViewModel.toDate
|
||||
selectedDate = meetingViewModel.toDate
|
||||
showTimePicker.toggle()
|
||||
}
|
||||
Spacer()
|
||||
Toggle("", isOn: $scheduleMeetingViewModel.allDayMeeting)
|
||||
Toggle("", isOn: $meetingViewModel.allDayMeeting)
|
||||
.labelsHidden()
|
||||
.tint(Color.orangeMain300)
|
||||
Text("All day")
|
||||
|
|
@ -222,16 +222,16 @@ struct ScheduleMeetingFragment: View {
|
|||
.foregroundStyle(Color.grayMain2c800)
|
||||
.frame(width: 24, height: 24)
|
||||
.padding(.leading, 16)
|
||||
Text(scheduleMeetingViewModel.toDateStr)
|
||||
Text(meetingViewModel.toDateStr)
|
||||
.fontWeight(.bold)
|
||||
.default_text_style_500(styleSize: 16)
|
||||
.onTapGesture {
|
||||
setFromDate = false
|
||||
selectedDate = scheduleMeetingViewModel.toDate
|
||||
selectedDate = meetingViewModel.toDate
|
||||
showDatePicker.toggle()
|
||||
}
|
||||
Spacer()
|
||||
Toggle("", isOn: $scheduleMeetingViewModel.allDayMeeting)
|
||||
Toggle("", isOn: $meetingViewModel.allDayMeeting)
|
||||
.labelsHidden()
|
||||
.tint(Color.orangeMain300)
|
||||
Text("All day")
|
||||
|
|
@ -279,7 +279,7 @@ struct ScheduleMeetingFragment: View {
|
|||
.frame(width: 24, height: 24)
|
||||
.padding(.leading, 16)
|
||||
|
||||
TextField("Add a description", text: $scheduleMeetingViewModel.description)
|
||||
TextField("Add a description", text: $meetingViewModel.description)
|
||||
.default_text_style_700(styleSize: 16)
|
||||
}
|
||||
|
||||
|
|
@ -290,9 +290,9 @@ struct ScheduleMeetingFragment: View {
|
|||
|
||||
VStack {
|
||||
NavigationLink(destination: {
|
||||
AddParticipantsFragment(addParticipantsViewModel: addParticipantsViewModel, confirmAddParticipantsFunc: scheduleMeetingViewModel.addParticipants)
|
||||
AddParticipantsFragment(addParticipantsViewModel: addParticipantsViewModel, confirmAddParticipantsFunc: meetingViewModel.addParticipants)
|
||||
.onAppear {
|
||||
addParticipantsViewModel.participantsToAdd = scheduleMeetingViewModel.participants
|
||||
addParticipantsViewModel.participantsToAdd = meetingViewModel.participants
|
||||
}
|
||||
}, label: {
|
||||
HStack(alignment: .center, spacing: 8) {
|
||||
|
|
@ -310,20 +310,20 @@ struct ScheduleMeetingFragment: View {
|
|||
}
|
||||
})
|
||||
|
||||
if !scheduleMeetingViewModel.participants.isEmpty {
|
||||
if !meetingViewModel.participants.isEmpty {
|
||||
ScrollView {
|
||||
ForEach(0..<scheduleMeetingViewModel.participants.count, id: \.self) { index in
|
||||
ForEach(0..<meetingViewModel.participants.count, id: \.self) { index in
|
||||
VStack {
|
||||
HStack {
|
||||
Avatar(contactAvatarModel: scheduleMeetingViewModel.participants[index].avatarModel, avatarSize: 50)
|
||||
Avatar(contactAvatarModel: meetingViewModel.participants[index].avatarModel, avatarSize: 50)
|
||||
.padding(.leading, 20)
|
||||
|
||||
Text(scheduleMeetingViewModel.participants[index].avatarModel.name)
|
||||
Text(meetingViewModel.participants[index].avatarModel.name)
|
||||
.default_text_style(styleSize: 16)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
Spacer()
|
||||
Button(action: {
|
||||
scheduleMeetingViewModel.participants.remove(at: index)
|
||||
meetingViewModel.participants.remove(at: index)
|
||||
}, label: {
|
||||
Image("x")
|
||||
.renderingMode(.template)
|
||||
|
|
@ -344,7 +344,7 @@ struct ScheduleMeetingFragment: View {
|
|||
.background(Color.gray200)
|
||||
|
||||
HStack(spacing: 8) {
|
||||
Toggle("", isOn: $scheduleMeetingViewModel.sendInvitations)
|
||||
Toggle("", isOn: $meetingViewModel.sendInvitations)
|
||||
.padding(.leading, 16)
|
||||
.labelsHidden()
|
||||
.tint(Color.orangeMain300)
|
||||
|
|
@ -358,7 +358,7 @@ struct ScheduleMeetingFragment: View {
|
|||
|
||||
Button {
|
||||
withAnimation {
|
||||
scheduleMeetingViewModel.schedule()
|
||||
meetingViewModel.schedule()
|
||||
}
|
||||
} label: {
|
||||
Image("check")
|
||||
|
|
@ -371,7 +371,7 @@ struct ScheduleMeetingFragment: View {
|
|||
|
||||
}
|
||||
.padding()
|
||||
if scheduleMeetingViewModel.operationInProgress {
|
||||
if meetingViewModel.operationInProgress {
|
||||
HStack {
|
||||
Spacer()
|
||||
VStack {
|
||||
|
|
@ -384,7 +384,7 @@ struct ScheduleMeetingFragment: View {
|
|||
Spacer()
|
||||
}.onDisappear {
|
||||
withAnimation {
|
||||
if scheduleMeetingViewModel.conferenceCreatedEvent {
|
||||
if meetingViewModel.conferenceCreatedEvent {
|
||||
meetingsListViewModel.computeMeetingsList()
|
||||
isShowScheduleMeetingFragment.toggle()
|
||||
}
|
||||
|
|
@ -463,26 +463,26 @@ struct ScheduleMeetingFragment: View {
|
|||
}
|
||||
|
||||
func pickDate() {
|
||||
let duration = min(scheduleMeetingViewModel.fromDate.distance(to: scheduleMeetingViewModel.toDate), 86400) // Limit auto correction of dates to 24h
|
||||
let duration = min(meetingViewModel.fromDate.distance(to: meetingViewModel.toDate), 86400) // Limit auto correction of dates to 24h
|
||||
if setFromDate {
|
||||
scheduleMeetingViewModel.fromDate = selectedDate
|
||||
meetingViewModel.fromDate = selectedDate
|
||||
// If new startdate is after previous end date, bump up the end date
|
||||
if selectedDate > scheduleMeetingViewModel.toDate {
|
||||
scheduleMeetingViewModel.toDate = Calendar.current.date(byAdding: .second, value: Int(duration), to: selectedDate)!
|
||||
if selectedDate > meetingViewModel.toDate {
|
||||
meetingViewModel.toDate = Calendar.current.date(byAdding: .second, value: Int(duration), to: selectedDate)!
|
||||
}
|
||||
} else {
|
||||
scheduleMeetingViewModel.toDate = selectedDate
|
||||
if selectedDate < scheduleMeetingViewModel.fromDate {
|
||||
meetingViewModel.toDate = selectedDate
|
||||
if selectedDate < meetingViewModel.fromDate {
|
||||
// If new end date is before the previous start date, bump down the start date to the earlier possible from current time
|
||||
if (Date.now.distance(to: selectedDate) < duration) {
|
||||
scheduleMeetingViewModel.fromDate = Date.now
|
||||
meetingViewModel.fromDate = Date.now
|
||||
} else {
|
||||
scheduleMeetingViewModel.fromDate = Calendar.current.date(byAdding: .second, value: (-1)*Int(duration), to: selectedDate)!
|
||||
meetingViewModel.fromDate = Calendar.current.date(byAdding: .second, value: (-1)*Int(duration), to: selectedDate)!
|
||||
}
|
||||
}
|
||||
}
|
||||
scheduleMeetingViewModel.computeDateLabels()
|
||||
scheduleMeetingViewModel.computeTimeLabels()
|
||||
meetingViewModel.computeDateLabels()
|
||||
meetingViewModel.computeTimeLabels()
|
||||
}
|
||||
|
||||
@Sendable private func delayColor() async {
|
||||
|
|
@ -499,7 +499,7 @@ struct ScheduleMeetingFragment: View {
|
|||
}
|
||||
|
||||
#Preview {
|
||||
ScheduleMeetingFragment(scheduleMeetingViewModel: ScheduleMeetingViewModel()
|
||||
ScheduleMeetingFragment(meetingViewModel: MeetingViewModel()
|
||||
, meetingsListViewModel: MeetingsListViewModel()
|
||||
, isShowScheduleMeetingFragment: .constant(true))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,39 @@
|
|||
//
|
||||
// MeetingsView.swift
|
||||
// Linphone
|
||||
//
|
||||
// Created by QuentinArguillere on 18/04/2024.
|
||||
//
|
||||
/*
|
||||
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct MeetingsView: View {
|
||||
|
||||
@ObservedObject var meetingsListViewModel: MeetingsListViewModel
|
||||
@ObservedObject var scheduleMeetingViewModel: ScheduleMeetingViewModel
|
||||
@ObservedObject var meetingViewModel: MeetingViewModel
|
||||
|
||||
@Binding var isShowScheduleMeetingFragment: Bool
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
ZStack(alignment: .bottomTrailing) {
|
||||
MeetingsFragment(meetingsListViewModel: meetingsListViewModel, scheduleMeetingViewModel: scheduleMeetingViewModel)
|
||||
MeetingsFragment(meetingsListViewModel: meetingsListViewModel, meetingViewModel: meetingViewModel)
|
||||
|
||||
Button {
|
||||
withAnimation {
|
||||
scheduleMeetingViewModel.resetViewModelData()
|
||||
meetingViewModel.resetViewModelData()
|
||||
isShowScheduleMeetingFragment.toggle()
|
||||
}
|
||||
} label: {
|
||||
|
|
@ -44,7 +56,7 @@ struct MeetingsView: View {
|
|||
#Preview {
|
||||
MeetingsView(
|
||||
meetingsListViewModel: MeetingsListViewModel(),
|
||||
scheduleMeetingViewModel: ScheduleMeetingViewModel(),
|
||||
meetingViewModel: MeetingViewModel(),
|
||||
isShowScheduleMeetingFragment: .constant(false)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,21 @@
|
|||
//
|
||||
// MeetingModel.swift
|
||||
// Linphone
|
||||
//
|
||||
// Created by QuentinArguillere on 19/03/2024.
|
||||
//
|
||||
/*
|
||||
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import linphonesw
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,21 @@
|
|||
//
|
||||
// MeetingsListItemModel.swift
|
||||
// Linphone
|
||||
//
|
||||
// Created by QuentinArguillere on 19/03/2024.
|
||||
//
|
||||
/*
|
||||
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import Foundation
|
||||
|
||||
extension String {
|
||||
|
|
|
|||
|
|
@ -19,145 +19,321 @@
|
|||
|
||||
import Foundation
|
||||
import linphonesw
|
||||
import Combine
|
||||
|
||||
class MeetingViewModel: ObservableObject {
|
||||
static let TAG = "[Meeting ViewModel]"
|
||||
/*
|
||||
private var coreContext = CoreContext.shared
|
||||
static let TAG = "[MeetingViewModel]"
|
||||
|
||||
@Published var showBackbutton: Bool = false
|
||||
@Published var isBroadcast: Bool = false
|
||||
@Published var isEditable: Bool = false
|
||||
@Published var isBroadcastSelected: Bool = false
|
||||
@Published var showBroadcastHelp: Bool = false
|
||||
@Published var subject: String = ""
|
||||
@Published var sipUri: String = ""
|
||||
@Published var description: String?
|
||||
@Published var description: String = ""
|
||||
@Published var allDayMeeting: Bool = false
|
||||
@Published var fromDateStr: String = ""
|
||||
@Published var fromTime: String = ""
|
||||
@Published var toDateStr: String = ""
|
||||
@Published var toTime: String = ""
|
||||
@Published var timezone: String = ""
|
||||
@Published var startDate: Date?
|
||||
@Published var endDate: Date?
|
||||
@Published var dateTime: String = ""
|
||||
@Published var sendInvitations: Bool = true
|
||||
@Published var participants: [SelectedAddressModel] = []
|
||||
@Published var operationInProgress: Bool = false
|
||||
@Published var conferenceCreatedEvent: Bool = false
|
||||
@Published var conferenceUri: String = ""
|
||||
|
||||
@Published var speakers: [ParticipantModel] = []
|
||||
@Published var participants: [ParticipantModel] = []
|
||||
@Published var conferenceInfoFoundEvent: Bool = false
|
||||
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 = ""
|
||||
|
||||
var meetingModel: MeetingModel
|
||||
|
||||
init(model: MeetingModel) {
|
||||
meetingModel = model
|
||||
init() {
|
||||
fromDate = Calendar.current.date(byAdding: .hour, value: 1, to: Date.now)!
|
||||
toDate = Calendar.current.date(byAdding: .hour, value: 2, to: Date.now)!
|
||||
computeDateLabels()
|
||||
computeTimeLabels()
|
||||
updateTimezone()
|
||||
}
|
||||
|
||||
func findConferenceInfo(uri: String) {
|
||||
coreContext.doOnCoreQueue { core in
|
||||
var confInfoFound = false
|
||||
if let address = try? Factory.Instance.createAddress(addr: uri) {
|
||||
|
||||
if let confInfo = core.findConferenceInformationFromUri(uri: address) {
|
||||
Log.info("\(MeetingViewModel.TAG) Conference info with SIP URI \(uri) was found")
|
||||
self.meetingModel.confInfo = confInfo
|
||||
self.configureConferenceInfo(core: core)
|
||||
confInfoFound = true
|
||||
} else {
|
||||
Log.error("\(MeetingViewModel.TAG) Conference info with SIP URI \(uri) couldn't be found!")
|
||||
confInfoFound = false
|
||||
}
|
||||
} else {
|
||||
Log.error("\(MeetingViewModel.TAG) Failed to parse SIP URI \(uri) as Address!")
|
||||
confInfoFound = false
|
||||
}
|
||||
DispatchQueue.main.sync {
|
||||
self.conferenceInfoFoundEvent = confInfoFound
|
||||
}
|
||||
}
|
||||
func resetViewModelData() {
|
||||
isBroadcastSelected = false
|
||||
showBroadcastHelp = false
|
||||
subject = ""
|
||||
description = ""
|
||||
allDayMeeting = false
|
||||
timezone = ""
|
||||
sendInvitations = true
|
||||
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()
|
||||
computeTimeLabels()
|
||||
updateTimezone()
|
||||
}
|
||||
|
||||
private func configureConferenceInfo(core: Core) {
|
||||
/*
|
||||
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
|
||||
}
|
||||
})
|
||||
|
||||
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)!
|
||||
func computeDateLabels() {
|
||||
var day = fromDate.formatted(Date.FormatStyle().weekday(.wide))
|
||||
var dayNumber = fromDate.formatted(Date.FormatStyle().day(.twoDigits))
|
||||
var month = fromDate.formatted(Date.FormatStyle().month(.wide))
|
||||
fromDateStr = "\(day) \(dayNumber), \(month)"
|
||||
Log.info("\(MeetingViewModel.TAG) computed start date is \(fromDateStr)")
|
||||
|
||||
day = toDate.formatted(Date.FormatStyle().weekday(.wide))
|
||||
dayNumber = toDate.formatted(Date.FormatStyle().day(.twoDigits))
|
||||
month = toDate.formatted(Date.FormatStyle().month(.wide))
|
||||
toDateStr = "\(day) \(dayNumber), \(month)"
|
||||
Log.info("\(MeetingViewModel.TAG)) computed end date is \(toDateStr)")
|
||||
}
|
||||
|
||||
func computeTimeLabels() {
|
||||
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()
|
||||
fromTime = formatter.string(from: fromDate)
|
||||
toTime = formatter.string(from: toDate)
|
||||
}
|
||||
|
||||
private func computeParticipantsList() {
|
||||
var speakersList: [ParticipantModel] = []
|
||||
var participantsList: [ParticipantModel] = []
|
||||
var allSpeaker = true
|
||||
let organizer = meetingModel.confInfo.organizer
|
||||
var organizerFound = false
|
||||
for pInfo in meetingModel.confInfo.participantInfos {
|
||||
if let participantAddress = pInfo.address {
|
||||
let isOrganizer = organizer != nil && organizer!.weakEqual(address2: participantAddress)
|
||||
|
||||
Log.info("\(MeetingViewModel.TAG) Conference \(meetingModel.confInfo.subject)[${conferenceInfo.subject}] \(isOrganizer ? "organizer: " : "participant: ") \(participantAddress.asStringUriOnly()) is a \(pInfo.role)")
|
||||
if isOrganizer {
|
||||
organizerFound = true
|
||||
}
|
||||
|
||||
if pInfo.role == Participant.Role.Listener {
|
||||
allSpeaker = false
|
||||
participantsList.append(ParticipantModel(address: participantAddress))
|
||||
} else {
|
||||
speakersList.append(ParticipantModel(address: participantAddress))
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
func addParticipants(participantsToAdd: [SelectedAddressModel]) {
|
||||
var list = participants
|
||||
for selectedAddr in participantsToAdd {
|
||||
if let found = list.first(where: { $0.address.weakEqual(address2: selectedAddr.address) }) {
|
||||
Log.info("\(MeetingViewModel.TAG) Participant \(found.address.asStringUriOnly()) already in list, skipping")
|
||||
continue
|
||||
}
|
||||
|
||||
list.append(selectedAddr)
|
||||
Log.info("\(MeetingViewModel.TAG) Added participant \(selectedAddr.address.asStringUriOnly())")
|
||||
}
|
||||
Log.info("\(MeetingViewModel.TAG) [\(list.count - participants.count) participants added, now there are \(list.count) participants in list")
|
||||
|
||||
participants = list
|
||||
}
|
||||
|
||||
private func fillConferenceInfo(confInfo: ConferenceInfo) {
|
||||
confInfo.subject = self.subject
|
||||
confInfo.description = self.description
|
||||
confInfo.dateTime = time_t(self.fromDate.timeIntervalSince1970)
|
||||
confInfo.duration = UInt(self.fromDate.distance(to: self.toDate) / 60)
|
||||
|
||||
let participantsList = self.participants
|
||||
var participantsInfoList: [ParticipantInfo] = []
|
||||
for participant in participantsList {
|
||||
if let info = try? Factory.Instance.createParticipantInfo(address: participant.address) {
|
||||
// For meetings, all participants must have Speaker role
|
||||
info.role = Participant.Role.Speaker
|
||||
participantsInfoList.append(info)
|
||||
} else {
|
||||
Log.error("\(MeetingViewModel.TAG) Failed to create Participant Info from address \(participant.address.asStringUriOnly())")
|
||||
}
|
||||
}
|
||||
confInfo.participantInfos = participantsInfoList
|
||||
}
|
||||
|
||||
private func initConferenceSchedulerAndListeners(core: Core) {
|
||||
self.conferenceScheduler = try? core.createConferenceScheduler()
|
||||
|
||||
if allSpeaker {
|
||||
Log.info("$TAG All participants have Speaker role, considering it is a meeting")
|
||||
participantsList = speakersList
|
||||
self.mSchedulerSubscriptions.insert(self.conferenceScheduler?.publisher?.onStateChanged?.postOnCoreQueue { (cbVal: (conferenceScheduler: ConferenceScheduler, state: ConferenceScheduler.State)) in
|
||||
|
||||
Log.info("\(MeetingViewModel.TAG) Conference state changed \(cbVal.state)")
|
||||
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 {
|
||||
let conferenceAddress = self.conferenceScheduler?.info?.uri
|
||||
if let confInfoToEdit = self.conferenceInfoToEdit {
|
||||
Log.info("\(MeetingViewModel.TAG) Conference info \(confInfoToEdit.uri?.asStringUriOnly() ?? "'nil'") has been updated")
|
||||
} else {
|
||||
Log.info("\(MeetingViewModel.TAG) Conference info created, address will be \(conferenceAddress?.asStringUriOnly() ?? "'nil'")")
|
||||
}
|
||||
|
||||
if self.sendInvitations {
|
||||
Log.info("\(MeetingViewModel.TAG) User asked for invitations to be sent, let's do it")
|
||||
if let chatRoomParams = try? core.createDefaultChatRoomParams() {
|
||||
chatRoomParams.groupEnabled = false
|
||||
chatRoomParams.backend = ChatRoom.Backend.FlexisipChat
|
||||
chatRoomParams.encryptionEnabled = true
|
||||
chatRoomParams.subject = "Meeting invitation" // Won't be used
|
||||
self.conferenceScheduler?.sendInvitations(chatRoomParams: chatRoomParams)
|
||||
} else {
|
||||
Log.error("\(MeetingViewModel.TAG) Failed to create default chatroom parameters. This should not happen")
|
||||
}
|
||||
} else {
|
||||
Log.info("\(MeetingViewModel.TAG) User didn't asked for invitations to be sent")
|
||||
DispatchQueue.main.async {
|
||||
self.operationInProgress = false
|
||||
self.conferenceCreatedEvent = true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.mSchedulerSubscriptions.insert(self.conferenceScheduler?.publisher?.onInvitationsSent?.postOnCoreQueue { (cbVal: (conferenceScheduler: ConferenceScheduler, failedInvitations: [Address])) in
|
||||
|
||||
if cbVal.failedInvitations.isEmpty {
|
||||
Log.info("\(MeetingViewModel.TAG) All invitations have been sent")
|
||||
} else if cbVal.failedInvitations.count == self.participants.count {
|
||||
Log.error("\(MeetingViewModel.TAG) No invitation sent!")
|
||||
// TODO: show error toast
|
||||
} else {
|
||||
Log.warn("\(MeetingViewModel.TAG) \(cbVal.failedInvitations.count) invitations couldn't have been sent for:")
|
||||
for failInv in cbVal.failedInvitations {
|
||||
Log.warn(failInv.asStringUriOnly())
|
||||
}
|
||||
// TODO: show error toast
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.operationInProgress = false
|
||||
self.conferenceCreatedEvent = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func schedule() {
|
||||
if subject.isEmpty || participants.isEmpty {
|
||||
Log.error("\(MeetingViewModel.TAG) Either no subject was set or no participant was selected, can't schedule meeting.")
|
||||
// TODO: show red toast
|
||||
return
|
||||
}
|
||||
operationInProgress = true
|
||||
|
||||
if !organizerFound, let organizerAddress = organizer {
|
||||
Log.info("$TAG Organizer not found in participants list, adding it to participants list")
|
||||
participantsList.append(ParticipantModel(address: organizerAddress))
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.isBroadcast = !allSpeaker
|
||||
self.speakers = speakersList
|
||||
self.participants = participantsList
|
||||
CoreContext.shared.doOnCoreQueue { core in
|
||||
Log.info("\(MeetingViewModel.TAG) Scheduling \(self.isBroadcastSelected ? "broadcast" : "meeting")")
|
||||
|
||||
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)
|
||||
}
|
||||
self.conferenceScheduler?.account = localAccount
|
||||
// Will trigger the conference creation automatically
|
||||
self.conferenceScheduler?.info = conferenceInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func update() {
|
||||
self.operationInProgress = true
|
||||
CoreContext.shared.doOnCoreQueue { core in
|
||||
Log.info("\(MeetingViewModel.TAG) Updating \(self.isBroadcastSelected ? "broadcast" : "meeting")")
|
||||
|
||||
if let conferenceInfo = self.conferenceInfoToEdit {
|
||||
self.fillConferenceInfo(confInfo: conferenceInfo)
|
||||
if self.conferenceScheduler == nil {
|
||||
self.initConferenceSchedulerAndListeners(core: core)
|
||||
}
|
||||
|
||||
// Will trigger the conference update automatically
|
||||
self.conferenceScheduler?.info = conferenceInfo
|
||||
} else {
|
||||
Log.error("No conference info to edit found!")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if let conferenceInfo = core.findConferenceInformationFromUri(uri: conferenceAddress) {
|
||||
|
||||
self.conferenceInfoToEdit = conferenceInfo
|
||||
Log.info("\(MeetingViewModel.TAG) Found conference info matching URI \(conferenceInfo.uri?.asString()) with subject \(conferenceInfo.subject)")
|
||||
|
||||
self.fromDate = Date(timeIntervalSince1970: TimeInterval(conferenceInfo.dateTime))
|
||||
self.toDate = Calendar.current.date(byAdding: .minute, value: Int(conferenceInfo.duration), to: self.fromDate)!
|
||||
|
||||
var list: [SelectedAddressModel] = []
|
||||
for partInfo in conferenceInfo.participantInfos {
|
||||
if let addr = partInfo.address {
|
||||
ContactAvatarModel.getAvatarModelFromAddress(address: addr) { avatarResult in
|
||||
let avatarModel = avatarResult
|
||||
self.participants.append(SelectedAddressModel(addr: addr, avModel: avatarModel))
|
||||
Log.info("\(MeetingViewModel.TAG) Loaded participant \(addr.asStringUriOnly())")
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.info("\(MeetingViewModel.TAG) \(list.count) participants loaded from found conference info")
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.subject = conferenceInfo.subject ?? ""
|
||||
self.description = conferenceInfo.description ?? ""
|
||||
self.isBroadcastSelected = false // TODO FIXME
|
||||
self.computeDateLabels()
|
||||
self.computeTimeLabels()
|
||||
self.updateTimezone()
|
||||
//self.participants = list
|
||||
}
|
||||
|
||||
} else {
|
||||
Log.error("\(MeetingViewModel.TAG) Failed to find a conference info matching URI [${conferenceAddress.asString()}], abort")
|
||||
}
|
||||
} else {
|
||||
Log.error("\(MeetingViewModel.TAG) Failed to parse conference URI [$conferenceUri], abort")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,339 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import linphonesw
|
||||
import Combine
|
||||
|
||||
class ScheduleMeetingViewModel: ObservableObject {
|
||||
static let TAG = "[ScheduleMeetingViewModel]"
|
||||
|
||||
@Published var isBroadcastSelected: Bool = false
|
||||
@Published var showBroadcastHelp: Bool = false
|
||||
@Published var subject: String = ""
|
||||
@Published var description: String = ""
|
||||
@Published var allDayMeeting: Bool = false
|
||||
@Published var fromDateStr: String = ""
|
||||
@Published var fromTime: String = ""
|
||||
@Published var toDateStr: String = ""
|
||||
@Published var toTime: String = ""
|
||||
@Published var timezone: String = ""
|
||||
@Published var sendInvitations: Bool = true
|
||||
@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)!
|
||||
toDate = Calendar.current.date(byAdding: .hour, value: 2, to: Date.now)!
|
||||
computeDateLabels()
|
||||
computeTimeLabels()
|
||||
updateTimezone()
|
||||
}
|
||||
|
||||
func resetViewModelData() {
|
||||
isBroadcastSelected = false
|
||||
showBroadcastHelp = false
|
||||
subject = ""
|
||||
description = ""
|
||||
allDayMeeting = false
|
||||
timezone = ""
|
||||
sendInvitations = true
|
||||
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()
|
||||
computeTimeLabels()
|
||||
updateTimezone()
|
||||
}
|
||||
|
||||
func computeDateLabels() {
|
||||
var day = fromDate.formatted(Date.FormatStyle().weekday(.wide))
|
||||
var dayNumber = fromDate.formatted(Date.FormatStyle().day(.twoDigits))
|
||||
var month = fromDate.formatted(Date.FormatStyle().month(.wide))
|
||||
fromDateStr = "\(day) \(dayNumber), \(month)"
|
||||
Log.info("\(ScheduleMeetingViewModel.TAG) computed start date is \(fromDateStr)")
|
||||
|
||||
day = toDate.formatted(Date.FormatStyle().weekday(.wide))
|
||||
dayNumber = toDate.formatted(Date.FormatStyle().day(.twoDigits))
|
||||
month = toDate.formatted(Date.FormatStyle().month(.wide))
|
||||
toDateStr = "\(day) \(dayNumber), \(month)"
|
||||
Log.info("\(ScheduleMeetingViewModel.TAG)) computed end date is \(toDateStr)")
|
||||
}
|
||||
|
||||
func computeTimeLabels() {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = Locale.current.identifier == "fr_FR" ? "HH:mm" : "h:mm a"
|
||||
fromTime = formatter.string(from: fromDate)
|
||||
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
|
||||
}
|
||||
|
||||
func addParticipants(participantsToAdd: [SelectedAddressModel]) {
|
||||
var list = participants
|
||||
for selectedAddr in participantsToAdd {
|
||||
if let found = list.first(where: { $0.address.weakEqual(address2: selectedAddr.address) }) {
|
||||
Log.info("\(ScheduleMeetingViewModel.TAG) Participant \(found.address.asStringUriOnly()) already in list, skipping")
|
||||
continue
|
||||
}
|
||||
|
||||
list.append(selectedAddr)
|
||||
Log.info("\(ScheduleMeetingViewModel.TAG) Added participant \(selectedAddr.address.asStringUriOnly())")
|
||||
}
|
||||
Log.info("\(ScheduleMeetingViewModel.TAG) [\(list.count - participants.count) participants added, now there are \(list.count) participants in list")
|
||||
|
||||
participants = list
|
||||
}
|
||||
|
||||
private func fillConferenceInfo(confInfo: ConferenceInfo) {
|
||||
confInfo.subject = self.subject
|
||||
confInfo.description = self.description
|
||||
confInfo.dateTime = time_t(self.fromDate.timeIntervalSince1970)
|
||||
confInfo.duration = UInt(self.fromDate.distance(to: self.toDate) / 60)
|
||||
|
||||
let participantsList = self.participants
|
||||
var participantsInfoList: [ParticipantInfo] = []
|
||||
for participant in participantsList {
|
||||
if let info = try? Factory.Instance.createParticipantInfo(address: participant.address) {
|
||||
// For meetings, all participants must have Speaker role
|
||||
info.role = Participant.Role.Speaker
|
||||
participantsInfoList.append(info)
|
||||
} else {
|
||||
Log.error("\(ScheduleMeetingViewModel.TAG) Failed to create Participant Info from address \(participant.address.asStringUriOnly())")
|
||||
}
|
||||
}
|
||||
confInfo.participantInfos = participantsInfoList
|
||||
}
|
||||
|
||||
private func initConferenceSchedulerAndListeners(core: Core) {
|
||||
self.conferenceScheduler = try? core.createConferenceScheduler()
|
||||
|
||||
self.mSchedulerSubscriptions.insert(self.conferenceScheduler?.publisher?.onStateChanged?.postOnCoreQueue { (cbVal: (conferenceScheduler: ConferenceScheduler, state: ConferenceScheduler.State)) in
|
||||
|
||||
Log.info("\(ScheduleMeetingViewModel.TAG) Conference state changed \(cbVal.state)")
|
||||
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 {
|
||||
let conferenceAddress = self.conferenceScheduler?.info?.uri
|
||||
if let confInfoToEdit = self.conferenceInfoToEdit {
|
||||
Log.info("\(ScheduleMeetingViewModel.TAG) Conference info \(confInfoToEdit.uri?.asStringUriOnly() ?? "'nil'") has been updated")
|
||||
} else {
|
||||
Log.info("\(ScheduleMeetingViewModel.TAG) Conference info created, address will be \(conferenceAddress?.asStringUriOnly() ?? "'nil'")")
|
||||
}
|
||||
|
||||
if self.sendInvitations {
|
||||
Log.info("\(ScheduleMeetingViewModel.TAG) User asked for invitations to be sent, let's do it")
|
||||
if let chatRoomParams = try? core.createDefaultChatRoomParams() {
|
||||
chatRoomParams.groupEnabled = false
|
||||
chatRoomParams.backend = ChatRoom.Backend.FlexisipChat
|
||||
chatRoomParams.encryptionEnabled = true
|
||||
chatRoomParams.subject = "Meeting invitation" // Won't be used
|
||||
self.conferenceScheduler?.sendInvitations(chatRoomParams: chatRoomParams)
|
||||
} else {
|
||||
Log.error("\(ScheduleMeetingViewModel.TAG) Failed to create default chatroom parameters. This should not happen")
|
||||
}
|
||||
} else {
|
||||
Log.info("\(ScheduleMeetingViewModel.TAG) User didn't asked for invitations to be sent")
|
||||
DispatchQueue.main.async {
|
||||
self.operationInProgress = false
|
||||
self.conferenceCreatedEvent = true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.mSchedulerSubscriptions.insert(self.conferenceScheduler?.publisher?.onInvitationsSent?.postOnCoreQueue { (cbVal: (conferenceScheduler: ConferenceScheduler, failedInvitations: [Address])) in
|
||||
|
||||
if cbVal.failedInvitations.isEmpty {
|
||||
Log.info("\(ScheduleMeetingViewModel.TAG) All invitations have been sent")
|
||||
} else if cbVal.failedInvitations.count == self.participants.count {
|
||||
Log.error("\(ScheduleMeetingViewModel.TAG) No invitation sent!")
|
||||
// TODO: show error toast
|
||||
} else {
|
||||
Log.warn("\(ScheduleMeetingViewModel.TAG) \(cbVal.failedInvitations.count) invitations couldn't have been sent for:")
|
||||
for failInv in cbVal.failedInvitations {
|
||||
Log.warn(failInv.asStringUriOnly())
|
||||
}
|
||||
// TODO: show error toast
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.operationInProgress = false
|
||||
self.conferenceCreatedEvent = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func schedule() {
|
||||
if subject.isEmpty || participants.isEmpty {
|
||||
Log.error("\(ScheduleMeetingViewModel.TAG) Either no subject was set or no participant was selected, can't schedule meeting.")
|
||||
// TODO: show red toast
|
||||
return
|
||||
}
|
||||
operationInProgress = true
|
||||
|
||||
CoreContext.shared.doOnCoreQueue { core in
|
||||
Log.info("\(ScheduleMeetingViewModel.TAG) Scheduling \(self.isBroadcastSelected ? "broadcast" : "meeting")")
|
||||
|
||||
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)
|
||||
}
|
||||
self.conferenceScheduler?.account = localAccount
|
||||
// Will trigger the conference creation automatically
|
||||
self.conferenceScheduler?.info = conferenceInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func update() {
|
||||
self.operationInProgress = true
|
||||
CoreContext.shared.doOnCoreQueue { core in
|
||||
Log.info("\(ScheduleMeetingViewModel.TAG) Updating \(self.isBroadcastSelected ? "broadcast" : "meeting")")
|
||||
|
||||
if let conferenceInfo = self.conferenceInfoToEdit {
|
||||
self.fillConferenceInfo(confInfo: conferenceInfo)
|
||||
if self.conferenceScheduler == nil {
|
||||
self.initConferenceSchedulerAndListeners(core: core)
|
||||
}
|
||||
|
||||
// Will trigger the conference update automatically
|
||||
self.conferenceScheduler?.info = conferenceInfo
|
||||
} else {
|
||||
Log.error("No conference info to edit found!")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if let conferenceInfo = core.findConferenceInformationFromUri(uri: conferenceAddress) {
|
||||
|
||||
self.conferenceInfoToEdit = conferenceInfo
|
||||
Log.info("\(ScheduleMeetingViewModel.TAG) Found conference info matching URI \(conferenceInfo.uri?.asString()) with subject \(conferenceInfo.subject)")
|
||||
|
||||
self.fromDate = Date(timeIntervalSince1970: TimeInterval(conferenceInfo.dateTime))
|
||||
self.toDate = Calendar.current.date(byAdding: .minute, value: Int(conferenceInfo.duration), to: self.fromDate)!
|
||||
|
||||
var list: [SelectedAddressModel] = []
|
||||
for partInfo in conferenceInfo.participantInfos {
|
||||
if let addr = partInfo.address {
|
||||
ContactAvatarModel.getAvatarModelFromAddress(address: addr) { avatarResult in
|
||||
let avatarModel = avatarResult
|
||||
self.participants.append(SelectedAddressModel(addr: addr, avModel: avatarModel))
|
||||
Log.info("\(ScheduleMeetingViewModel.TAG) Loaded participant \(addr.asStringUriOnly())")
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.info("\(ScheduleMeetingViewModel.TAG) \(list.count) participants loaded from found conference info")
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.subject = conferenceInfo.subject ?? ""
|
||||
self.description = conferenceInfo.description ?? ""
|
||||
self.isBroadcastSelected = false // TODO FIXME
|
||||
self.computeDateLabels()
|
||||
self.computeTimeLabels()
|
||||
self.updateTimezone()
|
||||
//self.participants = list
|
||||
}
|
||||
|
||||
} else {
|
||||
Log.error("\(ScheduleMeetingViewModel.TAG) Failed to find a conference info matching URI [${conferenceAddress.asString()}], abort")
|
||||
}
|
||||
} else {
|
||||
Log.error("\(ScheduleMeetingViewModel.TAG) Failed to parse conference URI [$conferenceUri], abort")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,9 +1,21 @@
|
|||
//
|
||||
// AddParticipantsViewModel.swift
|
||||
// Linphone
|
||||
//
|
||||
// Created by QuentinArguillere on 29/04/2024.
|
||||
//
|
||||
/*
|
||||
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue