diff --git a/AgoraWidgets.podspec b/AgoraWidgets.podspec index 0729d1c..029e9a1 100644 --- a/AgoraWidgets.podspec +++ b/AgoraWidgets.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = "AgoraWidgets" - spec.version = "2.8.100" + spec.version = "2.8.101" spec.summary = "Agora widgets" spec.description = "Agora native widgets" spec.homepage = "https://docs.agora.io/en/agora-class/landing-page?platform=iOS" @@ -18,7 +18,7 @@ Pod::Spec.new do |spec| spec.dependency "AgoraUIBaseViews", ">=2.8.0" spec.dependency "AgoraWidget", ">=2.8.0" spec.dependency "AgoraLog", "1.0.2" - spec.dependency "Armin", ">=1.1.0" + spec.dependency "Armin", "~> 1.0" spec.dependency "SwifterSwift" spec.dependency "Masonry" diff --git a/AgoraWidgets_Local.podspec b/AgoraWidgets_Local.podspec index 4ac4b02..a74b94a 100644 --- a/AgoraWidgets_Local.podspec +++ b/AgoraWidgets_Local.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = "AgoraWidgets" - spec.version = "2.8.100" + spec.version = "2.8.101" spec.summary = "SDKs/AgoraWidgets." spec.description = "Agora native widgets" spec.homepage = "https://docs.agora.io/en/agora-class/landing-page?platform=iOS" diff --git a/SDKs/AgoraWidgets/AgoraChat/Views/BottomBar/AgoraChatInputView.swift b/SDKs/AgoraWidgets/AgoraChat/Views/BottomBar/AgoraChatInputView.swift index 43e3e17..09f0fe6 100644 --- a/SDKs/AgoraWidgets/AgoraChat/Views/BottomBar/AgoraChatInputView.swift +++ b/SDKs/AgoraWidgets/AgoraChat/Views/BottomBar/AgoraChatInputView.swift @@ -80,6 +80,19 @@ extension AgoraChatInputView: UITextFieldDelegate { onClickSendMessage() return true } + + func textField(_ textField: UITextField, + shouldChangeCharactersIn range: NSRange, + replacementString string: String) -> Bool { + guard let currentText = textField.text else { + return true + } + + let newText = currentText.replacingCharacters(in: Range(range, in: currentText)!, + with: string) + + return newText.count <= 300 + } } // MARK: - Actions private extension AgoraChatInputView { @@ -234,4 +247,3 @@ extension AgoraChatInputView: AgoraUIContentContainer { sendButton.layer.cornerRadius = config.cornerRadius } } - diff --git a/SDKs/AgoraWidgets/AgoraChat/Views/Content/AgoraChatAnnouncementView.swift b/SDKs/AgoraWidgets/AgoraChat/Views/Content/AgoraChatAnnouncementView.swift index 9b97616..748ad1b 100644 --- a/SDKs/AgoraWidgets/AgoraChat/Views/Content/AgoraChatAnnouncementView.swift +++ b/SDKs/AgoraWidgets/AgoraChat/Views/Content/AgoraChatAnnouncementView.swift @@ -56,7 +56,6 @@ class AgoraChatAnnouncementView: UIView { required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - } // MARK: - AgoraUIContentContainer @@ -129,6 +128,7 @@ extension AgoraChatAnnouncementView: AgoraUIContentContainer { make?.left.equalTo()(13) make?.top.equalTo()(13) make?.width.height().lessThanOrEqualTo()(self)?.offset()(-14) + make?.bottom.lessThanOrEqualTo()(self)?.offset()(-34) } deleteButton.mas_makeConstraints { make in @@ -257,6 +257,11 @@ extension AgoraChatAnnouncementView: UITextViewDelegate { return false } + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.resignFirstResponder() + return true + } + func textViewDidEndEditing(_ textView: UITextView) { inputTextView.resignFirstResponder() } diff --git a/SDKs/AgoraWidgets/AgoraChat/Views/Content/AgoraChatMessageView.swift b/SDKs/AgoraWidgets/AgoraChat/Views/Content/AgoraChatMessageView.swift index 486abd8..93945b6 100644 --- a/SDKs/AgoraWidgets/AgoraChat/Views/Content/AgoraChatMessageView.swift +++ b/SDKs/AgoraWidgets/AgoraChat/Views/Content/AgoraChatMessageView.swift @@ -199,17 +199,35 @@ extension AgoraChatMessageView: UITableViewDelegate, UITableViewDataSource { cell.messageImageView.size = size cell.updateFrame() } else { - let url = URL(string: model.imageRemoteUrl) + let urlString = model.imageRemoteUrl + let url = URL(string: urlString) let brokenImage = UIConfig.agoraChat.picture.brokenImage + cell.messageImageView.sd_setImage(with: url, placeholderImage: brokenImage) { [weak self] downloadImage, error, cacheType, url in guard let `self` = self else { return } - cell.messageImageView.image = downloadImage - let size = cell.sizeWithImage(downloadImage) - cell.messageImageView.size = size - cell.updateFrame() + + guard self.messageDataSource.count > indexPath.row else { + return + } + + let type = self.messageDataSource[indexPath.row] + + guard case .image(var model) = type else { + return + } + + guard model.imageRemoteUrl == urlString else { + return + } + + model.image = downloadImage + + let new = AgoraChatMessageViewType.image(model) + + self.messageDataSource[indexPath.row] = new tableView.reloadRows(at: [indexPath], with: .none) @@ -229,11 +247,16 @@ extension AgoraChatMessageView: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let type = messageDataSource[indexPath.row] + guard case .image(let model) = type else { return } - setFullScreenImage(urlString: model.imageRemoteUrl, - localImage: model.image) + + guard let image = model.image else { + return + } + + setFullScreenImage(localImage: image) } } @@ -289,23 +312,14 @@ private extension AgoraChatMessageView { func updateNoticeMessageCell(cell: AgoraChatNoticeMessageCell, notice: String) { cell.noticeLabel.text = notice - } + } - func setFullScreenImage(urlString: String?, - localImage:UIImage?) { + func setFullScreenImage(localImage: UIImage) { let topVc = UIViewController.agora_top_view_controller() - if let localImage = localImage { - topVc.view.bringSubviewToFront(fullScreenImageView) - fullScreenImageView.agora_visible = true - fullScreenImageView.image = localImage - } else if let url = URL(string: urlString) { - topVc.view.bringSubviewToFront(fullScreenImageView) - fullScreenImageView.agora_visible = true - fullScreenImageView.sd_setImage(with: url) - } else { - return - } + topVc.view.bringSubviewToFront(fullScreenImageView) + fullScreenImageView.agora_visible = true + fullScreenImageView.image = localImage } @objc func onClickCloseFullScreenImage() { diff --git a/SDKs/AgoraWidgets/Assets/others/en.lproj/Localizable.strings b/SDKs/AgoraWidgets/Assets/others/en.lproj/Localizable.strings index 798ade0..7908f1d 100644 --- a/SDKs/AgoraWidgets/Assets/others/en.lproj/Localizable.strings +++ b/SDKs/AgoraWidgets/Assets/others/en.lproj/Localizable.strings @@ -101,6 +101,11 @@ fcr_popup_quiz_my_answer = "My Answer"; fcr_popup_quiz_start_again = "restart"; fcr_popup_quiz_answer_time = "Time elapsed"; +//slide +fcr_board_slide_retry = "course load failure, retry now"; +fcr_board_slide_retry_failure = "course load failure, please load next page or reopen"; + + // poll fcr_poll_title = "Poll"; fcr_poll_submit = "Submit"; diff --git a/SDKs/AgoraWidgets/Assets/others/zh-Hans.lproj/Localizable.strings b/SDKs/AgoraWidgets/Assets/others/zh-Hans.lproj/Localizable.strings index d0f2bca..3180d85 100644 --- a/SDKs/AgoraWidgets/Assets/others/zh-Hans.lproj/Localizable.strings +++ b/SDKs/AgoraWidgets/Assets/others/zh-Hans.lproj/Localizable.strings @@ -90,6 +90,10 @@ fcr_cloud_upload_file = "上传文件"; fcr_cloud_upload_pictures = "上传图片"; fcr_cloud_uploading = "上传中"; +//slide +fcr_board_slide_retry = "课件加载失败,正在重试"; +fcr_board_slide_retry_failure = "课件加载失败,请切换下一页或关闭课件后重试"; + // AnswerSelector fcr_popup_quiz = "答题器"; fcr_popup_quiz_post = "提交答案"; diff --git a/SDKs/AgoraWidgets/CloudDrive/FcrCloudDriveServerAPI.swift b/SDKs/AgoraWidgets/CloudDrive/FcrCloudDriveServerAPI.swift index 25bba49..ceaaa6d 100644 --- a/SDKs/AgoraWidgets/CloudDrive/FcrCloudDriveServerAPI.swift +++ b/SDKs/AgoraWidgets/CloudDrive/FcrCloudDriveServerAPI.swift @@ -54,7 +54,7 @@ class FcrCloudDriveServerAPI: AgoraWidgetServerAPI { request(event: "cloud-drive-delete-file", url: urlString, method: .delete, - parameters: list) { json in + anyParameters: list) { json in success(resourceUuid) } failure: { error in failure(error) @@ -117,7 +117,7 @@ class FcrCloudDriveServerAPI: AgoraWidgetServerAPI { request(event: "cloud-drive", url: urlString, method: .post, - parameters: parameters) { json in + anyParameters: parameters) { json in if let data = (json["data"] as? [[String: Any]])?.first { success(data) } else { diff --git a/SDKs/AgoraWidgets/Common/AgoraWidgetServerAPI.swift b/SDKs/AgoraWidgets/Common/AgoraWidgetServerAPI.swift index d8ccebf..6deeb73 100644 --- a/SDKs/AgoraWidgets/Common/AgoraWidgetServerAPI.swift +++ b/SDKs/AgoraWidgets/Common/AgoraWidgetServerAPI.swift @@ -85,7 +85,7 @@ public class AgoraWidgetServerAPI: NSObject { url: String, method: ArHttpMethod, header: [String: String]? = nil, - parameters: Any? = nil, + anyParameters: Any, success: JsonCompletion? = nil, failure: FailureCompletion? = nil) { var extra: [String: Any] = ["event": event] @@ -94,9 +94,7 @@ public class AgoraWidgetServerAPI: NSObject { extra["header"] = header.description } - if let `parameters` = parameters { - extra["parameters"] = parameters - } + extra["parameters"] = anyParameters self.armin.logTube?.log(info: "http request", extra: extra.description) @@ -114,10 +112,8 @@ public class AgoraWidgetServerAPI: NSObject { } // Parameters - if let params = parameters { - let jsonData = try? JSONSerialization.data(withJSONObject: params) - request.httpBody = jsonData - } + let jsonData = try? JSONSerialization.data(withJSONObject: anyParameters) + request.httpBody = jsonData request.allHTTPHeaderFields = ["x-agora-token": token, "x-agora-uid": userId, diff --git a/SDKs/AgoraWidgets/Whiteboard/FcrBoardWidget.swift b/SDKs/AgoraWidgets/Whiteboard/FcrBoardWidget.swift index 23aa21d..223a05f 100644 --- a/SDKs/AgoraWidgets/Whiteboard/FcrBoardWidget.swift +++ b/SDKs/AgoraWidgets/Whiteboard/FcrBoardWidget.swift @@ -10,6 +10,7 @@ import AgoraWidget import AgoraLog import Photos import Armin +import Whiteboard @objcMembers public class FcrBoardWidget: AgoraNativeWidget { // Views @@ -28,6 +29,7 @@ import Armin private var canJoin = false + private var retryTime = 0 // 教师角色加入房间成功时设置,学生角色监听grantedUsers变化设置 private var hasOperationPrivilege: Bool = false { didSet { @@ -57,6 +59,14 @@ import Armin analyzeGrantedUsersFromRoomProperties() } + func retry(slideId:String, slideIndex: Int){ + self.retryTime += 1 + self.boardRoom?.recoverSlide(slideId: slideId, slideIndex: slideIndex) + showToast("fcr_board_slide_retry".localized() + String(self.retryTime) + "/5", + type: .error) + } + + public override func onWidgetRoomPropertiesDeleted(_ properties: [String : Any]?, cause: [String : Any]?, keyPaths: [String], @@ -331,10 +341,10 @@ extension FcrBoardWidget { let backgroundColor = UIConfig.netlessBoard.backgroundColor let room = FcrBoardRoom(appId: config.boardAppId, region: boardRegion, - backgroundColor: backgroundColor) - room.delegate = self + backgroundColor: backgroundColor, + logTube: self) - room.logTube = self + room.delegate = self let ratio = view.ratio() @@ -665,6 +675,29 @@ extension FcrBoardWidget: FcrBoardRoomDelegate { break } } + func onSlideError(slideError: WhiteSlideErrorType, errorMessage: String, slideId: String, slideIndex: Int) { + if(slideError == WhiteSlideErrorType.resourceError || slideError == WhiteSlideErrorType.canvasCrash){ + if(self.retryTime == 0){ + self.retry(slideId: slideId, slideIndex: slideIndex) + }else if(self.retryTime < 5){ + DispatchQueue.main.asyncAfter(deadline: .now() + 5) { + self.retry(slideId: slideId, slideIndex: slideIndex) + } + }else{ + showToast("fcr_board_slide_retry_failure".localized(), + type: .error) + } + } else if(slideError == WhiteSlideErrorType.runtimeError){ + self.boardRoom?.recoverSlide(slideId: slideId, slideIndex: slideIndex + 1) + } else if(slideError == WhiteSlideErrorType.runtimeWarn) { + let slideExtra = ["slideId": slideId, + "slideIndex": slideIndex.agDescription] + log(content: "slide page error", + extra: slideExtra.agDescription, type: .warning, + fromClass: WhiteSDK.self, + funcName: "onSlideError") + } + } } // MARK: - FcrBoardMainWindowDelegate diff --git a/SDKs/AgoraWidgets/Whiteboard/Wrapper/FcrBoardProtocols.swift b/SDKs/AgoraWidgets/Whiteboard/Wrapper/FcrBoardProtocols.swift index 3071679..dfda98c 100644 --- a/SDKs/AgoraWidgets/Whiteboard/Wrapper/FcrBoardProtocols.swift +++ b/SDKs/AgoraWidgets/Whiteboard/Wrapper/FcrBoardProtocols.swift @@ -11,6 +11,7 @@ import Whiteboard // Public protocol FcrBoardRoomDelegate: NSObjectProtocol { func onConnectionStateUpdated(state: FcrBoardRoomConnectionState) + func onSlideError(slideError: WhiteSlideErrorType, errorMessage: String, slideId: String, slideIndex: Int) } protocol FcrBoardMainWindowDelegate: FcrBoardAudioMixingDelegate { diff --git a/SDKs/AgoraWidgets/Whiteboard/Wrapper/FcrBoardRoom.swift b/SDKs/AgoraWidgets/Whiteboard/Wrapper/FcrBoardRoom.swift index 1a7f25a..8e27931 100644 --- a/SDKs/AgoraWidgets/Whiteboard/Wrapper/FcrBoardRoom.swift +++ b/SDKs/AgoraWidgets/Whiteboard/Wrapper/FcrBoardRoom.swift @@ -8,7 +8,7 @@ import Foundation import Whiteboard -class FcrBoardRoom: NSObject { +class FcrBoardRoom: NSObject, WhiteSlideDelegate { private weak var whiteRoom: WhiteRoom? private let whiteView: WhiteBoardView private let whiteSDK: WhiteSDK @@ -24,7 +24,8 @@ class FcrBoardRoom: NSObject { init(appId: String, region: FcrBoardRegion, - backgroundColor: UIColor?) { + backgroundColor: UIColor?, + logTube: FcrBoardLogTube?) { let listener = FcrBoardListener() let whiteView = WhiteBoardView(frame: .zero, @@ -37,6 +38,7 @@ class FcrBoardRoom: NSObject { sdkConfig.region = region.netlessValue sdkConfig.useMultiViews = true sdkConfig.userCursor = true + sdkConfig.enableAppliancePlugin = true let whiteSDK = WhiteSDK(whiteBoardView: whiteView, config: sdkConfig, @@ -46,9 +48,11 @@ class FcrBoardRoom: NSObject { self.whiteView = whiteView self.whiteSDK = whiteSDK self.listener = listener + self.logTube = logTube super.init() + whiteSDK.setSlideDelegate(self) listener.roomNeedObserve = self registerH5App() @@ -73,12 +77,20 @@ class FcrBoardRoom: NSObject { funcName: "init") } + + func onSlideError(_ slideError: WhiteSlideErrorType, errorMessage: String, slideId: String, slideIndex: Int) { + delegate?.onSlideError(slideError: slideError, errorMessage: errorMessage, slideId: slideId, slideIndex: slideIndex) + } + + func recoverSlide(slideId:String, slideIndex:Int) { + self.whiteSDK.recoverSlide(slideId, slideIndex: slideIndex) + } + func join(config: FcrBoardRoomJoinConfig, superView: UIView, success: @escaping (FcrBoardMainWindow) -> Void, failure: @escaping (Error) -> Void) { hasLeft = false - joinConfig = config superView.addSubview(whiteView) @@ -163,6 +175,8 @@ private extension FcrBoardRoom { delegate?.onConnectionStateUpdated(state: state) } + + func getWhiteRoomConfig() -> WhiteRoomConfig? { guard let config = joinConfig else { log(content: "join config nil",