diff --git a/pullBar/AppDelegate.swift b/pullBar/AppDelegate.swift index a761435..310d99c 100644 --- a/pullBar/AppDelegate.swift +++ b/pullBar/AppDelegate.swift @@ -23,6 +23,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { @Default(.refreshRate) var refreshRate @Default(.buildType) var buildType + @Default(.counterType) var counterType @Default(.githubUsername) var githubUsername @FromKeychain(.githubToken) var githubToken @@ -84,19 +85,18 @@ extension AppDelegate { func refreshMenu() { NSLog("Refreshing menu") self.menu.removeAllItems() - self.statusBarItem.button?.title = "" - + if (githubUsername == "" || githubToken == "") { addMenuFooterItems() return } - - + + var assignedPulls: [Edge]? = [] var createdPulls: [Edge]? = [] var reviewRequestedPulls: [Edge]? = [] - - + + let group = DispatchGroup() if showAssigned { @@ -106,7 +106,7 @@ extension AppDelegate { group.leave() } } - + if showCreated { group.enter() ghClient.getCreatedPulls() { pulls in @@ -114,7 +114,7 @@ extension AppDelegate { group.leave() } } - + if showRequested { group.enter() ghClient.getReviewRequestedPulls() { pulls in @@ -122,47 +122,46 @@ extension AppDelegate { group.leave() } } - + group.notify(queue: .main) { - let isOneSelected = (self.showAssigned.intValue + self.showCreated.intValue + self.showRequested.intValue) == 1 - - if let assignedPulls = assignedPulls, let createdPulls = createdPulls, let reviewRequestedPulls = reviewRequestedPulls { - + self.statusBarItem.button?.title = "" + if self.showAssigned && !assignedPulls.isEmpty { + if self.counterType == .assigned { + self.statusBarItem.button?.title = String(assignedPulls.count) + } + self.menu.addItem(NSMenuItem(title: "Assigned (\(assignedPulls.count))", action: nil, keyEquivalent: "")) for pull in assignedPulls { self.menu.addItem(self.createMenuItem(pull: pull)) } self.menu.addItem(.separator()) - if isOneSelected { - self.statusBarItem.button?.title = String(assignedPulls.count) - } } if self.showCreated && !createdPulls.isEmpty { + if self.counterType == .created { + self.statusBarItem.button?.title = String(createdPulls.count) + } + self.menu.addItem(NSMenuItem(title: "Created (\(createdPulls.count))", action: nil, keyEquivalent: "")) for pull in createdPulls { self.menu.addItem(self.createMenuItem(pull: pull)) } self.menu.addItem(.separator()) - if isOneSelected { - self.statusBarItem.button?.title = String(createdPulls.count) - } - } - + if self.showRequested && !reviewRequestedPulls.isEmpty { + if self.counterType == .reviewRequested { + self.statusBarItem.button?.title = String(reviewRequestedPulls.count) + } + self.menu.addItem(NSMenuItem(title: "Review Requested (\(reviewRequestedPulls.count))", action: nil, keyEquivalent: "")) for pull in reviewRequestedPulls { self.menu.addItem(self.createMenuItem(pull: pull)) } self.menu.addItem(.separator()) - if isOneSelected { - self.statusBarItem.button?.title = String(reviewRequestedPulls.count) - } - } @@ -312,14 +311,9 @@ extension AppDelegate { issueItemTitle.appendIcon(iconName: "dot-fill", color: NSColor.gray) } - issueItem.submenu?.addItem(buildItem) } - - - } - } issueItem.attributedTitle = issueItemTitle @@ -347,7 +341,7 @@ extension AppDelegate { preferencesWindow.close() } preferencesWindow = NSWindow( - contentRect: NSRect(x: 0, y: 0, width: 500, height: 500), + contentRect: NSRect(x: 0, y: 0, width: 0, height: 0), styleMask: [.closable, .titled], backing: .buffered, defer: false diff --git a/pullBar/Extensions/DefaultsExtensions.swift b/pullBar/Extensions/DefaultsExtensions.swift index 91f4347..3cbf386 100644 --- a/pullBar/Extensions/DefaultsExtensions.swift +++ b/pullBar/Extensions/DefaultsExtensions.swift @@ -20,6 +20,7 @@ extension Defaults.Keys { static let refreshRate = Key("refreshRate", default: 5) static let buildType = Key("buildType", default: .none) + static let counterType = Key("counterType", default: .reviewRequested) } extension KeychainKeys { @@ -45,3 +46,26 @@ enum BuildType: String, Defaults.Serializable, CaseIterable, Identifiable { } } } + +enum CounterType: String, Defaults.Serializable, CaseIterable, Identifiable { + case assigned + case created + case reviewRequested + case none + + var id: Self { self } + + var description: String { + + switch self { + case .assigned: + return "assigned" + case .created: + return "created" + case .reviewRequested: + return "review requested" + case .none: + return "none" + } + } +} diff --git a/pullBar/Views/PreferencesView.swift b/pullBar/Views/PreferencesView.swift index 73bf961..ad20f6d 100644 --- a/pullBar/Views/PreferencesView.swift +++ b/pullBar/Views/PreferencesView.swift @@ -24,140 +24,141 @@ struct PreferencesView: View { @Default(.refreshRate) var refreshRate @Default(.buildType) var builtType + @Default(.counterType) var counterType @State private var showGhAlert = false @StateObject private var githubTokenValidator = GithubTokenValidator() @ObservedObject private var launchAtLogin = LaunchAtLogin.observable - + var body: some View { - Form { - Section { - VStack(alignment: .leading) { - Text("Authentication") - .font(.callout) - .bold() - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.top, 20) - Form { - Section { - HStack(alignment: .center) { - Text("GitHub username:").frame(width: 120, alignment: .trailing) - TextField("", text: $githubUsername) - .textFieldStyle(RoundedBorderTextFieldStyle()) - .disableAutocorrection(true) - .textContentType(.password) - .frame(width: 200) - } - - HStack(alignment: .center) { - Text("GitHub token:").frame(width: 120, alignment: .trailing) - VStack(alignment: .leading) { - HStack() { - SecureField("", text: $githubToken) - .textFieldStyle(RoundedBorderTextFieldStyle()) - .overlay( - Image(systemName: githubTokenValidator.iconName).foregroundColor(githubTokenValidator.iconColor) - .frame(maxWidth: .infinity, alignment: .trailing) - .padding(.trailing, 8) - ) - .frame(width: 380) - .onChange(of: githubToken) { _ in - githubTokenValidator.validate() - } - Button { - githubTokenValidator.validate() - } label: { - Image(systemName: "repeat") - } - .help("Retry") - } - Text("[Generate](https://github.com/settings/tokens/new?scopes=repo) a personal access token, make sure to select **repo** scope") - .font(.footnote) + + TabView { + Form { + HStack(alignment: .center) { + Text("GitHub username:").frame(width: 120, alignment: .trailing) + TextField("", text: $githubUsername) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disableAutocorrection(true) + .textContentType(.password) + .frame(width: 200) + } + + HStack(alignment: .center) { + Text("GitHub token:").frame(width: 120, alignment: .trailing) + VStack(alignment: .leading) { + HStack() { + SecureField("", text: $githubToken) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .overlay( + Image(systemName: githubTokenValidator.iconName).foregroundColor(githubTokenValidator.iconColor) + .frame(maxWidth: .infinity, alignment: .trailing) + .padding(.trailing, 8) + ) + .frame(width: 380) + .onChange(of: githubToken) { _ in + githubTokenValidator.validate() } + Button { + githubTokenValidator.validate() + } label: { + Image(systemName: "repeat") } + .help("Retry") } + Text("[Generate](https://github.com/settings/tokens/new?scopes=repo) a personal access token, make sure to select **repo** scope") + .font(.footnote) + .padding(.leading, 8) + .foregroundColor(.secondary) } - .padding() - .frame(maxWidth: .infinity) - .overlay( - RoundedRectangle(cornerRadius: 8) - .stroke(Color(.lightGray), lineWidth: 1) - ) - - - Text("General") - .font(.callout) - .bold() - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.top, 20) - Form { - Section { - HStack(alignment: .center) { - Text("Pull Requests:").frame(width: 120, alignment: .trailing) - VStack(alignment: .leading){ - Toggle("assigned", isOn: $showAssigned) - Toggle("created", isOn: $showCreated) - Toggle("review requested", isOn: $showRequested) - } - } - - HStack(alignment: .center) { - Text("Build Information:").frame(width: 120, alignment: .trailing) - Picker("", selection: $builtType, content: { - ForEach(BuildType.allCases) { bt in - Text(bt.description) - } - }) - .labelsHidden() - .pickerStyle(RadioGroupPickerStyle()) - .frame(width: 120) - } - - HStack(alignment: .center) { - Text("Show Avatar:").frame(width: 120, alignment: .trailing) - Toggle("", isOn: $showAvatar) - } - - HStack(alignment: .center) { - Text("Show Labels:").frame(width: 120, alignment: .trailing) - Toggle("", isOn: $showLabels) - } - - HStack(alignment: .center) { - Text("Refresh Rate:").frame(width: 120, alignment: .trailing) - Picker("", selection: $refreshRate, content: { - Text("1 minute").tag(1) - Text("5 minutes").tag(5) - Text("10 minutes").tag(10) - Text("15 minutes").tag(15) - Text("30 minutes").tag(30) - }).labelsHidden() - .pickerStyle(MenuPickerStyle()) - .frame(width: 100) - } - - HStack(alignment: .center) { - Text("Launch at login:").frame(width: 120, alignment: .trailing) - Toggle("", isOn: $launchAtLogin.isEnabled) - - } - + } + } + .padding() + .frame(maxWidth: .infinity) + .onAppear() { + githubTokenValidator.validate() + } + .tabItem{Text("Authentication")} + + Form { + HStack(alignment: .center) { + Text("Pull Requests:").frame(width: 120, alignment: .trailing) + VStack(alignment: .leading){ + Toggle("assigned", isOn: $showAssigned) + Toggle("created", isOn: $showCreated) + Toggle("review requested", isOn: $showRequested) + } + } + + HStack(alignment: .center) { + Text("Build Information:").frame(width: 120, alignment: .trailing) + Picker("", selection: $builtType, content: { + ForEach(BuildType.allCases) { bt in + Text(bt.description) } + }) + .labelsHidden() + .pickerStyle(RadioGroupPickerStyle()) + .frame(width: 120) + } + + HStack(alignment: .center) { + Text("Show Avatar:").frame(width: 120, alignment: .trailing) + Toggle("", isOn: $showAvatar) + } + + HStack(alignment: .center) { + Text("Show Labels:").frame(width: 120, alignment: .trailing) + Toggle("", isOn: $showLabels) + } + + HStack(alignment: .center) { + Text("Refresh Rate:").frame(width: 120, alignment: .trailing) + Picker("", selection: $refreshRate, content: { + Text("1 minute").tag(1) + Text("5 minutes").tag(5) + Text("10 minutes").tag(10) + Text("15 minutes").tag(15) + Text("30 minutes").tag(30) + }).labelsHidden() + .pickerStyle(MenuPickerStyle()) + .frame(width: 100) + } + + HStack(alignment: .center) { + Text("Launch at login:").frame(width: 120, alignment: .trailing) + Toggle("", isOn: $launchAtLogin.isEnabled) + + } + } + .padding(8) + .frame(maxWidth: .infinity) + .tabItem{Text("Menu")} + + Form { + HStack(alignment: .center) { + VStack(alignment: .leading) { + Text("Counter:") + Text("Number of pull requests next to the icon") + .font(.subheadline) + .foregroundColor(.secondary) } - .padding() - .frame(maxWidth: .infinity) - .overlay( - RoundedRectangle(cornerRadius: 8) - .stroke(Color(.lightGray), lineWidth: 1) - ) + Picker("", selection: $counterType, content: { + ForEach(CounterType.allCases) { bt in + Text(bt.description) + } + }) + .labelsHidden() + .pickerStyle(RadioGroupPickerStyle()) } } + .padding(8) + .frame(maxWidth: .infinity) + .tabItem{Text("Menubar icon")} + } .padding() - .onAppear() { - githubTokenValidator.validate() - } + } }