// // KMProductCompareWC.swift // PDF Reader Pro // // Created by wanjun on 2024/11/6. // import Cocoa import Combine @objcMembers class KMProductCompareWC: NSWindowController { @objc var modalSession: NSApplication.ModalSession? @IBOutlet @objc weak var tableView: NSTableView! @IBOutlet @objc weak var titleLabel: NSTextField! @IBOutlet @objc weak var topMainBox: NSBox! @IBOutlet @objc weak var topMainView: NSView! @IBOutlet @objc weak var topsubBox1: NSBox! @IBOutlet @objc weak var topSelectBox1: NSBox! @IBOutlet @objc weak var topsubBox2: NSBox! @IBOutlet @objc weak var topSelectBox2: NSBox! @IBOutlet @objc weak var topsubBox3: NSBox! @IBOutlet @objc weak var topSelectBox3: NSBox! @IBOutlet @objc weak var topsubBox4: NSBox! @IBOutlet @objc weak var topSelectBox4: NSBox! @IBOutlet weak var topsubBoxWidth1: NSLayoutConstraint! @IBOutlet weak var topsubBoxWidth2: NSLayoutConstraint! @IBOutlet weak var topsubBoxWidth3: NSLayoutConstraint! @IBOutlet weak var topsubBoxWidth4: NSLayoutConstraint! @IBOutlet @objc weak var switchLabel: NSTextField! // @IBOutlet @objc weak var switchButton: NSButton! @IBOutlet @objc weak var freeInfoView: NSView! @IBOutlet @objc weak var freeInfoLabel: NSTextField! @IBOutlet @objc weak var value1InfoView: NSView! // @IBOutlet @objc weak var value1InfoBox: NSBox! @IBOutlet @objc weak var value1InfoTypeBox: NSBox! @IBOutlet @objc weak var sixMouthProLabel: NSTextField! @IBOutlet @objc weak var oneYearProLabel: NSTextField! @IBOutlet weak var backgroundImage1: NSImageView! @IBOutlet @objc weak var switchBox: NSBox! @IBOutlet @objc weak var switchBtn: NSButton! @IBOutlet @objc weak var switchCircleView: NSView! @IBOutlet @objc weak var switchCircleLeftConst: NSLayoutConstraint! @IBOutlet @objc weak var saleBox: NSBox! @IBOutlet @objc weak var saleIv: NSImageView! @IBOutlet @objc weak var saleLabel: NSTextField! @IBOutlet @objc weak var value1InfoLabel: NSTextField! @IBOutlet @objc weak var value1PurchaseLabel: NSTextField! @IBOutlet @objc weak var value1PurchaseSubLabel: NSTextField! @IBOutlet @objc weak var value1PurchaseButton: KMCustomButton! @IBOutlet @objc weak var value1PurchaseBtnLbl: NSTextField! @IBOutlet @objc weak var value1PurchaseOriPriceLbl: NSTextField! @IBOutlet @objc weak var value1PurchaseBtnLblBtn: NSButton! // 标准版年订阅 @IBOutlet @objc weak var standardPlanInfoView: NSView! @IBOutlet @objc weak var standardPlanInfoLabel: NSTextField! @IBOutlet @objc weak var standardPlanPurchaseLabel: NSTextField! @IBOutlet @objc weak var standardPlanPurchaseSubLabel: NSTextField! @IBOutlet @objc weak var standardPlanPurchaseButton: NSButton! @IBOutlet @objc weak var standardPlanPurchaseLbl: NSTextField! @IBOutlet @objc weak var standardPlanPurchaseLblBtn: NSButton! @IBOutlet @objc weak var standardPlanPurchaseSubLabel2: NSTextField! // 高级版年订阅 @IBOutlet @objc weak var advancedPlanInfoView: NSView! @IBOutlet @objc weak var advancedPlanInfoLabel: NSTextField! @IBOutlet @objc weak var advancedPlanPurchaseLabel: NSTextField! @IBOutlet @objc weak var advancedPlanPurchaseSubLabel: NSTextField! @IBOutlet @objc weak var advancedPlanPurchaseButton: NSButton! @IBOutlet @objc weak var advancedPlanPurchaseLbl: NSTextField! @IBOutlet @objc weak var advancedPlanPurchaseLblBtn: NSButton! @IBOutlet @objc weak var advancedPlanPurchaseSubLabel2: NSTextField! @IBOutlet weak var backgroundImage3: NSImageView! @IBOutlet @objc weak var discountPermanent2: NSImageView! // 买断 @IBOutlet @objc weak var permanentInfoView: NSView! @IBOutlet @objc weak var permanentInfoLabel: NSTextField! @IBOutlet @objc weak var permanentPurchaseLabel: NSTextField! @IBOutlet @objc weak var permanentPurchaseSubLabel: NSTextField! @IBOutlet @objc weak var permanentPurchaseButton: NSButton! @IBOutlet @objc weak var permanentPurchaseLbl: NSTextField! @IBOutlet @objc weak var permanentPurchaseLblBtn: NSButton! @IBOutlet @objc weak var permanentPurchaseSubLabel2: NSTextField! @IBOutlet @objc weak var discountPermanent4: NSImageView! @IBOutlet @objc weak var aboutButton: NSButton! @IBOutlet @objc weak var btnRestore: HyperLinkButton! @IBOutlet @objc weak var privacyPolicyBtn: HyperLinkButton! @IBOutlet @objc weak var termOfSerBtn: HyperLinkButton! @IBOutlet @objc weak var textView: NSTextView! @IBOutlet @objc weak var specialView: NSView! @IBOutlet @objc weak var specialImageView: NSImageView! @IBOutlet @objc weak var specialLabel: NSTextField! @IBOutlet @objc weak var specialView1: NSView! @IBOutlet @objc weak var specialImageView1: NSImageView! @IBOutlet @objc weak var specialLabel1: NSTextField! @IBOutlet @objc weak var box: NSBox! @IBOutlet @objc weak var labelbox: NSBox! // @IBOutlet @objc weak var topButtonOffset: NSLayoutConstraint! @objc var value1Layer: CALayer? @objc var discountToSaveWinC: KMDiscountToSaveWindowController? @IBOutlet @objc weak var billedTopConst: NSLayoutConstraint! @IBOutlet @objc weak var discountYearly: NSImageView! @IBOutlet @objc weak var discountPermanent: NSImageView! // @IBOutlet @objc weak var discountPremium: NSImageView! @IBOutlet @objc weak var platformsView1: NSView! @IBOutlet @objc weak var platformsView2: NSView! @IBOutlet @objc weak var platformsView3: NSView! @IBOutlet @objc weak var platformsView4: NSView! private var model = KMProductModel.shared private var cancellables = Set() var orientation: Bool = false // 是否定向跳转 var orientationType: KMCompareTableType = .trial // 定向跳转比较表 static let shared: KMProductCompareWC = { let windowC = KMProductCompareWC(windowNibName: "KMProductCompareWC") return windowC }() override init(window: NSWindow?) { super.init(window: window) setupNotifications() } required init?(coder: NSCoder) { super.init(coder: coder) } // MARK: - Notification Setup private func setupNotifications() { NotificationCenter.default.addObserver( self, selector: #selector(IAPProductLoadedNotification(_:)), name: NSNotification.Name(rawValue: "KMIAPProductLoadedNotification"), object: nil ) NotificationCenter.default.addObserver( self, selector: #selector(IAPSubscriptionLoadedNotification(_:)), name: NSNotification.Name(rawValue: "KMIAPSubscriptionLoadedNotification"), object: nil ) NotificationCenter.default.addObserver( self, selector: #selector(IAPProductPurchasedNotification(_:)), name: NSNotification.Name(rawValue: "KMIAPProductPurchasedNotification"), object: nil ) NotificationCenter.default.addObserver( self, selector: #selector(IAPProductFailedNotification(_:)), name: NSNotification.Name(rawValue: "KMIAPProductFailedNotification"), object: nil ) NotificationCenter.default.addObserver( self, selector: #selector(IAPProductRestoreFinishedNotification(_:)), name: NSNotification.Name(rawValue: "KMIAPProductRestoreFinishedNotification"), object: nil ) NotificationCenter.default.addObserver( self, selector: #selector(IAPProductRestoreFailedNotification(_:)), name: NSNotification.Name(rawValue: "KMIAPProductRestoreFailedNotification"), object: nil ) DistributedNotificationCenter.default.addObserver(self, selector: #selector(themeChanged(_:)), name: NSNotification.Name("AppleInterfaceThemeChangedNotification"), object: nil) } // MARK: - Deinitialization deinit { NotificationCenter.default.removeObserver(self) } // MARK: - Singleton @objc class func sharedInstance() -> NSWindowController { struct Singleton { static let instance: KMProductCompareWC = KMProductCompareWC() } return Singleton.instance } override func windowDidLoad() { super.windowDidLoad() window?.backgroundColor = KMAppearance.Upgrade.vipBGColor() window?.isMovableByWindowBackground = true #if VERSION_DMG btnRestore.isHidden = true #endif // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. bindViewModel() if orientation { model.state = orientationType } else { model.getCurrentComparisonTableType() } value1InfoLabel.stringValue = NSLocalizedString("6-Month Plan", comment: "") value1InfoLabel.font = NSFont.UbuntuMediumFontWithSize(16) value1PurchaseButton.wantsLayer = true value1PurchaseButton.layer?.backgroundColor = NSColor(red: 1, green: 94/255, blue: 44/255, alpha: 1).cgColor value1PurchaseButton.layer?.cornerRadius = value1PurchaseButton.frame.height / 2 value1PurchaseBtnLblBtn.wantsLayer = true value1PurchaseButton.mouseMoveCallback = { [weak self] mouseEntered in guard let self = self else { return } if self.value1PurchaseBtnLblBtn.isEnabled == true { self.value1PurchaseBtnLblBtn.layer?.backgroundColor = mouseEntered ? NSColor.black.withAlphaComponent(0.15).cgColor : NSColor.clear.cgColor } } value1PurchaseBtnLblBtn.layer?.cornerRadius = value1PurchaseBtnLblBtn.frame.height / 2 value1PurchaseBtnLblBtn.layer?.masksToBounds = true standardPlanPurchaseButton.wantsLayer = true standardPlanPurchaseButton.layer?.cornerRadius = standardPlanPurchaseButton.frame.height / 2 self.standardPlanPurchaseButton.layer?.backgroundColor = NSColor.white.cgColor switchLabel.stringValue = NSLocalizedString("Choose your favorite payment and enjoy flexible upgrades.", comment: "") switchLabel.textColor = KMAppearance.Interactive.m0Color() box.wantsLayer = true box.shadow = NSShadow() box.layer?.shadowOpacity = 0.3 box.layer?.shadowColor = NSColor.black.withAlphaComponent(0.3).cgColor box.layer?.shadowOffset = NSMakeSize(0, 3) box.layer?.shadowRadius = 8 box.layer?.masksToBounds = false permanentPurchaseButton.wantsLayer = true permanentPurchaseButton.layer?.cornerRadius = permanentPurchaseButton.frame.height / 2 self.permanentPurchaseButton.layer?.backgroundColor = NSColor.white.cgColor let txtColor = NSColor(red: 102/255, green: 102/255, blue: 102/255, alpha: 1) aboutButton.title = NSLocalizedString("About Subscriptions", comment: "") aboutButton.setTitleColor(NSColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.7)) aboutButton.isHidden = true reloadData() reloadDataContent() #if !VERSION_DMG IAPProductsManager.default().loadProducts() #endif textView.string = String(format: "Subscription plans:\nPDF Reader Pro Standard - Annual Plan: $79.99/year\nPDF Reader Pro Advanced - Annual Plan: $99.99/year\nPDF Reader Pro Permanent: $119.99\n\nSubscriptions: %@\n- Subscriptions will be charged through your iTunes account at the confirmation of purchase.\n- Your subscriptions will automatically renew unless cancelled at least 24 hours before the end of current subscription period.\n- Your iTunes account will be charged for renewal within 24-hours prior to the end of the current period, for the same duration and at the current subscription price.\n- You may manage your subscriptions in your iTunes Account Settings after purchase.\n- No cancellation of the current subscription is allowed during an active subscription period.\n\n- Terms of service:\nhttps://www.pdfreaderpro.com/terms_of_service\n- Privacy Policy:\nhttps://www.pdfreaderpro.com/privacy-policy", "", NSLocalizedString("USD $39.99/(6 months), USD$59.99/(12 months), USD $59.99/(one-time purchase)", comment: "")) showDiscount() } func windowShouldClose(sender:Any) -> Bool { FMTrackEventManager.defaultManager.trackEvent(event: "PUW", withProperties: ["PUW_Btn":"PUW_Btn_Upgrade_Cancel"]) if model.isShowSale { trackEvent_free(eventName: "PromotionalOffer", params: ["PUW_Btn":"PUW_7OffUpgrade_Cancel"], platform: .AppCenter) } endModal(sender) return true } override func close() { super.close() endModal("") } @objc func startModal(_ sender: Any) { NSApp.stopModal() modalSession = NSApp.beginModalSession(for: window!) while NSApp.runModalSession(modalSession!) == .continue { // Modal loop } } @objc func endModal(_ sender: Any) { if let session = modalSession { NSApp.stopModal() NSApp.endModalSession(session) window?.orderOut(self) modalSession = nil } } @objc func showWindowRestore(_ sender: Any) { showWindow(sender) buttonItemClicked_Restore(sender) } @objc override func showWindow(_ sender: Any?) { super.showWindow(sender) #if VERSION_DMG model.getDMGProductDatas { success, result in } #endif if orientation { model.state = orientationType } else { model.getCurrentComparisonTableType() } languageLocalized() initializeUI() reloadData() if model.isShowSale { showDiscountToSaveWindow() } reloadDataContent() showDiscount() } // MARK: - Private Methods private func languageLocalized() -> Void { titleLabel.stringValue = NSLocalizedString("Subscribe to All Access Pack to enjoy more expanded features.", comment: "") btnRestore.title = NSLocalizedString("Restore", comment: "") btnRestore.toolTip = NSLocalizedString("Restore", comment: "") privacyPolicyBtn.title = NSLocalizedString("Privacy Policy", comment: "") privacyPolicyBtn.toolTip = NSLocalizedString("Privacy Policy", comment: "") termOfSerBtn.title = NSLocalizedString("Terms of Service", comment: "") termOfSerBtn.toolTip = NSLocalizedString("Terms of Service", comment: "") sixMouthProLabel.stringValue = NSLocalizedString("6-Month Plan", comment: "") oneYearProLabel.stringValue = NSLocalizedString("1-year Plan", comment: "") freeInfoLabel.stringValue = NSLocalizedString("Free", comment: "") standardPlanInfoLabel.stringValue = NSLocalizedString("PDF Reader Pro Standard - Annual Plan", tableName: "MemberCenterLocalizable", comment: "") standardPlanPurchaseSubLabel.stringValue = NSLocalizedString("Auto-renewal | Billed yearly", tableName: "MemberCenterLocalizable", comment: "") standardPlanPurchaseSubLabel2.stringValue = NSLocalizedString("(Tax Incl.)",tableName: "MemberCenterLocalizable", comment: "") advancedPlanInfoLabel.stringValue = NSLocalizedString("PDF Reader Pro Advanced - Annual Plan", tableName: "MemberCenterLocalizable", comment: "") advancedPlanPurchaseSubLabel.stringValue = NSLocalizedString("Auto-renewal | Billed yearly", tableName: "MemberCenterLocalizable", comment: "") advancedPlanPurchaseSubLabel2.stringValue = NSLocalizedString("(Tax Incl.)",tableName: "MemberCenterLocalizable", comment: "") permanentInfoLabel.stringValue = NSLocalizedString("PDF Reader Pro Permanent", tableName: "MemberCenterLocalizable", comment: "") permanentPurchaseSubLabel.stringValue = NSLocalizedString("one-time purchase", comment: "") permanentPurchaseSubLabel2.stringValue = NSLocalizedString("(Tax Incl.)",tableName: "MemberCenterLocalizable", comment: "") standardPlanPurchaseLbl.stringValue = NSLocalizedString("Subscribe", comment: "") if KMMemberInfo.shared.userScenarioType == .lite_type3 || KMMemberInfo.shared.userScenarioType == .lite_type11 { advancedPlanPurchaseLbl.stringValue = NSLocalizedString("Renew now",tableName: "MemberCenterLocalizable", comment: "“") } else { advancedPlanPurchaseLbl.stringValue = NSLocalizedString("Subscribe", comment: "") } permanentPurchaseLbl.stringValue = NSLocalizedString("Purchase", comment: "") #if VERSION_FREE #if VERSION_DMG // DMG if KMMemberInfo.shared.canTrail && model.state == .trial { advancedPlanPurchaseLbl.stringValue = NSLocalizedString("Free Trial", comment: "") } if model.state == .dmg_Base { standardPlanPurchaseLabel.stringValue = model.getProductPrice(.pdfReaderProStandard_dmg) advancedPlanPurchaseLabel.stringValue = model.getProductPrice(.pdfReaderProAdvanced_dmg) permanentPurchaseLabel.stringValue = model.getProductPrice(.pdfReaderProPermanent_dmg) } else if model.state == .dmg_Upgrades1 || model.state == .dmg_Upgrades2 { guard let productModels = model.dmgProductDatas?.listingProducts else { return } for productModel in productModels { if productModel.code == "advanced-annual-subscription" { model.getDMGProductPriceInfosForMember(productId: productModel.id, isEducation: 0) { [weak self] success, result in guard let self = self else { return } guard let results: KMMemberProductResult = result else { return } guard let productModels2 = results.listingProducts else { return } for productModel2 in productModels2 { if productModel2.code == "advanced-annual-subscription" { self.advancedPlanPurchaseLabel.stringValue = String(format: "USD $%.2f", Float(productModel2.upgradePriceString ?? "") ?? 0) } } } } else if productModel.code == "advanced-permanent-blackFive" { model.getDMGProductPriceInfosForMember(productId: productModel.id, isEducation: 0) { [weak self] success, result in guard let self = self else { return } guard let results: KMMemberProductResult = result else { return } guard let productModels2 = results.listingProducts else { return } for productModel2 in productModels2 { if productModel2.code == "advanced-annual-subscription-blackFive" { self.advancedPlanPurchaseLabel.stringValue = String(format: "USD $%.2f", Float(productModel2.upgradePriceString ?? "") ?? 0) } } } } } } else { advancedPlanPurchaseLabel.stringValue = model.getProductPrice(.pdfReaderProAdvanced_dmg) } #else // AppStore 免费版本 if model.state == .lite_Base { permanentPurchaseLabel.stringValue = model.getProductPrice(.allAccessPackPermanent_lite) } else if model.state == .lite_MacWindows { if KMMemberInfo.shared.userScenarioType == .lite_type5 { let platforms = KMMemberInfo.shared.vip_platforms let platformsArray = platforms .components(separatedBy: ",") .map { $0.trimmingCharacters(in: .whitespaces) } if platformsArray.count == 1 { advancedPlanPurchaseLabel.stringValue = model.getProductPrice(.advancedAddDevicesAllAccessPack12months_lite) } else if platformsArray.count == 2 { advancedPlanPurchaseLabel.stringValue = model.getProductPrice(.advancedAdd2DevicesAllAccessPack12months_lite) } else { advancedPlanPurchaseLabel.stringValue = model.getProductPrice(.advancedAdd2DevicesAllAccessPack12months_lite) } } else { advancedPlanPurchaseLabel.stringValue = model.getProductPrice(.advancedAddDevicesAllAccessPack12months_lite) } } else { // model.state == .trial advancedPlanPurchaseLabel.stringValue = model.getProductPrice(.fourDevicesAllAccessPackNew12months_lite) } #endif #else // AppStore 付费版 if model.state == .pro_Base { if KMMemberInfo.shared.userScenarioType == .pro_type3 { advancedPlanPurchaseLabel.stringValue = model.getProductPrice(.standardAddDevicesAllAccessPack12months_pro) permanentPurchaseLabel.stringValue = model.getProductPrice(.pdfToOfficePackPermanent_pro) } } else if model.state == .pro_Advanced { let platforms = KMMemberInfo.shared.vip_platforms let platformsArray = platforms .components(separatedBy: ",") .map { $0.trimmingCharacters(in: .whitespaces) } if KMMemberInfo.shared.userScenarioType == .pro_type1 { if platformsArray.count == 1 { advancedPlanPurchaseLabel.stringValue = model.getProductPrice(.advancedAddDevicesAllAccessPack12months_pro) } else if platformsArray.count == 2 { advancedPlanPurchaseLabel.stringValue = model.getProductPrice(.advancedAdd2DevicesAllAccessPack12months_pro) } else { advancedPlanPurchaseLabel.stringValue = model.getProductPrice(.advancedAdd2DevicesAllAccessPack12months_pro) } } else if KMMemberInfo.shared.userScenarioType == .pro_type4 { if platformsArray.count == 1 { advancedPlanPurchaseLabel.stringValue = model.getProductPrice(.advancedAddDevicesAllAccessPack12months_pro) } else if platformsArray.count == 2 { advancedPlanPurchaseLabel.stringValue = model.getProductPrice(.advancedAdd2DevicesAllAccessPack12months_pro) } else { advancedPlanPurchaseLabel.stringValue = model.getProductPrice(.advancedAdd2DevicesAllAccessPack12months_pro) } } } else { advancedPlanPurchaseLabel.stringValue = model.getProductPrice(.fourDevicesAllAccessPack12months_pro) } #endif } private func initializeUI() -> Void { btnRestore.setTitleColor(NSColor(named: "FFFFFF") ?? NSColor.white) btnRestore.font = NSFont.SFProTextRegularFont(13) btnRestore.mouseMoveCallback = { [weak self] mouseEntered in guard let self = self else { return } if mouseEntered { self.btnRestore.setTitleColor(NSColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.7)) } else { self.btnRestore.setTitleColor(NSColor(named: "FFFFFF") ?? NSColor.white) } } privacyPolicyBtn.setTitleColor(NSColor(named: "FFFFFF") ?? NSColor.white) privacyPolicyBtn.font = NSFont.SFProTextRegularFont(13) privacyPolicyBtn.mouseMoveCallback = { [weak self] mouseEntered in guard let self = self else { return } if mouseEntered { self.privacyPolicyBtn.setTitleColor(NSColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.7)) } else { self.privacyPolicyBtn.setTitleColor(NSColor(named: "FFFFFF") ?? NSColor.white) } } termOfSerBtn.setTitleColor(NSColor(named: "FFFFFF") ?? NSColor.white) termOfSerBtn.font = NSFont.SFProTextRegularFont(13) termOfSerBtn.mouseMoveCallback = { [weak self] mouseEntered in guard let self = self else { return } if mouseEntered { self.termOfSerBtn.setTitleColor(NSColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.7)) } else { self.termOfSerBtn.setTitleColor(NSColor(named: "FFFFFF") ?? NSColor.white) } } value1InfoTypeBox.fillColor = NSColor.clear value1InfoTypeBox.borderWidth = 0 sixMouthProLabel.textColor = .white sixMouthProLabel.font = NSFont.UbuntuMediumFontWithSize(14) sixMouthProLabel.lineBreakMode = .byTruncatingTail sixMouthProLabel.toolTip = sixMouthProLabel.stringValue sixMouthProLabel.alignment = .center oneYearProLabel.textColor = .white oneYearProLabel.font = NSFont.UbuntuMediumFontWithSize(14) oneYearProLabel.lineBreakMode = .byTruncatingTail oneYearProLabel.toolTip = oneYearProLabel.stringValue oneYearProLabel.alignment = .center switchBox.fillColor = NSColor(white: 1, alpha: 0.2) switchBox.cornerRadius = 12 switchBox.borderWidth = 1 switchBox.borderColor = NSColor(red: 235/255, green: 236/255, blue: 240/255, alpha: 1.0) switchCircleView.wantsLayer = true switchCircleView.layer?.cornerRadius = 8 switchCircleView.layer?.backgroundColor = NSColor.white.cgColor switchBtn.title = "" switchBtn.target = self switchBtn.action = #selector(switchAction) saleLabel.isHidden = true saleLabel.superview?.isHidden = true saleBox.borderWidth = 0 topMainBox.contentView = topMainView topsubBox1.isHidden = false topsubBox2.isHidden = false topsubBox3.isHidden = false topsubBox4.isHidden = false topSelectBox1.isHidden = true topSelectBox2.isHidden = true topSelectBox3.isHidden = true topSelectBox4.isHidden = true freeInfoLabel.font = NSFont.UbuntuMediumFontWithSize(14) standardPlanInfoLabel.font = NSFont.UbuntuMediumFontWithSize(14) advancedPlanInfoLabel.font = NSFont.UbuntuMediumFontWithSize(14) permanentInfoLabel.font = NSFont.UbuntuMediumFontWithSize(14) standardPlanPurchaseSubLabel.font = NSFont.SFProTextRegularFont(11) standardPlanPurchaseSubLabel2.font = NSFont.SFProTextRegularFont(11) advancedPlanPurchaseSubLabel.font = NSFont.SFProTextRegularFont(11) advancedPlanPurchaseSubLabel2.font = NSFont.SFProTextRegularFont(11) permanentPurchaseSubLabel.font = NSFont.UbuntuMediumFontWithSize(11) permanentPurchaseSubLabel2.font = NSFont.SFProTextRegularFont(11) value1PurchaseLabel.font = NSFont.UbuntuBoldFontWithSize(20) standardPlanPurchaseLabel.font = NSFont.UbuntuBoldFontWithSize(20) standardPlanPurchaseLabel.textColor = NSColor(red: 1.0, green: 231.0/255.0, blue: 106.0/255.0, alpha: 1.0) advancedPlanPurchaseLabel.font = NSFont.UbuntuBoldFontWithSize(20) advancedPlanPurchaseLabel.textColor = NSColor(red: 1.0, green: 231.0/255.0, blue: 106.0/255.0, alpha: 1.0) permanentPurchaseLabel.font = NSFont.UbuntuBoldFontWithSize(20) permanentPurchaseLabel.textColor = NSColor(red: 1.0, green: 231.0/255.0, blue: 106.0/255.0, alpha: 1.0) backgroundImage1.wantsLayer = true backgroundImage1.layer?.cornerRadius = 12 backgroundImage1.layer?.masksToBounds = true backgroundImage3.wantsLayer = true backgroundImage3.layer?.cornerRadius = 12 backgroundImage3.layer?.masksToBounds = true advancedPlanPurchaseButton.wantsLayer = true advancedPlanPurchaseButton.layer?.backgroundColor = NSColor(red: 1, green: 94/255, blue: 44/255, alpha: 1).cgColor advancedPlanPurchaseButton.layer?.cornerRadius = value1PurchaseButton.frame.height / 2 standardPlanPurchaseLbl.textColor = NSColor(named: "5528FF") ?? .blue advancedPlanPurchaseLbl.textColor = NSColor(named: "FFFFFF") ?? .white permanentPurchaseLbl.textColor = NSColor(named: "5528FF") ?? .blue var contentWidth = 970.0 var contentHeight = 660.0 if model.state == .dmg_Base { contentWidth = 970.0 contentHeight = 660.0 topsubBox1.contentView = freeInfoView topsubBox2.contentView = standardPlanInfoView topsubBox3.contentView = advancedPlanInfoView topsubBox4.contentView = permanentInfoView backgroundImage1.isHidden = true topSelectBox3.isHidden = false topsubBoxWidth1.constant = 149.5 topsubBoxWidth2.constant = 149.5 topsubBoxWidth3.constant = 149.5 topsubBoxWidth3.constant = 149.5 } else if model.state == .dmg_Upgrades1 { contentWidth = 689.0 contentHeight = 660.0 topsubBox2.isHidden = true topsubBox3.isHidden = true topsubBox4.isHidden = true topsubBox1.contentView = advancedPlanInfoView topSelectBox1.isHidden = false topsubBoxWidth1.constant = 320.0 } else if model.state == .dmg_Upgrades2 { contentWidth = 689.0 contentHeight = 660.0 topsubBox2.isHidden = true topsubBox3.isHidden = true topsubBox4.isHidden = true topsubBox1.contentView = advancedPlanInfoView topSelectBox1.isHidden = false topsubBoxWidth1.constant = 320.0 } else if model.state == .lite_Base { contentWidth = 970.0 contentHeight = 660.0 topsubBox4.isHidden = true topsubBox1.contentView = freeInfoView topsubBox2.contentView = value1InfoView topsubBox3.contentView = permanentInfoView topSelectBox2.isHidden = false topsubBoxWidth1.constant = 179.0 topsubBoxWidth2.constant = 240.0 topsubBoxWidth3.constant = 179.0 } else if model.state == .lite_MacWindows { contentWidth = 689.0 contentHeight = 660.0 topsubBox2.isHidden = true topsubBox3.isHidden = true topsubBox4.isHidden = true topsubBox1.contentView = advancedPlanInfoView topSelectBox1.isHidden = false topsubBoxWidth1.constant = 320.0 } else if model.state == .pro_Base { contentWidth = 970.0 contentHeight = 660.0 topsubBox3.isHidden = true topsubBox4.isHidden = true topsubBox1.contentView = advancedPlanInfoView topsubBox2.contentView = permanentInfoView backgroundImage1.isHidden = true topSelectBox1.isHidden = false topsubBoxWidth1.constant = 299.0 topsubBoxWidth2.constant = 299.0 } else if model.state == .pro_Advanced { contentWidth = 689.0 contentHeight = 660.0 topsubBox2.isHidden = true topsubBox3.isHidden = true topsubBox4.isHidden = true topsubBox1.contentView = advancedPlanInfoView topSelectBox1.isHidden = false topsubBoxWidth1.constant = 320.0 } else { // model.state == .trial contentWidth = 785.0 contentHeight = 660.0 topsubBox3.isHidden = true topsubBox4.isHidden = true topsubBox1.contentView = freeInfoView topsubBox2.contentView = advancedPlanInfoView backgroundImage1.isHidden = true topSelectBox2.isHidden = false topsubBoxWidth1.constant = 179.0 topsubBoxWidth2.constant = 240.0 } self.window?.setContentSize(NSSize(width: contentWidth, height: contentHeight)) self.window?.maxSize = NSSize(width: contentWidth, height: contentHeight) self.window?.minSize = NSSize(width: contentWidth, height: contentHeight) box.fillColor = KMAppearance.Layout.l1Color() labelbox.fillColor = .clear tableView.backgroundColor = KMAppearance.Layout.l0Color() textView.backgroundColor = .clear textView.textColor = NSColor.white.withAlphaComponent(0.5) value1PurchaseSubLabel.textColor = KMAppearance.Layout.w0Color() standardPlanPurchaseSubLabel.textColor = KMAppearance.Layout.w0Color() standardPlanPurchaseSubLabel2.textColor = KMAppearance.Layout.w0Color() advancedPlanPurchaseSubLabel.textColor = KMAppearance.Layout.w0Color() advancedPlanPurchaseSubLabel2.textColor = KMAppearance.Layout.w0Color() permanentPurchaseSubLabel.textColor = KMAppearance.Layout.w0Color() permanentPurchaseSubLabel2.textColor = KMAppearance.Layout.w0Color() #if VERSION_FREE #if VERSION_DMG // DMG #else // AppStore 免费版本 refreshPurchaseSwitch() #endif #else // AppStore 付费版 #endif } @objc func reloadData() { saleBox.isHidden = true saleIv.isHidden = true if model.isShowSale { if let info = KMAdvertisementManager.manager.info.StoreUserRecovery?.content?.first, let url = URL(string: KMAdvertisementModelTransition.transitionImagePath(image: info.saleImage, highlight: true)) { KMAdvertisementImage.imageWithURL(url: url) { [weak self] image in guard let self = self else { return } self.saleIv.image = image self.saleBox.isHidden = false self.saleIv.isHidden = false self.trackEvent_free(eventName: "PromotionalOffer", params: ["PUW_Exposure": "PUW_7OffUpgrade"], platform: .AppCenter) } } } else { saleBox.isHidden = true } // if KMMemberInfo.shared.isMemberAllFunction == true { // value1PurchaseButton.isEnabled = false // permanentPurchaseButton.isEnabled = false // permanentPurchaseLbl.textColor = NSColor(red: 85/255.0, green: 40/255.0, blue: 255/255.0, alpha: 0.5) // } else { // value1PurchaseButton.isEnabled = true // permanentPurchaseButton.isEnabled = true // permanentPurchaseLbl.textColor = NSColor(red: 85/255.0, green: 40/255.0, blue: 255/255.0, alpha: 1.0) // } permanentPurchaseLbl.font = NSFont.UbuntuBoldFontWithSize(16) value1PurchaseOriPriceLbl.font = NSFont.SFProTextRegularFont(12) } func refreshPurchaseSwitch() -> Void { #if VERSION_FREE #if VERSION_DMG // DMG #else // AppStore 免费版本 reloadData() if model.isPurchaseSwitch { discountYearly.isHidden = false sixMouthProLabel.textColor = NSColor(red: 117/255.0, green: 119/255.0, blue: 128/255.0, alpha: 1.0) oneYearProLabel.textColor = NSColor.white switchCircleLeftConst.constant = 22 value1InfoLabel.stringValue = NSLocalizedString("1-year Plan", comment: "") } else { discountYearly.isHidden = true sixMouthProLabel.textColor = NSColor.white oneYearProLabel.textColor = NSColor(red: 117/255.0, green: 119/255.0, blue: 128/255.0, alpha: 1.0) switchCircleLeftConst.constant = 4 value1InfoLabel.stringValue = NSLocalizedString("6-Month Plan", comment: "") } value1PurchaseBtnLblBtn.isEnabled = value1PurchaseButton.isEnabled value1PurchaseBtnLbl.stringValue = NSLocalizedString("Subscribe", comment: "") let fourDevicesAllAccessPackNew6Months_lite = IAPProductsManager.default().fourDevicesAllAccessPackNew6Months_lite let fourDevicesAllAccessPackNew12months_lite = IAPProductsManager.default().fourDevicesAllAccessPackNew12months_lite let isTrialPeriod_newlyMonth = fourDevicesAllAccessPackNew6Months_lite?.isTrialPeriod ?? false let isSubscribed_newlyMonth = fourDevicesAllAccessPackNew6Months_lite?.isSubscribed ?? false let isTrialPeriod_year = fourDevicesAllAccessPackNew12months_lite?.isTrialPeriod ?? false let isSubscribed_year = fourDevicesAllAccessPackNew12months_lite?.isSubscribed ?? false let introductoryPrice_newlyMonth = fourDevicesAllAccessPackNew6Months_lite?.product?.introductoryPrice let introductoryPrice_year = fourDevicesAllAccessPackNew12months_lite?.product?.introductoryPrice if isTrialPeriod_newlyMonth { value1PurchaseBtnLbl.stringValue = NSLocalizedString("Subscribe", comment: "") if isSubscribed_newlyMonth { value1PurchaseBtnLbl.stringValue = NSLocalizedString("Subscribed", comment: "") } } else if isTrialPeriod_year { value1PurchaseBtnLbl.stringValue = NSLocalizedString("Subscribe", comment: "") if isSubscribed_year { value1PurchaseBtnLbl.stringValue = NSLocalizedString("Subscribed", comment: "") } } else { if isSubscribed_newlyMonth || isSubscribed_year { value1PurchaseBtnLbl.stringValue = NSLocalizedString("Subscribed", comment: "") } else { value1PurchaseBtnLbl.stringValue = NSLocalizedString("Free Trial", comment: "") if #available(macOS 10.13.2, *) { #if !VERSION_DMG if introductoryPrice_newlyMonth == nil { value1PurchaseBtnLbl.stringValue = NSLocalizedString("Subscribe", comment: "") } else if introductoryPrice_year == nil { value1PurchaseBtnLbl.stringValue = NSLocalizedString("Subscribe", comment: "") } #endif } } } value1PurchaseBtnLbl.textColor = value1PurchaseBtnLblBtn.isEnabled ? NSColor.white : NSColor.white.withAlphaComponent(0.5) permanentPurchaseLblBtn.isEnabled = permanentPurchaseButton.isEnabled standardPlanPurchaseLblBtn.isEnabled = standardPlanPurchaseButton.isEnabled advancedPlanPurchaseLblBtn.isEnabled = advancedPlanPurchaseButton.isEnabled let averageMonthPrice: String? let monthPrice: String? let averageMonthPriceDiscount: String? let monthPriceDiscount: String? var allPrice: String? let isOffers_newlyMonth = fourDevicesAllAccessPackNew6Months_lite?.isOffers ?? false let isOffers_year = fourDevicesAllAccessPackNew12months_lite?.isOffers ?? false if isOffers_newlyMonth { specialView1.isHidden = false averageMonthPrice = fourDevicesAllAccessPackNew6Months_lite?.offersAveragePrice() monthPrice = fourDevicesAllAccessPackNew6Months_lite?.offersPrice() averageMonthPriceDiscount = fourDevicesAllAccessPackNew6Months_lite?.discountAveragePrice() monthPriceDiscount = fourDevicesAllAccessPackNew6Months_lite?.discountPrice() } else if isOffers_year { averageMonthPrice = fourDevicesAllAccessPackNew12months_lite?.offersAveragePrice() monthPrice = fourDevicesAllAccessPackNew12months_lite?.offersPrice() averageMonthPriceDiscount = fourDevicesAllAccessPackNew12months_lite?.discountAveragePrice() monthPriceDiscount = fourDevicesAllAccessPackNew12months_lite?.discountPrice() } else { specialView1.isHidden = true if !model.isPurchaseSwitch { averageMonthPrice = fourDevicesAllAccessPackNew6Months_lite?.averagePrice() monthPrice = fourDevicesAllAccessPackNew6Months_lite?.price() averageMonthPriceDiscount = fourDevicesAllAccessPackNew6Months_lite?.discountAveragePrice() monthPriceDiscount = fourDevicesAllAccessPackNew6Months_lite?.discountPrice() } else { averageMonthPrice = fourDevicesAllAccessPackNew12months_lite?.averagePrice() monthPrice = fourDevicesAllAccessPackNew12months_lite?.price() averageMonthPriceDiscount = fourDevicesAllAccessPackNew12months_lite?.discountAveragePrice() monthPriceDiscount = fourDevicesAllAccessPackNew12months_lite?.discountPrice() } } if let allAccessPackPermanent_lite = IAPProductsManager.default().allAccessPackPermanent_lite, allAccessPackPermanent_lite.isOffers { specialView.isHidden = false allPrice = allAccessPackPermanent_lite.offersPrice() } else { specialView.isHidden = true allPrice = IAPProductsManager.default().allAccessPackPermanent_lite?.price() } var off = 1 - (IAPProductsManager.default().allAccessPackPermanent_lite?.offers ?? 0) var offString = String(format: "%.f%%", off * 100) specialLabel.stringValue = String(format: NSLocalizedString("%@ off", comment: ""), offString) if model.isPurchaseSwitch { off = 1 - IAPProductsManager.default().fourDevicesAllAccessPackNew12months_lite.offers } else { off = 1 - IAPProductsManager.default().fourDevicesAllAccessPackNew6Months_lite.offers } offString = String(format: "%.f%%", off * 100) specialLabel1.stringValue = String(format: NSLocalizedString("%@ off", comment: ""), offString) let image = NSImage(named: "KMImageNameCompareTagBG") specialImageView.image = image specialImageView1.image = image if KMKdanRemoteConfig.remoteConfig.showAPP_AveragePrice() { if model.isShowSale { value1PurchaseSubLabel.stringValue = String(format: "%@/%@", averageMonthPriceDiscount ?? "", NSLocalizedString("mon", comment: "")) value1PurchaseOriPriceLbl.stringValue = String(format: "%@/%@", averageMonthPrice ?? "", NSLocalizedString("mon", comment: "")) value1PurchaseOriPriceLbl.isHidden = false billedTopConst.constant = 16 } else { value1PurchaseLabel.stringValue = String(format: "%@/%@", averageMonthPrice ?? "", NSLocalizedString("mon", comment: "")) value1PurchaseOriPriceLbl.isHidden = true billedTopConst.constant = 0 } } else { if model.isPurchaseSwitch { if model.isShowSale { value1PurchaseLabel.stringValue = String(format: "%@/%@", monthPriceDiscount ?? "", NSLocalizedString("12 months", comment: "")) value1PurchaseOriPriceLbl.stringValue = String(format: "%@/%@", monthPrice ?? "", NSLocalizedString("12 months", comment: "")) value1PurchaseOriPriceLbl.isHidden = false billedTopConst.constant = 16 } else { value1PurchaseLabel.stringValue = String(format: "%@/%@", monthPrice ?? "", NSLocalizedString("12 months", comment: "")) value1PurchaseOriPriceLbl.isHidden = true billedTopConst.constant = 0 } } else { if model.isShowSale { value1PurchaseLabel.stringValue = String(format: "%@/%@", monthPriceDiscount ?? "", NSLocalizedString("6mon", comment: "")) value1PurchaseOriPriceLbl.stringValue = String(format: "%@/%@", monthPrice ?? "", NSLocalizedString("6mon", comment: "")) value1PurchaseOriPriceLbl.isHidden = false billedTopConst.constant = 16 } else { value1PurchaseLabel.stringValue = String(format: "%@/%@", monthPrice ?? "", NSLocalizedString("6mon", comment: "")) value1PurchaseOriPriceLbl.isHidden = true billedTopConst.constant = 0 } } } value1PurchaseLabel.textColor = NSColor(red: 1.0, green: 231.0/255.0, blue: 106.0/255.0, alpha: 1.0) value1PurchaseOriPriceLbl.textColor = NSColor(white: 1.0, alpha: 0.7) value1PurchaseOriPriceLbl.attributedStringValue = NSAttributedString(string: value1PurchaseOriPriceLbl.stringValue, attributes: [ .font : NSFont.SFProTextRegularFont(12), .foregroundColor: NSColor(white: 1, alpha: 0.7), .strikethroughStyle: NSUnderlineStyle.single.rawValue ]) var tTips: String if model.isShowSale { tTips = String(format: NSLocalizedString("%@ for the first 6-month subscription", comment: ""), monthPriceDiscount ?? "") } else { tTips = String(format: NSLocalizedString("Billed every 6 months at %@", comment: ""), monthPrice ?? "") } if value1PurchaseBtnLbl.stringValue == NSLocalizedString("Free Trial", comment: "") { tTips = String(format: NSLocalizedString("Billed every 6 months at %@ after 7-day trial ends.", comment: ""), monthPrice ?? "") #if !VERSION_DMG if model.isPurchaseSwitch { if let fourDevicesAllAccessPackNew12months_lite = IAPProductsManager.default().fourDevicesAllAccessPackNew12months_lite.product { if #available(macOS 10.13.2, *) { let trialDays = IAPProductsManager.default().fourDevicesAllAccessPackNew12months_lite.product.introductoryPrice?.subscriptionPeriod.numberOfUnits tTips = String( format: NSLocalizedString("Billed every 12 months at %@ after %@-day trial ends.", comment: ""), monthPrice ?? "", "\(trialDays ?? 0)" ) } } } else { if let fourDevicesAllAccessPackNew6Months_lite = IAPProductsManager.default().fourDevicesAllAccessPackNew6Months_lite.product { if #available(macOS 10.13.2, *) { let trialDays = fourDevicesAllAccessPackNew6Months_lite.introductoryPrice?.subscriptionPeriod.numberOfUnits tTips = String( format: NSLocalizedString("Billed every 6 months at %@ after %@-day trial ends.", comment: ""), monthPrice ?? "", "\(trialDays ?? 0)" ) } } } #endif } if #available(macOS 10.13.2, *) { #if !VERSION_DMG if model.isPurchaseSwitch { if (IAPProductsManager.default().fourDevicesAllAccessPackNew12months_lite.product?.introductoryPrice == nil) { if model.isShowSale { tTips = String(format: NSLocalizedString("%@ for the first year's subscription", comment: ""), monthPriceDiscount ?? "") } else { tTips = String(format: NSLocalizedString("Billed every 12 months at %@", comment: ""), monthPrice ?? "") } } } else { if (IAPProductsManager.default().fourDevicesAllAccessPackNew6Months_lite.product.introductoryPrice == nil) { if model.isShowSale { tTips = String(format: NSLocalizedString("%@ for the first 6-month subscription", comment: ""), monthPriceDiscount ?? "") } else { tTips = String(format: NSLocalizedString("Billed every 6 months at %@", comment: ""), monthPrice ?? "") } } } #endif } value1PurchaseSubLabel.stringValue = String(format: NSLocalizedString("%@", comment: ""), tTips) value1PurchaseBtnLbl.font = NSFont.UbuntuBoldFontWithSize(16) #endif #else // AppStore 付费版 #endif } @objc func reloadDataContent() { tableView.reloadData() } func addWaitingView(to view: NSView) { removeWaitingView(from: view) let waitingView = WaitingView(frame: view.bounds) waitingView.autoresizingMask = [.width, .height] view.addSubview(waitingView) waitingView.startAnimation() } func removeWaitingView(from view: NSView) { for subview in view.subviews { if subview is WaitingView { subview.removeFromSuperview() break } } } func showDiscount() { discountYearly.isHidden = true discountPermanent.isHidden = true discountPermanent2.isHidden = true discountPermanent.isHidden = true #if VERSION_FREE #if VERSION_DMG // DMG if model.state != .dmg_Base { return } #else // AppStore 免费版本 if model.state != .lite_Base { return } #endif #else // AppStore 付费版 if model.state != .pro_Base { return } #endif let content = KMAdvertisementManager.manager.info.StoreUserRecovery?.content ?? [] if content.count > 0 { for (index, item) in content.enumerated() { switch index { case 1: configureDiscountView(discountYearly, with: item) case 2: configureDiscountView(discountPermanent4, with: item) case 3: configureDiscountView(discountPermanent2, with: item) case 4: configureDiscountView(discountPermanent, with: item) default: break } } } } private func configureDiscountView(_ view: NSImageView, with itemInfo: KMAdvertisementItemInfo) { if KMAdvertisementManager.checkAdvertisementValid(itemInfo) { view.isHidden = false let urlString = KMAdvertisementModelTransition.transitionImagePath(image: itemInfo.image, highlight: false) if let url = URL(string: urlString) { view.image = KMAdvertisementImage.imageWithURL(url: url) { image in view.image = image } } } else { view.isHidden = true } } private func showAlert(message: String) { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = message alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) alert.runModal() } private func handleVerificationFailure(msg: KMMemberProductResult?) { var message = msg?.msg ?? "" if msg?.code == 344 { message = NSLocalizedString("The current ticket has created an order.", tableName: "MemberCenterLocalizable", comment: "") } if !message.isEmpty { showAlert(message: message) } } // MARK: Button Actions @IBAction func buttonItemClicked_Subscribe(_ sender: Any) { if KMMemberInfo.shared.validFlag == "5" { KMMemberPromptWC.shared.showWindow(nil) KMMemberPromptWC.shared.tipType = .signouting } else { if model.isPurchaseSwitch { IAPProductsManager.default().makeSubProduct(IAPProductsManager.default().fourDevicesAllAccessPackNew12months_lite, discount: model.isCancelAutoRenew) } else { IAPProductsManager.default().makeSubProduct(IAPProductsManager.default().fourDevicesAllAccessPackNew6Months_lite, discount: model.isCancelAutoRenew) } addWaitingView(to: window?.contentView ?? NSView()) FMTrackEventManager.defaultManager.trackEvent(event: "PUW", withProperties: ["PUW_Btn": "PUW_Btn_Upgrade_6Mon"]) if model.isShowSale { kEventTag = 1 if model.isPurchaseSwitch { trackEvent_free(eventName: "PromotionalOffer", params: ["PUW_Btn": "PUW_7OffUpgrade_12Mon"], platform: .AppCenter) } else { trackEvent_free(eventName: "PromotionalOffer", params: ["PUW_Btn": "PUW_7OffUpgrade_6Mon"], platform: .AppCenter) } } } } func showDiscountToSaveWindow() { guard KMDiscountToSaveWindowController.needShow() else { return } guard let info = KMAdvertisementManager.manager.info.StoreUserRecovery?.content?.first, let url = URL(string: KMAdvertisementModelTransition.transitionImagePath(image: info.image, highlight: true)) else { return } addWaitingView(to: window?.contentView ?? NSView()) KMAdvertisementImage.imageWithURL(url: url) { [weak self] image in guard let self = self else { return } self.removeWaitingView(from: self.window?.contentView ?? NSView()) self.trackEvent_free(eventName: "PromotionalOffer", params: ["PUW_Exposure": "PUW_PromotionalOffer"], platform: .AppCenter) let winC = KMDiscountToSaveWindowController(windowNibName: "KMDiscountToSaveWindowController") self.discountToSaveWinC = winC self.window?.beginSheet(winC.window!) { _ in } winC.imgIv.image = image winC.itemClick = { idx in self.window?.endSheet(self.discountToSaveWinC?.window ?? NSWindow()) self.discountToSaveWinC = nil if idx == 2 { self.trackEvent_free(eventName: "PromotionalOffer", params: ["PUW_Btn": "PUW_PromotionalOffer_Subscribe"], platform: .AppCenter) } else if idx == 1 { self.trackEvent_free(eventName: "PromotionalOffer", params: ["PUW_Btn": "PUW_PromotionalOffer_Cancel"], platform: .AppCenter) } } } } @IBAction func buttonItemClicked_Restore(_ sender: Any) { model.productRestore() addWaitingView(to: window?.contentView ?? NSView()) } @IBAction func buttonItemClicked_Upgrade(_ sender: Any) { NSWorkspace.shared.open(URL(string: "macappstore://apps.apple.com/app/id825459243?mt=12")!) FMTrackEventManager.defaultManager.trackEvent(event: "PUW", withProperties: ["PUW_Btn": "PUW_Btn_Upgrade_Premium"]) } @IBAction func privacyPolicyAction(_ sender: NSButton) { model.privacyPolicyAction() } @IBAction func termOfSerAction(_ sender: NSButton) { model.termOfSerAction() } @IBAction func buttonItemClicked_LicenseUpgrade(_ sender: Any) { IAPProductsManager.default().make(IAPProductsManager.default().allAccessPackPermanent_lite) addWaitingView(to: window?.contentView ?? NSView()) FMTrackEventManager.defaultManager.trackEvent(event: "PUW", withProperties: ["PUW_Btn": "PUW_Btn_Upgrade_Permanent"]) } @IBAction func buttonItemClicked_AboutSubscriptions(_ sender: Any) { let tMessage = """ Subscription plans: Subscriptions: USD $39.99/(6 months), USD $79.99/(one-time purchase) - Subscriptions will be charged through your iTunes account at the confirmation of purchase. - Your subscriptions will automatically renew unless cancelled at least 24 hours before the end of current subscription period. - Your iTunes account will be charged for renewal within 24-hours prior to the end of the current period, for the same duration and at the current subscription price. - You may manage your subscriptions in your iTunes Account Settings after purchase. - No cancellation of the current subscription is allowed during an active subscription period. - Terms of service: \nhttps://www.pdfreaderpro.com/terms_of_service - Privacy Policy: \nhttps://www.pdfreaderpro.com/privacy-policy """ let alert = NSAlert() alert.alertStyle = .informational alert.messageText = NSLocalizedString(tMessage, comment: "") alert.runModal() } @IBAction func buttonItemClicked_CloseWindow(_ sender: Any) { close() } @objc func switchAction(_ sender: NSButton) { model.isPurchaseSwitch.toggle() } @IBAction func appstorePurchaseAction(_ sender: NSButton) { if KMMemberInfo.shared.validFlag == "5" { KMMemberPromptWC.shared.showWindow(nil) KMMemberPromptWC.shared.tipType = .signouting } else { #if !VERSION_DMG addWaitingView(to: window?.contentView ?? NSView()) #endif model.appstorePurchaseAction(sender.tag) { [weak self] success, msg in guard let self = self else { return } } #if VERSION_DMG close() #endif } } // MARK: - InAppPurchaseManager Notification @objc func IAPProductFailedNotification(_ notification: Notification) { removeWaitingView(from: window?.contentView ?? NSView()) } @objc func IAPProductPurchasedNotification(_ notification: Notification) { removeWaitingView(from: window?.contentView ?? NSView()) reloadData() #if VERSION_FREE #if VERSION_DMG // DMG #else // AppStore 免费版本 if IAPProductsManager.default().fourDevicesAllAccessPackNew6Months_lite.isSubscribed { if kEventTag == 1 { trackEvent_free(eventName: "PromotionalOffer", params: ["PUW_Btn": "PUW_7OffUpgrade_6MonthPaid"], platform: .AppCenter) } } else if IAPProductsManager.default().fourDevicesAllAccessPackNew12months_lite.isSubscribed { if kEventTag == 1 { trackEvent_free(eventName: "PromotionalOffer", params: ["PUW_Btn": "PUW_7OffUpgrade_12MonthPaid"], platform: .AppCenter) } } #endif #else // AppStore 付费版 #endif } @objc func IAPProductLoadedNotification(_ notification: Notification) { reloadData() } @objc func IAPProductRestoreFinishedNotification(_ notification: Notification) { removeWaitingView(from: window?.contentView ?? NSView()) reloadData() } @objc func IAPProductRestoreFailedNotification(_ notification: Notification) { removeWaitingView(from: window?.contentView ?? NSView()) } @objc func IAPSubscriptionLoadedNotification(_ notification: Notification) { removeWaitingView(from: window?.contentView ?? NSView()) reloadData() DispatchQueue.global().async { [weak self] in guard let self = self else { return } if KMMemberInfo.shared.isLogin { self.model.appStoreEquityVerification(notification) { success, msg in DispatchQueue.main.async { if success { // self.showAlert(message: NSLocalizedString("Binding Interest Success!", comment: "")) KMUserInfoVCModel().refreshUserInfo { success, msg,dic in } self.window?.close() } else { self.handleVerificationFailure(msg: msg) } } } } } } @objc func themeChanged(_ notification: Notification) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { self.box.fillColor = KMAppearance.Layout.l1Color() self.labelbox.fillColor = .clear self.tableView.backgroundColor = KMAppearance.Layout.l0Color() self.textView.backgroundColor = .clear self.textView.textColor = NSColor.white.withAlphaComponent(0.5) self.value1PurchaseSubLabel.textColor = KMAppearance.Layout.w0Color() self.permanentPurchaseSubLabel.textColor = KMAppearance.Layout.w0Color() } } // MARK: Bind Method func bindViewModel() -> Void { model.$state .receive(on: RunLoop.main) .sink { [weak self] newValue in } .store(in: &cancellables) model.$isPurchaseSwitch .receive(on: RunLoop.main) .sink { [weak self] newValue in self?.reloadDataContent() #if VERSION_FREE #if VERSION_DMG // DMG #else // AppStore 免费版本 self?.refreshPurchaseSwitch() #endif #else // AppStore 付费版 #endif } .store(in: &cancellables) } } extension KMProductCompareWC: NSTableViewDelegate, NSTableViewDataSource { // MARK: NSTableViewDataSource Methods func numberOfRows(in tableView: NSTableView) -> Int { return model.dataSource.count } // tableView:viewForTableColumn:row func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { let object = model.dataSource[row] if let stringObject = object as? String { if let cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier("KMProductCompareTableHeaderCell"), owner: self) as? KMProductCompareTableHeaderCell { cellView.textField?.stringValue = NSLocalizedString(stringObject, comment: "") if model.state == .dmg_Base { cellView.selectBoxLeft.constant = 352.0 + 149.5 + 149.5 cellView.selectBoxWidth.constant = 149.5 } else if model.state == .dmg_Upgrades1 { cellView.selectBoxLeft.constant = 352.0 cellView.selectBoxWidth.constant = 320.0 } else if model.state == .dmg_Upgrades2 { cellView.selectBoxLeft.constant = 352.0 cellView.selectBoxWidth.constant = 320.0 } else if model.state == .lite_Base { cellView.selectBoxLeft.constant = 352.0 + 179.0 cellView.selectBoxWidth.constant = 240.0 } else if model.state == .lite_MacWindows { cellView.selectBoxLeft.constant = 352.0 cellView.selectBoxWidth.constant = 320.0 } else if model.state == .pro_Base { cellView.selectBoxLeft.constant = 352.0 cellView.selectBoxWidth.constant = 299.0 } else if model.state == .pro_Advanced { cellView.selectBoxLeft.constant = 352.0 cellView.selectBoxWidth.constant = 320.0 } else { // model.state == .trial cellView.selectBoxLeft.constant = 352.0 + 179.0 cellView.selectBoxWidth.constant = 240.0 } return cellView } } else if let arrayObject = object as? [String] { if let cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier("KMProductCompareTableCell"), owner: self) as? KMProductCompareTableCell { let value1 = arrayObject.indices.contains(0) ? arrayObject[0] : "" let value2 = arrayObject.indices.contains(1) ? arrayObject[1] : "" let value3 = arrayObject.indices.contains(2) ? arrayObject[2] : "" let value4 = arrayObject.indices.contains(3) ? arrayObject[3] : "" let value5 = arrayObject.indices.contains(4) ? arrayObject[4] : "" let value6 = arrayObject.indices.contains(5) ? arrayObject[5] : "" cellView.textField?.stringValue = NSLocalizedString(value1, comment: "") cellView.lineBox?.isHidden = false if row + 1 == model.dataSource.count || model.dataSource[row + 1] is String { cellView.lineBox?.isHidden = true } checkOrNot(cellView) cellView.boxBottomConst?.constant = (row + 2 == model.dataSource.count) ? 0 : -4 updateCellView(cellView, value1: value2, value2: value3, value3: value4, value4: value5, value5: value6) return cellView } } else if object is [String: Any] { if let cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier("KMProductCompareTableEmptyCell"), owner: self) as? KMProductCompareTableEmptyCell { return cellView } } return nil } // tableView:heightOfRow func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { let object = model.dataSource[row] var cellView: NSTableCellView? if let stringObject = object as? String { if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier("KMProductCompareTableHeaderCell"), owner: self) as? KMProductCompareTableHeaderCell { cell.textField?.stringValue = NSLocalizedString(stringObject, comment: "") cellView = cell } } else if let arrayObject = object as? [String] { if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier("KMProductCompareTableCell"), owner: self) as? KMProductCompareTableCell { cell.textField?.stringValue = NSLocalizedString(arrayObject[0], comment: "") cellView = cell } } else if object is [String: Any] { return 40 } if let view = cellView { view.layoutSubtreeIfNeeded() return view.frame.size.height } else { return 44 } } // Helper function to update cell view for multiple values private func updateCellView(_ cellView: KMProductCompareTableCell, value1: String, value2: String, value3: String, value4: String, value5: String) { if model.state == .dmg_Base { configureCellView(cellView, value: value1, label: cellView.value1Label, imageView: cellView.value1ImageView, platformsBox: cellView.platformsBox1) configureCellView(cellView, value: value2, label: cellView.value2Label, imageView: cellView.value2ImageView, platformsBox: cellView.platformsBox2) configureCellView(cellView, value: value3, label: cellView.value3Label, imageView: cellView.value3ImageView, platformsBox: cellView.platformsBox3) configureCellView(cellView, value: value4, label: cellView.value4Label, imageView: cellView.value4ImageView, platformsBox: cellView.platformsBox4) } else if model.state == .dmg_Upgrades1 { configureCellView(cellView, value: value3, label: cellView.value1Label, imageView: cellView.value1ImageView, platformsBox: cellView.platformsBox1) } else if model.state == .dmg_Upgrades2 { configureCellView(cellView, value: value3, label: cellView.value1Label, imageView: cellView.value1ImageView, platformsBox: cellView.platformsBox1) } else if model.state == .lite_Base { configureCellView(cellView, value: value1, label: cellView.value1Label, imageView: cellView.value1ImageView, platformsBox: cellView.platformsBox1) configureCellView(cellView, value: value3, label: cellView.value2Label, imageView: cellView.value2ImageView, platformsBox: cellView.platformsBox2) configureCellView(cellView, value: value4, label: cellView.value3Label, imageView: cellView.value3ImageView, platformsBox: cellView.platformsBox3) } else if model.state == .lite_MacWindows { configureCellView(cellView, value: value3, label: cellView.value1Label, imageView: cellView.value1ImageView, platformsBox: cellView.platformsBox1) } else if model.state == .pro_Base { configureCellView(cellView, value: value3, label: cellView.value1Label, imageView: cellView.value1ImageView, platformsBox: cellView.platformsBox1) configureCellView(cellView, value: value5, label: cellView.value2Label, imageView: cellView.value2ImageView, platformsBox: cellView.platformsBox2) } else if model.state == .pro_Advanced { configureCellView(cellView, value: value3, label: cellView.value1Label, imageView: cellView.value1ImageView, platformsBox: cellView.platformsBox1) } else { // model.state == .trial configureCellView(cellView, value: value1, label: cellView.value1Label, imageView: cellView.value1ImageView, platformsBox: cellView.platformsBox1) configureCellView(cellView, value: value3, label: cellView.value2Label, imageView: cellView.value2ImageView, platformsBox: cellView.platformsBox2) } } // Helper function to configure each individual value private func configureCellView(_ cellView: KMProductCompareTableCell, value: String, label: NSTextField?, imageView: NSImageView?, platformsBox: NSBox?) { platformsBox?.isHidden = true label?.isHidden = false imageView?.isHidden = false switch value { case "X": label?.stringValue = "" imageView?.image = NSImage(named: "compare_nosupport") case "✓": label?.stringValue = "" imageView?.image = NSImage(named: "compare_support") case "∞": label?.stringValue = "" imageView?.image = NSImage(named: "compare_00") default: if value == "mac, windows, ios, android" || value == "mac, windows" || value == "mac" { platformsBox?.isHidden = false label?.isHidden = true imageView?.isHidden = true if value == "mac, windows, ios, android" { // platformsBox?.contentView = platformsView4 } else if value == "mac, windows" { platformsBox?.contentView = platformsView2 } else if value == "mac" { platformsBox?.contentView = platformsView1 } } else { label?.stringValue = NSLocalizedString(value, comment: "") imageView?.image = nil } } } private func checkOrNot(_ cellView: KMProductCompareTableCell) { cellView.selectBox1.isHidden = true cellView.selectBox2.isHidden = true cellView.selectBox3.isHidden = true cellView.selectBox4.isHidden = true cellView.cellView1.isHidden = true cellView.cellView2.isHidden = true cellView.cellView3.isHidden = true cellView.cellView4.isHidden = true if model.state == .dmg_Base { cellView.selectBox3.isHidden = false cellView.cellView1.isHidden = false cellView.cellView2.isHidden = false cellView.cellView3.isHidden = false cellView.cellView4.isHidden = false cellView.viewWidth1.constant = 149.5 cellView.viewWidth2.constant = 149.5 cellView.viewWidth3.constant = 149.5 cellView.viewWidth4.constant = 149.5 } else if model.state == .dmg_Upgrades1 { cellView.selectBox1.isHidden = false cellView.cellView1.isHidden = false cellView.viewWidth1.constant = 320.0 } else if model.state == .dmg_Upgrades2 { cellView.selectBox1.isHidden = false cellView.cellView1.isHidden = false cellView.viewWidth1.constant = 320.0 } else if model.state == .lite_Base { cellView.selectBox2.isHidden = false cellView.cellView1.isHidden = false cellView.cellView2.isHidden = false cellView.cellView3.isHidden = false cellView.viewWidth1.constant = 179.0 cellView.viewWidth2.constant = 240.0 cellView.viewWidth3.constant = 240.0 } else if model.state == .lite_MacWindows { cellView.selectBox1.isHidden = false cellView.viewWidth1.constant = 320.0 cellView.cellView1.isHidden = false } else if model.state == .pro_Base { cellView.selectBox1.isHidden = false cellView.viewWidth1.constant = 299.0 cellView.cellView1.isHidden = false } else if model.state == .pro_Advanced { cellView.selectBox1.isHidden = false cellView.viewWidth1.constant = 320.0 cellView.cellView1.isHidden = false } else { // model.state == .trial cellView.cellView1.isHidden = false cellView.cellView2.isHidden = false cellView.selectBox2.isHidden = false cellView.viewWidth1.constant = 179.0 cellView.viewWidth2.constant = 240.0 } } } class KMProductCompareTableCell: NSTableCellView { @IBOutlet weak var bgColorView: NSView! @IBOutlet @objc weak var cellView1: NSView! @IBOutlet @objc weak var cellView2: NSView! @IBOutlet @objc weak var cellView3: NSView! @IBOutlet @objc weak var cellView4: NSView! @IBOutlet weak var value1Label: NSTextField! @IBOutlet weak var value2Label: NSTextField! @IBOutlet weak var value3Label: NSTextField! @IBOutlet weak var value4Label: NSTextField! @IBOutlet weak var value1ImageView: NSImageView! @IBOutlet weak var value2ImageView: NSImageView! @IBOutlet weak var value3ImageView: NSImageView! @IBOutlet weak var value4ImageView: NSImageView! @IBOutlet weak var lineBox: NSBox! @IBOutlet weak var boxBottomConst: NSLayoutConstraint! @IBOutlet @objc weak var platformsBox1: NSBox! @IBOutlet @objc weak var platformsBox2: NSBox! @IBOutlet @objc weak var platformsBox3: NSBox! @IBOutlet @objc weak var platformsBox4: NSBox! @IBOutlet @objc weak var selectBox1: NSBox! @IBOutlet @objc weak var selectBox2: NSBox! @IBOutlet @objc weak var selectBox3: NSBox! @IBOutlet @objc weak var selectBox4: NSBox! @IBOutlet weak var viewWidth1: NSLayoutConstraint! @IBOutlet weak var viewWidth2: NSLayoutConstraint! @IBOutlet weak var viewWidth3: NSLayoutConstraint! @IBOutlet weak var viewWidth4: NSLayoutConstraint! override func awakeFromNib() { super.awakeFromNib() bgColorView.wantsLayer = true updateViewColor() NotificationCenter.default.addObserver( self, selector: #selector(themeChanged(_:)), name: NSNotification.Name("AppleInterfaceThemeChangedNotification"), object: nil ) } deinit { NotificationCenter.default.removeObserver(self) } @objc func themeChanged(_ notification: Notification) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { self.updateViewColor() } } private func updateViewColor() { var textColor = NSColor(red: 68.0 / 255.0, green: 68.0 / 255.0, blue: 68.0 / 255.0, alpha: 1.0) var subtextColor = NSColor(red: 141.0 / 255.0, green: 141.0 / 255.0, blue: 141.0 / 255.0, alpha: 1.0) var lineColor = NSColor(red: 204.0 / 255.0, green: 204.0 / 255.0, blue: 204.0 / 255.0, alpha: 0.4) if let layer = bgColorView.layer { if KMAppearance.isDarkMode() { layer.backgroundColor = NSColor(red: 24.0 / 255.0, green: 22.0 / 255.0, blue: 31.0 / 255.0, alpha: 1.0).cgColor textColor = NSColor(red: 255.0 / 255.0, green: 255.0 / 255.0, blue: 255.0 / 255.0, alpha: 0.8) subtextColor = NSColor(red: 255.0 / 255.0, green: 255.0 / 255.0, blue: 255.0 / 255.0, alpha: 0.6) lineColor = NSColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.15) } else { layer.backgroundColor = NSColor(red: 247.0 / 255.0, green: 245.0 / 255.0, blue: 1.0, alpha: 1.0).cgColor } } value1Label.textColor = subtextColor value2Label.textColor = subtextColor value3Label.textColor = subtextColor value4Label.textColor = subtextColor lineBox.fillColor = lineColor textField?.textColor = textColor } } class KMProductCompareTableHeaderCell: NSTableCellView { @IBOutlet weak var contentView: NSView? @IBOutlet weak var selectBoxLeft: NSLayoutConstraint! @IBOutlet weak var selectBoxWidth: NSLayoutConstraint! deinit { NotificationCenter.default.removeObserver(self) } override func awakeFromNib() { super.awakeFromNib() contentView?.wantsLayer = true updateViewColor() NotificationCenter.default.addObserver( self, selector: #selector(themeChanged(_:)), name: NSNotification.Name("AppleInterfaceThemeChangedNotification"), object: nil ) } @objc private func themeChanged(_ notification: Notification) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { self.updateViewColor() } } private func updateViewColor() { if KMAppearance.isDarkMode() { contentView?.layer?.backgroundColor = NSColor(red: 40/255, green: 38/255, blue: 47/255, alpha: 1).cgColor } else { contentView?.layer?.backgroundColor = NSColor(red: 237/255, green: 231/255, blue: 255/255, alpha: 1).cgColor } textField?.textColor = KMAppearance.Layout.h0Color() textField?.font = NSFont.boldSystemFont(ofSize: 14.0) } } class KMProductCompareTableEmptyCell: NSTableCellView { override func awakeFromNib() { super.awakeFromNib() self.wantsLayer = true updateViewColor() DistributedNotificationCenter.default.addObserver(self, selector: #selector(themeChanged(_:)), name: NSNotification.Name("AppleInterfaceThemeChangedNotification"), object: nil) } deinit { DistributedNotificationCenter.default.removeObserver(self) } @objc private func themeChanged(_ notification: Notification) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { self.updateViewColor() } } private func updateViewColor() { if KMAppearance.isDarkMode() { self.layer?.backgroundColor = NSColor(red: 24/255.0, green: 22/255.0, blue: 31/255.0, alpha: 1.0).cgColor } else { self.layer?.backgroundColor = NSColor(red: 247/255.0, green: 245/255.0, blue: 1.0, alpha: 1.0).cgColor } } }