Files
element-x-ios/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/GalleryGridView.swift
2026-08-03 14:33:12 +02:00

85 lines
3.1 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// Copyright 2026 Element Creations Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import Compound
import SwiftUI
struct GalleryGridView: View {
let items: [GalleryItem]
let mediaProvider: MediaProviderProtocol?
let contentScannerService: ContentScannerServiceProtocol?
let onItemTap: (Int) -> Void
static let spacing: CGFloat = 4
static let groupWidth: CGFloat = 264
static let maxVisible = 5
private var visibleCount: Int {
min(items.count, Self.maxVisible)
}
/// Number shown on the overflow tile when items.count > maxVisible.
private var overflowCount: Int {
// Represents the last visible item + the rest.
items.count > Self.maxVisible ? items.count - Self.maxVisible + 1 : 0
}
private struct Row {
let indices: [Int]
let height: CGFloat
}
/// Hero-style layout: 1 = full-width, 2 = two full-width stacked rows, 3 = full-width hero
/// over two squares, 4 = 2×2 squares, 5+ = top row two squares, bottom row three squares.
private var rows: [Row] {
switch visibleCount {
case 1: return [Row(indices: [0], height: 130)]
case 2: return [Row(indices: [0], height: 130),
Row(indices: [1], height: 130)]
case 3: return [Row(indices: [0], height: 130),
Row(indices: [1, 2], height: 130)]
case 4: return [Row(indices: [0, 1], height: 130),
Row(indices: [2, 3], height: 130)]
case 5: return [Row(indices: [0, 1], height: 130),
Row(indices: [2, 3, 4], height: 85)]
default: return []
}
}
var body: some View {
VStack(spacing: Self.spacing) {
ForEach(Array(rows.enumerated()), id: \.offset) { _, row in
let totalSpacing = CGFloat(row.indices.count - 1) * Self.spacing
let tileWidth = (Self.groupWidth - totalSpacing) / CGFloat(row.indices.count)
HStack(spacing: Self.spacing) {
ForEach(row.indices, id: \.self) { index in
tile(at: index, size: CGSize(width: tileWidth, height: row.height))
}
}
}
}
.clipShape(RoundedRectangle(cornerRadius: 6))
}
@ViewBuilder
private func tile(at index: Int, size: CGSize) -> some View {
if items.indices.contains(index) {
let isLastVisible = index == visibleCount - 1
let overflow = (overflowCount > 0 && isLastVisible) ? overflowCount : 0
GalleryItemTileView(item: items[index],
position: index + 1,
mediaProvider: mediaProvider,
contentScannerService: contentScannerService,
overflowCount: overflow) {
onItemTap(index)
}
.frame(width: size.width, height: size.height)
}
}
}