From 535f09fa11e4be266e00110068185360fb7de8ff Mon Sep 17 00:00:00 2001 From: xuyunshi <405029644@qq.com> Date: Wed, 22 Nov 2023 11:04:17 +0800 Subject: [PATCH] Join room history --- Flat.xcodeproj/project.pbxproj | 4 + .../triangle_down.imageset/Contents.json | 25 ++++ .../Common/triangle_down.imageset/down@2x.png | Bin 0 -> 375 bytes .../Common/triangle_down.imageset/down@3x.png | Bin 0 -> 497 bytes .../UIViewController+SafaArea.swift | 11 ++ Flat/Models/Flat/RoomBasicInfo.swift | 11 +- .../ClassRoom/ClassroomCoordinator.swift | 47 ++++++ .../HistoryJoinRoomPickerViewController.swift | 140 ++++++++++++++++++ .../Modules/Home/JoinRoomViewController.swift | 57 +++++-- .../Home/RoomDetailViewController.swift | 4 +- .../UIComponents/Share/ShareManager.swift | 2 +- Flat/en.lproj/Localizable.strings | 3 + Flat/zh-Hans.lproj/Localizable.strings | 3 + 13 files changed, 285 insertions(+), 22 deletions(-) create mode 100644 Flat/Assets.xcassets/Common/triangle_down.imageset/Contents.json create mode 100644 Flat/Assets.xcassets/Common/triangle_down.imageset/down@2x.png create mode 100644 Flat/Assets.xcassets/Common/triangle_down.imageset/down@3x.png create mode 100644 Flat/Modules/Home/HistoryJoinRoomPickerViewController.swift diff --git a/Flat.xcodeproj/project.pbxproj b/Flat.xcodeproj/project.pbxproj index f81dd731..943df233 100644 --- a/Flat.xcodeproj/project.pbxproj +++ b/Flat.xcodeproj/project.pbxproj @@ -203,6 +203,7 @@ 8A8507EF27E180A00072AF49 /* CloudStorageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A8507EE27E180A00072AF49 /* CloudStorageViewController.swift */; }; 8A8507F127E186450072AF49 /* CloudStorageInClassViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A8507F027E186450072AF49 /* CloudStorageInClassViewController.swift */; }; 8A86A4EF2806B6D60095F499 /* SMSAuthView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A86A4EE2806B6D60095F499 /* SMSAuthView.swift */; }; + 8A8797F92B0CA661009A951C /* HistoryJoinRoomPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A8797F82B0CA661009A951C /* HistoryJoinRoomPickerViewController.swift */; }; 8A8A240329879F63008319BB /* ClassroomStatusBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A8A240229879F63008319BB /* ClassroomStatusBar.swift */; }; 8A8A79B52A11E03900B000CF /* AppDelegate+keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A8A79B42A11E03900B000CF /* AppDelegate+keyboard.swift */; }; 8A8A79B72A11E09000B000CF /* GlobalKeyboardShortcutRespondable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A8A79B62A11E09000B000CF /* GlobalKeyboardShortcutRespondable.swift */; }; @@ -590,6 +591,7 @@ 8A8507EE27E180A00072AF49 /* CloudStorageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudStorageViewController.swift; sourceTree = ""; }; 8A8507F027E186450072AF49 /* CloudStorageInClassViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudStorageInClassViewController.swift; sourceTree = ""; }; 8A86A4EE2806B6D60095F499 /* SMSAuthView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SMSAuthView.swift; sourceTree = ""; }; + 8A8797F82B0CA661009A951C /* HistoryJoinRoomPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryJoinRoomPickerViewController.swift; sourceTree = ""; }; 8A8A240229879F63008319BB /* ClassroomStatusBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassroomStatusBar.swift; sourceTree = ""; }; 8A8A79B42A11E03900B000CF /* AppDelegate+keyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+keyboard.swift"; sourceTree = ""; }; 8A8A79B62A11E09000B000CF /* GlobalKeyboardShortcutRespondable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalKeyboardShortcutRespondable.swift; sourceTree = ""; }; @@ -1152,6 +1154,7 @@ 8ACE3A292730CD4600BC37F7 /* CreateClassRoomViewController.swift */, 8A0CA23628BF4A860035F71A /* CameraPreviewView.swift */, 8AAE53CE2731153500C35796 /* JoinRoomViewController.swift */, + 8A8797F82B0CA661009A951C /* HistoryJoinRoomPickerViewController.swift */, 8A59FF8528D8410C00318407 /* JoinRoomInputAccessView.swift */, 8AFB942D28B7550100B8795D /* CameraMicToggleView.swift */, 8A0CA23828C0519A0035F71A /* DeviceAutorizationHelper.swift */, @@ -2222,6 +2225,7 @@ 8A270BA12719686C00450DC6 /* LocaleManager.swift in Sources */, 8A8020932755F12600F5691C /* UIView+LoopForSuperView.swift in Sources */, 8A21DABC279FAA4C0060E25D /* UpdateRecordEndTimeRequest.swift in Sources */, + 8A8797F92B0CA661009A951C /* HistoryJoinRoomPickerViewController.swift in Sources */, 8AAC69B32717E3EB00F9A3B4 /* User.swift in Sources */, 8A80209A27570A4A00F5691C /* CloudStorageTableViewCell.swift in Sources */, 8A97EBB12719993C00965185 /* JoinRoomInfo.swift in Sources */, diff --git a/Flat/Assets.xcassets/Common/triangle_down.imageset/Contents.json b/Flat/Assets.xcassets/Common/triangle_down.imageset/Contents.json new file mode 100644 index 00000000..5f71e1c6 --- /dev/null +++ b/Flat/Assets.xcassets/Common/triangle_down.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "down@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "down@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Flat/Assets.xcassets/Common/triangle_down.imageset/down@2x.png b/Flat/Assets.xcassets/Common/triangle_down.imageset/down@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b81b28702ee221024748927e61ec48395746e0c7 GIT binary patch literal 375 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?2=RS;(M3{v?36l5$8 za(7}_cTVOdki(Mh=pr*F{Fa=?WK*mha4nWAGAj*D5o@AbWO5S zPH8-FQQ^f6hGPp3i&kG~PBCVD!zg1jVL~cb&9{2d_t`a@-t7MVMP5M(1U_k1Kdkh>ufPH2oA<9z zXVz5<#m>cMRfo0oItN$MRzt1`F)_1pO$YJ@3JkxI4aNks3!4Nrh1Bd@X))l#& zt3O;>!Cg=wd-(4mKc9d>v|u)bk_Gz z>1+y0$Y9gS8J7iZjGt8qi}5C$Y_zn}|M=0$Or6ul@R@;Tpx^D-1Wz9BTdi3S7X6Ho zUvTTbU)lTkPshtOeswl;3T)k8q3>DtVUE4+g5?~xTf=v`nC99&pC2BWG~-@sxTT2B zk(rrwYa|kqwNLADf4T6xGxg@z=uRu38ga>^KW1gvY=37_dds|TPU`U;ktS|ebXJv5 zo_2iaMx)D73#O&1^jZ`xiXcHVK{# zmznuEZoY@z;}bfvt6e_Hx<7mQ(|?J;qZ2-{^_?PKGBa1_M=mj~QJP|5bi03@heL0` zqZ2jtR!V= 10 { + items = items.dropLast() + } + items.insert(item, at: 0) + do { + let data = try JSONEncoder().encode(items) + UserDefaults.standard.setValue(data, forKey: joinRoomHistoryUserDefaultsKey) + updateJoinRoomHistoryItem() + } catch { + logger.error("joinRoomHistory encode error \(error)") + } + } + + @discardableResult + func updateJoinRoomHistoryItem() -> [JoinRoomHistoryItem] { + if let data = UserDefaults.standard.value(forKey: joinRoomHistoryUserDefaultsKey) as? Data { + do { + let items = try JSONDecoder().decode([JoinRoomHistoryItem].self, from: data) + self.joinRoomHisotryItems = items + } catch { + logger.error("joinRoomHistory decode error \(error)") + } + } + return self.joinRoomHisotryItems + } + var currentClassroomUUID: String? var enterClassDate: Date? @@ -140,6 +184,9 @@ class ClassroomCoordinator: NSObject { .map { (p, $0) } } } + .do(onNext: { [weak self] _ , info in + self?.insertJoinRoomHistoryItem(.init(roomName: info.title, roomInviteId: info.inviteCode)) + }) .map { ClassroomFactory.getClassRoomViewController(withPlayInfo: $0.0, basicInfo: $0.1, deviceStatus: deviceState) } .asSingle() } diff --git a/Flat/Modules/Home/HistoryJoinRoomPickerViewController.swift b/Flat/Modules/Home/HistoryJoinRoomPickerViewController.swift new file mode 100644 index 00000000..8626c16f --- /dev/null +++ b/Flat/Modules/Home/HistoryJoinRoomPickerViewController.swift @@ -0,0 +1,140 @@ +// +// HistoryJoinRoomPickerViewController.swift +// Flat +// +// Created by xuyunshi on 2023/11/21. +// Copyright © 2023 agora.io. All rights reserved. +// + +import UIKit + +class HistoryJoinRoomPickerViewController: UIViewController { + var items = ClassroomCoordinator.shared.joinRoomHisotryItems + + var roomIdConfirmHandler: ((String) ->Void)? + + override func viewDidLoad() { + super.viewDidLoad() + setupViews() + } + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + dismiss(animated: true) + } + + func setupViews() { + view.backgroundColor = UIColor.black.withAlphaComponent(0.3) + + let operationStack = UIStackView(arrangedSubviews: [clearButton, UIView(), confirmButton]) + operationStack.addLine(direction: .bottom, color: .borderColor, width: commonBorderWidth, inset: .init(top: 0, left: 14, bottom: 0, right: 14)) + operationStack.axis = .horizontal + + let stack = UIStackView(arrangedSubviews: [operationStack, picker, cancelButton]) + stack.backgroundColor = .color(type: .background) + stack.axis = .vertical + view.addSubview(stack) + operationStack.snp.makeConstraints { make in + make.height.equalTo(44) + } + stack.snp.makeConstraints { make in + make.left.right.bottom.equalTo(view.safeAreaLayoutGuide) + } + picker.snp.makeConstraints { make in + make.height.equalTo(144) + } + cancelButton.snp.makeConstraints { $0.height.equalTo(44) } + fillBottomSafeAreaWith(color: .color(type: .background)) + } + + @objc func onCancel() { + dismiss(animated: true) + } + + @objc func onConfirm() { + let row = picker.selectedRow(inComponent: 0) + if row + 1 > items.count { + dismiss(animated: true) + return + } + let item = items[row] + dismiss(animated: true) + roomIdConfirmHandler?(item.roomInviteId) + } + + @objc func onClear() { + ClassroomCoordinator.shared.clearJoinRoomHistoryItem() + items = [] + picker.reloadAllComponents() + } + + lazy var picker: UIPickerView = { + let view = UIPickerView() + view.backgroundColor = .color(type: .background) + view.delegate = self + view.dataSource = self + return view + }() + + lazy var clearButton: UIButton = { + let btn = UIButton(type: .system) + btn.setTitle(localizeStrings("ClearHistory"), for: .normal) + btn.addTarget(self, action: #selector(onClear), for: .touchUpInside) + btn.tintColor = .color(type: .text) + btn.contentEdgeInsets = .init(top: 0, left: 14, bottom: 0, right: 14) + return btn + }() + + lazy var confirmButton: UIButton = { + let btn = UIButton(type: .system) + btn.setTitle(localizeStrings("Confirm"), for: .normal) + btn.addTarget(self, action: #selector(onConfirm), for: .touchUpInside) + btn.tintColor = .color(type: .primary) + btn.contentEdgeInsets = .init(top: 0, left: 14, bottom: 0, right: 14) + return btn + }() + + lazy var cancelButton: UIButton = { + let btn = UIButton(type: .system) + btn.setTitle(localizeStrings("Cancel"), for: .normal) + btn.addTarget(self, action: #selector(onCancel), for: .touchUpInside) + btn.tintColor = .color(type: .text) + return btn + }() +} + +extension HistoryJoinRoomPickerViewController: UIPickerViewDelegate, UIPickerViewDataSource { + func numberOfComponents(in pickerView: UIPickerView) -> Int { + 1 + } + + func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + items.count + } + + func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { + let view = UIView(frame: .zero) + let leftLabel = UILabel() + leftLabel.font = .systemFont(ofSize: 14) + leftLabel.textColor = .color(type: .text) + leftLabel.text = items[row].roomName + leftLabel.textAlignment = .left + + let rightLabel = UILabel() + rightLabel.font = .systemFont(ofSize: 14) + rightLabel.textColor = .color(type: .text) + rightLabel.text = items[row].roomInviteId.formatterInviteCode + rightLabel.textAlignment = .right + + let stack = UIStackView(arrangedSubviews: [leftLabel, rightLabel]) + stack.axis = .horizontal + view.addSubview(stack) + stack.snp.makeConstraints { make in + make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 14, bottom: 0, right: 14)) + } + return view + } + + func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { + 44 + } +} diff --git a/Flat/Modules/Home/JoinRoomViewController.swift b/Flat/Modules/Home/JoinRoomViewController.swift index 0f04aedb..7c091c4f 100644 --- a/Flat/Modules/Home/JoinRoomViewController.swift +++ b/Flat/Modules/Home/JoinRoomViewController.swift @@ -29,12 +29,17 @@ class JoinRoomViewController: UIViewController { guard !fireKeyboardFirstTime else { return } if view.window != nil, view.bounds.width > 0 { fireKeyboardFirstTime = true - subjectTextField.becomeFirstResponder() + roomIdTextField.becomeFirstResponder() } } var fireKeyboardFirstTime = false + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + historyPickerButton.isEnabled = !ClassroomCoordinator.shared.updateJoinRoomHistoryItem().isEmpty + } + override func viewDidLoad() { super.viewDidLoad() setupViews() @@ -46,7 +51,7 @@ class JoinRoomViewController: UIViewController { // MARK: - Action @objc func onClickJoin(_ sender: UIButton) { - guard let uuid = subjectTextField.text?.replacingOccurrences(of: " ", with: ""), + guard let uuid = roomIdTextField.text?.replacingOccurrences(of: " ", with: ""), !uuid.isEmpty else { return } @@ -80,7 +85,7 @@ class JoinRoomViewController: UIViewController { } func bindJoinEnable() { - subjectTextField.rx.text.orEmpty.asDriver() + roomIdTextField.rx.text.orEmpty.asDriver() .map(\.isNotEmptyOrAllSpacing) .drive(with: self, onNext: { weakSelf, joinEnable in weakSelf.joinButton.isEnabled = joinEnable @@ -109,7 +114,7 @@ class JoinRoomViewController: UIViewController { let bottomStackItemHeight: CGFloat = 44 if traitCollection.hasCompact { - let centerStack = UIStackView(arrangedSubviews: [subjectTextField, previewView, deviceView]) + let centerStack = UIStackView(arrangedSubviews: [roomIdTextField, previewView, deviceView]) centerStack.axis = .vertical centerStack.alignment = .center centerStack.distribution = .equalCentering @@ -118,7 +123,7 @@ class JoinRoomViewController: UIViewController { make.center.equalToSuperview() } - subjectTextField.snp.makeConstraints { make in + roomIdTextField.snp.makeConstraints { make in make.width.equalTo(320) make.height.equalTo(66) } @@ -140,7 +145,7 @@ class JoinRoomViewController: UIViewController { } let bottomStack = UIStackView(arrangedSubviews: [deviceView, joinButton]) - let verticalStack = UIStackView(arrangedSubviews: [subjectTextField, previewView, bottomStack]) + let verticalStack = UIStackView(arrangedSubviews: [roomIdTextField, previewView, bottomStack]) verticalStack.axis = .vertical verticalStack.alignment = .center verticalStack.distribution = .equalCentering @@ -149,8 +154,8 @@ class JoinRoomViewController: UIViewController { make.edges.equalTo(view.safeAreaLayoutGuide).inset(UIEdgeInsets(top: 56, left: 16, bottom: 16, right: 16)) } - subjectTextField.setContentHuggingPriority(.defaultLow, for: .vertical) - subjectTextField.snp.makeConstraints { make in + roomIdTextField.setContentHuggingPriority(.defaultLow, for: .vertical) + roomIdTextField.snp.makeConstraints { make in make.width.equalTo(320) make.height.equalTo(66) } @@ -178,7 +183,7 @@ class JoinRoomViewController: UIViewController { // MARK: - Lazy - lazy var subjectTextField: BottomLineTextfield = { + lazy var roomIdTextField: BottomLineTextfield = { let tf = BottomLineTextfield() tf.textColor = .color(type: .text, .strong) tf.textAlignment = .center @@ -187,25 +192,47 @@ class JoinRoomViewController: UIViewController { tf.keyboardType = .numberPad tf.returnKeyType = .join tf.clearButtonMode = .whileEditing + tf.rightView = historyPickerButton + tf.rightViewMode = .unlessEditing tf.delegate = self tf.keyboardDistanceFromTextField = 188 return tf }() + + lazy var historyPickerButton: UIButton = { + let btn = UIButton(type: .system) + btn.frame = .init(x: 0, y: 0, width: 44, height: 44) + btn.setImage(UIImage(named: "triangle_down"), for: .normal) + btn.tintColor = .color(type: .text) + btn.addTarget(self, action: #selector(onClickHistory), for: .touchUpInside) + return btn + }() + + @objc func onClickHistory() { + let vc = HistoryJoinRoomPickerViewController() + vc.roomIdConfirmHandler = { [weak self] id in + self?.roomIdTextField.text = id + self?.roomIdTextField.sendActions(for: .editingChanged) + } + vc.modalTransitionStyle = .crossDissolve + vc.modalPresentationStyle = .overCurrentContext + present(vc, animated: true) + } @objc func handle(keyboardShowNotification notification: Notification) { guard let keyboardRect = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } guard let window = view.window else { return } let deviceEnd = deviceView.convert(CGPoint(x: 0, y: deviceView.bounds.height), to: window).y let isOverDeviceView = keyboardRect.origin.y > deviceEnd - if isOverDeviceView, subjectTextField.inputAccessoryView == roomInputAccessView { - subjectTextField.inputAccessoryView = nil + if isOverDeviceView, roomIdTextField.inputAccessoryView == roomInputAccessView { + roomIdTextField.inputAccessoryView = nil UIView.performWithoutAnimation { - self.subjectTextField.reloadInputViews() + self.roomIdTextField.reloadInputViews() } - } else if !isOverDeviceView, subjectTextField.inputAccessoryView == nil { - subjectTextField.inputAccessoryView = roomInputAccessView + } else if !isOverDeviceView, roomIdTextField.inputAccessoryView == nil { + roomIdTextField.inputAccessoryView = roomInputAccessView UIView.performWithoutAnimation { - self.subjectTextField.reloadInputViews() + self.roomIdTextField.reloadInputViews() } } } diff --git a/Flat/Modules/Home/RoomDetailViewController.swift b/Flat/Modules/Home/RoomDetailViewController.swift index b42e29c5..2cd0136f 100644 --- a/Flat/Modules/Home/RoomDetailViewController.swift +++ b/Flat/Modules/Home/RoomDetailViewController.swift @@ -100,7 +100,7 @@ class RoomDetailViewController: UIViewController { @IBAction func onClickCopy(_: Any) { guard let info else { return } - UIPasteboard.general.string = info.formatterInviteCode + UIPasteboard.general.string = info.inviteCode.formatterInviteCode toast(localizeStrings("Copy Success")) } @@ -186,7 +186,7 @@ class RoomDetailViewController: UIViewController { statusLabel.text = localizeStrings(status.rawValue) statusLabel.textColor = status == .Started ? .color(type: .success) : .color(type: .text) - roomNumberLabel.text = info.formatterInviteCode + roomNumberLabel.text = info.inviteCode.formatterInviteCode roomTypeLabel.text = localizeStrings(roomType.rawValue) if status == .Stopped { diff --git a/Flat/Modules/UIComponents/Share/ShareManager.swift b/Flat/Modules/UIComponents/Share/ShareManager.swift index 50dbfff7..d269e795 100644 --- a/Flat/Modules/UIComponents/Share/ShareManager.swift +++ b/Flat/Modules/UIComponents/Share/ShareManager.swift @@ -20,7 +20,7 @@ struct ShareInfo { formatter.timeStyle = .short time = formatter.string(from: roomDetail.beginTime) subject = roomDetail.title - number = roomDetail.formatterInviteCode + number = roomDetail.inviteCode.formatterInviteCode link = URL(string: Env().webBaseURL + "/join/\(roomDetail.roomUUID)")! } diff --git a/Flat/en.lproj/Localizable.strings b/Flat/en.lproj/Localizable.strings index 27d899c3..48e510bc 100644 --- a/Flat/en.lproj/Localizable.strings +++ b/Flat/en.lproj/Localizable.strings @@ -168,6 +168,9 @@ "ClassesStillLeftTips" = "You have rooms that have not been closed or withdrawn from, and you can only request to cancel your account after processing, the number remaining: "; +"ClearHistory" = "Clear History"; + + "ClearWhiteboard" = "Clear Whiteboard"; diff --git a/Flat/zh-Hans.lproj/Localizable.strings b/Flat/zh-Hans.lproj/Localizable.strings index 77f4e8f9..1ee4fc37 100644 --- a/Flat/zh-Hans.lproj/Localizable.strings +++ b/Flat/zh-Hans.lproj/Localizable.strings @@ -168,6 +168,9 @@ "ClassesStillLeftTips" = "你有尚未结束或退出的房间,处理完毕后才可以申请注销账号, 剩余数量: "; +"ClearHistory" = "清空记录"; + + "ClearWhiteboard" = "清空白板";