kevin пре 2 година
родитељ
комит
bf925da6b2

+ 121 - 2
appbuilder-ios/NexilisLite/NexilisLite/Resource/Palio.storyboard

@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
     <device id="retina6_1" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/>
         <capability name="Image references" minToolsVersion="12.0"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
         <capability name="System colors in document resources" minToolsVersion="11.0"/>
@@ -3044,6 +3044,124 @@
             </objects>
             <point key="canvasLocation" x="4672" y="1443"/>
         </scene>
+        <!--Seminar List View Controller-->
+        <scene sceneID="9jc-wF-EYe">
+            <objects>
+                <viewController storyboardIdentifier="seminarList" useStoryboardIdentifierAsRestorationIdentifier="YES" id="BHM-G8-71f" customClass="SeminarListViewController" customModule="NexilisLite" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="RDr-rx-fRa">
+                        <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="w21-oX-k1B">
+                                <rect key="frame" x="0.0" y="92" width="414" height="770"/>
+                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <prototypes>
+                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="seminarListCell" rowHeight="60" id="fKY-DZ-Ct6" customClass="SeminarListCell" customModule="NexilisLite" customModuleProvider="target">
+                                        <rect key="frame" x="0.0" y="50" width="414" height="60"/>
+                                        <autoresizingMask key="autoresizingMask"/>
+                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="fKY-DZ-Ct6" id="d88-ar-X7o">
+                                            <rect key="frame" x="0.0" y="0.0" width="414" height="60"/>
+                                            <autoresizingMask key="autoresizingMask"/>
+                                            <subviews>
+                                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qYm-ha-8fn">
+                                                    <rect key="frame" x="5" y="0.0" width="404" height="60"/>
+                                                    <subviews>
+                                                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="person.crop.circle.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="pyU-X7-6Oh">
+                                                            <rect key="frame" x="8" y="8.5" width="40" height="39"/>
+                                                            <color key="tintColor" systemColor="systemGray2Color"/>
+                                                            <constraints>
+                                                                <constraint firstAttribute="width" constant="40" id="A1W-jV-X76"/>
+                                                                <constraint firstAttribute="height" constant="40" id="H74-sj-Ntr"/>
+                                                            </constraints>
+                                                        </imageView>
+                                                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Usw-tA-7yM">
+                                                            <rect key="frame" x="344" y="10" width="40" height="40"/>
+                                                            <constraints>
+                                                                <constraint firstAttribute="width" secondItem="Usw-tA-7yM" secondAttribute="height" multiplier="1:1" id="8oX-aZ-5mr"/>
+                                                            </constraints>
+                                                            <state key="normal" image="pb_seminar_speaking"/>
+                                                        </button>
+                                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hhH-2F-CPx">
+                                                            <rect key="frame" x="56" y="20" width="100" height="20"/>
+                                                            <constraints>
+                                                                <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="100" id="NrJ-Lc-87a"/>
+                                                            </constraints>
+                                                            <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                                            <nil key="textColor"/>
+                                                            <nil key="highlightedColor"/>
+                                                        </label>
+                                                    </subviews>
+                                                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                                                    <constraints>
+                                                        <constraint firstAttribute="bottom" secondItem="hhH-2F-CPx" secondAttribute="bottom" constant="20" id="0hC-gu-o0x"/>
+                                                        <constraint firstAttribute="trailing" secondItem="Usw-tA-7yM" secondAttribute="trailing" constant="20" id="19S-tj-OPl"/>
+                                                        <constraint firstItem="Usw-tA-7yM" firstAttribute="top" secondItem="qYm-ha-8fn" secondAttribute="top" constant="10" id="3Dy-3M-QGh"/>
+                                                        <constraint firstAttribute="height" constant="60" id="DnK-7F-jvf"/>
+                                                        <constraint firstItem="Usw-tA-7yM" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="hhH-2F-CPx" secondAttribute="trailing" constant="8" symbolic="YES" id="EdQ-h1-2Ba"/>
+                                                        <constraint firstItem="pyU-X7-6Oh" firstAttribute="leading" secondItem="qYm-ha-8fn" secondAttribute="leading" constant="8" id="Gys-9e-Y6R"/>
+                                                        <constraint firstItem="hhH-2F-CPx" firstAttribute="top" secondItem="qYm-ha-8fn" secondAttribute="top" constant="20" id="KWA-xY-gdi"/>
+                                                        <constraint firstItem="pyU-X7-6Oh" firstAttribute="top" secondItem="qYm-ha-8fn" secondAttribute="top" constant="8.0000000000000018" id="Mrq-kV-4xN"/>
+                                                        <constraint firstItem="hhH-2F-CPx" firstAttribute="leading" secondItem="pyU-X7-6Oh" secondAttribute="trailing" constant="8" id="WX4-SY-gJh"/>
+                                                        <constraint firstAttribute="bottom" secondItem="Usw-tA-7yM" secondAttribute="bottom" constant="10" id="pda-zG-ngz"/>
+                                                    </constraints>
+                                                </view>
+                                            </subviews>
+                                            <constraints>
+                                                <constraint firstAttribute="bottom" secondItem="qYm-ha-8fn" secondAttribute="bottom" id="PDi-Hm-s6z"/>
+                                                <constraint firstAttribute="trailing" secondItem="qYm-ha-8fn" secondAttribute="trailing" constant="5" id="akW-JR-HHm"/>
+                                                <constraint firstItem="qYm-ha-8fn" firstAttribute="top" secondItem="d88-ar-X7o" secondAttribute="top" id="eKi-5T-STF"/>
+                                                <constraint firstItem="qYm-ha-8fn" firstAttribute="leading" secondItem="d88-ar-X7o" secondAttribute="leading" constant="5" id="tIL-5D-1xM"/>
+                                            </constraints>
+                                        </tableViewCellContentView>
+                                        <connections>
+                                            <outlet property="imagePerson" destination="pyU-X7-6Oh" id="ADa-Ob-pNj"/>
+                                            <outlet property="namePerson" destination="hhH-2F-CPx" id="NmC-bD-vB9"/>
+                                            <outlet property="speakerButton" destination="Usw-tA-7yM" id="WO9-Z8-TA1"/>
+                                        </connections>
+                                    </tableViewCell>
+                                </prototypes>
+                            </tableView>
+                        </subviews>
+                        <viewLayoutGuide key="safeArea" id="WKx-98-0oN"/>
+                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                        <constraints>
+                            <constraint firstItem="w21-oX-k1B" firstAttribute="bottom" secondItem="WKx-98-0oN" secondAttribute="bottom" id="3OR-y6-iUg"/>
+                            <constraint firstItem="w21-oX-k1B" firstAttribute="leading" secondItem="WKx-98-0oN" secondAttribute="leading" id="MRS-7A-g1N"/>
+                            <constraint firstItem="w21-oX-k1B" firstAttribute="trailing" secondItem="WKx-98-0oN" secondAttribute="trailing" id="VFC-Gy-IVX"/>
+                            <constraint firstItem="w21-oX-k1B" firstAttribute="leading" secondItem="WKx-98-0oN" secondAttribute="leading" id="hjk-fP-IJp"/>
+                            <constraint firstItem="w21-oX-k1B" firstAttribute="top" secondItem="WKx-98-0oN" secondAttribute="top" id="i3o-rE-2kq"/>
+                            <constraint firstItem="WKx-98-0oN" firstAttribute="bottom" secondItem="w21-oX-k1B" secondAttribute="bottom" id="jUJ-gc-c7c"/>
+                            <constraint firstItem="w21-oX-k1B" firstAttribute="top" secondItem="WKx-98-0oN" secondAttribute="top" id="n2f-uE-DAA"/>
+                            <constraint firstItem="WKx-98-0oN" firstAttribute="trailing" secondItem="w21-oX-k1B" secondAttribute="trailing" id="xfx-BY-QtC"/>
+                        </constraints>
+                    </view>
+                    <navigationItem key="navigationItem" id="Ixw-1O-sT3"/>
+                    <connections>
+                        <outlet property="tableView" destination="w21-oX-k1B" id="Hn0-ON-pUC"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="J34-3K-C9o" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="8525" y="1550"/>
+        </scene>
+        <!--Navigation Controller-->
+        <scene sceneID="QZB-X2-3Cl">
+            <objects>
+                <navigationController storyboardIdentifier="seminarListNav" automaticallyAdjustsScrollViewInsets="NO" id="fLe-EJ-GeO" sceneMemberID="viewController">
+                    <toolbarItems/>
+                    <navigationBar key="navigationBar" contentMode="scaleToFill" id="ESA-C7-dc0">
+                        <rect key="frame" x="0.0" y="48" width="414" height="44"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                    </navigationBar>
+                    <nil name="viewControllers"/>
+                    <connections>
+                        <segue destination="BHM-G8-71f" kind="relationship" relationship="rootViewController" id="oEd-aI-euD"/>
+                    </connections>
+                </navigationController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="t3Y-oi-Aib" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="7623" y="1550"/>
+        </scene>
     </scenes>
     <inferredMetricsTieBreakers>
         <segue reference="ZRi-3t-r4r"/>
