-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWidgetHeatmapPlaceholderShape.swift
More file actions
127 lines (108 loc) · 4.03 KB
/
Copy pathWidgetHeatmapPlaceholderShape.swift
File metadata and controls
127 lines (108 loc) · 4.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//
// WidgetHeatmapPlaceholderShape.swift
// DevLogWidgetCore
//
// Created by opfic on 4/30/26.
//
import Foundation
import DevLogCore
public struct WidgetHeatmapPlaceholderShape {
public let currentMonths: [WidgetHeatmapPlaceholderMonthShape]
public let quarterMonths: [WidgetHeatmapPlaceholderMonthShape]
public var currentMonthWeekCounts: [Int] {
currentMonths.map(\.weeks.count)
}
public var quarterWeekCounts: [Int] {
quarterMonths.map(\.weeks.count)
}
public init(
date: Date = Date(),
calendar: Calendar = .current
) {
let quarterStart = calendar.startOfQuarter(for: date)
let monthStarts = (0..<3).compactMap {
calendar.date(byAdding: .month, value: $0, to: quarterStart)
}
let widgetHeatmapPlaceholderMonthShapes = monthStarts.map {
Self.makeMonth(monthStart: $0, calendar: calendar)
}
if let currentMonth = widgetHeatmapPlaceholderMonthShapes.first(where: {
calendar.isDate($0.monthStart, equalTo: date, toGranularity: .month)
}) {
currentMonths = [currentMonth]
} else {
currentMonths = Array(widgetHeatmapPlaceholderMonthShapes.prefix(1))
}
quarterMonths = widgetHeatmapPlaceholderMonthShapes
}
private static func makeMonth(
monthStart: Date,
calendar: Calendar
) -> WidgetHeatmapPlaceholderMonthShape {
guard let monthInterval = calendar.dateInterval(of: .month, for: monthStart),
let monthLastDay = calendar.date(byAdding: .day, value: -1, to: monthInterval.end),
let firstWeekInterval = calendar.dateInterval(of: .weekOfYear, for: monthInterval.start),
let lastWeekInterval = calendar.dateInterval(of: .weekOfYear, for: monthLastDay) else {
return WidgetHeatmapPlaceholderMonthShape(monthStart: monthStart, weeks: [])
}
var weeks = [WidgetHeatmapPlaceholderWeekShape]()
var cursor = firstWeekInterval.start
while cursor < lastWeekInterval.end {
weeks.append(
WidgetHeatmapPlaceholderWeekShape(
id: weeks.count,
days: makeDays(
weekStart: cursor,
monthStart: monthStart,
calendar: calendar
)
)
)
guard let nextWeek = calendar.date(byAdding: .weekOfYear, value: 1, to: cursor) else {
break
}
cursor = nextWeek
}
return WidgetHeatmapPlaceholderMonthShape(monthStart: monthStart, weeks: weeks)
}
private static func makeDays(
weekStart: Date,
monthStart: Date,
calendar: Calendar
) -> [WidgetHeatmapPlaceholderDayShape] {
var days = [WidgetHeatmapPlaceholderDayShape]()
var cursor = weekStart
for _ in 0..<7 {
let normalizedDate = calendar.startOfDay(for: cursor)
days.append(
WidgetHeatmapPlaceholderDayShape(
date: normalizedDate,
isVisible: calendar.isDate(
normalizedDate,
equalTo: monthStart,
toGranularity: .month
)
)
)
guard let nextDay = calendar.date(byAdding: .day, value: 1, to: cursor) else {
break
}
cursor = nextDay
}
return days
}
}
public struct WidgetHeatmapPlaceholderMonthShape: Identifiable, Hashable {
public var id: Date { monthStart }
public let monthStart: Date
public let weeks: [WidgetHeatmapPlaceholderWeekShape]
}
public struct WidgetHeatmapPlaceholderWeekShape: Identifiable, Hashable {
public let id: Int
public let days: [WidgetHeatmapPlaceholderDayShape]
}
public struct WidgetHeatmapPlaceholderDayShape: Identifiable, Hashable {
public var id: Date { date }
public let date: Date
public let isVisible: Bool
}