265 lines
9.8 KiB
Swift
265 lines
9.8 KiB
Swift
import SwiftUI
|
|
|
|
struct ThreadDetailView: View {
|
|
let board: Board
|
|
let thread: Thread
|
|
@EnvironmentObject var settings: Settings
|
|
@EnvironmentObject var apiClient: APIClient
|
|
@EnvironmentObject var networkMonitor: NetworkMonitor
|
|
@State private var threadDetail: ThreadDetail?
|
|
@State private var comments: [Comment] = []
|
|
@State private var isLoading = false
|
|
@State private var errorMessage: String?
|
|
@State private var showingAddComment = false
|
|
@State private var activityOn = false
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
VStack(alignment: .leading, spacing: 16) {
|
|
if networkMonitor.offlineEffective {
|
|
HStack(spacing: 8) {
|
|
Image(systemName: "wifi.slash")
|
|
.foregroundColor(.orange)
|
|
Text("Оффлайн режим. Показаны сохранённые данные")
|
|
.foregroundColor(.secondary)
|
|
.font(.caption)
|
|
}
|
|
}
|
|
if isLoading {
|
|
VStack {
|
|
ProgressView()
|
|
.progressViewStyle(CircularProgressViewStyle())
|
|
Text("Загрузка треда...")
|
|
.foregroundColor(.secondary)
|
|
}
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
.padding()
|
|
} else if let error = errorMessage {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
Text("Ошибка загрузки")
|
|
.font(.headline)
|
|
.foregroundColor(.red)
|
|
Text(error)
|
|
.font(.body)
|
|
.foregroundColor(.secondary)
|
|
Button("Повторить") {
|
|
loadThreadDetail()
|
|
}
|
|
.buttonStyle(.bordered)
|
|
}
|
|
.padding()
|
|
} else if let detail = threadDetail {
|
|
ThreadContentView(thread: detail, showFiles: settings.showFiles)
|
|
|
|
Divider()
|
|
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
HStack {
|
|
Text("Комментарии (\(comments.count))")
|
|
.font(.headline)
|
|
|
|
Spacer()
|
|
|
|
Button("Добавить") {
|
|
showingAddComment = true
|
|
}
|
|
.buttonStyle(.bordered)
|
|
}
|
|
|
|
if comments.isEmpty {
|
|
Text("Комментариев пока нет")
|
|
.foregroundColor(.secondary)
|
|
.italic()
|
|
} else {
|
|
LazyVStack(alignment: .leading, spacing: 16) {
|
|
ForEach(comments) { comment in
|
|
CommentView(comment: comment, showFiles: settings.showFiles)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.padding()
|
|
}
|
|
.refreshable {
|
|
loadThreadDetail()
|
|
}
|
|
.navigationTitle("#\(thread.id)")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .navigationBarTrailing) {
|
|
HStack {
|
|
Button("Обновить") { loadThreadDetail() }
|
|
if #available(iOS 16.1, *) {
|
|
Toggle("", isOn: $activityOn)
|
|
.toggleStyle(SwitchToggleStyle(tint: .blue))
|
|
.labelsHidden()
|
|
.onChange(of: activityOn) { newValue in
|
|
guard settings.liveActivityEnabled else { return }
|
|
if newValue {
|
|
if let detail = threadDetail {
|
|
LiveActivityManager.shared.start(for: detail, comments: comments, settings: settings)
|
|
}
|
|
} else {
|
|
LiveActivityManager.shared.end(threadId: thread.id)
|
|
}
|
|
}
|
|
.onAppear {
|
|
if settings.liveActivityEnabled {
|
|
if #available(iOS 16.1, *) {
|
|
activityOn = LiveActivityManager.shared.isActive(threadId: thread.id)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.sheet(isPresented: $showingAddComment) {
|
|
AddCommentView(boardCode: board.code, threadId: thread.id)
|
|
.environmentObject(settings)
|
|
.environmentObject(apiClient)
|
|
}
|
|
.onAppear {
|
|
if threadDetail == nil {
|
|
loadThreadDetail()
|
|
}
|
|
}
|
|
}
|
|
|
|
private func loadThreadDetail() {
|
|
isLoading = true
|
|
errorMessage = nil
|
|
|
|
apiClient.getFullThread(boardCode: board.code, threadId: thread.id) { result in
|
|
DispatchQueue.main.async {
|
|
self.isLoading = false
|
|
|
|
switch result {
|
|
case .success(let (detail, loadedComments)):
|
|
self.threadDetail = detail
|
|
self.comments = loadedComments
|
|
if #available(iOS 16.1, *), settings.liveActivityEnabled, activityOn {
|
|
LiveActivityManager.shared.update(threadId: thread.id, comments: loadedComments, settings: settings)
|
|
}
|
|
case .failure(let error):
|
|
self.errorMessage = error.localizedDescription
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ThreadContentView: View {
|
|
let thread: ThreadDetail
|
|
let showFiles: Bool
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
Text(thread.title)
|
|
.font(.title2)
|
|
.fontWeight(.bold)
|
|
|
|
HStack {
|
|
Text(thread.creationDate, style: .date)
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
|
|
Spacer()
|
|
}
|
|
|
|
if showFiles && !thread.files.isEmpty {
|
|
FilesView(files: thread.files)
|
|
}
|
|
|
|
if !thread.text.isEmpty {
|
|
Text(thread.text)
|
|
.font(.body)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct CommentView: View {
|
|
let comment: Comment
|
|
let showFiles: Bool
|
|
@EnvironmentObject var settings: Settings
|
|
|
|
var body: some View {
|
|
if settings.compactMode {
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
HStack {
|
|
Text("ID: \(comment.id)")
|
|
.font(.caption2)
|
|
.foregroundColor(.secondary)
|
|
|
|
Spacer()
|
|
|
|
Text(comment.creationDate, style: .date)
|
|
.font(.caption2)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
|
|
if !comment.text.isEmpty {
|
|
Text(comment.formattedText)
|
|
.font(.caption)
|
|
.lineLimit(2)
|
|
}
|
|
|
|
if showFiles && !comment.files.isEmpty {
|
|
HStack {
|
|
Image(systemName: "paperclip")
|
|
.foregroundColor(.blue)
|
|
.font(.caption2)
|
|
Text("\(comment.files.count)")
|
|
.font(.caption2)
|
|
.foregroundColor(.secondary)
|
|
Spacer()
|
|
}
|
|
}
|
|
}
|
|
.padding(.horizontal, 8)
|
|
.padding(.vertical, 6)
|
|
.background(Color(.systemGray6))
|
|
.cornerRadius(6)
|
|
} else {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
HStack {
|
|
Text("ID: \(comment.id)")
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
|
|
Spacer()
|
|
|
|
Text(comment.creationDate, style: .date)
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
|
|
if showFiles && !comment.files.isEmpty {
|
|
FilesView(files: comment.files)
|
|
}
|
|
|
|
if !comment.text.isEmpty {
|
|
Text(comment.formattedText)
|
|
.font(.body)
|
|
}
|
|
}
|
|
.padding()
|
|
.background(Color(.systemGray6))
|
|
.cornerRadius(8)
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
NavigationView {
|
|
ThreadDetailView(
|
|
board: Board(code: "test", description: "Test board"),
|
|
thread: Thread(id: 1, title: "Test Thread", text: "Test content", creation: "2023-01-01T00:00:00Z", board: "test", rating: nil, pinned: nil, files: [])
|
|
)
|
|
.environmentObject(Settings())
|
|
.environmentObject(APIClient())
|
|
}
|
|
} |