mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Merge branch 'pr-215' into feature/liveactivity
This is a trial merge
This commit is contained in:
commit
cf210eb192
1 changed files with 115 additions and 9 deletions
|
|
@ -131,6 +131,9 @@ final class DoNotDisturbManager: ObservableObject {
|
||||||
|
|
||||||
if nameChanged {
|
if nameChanged {
|
||||||
self.currentFocusModeName = finalName
|
self.currentFocusModeName = finalName
|
||||||
|
.localizedCaseInsensitiveContains(
|
||||||
|
"Reduce Interruptions"
|
||||||
|
) ? "Reduce Interr." : finalName
|
||||||
}
|
}
|
||||||
|
|
||||||
if identifierChanged || nameChanged || shouldToggleActive {
|
if identifierChanged || nameChanged || shouldToggleActive {
|
||||||
|
|
@ -307,6 +310,7 @@ enum FocusModeType: String, CaseIterable {
|
||||||
case gaming = "com.apple.focus.gaming"
|
case gaming = "com.apple.focus.gaming"
|
||||||
case mindfulness = "com.apple.focus.mindfulness"
|
case mindfulness = "com.apple.focus.mindfulness"
|
||||||
case reading = "com.apple.focus.reading"
|
case reading = "com.apple.focus.reading"
|
||||||
|
case reduceInterruptions = "com.apple.focus.reduce-interruptions"
|
||||||
case custom = "com.apple.focus.custom"
|
case custom = "com.apple.focus.custom"
|
||||||
case unknown = ""
|
case unknown = ""
|
||||||
|
|
||||||
|
|
@ -321,6 +325,7 @@ enum FocusModeType: String, CaseIterable {
|
||||||
case .gaming: return "Gaming"
|
case .gaming: return "Gaming"
|
||||||
case .mindfulness: return "Mindfulness"
|
case .mindfulness: return "Mindfulness"
|
||||||
case .reading: return "Reading"
|
case .reading: return "Reading"
|
||||||
|
case .reduceInterruptions: return "Reduce Interr."
|
||||||
case .custom: return "Focus"
|
case .custom: return "Focus"
|
||||||
case .unknown: return "Focus Mode"
|
case .unknown: return "Focus Mode"
|
||||||
}
|
}
|
||||||
|
|
@ -337,6 +342,7 @@ enum FocusModeType: String, CaseIterable {
|
||||||
case .gaming: return "gamecontroller.fill"
|
case .gaming: return "gamecontroller.fill"
|
||||||
case .mindfulness: return "circle.hexagongrid"
|
case .mindfulness: return "circle.hexagongrid"
|
||||||
case .reading: return "book.closed.fill"
|
case .reading: return "book.closed.fill"
|
||||||
|
case .reduceInterruptions: return "apple.intelligence"
|
||||||
case .custom: return "app.badge"
|
case .custom: return "app.badge"
|
||||||
case .unknown: return "moon.fill"
|
case .unknown: return "moon.fill"
|
||||||
}
|
}
|
||||||
|
|
@ -362,7 +368,11 @@ enum FocusModeType: String, CaseIterable {
|
||||||
return image
|
return image
|
||||||
}
|
}
|
||||||
|
|
||||||
return Image(systemName: sfSymbol)
|
return Image(
|
||||||
|
systemName: self == .custom
|
||||||
|
? self.getCustomIconFromFile()
|
||||||
|
: sfSymbol
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
var accentColor: Color {
|
var accentColor: Color {
|
||||||
|
|
@ -385,8 +395,10 @@ enum FocusModeType: String, CaseIterable {
|
||||||
return Color(red: 0.361, green: 0.898, blue: 0.883, opacity: 1.0)
|
return Color(red: 0.361, green: 0.898, blue: 0.883, opacity: 1.0)
|
||||||
case .reading:
|
case .reading:
|
||||||
return Color(red: 1.000, green: 0.622, blue: 0.044, opacity: 1.0)
|
return Color(red: 1.000, green: 0.622, blue: 0.044, opacity: 1.0)
|
||||||
|
case .reduceInterruptions:
|
||||||
|
return Color(red: 0.686, green: 0.322, blue: 0.871, opacity: 1.0)
|
||||||
case .custom:
|
case .custom:
|
||||||
return Color(red: 0.513, green: 0.478, blue: 0.965)
|
return self.getCustomAccentColorFromFile()
|
||||||
case .unknown:
|
case .unknown:
|
||||||
return Color(red: 0.370, green: 0.360, blue: 0.902)
|
return Color(red: 0.370, green: 0.360, blue: 0.902)
|
||||||
}
|
}
|
||||||
|
|
@ -425,7 +437,7 @@ extension FocusModeType {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if normalizedLowercased.hasPrefix("com.apple.focus.") {
|
if normalizedLowercased.hasPrefix("com.apple.focus") {
|
||||||
self = .custom
|
self = .custom
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -434,23 +446,31 @@ extension FocusModeType {
|
||||||
}
|
}
|
||||||
|
|
||||||
static func resolve(identifier: String?, name: String?) -> FocusModeType {
|
static func resolve(identifier: String?, name: String?) -> FocusModeType {
|
||||||
let trimmedName = name?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
if let name, !name.isEmpty {
|
||||||
if !trimmedName.isEmpty {
|
|
||||||
if let match = FocusModeType.allCases.first(where: {
|
if let match = FocusModeType.allCases.first(where: {
|
||||||
guard !$0.displayName.isEmpty else { return false }
|
guard !$0.displayName.isEmpty else { return false }
|
||||||
return $0.displayName.compare(trimmedName, options: [.caseInsensitive, .diacriticInsensitive]) == .orderedSame
|
return $0.displayName.compare(name, options: [.caseInsensitive, .diacriticInsensitive]) == .orderedSame
|
||||||
}) {
|
}) {
|
||||||
return match
|
return match
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let trimmedIdentifier = identifier?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
if let identifier, !identifier.isEmpty {
|
||||||
if !trimmedIdentifier.isEmpty {
|
return FocusModeType(identifier: identifier)
|
||||||
return FocusModeType(identifier: trimmedIdentifier)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return .doNotDisturb
|
return .doNotDisturb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCustomIconFromFile() -> String {
|
||||||
|
return FocusMetadataReader.shared
|
||||||
|
.getIcon(for: DoNotDisturbManager.shared.currentFocusModeName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCustomAccentColorFromFile() -> Color {
|
||||||
|
return FocusMetadataReader.shared
|
||||||
|
.getAccentColor(for: DoNotDisturbManager.shared.currentFocusModeName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Metadata helpers
|
// MARK: - Metadata helpers
|
||||||
|
|
@ -943,3 +963,89 @@ private enum FocusMetadataDecoder {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class FocusMetadataReader {
|
||||||
|
private let pathToDatabase:URL = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent("Library/DoNotDisturb/DB/ModeConfigurations.json")
|
||||||
|
|
||||||
|
struct DNDConfigRoot: Codable {
|
||||||
|
let data: [DNDDataEntry]
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DNDDataEntry: Codable {
|
||||||
|
let modeConfigurations: [String: DNDModeWrapper]
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DNDModeWrapper: Codable {
|
||||||
|
let mode: DNDMode
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DNDMode: Codable {
|
||||||
|
let name: String
|
||||||
|
let symbolImageName: String?
|
||||||
|
let tintColorName: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
private init(){}
|
||||||
|
|
||||||
|
static let shared = FocusMetadataReader()
|
||||||
|
|
||||||
|
private func getModeConfig(for focusName: String) -> DNDMode? {
|
||||||
|
do {
|
||||||
|
let data = try Data(contentsOf: pathToDatabase)
|
||||||
|
let root = try JSONDecoder().decode(DNDConfigRoot.self, from: data)
|
||||||
|
|
||||||
|
for entry in root.data {
|
||||||
|
for wrapper in entry.modeConfigurations.values {
|
||||||
|
if wrapper.mode.name
|
||||||
|
.localizedCaseInsensitiveCompare(focusName) == .orderedSame {
|
||||||
|
return wrapper.mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
print("JSON Error: \(error)")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch the icon for the current focus from disk. If the focus is not found return the placeholder `app.badge`
|
||||||
|
/// - Returns A string representing the sfSymbol of the current focus
|
||||||
|
func getIcon(for focus: String) -> String {
|
||||||
|
guard let mode = getModeConfig(for: focus) else { return "app.badge" }
|
||||||
|
return mode.symbolImageName ?? "app.badge"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch the accent color for the current focus from disk. If the focus is not found return the placeholder `Color.indigo`
|
||||||
|
/// - Returns A Color representing the accent color for the current focus
|
||||||
|
func getAccentColor(for focus: String) -> Color {
|
||||||
|
guard let mode = getModeConfig(for: focus),
|
||||||
|
let colorName = mode.tintColorName else { return .indigo }
|
||||||
|
|
||||||
|
return Color.stringToColor(for: colorName)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Color {
|
||||||
|
static func stringToColor(for string:String) -> Color {
|
||||||
|
let cleanName = string.lowercased()
|
||||||
|
.replacingOccurrences(of: "system", with: "")
|
||||||
|
.replacingOccurrences(of: "color", with: "")
|
||||||
|
|
||||||
|
switch cleanName {
|
||||||
|
case "red": return .red
|
||||||
|
case "orange": return .orange
|
||||||
|
case "yellow": return .yellow
|
||||||
|
case "green": return .green
|
||||||
|
case "mint": return .mint
|
||||||
|
case "teal": return .teal
|
||||||
|
case "cyan": return .cyan
|
||||||
|
case "blue": return .blue
|
||||||
|
case "indigo": return .indigo
|
||||||
|
case "purple": return .purple
|
||||||
|
case "pink": return .pink
|
||||||
|
case "gray", "grey": return .gray
|
||||||
|
default: return .indigo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue