374 lines
13 KiB
Swift
374 lines
13 KiB
Swift
//
|
|
// LockDistractVC.swift
|
|
// QuickLocation
|
|
//
|
|
|
|
import UIKit
|
|
import RxSwift
|
|
import RxCocoa
|
|
|
|
final class LockDistractVC: BaseViewController {
|
|
|
|
private var rootView: LockDistractView!
|
|
private var groupModel: GroupModel?
|
|
private var ownedGroups: [GroupInfoModel] = []
|
|
private var activeGroupKey: String = ""
|
|
private var members: [GroupMemberModel] = []
|
|
private var selectedIndex = 0
|
|
private var lockAppsDisposable: Disposable?
|
|
private let lockStateSubscriptionOwner = UUID()
|
|
|
|
override var isNavigationBarHidden: Bool { true }
|
|
|
|
override func loadView() {
|
|
rootView = LockDistractView(frame: UIScreen.main.bounds)
|
|
view = rootView
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
view.backgroundColor = UIColor(hexStr: "#FAFAFA")
|
|
bind()
|
|
loadGroupMembers()
|
|
}
|
|
|
|
@MainActor deinit {
|
|
MQTTService.shared.updateLockStateSubscriptions(
|
|
owner: lockStateSubscriptionOwner,
|
|
userIds: []
|
|
)
|
|
}
|
|
|
|
private func bind() {
|
|
rootView.navView.onBackTapped = { [weak self] in
|
|
self?.navigationController?.popViewController(animated: true)
|
|
}
|
|
|
|
rootView.switchGroupBtn.rx.tap
|
|
.subscribe(onNext: { [weak self] in
|
|
self?.switchGroup()
|
|
})
|
|
.disposed(by: disposeBag)
|
|
|
|
rootView.memberPrevBtn.rx.tap
|
|
.subscribe(onNext: { [weak self] in
|
|
self?.stepMember(-1)
|
|
})
|
|
.disposed(by: disposeBag)
|
|
|
|
rootView.memberNextBtn.rx.tap
|
|
.subscribe(onNext: { [weak self] in
|
|
self?.stepMember(1)
|
|
})
|
|
.disposed(by: disposeBag)
|
|
|
|
rootView.notFetchedView.rx.tap
|
|
.subscribe(onNext: { _ in
|
|
LockDistractPermissionPopView.show()
|
|
}).disposed(by: disposeBag)
|
|
|
|
rootView.onPrivilegeAction = { [weak self] in
|
|
guard let self else { return }
|
|
if self.selectedMember?.user_id == AppContextManager.shared.userId {
|
|
AppRouter.push(Route.privacySettings)
|
|
} else {
|
|
DLToast.show(text: "已提醒")
|
|
}
|
|
}
|
|
|
|
rootView.lockBtn.rx.tap
|
|
.subscribe(onNext: { [weak self] in
|
|
self?.performPrimaryAction()
|
|
})
|
|
.disposed(by: disposeBag)
|
|
|
|
NotificationCenter.default.rx.notification(.lockDistractAppsDidChange)
|
|
.observe(on: MainScheduler.instance)
|
|
.subscribe(onNext: { [weak self] notification in
|
|
guard let self else { return }
|
|
let groupKey = notification.userInfo?[LockDistractAppsChangeUserInfoKey.groupKey] as? String ?? ""
|
|
let userId = notification.userInfo?[LockDistractAppsChangeUserInfoKey.userId] as? String ?? ""
|
|
guard groupKey.isEmpty || groupKey == self.activeGroupKey,
|
|
userId.isEmpty || userId == self.selectedMember?.user_id else { return }
|
|
self.loadLockableApps()
|
|
})
|
|
.disposed(by: disposeBag)
|
|
|
|
NotificationCenter.default.rx.notification(UIApplication.didBecomeActiveNotification)
|
|
.observe(on: MainScheduler.instance)
|
|
.subscribe(onNext: { [weak self] _ in
|
|
self?.loadLockableApps()
|
|
})
|
|
.disposed(by: disposeBag)
|
|
}
|
|
|
|
private func loadGroupMembers() {
|
|
GroupService.groupInfo().subscribe(onNext: { [weak self] response in
|
|
guard let self, let model = response.model else { return }
|
|
self.applyOwnedGroups(from: model)
|
|
}).disposed(by: disposeBag)
|
|
}
|
|
|
|
private func applyOwnedGroups(from model: GroupModel) {
|
|
let owned = model.groups.filter(\.is_owner)
|
|
guard !owned.isEmpty else {
|
|
MQTTService.shared.updateLockStateSubscriptions(
|
|
owner: lockStateSubscriptionOwner,
|
|
userIds: []
|
|
)
|
|
DLToast.show(text: "只有圈主才能使用此功能") { [weak self] in
|
|
self?.navigationController?.popViewController(animated: true)
|
|
}
|
|
return
|
|
}
|
|
groupModel = model
|
|
ownedGroups = owned
|
|
if owned.contains(where: { $0.group_key == model.default_group_key }) {
|
|
activeGroupKey = model.default_group_key
|
|
} else {
|
|
activeGroupKey = owned[0].group_key
|
|
}
|
|
updateGroupName()
|
|
loadUsers(for: activeGroupKey)
|
|
}
|
|
|
|
private func ownedGroupModelForPicker() -> GroupModel? {
|
|
guard var model = groupModel else { return nil }
|
|
model.groups = ownedGroups
|
|
model.default_group_key = activeGroupKey
|
|
return model
|
|
}
|
|
|
|
private func loadUsers(for groupKey: String) {
|
|
guard !groupKey.isEmpty else {
|
|
applyMembers([])
|
|
return
|
|
}
|
|
GroupService.groupUsers(groupKey: groupKey)
|
|
.subscribe(onNext: { [weak self] response in
|
|
guard response.isValid(for: groupKey) else {
|
|
DLToast.showError(text: response.message ?? "获取圈子成员失败")
|
|
return
|
|
}
|
|
self?.applyMembers(response.list)
|
|
}, onError: { error in
|
|
DLToast.showError(text: error.localizedDescription)
|
|
})
|
|
.disposed(by: disposeBag)
|
|
}
|
|
|
|
private func applyMembers(_ members: [GroupMemberModel]) {
|
|
self.members = members
|
|
MQTTService.shared.updateLockStateSubscriptions(
|
|
owner: lockStateSubscriptionOwner,
|
|
userIds: members.map(\.user_id)
|
|
)
|
|
selectedIndex = 0
|
|
refreshMember()
|
|
rootView.setTodayLockCount(0)
|
|
loadLockableApps()
|
|
}
|
|
|
|
private func stepMember(_ delta: Int) {
|
|
guard !members.isEmpty else { return }
|
|
let count = members.count
|
|
selectedIndex = (selectedIndex + delta + count) % count
|
|
refreshMember()
|
|
loadLockableApps()
|
|
}
|
|
|
|
private func refreshMember() {
|
|
guard members.indices.contains(selectedIndex) else {
|
|
rootView.configureMember(name: " ")
|
|
rootView.configureLocked(false)
|
|
rootView.setPrivilegeDenied(false, isCurrentUser: false)
|
|
return
|
|
}
|
|
let m = members[selectedIndex]
|
|
let name = m.showName
|
|
let isSelf = m.user_id.trimmed == AppContextManager.shared.userId.trimmed
|
|
rootView.configureMember(
|
|
name: name,
|
|
headPic: m.displayHeadPic,
|
|
isCurrentUser: isSelf
|
|
)
|
|
rootView.configureLocked(false)
|
|
rootView.setPrivilegeDenied(!m.extra.privilege.app_lock, isCurrentUser: isSelf)
|
|
}
|
|
|
|
private func updateGroupName() {
|
|
let name = groupModel?.groups.first(where: { $0.group_key == activeGroupKey })?.name ?? ""
|
|
rootView.setGroupName(name)
|
|
}
|
|
|
|
private func switchGroup() {
|
|
guard let pickerModel = ownedGroupModelForPicker() else {
|
|
loadGroupMembers()
|
|
return
|
|
}
|
|
GroupListPopView.show(
|
|
groupModel: pickerModel,
|
|
currentMembers: members,
|
|
selectedKey: activeGroupKey
|
|
) { [weak self] groupKey in
|
|
guard let self, let key = groupKey, key != self.activeGroupKey else { return }
|
|
self.activeGroupKey = key
|
|
self.updateGroupName()
|
|
self.loadUsers(for: key)
|
|
}
|
|
}
|
|
|
|
private var selectedMember: GroupMemberModel? {
|
|
members.indices.contains(selectedIndex) ? members[selectedIndex] : nil
|
|
}
|
|
|
|
private func loadLockableApps() {
|
|
lockAppsDisposable?.dispose()
|
|
rootView.configureLockableApps([])
|
|
guard let member = selectedMember, !activeGroupKey.isEmpty else { return }
|
|
if !member.extra.privilege.app_lock {
|
|
rootView.setTodayLockCount(0)
|
|
return
|
|
}
|
|
let targetUserId = member.user_id
|
|
let requestGroupKey = activeGroupKey
|
|
lockAppsDisposable = UserService.phoneLockApps(userId: targetUserId, groupKey: requestGroupKey)
|
|
.subscribe(onNext: { [weak self] response in
|
|
guard let self,
|
|
self.activeGroupKey == requestGroupKey,
|
|
self.selectedMember?.user_id == targetUserId else { return }
|
|
let apps = response.model?.apps ?? []
|
|
let lockedCount = apps.filter(\.locked).count
|
|
let action = self.primaryAction(
|
|
locked: lockedCount > 0,
|
|
fromUser: response.resolvedFromUser,
|
|
member: member
|
|
)
|
|
self.rootView.configureLockableApps(apps)
|
|
self.rootView.configureLocked(lockedCount > 0, action: action)
|
|
self.rootView.setTodayLockCount(lockedCount)
|
|
}, onError: { [weak self] error in
|
|
guard let self,
|
|
self.activeGroupKey == requestGroupKey,
|
|
self.selectedMember?.user_id == targetUserId else { return }
|
|
self.rootView.configureLockableApps([])
|
|
self.rootView.configureLocked(false)
|
|
self.rootView.setTodayLockCount(0)
|
|
DLToast.show(text: error.gatewayMessage ?? "获取可锁应用失败")
|
|
})
|
|
lockAppsDisposable?.disposed(by: disposeBag)
|
|
}
|
|
|
|
private func primaryAction(
|
|
locked: Bool,
|
|
fromUser: String,
|
|
member: GroupMemberModel
|
|
) -> LockDistractPrimaryAction {
|
|
guard locked else { return .lock }
|
|
let currentUserId = AppContextManager.shared.userId.trimmed
|
|
let lockOwnerId = fromUser.trimmed
|
|
if !currentUserId.isEmpty, lockOwnerId == currentUserId {
|
|
return .unlock
|
|
}
|
|
if member.user_id.trimmed == currentUserId {
|
|
return .requestUnlock
|
|
}
|
|
return .hidden
|
|
}
|
|
|
|
private func requestUnlockForLockedApps() {
|
|
let lockedApps = rootView.lockableApps.filter(\.locked)
|
|
let tokens = lockedApps.map(\.token).filter { !$0.isEmpty }
|
|
let os = lockedApps.first(where: { !$0.os.isEmpty })?.os ?? "ios"
|
|
guard !activeGroupKey.isEmpty, !tokens.isEmpty else {
|
|
DLToast.show(text: "锁定数据缺失")
|
|
return
|
|
}
|
|
DLToast.showLoading()
|
|
UserService.requestPhoneUnlock(os: os, groupKey: activeGroupKey, tokens: tokens)
|
|
.subscribe(onNext: { _ in
|
|
DLToast.show(text: "已向圈主发送解锁请求")
|
|
}, onError: { error in
|
|
DLToast.show(text: error.gatewayMessage ?? "发送失败")
|
|
})
|
|
.disposed(by: disposeBag)
|
|
}
|
|
|
|
private func unlockLockedApps() {
|
|
guard let member = selectedMember else {
|
|
DLToast.show(text: "解锁数据缺失")
|
|
return
|
|
}
|
|
let lockedApps = rootView.lockableApps.filter(\.locked)
|
|
let tokens = lockedApps.map(\.token).filter { !$0.isEmpty }
|
|
let os = lockedApps.first(where: { !$0.os.isEmpty })?.os ?? "ios"
|
|
guard !activeGroupKey.isEmpty, !tokens.isEmpty else {
|
|
DLToast.show(text: "解锁数据缺失")
|
|
return
|
|
}
|
|
DLToast.showLoading()
|
|
rootView.lockBtn.isEnabled = false
|
|
UserService.phoneUnlock(
|
|
os: os,
|
|
groupKey: activeGroupKey,
|
|
userId: member.user_id,
|
|
tokens: tokens
|
|
)
|
|
.subscribe(onNext: { [weak self] _ in
|
|
self?.rootView.lockBtn.isEnabled = true
|
|
DLToast.show(text: "已解锁")
|
|
self?.loadLockableApps()
|
|
}, onError: { [weak self] error in
|
|
self?.rootView.lockBtn.isEnabled = true
|
|
DLToast.show(text: error.gatewayMessage ?? "解锁失败")
|
|
})
|
|
.disposed(by: disposeBag)
|
|
}
|
|
|
|
private func performPrimaryAction() {
|
|
switch rootView.primaryAction {
|
|
case .requestUnlock:
|
|
requestUnlockForLockedApps()
|
|
case .unlock:
|
|
unlockLockedApps()
|
|
case .lock:
|
|
lockSelectedApps()
|
|
case .hidden:
|
|
break
|
|
}
|
|
}
|
|
|
|
private func lockSelectedApps() {
|
|
guard let member = selectedMember, !activeGroupKey.isEmpty else {
|
|
DLToast.show(text: "请选择要锁定的成员")
|
|
return
|
|
}
|
|
let apps = rootView.selectedLockApps
|
|
let tokens = apps.map(\.token).filter { !$0.isEmpty }
|
|
guard !tokens.isEmpty else {
|
|
DLToast.show(text: "请选择要锁定的应用")
|
|
return
|
|
}
|
|
let os = apps.first(where: { !$0.os.isEmpty })?.os ?? "ios"
|
|
DLToast.showLoading()
|
|
rootView.lockBtn.isEnabled = false
|
|
UserService.phoneLock(
|
|
os: os,
|
|
groupKey: activeGroupKey,
|
|
userId: member.user_id,
|
|
tokens: tokens,
|
|
iconIndex: rootView.selectedLockIconIndex,
|
|
message: rootView.lockMessage
|
|
)
|
|
.subscribe(onNext: { [weak self] _ in
|
|
self?.rootView.lockBtn.isEnabled = true
|
|
DLToast.show(text: "锁定成功")
|
|
self?.loadLockableApps()
|
|
}, onError: { [weak self] error in
|
|
self?.rootView.lockBtn.isEnabled = true
|
|
DLToast.show(text: error.gatewayMessage ?? "锁定失败")
|
|
})
|
|
.disposed(by: disposeBag)
|
|
}
|
|
}
|