Atoll/DynamicIsland/DynamicIslandViewCoordinator.swift
Hariharan Mudaliar 579c4d3ca6 Working Clipboard
2025-07-21 14:29:24 +05:30

224 lines
7.2 KiB
Swift

//
// DynamicIslandViewCoordinator.swift
// DynamicIsland
//
// Created by Alexander on 2024-11-20.
//
import Combine
import Defaults
import SwiftUI
import TheBoringWorkerNotifier
enum SneakContentType {
case brightness
case volume
case backlight
case music
case mic
case battery
case download
case timer
}
struct sneakPeek {
var show: Bool = false
var type: SneakContentType = .music
var value: CGFloat = 0
var icon: String = ""
}
struct SharedSneakPeek: Codable {
var show: Bool
var type: String
var value: String
var icon: String
}
enum BrowserType {
case chromium
case safari
}
struct ExpandedItem {
var show: Bool = false
var type: SneakContentType = .battery
var value: CGFloat = 0
var browser: BrowserType = .chromium
}
class DynamicIslandViewCoordinator: ObservableObject {
static let shared = DynamicIslandViewCoordinator()
var notifier: TheBoringWorkerNotifier = .init()
@Published var currentView: NotchViews = .home
private var sneakPeekDispatch: DispatchWorkItem?
private var expandingViewDispatch: DispatchWorkItem?
@AppStorage("firstLaunch") var firstLaunch: Bool = true
@AppStorage("showWhatsNew") var showWhatsNew: Bool = true
@AppStorage("musicLiveActivityEnabled") var musicLiveActivityEnabled: Bool = true
@AppStorage("timerLiveActivityEnabled") var timerLiveActivityEnabled: Bool = true
@AppStorage("currentMicStatus") var currentMicStatus: Bool = true
@AppStorage("alwaysShowTabs") var alwaysShowTabs: Bool = true {
didSet {
if !alwaysShowTabs {
openLastTabByDefault = false
if TrayDrop.shared.isEmpty || !Defaults[.openShelfByDefault] {
currentView = .home
}
}
}
}
@AppStorage("openLastTabByDefault") var openLastTabByDefault: Bool = false {
didSet {
if openLastTabByDefault {
alwaysShowTabs = true
}
}
}
@AppStorage("hudReplacement") var hudReplacement: Bool = true {
didSet {
notifier.postNotification(name: notifier.toggleHudReplacementNotification.name, userInfo: nil)
}
}
@AppStorage("preferred_screen_name") var preferredScreen = NSScreen.main?.localizedName ?? "Unknown" {
didSet {
selectedScreen = preferredScreen
NotificationCenter.default.post(name: Notification.Name.selectedScreenChanged, object: nil)
}
}
@Published var selectedScreen: String = NSScreen.main?.localizedName ?? "Unknown"
@Published var optionKeyPressed: Bool = true
private init() {
selectedScreen = preferredScreen
notifier = TheBoringWorkerNotifier()
}
func setupWorkersNotificationObservers() {
notifier.setupObserver(notification: notifier.micStatusNotification, handler: initialMicStatus)
notifier.setupObserver(notification: notifier.sneakPeakNotification, handler: sneakPeekEvent)
}
@objc func sneakPeekEvent(_ notification: Notification) {
let decoder = JSONDecoder()
if let decodedData = try? decoder.decode(SharedSneakPeek.self, from: notification.userInfo?.first?.value as! Data) {
let contentType = decodedData.type == "brightness" ? SneakContentType.brightness : decodedData.type == "volume" ? SneakContentType.volume : decodedData.type == "backlight" ? SneakContentType.backlight : decodedData.type == "mic" ? SneakContentType.mic : decodedData.type == "timer" ? SneakContentType.timer : SneakContentType.brightness
let value = CGFloat((NumberFormatter().number(from: decodedData.value) ?? 0.0).floatValue)
let icon = decodedData.icon
print(decodedData)
toggleSneakPeek(status: decodedData.show, type: contentType, value: value, icon: icon)
} else {
print("Failed to decode JSON data")
}
}
func toggleSneakPeek(status: Bool, type: SneakContentType, duration: TimeInterval = 1.5, value: CGFloat = 0, icon: String = "") {
sneakPeekDuration = duration
if type != .music && type != .timer {
// close()
if !hudReplacement {
return
}
}
DispatchQueue.main.async {
withAnimation(.smooth) {
self.sneakPeek.show = status
self.sneakPeek.type = type
self.sneakPeek.value = value
self.sneakPeek.icon = icon
}
}
if type == .mic {
currentMicStatus = value == 1
}
}
private var sneakPeekDuration: TimeInterval = 1.5
@Published var sneakPeek: sneakPeek = .init() {
didSet {
if sneakPeek.show {
sneakPeekDispatch?.cancel()
sneakPeekDispatch = DispatchWorkItem { [weak self] in
guard let self = self else { return }
withAnimation {
self.toggleSneakPeek(status: false, type: SneakContentType.music)
self.sneakPeekDuration = 1.5
}
}
DispatchQueue.main
.asyncAfter(
deadline: .now() + self.sneakPeekDuration,
execute: sneakPeekDispatch!
)
}
}
}
func toggleExpandingView(status: Bool, type: SneakContentType, value: CGFloat = 0, browser: BrowserType = .chromium) {
if expandingView.show {
withAnimation(.smooth) {
self.expandingView.show = false
}
}
DispatchQueue.main.async {
withAnimation(.smooth) {
self.expandingView.show = status
self.expandingView.type = type
self.expandingView.value = value
self.expandingView.browser = browser
}
}
}
@Published var expandingView: ExpandedItem = .init() {
didSet {
if expandingView.show {
expandingViewDispatch?.cancel()
expandingViewDispatch = DispatchWorkItem { [weak self] in
guard let self = self else { return }
self.toggleExpandingView(status: false, type: SneakContentType.battery)
}
DispatchQueue.main.asyncAfter(deadline: .now() + (expandingView.type == .download ? 2 : 3), execute: expandingViewDispatch!)
}
}
}
@objc func initialMicStatus(_ notification: Notification) {
currentMicStatus = notification.userInfo?.first?.value as! Bool
}
func toggleMic() {
notifier.postNotification(name: notifier.toggleMicNotification.name, userInfo: nil)
}
func showEmpty() {
currentView = .home
}
// MARK: - Clipboard Management
@Published var shouldToggleClipboardPopover: Bool = false
func toggleClipboardPopover() {
// Toggle the published property to trigger UI updates
shouldToggleClipboardPopover.toggle()
}
}