mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-17 11:08:06 +00:00
My Conferences view
This commit is contained in:
parent
176cfd8b4b
commit
bd1ea4a5e3
9 changed files with 169 additions and 67 deletions
|
|
@ -55,7 +55,7 @@ class ScheduledConferenceData {
|
|||
durationFormatter.unitsStyle = .positional
|
||||
durationFormatter.allowedUnits = [.minute, .second ]
|
||||
durationFormatter.zeroFormattingBehavior = [ .pad ]
|
||||
duration.value = durationFormatter.string(from: TimeInterval(conferenceInfo.duration))
|
||||
duration.value = conferenceInfo.duration > 0 ? durationFormatter.string(from: TimeInterval(conferenceInfo.duration)) : nil
|
||||
|
||||
organizer.value = conferenceInfo.organizer?.addressBookEnhancedDisplayName()
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@ class ScheduledConferencesViewModel {
|
|||
|
||||
func computeConferenceInfoList() {
|
||||
conferences.value!.removeAll()
|
||||
core.futureConferenceInformationList.forEach { conferenceInfo in // Sorted in the sdk
|
||||
let now = Date().timeIntervalSince1970 // Linphone uses time_t in seconds
|
||||
let oneHourAgo = now - 3600 // Show all conferences from 1 hour ago and forward
|
||||
core.getConferenceInformationListAfterTime(time: time_t(oneHourAgo)).forEach { conferenceInfo in
|
||||
conferences.value!.append(ScheduledConferenceData(conferenceInfo: conferenceInfo))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,42 +26,58 @@ class ScheduledConferencesCell: UITableViewCell {
|
|||
|
||||
let corner_radius = 7.0
|
||||
let border_width = 2.0
|
||||
static let button_size = 40
|
||||
|
||||
let clockIcon = UIImageView(image: UIImage(named: "conference_schedule_time_default"))
|
||||
let timeDuration = StyledLabel(VoipTheme.conference_invite_desc_font)
|
||||
let organiser = StyledLabel(VoipTheme.conference_invite_desc_font)
|
||||
let subject = StyledLabel(VoipTheme.conference_invite_subject_font)
|
||||
let participantsIcon = UIImageView(image: UIImage(named: "conference_schedule_participants_default"))
|
||||
let participants = StyledLabel(VoipTheme.conference_invite_desc_font)
|
||||
let infoConf = UIButton()
|
||||
|
||||
let descriptionTitle = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_description_title)
|
||||
let descriptionValue = StyledLabel(VoipTheme.conference_scheduling_font)
|
||||
let urlTitle = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_address_title)
|
||||
let urlAndCopy = UIView()
|
||||
let descriptionTitle = StyledLabel(VoipTheme.conference_invite_desc_font, VoipTexts.conference_description_title)
|
||||
let descriptionValue = StyledLabel(VoipTheme.conference_invite_desc_font)
|
||||
let urlTitle = StyledLabel(VoipTheme.conference_invite_desc_font, VoipTexts.conference_schedule_address_title)
|
||||
let urlValue = StyledLabel(VoipTheme.conference_scheduling_font)
|
||||
let copyLink = CallControlButton(buttonTheme: VoipTheme.scheduled_conference_action("voip_copy"))
|
||||
let joinEditDelete = UIView()
|
||||
let copyLink = CallControlButton(width:button_size,height:button_size,buttonTheme: VoipTheme.scheduled_conference_action("voip_copy"))
|
||||
let joinConf = FormButton(title:VoipTexts.conference_invite_join.uppercased(), backgroundStateColors: VoipTheme.button_green_background)
|
||||
let deleteConf = CallControlButton(buttonTheme: VoipTheme.scheduled_conference_action("voip_delete"))
|
||||
let editConf = CallControlButton(buttonTheme: VoipTheme.scheduled_conference_action("voip_edit"))
|
||||
|
||||
let deleteConf = CallControlButton(width:button_size,height:button_size,buttonTheme: VoipTheme.scheduled_conference_action("voip_delete"))
|
||||
let editConf = CallControlButton(width:button_size,height:button_size,buttonTheme: VoipTheme.scheduled_conference_action("voip_edit"))
|
||||
var owningTableView : UITableView? = nil
|
||||
let joinEditDelete = UIStackView()
|
||||
let expandedRows = UIStackView()
|
||||
|
||||
var conferenceData: ScheduledConferenceData? = nil {
|
||||
didSet {
|
||||
if let data = conferenceData {
|
||||
timeDuration.text = "\(data.time) ( \(data.duration) )"
|
||||
timeDuration.addIndicatorIcon(iconName: "conference_schedule_time_default", trailing: false)
|
||||
timeDuration.text = "\(data.time.value)"+(data.duration.value != nil ? " ( \(data.duration.value) )" : "")
|
||||
organiser.text = VoipTexts.conference_schedule_organizer+data.organizer.value!
|
||||
subject.text = data.subject.value!
|
||||
descriptionValue.text = data.description.value!
|
||||
urlValue.text = data.address.value!
|
||||
data.expanded.readCurrentAndObserve { expanded in
|
||||
self.contentView.layer.borderWidth = expanded == true ? 2.0 : 0.0
|
||||
self.descriptionTitle.isHidden = expanded != true
|
||||
self.descriptionValue.isHidden = expanded != true
|
||||
self.urlAndCopy.isHidden = expanded != true
|
||||
self.joinEditDelete.isHidden = expanded != true
|
||||
self.descriptionTitle.isHidden = expanded != true || self.descriptionValue.text?.count == 0
|
||||
self.descriptionValue.isHidden = expanded != true || self.descriptionValue.text?.count == 0
|
||||
self.infoConf.isSelected = expanded == true
|
||||
self.participants.text = expanded == true ? data.participantsExpanded.value : data.participantsShort.value
|
||||
self.participants.addIndicatorIcon(iconName: "conference_schedule_participants_default", trailing: false)
|
||||
self.participants.numberOfLines = expanded == true ? 6 : 2
|
||||
self.expandedRows.isHidden = expanded != true
|
||||
self.joinEditDelete.isHidden = expanded != true
|
||||
if let myAddress = Core.get().defaultAccount?.params?.identityAddress {
|
||||
self.editConf.isHidden = expanded != true || data.conferenceInfo.organizer?.weakEqual(address2: myAddress) != true
|
||||
} else {
|
||||
self.editConf.isHidden = true
|
||||
}
|
||||
self.participants.removeConstraints().alignUnder(view: self.subject,withMargin: 15).toRightOf(self.participantsIcon,withLeftMargin:10).toRightOf(self.participantsIcon,withLeftMargin:10).toLeftOf(self.infoConf,withRightMargin: 15).done()
|
||||
self.joinEditDelete.removeConstraints().alignUnder(view: self.expandedRows,withMargin: 15).alignParentRight(withMargin: 10).done()
|
||||
if (expanded == true) {
|
||||
self.joinEditDelete.alignParentBottom(withMargin: 10).done()
|
||||
} else {
|
||||
self.participants.alignParentBottom(withMargin: 10).done()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -75,48 +91,90 @@ class ScheduledConferencesCell: UITableViewCell {
|
|||
contentView.backgroundColor = VoipTheme.header_background_color
|
||||
contentView.layer.borderColor = VoipTheme.primary_color.cgColor
|
||||
|
||||
let rows = UIStackView()
|
||||
rows.axis = .vertical
|
||||
rows.addArrangedSubview(timeDuration)
|
||||
rows.addArrangedSubview(subject)
|
||||
|
||||
contentView.addSubview(clockIcon)
|
||||
clockIcon.alignParentTop(withMargin: 15).square(15).alignParentLeft(withMargin: 10).done()
|
||||
|
||||
let participantsAndInfos = UIView()
|
||||
participantsAndInfos.addSubview(participants)
|
||||
participants.alignParentLeft().done()
|
||||
participantsAndInfos.addSubview(infoConf)
|
||||
infoConf.toRightOf(participants).done()
|
||||
rows.addArrangedSubview(participantsAndInfos)
|
||||
infoConf.applyTintedIcons(tintedIcons: VoipTheme.conference_info_button)
|
||||
contentView.addSubview(timeDuration)
|
||||
timeDuration.alignParentTop(withMargin: 15).toRightOf(clockIcon,withLeftMargin:10).alignHorizontalCenterWith(clockIcon).done()
|
||||
|
||||
contentView.addSubview(organiser)
|
||||
organiser.alignParentTop(withMargin: 15).toRightOf(timeDuration, withLeftMargin:10).alignParentRight(withMargin:10).alignHorizontalCenterWith(clockIcon).done()
|
||||
|
||||
contentView.addSubview(subject)
|
||||
subject.alignUnder(view: timeDuration,withMargin: 15).alignParentLeft(withMargin: 10).done()
|
||||
|
||||
contentView.addSubview(participantsIcon)
|
||||
participantsIcon.alignUnder(view: subject,withMargin: 15).square(15).alignParentLeft(withMargin: 10).done()
|
||||
|
||||
infoConf.onClick {
|
||||
self.conferenceData?.toggleExpand()
|
||||
self.owningTableView?.reloadData()
|
||||
}
|
||||
|
||||
rows.addArrangedSubview(descriptionTitle)
|
||||
rows.addArrangedSubview(descriptionValue)
|
||||
contentView.addSubview(infoConf)
|
||||
infoConf.imageView?.contentMode = .scaleAspectFit
|
||||
infoConf.alignUnder(view: subject,withMargin: 15).square(30).alignParentRight(withMargin: 10).alignHorizontalCenterWith(participantsIcon).done()
|
||||
infoConf.applyTintedIcons(tintedIcons: VoipTheme.conference_info_button)
|
||||
|
||||
rows.addArrangedSubview(urlTitle)
|
||||
urlAndCopy.addSubview(urlValue)
|
||||
|
||||
contentView.addSubview(participants)
|
||||
participants.alignUnder(view: subject,withMargin: 15).toRightOf(participantsIcon,withLeftMargin:10).toRightOf(participantsIcon,withLeftMargin:10).toLeftOf(infoConf,withRightMargin: 15).done()
|
||||
|
||||
expandedRows.axis = .vertical
|
||||
expandedRows.spacing = 10
|
||||
contentView.addSubview(expandedRows)
|
||||
expandedRows.alignUnder(view: participants,withMargin: 15).matchParentSideBorders(insetedByDx:10).done()
|
||||
|
||||
expandedRows.addArrangedSubview(descriptionTitle)
|
||||
expandedRows.addArrangedSubview(descriptionValue)
|
||||
|
||||
expandedRows.addArrangedSubview(urlTitle)
|
||||
let urlAndCopy = UIStackView()
|
||||
urlAndCopy.addArrangedSubview(urlValue)
|
||||
urlValue.backgroundColor = .white
|
||||
self.urlValue.isEnabled = false
|
||||
urlValue.alignParentLeft().done()
|
||||
urlAndCopy.addSubview(copyLink)
|
||||
copyLink.toLeftOf(urlValue).done()
|
||||
rows.addArrangedSubview(urlAndCopy)
|
||||
|
||||
joinEditDelete.addSubview(joinConf)
|
||||
joinEditDelete.addSubview(editConf)
|
||||
joinEditDelete.addSubview(deleteConf)
|
||||
deleteConf.alignParentRight().done()
|
||||
editConf.toLeftOf(deleteConf).done()
|
||||
joinConf.toLeftOf(deleteConf).done()
|
||||
|
||||
joinConf.onClick {
|
||||
/*
|
||||
ConferenceWaitingRoomFragment *view = VIEW(ConferenceWaitingRoomFragment);
|
||||
[PhoneMainView.instance changeCurrentView:ConferenceWaitingRoomFragment.compositeViewDescription];
|
||||
[view setDetailsWithSubject:@"Sujet de la conférence" url:@"toto"];
|
||||
return;
|
||||
*/
|
||||
urlAndCopy.addArrangedSubview(copyLink)
|
||||
copyLink.toRightOf(urlValue,withLeftMargin: 10).done()
|
||||
expandedRows.addArrangedSubview(urlAndCopy)
|
||||
copyLink.onClick {
|
||||
UIPasteboard.general.string = self.conferenceData?.address.value!
|
||||
VoipDialog.toast(message: VoipTexts.conference_schedule_address_copied_to_clipboard)
|
||||
}
|
||||
|
||||
joinEditDelete.axis = .horizontal
|
||||
joinEditDelete.spacing = 10
|
||||
joinEditDelete.distribution = .equalSpacing
|
||||
|
||||
contentView.addSubview(joinEditDelete)
|
||||
joinEditDelete.alignUnder(view: expandedRows,withMargin: 15).alignParentRight(withMargin: 10).done()
|
||||
|
||||
|
||||
joinEditDelete.addArrangedSubview(joinConf)
|
||||
joinConf.width(150).done()
|
||||
joinConf.onClick {
|
||||
let view : ConferenceWaitingRoomFragment = self.VIEW(ConferenceWaitingRoomFragment.compositeViewDescription())
|
||||
PhoneMainView.instance().changeCurrentView(view.compositeViewDescription())
|
||||
view.setDetails(subject: (self.conferenceData?.subject.value)!, url: (self.conferenceData?.address.value)!)
|
||||
}
|
||||
|
||||
joinEditDelete.addArrangedSubview(editConf)
|
||||
editConf.onClick {
|
||||
// TODO
|
||||
}
|
||||
|
||||
joinEditDelete.addArrangedSubview(deleteConf)
|
||||
deleteConf.onClick {
|
||||
let delete = ButtonAttributes(text:VoipTexts.conference_info_confirm_removal_delete, action: {
|
||||
Core.get().deleteConferenceInformation(conferenceInfo: self.conferenceData!.conferenceInfo)
|
||||
ScheduledConferencesViewModel.shared.computeConferenceInfoList()
|
||||
self.owningTableView?.reloadData()
|
||||
}, isDestructive:false)
|
||||
let cancel = ButtonAttributes(text:VoipTexts.cancel, action: {}, isDestructive:true)
|
||||
VoipDialog(message:VoipTexts.conference_info_confirm_removal, givenButtons: [cancel,delete]).show()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import UIKit
|
|||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
@objc class ScheduledConferencesView: BackNextNavigationView, UICompositeViewDelegate, UITableViewDataSource {
|
||||
@objc class ScheduledConferencesView: BackNextNavigationView, UICompositeViewDelegate, UITableViewDataSource, UITableViewDelegate {
|
||||
|
||||
let conferenceListView = UITableView()
|
||||
let noConference = StyledLabel(VoipTheme.empty_list_font,VoipTexts.conference_no_schedule)
|
||||
|
|
@ -37,17 +37,20 @@ import linphonesw
|
|||
backAction: {
|
||||
PhoneMainView.instance().popView(self.compositeViewDescription())
|
||||
},nextAction: {
|
||||
PhoneMainView.instance().changeCurrentView(ConferenceSchedulingView.compositeDescription)
|
||||
},
|
||||
nextActionEnableCondition: MutableLiveData(false),
|
||||
nextActionEnableCondition: MutableLiveData(true),
|
||||
title:VoipTexts.conference_scheduled)
|
||||
super.nextButton.isHidden = true
|
||||
|
||||
super.nextButton.applyTintedIcons(tintedIcons: VoipTheme.conference_create_button)
|
||||
|
||||
|
||||
contentView.addSubview(conferenceListView)
|
||||
conferenceListView.isScrollEnabled = false
|
||||
self.view.addSubview(conferenceListView)
|
||||
conferenceListView.isScrollEnabled = true
|
||||
conferenceListView.dataSource = self
|
||||
conferenceListView.delegate = self
|
||||
conferenceListView.register(ScheduledConferencesCell.self, forCellReuseIdentifier: "ScheduledConferencesCell")
|
||||
conferenceListView.allowsSelection = false
|
||||
conferenceListView.rowHeight = UITableView.automaticDimension
|
||||
if #available(iOS 15.0, *) {
|
||||
conferenceListView.allowsFocus = false
|
||||
}
|
||||
|
|
@ -56,7 +59,6 @@ import linphonesw
|
|||
|
||||
view.addSubview(noConference)
|
||||
noConference.center().done()
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -66,21 +68,22 @@ import linphonesw
|
|||
super.viewWillAppear(animated)
|
||||
self.conferenceListView.reloadData()
|
||||
self.conferenceListView.removeConstraints().done()
|
||||
self.conferenceListView.matchParentSideBorders().alignUnder(view: super.topBar,withMargin: self.form_margin).alignParentBottom().done()
|
||||
self.conferenceListView.matchParentSideBorders(insetedByDx: 10).alignUnder(view: super.topBar,withMargin: self.form_margin).alignParentBottom().done()
|
||||
noConference.isHidden = !ScheduledConferencesViewModel.shared.daySplitted.isEmpty
|
||||
}
|
||||
|
||||
// TableView datasource delegate
|
||||
|
||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
let daysArray = Array(ScheduledConferencesViewModel.shared.daySplitted.keys)
|
||||
let daysArray = Array(ScheduledConferencesViewModel.shared.daySplitted.keys.sorted().reversed())
|
||||
let day = daysArray[section]
|
||||
return TimestampUtils.dateToString(date: day)
|
||||
return TimestampUtils.dateLongToString(date: day)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
|
||||
guard let header = view as? UITableViewHeaderFooterView else { return }
|
||||
header.textLabel?.applyStyle(VoipTheme.conference_invite_title_font)
|
||||
header.textLabel?.matchParentSideBorders().done()
|
||||
}
|
||||
|
||||
func numberOfSections(in tableView: UITableView) -> Int {
|
||||
|
|
@ -88,19 +91,30 @@ import linphonesw
|
|||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
let daysArray = Array(ScheduledConferencesViewModel.shared.daySplitted.keys)
|
||||
let daysArray = Array(ScheduledConferencesViewModel.shared.daySplitted.keys.sorted().reversed())
|
||||
let day = daysArray[section]
|
||||
return ScheduledConferencesViewModel.shared.daySplitted[day]!.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
let daysArray = Array(ScheduledConferencesViewModel.shared.daySplitted.keys.sorted().reversed())
|
||||
let day = daysArray[indexPath.section]
|
||||
guard let data = ScheduledConferencesViewModel.shared.daySplitted[day]?[indexPath.row] else {
|
||||
return UITableView.automaticDimension
|
||||
}
|
||||
return data.expanded.value! ? UITableView.automaticDimension : 100
|
||||
}
|
||||
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell:ScheduledConferencesCell = tableView.dequeueReusableCell(withIdentifier: "ScheduledConferencesCell") as! ScheduledConferencesCell
|
||||
let daysArray = Array(ScheduledConferencesViewModel.shared.daySplitted.keys)
|
||||
let daysArray = Array(ScheduledConferencesViewModel.shared.daySplitted.keys.sorted().reversed())
|
||||
let day = daysArray[indexPath.section]
|
||||
guard let data = ScheduledConferencesViewModel.shared.daySplitted[day]?[indexPath.row] else {
|
||||
return cell
|
||||
}
|
||||
cell.conferenceData = data
|
||||
cell.owningTableView = tableView
|
||||
return cell
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -308,6 +308,12 @@ extension UIView {
|
|||
return self
|
||||
}
|
||||
|
||||
func toLeftOf(_ view:UIView, withRightMargin:CGFloat) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.right.equalTo(view.snp.left).offset(-withRightMargin)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func centerX(withDx:Int = 0) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
|
|
|
|||
|
|
@ -53,6 +53,13 @@ class TimestampUtils {
|
|||
return dateFormatter.string(from: date)
|
||||
}
|
||||
|
||||
static func dateLongToString(date:Date) -> String {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateStyle = .long
|
||||
dateFormatter.timeStyle = .none
|
||||
return dateFormatter.string(from: date)
|
||||
}
|
||||
|
||||
static func timeToString(date:Date) -> String {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateStyle = .none
|
||||
|
|
|
|||
|
|
@ -104,7 +104,8 @@ import UIKit
|
|||
static let conference_schedule_organizer = NSLocalizedString("Organizer:",comment:"")
|
||||
static let conference_go_to_chat = NSLocalizedString("Conference's chat room",comment:"")
|
||||
static let conference_creation_failed = NSLocalizedString("Failed to create conference",comment:"")
|
||||
|
||||
static let conference_info_confirm_removal = NSLocalizedString("Do you really want to delete this conference?",comment:"")
|
||||
static let conference_info_confirm_removal_delete = NSLocalizedString("Delete",comment:"")
|
||||
|
||||
// Call Stats
|
||||
|
||||
|
|
|
|||
|
|
@ -388,6 +388,11 @@ class VoipTheme { // Names & values replicated from Android
|
|||
UIButton.State.selected.rawValue : TintableIcon(name: "voip_info",tintColor: LightDarkColor(primary_color,primary_color)),
|
||||
]
|
||||
|
||||
static let conference_create_button = [
|
||||
UIButton.State.normal.rawValue : TintableIcon(name: "voip_conference_new",tintColor: LightDarkColor(voip_dark_gray,voip_dark_gray)),
|
||||
UIButton.State.highlighted.rawValue : TintableIcon(name: "voip_conference_new",tintColor: LightDarkColor(primary_color,primary_color)),
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -86,11 +86,11 @@ class VoipDialog : UIView{
|
|||
}
|
||||
|
||||
func show() {
|
||||
rootVC()?.view.addSubview(self)
|
||||
VoipDialog.rootVC()?.view.addSubview(self)
|
||||
matchParentDimmensions().done()
|
||||
}
|
||||
|
||||
private func rootVC() -> UIViewController? {
|
||||
private static func rootVC() -> UIViewController? {
|
||||
return UIApplication.getTopMostViewController()
|
||||
}
|
||||
|
||||
|
|
@ -98,6 +98,15 @@ class VoipDialog : UIView{
|
|||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
static func toast(message:String, timeout:CGFloat = 1.5) {
|
||||
let alertDisapperTimeInSeconds = 2.0
|
||||
let alert = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet)
|
||||
rootVC()?.present(alert, animated: true)
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + alertDisapperTimeInSeconds) {
|
||||
alert.dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct ButtonAttributes {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue