Fix media list UI

This commit is contained in:
Benoit Martins 2026-02-10 15:42:05 +01:00
parent 112d7bbaa9
commit b753b5925e
3 changed files with 121 additions and 84 deletions

View file

@ -2,6 +2,6 @@ import Foundation
public enum AppGitInfo {
public static let branch = "feature/medias_and_documents_lists"
public static let commit = "cad143172"
public static let commit = "ab47b1ab5"
public static let tag = "6.1.0-alpha"
}

View file

@ -69,10 +69,12 @@ struct ConversationDocumentsListFragment: View {
VStack(spacing: 0) {
List {
ForEach(conversationDocumentsListViewModel.documentsList, id: \.path) { file in
MediaGridItemView(file: file)
.background()
DocumentRow(file: file)
.padding(.vertical, 4)
.padding(.horizontal, 8)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.listRowSeparator(.hidden)
.listRowBackground(Color.clear)
}
}
.safeAreaInset(edge: .top, content: {
@ -82,7 +84,7 @@ struct ConversationDocumentsListFragment: View {
.listStyle(.plain)
.overlay(
VStack {
if true {
if conversationDocumentsListViewModel.documentsList.isEmpty {
Spacer()
Text("conversation_no_document_found")
.multilineTextAlignment(.leading)
@ -90,7 +92,7 @@ struct ConversationDocumentsListFragment: View {
Spacer()
}
}
.padding(.all)
.padding(.all)
)
}
.frame(maxWidth: .infinity)
@ -111,34 +113,62 @@ struct ConversationDocumentsListFragment: View {
}
struct DocumentRow: View {
@State private var selectedURLAttachment: URL?
@ObservedObject var file: FileModel
var body: some View {
ZStack(alignment: .bottomTrailing) {
if let previewPath = file.mediaPreview,
let image = UIImage(contentsOfFile: previewPath) {
Image(uiImage: image)
.resizable()
.scaledToFill()
.frame(height: 110)
.clipped()
} else {
Rectangle()
.fill(Color.gray.opacity(0.2))
.frame(height: 110)
HStack {
VStack {
Image(getImageOfType(filename: file.fileName, type: file.mimeTypeString))
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.grayMain2c700)
.frame(width: 60, height: 60, alignment: .leading)
}
if let duration = file.audioVideoDuration, file.isVideoPreview {
Text(duration)
.font(.caption2)
.padding(6)
.background(Color.black.opacity(0.6))
.foregroundColor(.white)
.clipShape(RoundedRectangle(cornerRadius: 6))
.padding(6)
.frame(width: 100, height: 100)
.background(Color.grayMain2c200)
.onTapGesture {
selectedURLAttachment = URL(fileURLWithPath: file.originalPath)
}
VStack {
Text(file.fileName)
.foregroundStyle(Color.grayMain2c700)
.default_text_style_600(styleSize: 14)
.truncationMode(.middle)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
if file.fileSize > 0 {
Text(Int(file.fileSize).formatBytes())
.default_text_style_300(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
}
}
.padding(.horizontal, 10)
.frame(maxWidth: .infinity, alignment: .leading)
}
.background(.white)
.clipShape(RoundedRectangle(cornerRadius: 10))
.onTapGesture {
selectedURLAttachment = URL(fileURLWithPath: file.originalPath)
}
}
func getImageOfType(filename: String, type: String) -> String {
print("mimemime \(type)")
if type == "audio/mpeg" {
return "file-audio"
} else if type == "application/pdf"
|| filename.lowercased().hasSuffix(".pdf") == true {
return "file-pdf"
} else if type.hasPrefix("text/") == true
|| ["txt", "md", "json", "xml", "csv", "log"].contains(filename.split(separator: ".").last?.lowercased()) {
return "file-text"
} else {
return "file"
}
.cornerRadius(8)
}
}

View file

@ -86,33 +86,42 @@ struct ConversationMediaListFragment: View {
struct ConversationMediaGridView: View {
@ObservedObject var viewModel: ConversationMediaListViewModel
private let columns = [
GridItem(.flexible(), spacing: 1),
GridItem(.flexible(), spacing: 1),
GridItem(.flexible(), spacing: 1)
]
@State private var selectedURLAttachment: URL?
private let columns = 4
private let spacing: CGFloat = 2
var body: some View {
VStack(spacing: 0) {
if !viewModel.mediaList.isEmpty && !viewModel.operationInProgress {
ScrollView {
LazyVGrid(columns: columns, spacing: 1) {
ForEach(viewModel.mediaList, id: \.path) { file in
MediaGridItemView(file: file)
.onTapGesture {
//viewModel.openMediaEvent.send(file)
}
.onAppear {
if file == viewModel.mediaList.last {
viewModel.loadMoreData(totalItemsCount: viewModel.mediaList.count)
GeometryReader { geometry in
let totalSpacing = spacing * CGFloat(columns - 1)
let itemWidth = (geometry.size.width - totalSpacing) / CGFloat(columns)
ScrollView {
LazyVGrid(
columns: Array(repeating: GridItem(.fixed(itemWidth), spacing: spacing), count: columns),
spacing: spacing
) {
ForEach(viewModel.mediaList, id: \.path) { file in
MediaGridItemView(file: file)
.aspectRatio(1, contentMode: .fit)
.frame(width: itemWidth, height: itemWidth)
.clipped()
.onTapGesture {
selectedURLAttachment = URL(fileURLWithPath: file.originalPath)
}
}
.onAppear {
if file == viewModel.mediaList.last {
viewModel.loadMoreData(totalItemsCount: viewModel.mediaList.count)
}
}
}
}
.padding(.horizontal, spacing)
.padding(.top, spacing)
}
.padding(.horizontal, 2)
.padding(.top, 12)
}
.quickLookPreview($selectedURLAttachment, in: viewModel.mediaList.compactMap { URL(fileURLWithPath: $0.originalPath) })
} else if viewModel.mediaList.isEmpty && !viewModel.operationInProgress {
Spacer()
Text("conversation_no_media_found")
@ -125,49 +134,47 @@ struct ConversationMediaGridView: View {
}
struct MediaGridItemView: View {
@ObservedObject var file: FileModel
var body: some View {
ZStack(alignment: .bottomTrailing) {
if let previewPath = file.mediaPreview,
let image = UIImage(contentsOfFile: previewPath) {
Image(uiImage: image)
.resizable()
.scaledToFill()
.frame(width: 120, height: 120)
.clipped()
} else {
Rectangle()
.fill(Color.gray.opacity(0.2))
.frame(width: 120, height: 120)
}
if file.isVideoPreview {
VStack {
Spacer()
Image("play-fill")
.renderingMode(.template)
GeometryReader { geo in
ZStack(alignment: .bottomTrailing) {
if let previewPath = file.mediaPreview,
let image = UIImage(contentsOfFile: previewPath) {
Image(uiImage: image)
.resizable()
.foregroundStyle(.white)
.frame(width: 35, height: 35)
Spacer()
.scaledToFill()
.frame(width: geo.size.width, height: geo.size.height)
.clipped()
} else {
Rectangle()
.fill(Color.gray.opacity(0.2))
.frame(width: geo.size.width, height: geo.size.height)
}
if file.isVideoPreview {
Image("play-fill")
.resizable()
.renderingMode(.template)
.scaledToFit()
.frame(width: geo.size.width * 0.3, height: geo.size.height * 0.3)
.foregroundColor(.white)
.shadow(radius: 2)
.position(x: geo.size.width / 2, y: geo.size.height / 2)
}
if let duration = file.audioVideoDuration, file.isVideoPreview {
Text(duration)
.font(.caption2)
.padding(4)
.background(Color.black.opacity(0.6))
.foregroundColor(.white)
.clipShape(RoundedRectangle(cornerRadius: 4))
.padding(6)
}
.frame(width: 120, height: 120)
}
if let duration = file.audioVideoDuration, file.isVideoPreview {
Text(duration)
.font(.caption2)
.padding(6)
.background(Color.black.opacity(0.6))
.foregroundColor(.white)
.clipShape(RoundedRectangle(cornerRadius: 6))
.padding(6)
}
.cornerRadius(8)
}
.cornerRadius(8)
.aspectRatio(1, contentMode: .fit)
}
}