Skip to content

Commit

Permalink
Add automatic check for updates
Browse files Browse the repository at this point in the history
  • Loading branch information
streetturtle committed Oct 30, 2023
1 parent 520b32d commit 69a667f
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 50 deletions.
35 changes: 25 additions & 10 deletions JiraBar/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {

let config = NSImage.SymbolConfiguration(pointSize: 24, weight: .regular)
unknownPersonAvatar = NSImage(systemSymbolName: "person.crop.circle.badge.questionmark", accessibilityDescription: nil)!.withSymbolConfiguration(config)!

checkForUpdates()
}

func applicationWillTerminate(_ aNotification: Notification) {
Expand All @@ -72,11 +72,11 @@ extension AppDelegate {
self.statusBarItem.button?.title = String(issues.count)
let issuesByStatus = Dictionary(grouping: issues) { $0.fields.status.name }
.sorted { $0.key < $1.key }

for (status, issuess) in issuesByStatus {
self.menu.addItem(.separator())
self.menu.addItem(withTitle: status, action: nil, keyEquivalent: "")

for issue in issuess {
let issueItem = NSMenuItem(title: "", action: #selector(self.openLink), keyEquivalent: "")

Expand All @@ -90,19 +90,19 @@ extension AppDelegate {
.appendString(string: issue.fields.assignee?.displayName ?? "Unassign", color: "#888888")
.appendSeparator()
.appendString(string: issue.fields.issuetype.name, color: "#888888")


issueItem.attributedTitle = issueItemTitle
if issue.fields.summary.count > 50 {
issueItem.toolTip = issue.fields.summary
}
issueItem.representedObject = URL(string: "\(self.jiraHost)/browse/\(issue.key)")

self.jiraClient.getTransitionsByIssueKey(issueKey: issue.key) { transitions in
if !transitions.isEmpty {
let transitionsMenu = NSMenu()
issueItem.submenu = transitionsMenu

for transition in transitions {
let transitionItem = NSMenuItem(title: transition.name, action: #selector(self.transitionIssue), keyEquivalent: "")
transitionItem.representedObject = [issue.key, transition.id]
Expand Down Expand Up @@ -131,7 +131,7 @@ extension AppDelegate {
let createNewItem = NSMenuItem(title: "Create issue", action: #selector(self.openCreateNewIssue), keyEquivalent: "")
createNewItem.image = NSImage(systemSymbolName: "plus", accessibilityDescription: nil)
self.menu.addItem(createNewItem)

self.menu.addItem(.separator())
self.menu.addItem(withTitle: "Preferences...", action: #selector(self.openPrefecencesWindow), keyEquivalent: "")
self.menu.addItem(withTitle: "About JiraBar", action: #selector(self.openAboutWindow), keyEquivalent: "")
Expand All @@ -149,7 +149,7 @@ extension AppDelegate {
self.refreshMenu()
}
}

@objc
func openSearchResults() {
let encodedPath = jql.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)
Expand Down Expand Up @@ -184,7 +184,7 @@ extension AppDelegate {
preferencesWindow.contentView = NSHostingView(rootView: contentView)
preferencesWindow.makeKeyAndOrderFront(nil)
preferencesWindow.styleMask.remove(.resizable)

// allow the preference window can be focused automatically when opened
NSApplication.shared.activate(ignoringOtherApps: true)

Expand Down Expand Up @@ -213,7 +213,7 @@ extension AppDelegate {
aboutWindow.contentView = NSHostingView(rootView: contentView)
aboutWindow.makeKeyAndOrderFront(nil)
aboutWindow.styleMask.remove(.resizable)

// allow the preference window can be focused automatically when opened
NSApplication.shared.activate(ignoringOtherApps: true)

Expand Down Expand Up @@ -247,5 +247,20 @@ extension AppDelegate {
}
}
}

@objc
func checkForUpdates() {
let currentVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
GithubClient().getLatestRelease { latestRelease in
if let latestRelease = latestRelease {
let versionComparison = currentVersion.compare(latestRelease.name.replacingOccurrences(of: "v", with: ""), options: .numeric)
if versionComparison == .orderedAscending {
let newVersionItem = NSMenuItem(title: "New version available", action: #selector(self.openLink), keyEquivalent: "")
newVersionItem.representedObject = URL(string: latestRelease.htmlUrl)
self.menu.addItem(newVersionItem)
}
}
}
}
}

36 changes: 36 additions & 0 deletions JiraBar/Github/GithubClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// GithubClient.swift
// jiraBar
//
// Created by Pavel Makhov on 2023-10-29.
//

import Foundation
import Alamofire

public class GithubClient {

func getLatestRelease(completion:@escaping (((LatestRelease?) -> Void))) -> Void {
let headers: HTTPHeaders = [
.contentType("application/json"),
.accept("application/json")
]
AF.request("https://api.github.com/repos/menubar-apps/JiraBar/releases/latest",
method: .get,
encoding: JSONEncoding.default,
headers: headers)
.validate(statusCode: 200..<300)
.responseDecodable(of: LatestRelease.self) { response in
switch response.result {
case .success(let latestRelease):
completion(latestRelease)
case .failure(let error):
completion(nil)
if let data = response.data {
let json = String(data: data, encoding: String.Encoding.utf8)
}
sendNotification(body: error.localizedDescription)
}
}
}
}
29 changes: 29 additions & 0 deletions JiraBar/Github/GithubDtos.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// GithubDtos.swift
// jiraBar
//
// Created by Pavel Makhov on 2023-10-29.
//

import Foundation

struct LatestRelease: Codable {

var name: String
var htmlUrl: String
var assets: [Asset]

enum CodingKeys: String, CodingKey {
case name
case assets
case htmlUrl = "html_url"
}
}

struct Asset: Codable {
var name: String

enum CodingKeys: String, CodingKey {
case name
}
}
80 changes: 40 additions & 40 deletions JiraBar/Views/PreferencesView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,48 +16,48 @@ struct PreferencesView: View {

Spacer()
HStack {
Spacer()
Form {
TextField("Username:", text: $jiraUsername)
.textFieldStyle(RoundedBorderTextFieldStyle())
TextField("Host:", text: $jiraHost)
.textFieldStyle(RoundedBorderTextFieldStyle())
SecureField("Token:", text: $jiraToken)
.textFieldStyle(RoundedBorderTextFieldStyle())
.overlay(
Image(systemName: jiraTokenValidator.iconName).foregroundColor(jiraTokenValidator.iconColor)
.frame(maxWidth: .infinity, alignment: .trailing)
.padding(.trailing, 8)
)
.onChange(of: jiraToken) { _ in
jiraTokenValidator.validate()
}

Text("Jira Cloud: generate an [API Token](https://id.atlassian.com/manage/api-tokens)")
.font(.footnote)
Text("Jira Server: use your password as a token")
.font(.footnote)

Divider()

TextField("JQL Query:", text: $jql)
.textFieldStyle(RoundedBorderTextFieldStyle())
Text("Use advanced search in Jira to create a JQL query and then paste it here")
.font(.footnote)
TextField("Max Results:", text: $maxResults)
.textFieldStyle(RoundedBorderTextFieldStyle())
.frame(width: 120)
Picker("Refresh Rate:", selection: $refreshRate) {
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)
Spacer()
Form {
TextField("Username:", text: $jiraUsername)
.textFieldStyle(RoundedBorderTextFieldStyle())
TextField("Host:", text: $jiraHost)
.textFieldStyle(RoundedBorderTextFieldStyle())
SecureField("Token:", text: $jiraToken)
.textFieldStyle(RoundedBorderTextFieldStyle())
.overlay(
Image(systemName: jiraTokenValidator.iconName).foregroundColor(jiraTokenValidator.iconColor)
.frame(maxWidth: .infinity, alignment: .trailing)
.padding(.trailing, 8)
)
.onChange(of: jiraToken) { _ in
jiraTokenValidator.validate()
}
.frame(width: 200)

Text("Jira Cloud: generate an [API Token](https://id.atlassian.com/manage/api-tokens)")
.font(.footnote)
Text("Jira Server: use your password as a token")
.font(.footnote)

Divider()

TextField("JQL Query:", text: $jql)
.textFieldStyle(RoundedBorderTextFieldStyle())
Text("Use advanced search in Jira to create a JQL query and then paste it here")
.font(.footnote)
TextField("Max Results:", text: $maxResults)
.textFieldStyle(RoundedBorderTextFieldStyle())
.frame(width: 120)
Picker("Refresh Rate:", selection: $refreshRate) {
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)
}
.frame(width: 200)
}
Spacer()

Spacer()
}
.padding()
.frame(width: 500)
Expand Down
16 changes: 16 additions & 0 deletions jiraBar.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
objects = {

/* Begin PBXBuildFile section */
768D68872AEF367F004261DB /* GithubClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 768D68862AEF367F004261DB /* GithubClient.swift */; };
768D68892AEF3746004261DB /* GithubDtos.swift in Sources */ = {isa = PBXBuildFile; fileRef = 768D68882AEF3746004261DB /* GithubDtos.swift */; };
769F4E4F2775640900594911 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 769F4E4E2775640900594911 /* AppDelegate.swift */; };
769F4E532775640A00594911 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 769F4E522775640A00594911 /* Assets.xcassets */; };
769F4E562775640A00594911 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 769F4E542775640A00594911 /* Main.storyboard */; };
Expand All @@ -27,6 +29,8 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
768D68862AEF367F004261DB /* GithubClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GithubClient.swift; sourceTree = "<group>"; };
768D68882AEF3746004261DB /* GithubDtos.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GithubDtos.swift; sourceTree = "<group>"; };
769F4E4B2775640900594911 /* jiraBar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = jiraBar.app; sourceTree = BUILT_PRODUCTS_DIR; };
769F4E4E2775640900594911 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
769F4E522775640A00594911 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
Expand Down Expand Up @@ -59,6 +63,15 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
768D68852AEF3666004261DB /* Github */ = {
isa = PBXGroup;
children = (
768D68862AEF367F004261DB /* GithubClient.swift */,
768D68882AEF3746004261DB /* GithubDtos.swift */,
);
path = Github;
sourceTree = "<group>";
};
769F4E422775640900594911 = {
isa = PBXGroup;
children = (
Expand All @@ -80,6 +93,7 @@
children = (
769F4E73277795D700594911 /* Views */,
769F4E6927764BFE00594911 /* Extensions */,
768D68852AEF3666004261DB /* Github */,
769F4E60277569D900594911 /* Jira */,
769F4E4E2775640900594911 /* AppDelegate.swift */,
769F4E522775640A00594911 /* Assets.xcassets */,
Expand Down Expand Up @@ -213,6 +227,8 @@
769F4E6B27764C3000594911 /* NSMutableAttributedString.swift in Sources */,
76D5F9E128D2606D009EBD80 /* JiraTokenValidator.swift in Sources */,
769F4E62277569E700594911 /* JiraClient.swift in Sources */,
768D68892AEF3746004261DB /* GithubDtos.swift in Sources */,
768D68872AEF367F004261DB /* GithubClient.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down

0 comments on commit 69a667f

Please sign in to comment.