Compare commits
No commits in common. "main" and "v1.5.1" have entirely different histories.
12 changed files with 22 additions and 50 deletions
BIN
MacTorn-v1.4.4.zip
Normal file
BIN
MacTorn-v1.4.4.zip
Normal file
Binary file not shown.
BIN
MacTorn-v1.4.5.zip
Normal file
BIN
MacTorn-v1.4.5.zip
Normal file
Binary file not shown.
BIN
MacTorn-v1.4.6.zip
Normal file
BIN
MacTorn-v1.4.6.zip
Normal file
Binary file not shown.
BIN
MacTorn-v1.4.7.zip
Normal file
BIN
MacTorn-v1.4.7.zip
Normal file
Binary file not shown.
|
|
@ -14,7 +14,7 @@ struct MacTornApp: App {
|
||||||
.onAppear {
|
.onAppear {
|
||||||
updateAppearance()
|
updateAppearance()
|
||||||
}
|
}
|
||||||
.onChange(of: appearanceModeRaw) {
|
.onChange(of: appearanceModeRaw) { _ in
|
||||||
updateAppearance()
|
updateAppearance()
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
// MARK: - Constants
|
|
||||||
enum TornConstants {
|
|
||||||
static let developerID = 2362436
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Root Response
|
// MARK: - Root Response
|
||||||
struct TornResponse: Codable {
|
struct TornResponse: Codable {
|
||||||
let name: String?
|
let name: String?
|
||||||
|
|
@ -378,7 +373,7 @@ struct AttackResult: Codable, Identifiable {
|
||||||
let result: String?
|
let result: String?
|
||||||
let respect: Double?
|
let respect: Double?
|
||||||
|
|
||||||
let id: String
|
var id: String { code ?? UUID().uuidString }
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case code
|
case code
|
||||||
|
|
@ -391,33 +386,6 @@ struct AttackResult: Codable, Identifiable {
|
||||||
case result, respect
|
case result, respect
|
||||||
}
|
}
|
||||||
|
|
||||||
init(code: String?, timestampStarted: Int?, timestampEnded: Int?, attackerId: Int?, attackerName: String?, defenderId: Int?, defenderName: String?, result: String?, respect: Double?) {
|
|
||||||
self.code = code
|
|
||||||
self.timestampStarted = timestampStarted
|
|
||||||
self.timestampEnded = timestampEnded
|
|
||||||
self.attackerId = attackerId
|
|
||||||
self.attackerName = attackerName
|
|
||||||
self.defenderId = defenderId
|
|
||||||
self.defenderName = defenderName
|
|
||||||
self.result = result
|
|
||||||
self.respect = respect
|
|
||||||
self.id = code ?? UUID().uuidString
|
|
||||||
}
|
|
||||||
|
|
||||||
init(from decoder: Decoder) throws {
|
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
||||||
code = try container.decodeIfPresent(String.self, forKey: .code)
|
|
||||||
timestampStarted = try container.decodeIfPresent(Int.self, forKey: .timestampStarted)
|
|
||||||
timestampEnded = try container.decodeIfPresent(Int.self, forKey: .timestampEnded)
|
|
||||||
attackerId = try container.decodeIfPresent(Int.self, forKey: .attackerId)
|
|
||||||
attackerName = try container.decodeIfPresent(String.self, forKey: .attackerName)
|
|
||||||
defenderId = try container.decodeIfPresent(Int.self, forKey: .defenderId)
|
|
||||||
defenderName = try container.decodeIfPresent(String.self, forKey: .defenderName)
|
|
||||||
result = try container.decodeIfPresent(String.self, forKey: .result)
|
|
||||||
respect = try container.decodeIfPresent(Double.self, forKey: .respect)
|
|
||||||
id = code ?? UUID().uuidString
|
|
||||||
}
|
|
||||||
|
|
||||||
func opponentName(forUserId userId: Int) -> String {
|
func opponentName(forUserId userId: Int) -> String {
|
||||||
let name: String?
|
let name: String?
|
||||||
if attackerId == userId {
|
if attackerId == userId {
|
||||||
|
|
|
||||||
|
|
@ -103,8 +103,14 @@ class NotificationManager: NSObject, UNUserNotificationCenterDelegate {
|
||||||
|
|
||||||
/// Cancel all travel-related notifications
|
/// Cancel all travel-related notifications
|
||||||
func cancelTravelNotifications() {
|
func cancelTravelNotifications() {
|
||||||
let identifiers = TravelNotificationSetting.defaults.map { "\($0.id)_alert" }
|
let identifiers = [
|
||||||
|
"travel_2min_alert",
|
||||||
|
"travel_1min_alert",
|
||||||
|
"travel_30sec_alert",
|
||||||
|
"travel_10sec_alert"
|
||||||
|
]
|
||||||
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: identifiers)
|
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: identifiers)
|
||||||
|
print("Cancelled travel notifications")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cancel a specific notification by identifier
|
/// Cancel a specific notification by identifier
|
||||||
|
|
|
||||||
|
|
@ -312,9 +312,6 @@ class AppState: ObservableObject {
|
||||||
} else {
|
} else {
|
||||||
await updateItemError(itemId: itemId, error: "No listings")
|
await updateItemError(itemId: itemId, error: "No listings")
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
logger.error("Item \(itemId): failed to parse JSON response")
|
|
||||||
await updateItemError(itemId: itemId, error: "Parse Error")
|
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
logger.error("Item \(itemId) price fetch error: \(error.localizedDescription)")
|
logger.error("Item \(itemId) price fetch error: \(error.localizedDescription)")
|
||||||
|
|
@ -584,7 +581,9 @@ class AppState: ObservableObject {
|
||||||
// Check if feedback prompt should be shown
|
// Check if feedback prompt should be shown
|
||||||
self.checkFeedbackPrompt()
|
self.checkFeedbackPrompt()
|
||||||
|
|
||||||
logger.info("Data updated, lastUpdated: \(self.lastUpdated?.description ?? "nil")")
|
// Force UI update by triggering objectWillChange
|
||||||
|
self.objectWillChange.send()
|
||||||
|
logger.info("UI update triggered, lastUpdated: \(self.lastUpdated?.description ?? "nil")")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ struct ContentView: View {
|
||||||
private var headerView: some View {
|
private var headerView: some View {
|
||||||
HStack {
|
HStack {
|
||||||
if let lastUpdated = appState.lastUpdated {
|
if let lastUpdated = appState.lastUpdated {
|
||||||
Text("Updated: \(lastUpdated, formatter: Self.timeFormatter)")
|
Text("Updated: \(lastUpdated, formatter: timeFormatter)")
|
||||||
.font(.caption2)
|
.font(.caption2)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
|
|
@ -177,9 +177,9 @@ struct ContentView: View {
|
||||||
.padding(.bottom, 8)
|
.padding(.bottom, 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
private static let timeFormatter: DateFormatter = {
|
private var timeFormatter: DateFormatter {
|
||||||
let formatter = DateFormatter()
|
let formatter = DateFormatter()
|
||||||
formatter.timeStyle = .short
|
formatter.timeStyle = .short
|
||||||
return formatter
|
return formatter
|
||||||
}()
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ struct CreditsView: View {
|
||||||
@Binding var showCredits: Bool
|
@Binding var showCredits: Bool
|
||||||
|
|
||||||
// MARK: - Developer
|
// MARK: - Developer
|
||||||
private let developer = TornContributor(name: "bombel", tornID: TornConstants.developerID)
|
private let developer = TornContributor(name: "bombel", tornID: 2362436)
|
||||||
|
|
||||||
// MARK: - Special Thanks
|
// MARK: - Special Thanks
|
||||||
private let specialThanks: [TornContributor] = [
|
private let specialThanks: [TornContributor] = [
|
||||||
|
|
@ -92,9 +92,7 @@ struct CreditsView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
if let tornID = developer.tornID {
|
openTornProfile(developer.tornID!)
|
||||||
openTornProfile(tornID)
|
|
||||||
}
|
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Text(developer.name)
|
Text(developer.name)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ struct SettingsView: View {
|
||||||
@State private var showCredits: Bool = false
|
@State private var showCredits: Bool = false
|
||||||
@State private var availableBrowsers: [PreferredBrowser] = PreferredBrowser.availableBrowsers()
|
@State private var availableBrowsers: [PreferredBrowser] = PreferredBrowser.availableBrowsers()
|
||||||
|
|
||||||
private let developerID = TornConstants.developerID
|
// Developer ID for tip feature (bombel)
|
||||||
|
private let developerID = 2362436
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if showCredits {
|
if showCredits {
|
||||||
|
|
@ -73,7 +74,7 @@ struct SettingsView: View {
|
||||||
Text("2m").tag(120)
|
Text("2m").tag(120)
|
||||||
}
|
}
|
||||||
.pickerStyle(.segmented)
|
.pickerStyle(.segmented)
|
||||||
.onChange(of: appState.refreshInterval) {
|
.onChange(of: appState.refreshInterval) { _ in
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
appState.startPolling()
|
appState.startPolling()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -136,8 +136,8 @@ final class MacTornUITests: XCTestCase {
|
||||||
// MARK: - UI Test Helpers
|
// MARK: - UI Test Helpers
|
||||||
|
|
||||||
extension XCUIElement {
|
extension XCUIElement {
|
||||||
/// Wait for element to appear within the given timeout
|
/// Wait for element to exist with timeout
|
||||||
func waitForAppearance(timeout: TimeInterval = 5) -> Bool {
|
func waitForExistence(timeout: TimeInterval = 5) -> Bool {
|
||||||
return self.waitForExistence(timeout: timeout)
|
return self.waitForExistence(timeout: timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue