mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-20 04:03:26 +01:00
[AltServer] Refactors common NSMenu logic into MenuController
This commit is contained in:
@@ -21,6 +21,8 @@ private let altstoreAppURL = URL(string: "https://f000.backblazeb2.com/file/alts
|
|||||||
private let altstoreAppURL = URL(string: "https://f000.backblazeb2.com/file/altstore/altstore.ipa")!
|
private let altstoreAppURL = URL(string: "https://f000.backblazeb2.com/file/altstore/altstore.ipa")!
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extension ALTDevice: MenuDisplayable {}
|
||||||
|
|
||||||
@NSApplicationMain
|
@NSApplicationMain
|
||||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
|
||||||
@@ -41,6 +43,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
private weak var authenticationAppleIDTextField: NSTextField?
|
private weak var authenticationAppleIDTextField: NSTextField?
|
||||||
private weak var authenticationPasswordTextField: NSSecureTextField?
|
private weak var authenticationPasswordTextField: NSSecureTextField?
|
||||||
|
|
||||||
|
private var connectedDevicesMenuController: MenuController<ALTDevice>!
|
||||||
|
private var sideloadIPAConnectedDevicesMenuController: MenuController<ALTDevice>!
|
||||||
|
|
||||||
func applicationDidFinishLaunching(_ aNotification: Notification)
|
func applicationDidFinishLaunching(_ aNotification: Notification)
|
||||||
{
|
{
|
||||||
UserDefaults.standard.registerDefaults()
|
UserDefaults.standard.registerDefaults()
|
||||||
@@ -56,8 +61,20 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
self.statusItem = item
|
self.statusItem = item
|
||||||
|
|
||||||
self.appMenu.delegate = self
|
self.appMenu.delegate = self
|
||||||
self.connectedDevicesMenu.delegate = self
|
|
||||||
self.sideloadIPAConnectedDevicesMenu.delegate = self
|
let placeholder = NSLocalizedString("No Connected Devices", comment: "")
|
||||||
|
|
||||||
|
self.connectedDevicesMenuController = MenuController<ALTDevice>(menu: self.connectedDevicesMenu, items: [])
|
||||||
|
self.connectedDevicesMenuController.placeholder = placeholder
|
||||||
|
self.connectedDevicesMenuController.action = { [weak self] device in
|
||||||
|
self?.installAltStore(to: device)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.sideloadIPAConnectedDevicesMenuController = MenuController<ALTDevice>(menu: self.sideloadIPAConnectedDevicesMenu, items: [])
|
||||||
|
self.sideloadIPAConnectedDevicesMenuController.placeholder = placeholder
|
||||||
|
self.sideloadIPAConnectedDevicesMenuController.action = { [weak self] device in
|
||||||
|
self?.sideloadIPA(to: device)
|
||||||
|
}
|
||||||
|
|
||||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) { (success, error) in
|
UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) { (success, error) in
|
||||||
guard success else { return }
|
guard success else { return }
|
||||||
@@ -89,20 +106,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
|
|
||||||
private extension AppDelegate
|
private extension AppDelegate
|
||||||
{
|
{
|
||||||
@objc func installAltStore(_ item: NSMenuItem)
|
@objc func installAltStore(to device: ALTDevice)
|
||||||
{
|
{
|
||||||
guard let index = item.menu?.index(of: item), index != -1 else { return }
|
|
||||||
|
|
||||||
let device = self.connectedDevices[index]
|
|
||||||
self.installApplication(at: altstoreAppURL, to: device)
|
self.installApplication(at: altstoreAppURL, to: device)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func sideloadIPA(_ item: NSMenuItem)
|
@objc func sideloadIPA(to device: ALTDevice)
|
||||||
{
|
{
|
||||||
guard let index = item.menu?.index(of: item), index != -1 else { return }
|
|
||||||
|
|
||||||
let device = self.connectedDevices[index]
|
|
||||||
|
|
||||||
let openPanel = NSOpenPanel()
|
let openPanel = NSOpenPanel()
|
||||||
openPanel.canChooseDirectories = false
|
openPanel.canChooseDirectories = false
|
||||||
openPanel.allowsMultipleSelection = false
|
openPanel.allowsMultipleSelection = false
|
||||||
@@ -303,6 +313,9 @@ extension AppDelegate: NSMenuDelegate
|
|||||||
guard menu == self.appMenu else { return }
|
guard menu == self.appMenu else { return }
|
||||||
|
|
||||||
self.connectedDevices = ALTDeviceManager.shared.availableDevices
|
self.connectedDevices = ALTDeviceManager.shared.availableDevices
|
||||||
|
|
||||||
|
self.connectedDevicesMenuController.items = self.connectedDevices
|
||||||
|
self.sideloadIPAConnectedDevicesMenuController.items = self.connectedDevices
|
||||||
|
|
||||||
self.launchAtLoginMenuItem.target = self
|
self.launchAtLoginMenuItem.target = self
|
||||||
self.launchAtLoginMenuItem.action = #selector(AppDelegate.toggleLaunchAtLogin(_:))
|
self.launchAtLoginMenuItem.action = #selector(AppDelegate.toggleLaunchAtLogin(_:))
|
||||||
@@ -323,37 +336,6 @@ extension AppDelegate: NSMenuDelegate
|
|||||||
self.installMailPluginMenuItem.target = self
|
self.installMailPluginMenuItem.target = self
|
||||||
self.installMailPluginMenuItem.action = #selector(AppDelegate.handleInstallMailPluginMenuItem(_:))
|
self.installMailPluginMenuItem.action = #selector(AppDelegate.handleInstallMailPluginMenuItem(_:))
|
||||||
}
|
}
|
||||||
|
|
||||||
func numberOfItems(in menu: NSMenu) -> Int
|
|
||||||
{
|
|
||||||
guard menu == self.connectedDevicesMenu || menu == self.sideloadIPAConnectedDevicesMenu else { return -1 }
|
|
||||||
|
|
||||||
return self.connectedDevices.isEmpty ? 1 : self.connectedDevices.count
|
|
||||||
}
|
|
||||||
|
|
||||||
func menu(_ menu: NSMenu, update item: NSMenuItem, at index: Int, shouldCancel: Bool) -> Bool
|
|
||||||
{
|
|
||||||
guard menu == self.connectedDevicesMenu || menu == self.sideloadIPAConnectedDevicesMenu else { return false }
|
|
||||||
|
|
||||||
if self.connectedDevices.isEmpty
|
|
||||||
{
|
|
||||||
item.title = NSLocalizedString("No Connected Devices", comment: "")
|
|
||||||
item.isEnabled = false
|
|
||||||
item.target = nil
|
|
||||||
item.action = nil
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
let device = self.connectedDevices[index]
|
|
||||||
item.title = device.name
|
|
||||||
item.isEnabled = true
|
|
||||||
item.target = self
|
|
||||||
item.action = (menu == self.connectedDevicesMenu) ? #selector(AppDelegate.installAltStore(_:)) : #selector(AppDelegate.sideloadIPA(_:))
|
|
||||||
item.tag = index
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AppDelegate: NSTextFieldDelegate
|
extension AppDelegate: NSTextFieldDelegate
|
||||||
|
|||||||
@@ -93,9 +93,6 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
</items>
|
</items>
|
||||||
<connections>
|
|
||||||
<outlet property="delegate" destination="Voe-Tx-rLC" id="VYb-BL-Zri"/>
|
|
||||||
</connections>
|
|
||||||
</menu>
|
</menu>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem title="Sideload .ipa" id="x0e-zI-0A2" userLabel="Install .ipa">
|
<menuItem title="Sideload .ipa" id="x0e-zI-0A2" userLabel="Install .ipa">
|
||||||
@@ -109,9 +106,6 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
</items>
|
</items>
|
||||||
<connections>
|
|
||||||
<outlet property="delegate" destination="Voe-Tx-rLC" id="N3K-su-XV6"/>
|
|
||||||
</connections>
|
|
||||||
</menu>
|
</menu>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem isSeparatorItem="YES" id="1ZZ-BB-xHy"/>
|
<menuItem isSeparatorItem="YES" id="1ZZ-BB-xHy"/>
|
||||||
|
|||||||
115
AltServer/MenuController.swift
Normal file
115
AltServer/MenuController.swift
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
//
|
||||||
|
// MenuController.swift
|
||||||
|
// AltServer
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 3/3/21.
|
||||||
|
// Copyright © 2021 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
protocol MenuDisplayable
|
||||||
|
{
|
||||||
|
var name: String { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
class MenuController<T: MenuDisplayable & Hashable>: NSObject, NSMenuDelegate
|
||||||
|
{
|
||||||
|
let menu: NSMenu
|
||||||
|
|
||||||
|
var items: [T] {
|
||||||
|
didSet {
|
||||||
|
self.submenus.removeAll()
|
||||||
|
self.updateMenu()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var placeholder: String? {
|
||||||
|
didSet {
|
||||||
|
self.updateMenu()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var action: ((T) -> Void)?
|
||||||
|
|
||||||
|
var submenuHandler: ((T) -> NSMenu)?
|
||||||
|
private var submenus = [T: NSMenu]()
|
||||||
|
|
||||||
|
init(menu: NSMenu, items: [T])
|
||||||
|
{
|
||||||
|
self.menu = menu
|
||||||
|
self.items = items
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.menu.delegate = self
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
private func performAction(_ menuItem: NSMenuItem)
|
||||||
|
{
|
||||||
|
guard case let index = self.menu.index(of: menuItem), index != -1 else { return }
|
||||||
|
|
||||||
|
let item = self.items[index]
|
||||||
|
self.action?(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
func numberOfItems(in menu: NSMenu) -> Int
|
||||||
|
{
|
||||||
|
let numberOfItems = (self.items.isEmpty && self.placeholder != nil) ? 1 : self.items.count
|
||||||
|
return numberOfItems
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
func menu(_ menu: NSMenu, update menuItem: NSMenuItem, at index: Int, shouldCancel: Bool) -> Bool
|
||||||
|
{
|
||||||
|
if let text = self.placeholder, self.items.isEmpty
|
||||||
|
{
|
||||||
|
menuItem.title = text
|
||||||
|
menuItem.isEnabled = false
|
||||||
|
menuItem.target = nil
|
||||||
|
menuItem.action = nil
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
let item = self.items[index]
|
||||||
|
|
||||||
|
menuItem.title = item.name
|
||||||
|
menuItem.isEnabled = true
|
||||||
|
menuItem.target = self
|
||||||
|
menuItem.action = #selector(MenuController.performAction(_:))
|
||||||
|
menuItem.tag = index
|
||||||
|
|
||||||
|
if let submenu = self.submenus[item] ?? self.submenuHandler?(item)
|
||||||
|
{
|
||||||
|
menuItem.submenu = submenu
|
||||||
|
|
||||||
|
// Cache submenu to prevent duplicate calls to submenuHandler.
|
||||||
|
self.submenus[item] = submenu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension MenuController
|
||||||
|
{
|
||||||
|
func updateMenu()
|
||||||
|
{
|
||||||
|
self.menu.removeAllItems()
|
||||||
|
|
||||||
|
let numberOfItems = self.numberOfItems(in: self.menu)
|
||||||
|
for index in 0 ..< numberOfItems
|
||||||
|
{
|
||||||
|
let menuItem = NSMenuItem(title: "", action: nil, keyEquivalent: "")
|
||||||
|
guard self.menu(self.menu, update: menuItem, at: index, shouldCancel: false) else { break }
|
||||||
|
|
||||||
|
self.menu.addItem(menuItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.menu.update()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -332,6 +332,7 @@
|
|||||||
BFF00D302501BD7D00746320 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = BFF00D2F2501BD7D00746320 /* Intents.intentdefinition */; };
|
BFF00D302501BD7D00746320 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = BFF00D2F2501BD7D00746320 /* Intents.intentdefinition */; };
|
||||||
BFF00D322501BDA100746320 /* BackgroundRefreshAppsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF00D312501BDA100746320 /* BackgroundRefreshAppsOperation.swift */; };
|
BFF00D322501BDA100746320 /* BackgroundRefreshAppsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF00D312501BDA100746320 /* BackgroundRefreshAppsOperation.swift */; };
|
||||||
BFF00D342501BDCF00746320 /* IntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF00D332501BDCF00746320 /* IntentHandler.swift */; };
|
BFF00D342501BDCF00746320 /* IntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF00D332501BDCF00746320 /* IntentHandler.swift */; };
|
||||||
|
BFF0394B25F0551600BE607D /* MenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0394A25F0551600BE607D /* MenuController.swift */; };
|
||||||
BFF0B68E23219520007A79E1 /* PatreonViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B68D23219520007A79E1 /* PatreonViewController.swift */; };
|
BFF0B68E23219520007A79E1 /* PatreonViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B68D23219520007A79E1 /* PatreonViewController.swift */; };
|
||||||
BFF0B69023219C6D007A79E1 /* PatreonComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B68F23219C6D007A79E1 /* PatreonComponents.swift */; };
|
BFF0B69023219C6D007A79E1 /* PatreonComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B68F23219C6D007A79E1 /* PatreonComponents.swift */; };
|
||||||
BFF0B6922321A305007A79E1 /* AboutPatreonHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFF0B6912321A305007A79E1 /* AboutPatreonHeaderView.xib */; };
|
BFF0B6922321A305007A79E1 /* AboutPatreonHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFF0B6912321A305007A79E1 /* AboutPatreonHeaderView.xib */; };
|
||||||
@@ -775,6 +776,7 @@
|
|||||||
BFF00D2F2501BD7D00746320 /* Intents.intentdefinition */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.intentdefinition; path = Intents.intentdefinition; sourceTree = "<group>"; };
|
BFF00D2F2501BD7D00746320 /* Intents.intentdefinition */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.intentdefinition; path = Intents.intentdefinition; sourceTree = "<group>"; };
|
||||||
BFF00D312501BDA100746320 /* BackgroundRefreshAppsOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundRefreshAppsOperation.swift; sourceTree = "<group>"; };
|
BFF00D312501BDA100746320 /* BackgroundRefreshAppsOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundRefreshAppsOperation.swift; sourceTree = "<group>"; };
|
||||||
BFF00D332501BDCF00746320 /* IntentHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntentHandler.swift; sourceTree = "<group>"; };
|
BFF00D332501BDCF00746320 /* IntentHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntentHandler.swift; sourceTree = "<group>"; };
|
||||||
|
BFF0394A25F0551600BE607D /* MenuController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuController.swift; sourceTree = "<group>"; };
|
||||||
BFF0B68D23219520007A79E1 /* PatreonViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatreonViewController.swift; sourceTree = "<group>"; };
|
BFF0B68D23219520007A79E1 /* PatreonViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatreonViewController.swift; sourceTree = "<group>"; };
|
||||||
BFF0B68F23219C6D007A79E1 /* PatreonComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatreonComponents.swift; sourceTree = "<group>"; };
|
BFF0B68F23219C6D007A79E1 /* PatreonComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatreonComponents.swift; sourceTree = "<group>"; };
|
||||||
BFF0B6912321A305007A79E1 /* AboutPatreonHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AboutPatreonHeaderView.xib; sourceTree = "<group>"; };
|
BFF0B6912321A305007A79E1 /* AboutPatreonHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AboutPatreonHeaderView.xib; sourceTree = "<group>"; };
|
||||||
@@ -975,6 +977,7 @@
|
|||||||
BFE48974238007CE003239E0 /* AnisetteDataManager.swift */,
|
BFE48974238007CE003239E0 /* AnisetteDataManager.swift */,
|
||||||
BFC712BA2512B9CF00AB5EBE /* PluginManager.swift */,
|
BFC712BA2512B9CF00AB5EBE /* PluginManager.swift */,
|
||||||
BFAD67A225E0854500D4C4D1 /* DeveloperDiskManager.swift */,
|
BFAD67A225E0854500D4C4D1 /* DeveloperDiskManager.swift */,
|
||||||
|
BFF0394A25F0551600BE607D /* MenuController.swift */,
|
||||||
BF703195229F36FF006E110F /* Devices */,
|
BF703195229F36FF006E110F /* Devices */,
|
||||||
BFD52BDC22A0A659000B7ED1 /* Connections */,
|
BFD52BDC22A0A659000B7ED1 /* Connections */,
|
||||||
BF055B4A233B528B0086DEA9 /* Extensions */,
|
BF055B4A233B528B0086DEA9 /* Extensions */,
|
||||||
@@ -2280,6 +2283,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
BFF767C82489A74E0097E58C /* WirelessConnectionHandler.swift in Sources */,
|
BFF767C82489A74E0097E58C /* WirelessConnectionHandler.swift in Sources */,
|
||||||
|
BFF0394B25F0551600BE607D /* MenuController.swift in Sources */,
|
||||||
BFECAC8024FD950B0077C41F /* ConnectionManager.swift in Sources */,
|
BFECAC8024FD950B0077C41F /* ConnectionManager.swift in Sources */,
|
||||||
BFECAC8324FD950B0077C41F /* NetworkConnection.swift in Sources */,
|
BFECAC8324FD950B0077C41F /* NetworkConnection.swift in Sources */,
|
||||||
BF541C0B25E5A5FA00CD46B2 /* FileManager+URLs.swift in Sources */,
|
BF541C0B25E5A5FA00CD46B2 /* FileManager+URLs.swift in Sources */,
|
||||||
|
|||||||
Reference in New Issue
Block a user