From 153c8ac0801cab663e030338af15938b74f122e2 Mon Sep 17 00:00:00 2001 From: zeng Date: Mon, 28 Apr 2025 10:57:59 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B4=BB=E5=8A=A8=E9=A1=B5=E9=9D=A2=E5=AF=B9?= =?UTF-8?q?=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Base/Controller/SPTabBarController.swift | 6 +- MoviaBox/Base/Extension/UIColor+SPAdd.swift | 4 ++ .../Base/Networking/API/SPRewardsAPI.swift | 25 ++++++++ MoviaBox/Base/Networking/API/SPVideoAPI.swift | 6 +- MoviaBox/Base/Networking/Base/SPURLPath.swift | 2 + MoviaBox/Base/WebView/SPWebMessageModel.swift | 25 ++++++++ .../SPWebViewController+ScriptMessage.swift | 56 ++++++++++++++++++ .../Base/WebView/SPWebViewController.swift | 2 +- .../Class/Mine/View/SPMineHeaderView.swift | 2 + .../SPPlayerDetailViewController.swift | 20 +++++-- .../SPPlayerListViewController.swift | 17 ++++-- .../Class/Player/View/SPEpisodeCell.swift | 16 +++++ .../Class/Player/View/SPPlayBuyView.swift | 43 ++++++++++++++ .../Controller/SPRewardsViewController.swift | 23 +++++++ .../icon/lock_bg_01.imageset/Contents.json | 22 +++++++ .../lock_bg_01.imageset/Rectangle 85@2x.png | Bin 0 -> 485 bytes .../lock_bg_01.imageset/Rectangle 85@3x.png | Bin 0 -> 666 bytes .../icon/lock_icon_01.imageset/Contents.json | 22 +++++++ .../icon/lock_icon_01.imageset/Frame@2x.png | Bin 0 -> 353 bytes .../icon/lock_icon_01.imageset/Frame@3x.png | Bin 0 -> 463 bytes MoviaBox/Source/en.lproj/Localizable.strings | 1 + 21 files changed, 278 insertions(+), 14 deletions(-) create mode 100644 MoviaBox/Base/Networking/API/SPRewardsAPI.swift create mode 100644 MoviaBox/Base/WebView/SPWebMessageModel.swift create mode 100644 MoviaBox/Class/Player/View/SPPlayBuyView.swift create mode 100644 MoviaBox/Class/Rewards/Controller/SPRewardsViewController.swift create mode 100644 MoviaBox/Source/Assets.xcassets/icon/lock_bg_01.imageset/Contents.json create mode 100644 MoviaBox/Source/Assets.xcassets/icon/lock_bg_01.imageset/Rectangle 85@2x.png create mode 100644 MoviaBox/Source/Assets.xcassets/icon/lock_bg_01.imageset/Rectangle 85@3x.png create mode 100644 MoviaBox/Source/Assets.xcassets/icon/lock_icon_01.imageset/Contents.json create mode 100644 MoviaBox/Source/Assets.xcassets/icon/lock_icon_01.imageset/Frame@2x.png create mode 100644 MoviaBox/Source/Assets.xcassets/icon/lock_icon_01.imageset/Frame@3x.png diff --git a/MoviaBox/Base/Controller/SPTabBarController.swift b/MoviaBox/Base/Controller/SPTabBarController.swift index b32c02b..489d3a3 100644 --- a/MoviaBox/Base/Controller/SPTabBarController.swift +++ b/MoviaBox/Base/Controller/SPTabBarController.swift @@ -17,11 +17,13 @@ class SPTabBarController: UITabBarController { let nav2 = createNavigationController(viewController: SPExplorePageController(), title: "For You".localized, image: UIImage(named: "tabbar_icon_02"), selectedImage: UIImage(named: "tabbar_icon_02_selected")) - let nav4 = createNavigationController(viewController: SPMyListViewController(), title: "My list".localized, image: UIImage(named: "tabbar_icon_04"), selectedImage: UIImage(named: "tabbar_icon_04_selected")) + let nav3 = createNavigationController(viewController: SPMyListViewController(), title: "My list".localized, image: UIImage(named: "tabbar_icon_04"), selectedImage: UIImage(named: "tabbar_icon_04_selected")) + + let nav4 = createNavigationController(viewController: SPRewardsViewController(), title: "Rewards".localized, image: UIImage(named: "tabbar_icon_04"), selectedImage: UIImage(named: "tabbar_icon_04_selected")) let nav5 = createNavigationController(viewController: SPMineViewController(), title: "Profile".localized, image: UIImage(named: "tabbar_icon_05"), selectedImage: UIImage(named: "tabbar_icon_05_selected")) - self.viewControllers = [nav1, nav2, nav4, nav5] + self.viewControllers = [nav1, nav2, nav3, nav4, nav5] } diff --git a/MoviaBox/Base/Extension/UIColor+SPAdd.swift b/MoviaBox/Base/Extension/UIColor+SPAdd.swift index fd552e5..8e89033 100644 --- a/MoviaBox/Base/Extension/UIColor+SPAdd.swift +++ b/MoviaBox/Base/Extension/UIColor+SPAdd.swift @@ -252,5 +252,9 @@ extension UIColor { static func color0D0807(alpha: CGFloat = 1) -> UIColor { return color(hex: 0x0D0807, alpha: alpha) } + + static func colorF2C879(alpha: CGFloat = 1) -> UIColor { + return color(hex: 0xF2C879, alpha: alpha) + } } diff --git a/MoviaBox/Base/Networking/API/SPRewardsAPI.swift b/MoviaBox/Base/Networking/API/SPRewardsAPI.swift new file mode 100644 index 0000000..4ab9619 --- /dev/null +++ b/MoviaBox/Base/Networking/API/SPRewardsAPI.swift @@ -0,0 +1,25 @@ +// +// SPRewardsAPI.swift +// MoviaBox +// +// Created by 佳尔 on 2025/4/28. +// + +import UIKit + +class SPRewardsAPI: NSObject { + ///开启通知领金币 + static func requestUploadOpenNotify(completer: ((_ finish: Bool) -> Void)?) { + + var param = SPNetworkParameters(path: "openNotify") + + SPNetwork.request(parameters: param) { (response: SPNetworkResponse) in + if response.code == SPNetworkCodeSucceed { + completer?(true) + } else { + completer?(false) + } + } + } + +} diff --git a/MoviaBox/Base/Networking/API/SPVideoAPI.swift b/MoviaBox/Base/Networking/API/SPVideoAPI.swift index 1baf170..641aa01 100644 --- a/MoviaBox/Base/Networking/API/SPVideoAPI.swift +++ b/MoviaBox/Base/Networking/API/SPVideoAPI.swift @@ -10,7 +10,7 @@ import UIKit class SPVideoAPI: NSObject { ///获取视频详情 - static func requestVideoDetail(videoId: String?, shortPlayId: String, completer: ((_ model: SPVideoDetailModel?) -> Void)?) { + static func requestVideoDetail(videoId: String?, shortPlayId: String, activityId: String? = nil, completer: ((_ model: SPVideoDetailModel?) -> Void)?) { var parameters: [String : Any] = [ "short_play_id" : shortPlayId ] @@ -18,6 +18,10 @@ class SPVideoAPI: NSObject { if let videoId = videoId { parameters["video_id"] = videoId } + if let activityId = activityId { + parameters["activity_id"] = activityId + } + var param = SPNetworkParameters(path: "/getVideoDetails") param.method = .get param.parameters = parameters diff --git a/MoviaBox/Base/Networking/Base/SPURLPath.swift b/MoviaBox/Base/Networking/Base/SPURLPath.swift index 41d8fd5..35c5669 100644 --- a/MoviaBox/Base/Networking/Base/SPURLPath.swift +++ b/MoviaBox/Base/Networking/Base/SPURLPath.swift @@ -49,5 +49,7 @@ let SPFeedBackHomeWebUrl = SPCampaignWebURL + "/pages/leave/index" let SPFeedBackListWebUrl = SPCampaignWebURL + "/pages/leave/list" ///反馈详情 let SPFeedBackDetailWebUrl = SPCampaignWebURL + "/pages/leave/detail" +///活动页面 +let SPRewardsWebUrl = SPCampaignWebURL diff --git a/MoviaBox/Base/WebView/SPWebMessageModel.swift b/MoviaBox/Base/WebView/SPWebMessageModel.swift new file mode 100644 index 0000000..9efff82 --- /dev/null +++ b/MoviaBox/Base/WebView/SPWebMessageModel.swift @@ -0,0 +1,25 @@ +// +// SPWebMessageModel.swift +// MoviaBox +// +// Created by 佳尔 on 2025/4/28. +// + +import UIKit +import SmartCodable + +class SPWebMessageModel: SPModel, SmartCodable { + + var type: String? + + var data: SPWebMessageData? + +} + +struct SPWebMessageData: SmartCodable { + + var activity_id: String? + var short_play_id: String? + var link: String? + +} diff --git a/MoviaBox/Base/WebView/SPWebViewController+ScriptMessage.swift b/MoviaBox/Base/WebView/SPWebViewController+ScriptMessage.swift index afca080..de5ee0d 100644 --- a/MoviaBox/Base/WebView/SPWebViewController+ScriptMessage.swift +++ b/MoviaBox/Base/WebView/SPWebViewController+ScriptMessage.swift @@ -45,12 +45,68 @@ extension SPWebViewController { } else if name == WebMessageOpenPhotoPicker { let vc = SPImagePickerManager.createImagePicker(delegate: self) self.present(vc, animated: true) + + } else if name == WebMessageAPP { + guard let body = message.body as? [String : Any] else { return } + guard let model = SPWebMessageModel.deserialize(from: body) else { return } + let type = model.type + let data = model.data + + if type == "login" {//登录 + self.navigationController?.pushViewController(SPLoginViewController(), animated: true) + + } else if type == "open_notify" {//打开通知 + openNotify() + + } else if type == "watch_video" { //去看剧 + + let vc = SPPlayerDetailViewController() + vc.shortPlayId = data?.short_play_id + vc.activityId = data?.activity_id + self.navigationController?.pushViewController(vc, animated: true) + + } else { + guard let urlStr = data?.link else { return } + guard let url = URL(string: urlStr) else { return } + if UIApplication.shared.canOpenURL(url) { + UIApplication.shared.open(url) + } + + } + + + } } } +extension SPWebViewController { + ///打开系统通知 + func openNotify() { + UNUserNotificationCenter.current().getNotificationSettings { settings in + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + if settings.authorizationStatus != .authorized {//未开启通知打开设置 + if #available(iOS 16.0, *) { + if let url = URL(string: UIApplication.openNotificationSettingsURLString) { + UIApplication.shared.open(url) + } + } else { + if let url = URL(string: UIApplication.openSettingsURLString) { + UIApplication.shared.open(url) + } + } + } else {//开启通知上报结果 + SPRewardsAPI.requestUploadOpenNotify(completer: nil) + } + } + } + } + +} + //MARK: -------------- TZImagePickerControllerDelegate -------------- extension SPWebViewController: TZImagePickerControllerDelegate { diff --git a/MoviaBox/Base/WebView/SPWebViewController.swift b/MoviaBox/Base/WebView/SPWebViewController.swift index 52e23fc..a00dbaa 100644 --- a/MoviaBox/Base/WebView/SPWebViewController.swift +++ b/MoviaBox/Base/WebView/SPWebViewController.swift @@ -28,7 +28,7 @@ class SPWebViewController: SPViewController { override func viewDidLoad() { super.viewDidLoad() // self.edgesForExtendedLayout = .top - configNavigationBack() +// configNavigationBack() setBackgroundView(isShowGradient: false, bgImage: nil) diff --git a/MoviaBox/Class/Mine/View/SPMineHeaderView.swift b/MoviaBox/Class/Mine/View/SPMineHeaderView.swift index 7680903..d61cc4a 100644 --- a/MoviaBox/Class/Mine/View/SPMineHeaderView.swift +++ b/MoviaBox/Class/Mine/View/SPMineHeaderView.swift @@ -14,6 +14,8 @@ class SPMineHeaderView: UIView { var stackHeight = 0.0 stackHeight += memberView.intrinsicContentSize.height + + stackHeight += self.stackView.spacing stackHeight += walletView.intrinsicContentSize.height if playHistoryArr?.count ?? 0 > 0 { diff --git a/MoviaBox/Class/Player/Controller/SPPlayerDetailViewController.swift b/MoviaBox/Class/Player/Controller/SPPlayerDetailViewController.swift index 3be4808..c834acb 100644 --- a/MoviaBox/Class/Player/Controller/SPPlayerDetailViewController.swift +++ b/MoviaBox/Class/Player/Controller/SPPlayerDetailViewController.swift @@ -20,6 +20,7 @@ class SPPlayerDetailViewController: SPPlayerListViewController { var videoId: String? var shortPlayId: String? + var activityId: String? var playHistoryModel: SPShortModel? private var detailModel: SPVideoDetailModel? @@ -76,12 +77,19 @@ class SPPlayerDetailViewController: SPPlayerListViewController { } override func play() { - super.play() - if let _ = self.detailModel, - let videoInfo = self.viewModel.currentPlayer?.videoInfo - { - SPVideoAPI.requestRequestVideoPlayHistory(videoId: videoInfo.short_play_video_id ?? "", shortPlayId: videoInfo.short_play_id ?? "") + guard let videoInfo = self.viewModel.currentPlayer?.videoInfo else { return } + if videoInfo.is_lock == true { + self.pause() + + let view = SPPlayBuyView() + view.present(in: nil) + + return } + + super.play() + + SPVideoAPI.requestRequestVideoPlayHistory(videoId: videoInfo.short_play_video_id ?? "", shortPlayId: videoInfo.short_play_id ?? "") } } @@ -174,7 +182,7 @@ extension SPPlayerDetailViewController { private func requestDetailData() { guard let shortPlayId = self.shortPlayId else { return } - SPVideoAPI.requestVideoDetail(videoId: videoId, shortPlayId: shortPlayId) { [weak self] model in + SPVideoAPI.requestVideoDetail(videoId: videoId, shortPlayId: shortPlayId, activityId: activityId) { [weak self] model in guard let self = self else { return } if let model = model { self.detailModel = model diff --git a/MoviaBox/Class/Player/Controller/SPPlayerListViewController.swift b/MoviaBox/Class/Player/Controller/SPPlayerListViewController.swift index adba709..67f1d2b 100644 --- a/MoviaBox/Class/Player/Controller/SPPlayerListViewController.swift +++ b/MoviaBox/Class/Player/Controller/SPPlayerListViewController.swift @@ -112,6 +112,7 @@ class SPPlayerListViewController: SPViewController { super.viewDidAppear(animated) if getDataCount() > 0 && self.viewModel.isPlaying { self.viewModel.currentPlayer?.start() +// self.play() } } @@ -179,6 +180,11 @@ class SPPlayerListViewController: SPViewController { } } + func pause() { + self.viewModel.isPlaying = false + self.viewModel.currentPlayer?.pause() + } + func reloadData(completion: (() -> Void)? = nil) { CATransaction.setCompletionBlock { completion?() @@ -246,11 +252,14 @@ extension SPPlayerListViewController { // } if self.viewModel.isPlaying { - self.viewModel.isPlaying = false - self.viewModel.currentPlayer?.pause() + self.pause() +// self.viewModel.isPlaying = false +// self.viewModel.currentPlayer?.pause() } else { - self.viewModel.isPlaying = true - self.viewModel.currentPlayer?.start() + self.play() + +// self.viewModel.isPlaying = true +// self.viewModel.currentPlayer?.start() } } diff --git a/MoviaBox/Class/Player/View/SPEpisodeCell.swift b/MoviaBox/Class/Player/View/SPEpisodeCell.swift index 3070054..d6bd268 100644 --- a/MoviaBox/Class/Player/View/SPEpisodeCell.swift +++ b/MoviaBox/Class/Player/View/SPEpisodeCell.swift @@ -13,6 +13,8 @@ class SPEpisodeCell: SPCollectionViewCell { var videoInfoModel: SPVideoInfoModel? { didSet { textLabel.text = "\(videoInfoModel?.episode ?? "0")" + + lockImageView.isHidden = !(videoInfoModel?.is_lock ?? false) } } @@ -36,6 +38,14 @@ class SPEpisodeCell: SPCollectionViewCell { return label }() + private lazy var lockImageView: UIButton = { + let view = UIButton(type: .custom) + view.isUserInteractionEnabled = false + view.setImage(UIImage(named: "lock_icon_01"), for: .normal) + view.setBackgroundImage(UIImage(named: "lock_bg_01"), for: .normal) + return view + }() + override init(frame: CGRect) { super.init(frame: frame) @@ -56,11 +66,17 @@ extension SPEpisodeCell { private func _setupUI() { contentView.addSubview(textLabel) + contentView.addSubview(lockImageView) textLabel.snp.makeConstraints { make in make.center.equalToSuperview() } + lockImageView.snp.makeConstraints { make in + make.right.top.equalToSuperview() + } + + } diff --git a/MoviaBox/Class/Player/View/SPPlayBuyView.swift b/MoviaBox/Class/Player/View/SPPlayBuyView.swift new file mode 100644 index 0000000..e28c9cd --- /dev/null +++ b/MoviaBox/Class/Player/View/SPPlayBuyView.swift @@ -0,0 +1,43 @@ +// +// SPPlayBuyView.swift +// MoviaBox +// +// Created by 佳尔 on 2025/4/28. +// + +import UIKit + +class SPPlayBuyView: HWPanModalContentView { + + + + override init(frame: CGRect) { + super.init(frame: frame) + + self.backgroundColor = .red + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + //MARK: HWPanModalPresentable + override func panScrollable() -> UIScrollView? { + return nil + } + + override func longFormHeight() -> PanModalHeight { + return PanModalHeightMake(.content, kSPScreenHeight * (3 / 4)) + } + + override func showDragIndicator() -> Bool { + return false + } + + override func backgroundConfig() -> HWBackgroundConfig { + let config = HWBackgroundConfig() + config.backgroundAlpha = 0.6 + return config + } +} diff --git a/MoviaBox/Class/Rewards/Controller/SPRewardsViewController.swift b/MoviaBox/Class/Rewards/Controller/SPRewardsViewController.swift new file mode 100644 index 0000000..bbd1f22 --- /dev/null +++ b/MoviaBox/Class/Rewards/Controller/SPRewardsViewController.swift @@ -0,0 +1,23 @@ +// +// SPRewardsViewController.swift +// MoviaBox +// +// Created by 佳尔 on 2025/4/28. +// + +import UIKit + +class SPRewardsViewController: SPCampaignWebViewController { + + override func viewDidLoad() { + self.urlStr = SPRewardsWebUrl + + super.viewDidLoad() + + + } + + + + +} diff --git a/MoviaBox/Source/Assets.xcassets/icon/lock_bg_01.imageset/Contents.json b/MoviaBox/Source/Assets.xcassets/icon/lock_bg_01.imageset/Contents.json new file mode 100644 index 0000000..12d6359 --- /dev/null +++ b/MoviaBox/Source/Assets.xcassets/icon/lock_bg_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle 85@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Rectangle 85@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MoviaBox/Source/Assets.xcassets/icon/lock_bg_01.imageset/Rectangle 85@2x.png b/MoviaBox/Source/Assets.xcassets/icon/lock_bg_01.imageset/Rectangle 85@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c370593af9c9fa4106456f5a8539815f75de3880 GIT binary patch literal 485 zcmVKA3!US$Go*BN|aBnXL5Zc7&^-hdTS){gbsPS1}F{=A2kzsjiyjeAslkqO9M{(1}q)TAvN3_J!i*S>y;DhBp+HQF-|Zk@iv4(YO(5*@7(EphWH*=TN{>;hZ2|1G)_A%7K0)O@f%f bEo_)SGemVK^_WXn00000NkvXXu0mjfMP|lG literal 0 HcmV?d00001 diff --git a/MoviaBox/Source/Assets.xcassets/icon/lock_bg_01.imageset/Rectangle 85@3x.png b/MoviaBox/Source/Assets.xcassets/icon/lock_bg_01.imageset/Rectangle 85@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..15004e58e431574fd066c043d02cde4f78a333a6 GIT binary patch literal 666 zcmeAS@N?(olHy`uVBq!ia0vp^PC#tM!3HE*RQp|k6lZ})WHAGSq6G*ux=u4p017e| z2e~^jtUD+35~MKQ(btiIVPik{pF~y$1}1$^7srqa#<#P4^DaAx9RGh_lSQTLlJpNY z74P*P`4i?f$SpjrA|2D|TDDYufi2@BmXqfic^|Uva|{ya4DF~qv*Yq37qwD*vv1Y* znlmfX_MAAMf9x}>hGogbp5!a6&qTLQn3TCTZtsoA0R6a!oeOg8lCFrT>2#c|Yx;cm z#bKWQ1=p0!Jl@`)X`5X9$7t4)9qPV9h1`ttE2}s#Z}k86!u+C%_p$cS_nAvL z{5c-^Pyc+;bLy$y9I4qm{J!pd*qrOT^rT=Rx8bgJEB|=4J^cTwL*OCj+@7c3cLaxN zc(y3##KzX@X-`l)baDTT{ysBf)vkiaJ(K6(KXx~1o`)b`a>d)NT460q>u2)(HW#1d zdgCMO-u*vyqgc2C6=PX=5+4M8RO0xe=dXIk^j*`8??>HR4hxiqvV3~>(arUM?vo8} zEr16!;Gk;hQajx6{gXhpm*EepHXZofIoY_B7^|K|5^HJ{If6cdg$}>gI&c0!L zAekf8x8=3=n{~h_b4r~lG~45)$shmSwFeHREnTYgt?J_`gZJK(9B*??KWVV#^B2vX zli%^Czvj9cXX5F*`|jimjX_ttB8_ZHj(?mbB|G)HPQ3f%&3o=Am=v)@St@lssb$yL tR=z*itHs8{W#XIH83)*!cX|KhZs%I#SsZ#K4w#e}JYD@<);T3K0RSsk9Y+8F literal 0 HcmV?d00001 diff --git a/MoviaBox/Source/Assets.xcassets/icon/lock_icon_01.imageset/Contents.json b/MoviaBox/Source/Assets.xcassets/icon/lock_icon_01.imageset/Contents.json new file mode 100644 index 0000000..5c4d3b1 --- /dev/null +++ b/MoviaBox/Source/Assets.xcassets/icon/lock_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MoviaBox/Source/Assets.xcassets/icon/lock_icon_01.imageset/Frame@2x.png b/MoviaBox/Source/Assets.xcassets/icon/lock_icon_01.imageset/Frame@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..af94b569b15e7874d78054876ab172486d7b625a GIT binary patch literal 353 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VXMsm#F$05`DhM-r2B~}i3NjW4 zxjQkeJ16rJ$YDu$^mSxl*x1kgCy^D%|K{o97*fIbc8WJ2qoY9EYO8C@?lefBIhK5q zvw*3PN#>yM9R~FT)ug9v&p1T6wRin_J9R?m84l}uBhT>A>({?}w7od}sQlov4w*gj z2E{zVercbZiZ^e~nj{?|+IjLfs~(5X-kpan8Mo{Xy1VS@PLp)!C1=utS*}`MzxC$( z(hsf1tClLuaa~PkoAq7RNPBj%@|Nv;ejk=RP`5&4u|yi1P_gxvoq==rw(0R3s7tuc zk*%0|$=cApvQhqJ=IO6Vg$ZYzjVGTjyBL)v_N@A>+nt=I*52U8ts1v%_0q$c5}xMO w?0stgf&Y&~tGd4+VWpEvTW>ZXDu9DRl?Vl{E9)^N3GoGx#jaXDO!m##dUOT5CTl zvr-w2>vl>jk8}5Qh=_k9;+#C`wzvp_oVAHRVcrF0@!q#mb6T3}8Rrryt_-F^^2>Bv z@nps@^wxtT_zO6y+oi5v#77WM=`z34oPHPzKK4*=G$06rK=d@?9YzNOcec}I7%r)I z7LHT>TPWO0Lb{uN>VTAIYP0OUC~ibB6-7*a`viN`5#rg4F>UlO4n?fq^^cc#AD