diff --git a/WOKA.xcodeproj/project.pbxproj b/WOKA.xcodeproj/project.pbxproj index e43fcf7..ddac312 100644 --- a/WOKA.xcodeproj/project.pbxproj +++ b/WOKA.xcodeproj/project.pbxproj @@ -174,6 +174,7 @@ 52ACC13A2C64CD0100791528 /* MyOrderDetailsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52ACC1382C64CD0100791528 /* MyOrderDetailsCell.swift */; }; 52ACC13B2C64CD0100791528 /* MyOrderDetailsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 52ACC1392C64CD0100791528 /* MyOrderDetailsCell.xib */; }; 52ACC13D2C64DFC300791528 /* MyListViewAllVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52ACC13C2C64DFC300791528 /* MyListViewAllVC.swift */; }; + 52ADF4762CBFAE3C002B9F4B /* HapticFeedbackGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52ADF4752CBFAE32002B9F4B /* HapticFeedbackGenerator.swift */; }; 52AECA802C08BCB6004A7579 /* PlayerVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52AECA7F2C08BCB6004A7579 /* PlayerVC.swift */; }; 52AF71EE2C36AD3100BC5972 /* GamesListVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52AF71ED2C36AD3100BC5972 /* GamesListVM.swift */; }; 52AF71F02C36B29A00BC5972 /* GamesListDM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52AF71EF2C36B29A00BC5972 /* GamesListDM.swift */; }; @@ -586,6 +587,7 @@ 52ACC1382C64CD0100791528 /* MyOrderDetailsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyOrderDetailsCell.swift; sourceTree = ""; }; 52ACC1392C64CD0100791528 /* MyOrderDetailsCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MyOrderDetailsCell.xib; sourceTree = ""; }; 52ACC13C2C64DFC300791528 /* MyListViewAllVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyListViewAllVC.swift; sourceTree = ""; }; + 52ADF4752CBFAE32002B9F4B /* HapticFeedbackGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticFeedbackGenerator.swift; sourceTree = ""; }; 52AECA7F2C08BCB6004A7579 /* PlayerVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerVC.swift; sourceTree = ""; }; 52AF71ED2C36AD3100BC5972 /* GamesListVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GamesListVM.swift; sourceTree = ""; }; 52AF71EF2C36B29A00BC5972 /* GamesListDM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GamesListDM.swift; sourceTree = ""; }; @@ -1527,6 +1529,7 @@ 52C8B0512BDA4B51003B51D0 /* Helpers */ = { isa = PBXGroup; children = ( + 52ADF4752CBFAE32002B9F4B /* HapticFeedbackGenerator.swift */, 525855162CBD3C6C00E6002A /* PlayerStatusEnum.swift */, 52423CD02CB6A8A300623CA7 /* CheckReachability.swift */, 5282DB2D2C981E9300465BA1 /* AdResuable */, @@ -2518,6 +2521,7 @@ 9CBCB29B2BE4D614007D7934 /* LoginVC.swift in Sources */, 52BC3BE82C0E04A9002FACA6 /* FaqListDM.swift in Sources */, 9C56E83B2BDBC6E600E4CA14 /* SelectAgeVM.swift in Sources */, + 52ADF4762CBFAE3C002B9F4B /* HapticFeedbackGenerator.swift in Sources */, 9C535DC02C00B36000DA6DCD /* HomeVC.swift in Sources */, 52B8D4E22C04A25E00ED65F3 /* Segue.swift in Sources */, 52AC2D272C29791500337473 /* JWPlayerManager.swift in Sources */, @@ -2905,7 +2909,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = WOKA/WOKA.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 6; + CURRENT_PROJECT_VERSION = 7; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 4S9A74ZB6H; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -2949,7 +2953,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = WOKA/WOKA.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 6; + CURRENT_PROJECT_VERSION = 7; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 4S9A74ZB6H; ENABLE_USER_SCRIPT_SANDBOXING = NO; diff --git a/WOKA/Audio Books/AudioBookHomeVC.swift b/WOKA/Audio Books/AudioBookHomeVC.swift index 6aaf2fa..ea0ce49 100644 --- a/WOKA/Audio Books/AudioBookHomeVC.swift +++ b/WOKA/Audio Books/AudioBookHomeVC.swift @@ -28,7 +28,6 @@ class AudioBookHomeVC: UIViewController { @IBOutlet weak var listenView: ShimmerEffectView! @IBOutlet weak var noDataView: UIView! - @IBOutlet weak var loadMoreBtn: LocalisedElementsButton! @IBOutlet weak var loadMoreActivityIndicator: UIActivityIndicatorView! override func viewDidLoad() { @@ -86,25 +85,32 @@ class AudioBookHomeVC: UIViewController { vm.updateTableHeight() } - @IBAction func loadMoreBtn(_ sender: LocalisedElementsButton) { - PersistentStorage.shared.addOthersCount() - if CheckReachability.reachability?.isReachable == false{ - self.toast(msg: K.ConstantString.noInternet , time: 2) - return - } - loadMoreBtn.isHidden = true - vm.pageNo += 1 - loadMoreActivityIndicator.startAnimating() - vm.getShowListing() - } +// @IBAction func loadMoreBtn(_ sender: LocalisedElementsButton) { +// PersistentStorage.shared.addOthersCount() +// if CheckReachability.reachability?.isReachable == false{ +// self.toast(msg: K.ConstantString.noInternet , time: 2) +// return +// } +// loadMoreBtn.isHidden = true +// vm.pageNo += 1 +// loadMoreActivityIndicator.startAnimating() +// vm.getShowListing() +// } @IBAction func listenAudioBtnTapped(_ sender: LocalisedElementsButton) { /* MAke logic for ads */ - if let adsData = AuthFunc.shareInstance.adsData{ + if let adsData = AuthFunc.shareInstance.adsData, let audioBooksAd = adsData.result?.filter({$0.slug == AdsEnum.audioBooks.rawValue}).first{ + + // URL maybe null so handle it + if audioBooksAd.advertisement?.isActive == "1" && audioBooksAd.advertisement?.adLink == nil{ + // it means local ad is active and url is null + return + } + // check if ads data contains ad for webseries - if let audioBooksAd = adsData.result?.filter({$0.slug == AdsEnum.audioBooks.rawValue}).first, let adLink = audioBooksAd.advertisement?.adLink, let adID = audioBooksAd.advertisement?.id{ + if let adLink = audioBooksAd.advertisement?.adLink, let adID = audioBooksAd.advertisement?.id{ PersistentStorage.shared.addAdsCount(adID: adID,clicks: 1) if let url = URL(string: adLink), UIApplication.shared.canOpenURL(url) { UIApplication.shared.open(url) @@ -405,6 +411,27 @@ extension AudioBookHomeVC: UIScrollViewDelegate { UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() } + + // dont do network call if the showdata count is same as the total count provided by api + // also check if already the api call is done + if vm.audioListData.count == vm.totalDataCount || vm.isAudioDataLoading == true {return} + + let contentHeight = scrollView.contentSize.height + let height = scrollView.frame.size.height + + // Check if the user scrolled to the bottom + if y > contentHeight - height - 50{ + + // check if internet is available + if CheckReachability.reachability?.isReachable == false{ + return + } + + //start the acitvity indicator + loadMoreActivityIndicator.startAnimating() + vm.isAudioDataLoading = true + vm.getShowListing(isBtnClick: true) + } } } diff --git a/WOKA/Audio Books/AudioBookHomeVM.swift b/WOKA/Audio Books/AudioBookHomeVM.swift index 1758ac8..dd11af1 100644 --- a/WOKA/Audio Books/AudioBookHomeVM.swift +++ b/WOKA/Audio Books/AudioBookHomeVM.swift @@ -17,7 +17,11 @@ class AudioBookHomeVM{ var headerData : ListenAudioListingDM.AudioDatum? + // PAgination variables var pageNo = 0 + var isAudioDataLoading = false + var totalDataCount = 0 // this will hold the total count from api + var maxHeaderHeight = 0.0 var headerBannerView = GADBannerView() @@ -168,6 +172,7 @@ class AudioBookHomeVM{ case .success(let data): guard let self else{ Utilities.dismissProgressHUD() + self?.isAudioDataLoading = false return } switch data.success{ @@ -176,14 +181,27 @@ class AudioBookHomeVM{ Error */ Utilities.dismissProgressHUD() + self.isAudioDataLoading = false vc.toast(msg: data.message ?? "Unrecognised error" , time: 2) case 1: Utilities.dismissProgressHUD() - getContinueWatching() + if !isBtnClick{ + getContinueWatching() + HapticFeedbackGenerator.shared.rigidImpact() + } + self.pageNo += 1 + self.isAudioDataLoading = false guard let dataCount = data.data?.totalRecords ,let data = data.data?.audioData else{return} + + self.totalDataCount = dataCount + self.audioListData.append(contentsOf: data) self.vc.audioListingTableView.reloadData() - self.vc.tableHeight.constant = self.vc.audioListingTableView.contentSize.height + 100 + + if !isBtnClick{ + self.vc.tableHeight.constant = self.vc.audioListingTableView.contentSize.height + 100 + } + self.vc.audioListingTableView.layoutIfNeeded() self.vc.tableHeight.constant = self.vc.audioListingTableView.contentSize.height @@ -198,39 +216,12 @@ class AudioBookHomeVM{ self.setHeaderData() } } -// if let adsData = AuthFunc.shareInstance.adsData{ -// // check if ads data contains ad for webseries -// if let audioBooksAd = adsData.result?.filter({$0.slug == AdsEnum.audioBooks.rawValue}).first, let bannerImage = audioBooksAd.advertisement?.bannerImage, let buttonImage = audioBooksAd.advertisement?.buttonImage{ -// vc.headerImage.imageURL(bannerImage, color: .white) -// vc.headerBtn.setTitle("", for: .normal) -// vc.headerTitleLabel.text = "" -// vc.headerTitleHeight.constant = 10 -// vc.headerBtn.backgroundColor = .clear -// vc.headerBtn.sd_setBackgroundImage(with: URL(string:buttonImage), for: .normal) -// }else{ -// if !isBtnClick{ -// self.headerData = self.audioListData.first -// self.setHeaderData() -// } -// } -// }else{ -// if !isBtnClick{ -// self.headerData = self.audioListData.first -// self.setHeaderData() -// } -// } self.stopShimmer() self.vc.loadMoreActivityIndicator.stopAnimating() self.vc.loadMoreActivityIndicator.hidesWhenStopped = true - if self.audioListData.count == dataCount{ - self.vc.loadMoreBtn.isHidden = true - }else{ - self.vc.loadMoreBtn.isHidden = false - } - checkAds() default: break @@ -238,9 +229,11 @@ class AudioBookHomeVM{ case .failure(let error): guard let self else{ Utilities.dismissProgressHUD() + self?.isAudioDataLoading = false return } Utilities.dismissProgressHUD() + self.isAudioDataLoading = false vc.toast(msg: error.localizedDescription , time: K.ConstantString.errorTime) self.stopShimmer() self.vc.noDataView.isHidden = false diff --git a/WOKA/Audio Books/AudioBooks.storyboard b/WOKA/Audio Books/AudioBooks.storyboard index 7b2c9d7..a1d042b 100644 --- a/WOKA/Audio Books/AudioBooks.storyboard +++ b/WOKA/Audio Books/AudioBooks.storyboard @@ -98,7 +98,7 @@ - + - + - - - - @@ -281,7 +257,6 @@ - diff --git a/WOKA/Games/Controller/GamesListVC.swift b/WOKA/Games/Controller/GamesListVC.swift index b23ad2c..b1b37a7 100644 --- a/WOKA/Games/Controller/GamesListVC.swift +++ b/WOKA/Games/Controller/GamesListVC.swift @@ -22,7 +22,7 @@ class GamesListVC: UIViewController { @IBOutlet weak var gamesLoadingView: ShimmerEffectView! @IBOutlet weak var noDataView: UIView! - @IBOutlet weak var loadMoreBtn: LocalisedElementsButton! +// @IBOutlet weak var loadMoreBtn: LocalisedElementsButton! @IBOutlet weak var loadMoreActivityIndicator: UIActivityIndicatorView! var vm = GamesListVM() @@ -35,8 +35,6 @@ class GamesListVC: UIViewController { self.title = "GAMES".localized(loc: AuthFunc.shareInstance.languageSelected.rawValue) navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default) navigationController?.navigationBar.shadowImage = UIImage() - - } override func viewWillAppear(_ animated: Bool) { @@ -84,9 +82,16 @@ class GamesListVC: UIViewController { /* MAke logic for ads */ - if let adsData = AuthFunc.shareInstance.adsData{ + if let adsData = AuthFunc.shareInstance.adsData, let gamesAd = adsData.result?.filter({$0.slug == AdsEnum.games.rawValue}).first{ + + // URL maybe null so handle it + if gamesAd.advertisement?.isActive == "1" && gamesAd.advertisement?.adLink == nil{ + // it means local ad is active and url is null + return + } + // check if ads data contains ad for webseries - if let gamesAd = adsData.result?.filter({$0.slug == AdsEnum.games.rawValue}).first, let adLink = gamesAd.advertisement?.adLink, let adID = gamesAd.advertisement?.id{ + if let adLink = gamesAd.advertisement?.adLink, let adID = gamesAd.advertisement?.id{ PersistentStorage.shared.addAdsCount(adID: adID,clicks: 1) if let url = URL(string: adLink), UIApplication.shared.canOpenURL(url) { UIApplication.shared.open(url) @@ -110,17 +115,17 @@ class GamesListVC: UIViewController { self.present(vcPush, animated: true) } - @IBAction func loadMoreBtnTapped(_ sender: LocalisedElementsButton) { - PersistentStorage.shared.addOthersCount() - if CheckReachability.reachability?.isReachable == false{ - self.toast(msg: K.ConstantString.noInternet , time: 2) - return - } - loadMoreBtn.isHidden = true - vm.pageNo += 1 - loadMoreActivityIndicator.startAnimating() - vm.getGamesListing(isBtnClick: true) - } +// @IBAction func loadMoreBtnTapped(_ sender: LocalisedElementsButton) { +// PersistentStorage.shared.addOthersCount() +// if CheckReachability.reachability?.isReachable == false{ +// self.toast(msg: K.ConstantString.noInternet , time: 2) +// return +// } +// loadMoreBtn.isHidden = true +// vm.pageNo += 1 +// loadMoreActivityIndicator.startAnimating() +// vm.getGamesListing(isBtnClick: true) +// } @IBAction func retryBtnTapped(_ sender: LocalisedElementsButton) { /* @@ -298,5 +303,26 @@ extension GamesListVC: UIScrollViewDelegate { UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() } + + // dont do network call if the showdata count is same as the total count provided by api + // also check if already the api call is done + if vm.gameData.count == vm.totalDataCount || vm.isGameDataLoading == true {return} + + let contentHeight = scrollView.contentSize.height + let height = scrollView.frame.size.height + + // Check if the user scrolled to the bottom + if y > contentHeight - height - 50{ + + // check if internet is available + if CheckReachability.reachability?.isReachable == false{ + return + } + + //start the acitvity indicator + loadMoreActivityIndicator.startAnimating() + vm.isGameDataLoading = true + vm.getGamesListing(isBtnClick: true) + } } } diff --git a/WOKA/Games/Controller/GamesWebViewVC.swift b/WOKA/Games/Controller/GamesWebViewVC.swift index 61a9fdb..428291b 100644 --- a/WOKA/Games/Controller/GamesWebViewVC.swift +++ b/WOKA/Games/Controller/GamesWebViewVC.swift @@ -135,9 +135,7 @@ class GamesWebViewVC: UIViewController, WKNavigationDelegate,UIGestureRecognizer AdReusable.sharedInstance.setupBannerAd(bannerView: self.bottomBannerView, in: adView, adUnitID: K.GoogleAdIDs.themeTwo, viewController: self) }) } - } - // MARK: - App LifeCycle HAndler diff --git a/WOKA/Games/Games.storyboard b/WOKA/Games/Games.storyboard index 390f6da..0a06305 100644 --- a/WOKA/Games/Games.storyboard +++ b/WOKA/Games/Games.storyboard @@ -99,7 +99,7 @@ - + @@ -246,7 +222,6 @@ - diff --git a/WOKA/Games/ViewModel/GamesListVM.swift b/WOKA/Games/ViewModel/GamesListVM.swift index d43d8ac..be2a09c 100644 --- a/WOKA/Games/ViewModel/GamesListVM.swift +++ b/WOKA/Games/ViewModel/GamesListVM.swift @@ -14,12 +14,17 @@ class GamesListVM{ weak var vc : GamesListVC! var gameData = [GamesListDM.GameDatum]() var indexToLoad = 0 - var pageNo = 0 var maxHeaderHeight = 0.0 + // PAgination variables + var pageNo = 0 + var isGameDataLoading = false + var totalDataCount = 0 // this will hold the total count from api + var headerBannerView = GADBannerView() var headerBannerBottomView = GADBannerView() - + let feedbackGenerator = UIImpactFeedbackGenerator(style: .rigid) + func initView(){ setupCell() vc.scrollView.indicatorStyle = .white // or .white @@ -124,6 +129,7 @@ class GamesListVM{ case .success(let data): guard let self else{ Utilities.dismissProgressHUD() + self?.isGameDataLoading = false return } switch data.success{ @@ -132,69 +138,68 @@ class GamesListVM{ Error */ Utilities.dismissProgressHUD() + self.isGameDataLoading = false self.vc.loadMoreActivityIndicator.stopAnimating() self.vc.loadMoreActivityIndicator.hidesWhenStopped = true // vc.toast(msg: data.message ?? "Unrecognised error" , time: 2) case 1: Utilities.dismissProgressHUD() + self.isGameDataLoading = false + self.pageNo += 1 guard let dataCount = data.data?.totalRecords, let data = data.data?.gameData else{return} + + //update the total data count + self.totalDataCount = dataCount + self.gameData.append(contentsOf: data) + + //Reload games tableview and increase height inside the scrollview self.vc.gamesListingTableView.reloadData() - self.vc.tableHeight.constant = self.vc.gamesListingTableView.contentSize.height + 100 + if !isBtnClick{ + HapticFeedbackGenerator.shared.rigidImpact() + self.vc.tableHeight.constant = self.vc.gamesListingTableView.contentSize.height + 100 + } self.vc.gamesListingTableView.layoutIfNeeded() self.vc.tableHeight.constant = self.vc.gamesListingTableView.contentSize.height - /* - MAke logic for ads - */ - if let adsData = AuthFunc.shareInstance.adsData{ - // check if ads data contains ad for webseries - if let gamesAd = adsData.result?.filter({$0.slug == AdsEnum.games.rawValue}).first, let bannerImage = gamesAd.advertisement?.bannerImage, let buttonImage = gamesAd.advertisement?.buttonImage{ - vc.headerImage.imageURL(bannerImage, color: .white) - vc.headerBtn.setTitle("", for: .normal) - vc.headerTitleLabel.text = "" - vc.headerTitleHeight.constant = 10 - vc.headerBtn.backgroundColor = .clear - vc.headerBtn.sd_setBackgroundImage(with: URL(string:buttonImage), for: .normal) - }else{ - if !isBtnClick{ - setHeaderData() - } - } - }else{ - if !isBtnClick{ - setHeaderData() - } - } - - self.stopShimmer() - self.vc.loadMoreActivityIndicator.stopAnimating() self.vc.loadMoreActivityIndicator.hidesWhenStopped = true - if self.gameData.count == dataCount{ - self.vc.loadMoreBtn.isHidden = true - }else{ - self.vc.loadMoreBtn.isHidden = false - } - -// if self.gameData.count.isMultiple(of: 5) && !self.stopFetch{ -// // if not multiple of 10, means more data can be there, show more btn -// self.vc.loadMoreBtn.isHidden = false + /* + MAke logic for ads +// */ +// if let adsData = AuthFunc.shareInstance.adsData{ +// // check if ads data contains ad for webseries +// if let gamesAd = adsData.result?.filter({$0.slug == AdsEnum.games.rawValue}).first, let bannerImage = gamesAd.advertisement?.bannerImage, let buttonImage = gamesAd.advertisement?.buttonImage{ +// vc.headerImage.imageURL(bannerImage, color: .white) +// vc.headerBtn.setTitle("", for: .normal) +// vc.headerTitleLabel.text = "" +// vc.headerTitleHeight.constant = 10 +// vc.headerBtn.backgroundColor = .clear +// vc.headerBtn.sd_setBackgroundImage(with: URL(string:buttonImage), for: .normal) +// }else{ +// if !isBtnClick{ +// setHeaderData() +// } +// } // }else{ -// self.stopFetch = true -// self.vc.loadMoreBtn.isHidden = true +// if !isBtnClick{ +// setHeaderData() +// } // } + default: - break + self.isGameDataLoading = false } case .failure(let error): guard let self else{ Utilities.dismissProgressHUD() + self?.isGameDataLoading = false return } Utilities.dismissProgressHUD() + self.isGameDataLoading = false self.stopShimmer() self.vc.noDataView.isHidden = false vc.toast(msg: error.localizedDescription, time: K.ConstantString.errorTime) diff --git a/WOKA/Helpers/HapticFeedbackGenerator.swift b/WOKA/Helpers/HapticFeedbackGenerator.swift new file mode 100644 index 0000000..657af67 --- /dev/null +++ b/WOKA/Helpers/HapticFeedbackGenerator.swift @@ -0,0 +1,101 @@ +// +// HapticFeedbackGenerator.swift +// WOKA +// +// Created by MacBook Pro on 16/10/24. +// + +import UIKit + +class HapticFeedbackGenerator{ + + // Singleton instance for reuse across the app + static let shared = HapticFeedbackGenerator() + + // Private initializer to prevent instantiation from outside + private init() {} + + /* + // Trigger light impact feedback + HapticFeedbackGenerator.shared.lightImpact() + + // Trigger success notification feedback + HapticFeedbackGenerator.shared.successNotification() + + // Trigger selection feedback + HapticFeedbackGenerator.shared.simpleSelection() + + */ + + // MARK: - Impact Feedback + func impact(style: UIImpactFeedbackGenerator.FeedbackStyle) { + let generator = UIImpactFeedbackGenerator(style: style) + generator.prepare() // Prepares the feedback generator to reduce latency + generator.impactOccurred() // Trigger the haptic feedback + } + + // MARK: - Notification Feedback + func notification(type: UINotificationFeedbackGenerator.FeedbackType) { + let generator = UINotificationFeedbackGenerator() + generator.prepare() + generator.notificationOccurred(type) + } + + // MARK: - Selection Feedback + func selection() { + let generator = UISelectionFeedbackGenerator() + generator.prepare() + generator.selectionChanged() + } + + // MARK: - Common Presets + + // Light impact feedback + func lightImpact() { + impact(style: .light) + } + + // Medium impact feedback + func mediumImpact() { + impact(style: .medium) + } + + // Heavy impact feedback + func heavyImpact() { + impact(style: .heavy) + } + + // Soft impact feedback (available iOS 13+) + func softImpact() { + if #available(iOS 13.0, *) { + impact(style: .soft) + } + } + + // Rigid impact feedback (available iOS 13+) + func rigidImpact() { + if #available(iOS 13.0, *) { + impact(style: .rigid) + } + } + + // Success notification feedback + func successNotification() { + notification(type: .success) + } + + // Warning notification feedback + func warningNotification() { + notification(type: .warning) + } + + // Error notification feedback + func errorNotification() { + notification(type: .error) + } + + // Simple selection feedback + func simpleSelection() { + selection() + } +} diff --git a/WOKA/Karaoke/Controller/KaraokeListingVC.swift b/WOKA/Karaoke/Controller/KaraokeListingVC.swift index 839a531..ef49b45 100644 --- a/WOKA/Karaoke/Controller/KaraokeListingVC.swift +++ b/WOKA/Karaoke/Controller/KaraokeListingVC.swift @@ -26,7 +26,6 @@ class KaraokeListingVC: UIViewController { @IBOutlet weak var karaokeListingTableView: UITableView! @IBOutlet weak var tableHeight: NSLayoutConstraint! - @IBOutlet weak var loadMoreBtn: LocalisedElementsButton! @IBOutlet weak var loadMoreActivityIndicator: UIActivityIndicatorView! var vm = KaraokeListingVM() @@ -85,26 +84,32 @@ class KaraokeListingVC: UIViewController { } } - @IBAction func loadMoreBtnTapped(_ sender: LocalisedElementsButton) { - PersistentStorage.shared.addOthersCount() - if CheckReachability.reachability?.isReachable == false{ - self.toast(msg: K.ConstantString.noInternet , time: 2) - return - } - loadMoreBtn.isHidden = true - vm.pageNo += 1 - loadMoreActivityIndicator.startAnimating() - vm.getKaraokeListing(isBtnClick: true) - } +// @IBAction func loadMoreBtnTapped(_ sender: LocalisedElementsButton) { +// PersistentStorage.shared.addOthersCount() +// if CheckReachability.reachability?.isReachable == false{ +// self.toast(msg: K.ConstantString.noInternet , time: 2) +// return +// } +// loadMoreBtn.isHidden = true +// vm.pageNo += 1 +// loadMoreActivityIndicator.startAnimating() +// vm.getKaraokeListing(isBtnClick: true) +// } @IBAction func singBtnTapped(_ sender: LocalisedElementsButton) { /* MAke logic for ads */ - if let adsData = AuthFunc.shareInstance.adsData{ + if let adsData = AuthFunc.shareInstance.adsData, let karaokeAd = adsData.result?.filter({$0.slug == AdsEnum.karaoke.rawValue}).first{ // check if ads data contains ad for webseries - if let karaokeAd = adsData.result?.filter({$0.slug == AdsEnum.karaoke.rawValue}).first, let adLink = karaokeAd.advertisement?.adLink, let adID = karaokeAd.advertisement?.id{ + // URL maybe null so handle it + if karaokeAd.advertisement?.isActive == "1" && karaokeAd.advertisement?.adLink == nil{ + // it means local ad is active and url is null + return + } + + if let adLink = karaokeAd.advertisement?.adLink, let adID = karaokeAd.advertisement?.id{ PersistentStorage.shared.addAdsCount(adID: adID ,clicks: 1) if let url = URL(string: adLink), UIApplication.shared.canOpenURL(url) { @@ -399,5 +404,26 @@ extension KaraokeListingVC: UIScrollViewDelegate { UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() } + + // dont do network call if the showdata count is same as the total count provided by api + // also check if already the api call is done + if vm.karaokeListData.count == vm.totalDataCount || vm.isKaraokeDataLoading == true {return} + + let contentHeight = scrollView.contentSize.height + let height = scrollView.frame.size.height + + // Check if the user scrolled to the bottom + if y > contentHeight - height - 50{ + + // check if internet is available + if CheckReachability.reachability?.isReachable == false{ + return + } + + //start the acitvity indicator + loadMoreActivityIndicator.startAnimating() + vm.isKaraokeDataLoading = true + vm.getKaraokeListing(isBtnClick: true) + } } } diff --git a/WOKA/Karaoke/Karaoke.storyboard b/WOKA/Karaoke/Karaoke.storyboard index 86b3056..7babb41 100644 --- a/WOKA/Karaoke/Karaoke.storyboard +++ b/WOKA/Karaoke/Karaoke.storyboard @@ -106,7 +106,7 @@ - + - + - - - - @@ -288,7 +263,6 @@ - diff --git a/WOKA/Karaoke/ViewModel/KaraokeListingVM.swift b/WOKA/Karaoke/ViewModel/KaraokeListingVM.swift index 781c49b..e75aedf 100644 --- a/WOKA/Karaoke/ViewModel/KaraokeListingVM.swift +++ b/WOKA/Karaoke/ViewModel/KaraokeListingVM.swift @@ -17,12 +17,18 @@ class KaraokeListingVM{ var headerData : KaraokeListingDM.KaraokeDatum? + // PAgination variables var pageNo = 0 + var isKaraokeDataLoading = false + var totalDataCount = 0 // this will hold the total count from api + var maxHeaderHeight = 0.0 var headerBannerView = GADBannerView() var headerBannerBottomView = GADBannerView() + let feedbackGenerator = UIImpactFeedbackGenerator(style: .rigid) + func initView(){ setupCell() vc.scrollView.indicatorStyle = .white // or .white @@ -168,6 +174,7 @@ class KaraokeListingVM{ switch result{ case .success(let data): guard let self else{ + self?.isKaraokeDataLoading = false return } switch data.success{ @@ -175,13 +182,23 @@ class KaraokeListingVM{ /* Error */ - self.vc.loadMoreBtn.isHidden = true + self.isKaraokeDataLoading = false self.vc.loadMoreActivityIndicator.stopAnimating() self.vc.loadMoreActivityIndicator.hidesWhenStopped = true // vc.toast(msg: data.message ?? "Unrecognised error" , time: 2) case 1: - getContinueWatching() + if !isBtnClick{ + getContinueWatching() + feedbackGenerator.impactOccurred() + } + + self.isKaraokeDataLoading = false + self.pageNo += 1 + guard let dataCount = data.data?.totalRecords, let data = data.data?.karaokeData else{return} + + self.totalDataCount = dataCount + self.karaokeListData.append(contentsOf: data) self.vc.karaokeListingTableView.reloadData() self.vc.tableHeight.constant = self.vc.karaokeListingTableView.contentSize.height + 100 @@ -197,24 +214,19 @@ class KaraokeListingVM{ self.setHeaderData() } } - self.vc.loadMoreActivityIndicator.stopAnimating() self.vc.loadMoreActivityIndicator.hidesWhenStopped = true - - if self.karaokeListData.count == dataCount{ - self.vc.loadMoreBtn.isHidden = true - }else{ - self.vc.loadMoreBtn.isHidden = false - } default: - break + self.isKaraokeDataLoading = false } case .failure(let error): guard let self else{ + self?.isKaraokeDataLoading = false return } self.stopShimmer() + self.isKaraokeDataLoading = false vc.toast(msg: error.localizedDescription , time: 2) vc.noDataStack.isHidden = false } diff --git a/WOKA/Shop/Controller/ShopListingVC.swift b/WOKA/Shop/Controller/ShopListingVC.swift index bc106ab..d2f335f 100644 --- a/WOKA/Shop/Controller/ShopListingVC.swift +++ b/WOKA/Shop/Controller/ShopListingVC.swift @@ -95,9 +95,16 @@ extension ShopListingVC : TableViewSRC{ MAke logic for ads */ if vm.superCatData[indexPath.row].isAD == true{ - if let adsData = AuthFunc.shareInstance.adsData{ + if let adsData = AuthFunc.shareInstance.adsData, let shopSuperCatAd = adsData.result?.filter({$0.slug == AdsEnum.shop_super_category.rawValue}).first{ + + // URL maybe null so handle it + if shopSuperCatAd.advertisement?.isActive == "1" && shopSuperCatAd.advertisement?.adLink == nil{ + // it means local ad is active and url is null + return + } + // check if ads data contains ad for webseries - if let shopSuperCatAd = adsData.result?.filter({$0.slug == AdsEnum.shop_super_category.rawValue}).first,let adLink = shopSuperCatAd.advertisement?.adLink, let adID = shopSuperCatAd.advertisement?.id{ + if let adLink = shopSuperCatAd.advertisement?.adLink, let adID = shopSuperCatAd.advertisement?.id{ PersistentStorage.shared.addAdsCount(adID: adID,clicks: 1) if let url = URL(string: adLink), UIApplication.shared.canOpenURL(url) { UIApplication.shared.open(url) diff --git a/WOKA/Theme/Base.lproj/Theme.storyboard b/WOKA/Theme/Base.lproj/Theme.storyboard index dda194c..82f4e5f 100644 --- a/WOKA/Theme/Base.lproj/Theme.storyboard +++ b/WOKA/Theme/Base.lproj/Theme.storyboard @@ -581,16 +581,16 @@ - + - + @@ -652,7 +652,7 @@ - + - + - + @@ -696,7 +696,7 @@ - + - +