conference: add moderator actions

Change-Id: Id333299348db83f8de28ba1d34412d0286a885a2
diff --git a/Ring/Ring/Bridging/CallsAdapter.h b/Ring/Ring/Bridging/CallsAdapter.h
index 9e77e3a..0db4575 100644
--- a/Ring/Ring/Bridging/CallsAdapter.h
+++ b/Ring/Ring/Bridging/CallsAdapter.h
@@ -49,4 +49,7 @@
 - (void)setActiveParticipant:(NSString*)callId forConference:(NSString*)conferenceId;
 - (void)setConferenceLayout:(int)layout forConference:(NSString*)conferenceId;
 - (NSArray*)getConferenceInfo:(NSString*)conferenceId;
+- (void)setConferenceModerator:(NSString*)participantId forConference:(NSString*)conferenceId active:(BOOL)isActive;
+- (void)muteConferenceParticipant:(NSString*)participantId forConference:(NSString*)conferenceId active:(BOOL)isActive;
+- (void)hangupConferenceParticipant:(NSString*)participantId forConference:(NSString*)conferenceId;
 @end
diff --git a/Ring/Ring/Bridging/CallsAdapter.mm b/Ring/Ring/Bridging/CallsAdapter.mm
index 17fbf5c..a6754e7 100644
--- a/Ring/Ring/Bridging/CallsAdapter.mm
+++ b/Ring/Ring/Bridging/CallsAdapter.mm
@@ -221,6 +221,18 @@
     setConferenceLayout(std::string([conferenceId UTF8String]), layout);
 }
 
+- (void)setConferenceModerator:(NSString*)participantId forConference:(NSString*)conferenceId active:(BOOL)isActive {
+    setModerator(std::string([conferenceId UTF8String]), std::string([participantId UTF8String]), isActive);
+}
+
+- (void)muteConferenceParticipant:(NSString*)participantId forConference:(NSString*)conferenceId active:(BOOL)isActive {
+    muteParticipant(std::string([conferenceId UTF8String]), std::string([participantId UTF8String]), isActive);
+}
+
+- (void)hangupConferenceParticipant:(NSString*)participantId forConference:(NSString*)conferenceId {
+    hangupParticipant(std::string([conferenceId UTF8String]), std::string([participantId UTF8String]));
+}
+
 - (NSArray*)getConferenceInfo:(NSString*)conferenceId {
     auto result = getConferenceInfos(std::string([conferenceId UTF8String]));
     NSArray* arrayResult = [Utils vectorOfMapsToArray:result];
diff --git a/Ring/Ring/Calls/CallViewController.storyboard b/Ring/Ring/Calls/CallViewController.storyboard
index cc7f026..7106245 100644
--- a/Ring/Ring/Calls/CallViewController.storyboard
+++ b/Ring/Ring/Calls/CallViewController.storyboard
@@ -75,13 +75,13 @@
                                             <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="z3c-S7-uGw">
                                                 <rect key="frame" x="0.0" y="0.0" width="375" height="110"/>
                                                 <subviews>
-                                                    <stackView opaque="NO" contentMode="scaleToFill" distribution="fillProportionally" alignment="center" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="MdN-dF-4x3">
-                                                        <rect key="frame" x="20" y="0.0" width="139.66666666666666" height="30"/>
+                                                    <stackView opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" distribution="equalSpacing" alignment="center" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="MdN-dF-4x3">
+                                                        <rect key="frame" x="20" y="0.0" width="30" height="30"/>
                                                         <subviews>
                                                             <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="GF7-hD-E63">
                                                                 <rect key="frame" x="0.0" y="0.0" width="30" height="30"/>
                                                                 <constraints>
-                                                                    <constraint firstAttribute="width" constant="30" id="rKY-t8-TpZ"/>
+                                                                    <constraint firstAttribute="width" constant="30" id="hq4-4B-hxi"/>
                                                                     <constraint firstAttribute="height" constant="30" id="uyC-ZH-E3S"/>
                                                                 </constraints>
                                                                 <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -99,17 +99,17 @@
                                                                     </userDefinedRuntimeAttribute>
                                                                 </userDefinedRuntimeAttributes>
                                                             </button>
-                                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cgd-Wa-clf">
-                                                                <rect key="frame" x="50.000000000000007" y="0.0" width="89.666666666666686" height="30"/>
-                                                                <fontDescription key="fontDescription" type="system" pointSize="23"/>
-                                                                <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-                                                                <nil key="highlightedColor"/>
-                                                            </label>
                                                         </subviews>
                                                         <constraints>
                                                             <constraint firstAttribute="height" constant="30" id="7GR-7Z-Jln"/>
                                                         </constraints>
                                                     </stackView>
+                                                    <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cgd-Wa-clf">
+                                                        <rect key="frame" x="70" y="15" width="0.0" height="0.0"/>
+                                                        <fontDescription key="fontDescription" type="system" pointSize="23"/>
+                                                        <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                                        <nil key="highlightedColor"/>
+                                                    </label>
                                                     <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="00:00" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fac-lR-4on">
                                                         <rect key="frame" x="19.999999999999996" y="50" width="52.666666666666657" height="24"/>
                                                         <fontDescription key="fontDescription" type="system" weight="light" pointSize="20"/>
@@ -121,7 +121,9 @@
                                                 <constraints>
                                                     <constraint firstItem="fac-lR-4on" firstAttribute="top" secondItem="MdN-dF-4x3" secondAttribute="bottom" constant="20" id="1ja-6Q-FJk"/>
                                                     <constraint firstItem="MdN-dF-4x3" firstAttribute="top" secondItem="z3c-S7-uGw" secondAttribute="top" id="TVu-Mv-p0D"/>
+                                                    <constraint firstItem="cgd-Wa-clf" firstAttribute="leading" secondItem="MdN-dF-4x3" secondAttribute="trailing" constant="20" id="VIE-lK-GeA"/>
                                                     <constraint firstItem="fac-lR-4on" firstAttribute="leading" secondItem="MdN-dF-4x3" secondAttribute="leading" id="cJ6-kf-a3T"/>
+                                                    <constraint firstItem="cgd-Wa-clf" firstAttribute="centerY" secondItem="MdN-dF-4x3" secondAttribute="centerY" id="unp-V8-eN9"/>
                                                 </constraints>
                                             </view>
                                         </subviews>
@@ -198,6 +200,7 @@
                                     <constraint firstItem="K0W-KI-Ul4" firstAttribute="centerY" secondItem="DMu-Or-dd7" secondAttribute="centerY" id="cTk-Hv-nxM"/>
                                     <constraint firstItem="Zmp-OX-Cez" firstAttribute="centerX" secondItem="DMu-Or-dd7" secondAttribute="centerX" id="f1S-rO-Hld"/>
                                     <constraint firstItem="K0W-KI-Ul4" firstAttribute="height" secondItem="DMu-Or-dd7" secondAttribute="height" id="grY-ie-rVw"/>
+                                    <constraint firstItem="CfE-DF-buX" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="cgd-Wa-clf" secondAttribute="trailing" priority="750" constant="7" id="hmN-pb-JlG"/>
                                     <constraint firstItem="K0W-KI-Ul4" firstAttribute="centerX" secondItem="DMu-Or-dd7" secondAttribute="centerX" id="oCc-Yp-7Ay"/>
                                     <constraint firstItem="ZK1-Be-lcD" firstAttribute="width" secondItem="ZVy-nB-bKJ" secondAttribute="width" id="uTd-rs-MJH"/>
                                     <constraint firstAttribute="trailing" secondItem="ZK1-Be-lcD" secondAttribute="trailing" id="ugJ-SF-Enn"/>
@@ -206,10 +209,10 @@
                                 </constraints>
                             </view>
                             <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" placeholderIntrinsicWidth="375" placeholderIntrinsicHeight="128" alwaysBounceHorizontal="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bij-Xb-EKH">
-                                <rect key="frame" x="0.0" y="856" width="375" height="300"/>
+                                <rect key="frame" x="0.0" y="806" width="375" height="360"/>
                                 <subviews>
                                     <stackView opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" distribution="equalSpacing" alignment="top" spacing="40" translatesAutoresizingMaskIntoConstraints="NO" id="7G0-Fp-Xc3">
-                                        <rect key="frame" x="10" y="10" width="355" height="260"/>
+                                        <rect key="frame" x="10" y="10" width="355" height="320"/>
                                     </stackView>
                                 </subviews>
                                 <constraints>
@@ -219,7 +222,7 @@
                                     <constraint firstItem="7G0-Fp-Xc3" firstAttribute="centerX" secondItem="bij-Xb-EKH" secondAttribute="centerX" placeholder="YES" id="JZj-B5-YW6"/>
                                     <constraint firstItem="7G0-Fp-Xc3" firstAttribute="leading" secondItem="bij-Xb-EKH" secondAttribute="leading" constant="10" id="QC5-Mh-VcC"/>
                                     <constraint firstAttribute="bottom" secondItem="7G0-Fp-Xc3" secondAttribute="bottom" constant="30" id="lUV-jq-9bZ"/>
-                                    <constraint firstAttribute="height" constant="300" id="q8G-dB-syM"/>
+                                    <constraint firstAttribute="height" constant="360" id="q8G-dB-syM"/>
                                     <constraint firstItem="7G0-Fp-Xc3" firstAttribute="height" secondItem="bij-Xb-EKH" secondAttribute="height" constant="-40" id="xAL-yn-Zen"/>
                                 </constraints>
                             </scrollView>
@@ -334,7 +337,7 @@
                             <constraint firstItem="ZVy-nB-bKJ" firstAttribute="centerY" secondItem="QpJ-Sx-9dG" secondAttribute="centerY" id="bAN-gX-nPE"/>
                             <constraint firstAttribute="bottom" secondItem="LK6-u0-eLU" secondAttribute="bottom" id="dXj-zI-fQb"/>
                             <constraint firstItem="ZVy-nB-bKJ" firstAttribute="centerX" secondItem="lZI-X0-bkP" secondAttribute="centerX" id="ff0-Nw-f2Y"/>
-                            <constraint firstItem="bij-Xb-EKH" firstAttribute="top" secondItem="ZK1-Be-lcD" secondAttribute="bottom" id="pgr-29-Lef"/>
+                            <constraint firstItem="bij-Xb-EKH" firstAttribute="top" secondItem="ZK1-Be-lcD" secondAttribute="bottom" constant="-50" id="pgr-29-Lef"/>
                             <constraint firstItem="5E0-lB-SkS" firstAttribute="width" secondItem="QpJ-Sx-9dG" secondAttribute="width" id="rOQ-In-yON"/>
                             <constraint firstItem="ZVy-nB-bKJ" firstAttribute="width" secondItem="QpJ-Sx-9dG" secondAttribute="width" id="sCh-Gw-iu0"/>
                             <constraint firstItem="MdN-dF-4x3" firstAttribute="leading" secondItem="lZI-X0-bkP" secondAttribute="leading" constant="20" id="tsz-q2-iDb"/>
diff --git a/Ring/Ring/Calls/CallViewController.swift b/Ring/Ring/Calls/CallViewController.swift
index e611288..13d8e01 100644
--- a/Ring/Ring/Calls/CallViewController.swift
+++ b/Ring/Ring/Calls/CallViewController.swift
@@ -156,6 +156,27 @@
                 self.updateconferenceLayoutSize()
                 let participants = self.viewModel.getConferenceParticipants()
                 self.conferenceLayout.setParticipants(participants: participants)
+                guard let unwrapParticipants = participants, self.viewModel.isCurrentModerator(), !self.viewModel.isHostCall else { return }
+                self.conferenceCalls.arrangedSubviews.forEach({ (view) in
+                    view.removeFromSuperview()
+                })
+                for participant in unwrapParticipants {
+                    let callView =
+                        ConferenceParticipantView(frame:
+                            CGRect(x: 0, y: 0,
+                                   width: inConfViewWidth, height: inConfViewHeight))
+                    let injectionBag = self.viewModel.injectionBag
+                    let isLocal = self.viewModel.isLocalCall(participantId: participant.uri ?? "")
+                    let pendingCallViewModel =
+                        ConferenceParticipantViewModel(with: nil,
+                                                       injectionBag: injectionBag,
+                                                       isLocal: isLocal,
+                                                       participantId: participant.uri ?? "",
+                                                       participantUserName: participant.displayName)
+                    callView.viewModel = pendingCallViewModel
+                    callView.delegate = self
+                    self.conferenceCalls.addArrangedSubview(callView)
+                }
             })
             .disposed(by: self.disposeBag)
     }
