mirror of
https://github.com/pawelorzech/MacTorn.git
synced 2026-01-30 04:04:27 +00:00
Add automatic update checker and UI integration
Introduces an UpdateManager to check for new releases on GitHub and integrates update notifications into the app's state and SettingsView. Updates Info.plist to version 1.2, adds MacTorn-v1.2.zip, archives v1.0 zip, and revises README to document the new update checker and other improvements.
This commit is contained in:
parent
e75131aa67
commit
10a441e557
8 changed files with 144 additions and 23 deletions
BIN
MacTorn-v1.2.zip
Normal file
BIN
MacTorn-v1.2.zip
Normal file
Binary file not shown.
|
|
@ -17,8 +17,8 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.0</string>
|
<string>1.2</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>1.2</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
|
|
@ -440,6 +440,75 @@ struct WatchlistItem: Codable, Identifiable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Update Manager
|
||||||
|
struct GitHubRelease: Codable {
|
||||||
|
let tagName: String
|
||||||
|
let htmlUrl: String
|
||||||
|
let body: String
|
||||||
|
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case tagName = "tag_name"
|
||||||
|
case htmlUrl = "html_url"
|
||||||
|
case body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UpdateManager {
|
||||||
|
static let shared = UpdateManager()
|
||||||
|
|
||||||
|
// Configure your repository here
|
||||||
|
private let githubOwner = "pawelorzech"
|
||||||
|
private let githubRepo = "MacTorn"
|
||||||
|
|
||||||
|
func checkForUpdates(currentVersion: String) async -> GitHubRelease? {
|
||||||
|
let urlString = "https://api.github.com/repos/\(githubOwner)/\(githubRepo)/releases/latest"
|
||||||
|
guard let url = URL(string: urlString) else { return nil }
|
||||||
|
|
||||||
|
do {
|
||||||
|
let (data, response) = try await URLSession.shared.data(from: url)
|
||||||
|
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse,
|
||||||
|
httpResponse.statusCode == 200 else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let release = try JSONDecoder().decode(GitHubRelease.self, from: data)
|
||||||
|
|
||||||
|
// Compare versions
|
||||||
|
let versionString = release.tagName.replacingOccurrences(of: "v", with: "")
|
||||||
|
|
||||||
|
if isVersion(versionString, greaterThan: currentVersion) {
|
||||||
|
return release
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
print("Update check failed: \(error)")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private func isVersion(_ newVersion: String, greaterThan currentVersion: String) -> Bool {
|
||||||
|
let newComponents = newVersion.split(separator: ".").compactMap { Int($0) }
|
||||||
|
let currentComponents = currentVersion.split(separator: ".").compactMap { Int($0) }
|
||||||
|
|
||||||
|
let maxLength = max(newComponents.count, currentComponents.count)
|
||||||
|
|
||||||
|
for i in 0..<maxLength {
|
||||||
|
let new = i < newComponents.count ? newComponents[i] : 0
|
||||||
|
let current = i < currentComponents.count ? currentComponents[i] : 0
|
||||||
|
|
||||||
|
if new > current {
|
||||||
|
return true
|
||||||
|
} else if new < current {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Error
|
// MARK: - Error
|
||||||
struct TornError: Codable {
|
struct TornError: Codable {
|
||||||
let code: Int
|
let code: Int
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,13 @@ class AppState: ObservableObject {
|
||||||
@Published var propertiesData: [PropertyInfo]?
|
@Published var propertiesData: [PropertyInfo]?
|
||||||
@Published var watchlistItems: [WatchlistItem] = []
|
@Published var watchlistItems: [WatchlistItem] = []
|
||||||
|
|
||||||
|
// MARK: - Update State
|
||||||
|
@Published var updateAvailable: GitHubRelease?
|
||||||
|
|
||||||
// MARK: - Managers
|
// MARK: - Managers
|
||||||
let launchAtLogin = LaunchAtLoginManager()
|
let launchAtLogin = LaunchAtLoginManager()
|
||||||
let shortcutsManager = ShortcutsManager()
|
let shortcutsManager = ShortcutsManager()
|
||||||
|
let updateManager = UpdateManager.shared
|
||||||
|
|
||||||
// MARK: - State Comparison
|
// MARK: - State Comparison
|
||||||
private var previousBars: Bars?
|
private var previousBars: Bars?
|
||||||
|
|
@ -458,6 +462,19 @@ class AppState: ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Updates
|
||||||
|
func checkForAppUpdates() {
|
||||||
|
guard let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else { return }
|
||||||
|
|
||||||
|
Task {
|
||||||
|
if let release = await updateManager.checkForUpdates(currentVersion: currentVersion) {
|
||||||
|
await MainActor.run {
|
||||||
|
self.updateAvailable = release
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Errors
|
// MARK: - Errors
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
.task {
|
.task {
|
||||||
await NotificationManager.shared.requestPermission()
|
await NotificationManager.shared.requestPermission()
|
||||||
|
appState.checkForAppUpdates()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,14 +114,47 @@ struct SettingsView: View {
|
||||||
.background(Color.purple.opacity(0.05))
|
.background(Color.purple.opacity(0.05))
|
||||||
.cornerRadius(8)
|
.cornerRadius(8)
|
||||||
|
|
||||||
// GitHub
|
// Update Section
|
||||||
HStack(spacing: 4) {
|
if let update = appState.updateAvailable {
|
||||||
Image(systemName: "chevron.left.forwardslash.chevron.right")
|
VStack(spacing: 8) {
|
||||||
.font(.caption2)
|
HStack {
|
||||||
.foregroundColor(.gray)
|
Image(systemName: "arrow.triangle.2.circlepath")
|
||||||
Link("View on GitHub",
|
.foregroundColor(.green)
|
||||||
destination: URL(string: "https://github.com/pawelorzech/MacTorn")!)
|
Text("New version available: \(update.tagName)")
|
||||||
.font(.caption)
|
.font(.caption.bold())
|
||||||
|
}
|
||||||
|
|
||||||
|
Button("Download Update") {
|
||||||
|
if let url = URL(string: update.htmlUrl) {
|
||||||
|
NSWorkspace.shared.open(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.buttonStyle(.borderedProminent)
|
||||||
|
.controlSize(.small)
|
||||||
|
}
|
||||||
|
.padding(10)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.background(Color.green.opacity(0.1))
|
||||||
|
.cornerRadius(8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GitHub & Version
|
||||||
|
VStack(spacing: 4) {
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
Image(systemName: "chevron.left.forwardslash.chevron.right")
|
||||||
|
.font(.caption2)
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
Link("View on GitHub",
|
||||||
|
destination: URL(string: "https://github.com/pawelorzech/MacTorn")!)
|
||||||
|
.font(.caption)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
|
||||||
|
Text("v\(version)")
|
||||||
|
.font(.caption2)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.opacity(0.5)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
|
|
|
||||||
27
README.md
27
README.md
|
|
@ -6,7 +6,9 @@ A native macOS menu bar app for monitoring your **Torn** game status.
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||

|
<p align="center">
|
||||||
|
<img src="app.png" alt="MacTorn Screenshot" width="600">
|
||||||
|
</p>
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
|
@ -35,18 +37,17 @@ A native macOS menu bar app for monitoring your **Torn** game status.
|
||||||
- Armory quick-use buttons
|
- Armory quick-use buttons
|
||||||
|
|
||||||
### 📈 Watchlist Tab
|
### 📈 Watchlist Tab
|
||||||
- Track item prices
|
- Track item prices (Latest API v2 support)
|
||||||
|
- Displays lowest market price AND quantity (e.g., `$4.2M x12`)
|
||||||
- Price change indicators
|
- Price change indicators
|
||||||
- Add/remove items from watchlist
|
- Add/remove items from watchlist
|
||||||
|
|
||||||
### 🏠 Properties Tab
|
|
||||||
- Property info and vault contents
|
|
||||||
- Upkeep status and countdown
|
|
||||||
|
|
||||||
### ⚙️ General
|
### ⚙️ General
|
||||||
- 🔔 Smart notifications for bars, cooldowns, landing, chain
|
- **🔄 Update Checker**: Automatically notifies you when a new version is available on GitHub.
|
||||||
- 🕒 Configurable refresh intervals (15s/30s/60s/2m)
|
- **🔔 Smart Notifications**: Alerts for bar thresholds, cooldown ready, landing, chain expiring.
|
||||||
- 🚀 Launch at Login
|
- **🕒 Configurable Refresh**: Intervals (15s/30s/60s/2m).
|
||||||
|
- **🚀 Launch at Login**: Start seamlessly with macOS.
|
||||||
|
- **⚡️ Optimized Startup**: Non-blocking data fetching for instant UI responsiveness.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
@ -55,12 +56,12 @@ A native macOS menu bar app for monitoring your **Torn** game status.
|
||||||
3. Open MacTorn from Applications
|
3. Open MacTorn from Applications
|
||||||
4. Enter your [Torn API Key](https://www.torn.com/preferences.php#tab=api)
|
4. Enter your [Torn API Key](https://www.torn.com/preferences.php#tab=api)
|
||||||
|
|
||||||
> **Note**: If you download an unsigned build, macOS Gatekeeper will block it. Right-click the app and select "Open", or go to System Settings → Privacy & Security → Open Anyway.
|
> **Note**: If you download an unsigned build, macOS Gatekeeper may block it. Right-click the app and select "Open", or go to System Settings → Privacy & Security → Open Anyway.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- macOS 13.0 (Ventura) or later
|
- macOS 13.0 (Ventura) or later
|
||||||
- Torn API Key with access to: basic, bars, cooldowns, travel, profile, events, messages
|
- Torn API Key with access to: basic, bars, cooldowns, travel, profile, events, messages, market
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
|
@ -70,8 +71,8 @@ Choose polling frequency: 15s, 30s, 60s, or 120s
|
||||||
### Notifications
|
### Notifications
|
||||||
MacTorn sends notifications for bar thresholds, cooldown ready, landing, chain expiring, and release. Notification defaults are stored locally.
|
MacTorn sends notifications for bar thresholds, cooldown ready, landing, chain expiring, and release. Notification defaults are stored locally.
|
||||||
|
|
||||||
### Quick Links
|
### Updates
|
||||||
8 preset shortcuts to common Torn pages (fully editable)
|
The app checks for updates automatically on startup. If a new version is available, you'll see a notification in the **Settings** tab.
|
||||||
|
|
||||||
## Building from Source
|
## Building from Source
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue