1eb21c71ce
- Swift 6, SwiftUI, MVVM + async/await architecture - iOS 17.0 minimum deployment target - Two targets: Mayday app + MaydayLiveActivity widget extension - Models: UserResponse, TokenPair, AppNotification, SessionResponse, AlertAttributes - Services: HTTPClient (actor), AuthService, KeychainService, NotificationsAPIService, PushNotificationService - ViewModels: AuthViewModel, NotificationsViewModel, SettingsViewModel - Views: Login/Register/VerifyEmail, NotificationsList/Detail, Settings/ChangePassword/Sessions - APNs push notifications with UIApplicationDelegate - ActivityKit Live Activities for Dynamic Island + Lock Screen - Keychain (Security framework) token storage - 30-second polling with pagination for notifications - Xcode project file (project.pbxproj) with correct build phases for both targets Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
69 lines
2.3 KiB
Swift
69 lines
2.3 KiB
Swift
import SwiftUI
|
|
|
|
struct NotificationDetailView: View {
|
|
let notification: AppNotification
|
|
let viewModel: NotificationsViewModel
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
VStack(alignment: .leading, spacing: 20) {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
Text(notification.topic)
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
Text(notification.subject)
|
|
.font(.title2.bold())
|
|
}
|
|
|
|
Divider()
|
|
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
Text("Подробности:")
|
|
.font(.headline)
|
|
Text(notification.body)
|
|
.font(.body)
|
|
}
|
|
|
|
if let metadata = notification.metadata, !metadata.isEmpty {
|
|
Divider()
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
Text("Метаданные:")
|
|
.font(.headline)
|
|
ForEach(metadata.sorted(by: { $0.key < $1.key }), id: \.key) { key, value in
|
|
HStack {
|
|
Text(key).foregroundStyle(.secondary)
|
|
Spacer()
|
|
Text(value)
|
|
}
|
|
.font(.footnote)
|
|
}
|
|
}
|
|
}
|
|
|
|
Divider()
|
|
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
LabeledContent("Получено") {
|
|
Text(notification.createdAt.formatted(date: .abbreviated, time: .shortened))
|
|
}
|
|
LabeledContent("Статус") {
|
|
Text(notification.status.rawValue)
|
|
}
|
|
LabeledContent("Канал") {
|
|
Text(notification.channel.rawValue)
|
|
}
|
|
}
|
|
.font(.footnote)
|
|
|
|
Spacer()
|
|
}
|
|
.padding()
|
|
}
|
|
.navigationTitle("Уведомление")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.task {
|
|
await viewModel.markAsRead(notification)
|
|
}
|
|
}
|
|
}
|