diff --git a/Main.storyboard b/Main.storyboard index 233009b6..01d8dbd2 100755 --- a/Main.storyboard +++ b/Main.storyboard @@ -3,7 +3,7 @@ - + diff --git a/Thrive Church Official App.xcodeproj/project.pbxproj b/Thrive Church Official App.xcodeproj/project.pbxproj index 27e6fd79..69a1fc80 100644 --- a/Thrive Church Official App.xcodeproj/project.pbxproj +++ b/Thrive Church Official App.xcodeproj/project.pbxproj @@ -1019,12 +1019,12 @@ CODE_SIGN_ENTITLEMENTS = "Thrive Church Official App.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1.7.6.8; + CURRENT_PROJECT_VERSION = 1.7.7.3; DEVELOPMENT_TEAM = 87ME42WJYA; INFOPLIST_FILE = "$(SRCROOT)/Thrive Community Church/Properties/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 1.7.6; + MARKETING_VERSION = 1.7.7; PRODUCT_BUNDLE_IDENTIFIER = "com.thrive-fl.ThriveCommunityChurch"; PRODUCT_NAME = "Thrive Church Official App"; PROVISIONING_PROFILE = ""; @@ -1044,12 +1044,12 @@ CODE_SIGN_ENTITLEMENTS = "Thrive Church Official App.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1.7.6.8; + CURRENT_PROJECT_VERSION = 1.7.7.3; DEVELOPMENT_TEAM = 87ME42WJYA; INFOPLIST_FILE = "$(SRCROOT)/Thrive Community Church/Properties/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 1.7.6; + MARKETING_VERSION = 1.7.7; PRODUCT_BUNDLE_IDENTIFIER = "com.thrive-fl.ThriveCommunityChurch"; PRODUCT_NAME = "Thrive Church Official App"; PROVISIONING_PROFILE = ""; diff --git a/Thrive Community Church/Extensions/DoubleExtension.swift b/Thrive Community Church/Extensions/DoubleExtension.swift index 79076fc5..1352c36c 100644 --- a/Thrive Community Church/Extensions/DoubleExtension.swift +++ b/Thrive Community Church/Extensions/DoubleExtension.swift @@ -32,13 +32,6 @@ extension Double { return (Double(self) / 1024 / 1024) } - /// return a number of seconds into a number of hours minutes and seconds - func secondsToHoursMinutesSeconds() -> (Double, Double, Double) { - let (hr, minf) = modf (self / 3600) - let (min, secf) = modf (60 * minf) - return (hr, min, 60 * secf) - } - /// return a duration in minutes or hours for the length of a number of seconds func formatDurationForUI(displayAsPositional: Bool = false) -> String? { diff --git a/Thrive Community Church/Extensions/UIColorExtension.swift b/Thrive Community Church/Extensions/UIColorExtension.swift index cc5f0cb8..c324f0b2 100644 --- a/Thrive Community Church/Extensions/UIColorExtension.swift +++ b/Thrive Community Church/Extensions/UIColorExtension.swift @@ -16,5 +16,6 @@ extension UIColor { static var lighterBlueGray = UIColor(red: 70/255, green: 106/255, blue: 134/255, alpha: 1) static var bgDarkBlue = UIColor(red: 27/255, green: 41/255, blue: 51/255, alpha: 1) static var almostBlack = UIColor(red: 25/255, green: 25/255, blue: 25/255, alpha: 1) + static var transparentAlmostBlack = UIColor(red: 25/255, green: 25/255, blue: 25/255, alpha: 0.5) static var darkGrey = UIColor(red: 63/255, green: 63/255, blue: 63/255, alpha: 1) } diff --git a/Thrive Community Church/MainViewControllers/Listen/Controllers/ListenCollectionViewController.swift b/Thrive Community Church/MainViewControllers/Listen/Controllers/ListenCollectionViewController.swift index d9a55307..d4c09010 100644 --- a/Thrive Community Church/MainViewControllers/Listen/Controllers/ListenCollectionViewController.swift +++ b/Thrive Community Church/MainViewControllers/Listen/Controllers/ListenCollectionViewController.swift @@ -21,6 +21,7 @@ MFMailComposeViewControllerDelegate { var apiUrl: String = "nil" var secondsRemaining: Double? var expireTime: Date? + var nextLive: Date? var timer = Timer() var loading: Bool = false var pollingData: LivePollingResponse? @@ -29,6 +30,7 @@ MFMailComposeViewControllerDelegate { var playedMessage: Bool = false private var alreadySelected: Bool = false var seriesMapping = [String: SermonSeries]() + var countdownTimer: Timer? = nil // Loading View var footerView: CustomFooterView? @@ -59,10 +61,26 @@ MFMailComposeViewControllerDelegate { let backgroundView: UIView = { let view = UIView() view.backgroundColor = UIColor.bgDarkBlue - view.translatesAutoresizingMaskIntoConstraints = true + view.translatesAutoresizingMaskIntoConstraints = false return view }() + let countdownBannerView: UIView = { + let view = UIView() + view.backgroundColor = .clear + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + let countdownBannerLabel: UILabel = { + let label = UILabel() + label.font = UIFont(name: "Avenir-Medium", size: 15) + label.textColor = .lessLightLightGray + label.textAlignment = .center + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + let retryButton: UIButton = { let button = UIButton() button.setTitle("Retry?", for: .normal) @@ -376,6 +394,101 @@ MFMailComposeViewControllerDelegate { } } + func removeTimer() { + self.countdownTimer?.invalidate() + self.countdownTimer = nil + self.countdownTimer = Timer() + } + + func setBannerTime(nextLive: Date) { + view.addSubview(countdownBannerView) + + let now = Date() + let nowDateFormatter = DateFormatter() + nowDateFormatter.locale = Locale(identifier: "en_US_POSIX") + nowDateFormatter.timeZone = TimeZone(secondsFromGMT: 0) + nowDateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" + let nowString = nowDateFormatter.string(from: now) + + let nowDate = nowDateFormatter.date(from: nowString) ?? Date() + + let remaining: TimeInterval = nextLive.timeIntervalSince(nowDate) + + if (remaining.isZero || remaining.isLess(than: 0.0)) { + return + } + + let formatter = DateComponentsFormatter() + formatter.allowedUnits = [.day, .hour, .minute, .second] + formatter.unitsStyle = .abbreviated + + let formattedString = formatter.string(from: remaining)! + + countdownBannerLabel.text = "We're live in \(formattedString)" + + NSLayoutConstraint.activate([ + countdownBannerView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + countdownBannerView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + countdownBannerView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + countdownBannerView.heightAnchor.constraint(equalToConstant: 22) + ]) + + let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.dark) + let blurEffectView = UIVisualEffectView(effect: blurEffect) + blurEffectView.frame = countdownBannerView.bounds + blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + countdownBannerView.addSubview(blurEffectView) + + + countdownBannerView.addSubview(countdownBannerLabel) + NSLayoutConstraint.activate([ + countdownBannerLabel.centerYAnchor.constraint(equalTo: countdownBannerView.centerYAnchor), + countdownBannerLabel.centerXAnchor.constraint(equalTo: countdownBannerView.centerXAnchor) + ]) + + startTimerCountdownForNextLive(remaining: remaining) + } + + func startTimerCountdownForNextLive(remaining: TimeInterval) { + + + self.countdownTimer = Timer.scheduledTimer(timeInterval: 1.0, + target: self, + selector: #selector(self.processCountdownTimerChange), + userInfo: nil, repeats: true) + + // make the timer tick even when the user is scrolling + RunLoop.current.add(self.countdownTimer!, forMode: .common) + } + + @objc func processCountdownTimerChange() { + + let now = Date() + + let remaining: TimeInterval = nextLive?.timeIntervalSince(now) ?? 0 + + if (remaining.isZero || remaining.isLess(than: 0.0)) { + self.countdownBannerView.isHidden = true + self.removeTimer() + self.livestreamButton.isEnabled = true + + // let's check after 30s if the stream is "really active" + // this allows the stream to expire after it should but gives the server 30s + // to set the stream as acive + DispatchQueue.main.asyncAfter(wallDeadline: .now() + 30, execute: { + self.pollForLiveData() + }) + } + + let formatter = DateComponentsFormatter() + formatter.allowedUnits = [.day, .hour, .minute, .second] + formatter.unitsStyle = .abbreviated + + let formattedString = formatter.string(from: remaining)! + + countdownBannerLabel.text = "We're live in \(formattedString)" + } + func setupViews() { view.addSubview(backgroundView) view.addSubview(apiErrorMessage) diff --git a/Thrive Community Church/MainViewControllers/Listen/Controllers/ListenCollectionViewControllerRepoExtension.swift b/Thrive Community Church/MainViewControllers/Listen/Controllers/ListenCollectionViewControllerRepoExtension.swift index 60d98fba..3bdd8cbf 100644 --- a/Thrive Community Church/MainViewControllers/Listen/Controllers/ListenCollectionViewControllerRepoExtension.swift +++ b/Thrive Community Church/MainViewControllers/Listen/Controllers/ListenCollectionViewControllerRepoExtension.swift @@ -183,6 +183,18 @@ extension ListenCollectionViewController { else { // maybe we can have an async thread here running that checks to see if every minute, // we are getting close to beginning a live stream? (thoughts) + + DispatchQueue.main.async { + // get the next time from the API + let nowDateFormatter = DateFormatter() + nowDateFormatter.locale = Locale(identifier: "en_US_POSIX") + nowDateFormatter.timeZone = TimeZone(secondsFromGMT: 0) + nowDateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" + + self.nextLive = nowDateFormatter.date(from: livestream.NextLive ?? "") ?? Date() + + self.setBannerTime(nextLive: self.nextLive!) + } } }) } @@ -205,7 +217,6 @@ extension ListenCollectionViewController { return } - do { //let JSON = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) diff --git a/Thrive Community Church/MainViewControllers/Listen/Controllers/SeriesViewController.swift b/Thrive Community Church/MainViewControllers/Listen/Controllers/SeriesViewController.swift index 36c1e5bc..cccbf950 100644 --- a/Thrive Community Church/MainViewControllers/Listen/Controllers/SeriesViewController.swift +++ b/Thrive Community Church/MainViewControllers/Listen/Controllers/SeriesViewController.swift @@ -231,6 +231,14 @@ class SeriesViewController: UIViewController, UITableViewDelegate, UITableViewDa let selectedMessage = messages[indexPath.row] messageForDownload = selectedMessage + // tapping on an item that has no options doesn't do anything + if selectedMessage.AudioUrl == nil && selectedMessage.VideoUrl == nil && + selectedMessage.PassageRef == nil + { + self.seriesTable.deselectRow(indexPath: indexPath) + return + } + let alert = UIAlertController(title: "\(series?.Name ?? "") - Week \(selectedMessage.WeekNum ?? 0)", message: "Please select an action", preferredStyle: .actionSheet) diff --git a/Thrive Community Church/MainViewControllers/Listen/Models/LivestreamingResponse.swift b/Thrive Community Church/MainViewControllers/Listen/Models/LivestreamingResponse.swift index 20fe913f..231ccefb 100644 --- a/Thrive Community Church/MainViewControllers/Listen/Models/LivestreamingResponse.swift +++ b/Thrive Community Church/MainViewControllers/Listen/Models/LivestreamingResponse.swift @@ -15,4 +15,5 @@ class LivestreamingResponse: NSObject, Decodable { var Title: String? var SpecialEventTimes: String? var IsSpecialEvent: Bool = false + var NextLive: String? } diff --git a/VersionHistory.md b/VersionHistory.md index 69b29d6c..437fbe06 100644 --- a/VersionHistory.md +++ b/VersionHistory.md @@ -1,8 +1,16 @@ # 1.7 -### Rev. 6 (current) +### Rev. 7 (current)
- Oct. 17, 2020 + Dec 27, 2020 +
    +
  • You'll now be able to see how long until we go live!
  • +
+
+ +### Rev. 6 +
+ Oct 17, 2020
  • Sign up to receive notifications from us right on your phone!
  • Fixing an issue with users who attempt to listen to sermon audio from recently played when they only have 1 recently played item