Explorar el Código

add seminar view

kevin hace 2 años
padre
commit
310bb30724

+ 5 - 0
appbuilder-ios/NexilisLite/NexilisLite/Resource/id.lproj/Localizable.strings

@@ -261,6 +261,7 @@
 "You have requested Call Center, please wait for response." = "Anda telah meminta Pusat Panggilan, harap tunggu tanggapan.";
 "Audio Call Ended" = "Panggilan Suara telah berakhir";
 "Live Streaming session hasn\'t started yet" = "Sesi Siaran Langsung belum dimulai";
+"Seminar session hasn\'t started yet" = "Sesi Seminar belum dimulai";
 "Backup & Restore" = "Cadangkan & Pulihkan";
 "Last Backup" = "Cadangan terakhir";
 "Total Size" = "Ukuran Keseluruhan";
@@ -292,3 +293,7 @@
 "Start Chat" = "Mulai Obrolan";
 "Start Audio Call" = "Mulai Panggilan Suara";
 "Start Video Call" = "Mualai Panngilan Video";
+"Seminar title can't be empty" = "Judul Seminar tidak boleh kosong";
+"Seminar description can't be empty" = "Deskripsi Seminar tidak boleh kosong";
+"Live Streaming title can't be empty" = "Judul Siaran Langsung tidak boleh kosong";
+"Live Streaming description can't be empty" = "Deskripsi Siaran Langsung tidak boleh kosong";

+ 6 - 0
appbuilder-ios/NexilisLite/NexilisLite/Source/Callback.swift

@@ -61,6 +61,12 @@ class Callback : CallBack {
         if let delegate = Nexilis.shared.streamingDelagate {
             delegate.onJoinLS(state: nState, message: sMessage)
         }
+        if let delegate = Nexilis.shared.seminarDelegate {
+            delegate.onStartSeminar(state: nState, message: sMessage)
+        }
+        if let delegate = Nexilis.shared.seminarDelegate {
+            delegate.onJoinSeminar(state: nState, message: sMessage)
+        }
         return 1
     }
     

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

@@ -719,6 +719,24 @@ public class CoreMessage_TMessageBank {
         return tmessage
     }
     
+    public static func createSeminar(title: String, type:String, typeValue: String = "", category: String, notifType: String, blogId: String, data: String) -> TMessage {
+        let me = UserDefaults.standard.string(forKey: "me")!
+        let tmessage = TMessage()
+        tmessage.mCode = CoreMessage_TMessageCode.SEMINAR_CREATE
+        tmessage.mPIN = me
+        tmessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tmessage.mBodies[CoreMessage_TMessageKey.TITLE] = title
+        tmessage.mBodies[CoreMessage_TMessageKey.BROADCAST_FLAG] = type
+        tmessage.mBodies[CoreMessage_TMessageKey.F_PIN] = me
+        tmessage.mBodies[CoreMessage_TMessageKey.VALUE] = typeValue
+        tmessage.mBodies[CoreMessage_TMessageKey.CATEGORY_ID] = category
+        tmessage.mBodies[CoreMessage_TMessageKey.BLOG_ID] = blogId
+        tmessage.mBodies[CoreMessage_TMessageKey.TYPE] = notifType
+        tmessage.mBodies[CoreMessage_TMessageKey.DATA] = data
+        tmessage.mBodies[CoreMessage_TMessageKey.BUSINESS_ENTITY] = ""
+        return tmessage
+    }
+    
     public static func getUploadTimeline(post_id: String, title: String, description: String, link: String, type: String, created_date: String, audition_date: String, thumb_id: String, privacy: String, file_id: String, video_duration: String, category: String, file_type: String, ads_type: String) -> TMessage {
         let me = UserDefaults.standard.string(forKey: "me")!
         let tmessage = TMessage()
@@ -916,6 +934,71 @@ public class CoreMessage_TMessageBank {
         tmessage.mBodies[CoreMessage_TMessageKey.REQUEST_ID] = request_id
         return tmessage
     }
+    public static func joinSeminar(broadcast_id: String, request_id:String) -> TMessage {
+        let me = UserDefaults.standard.string(forKey: "me")!
+        let tmessage = TMessage()
+        tmessage.mCode = CoreMessage_TMessageCode.SEMINAR_JOIN
+        tmessage.mPIN = me
+        tmessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tmessage.mBodies[CoreMessage_TMessageKey.BROADCAST_ID] = broadcast_id
+        tmessage.mBodies[CoreMessage_TMessageKey.REQUEST_ID] = request_id
+        return tmessage
+    }
+    public static func removeSeminar(broadcast_id: String, request_id:String) -> TMessage {
+        let me = UserDefaults.standard.string(forKey: "me")!
+        let tmessage = TMessage()
+        tmessage.mCode = CoreMessage_TMessageCode.SEMINAR_REMOVE
+        tmessage.mPIN = me
+        tmessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tmessage.mBodies[CoreMessage_TMessageKey.BROADCAST_ID] = broadcast_id
+        tmessage.mBodies[CoreMessage_TMessageKey.REQUEST_ID] = request_id
+        return tmessage
+    }
+    public static func leftSeminar(broadcast_id: String, request_id:String) -> TMessage {
+        let me = UserDefaults.standard.string(forKey: "me")!
+        let tmessage = TMessage()
+        tmessage.mCode = CoreMessage_TMessageCode.SEMINAR_LEFT
+        tmessage.mPIN = me
+        tmessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tmessage.mBodies[CoreMessage_TMessageKey.BROADCAST_ID] = broadcast_id
+        tmessage.mBodies[CoreMessage_TMessageKey.REQUEST_ID] = request_id
+        return tmessage
+    }
+    
+    public static func getSeminarRaiseHand(p_pin: String, l_pin: String, status: String) -> TMessage {
+        let tmessage = TMessage()
+        tmessage.mCode = CoreMessage_TMessageCode.SEMINAR_PUSH_RAISE_HAND
+        tmessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tmessage.mPIN = p_pin
+        tmessage.mBodies[CoreMessage_TMessageKey.F_PIN] =  p_pin
+        tmessage.mBodies[CoreMessage_TMessageKey.L_PIN] = l_pin
+        tmessage.mBodies[CoreMessage_TMessageKey.STATUS] = status
+        return tmessage
+    }
+    
+    public static func getSeminarDraw(broadcaster: String, flag: String) -> TMessage {
+        let me = UserDefaults.standard.string(forKey: "me")!
+        let tmessage = TMessage()
+        tmessage.mCode = CoreMessage_TMessageCode.SEMINAR_DRAW
+        tmessage.mPIN = me
+        tmessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tmessage.mBodies[CoreMessage_TMessageKey.F_PIN] = tmessage.mPIN
+        tmessage.mBodies[CoreMessage_TMessageKey.L_PIN] = broadcaster
+        tmessage.mBodies[CoreMessage_TMessageKey.INDEX] = flag
+        return tmessage
+    }
+    
+    public static func getSeminarFaceDetection(p_pin: String, l_pin: String, flag: String) -> TMessage {
+        let tmessage = TMessage()
+        tmessage.mCode = CoreMessage_TMessageCode.SEMINAR_FACE_DETECTION
+        tmessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tmessage.mPIN = p_pin
+        tmessage.mBodies[CoreMessage_TMessageKey.F_PIN] = p_pin
+        tmessage.mBodies[CoreMessage_TMessageKey.L_PIN] = l_pin
+        tmessage.mBodies[CoreMessage_TMessageKey.STATUS] = flag
+        return tmessage
+    }
+    
     public static func getSendLSChat(l_pin: String, message_text:String) -> TMessage {
         let me = UserDefaults.standard.string(forKey: "me")!
         let tmessage = TMessage()
@@ -927,6 +1010,17 @@ public class CoreMessage_TMessageBank {
         tmessage.mBodies[CoreMessage_TMessageKey.MESSAGE_TEXT] = message_text
         return tmessage
     }
+    public static func getSendSeminarChat(l_pin: String, message_text:String) -> TMessage {
+        let me = UserDefaults.standard.string(forKey: "me")!
+        let tmessage = TMessage()
+        tmessage.mCode = CoreMessage_TMessageCode.SEMINAR_PUSH_CHAT
+        tmessage.mPIN = me
+        tmessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tmessage.mBodies[CoreMessage_TMessageKey.F_PIN] = me
+        tmessage.mBodies[CoreMessage_TMessageKey.L_PIN] = l_pin
+        tmessage.mBodies[CoreMessage_TMessageKey.MESSAGE_TEXT] = message_text
+        return tmessage
+    }
     public static func getSendLSEmotion(l_pin: String, emotion_type:String) -> TMessage {
         let me = UserDefaults.standard.string(forKey: "me")!
         let tmessage = TMessage()
@@ -1526,6 +1620,21 @@ public class CoreMessage_TMessageBank {
         return tmessage
     }
     
+    public static func getStartSeminarInvited(title:String, type:String,typeValue:String,category:String,blog_id:String) -> TMessage {
+        let me = UserDefaults.standard.string(forKey: "me")!
+        let tmessage = TMessage()
+        tmessage.mCode = CoreMessage_TMessageCode.SEMINAR_START_INVITED
+        tmessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tmessage.mPIN = me
+        tmessage.mBodies[CoreMessage_TMessageKey.F_PIN] = me
+        tmessage.mBodies[CoreMessage_TMessageKey.TITLE] = title
+        tmessage.mBodies[CoreMessage_TMessageKey.BROADCAST_FLAG] = type
+        tmessage.mBodies[CoreMessage_TMessageKey.VALUE] = type
+        tmessage.mBodies[CoreMessage_TMessageKey.CATEGORY_ID] = category
+        tmessage.mBodies[CoreMessage_TMessageKey.BLOG_ID] = blog_id
+        return tmessage
+    }
+    
     public static func getListSubAccount() -> TMessage {
         let me = UserDefaults.standard.string(forKey: "me")!
         let tmessage = TMessage()

+ 12 - 0
appbuilder-ios/NexilisLite/NexilisLite/Source/CoreMessage_TMessageCode.swift

@@ -328,6 +328,18 @@ public class CoreMessage_TMessageCode {
     public static let REQUEST_START_LIVE_VIDEO = "RLV";
     public static let OUT_FROM_STREAMING_TAB   = "OLS";
     
+    public static let SEMINAR_START_INVITED = "CSR0";
+    public static let SEMINAR_CREATE = "CSR1";
+    public static let SEMINAR_PUSH_CHAT = "CSR2";
+    public static let SEMINAR_PUSH_JOIN = "CSR3";
+    public static let SEMINAR_JOIN = "CSR4";
+    public static let SEMINAR_REMOVE = "CSR5";
+    public static let SEMINAR_LEFT = "CSR6";
+    public static let SEMINAR_PUSH_LEFT = "CSR7";
+    public static let SEMINAR_PUSH_RAISE_HAND = "CSR8";
+    public static let SEMINAR_DRAW = "CSR10";
+    public static let SEMINAR_FACE_DETECTION = "CSR11";
+    
     public static let GET_BATCH_BUDDIES_BASED_SCROLL     = "C70";
     public static let CHANGE_BATCH_PERSON_INFO_BASED_SCROLL = "A00X4";
     

+ 61 - 0
appbuilder-ios/NexilisLite/NexilisLite/Source/IncomingThread.swift

@@ -110,6 +110,16 @@ class IncomingThread {
             sendUpdateTyping(message: message)
         } else if message.getCode() == CoreMessage_TMessageCode.LIVE_PROFILE_PUSH_CHAT {
             getLSChat(message: message)
+        } else if message.getCode() == CoreMessage_TMessageCode.SEMINAR_JOIN {
+            joinSeminar(message: message)
+        } else if message.getCode() == CoreMessage_TMessageCode.SEMINAR_LEFT {
+            leftSeminar(message: message)
+        } else if message.getCode() == CoreMessage_TMessageCode.SEMINAR_PUSH_CHAT {
+            getSeminarChat(message: message)
+        } else if message.getCode() == CoreMessage_TMessageCode.SEMINAR_PUSH_RAISE_HAND {
+            getSeminarRaiseHand(message: message)
+        } else if message.getCode() == CoreMessage_TMessageCode.SEMINAR_FACE_DETECTION {
+            
         } else if message.getCode() == CoreMessage_TMessageCode.SCREEN_SHARING {
             incomingScreenSharing(message: message, state: 1)
         } else if message.getCode() == CoreMessage_TMessageCode.SCREEN_SHARING_STOP {
@@ -1393,6 +1403,57 @@ class IncomingThread {
         }
         ack(message: message)
     }
+    private func joinSeminar(message: TMessage) -> Void {
+        let broadcaster = message.getBody(key: CoreMessage_TMessageKey.L_PIN,default_value: "")
+        let f_pin = message.getBody(key: CoreMessage_TMessageKey.F_PIN,default_value: "")
+        let thumb_id = message.getBody(key: CoreMessage_TMessageKey.THUMB_ID,default_value: "")
+        let name = message.getBody(key: CoreMessage_TMessageKey.NAME,default_value: "")
+        let quantity = message.getBody(key: CoreMessage_TMessageKey.QUANTITY,default_value: "")
+        let data = broadcaster+","+f_pin+","+thumb_id+","+name+","+quantity
+        print(data)
+        if let delegate = Nexilis.shared.seminarDelegate {
+            delegate.onJoinSeminar(state: 98, message: data)
+        }
+        ack(message: message)
+    }
+    private func leftSeminar(message: TMessage) -> Void {
+        let broadcaster = message.getBody(key: CoreMessage_TMessageKey.L_PIN,default_value: "")
+        let f_pin = message.getBody(key: CoreMessage_TMessageKey.F_PIN,default_value: "")
+        let thumb_id = message.getBody(key: CoreMessage_TMessageKey.THUMB_ID,default_value: "")
+        let name = message.getBody(key: CoreMessage_TMessageKey.NAME,default_value: "")
+        let quantity = message.getBody(key: CoreMessage_TMessageKey.QUANTITY,default_value: "")
+        let data = broadcaster+","+f_pin+","+thumb_id+","+name+","+quantity
+        print(data)
+        if let delegate = Nexilis.shared.seminarDelegate {
+            delegate.onJoinSeminar(state: 97, message: data)
+        }
+        ack(message: message)
+    }
+    private func getSeminarRaiseHand(message: TMessage) -> Void {
+        let l_pin = message.getBody(key: CoreMessage_TMessageKey.L_PIN,default_value: "")
+        let f_pin = message.getBody(key: CoreMessage_TMessageKey.F_PIN,default_value: "")
+        let status = message.getBody(key: CoreMessage_TMessageKey.STATUS,default_value: "")
+        let data = l_pin+","+f_pin+","+status
+        print(data)
+        if let delegate = Nexilis.shared.seminarDelegate {
+            delegate.onJoinSeminar(state: 96, message: data)
+        }
+        ack(message: message)
+    }
+    private func getSeminarChat(message: TMessage) -> Void {
+
+        let l_pin = message.getBody(key: CoreMessage_TMessageKey.L_PIN,default_value: "")
+        let f_pin = message.getBody(key: CoreMessage_TMessageKey.F_PIN,default_value: "")
+        let thumb_id = message.getBody(key: CoreMessage_TMessageKey.THUMB_ID,default_value: "")
+        let name = message.getBody(key: CoreMessage_TMessageKey.NAME,default_value: "")
+        let messages = message.getBody(key: CoreMessage_TMessageKey.MESSAGE_TEXT,default_value: "")
+        let data = l_pin+","+f_pin+","+thumb_id+","+name+","+messages
+        print(data)
+        if let delegate = Nexilis.shared.seminarDelegate {
+            delegate.onJoinSeminar(state: 95, message: data)
+        }
+        ack(message: message)
+    }
     private func incomingScreenSharing(message: TMessage, state: Int) -> Void {
         let f_pin = message.getBody(key: CoreMessage_TMessageKey.F_PIN,default_value: "")
         let l_pin = message.getBody(key: CoreMessage_TMessageKey.L_PIN,default_value: "")

+ 94 - 1
appbuilder-ios/NexilisLite/NexilisLite/Source/Nexilis.swift

@@ -1270,6 +1270,8 @@ public class Nexilis: NSObject {
     
     weak open var streamingDelagate: LiveStreamingDelegate?
     
+    weak open var seminarDelegate: SeminarDelegate?
+    
     weak open var personInfoDelegate: PersonInfoDelegate?
     
     weak open var screenSharingDelegate: ScreenSharingDelegate?
@@ -1322,6 +1324,11 @@ public protocol LiveStreamingDelegate: NSObjectProtocol {
     func onJoinLS(state: Int, message: String)
 }
 
+public protocol SeminarDelegate: NSObjectProtocol {
+    func onStartSeminar(state: Int, message: String)
+    func onJoinSeminar(state: Int, message: String)
+}
+
 public protocol VideoCallDelegate: NSObjectProtocol {
     func onInitiateVideoCall(destination:String,state: Int, message: String)
     func onAcceptVideoCall(originator:String,state: Int, message: String)
@@ -1961,7 +1968,31 @@ extension Nexilis: MessageDelegate {
                             }))
                             nc.present(alert, animated: true, completion: nil)
 //                                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "isRunningStreaming"), object: nil, userInfo: dataMessage)
-                        } else {
+                        } else if nc.visibleViewController is SeminarViewController {
+                            let vc = nc.visibleViewController as! SeminarViewController
+                            var alert = UIAlertController(title: "", message: "Are you sure you want to end Seminar, and open notification?".localized(), preferredStyle: .alert)
+                            if !vc.isLive {
+                                alert = UIAlertController(title: "", message: "Are you sure you want to leave Seminar, and open notification?".localized(), preferredStyle: .alert)
+                            }
+                            alert.addAction(UIAlertAction(title: "No".localized(), style: UIAlertAction.Style.default, handler: { _ in
+                                DispatchQueue.global().async {
+                                    _ = Nexilis.write(message: CoreMessage_TMessageBank.timeOutRequestCallCenter(channel: message.getBody(key: CoreMessage_TMessageKey.CHANNEL), l_pin: message.getBody(key: CoreMessage_TMessageKey.L_PIN)))
+                                }
+                                Nexilis.onGoingPushCC.removeAll()
+                                alert.dismiss(animated: true, completion: nil)
+                            }))
+                            alert.addAction(UIAlertAction(title: "Yes".localized(), style: UIAlertAction.Style.default, handler: { _ in
+                                DispatchQueue.global().async {
+                                    API.terminateBC(sBroadcasterID: vc.isLive ? nil : vc.data)
+                                    vc.sendLeft()
+                                }
+                                vc.dismiss(animated: true, completion: {
+                                    acceptCC()
+                                })
+                            }))
+                            nc.present(alert, animated: true, completion: nil)
+//                                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "isRunningStreaming"), object: nil, userInfo: dataMessage)
+                        }  else {
                             acceptCC()
                         }
                     } else {
@@ -2297,6 +2328,32 @@ extension Nexilis: MessageDelegate {
                                 })
                             }))
                             nc.present(alert, animated: true, completion: nil)
+                        } else if nc.visibleViewController is SeminarViewController {
+                            let vc = nc.visibleViewController as! SeminarViewController
+                            var alert = UIAlertController(title: "", message: "Are you sure you want to end Seminar, and open notification?".localized(), preferredStyle: .alert)
+                            if !vc.isLive {
+                                alert = UIAlertController(title: "", message: "Are you sure you want to leave Seminar, and open notification?".localized(), preferredStyle: .alert)
+                            }
+                            alert.addAction(UIAlertAction(title: "No".localized(), style: UIAlertAction.Style.default, handler: { _ in
+                                DispatchQueue.global().async {
+                                    if let result = Nexilis.writeSync(message: CoreMessage_TMessageBank.acceptCCRoomInvite(l_pin: message.getPIN(), type: 0, ticket_id: message.getBody(key: CoreMessage_TMessageKey.CALL_CENTER_ID))) {
+                                        if result.isOk() {
+                                            return
+                                        }
+                                    }
+                                }
+                                alert.dismiss(animated: true, completion: nil)
+                            }))
+                            alert.addAction(UIAlertAction(title: "Yes".localized(), style: UIAlertAction.Style.default, handler: { _ in
+                                DispatchQueue.global().async {
+                                    API.terminateBC(sBroadcasterID: vc.isLive ? nil : vc.data)
+                                    vc.sendLeft()
+                                }
+                                vc.dismiss(animated: true, completion: {
+                                    acceptCC()
+                                })
+                            }))
+                            nc.present(alert, animated: true, completion: nil)
                         } else {
                             acceptCC()
                         }
