diff --git a/Box42.xcodeproj/project.pbxproj b/Box42.xcodeproj/project.pbxproj index c4c677f..f2eb8dd 100644 --- a/Box42.xcodeproj/project.pbxproj +++ b/Box42.xcodeproj/project.pbxproj @@ -68,7 +68,7 @@ DE77BA562A82637900713683 /* StateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE77BA552A82637900713683 /* StateManager.swift */; }; DE77BBA22A9DDC40006CC98B /* ScriptsFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE77BBA12A9DDC40006CC98B /* ScriptsFileManager.swift */; }; DE77BBA62A9DDF2B006CC98B /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE0A91772A8F014F00D1D6F1 /* WebView.swift */; }; - DE77BBCD2A9E0568006CC98B /* ExcuteScripts.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE77BBCC2A9E0568006CC98B /* ExcuteScripts.swift */; }; + DE77BBCD2A9E0568006CC98B /* ExecuteScripts.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE77BBCC2A9E0568006CC98B /* ExecuteScripts.swift */; }; DE77BBD22A9E0AE8006CC98B /* WebViewUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE77BBD12A9E0AE8006CC98B /* WebViewUI.swift */; }; DE77BBE22A9E0F70006CC98B /* Scripts.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE77BBE12A9E0F70006CC98B /* Scripts.swift */; }; DE77BBF02A9E38C6006CC98B /* GetUserProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE77BBA92A9DE680006CC98B /* GetUserProfile.swift */; }; @@ -90,6 +90,14 @@ DE94570C2A9E69EB00B0B768 /* ScriptExcuteButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE94570B2A9E69EB00B0B768 /* ScriptExcuteButton.swift */; }; DE94570F2A9E69F200B0B768 /* ScriptDeleteButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE94570E2A9E69F200B0B768 /* ScriptDeleteButton.swift */; }; DE9457162A9E6D3000B0B768 /* ScriptQuickSlotButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9457112A9E6A0000B0B768 /* ScriptQuickSlotButton.swift */; }; + DE9457192A9EEEF000B0B768 /* ScriptCellManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9457182A9EEEF000B0B768 /* ScriptCellManager.swift */; }; + DE94571C2A9EFB7800B0B768 /* Associated+NSButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE94571B2A9EFB7800B0B768 /* Associated+NSButton.swift */; }; + DE9457282A9F6E4400B0B768 /* GetUserMeScripts.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9457272A9F6E4400B0B768 /* GetUserMeScripts.swift */; }; + DE94572C2A9F75D500B0B768 /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE94572B2A9F75D500B0B768 /* API.swift */; }; + DE9457312A9FB01400B0B768 /* PreferencesTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9457302A9FB01400B0B768 /* PreferencesTableView.swift */; }; + DE9457342A9FB1D300B0B768 /* RequestAccessView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9457332A9FB1D300B0B768 /* RequestAccessView.swift */; }; + DE9457372A9FC0A800B0B768 /* SecurityScopedResourceAccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9457362A9FC0A800B0B768 /* SecurityScopedResourceAccess.swift */; }; + DE94573D2A9FD33400B0B768 /* StorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE94573C2A9FD33400B0B768 /* StorageView.swift */; }; DE97CA692A9A6364001073DE /* PixelConversion+CGFloat.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE97CA682A9A6364001073DE /* PixelConversion+CGFloat.swift */; }; DE97CA792A9A6F6A001073DE /* QuickSlotHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE97CA782A9A6F6A001073DE /* QuickSlotHeaderView.swift */; }; DE97CA7C2A9A7199001073DE /* QuickSlotGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE97CA7B2A9A7199001073DE /* QuickSlotGroupView.swift */; }; @@ -171,7 +179,7 @@ DE77BA552A82637900713683 /* StateManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateManager.swift; sourceTree = ""; }; DE77BBA12A9DDC40006CC98B /* ScriptsFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptsFileManager.swift; sourceTree = ""; }; DE77BBA92A9DE680006CC98B /* GetUserProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetUserProfile.swift; sourceTree = ""; }; - DE77BBCC2A9E0568006CC98B /* ExcuteScripts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExcuteScripts.swift; sourceTree = ""; }; + DE77BBCC2A9E0568006CC98B /* ExecuteScripts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExecuteScripts.swift; sourceTree = ""; }; DE77BBD12A9E0AE8006CC98B /* WebViewUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewUI.swift; sourceTree = ""; }; DE77BBE12A9E0F70006CC98B /* Scripts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Scripts.swift; sourceTree = ""; }; DE77BBE92A9E2DDF006CC98B /* UserManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserManager.swift; sourceTree = ""; }; @@ -193,6 +201,14 @@ DE94570B2A9E69EB00B0B768 /* ScriptExcuteButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptExcuteButton.swift; sourceTree = ""; }; DE94570E2A9E69F200B0B768 /* ScriptDeleteButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptDeleteButton.swift; sourceTree = ""; }; DE9457112A9E6A0000B0B768 /* ScriptQuickSlotButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptQuickSlotButton.swift; sourceTree = ""; }; + DE9457182A9EEEF000B0B768 /* ScriptCellManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptCellManager.swift; sourceTree = ""; }; + DE94571B2A9EFB7800B0B768 /* Associated+NSButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Associated+NSButton.swift"; sourceTree = ""; }; + DE9457272A9F6E4400B0B768 /* GetUserMeScripts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetUserMeScripts.swift; sourceTree = ""; }; + DE94572B2A9F75D500B0B768 /* API.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = API.swift; sourceTree = ""; }; + DE9457302A9FB01400B0B768 /* PreferencesTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesTableView.swift; sourceTree = ""; }; + DE9457332A9FB1D300B0B768 /* RequestAccessView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestAccessView.swift; sourceTree = ""; }; + DE9457362A9FC0A800B0B768 /* SecurityScopedResourceAccess.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecurityScopedResourceAccess.swift; sourceTree = ""; }; + DE94573C2A9FD33400B0B768 /* StorageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageView.swift; sourceTree = ""; }; DE97CA682A9A6364001073DE /* PixelConversion+CGFloat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PixelConversion+CGFloat.swift"; sourceTree = ""; }; DE97CA6E2A9A6EFC001073DE /* QuickSlotViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickSlotViewModel.swift; sourceTree = ""; }; DE97CA712A9A6F0D001073DE /* QuickSlotButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickSlotButtonModel.swift; sourceTree = ""; }; @@ -420,6 +436,7 @@ DE1F1A282A8B50E200A88DD8 /* BoxSizeManager.swift */, DE77BA552A82637900713683 /* StateManager.swift */, DE0A91622A8E6A5400D1D6F1 /* Constants.swift */, + DE9457362A9FC0A800B0B768 /* SecurityScopedResourceAccess.swift */, ); path = Shared; sourceTree = ""; @@ -428,6 +445,8 @@ isa = PBXGroup; children = ( DE77BBA92A9DE680006CC98B /* GetUserProfile.swift */, + DE9457272A9F6E4400B0B768 /* GetUserMeScripts.swift */, + DE94572B2A9F75D500B0B768 /* API.swift */, ); path = API; sourceTree = ""; @@ -496,7 +515,7 @@ children = ( DE0A917A2A8F0CA800D1D6F1 /* AppleScripts+ShowMessage.swift */, DE77BBA12A9DDC40006CC98B /* ScriptsFileManager.swift */, - DE77BBCC2A9E0568006CC98B /* ExcuteScripts.swift */, + DE77BBCC2A9E0568006CC98B /* ExecuteScripts.swift */, DE7886272A9D186700FE21DD /* ScriptsViewController.swift */, ); path = Controller; @@ -505,7 +524,8 @@ DE78862B2A9D1A9E00FE21DD /* View */ = { isa = PBXGroup; children = ( - DE78862C2A9D1ADE00FE21DD /* PreferencesCell.swift */, + DE9457412A9FD69800B0B768 /* Table */, + DE94573F2A9FD68C00B0B768 /* Funtion */, ); path = View; sourceTree = ""; @@ -528,8 +548,7 @@ DE2AD3282A824EEB00002D51 /* Accessibility.swift */, DE874F4D2A591DEA00FC3B77 /* Hotkey.swift */, ); - name = Preferences; - path = QuickSlot/Preferences; + path = Preferences; sourceTree = ""; }; DE9456F62A9E44F100B0B768 /* Icon */ = { @@ -566,6 +585,25 @@ children = ( DE7886032A9C71CB00FE21DD /* ScriptsTableView.swift */, DE9456FF2A9E5B0900B0B768 /* ScriptCell.swift */, + DE9457182A9EEEF000B0B768 /* ScriptCellManager.swift */, + ); + path = Table; + sourceTree = ""; + }; + DE94573F2A9FD68C00B0B768 /* Funtion */ = { + isa = PBXGroup; + children = ( + DE9457332A9FB1D300B0B768 /* RequestAccessView.swift */, + DE94573C2A9FD33400B0B768 /* StorageView.swift */, + ); + path = Funtion; + sourceTree = ""; + }; + DE9457412A9FD69800B0B768 /* Table */ = { + isa = PBXGroup; + children = ( + DE78862C2A9D1ADE00FE21DD /* PreferencesCell.swift */, + DE9457302A9FB01400B0B768 /* PreferencesTableView.swift */, ); path = Table; sourceTree = ""; @@ -687,6 +725,7 @@ DE24E63A2A8FE93900E29F5D /* NSImage.swift */, DE98E83A2A98DB6000F8744A /* RotateImage+NSImage.swift */, DE97CA682A9A6364001073DE /* PixelConversion+CGFloat.swift */, + DE94571B2A9EFB7800B0B768 /* Associated+NSButton.swift */, ); path = Extensions; sourceTree = ""; @@ -785,8 +824,10 @@ DE4408082A9240300091937A /* BoxFunctionButtonView.swift in Sources */, DE3FF3742A978AB8009C88EF /* WindowMaximizeButton.swift in Sources */, DE3FF3A32A97D2A6009C88EF /* DisplayURLTextfield.swift in Sources */, + DE9457192A9EEEF000B0B768 /* ScriptCellManager.swift in Sources */, DE9457002A9E5B0900B0B768 /* ScriptCell.swift in Sources */, DE7886042A9C71CB00FE21DD /* ScriptsTableView.swift in Sources */, + DE9457312A9FB01400B0B768 /* PreferencesTableView.swift in Sources */, DE97CA872A9A7407001073DE /* QuickSlotButtonModel.swift in Sources */, DE77BA562A82637900713683 /* StateManager.swift in Sources */, DE1F1A1C2A8B50C500A88DD8 /* BoxBaseContainerViewController.swift in Sources */, @@ -794,10 +835,12 @@ DE018BE72A509B1E00FF0AA3 /* WebViewController.swift in Sources */, DE98E8552A98EA7900F8744A /* WindowButtonUI.swift in Sources */, DE4407FA2A923E860091937A /* BoxFunctionViewController.swift in Sources */, + DE94572C2A9F75D500B0B768 /* API.swift in Sources */, DE4407FE2A923EA90091937A /* PreferenceButtonView.swift in Sources */, DE77BBF02A9E38C6006CC98B /* GetUserProfile.swift in Sources */, DEF749322A85657600D987C8 /* NSScreen.swift in Sources */, DE018BF02A509B2F00FF0AA3 /* MenubarViewController.swift in Sources */, + DE94571C2A9EFB7800B0B768 /* Associated+NSButton.swift in Sources */, DE6332E42A9BB8F800DCFAF6 /* QuickSlotButtonCollectionViewController.swift in Sources */, DE9457062A9E69C100B0B768 /* ScriptNameLabel.swift in Sources */, DE77BA512A82580400713683 /* MenubarViewModel.swift in Sources */, @@ -811,22 +854,26 @@ DE77BBA62A9DDF2B006CC98B /* WebView.swift in Sources */, DE0A91832A8F889000D1D6F1 /* GoHomePageViaToolbar().swift in Sources */, DE6332F22A9BCA2C00DCFAF6 /* QuickSlotScriptsLogicController.swift in Sources */, + DE9457342A9FB1D300B0B768 /* RequestAccessView.swift in Sources */, DE018BB32A5099F900FF0AA3 /* AppDelegate.swift in Sources */, DE78860C2A9C770300FE21DD /* ScriptsViewModel.swift in Sources */, DE0A91632A8E6A5400D1D6F1 /* Constants.swift in Sources */, DE7886172A9CCB3B00FE21DD /* UserProfile.swift in Sources */, DE0A91902A8F88CA00D1D6F1 /* DisplayURLInToolbar.swift in Sources */, - DE77BBCD2A9E0568006CC98B /* ExcuteScripts.swift in Sources */, + DE77BBCD2A9E0568006CC98B /* ExecuteScripts.swift in Sources */, DE9456F82A9E44FD00B0B768 /* IconController.swift in Sources */, DE018BF32A509B3300FF0AA3 /* MenubarModel.swift in Sources */, DE7A257A2A6D8CA20043225A /* PreferencesViewController.swift in Sources */, DE24E63B2A8FE93900E29F5D /* NSImage.swift in Sources */, DE94570F2A9E69F200B0B768 /* ScriptDeleteButton.swift in Sources */, DE97CA792A9A6F6A001073DE /* QuickSlotHeaderView.swift in Sources */, + DE9457372A9FC0A800B0B768 /* SecurityScopedResourceAccess.swift in Sources */, DE0A916D2A8E7DD700D1D6F1 /* HoverButton.swift in Sources */, DE97CA862A9A7404001073DE /* QuickSlotViewModel.swift in Sources */, DE3FF3762A978AB8009C88EF /* WindowCloseButton.swift in Sources */, DE9457092A9E69D100B0B768 /* ScriptDescriptionsLabel.swift in Sources */, + DE94573D2A9FD33400B0B768 /* StorageView.swift in Sources */, + DE9457282A9F6E4400B0B768 /* GetUserMeScripts.swift in Sources */, DE77BBD22A9E0AE8006CC98B /* WebViewUI.swift in Sources */, DE3FF3772A978AB8009C88EF /* WindowMinimizeButton.swift in Sources */, DE4408022A923EB60091937A /* PinButtonView.swift in Sources */, diff --git a/Box42/Extensions/Associated+NSButton.swift b/Box42/Extensions/Associated+NSButton.swift new file mode 100644 index 0000000..f6d610f --- /dev/null +++ b/Box42/Extensions/Associated+NSButton.swift @@ -0,0 +1,21 @@ +// +// Associated+NSButton.swift +// Box42 +// +// Created by Chanhee Kim on 8/30/23. +// + +import AppKit + +private var AssociatedObjectKey: UInt8 = 0 + +extension NSButton { + var associatedString: String? { + get { + return objc_getAssociatedObject(self, &AssociatedObjectKey) as? String + } + set(newValue) { + objc_setAssociatedObject(self, &AssociatedObjectKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } +} diff --git a/Box42/FunctionButton/BoxFunctionViewController.swift b/Box42/FunctionButton/BoxFunctionViewController.swift index 42e3396..0c1a9b3 100644 --- a/Box42/FunctionButton/BoxFunctionViewController.swift +++ b/Box42/FunctionButton/BoxFunctionViewController.swift @@ -8,9 +8,13 @@ import Cocoa class BoxFunctionViewController: NSViewController { + + var pinButtonView: PinButtonView? + override func loadView() { let functionViewGroup = BoxFunctionViewGroup() + pinButtonView = functionViewGroup.pinButton functionViewGroup.preferenceAction = preference functionViewGroup.pinAction = pin functionViewGroup.quitAction = quit @@ -28,6 +32,16 @@ class BoxFunctionViewController: NSViewController { } func pin() { + StateManager.shared.togglePin() + + let newImage: NSImage + if StateManager.shared.pin { + newImage = NSImage(imageLiteralResourceName: "pin-box-ver") + } else { + newImage = NSImage(imageLiteralResourceName: "pin-box") + } + + pinButtonView?.changePinImage(to: newImage) // 이미지 변경 print("pin") } @@ -37,7 +51,7 @@ class BoxFunctionViewController: NSViewController { } weak var delegate: BoxFunctionViewControllerDelegate? - + func box() { print("box") delegate?.didTapBoxButton() diff --git a/Box42/FunctionButton/View/PinButtonView.swift b/Box42/FunctionButton/View/PinButtonView.swift index 684e3be..9c9db8b 100644 --- a/Box42/FunctionButton/View/PinButtonView.swift +++ b/Box42/FunctionButton/View/PinButtonView.swift @@ -49,6 +49,10 @@ class PinButtonView: NSView { fatalError("init(coder:) has not been implemented") } + func changePinImage(to image: NSImage) { + pinBoxButton.image = image + } + @objc func pin() { callback?() } diff --git a/Box42/Main/keyDown+BoxBaseContainerViewController.swift b/Box42/Main/keyDown+BoxBaseContainerViewController.swift index f16fcfc..f73306a 100644 --- a/Box42/Main/keyDown+BoxBaseContainerViewController.swift +++ b/Box42/Main/keyDown+BoxBaseContainerViewController.swift @@ -22,6 +22,10 @@ extension BoxBaseContainerViewController { StorageConfig.shared.setPeriod(.period1s) } + if event.modifierFlags.contains(.command) && event.keyCode == 15 { + print("Cmd + R pressed, reloading...") + WebViewManager.shared.hostingWebView?.reload() + } if event.keyCode == 53 { // Escape 키의 keyCode는 53입니다. print("escape") diff --git a/Box42/QuickSlot/Preferences/Accessibility.swift b/Box42/Preferences/Accessibility.swift similarity index 100% rename from Box42/QuickSlot/Preferences/Accessibility.swift rename to Box42/Preferences/Accessibility.swift diff --git a/Box42/Preferences/Controller/PreferencesViewController.swift b/Box42/Preferences/Controller/PreferencesViewController.swift new file mode 100644 index 0000000..7dc8692 --- /dev/null +++ b/Box42/Preferences/Controller/PreferencesViewController.swift @@ -0,0 +1,35 @@ +// +// PreferencesViewController.swift +// Box42 +// +// Created by Chanhee Kim on 7/24/23. +// + +import Cocoa +import SnapKit + +class PreferencesViewController: NSViewController { + var prefTableView : PreferencesTableView? + + override func loadView() { + self.view = NSView() + self.view.wantsLayer = true + self.view.layer?.backgroundColor = NSColor.blue.cgColor + + prefTableView = PreferencesTableView(frame: .zero) + prefTableView?.setup() +// prefTableView?.viewModel = viewModel + + let scrollView = NSScrollView() + scrollView.documentView = prefTableView + self.view.addSubview(scrollView) + + scrollView.snp.makeConstraints({ make in + make.edges.equalToSuperview() + }) + + prefTableView?.snp.makeConstraints({ make in + make.edges.equalToSuperview() + }) + } +} diff --git a/Box42/QuickSlot/Preferences/Hotkey.swift b/Box42/Preferences/Hotkey.swift similarity index 100% rename from Box42/QuickSlot/Preferences/Hotkey.swift rename to Box42/Preferences/Hotkey.swift diff --git a/Box42/QuickSlot/Preferences/PreferencesView.swift b/Box42/Preferences/PreferencesView.swift similarity index 100% rename from Box42/QuickSlot/Preferences/PreferencesView.swift rename to Box42/Preferences/PreferencesView.swift diff --git a/Box42/Preferences/PreferencesViewController.swift b/Box42/Preferences/PreferencesViewController.swift deleted file mode 100644 index e132672..0000000 --- a/Box42/Preferences/PreferencesViewController.swift +++ /dev/null @@ -1,147 +0,0 @@ -// -// PreferencesViewController.swift -// Box42 -// -// Created by Chanhee Kim on 7/24/23. -// - -import Cocoa -import Foundation - -class PreferencesViewController: NSViewController { - let menubarVC = MenubarViewController() - private var stackView: NSStackView! - private var rightView: NSView! - private var outputView: NSTextView! - - override func loadView() { - self.view = NSView() - self.stackView = NSStackView() - self.stackView.orientation = .vertical - self.stackView.distribution = .fillEqually - self.stackView.spacing = 20 - self.view.addSubview(stackView) - stackView.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - stackView.topAnchor.constraint(equalTo: self.view.topAnchor), - stackView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), - stackView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), - stackView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor) - ]) - - let leftView = NSView() - self.rightView = NSView() - self.stackView.addArrangedSubview(leftView) - self.stackView.addArrangedSubview(rightView) - - outputView = NSTextView() - outputView.translatesAutoresizingMaskIntoConstraints = false - rightView.addSubview(outputView) - - NSLayoutConstraint.activate([ - outputView.topAnchor.constraint(equalTo: rightView.topAnchor), - outputView.leadingAnchor.constraint(equalTo: rightView.leadingAnchor), - outputView.trailingAnchor.constraint(equalTo: rightView.trailingAnchor), - outputView.bottomAnchor.constraint(equalTo: rightView.bottomAnchor) - ]) - - - var stackBox: [NSView] = [] - - - let scripts = Scripts().info - scripts.forEach { (script) in - stackBox.append(NSButton(title: "\(script.name) Script: \(script.description)", target: self, action: #selector(scriptButtonPressed))) - - } - -// let scriptButton = NSButton(title: "Script", target: self, action: #selector(scriptButtonPressed)) - let appleScriptButton = NSButton(title: "Apple Script", target: self, action: #selector(scriptButtonPressed)) - let etcButton = NSButton(title: "Etc.", target: self, action: #selector(etcButtonPressed)) - -// stackBox.append(scriptButton) - stackBox.append(appleScriptButton) - stackBox.append(etcButton) - let buttonStackView = NSStackView(views: stackBox) - buttonStackView.orientation = .vertical - buttonStackView.distribution = .fillEqually - buttonStackView.spacing = 20 - leftView.addSubview(buttonStackView) - buttonStackView.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - buttonStackView.topAnchor.constraint(equalTo: leftView.topAnchor), - buttonStackView.leadingAnchor.constraint(equalTo: leftView.leadingAnchor), - buttonStackView.trailingAnchor.constraint(equalTo: leftView.trailingAnchor), - buttonStackView.bottomAnchor.constraint(equalTo: leftView.bottomAnchor) - ]) - } - - @objc func changeIconButtonPressed(_ sender: NSButton) { - // Change the content of the right view for icon changing - let icon = sender.title.split(separator: " ").map{String($0)} - print(icon[1]) - menubarVC.menubarStopRunning() - menubarVC.buttonImageChange(icon[1]) - menubarVC.menubarStartRunning() - } - - @objc func scriptButtonPressed(_ sender: NSButton) { - let script = sender.title.split(separator: " ").map{String($0)} - if script[1] == "Script:" { - if let scriptPath = Bundle.main.path(forResource: script[0], ofType: "sh") { - let task = Process() - task.launchPath = "/bin/sh" - task.arguments = [scriptPath] - - let outputPipe = Pipe() - task.standardOutput = outputPipe - task.standardError = outputPipe - - - task.standardError = outputPipe - -// outputPipe.fileHandleForReading.readabilityHandler = { [weak self] fileHandle in -// if #available(OSX 10.15.4, *) { -// if let data = try? fileHandle.readToEnd(), let output = String(data: data, encoding: .utf8) { -// DispatchQueue.main.async { -// if let outputView = self?.outputView { -// outputView.string += "\(output)" -// } else { -// print("outputView is nil") -// } -// } -// } -// } else { -// // Fallback on earlier versions -// } -// } - - - task.launch() - task.waitUntilExit() - -// let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile() -// let output = String(data: outputData, encoding: .utf8) ?? "" -// print("Output: \(output)") - } else { - print("Script not found") - } - } else if sender.title == "Apple Script" { - let appleScriptCode = "display dialog \"Hello, World!\"" - - if let appleScript = NSAppleScript(source: appleScriptCode) { - var errorDict: NSDictionary? = nil - appleScript.executeAndReturnError(&errorDict) - - if let error = errorDict { - print("Error: \(error)") - } - } - } - } - - @objc func etcButtonPressed() { - // Change the content of the right view for etc. - } -} - diff --git a/Box42/Preferences/View/Funtion/RequestAccessView.swift b/Box42/Preferences/View/Funtion/RequestAccessView.swift new file mode 100644 index 0000000..0062579 --- /dev/null +++ b/Box42/Preferences/View/Funtion/RequestAccessView.swift @@ -0,0 +1,136 @@ +// +// RequestAccessView.swift +// Box42 +// +// Created by Chanhee Kim on 8/31/23. +// + +import AppKit +import SnapKit + +class RequestAccessView: NSView { + var requestAccessTextField: NSTextField = NSTextField() + var grantAccessButton: NSButton = NSButton() + var revokeAccessButton: NSButton = NSButton() + var directoryNameTextField: NSTextField = NSTextField() + + override init(frame frameRect: NSRect) { + super.init(frame: .zero) + self.wantsLayer = true + self.layer?.backgroundColor = NSColor(hex: "#7FFFFFFF").cgColor + self.layer?.cornerRadius = 13 + + // Add subviews + self.addSubview(requestAccessTextField) + self.addSubview(grantAccessButton) + self.addSubview(revokeAccessButton) + self.addSubview(directoryNameTextField) + + // Initialize UI elements + textfieldInit() + buttonInit() + directoryNameTextFieldInit() + + // Set constraints + textfieldConstraints() + buttonConstraints() + directoryNameTextFieldConstraints() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func textfieldInit() { + requestAccessTextField.stringValue = "Script 및 기능들을 실행하기 위해서 루트 디렉토리의 권한이 필요합니다." + requestAccessTextField.font = NSFont.systemFont(ofSize: 15) + requestAccessTextField.isEditable = false + requestAccessTextField.isBordered = false + requestAccessTextField.backgroundColor = NSColor.clear + requestAccessTextField.lineBreakMode = .byWordWrapping + requestAccessTextField.maximumNumberOfLines = 3 + } + + func buttonInit() { + grantAccessButton.title = "권한 부여" + grantAccessButton.target = self + grantAccessButton.action = #selector(requestFolderAccess(_:)) + + revokeAccessButton.title = "권한 취소" + revokeAccessButton.target = self + revokeAccessButton.action = #selector(revokeFolderAccess(_:)) + } + + func directoryNameTextFieldInit() { + directoryNameTextField.stringValue = "선택된 디렉터리: 없음" + directoryNameTextField.font = NSFont.systemFont(ofSize: 15) + directoryNameTextField.isEditable = false + directoryNameTextField.isBordered = false + directoryNameTextField.backgroundColor = NSColor.clear + directoryNameTextField.lineBreakMode = .byWordWrapping + } + + func directoryNameTextFieldConstraints() { + directoryNameTextField.snp.makeConstraints { make in + make.top.equalTo(revokeAccessButton.snp.bottom).offset(10) + make.leading.equalToSuperview().offset(17) + make.trailing.equalToSuperview().offset(-17) + } + } + + func textfieldConstraints() { + requestAccessTextField.snp.makeConstraints { make in + make.top.equalToSuperview().offset(10) + make.leading.equalToSuperview().offset(17) + make.trailing.equalToSuperview().offset(-17) + } + } + + func buttonConstraints() { + grantAccessButton.snp.makeConstraints { make in + make.top.equalTo(requestAccessTextField.snp.bottom).offset(10) + make.leading.equalToSuperview().offset(17) + } + + revokeAccessButton.snp.makeConstraints { make in + make.top.equalTo(requestAccessTextField.snp.bottom).offset(10) + make.leading.equalTo(grantAccessButton.snp.trailing).offset(10) + } + } + + @objc func requestFolderAccess(_ sender: NSButton) { + let openPanel = NSOpenPanel() + openPanel.title = "Choose a folder" + openPanel.showsResizeIndicator = true + openPanel.showsHiddenFiles = false + openPanel.canChooseDirectories = true + openPanel.canCreateDirectories = true + openPanel.allowsMultipleSelection = false + openPanel.canChooseFiles = false + + if openPanel.runModal() == NSApplication.ModalResponse.OK { + let result = openPanel.url + + if let result = result { + print("Selected folder is \(result.path)") + + directoryNameTextField.stringValue = "선택된 디렉터리: \(result.path)" + + do { + let bookmarkData = try result.bookmarkData(options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil) + UserDefaults.standard.set(bookmarkData, forKey: "bookmarkData") + } catch { + print("Error creating bookmark: \(error)") + } + } + } else { + // User clicked on "Cancel" + return + } + } + + @objc func revokeFolderAccess(_ sender: NSButton) { + // TODO: Add code to revoke folder access + } + +} diff --git a/Box42/Preferences/View/Funtion/StorageView.swift b/Box42/Preferences/View/Funtion/StorageView.swift new file mode 100644 index 0000000..77650b6 --- /dev/null +++ b/Box42/Preferences/View/Funtion/StorageView.swift @@ -0,0 +1,112 @@ +// +// StorageView.swift +// Box42 +// +// Created by Chanhee Kim on 8/31/23. +// +import AppKit +import SnapKit + +class StorageView: NSView { + + var currentStorageTextField: NSTextField = NSTextField() + var remainingStorageTextField: NSTextField = NSTextField() + var totalStorageTextField: NSTextField = NSTextField() + var thresholdTextField: NSTextField = NSTextField() + var intervalTextField: NSTextField = NSTextField() + var executeScriptButton: NSButton = NSButton() + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + + // Add subviews + addSubview(currentStorageTextField) + addSubview(remainingStorageTextField) + addSubview(totalStorageTextField) + addSubview(thresholdTextField) + addSubview(intervalTextField) + addSubview(executeScriptButton) + + // Initialize UI elements + initTextFields() + initButton() + + // Set constraints + setConstraints() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func initTextFields() { + // Initialize textfields + currentStorageTextField.stringValue = "Current Storage: ?? GB" + remainingStorageTextField.stringValue = "Remaining Storage: ?? GB" + totalStorageTextField.stringValue = "Total Storage: ?? GB" + thresholdTextField.placeholderString = "Enter threshold (%)" + intervalTextField.placeholderString = "Enter interval (seconds)" + + [currentStorageTextField, remainingStorageTextField, totalStorageTextField, thresholdTextField, intervalTextField].forEach { textField in + textField.isEditable = false + textField.isBordered = false + textField.backgroundColor = NSColor.clear + } + + intervalTextField.isEditable = true + thresholdTextField.isEditable = true + } + + func initButton() { + executeScriptButton.title = "Run Script" + executeScriptButton.target = self + executeScriptButton.action = #selector(runScript(_:)) + } + + func setConstraints() { + // Use SnapKit to set constraints + currentStorageTextField.snp.makeConstraints { make in + make.top.leading.equalToSuperview().offset(20) + } + + remainingStorageTextField.snp.makeConstraints { make in + make.top.equalTo(currentStorageTextField.snp.bottom).offset(10) + make.leading.equalToSuperview().offset(20) + } + + totalStorageTextField.snp.makeConstraints { make in + make.top.equalTo(remainingStorageTextField.snp.bottom).offset(10) + make.leading.equalToSuperview().offset(20) + } + + thresholdTextField.snp.makeConstraints { make in + make.top.equalTo(totalStorageTextField.snp.bottom).offset(10) + make.leading.equalToSuperview().offset(20) + make.height.equalTo(30) + make.width.equalTo(100) + } + + intervalTextField.snp.makeConstraints { make in + make.top.equalTo(thresholdTextField.snp.bottom).offset(10) + make.leading.equalToSuperview().offset(20) + make.height.equalTo(30) + make.width.equalTo(100) + } + + executeScriptButton.snp.makeConstraints { make in + make.top.equalTo(intervalTextField.snp.bottom).offset(20) + make.leading.equalToSuperview().offset(20) + } + } + + @objc func runScript(_ sender: NSButton) { + // Logic to run script + } + + // Update storage information + func updateStorageInfo(current: String, remaining: String, total: String) { + currentStorageTextField.stringValue = "Current Storage: \(current) GB" + remainingStorageTextField.stringValue = "Remaining Storage: \(remaining) GB" + totalStorageTextField.stringValue = "Total Storage: \(total) GB" + } +} diff --git a/Box42/QuickSlot/Preferences/View/PreferencesCell.swift b/Box42/Preferences/View/Table/PreferencesCell.swift similarity index 100% rename from Box42/QuickSlot/Preferences/View/PreferencesCell.swift rename to Box42/Preferences/View/Table/PreferencesCell.swift diff --git a/Box42/Preferences/View/Table/PreferencesTableView.swift b/Box42/Preferences/View/Table/PreferencesTableView.swift new file mode 100644 index 0000000..2c0ec06 --- /dev/null +++ b/Box42/Preferences/View/Table/PreferencesTableView.swift @@ -0,0 +1,86 @@ +// +// PreferencesTableView.swift +// Box42 +// +// Created by Chanhee Kim on 8/31/23. +// + +import AppKit +import SnapKit +import Combine + +enum PreferencesCellList: CaseIterable { + case requestAccessView + case storage + case cpu + case my + + var height: CGFloat { + switch self { + case .requestAccessView: + return 100.0 + case .storage: + return 300.0 + case .cpu: + return 40.0 + case .my: + return 50.0 + } + } +} + +class PreferencesTableView: NSTableView { + let requestAccessView = RequestAccessView() + let storageView = StorageView() + + func setup() { + self.delegate = self + self.dataSource = self + + let column1 = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("Preferences")) + column1.width = 100.0 + column1.title = "Preferences" + self.addTableColumn(column1) + + self.selectionHighlightStyle = .none + } + +} + +extension PreferencesTableView: NSTableViewDelegate, NSTableViewDataSource { + func getCellForRow(at row: Int) -> NSView { + let allCases = PreferencesCellList.allCases + if row >= 0 && row < allCases.count { + switch allCases[row] { + case .requestAccessView: + return requestAccessView + case .storage: + return StorageView() + case .cpu: + // Return the view for the CPU cell + return NSView() // Placeholder + case .my: + // Return the view for the "my" cell + return NSView() // Placeholder + } + } + return NSView() // Default view if out of bounds or undefined + } + + func numberOfRows(in tableView: NSTableView) -> Int { + return PreferencesCellList.allCases.count + } + + func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { + return getCellForRow(at: row) + } + + func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { + let allCases = PreferencesCellList.allCases + if row >= 0 && row < allCases.count { + return allCases[row].height + } + + return 44.0 // Default height + } +} diff --git a/Box42/QuickSlot/Controller/QuickSlotScriptsLogicController.swift b/Box42/QuickSlot/Controller/QuickSlotScriptsLogicController.swift index 41bc732..6c2ada1 100644 --- a/Box42/QuickSlot/Controller/QuickSlotScriptsLogicController.swift +++ b/Box42/QuickSlot/Controller/QuickSlotScriptsLogicController.swift @@ -17,30 +17,23 @@ class ScriptsLogicController { @objc func handleButtonTapped(notification: NSNotification) { if let button = notification.object as? NSButton { - let buttonTitle = button.title - print("Button with title \(buttonTitle) was tapped") - - DispatchQueue.global(qos: .background).async { [weak self] in - if buttonTitle == QuickSlotUI.title.clean { - self?.executeCleanScript() - } - } + SecurityScopedResourceAccess.accessResourceExecuteShellScript(scriptPath: button.associatedString ?? "") } } - + private func executeCleanScript() { if let scriptPath = Bundle.main.path(forResource: "cleanCache", ofType: "sh") { let task = Process() task.launchPath = "/bin/sh" task.arguments = [scriptPath] - + let outputPipe = Pipe() task.standardOutput = outputPipe task.standardError = outputPipe - + task.launch() task.waitUntilExit() - + let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile() let output = String(data: outputData, encoding: .utf8) ?? "" diff --git a/Box42/QuickSlot/Model/QuickSlotButtonModel.swift b/Box42/QuickSlot/Model/QuickSlotButtonModel.swift index 2929b82..b965db8 100644 --- a/Box42/QuickSlot/Model/QuickSlotButtonModel.swift +++ b/Box42/QuickSlot/Model/QuickSlotButtonModel.swift @@ -11,9 +11,11 @@ import Foundation struct QuickSlotButtonModel { let id: UUID var title: String + var path: String? - init(id: UUID = UUID(), title: String = "Default") { + init(id: UUID = UUID(), title: String = "Default", path: String? = nil) { self.id = id self.title = title + self.path = path } } diff --git a/Box42/QuickSlot/Preferences/Controller/PreferencesViewController.swift b/Box42/QuickSlot/Preferences/Controller/PreferencesViewController.swift deleted file mode 100644 index 1fede64..0000000 --- a/Box42/QuickSlot/Preferences/Controller/PreferencesViewController.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// PreferencesViewController.swift -// Box42 -// -// Created by Chanhee Kim on 7/24/23. -// - -import Cocoa -import SnapKit - -class PreferencesViewController: NSViewController { - var prefTableView : NSTableView? - - override func loadView() { - self.view = NSView() - self.view.wantsLayer = true - self.view.layer?.backgroundColor = NSColor.blue.cgColor - - prefTableView = NSTableView(frame: .zero) - // Column 추가 - let column1 = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("Column1")) - column1.width = 100.0 - column1.title = "Column 1" - prefTableView?.addTableColumn(column1) - - // delegate와 dataSource 설정 - prefTableView?.delegate = self - prefTableView?.dataSource = self - - // TableView를 스크롤 뷰에 추가 (일반적으로 NSTableView는 NSScrollView 안에 위치합니다) - let scrollView = NSScrollView() - scrollView.documentView = prefTableView - self.view.addSubview(scrollView) - - - scrollView.snp.makeConstraints({ make in - make.edges.equalToSuperview() - }) - - prefTableView?.snp.makeConstraints({ make in - make.edges.equalToSuperview() - }) - } -} - -extension PreferencesViewController: NSTableViewDelegate, NSTableViewDataSource { - func numberOfRows(in tableView: NSTableView) -> Int { - return 10 // 총 로우 수 - } - - func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { - let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier("MyCell"), owner: self) as? NSTableCellView ?? NSTableCellView() - cell.textField?.stringValue = "Row \(row), Column \(tableColumn?.identifier ?? NSUserInterfaceItemIdentifier(""))" - return cell - } - - func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { - return 44.0 // 셀 높이를 44로 설정 - } -} - diff --git a/Box42/QuickSlot/View/ButtonCollectionView/QuickSlotButtonCollectionViewController.swift b/Box42/QuickSlot/View/ButtonCollectionView/QuickSlotButtonCollectionViewController.swift index 52b2697..c5d60bc 100644 --- a/Box42/QuickSlot/View/ButtonCollectionView/QuickSlotButtonCollectionViewController.swift +++ b/Box42/QuickSlot/View/ButtonCollectionView/QuickSlotButtonCollectionViewController.swift @@ -97,6 +97,7 @@ extension QuickSlotButtonCollectionViewController: NSCollectionViewDelegate, NSC btn.layer?.backgroundColor = NSColor.red.cgColor customItem.view.addSubview(btn) btn.frame = CGRect(x: 0, y: 0, width: QuickSlotUI.size.button, height: QuickSlotUI.size.button) + btn.associatedString = buttonModel.path } return item } diff --git a/Box42/QuickSlot/ViewModel/QuickSlotViewModel.swift b/Box42/QuickSlot/ViewModel/QuickSlotViewModel.swift index 0d16006..d636599 100644 --- a/Box42/QuickSlot/ViewModel/QuickSlotViewModel.swift +++ b/Box42/QuickSlot/ViewModel/QuickSlotViewModel.swift @@ -13,7 +13,8 @@ class QuickSlotViewModel { @Published var buttons: [QuickSlotButtonModel] = [] private init() { - let button1 = QuickSlotButtonModel(title: QuickSlotUI.title.clean) + let button1 = QuickSlotButtonModel(title: QuickSlotUI.title.clean, + path: Bundle.main.path(forResource: "cleanCache", ofType: "sh")) let button2 = QuickSlotButtonModel(title: QuickSlotUI.title.preferences) let button3 = QuickSlotButtonModel(title: QuickSlotUI.title.scripts) let button4 = QuickSlotButtonModel(title: QuickSlotUI.title.user) @@ -21,8 +22,8 @@ class QuickSlotViewModel { buttons = [button1, button2, button3, button4] } + // 퀵슬롯 안에 해당 버튼이 없으면 추가 func addButton(_ button: QuickSlotButtonModel) { - if buttons.count > 7 { return } if !buttons.contains(where: { $0.id == button.id }) { buttons.append(button) } diff --git a/Box42/Resources/AppDelegate.swift b/Box42/Resources/AppDelegate.swift index 5555c9b..d674746 100644 --- a/Box42/Resources/AppDelegate.swift +++ b/Box42/Resources/AppDelegate.swift @@ -23,14 +23,16 @@ class AppDelegate: NSObject, NSApplicationDelegate { // alertAccessibility() // hotkey() - // storage.storageTimerEvent() +// storage.storageTimerEvent() _ = UserManager.shared _ = ScriptsLogicController.shared // MARK: - 유저데이터 동기화 -// WebViewManager.shared.getCookie() -// API.getUserProfile(WebViewManager.shared.getCookieWebKit) + WebViewManager.shared.getCookie() + API.getUserProfile(WebViewManager.shared.getCookieWebKit) _ = QuickSlotViewModel.shared + API.getUserMeScripts(WebViewManager.shared.getCookieWebKit) + _ = ScriptViewModel.shared } func applicationWillTerminate(_ aNotification: Notification) { diff --git a/Box42/Resources/Assets.xcassets/uibuttons/Bookmark.imageset/Contents.json b/Box42/Resources/Assets.xcassets/uibuttons/Bookmark.imageset/Contents.json index f843e5b..c1d7e62 100644 --- a/Box42/Resources/Assets.xcassets/uibuttons/Bookmark.imageset/Contents.json +++ b/Box42/Resources/Assets.xcassets/uibuttons/Bookmark.imageset/Contents.json @@ -5,7 +5,7 @@ "scale" : "1x" }, { - "filename" : "Bookmark icon.png", + "filename" : "bookmark icon.png", "idiom" : "universal", "scale" : "2x" }, diff --git a/Box42/Resources/Assets.xcassets/uibuttons/Star.imageset/Contents.json b/Box42/Resources/Assets.xcassets/uibuttons/Star.imageset/Contents.json index bad711e..2e47fcd 100644 --- a/Box42/Resources/Assets.xcassets/uibuttons/Star.imageset/Contents.json +++ b/Box42/Resources/Assets.xcassets/uibuttons/Star.imageset/Contents.json @@ -5,7 +5,7 @@ "scale" : "1x" }, { - "filename" : "Star 3.png", + "filename" : "star.png", "idiom" : "universal", "scale" : "2x" }, diff --git a/Box42/Resources/Assets.xcassets/uibuttons/Star.imageset/Star 3.png b/Box42/Resources/Assets.xcassets/uibuttons/Star.imageset/star.png similarity index 100% rename from Box42/Resources/Assets.xcassets/uibuttons/Star.imageset/Star 3.png rename to Box42/Resources/Assets.xcassets/uibuttons/Star.imageset/star.png diff --git a/Box42/Resources/Info.plist b/Box42/Resources/Info.plist index 1520f65..59cd59d 100644 --- a/Box42/Resources/Info.plist +++ b/Box42/Resources/Info.plist @@ -2,6 +2,8 @@ + NSDocumentsFolderUsageDescription + 원활한 앱 구동을 위해 유저 디렉토리의 권한을 요청합니다. NSAppTransportSecurity NSAllowsArbitraryLoads diff --git a/Box42/Scripts/Controller/ExcuteScripts.swift b/Box42/Scripts/Controller/ExecuteScripts.swift similarity index 84% rename from Box42/Scripts/Controller/ExcuteScripts.swift rename to Box42/Scripts/Controller/ExecuteScripts.swift index cf36f28..1692d77 100644 --- a/Box42/Scripts/Controller/ExcuteScripts.swift +++ b/Box42/Scripts/Controller/ExecuteScripts.swift @@ -1,5 +1,5 @@ // -// ExcuteScripts.swift +// ExecuteScripts.swift // Box42 // // Created by Chanhee Kim on 8/29/23. @@ -7,7 +7,7 @@ import Foundation -class ExcuteScripts { +class ExecuteScripts { static func executeShellScript(path: String) { let task = Process() task.launchPath = "/bin/sh" diff --git a/Box42/Scripts/Controller/ScriptsFileManager.swift b/Box42/Scripts/Controller/ScriptsFileManager.swift index 5b4c69f..7cae68f 100644 --- a/Box42/Scripts/Controller/ScriptsFileManager.swift +++ b/Box42/Scripts/Controller/ScriptsFileManager.swift @@ -16,7 +16,7 @@ class ScriptsFileManager { if let savedURL = savedURL, fileManager.fileExists(atPath: savedURL.path) { print("File already exists, executing...") - ExcuteScripts.executeShellScript(path: savedURL.path) + SecurityScopedResourceAccess.accessResourceExecuteShellScript(scriptPath: savedURL.path) return } @@ -37,8 +37,7 @@ class ScriptsFileManager { try fileManager.moveItem(at: location, to: savedURL) print("Saved URL: ", savedURL) - - ExcuteScripts.executeShellScript(path: savedURL.path) + SecurityScopedResourceAccess.accessResourceExecuteShellScript(scriptPath: savedURL.path) } catch { print("File error: \(error)") diff --git a/Box42/Scripts/Controller/ScriptsViewController.swift b/Box42/Scripts/Controller/ScriptsViewController.swift index ed12375..e86f46d 100644 --- a/Box42/Scripts/Controller/ScriptsViewController.swift +++ b/Box42/Scripts/Controller/ScriptsViewController.swift @@ -18,8 +18,6 @@ class ScriptsViewController: NSViewController { override func loadView() { self.view = NSView() - self.view.wantsLayer = true - self.view.layer?.backgroundColor = NSColor.blue.cgColor scriptsTableView = ScriptsTableView(frame: .zero) scriptsTableView?.setup() diff --git a/Box42/Scripts/Model/Scripts.swift b/Box42/Scripts/Model/Scripts.swift index 7671c3d..a83a5c4 100644 --- a/Box42/Scripts/Model/Scripts.swift +++ b/Box42/Scripts/Model/Scripts.swift @@ -15,10 +15,14 @@ struct Script: Codable { var id: UUID var name: String var description: String + var path: String + var savedId: Int - init(name: String, description: String) { + init(name: String, description: String, path: String, savedId: Int) { self.id = UUID() self.name = name self.description = description + self.path = path + self.savedId = savedId } } diff --git a/Box42/Scripts/View/Button/ScriptQuickSlotButton.swift b/Box42/Scripts/View/Button/ScriptQuickSlotButton.swift index a8d2420..e2351d7 100644 --- a/Box42/Scripts/View/Button/ScriptQuickSlotButton.swift +++ b/Box42/Scripts/View/Button/ScriptQuickSlotButton.swift @@ -73,7 +73,7 @@ class ScriptQuickSlotButton: NSButton { guard self.window != nil else { return } - + if self.title == "퀵슬롯" { return } // 현재 버튼의 위치를 윈도우 기준으로 변환 let initialLocation = self.frame.origin diff --git a/Box42/Scripts/View/Table/ScriptCell.swift b/Box42/Scripts/View/Table/ScriptCell.swift index bf75e4d..57d421b 100644 --- a/Box42/Scripts/View/Table/ScriptCell.swift +++ b/Box42/Scripts/View/Table/ScriptCell.swift @@ -100,9 +100,25 @@ class ScriptCell: NSTableCellView { } } + // script 내부 클릭시 1차 실행 + // 있는거면 지우고 없는거면 추가 @objc func quickSlotButtonclicked() { - if let id = script?.id { - viewModel?.quickSlotScript(id: id) + guard let id = script?.id else { + return + } + + let alreadyExists = QuickSlotViewModel.shared.buttons.contains { $0.id == id } + + if alreadyExists { + QuickSlotViewModel.shared.removeButton(id) + quickSlotButton.title = "퀵슬롯" + } else { + if QuickSlotViewModel.shared.buttons.count > 7 { + return + } else { + quickSlotButton.title = "저장됨" + viewModel?.quickSlotScript(id: id) + } } } } diff --git a/Box42/Scripts/View/Table/ScriptCellManager.swift b/Box42/Scripts/View/Table/ScriptCellManager.swift new file mode 100644 index 0000000..b471493 --- /dev/null +++ b/Box42/Scripts/View/Table/ScriptCellManager.swift @@ -0,0 +1,109 @@ +// +// ScriptCellManager.swift +// Box42 +// +// Created by Chanhee Kim on 8/30/23. +// + +import AppKit +import SnapKit + +// MARK: - 다음 버전에 추가 예정 +class ScriptCellManager: NSTableCellView { + var nameLabel: NSTextField = NSTextField() + var descriptionLabel: NSTextField = NSTextField() + var excuteButton: ScriptExcuteButton = ScriptExcuteButton() + var deleteButton: ScriptDeleteButton = ScriptDeleteButton() + var quickSlotButton: ScriptQuickSlotButton = ScriptQuickSlotButton() + + var viewModel: ScriptViewModel? + var script: Script? + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupUI() { + addSubview(nameLabel) + addSubview(descriptionLabel) + addSubview(quickSlotButton) + addSubview(excuteButton) + addSubview(deleteButton) + + nameLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(16) + make.width.lessThanOrEqualTo(200).priority(.high) // 최대 너비와 우선순위 설정 + } + + deleteButton.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview().offset(-16) + make.width.equalTo(53) + make.height.equalTo(40) + } + + excuteButton.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalTo(deleteButton.snp.left).offset(-8) + make.width.equalTo(70) + make.height.equalTo(40) + } + + quickSlotButton.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalTo(excuteButton.snp.left).offset(-8) + make.width.equalTo(53) + make.height.equalTo(40) + } + + descriptionLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalTo(nameLabel.snp.right).offset(8) + make.right.lessThanOrEqualTo(quickSlotButton.snp.left).offset(-8) + make.width.greaterThanOrEqualTo(100).priority(.low) // 최소 너비와 낮은 우선순위 설정 + } + } + + + + func configure(with script: Script, viewModel: ScriptViewModel?) { + self.script = script + self.viewModel = viewModel + nameLabel.stringValue = script.name + descriptionLabel.stringValue = script.description + + deleteButton.target = self + deleteButton.action = #selector(deleteButtonClicked) + + excuteButton.target = self + excuteButton.action = #selector(excuteButtonClicked) + + quickSlotButton.target = self + quickSlotButton.action = #selector(quickSlotButtonclicked) + + } + + @objc func deleteButtonClicked() { + if let id = script?.id { + viewModel?.deleteScript(id: id) + } + } + + @objc func excuteButtonClicked() { + if let id = script?.id { + viewModel?.excuteScript(id: id) + } + } + + @objc func quickSlotButtonclicked() { + if let id = script?.id { + viewModel?.quickSlotScript(id: id) + } + } +} diff --git a/Box42/Scripts/View/Table/ScriptsTableView.swift b/Box42/Scripts/View/Table/ScriptsTableView.swift index 44c155c..2d77ef0 100644 --- a/Box42/Scripts/View/Table/ScriptsTableView.swift +++ b/Box42/Scripts/View/Table/ScriptsTableView.swift @@ -30,28 +30,47 @@ class ScriptsTableView: NSTableView { self.delegate = self self.dataSource = self - let column1 = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("Column1")) + let column1 = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("Scripts")) column1.width = 100.0 - column1.title = "Column 1" + column1.title = "Scripts" self.addTableColumn(column1) } } extension ScriptsTableView: NSTableViewDelegate, NSTableViewDataSource { - + func getCellForRow(at row: Int) -> NSView { + guard let viewModel = viewModel else { + return NSView() + } + + if row < viewModel.scripts.count { + return getScriptCell(for: viewModel.scripts[row], viewModel: viewModel) + } else { + // MARK: - 다음 버전에 추가 예정 + return getScriptCellManager() + } + } + + private func getScriptCell(for script: Script, viewModel: ScriptViewModel) -> ScriptCell { + let cell = ScriptCell(frame: .zero) + cell.configure(with: script, viewModel: viewModel) + return cell + } + + private func getScriptCellManager() -> ScriptCellManager { + let scriptCellManger = ScriptCellManager(frame: .zero) + return scriptCellManger + } + func numberOfRows(in tableView: NSTableView) -> Int { - return viewModel?.scripts.count ?? 0 + return viewModel?.scripts.count ?? 0 // + 1 } func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { - let cell = ScriptCell(frame: .zero) // 또는 원하는 frame 값을 설정 - if let script = viewModel?.scripts[row] { - cell.configure(with: script, viewModel: viewModel) - } - return cell // 이 줄을 추가 + getCellForRow(at: row) } func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { - return 44.0 // 셀 높이를 44로 설정 + return 44.0 } } diff --git a/Box42/Scripts/ViewModel/ScriptsViewModel.swift b/Box42/Scripts/ViewModel/ScriptsViewModel.swift index 75955fd..d81c86e 100644 --- a/Box42/Scripts/ViewModel/ScriptsViewModel.swift +++ b/Box42/Scripts/ViewModel/ScriptsViewModel.swift @@ -9,25 +9,45 @@ import AppKit import Combine class ScriptViewModel: NSObject { + static let shared = ScriptViewModel() + @Published var scripts: [Script] = [] override init() { self.scripts = [ - Script(name: "cleanCache", description: "Cleaning cache"), - Script(name: "brewInGoinfre", description: "Brew download in goinfre") + Script(name: "cleanCache", + description: "Cleaning cache", + path: Bundle.main.path(forResource: "cleanCache", ofType: "sh") ?? "", savedId: -1 ), + Script(name: "brewInGoinfre", + description: "Brew download in goinfre", + path: Bundle.main.path(forResource: "brewInGoinfre", ofType: "sh") ?? "", savedId: -1), + Script(name: "exportMacOSInfo", + description: "export setting MacOS Info", + path: Bundle.main.path(forResource: "exportMacOSInfo", ofType: "sh") ?? "", savedId: -1 ), + Script(name: "importMacOSInfo", + description: "import MacOS Info", + path: Bundle.main.path(forResource: "importMacOSInfo", ofType: "sh") ?? "", savedId: -1), + Script(name: "key Mapping", + description: "key Mapping", + path: Bundle.main.path(forResource: "keyMapping", ofType: "sh") ?? "", savedId: -1 ), + Script(name: "nodeInstall", + description: "node Install", + path: Bundle.main.path(forResource: "nodeInstall", ofType: "sh") ?? "", savedId: -1) ] } // Create - func addScript(name: String, description: String) { - let newScript = Script(name: name, description: description) + func addScript(name: String, description: String, path: String) { + let newScript = Script(name: name, description: description, path: path, savedId: -1) scripts.append(newScript) } // Read func excuteScript(id: UUID) { if let index = scripts.firstIndex(where: { $0.id == id }) { - ExcuteScripts.executeShellScript(path: scripts[index].name) +// ExecuteScripts.executeShellScript(path: scripts[index].name) + // MARK: - 파일스크립트 매니저에서 권한을 얻은 실행으로 실행합니다. + SecurityScopedResourceAccess.accessResourceExecuteShellScript(scriptPath: scripts[index].path) } } @@ -41,15 +61,21 @@ class ScriptViewModel: NSObject { // Delete func deleteScript(id: UUID) { + // delete 요청 보내야함 보내고 성공하면 지우기 scripts.removeAll(where: { $0.id == id }) QuickSlotViewModel.shared.removeButton(id) } + // 새로운 스크립트 배열로 교체하는 메서드 + func replaceScripts(with newScripts: [Script]) { + self.scripts = newScripts + } + + // 스크립트안에서 해당하는 스크립트를 찾아서 quickslotVM에 추가 func quickSlotScript(id: UUID) { if let index = scripts.firstIndex(where: { $0.id == id }) { - let button = QuickSlotButtonModel(id: id, title: scripts[index].name) + let button = QuickSlotButtonModel(id: id, title: scripts[index].name, path: scripts[index].path) QuickSlotViewModel.shared.addButton(button) } } - } diff --git a/Box42/Shared/API/API.swift b/Box42/Shared/API/API.swift new file mode 100644 index 0000000..f9cb069 --- /dev/null +++ b/Box42/Shared/API/API.swift @@ -0,0 +1,63 @@ +// +// Shared.swift +// Box42 +// +// Created by Chanhee Kim on 8/30/23. +// + +import WebKit + +class API { + // GET + static func fetchDataFromAPI(withURL urlString: String, forType type: T.Type, completion: @escaping (Result) -> Void) { + + let url = URL(string: urlString)! + var request = URLRequest(url: url) + request.httpShouldHandleCookies = true + + let task = URLSession.shared.dataTask(with: request) { (data, response, error) in + if let error = error { + completion(.failure(error)) + return + } + + guard let data = data else { + completion(.failure(NSError(domain: "NoData", code: -1, userInfo: nil))) + return + } + + do { + let decodedData = try JSONDecoder().decode(type, from: data) + completion(.success(decodedData)) + } catch let jsonError { + completion(.failure(jsonError)) + } + } + task.resume() + } + + // DELETE + static func deleteDataFromAPI(withURL urlString: String, completion: @escaping (Result) -> Void) { + + let url = URL(string: urlString)! + var request = URLRequest(url: url) + request.httpMethod = "DELETE" + request.httpShouldHandleCookies = true + + let task = URLSession.shared.dataTask(with: request) { (data, response, error) in + if let error = error { + completion(.failure(error)) + return + } + + if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode != 200 { + completion(.failure(NSError(domain: "InvalidStatusCode", code: httpResponse.statusCode, userInfo: nil))) + return + } + + completion(.success(data)) + } + task.resume() + } + +} diff --git a/Box42/Shared/API/GetUserMeScripts.swift b/Box42/Shared/API/GetUserMeScripts.swift new file mode 100644 index 0000000..7fa7d15 --- /dev/null +++ b/Box42/Shared/API/GetUserMeScripts.swift @@ -0,0 +1,34 @@ +// +// GetScripts.swift +// Box42 +// +// Created by Chanhee Kim on 8/30/23. +// + +import WebKit + +extension API { + // MARK: - Scripts GET: https://api.42box.site/user-service/users/me/scripts + // result : scripts.shared 저장 + + static func getUserMeScripts(_ webView: WKWebView) { + webView.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in + let cookieStorage = HTTPCookieStorage.shared + for cookie in cookies { + cookieStorage.setCookie(cookie) + } + + fetchDataFromAPI(withURL: "https://api.42box.site/user-service/users/me/scripts", forType: [Script].self) { (result: Result<[Script], Error>) in + switch result { + case .success(let scripts): + print(">> MacOS Get :", scripts) + DispatchQueue.main.async { + ScriptViewModel.shared.replaceScripts(with: scripts) + } + case .failure(let error): + print("Error: \(error)") + } + } + } + } +} diff --git a/Box42/Shared/API/GetUserProfile.swift b/Box42/Shared/API/GetUserProfile.swift index 8fa41d1..1815669 100644 --- a/Box42/Shared/API/GetUserProfile.swift +++ b/Box42/Shared/API/GetUserProfile.swift @@ -7,45 +7,63 @@ import WebKit -class API { +extension API { // MARK: - 유저 정보 (Back) GET: https://api.42box.site/user-service/users/me - // return : 실패 nil 반환 - // : 성공 UserProfile() + // result : 성공 UserProfile.shared 저장 + static func getUserProfile(_ webView: WKWebView) { - var userProfile: UserProfile? - webView.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in let cookieStorage = HTTPCookieStorage.shared - for cookie in cookies { -// print("\(cookie.name) = \(cookie.value)") cookieStorage.setCookie(cookie) } - var request = URLRequest(url: URL(string: "https://api.42box.site/user-service/users/me")!) - request.httpShouldHandleCookies = true - let task = URLSession.shared.dataTask(with: request) { (data, response, error) in - if let error = error { - print("Error: \(error)") - return - } - - guard let data = data else { - print("No data received.") - return - } - - do { - userProfile = try JSONDecoder().decode(UserProfile.self, from: data) - - print(">> MacOS Get", userProfile) + fetchDataFromAPI(withURL: "https://api.42box.site/user-service/users/me", forType: UserProfile.self) { (result: Result) in + switch result { + case .success(let userProfile): + print(">> MacOS Get :", userProfile) UserManager.shared.updateUserProfile(newProfile: userProfile) - - } catch let jsonError { - print("JSON Parsing Error: \(jsonError)") + case .failure(let error): + print("Error: \(error)") } } - task.resume() } } } + +//static func getUserProfile(_ webView: WKWebView) { +// webView.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in +// let cookieStorage = HTTPCookieStorage.shared +// +// for cookie in cookies { +//// print("\(cookie.name) = \(cookie.value)") +// cookieStorage.setCookie(cookie) +// } +// +// var request = URLRequest(url: URL(string: "https://api.42box.site/user-service/users/me")!) +// request.httpShouldHandleCookies = true +// let task = URLSession.shared.dataTask(with: request) { (data, response, error) in +// if let error = error { +// print("Error: \(error)") +// return +// } +// +// guard let data = data else { +// print("No data received.") +// return +// } +// +// do { +// let userProfile = try JSONDecoder().decode(UserProfile.self, from: data) +// +// print(">> MacOS Get :", userProfile ) +// UserManager.shared.updateUserProfile(newProfile: userProfile) +// +// } catch let jsonError { +// print("JSON Parsing Error: \(jsonError)") +// } +// } +// task.resume() +// } +//} +// diff --git a/Box42/Shared/SecurityScopedResourceAccess.swift b/Box42/Shared/SecurityScopedResourceAccess.swift new file mode 100644 index 0000000..a919d15 --- /dev/null +++ b/Box42/Shared/SecurityScopedResourceAccess.swift @@ -0,0 +1,59 @@ +// +// SecurityScopedResourceAccess.swift +// Box42 +// +// Created by Chanhee Kim on 8/31/23. +// + +import Foundation + +class SecurityScopedResourceAccess { + private static let queue = DispatchQueue(label: "com.yourApp.securityAccessQueue", attributes: .concurrent) + private static var isAccessing = false + static var bookmarkData: Data? { + get { + return UserDefaults.standard.data(forKey: "bookmarkData") + } + set { + UserDefaults.standard.set(newValue, forKey: "bookmarkData") + } + } + + static func accessResourceExecuteShellScript(scriptPath: String) { + queue.async(flags: .barrier) { + var url: URL? = nil // 이 부분을 추가하여 url 변수의 스코프를 확장합니다. + do { + var staleBookmarkData = false + guard let bookmarkData = self.bookmarkData else { + print("Bookmark data not available.") + return + } + + print("Stored bookmark data: \(String(describing: UserDefaults.standard.data(forKey: "bookmarkData")))") + + + url = try URL(resolvingBookmarkData: bookmarkData, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &staleBookmarkData) + + if staleBookmarkData { + // Refresh the bookmark data and save it. + } + + isAccessing = url?.startAccessingSecurityScopedResource() ?? false + + // Perform work here + if isAccessing { + ExecuteScripts.executeShellScript(path: scriptPath) + } + + } catch { + print("An error occurred: \(error)") + } + + // Cleanup + if isAccessing { + // Make sure to match this with a call to startAccessingSecurityScopedResource() + url?.stopAccessingSecurityScopedResource() + } + } + } +} diff --git a/Box42/WebView/Model/WebViewUI.swift b/Box42/WebView/Model/WebViewUI.swift index 50679a9..b3d5582 100644 --- a/Box42/WebView/Model/WebViewUI.swift +++ b/Box42/WebView/Model/WebViewUI.swift @@ -12,5 +12,6 @@ enum WebViewUI { static let download = "download" static let icon = "icon" static let userProfile = "userProfile" + static let script = "script" } } diff --git a/Box42/WebView/WebView.swift b/Box42/WebView/WebView.swift index 8e3e12e..80b8b3b 100644 --- a/Box42/WebView/WebView.swift +++ b/Box42/WebView/WebView.swift @@ -24,6 +24,7 @@ class WebView: WKWebView, WKScriptMessageHandler { contentController.add(self, name: WebViewUI.transfer.download) contentController.add(self, name: WebViewUI.transfer.icon) contentController.add(self, name: WebViewUI.transfer.userProfile) + contentController.add(self, name: WebViewUI.transfer.script) self.configuration.preferences.javaScriptCanOpenWindowsAutomatically = true self.configuration.preferences.javaScriptEnabled = true @@ -57,10 +58,24 @@ extension WebView { do { let decoder = JSONDecoder() let userProfile = try decoder.decode(UserProfile.self, from: userProfileJson!) - print(userProfile.icon) UserManager.shared.updateUserProfile(newProfile: userProfile) + } catch { + print("JSON decoding failed: \(error)") + } + } + + if message.name == WebViewUI.transfer.script, let scriptString = message.body as? String { + let scriptJson = scriptString.data(using: .utf8) + + do { + let decoder = JSONDecoder() + let downScript = try decoder.decode(Script.self, from: scriptJson!) + print(downScript) + + + } catch { print("JSON decoding failed: \(error)") } diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..81101ad --- /dev/null +++ b/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "SnapKit", + "repositoryURL": "https://github.com/SnapKit/SnapKit", + "state": { + "branch": null, + "revision": "f222cbdf325885926566172f6f5f06af95473158", + "version": "5.6.0" + } + } + ] + }, + "version": 1 +}