Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Do Not Disturb switching via a shortcut #82

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Have you ever heard of Pomodoro? It’s a great technique to help you keep track
TomatoBar is world's neatest Pomodoro timer for the macOS menu bar. All the essential features are here - configurable
work and rest intervals, optional sounds, discreet actionable notifications, global hotkey.

TomatoBar is fully sandboxed with no entitlements.
TomatoBar is fully sandboxed with no entitlements (except for the Apple Events entitlement, used to run the Do Not Disturb toggle shortcut).

Download the latest release <a href="https://github.com/ivoronin/TomatoBar/releases/latest/">here</a> or install using Homebrew:
```
Expand All @@ -43,3 +43,4 @@ Touch bar integration and older macOS versions (earlier than Big Sur) are suppor

## Licenses
- Timer sounds are licensed from buddhabeats
- "macos-focus-mode.shortcut" is taken from the <a href="https://github.com/arodik/macos-focus-mode">macos-focus-mode</a> project under the MIT license.
10 changes: 10 additions & 0 deletions TomatoBar.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
8AA5B3EB281295DB00BE1A6C /* KeyboardShortcuts in Frameworks */ = {isa = PBXBuildFile; productRef = 8AA5B3EA281295DB00BE1A6C /* KeyboardShortcuts */; };
8AA5B3F22812A74600BE1A6C /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AA5B3F12812A74600BE1A6C /* Notifications.swift */; };
8AEE49AF2AA75A5E00243D2F /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AEE49AE2AA75A5E00243D2F /* Log.swift */; };
D05258232C76B6B7005093E3 /* DND.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05258222C76B6B7005093E3 /* DND.swift */; };
D05258252C76C7E0005093E3 /* macos-focus-mode.shortcut in Resources */ = {isa = PBXBuildFile; fileRef = D05258242C76C7D4005093E3 /* macos-focus-mode.shortcut */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -34,6 +36,8 @@
8AA5B3F12812A74600BE1A6C /* Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = "<group>"; };
8AEE49AE2AA75A5E00243D2F /* Log.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = "<group>"; };
C4C8DE0E2930DBB4003B1110 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
D05258222C76B6B7005093E3 /* DND.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DND.swift; sourceTree = "<group>"; };
D05258242C76C7D4005093E3 /* macos-focus-mode.shortcut */ = {isa = PBXFileReference; lastKnownFileType = file; path = "macos-focus-mode.shortcut"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -70,9 +74,11 @@
8A21B78A27EE31A20009E5DB /* TomatoBar */ = {
isa = PBXGroup;
children = (
D05258242C76C7D4005093E3 /* macos-focus-mode.shortcut */,
8A21B78B27EE31A20009E5DB /* App.swift */,
8AEE49AE2AA75A5E00243D2F /* Log.swift */,
8A21B78D27EE31A20009E5DB /* View.swift */,
D05258222C76B6B7005093E3 /* DND.swift */,
8A9F700827EE396E0012A1EC /* State.swift */,
8A9F701827EE3DAF0012A1EC /* Timer.swift */,
8A9F701E27EE536D0012A1EC /* Player.swift */,
Expand Down Expand Up @@ -161,6 +167,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D05258252C76C7E0005093E3 /* macos-focus-mode.shortcut in Resources */,
8A21B79027EE31A30009E5DB /* Assets.xcassets in Resources */,
8AA5434A2930F008002A47FC /* Localizable.strings in Resources */,
);
Expand Down Expand Up @@ -197,6 +204,7 @@
files = (
8A9F701F27EE536D0012A1EC /* Player.swift in Sources */,
8AEE49AF2AA75A5E00243D2F /* Log.swift in Sources */,
D05258232C76B6B7005093E3 /* DND.swift in Sources */,
8A21B78E27EE31A20009E5DB /* View.swift in Sources */,
8A9F701927EE3DAF0012A1EC /* Timer.swift in Sources */,
8A21B78C27EE31A20009E5DB /* App.swift in Sources */,
Expand Down Expand Up @@ -354,6 +362,7 @@
INFOPLIST_FILE = TomatoBar/Info.plist;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
INFOPLIST_KEY_LSUIElement = YES;
INFOPLIST_KEY_NSAppleEventsUsageDescription = "Allow changing the state of Do Not Disturb";
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Ilya Voronin. All rights reserved. https://github.com/ivoronin/TomatoBar";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
Expand Down Expand Up @@ -389,6 +398,7 @@
INFOPLIST_FILE = TomatoBar/Info.plist;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
INFOPLIST_KEY_LSUIElement = YES;
INFOPLIST_KEY_NSAppleEventsUsageDescription = "Allow changing the state of Do Not Disturb";
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Ilya Voronin. All rights reserved. https://github.com/ivoronin/TomatoBar";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
Expand Down
37 changes: 37 additions & 0 deletions TomatoBar/DND.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import ScriptingBridge
import Cocoa

@objc protocol ShortcutsEvents {
@objc optional var shortcuts: SBElementArray { get }
}
@objc protocol Shortcut {
@objc optional var name: String { get }
@objc optional func run(withInput: Any?) -> Any?
}

extension SBApplication: ShortcutsEvents {}
extension SBObject: Shortcut {}

public func DoNotDisturb(state: Bool) -> Bool {
guard
let app: ShortcutsEvents = SBApplication(bundleIdentifier: "com.apple.shortcuts.events"),
let shortcuts = app.shortcuts else {
return false
}

guard let shortcut = shortcuts.object(withName: "macos-focus-mode") as? Shortcut else {
return false
}

guard shortcut.name == "macos-focus-mode" else {
if let shortcutURL = Bundle.main.url(forResource: "macos-focus-mode", withExtension: "shortcut") {
NSWorkspace.shared.open(shortcutURL)
}
return false
}

let input = state ? "on" : "off"
_ = shortcut.run?(withInput: input)

return true
}
14 changes: 14 additions & 0 deletions TomatoBar/Timer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class TBTimer: ObservableObject {
@AppStorage("shortRestIntervalLength") var shortRestIntervalLength = 5
@AppStorage("longRestIntervalLength") var longRestIntervalLength = 15
@AppStorage("workIntervalsInSet") var workIntervalsInSet = 4
@AppStorage("toggleDoNotDisturb") var toggleDoNotDisturb = false
// This preference is "hidden"
@AppStorage("overrunTimeLimit") var overrunTimeLimit = -60.0

Expand Down Expand Up @@ -180,6 +181,14 @@ class TBTimer: ObservableObject {
player.playWindup()
player.startTicking()
startTimer(seconds: workIntervalLength * 60)
if (toggleDoNotDisturb) {
DispatchQueue.main.async { [self] in
let res = DoNotDisturb(state: true)
if !res {
stateMachine <-! .startStop
}
}
}
}

private func onWorkFinish(context _: TBStateMachine.Context) {
Expand All @@ -189,6 +198,11 @@ class TBTimer: ObservableObject {

private func onWorkEnd(context _: TBStateMachine.Context) {
player.stopTicking()
if (toggleDoNotDisturb) {
DispatchQueue.main.async {
_ = DoNotDisturb(state: false)
}
}
}

private func onRestStart(context _: TBStateMachine.Context) {
Expand Down
9 changes: 9 additions & 0 deletions TomatoBar/TomatoBar.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,14 @@
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.automation.apple-events</key>
<true/>
<key>com.apple.security.scripting-targets</key>
<dict>
<key>com.apple.shortcuts.events</key>
<array>
<string>com.apple.shortcuts.run</string>
</array>
</dict>
</dict>
</plist>
21 changes: 12 additions & 9 deletions TomatoBar/View.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ private struct SettingsView: View {
.onChange(of: timer.showTimerInMenuBar) { _ in
timer.updateTimeLeft()
}
Toggle(isOn: $timer.toggleDoNotDisturb) {
Text(NSLocalizedString("SettingsView.toggleDoNotDisturb.label",
comment: "Toggle Do Not Disturb"))
.frame(maxWidth: .infinity, alignment: .leading)
}
.toggleStyle(.switch)
.help(NSLocalizedString("SettingsView.toggleDoNotDisturb.help",
comment: "Toggle Do Not Disturb hint"))
Toggle(isOn: $launchAtLogin.isEnabled) {
Text(NSLocalizedString("SettingsView.launchAtLogin.label",
comment: "Launch at login label"))
Expand Down Expand Up @@ -208,23 +216,18 @@ struct TBPopoverView: View {
.keyboardShortcut("q")
}
}
.frame(width: 240)
.fixedSize()
#if DEBUG
/*
After several hours of Googling and trying various StackOverflow
recipes I still haven't figured a reliable way to auto resize
popover to fit all it's contents (pull requests are welcome!).
The following code block is used to determine the optimal
geometry of the popover.
*/
.overlay(
.overlay(
GeometryReader { proxy in
debugSize(proxy: proxy)
}
)
#endif
/* Use values from GeometryReader */
// .frame(width: 240, height: 276)
.padding(12)
.padding(12)
}
}

Expand Down
2 changes: 2 additions & 0 deletions TomatoBar/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"SettingsView.shortcut.label" = "Shortcut";
"SettingsView.stopAfterBreak.label" = "Stop after break";
"SettingsView.showTimerInMenuBar.label" = "Show timer in menu bar";
"SettingsView.toggleDoNotDisturb.label" = "Toggle Do Not Disturb";
"SettingsView.toggleDoNotDisturb.help" = "Runs a shortcut to enable/disable Do Not Disturb on work/rest transitions";
"SettingsView.launchAtLogin.label" = "Launch at login";
"SoundsView.isWindupEnabled.label" = "Windup";
"SoundsView.isDingEnabled.label" = "Ding";
Expand Down
Binary file added TomatoBar/macos-focus-mode.shortcut
Binary file not shown.