import SwiftUI struct GiftBoxListView: View { @StateObject private var viewModel = GiftBoxListViewModel() @State private var hasAppeared = false var body: some View { VStack(spacing: 0) { DetailNavigationBar(title: "선물함") .frame(height: 54) BaseView(isLoading: .constant(viewModel.isLoading)) { GeometryReader { geometry in ScrollView { if viewModel.hasLoaded { if viewModel.items.isEmpty && isCreator { GiftBoxEmptyView { AppState.shared.setAppStep(step: GiftConstants.feedbackStep) } .frame(minHeight: geometry.size.height) } else { listContent } } } .refreshable { await viewModel.refresh() } } } .sodaToast(isPresented: $viewModel.isShowPopup, message: viewModel.errorMessage) } .background(Color.black.ignoresSafeArea()) .onAppear { guard !hasAppeared else { return } hasAppeared = true viewModel.fetchInitialPage() } } private var isCreator: Bool { UserDefaults.string(forKey: .role) == MemberRole.CREATOR.rawValue } private var listContent: some View { VStack(spacing: SodaSpacing.s12) { GiftBoxInfoBannerView(direction: viewModel.items.contains { GiftDisplayPolicy.listCard(from: $0)?.direction == .received } ? .received : .sent) LazyVStack(spacing: 0) { LazyVStack(spacing: SodaSpacing.s8) { ForEach(viewModel.items, id: \.applicationNo) { item in if let model = GiftDisplayPolicy.listCard(from: item) { GiftBoxCardView(model: model) { AppState.shared.setAppStep(step: .giftDetail(applicationNo: model.applicationNo)) } } } if viewModel.isLoadingMore { ProgressView() .tint(.white) .padding(SodaSpacing.s14) } } if let lastItem = viewModel.items.last { Color.clear .frame(height: 1) .allowsHitTesting(false) .accessibilityHidden(true) .id(lastItem.applicationNo) .onAppear { viewModel.fetchNextPage(currentItem: lastItem) } } } } .padding(.top, SodaSpacing.s8) .padding(.bottom, SodaSpacing.s14) } }