LNOrderAcceptSettingsViewController.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. //
  2. // LNOrderAcceptSettingsViewController.swift
  3. // Gami
  4. //
  5. // Created by OneeChan on 2026/1/23.
  6. //
  7. import Foundation
  8. import UIKit
  9. import SnapKit
  10. extension UIView {
  11. func pushToOrderAcceptSettings() {
  12. let vc = LNOrderAcceptSettingsViewController()
  13. navigationController?.pushViewController(vc, animated: true)
  14. }
  15. }
  16. class LNOrderAcceptSettingsViewController: LNViewController {
  17. private let timeLabel = UILabel()
  18. private var selectTime: (from: Int, to: Int) = (0, 1) {
  19. didSet {
  20. if selectTime.from + 24 == selectTime.to || selectTime.from == selectTime.to {
  21. timeLabel.text = .init(key: "B00100")
  22. return
  23. }
  24. let fromTime = String(format: "%02d:00", selectTime.from)
  25. let toTime: String = if selectTime.to > 23 {
  26. .init(key: "B00084") + String(format: "%02d:00", selectTime.to - 24)
  27. } else {
  28. String(format: "%02d:00", selectTime.to)
  29. }
  30. timeLabel.text = fromTime + "~" + toTime
  31. }
  32. }
  33. private let dateLabel = UILabel()
  34. private var selectDate: [LNOrderAcceptWeekDay] = [] {
  35. didSet {
  36. dateLabel.text = selectedDateDesc()
  37. }
  38. }
  39. private let autoReplySwitch = UISwitch()
  40. private var isAutoReply: Bool = false {
  41. didSet {
  42. autoReplySwitch.isOn = isAutoReply
  43. }
  44. }
  45. private var config: LNOrderAcceptConfig?
  46. override func viewDidLoad() {
  47. super.viewDidLoad()
  48. setupViews()
  49. loadConfig()
  50. }
  51. }
  52. extension LNOrderAcceptSettingsViewController {
  53. private func loadConfig() {
  54. showLoading()
  55. LNGameMateManager.shared.getOrderAccetpConfig { [weak self] config in
  56. dismissLoading()
  57. guard let self else { return }
  58. guard let config else {
  59. navigationController?.popViewController(animated: true)
  60. return
  61. }
  62. let components = config.timeRange.components(separatedBy: "-")
  63. if components.count == 2 {
  64. let from = extractHourFromTimeString(components[0])
  65. var to = extractHourFromTimeString(components[1])
  66. if to <= from {
  67. to += 24
  68. }
  69. selectTime = (from, to)
  70. } else {
  71. selectTime = (0, 1)
  72. }
  73. selectDate = config.weekNums
  74. isAutoReply = config.autoWelcomeOpen
  75. self.config = config
  76. }
  77. }
  78. private func saveConfig() {
  79. let timeRange = String(format: "%02d:00", selectTime.from) + "-" + String(format: "%02d:00", selectTime.to >= 24 ? selectTime.to - 24 : selectTime.to)
  80. if let config,
  81. config.weekNums == selectDate,
  82. config.timeRange == timeRange {
  83. return
  84. }
  85. showLoading()
  86. let newConfig = LNOrderAcceptConfig()
  87. newConfig.weekNums = selectDate
  88. newConfig.timeRange = timeRange
  89. LNGameMateManager.shared.setOrderAcceptConfig(config: newConfig) { _ in
  90. dismissLoading()
  91. }
  92. }
  93. private func extractHourFromTimeString(_ timeString: String) -> Int {
  94. let components = timeString.components(separatedBy: ":")
  95. guard components.count == 2 else {
  96. return 0
  97. }
  98. let hourString = components[0]
  99. guard let hour = Int(hourString) else {
  100. return 0
  101. }
  102. guard hour >= 0 && hour <= 23 else {
  103. return 0
  104. }
  105. return hour
  106. }
  107. private func selectedDateDesc() -> String {
  108. guard !selectDate.isEmpty else { return "" }
  109. var mergedSegments: [String] = []
  110. var start = selectDate[0]
  111. var end = selectDate[0]
  112. for day in selectDate {
  113. if day == selectDate[0] {
  114. continue
  115. }
  116. // 如果当前星期与上一个连续,更新结束值
  117. if day.rawValue == end.rawValue + 1 {
  118. end = day
  119. } else {
  120. // 不连续则保存当前段,并重置起始/结束
  121. mergedSegments.append(mergeSegment(start: start, end: end))
  122. start = day
  123. end = day
  124. }
  125. }
  126. mergedSegments.append(mergeSegment(start: start, end: end))
  127. return mergedSegments.joined(separator: ",")
  128. }
  129. private func mergeSegment(start: LNOrderAcceptWeekDay, end: LNOrderAcceptWeekDay) -> String {
  130. let formatter = DateFormatter()
  131. formatter.locale = curLocal
  132. let symbols = formatter.weekdaySymbols!
  133. var segments: [String] = []
  134. segments.append(symbols[start.rawValue - 1])
  135. if start != end {
  136. segments.append(.init(key: "B00081"))
  137. segments.append(symbols[end.rawValue - 1])
  138. }
  139. return segments.joined(separator: LNAppConfig.shared.curLang == .chiness ? "" : " ")
  140. }
  141. }
  142. extension LNOrderAcceptSettingsViewController {
  143. private func setupViews() {
  144. title = .init(key: "B00078")
  145. view.backgroundColor = .primary_1
  146. let stackView = UIStackView()
  147. stackView.axis = .vertical
  148. stackView.spacing = 10
  149. view.addSubview(stackView)
  150. stackView.snp.makeConstraints { make in
  151. make.horizontalEdges.equalToSuperview().inset(16)
  152. make.top.equalToSuperview().offset(12)
  153. }
  154. timeLabel.font = .body_s
  155. timeLabel.textColor = .text_4
  156. let timeRangeView = buildItemView(title: .init(key: "B00079"), contentView: timeLabel)
  157. timeRangeView.onTap { [weak self] in
  158. guard let self else { return }
  159. let panel = LNHourRangePickerPanel()
  160. panel.setTitles(.init(key: "B00082"), desc: .init(key: "B00083"))
  161. panel.setDefault(from: selectTime.from, to: selectTime.to)
  162. panel.handler = { [weak self] from, to in
  163. guard let self else { return }
  164. selectTime = (from, to)
  165. saveConfig()
  166. }
  167. panel.popup()
  168. }
  169. stackView.addArrangedSubview(timeRangeView)
  170. dateLabel.font = .body_s
  171. dateLabel.textColor = .text_4
  172. let weekdayView = buildItemView(title: .init(key: "B00080"), contentView: dateLabel)
  173. weekdayView.onTap { [weak self] in
  174. guard let self else { return }
  175. let formatter = DateFormatter()
  176. formatter.locale = curLocal
  177. let panel = LNMultiSelectionPanel()
  178. panel.setTitles(.init(key: "B00085"), desc: .init(key: "B00086"))
  179. panel.update(formatter.weekdaySymbols, curSelected: selectDate.map({ $0.text }))
  180. panel.handler = { [weak self] indexs in
  181. guard let self else { return }
  182. var dates: [LNOrderAcceptWeekDay] = []
  183. for index in indexs {
  184. dates.append(LNOrderAcceptWeekDay.allCases[index])
  185. }
  186. selectDate = dates
  187. saveConfig()
  188. }
  189. panel.popup()
  190. }
  191. stackView.addArrangedSubview(weekdayView)
  192. stackView.addArrangedSubview(buildWelcomeView())
  193. }
  194. private func buildItemView(title: String, contentView: UIView) -> UIView {
  195. let container = UIView()
  196. container.layer.cornerRadius = 12
  197. container.backgroundColor = .fill
  198. container.snp.makeConstraints { make in
  199. make.height.equalTo(60)
  200. }
  201. let arrow = UIImageView.arrowImageView(size: 14)
  202. arrow.tintColor = .text_4
  203. container.addSubview(arrow)
  204. arrow.snp.makeConstraints { make in
  205. make.centerY.equalToSuperview()
  206. make.trailing.equalToSuperview().offset(-12)
  207. }
  208. contentView.setContentHuggingPriority(.defaultLow, for: .horizontal)
  209. contentView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
  210. container.addSubview(contentView)
  211. contentView.snp.makeConstraints { make in
  212. make.centerY.equalToSuperview()
  213. make.trailing.equalTo(arrow.snp.leading).offset(-2)
  214. }
  215. let titleLabel = UILabel()
  216. titleLabel.font = .heading_h4
  217. titleLabel.textColor = .text_5
  218. titleLabel.text = title
  219. titleLabel.setContentHuggingPriority(.required, for: .horizontal)
  220. titleLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
  221. container.addSubview(titleLabel)
  222. titleLabel.snp.makeConstraints { make in
  223. make.centerY.equalToSuperview()
  224. make.leading.equalToSuperview().offset(12)
  225. make.trailing.lessThanOrEqualTo(contentView.snp.leading).offset(-12)
  226. }
  227. return container
  228. }
  229. private func buildWelcomeView() -> UIView {
  230. let container = UIView()
  231. container.layer.cornerRadius = 12
  232. container.backgroundColor = .fill
  233. container.onTap { [weak self] in
  234. guard let self else { return }
  235. view.pushToAutoReplyList()
  236. }
  237. let holder = UIView()
  238. container.addSubview(holder)
  239. holder.snp.makeConstraints { make in
  240. make.horizontalEdges.equalToSuperview()
  241. make.top.equalToSuperview()
  242. make.height.equalTo(53)
  243. }
  244. let arrow = UIImageView.arrowImageView(size: 14)
  245. arrow.tintColor = .text_4
  246. holder.addSubview(arrow)
  247. arrow.snp.makeConstraints { make in
  248. make.centerY.equalToSuperview()
  249. make.trailing.equalToSuperview().offset(-12)
  250. }
  251. let titleLabel = UILabel()
  252. titleLabel.font = .heading_h4
  253. titleLabel.textColor = .text_5
  254. titleLabel.text = .init(key: "B00112")
  255. holder.addSubview(titleLabel)
  256. titleLabel.snp.makeConstraints { make in
  257. make.centerY.equalToSuperview()
  258. make.leading.equalToSuperview().offset(12)
  259. make.trailing.lessThanOrEqualTo(arrow.snp.leading).offset(-12)
  260. }
  261. let openView = UIView()
  262. container.addSubview(openView)
  263. openView.snp.makeConstraints { make in
  264. make.horizontalEdges.equalToSuperview()
  265. make.top.equalTo(holder.snp.bottom)
  266. make.bottom.equalToSuperview().offset(-8)
  267. }
  268. let tipsLabel = UILabel()
  269. tipsLabel.font = .body_s
  270. tipsLabel.textColor = .text_5
  271. tipsLabel.text = .init(key: "B00113")
  272. openView.addSubview(tipsLabel)
  273. tipsLabel.snp.makeConstraints { make in
  274. make.centerY.equalToSuperview()
  275. make.leading.equalToSuperview().offset(12)
  276. }
  277. let scaleX: CGFloat = 40.0 / 51.0
  278. let scaleY: CGFloat = 24.5 / 31.0
  279. autoReplySwitch.onTintColor = .primary_5
  280. autoReplySwitch.transform = CGAffineTransform(scaleX: scaleX, y: scaleY)
  281. autoReplySwitch.addAction(UIAction(handler: { [weak self] _ in
  282. guard let self else { return }
  283. showLoading()
  284. LNGameMateManager.shared.enableAutoReplay(on: autoReplySwitch.isOn) { [weak self] success in
  285. dismissLoading()
  286. guard let self else { return }
  287. guard success else {
  288. autoReplySwitch.isOn.toggle()
  289. return
  290. }
  291. isAutoReply = autoReplySwitch.isOn
  292. }
  293. }), for: .valueChanged)
  294. openView.addSubview(autoReplySwitch)
  295. autoReplySwitch.snp.makeConstraints { make in
  296. make.verticalEdges.equalToSuperview()
  297. make.trailing.equalToSuperview().offset(-12)
  298. }
  299. return container
  300. }
  301. }