Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ struct BundleDetailView<Route: BundleRoute>: View {
@State var isNavigatingDeeper: Bool = true
@State private var dismissTask: Task<Void, Never>?
@State private var readyDelayTask: Task<Void, Never>?
@State private var sceneReloadTrigger = UUID()

/// MultiKeyringScene에 전달할 키링 데이터 리스트
@State var keyringDataList: [MultiKeyringScene.KeyringData] = []
Expand Down Expand Up @@ -90,27 +91,14 @@ struct BundleDetailView<Route: BundleRoute>: View {
withAnimation(.easeOut(duration: 0.3)) {
isSceneReady = true
}
// 마지막으로 로드한 구성 id를 뷰모델에 저장 (뷰모델이 다음 진입 시 동일 구성 판정)
bundleVM.updateLastConfigIds(
background: bundleVM.selectedBackground,
carabiner: bundleVM.selectedCarabiner,
keyringDataList: keyringDataList
)
}
}
}
} //: onAllKeyringsReady
)
.animation(.easeInOut(duration: 0.3), value: isSceneReady)
/// 씬 재생성 조건을 위한 ID 설정 -> 배경, 카라비너, 키링 구성이 변경되면 씬을 완전히 재생성
.id("scene_\(background.id ?? "")_\(carabiner.id ?? "")_\(keyringDataList.map { "\($0.index)_\($0.bodyImageURL.hashValue)" }.joined(separator: "_"))")
.onAppear {
bundleVM.returnBackgroundId = bundle.selectedBackground
bundleVM.returnCarabinerId = bundle.selectedCarabiner
bundleVM.returnKeyringsId = bundle.keyrings
.sorted()
.joined(separator: "|")
}
.id(sceneId(background: background, carabiner: carabiner))

