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
40 changes: 26 additions & 14 deletions GAMSS/Sources/Core/Components/Modal/ModalContainerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import SwiftUI

struct ModalContainerView<Content: View>: View {
@Binding private var isPresented: Bool
@State private var isAppeared = false

private let dismissOnBackgroundTap: Bool
private let content: () -> Content
Expand All @@ -24,22 +25,33 @@ struct ModalContainerView<Content: View>: View {
}

var body: some View {
ZStack {
Color.colorBlack
.opacity(0.7)
.ignoresSafeArea()
.onTapGesture {
guard dismissOnBackgroundTap else { return }
Color.clear
.frame(width: 0, height: 0)
.fullScreenCover(isPresented: $isPresented) {
ZStack {
Color.colorBlack
.opacity(isAppeared ? 0.7 : 0)
.ignoresSafeArea()
.onTapGesture {
guard dismissOnBackgroundTap else { return }
isPresented = false
}

withAnimation(.easeInOut(duration: 0.25)) {
isPresented = false
content()
.padding(.horizontal, 28)
.scaleEffect(isAppeared ? 1 : 0.9)
.opacity(isAppeared ? 1 : 0)
}
.presentationBackground(.clear)
.onAppear {
withAnimation(.easeOut(duration: 0.25)) {
isAppeared = true
}
}

content()
.transition(.scale.combined(with: .opacity))
.padding(.horizontal, 28)
}
.animation(.easeInOut(duration: 0.25), value: isPresented)
.onDisappear {
isAppeared = false
}
}
.transaction { $0.disablesAnimations = true }
}
}
48 changes: 22 additions & 26 deletions GAMSS/Sources/Presentation/Chat/ChatView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,20 @@ struct ChatView: View {
ZStack(alignment: .topTrailing) {
chatContent

if viewModel.isEndConfirmationPresented {
ModalContainerView(isPresented: $viewModel.isEndConfirmationPresented) {
ModalContentView(
title: "대화를 종료하고 감정 기록을 생성할게요",
subtitle: "감정 기록 생성 시 대화는 종료되며,\n더 이상 대화를 이어갈 수 없어요.",
actions: [
.init(title: "뒤로가기", style: .secondary, action: {
viewModel.isEndConfirmationPresented = false
}),
.init(title: "기록 생성하기", style: .primary, action: {
viewModel.isEndConfirmationPresented = false
Task { await viewModel.confirmEndConversation() }
})
]
)
}
ModalContainerView(isPresented: $viewModel.isEndConfirmationPresented) {
ModalContentView(
title: "대화를 종료하고 감정 기록을 생성할게요",
subtitle: "감정 기록 생성 시 대화는 종료되며,\n더 이상 대화를 이어갈 수 없어요.",
actions: [
.init(title: "뒤로가기", style: .secondary, action: {
viewModel.isEndConfirmationPresented = false
}),
.init(title: "기록 생성하기", style: .primary, action: {
viewModel.isEndConfirmationPresented = false
Task { await viewModel.confirmEndConversation() }
})
]
)
}

if viewModel.isTokenUsagePopoverPresented {
Expand All @@ -71,16 +69,14 @@ struct ChatView: View {
.padding(.trailing, Spacing.spacing400)
}

if viewModel.riskDetection != nil {
ModalContainerView(isPresented: Binding(
get: { viewModel.riskDetection != nil },
set: { if !$0 { viewModel.riskDetection = nil } }
)) {
SupportAgencyDialogContentView(
detection: viewModel.riskDetection ?? .none,
onDismiss: { viewModel.riskDetection = nil }
)
}
ModalContainerView(isPresented: Binding(
get: { viewModel.riskDetection != nil },
set: { if !$0 { viewModel.riskDetection = nil } }
)) {
SupportAgencyDialogContentView(
detection: viewModel.riskDetection ?? .none,
onDismiss: { viewModel.riskDetection = nil }
)
}

if viewModel.isEnding {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct ConversationListView: View {
@StateObject private var viewModel: ConversationListViewModel
@State private var selectedConversation: ConversationSummary?
@State private var isSettingPresented = false
@State private var isDeleteConversationPresented = false

init(viewModel: ConversationListViewModel) {
_viewModel = StateObject(wrappedValue: viewModel)
Expand Down Expand Up @@ -83,7 +84,7 @@ struct ConversationListView: View {

if viewModel.currentMode == .delete {
Button {
Task { await viewModel.deleteConversations() }
isDeleteConversationPresented = true
} label: {
Text("삭제하기")
.typography(.body3Medium)
Expand Down Expand Up @@ -140,6 +141,23 @@ struct ConversationListView: View {
)) {
Button("확인", role: .cancel) {}
}
.background {
ModalContainerView(isPresented: $isDeleteConversationPresented) {
ModalContentView(
title: "대화를 정말 삭제할까요?",
subtitle: "대화를 이어갈 수 없으며, 카드를 생성할 수 없습니다.\n채팅은 영구적으로 삭제됩니다.",
actions: [
.init(title: "뒤로가기", style: .secondary, action: {
isDeleteConversationPresented = false
}),
.init(title: "삭제하기", style: .destructive, action: {
isDeleteConversationPresented = false
Task { await viewModel.deleteConversations() }
})
]
)
}
}
}

private var dateHeader: some View {
Expand Down
58 changes: 27 additions & 31 deletions GAMSS/Sources/Presentation/Setting/Account/AccountView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,43 +46,39 @@ struct AccountView: View {
Button("확인", role: .cancel) {}
}

if isLogoutModalPresented {
ModalContainerView(isPresented: $isLogoutModalPresented) {
ModalContentView(title: "로그아웃 하시겠어요?", actions: [
.init(title: "로그아웃", style: .secondary, action: {
isLogoutModalPresented = false
ModalContainerView(isPresented: $isLogoutModalPresented) {
ModalContentView(title: "로그아웃 하시겠어요?", actions: [
.init(title: "로그아웃", style: .secondary, action: {
isLogoutModalPresented = false

Task {
await viewModel.logout()
loginSession.value = .current
}
}),
.init(title: "마저 사용하기", style: .primary, action: {
isLogoutModalPresented = false
})
])
}

ModalContainerView(isPresented: $isWithdrawModalPresented) {
ModalContentView(
title: "정말 탈퇴하시겠어요?",
subtitle: "회원 탈퇴 시 지금까지 기록된 카드와 대화 내용은\n 영원히 삭제되며 복구되지 않아요.",
actions: [
.init(title: "뒤로가기", style: .secondary, action: {
isWithdrawModalPresented = false
}),
.init(title: "탈퇴하기", style: .destructive, action: {
isWithdrawModalPresented = false

Task {
await viewModel.logout()
await viewModel.deleteMember()
loginSession.value = .current
}
}),
.init(title: "마저 사용하기", style: .primary, action: {
isLogoutModalPresented = false
})
])
}
}

if isWithdrawModalPresented {
ModalContainerView(isPresented: $isWithdrawModalPresented) {
ModalContentView(
title: "정말 탈퇴하시겠어요?",
subtitle: "회원 탈퇴 시 지금까지 기록된 카드와 대화 내용은\n 영원히 삭제되며 복구되지 않아요.",
actions: [
.init(title: "뒤로가기", style: .secondary, action: {
isWithdrawModalPresented = false
}),
.init(title: "탈퇴하기", style: .destructive, action: {
isWithdrawModalPresented = false

Task {
await viewModel.deleteMember()
loginSession.value = .current
}
})
])
}
}
}
.hidesTabBar()
Expand Down
Loading