203 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
			
		
		
	
	
			203 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
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)
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 |