VStack {
Spacer()
Expand Down Expand Up @@ -222,6 +210,17 @@ extension BundleDetailView {
let newKeyringDataList = await bundleVM.createKeyringDataList(bundle: bundle, carabiner: carabiner)
keyringDataList = newKeyringDataList

// 항상 lastConfigIds 업데이트
let bgId = bundleVM.makeBackgroundId(bundleVM.selectedBackground)
let cbId = bundleVM.makeCarabinerId(bundleVM.selectedCarabiner)
let krId = bundleVM.makeKeyringsId(newKeyringDataList)

bundleVM.lastBackgroundIdForDetail = bgId
bundleVM.lastCarabinerIdForDetail = cbId
bundleVM.lastKeyringsIdForDetail = krId

sceneReloadTrigger = UUID()

// 키링 데이터까지 불러오고 난 후에도 키링의 개수가 0개라면 바로 씬을 준비 완료 상태로 체크
if newKeyringDataList.isEmpty {
isSceneReady = true
Expand Down Expand Up @@ -289,12 +288,17 @@ extension BundleDetailView {
@MainActor
private func handleBundleChange() async {
guard let bundle = bundleVM.selectedBundle else { return }

// 동일 구성인지 확인(+변경 감지)
if bundleVM.shouldSkipReloadForReturnedConfig() {
let shouldSkip = bundleVM.shouldSkipReloadForReturnedConfig()

if shouldSkip {
restoreSceneIfNeeded(bundle)
isSceneReady = true
return
}

// 리로드 진행 - loadBundleData 호출
isSceneReady = false
readyDelayTask?.cancel()
readyDelayTask = nil
Expand All @@ -312,6 +316,19 @@ extension BundleDetailView {
if bundleVM.selectedCarabiner == nil {
bundleVM.selectedCarabiner = bundleVM.resolveCarabiner(from: bundle.selectedCarabiner)
}

// keyringDataList도 복원 (NameEditView에서 돌아올 때 필요)
if keyringDataList.isEmpty, let carabiner = bundleVM.selectedCarabiner {
Task {
let newKeyringDataList = await bundleVM.createKeyringDataList(bundle: bundle, carabiner: carabiner)
await MainActor.run {
keyringDataList = newKeyringDataList
isSceneReady = true
}
}
return
}

readyDelayTask?.cancel()
readyDelayTask = nil
isSceneReady = true
Expand All @@ -329,6 +346,19 @@ extension BundleDetailView {
}
.joined(separator: ";")
}

private func sceneId(
background: Background,
carabiner: Carabiner
) -> String {
let bgId = background.id ?? ""
let cbId = carabiner.id ?? ""
let krIds = keyringDataList
.map { "\($0.index)_\($0.bodyImageURL.hashValue)" }
.joined(separator: "_")

return "scene_\(bgId)_\(cbId)_\(krIds)_\(sceneReloadTrigger.uuidString)"
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ extension BundleEditView {
private var customNavigationBar: some View {
CustomNavigationBar {
BackToolbarButton {
bundleVM.lastBackgroundIdForDetail = ""
bundleVM.lastCarabinerIdForDetail = ""
bundleVM.lastKeyringsIdForDetail = ""

isNavigatingAway = true
router.pop()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ struct BundleNameEditView<Route: BundleRoute>: View {
morePadding = 40
}
TabBarManager.hide()

Task {
await withCheckedContinuation { (continuation: CheckedContinuation<Void, Never>) in
bundleVM.fetchAllBackgrounds { _ in
bundleVM.fetchAllCarabiners { _ in
continuation.resume()
}
}
}
}
}
// 키보드 높이 변경 시 SwiftUI 애니메이션 비활성화
.onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { notification in
Expand Down Expand Up @@ -150,16 +160,61 @@ extension BundleNameEditView {
private var customNavigationBar: some View {
CustomNavigationBar {
BackToolbarButton {
router.pop()

if let bundle = bundleVM.selectedBundle {
// resolveBackground/Carabiner가 nil을 반환할 수 있으므로 확인
guard let bg = bundleVM.resolveBackground(from: bundle.selectedBackground),
let cb = bundleVM.resolveCarabiner(from: bundle.selectedCarabiner) else {
router.pop()
return
}

let bgId = bundleVM.makeBackgroundId(bg)
let cbId = bundleVM.makeCarabinerId(cb)

bundleVM.returnBackgroundId = bgId
bundleVM.returnCarabinerId = cbId

Task {
var krList: [MultiKeyringScene.KeyringData] = []
krList = await bundleVM.createKeyringDataList(bundle: bundle, carabiner: cb)
let krId = bundleVM.makeKeyringsId(krList)

await MainActor.run {
bundleVM.returnKeyringsId = krId
// returnIds 설정 완료, pop
router.pop()
}
}
} else {
// bundle이 nil, 바로 pop
router.pop()
}
}
} center: {
EmptyView()
} trailing: {
NextToolbarButton {
handleCheckButtonTap()
}
.disabled(isUpdating || bundleName.isEmpty || bundleName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || hasProfanity)
completeButton
}
}

private var completeButton: some View {
let isCompleteEnabled = !isUpdating &&
!bundleName.isEmpty &&
!bundleName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty &&
!hasProfanity

return Button {
guard isCompleteEnabled else { return }
handleCheckButtonTap()
} label: {
Text("완료")
.typography(.suit17B)
.foregroundStyle(isCompleteEnabled ? .main500 : .gray200)
}
.frame(width: 62, height: 44)
.glassEffect(.regular.interactive(), in: .capsule)
.disabled(!isCompleteEnabled)
}

private func handleCheckButtonTap() {
Expand All @@ -182,8 +237,8 @@ extension BundleNameEditView {
let bgId = bundleVM.makeBackgroundId(bg)
let cbId = bundleVM.makeCarabinerId(cb)

// keyringsId는 현재 번들의 구성 기반으로 재구성
Task {
// returnIds 먼저 설정
var krList: [MultiKeyringScene.KeyringData] = []
if let carabiner = cb {
krList = await bundleVM.createKeyringDataList(bundle: bundle, carabiner: carabiner)
Expand All @@ -195,14 +250,15 @@ extension BundleNameEditView {
bundleVM.returnCarabinerId = cbId
bundleVM.returnKeyringsId = krId
}
}

bundleVM.updateBundleName(bundle: bundle, newName: bundleName.trimmingCharacters(in: .whitespacesAndNewlines)) { success in
DispatchQueue.main.async {
self.isUpdating = false
if success {
bundleVM.selectedBundle?.name = self.bundleName.trimmingCharacters(in: .whitespacesAndNewlines)
router.pop()

// returnIds 설정 완료 후 이름 업데이트
bundleVM.updateBundleName(bundle: bundle, newName: bundleName.trimmingCharacters(in: .whitespacesAndNewlines)) { success in
DispatchQueue.main.async {
self.isUpdating = false
if success {
bundleVM.selectedBundle?.name = self.bundleName.trimmingCharacters(in: .whitespacesAndNewlines)
router.pop()
}
}
}
}
Expand Down