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
32 changes: 29 additions & 3 deletions ComfortableMove/ComfortableMove/Core/Manager/AlertManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ enum AlertType: Identifiable {
case bluetoothUnsupported
case bluetoothUnauthorized
case locationUnauthorized
case bluetoothConfirm(busName: String, onConfirm: () -> Void, onCancel: () -> Void)
case bluetoothSuccess
case bluetoothFailure

var id: String {
switch self {
Expand All @@ -23,13 +26,17 @@ enum AlertType: Identifiable {
case .bluetoothUnsupported: return "bluetoothUnsupported"
case .bluetoothUnauthorized: return "bluetoothUnauthorized"
case .locationUnauthorized: return "locationUnauthorized"
case .bluetoothConfirm: return "bluetoothConfirm"
case .bluetoothSuccess: return "bluetoothSuccess"
case .bluetoothFailure: return "bluetoothFailure"
}
}

var priority: Int {
switch self {
case .locationUnauthorized: return 3
case .bluetoothUnsupported, .bluetoothUnauthorized: return 2
case .bluetoothConfirm, .bluetoothSuccess, .bluetoothFailure: return 1
case .noBusInfo: return 1
case .apiError: return 0
}
Expand All @@ -47,6 +54,12 @@ enum AlertType: Identifiable {
return "블루투스 권한 필요"
case .locationUnauthorized:
return "위치 권한 필요"
case .bluetoothConfirm(let busName, _, _):
return "\(busName)버스에 배려석 알림을 전송하시겠습니까?"
case .bluetoothSuccess:
return "알림 전송 완료"
case .bluetoothFailure:
return "버스 배려석 알림 전송에 실패하였습니다."
}
}

Expand All @@ -62,31 +75,44 @@ enum AlertType: Identifiable {
return "블루투스 권한이 필요합니다.\n설정에서 블루투스 권한을 허용해주세요."
case .locationUnauthorized:
return "위치 권한이 필요합니다.\n설정에서 위치 권한을 허용해주세요."
case .bluetoothConfirm:
return ""
case .bluetoothSuccess:
return ""
case .bluetoothFailure:
return "다시 한번 시도해주세요."
}
}

var shouldBlockApp: Bool {
switch self {
case .bluetoothUnsupported, .bluetoothUnauthorized, .locationUnauthorized:
return true
case .noBusInfo, .apiError:
case .noBusInfo, .apiError, .bluetoothConfirm, .bluetoothSuccess, .bluetoothFailure:
return false
}
}

var primaryButtonText: String {
shouldBlockApp ? "설정으로 이동" : "확인"
}

var isConfirmAlert: Bool {
if case .bluetoothConfirm = self {
return true
}
return false
}
}