@@ -2841,6 +2898,24 @@ extension Nexilis: MessageDelegate {
                                         })
                                     }))
                                     nc.present(alert, animated: true, completion: nil)
+//                                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "isRunningStreaming"), object: nil, userInfo: dataMessage)
+                                } else if nc.visibleViewController is SeminarViewController {
+                                    let vc = nc.visibleViewController as! SeminarViewController
+                                    var alert = UIAlertController(title: "", message: "Are you sure you want to end Seminar, and open notification?".localized(), preferredStyle: .alert)
+                                    if !vc.isLive {
+                                        alert = UIAlertController(title: "", message: "Are you sure you want to leave Seminar, and open notification?".localized(), preferredStyle: .alert)
+                                    }
+                                    alert.addAction(UIAlertAction(title: "No".localized(), style: UIAlertAction.Style.default, handler: nil))
+                                    alert.addAction(UIAlertAction(title: "Yes".localized(), style: UIAlertAction.Style.default, handler: { _ in
+                                        DispatchQueue.global().async {
+                                            API.terminateBC(sBroadcasterID: vc.isLive ? nil : vc.data)
+                                            vc.sendLeft()
+                                        }
+                                        vc.dismiss(animated: true, completion: {
+                                            openEditor()
+                                        })
+                                    }))
+                                    nc.present(alert, animated: true, completion: nil)
 //                                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "isRunningStreaming"), object: nil, userInfo: dataMessage)
                                 } else if nc.visibleViewController is EditorPersonal {
                                     let vc = nc.visibleViewController as! EditorPersonal
@@ -2916,6 +2991,24 @@ extension Nexilis: MessageDelegate {
                                         })
                                     }))
                                     nc.present(alert, animated: true, completion: nil)
