Fixed the width and color

This commit is contained in:
Hariharan Mudaliar 2025-11-10 02:19:31 +05:30
commit 39c135cfd7
6 changed files with 217 additions and 73 deletions

View file

@ -8497,10 +8497,6 @@
},
"Fit to Notch" : {
},
"Focus mode active" : {
"comment" : "An accessibility label for the focus indicator.",
"isCommentAutoGenerated" : true
},
"Focus Status" : {

View file

@ -2,112 +2,211 @@
// DoNotDisturbLiveActivity.swift
// DynamicIsland
//
// Renders the closed-notch Focus indicator with a purple moon badge.
// Renders the closed-notch Focus indicator with per-mode colours and
// an icon-first layout that collapses gracefully when Focus ends.
//
import Defaults
import SwiftUI
struct DoNotDisturbLiveActivity: View {
@EnvironmentObject var vm: DynamicIslandViewModel
@ObservedObject var manager = DoNotDisturbManager.shared
@State private var isExpanded = false
@Default(.showDoNotDisturbLabel) private var showLabelSetting
private let focusColor = Color.purple
@State private var isExpanded = false
@State private var showInactiveIcon = false
@State private var iconScale: CGFloat = 1.0
@State private var scaleResetTask: Task<Void, Never>?
@State private var collapseTask: Task<Void, Never>?
@State private var cleanupTask: Task<Void, Never>?
var body: some View {
HStack(spacing: 0) {
leadingWing
.frame(width: leadingWingWidth, height: wingHeight)
iconWing
.frame(width: iconWingWidth, height: wingHeight)
Rectangle()
.fill(Color.black)
.frame(width: vm.closedNotchSize.width)
trailingWing
.frame(width: trailingWingWidth, height: wingHeight)
labelWing
.frame(width: labelWingWidth, height: wingHeight)
}
.frame(height: vm.effectiveClosedNotchHeight)
.accessibilityElement(children: .ignore)
.accessibilityLabel(accessibilityDescription)
.onAppear {
withAnimation(.smooth(duration: 0.35)) {
isExpanded = true
}
}
.onChange(of: manager.isDoNotDisturbActive) { _, isActive in
withAnimation(.smooth(duration: 0.35)) {
isExpanded = isActive
}
}
.onAppear(perform: handleInitialState)
.onChange(of: manager.isDoNotDisturbActive, handleFocusStateChange)
.onDisappear(perform: cancelPendingTasks)
}
// MARK: - Layout helpers
private var wingHeight: CGFloat {
max(vm.effectiveClosedNotchHeight - 10, 20)
}
private var leadingWingWidth: CGFloat {
isExpanded ? max(vm.effectiveClosedNotchHeight - 10, 20) : 0
private var iconWingWidth: CGFloat {
(isExpanded || showInactiveIcon) ? max(vm.effectiveClosedNotchHeight - 12, 24) : 0
}
private var trailingWingWidth: CGFloat {
guard isExpanded else { return 0 }
return max(vm.closedNotchSize.width * 0.65, 120)
private var labelWingWidth: CGFloat {
shouldShowLabel ? max(vm.closedNotchSize.width * 0.45, 110) : 0
}
private var focusSymbol: String {
if let symbol = FocusModeType(rawValue: manager.currentFocusModeIdentifier)?.sfSymbol {
return symbol
}
return FocusModeType.doNotDisturb.sfSymbol
private var shouldShowLabel: Bool {
showLabelSetting && isExpanded && !labelText.isEmpty
}
private var focusLabel: String {
if !manager.currentFocusModeName.isEmpty {
return manager.currentFocusModeName
// MARK: - Focus metadata
private var focusMode: FocusModeType {
FocusModeType(rawValue: manager.currentFocusModeIdentifier) ?? .doNotDisturb
}
return FocusModeType(rawValue: manager.currentFocusModeIdentifier)?.displayName ?? "Do Not Disturb"
private var activeAccentColor: Color {
focusMode.accentColor
}
private var labelText: String {
let trimmed = manager.currentFocusModeName.trimmingCharacters(in: .whitespacesAndNewlines)
return trimmed.isEmpty ? "Focus" : trimmed
}
private var accessibilityDescription: String {
"Focus mode active: \(focusLabel)"
if manager.isDoNotDisturbActive {
return "Focus active: \(labelText)"
} else {
return "Focus inactive"
}
}
private var leadingWing: some View {
private var currentIconName: String {
if manager.isDoNotDisturbActive {
return focusMode.sfSymbol
} else if showInactiveIcon {
return focusMode.inactiveSymbol
} else {
return focusMode.sfSymbol
}
}
private var currentIconColor: Color {
if manager.isDoNotDisturbActive {
return activeAccentColor
} else if showInactiveIcon {
return .white
} else {
return activeAccentColor
}
}
private var iconBackground: Color {
if manager.isDoNotDisturbActive {
return activeAccentColor.opacity(0.18)
} else if showInactiveIcon {
return Color.white.opacity(0.10)
} else {
return Color.black.opacity(0.001)
}
}
// MARK: - Subviews
private var iconWing: some View {
Color.clear
.background {
if isExpanded {
ZStack {
.overlay(alignment: .center) {
if iconWingWidth > 0 {
RoundedRectangle(cornerRadius: 6, style: .continuous)
.fill(focusColor.opacity(0.18))
Image(systemName: focusSymbol)
.font(.system(size: 12, weight: .semibold))
.foregroundColor(focusColor)
}
.frame(width: leadingWingWidth, height: wingHeight)
.fill(iconBackground)
.overlay {
Image(systemName: currentIconName)
.font(.system(size: 13, weight: .semibold))
.foregroundColor(currentIconColor)
.scaleEffect(iconScale)
.animation(.none, value: iconScale)
}
}
}
.animation(.smooth(duration: 0.3), value: iconWingWidth)
}
private var trailingWing: some View {
private var labelWing: some View {
Color.clear
.background {
if isExpanded {
HStack {
Text(focusLabel)
.overlay(alignment: .trailing) {
if shouldShowLabel {
Text(labelText)
.font(.system(size: 12, weight: .semibold, design: .rounded))
.foregroundColor(focusColor)
.foregroundColor(activeAccentColor)
.lineLimit(1)
.padding(.horizontal, 12)
.padding(.vertical, 6)
}
.frame(maxWidth: .infinity, alignment: .trailing)
.background(
Capsule(style: .continuous)
.fill(focusColor.opacity(0.18))
)
.contentTransition(.opacity)
.padding(.horizontal, 4)
}
}
.animation(.smooth(duration: 0.3), value: shouldShowLabel)
}
// MARK: - State transitions
private func handleInitialState() {
if manager.isDoNotDisturbActive {
withAnimation(.spring(response: 0.35, dampingFraction: 0.85)) {
isExpanded = true
}
}
}
private func handleFocusStateChange(_ oldValue: Bool, _ isActive: Bool) {
if isActive {
cancelPendingTasks()
showInactiveIcon = false
withAnimation(.spring(response: 0.35, dampingFraction: 0.85)) {
iconScale = 1.0
isExpanded = true
}
} else {
triggerInactiveAnimation()
}
}
private func triggerInactiveAnimation() {
showInactiveIcon = true
withAnimation(.interpolatingSpring(stiffness: 220, damping: 12)) {
iconScale = 1.2
}
scaleResetTask?.cancel()
scaleResetTask = Task { @MainActor in
try? await Task.sleep(for: .milliseconds(180))
withAnimation(.spring(response: 0.28, dampingFraction: 0.75)) {
iconScale = 1.0
}
}
collapseTask?.cancel()
collapseTask = Task { @MainActor in
try? await Task.sleep(for: .milliseconds(320))
withAnimation(.smooth(duration: 0.32)) {
isExpanded = false
}
}
cleanupTask?.cancel()
cleanupTask = Task { @MainActor in
try? await Task.sleep(for: .milliseconds(650))
showInactiveIcon = false
}
}
private func cancelPendingTasks() {
scaleResetTask?.cancel()
collapseTask?.cancel()
cleanupTask?.cancel()
scaleResetTask = nil
collapseTask = nil
cleanupTask = nil
}
}

View file

@ -175,6 +175,7 @@ struct GeneralSettings: View {
@Default(.showRecordingIndicator) var showRecordingIndicator
@Default(.enableDoNotDisturbDetection) var enableDoNotDisturbDetection
@Default(.showDoNotDisturbIndicator) var showDoNotDisturbIndicator
@Default(.showDoNotDisturbLabel) var showDoNotDisturbLabel
@Default(.enableMinimalisticUI) var enableMinimalisticUI
@Default(.lockScreenGlassStyle) var lockScreenGlassStyle
@Default(.lockScreenShowAppIcon) var lockScreenShowAppIcon
@ -274,6 +275,9 @@ struct GeneralSettings: View {
Defaults.Toggle("Show Focus Indicator", key: .showDoNotDisturbIndicator)
.disabled(!enableDoNotDisturbDetection)
Defaults.Toggle("Show Focus Label", key: .showDoNotDisturbLabel)
.disabled(!enableDoNotDisturbDetection)
if doNotDisturbManager.isMonitoring {
HStack {
Text("Focus Status")

View file

@ -10,31 +10,39 @@ import SwiftUI
struct FocusIndicator: View {
@ObservedObject var manager = DoNotDisturbManager.shared
private let focusColor = Color.purple
var body: some View {
Capsule()
.fill(Color.black)
.overlay {
ZStack {
Circle()
.fill(focusColor.opacity(0.2))
.fill(accentColor.opacity(0.2))
.frame(width: 20, height: 20)
Image(systemName: focusSymbol)
.font(.system(size: 12, weight: .semibold))
.foregroundStyle(focusColor)
.foregroundStyle(accentColor)
}
}
.accessibilityElement(children: .ignore)
.accessibilityLabel("Focus mode active")
.accessibilityLabel(accessibilityLabel)
}
private var focusMode: FocusModeType {
FocusModeType(rawValue: manager.currentFocusModeIdentifier) ?? .doNotDisturb
}
private var focusSymbol: String {
if let symbol = FocusModeType(rawValue: manager.currentFocusModeIdentifier)?.sfSymbol {
return symbol
focusMode.sfSymbol
}
return FocusModeType.doNotDisturb.sfSymbol
private var accentColor: Color {
focusMode.accentColor
}
private var accessibilityLabel: String {
let name = manager.currentFocusModeName.isEmpty ? focusMode.displayName : manager.currentFocusModeName
return "Focus active: \(name)"
}
}

View file

@ -187,4 +187,40 @@ enum FocusModeType: String {
case .unknown: return "moon.zzz.fill"
}
}
var accentColor: Color {
switch self {
case .doNotDisturb:
return Color(red: 0.370, green: 0.360, blue: 0.902)
case .work:
return Color(red: 0.133, green: 0.475, blue: 0.992)
case .personal:
return Color(red: 0.937, green: 0.282, blue: 0.624)
case .sleep:
return Color(red: 0.341, green: 0.384, blue: 0.980)
case .driving:
return Color(red: 0.988, green: 0.561, blue: 0.153)
case .fitness:
return Color(red: 0.176, green: 0.804, blue: 0.459)
case .gaming:
return Color(red: 0.639, green: 0.329, blue: 0.937)
case .mindfulness:
return Color(red: 0.239, green: 0.718, blue: 0.682)
case .reading:
return Color(red: 0.239, green: 0.596, blue: 0.965)
case .custom:
return Color(red: 0.513, green: 0.478, blue: 0.965)
case .unknown:
return Color(red: 0.370, green: 0.360, blue: 0.902)
}
}
var inactiveSymbol: String {
switch self {
case .doNotDisturb:
return "moon.circle"
default:
return sfSymbol
}
}
}

View file

@ -487,6 +487,7 @@ extension Defaults.Keys {
// MARK: Focus / Do Not Disturb Detection
static let enableDoNotDisturbDetection = Key<Bool>("enableDoNotDisturbDetection", default: true)
static let showDoNotDisturbIndicator = Key<Bool>("showDoNotDisturbIndicator", default: true)
static let showDoNotDisturbLabel = Key<Bool>("showDoNotDisturbLabel", default: true)
// MARK: Privacy Indicators (Camera & Microphone Detection)
static let enableCameraDetection = Key<Bool>("enableCameraDetection", default: true)