Skip to content

Commit 683c75a

Browse files
committed
feat: add calendar subtitle modes and lunar support
1 parent 09d6a77 commit 683c75a

5 files changed

Lines changed: 170 additions & 1 deletion

File tree

boringNotch/Localizable.xcstrings

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1805,6 +1805,23 @@
18051805
}
18061806
}
18071807
}
1808+
},
1809+
"Alternative calendars" : {
1810+
"extractionState" : "manual",
1811+
"localizations" : {
1812+
"en" : {
1813+
"stringUnit" : {
1814+
"state" : "translated",
1815+
"value" : "Alternative calendars"
1816+
}
1817+
}
1818+
}
1819+
},
1820+
"Always alternate" : {
1821+
1822+
},
1823+
"Always default" : {
1824+
18081825
},
18091826
"Always show full event titles" : {
18101827
"localizations" : {
@@ -3412,6 +3429,17 @@
34123429
}
34133430
}
34143431
},
3432+
"Calendar subtitle display" : {
3433+
"extractionState" : "manual",
3434+
"localizations" : {
3435+
"en" : {
3436+
"stringUnit" : {
3437+
"state" : "translated",
3438+
"value" : "Calendar subtitle display"
3439+
}
3440+
}
3441+
}
3442+
},
34153443
"Calendars" : {
34163444
"localizations" : {
34173445
"ar" : {
@@ -10647,6 +10675,9 @@
1064710675
}
1064810676
}
1064910677
}
10678+
},
10679+
"Lunar" : {
10680+
1065010681
},
1065110682
"Made with 🫶🏻 by not so boring not.people" : {
1065210683
"localizations" : {
@@ -13561,6 +13592,9 @@
1356113592
}
1356213593
}
1356313594
}
13595+
},
13596+
"Now Playing" : {
13597+
1356413598
},
1356513599
"Open Calendar Settings" : {
1356613600
"localizations" : {
@@ -19000,6 +19034,9 @@
1900019034
}
1900119035
}
1900219036
}
19037+
},
19038+
"Tap to switch" : {
19039+
1900319040
},
1900419041
"Time to Full Charge: %lld min" : {
1900519042
"localizations" : {

boringNotch/components/Calendar/BoringCalendar.swift

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,34 @@ struct CalendarView: View {
233233
@EnvironmentObject var vm: BoringViewModel
234234
@ObservedObject private var calendarManager = CalendarManager.shared
235235
@State private var selectedDate = Date()
236+
@Default(.calendarSubtitleDisplayMode) var displayMode
237+
@Default(.alternativeCalendar) var alternativeCalendar
238+
@Default(.calendarIsShowingAlternate) var isShowingAlternate
239+
240+
private var shouldShowAlternate: Bool {
241+
switch displayMode {
242+
case .alwaysDefault:
243+
return false
244+
case .alwaysAlternate:
245+
return true
246+
case .tapToSwitch:
247+
return isShowingAlternate
248+
}
249+
}
250+
251+
// A hidden view strictly for sizing
252+
private func subtitleSizingView(date: Date) -> some View {
253+
ZStack {
254+
Text(date.formatted(.dateTime.year()))
255+
switch alternativeCalendar {
256+
case .lunar:
257+
Text(date.formatted(.dateTime.lunar()))
258+
}
259+
}
260+
.font(.title3)
261+
.fontWeight(.light)
262+
.fixedSize() // Ensure it takes up its natural calculated size
263+
}
236264

237265
var body: some View {
238266
VStack(spacing: 0) {
@@ -242,10 +270,34 @@ struct CalendarView: View {
242270
.font(.title3)
243271
.fontWeight(.semibold)
244272
.foregroundColor(.white)
245-
Text(selectedDate.formatted(.dateTime.year()))
273+
274+
ZStack(alignment: .leading) {
275+
// Invisible layer to reserve space for the widest possible content
276+
subtitleSizingView(date: selectedDate)
277+
.hidden()
278+
279+
// Visible content
280+
Group {
281+
if shouldShowAlternate {
282+
switch alternativeCalendar {
283+
case .lunar:
284+
Text(selectedDate.formatted(.dateTime.lunar()))
285+
}
286+
} else {
287+
Text(selectedDate.formatted(.dateTime.year()))
288+
}
289+
}
246290
.font(.title3)
247291
.fontWeight(.light)
248292
.foregroundColor(Color(white: 0.65))
293+
.onTapGesture {
294+
if displayMode == .tapToSwitch {
295+
withAnimation(.smooth(duration: 0.2)) {
296+
isShowingAlternate.toggle()
297+
}
298+
}
299+
}
300+
}
249301
}
250302

251303
ZStack(alignment: .top) {

boringNotch/components/Settings/Views/CalendarSettingsView.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ struct CalendarSettings: View {
1515
@Default(.hideCompletedReminders) var hideCompletedReminders
1616
@Default(.hideAllDayEvents) var hideAllDayEvents
1717
@Default(.autoScrollToNextEvent) var autoScrollToNextEvent
18+
@Default(.calendarSubtitleDisplayMode) var calendarSubtitleDisplayMode
19+
@Default(.alternativeCalendar) var alternativeCalendar
1820

1921
var body: some View {
2022
Form {
@@ -33,6 +35,23 @@ struct CalendarSettings: View {
3335
Defaults.Toggle(key: .showFullEventTitles) {
3436
Text("Always show full event titles")
3537
}
38+
39+
Picker("Calendar subtitle display", selection: $calendarSubtitleDisplayMode) {
40+
ForEach(CalendarSubtitleDisplayMode.allCases) { item in
41+
Text(item.localizedString).tag(item)
42+
}
43+
}
44+
.pickerStyle(.menu)
45+
46+
if calendarSubtitleDisplayMode != .alwaysDefault {
47+
Picker("Alternative calendars", selection: $alternativeCalendar) {
48+
ForEach(AlternativeCalendarType.allCases) { item in
49+
Text(item.localizedString).tag(item)
50+
}
51+
}
52+
.pickerStyle(.menu)
53+
}
54+
3655
Section(header: Text("Calendars")) {
3756
if calendarManager.calendarAuthorizationStatus != .fullAccess {
3857
Text("Calendar access is denied. Please enable it in System Settings.")

boringNotch/extensions/DataTypes+Extensions.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,31 @@ extension Date {
4242
}
4343
}
4444

45+
struct LunarDateStyle: FormatStyle {
46+
func format(_ value: Date) -> String {
47+
let formatter = DateFormatter()
48+
formatter.calendar = Foundation.Calendar(identifier: .chinese)
49+
formatter.locale = Locale(identifier: "zh_CN")
50+
formatter.dateStyle = .long
51+
formatter.timeStyle = .none
52+
53+
let fullDate = formatter.string(from: value)
54+
55+
if let yearRange = fullDate.range(of: "") {
56+
let extracted = String(fullDate[yearRange.upperBound...])
57+
return extracted
58+
}
59+
60+
return fullDate
61+
}
62+
}
63+
64+
extension Date.FormatStyle {
65+
func lunar() -> LunarDateStyle {
66+
LunarDateStyle()
67+
}
68+
}
69+
4570
extension NSSize {
4671
var s: String { "\(width.i)×\(height.i)" }
4772

boringNotch/models/Constants.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,38 @@ enum OSDControlSource: String, CaseIterable, Identifiable, Defaults.Serializable
134134
}
135135
}
136136

137+
enum CalendarSubtitleDisplayMode: String, CaseIterable, Identifiable, Defaults.Serializable {
138+
case alwaysDefault
139+
case alwaysAlternate
140+
case tapToSwitch
141+
142+
var id: String { self.rawValue }
143+
144+
var localizedString: String {
145+
switch self {
146+
case .alwaysDefault:
147+
return NSLocalizedString("Always default", comment: "")
148+
case .alwaysAlternate:
149+
return NSLocalizedString("Always alternate", comment: "")
150+
case .tapToSwitch:
151+
return NSLocalizedString("Tap to switch", comment: "")
152+
}
153+
}
154+
}
155+
156+
enum AlternativeCalendarType: String, CaseIterable, Identifiable, Defaults.Serializable {
157+
case lunar = "Lunar"
158+
159+
var id: String { self.rawValue }
160+
161+
var localizedString: String {
162+
switch self {
163+
case .lunar:
164+
return NSLocalizedString("Lunar", comment: "")
165+
}
166+
}
167+
}
168+
137169
extension Defaults.Keys {
138170
// MARK: General
139171
static let menubarIcon = Key<Bool>("menubarIcon", default: true)
@@ -242,6 +274,10 @@ extension Defaults.Keys {
242274
static let showFullEventTitles = Key<Bool>("showFullEventTitles", default: false)
243275
static let autoScrollToNextEvent = Key<Bool>("autoScrollToNextEvent", default: true)
244276

277+
static let calendarSubtitleDisplayMode = Key<CalendarSubtitleDisplayMode>("calendarSubtitleDisplayMode", default: .alwaysDefault)
278+
static let alternativeCalendar = Key<AlternativeCalendarType>("alternativeCalendar", default: .lunar)
279+
static let calendarIsShowingAlternate = Key<Bool>("calendarIsShowingAlternate", default: false)
280+
245281
// MARK: Fullscreen Media Detection
246282
static let hideNotchOption = Key<HideNotchOption>("hideNotchOption", default: .nowPlayingOnly)
247283

0 commit comments

Comments
 (0)