+//                                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "isRunningStreaming"), object: nil, userInfo: dataMessage)
+                                } else if nc.visibleViewController is SeminarViewController {
+                                    let vc = nc.visibleViewController as! SeminarViewController
+                                    var alert = UIAlertController(title: "", message: "Are you sure you want to end Seminar, and open notification?".localized(), preferredStyle: .alert)
+                                    if !vc.isLive {
+                                        alert = UIAlertController(title: "", message: "Are you sure you want to leave Seminar, and open notification?".localized(), preferredStyle: .alert)
+                                    }
+                                    alert.addAction(UIAlertAction(title: "No".localized(), style: UIAlertAction.Style.default, handler: nil))
+                                    alert.addAction(UIAlertAction(title: "Yes".localized(), style: UIAlertAction.Style.default, handler: { _ in
+                                        DispatchQueue.global().async {
+                                            API.terminateBC(sBroadcasterID: vc.isLive ? nil : vc.data)
+                                            vc.sendLeft()
+                                        }
+                                        vc.dismiss(animated: true, completion: {
+                                            openEditor()
+                                        })
+                                    }))
+                                    nc.present(alert, animated: true, completion: nil)
 //                                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "isRunningStreaming"), object: nil, userInfo: dataMessage)
                                 } else if nc.visibleViewController is EditorGroup {
                                     let vc = nc.visibleViewController as! EditorGroup

+ 14 - 4
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Chat/EditorGroup.swift

@@ -2875,16 +2875,26 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
         }
         let message = dataMessages[indexPath.row]
         if let attachmentFlag = message["attachment_flag"], let attachmentFlag = attachmentFlag as? String {
-            if attachmentFlag == "27" {
-                let streamingController = QmeraCreateStreamingViewController()
-                streamingController.isJoin = true
+            if attachmentFlag == "27" || attachmentFlag == "26" {
+                let streamingController = (attachmentFlag == "27") ? QmeraCreateStreamingViewController() : CreateSeminarViewController()
+                switch(attachmentFlag){
+                    case "27":
+                        (streamingController as! QmeraCreateStreamingViewController).isJoin = true
+                    default:
+                        (streamingController as! CreateSeminarViewController).isJoin = true
+                }
                 if let messageText = message["message_text"],
                    let messageText = messageText as? String,
                    var json = try! JSONSerialization.jsonObject(with: messageText.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
                     if json["blog"] == nil {
                         json["blog"] = message["blog_id"] ?? nil
                     }
-                    streamingController.data = json
+                    switch(attachmentFlag){
+                        case "27":
+                            (streamingController as! QmeraCreateStreamingViewController).data = json
+                        default:
+                            (streamingController as! CreateSeminarViewController).data = json
+                    }
                 }
                 let streamingNav = UINavigationController(rootViewController: streamingController)
                 streamingNav.modalPresentationStyle = .custom

+ 14 - 4
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift

@@ -3899,17 +3899,27 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
         }
         let message = dataMessages[indexPath.row]
         if let attachmentFlag = message["attachment_flag"], let attachmentFlag = attachmentFlag as? String {
-            if attachmentFlag == "27" {
-                let streamingController = QmeraCreateStreamingViewController()
+            if attachmentFlag == "27" || attachmentFlag == "26" {
+                let streamingController = (attachmentFlag == "27") ? QmeraCreateStreamingViewController() : CreateSeminarViewController()
                 if let messageText = message["message_text"],
                    let messageText = messageText as? String,
                    var json = try! JSONSerialization.jsonObject(with: messageText.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
                         if json["blog"] == nil {
                             json["blog"] = message["blog_id"] ?? nil
                         }
-                        streamingController.data = json
+                        switch(attachmentFlag){
+                            case "27":
+                                (streamingController as! QmeraCreateStreamingViewController).data = json
+                            default:
+                                (streamingController as! CreateSeminarViewController).data = json
+                        }
                         if json["by"] as? String != UserDefaults.standard.string(forKey: "me") as String? {
-                            streamingController.isJoin = true
+                            switch(attachmentFlag){
+                                case "27":
+                                    (streamingController as! QmeraCreateStreamingViewController).isJoin = true
+                                default:
+                                    (streamingController as! CreateSeminarViewController).isJoin = true
+                            }
                         }
                     }
                 let streamingNav = UINavigationController(rootViewController: streamingController)

+ 586 - 0
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Streaming/CreateSeminarViewController.swift

@@ -0,0 +1,586 @@
+//
+//  CreateSeminarViewController.swift
+//  NexilisLite
+//
+//  Created by Maronakins on 31/05/23.
+//
+
+import UIKit
+import NotificationBannerSwift
+import AVFoundation
+
+public class CreateSeminarViewController: UITableViewController {
+    
+    var isJoin = false
+    
+    var data: [String: Any] = [:]
+    
+    private enum Section {
+        case chooser
+        case title
+        case description
+        case users
+        case groups
+    }
+    
+    private var sections: [Section] = [
+        .chooser,
+        .title,
+        .description
+    ]
+    
+    private var chooser: [Chooser] = [
+        Chooser(title: "Target Audience".localized(), value: AudienceViewController().data.first),
+        Chooser(title: "Promotion Type".localized(), value: TypeViewController().data.first)
+    ]
+    
+    private var users: [User] = [] {
+        didSet {
+            DispatchQueue.main.async { [weak self] in
+                self?.tableView.reloadData()
+            }
+        }
+    }
+    
+    private var groups: [Group] = [] {
+        didSet {
+            DispatchQueue.main.async { [weak self] in
+                self?.tableView.reloadData()
+            }
+        }
+    }
+    
+    private let cellIdentifier = "reuseIdentifier"
+    
+    lazy var table: UITableView = {
+        let tableView = UITableView(frame: CGRect.zero, style: .grouped)
+        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellIdentifier)
+        return tableView
+    }()
+    
+    lazy var titleView: UITextField = {
+        let textField = UITextField()
+        textField.borderStyle = .none
+        textField.placeholder = "Title".localized()
+        return textField
+    }()
+    
+    lazy var descriptionView: UITextView = {
+        let textView = UITextView()
+        textView.text = "Description".localized()
+        textView.textColor = UIColor.lightGray
+        return textView
+    }()
+    
+    deinit {
+        print(#function, ">>>> TADAA1")
+        NotificationCenter.default.removeObserver(self)
+        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "refreshView"), object: nil, userInfo: nil)
+    }
+    
+    public override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        title = "Seminar".localized()
+        
+        navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(didTapCancel(sender:)))
+        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Start".localized(), style: .plain, target: self, action: #selector(didTapRight(sender:)))
+        
+        descriptionView.delegate = self
+        
+        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
+        tapGesture.cancelsTouchesInView = false
+        table.addGestureRecognizer(tapGesture)
+        
+        if isJoin {
+            navigationItem.rightBarButtonItem?.title = "Join".localized()
+            titleView.isEnabled = false
+            descriptionView.isEditable = false
+            if let a = data["type"] as? String {
+                chooser[0].value = AudienceViewController().data[getTypeIndex(value: a)]
+            }
+            if let b = data["broadcast_type"] as? String {
+                chooser[1].value = TypeViewController().data[getBroadcastIndex(value: b)]
+            }
+            if let c = data["title"] as? String {
+                titleView.text = c
+            }
+            if let d = data["description"] as? String {
+                descriptionView.text = d
+            }
+        } else if !isJoin && !data.isEmpty {
+            navigationItem.rightBarButtonItem?.title = "Start".localized()
+            titleView.isEnabled = false
+            descriptionView.isEditable = false
+            if let a = data["type"] as? String {
+                chooser[0].value = AudienceViewController().data[getTypeIndex(value: a)]
+            }
+            if let b = data["broadcast_type"] as? String {
+                chooser[1].value = TypeViewController().data[getBroadcastIndex(value: b)]
+            }
+            if let c = data["title"] as? String {
+                titleView.text = c
+            }
+            if let d = data["description"] as? String {
+                descriptionView.text = d
+            }
+        }
+        
+        tableView = table
+    }
+    
+    @objc func dismissKeyboard() {
+        titleView.resignFirstResponder()
+        descriptionView.resignFirstResponder()
+    }
+    
+    @objc func didTapCancel(sender: AnyObject) {
+        navigationController?.dismiss(animated: true, completion: nil)
+    }
+    
+    @objc func didTapRight(sender: Any?) {
+        let controller = SeminarViewController()
+        controller.isLive = !isJoin
+        if isJoin {
+            guard let by = data["by"] as? String else {
+                return
+            }
+            let dataBlog = data["blog"] as? String
+            if dataBlog != nil {
+                if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getIsInitiatorJoin(p_broadcaster: by, p_category: "3", blog_id: dataBlog!)) {
+                    if response.getBody(key: CoreMessage_TMessageKey.ERRCOD) != "00" {
+                        let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
+                        imageView.tintColor = .white
+                        let banner = FloatingNotificationBanner(title: "Seminar session hasn\'t started yet".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                        banner.show()
+                        return
+                    }
+                } else {
+                    let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
+                    imageView.tintColor = .white
+                    let banner = FloatingNotificationBanner(title: "No Network. Please try again.".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                    banner.show()
+                    return
+                }
+            }
+            controller.data = by
+            controller.streamingData = data
+        } else {
+            let goAudioCall = Nexilis.checkMicPermission()
+            if !goAudioCall {
+                let alert = UIAlertController(title: "Attention!".localized(), message: "Please allow microphone permission in your settings".localized(), preferredStyle: .alert)
+                alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: { _ in
+                    if let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) {
+                        UIApplication.shared.open(url, options: [:], completionHandler: nil)
+                    }
+                }))
+                if UIApplication.shared.visibleViewController?.navigationController != nil {
+                    UIApplication.shared.visibleViewController?.navigationController?.present(alert, animated: true, completion: nil)
+                } else {
+                    UIApplication.shared.visibleViewController?.present(alert, animated: true, completion: nil)
+                }
+                return
+            }
+            var permissionCheck = -1
+            if AVCaptureDevice.authorizationStatus(for: .video) ==  .authorized {
+                permissionCheck = 1
+            } else if AVCaptureDevice.authorizationStatus(for: .video) ==  .denied {
+                permissionCheck = 0
+            } else {
+                AVCaptureDevice.requestAccess(for: .video, completionHandler: { (granted: Bool) -> Void in
+                    if granted == true {
+                        permissionCheck = 1
+                    } else {
+                        permissionCheck = 0
+                    }
+                })
+            }
+            
+            while permissionCheck == -1 {
+                sleep(1)
+            }
+            
+            if permissionCheck == 0 {
+                let alert = UIAlertController(title: "Attention!".localized(), message: "Please allow camera permission in your settings".localized(), preferredStyle: .alert)
+                alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: { _ in
+                    if let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) {
+                        UIApplication.shared.open(url, options: [:], completionHandler: nil)
+                    }
+                }))
+                if UIApplication.shared.visibleViewController?.navigationController != nil {
+                    UIApplication.shared.visibleViewController?.navigationController?.present(alert, animated: true, completion: nil)
+                } else {
+                    UIApplication.shared.visibleViewController?.present(alert, animated: true, completion: nil)
+                }
+                return
+            }
+            var data: [String: Any] = [:]
+            if !isJoin && !self.data.isEmpty {
+                data = self.data
+            } else if self.data.isEmpty {
+                guard let streamingTitle = titleView.text else {
+                    return
+                }
+                guard let streamingDesc = descriptionView.text else {
+                    return
+                }
+                
+                let id = "LST\(Date().currentTimeMillis().toHex())"
+                
+                data["title"] = streamingTitle
+                data["description"] = streamingDesc
+                data["by"] = UserDefaults.standard.string(forKey: "me") ?? ""
+                data["time"] = Date().currentTimeMillis()
+                data["blog"] = id
+                
+                if streamingTitle.trimmingCharacters(in: .whitespaces).isEmpty {
+                    let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
+                    imageView.tintColor = .white
+                    let banner = FloatingNotificationBanner(title: "Seminar title can't be empty".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                    banner.show()
+                    return
+                } else if streamingDesc.trimmingCharacters(in: .whitespaces).isEmpty {
+                    let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
+                    imageView.tintColor = .white
+                    let banner = FloatingNotificationBanner(title: "Seminar description can't be empty".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                    banner.show()
+                    return
+                }
+                
+                var type: String = "0" // Friend
+                let groups: [String] = groups.map{ $0.id }
+                let members: [String] = users.map{ $0.pin }
+                switch chooser[0].id {
+                case 0:
+                    type = "7"
+                case 1:
+                    type = "8"
+                case 2:
+                    type = "3"
+                case 3:
+                    type = "6"
+                case 4:
+                    type = "5"
+                default:
+                    type = "0"
+                }
+                
+                data["groups"] = groups
+                data["members"] = members
+                data["type"] = type
+                
+                var notif: String = "0"
+                switch chooser[1].id {
+                case 0:
+                    notif = "1"
+                default:
+                    notif = "2"
+                }
+                
+                data["broadcast_type"] = notif
+                guard let json = String(data: try! JSONSerialization.data(withJSONObject: data, options: []), encoding: String.Encoding.utf8) else {
+                    return
+                }
+                
+                if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.createSeminar(title: "1~\(data["title"] ?? "")", type: data["type"] as! String, category: "4", notifType: data["broadcast_type"] as! String, blogId: data["blog"] as! String, data: json)) {
+                    if response.getBody(key: CoreMessage_TMessageKey.ERRCOD) != "00" {
+                        let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
+                        imageView.tintColor = .white
+                        let banner = FloatingNotificationBanner(title: "Server Busy. Please try again.".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                        banner.show()
+                        return
+                    }
+                } else {
+                    let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
+                    imageView.tintColor = .white
+                    let banner = FloatingNotificationBanner(title: "No Network. Please try again.".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                    banner.show()
+                    return
+                }
+                
+                Nexilis.saveMessageBot(textMessage: json, blog_id: data["blog"] as? String ?? "", attachment_type: "26")
+                NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
+            }
+            controller.data = UserDefaults.standard.string(forKey: "me")!
+            controller.streamingData = data
+        }
+        navigationController?.show(controller, sender: nil)
+    }
+    
+    private func getTypeIndex(value: String) -> Int {
+        var type = 0
+        switch value {
+        case "7":
+            type = 0
+        case "8":
+            type = 1
+        case "3":
+            type = 2
+        case "6":
+            type = 3
+        case "5":
+            type = 4
+        default:
+            type = 0
+        }
+        return type
+    }
+    
+    private func getBroadcastIndex(value: String) -> Int {
+        var notif = 0
+        switch value {
+        case "1":
+            notif = 0
+        default:
+            notif = 1
+        }
+        return notif
+    }
+    
+    // MARK: - Table view data source
+    
+    public override func numberOfSections(in tableView: UITableView) -> Int {
+        return sections.count
+    }
+    
+    public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        switch sections[section] {
+        case .chooser:
+            return 2
+        case .users:
+            return users.count + 1
+        case .groups:
+            return groups.count + 1
+        default:
+            return 1
+        }
+    }
+    
+    public override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        switch sections[indexPath.section] {
+        case .description:
+            return 100
+        default:
+            return 44
+        }
+    }
+    
+    public override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
+        if isJoin, sections[indexPath.section] == .chooser {
+            return nil
+        }
+        return indexPath
+    }
+    
+    public override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        tableView.deselectRow(at: indexPath, animated: true)
+        switch sections[indexPath.section] {
+        case .chooser:
+            if indexPath.row == 0 {
+                if isJoin || (!isJoin && !data.isEmpty){
+                    return
+                }
+                let chooser = chooser[indexPath.row]
+                let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "audienceView") as AudienceViewController
+                controller.selected = chooser.value
+                controller.isDismiss = { [weak self] index in
+                    chooser.id = index
+                    chooser.value = AudienceViewController().data[index]
+                    guard let sec = self?.sections else {
+                        return
+                    }
+                    if sec.count > 4 {
+                        self?.sections.removeLast()
+                    }
+                    if chooser.value == "Group".localized() {
+                        self?.sections.append(.groups)
+                        if self?.users.count != 0 {
+                            self?.users.removeAll()
+                        }
+                    } else if chooser.value == "User".localized() {
+                        self?.sections.append(.users)
+                        if self?.groups.count != 0{
+                            self?.groups.removeAll()
+                        }
+                    } else {
+                        if self?.users.count != 0 {
+                            self?.users.removeAll()
+                        } else if self?.groups.count != 0{
+                            self?.groups.removeAll()
+                        }
+                    }
+                    DispatchQueue.main.async {
+                        tableView.reloadData()
+                    }
+                }
+                navigationController?.show(controller, sender: nil)
+            } else if indexPath.row == 1 {
+                if isJoin || (!isJoin && !data.isEmpty){
+                    return
+                }
+                let chooser = chooser[indexPath.row]
+                let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "typeView") as TypeViewController
+                controller.selected = chooser.value
+                controller.isDismiss = { index in
+                    chooser.id = index
+                    chooser.value = TypeViewController().data[index]
+                    DispatchQueue.main.async {
+                        tableView.reloadRows(at: [indexPath], with: .automatic)
+                    }
+                }
+                navigationController?.show(controller, sender: nil)
+            }
+        case .users:
+            if indexPath.row == 0 {
+                let controller = QmeraUserChooserViewController()
+                controller.ignored.append(contentsOf: users)
+                controller.isDismiss = { users in
+                    self.users.append(contentsOf: users)
+                }
+                navigationController?.show(controller, sender: nil)
+            }
+        case .groups:
+            if indexPath.row == 0 {
+                let controller = QmeraGroupStreamingViewController()
+                controller.ignored.append(contentsOf: groups)
+                controller.isDismiss = { groups in
+                    self.groups.append(contentsOf: groups)
+                }
+                navigationController?.show(controller, sender: nil)
+            }
+        default:
+            return
+        }
+    }
+    
+    public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
+        cell.contentView.subviews.forEach{ NSLayoutConstraint.deactivate($0.constraints); $0.removeFromSuperview() }
+        cell.contentConfiguration = nil
+        cell.accessoryType = .none
+        cell.selectionStyle = .none
+        switch sections[indexPath.section] {
+        case .chooser:
+            var content = cell.defaultContentConfiguration()
+            let data = chooser[indexPath.row]
+            content.text = data.title
+            content.secondaryText = data.value
+            content.textProperties.font = UIFont.systemFont(ofSize: 14)
+            content.secondaryTextProperties.color = .systemGray
+            content.secondaryTextProperties.font = UIFont.systemFont(ofSize: 14)
+            content.prefersSideBySideTextAndSecondaryText = true
+            cell.contentConfiguration = content
+            cell.accessoryType = .disclosureIndicator
+            cell.selectionStyle = .default
+        case .title:
+            cell.contentView.addSubview(titleView)
+            titleView.anchor(top: cell.topAnchor, left: cell.leftAnchor, bottom: cell.bottomAnchor, right: cell.rightAnchor, paddingLeft: 20, paddingRight: 20)
+        case .description:
+            let stack = UIStackView()
+            stack.axis = .vertical
+            stack.distribution = .fill
+            cell.contentView.addSubview(stack)
+            stack.anchor(top: cell.topAnchor, left: cell.leftAnchor, bottom: cell.bottomAnchor, right: cell.rightAnchor, paddingTop: 8, paddingLeft: 20, paddingBottom: 8, paddingRight: 20)
+            stack.addArrangedSubview(descriptionView)
+        case .users:
+            var content = cell.defaultContentConfiguration()
+            if indexPath.row == 0 {
+                content.image = UIImage(systemName: "plus.circle.fill")
+                content.imageProperties.tintColor = .mainColor
+                content.text = "Add user".localized()
+                content.textProperties.font = UIFont.systemFont(ofSize: 14)
+                cell.accessoryType = .disclosureIndicator
+                cell.selectionStyle = .default
+            } else {
+                let data = users[indexPath.row - 1]
+                getImage(name: data.thumb, placeholderImage: UIImage(named: "Profile---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
+                    content.image = image
+                }
+                content.text = data.fullName
+                content.textProperties.font = UIFont.systemFont(ofSize: 14)
+            }
+            cell.contentConfiguration = content
+        case .groups:
+            var content = cell.defaultContentConfiguration()
+            if indexPath.row == 0 {
+                content.image = UIImage(systemName: "plus.circle.fill")
+                content.imageProperties.tintColor = .mainColor
+                content.text = "Add group".localized()
+                content.textProperties.font = UIFont.systemFont(ofSize: 14)
+                cell.accessoryType = .disclosureIndicator
+                cell.selectionStyle = .default
+            } else {
+                let data = groups[indexPath.row - 1]
+                getImage(name: data.profile, placeholderImage: UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
+                    content.image = image
+                }
+                content.text = data.name
+                content.textProperties.font = UIFont.systemFont(ofSize: 14)
+            }
+            cell.contentConfiguration = content
+        }
+        return cell
+    }
+    
+    public override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
+        switch sections[indexPath.section] {
+        case .users:
+            if (editingStyle == .delete) {
+                users.remove(at: indexPath.row - 1)
+                tableView.deleteRows(at: [indexPath], with: .automatic)
+            }
+        case .groups:
+            if (editingStyle == .delete) {
+                groups.remove(at: indexPath.row - 1)
+                tableView.deleteRows(at: [indexPath], with: .automatic)
+            }
+        default:
+            return
+        }
+    }
+    
+    public override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
+        if indexPath.section < 4 {
+            return .none
+        } else if indexPath.section == 4 && indexPath.row == 0 {
+            return .none
+        }
+
+        return .delete
+    }
+    
+    @IBAction func result(unwind segue: UIStoryboardSegue) {
+        
+    }
+    
+}
+
+private class Chooser {
+    
+    let title: String
+    var id: Int = 0
+    var value: String?
+    
+    init(title: String, id: Int = 0, value: String?) {
+        self.title = title
+        self.id = id
+        self.value = value
+    }
+    
+}
+
+extension CreateSeminarViewController: UITextViewDelegate {
+    public func textViewDidBeginEditing(_ textView: UITextView) {
+        if textView.textColor == UIColor.lightGray {
+            textView.text = nil
+            textView.textColor = UIColor.black
+        }
+    }
+    
+    public func textViewDidEndEditing(_ textView: UITextView) {
+        if textView.text.isEmpty {
+            textView.text = "Description".localized()
+            textView.textColor = UIColor.lightGray
+        }
+    }
+}

