// // ChatBgSelectionView.swift // SodaLive // // Created by klaus on 9/3/25. // import SwiftUI struct ChatBgSelectionView: View { @StateObject var viewModel = ChatBgSelectionViewModel() private let columns = Array( repeating: GridItem(.flexible(), spacing: 0), count: 3 ) let characterId: Int64 let selectedBgImageId: Int let onTapBgImage: (CharacterImageListItemResponse) -> Void @Binding var isShowing: Bool var body: some View { BaseView(isLoading: $viewModel.isLoading) { VStack(spacing: 0) { DetailNavigationBar(title: "배경 사진 선택") { isShowing = false } // 갤러리 그리드 ScrollView { LazyVGrid(columns: columns, spacing: 0) { ForEach(viewModel.galleryItems, id: \.self) { item in galleryImageView(item: item) .onAppear { viewModel.loadMoreIfNeeded(currentItem: item) } } } .frame(maxWidth: screenSize().width) } Spacer() } .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) { GeometryReader { geo in HStack { Spacer() Text(viewModel.errorMessage) .padding(.vertical, 13.3) .frame(alignment: .center) .frame(maxWidth: .infinity) .padding(.horizontal, 33.3) .font(.custom(Font.medium.rawValue, size: 12)) .background(Color.button) .foregroundColor(Color.white) .multilineTextAlignment(.center) .cornerRadius(20) .padding(.top, 66.7) Spacer() } } } } .onAppear { viewModel.characterId = characterId viewModel.loadInitialData() } } @ViewBuilder private func galleryImageView(item: CharacterImageListItemResponse) -> some View { GeometryReader { geo in let width = geo.size.width let height = width * 5 / 4 ZStack(alignment: .bottomTrailing) { // 이미지 AsyncImage(url: URL(string: item.imageUrl)) { image in image .resizable() .scaledToFill() .frame(width: width, height: height) .clipped() } placeholder: { Rectangle() .fill(Color.gray.opacity(0.3)) } .overlay { Rectangle() .stroke(lineWidth: 5) .foregroundColor(.button) .opacity(selectedBgImageId == item.id ? 1 : 0) } if selectedBgImageId == item.id { Text("현재 배경") .font(.custom(Font.preRegular.rawValue, size: 12)) .foregroundColor(.white) .padding(.horizontal, 6) .padding(.vertical, 2) .background(Color.button) .cornerRadius(6.7) .padding(6) } } .frame(width: width, height: height) .clipped() .contentShape(Rectangle()) .onTapGesture { onTapBgImage(item) } } .aspectRatio(4/5, contentMode: .fit) } } #Preview { ChatBgSelectionView( characterId: 0, selectedBgImageId: 1, onTapBgImage: { _ in }, isShowing: .constant(true) ) }