v2.0.7 — Show account email in settings auth section

This commit is contained in:
superdooper86
2026-05-07 15:21:35 +02:00
parent a1c3c0b314
commit d2815d65ce
5 changed files with 54 additions and 15 deletions
+25 -7
View File
@@ -596,13 +596,31 @@ struct SettingsView: View {
.font(.system(size: 12, weight: .medium)) .font(.system(size: 12, weight: .medium))
.foregroundColor(.secondary) .foregroundColor(.secondary)
HStack(spacing: 6) { if vm.isSignedIn {
Circle() VStack(alignment: .leading, spacing: 2) {
.fill(vm.isSignedIn ? Color.green : Color.orange) HStack(spacing: 6) {
.frame(width: 7, height: 7) Circle()
Text(vm.isSignedIn ? "Signed in to claude.ai" : "Not signed in") .fill(Color.green)
.font(.system(size: 12, weight: .medium)) .frame(width: 7, height: 7)
.foregroundColor(vm.isSignedIn ? .primary : .orange) Text("Signed in to claude.ai")
.font(.system(size: 13, weight: .semibold))
}
if !vm.userEmail.isEmpty {
Text(vm.userEmail)
.font(.system(size: 11.5))
.foregroundColor(.secondary)
.padding(.leading, 13)
}
}
} else {
HStack(spacing: 6) {
Circle()
.fill(Color.orange)
.frame(width: 7, height: 7)
Text("Not signed in")
.font(.system(size: 13, weight: .semibold))
.foregroundColor(.orange)
}
} }
Text("Your session is stored locally and persists across restarts.") Text("Your session is stored locally and persists across restarts.")
+2 -2
View File
@@ -15,9 +15,9 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2.0.6</string> <string>2.0.7</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>13</string> <string>14</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>13.0</string> <string>13.0</string>
<key>LSUIElement</key> <key>LSUIElement</key>
+22 -1
View File
@@ -14,6 +14,7 @@ class UsageViewModel: ObservableObject {
@Published var planLabel: String = "Claude" @Published var planLabel: String = "Claude"
@Published var isNotAuthenticated: Bool = false @Published var isNotAuthenticated: Bool = false
@Published var isSignedIn: Bool = false @Published var isSignedIn: Bool = false
@Published var userEmail: String = ""
@Published var triggerLogin: Bool = false @Published var triggerLogin: Bool = false
@Published var showInMenuBar: Bool = true { @Published var showInMenuBar: Bool = true {
didSet { UserDefaults.standard.set(showInMenuBar, forKey: "show_in_menubar") } didSet { UserDefaults.standard.set(showInMenuBar, forKey: "show_in_menubar") }
@@ -46,6 +47,7 @@ class UsageViewModel: ObservableObject {
isSignedIn = false isSignedIn = false
isNotAuthenticated = true isNotAuthenticated = true
lastUpdated = nil lastUpdated = nil
userEmail = ""
limits = [] limits = []
extraUsage = nil extraUsage = nil
prepaidCredits = nil prepaidCredits = nil
@@ -63,7 +65,9 @@ class UsageViewModel: ObservableObject {
async let usageFetch = fetchUsage(orgId: orgId) async let usageFetch = fetchUsage(orgId: orgId)
async let prepaidFetch = fetchPrepaidCredits(orgId: orgId) async let prepaidFetch = fetchPrepaidCredits(orgId: orgId)
async let overageFetch = fetchOverageSpendLimit(orgId: orgId) async let overageFetch = fetchOverageSpendLimit(orgId: orgId)
let (usage, prepaid, overage) = try await (usageFetch, prepaidFetch, overageFetch) async let emailFetch = fetchUserEmail()
let (usage, prepaid, overage, email) = try await (usageFetch, prepaidFetch, overageFetch, emailFetch)
if let email { userEmail = email }
limits = buildLimits(from: usage) limits = buildLimits(from: usage)
extraUsage = usage.extraUsage extraUsage = usage.extraUsage
prepaidCredits = prepaid prepaidCredits = prepaid
@@ -121,6 +125,23 @@ class UsageViewModel: ObservableObject {
return try JSONDecoder().decode(UsageResponse.self, from: data) return try JSONDecoder().decode(UsageResponse.self, from: data)
} }
private func fetchUserEmail() async throws -> String? {
let url = URL(string: "https://claude.ai/api/bootstrap")!
var req = URLRequest(url: url)
req.setValue("application/json", forHTTPHeaderField: "accept")
let cookies = await WKWebsiteDataStore.default().httpCookieStore.allCookies()
let claudeCookies = cookies.filter { $0.domain.contains("claude.ai") }
if let header = HTTPCookie.requestHeaderFields(with: claudeCookies)["Cookie"] {
req.setValue(header, forHTTPHeaderField: "Cookie")
}
let (data, response) = try await URLSession.shared.data(for: req)
guard let http = response as? HTTPURLResponse, http.statusCode == 200 else { return nil }
guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let account = json["account"] as? [String: Any],
let email = account["email_address"] as? String else { return nil }
return email
}
private func fetchPrepaidCredits(orgId: String) async throws -> PrepaidCredits? { private func fetchPrepaidCredits(orgId: String) async throws -> PrepaidCredits? {
let url = URL(string: "https://claude.ai/api/organizations/\(orgId)/prepaid/credits")! let url = URL(string: "https://claude.ai/api/organizations/\(orgId)/prepaid/credits")!
var req = URLRequest(url: url) var req = URLRequest(url: url)
+2 -2
View File
@@ -8,7 +8,7 @@
[![macOS](https://img.shields.io/badge/macOS-13.0+-000000?style=flat&logo=apple&logoColor=white)](https://www.apple.com/macos/) [![macOS](https://img.shields.io/badge/macOS-13.0+-000000?style=flat&logo=apple&logoColor=white)](https://www.apple.com/macos/)
[![Swift](https://img.shields.io/badge/Swift-5.9-F05138?style=flat&logo=swift&logoColor=white)](https://swift.org) [![Swift](https://img.shields.io/badge/Swift-5.9-F05138?style=flat&logo=swift&logoColor=white)](https://swift.org)
[![Version](https://img.shields.io/badge/version-2.0.6-orange?style=flat)](https://github.com/superdooper86/claudechecker/releases) [![Version](https://img.shields.io/badge/version-2.0.7-orange?style=flat)](https://github.com/superdooper86/claudechecker/releases)
[![License](https://img.shields.io/badge/license-MIT-blue?style=flat)](LICENSE) [![License](https://img.shields.io/badge/license-MIT-blue?style=flat)](LICENSE)
</div> </div>
@@ -138,7 +138,7 @@ ClaudeChecker reads from the following `claude.ai` API endpoints (authenticated
| Endpoint | Used for | | Endpoint | Used for |
|---|---| |---|---|
| `/api/bootstrap` | Organisation UUID | | `/api/bootstrap` | Organisation UUID and account email |
| `/api/organizations/{id}/usage` | 5-hour and 7-day quota windows | | `/api/organizations/{id}/usage` | 5-hour and 7-day quota windows |
| `/api/organizations/{id}/overage_spend_limit` | Extra usage spent and monthly limit | | `/api/organizations/{id}/overage_spend_limit` | Extra usage spent and monthly limit |
| `/api/organizations/{id}/prepaid/credits` | Current credit balance | | `/api/organizations/{id}/prepaid/credits` | Current credit balance |
+3 -3
View File
@@ -1,5 +1,5 @@
{ {
"version": "2.0.6", "version": "2.0.7",
"url": "https://github.com/superdooper86/claudechecker/releases/download/v2.0.6/ClaudeChecker.zip", "url": "https://github.com/superdooper86/claudechecker/releases/download/v2.0.7/ClaudeChecker.zip",
"notes": "Authentication section shows sign-in status with re-authenticate and sign out options." "notes": "Show account email in settings auth section."
} }