81 lines
3.5 KiB
Swift
81 lines
3.5 KiB
Swift
import Foundation
|
|
import SwiftUI
|
|
|
|
struct MessagesView: View {
|
|
@EnvironmentObject private var model: AppModel
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
LazyVStack(alignment: .leading, spacing: 12) {
|
|
Text(model.text("Wiadomości", "Messages")).font(.largeTitle.bold())
|
|
Text(model.text("Najnowsze wiadomości są wyświetlane na górze.", "The newest messages appear first."))
|
|
.foregroundStyle(.secondary)
|
|
.padding(.bottom, 4)
|
|
if model.messages.isEmpty {
|
|
VStack(spacing: 12) {
|
|
Image(systemName: "sparkles").font(.largeTitle).foregroundStyle(KTheme.orange)
|
|
Text(model.text("Nie masz jeszcze żadnych wiadomości.", "You do not have any messages yet."))
|
|
.multilineTextAlignment(.center)
|
|
}
|
|
.frame(maxWidth: .infinity)
|
|
.kasztelanPanel()
|
|
} else {
|
|
ForEach(model.messages) { message in
|
|
messageCard(message)
|
|
}
|
|
}
|
|
}
|
|
.padding(20)
|
|
}
|
|
.background(.clear)
|
|
.toolbar { AppToolbar() }
|
|
.toolbarBackground(.hidden, for: .navigationBar)
|
|
.refreshable { await model.refresh() }
|
|
.task { await model.refresh() }
|
|
}
|
|
|
|
private func messageCard(_ message: KasztelanMessage) -> some View {
|
|
VStack(alignment: .leading, spacing: 10) {
|
|
HStack(alignment: .top) {
|
|
Circle().fill(KTheme.orange).frame(width: 9, height: 9).padding(.top, 6)
|
|
VStack(alignment: .leading, spacing: 3) {
|
|
Text(message.title.isEmpty ? "Kasztelan" : message.title).font(.headline)
|
|
Text(messageDate(message.sentAt)).font(.caption).foregroundStyle(.secondary)
|
|
}
|
|
Spacer()
|
|
Button(role: .destructive) { model.deleteMessage(id: message.id) } label: {
|
|
Image(systemName: "xmark.circle.fill").foregroundStyle(.secondary)
|
|
}
|
|
.accessibilityLabel(model.text("Usuń wiadomość", "Delete message"))
|
|
}
|
|
Text(linkedMessage(message.text))
|
|
.tint(KTheme.orange)
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
.textSelection(.enabled)
|
|
}
|
|
.kasztelanPanel()
|
|
}
|
|
|
|
private func messageDate(_ seconds: Int) -> String {
|
|
guard seconds > 0 else { return model.text("Teraz", "Now") }
|
|
let formatter = DateFormatter()
|
|
formatter.dateFormat = model.language == .pl ? "dd.MM.yyyy HH:mm" : "dd/MM/yyyy HH:mm"
|
|
return formatter.string(from: Date(timeIntervalSince1970: TimeInterval(seconds)))
|
|
}
|
|
|
|
private func linkedMessage(_ value: String) -> AttributedString {
|
|
let result = NSMutableAttributedString(string: value)
|
|
guard let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) else {
|
|
return AttributedString(value)
|
|
}
|
|
let fullRange = NSRange(value.startIndex..<value.endIndex, in: value)
|
|
detector.enumerateMatches(in: value, options: [], range: fullRange) { match, _, _ in
|
|
guard let match, let url = match.url,
|
|
let scheme = url.scheme?.lowercased(),
|
|
scheme == "http" || scheme == "https" else { return }
|
|
result.addAttribute(.link, value: url, range: match.range)
|
|
}
|
|
return AttributedString(result)
|
|
}
|
|
}
|