// // NewCharacterListView.swift // SodaLive // // Created by klaus on 9/12/25. // import SwiftUI struct NewCharacterListView: View { @StateObject private var viewModel = NewCharacterListViewModel() private let horizontalPadding: CGFloat = 12 private let gridSpacing: CGFloat = 12 var body: some View { NavigationStack { BaseView(isLoading: $viewModel.isLoading) { VStack(spacing: 8) { // Toolbar DetailNavigationBar(title: "신규 캐릭터 전체보기") VStack(alignment: .leading, spacing: 12) { // 전체 n개 HStack(spacing: 0) { Text("전체 ") .font(.custom(Font.preRegular.rawValue, size: 12)) .foregroundColor(Color(hex: "e2e2e2")) Text("\(viewModel.totalCount)") .font(.custom(Font.preRegular.rawValue, size: 12)) .foregroundColor(Color(hex: "ff5c49")) Text("개") .font(.custom(Font.preRegular.rawValue, size: 12)) .foregroundColor(Color(hex: "e2e2e2")) Spacer() } .padding(.horizontal, 24) // Grid 3열 GeometryReader { geo in let totalSpacing: CGFloat = gridSpacing * 2 let width = (geo.size.width - (horizontalPadding * 2) - totalSpacing) / 3 ScrollView(.vertical, showsIndicators: false) { LazyVGrid( columns: Array( repeating: GridItem( .flexible(), spacing: gridSpacing, alignment: .topLeading ), count: 3 ), alignment: .leading, spacing: gridSpacing ) { ForEach(viewModel.items.indices, id: \.self) { idx in let item = viewModel.items[idx] NavigationLink(value: item.characterId) { CharacterItemView( character: item, size: width, rank: 0, isShowRank: false ) .onAppear { viewModel.loadMoreIfNeeded(currentIndex: idx) } } } } .padding(.horizontal, horizontalPadding) if viewModel.isLoadingMore { HStack { Spacer() ProgressView() .progressViewStyle(CircularProgressViewStyle(tint: .white)) .padding(.vertical, 16) Spacer() } } } } .frame(minHeight: 0, maxHeight: .infinity) } .padding(.vertical, 12) .onAppear { // 최초 1회만 로드하여 상세 진입 후 복귀 시 스크롤 위치가 유지되도록 함 if viewModel.items.isEmpty { viewModel.fetch() } } } .background(Color.black) } .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) { GeometryReader { geo in HStack { Spacer() Text(viewModel.errorMessage) .padding(.vertical, 13.3) .frame(width: geo.size.width - 66.7, alignment: .center) .font(.custom(Font.medium.rawValue, size: 12)) .background(Color.button) .foregroundColor(Color.white) .multilineTextAlignment(.center) .cornerRadius(20) .padding(.top, 66.7) Spacer() } } } .navigationDestination(for: Int.self) { characterId in CharacterDetailView(characterId: characterId) } } } } #Preview { NewCharacterListView() .background(Color.black) }