+ 622 - 0
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Streaming/SeminarViewController.swift

@@ -0,0 +1,622 @@
+//
+//  SeminarViewController.swift
+//  NexilisLite
+//
+//  Created by Maronakins on 01/06/23.
+//
+
+import UIKit
+import nuSDKService
+
+class SeminarViewController: UIViewController {
+    
+    var data: String = ""
+    
+    var streamingData: [String: Any] = [:]
+    
+    var isLive: Bool = false
+    
+    private var textViewHeightConstraint: NSLayoutConstraint!
+    
+    private let keyboardLayoutGuide = UILayoutGuide()
+    
+    private var keyboardTopAnchorConstraint: NSLayoutConstraint!
+    
+    private let cellIdentifier = "reuseCell"
+    
+    private var frontCamera = true
+    
+    private var heightTableView: NSLayoutConstraint?
+    
+    private var chats: [SeminarChat] = [] {
+        didSet {
+            DispatchQueue.main.async {
+                self.tableView.reloadData()
+                if self.tableView.contentSize.height <= 167.0 {
+                    self.heightTableView?.constant = self.tableView.contentSize.height
+                } else {
+                    self.heightTableView?.constant = 167.0
+                }
+                self.tableView.scrollToBottom()
+            }
+        }
+    }
+    
+    private var isOversized: Bool = false {
+        didSet {
+            guard oldValue != isOversized else {
+                return
+            }
+            if isOversized {
+                textViewHeightConstraint = textView.heightAnchor.constraint(equalToConstant: textView.frame.height)
+                NSLayoutConstraint.activate([textViewHeightConstraint])
+            } else {
+                NSLayoutConstraint.deactivate([textViewHeightConstraint])
+            }
+            textView.isScrollEnabled = isOversized
+            textView.setNeedsUpdateConstraints()
+        }
+    }
+    
+    lazy var status: UILabel = {
+        let label = UILabel()
+        label.font = UIFont.systemFont(ofSize: 14)
+        label.textColor = .mainColor
+        label.textAlignment = .center
+        label.text = streamingData["title"] as? String
+        return label
+    }()
+    
+    lazy var tvCameraPreviewB: UIImageView = {
+        let imageView = UIImageView()
+        imageView.contentMode = .scaleAspectFit
+        return imageView
+    }()
+    
+    lazy var tvCameraPreviewS: UIImageView = {
+        let imageView = UIImageView()
+        imageView.contentMode = .scaleAspectFit
+        return imageView
+    }()
+    
+    lazy var ivRemoteViewS: UIImageView = {
+        let imageView = UIImageView()
+        imageView.contentMode = .scaleAspectFit
+        return imageView
+    }()
+    
+    lazy var ivRemoteViewM: UIImageView = {
+        let imageView = UIImageView()
+        imageView.contentMode = .scaleAspectFit
+        return imageView
+    }()
+    
+    lazy var tableView: UITableView = {
+        let tableView = UITableView()
+        tableView.dataSource = self
+        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellIdentifier)
+        tableView.tableFooterView = UIView()
+        tableView.separatorStyle = .none
+        tableView.separatorInset = .zero
+        tableView.tintColor = .clear
+        tableView.backgroundColor = .clear
+        tableView.showsVerticalScrollIndicator = false
+        return tableView
+    }()
+    
+    lazy var textView: UITextView = {
+        let textView = UITextView()
+        textView.delegate = self
+        textView.layer.borderWidth = 1.0
+        textView.layer.borderColor = UIColor.white.cgColor
+        textView.layer.backgroundColor = UIColor.clear.cgColor
+        textView.isScrollEnabled = false
+        textView.font = UIFont.systemFont(ofSize: 14)
+        textView.text = "Send Comment".localized()
+        textView.textColor = UIColor.secondaryColor
+        return textView
+    }()
+    
+//    lazy var like: UIButton = {
+//        let button = UIButton()
+//        button.setImage(UIImage(systemName: "heart")?.withTintColor(.white, renderingMode: .alwaysOriginal), for: .normal)
+//        button.imageView?.contentMode = .scaleAspectFit
+//        button.contentVerticalAlignment = .fill
+//        button.contentHorizontalAlignment = .fill
+//        button.addTarget(self, action: #selector(liked(sender:)), for: .touchUpInside)
+//        return button
+//    }()
+//
+    lazy var send: UIButton = {
+        let button = UIButton()
+        button.setImage(UIImage(named: "Send-(White)", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), for: .normal)
+        button.imageView?.contentMode = .scaleAspectFit
+        button.contentVerticalAlignment = .fill
+        button.contentHorizontalAlignment = .fill
+        button.backgroundColor = .mainColor
+        button.addTarget(self, action: #selector(sent(sender:)), for: .touchUpInside)
+        return button
+    }()
+    
+    lazy var stack: UIView = {
+        let stack = UIView()
+        return stack
+    }()
+    
+    lazy var count: UILabel = {
+        let count = UILabel()
+        count.text = "0"
+        count.font = UIFont.systemFont(ofSize: 14)
+        count.textColor = .mainColor
+        return count
+    }()
+    
+    lazy var countViewer: UILabel = {
+        let count = UILabel()
+        count.text = "0"
+        count.font = UIFont.systemFont(ofSize: 14)
+        count.textColor = .mainColor
+        return count
+    }()
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        navigationController?.changeAppearance(clear: true)
+        
+        let buttonBack = UIButton()
+        buttonBack.frame = CGRect(x:0, y:0, width:30, height:30)
+        buttonBack.setImage(UIImage(systemName: "chevron.backward")?.withTintColor(.mainColor, renderingMode: .alwaysOriginal), for: .normal)
+        buttonBack.backgroundColor = .white.withAlphaComponent(0.2)
+        buttonBack.layer.cornerRadius = 15.0
+        buttonBack.addTarget(self, action: #selector(close(sender:)), for: .touchUpInside)
+        navigationItem.leftBarButtonItem = UIBarButtonItem(customView: buttonBack)
+        
+        let statusView = UIView()
+        view.backgroundColor = .clear
+        if isLive {
+            view.addSubview(tvCameraPreviewB)
+            view.addSubview(ivRemoteViewS)
+        }
+        else {
+            view.addSubview(tvCameraPreviewS)
+            view.addSubview(ivRemoteViewM)
+            view.addSubview(ivRemoteViewS)
+        }
+        view.addSubview(statusView)
+        view.addSubview(stack)
+        view.addLayoutGuide(keyboardLayoutGuide)
+        
+        if isLive {
+            addCountViewerView()
+        }
+        
+        keyboardTopAnchorConstraint = view.layoutMarginsGuide.bottomAnchor.constraint(equalTo: keyboardLayoutGuide.topAnchor, constant: 0)
+        keyboardTopAnchorConstraint.isActive = true
+        keyboardLayoutGuide.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor).isActive = true
+        
+        imageView.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor)
+        statusView.backgroundColor = .white.withAlphaComponent(0.2)
+        statusView.layer.cornerRadius = 8.0
+        statusView.layer.masksToBounds = true
+        statusView.addSubview(status)
+        status.anchor(left: statusView.leftAnchor, right: statusView.rightAnchor, paddingLeft: 10, paddingRight: 10, centerX: statusView.centerXAnchor, centerY: statusView.centerYAnchor)
+        statusView.anchor(top: view.topAnchor, left: view.leftAnchor, right: view.rightAnchor, paddingTop: 30, paddingLeft: 80, paddingRight: 80, centerX: view.centerXAnchor, height: 30, dynamicLeft: true, dynamicRight: true)
+        stack.anchor(left: view.leftAnchor, bottom: keyboardLayoutGuide.topAnchor, right: view.rightAnchor, paddingLeft: 20, paddingBottom: 30, paddingRight: 20, height: 200)
+        
+        stack.addSubview(tableView)
+        stack.addSubview(textView)
+        stack.addSubview(send)
+        
+        send.layer.cornerRadius = 16.5
+        send.layer.masksToBounds = true
+        
+        textView.anchor(left: stack.leftAnchor, bottom: stack.bottomAnchor, right: stack.rightAnchor)
+        send.anchor(bottom: textView.bottomAnchor, right: textView.rightAnchor, paddingBottom: 2, width: 33, height: 33)
+        tableView.anchor(left: stack.leftAnchor, bottom: textView.topAnchor, right: stack.rightAnchor)
+        heightTableView = tableView.heightAnchor.constraint(equalToConstant: 44.0)
+        heightTableView?.isActive = true
+        textView.layoutIfNeeded()
+        
+        textView.layer.cornerRadius = textView.frame.height / 2
+        textView.textContainerInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: textView.frame.height + 8)
+        
+        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(hideKeyboard)))
+        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
+        
+        Nexilis.shared.seminarDelegate = self
+        if isLive {
+            let buttonRotate = UIButton()
+            buttonRotate.frame = CGRect(x:0, y:0, width:30, height:30)
+            buttonRotate.setImage(UIImage(systemName: "arrow.triangle.2.circlepath.camera")?.withTintColor(.mainColor, renderingMode: .alwaysOriginal), for: .normal)
+            buttonRotate.backgroundColor = .white.withAlphaComponent(0.2)
+            buttonRotate.layer.cornerRadius = 15.0
+            buttonRotate.addTarget(self, action: #selector(camera(sender:)), for: .touchUpInside)
+            navigationItem.rightBarButtonItem = UIBarButtonItem(customView: buttonRotate)
+            
+            API.ibca(sTitle: data, nCamIdx: 1, nResIdx: 2, nVQuality: 4, tvCameraPreview: tvCameraPreviewB, ivRemoteS: ivRemoteViewS)
+        } else {
+            API.iabc(nCamIdx: 1, nResIdx: 2, nVQuality: 4, tvCameraPreview: tvCameraPreviewS, sBroadcasterID: data, ivRemoteM: ivRemoteViewM, ivRemoteS: ivRemoteViewS)
+        }
+    }
+    
+//    func addLikeView() {
+//        let viewLiked = UIView()
+//        view.addSubview(viewLiked)
+//        viewLiked.anchor(top: view.safeAreaLayoutGuide.topAnchor, right: view.rightAnchor, paddingRight: 20, height: 40)
+//        viewLiked.backgroundColor = .white.withAlphaComponent(0.2)
+//        viewLiked.layer.cornerRadius = 8.0
+//        viewLiked.layer.masksToBounds = true
+//
+//        let imageLiked = UIImageView()
+//        viewLiked.addSubview(imageLiked)
+//        imageLiked.anchor(left: viewLiked.leftAnchor, paddingLeft: 5.0, centerY: viewLiked.centerYAnchor)
+//        imageLiked.image = UIImage(systemName: "heart.fill")?.withTintColor(.red, renderingMode: .alwaysOriginal)
+//
+//        viewLiked.addSubview(count)
+//        count.anchor(left: imageLiked.rightAnchor, right:viewLiked.rightAnchor, paddingLeft: 5.0, paddingRight: 5.0, centerY: viewLiked.centerYAnchor)
+//
+//    }
+    
+    func addCountViewerView() {
+        let viewCountViewer = UIView()
+        view.addSubview(viewCountViewer)
+        viewCountViewer.anchor(top: view.safeAreaLayoutGuide.topAnchor, left: view.leftAnchor, paddingLeft: 20, height: 40)
+        viewCountViewer.backgroundColor = .white.withAlphaComponent(0.2)
+        viewCountViewer.layer.cornerRadius = 8.0
+        viewCountViewer.layer.masksToBounds = true
+        
+        let imageEye = UIImageView()
+        viewCountViewer.addSubview(imageEye)
+        imageEye.anchor(left: viewCountViewer.leftAnchor, paddingLeft: 5.0, centerY: viewCountViewer.centerYAnchor)
+        imageEye.image = UIImage(systemName: "eye.fill")?.withTintColor(.black, renderingMode: .alwaysOriginal)
+        
+        viewCountViewer.addSubview(countViewer)
+        countViewer.anchor(left: imageEye.rightAnchor, right:viewCountViewer.rightAnchor, paddingLeft: 5.0, paddingRight: 5.0, centerY: viewCountViewer.centerYAnchor)
+        
+    }
+    
+    override func viewWillDisappear(_ animated: Bool) {
+        navigationController?.changeAppearance(clear: false)
+    }
+    
+    @objc func close(sender: Any?) {
+        hideKeyboard()
+        var alert = UIAlertController(title: "", message: "Are you sure you want to end Live Streaming?".localized(), preferredStyle: .alert)
+        if !isLive {
+            alert = UIAlertController(title: "", message: "Are you sure you want to leave Live Streaming?".localized(), preferredStyle: .alert)
+        }
+        alert.addAction(UIAlertAction(title: "No".localized(), style: UIAlertAction.Style.default, handler: nil))
+        alert.addAction(UIAlertAction(title: "Yes".localized(), style: UIAlertAction.Style.default, handler: {[weak self] _ in
+            DispatchQueue.global().async {
+                API.terminateBC(sBroadcasterID: self?.isLive ?? true ? nil : self?.data)
+                self?.sendLeft()
+            }
+            self?.navigationController?.dismiss(animated: true, completion: nil)
+        }))
+        self.present(alert, animated: true, completion: nil)
+    }
+    
+    @objc func camera(sender: Any?) {
+        if frontCamera {
+            API.changeCameraParam(nCameraIdx: 0, nResolutionIndex: 2, nQuality: 4)
+            frontCamera = false
+        } else {
+            API.changeCameraParam(nCameraIdx: 1, nResolutionIndex: 2, nQuality: 4)
+            frontCamera = true
+        }
+    }
+    
+    @objc func sent(sender: Any?) {
+        if textView.textColor == UIColor.secondaryColor {
+            return
+        }
+        guard let text = textView.text, !text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
+            return
+        }
+        guard let me = User.getDataCanNil(pin: UserDefaults.standard.string(forKey: "me")) else {
+            return
+        }
+        chats.append(SeminarChat(name: "You".localized(), thumb: me.thumb, messageText: text.trimmingCharacters(in: .whitespacesAndNewlines)))
+        if textViewHeightConstraint != nil {
+            NSLayoutConstraint.deactivate([textViewHeightConstraint])
+        }
+        textView.isScrollEnabled = false
+        textView.setNeedsUpdateConstraints()
+        DispatchQueue.global().async {
+            self.sendChat(text: text.trimmingCharacters(in: .whitespacesAndNewlines))
+        }
+        textView.text = ""
+    }
+    
+    @objc func hideKeyboard() {
+        view.endEditing(true)
+    }
+    
+    var isShow: Bool = false
+    
+    @objc func keyboardWillShow(notification: Notification) {
+        if !isShow {
+            isShow = true
+            keyboard(notification: notification, show: true)
+        }
+    }
+    
+    @objc func keyboardWillHide(notification: Notification) {
+        isShow = false
+        keyboard(notification: notification, show: false)
+    }
+    
+    private func keyboard(notification: Notification, show: Bool) {
+        let keyboardFrameEnd = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue
+        let animationDuration = (notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue
+        let rawAnimationCurve = (notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber)?.uint32Value
+        guard let animDuration = animationDuration,
+              let keyboardFrame = keyboardFrameEnd,
+              let rawAnimCurve = rawAnimationCurve else {
+            return
+        }
+        keyboardTopAnchorConstraint.constant = show ? keyboardFrame.cgRectValue.height : 0
+        view.setNeedsLayout()
+        let rawAnimCurveAdjusted = UInt(rawAnimCurve << 16)
+        let animationCurve = UIView.AnimationOptions(rawValue: rawAnimCurveAdjusted)
+        UIView.animate(withDuration: animDuration, delay: 0.0, options: [.beginFromCurrentState, animationCurve], animations: {
+            self.view.layoutIfNeeded()
+        }, completion: nil)
+    }
+    
+    deinit {
+        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
+        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
+    }
+    
+    private func sendLive() {
+        guard let title = streamingData["title"] as? String else {
+            return
+        }
+        guard let type = streamingData["type"] as? String else {
+            return
+        }
+        guard let blog = streamingData["blog"] as? String else {
+            return
+        }
+        _ = Nexilis.write(message: CoreMessage_TMessageBank.getStartSeminarInvited(title: title, type: type, typeValue: "", category: "3", blog_id: blog))
+    }
+    
+    private func sendJoin() {
+        let id = Date().currentTimeMillis().toHex()
+        _ = Nexilis.write(message: CoreMessage_TMessageBank.joinSeminar(broadcast_id: data, request_id: id))
+    }
+    
+    public func sendLeft() {
+        let id = Date().currentTimeMillis().toHex()
+        _ = Nexilis.write(message: CoreMessage_TMessageBank.leftSeminar(broadcast_id: data, request_id: id))
+    }
+    
+    private func sendChat(text: String) {
+        _ = Nexilis.write(message: CoreMessage_TMessageBank.getSendSeminarChat(l_pin: data, message_text: text))
+    }
+    
+    private func raiseHand() {
+        guard let me = User.getDataCanNil(pin: UserDefaults.standard.string(forKey: "me")) else {
+            return
+        }
+        _ = Nexilis.write(message: CoreMessage_TMessageBank.getSeminarRaiseHand(p_pin: me, l_pin: data, status: "1"))
+    }
+    
+    private func cancelRaiseHand() {
+        guard let me = User.getDataCanNil(pin: UserDefaults.standard.string(forKey: "me")) else {
+            return
+        }
+        _ = Nexilis.write(message: CoreMessage_TMessageBank.getSeminarRaiseHand(p_pin: me, l_pin: data, status: "0"))
+    }
+    
+}
+
+extension SeminarViewController: UITextViewDelegate {
+    
+    func textViewDidChange(_ textView: UITextView) {
+        isOversized = textView.contentSize.height >= 100
+    }
+    
+    func textViewDidBeginEditing(_ textView: UITextView) {
+        if textView.textColor == UIColor.secondaryColor {
+            textView.text = nil
+            textView.textColor = .white
+        }
+    }
+    
+    func textViewDidEndEditing(_ textView: UITextView) {
+        if textView.text.isEmpty {
+            textView.text = "Send Comment".localized()
+            textView.textColor = UIColor.secondaryColor
+        }
+    }
+    
+}
+
+extension SeminarViewController: UITableViewDataSource {
+    
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return chats.count
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
+        cell.tintColor = .clear
+        cell.backgroundColor = .clear
+        cell.selectionStyle = .none
+        let chat = chats[indexPath.row]
+        let content = cell.contentView
+        content.subviews.forEach({ $0.removeFromSuperview() })
+        let viewContent = UIView()
+        content.addSubview(viewContent)
+        viewContent.anchor(top: content.topAnchor, left: content.leftAnchor, bottom: content.bottomAnchor, right: content.rightAnchor)
+        viewContent.backgroundColor = .clear
+        if !chat.isInfo {
+            let image = UIImageView()
+            viewContent.addSubview(image)
+            image.anchor(top: viewContent.topAnchor, left: viewContent.leftAnchor, width: 30, height: 30)
+            if !chat.thumb.isEmpty {
+                getImage(name: chat.thumb, placeholderImage: UIImage(systemName: "person.circle.fill")!, isCircle: true, tableView: tableView, indexPath: indexPath, completion: { result, isDownloaded, imagePerson in
+                    image.image = imagePerson
+                })
+            } else {
+                image.image = UIImage(systemName: "person.circle.fill")!
+                image.tintColor = .lightGray
+            }
+            let name = UILabel()
+            viewContent.addSubview(name)
+            name.anchor(top: viewContent.topAnchor, left: image.rightAnchor, paddingLeft: 3.0)
+            name.numberOfLines = 1
+            name.text = chat.name
+            name.font = UIFont.boldSystemFont(ofSize: 14)
+            name.textColor = .secondaryColor
+            let message = UILabel()
+            viewContent.addSubview(message)
+            message.anchor(top: name.bottomAnchor, left: image.rightAnchor, bottom: content.bottomAnchor, right:viewContent.rightAnchor, paddingLeft: 3.0, paddingBottom: 5.0)
+            message.numberOfLines = 0
+            message.text = chat.messageText
+            message.font = UIFont.systemFont(ofSize: 14)
+            message.textColor = .white
+        } else {
+            let image = UIImageView()
+            viewContent.addSubview(image)
+            image.anchor(left: viewContent.leftAnchor, centerY: viewContent.centerYAnchor, width: 30, height: 30)
+            if !chat.thumb.isEmpty {
+                getImage(name: chat.thumb, placeholderImage: UIImage(systemName: "person.circle.fill")!, isCircle: true, tableView: tableView, indexPath: indexPath, completion: { result, isDownloaded, imagePerson in
+                    image.image = imagePerson
+                })
+            } else {
+                image.image = UIImage(systemName: "person.circle.fill")!
+                image.tintColor = .lightGray
+            }
+            let name = UILabel()
+            viewContent.addSubview(name)
+            name.anchor(left: image.rightAnchor, paddingLeft: 3.0, centerY: viewContent.centerYAnchor)
+            name.numberOfLines = 1
+            name.text = chat.name
+            name.font = UIFont.italicSystemFont(ofSize: 14)
+            name.textColor = .secondaryColor
+            let message = UILabel()
+            viewContent.addSubview(message)
+            message.anchor(left: name.rightAnchor, paddingLeft: 3.0, centerY: viewContent.centerYAnchor)
+            message.numberOfLines = 0
+            message.text = chat.messageText
+            message.font = UIFont.italicSystemFont(ofSize: 14)
+            message.textColor = .white
+        }
+        return cell
+    }
+    
+}
+
+extension SeminarViewController: SeminarDelegate {
+    
+    func onStartSeminar(state: Int, message: String) {
+        if state == 0, message.contains("Initiating") {
+            DispatchQueue.main.async {
+                self.imageView.transform = CGAffineTransform.init(scaleX: 1.6, y: 1.6)
+            }
+        } else if state == 12 {
+            sendLive()
+        }
+    }
+    
+    func onJoinSeminar(state: Int, message: String) {
+        if state == 22 {
+            let m = message.split(separator: ",")
+            let _ = String(m[0])
+            let _ = String(m[1])
+            let camera = Int(m[2])
+            let platform = Int(m[3])
+            if platform == 1 { // Android
+                DispatchQueue.main.async {
+                    self.imageView.transform = CGAffineTransform.init(scaleX: 1.9, y: 1.9).rotated(by: camera == 1 ? (CGFloat.pi * 3)/2 : (CGFloat.pi)/2)
+                }
+            } else {
+                DispatchQueue.main.async {
+                    self.imageView.transform = CGAffineTransform.init(scaleX: 1.9, y: 1.9).rotated(by: camera == 1 ? (CGFloat.pi * 5)/2 : (CGFloat.pi)/2)
+                }
+            }
+            sendJoin()
+        } else if state == 23 {
+            let m = message.split(separator: ",")
+            let _ = String(m[0])
+            let _ = String(m[1])
+            let camera = Int(m[2])
+            let platform = Int(m[3])
+            if platform == 1 { // Android
+                DispatchQueue.main.async {
+                    self.imageView.transform = CGAffineTransform.init(scaleX: 1.9, y: 1.9).rotated(by: camera == 1 ? (CGFloat.pi * 3)/2 : (CGFloat.pi)/2)
+                }
+            } else {
+                DispatchQueue.main.async {
+                    self.imageView.transform = CGAffineTransform.init(scaleX: 1.9, y: 1.9).rotated(by: camera == 1 ? (CGFloat.pi * 5)/2 : (CGFloat.pi)/2)
+                }
+            }
+        } else if state == 88 {
+            DispatchQueue.main.async {
+                self.status.text = "Seminar ended".localized()
+            }
+            DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
+                self.navigationController?.dismiss(animated: true, completion: nil)
+            })
+        } else if state == 95 { // chat
+            let m = message.split(separator: ",", omittingEmptySubsequences: false)
+            let name = m[3].trimmingCharacters(in: .whitespaces)
+            let thumb = m[2].trimmingCharacters(in: .whitespaces)
+            let text = m[4].trimmingCharacters(in: .whitespaces)
+            chats.append(SeminarChat(name: name, thumb: thumb, messageText: text))
+        } else if state == 96 { // raise hand
+            let m = message.split(separator: ",", omittingEmptySubsequences: false)
+            let status = m[2].trimmingCharacters(in: .whitespaces)
+            // wat to do
+            
+        } else if state == 97 { // someone left
+            let m = message.split(separator: ",", omittingEmptySubsequences: false)
+            let name = m[3].trimmingCharacters(in: .whitespaces)
+            let thumb = m[2].trimmingCharacters(in: .whitespaces)
+            let text = "Left".localized()
+            chats.append(SeminarChat(name: name, thumb: thumb, messageText: text, isInfo: true))
+            DispatchQueue.main.async {
+                self.countViewer.text = "\(Int(self.countViewer.text!)! - 1)"
+            }
+        } else if state == 98 { // someone join
+            let m = message.split(separator: ",", omittingEmptySubsequences: false)
+            let name = m[3].trimmingCharacters(in: .whitespaces)
+            let thumb = m[2].trimmingCharacters(in: .whitespaces)
+            let text = "Joined".localized()
+            chats.append(SeminarChat(name: name, thumb: thumb, messageText: text, isInfo: true))
+            DispatchQueue.main.async {
+                self.countViewer.text = "\(Int(self.countViewer.text!)! + 1)"
+            }
+        }
+    }
+    
+}
+
+class SeminarChat: Model {
+    
+    let name: String
+    let thumb: String
+    let messageText: String
+    let isInfo: Bool
+    
+    init(name: String, thumb: String, messageText: String, isInfo: Bool = false) {
+        self.name = name
+        self.thumb = thumb
+        self.messageText = messageText
+        self.isInfo = isInfo
+    }
+    
+    static func == (lhs: SeminarChat, rhs: SeminarChat) -> Bool {
+        return false
+    }
+    
+    var description: String {
+        return ""
+    }
+    
+}