mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-17 11:08:06 +00:00
Implement conference details view with edit option.
This commit is contained in:
parent
0aeef2f022
commit
570007c2c6
10 changed files with 409 additions and 90 deletions
21
Linphone/Assets.xcassets/video-conference.imageset/Contents.json
vendored
Normal file
21
Linphone/Assets.xcassets/video-conference.imageset/Contents.json
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
1
Linphone/Assets.xcassets/video-conference.imageset/video-conference.svg
vendored
Normal file
1
Linphone/Assets.xcassets/video-conference.imageset/video-conference.svg
vendored
Normal 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 |
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue