diff --git a/Kickstarter-iOS/Features/Dashboard/Controller/DashboardViewControllerTests.swift b/Kickstarter-iOS/Features/Dashboard/Controller/DashboardViewControllerTests.swift index 0e96af8406..2a51b6bb13 100644 --- a/Kickstarter-iOS/Features/Dashboard/Controller/DashboardViewControllerTests.swift +++ b/Kickstarter-iOS/Features/Dashboard/Controller/DashboardViewControllerTests.swift @@ -130,5 +130,5 @@ private let fundingStats = stats.enumerated().map { idx, pledged in .template |> ProjectStatsEnvelope.FundingDateStats.lens.cumulativePledged .~ pledged |> ProjectStatsEnvelope.FundingDateStats.lens.date - .~ (cosmicSurgery.dates.launchedAt + TimeInterval(idx * 86_400)) + .~ ((cosmicSurgery.dates.launchedAt ?? 0) + TimeInterval(idx * 86_400)) } diff --git a/Kickstarter-iOS/Features/DashboardProjects/Views/FundingGraphView.swift b/Kickstarter-iOS/Features/DashboardProjects/Views/FundingGraphView.swift index f4dba52e93..3e925525f0 100644 --- a/Kickstarter-iOS/Features/DashboardProjects/Views/FundingGraphView.swift +++ b/Kickstarter-iOS/Features/DashboardProjects/Views/FundingGraphView.swift @@ -66,10 +66,15 @@ public final class FundingGraphView: UIView { .enumerated() .map { index, stat in CGPoint(x: index, y: stat.cumulativePledged) } - let durationInDays = dateToDayNumber( - launchDate: project.dates.launchedAt, - currentDate: self.project.dates.deadline - ) + var durationInDays: CGFloat = 1 + + if let launchedAt = project.dates.launchedAt, + let deadline = project.dates.deadline { + durationInDays = dateToDayNumber( + launchDate: launchedAt, + currentDate: deadline + ) + } let goal = self.project.stats.goal diff --git a/Kickstarter-iOS/Features/DashboardProjects/Views/FundingGraphViewTests.swift b/Kickstarter-iOS/Features/DashboardProjects/Views/FundingGraphViewTests.swift index 3be17a64a7..256fc6fa6e 100644 --- a/Kickstarter-iOS/Features/DashboardProjects/Views/FundingGraphViewTests.swift +++ b/Kickstarter-iOS/Features/DashboardProjects/Views/FundingGraphViewTests.swift @@ -133,6 +133,6 @@ private func fundingStats(forProject project: Project, pledgeValues: [Int]) ProjectStatsEnvelope.FundingDateStats.template |> ProjectStatsEnvelope.FundingDateStats.lens.cumulativePledged .~ pledged |> ProjectStatsEnvelope.FundingDateStats.lens.date - .~ (project.dates.launchedAt + TimeInterval(idx * 60 * 60 * 24)) + .~ ((project.dates.launchedAt ?? 0.0) + TimeInterval(idx * 60 * 60 * 24)) } } diff --git a/Kickstarter-iOS/Library/CookieRefTagFunctions.swift b/Kickstarter-iOS/Library/CookieRefTagFunctions.swift index da6c4c040e..963e954d12 100644 --- a/Kickstarter-iOS/Library/CookieRefTagFunctions.swift +++ b/Kickstarter-iOS/Library/CookieRefTagFunctions.swift @@ -55,8 +55,10 @@ public func cookieFrom(refTag: RefTag, project: Project) -> HTTPCookie? { properties[.domain] = URL(string: project.urls.web.project)?.host properties[.path] = URL(string: project.urls.web.project)?.path properties[.version] = 0 - properties[.expires] = AppEnvironment.current.dateType - .init(timeIntervalSince1970: project.dates.deadline).date + + if let deadline = project.dates.deadline { + properties[.expires] = AppEnvironment.current.dateType.init(timeIntervalSince1970: deadline).date + } return HTTPCookie(properties: properties) } diff --git a/KsApi/models/Project.swift b/KsApi/models/Project.swift index 3f7536b8ce..4f42babc55 100644 --- a/KsApi/models/Project.swift +++ b/KsApi/models/Project.swift @@ -166,24 +166,33 @@ public struct Project { } public struct Dates { - public var deadline: TimeInterval + public var deadline: TimeInterval? public var featuredAt: TimeInterval? public var finalCollectionDate: TimeInterval? - public var launchedAt: TimeInterval + public var launchedAt: TimeInterval? public var stateChangedAt: TimeInterval /** Returns project duration in Days */ public func duration(using calendar: Calendar = .current) -> Int? { - let deadlineDate = Date(timeIntervalSince1970: self.deadline) - let launchedAtDate = Date(timeIntervalSince1970: self.launchedAt) + guard let deadlineDateValue = self.deadline, + let launchedAtDateValue = self.launchedAt else { + return nil + } + + let deadlineDate = Date(timeIntervalSince1970: deadlineDateValue) + let launchedAtDate = Date(timeIntervalSince1970: launchedAtDateValue) return calendar.dateComponents([.day], from: launchedAtDate, to: deadlineDate).day } public func hoursRemaining(from date: Date = Date(), using calendar: Calendar = .current) -> Int? { - let deadlineDate = Date(timeIntervalSince1970: self.deadline) + guard let deadlineDateValue = self.deadline else { + return nil + } + + let deadlineDate = Date(timeIntervalSince1970: deadlineDateValue) guard let hoursRemaining = calendar.dateComponents([.hour], from: date, to: deadlineDate).hour else { return nil @@ -225,8 +234,12 @@ public struct Project { } public func endsIn48Hours(today: Date = Date()) -> Bool { + guard let datesDeadlineValue = self.dates.deadline else { + return true + } + let twoDays: TimeInterval = 60.0 * 60.0 * 48.0 - return self.dates.deadline - today.timeIntervalSince1970 <= twoDays + return datesDeadlineValue - today.timeIntervalSince1970 <= twoDays } public func isFeaturedToday(today: Date = Date(), calendar: Calendar = .current) -> Bool { diff --git a/KsApi/models/lenses/Project.DatesLenses.swift b/KsApi/models/lenses/Project.DatesLenses.swift index 8d5bb5b829..912218cb09 100644 --- a/KsApi/models/lenses/Project.DatesLenses.swift +++ b/KsApi/models/lenses/Project.DatesLenses.swift @@ -3,7 +3,7 @@ import Prelude extension Project.Dates { public enum lens { - public static let deadline = Lens( + public static let deadline = Lens( view: { $0.deadline }, set: { Project.Dates( deadline: $0, featuredAt: $1.featuredAt, launchedAt: $1.launchedAt, @@ -19,7 +19,7 @@ extension Project.Dates { ) } ) - public static let launchedAt = Lens( + public static let launchedAt = Lens( view: { $0.launchedAt }, set: { Project.Dates( deadline: $1.deadline, featuredAt: $1.featuredAt, launchedAt: $0, diff --git a/KsApi/models/lenses/ProjectLenses.swift b/KsApi/models/lenses/ProjectLenses.swift index fc4a7024d3..5bb8c4ba8f 100644 --- a/KsApi/models/lenses/ProjectLenses.swift +++ b/KsApi/models/lenses/ProjectLenses.swift @@ -502,7 +502,7 @@ extension Lens where Whole == Project, Part == Project.MemberData { } extension Lens where Whole == Project, Part == Project.Dates { - public var deadline: Lens { + public var deadline: Lens { return Project.lens.dates .. Project.Dates.lens.deadline } @@ -510,7 +510,7 @@ extension Lens where Whole == Project, Part == Project.Dates { return Project.lens.dates .. Project.Dates.lens.featuredAt } - public var launchedAt: Lens { + public var launchedAt: Lens { return Project.lens.dates .. Project.Dates.lens.launchedAt } diff --git a/Library/Tracking/KSRAnalytics.swift b/Library/Tracking/KSRAnalytics.swift index be6d1a4706..749e295443 100644 --- a/Library/Tracking/KSRAnalytics.swift +++ b/Library/Tracking/KSRAnalytics.swift @@ -1337,9 +1337,9 @@ private func projectProperties( props["comments_count"] = project.stats.commentsCount ?? 0 props["currency"] = project.stats.currency props["creator_uid"] = String(project.creator.id) - props["deadline"] = project.dates.deadline.toISO8601DateTimeString() + props["deadline"] = project.dates.deadline?.toISO8601DateTimeString() props["has_add_ons"] = project.hasAddOns - props["launched_at"] = project.dates.launchedAt.toISO8601DateTimeString() + props["launched_at"] = project.dates.launchedAt?.toISO8601DateTimeString() props["name"] = project.name props["pid"] = String(project.id) props["category"] = project.category.parentAnalyticsName diff --git a/Library/ViewModels/BackerDashboardProjectCellViewModel.swift b/Library/ViewModels/BackerDashboardProjectCellViewModel.swift index e1455c4f48..2c2d0b0f0e 100644 --- a/Library/ViewModels/BackerDashboardProjectCellViewModel.swift +++ b/Library/ViewModels/BackerDashboardProjectCellViewModel.swift @@ -88,7 +88,11 @@ public final class BackerDashboardProjectCellViewModel: BackerDashboardProjectCe private func metadataString(for project: Project) -> String { switch project.state { case .live: - let duration = Format.duration(secondsInUTC: project.dates.deadline, abbreviate: true, useToGo: false) + guard let deadline = project.dates.deadline else { + return "" + } + + let duration = Format.duration(secondsInUTC: deadline, abbreviate: true, useToGo: false) return "\(duration.time) \(duration.unit)" default: return stateString(for: project) diff --git a/Library/ViewModels/DashboardFundingCellViewModel.swift b/Library/ViewModels/DashboardFundingCellViewModel.swift index ea56a4563b..28d4823b5b 100644 --- a/Library/ViewModels/DashboardFundingCellViewModel.swift +++ b/Library/ViewModels/DashboardFundingCellViewModel.swift @@ -79,7 +79,11 @@ public final class DashboardFundingCellViewModel: DashboardFundingCellViewModelI self.backersText = statsProject.map { _, project in Format.wholeNumber(project.stats.backersCount) } self.deadlineDateText = statsProject.map { _, project in - Format.date(secondsInUTC: project.dates.deadline, dateStyle: .short, timeStyle: .none) + guard let deadline = project.dates.deadline else { + return "" + } + + return Format.date(secondsInUTC: deadline, dateStyle: .short, timeStyle: .none) } self.goalText = statsProject.map { _, project in @@ -111,14 +115,22 @@ public final class DashboardFundingCellViewModel: DashboardFundingCellViewModelI self.launchDateText = statsProject .map { _, project in - Format.date(secondsInUTC: project.dates.launchedAt, dateStyle: .short, timeStyle: .none) + guard let launchedAt = project.dates.launchedAt else { + return "" + } + + return Format.date(secondsInUTC: launchedAt, dateStyle: .short, timeStyle: .none) } self.pledgedText = statsProject .map { _, project in Format.currency(project.stats.pledged, country: project.country) } - let timeRemaining = statsProject.map { _, project in - Format.duration(secondsInUTC: project.dates.deadline, useToGo: true) + let timeRemaining = statsProject.map { _, project -> (String, String) in + guard let deadline = project.dates.deadline else { + return ("", "") + } + + return Format.duration(secondsInUTC: deadline, useToGo: true) } self.timeRemainingTitleText = timeRemaining.map(first) @@ -130,7 +142,12 @@ public final class DashboardFundingCellViewModel: DashboardFundingCellViewModelI let pledged = Format.currency(project.stats.pledged, country: project.country) let goal = Format.currency(project.stats.goal, country: project.country) let backersCount = project.stats.backersCount - let (time, unit) = Format.duration(secondsInUTC: project.dates.deadline, useToGo: false) + var (time, unit) = ("", "") + + if let deadline = project.dates.deadline { + (time, unit) = Format.duration(secondsInUTC: deadline, useToGo: false) + } + let timeLeft = time + " " + unit return project.state == .live ? diff --git a/Library/ViewModels/DashboardFundingCellViewModelTests.swift b/Library/ViewModels/DashboardFundingCellViewModelTests.swift index 167836d33d..cd59fb49a7 100644 --- a/Library/ViewModels/DashboardFundingCellViewModelTests.swift +++ b/Library/ViewModels/DashboardFundingCellViewModelTests.swift @@ -44,6 +44,7 @@ internal final class DashboardFundingCellViewModelTests: TestCase { |> Project.lens.country .~ .us let stats = [ProjectStatsEnvelope.FundingDateStats.template] + let deadline = liveProject.dates.deadline! self.vm.inputs.configureWith(fundingDateStats: stats, project: liveProject) @@ -53,14 +54,15 @@ internal final class DashboardFundingCellViewModelTests: TestCase { pledged: Format.currency(liveProject.stats.pledged, country: liveProject.country), goal: Format.currency(liveProject.stats.goal, country: liveProject.country), backers_count: liveProject.stats.backersCount, - time_left: Format.duration(secondsInUTC: liveProject.dates.deadline).time + " " + - Format.duration(secondsInUTC: liveProject.dates.deadline).unit + time_left: Format.duration(secondsInUTC: deadline).time + " " + + Format.duration(secondsInUTC: deadline).unit ) ], "Live project stats value emits." ) let nonLiveProject = .template |> Project.lens.state .~ .successful + let nonLiveDeadline = nonLiveProject.dates.deadline! self.vm.inputs.configureWith(fundingDateStats: stats, project: nonLiveProject) @@ -70,15 +72,15 @@ internal final class DashboardFundingCellViewModelTests: TestCase { pledged: Format.currency(liveProject.stats.pledged, country: liveProject.country), goal: Format.currency(liveProject.stats.goal, country: liveProject.country), backers_count: liveProject.stats.backersCount, - time_left: Format.duration(secondsInUTC: liveProject.dates.deadline).time + " " + - Format.duration(secondsInUTC: liveProject.dates.deadline).unit + time_left: Format.duration(secondsInUTC: deadline).time + " " + + Format.duration(secondsInUTC: deadline).unit ), Strings.dashboard_graphs_funding_accessibility_non_live_stat_value( pledged: Format.currency(nonLiveProject.stats.pledged, country: nonLiveProject.country), goal: Format.currency(nonLiveProject.stats.goal, country: nonLiveProject.country), backers_count: nonLiveProject.stats.backersCount, - time_left: Format.duration(secondsInUTC: nonLiveProject.dates.deadline).time + " " + - Format.duration(secondsInUTC: nonLiveProject.dates.deadline).unit + time_left: Format.duration(secondsInUTC: nonLiveDeadline).time + " " + + Format.duration(secondsInUTC: nonLiveDeadline).unit ) ], "Non live project stats value emits." diff --git a/Library/ViewModels/DiscoveryPostcardViewModel.swift b/Library/ViewModels/DiscoveryPostcardViewModel.swift index cdd2b60380..f51f5f9022 100644 --- a/Library/ViewModels/DiscoveryPostcardViewModel.swift +++ b/Library/ViewModels/DiscoveryPostcardViewModel.swift @@ -164,9 +164,13 @@ public final class DiscoveryPostcardViewModel: DiscoveryPostcardViewModelType, self.backersSubtitleLabelText = backersTitleAndSubtitleText.map { _, subtitle in subtitle ?? "" } let deadlineTitleAndSubtitle = configuredProject - .map { - $0.state == .live - ? Format.duration(secondsInUTC: $0.dates.deadline, useToGo: true) + .map { project -> (String, String) in + guard let datesDeadline = project.dates.deadline else { + return ("", "") + } + + return project.state == .live + ? Format.duration(secondsInUTC: datesDeadline, useToGo: true) : ("", "") } diff --git a/Library/ViewModels/DiscoveryPostcardViewModelTests.swift b/Library/ViewModels/DiscoveryPostcardViewModelTests.swift index e80a3a9282..a4fd3355a0 100644 --- a/Library/ViewModels/DiscoveryPostcardViewModelTests.swift +++ b/Library/ViewModels/DiscoveryPostcardViewModelTests.swift @@ -166,12 +166,13 @@ internal final class DiscoveryPostcardViewModelTests: TestCase { func testProjectStatsEmit() { let project = Project.template + let deadline = project.dates.deadline! self.vm.inputs.configure(with: (project, nil, nil)) self.backersTitleLabelText.assertValues([Format.wholeNumber(project.stats.backersCount)]) - let deadlineTitleAndSubtitle = Format.duration(secondsInUTC: project.dates.deadline, useToGo: true) + let deadlineTitleAndSubtitle = Format.duration(secondsInUTC: deadline, useToGo: true) self.deadlineSubtitleLabelText.assertValues([deadlineTitleAndSubtitle.unit]) self.deadlineTitleLabelText.assertValues([deadlineTitleAndSubtitle.time]) diff --git a/Library/ViewModels/DiscoveryProjectCardViewModel.swift b/Library/ViewModels/DiscoveryProjectCardViewModel.swift index 12321619ed..31f2e3b501 100644 --- a/Library/ViewModels/DiscoveryProjectCardViewModel.swift +++ b/Library/ViewModels/DiscoveryProjectCardViewModel.swift @@ -115,8 +115,12 @@ public final class DiscoveryProjectCardViewModel: DiscoveryProjectCardViewModelT case .failed: return ("", Strings.profile_projects_status_unsuccessful()) case .successful: return ("", Strings.profile_projects_status_successful()) case .live: + guard let projectDeadline = project.dates.deadline else { + return ("", "") + } + let (duration, unit) = Format.duration( - secondsInUTC: project.dates.deadline, + secondsInUTC: projectDeadline, abbreviate: false, useToGo: true, env: AppEnvironment.current diff --git a/Library/ViewModels/ManagePledgeViewModel.swift b/Library/ViewModels/ManagePledgeViewModel.swift index a7b2ac2437..b77065b7f2 100644 --- a/Library/ViewModels/ManagePledgeViewModel.swift +++ b/Library/ViewModels/ManagePledgeViewModel.swift @@ -526,7 +526,8 @@ private func managePledgeSummaryViewData( backedReward: Reward, backing: Backing ) -> ManagePledgeSummaryViewData? { - guard let backer = backing.backer else { return nil } + guard let backer = backing.backer, + let deadline = project.dates.deadline else { return nil } let isRewardLocalPickup = isRewardLocalPickup(backing.reward) @@ -546,7 +547,7 @@ private func managePledgeSummaryViewData( pledgeAmount: backing.amount, pledgedOn: backing.pledgedAt, projectCurrencyCountry: projectCurrencyCountry, - projectDeadline: project.dates.deadline, + projectDeadline: deadline, projectState: project.state, rewardMinimum: allRewardsTotal(for: backing), shippingAmount: backing.shippingAmount.flatMap(Double.init), diff --git a/Library/ViewModels/MostPopularSearchProjectCellViewModel.swift b/Library/ViewModels/MostPopularSearchProjectCellViewModel.swift index 7d7257e5c5..5d5d2ba4fc 100644 --- a/Library/ViewModels/MostPopularSearchProjectCellViewModel.swift +++ b/Library/ViewModels/MostPopularSearchProjectCellViewModel.swift @@ -69,7 +69,12 @@ public final class MostPopularSearchProjectCellViewModel: MostPopularSearchProje private func metadataString(for project: Project) -> String { switch project.state { case .live: - let duration = Format.duration(secondsInUTC: project.dates.deadline, abbreviate: true, useToGo: false) + guard let deadline = project.dates.deadline else { + return "" + } + + let duration = Format.duration(secondsInUTC: deadline, abbreviate: true, useToGo: false) + return "\(duration.time) \(duration.unit)" default: return stateString(for: project) diff --git a/Library/ViewModels/PledgeSummaryViewModel.swift b/Library/ViewModels/PledgeSummaryViewModel.swift index e9ec469b73..fd9a067318 100644 --- a/Library/ViewModels/PledgeSummaryViewModel.swift +++ b/Library/ViewModels/PledgeSummaryViewModel.swift @@ -125,7 +125,12 @@ private func attributedCurrency(with project: Project, total: Double) -> NSAttri } private func attributedConfirmationString(with project: Project, pledgeTotal: Double) -> NSAttributedString { - let date = Format.date(secondsInUTC: project.dates.deadline, template: "MMMM d, yyyy") + var date = "" + + if let deadline = project.dates.deadline { + date = Format.date(secondsInUTC: deadline, template: "MMMM d, yyyy") + } + let projectCurrencyCountry = projectCountry(forCurrency: project.stats.currency) ?? project.country let pledgeTotal = Format.currency(pledgeTotal, country: projectCurrencyCountry) diff --git a/Library/ViewModels/ProjectActivityLaunchCellViewModel.swift b/Library/ViewModels/ProjectActivityLaunchCellViewModel.swift index 7b7d7a778a..17f8d547c2 100644 --- a/Library/ViewModels/ProjectActivityLaunchCellViewModel.swift +++ b/Library/ViewModels/ProjectActivityLaunchCellViewModel.swift @@ -31,12 +31,18 @@ public final class ProjectActivityLaunchCellViewModel: ProjectActivityLaunchCell self.backgroundImageURL = project.map { $0.photo.med }.map(URL.init(string:)) self.title = project.map { project in - Strings.dashboard_activity_project_name_launched( - project_name: project.name, - launch_date: Format.date( - secondsInUTC: project.dates.launchedAt, + var formatted = "" + + if let launchedAtDate = project.dates.launchedAt { + formatted = Format.date( + secondsInUTC: launchedAtDate, dateStyle: .long, timeStyle: .none - ).nonBreakingSpaced(), + ).nonBreakingSpaced() + } + + return Strings.dashboard_activity_project_name_launched( + project_name: project.name, + launch_date: formatted, goal: Format.currency(project.stats.goal, country: project.country).nonBreakingSpaced() ) } diff --git a/Library/ViewModels/ProjectActivitySuccessCellViewModel.swift b/Library/ViewModels/ProjectActivitySuccessCellViewModel.swift index 0820e4edd0..1c7342f36d 100644 --- a/Library/ViewModels/ProjectActivitySuccessCellViewModel.swift +++ b/Library/ViewModels/ProjectActivitySuccessCellViewModel.swift @@ -31,14 +31,20 @@ public final class ProjectActivitySuccessCellViewModel: ProjectActivitySuccessCe self.backgroundImageURL = project.map { $0.photo.med }.map(URL.init(string:)) self.title = project.map { project in - Strings.dashboard_activity_successfully_raised_pledged( + var projectDeadline = "" + + if let projectDeadlineValue = project.dates.deadline { + projectDeadline = Format.date( + secondsInUTC: projectDeadlineValue, dateStyle: .long, + timeStyle: .none + ).nonBreakingSpaced() + } + + return Strings.dashboard_activity_successfully_raised_pledged( pledged: Format.currency(project.stats.pledged, country: project.country).nonBreakingSpaced(), backers: Strings.general_backer_count_backers(backer_count: project.stats.backersCount) .nonBreakingSpaced(), - deadline: Format.date( - secondsInUTC: project.dates.deadline, dateStyle: .long, - timeStyle: .none - ).nonBreakingSpaced() + deadline: projectDeadline ) } } diff --git a/Library/ViewModels/ProjectPamphletCreatorHeaderCellViewModel.swift b/Library/ViewModels/ProjectPamphletCreatorHeaderCellViewModel.swift index b994963f9d..c9c942e828 100644 --- a/Library/ViewModels/ProjectPamphletCreatorHeaderCellViewModel.swift +++ b/Library/ViewModels/ProjectPamphletCreatorHeaderCellViewModel.swift @@ -56,19 +56,23 @@ private func title(for project: Project) -> String { return project.state == .live ? Strings.View_progress() : Strings.View_dashboard() } -private func attributedLaunchDateString(with project: Project) - -> NSAttributedString? { - let date = Format.date( - secondsInUTC: project.dates.launchedAt, - dateStyle: .long, - timeStyle: .none, - timeZone: UTCTimeZone - ) - let fullString = Strings.You_launched_this_project_on_launch_date(launch_date: date) +private func attributedLaunchDateString(with project: Project) -> NSAttributedString? { + var launchDate = "" + + if let date = project.dates.launchedAt { + launchDate = Format.date( + secondsInUTC: date, + dateStyle: .long, + timeStyle: .none, + timeZone: UTCTimeZone + ) + } + + let fullString = Strings.You_launched_this_project_on_launch_date(launch_date: launchDate) let attributedString: NSMutableAttributedString = NSMutableAttributedString(string: fullString) let fullRange = (fullString as NSString).localizedStandardRange(of: fullString) - let rangeDate: NSRange = (fullString as NSString).localizedStandardRange(of: date) + let rangeDate: NSRange = (fullString as NSString).localizedStandardRange(of: launchDate) let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.alignment = .left diff --git a/Library/ViewModels/ProjectPamphletMainCellViewModel.swift b/Library/ViewModels/ProjectPamphletMainCellViewModel.swift index 59e7b37730..c1914cffbb 100644 --- a/Library/ViewModels/ProjectPamphletMainCellViewModel.swift +++ b/Library/ViewModels/ProjectPamphletMainCellViewModel.swift @@ -190,8 +190,14 @@ public final class ProjectPamphletMainCellViewModel: ProjectPamphletMainCellView self.categoryNameLabelText = project.map { $0.category.name } - let deadlineTitleAndSubtitle = project.map { - Format.duration(secondsInUTC: $0.dates.deadline, useToGo: true) + let deadlineTitleAndSubtitle = project.map { project -> (String, String) in + var durationValue = ("", "") + + if let deadline = project.dates.deadline { + durationValue = Format.duration(secondsInUTC: deadline, useToGo: true) + } + + return durationValue } self.deadlineTitleLabelText = deadlineTitleAndSubtitle.map(first) @@ -345,7 +351,12 @@ private func statsStackViewAccessibilityLabelForProject(_ project: Project, need ) let backersCount = project.stats.backersCount - let (time, unit) = Format.duration(secondsInUTC: project.dates.deadline, useToGo: true) + var (time, unit) = ("", "") + + if let deadline = project.dates.deadline { + (time, unit) = Format.duration(secondsInUTC: deadline, useToGo: true) + } + let timeLeft = time + " " + unit return project.state == .live diff --git a/Library/ViewModels/RewardAddOnCardViewModel.swift b/Library/ViewModels/RewardAddOnCardViewModel.swift index ebd07bd950..069397aae8 100644 --- a/Library/ViewModels/RewardAddOnCardViewModel.swift +++ b/Library/ViewModels/RewardAddOnCardViewModel.swift @@ -317,11 +317,12 @@ private func timeLeftString(project: Project, reward: Reward) -> String? { let isUnlimitedOrAvailable = reward.limit == nil || reward.remaining ?? 0 > 0 if let endsAt = reward.endsAt, + let deadline = project.dates.deadline, endsAt > 0, endsAt >= AppEnvironment.current.dateType.init().timeIntervalSince1970, isUnlimitedOrAvailable { let (time, unit) = Format.duration( - secondsInUTC: min(endsAt, project.dates.deadline), + secondsInUTC: min(endsAt, deadline), abbreviate: true, useToGo: false ) diff --git a/Library/ViewModels/RewardCardViewModel.swift b/Library/ViewModels/RewardCardViewModel.swift index a6b79f6913..defd1cc010 100644 --- a/Library/ViewModels/RewardCardViewModel.swift +++ b/Library/ViewModels/RewardCardViewModel.swift @@ -259,11 +259,12 @@ private func timeLeftString(project: Project, reward: Reward) -> RewardCardPillD if project.state == .live, let endsAt = reward.endsAt, + let deadline = project.dates.deadline, endsAt > 0, endsAt >= AppEnvironment.current.dateType.init().timeIntervalSince1970, isUnlimitedOrAvailable { let (time, unit) = Format.duration( - secondsInUTC: min(endsAt, project.dates.deadline), + secondsInUTC: min(endsAt, deadline), abbreviate: true, useToGo: false )