KMCheckInWindowController.swift 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. //
  2. // KMCheckInWindowController.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by User-Tangchao on 2024/12/18.
  6. //
  7. import Cocoa
  8. class KMCheckInWindowController: KMBaseWindowController {
  9. @IBOutlet weak var contentBox: NSBox!
  10. @IBOutlet weak var backgroundIv: NSImageView!
  11. @IBOutlet weak var titleLabel: NSTextField!
  12. @IBOutlet weak var subTitleBox: NSBox!
  13. @IBOutlet weak var giftIv: NSImageView!
  14. @IBOutlet weak var subTitleLabel: NSTextField!
  15. @IBOutlet weak var despScrollView: NSScrollView!
  16. @IBOutlet var despTextView: NSTextView!
  17. @IBOutlet weak var checkInBox: NSBox!
  18. @IBOutlet weak var checkTopBox: NSBox!
  19. @IBOutlet weak var checkInLabel: NSTextField!
  20. @IBOutlet weak var checkInTotalLabel: NSTextField!
  21. @IBOutlet weak var checkInBackgroundIv: NSImageView!
  22. @IBOutlet weak var checkInBackgroundIv2: NSImageView!
  23. @IBOutlet weak var checkInScrollView: NSScrollView!
  24. @IBOutlet weak var checkInCollectionView: NSCollectionView!
  25. @IBOutlet weak var ipIv: NSImageView!
  26. static let shared = KMCheckInWindowController(windowNibName: "KMCheckInWindowController")
  27. private var dayNum_: Int = 0
  28. private var learnMoreActionString_ = "action://LearnMore"
  29. private var cellItemIdentifier_ = NSUserInterfaceItemIdentifier(rawValue: "CellID")
  30. private var showPopGuideing_ = false
  31. private var datas_: [KMCheckInModel] = []
  32. override func windowDidLoad() {
  33. super.windowDidLoad()
  34. window?.standardWindowButton(.zoomButton)?.isHidden = true
  35. window?.standardWindowButton(.miniaturizeButton)?.isHidden = true
  36. let closeButton = window?.standardWindowButton(.closeButton)
  37. closeButton?.target = self
  38. closeButton?.action = #selector(closeAction)
  39. window?.delegate = self
  40. backgroundIv.image = NSImage(named: "KMImageNameCheckInBg")
  41. backgroundIv.image?.size = window?.frame.size ?? NSMakeSize(661, 442)
  42. titleLabel.font = .UbuntuMediumFontWithSize(30)
  43. subTitleBox.borderWidth = 0
  44. giftIv.image = NSImage(named: "KMImageNameCheckInGift")
  45. subTitleLabel.font = .UbuntuMediumFontWithSize(20)
  46. despScrollView.drawsBackground = false
  47. despScrollView.backgroundColor = .clear
  48. despScrollView.hasVerticalScroller = false
  49. despScrollView.hasHorizontalScroller = false
  50. _initDespAttriText()
  51. despTextView.isSelectable = true
  52. despTextView.isEditable = false
  53. despTextView.delegate = self
  54. despTextView.backgroundColor = .clear
  55. checkInBox.borderWidth = 0
  56. checkInBackgroundIv.image = NSImage(named: "KMImageNameCheckInListBg")
  57. checkInBackgroundIv2.image = NSImage(named: "KMImageNameCheckInListBg2")
  58. checkTopBox.borderWidth = 0
  59. checkInLabel.stringValue = NSLocalizedString("Cumulatively Sign in to Get AI Credits", comment: "")
  60. checkInLabel.font = .SFProTextRegularFont(16)
  61. checkInTotalLabel.font = .SFProTextRegularFont(12)
  62. checkInTotalLabel.alignment = .center
  63. checkInScrollView.drawsBackground = false
  64. checkInScrollView.borderType = .noBorder
  65. checkInCollectionView.wantsLayer = true
  66. checkInCollectionView.backgroundColors = [.clear]
  67. checkInCollectionView.delegate = self
  68. checkInCollectionView.dataSource = self
  69. checkInCollectionView.register(KMCheckInViewItem.self, forItemWithIdentifier: cellItemIdentifier_)
  70. let layout = checkInCollectionView.collectionViewLayout as? NSCollectionViewFlowLayout
  71. layout?.itemSize = NSMakeSize(84, 115)
  72. layout?.minimumLineSpacing = 0
  73. layout?.minimumInteritemSpacing = 0
  74. layout?.sectionInset = .init(top: 0, left: 12, bottom: 0, right: 12)
  75. ipIv.image = NSImage(named: "KMImageNameCheckInIP")
  76. _initData()
  77. self.checkInCollectionView.reloadData()
  78. interfaceThemeDidChanged(self.window?.appearance?.name ?? .aqua)
  79. }
  80. override func interfaceThemeDidChanged(_ appearance: NSAppearance.Name) {
  81. super.interfaceThemeDidChanged(appearance)
  82. KMMainThreadExecute {
  83. if KMAppearance.isDarkMode() {
  84. self.titleLabel.textColor = .white
  85. self.subTitleLabel.textColor = .white
  86. } else {
  87. self.titleLabel.textColor = NSColor(hex: "#00071D")
  88. self.subTitleLabel.textColor = NSColor(hex: "#0E1114")
  89. }
  90. self.checkInLabel.textColor = NSColor(hex: "#42464D")
  91. self.checkInTotalLabel.textColor = NSColor(hex: "#42464D")
  92. self._initDespAttriText()
  93. }
  94. }
  95. // MARK: - Private Methods
  96. private func _initData() {
  97. datas_.removeAll()
  98. for i in 1 ... 7 {
  99. let model = KMCheckInModel()
  100. model.dayNum = i
  101. if i == 1 || i == 3 || i == 7 {
  102. model.isGift = true
  103. if i == 7 {
  104. model.creditNum = 20
  105. } else {
  106. model.creditNum = 10
  107. }
  108. }
  109. datas_.append(model)
  110. }
  111. }
  112. private func _initDespAttriText() {
  113. let string = NSLocalizedString("PDF Reader Pro AI Tools: With AI Summarize, Translate, Proofread, and Rewrite", comment: "")
  114. let linkString = NSLocalizedString("Learn More", comment: "")
  115. let textString = String(format: "%@ %@", string, linkString)
  116. let style = NSMutableParagraphStyle()
  117. var textColor = NSColor(hex: "#0E1114")
  118. var linkColor = NSColor(hex: "#4982E6")
  119. if KMAppearance.isDarkMode() {
  120. textColor = .white
  121. linkColor = NSColor(hex: "#227AFF")
  122. }
  123. let font = NSFont.SFProTextRegularFont(14)
  124. let attriText = NSMutableAttributedString(string: textString, attributes: [
  125. .foregroundColor: textColor,
  126. .font: font,
  127. .paragraphStyle: style
  128. ])
  129. let range = (textString as NSString).range(of: linkString)
  130. attriText.addAttributes([
  131. .foregroundColor: linkColor,
  132. .underlineStyle: NSUnderlineStyle.single.rawValue,
  133. .link: learnMoreActionString_,
  134. .font: font
  135. ], range: range)
  136. despTextView.textStorage?.setAttributedString(attriText)
  137. }
  138. private func _openAIWindow() {
  139. let winC = AINewConfigWindowController.currentWC()
  140. winC.chooseCurFileHandle = { windowVC in
  141. if AIChatInfoManager.defaultManager.currentFilePath.isEmpty == false {
  142. var didFileEdit = false
  143. var curDoc: KMMainDocument? = nil
  144. for document in NSDocumentController.shared.documents {
  145. if document.fileURL?.path == AIChatInfoManager.defaultManager.currentFilePath {
  146. didFileEdit = document.isDocumentEdited
  147. curDoc = document as? KMMainDocument
  148. break
  149. }
  150. }
  151. if didFileEdit {
  152. let tempFileURL = FileManager.default.temporaryDirectory.appendingPathComponent(AIChatInfoManager.defaultManager.currentFilePath.lastPathComponent)
  153. if FileManager.default.fileExists(atPath: tempFileURL.path) {
  154. do {
  155. try FileManager.default.removeItem(at: tempFileURL)
  156. } catch {
  157. }
  158. }
  159. curDoc?.mainViewController?.SaveTempPDFDocumentToURLPath(tempPath: tempFileURL.path)
  160. }
  161. winC.window?.becomeMain()
  162. }
  163. }
  164. if winC.window?.isVisible == true && winC.didSetOriginFrame == true {
  165. } else {
  166. winC.window?.center()
  167. winC.didSetOriginFrame = true
  168. }
  169. winC.eventLabel = "AITools_Onboard"
  170. winC.showWindow(nil)
  171. }
  172. private func _updateItemView(_ itemView: KMCheckInItemView, model: KMCheckInModel) {
  173. itemView.backgroundView.wantsLayer = true
  174. itemView.backgroundView.layer?.cornerRadius = 8
  175. let selectedBgColor = NSColor(hex: "#FEE4EC")
  176. let unSelectedBgColor = NSColor(hex: "#FCFDFF")
  177. let unSelectedBgBorderColor = NSColor(hex: "#EBECF0")
  178. if model.isGift {
  179. let itemView = itemView as? KMCheckInGiftItemView
  180. itemView?.numLabel.stringValue = "+\(model.creditNum)"
  181. itemView?.numLabel.font = .SFProTextRegularFont(14)
  182. itemView?.creditsLabel.stringValue = NSLocalizedString("Credits", comment: "")
  183. itemView?.creditsLabel.font = .SFProTextRegularFont(14)
  184. itemView?.giftIv.image = NSImage(named: "KMImageNameCheckInGift")
  185. if model.isSelected {
  186. if dayNum_ == model.dayNum {
  187. itemView?.numLabel.textColor = NSColor(hex: "#FCFDFF")
  188. itemView?.creditsLabel.textColor = NSColor(hex: "#FCFDFF")
  189. itemView?.backgroundView.layer?.backgroundColor = NSColor(hex: "#F76959").cgColor
  190. itemView?.gradientView.colors = [NSColor(hex: "#FA388C"), NSColor(hex: "#F76959")]
  191. itemView?.gradientView.angle = -90
  192. } else {
  193. itemView?.gradientView.isHidden = true
  194. itemView?.numLabel.textColor = NSColor(hex: "#FA1E5D")
  195. itemView?.creditsLabel.textColor = NSColor(hex: "#FA1E5D")
  196. itemView?.backgroundView.layer?.backgroundColor = selectedBgColor.cgColor
  197. }
  198. } else {
  199. itemView?.gradientView.isHidden = true
  200. itemView?.numLabel.textColor = NSColor(hex: "#757780")
  201. itemView?.creditsLabel.textColor = NSColor(hex: "#757780")
  202. itemView?.backgroundView.layer?.borderWidth = 1
  203. itemView?.backgroundView.layer?.borderColor = unSelectedBgBorderColor.cgColor
  204. itemView?.backgroundView.layer?.backgroundColor = unSelectedBgColor.cgColor
  205. }
  206. } else {
  207. if model.isSelected {
  208. itemView.checkIv.image = NSImage(named: "KMImageNameCheckInSelected")
  209. itemView.backgroundView.layer?.backgroundColor = selectedBgColor.cgColor
  210. } else {
  211. itemView.checkIv.image = NSImage(named: "KMImageNameCheckInNormal")
  212. itemView.backgroundView.layer?.borderWidth = 1
  213. itemView.backgroundView.layer?.borderColor = unSelectedBgBorderColor.cgColor
  214. itemView.backgroundView.layer?.backgroundColor = unSelectedBgColor.cgColor
  215. }
  216. }
  217. itemView.nameLabel.stringValue = String(format: NSLocalizedString("Day %d", comment: ""), model.dayNum)
  218. itemView.nameLabel.font = .SFProTextRegularFont(12)
  219. itemView.nameLabel.textColor = NSColor(hex: "#757780")
  220. }
  221. private func _trackEvent(dayNum: Int) {
  222. if dayNum == 1 {
  223. trackEvent(eventName: "PUW_2", params: ["PUW_Exposure" : "CumulativeOnline_1Day"], platform: .AppCenter)
  224. } else if dayNum == 3 {
  225. trackEvent(eventName: "PUW_2", params: ["PUW_Exposure" : "CumulativeOnline_3Day"], platform: .AppCenter)
  226. } else if dayNum == 7 {
  227. trackEvent(eventName: "PUW_2", params: ["PUW_Exposure" : "CumulativeOnline_7Day"], platform: .AppCenter)
  228. }
  229. }
  230. private func _fetchBrowserWindowC() -> KMBrowserWindowController? {
  231. if let win = NSApp.mainWindow as? CTBrowserWindow {
  232. return win.windowController as? KMBrowserWindowController
  233. }
  234. if let win = NSApp.keyWindow as? CTBrowserWindow {
  235. return win.windowController as? KMBrowserWindowController
  236. }
  237. for win in NSApp.windows {
  238. if win.isVisible == false {
  239. continue
  240. }
  241. guard let winC = win.windowController as? KMBrowserWindowController else {
  242. continue
  243. }
  244. if winC.browser == nil {
  245. continue
  246. }
  247. return winC
  248. }
  249. return nil
  250. }
  251. private func _showPopGuide() {
  252. if KMMemberInfo.shared.isLogin == false { // 未登陆
  253. return
  254. }
  255. guard let winC = _fetchBrowserWindowC() else { // 没找到有效的窗口
  256. return
  257. }
  258. guard let doc = winC.browser.activeTabContents() as? KMMainDocument else {
  259. return
  260. }
  261. if doc.isHome || doc.isNewTab {
  262. return
  263. }
  264. if showPopGuideing_ { // 正在显示,避免出现多个
  265. return
  266. }
  267. showPopGuideing_ = true
  268. doc.mainViewController?.loadOpenFileFunctionGuide(.aiToolForCheckIn)
  269. }
  270. // MARK: - Actions
  271. @objc func closeAction() {
  272. _showPopGuide()
  273. closeWindow()
  274. }
  275. // MARK: - Public Methods
  276. public func openWindow(num: Int) {
  277. showWindow(nil)
  278. showPopGuideing_ = false
  279. dayNum_ = num
  280. if dayNum_ == 1 {
  281. titleLabel.stringValue = NSLocalizedString("Welcome to Becoming a Member of PDF Reader Pro", comment: "")
  282. } else {
  283. titleLabel.stringValue = String(format: NSLocalizedString("Cumulatively Online for %d Days!", comment: ""), dayNum_)
  284. }
  285. if datas_.isEmpty {
  286. _initData()
  287. }
  288. var creditNum = 0
  289. for model in datas_ {
  290. model.isSelected = model.dayNum <= dayNum_
  291. if model.dayNum == dayNum_ {
  292. if dayNum_ == 1 {
  293. subTitleLabel.stringValue = String(format: NSLocalizedString("First Meeting Gift - %d AI Credits", comment: ""), model.creditNum)
  294. } else {
  295. subTitleLabel.stringValue = String(format: NSLocalizedString("%d AI Credits Giveaway", comment: ""), model.creditNum)
  296. }
  297. }
  298. if model.dayNum <= dayNum_ {
  299. creditNum += model.creditNum
  300. }
  301. }
  302. checkInTotalLabel.stringValue = String(format: NSLocalizedString("Total AI Credits", comment: "")+": %d", creditNum)
  303. checkInCollectionView.reloadData()
  304. _trackEvent(dayNum: dayNum_)
  305. }
  306. public func closeWindow() {
  307. window?.close()
  308. }
  309. public func learnMoreAction() {
  310. closeWindow()
  311. _openAIWindow()
  312. }
  313. public func needShow() -> Bool {
  314. let member = KMMemberInfo.shared
  315. if member.isLogin == false {
  316. return false
  317. }
  318. #if DEBUG
  319. return true
  320. #endif
  321. if member.is_advanced_year_subscribe() == false { // 不是高级版年订阅
  322. return false
  323. }
  324. if member.isCancelSubscribe() { // 已经退订
  325. return false
  326. }
  327. // 高级版年订阅
  328. var isTrail = false
  329. for vip in member.activeVips {
  330. if vip.levels == "3" && vip.paymentModel == "1" && vip.cycle == 4 {
  331. isTrail = vip.isTrail == "1"
  332. break
  333. }
  334. }
  335. if isTrail == false {
  336. return false
  337. }
  338. return true
  339. }
  340. }
  341. // MARK: - NSWindowDelegate
  342. extension KMCheckInWindowController: NSWindowDelegate {
  343. func windowDidResize(_ notification: Notification) {
  344. guard let data = window?.isEqual(to: notification.object), data == true else {
  345. return
  346. }
  347. backgroundIv.image?.size = window?.frame.size ?? NSMakeSize(661, 442)
  348. }
  349. }
  350. // MARK: - NSTextViewDelegate
  351. extension KMCheckInWindowController: NSTextViewDelegate {
  352. func textView(_ textView: NSTextView, clickedOnLink link: Any, at charIndex: Int) -> Bool {
  353. if let data = link as? String, data == learnMoreActionString_ {
  354. learnMoreAction()
  355. return true
  356. }
  357. return false
  358. }
  359. }
  360. // MARK: - NSCollectionViewDelegate, NSCollectionViewDataSource
  361. extension KMCheckInWindowController: NSCollectionViewDelegate, NSCollectionViewDataSource {
  362. func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
  363. return datas_.count
  364. }
  365. func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
  366. let model = datas_[indexPath.item]
  367. if model.isGift {
  368. let cell = collectionView.makeItem(withIdentifier: cellItemIdentifier_, for: indexPath) as? KMCheckInViewItem
  369. let itemView = KMCheckInGiftItemView()
  370. cell?.contentBox.contentView = itemView
  371. _updateItemView(itemView, model: model)
  372. return cell ?? NSCollectionViewItem()
  373. }
  374. let cell = collectionView.makeItem(withIdentifier: cellItemIdentifier_, for: indexPath) as? KMCheckInViewItem
  375. let itemView = KMCheckInItemView()
  376. cell?.contentBox.contentView = itemView
  377. _updateItemView(itemView, model: model)
  378. return cell ?? NSCollectionViewItem()
  379. }
  380. }