Skip to content

Commit

Permalink
fix: 🔍 Show the dock icon when the browser window is open (#66)
Browse files Browse the repository at this point in the history
This change toggles the application policy from 'accessory' to 'regular'
whenever one or more browser windows are open. This means that whenever
a window is visible, the app should appear in the application switcher
(Cmd+Tab).

There are also a couple of drive-by fixes, cleaning up the build script
a little, and extracting the update view and model into their own files.
  • Loading branch information
jbmorley authored Jun 29, 2024
1 parent 982bcd6 commit ca9a625
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 36 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.build
.swiftpm
/archives
/build
12 changes: 12 additions & 0 deletions Reconnect.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
D83658B72C29596F00B45693 /* NavigationStackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D83658B62C29596F00B45693 /* NavigationStackTests.swift */; };
D83658B92C298C4F00B45693 /* NavigationStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = D83658B82C298C4F00B45693 /* NavigationStack.swift */; };
D83658BB2C29A09300B45693 /* HistoryItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D83658BA2C29A09300B45693 /* HistoryItemView.swift */; };
D83B4DDF2C2F9995003C3DC1 /* CheckForUpdatesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D83B4DDE2C2F9995003C3DC1 /* CheckForUpdatesView.swift */; };
D83B4DE12C2F99C3003C3DC1 /* CheckForUpdatesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D83B4DE02C2F99C3003C3DC1 /* CheckForUpdatesViewModel.swift */; };
D84351502C2B636C009055C1 /* DriveInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D843514F2C2B636C009055C1 /* DriveInfo.swift */; };
D84964DA2C1BFCB600405656 /* ReconnectApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84964D92C1BFCB600405656 /* ReconnectApp.swift */; };
D84964DE2C1BFCB700405656 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D84964DD2C1BFCB700405656 /* Assets.xcassets */; };
Expand All @@ -23,6 +25,7 @@
D84964F62C1BFCB700405656 /* ReconnectUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84964F52C1BFCB700405656 /* ReconnectUITests.swift */; };
D84964F82C1BFCB700405656 /* ReconnectUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84964F72C1BFCB700405656 /* ReconnectUITestsLaunchTests.swift */; };
D84965062C1BFF1F00405656 /* Socket in Frameworks */ = {isa = PBXBuildFile; productRef = D84965052C1BFF1F00405656 /* Socket */; };
D86924C82C30964400A8492C /* DockIconManaager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86924C72C30964400A8492C /* DockIconManaager.swift */; };
D87AAC8A2C27EB070091F442 /* FileServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87AAC892C27EB070091F442 /* FileServer.swift */; };
D886DA742C28CDAD00E84BDA /* BrowserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D886DA732C28CDAD00E84BDA /* BrowserView.swift */; };
D886DA762C29343900E84BDA /* BrowserModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D886DA752C29343900E84BDA /* BrowserModel.swift */; };
Expand Down Expand Up @@ -64,6 +67,8 @@
D83658B62C29596F00B45693 /* NavigationStackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationStackTests.swift; sourceTree = "<group>"; };
D83658B82C298C4F00B45693 /* NavigationStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationStack.swift; sourceTree = "<group>"; };
D83658BA2C29A09300B45693 /* HistoryItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryItemView.swift; sourceTree = "<group>"; };
D83B4DDE2C2F9995003C3DC1 /* CheckForUpdatesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckForUpdatesView.swift; sourceTree = "<group>"; };
D83B4DE02C2F99C3003C3DC1 /* CheckForUpdatesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckForUpdatesViewModel.swift; sourceTree = "<group>"; };
D843514F2C2B636C009055C1 /* DriveInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DriveInfo.swift; sourceTree = "<group>"; };
D84964D62C1BFCB600405656 /* Reconnect.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Reconnect.app; sourceTree = BUILT_PRODUCTS_DIR; };
D84964D92C1BFCB600405656 /* ReconnectApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReconnectApp.swift; sourceTree = "<group>"; };
Expand All @@ -75,6 +80,7 @@
D84964F12C1BFCB700405656 /* ReconnectUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReconnectUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
D84964F52C1BFCB700405656 /* ReconnectUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReconnectUITests.swift; sourceTree = "<group>"; };
D84964F72C1BFCB700405656 /* ReconnectUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReconnectUITestsLaunchTests.swift; sourceTree = "<group>"; };
D86924C72C30964400A8492C /* DockIconManaager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DockIconManaager.swift; sourceTree = "<group>"; };
D876E60C2C22BAA9004A6881 /* libSystem.B.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libSystem.B.tbd; path = usr/lib/libSystem.B.tbd; sourceTree = SDKROOT; };
D876E60D2C22BAB9004A6881 /* libSystem.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libSystem.tbd; path = usr/lib/libSystem.tbd; sourceTree = SDKROOT; };
D87AAC892C27EB070091F442 /* FileServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileServer.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -128,6 +134,7 @@
children = (
D8184C462C253C59008FA79B /* ApplicationModel.swift */,
D886DA752C29343900E84BDA /* BrowserModel.swift */,
D83B4DE02C2F99C3003C3DC1 /* CheckForUpdatesViewModel.swift */,
D8C080792C1D7D8D003128AB /* ReconnectError.swift */,
);
path = Model;
Expand All @@ -137,6 +144,7 @@
isa = PBXGroup;
children = (
D886DA732C28CDAD00E84BDA /* BrowserView.swift */,
D83B4DDE2C2F9995003C3DC1 /* CheckForUpdatesView.swift */,
D88FA14F2C2CE29900805DBD /* ContentView.swift */,
D83658BA2C29A09300B45693 /* HistoryItemView.swift */,
D8D3E7A02C25410E003E696D /* MainMenu.swift */,
Expand All @@ -148,6 +156,7 @@
D822EA0F2C2BA305008A4BAA /* Utilities */ = {
isa = PBXGroup;
children = (
D86924C72C30964400A8492C /* DockIconManaager.swift */,
D83658B82C298C4F00B45693 /* NavigationStack.swift */,
D822EA122C2BA92E008A4BAA /* SerialDeviceMonitor.swift */,
);
Expand Down Expand Up @@ -413,12 +422,15 @@
D8E31EB52C26E10900350082 /* Licensable.swift in Sources */,
D886DA742C28CDAD00E84BDA /* BrowserView.swift in Sources */,
D88FA1502C2CE29900805DBD /* ContentView.swift in Sources */,
D83B4DE12C2F99C3003C3DC1 /* CheckForUpdatesViewModel.swift in Sources */,
D83658B92C298C4F00B45693 /* NavigationStack.swift in Sources */,
D822EA132C2BA92E008A4BAA /* SerialDeviceMonitor.swift in Sources */,
D86924C82C30964400A8492C /* DockIconManaager.swift in Sources */,
D87AAC8A2C27EB070091F442 /* FileServer.swift in Sources */,
D8184C472C253C59008FA79B /* ApplicationModel.swift in Sources */,
D89B5E8E2C2AA8680014A5B6 /* Sidebar.swift in Sources */,
D886DA762C29343900E84BDA /* BrowserModel.swift in Sources */,
D83B4DDF2C2F9995003C3DC1 /* CheckForUpdatesView.swift in Sources */,
D8E31EB72C26E13E00350082 /* String.swift in Sources */,
D8D3E7A12C25410E003E696D /* MainMenu.swift in Sources */,
D84964DA2C1BFCB600405656 /* ReconnectApp.swift in Sources */,
Expand Down
32 changes: 32 additions & 0 deletions Reconnect/Model/CheckForUpdatesViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Reconnect -- Psion connectivity for macOS
//
// Copyright (C) 2024 Jason Morley
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

import SwiftUI

import Sparkle

// This view model class publishes when new updates can be checked by the user
final class CheckForUpdatesViewModel: ObservableObject {
@Published var canCheckForUpdates = false

init(updater: SPUUpdater) {
updater.publisher(for: \.canCheckForUpdates)
.assign(to: &$canCheckForUpdates)
}
}

30 changes: 0 additions & 30 deletions Reconnect/ReconnectApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,36 +22,6 @@ import Diligence
import Interact
import Sparkle

// This view model class publishes when new updates can be checked by the user
final class CheckForUpdatesViewModel: ObservableObject {
@Published var canCheckForUpdates = false

init(updater: SPUUpdater) {
updater.publisher(for: \.canCheckForUpdates)
.assign(to: &$canCheckForUpdates)
}
}

// This is the view for the Check for Updates menu item
// Note this intermediate view is necessary for the disabled state on the menu item to work properly before Monterey.
// See https://stackoverflow.com/questions/68553092/menu-not-updating-swiftui-bug for more info
struct CheckForUpdatesView: View {
@ObservedObject private var checkForUpdatesViewModel: CheckForUpdatesViewModel
private let updater: SPUUpdater

init(updater: SPUUpdater) {
self.updater = updater

// Create our view model for our CheckForUpdatesView
self.checkForUpdatesViewModel = CheckForUpdatesViewModel(updater: updater)
}

var body: some View {
Button("Check for Updates…", action: updater.checkForUpdates)
.disabled(!checkForUpdatesViewModel.canCheckForUpdates)
}
}

@main
struct ReconnectApp: App {

Expand Down
62 changes: 62 additions & 0 deletions Reconnect/Utilities/DockIconManaager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Reconnect -- Psion connectivity for macOS
//
// Copyright (C) 2024 Jason Morley
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

import SwiftUI

fileprivate class DockIconManager {

static var shared: DockIconManager = {
return DockIconManager()
}()

@MainActor var activeRequests: Int = 0 {
didSet {
if activeRequests > 0 {
NSApp.setActivationPolicy(.regular)
} else {
NSApp.setActivationPolicy(.accessory)
}
}
}

func requestDockIcon() async {
await MainActor.run {
activeRequests = activeRequests + 1
}
defer {
Task { @MainActor in
activeRequests = activeRequests - 1
}
}
while !Task.isCancelled {
try? await Task.sleep(for: .seconds(60*60*24))
}
}

}

extension View {

func showsDockIcon() -> some View {
self
.task {
await DockIconManager.shared.requestDockIcon()
}
}

}
41 changes: 41 additions & 0 deletions Reconnect/Views/CheckForUpdatesView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Reconnect -- Psion connectivity for macOS
//
// Copyright (C) 2024 Jason Morley
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

import SwiftUI

import Sparkle

// This is the view for the Check for Updates menu item
// Note this intermediate view is necessary for the disabled state on the menu item to work properly before Monterey.
// See https://stackoverflow.com/questions/68553092/menu-not-updating-swiftui-bug for more info
struct CheckForUpdatesView: View {
@ObservedObject private var checkForUpdatesViewModel: CheckForUpdatesViewModel
private let updater: SPUUpdater

init(updater: SPUUpdater) {
self.updater = updater

// Create our view model for our CheckForUpdatesView
self.checkForUpdatesViewModel = CheckForUpdatesViewModel(updater: updater)
}

var body: some View {
Button("Check for Updates…", action: updater.checkForUpdates)
.disabled(!checkForUpdatesViewModel.canCheckForUpdates)
}
}
11 changes: 7 additions & 4 deletions Reconnect/Views/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ struct ContentView: View {
let fileServer = FileServer(host: "127.0.0.1", port: 7501)

var body: some View {
if applicationModel.isConnected {
BrowserView(fileServer: fileServer)
} else {
ContentUnavailableView("Not Connected", systemImage: "star")
VStack {
if applicationModel.isConnected {
BrowserView(fileServer: fileServer)
} else {
ContentUnavailableView("Not Connected", systemImage: "star")
}
}
.showsDockIcon()
}

}
7 changes: 7 additions & 0 deletions Reconnect/Views/MainMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ struct MainMenu: View {
}
Divider()
CheckForUpdatesView(updater: applicationModel.updaterController.updater)

Divider()

Button("Show icon") {
NSApp.setActivationPolicy(.regular)
}

Divider()
Button("Quit") {
applicationModel.quit()
Expand Down
9 changes: 7 additions & 2 deletions scripts/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,18 @@ sudo xcode-select --switch "$MACOS_XCODE_PATH"
# List the available schemes.
xcode_project -list

# Clean up the build directory.
# Clean up and recreate the output directories.

if [ -d "$BUILD_DIRECTORY" ] ; then
rm -r "$BUILD_DIRECTORY"
fi
mkdir -p "$BUILD_DIRECTORY"

if [ -d "$ARCHIVES_DIRECTORY" ] ; then
rm -r "$BUILD_DIRECTORY"
fi
mkdir -p "$ARCHIVES_DIRECTORY"

# Create the a new keychain.
if [ -d "$TEMPORARY_DIRECTORY" ] ; then
rm -rf "$TEMPORARY_DIRECTORY"
Expand Down Expand Up @@ -187,7 +193,6 @@ echo -n "$SPARKLE_PRIVATE_KEY_BASE64" | base64 --decode -o "$SPARKLE_PRIVATE_KEY

# Generate the appcast.
cd "$ROOT_DIRECTORY"
mkdir -p "$ARCHIVES_DIRECTORY"
cp "$RELEASE_ZIP_PATH" "$ARCHIVES_DIRECTORY"
changes notes --template "$RELEASE_NOTES_TEMPLATE_PATH" >> "$ARCHIVES_DIRECTORY/$RELEASE_BASENAME.html"
"$GENERATE_APPCAST" --ed-key-file "$SPARKLE_PRIVATE_KEY_FILE" "$ARCHIVES_DIRECTORY"
Expand Down

0 comments on commit ca9a625

Please sign in to comment.