fix: address all PR review comments
- HTTPClient: replace isRefreshing bool with shared Task to safely coalesce concurrent 401 refresh attempts; surface JSON serialization error instead of silently dropping request body - AuthService.logout: always clear Keychain tokens via defer, even when refresh token is absent, preventing stale access token - NotificationsAPIService: remove updateAppBadge (UIKit call moved to @MainActor NotificationsViewModel); drop unused UIKit import - NotificationsViewModel: guard startPolling() against duplicate tasks; update badge directly on @MainActor instead of hopping to actor - VerifyEmailView: replace Timer (never invalidated) with async Task cancelled in .onDisappear - NotificationsView: use Text(date, style: .relative) — auto-updates without custom formatter; remove duplicate Date extension - SettingsView: handle logoutAll errors explicitly with alert instead of silently proceeding with local logout - MaydayLiveActivity/Info.plist: add NSExtensionPrincipalClass so the widget extension is discoverable by the system - Live Activity widget: replace frozen duration(from:) with Text(date, style: .timer); replace frozen relativeFormatted with Text(date, style: .relative); localize status badge to Russian Co-authored-by: robonen <26167508+robonen@users.noreply.github.com>
This commit is contained in:
@@ -24,6 +24,8 @@
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.widgetkit-extension</string>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).MaydayLiveActivityBundle</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -35,9 +35,13 @@ struct MaydayLiveActivityLiveActivity: Widget {
|
||||
Spacer()
|
||||
VStack(alignment: .trailing, spacing: 2) {
|
||||
statusBadge(context.state.status)
|
||||
Text("Длит.: \(duration(from: context.state.startedAt))")
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
// Text(date, style: .timer) updates automatically without re-render.
|
||||
HStack(spacing: 2) {
|
||||
Text("Длит.:")
|
||||
Text(context.state.startedAt, style: .timer)
|
||||
}
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
@@ -77,7 +81,8 @@ struct MaydayLiveActivityLiveActivity: Widget {
|
||||
.font(.caption)
|
||||
.foregroundStyle(severityColor(context.attributes.severity))
|
||||
}
|
||||
Text(context.state.startedAt.relativeFormatted)
|
||||
// Text(date, style: .relative) updates automatically without re-render.
|
||||
Text(context.state.startedAt, style: .relative)
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
@@ -92,8 +97,8 @@ struct MaydayLiveActivityLiveActivity: Widget {
|
||||
@ViewBuilder
|
||||
func statusBadge(_ status: AlertStatus) -> some View {
|
||||
let (text, color): (String, Color) = status == .active
|
||||
? ("active", .red)
|
||||
: ("resolved", .green)
|
||||
? ("активен", .red)
|
||||
: ("завершён", .green)
|
||||
Text(text)
|
||||
.font(.caption2.bold())
|
||||
.padding(.horizontal, 6)
|
||||
@@ -110,26 +115,4 @@ struct MaydayLiveActivityLiveActivity: Widget {
|
||||
case .info: return .blue
|
||||
}
|
||||
}
|
||||
|
||||
func duration(from startDate: Date) -> String {
|
||||
let interval = Date().timeIntervalSince(startDate)
|
||||
let minutes = Int(interval / 60)
|
||||
let hours = minutes / 60
|
||||
if hours > 0 {
|
||||
return "\(hours)ч \(minutes % 60)м"
|
||||
}
|
||||
return "\(minutes)м"
|
||||
}
|
||||
}
|
||||
|
||||
extension Date {
|
||||
var relativeFormatted: String {
|
||||
Date.relativeDateTimeFormatter.localizedString(for: self, relativeTo: Date())
|
||||
}
|
||||
|
||||
private static let relativeDateTimeFormatter: RelativeDateTimeFormatter = {
|
||||
let formatter = RelativeDateTimeFormatter()
|
||||
formatter.locale = Locale(identifier: "ru_RU")
|
||||
return formatter
|
||||
}()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user