@@ -3064,6 +3182,7 @@
         <image name="message.fill" catalog="system" width="128" height="114"/>
         <image name="mic.slash.fill" catalog="system" width="108" height="128"/>
         <image name="pb_cd_person" width="512" height="512"/>
+        <image name="pb_seminar_speaking" width="512" height="512"/>
         <image name="pdf-icon" width="860" height="901"/>
         <image name="pencil" catalog="system" width="128" height="113"/>
         <image name="person.badge.plus.fill" catalog="system" width="128" height="125"/>

+ 11 - 0
appbuilder-ios/NexilisLite/NexilisLite/Source/CoreMessage_TMessageBank.swift

@@ -2343,4 +2343,15 @@ public class CoreMessage_TMessageBank {
         tmessage.mBodies[CoreMessage_TMessageKey.STATUS] = "\(CoreMessage_TMessageCode.SS_END)"
         return tmessage;
     }
+    
+    public static func getSimplePersonInfoWA(f_pin: String) -> TMessage{
+        let tmessage = TMessage()
+        tmessage.mCode = CoreMessage_TMessageCode.GET_SIMPLE_PERSON_INFO
+        tmessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tmessage.mPIN = UserDefaults.standard.string(forKey: "me")!
+        tmessage.mBodies[CoreMessage_TMessageKey.L_PIN] = f_pin
+        tmessage.mBodies[CoreMessage_TMessageKey.STATUS] = "1"
+        return tmessage
+    }
+    
 }

+ 200 - 0
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Streaming/SeminarListViewController.swift

@@ -0,0 +1,200 @@
+//
+//  SeminarListViewController.swift
+//  NexilisLite
+//
+//  Created by Maronakins on 13/06/23.
+//
+
+import UIKit
+import nuSDKService
+
+class SeminarListViewController: UIViewController {
+    
+    @IBOutlet weak var tableView: UITableView!
+
+    var searchController: UISearchController!
+    
+    var data: [SeminarViewer] = []
+    
+    var fillteredData: [SeminarViewer] = []
+    
+    var isSearchBarEmpty: Bool {
+        return searchController.searchBar.text!.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
+    }
+    
+    var isFilltering: Bool {
+        return searchController.isActive && !isSearchBarEmpty
+    }
+    
+    var makeSpeaker: ((SeminarViewer) -> ())?
+    
+    var removeSpeaker: ((SeminarViewer) -> ())?
+    
+    var timerSearch: Timer?
+    
+    func filterContentForSearchText(_ searchText: String) {
+        fillteredData = data.filter{ $0.name.lowercased().contains(searchText.lowercased()) }
+        tableView.reloadData()
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        title = "Seminar".localized()
+        
+        let attributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
+        let navBarAppearance = UINavigationBarAppearance()
+        navBarAppearance.configureWithOpaqueBackground()
+        navBarAppearance.backgroundColor = UIColor.mainColor
+        navBarAppearance.titleTextAttributes = attributes
+        navigationController?.navigationBar.standardAppearance = navBarAppearance
+        navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
+        
+        navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(cancel(sender:)))
+        
+        searchController = UISearchController(searchResultsController: nil)
+        searchController.delegate = self
+        searchController.searchResultsUpdater = self
+        searchController.searchBar.autocapitalizationType = .none
+        searchController.searchBar.delegate = self
+        searchController.searchBar.barTintColor = .secondaryColor
+        searchController.searchBar.searchTextField.backgroundColor = .secondaryColor
+        searchController.obscuresBackgroundDuringPresentation = false
+        searchController.searchBar.searchTextField.attributedPlaceholder = NSAttributedString(string: "Search".localized(), attributes: [NSAttributedString.Key.foregroundColor: UIColor.gray])
+        searchController.searchBar.setMagnifyingGlassColorTo(color: .mainColor)
+        searchController.searchBar.tintColor = .mainColor
+        
+        definesPresentationContext = true
+        
+        navigationItem.searchController = searchController
+        navigationItem.hidesSearchBarWhenScrolling = false
+        
+    }
+    
+    @objc func cancel(sender: Any) {
+        navigationController?.dismiss(animated: true, completion: nil)
+    }
+    
+    func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
+        searchBar.showsCancelButton = true
+        let cBtn = searchBar.value(forKey: "cancelButton") as! UIButton
+        cBtn.setTitle("Cancel".localized(), for: .normal)
+    }
+    
+    func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
+        searchBar.showsCancelButton = false
+    }
+    
+    func showChooserButtonTapped(viewer: SeminarViewer) {
+        // Create an instance of the alert controller with the action sheet style
+        let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
+        
+        // Add action buttons to the alert controller
+        var actionTitle = "Make Speaker"
+        if(viewer.isSpeak){
+            actionTitle = "Remove Speaker"
+        }
+        let action = UIAlertAction(title: actionTitle, style: .default) { (_) in
+            self.handleSpeaker(viewer: viewer)
+        }
+        alertController.addAction(action)
+        
+        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
+        alertController.addAction(cancelAction)
+        
+        present(alertController, animated: true, completion: nil)
+    }
+    
+    func handleSpeaker(viewer: SeminarViewer) {
+        navigationController?.dismiss(animated: true)
+        if (viewer.isSpeak){
+            removeSpeaker!(viewer)
+        }
+        else {
+            makeSpeaker!(viewer)
+        }
+    }
+
+}
+
+class SeminarListCell: UITableViewCell {
+    @IBOutlet weak var imagePerson: UIImageView!
+    @IBOutlet weak var speakerButton: UIButton!
+    @IBOutlet weak var namePerson: UILabel!
+}
+
+// MARK: - Extension
+
+extension SeminarListViewController: UITableViewDelegate {
+    
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        let viewer: SeminarViewer
+        if isFilltering {
+            viewer = fillteredData[indexPath.row]
+        } else {
+            viewer = data[indexPath.row]
+        }
+        showChooserButtonTapped(viewer: viewer)
+    }
+    
+}
+
+extension SeminarListViewController: UITableViewDataSource {
+    
+    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        return 60
+    }
+    
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        if isFilltering {
+            return fillteredData.count
+        }
+        return data.count
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = tableView.dequeueReusableCell(withIdentifier: "seminarListCell", for: indexPath) as! SeminarListCell
+        cell.imagePerson.layer.masksToBounds = false
+        cell.imagePerson.circle()
+        cell.imagePerson.clipsToBounds = true
+        cell.imagePerson.image = UIImage(named: "Profile---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
+        cell.imagePerson.contentMode = .scaleAspectFit
+        let user: SeminarViewer
+        if isFilltering {
+            user = fillteredData[indexPath.row]
+        } else {
+            user = data[indexPath.row]
+        }
+        let pictureImage = user.thumb
+        if (pictureImage != "") {
+            cell.imagePerson.setImage(name: pictureImage)
+            cell.imagePerson.contentMode = .scaleAspectFill
+        }
+        cell.namePerson.text = user.name
+        if (user.isSpeak){
+            cell.speakerButton.isHidden = false
+            cell.speakerButton.setImage(UIImage(named: "pb_seminar_speaking", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), for: .normal)
+        }
+        else if (user.isRaise){
+            cell.speakerButton.isHidden = false
+            cell.speakerButton.setImage(UIImage(named: "pb_raise_hand", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), for: .normal)
+        }
+        else {
+            cell.speakerButton.isHidden = true
+        }
+        return cell
+    }
+}
+
+extension SeminarListViewController: UISearchControllerDelegate, UISearchBarDelegate, UISearchResultsUpdating {
+    
+    func updateSearchResults(for searchController: UISearchController) {
+        timerSearch?.invalidate()
+        let currentText = searchController.searchBar.text!.trimmingCharacters(in: .whitespacesAndNewlines)
+        if currentText.count >= 2 || currentText.isEmpty {
+            timerSearch = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: {_ in
+                self.filterContentForSearchText(currentText)
+            })
+        }
+    }
+}

+ 20 - 1
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Streaming/SeminarViewController.swift

@@ -30,6 +30,8 @@ class SeminarViewController: UIViewController {
     
     private var heightTableView: NSLayoutConstraint?
     
+    var wbVC : WhiteboardViewController?
+    
     private var viewers: [SeminarViewer] = []
     
     private var chats: [SeminarChat] = [] {
@@ -418,11 +420,15 @@ class SeminarViewController: UIViewController {
     }
     
     @objc func screenShare(sender: Any?) {
-        // TODO: implement screen sharing   
+        // TODO: implement screen sharing
+        
     }
     
     @objc func whiteboard(sender: Any?) {
         // TODO: implement whiteboard
+        _ = Nexilis.write(message: CoreMessage_TMessageBank.getSeminarDraw(broadcaster: data, flag: "1"))
+        
+        
     }
     
     @objc func hideKeyboard() {
@@ -506,6 +512,19 @@ class SeminarViewController: UIViewController {
         guard hasRaiseHand else {
             return
         }
+        let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "seminarListNav") as! UINavigationController
+        if let vc = controller.viewControllers.first as? SeminarListViewController {
+            vc.makeSpeaker = { viewer in
+                API.sabc(sAudienceID: viewer.f_pin)
+                self.viewers.first(where: {$0.f_pin == viewer.f_pin})?.isRaise = false
+                self.viewers.first(where: {$0.f_pin == viewer.f_pin})?.isSpeak = true
+            }
+            vc.removeSpeaker = { viewer in
+                API.eabc(sAudienceID: viewer.f_pin)
+                self.viewers.first(where: {$0.f_pin == viewer.f_pin})?.isSpeak = false
+            }
+        }
+        self.navigationController?.present(controller, animated: true, completion: nil)
         // TODO: Show list viewer
     }