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
23 changes: 20 additions & 3 deletions DevLog/App/RootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,19 @@ struct RootView: View {
@Environment(\.diContainer) var container: DIContainer
@State var viewModel: RootViewModel
@State private var selectedRoute: AppRoute?
@State private var selectedMainTab = MainTab.home

var body: some View {
ZStack {
Color(UIColor.systemGroupedBackground).ignoresSafeArea()
if let signIn = viewModel.state.signIn {
if signIn {
MainView(viewModel: MainViewModel(
unreadPushCountUseCase: container.resolve(ObserveUnreadPushCountUseCase.self)
))
MainView(
viewModel: MainViewModel(
unreadPushCountUseCase: container.resolve(ObserveUnreadPushCountUseCase.self)
),
selectedTab: $selectedMainTab
)
} else {
LoginView(viewModel: LoginViewModel(
signInUseCase: container.resolve(SignInUseCase.self))
Expand All @@ -29,6 +33,19 @@ struct RootView: View {
}
.preferredColorScheme(viewModel.state.theme.colorScheme)
.onAppear { viewModel.send(.onAppear) }
.onChange(of: viewModel.state.signIn) { _, value in
guard value == false else { return }
selectedMainTab = .home
}
.onOpenURL { url in
guard let mainTab = MainTab(widgetURL: url) else { return }
switch viewModel.state.signIn {
case .some(false):
selectedMainTab = .home
case .some(true), .none:
selectedMainTab = mainTab
}
Comment thread
opficdev marked this conversation as resolved.
}
.alert(viewModel.state.alertTitle, isPresented: Binding(
get: { viewModel.state.showAlert },
set: { viewModel.send(.setAlert($0)) }
Expand Down
28 changes: 28 additions & 0 deletions DevLog/App/Routing/MainTab.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// MainTab.swift
// DevLog
//
// Created by opfic on 4/30/26.
//

import Foundation

enum MainTab: Hashable {
case home
case today
case notification
case profile

init?(widgetURL: URL) {
guard widgetURL.scheme?.lowercased() == WidgetDeepLink.scheme.lowercased() else { return nil }

switch widgetURL.host {
case WidgetDeepLink.todayTodoHost:
self = .today
case WidgetDeepLink.heatmapHost:
self = .profile
default:
return nil
}
}
}
7 changes: 6 additions & 1 deletion DevLog/UI/Common/MainView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import SwiftUI
struct MainView: View {
@Environment(\.diContainer) var container: DIContainer
@State var viewModel: MainViewModel
@Binding var selectedTab: MainTab

var body: some View {
TabView {
TabView(selection: $selectedTab) {
HomeView(viewModel: HomeViewModel(
fetchPreferencesUseCase: container.resolve(FetchTodoCategoryPreferencesUseCase.self),
updatePreferencesUseCase: container.resolve(UpdateTodoCategoryPreferencesUseCase.self),
Expand All @@ -28,6 +29,7 @@ struct MainView: View {
Image(systemName: "house.fill")
Text(String(localized: "nav_home"))
}
.tag(MainTab.home)
TodayView(viewModel: TodayViewModel(
fetchTodosUseCase: container.resolve(FetchTodosUseCase.self),
fetchTodoByIdUseCase: container.resolve(FetchTodoByIdUseCase.self),
Expand All @@ -39,6 +41,7 @@ struct MainView: View {
Image(systemName: "sun.max.fill")
Text(String(localized: "nav_today"))
}
.tag(MainTab.today)
PushNotificationListView(viewModel: PushNotificationListViewModel(
fetchUseCase: container.resolve(FetchPushNotificationsUseCase.self),
deleteUseCase: container.resolve(DeletePushNotificationUseCase.self),
Expand All @@ -52,6 +55,7 @@ struct MainView: View {
Text(String(localized: "nav_notifications"))
}
.badge(viewModel.state.unreadPushCount)
.tag(MainTab.notification)
ProfileView(viewModel: ProfileViewModel(
fetchUserDataUseCase: container.resolve(FetchUserDataUseCase.self),
fetchTodosUseCase: container.resolve(FetchTodosUseCase.self),
Expand All @@ -64,6 +68,7 @@ struct MainView: View {
Image(systemName: "person.crop.circle.fill")
Text(String(localized: "nav_profile"))
}
.tag(MainTab.profile)
}
.onAppear {
viewModel.send(.onAppear)
Expand Down
1 change: 1 addition & 0 deletions DevLogWidget/Heatmap/HeatmapWidget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ struct HeatmapWidget: Widget {
) { entry in
HeatmapWidgetEntryView(entry: entry)
.containerBackground(.fill.tertiary, for: .widget)
.widgetURL(WidgetDeepLink.heatmapURL)
}
.configurationDisplayName("Heatmap")
.description("활동 히트맵을 표시합니다.")
Expand Down
1 change: 1 addition & 0 deletions DevLogWidget/Today/TodayTodoWidget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ struct TodayTodoWidget: Widget {
) { entry in
TodayTodoWidgetEntryView(entry: entry)
.containerBackground(.fill.tertiary, for: .widget)
.widgetURL(WidgetDeepLink.todayTodoURL)
}
.description("오늘 기준 Todo 목록을 표시합니다.")
.configurationDisplayName("Today")
Expand Down
29 changes: 29 additions & 0 deletions WidgetShared/WidgetDeepLink.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// WidgetDeepLink.swift
// DevLog
//
// Created by opfic on 4/30/26.
//

import Foundation

enum WidgetDeepLink {
static let scheme = "DevLog"
static let todayTodoHost = "today"
static let heatmapHost = "profile"

static var todayTodoURL: URL? {
url(host: todayTodoHost)
}

static var heatmapURL: URL? {
url(host: heatmapHost)
}

private static func url(host: String) -> URL? {
var urlComponents = URLComponents()
urlComponents.scheme = scheme
urlComponents.host = host
return urlComponents.url
}
}