Commit 565cac49 authored by Kateryna Kostiuk's avatar Kateryna Kostiuk

call: add conference

Change-Id: Ie6a14dbff5d7b00bdf418b1170fd98308788f68b
parent fa43b468
This diff is collapsed.
......@@ -40,4 +40,10 @@
- (BOOL) muteMedia:(NSString*)callId mediaType:(NSString*)media muted:(bool)muted;
- (void) playDTMF:(NSString*)code;
- (BOOL)joinConference:(NSString*)confID call:(NSString*)callID;
- (BOOL)joinConferences:(NSString*)firstConf secondConference:(NSString*)secondConf;
- (BOOL)joinCall:(NSString*)firstCall second:(NSString*)secondCall;
- (NSDictionary<NSString*,NSString*>*)getConferenceDetails:(NSString*)conferenceId;
- (NSArray<NSString*>*)getConferenceCalls:(NSString*)conferenceId;
- (BOOL)hangUpConference:(NSString*)conferenceId;
@end
......@@ -115,6 +115,28 @@ static id <CallsAdapterDelegate> _delegate;
}
}));
callHandlers.insert(exportable_callback<CallSignal::ConferenceCreated>([&](const std::string& confId) {
if (CallsAdapter.delegate) {
NSString* confIdString = [NSString stringWithUTF8String:confId.c_str()];
[CallsAdapter.delegate conferenceCreatedWithConference: confIdString];
}
}));
callHandlers.insert(exportable_callback<CallSignal::ConferenceChanged>([&](const std::string& confId, const std::string& state) {
if (CallsAdapter.delegate) {
NSString* confIdString = [NSString stringWithUTF8String:confId.c_str()];
NSString* stateString = [NSString stringWithUTF8String:state.c_str()];
[CallsAdapter.delegate conferenceChangedWithConference: confIdString state: stateString];
}
}));
callHandlers.insert(exportable_callback<CallSignal::ConferenceRemoved>([&](const std::string& confId) {
if (CallsAdapter.delegate) {
NSString* confIdString = [NSString stringWithUTF8String:confId.c_str()];
[CallsAdapter.delegate conferenceRemovedWithConference: confIdString];
}
}));
registerSignalHandlers(callHandlers);
}
......@@ -168,6 +190,32 @@ static id <CallsAdapterDelegate> _delegate;
return muteLocalMedia(std::string([callId UTF8String]), std::string([media UTF8String]), muted);
}
- (BOOL)joinConference:(NSString*)confID call:(NSString*)callID {
return addParticipant(std::string([callID UTF8String]), std::string([confID UTF8String]));
}
- (BOOL)joinCall:(NSString*)firstCall second:(NSString*)secondCall {
return joinParticipant(std::string([firstCall UTF8String]), std::string([secondCall UTF8String]));
}
- (BOOL)joinConferences:(NSString*)firstConf secondConference:(NSString*)secondConf {
return joinConference(std::string([firstConf UTF8String]), std::string([secondConf UTF8String]));
}
- (BOOL)hangUpConference:(NSString*)conferenceId {
return hangUpConference(std::string([conferenceId UTF8String]));
}
- (NSDictionary<NSString*,NSString*>*)getConferenceDetails:(NSString*)conferenceId {
std::map<std::string, std::string> confDetails = getConferenceDetails(std::string([conferenceId UTF8String]));
return [Utils mapToDictionnary:confDetails];
}
- (NSArray<NSString*>*)getConferenceCalls:(NSString*)conferenceId {
std::vector<std::string> calls = getParticipantList(std::string([conferenceId UTF8String]));
return [Utils vectorToArray:calls];
}
#pragma mark AccountAdapterDelegate
+ (id <CallsAdapterDelegate>)delegate {
......
......@@ -42,6 +42,7 @@ struct Renderer
SinkTarget::FrameBufferPtr daemonFramePtr_;
int width;
int height;
NSString* rendererId;
void bindAVSinkFunctions() {
avtarget.push = [this](std::unique_ptr<DRing::VideoFrame> frame) {
......@@ -52,7 +53,7 @@ struct Renderer
UIImage *image = [Utils
convertHardwareDecodedFrameToImage: std::move(frame->pointer())];
isRendering = true;
[VideoAdapter.delegate writeFrameWithImage: image];
[VideoAdapter.delegate writeFrameWithImage: image forCallId: rendererId];
isRendering = false;
}
};
......@@ -87,7 +88,7 @@ struct Renderer
UIImage* image = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
isRendering = true;
[VideoAdapter.delegate writeFrameWithImage: image];
[VideoAdapter.delegate writeFrameWithImage: image forCallId: rendererId];
isRendering = false;
}
}
......@@ -162,6 +163,7 @@ static id <VideoAdapterDelegate> _delegate;
auto renderer = std::make_shared<Renderer>();
renderer->width = static_cast<int>(w);
renderer->height = static_cast<int>(h);
renderer->rendererId = sinkId;
if(self.getDecodingAccelerated) {
renderer->bindAVSinkFunctions();
DRing::registerAVSinkTarget(_sinkId, renderer->avtarget);
......
......@@ -37,6 +37,8 @@ class ButtonsContainerView: UIView, NibLoadable {
@IBOutlet weak var cancelButton: UIButton!
@IBOutlet weak var switchCameraButton: UIButton!
@IBOutlet weak var acceptCallButton: UIButton!
@IBOutlet weak var addContactButton: UIButton!
@IBOutlet weak var sendMessageButton: UIButton!
//Constraints
@IBOutlet weak var cancelButtonWidthConstraint: NSLayoutConstraint!
......@@ -98,6 +100,8 @@ class ButtonsContainerView: UIView, NibLoadable {
dialpadButton.isHidden = true
switchSpeakerButton.isHidden = true
cancelButton.isHidden = false
addContactButton.isHidden = true
sendMessageButton.isHidden = true
if self.viewModel?.isIncoming ?? false {
acceptCallButton.isHidden = false
cancelButtonBottomConstraint.constant = 60
......@@ -111,52 +115,71 @@ class ButtonsContainerView: UIView, NibLoadable {
func optionsWithSpeaker() {
acceptCallButton.isHidden = true
cancelButtonCenterConstraint.constant = 0
if !self.isCallStarted {
self.isCallStarted = true
self.backgroundBlurEffect.isHidden = false
muteAudioButton.isHidden = false
if self.viewModel?.isAudioOnly ?? false {
muteVideoButton.isHidden = true
switchCameraButton.isHidden = true
if self.viewModel?.isSipCall ?? false {
dialpadButton.isHidden = false
}
cancelButtonBottomConstraint.constant = 20
} else {
muteVideoButton.isHidden = false
switchCameraButton.isHidden = false
cancelButtonBottomConstraint.constant = 120
self.backgroundBlurEffect.isHidden = false
muteAudioButton.isHidden = false
if self.viewModel?.isAudioOnly ?? false {
muteVideoButton.isHidden = true
switchCameraButton.isHidden = true
if self.viewModel?.isSipCall ?? false {
dialpadButton.isHidden = false
}
pauseCallButton.isHidden = false
switchSpeakerButton.isEnabled = true
switchSpeakerButton.isHidden = false
cancelButton.isHidden = false
cancelButtonBottomConstraint.constant = 20
} else {
muteVideoButton.isHidden = false
switchCameraButton.isHidden = false
cancelButtonBottomConstraint.constant = 80
}
pauseCallButton.isHidden = false
switchSpeakerButton.isEnabled = true
switchSpeakerButton.isHidden = false
cancelButton.isHidden = false
addContactButton.isHidden = false
sendMessageButton.isHidden = false
setUpConference()
}
func optionsWithoutSpeaker() {
acceptCallButton.isHidden = true
cancelButtonCenterConstraint.constant = 0
if !self.isCallStarted {
self.isCallStarted = true
if self.viewModel?.isAudioOnly ?? false {
muteVideoButton.isHidden = true
switchCameraButton.isHidden = true
if self.viewModel?.isSipCall ?? false {
dialpadButton.isHidden = false
}
cancelButtonBottomConstraint.constant = 20
} else {
switchCameraButton.isHidden = false
muteVideoButton.isHidden = false
cancelButtonBottomConstraint.constant = 120
if self.viewModel?.isAudioOnly ?? false {
muteVideoButton.isHidden = true
switchCameraButton.isHidden = true
if self.viewModel?.isSipCall ?? false {
dialpadButton.isHidden = false
}
switchSpeakerButton.isEnabled = false
self.muteAudioButton.isHidden = false
switchSpeakerButton.isHidden = false
self.backgroundBlurEffect.isHidden = false
pauseCallButton.isHidden = false
cancelButton.isHidden = false
cancelButtonBottomConstraint.constant = 20
} else {
switchCameraButton.isHidden = false
muteVideoButton.isHidden = false
cancelButtonBottomConstraint.constant = 80
}
switchSpeakerButton.isEnabled = false
self.muteAudioButton.isHidden = false
switchSpeakerButton.isHidden = false
self.backgroundBlurEffect.isHidden = false
pauseCallButton.isHidden = false
cancelButton.isHidden = false
addContactButton.isHidden = false
sendMessageButton.isHidden = false
setUpConference()
}
func setUpConference() {
if !(self.viewModel?.isConference ?? false) {
return
}
sendMessageButton.isHidden = true
pauseCallButton.isHidden = true
muteAudioButton.isHidden = true
muteVideoButton.isHidden = true
cancelButtonBottomConstraint.constant = 0
}
func updateView() {
if switchSpeakerButton.isEnabled && !switchSpeakerButton.isHidden {
self.optionsWithSpeaker()
} else if !switchSpeakerButton.isHidden {
self.optionsWithSpeaker()
}
}
}
This diff is collapsed.
......@@ -35,6 +35,7 @@ class ButtonsContainerViewModel {
var isAudioOnly: Bool
var isSipCall: Bool
var isIncoming: Bool
var isConference = false
let avalaibleCallOptions = BehaviorSubject<CallOptions>(value: .none)
lazy var observableCallOptions: Observable<CallOptions> = { [unowned self] in
......@@ -51,14 +52,21 @@ class ButtonsContainerViewModel {
checkCallOptions()
}
lazy var currentCall: Observable<CallModel> = {
self.callService
.currentCall(callId: self.callID)
.share().asObservable()
}()
private func checkCallOptions() {
let callIsActive: Observable<Bool> = {
self.callService.currentCall.filter({ [weak self] call in
return call.state == .current && call.callId == self?.callID
let callIsActive: Observable<Bool> = self.currentCall
.startWith(self.callService.call(callID: self.callID) ?? CallModel())
.filter({ call in
return call.state == .current
}).map({_ in
return true
})
}()
callIsActive
.subscribe(onNext: { [weak self] active in
if !active {
......
This diff is collapsed.
This diff is collapsed.
/*
* Copyright (C) 2019 Savoir-faire Linux Inc.
*
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import RxDataSources
import RxSwift
struct Contact {
var uri: String
var accountID: String
var registeredName: String
var hash: String
lazy var presenceStatus: Variable<Bool>? = {
self.presenceService
.contactPresence[self.hash]
}()
lazy var firstLine: String! = {
if let contactProfile = profile,
let profileAlias = contactProfile.alias,
!profileAlias.isEmpty {
return profileAlias
}
return registeredName.isEmpty ? hash : registeredName
}()
lazy var secondLine: String! = {
if firstLine == hash {
return ""
}
if firstLine == registeredName {
return hash
}
return registeredName.isEmpty ? hash : registeredName
}()
var profile: Profile?
var presenceService: PresenceService
init (contactUri: String, accountId: String,
registrName: String, presService: PresenceService,
contactProfile: Profile?) {
uri = contactUri
presenceService = presService
accountID = accountId
registeredName = registrName
profile = contactProfile
hash = ""
}
static func == (lhs: Contact, rhs: Contact) -> Bool {
return (lhs.uri == rhs.uri &&
lhs.accountID == rhs.accountID &&
lhs.registeredName == rhs.registeredName)
}
}
struct ConferencableItem {
var conferenceID: String
var contacts: [Contact]
}
struct ContactPickerSection {
var header: String
var items: [ConferencableItem]
}
extension ContactPickerSection: SectionModelType {
typealias Item = ConferencableItem
init(original: ContactPickerSection, items: [Item]) {
self = original
self.items = items
}
}
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="ZwP-Qn-oLY">
<device id="retina6_1" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Contact Picker View Controller-->
<scene sceneID="QZd-Vi-EyD">
<objects>
<viewController id="ZwP-Qn-oLY" customClass="ContactPickerViewController" customModule="Ring" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="TtT-WG-OAE">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="VI3-Wm-odB">
<rect key="frame" x="0.0" y="88" width="414" height="774"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="fBg-Me-pPw">
<rect key="frame" x="0.0" y="0.0" width="414" height="774"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<visualEffectView opaque="NO" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tVM-qL-OUe">
<rect key="frame" x="0.0" y="0.0" width="414" height="774"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" ambiguous="YES" insetsLayoutMarginsFromSafeArea="NO" id="hit-jZ-Pl5">
<rect key="frame" x="0.0" y="0.0" width="414" height="774"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
<vibrancyEffect>
<blurEffect style="light"/>
</vibrancyEffect>
</visualEffectView>
</subviews>
</view>
<blurEffect style="extraLight"/>
</visualEffectView>
<searchBar contentMode="redraw" translatesAutoresizingMaskIntoConstraints="NO" id="ht5-JP-L4t">
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="Ott-Go-5mf"/>
</constraints>
<textInputTraits key="textInputTraits"/>
</searchBar>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" allowsSelectionDuringEditing="YES" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="i1P-Li-H82">
<rect key="frame" x="0.0" y="88" width="414" height="808"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<connections>
<outlet property="delegate" destination="ZwP-Qn-oLY" id="EoQ-WF-bN4"/>
</connections>
</tableView>
</subviews>
<constraints>
<constraint firstItem="ht5-JP-L4t" firstAttribute="top" secondItem="HLr-8o-AJK" secondAttribute="top" id="2g9-Dm-zDQ"/>
<constraint firstItem="i1P-Li-H82" firstAttribute="trailing" secondItem="HLr-8o-AJK" secondAttribute="trailing" id="44P-xu-w8R"/>
<constraint firstItem="i1P-Li-H82" firstAttribute="top" secondItem="ht5-JP-L4t" secondAttribute="bottom" id="5Hu-hl-ywR"/>
<constraint firstItem="VI3-Wm-odB" firstAttribute="leading" secondItem="HLr-8o-AJK" secondAttribute="leading" id="GkO-uY-mmD"/>
<constraint firstItem="VI3-Wm-odB" firstAttribute="top" secondItem="ht5-JP-L4t" secondAttribute="bottom" id="MYo-pX-Fn0"/>
<constraint firstItem="HLr-8o-AJK" firstAttribute="trailing" secondItem="VI3-Wm-odB" secondAttribute="trailing" id="aO0-4s-CoA"/>
<constraint firstItem="ht5-JP-L4t" firstAttribute="leading" secondItem="HLr-8o-AJK" secondAttribute="leading" id="cBL-Q8-eUL"/>
<constraint firstItem="HLr-8o-AJK" firstAttribute="bottom" secondItem="VI3-Wm-odB" secondAttribute="bottom" id="l3e-iB-s2n"/>
<constraint firstItem="i1P-Li-H82" firstAttribute="leading" secondItem="HLr-8o-AJK" secondAttribute="leading" id="mk9-0g-iwg"/>
<constraint firstAttribute="bottom" secondItem="i1P-Li-H82" secondAttribute="bottom" id="pIJ-1T-FIT"/>
<constraint firstItem="ht5-JP-L4t" firstAttribute="trailing" secondItem="HLr-8o-AJK" secondAttribute="trailing" id="pz4-KI-DV9"/>
</constraints>
<viewLayoutGuide key="safeArea" id="HLr-8o-AJK"/>
</view>
<connections>
<outlet property="searchBar" destination="ht5-JP-L4t" id="adL-r1-B3M"/>
<outlet property="tableView" destination="i1P-Li-H82" id="Hbd-c3-3WV"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="97S-uj-psK" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-404.34782608695656" y="142.63392857142856"/>
</scene>
</scenes>
</document>
/*
* Copyright (C) 2019 Savoir-faire Linux Inc.
*
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import UIKit
import RxSwift
import RxDataSources
import RxCocoa
import Reusable
import SwiftyBeaver
class ContactPickerViewController: UIViewController, StoryboardBased, ViewModelBased, UITableViewDelegate, UIGestureRecognizerDelegate {
private let log = SwiftyBeaver.self
@IBOutlet weak var searchBar: UISearchBar!
@IBOutlet weak var tableView: UITableView!
var viewModel: ContactPickerViewModel!
fileprivate let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
self.setupDataSources()
self.setupTableViews()
self.setupSearchBar()
let dismissGR = UISwipeGestureRecognizer(target: self, action: #selector(remove(gesture:)))
dismissGR.direction = UISwipeGestureRecognizer.Direction.down
dismissGR.delegate = self
self.searchBar.addGestureRecognizer(dismissGR)
}
@objc func remove(gesture: UISwipeGestureRecognizer) {
if gesture.direction != UISwipeGestureRecognizer.Direction.down { return }
self.removeView()
}
func removeView() {
let initialFrame = CGRect(x: 0, y: self.view.frame.size.height * 2, width: self.view.frame.size.width, height: self.view.frame.size.height * 0.7)
UIView.animate(withDuration: 0.2, animations: { [unowned self] in
self.view.frame = initialFrame
self.view.layoutIfNeeded()
}, completion: { [weak self] _ in
if let parent = self?.parent as? CallViewController {
parent.addTapGesture()
self?.didMove(toParent: nil)
}
self?.view.removeFromSuperview()
self?.removeFromParent()
})
}
func setupDataSources() {
let configureCell: (TableViewSectionedDataSource, UITableView, IndexPath, ContactPickerSection.Item)
-> UITableViewCell = {
( dataSource: TableViewSectionedDataSource<ContactPickerSection>,
tableView: UITableView,
indexPath: IndexPath,
contactItem: ContactPickerSection.Item) in
let cell = tableView.dequeueReusableCell(for: indexPath, cellType: ConversationCell.self)
if contactItem.contacts.count < 1 {
return cell
}
cell.newMessagesIndicator.isHidden = true
cell.newMessagesLabel.isHidden = true
cell.lastMessageDateLabel.isHidden = true
cell.presenceIndicator.isHidden = true
if contactItem.contacts.count > 1 {
cell.avatarView.isHidden = true
var name = ""
contactItem.contacts.forEach { contact in
var mutableContact = contact
name += mutableContact.firstLine
if contactItem.contacts.last! == contact {
return
}
name += " ,"
}
cell.nameLabel.text = name
return cell
}
var contact = contactItem.contacts.first!
cell.nameLabel.text = contact.firstLine
cell.lastMessagePreviewLabel.text = contact.secondLine
var imageData: Data?
if let contactProfile = contact.profile, let photo = contactProfile.photo,
let data = NSData(base64Encoded: photo, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) as Data? {
imageData = data
}
cell.avatarView
.addSubview(
AvatarView(profileImageData: imageData,
username: contact.firstLine, size: 40))
guard let status = contact.presenceStatus else {
return cell
}
status
.asObservable()
.observeOn(MainScheduler.instance)
.startWith(status.value)
.subscribe(onNext: { precence in
cell.presenceIndicator.isHidden = !precence
})
.disposed(by: cell.disposeBag)
return cell
}
let contactDataSource = RxTableViewSectionedReloadDataSource<ContactPickerSection>(configureCell: configureCell)
self.viewModel.searchResultItems
.bind(to: self.tableView.rx.items(dataSource: contactDataSource))
.disposed(by: disposeBag)
contactDataSource.titleForHeaderInSection = { dataSource, index in
return dataSource.sectionModels[index].header
}
}
func setupTableViews() {
self.tableView.rowHeight = 64.0
self.tableView.delegate = self
self.tableView.register(cellType: ConversationCell.self)
self.tableView.rx.itemSelected.subscribe(onNext: { [unowned self] indexPath in
if let contactToAdd: ConferencableItem = try? self.tableView.rx.model(at: indexPath) {
self.viewModel.addContactToConference(contact: contactToAdd)
self.removeView()
}
}).disposed(by: disposeBag)
}
func setupSearchBar() {
self.searchBar.returnKeyType = .done
self.searchBar.autocapitalizationType = .none
self.searchBar.tintColor = UIColor.jamiMain
self.searchBar.barTintColor = UIColor.jamiNavigationBar
self.searchBar.rx.text.orEmpty
.throttle(0.5, scheduler: MainScheduler.instance)
.distinctUntilChanged()
.bind(to: self.viewModel.search)
.disposed(by: disposeBag)
self.searchBar.rx.searchButtonClicked.subscribe(onNext: { [unowned self] in
self.searchBar.resignFirstResponder()
}).disposed(by: disposeBag)
}
}
This diff is collapsed.
/*
* Copyright (C) 2019 Savoir-faire Linux Inc.
*
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import UIKit
import Reusable
import RxSwift
class ConferencePendingCallView: UIView {
@IBOutlet var containerView: UIView!
@IBOutlet var backgroundView: UIView!
@IBOutlet var nameLabel: UILabel!
@IBOutlet var cancelButton: UIButton!
let disposeBag = DisposeBag()
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.commonInit()
}
func commonInit() {
Bundle.main.loadNibNamed("ConferencePendingCallView", owner: self, options: nil)
addSubview(containerView)
containerView.frame = self.bounds
}
var viewModel: ConferencePendingCallViewModel? {
didSet {
self.viewModel?.removeView
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] remove in
if remove {
self?.removeFromSuperview()
}
}).disposed(by: self.disposeBag)
self.cancelButton.rx.tap
.subscribe(onNext: { [weak self] in
self?.viewModel?.cancelCall()
self?.removeFromSuperview()
}).disposed(by: self.disposeBag)
self.viewModel?.displayName.drive(self.nameLabel.rx.text)
.disposed(by: self.disposeBag)
UIView.animate(withDuration: 1,
delay: 0.0,