Working stuff

This commit is contained in:
Hariharan Mudaliar 2025-11-10 22:42:34 +05:30
commit 8e003b620f
6 changed files with 91 additions and 3 deletions

View file

@ -406,7 +406,7 @@ struct ContentView: View {
.frame(width: 76, alignment: .trailing) .frame(width: 76, alignment: .trailing)
} }
.frame(height: vm.effectiveClosedNotchHeight + (isHovering ? 8 : 0), alignment: .center) .frame(height: vm.effectiveClosedNotchHeight + (isHovering ? 8 : 0), alignment: .center)
} else if coordinator.sneakPeek.show && Defaults[.inlineHUD] && (coordinator.sneakPeek.type != .music) && (coordinator.sneakPeek.type != .battery) && (coordinator.sneakPeek.type != .timer) && (coordinator.sneakPeek.type != .volume || vm.notchState == .closed) { } else if coordinator.sneakPeek.show && Defaults[.inlineHUD] && (coordinator.sneakPeek.type != .music) && (coordinator.sneakPeek.type != .battery) && (coordinator.sneakPeek.type != .timer) && (coordinator.sneakPeek.type != .reminder) && (coordinator.sneakPeek.type != .volume || vm.notchState == .closed) {
InlineHUD(type: $coordinator.sneakPeek.type, value: $coordinator.sneakPeek.value, icon: $coordinator.sneakPeek.icon, hoverAnimation: $isHovering, gestureProgress: $gestureProgress) InlineHUD(type: $coordinator.sneakPeek.type, value: $coordinator.sneakPeek.value, icon: $coordinator.sneakPeek.icon, hoverAnimation: $isHovering, gestureProgress: $gestureProgress)
.transition(.opacity) .transition(.opacity)
} else if (!coordinator.expandingView.show || coordinator.expandingView.type == .music) && vm.notchState == .closed && (musicManager.isPlaying || !musicManager.isPlayerIdle) && coordinator.musicLiveActivityEnabled && !vm.hideOnClosed && !lockScreenManager.isLocked { } else if (!coordinator.expandingView.show || coordinator.expandingView.type == .music) && vm.notchState == .closed && (musicManager.isPlaying || !musicManager.isPlayerIdle) && coordinator.musicLiveActivityEnabled && !vm.hideOnClosed && !lockScreenManager.isLocked {
@ -469,6 +469,19 @@ struct ContentView: View {
.padding(.bottom, 10) .padding(.bottom, 10)
} }
} }
else if coordinator.sneakPeek.type == .reminder {
if !vm.hideOnClosed && Defaults[.sneakPeekStyles] == .standard, let reminder = reminderManager.activeReminder {
GeometryReader { geo in
MarqueeText(
.constant(reminderSneakPeekText(for: reminder)),
textColor: reminderColor(for: reminder.event),
minDuration: 1,
frameWidth: geo.size.width
)
}
.padding(.bottom, 10)
}
}
} }
} }
} }
@ -509,6 +522,32 @@ struct ContentView: View {
} }
} }
private func reminderColor(for event: EventModel) -> Color {
Color(nsColor: event.calendar.color).ensureMinimumBrightness(factor: 0.7)
}
private func reminderSneakPeekText(for entry: ReminderLiveActivityManager.ReminderEntry) -> String {
let title = entry.event.title.isEmpty ? "Upcoming Reminder" : entry.event.title
let remaining = max(entry.event.start.timeIntervalSince(reminderManager.currentDate), 0)
let minutes = Int(ceil(remaining / 60))
let timeString = reminderTimeFormatter.string(from: entry.event.start)
if minutes <= 0 {
return "\(title) • starts now @ \(timeString)"
} else if minutes == 1 {
return "\(title) • in 1 min @ \(timeString)"
} else {
return "\(title) • in \(minutes) min @ \(timeString)"
}
}
private let reminderTimeFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .short
return formatter
}()
@ViewBuilder @ViewBuilder
func DynamicIslandFaceAnimation() -> some View { func DynamicIslandFaceAnimation() -> some View {
HStack { HStack {

View file

@ -144,9 +144,17 @@ class DynamicIslandViewCoordinator: ObservableObject {
} }
func toggleSneakPeek(status: Bool, type: SneakContentType, duration: TimeInterval = 1.5, value: CGFloat = 0, icon: String = "") { func toggleSneakPeek(status: Bool, type: SneakContentType, duration: TimeInterval = 1.5, value: CGFloat = 0, icon: String = "") {
let resolvedDuration = type == .timer ? 10 : duration let resolvedDuration: TimeInterval
switch type {
case .timer:
resolvedDuration = 10
case .reminder:
resolvedDuration = Defaults[.reminderSneakPeekDuration]
default:
resolvedDuration = duration
}
sneakPeekDuration = resolvedDuration sneakPeekDuration = resolvedDuration
if type != .music && type != .timer { if type != .music && type != .timer && type != .reminder {
// close() // close()
if !hudReplacement { if !hudReplacement {
return return

View file

@ -872,6 +872,10 @@
"comment" : "A text label displaying the current value of the \"Notify before\" slider. The argument is the current value of the slider, expressed in minutes.", "comment" : "A text label displaying the current value of the \"Notify before\" slider. The argument is the current value of the slider, expressed in minutes.",
"isCommentAutoGenerated" : true "isCommentAutoGenerated" : true
}, },
"%lld s" : {
"comment" : "A text view displaying the duration of the \"sneak peek\" feature in the reminder live activity section of the calendar settings view. The text is followed by a unit of measurement (\"s\").",
"isCommentAutoGenerated" : true
},
"%lld%%" : { "%lld%%" : {
"localizations" : { "localizations" : {
"ar" : { "ar" : {
@ -17177,6 +17181,10 @@
"comment" : "A description of how the color-coding feature works.", "comment" : "A description of how the color-coding feature works.",
"isCommentAutoGenerated" : true "isCommentAutoGenerated" : true
}, },
"Sneak peek duration" : {
"comment" : "A label describing the duration of the \"sneak peek\" feature in the calendar settings.",
"isCommentAutoGenerated" : true
},
"Sneak Peek shows the media title and artist under the notch for a few seconds." : { "Sneak Peek shows the media title and artist under the notch for a few seconds." : {
"localizations" : { "localizations" : {
"ar" : { "ar" : {

View file

@ -956,6 +956,7 @@ struct CalendarSettings: View {
@Default(.enableReminderLiveActivity) var enableReminderLiveActivity @Default(.enableReminderLiveActivity) var enableReminderLiveActivity
@Default(.reminderPresentationStyle) var reminderPresentationStyle @Default(.reminderPresentationStyle) var reminderPresentationStyle
@Default(.reminderLeadTime) var reminderLeadTime @Default(.reminderLeadTime) var reminderLeadTime
@Default(.reminderSneakPeekDuration) var reminderSneakPeekDuration
var body: some View { var body: some View {
Form { Form {
@ -1026,6 +1027,19 @@ struct CalendarSettings: View {
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
.frame(width: 60, alignment: .trailing) .frame(width: 60, alignment: .trailing)
} }
HStack {
Text("Sneak peek duration")
Slider(
value: $reminderSneakPeekDuration,
in: 3...20,
step: 1
)
.disabled(!enableReminderLiveActivity)
Text("\(Int(reminderSneakPeekDuration)) s")
.foregroundStyle(.secondary)
.frame(width: 60, alignment: .trailing)
}
} }
Section(header: Text("Select Calendars")) { Section(header: Text("Select Calendars")) {

View file

@ -259,6 +259,13 @@ final class ReminderLiveActivityManager: ObservableObject {
} }
if activeReminder != entry { if activeReminder != entry {
activeReminder = entry activeReminder = entry
DynamicIslandViewCoordinator.shared.toggleSneakPeek(
status: true,
type: .reminder,
duration: Defaults[.reminderSneakPeekDuration],
value: 0,
icon: glyphName(for: entry.event)
)
} }
startTickerIfNeeded() startTickerIfNeeded()
} else { } else {
@ -269,4 +276,15 @@ final class ReminderLiveActivityManager: ObservableObject {
scheduleEvaluation(at: entry.triggerDate) scheduleEvaluation(at: entry.triggerDate)
} }
} }
private func glyphName(for event: EventModel) -> String {
switch event.type {
case .birthday:
return "gift.fill"
case .reminder(let completed):
return completed ? "checkmark.circle" : "bell.fill"
case .event:
return event.isMeeting ? "person.2.fill" : "calendar"
}
}
} }

View file

@ -463,6 +463,7 @@ extension Defaults.Keys {
static let enableReminderLiveActivity = Key<Bool>("enableReminderLiveActivity", default: true) static let enableReminderLiveActivity = Key<Bool>("enableReminderLiveActivity", default: true)
static let reminderPresentationStyle = Key<ReminderPresentationStyle>("reminderPresentationStyle", default: .ringCountdown) static let reminderPresentationStyle = Key<ReminderPresentationStyle>("reminderPresentationStyle", default: .ringCountdown)
static let reminderLeadTime = Key<Int>("reminderLeadTime", default: 5) static let reminderLeadTime = Key<Int>("reminderLeadTime", default: 5)
static let reminderSneakPeekDuration = Key<Double>("reminderSneakPeekDuration", default: 10)
static let timerControlWindowEnabled = Key<Bool>("timerControlWindowEnabled", default: true) static let timerControlWindowEnabled = Key<Bool>("timerControlWindowEnabled", default: true)
// MARK: ColorPicker Feature // MARK: ColorPicker Feature