@@ -447,17 +468,20 @@
             .asObservable()
             .observeOn(MainScheduler.instance)
             .subscribe(onNext: { [weak self] enteredConference in
-                guard let call = self?.viewModel.call else { return }
+                guard let call = self?.viewModel.call,
+                      let self = self else { return }
                 if call.state != .current { return }
-                self?.updateconferenceLayoutSize()
-                self?.buttonsContainer.updateView()
-                self?.infoContainer.isHidden = enteredConference ? true : false
-                self?.resizeCapturedVideo(withInfoContainer: false)
+                self.updateconferenceLayoutSize()
+                self.buttonsContainer.updateView()
+                self.infoContainer.isHidden = enteredConference ? true : false
+                self.conferenceCallsTop.constant = enteredConference ? 0 : -50
+                self.resizeCapturedVideo(withInfoContainer: false)
+                // for moderator participants will be added in layoutUpdated
+                if self.viewModel.isCurrentModerator() { return }
                 // if entered conference add first participant to conference list
                 if enteredConference {
-                    self?.removeConferenceParticipantMenu()
-                    guard let injectionBag = self?.viewModel.injectionBag
-                    else { return }
+                    self.removeConferenceParticipantMenu()
+                    let injectionBag = self.viewModel.injectionBag
                     // add self as a master call
                     let mainCallView =
                         ConferenceParticipantView(frame: CGRect(x: 0,
@@ -466,24 +490,31 @@
                                                                 height: inConfViewHeight))
                     let mainCallViewModel =
                         ConferenceParticipantViewModel(with: nil,
-                                                       injectionBag: injectionBag)
+                                                       injectionBag: injectionBag,
+                                                       isLocal: true,
+                                                       participantId: "",
+                                                       participantUserName: "")
                     mainCallView.viewModel = mainCallViewModel
                     mainCallView.delegate = self
-                    self?.conferenceCalls.insertArrangedSubview(mainCallView, at: 0)
+                    self.conferenceCalls.insertArrangedSubview(mainCallView, at: 0)
                     let callView =
                         ConferenceParticipantView(frame: CGRect(x: 0,
                                                                 y: 0,
                                                                 width: inConfViewWidth,
                                                                 height: inConfViewHeight))
+                    let name = call.displayName.isEmpty ? call.registeredName.isEmpty ? call.participantUri.filterOutHost() : call.registeredName : call.displayName
                     let pendingCallViewModel =
-                        ConferenceParticipantViewModel(with: call,
-                                                       injectionBag: injectionBag)
+                        ConferenceParticipantViewModel(with: call.callId,
+                                                       injectionBag: injectionBag,
+                                                       isLocal: false,
+                                                       participantId: call.paricipantHash(),
+                                                       participantUserName: name)
                     callView.viewModel = pendingCallViewModel
                     callView.delegate = self
-                    self?.conferenceCalls.insertArrangedSubview(callView, at: 1)
+                    self.conferenceCalls.insertArrangedSubview(callView, at: 1)
                 } else {
-                    self?.removeConferenceParticipantMenu()
-                    self?.conferenceCalls.arrangedSubviews.forEach({ (view) in
+                    self.removeConferenceParticipantMenu()
+                    self.conferenceCalls.arrangedSubviews.forEach({ (view) in
                         view.removeFromSuperview()
                     })
                 }
@@ -493,17 +524,24 @@
         self.viewModel.callForConference
             .observeOn(MainScheduler.instance)
             .subscribe(onNext: { [weak self] call in
+                guard let self = self else { return }
+                // for moderator participants will be added in layoutUpdated
+                if self.viewModel.isCurrentModerator() { return }
                 let callView =
                     ConferenceParticipantView(frame:
                         CGRect(x: 0, y: 0,
                                width: inConfViewWidth, height: inConfViewHeight))
-                guard let injectionBag = self?.viewModel.injectionBag else { return }
+                let injectionBag = self.viewModel.injectionBag
+                let name = call.displayName.isEmpty ? call.registeredName.isEmpty ? call.participantUri.filterOutHost() : call.registeredName : call.displayName
                 let pendingCallViewModel =
-                    ConferenceParticipantViewModel(with: call,
-                                                   injectionBag: injectionBag)
+                    ConferenceParticipantViewModel(with: call.callId,
+                                                   injectionBag: injectionBag,
+                                                   isLocal: false,
+                                                   participantId: call.paricipantHash(),
+                                                   participantUserName: name)
                 callView.viewModel = pendingCallViewModel
                 callView.delegate = self
-                self?.conferenceCalls.addArrangedSubview(callView)
+                self.conferenceCalls.addArrangedSubview(callView)
             })
             .disposed(by: self.disposeBag)
 
@@ -729,7 +767,8 @@
 
         UIView.animate(withDuration: 0.2, animations: { [weak self] in
             self?.infoContainerTopConstraint.constant = -10
-            self?.conferenceCallsTop.constant = 0
+            let isConference: Bool = self?.viewModel.conferenceMode.value ?? true
+            self?.conferenceCallsTop.constant = isConference ? 0 : -50
             if UIDevice.current.hasNotch && (self?.orientation == .landscapeRight || self?.orientation == .landscapeLeft) {
                 self?.buttonsContainerBottomConstraint.constant = 1
             } else if UIDevice.current.userInterfaceIdiom == .pad {
@@ -786,26 +825,61 @@
 }
 
 extension CallViewController: ConferenceParticipantViewDelegate {
-    func addConferenceParticipantMenu(origin: CGPoint, displayName: String, callId: String?, hangup: @escaping (() -> Void)) {
+    func addConferenceParticipantMenu(origin: CGPoint, displayName: String, participantId: String, callId: String?, hangup: @escaping (() -> Void)) {
         // remove menu if it is already present
         if self.conferenceParticipantMenu?.frame.origin == origin {
             self.removeConferenceParticipantMenu()
             return
         }
         let menuView = ConferenceActionMenu(frame: CGRect(origin: origin, size: CGSize(width: self.view.frame.size.width, height: self.view.frame.size.height)))
-        menuView.configureWith(mode: self.viewModel.getItemsForConferenceMenu(participantCallId: callId), displayName: displayName)
+        var muteEnabled = false
+        var muteText = ""
+        var moderatorText = ""
+        var isModerator = false
+        var isAudioMuted = false
+        var pending = true
+
+        if let participant = self.viewModel.getConferencePartisipant(participantId: participantId) {
+            muteEnabled = !participant.isAudioLocalyMuted
+            muteText = participant.isAudioMuted ? L10n.Calls.unmuteAudio : L10n.Calls.muteAudio
+            moderatorText = participant.isModerator ? L10n.Calls.removeModerator : L10n.Calls.setModerator
+            isModerator = participant.isModerator
+            isAudioMuted = participant.isAudioMuted
+            pending = false
+        }
+
+        menuView.configureWith(items: self.viewModel.getItemsForConferenceMenu(participantId: participantId, callId: callId ?? ""),
+                               displayName: displayName,
+                               muteText: muteText,
+                               moderatorText: moderatorText,
+                               muteEnabled: muteEnabled)
         menuView.addHangUpAction { [weak self] in
-            hangup()
+            if pending {
+                hangup()
+            } else {
+                self?.viewModel.hangupParticipant(participantId: participantId)
+            }
             self?.removeConferenceParticipantMenu()
         }
         menuView.addMaximizeAction { [weak self] in
             self?.removeConferenceParticipantMenu()
-            self?.viewModel.setActiveParticipant(callId: callId, maximize: true)
+            self?.viewModel.setActiveParticipant(jamiId: participantId, maximize: true)
         }
         menuView.addMinimizeAction { [weak self] in
             self?.removeConferenceParticipantMenu()
-            self?.viewModel.setActiveParticipant(callId: callId, maximize: false)
+            self?.viewModel.setActiveParticipant(jamiId: participantId, maximize: false)
         }
+
+        menuView.addSetModeratorAction { [weak self] in
+            self?.removeConferenceParticipantMenu()
+            self?.viewModel.setModeratorParticipant(participantId: participantId, active: !isModerator)
+        }
+
+        menuView.addMuteAction { [weak self] in
+            self?.removeConferenceParticipantMenu()
+            self?.viewModel.muteParticipant(participantId: participantId, active: !isAudioMuted)
+        }
+
         let point = conferenceCallsScrolView.convert(menuView.frame.origin, to: self.view)
         let offset = self.view.frame.size.width - point.x - menuView.frame.size.width
         if offset < 0 {
diff --git a/Ring/Ring/Calls/CallViewModel.swift b/Ring/Ring/Calls/CallViewModel.swift
index d5ef37d..151a3fa 100644
--- a/Ring/Ring/Calls/CallViewModel.swift
+++ b/Ring/Ring/Calls/CallViewModel.swift
@@ -48,6 +48,7 @@
 
     var isHeadsetConnected = false
     var isAudioOnly = false
+    var isHostCall = false
 
     private lazy var currentCallVariable: BehaviorRelay<CallModel> = {
         BehaviorRelay<CallModel>(value: self.call ?? CallModel())
@@ -86,23 +87,36 @@
             self.callService.currentConferenceEvent
                 .asObservable()
                 .filter({ [weak self] conference-> Bool in
-                    return conference.calls.contains(self?.call?.callId ?? "") ||
-                        conference.conferenceID == self?.rendererId
+                    guard let self = self else { return false }
+                    return conference.calls.contains(self.call?.callId ?? "") ||
+                        conference.conferenceID == self.rendererId
                 })
                 .subscribe(onNext: { [weak self] conf in
+                    guard let self = self else { return }
                     if conf.conferenceID.isEmpty {
                         return
                     }
                     if conf.state == ConferenceState.infoUpdated.rawValue {
-                        self?.layoutUpdated.accept(true)
+                        self.layoutUpdated.accept(true)
+                        guard let account = self.accountService.currentAccount, !self.isHostCall else {
+                            return
+                        }
+                        let isModerator = self.callService.isModerator(participantId: account.jamiId, inConference: conf.conferenceID)
+                        if isModerator != self.containerViewModel?.isConference {
+//                            guard let updatedCall = self.callService.call(callID: call.callId) else { return }
+//                            self.call = updatedCall
+                            self.containerViewModel?.isConference = isModerator
+                            self.conferenceMode.accept(isModerator)
+                        }
                         return
                     }
-                    guard let updatedCall = self?.callService.call(callID: call.callId) else { return }
-                    self?.call = updatedCall
+                    guard let updatedCall = self.callService.call(callID: call.callId) else { return }
+                    self.call = updatedCall
                     let conferenceCreated = conf.state == ConferenceState.conferenceCreated.rawValue
-                    self?.rendererId = conferenceCreated ? conf.conferenceID : self!.call!.callId
-                    self?.containerViewModel?.isConference = conferenceCreated
-                    self?.conferenceMode.accept(conferenceCreated)
+                    self.rendererId = conferenceCreated ? conf.conferenceID : self.call!.callId
+                    self.isHostCall = conferenceCreated
+                    self.containerViewModel?.isConference = conferenceCreated
+                    self.conferenceMode.accept(conferenceCreated)
                 })
                 .disposed(by: self.disposeBag)
             self.rendererId = call.callId
@@ -627,15 +641,26 @@
 }
 // MARK: conference layout
 extension CallViewModel {
-    func setActiveParticipant(callId: String?, maximize: Bool) {
-        guard  let jamiId = self.accountService.currentAccount?.jamiId else { return }
-        self.callService.setActiveParticipant(callId: callId, conferenceId: self.rendererId, maximixe: maximize, jamiId: jamiId)
+    func setActiveParticipant(jamiId: String, maximize: Bool) {
+        self.callService.setActiveParticipant(conferenceId: self.rendererId, maximixe: maximize, jamiId: jamiId.filterOutHost())
     }
 
     func getConferenceVideoSize() -> CGSize {
         return self.videoService.getConferenceVideoSize(confId: self.rendererId)
     }
 
+    func muteParticipant(participantId: String, active: Bool) {
+        self.callService.muteParticipant(confId: self.rendererId, participantId: participantId.filterOutHost(), active: active)
+    }
+
+    func setModeratorParticipant(participantId: String, active: Bool) {
+        self.callService.setModeratorParticipant(confId: self.rendererId, participantId: participantId.filterOutHost(), active: active)
+    }
+
+    func hangupParticipant(participantId: String) {
+        self.callService.hangupParticipant(confId: self.rendererId, participantId: participantId.filterOutHost())
+    }
+
     func getConferenceParticipants() -> [ConferenceParticipant]? {
         guard let account = self.accountService.currentAccount,
             let participants = self.callService.getConferenceParticipants(for: self.rendererId),
@@ -645,7 +670,7 @@
             // master call
             if uri.isEmpty {
                 //check if master call is local or remote
-                if !self.conferenceMode.value {
+                if !self.isHostCall {
                     participant.displayName = call.getDisplayName()
                 } else {
                     participant.displayName = L10n.Account.me
@@ -673,15 +698,47 @@
         return participants
     }
 
-    func getItemsForConferenceMenu(participantCallId: String?) -> MenuMode {
-        let conference = self.callService.call(callID: self.rendererId)
-        // menu for master call
-        guard let callId = participantCallId else {
-            let active = self.callService.isParticipant(participantURI: "", activeIn: self.rendererId)
-            return menuItemsManager.getMenuItemsForMasterCall(conference: conference, active: active)
+    func getConferencePartisipant(participantId: String) -> ConferenceParticipant? {
+        guard let participants = self.getConferenceParticipants() else { return nil }
+        return participants.filter { participant in
+            return participant.uri?.filterOutHost() == participantId.filterOutHost()
+        }.first
+    }
+
+    func isLocalCall(participantId: String) -> Bool {
+        guard let account = self.accountService.currentAccount else { return false }
+        return account.jamiId == participantId.filterOutHost()
+    }
+
+    func isHostCall(participantId: String) -> Bool {
+        guard let account = self.accountService.currentAccount else { return false }
+        if self.isHostCall {
+            return account.jamiId == participantId.filterOutHost()
         }
-        let call = self.callService.call(callID: callId)
-        let active = self.callService.isParticipant(participantURI: call?.participantUri, activeIn: self.rendererId)
-        return menuItemsManager.getMenuItemsFor(call: call, conference: conference, active: active)
+        return call?.participantUri.filterOutHost() == participantId.filterOutHost()
+    }
+
+    func isCurrentModerator() -> Bool {
+        guard let account = self.accountService.currentAccount else { return false }
+        return self.callService.isModerator(participantId: account.jamiId, inConference: self.rendererId)
+    }
+
+    func getItemsForConferenceMenu(participantId: String, callId: String) -> [MenuItem] {
+        let conference = self.callService.call(callID: self.rendererId)
+        let active = self.callService.isParticipant(participantURI: participantId, activeIn: self.rendererId)
+        // menu for local call
+        if self.isLocalCall(participantId: participantId) || participantId.isEmpty {
+            return menuItemsManager.getMenuItemsForLocalCall(conference: conference, active: active)
+        }
+        let isModerator = self.isCurrentModerator()
+        var role = RoleInCall.regular
+        let callIsHost = self.isHostCall(participantId: participantId)
+        if self.isHostCall {
+            role = RoleInCall.host
+        } else if isModerator {
+            role = RoleInCall.moderator
+        }
+        let participantCall = isModerator ? call : self.callService.call(callID: callId)
+        return menuItemsManager.getMenuItemsFor(call: participantCall, isHost: callIsHost, conference: conference, active: active, role: role)
     }
 }
diff --git a/Ring/Ring/Calls/Conference/ConferenceMenuItemsManager.swift b/Ring/Ring/Calls/Conference/ConferenceMenuItemsManager.swift
index 9aabd29..3d6dd63 100644
--- a/Ring/Ring/Calls/Conference/ConferenceMenuItemsManager.swift
+++ b/Ring/Ring/Calls/Conference/ConferenceMenuItemsManager.swift
@@ -17,41 +17,110 @@
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
 */
-
+enum RoleInCall {
+    case moderator
+    case host
+    case regular
+}
 class ConferenceMenuItemsManager {
-    func getMenuItemsForMasterCall(conference: CallModel?, active: Bool?) -> MenuMode {
+    func getMenuItemsForLocalCall(conference: CallModel?, active: Bool?) -> [MenuItem] {
+        var menu = [MenuItem]()
+        menu.append(.name)
         guard let conference = conference else {
-            return MenuMode.onlyName
+            return menu
         }
         guard let active = active else {
-            return MenuMode.onlyName
+            return menu
         }
         switch conference.layout {
         case .grid:
-            return MenuMode.withoutHangUPAndMinimize
+            menu.append(.maximize)
         case .oneWithSmal:
-            return active ? MenuMode.withoutHangUp : MenuMode.withoutHangUPAndMinimize
+            menu.append(.maximize)
+            if active {
+                menu.append(.minimize)
+            }
         case .one:
-            return active ? MenuMode.withoutHangUPAndMaximize : MenuMode.withoutHangUPAndMinimize
+            if active {
+                menu.append(.minimize)
+            } else {
+                menu.append(.maximize)
+            }
         }
+        menu.append(.muteAudio)
+        return menu
     }
 
-    func getMenuItemsFor(call: CallModel?, conference: CallModel?, active: Bool?) -> MenuMode {
+    func getMenuItemsFor(call: CallModel?, isHost: Bool, conference: CallModel?, active: Bool?, role: RoleInCall) -> [MenuItem] {
+        var menu = [MenuItem]()
+        menu.append(.name)
         guard let conference = conference,
             let call = call else {
-                return MenuMode.onlyName
+            return menu
         }
         if call.state != CallState.current {
-            return MenuMode.withoutMaximizeAndMinimize
+            menu.append(.hangup)
+            return menu
         }
-        guard let active = active else { return MenuMode.onlyName }
+        guard let active = active else {
+            return menu
+        }
         switch conference.layout {
         case .grid:
-            return MenuMode.withoutMinimize
+            menu.append(.maximize)
+            switch role {
+            case .host:
+                menu.append(.muteAudio)
+                menu.append(.setModerator)
+                menu.append(.hangup)
+            case .moderator:
+                menu.append(.muteAudio)
+                if !isHost {
+                    menu.append(.hangup)
+                }
+            case .regular:
+                break
+            }
         case .oneWithSmal:
-            return active ? MenuMode.all : MenuMode.withoutMinimize
+            if active {
+                menu.append(.maximize)
+                menu.append(.minimize)
+            } else {
+                menu.append(.maximize)
+            }
+            switch role {
+            case .host:
+                menu.append(.muteAudio)
+                menu.append(.setModerator)
+                menu.append(.hangup)
+            case .moderator:
+                menu.append(.muteAudio)
+                if !isHost {
+                    menu.append(.hangup)
+                }
+            case .regular:
+                break
+            }
         case .one:
-            return active ? MenuMode.withoutMaximize : MenuMode.withoutMinimize
+            if active {
+                menu.append(.minimize)
+            } else {
+                menu.append(.maximize)
+            }
+            switch role {
+            case .host:
+                menu.append(.muteAudio)
+                menu.append(.setModerator)
+                menu.append(.hangup)
+            case .moderator:
+                menu.append(.muteAudio)
+                if !isHost {
+                    menu.append(.hangup)
+                }
+            case .regular:
+                break
+            }
         }
+        return menu
     }
 }
diff --git a/Ring/Ring/Calls/views/ConferenceActionsMenu.swift b/Ring/Ring/Calls/views/ConferenceActionsMenu.swift
index 80cb87e..8839fff 100644
--- a/Ring/Ring/Calls/views/ConferenceActionsMenu.swift
+++ b/Ring/Ring/Calls/views/ConferenceActionsMenu.swift
@@ -21,54 +21,69 @@
 import UIKit
 import RxSwift
 
-enum MenuMode {
-    case withoutHangUp // for master call
-    case withoutMaximize
-    case withoutMinimize
-    case withoutMaximizeAndMinimize
-    case withoutHangUPAndMinimize
-    case withoutHangUPAndMaximize
-    case onlyName
-    case all
+enum MenuItem {
+    case name
+    case hangup
+    case minimize
+    case maximize
+    case setModerator
+    case muteAudio
 }
 
 class ConferenceActionMenu: UIView {
     private let marginY: CGFloat = 20
     private let marginX: CGFloat = 20
-    private let maxWidth: CGFloat = 120
+    private let maxWidth: CGFloat = 200
     private let menuItemWidth: CGFloat = 80
     private let menuItemHight: CGFloat = 30
     private let textSize: CGFloat = 20
     private var hangUpButton: UIButton?
     private var maximizeButton: UIButton?
     private var minimizeButton: UIButton?
+    private var setModeratorButton: UIButton?
+    private var muteAudioButton: UIButton?
     private let disposeBag = DisposeBag()
+    private var muteLabelText: String = ""
+    private var moderatorLabelText: String = ""
+    private var muteButtonEnabled: Bool = false
+    private var hasSetMute: Bool = false
 
-    func configureWith(mode: MenuMode, displayName: String) {
+    func configureWith(items: [MenuItem], displayName: String, muteText: String, moderatorText: String, muteEnabled: Bool) {
         self.addDisplayName(displayName: displayName)
-        switch mode {
-        case .withoutHangUp:
-            self.configureWithoutHangUP()
-        case .withoutMaximize:
-            self.configureWithoutMaximize()
-        case .withoutMinimize:
-            self.configureWithoutMinimize()
-        case .withoutMaximizeAndMinimize:
-            self.configureWithoutMaximizeAndMinimize()
-        case .withoutHangUPAndMinimize:
-            self.configureWithoutHangUPAndMinimize()
-        case .withoutHangUPAndMaximize:
-            self.configureWithoutHangUPAndMaximize()
-        case .all:
-            self.configureWithAllItems()
-        case .onlyName:
-            break
+        muteLabelText = muteText
+        moderatorLabelText = moderatorText
+        muteButtonEnabled = muteEnabled
+        let itemsWithoutName = items.filter { item in
+            item != .name
+        }
+        if !itemsWithoutName.isEmpty {
+        for index in 1...itemsWithoutName.count {
+            let position: CGFloat = self.marginY * CGFloat((index + 1)) + menuItemHight * CGFloat(index)
+            self.addItem(item: itemsWithoutName[index - 1], positionY: position)
+        }
         }
         self.updateWidth()
         self.updateHeight()
         self.addBackground()
     }
 
+    func addItem(item: MenuItem, positionY: CGFloat) {
+        switch item {
+        case .minimize:
+            self.addMinimizeButton(positionY: positionY)
+        case .maximize:
+            self.addMaximizeButton(positionY: positionY)
+        case .setModerator:
+            self.addSetModeratorButton(positionY: positionY)
+        case .muteAudio:
+            self.addMuteAudioButton(positionY: positionY)
+        case .hangup:
+            self.addHangUpButton(positionY: positionY)
+        case .name:
+            break
+        }
+    }
+
     func addHangUpAction(hangup: @escaping (() -> Void)) {
         guard let button = hangUpButton else { return }
         button.rx.tap
@@ -90,6 +105,20 @@
             .disposed(by: self.disposeBag)
     }
 
+    func addSetModeratorAction(setModerator: @escaping (() -> Void)) {
+        guard let button = setModeratorButton else { return }
+        button.rx.tap
+            .subscribe(onNext: { setModerator() })
+            .disposed(by: self.disposeBag)
+    }
+
+    func addMuteAction(mute: @escaping (() -> Void)) {
+        guard let button = muteAudioButton else { return }
+        button.rx.tap
+            .subscribe(onNext: { mute() })
+            .disposed(by: self.disposeBag)
+    }
+
     private func addDisplayName(displayName: String) {
         let labelName = UILabel(frame: CGRect(x: marginX, y: marginY, width: menuItemWidth, height: menuItemHight))
         labelName.font = labelName.font.withSize(self.textSize)
@@ -142,6 +171,34 @@
         self.addSubview(self.minimizeButton!)
     }
 
+    private func addSetModeratorButton(positionY: CGFloat) {
+        let setModeratotLabel = UILabel(frame: CGRect(x: marginX, y: positionY, width: menuItemWidth, height: menuItemHight))
+        setModeratotLabel.font = setModeratotLabel.font.withSize(self.textSize)
+        setModeratotLabel.text = moderatorLabelText
+        setModeratotLabel.sizeToFit()
+        setModeratotLabel.textAlignment = .center
+        self.setModeratorButton = UIButton(frame: setModeratotLabel.frame)
+        self.addSubview(setModeratotLabel)
+        self.addSubview(self.setModeratorButton!)
+    }
+
+    private func addMuteAudioButton(positionY: CGFloat) {
+        let muteAudioLabel = UILabel(frame: CGRect(x: marginX, y: positionY, width: menuItemWidth, height: menuItemHight))
+        muteAudioLabel.font = muteAudioLabel.font.withSize(self.textSize)
+        muteAudioLabel.text = muteLabelText
+        muteAudioLabel.sizeToFit()
+        muteAudioLabel.textAlignment = .center
+        if #available(iOS 13.0, *) {
+            muteAudioLabel.textColor = muteButtonEnabled ? UIColor.label : UIColor.quaternaryLabel
+        } else {
+            muteAudioLabel.textColor = muteButtonEnabled ? UIColor.white : UIColor.lightText
+        }
+        self.addSubview(muteAudioLabel)
+        if !muteButtonEnabled { return }
+        self.muteAudioButton = UIButton(frame: muteAudioLabel.frame)
+        self.addSubview(self.muteAudioButton!)
+    }
+
     private func updateHeight() {
         var numberOfLabels: CGFloat = 0
         self.subviews.forEach { (childView) in
@@ -167,49 +224,4 @@
             childView.frame.size.width = finalWidth
         }
     }
-
-    private func configureWithoutHangUP() {
-        let firstY: CGFloat = CGFloat(self.marginY * 2 + menuItemHight)
-        let secondY: CGFloat = CGFloat(self.marginY * 3 + menuItemHight * 2)
-        self.addMaximizeButton(positionY: firstY)
-        self.addMinimizeButton(positionY: secondY)
-    }
-
-    private func configureWithoutMaximize() {
-        let firstY: CGFloat = CGFloat(self.marginY * 2 + menuItemHight)
-        let secondY: CGFloat = CGFloat(self.marginY * 3 + menuItemHight * 2)
-        self.addHangUpButton(positionY: firstY)
-        self.addMinimizeButton(positionY: secondY)
-    }
-
-    private func configureWithoutMinimize() {
-        let firstY: CGFloat = CGFloat(self.marginY * 2 + menuItemHight)
-        let secondY: CGFloat = CGFloat(self.marginY * 3 + menuItemHight * 2)
-        self.addHangUpButton(positionY: firstY)
-        self.addMaximizeButton(positionY: secondY)
-    }
-
-    private func configureWithoutMaximizeAndMinimize() {
-        let firstY: CGFloat = CGFloat(self.marginY * 2 + menuItemHight)
-        self.addHangUpButton(positionY: firstY)
-    }
-
-    private func configureWithoutHangUPAndMinimize() {
-        let firstY: CGFloat = CGFloat(self.marginY * 2 + menuItemHight)
-        self.addMaximizeButton(positionY: firstY)
-    }
-
-    private func configureWithoutHangUPAndMaximize() {
-        let firstY: CGFloat = CGFloat(self.marginY * 2 + menuItemHight)
-        self.addMinimizeButton(positionY: firstY)
-    }
-
-    private func configureWithAllItems() {
-        let firstY: CGFloat = CGFloat(self.marginY * 2 + menuItemHight)
-        let secondY: CGFloat = CGFloat(self.marginY * 3 + menuItemHight * 2)
-        let thirdtY: CGFloat = CGFloat(self.marginY * 4 + menuItemHight * 3)
-        self.addHangUpButton(positionY: firstY)
-        self.addMaximizeButton(positionY: secondY)
-        self.addMinimizeButton(positionY: thirdtY)
-    }
 }
diff --git a/Ring/Ring/Calls/views/ConferenceParticipantView.swift b/Ring/Ring/Calls/views/ConferenceParticipantView.swift
index c51b8e4..c747caa 100644
--- a/Ring/Ring/Calls/views/ConferenceParticipantView.swift
+++ b/Ring/Ring/Calls/views/ConferenceParticipantView.swift
@@ -23,7 +23,7 @@
 import RxSwift
 
 protocol ConferenceParticipantViewDelegate: class {
-    func addConferenceParticipantMenu(origin: CGPoint, displayName: String, callId: String?, hangup: @escaping (() -> Void))
+    func addConferenceParticipantMenu(origin: CGPoint, displayName: String, participantId: String, callId: String?, hangup: @escaping (() -> Void))
     func removeConferenceParticipantMenu()
 }
 
@@ -63,12 +63,14 @@
     @objc
     func showMenu() {
         guard let name = self.viewModel?.getName() else { return }
+        let participantId: String = self.viewModel?.getParticipantId() ?? ""
         let callId = self.viewModel?.getCallId()
         let menu = UIView(frame: CGRect(x: 50, y: 50, width: 50, height: 50))
         let frame = self.convert(menu.frame, to: self.superview)
         self.delegate?
             .addConferenceParticipantMenu(origin: frame.origin,
                                           displayName: name,
+                                          participantId: participantId,
                                           callId: callId,
                                           hangup: {
                                             [weak self] in
diff --git a/Ring/Ring/Calls/views/ConferenceParticipantViewModel.swift b/Ring/Ring/Calls/views/ConferenceParticipantViewModel.swift
index c2a8a78..3b4abb5 100644
--- a/Ring/Ring/Calls/views/ConferenceParticipantViewModel.swift
+++ b/Ring/Ring/Calls/views/ConferenceParticipantViewModel.swift
@@ -22,18 +22,20 @@
 import RxCocoa
 
 class ConferenceParticipantViewModel {
-    private let call: CallModel? // for conference master call is nil
+    private let callId: String? // for conference master or for moderator call is nil
+    private let participantId: String
+    private let participantUserName: String
     private let callsSercive: CallsService
     private let profileService: ProfilesService
     private let accountService: AccountsService
-    private let isMasterCall: Bool
+    private let isLocal: Bool
     private let disposeBag = DisposeBag()
 
     private lazy var contactImageData: Observable<String?> = {
         guard let account = self.accountService.currentAccount else {
             return Observable.just(nil)
         }
-        guard let call = call else {
+        if isLocal {
             return self.profileService.getAccountProfile(accountId: account.id).map { profile in
                 if let alias = profile.alias, !alias.isEmpty {
                     self.displayName.accept(alias)
@@ -43,14 +45,13 @@
         }
         let type = account.type == AccountType.sip ? URIType.sip : URIType.ring
         guard let uriString = JamiURI.init(schema: type,
-                                           infoHach: call.participantUri,
+                                           infoHach: participantId,
                                            account: account).uriString else { return Observable.just(nil) }
         return profileService.getProfile(uri: uriString,
                                          createIfNotexists: false,
                                          accountId: account.id)
             .map { profile in
                 if let alias = profile.alias, !alias.isEmpty {
-                    self.call?.displayName = alias
                     self.displayName.accept(alias)
                 }
                 return profile.photo
@@ -59,8 +60,8 @@
 
     private lazy var displayName: BehaviorRelay<String> = {
         var initialName = ""
-        if let call = call {
-            initialName = call.getDisplayName()
+        if !isLocal {
+            initialName = participantUserName
         } else if let account = self.accountService.currentAccount {
             initialName = account.registeredName
         }
@@ -68,9 +69,8 @@
     }()
 
     lazy var removeView: Observable<Bool>? = {
-        guard let call = call else { return nil }
-        return self.callsSercive.currentCall(callId: call.callId )
-        .startWith(call)
+        guard let callId = callId else { return nil }
+        return self.callsSercive.currentCall(callId: callId)
             .map({ callModel in
                 return (callModel.state == .over ||
                     callModel.state == .failure ||
@@ -86,28 +86,34 @@
             }
     }()
 
-    init(with call: CallModel?, injectionBag: InjectionBag) {
-        self.call = call
+    init(with callId: String?, injectionBag: InjectionBag, isLocal: Bool, participantId: String, participantUserName: String) {
+        self.callId = callId
         self.callsSercive = injectionBag.callService
         self.profileService = injectionBag.profileService
         self.accountService = injectionBag.accountService
-        self.isMasterCall = call == nil
+        self.isLocal = isLocal
+        self.participantId = participantId
+        self.participantUserName = participantUserName
     }
 
     func getName() -> String {
-        guard call != nil else {
+        if isLocal {
             return L10n.Account.me
         }
         return self.displayName.value
     }
 
     func getCallId() -> String? {
-        return self.call?.callId
+        return self.callId
+    }
+
+    func getParticipantId() -> String {
+        return participantId
     }
 
     func cancelCall() {
-        guard let call = self.call else { return }
-        self.callsSercive.hangUp(callId: call.callId)
+        guard let callId = self.callId else { return }
+        self.callsSercive.hangUp(callId: callId)
             .subscribe(onCompleted: { })
             .disposed(by: disposeBag)
     }
diff --git a/Ring/Ring/Constants/Generated/Strings.swift b/Ring/Ring/Constants/Generated/Strings.swift
index 17e9c27..e38caff 100644
--- a/Ring/Ring/Constants/Generated/Strings.swift
+++ b/Ring/Ring/Constants/Generated/Strings.swift
@@ -297,12 +297,20 @@
     internal static let maximize = L10n.tr("Localizable", "calls.maximize")
     /// minimize
     internal static let minimize = L10n.tr("Localizable", "calls.minimize")
+    /// mute audio
+    internal static let muteAudio = L10n.tr("Localizable", "calls.muteAudio")
+    /// unset moderator
+    internal static let removeModerator = L10n.tr("Localizable", "calls.removeModerator")
     /// Ringing…
     internal static let ringing = L10n.tr("Localizable", "calls.ringing")
     /// Searching…
     internal static let searching = L10n.tr("Localizable", "calls.searching")
+    /// set moderator
+    internal static let setModerator = L10n.tr("Localizable", "calls.setModerator")
     /// Unknown
     internal static let unknown = L10n.tr("Localizable", "calls.unknown")
+    /// unmute audio
+    internal static let unmuteAudio = L10n.tr("Localizable", "calls.unmuteAudio")
   }
 
   internal enum ContactPage {
diff --git a/Ring/Ring/Features/Me/Me/MeViewModel.swift b/Ring/Ring/Features/Me/Me/MeViewModel.swift
index c2c9f5a..7b7e19b 100644
--- a/Ring/Ring/Features/Me/Me/MeViewModel.swift
+++ b/Ring/Ring/Features/Me/Me/MeViewModel.swift
@@ -128,7 +128,7 @@
         return Observable
             .combineLatest(userName.startWith(""), ringId.startWith("")) { (name, ringID) in
                 var items: [SettingsSection.SectionRow] = [.sectionHeader(title: L10n.AccountPage.credentialsHeader),
-                                                            .jamiID(label: ringID)]
+                                                           .jamiID(label: ringID)]
                 items.append(.jamiUserName(label: name))
                 items.append(.shareAccountDetails)
             return SettingsSection
diff --git a/Ring/Ring/Models/ConferenceParticipant.swift b/Ring/Ring/Models/ConferenceParticipant.swift
index a478bb6..1b5a54f 100644
--- a/Ring/Ring/Models/ConferenceParticipant.swift
+++ b/Ring/Ring/Models/ConferenceParticipant.swift
@@ -26,6 +26,10 @@
     var uri: String?
     var isActive: Bool = false
     var displayName: String = ""
+    var isModerator: Bool = false
+    var isAudioLocalyMuted: Bool = false
+    var isAudioMuted: Bool = false
+    var isVideoMuted: Bool = false
 
     init (info: [String: String], onlyURIAndActive: Bool) {
         self.uri = info["uri"]
@@ -47,5 +51,17 @@
         if let participantHeight = info["h"] {
             self.height = CGFloat((participantHeight as NSString).doubleValue)
         }
+        if let videoMuted = info["videoMuted"] {
+            self.isVideoMuted = videoMuted.boolValue
+        }
+        if let audioLocalMuted = info["audioLocalMuted"] {
+            self.isAudioLocalyMuted = audioLocalMuted.boolValue
+        }
+        if let audioModeratorMuted = info["audioModeratorMuted"] {
+            self.isAudioMuted = audioModeratorMuted.boolValue
+        }
+        if let isModerator = info["isModerator"] {
+            self.isModerator = isModerator.boolValue
+        }
     }
 }
diff --git a/Ring/Ring/Resources/en.lproj/Localizable.strings b/Ring/Ring/Resources/en.lproj/Localizable.strings
index 6eaee9d..19d32d2 100644
--- a/Ring/Ring/Resources/en.lproj/Localizable.strings
+++ b/Ring/Ring/Resources/en.lproj/Localizable.strings
@@ -185,6 +185,10 @@
 "calls.haghUp" = "hang up";
 "calls.maximize" = "maximize";
 "calls.minimize" = "minimize";
+"calls.setModerator" = "set moderator";
+"calls.removeModerator" = "unset moderator";
+"calls.muteAudio" = "mute audio";
+"calls.unmuteAudio" = "unmute audio";
 
 //Account Page
 "accountPage.devicesListHeader" = "Devices";
diff --git a/Ring/Ring/Services/CallsService.swift b/Ring/Ring/Services/CallsService.swift
index be5cdcf..7cd6958 100644
--- a/Ring/Ring/Services/CallsService.swift
+++ b/Ring/Ring/Services/CallsService.swift
@@ -174,7 +174,7 @@
         guard let uri = participantURI,
             let participantsArray = self.callsAdapter.getConferenceInfo(conferenceId) as? [[String: String]] else { return nil }
         let participants = self.arrayToConferenceParticipants(participants: participantsArray, onlyURIAndActive: true)
-        for participant in participants where participant.uri == uri {
+        for participant in participants where participant.uri?.filterOutHost() == uri.filterOutHost() {
             return participant.isActive
         }
         return nil
@@ -196,19 +196,24 @@
         currentConferenceEvent.accept(ConferenceUpdates(conferenceID, ConferenceState.infoUpdated.rawValue, [""]))
     }
 
+    func isModerator(participantId: String, inConference confId: String) -> Bool {
+        let participants = self.conferenceInfos[confId]
+        let participant = participants?.filter({ confParticipant in
+            return confParticipant.uri?.filterOutHost() == participantId.filterOutHost()
+        }).first
+        return participant?.isModerator ?? false
+    }
+
     func getConferenceParticipants(for conferenceId: String) -> [ConferenceParticipant]? {
         return conferenceInfos[conferenceId]
     }
 
-    func setActiveParticipant(callId: String?, conferenceId: String, maximixe: Bool, jamiId: String) {
-        let participantURI = callId == nil ? "" : self.call(callID: callId!)?.participantUri
+    func setActiveParticipant(conferenceId: String, maximixe: Bool, jamiId: String) {
         guard let conference = self.call(callID: conferenceId),
-            let uri = participantURI,
-            let isActive = self.isParticipant(participantURI: uri, activeIn: conferenceId) else { return }
+              let isActive = self.isParticipant(participantURI: jamiId, activeIn: conferenceId) else { return }
         let newLayout = isActive ? self.getNewLayoutForActiveParticipant(currentLayout: conference.layout, maximixe: maximixe) : .oneWithSmal
         conference.layout = newLayout
-        let participant = callId == nil ? jamiId : participantURI
-        self.callsAdapter.setActiveParticipant(participant, forConference: conferenceId)
+        self.callsAdapter.setActiveParticipant(jamiId, forConference: conferenceId)
         self.callsAdapter.setConferenceLayout(newLayout.rawValue, forConference: conferenceId)
     }
 
@@ -667,4 +672,16 @@
             self.call(callID: callID)?.participantsCallId = conferenceCalls
         }
     }
+
+    func muteParticipant(confId: String, participantId: String, active: Bool) {
+        self.callsAdapter.muteConferenceParticipant(participantId, forConference: confId, active: active)
+    }
+
+    func setModeratorParticipant(confId: String, participantId: String, active: Bool) {
+        self.callsAdapter.setConferenceModerator(participantId, forConference: confId, active: active)
+    }
+
+    func hangupParticipant(confId: String, participantId: String) {
+        self.callsAdapter.hangupConferenceParticipant(participantId, forConference: confId)
+    }
 }
diff --git a/Ring/RingTests/ConferenceMenuItemsManagerTest.swift b/Ring/RingTests/ConferenceMenuItemsManagerTest.swift
index 302f61c..df50a1d 100644
--- a/Ring/RingTests/ConferenceMenuItemsManagerTest.swift
+++ b/Ring/RingTests/ConferenceMenuItemsManagerTest.swift
@@ -31,58 +31,58 @@
         super.tearDown()
     }
 
-    func testGetMenuItemsForMasterCallNil() {
+    func testGetMenuItemsForLocalCallNil() {
         let manager = ConferenceMenuItemsManager()
         let conference: CallModel? = nil
         let active = true
-        XCTAssertTrue(manager.getMenuItemsForMasterCall(conference: conference, active: active) == MenuMode.onlyName)
+        XCTAssertTrue(manager.getMenuItemsForLocalCall(conference: conference, active: active) == [.name])
     }
 
-    func testGetMenuItemsForeMasterCallWithoutActiveCall() {
+    func testGetMenuItemsForeLocalCallWithoutActiveCall() {
         let manager = ConferenceMenuItemsManager()
         let conference = CallModel()
         let active: Bool? = nil
-        XCTAssertTrue(manager.getMenuItemsForMasterCall(conference: conference, active: active) == MenuMode.onlyName)
+        XCTAssertTrue(manager.getMenuItemsForLocalCall(conference: conference, active: active) == [.name])
     }
 
-    func testGetMenuItemsForMasterCallWithConferenceGridLayout() {
+    func testGetMenuItemsForLocalCallWithConferenceGridLayout() {
         let manager = ConferenceMenuItemsManager()
         let conference = CallModel()
         conference.layout = .grid
         let active = true
-        XCTAssertTrue(manager.getMenuItemsForMasterCall(conference: conference, active: active) == MenuMode.withoutHangUPAndMinimize)
+        XCTAssertTrue(manager.getMenuItemsForLocalCall(conference: conference, active: active) == [.name, .maximize, .muteAudio])
     }
 
-    func testGetMenuItemsForActiveMasterCallWithConferenceOneWithSmalLayout() {
+    func testGetMenuItemsForActiveLocalCallWithConferenceOneWithSmalLayout() {
         let manager = ConferenceMenuItemsManager()
         let conference = CallModel()
         conference.layout = .oneWithSmal
         let active = true
-        XCTAssertTrue(manager.getMenuItemsForMasterCall(conference: conference, active: active) == MenuMode.withoutHangUp)
+        XCTAssertTrue(manager.getMenuItemsForLocalCall(conference: conference, active: active) == [.name, .maximize, .minimize, .muteAudio])
     }
 
-    func testGetMenuItemsForNotActiveMasterCallWithConferenceOneWithSmalLayout() {
+    func testGetMenuItemsForNotActiveLocalCallWithConferenceOneWithSmalLayout() {
         let manager = ConferenceMenuItemsManager()
         let conference = CallModel()
         conference.layout = .oneWithSmal
         let active = false
-        XCTAssertTrue(manager.getMenuItemsForMasterCall(conference: conference, active: active) == MenuMode.withoutHangUPAndMinimize)
+        XCTAssertTrue(manager.getMenuItemsForLocalCall(conference: conference, active: active) == [.name, .maximize, .muteAudio])
     }
 
-    func testGetMenuItemsForActiveMasterCallWithConferenceOneLayout() {
+    func testGetMenuItemsForActiveLocalCallWithConferenceOneLayout() {
         let manager = ConferenceMenuItemsManager()
         let conference = CallModel()
         conference.layout = .one
         let active = true
-        XCTAssertTrue(manager.getMenuItemsForMasterCall(conference: conference, active: active) == MenuMode.withoutHangUPAndMaximize)
+        XCTAssertTrue(manager.getMenuItemsForLocalCall(conference: conference, active: active) == [.name, .minimize, .muteAudio])
     }
 
-    func testGetMenuItemsForNotActiveMasterCallWithConferenceOneLayout() {
+    func testGetMenuItemsForNotActiveLocalCallWithConferenceOneLayout() {
         let manager = ConferenceMenuItemsManager()
         let conference = CallModel()
         conference.layout = .one
         let active = false
-        XCTAssertTrue(manager.getMenuItemsForMasterCall(conference: conference, active: active) == MenuMode.withoutHangUPAndMinimize)
+        XCTAssertTrue(manager.getMenuItemsForLocalCall(conference: conference, active: active) == [.name, .maximize, .muteAudio])
     }
 
     func testGetMenuItemsForNilConference() {
@@ -90,7 +90,9 @@
         let conference: CallModel? = nil
         let call: CallModel? = CallModel()
         let active = true
-        XCTAssertTrue(manager.getMenuItemsFor(call: call, conference: conference, active: active) == MenuMode.onlyName)
+        let role = RoleInCall.host
+        let isHost = false
+        XCTAssertTrue(manager.getMenuItemsFor(call: call, isHost: isHost, conference: conference, active: active, role: role) == [.name])
     }
 
     func testGetMenuItemsForNilCall() {
@@ -98,7 +100,9 @@
         let conference: CallModel? = CallModel()
         let call: CallModel? = nil
         let active = true
-        XCTAssertTrue(manager.getMenuItemsFor(call: call, conference: conference, active: active) == MenuMode.onlyName)
+        let role = RoleInCall.host
+        let isHost = false
+        XCTAssertTrue(manager.getMenuItemsFor(call: call, isHost: isHost, conference: conference, active: active, role: role) == [.name])
     }
 
     func testGetMenuItemsWithoutActiveCall() {
@@ -106,8 +110,10 @@
         let conference: CallModel? = CallModel()
         let call: CallModel? = CallModel()
         call?.state = .current
+        let role = RoleInCall.host
         let active: Bool? = nil
-        XCTAssertTrue(manager.getMenuItemsFor(call: call, conference: conference, active: active) == MenuMode.onlyName)
+        let isHost = false
+        XCTAssertTrue(manager.getMenuItemsFor(call: call, isHost: isHost, conference: conference, active: active, role: role) == [.name])
     }
 
     func testGetMenuItemsForConnectingCall() {
@@ -115,8 +121,10 @@
         let conference: CallModel? = CallModel()
         let call: CallModel? = CallModel()
         call?.state = .connecting
+        let role = RoleInCall.host
         let active: Bool? = true
-        XCTAssertTrue(manager.getMenuItemsFor(call: call, conference: conference, active: active) == MenuMode.withoutMaximizeAndMinimize)
+        let isHost = false
+        XCTAssertTrue(manager.getMenuItemsFor(call: call, isHost: isHost, conference: conference, active: active, role: role) == [.name, .hangup])
     }
 
     func testGetMenuItemsForRingingCall() {
@@ -124,8 +132,10 @@
         let conference: CallModel? = CallModel()
         let call: CallModel? = CallModel()
         call?.state = .ringing
+        let role = RoleInCall.host
         let active: Bool? = true
-        XCTAssertTrue(manager.getMenuItemsFor(call: call, conference: conference, active: active) == MenuMode.withoutMaximizeAndMinimize)
+        let isHost = false
+        XCTAssertTrue(manager.getMenuItemsFor(call: call, isHost: isHost, conference: conference, active: active, role: role) == [.name, .hangup])
     }
 
     func testGetMenuItemsForHoldingCall() {
@@ -133,8 +143,10 @@
         let conference: CallModel? = CallModel()
         let call: CallModel? = CallModel()
         call?.state = .hold
+        let role = RoleInCall.host
         let active: Bool? = true
-        XCTAssertTrue(manager.getMenuItemsFor(call: call, conference: conference, active: active) == MenuMode.withoutMaximizeAndMinimize)
+        let isHost = false
+        XCTAssertTrue(manager.getMenuItemsFor(call: call, isHost: isHost, conference: conference, active: active, role: role) == [.name, .hangup])
     }
 
     func testGetMenuItemsForCallWithConferenceGridLayout() {
@@ -143,8 +155,10 @@
         conference?.layout = .grid
         let call: CallModel? = CallModel()
         call?.state = .current
+        let role = RoleInCall.host
         let active: Bool? = true
-        XCTAssertTrue(manager.getMenuItemsFor(call: call, conference: conference, active: active) == MenuMode.withoutMinimize)
+        let isHost = false
+        XCTAssertTrue(manager.getMenuItemsFor(call: call, isHost: isHost, conference: conference, active: active, role: role) == [.name, .maximize, .muteAudio, .setModerator, .hangup])
     }
 
     func testGetMenuItemsForActiveCallWithConferenceOneWithSmalLayout() {
@@ -152,9 +166,11 @@
         let conference: CallModel? = CallModel()
         conference?.layout = .oneWithSmal
         let call: CallModel? = CallModel()
+        let role = RoleInCall.host
         call?.state = .current
         let active: Bool? = true
-        XCTAssertTrue(manager.getMenuItemsFor(call: call, conference: conference, active: active) == MenuMode.all)
+        let isHost = false
+        XCTAssertTrue(manager.getMenuItemsFor(call: call, isHost: isHost, conference: conference, active: active, role: role) == [.name, .maximize, .minimize, .muteAudio, .setModerator, .hangup])
     }
 
     func testGetMenuItemsForNotActiveCallWithConferenceOneWithSmalLayout() {
@@ -162,9 +178,11 @@
         let conference: CallModel? = CallModel()
         conference?.layout = .oneWithSmal
         let call: CallModel? = CallModel()
+        let role = RoleInCall.host
         call?.state = .current
         let active: Bool? = false
-        XCTAssertTrue(manager.getMenuItemsFor(call: call, conference: conference, active: active) == MenuMode.withoutMinimize)
+        let isHost = false
+        XCTAssertTrue(manager.getMenuItemsFor(call: call, isHost: isHost, conference: conference, active: active, role: role) == [.name, .maximize, .muteAudio, .setModerator, .hangup])
     }
 
     func testGetMenuItemsForActiveCallWithConferenceOneLayout() {
@@ -173,8 +191,10 @@
         conference?.layout = .one
         let call: CallModel? = CallModel()
         call?.state = .current
+        let role = RoleInCall.host
         let active: Bool? = true
-        XCTAssertTrue(manager.getMenuItemsFor(call: call, conference: conference, active: active) == MenuMode.withoutMaximize)
+        let isHost = false
+        XCTAssertTrue(manager.getMenuItemsFor(call: call, isHost: isHost, conference: conference, active: active, role: role) == [.name, .minimize, .muteAudio, .setModerator, .hangup])
     }
 
     func testGetMenuItemsForNotActiveCallWithConferenceOneLayout() {
@@ -183,7 +203,9 @@
         conference?.layout = .one
         let call: CallModel? = CallModel()
         call?.state = .current
+        let role = RoleInCall.host
         let active: Bool? = false
-        XCTAssertTrue(manager.getMenuItemsFor(call: call, conference: conference, active: active) == MenuMode.withoutMinimize)
+        let isHost = false
+        XCTAssertTrue(manager.getMenuItemsFor(call: call, isHost: isHost, conference: conference, active: active, role: role) == [.name, .maximize, .muteAudio, .setModerator, .hangup])
     }
 }