// MARK: - Alert Manager
class AlertManager: ObservableObject {
@Published var currentAlert: AlertType?

func showAlert(_ type: AlertType) {
// 현재 alert가 없거나, 새로운 alert의 우선순위가 더 높은 경우에만 표시
// 현재 alert가 없거나, 새로운 alert의 우선순위가 더 높거나 같은 경우 표시
if let current = currentAlert {
if type.priority > current.priority {
if type.priority >= current.priority {
currentAlert = type
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ extension BluetoothManager: CBCentralManagerDelegate {
return
}

// 버스 번호로 필터링
// 버스 번호로 필터링 (대소문자 무시)
guard let busNumber = BluetoothConfig.busNumber(from: finalDeviceName),
busNumber == targetBusNumber else {
busNumber.lowercased() == targetBusNumber?.lowercased() else {
Logger.log(message: "⚠️ 다른 버스(\(BluetoothConfig.busNumber(from: finalDeviceName) ?? "알 수 없음")번) - 무시")
return
}
Expand Down
105 changes: 56 additions & 49 deletions ComfortableMove/ComfortableMove/Core/Presentation/Home/HomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ struct HomeView: View {
@State private var nearestStation: StationItem? // 가장 가까운 정류소
@State private var isLoadingStation = false

// Alert 상태
@State private var showConfirmAlert = false
@State private var showSuccessAlert = false
@State private var showFailureAlert = false

// 화면 표시 상태
@State private var showHelpPage = false
Expand Down Expand Up @@ -74,8 +70,14 @@ struct HomeView: View {
VStack(spacing: 30) {
// 중앙 버튼
Button(action: {
Logger.log(message: "🔘 중앙 버튼 클릭 - selectedRouteName: \(selectedRouteName ?? "nil"), isButtonTapped: \(isButtonTapped)")
if selectedRouteName != nil && isButtonTapped {
showConfirmAlert = true
Logger.log(message: "✅ 확인 Alert 표시")
alertManager.showAlert(.bluetoothConfirm(
busName: selectedBusName,
onConfirm: { sendCourtesySeatNotification() },
onCancel: { resetButtonState() }
))
}
}) {
ZStack {
Expand Down Expand Up @@ -225,44 +227,8 @@ struct HomeView: View {
refreshBusArrivals()
}
}
.alert(isPresented: $showConfirmAlert) {
Alert(
title: Text("\(selectedBusName)버스에 배려석 알림을 전송하시겠습니까?"),
primaryButton: .destructive(Text("취소")) {
resetButtonState()
},
secondaryButton: .default(Text("확인")) {
sendCourtesySeatNotification()
}
)
}
.alert("알림 전송 완료", isPresented: $showSuccessAlert) {
Button("확인", role: .cancel) { }
}
.alert("버스 배려석 알림 전송에 실패하였습니다.", isPresented: $showFailureAlert) {
Button("확인", role: .cancel) { }
} message: {
Text("다시 한번 시도해주세요.")
}
.alert(item: $alertManager.currentAlert) { alertType in
if alertType.shouldBlockApp {
return Alert(
title: Text(alertType.title),
message: Text(alertType.message),
primaryButton: .default(Text(alertType.primaryButtonText)) {
alertManager.openSettings()
},
secondaryButton: .cancel(Text("취소"))
)
} else {
return Alert(
title: Text(alertType.title),
message: Text(alertType.message),
dismissButton: .default(Text(alertType.primaryButtonText)) {
alertManager.dismissAlert()
}
)
}
createAlert(for: alertType)
}
.overlay(
showHelpPage ? HelpPageView(isPresented: $showHelpPage) : nil
Expand All @@ -271,6 +237,43 @@ struct HomeView: View {
}
}

// MARK: - Create Alert
private func createAlert(for alertType: AlertType) -> Alert {
if case .bluetoothConfirm(_, let onConfirm, let onCancel) = alertType {
return Alert(
title: Text(alertType.title),
primaryButton: .default(Text("확인")) {
alertManager.dismissAlert()
onConfirm()
},
secondaryButton: .cancel(Text("취소")) {
alertManager.dismissAlert()
onCancel()
}
)
}

if alertType.shouldBlockApp {
return Alert(
title: Text(alertType.title),
message: Text(alertType.message),
primaryButton: .default(Text(alertType.primaryButtonText)) {
alertManager.openSettings()
},
secondaryButton: .cancel(Text("취소"))
)
}

let messageText: Text? = alertType.message.isEmpty ? nil : Text(alertType.message)
return Alert(
title: Text(alertType.title),
message: messageText,
dismissButton: .default(Text(alertType.primaryButtonText)) {
alertManager.dismissAlert()
}
)
}

// MARK: - Setup Alert Callbacks
private func setupAlertCallbacks() {
bluetoothManager.onBluetoothUnsupported = {
Expand All @@ -289,14 +292,18 @@ struct HomeView: View {

// MARK: - 배려석 알림 전송
private func sendCourtesySeatNotification() {
bluetoothManager.sendCourtesySeatNotification(busNumber: selectedBusName) { success in
if success {
showSuccessAlert = true
} else {
showFailureAlert = true
Logger.log(message: "📲 sendCourtesySeatNotification 호출됨 - 버스: \(selectedBusName)")
bluetoothManager.sendCourtesySeatNotification(busNumber: selectedBusName) { success in
DispatchQueue.main.async {
Logger.log(message: "📲 Bluetooth 전송 완료 - success: \(success)")
if success {
alertManager.showAlert(.bluetoothSuccess)
} else {
alertManager.showAlert(.bluetoothFailure)
}
// 알림 전송 완료/실패 후 초기화
resetButtonState()
}
// 알림 전송 완료/실패 후 초기화
resetButtonState()
}
}

Expand Down