import SwiftUI struct NewHomeTab: View { @ObservedObject var viewModel: NewHomeTabViewModel // Ширина колонки теперь вычисляется в ViewModel, но нам она нужна и здесь для PostGridItem private let columnWidth = (UIScreen.main.bounds.width - 14) / 2 var body: some View { VStack { if viewModel.isLoading { ProgressView("Загрузка ленты...") } else { RefreshableScrollView(isRefreshing: $viewModel.isRefreshing, onRefresh: { viewModel.refreshData() }) { HStack(alignment: .top, spacing: 6) { LazyVStack(spacing: 6) { ForEach(viewModel.column1Posts) { post in PostGridItem(post: post, width: columnWidth) } } LazyVStack(spacing: 6) { ForEach(viewModel.column2Posts) { post in PostGridItem(post: post, width: columnWidth) } } } .padding(.horizontal, 4) } } } .onAppear { viewModel.fetchDataIfNeeded() } // .background(Color(.secondarySystemBackground)) // Фон для всей вкладки } } struct PostGridItem: View { let post: Post let width: CGFloat // Ширина элемента @State private var isNavigationActive = false // Состояние для программной навигации // Формируем URL для загрузки изображения private var imageURL: URL? { // Используем picsum.photos для получения уникального изображения для каждого поста // Используем реальные размеры для большей вариативности if let media = post.media.first, let w = media.width, let h = media.height { return URL(string: "https://picsum.photos/seed/\(post.id.uuidString)/\(w)/\(h)") } return URL(string: "https://picsum.photos/seed/\(post.id.uuidString)/400/400") } // Вычисляем высоту изображения на основе данных из модели private var imageHeight: CGFloat { guard let media = post.media.first, let mediaWidth = media.width, let mediaHeight = media.height, mediaWidth > 0 else { return width // Возвращаем 1:1, если данных нет } let aspectRatio = CGFloat(mediaHeight) / CGFloat(mediaWidth) return width * aspectRatio } var body: some View { ZStack { // Невидимая ссылка для навигации, активируемая состоянием NavigationLink(destination: PostDetailView(post: post), isActive: $isNavigationActive) { EmptyView() } .opacity(0) // Видимый контент карточки VStack(alignment: .leading, spacing: 0) { // Убираем отступ между картинкой и текстом // 1. Медиа контент if let url = imageURL { // Создаем контейнер с четкими границами, чтобы избежать перекрытия Color.clear .frame(width: width, height: imageHeight) // Используем вычисленную высоту .background( RemoteImageView(url: url) .scaledToFill() ) .clipped() .overlay( // Наложение для дополнительной информации ZStack { // Верхний ряд: дата и иконка видео VStack { HStack { Text(post.createdAt, style: .relative) .font(.caption2) .foregroundColor(.white) .padding(.horizontal, 6) .padding(.vertical, 3) .background(Color.black.opacity(0.5)) .clipShape(Capsule()) Spacer() if post.isVideo { Image(systemName: "play.circle.fill") .font(.title3) .foregroundColor(.white) .shadow(radius: 2) } } Spacer() } .padding(6) // Нижний ряд: просмотры VStack { Spacer() HStack { HStack(spacing: 4) { Image(systemName: "eye.fill") Text("\(post.views)") } .font(.caption2) .foregroundColor(.white) .padding(.horizontal, 6) .padding(.vertical, 3) .background(Color.black.opacity(0.5)) .clipShape(Capsule()) Spacer() } } .padding(6) } ) .contentShape(Rectangle()) // Указываем форму для жеста .onTapGesture { isNavigationActive = true // Активируем навигацию по тапу на картинку } } // Контейнер для текста, который создает эффект "расширения" VStack(alignment: .leading, spacing: 8) { // 2. Название поста if let title = post.title, !title.isEmpty { Text(title) .font(.subheadline) .lineLimit(2) .contentShape(Rectangle()) // И по тапу на заголовок .onTapGesture { isNavigationActive = true } } // 3. Информация об авторе и лайки HStack { Button(action: { print("account \(post.id)") // пока ничего не делаем }) { HStack(spacing: 4) { Image(systemName: "person.circle.fill") .resizable() .frame(width: 20, height: 20) .foregroundColor(.gray) Text(post.authorUsername) .font(.footnote) .lineLimit(1) .foregroundColor(.primary) } } // .buttonStyle больше не нужен, т.к. нет конфликта Spacer() Button(action: { print("like \(post.id)") // пока ничего не делаем }) { HStack(spacing: 4) { Image(systemName: post.isLikedByCurrentUser ? "heart.fill" : "heart") .foregroundColor(post.isLikedByCurrentUser ? .red : .primary) Text("\(post.likes)") .font(.subheadline) .foregroundColor(.primary) } } } } .padding(8) .background(Color(UIColor.systemBackground)) // Фон только для текстовой части } .cornerRadius(6) // Закругляем всю карточку .clipped() // Обрезаем дочерние вью по закругленной форме родителя .shadow(color: Color.black.opacity(0.1), radius: 5, x: 0, y: 2) } } }