Skip to content

Commit

Permalink
[WEB-999] Prelaunch Dates Blockers (#1801)
Browse files Browse the repository at this point in the history
* these use cases should all be safe, but still need to run tests and do a quick smoketest with break points.

* library and kickstarter-ios test corrections
  • Loading branch information
msadoon committed Mar 9, 2023
1 parent 2733127 commit 2fb519b
Show file tree
Hide file tree
Showing 23 changed files with 158 additions and 66 deletions.
Expand Up @@ -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))
}
Expand Up @@ -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

Expand Down
Expand Up @@ -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))
}
}
6 changes: 4 additions & 2 deletions Kickstarter-iOS/Library/CookieRefTagFunctions.swift
Expand Up @@ -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)
}
25 changes: 19 additions & 6 deletions KsApi/models/Project.swift
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions KsApi/models/lenses/Project.DatesLenses.swift
Expand Up @@ -3,7 +3,7 @@ import Prelude

extension Project.Dates {
public enum lens {
public static let deadline = Lens<Project.Dates, TimeInterval>(
public static let deadline = Lens<Project.Dates, TimeInterval?>(
view: { $0.deadline },
set: { Project.Dates(
deadline: $0, featuredAt: $1.featuredAt, launchedAt: $1.launchedAt,
Expand All @@ -19,7 +19,7 @@ extension Project.Dates {
) }
)

public static let launchedAt = Lens<Project.Dates, TimeInterval>(
public static let launchedAt = Lens<Project.Dates, TimeInterval?>(
view: { $0.launchedAt },
set: { Project.Dates(
deadline: $1.deadline, featuredAt: $1.featuredAt, launchedAt: $0,
Expand Down
4 changes: 2 additions & 2 deletions KsApi/models/lenses/ProjectLenses.swift
Expand Up @@ -502,15 +502,15 @@ extension Lens where Whole == Project, Part == Project.MemberData {
}

extension Lens where Whole == Project, Part == Project.Dates {
public var deadline: Lens<Project, TimeInterval> {
public var deadline: Lens<Project, TimeInterval?> {
return Project.lens.dates .. Project.Dates.lens.deadline
}

public var featuredAt: Lens<Project, TimeInterval?> {
return Project.lens.dates .. Project.Dates.lens.featuredAt
}

public var launchedAt: Lens<Project, TimeInterval> {
public var launchedAt: Lens<Project, TimeInterval?> {
return Project.lens.dates .. Project.Dates.lens.launchedAt
}

Expand Down
4 changes: 2 additions & 2 deletions Library/Tracking/KSRAnalytics.swift
Expand Up @@ -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
Expand Down
6 changes: 5 additions & 1 deletion Library/ViewModels/BackerDashboardProjectCellViewModel.swift
Expand Up @@ -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)
Expand Down
27 changes: 22 additions & 5 deletions Library/ViewModels/DashboardFundingCellViewModel.swift
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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 ?
Expand Down
14 changes: 8 additions & 6 deletions Library/ViewModels/DashboardFundingCellViewModelTests.swift
Expand Up @@ -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)

Expand All @@ -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)

Expand All @@ -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."
Expand Down
10 changes: 7 additions & 3 deletions Library/ViewModels/DiscoveryPostcardViewModel.swift
Expand Up @@ -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)
: ("", "")
}

Expand Down
3 changes: 2 additions & 1 deletion Library/ViewModels/DiscoveryPostcardViewModelTests.swift
Expand Up @@ -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])

Expand Down
6 changes: 5 additions & 1 deletion Library/ViewModels/DiscoveryProjectCardViewModel.swift
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions Library/ViewModels/ManagePledgeViewModel.swift
Expand Up @@ -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)

Expand All @@ -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),
Expand Down
Expand Up @@ -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)
Expand Down
7 changes: 6 additions & 1 deletion Library/ViewModels/PledgeSummaryViewModel.swift
Expand Up @@ -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)

Expand Down
16 changes: 11 additions & 5 deletions Library/ViewModels/ProjectActivityLaunchCellViewModel.swift
Expand Up @@ -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()
)
}
Expand Down

0 comments on commit 2fb519b

Please sign in to comment.