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
28 changes: 25 additions & 3 deletions MacApp/Relay/AppFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,24 @@ struct AppFeature {
@ObservableState
struct State: Equatable {
enum Route: Equatable {
case welcome
case welcome(WelcomeFeature.State)
case main(MainFeature.State)
}

var route: Route

/// Вспомогательный доступ для Scope/ifLet — ненулевой если маршрут .welcome.
var routeWelcome: WelcomeFeature.State? {
get {
guard case let .welcome(s) = route else { return nil }
return s
}
set {
guard let newValue else { return }
route = .welcome(newValue)
}
}

/// Вспомогательный доступ для Scope/ifLet — ненулевой если маршрут .main.
var routeMain: MainFeature.State? {
get {
Expand All @@ -31,7 +43,7 @@ struct AppFeature {
}
}

init(route: Route = .main(MainFeature.State())) {
init(route: Route = .welcome(WelcomeFeature.State())) {
self.route = route
}
}
Expand All @@ -40,6 +52,7 @@ struct AppFeature {

@CasePathable
enum Action {
case welcome(WelcomeFeature.Action)
case main(MainFeature.Action)
case showWelcome
case showMain
Expand All @@ -53,7 +66,7 @@ struct AppFeature {
Reduce { state, action in
switch action {
case .showWelcome:
state.route = .welcome
state.route = .welcome(WelcomeFeature.State())
return .none

case .showMain:
Expand All @@ -65,10 +78,19 @@ struct AppFeature {
state.route = .main(MainFeature.State())
return .send(.main(._openProject(url)))

case let .welcome(.delegate(.projectSelected(url))):
return .send(.openProject(url))

case .welcome:
return .none

case .main:
return .none
}
}
.ifLet(\.routeWelcome, action: \.welcome) {
WelcomeFeature()
}
.ifLet(\.routeMain, action: \.main) {
MainFeature()
}
Expand Down
4 changes: 3 additions & 1 deletion MacApp/Relay/AppRootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ struct AppRootView: View {
var body: some View {
switch store.route {
case .welcome:
WelcomeView(store: store)
if let welcomeStore = store.scope(state: \.routeWelcome, action: \.welcome) {
WelcomeView(store: welcomeStore)
}

case .main:
MainView(store: store.scope(state: \.routeMain!, action: \.main))
Expand Down
60 changes: 22 additions & 38 deletions MacApp/Relay/Welcome/WelcomeView.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import AppKit
import ComposableArchitecture
import SwiftData
import SwiftUI

// MARK: - WelcomeView
Expand All @@ -9,16 +8,7 @@ import SwiftUI
/// Показывается при запуске если нет открытого проекта.
struct WelcomeView: View {

let store: StoreOf<AppFeature>

@Query(
filter: #Predicate<ProjectModel> { _ in true },
sort: \ProjectModel.lastOpenedAt,
order: .reverse
)
private var allProjects: [ProjectModel]

@Environment(\.modelContext) private var modelContext
@Bindable var store: StoreOf<WelcomeFeature>

var body: some View {
HSplitView {
Expand All @@ -30,6 +20,9 @@ struct WelcomeView: View {
}
.frame(minWidth: 700, minHeight: 460)
.background(Color(nsColor: .windowBackgroundColor))
.onAppear {
store.send(.onAppear)
}
}

// MARK: - Left panel
Expand Down Expand Up @@ -87,7 +80,7 @@ struct WelcomeView: View {

Divider()

if allProjects.isEmpty {
if store.recentProjects.isEmpty {
emptyState
} else {
projectsList
Expand Down Expand Up @@ -120,17 +113,17 @@ struct WelcomeView: View {
// MARK: - Projects list

private var projectsList: some View {
ScrollView {
LazyVStack(spacing: 0) {
// Favorites pinned at top
let favorites = allProjects.filter(\.isFavorite)
let recents = allProjects.filter { !$0.isFavorite }
let projects = store.filteredProjects
let favorites = projects.filter(\.isFavorite)
let recents = projects.filter { !$0.isFavorite }

return ScrollView {
LazyVStack(spacing: 0) {
if !favorites.isEmpty {
sectionHeader("Favorites")
ForEach(favorites, id: \.path) { project in
ForEach(favorites) { project in
RecentProjectRow(project: project) {
openRecentProject(project)
openRecent(project)
} onToggleFavorite: {
toggleFavorite(project)
} onDelete: {
Expand All @@ -144,9 +137,9 @@ struct WelcomeView: View {
if !favorites.isEmpty {
sectionHeader("Recent")
}
ForEach(recents.prefix(20), id: \.path) { project in
ForEach(recents.prefix(20)) { project in
RecentProjectRow(project: project) {
openRecentProject(project)
openRecent(project)
} onToggleFavorite: {
toggleFavorite(project)
} onDelete: {
Expand Down Expand Up @@ -183,36 +176,27 @@ struct WelcomeView: View {
panel.prompt = "Open"

guard panel.runModal() == .OK, let url = panel.url else { return }

let repo = ProjectRepository(modelContext: modelContext)
repo.addProject(path: url)

store.send(.openProject(url))
store.send(.folderSelected(url))
}

private func openRecentProject(_ project: ProjectModel) {
let url = URL(filePath: project.path)
let repo = ProjectRepository(modelContext: modelContext)
repo.touchProject(path: url)
store.send(.openProject(url))
private func openRecent(_ project: ProjectInfo) {
store.send(.openRecent(URL(filePath: project.path)))
}

private func toggleFavorite(_ project: ProjectModel) {
let repo = ProjectRepository(modelContext: modelContext)
repo.toggleFavorite(path: URL(filePath: project.path))
private func toggleFavorite(_ project: ProjectInfo) {
store.send(.toggleFavorite(URL(filePath: project.path)))
}

private func deleteProject(_ project: ProjectModel) {
let repo = ProjectRepository(modelContext: modelContext)
repo.deleteProject(path: URL(filePath: project.path))
private func deleteProject(_ project: ProjectInfo) {
store.send(.removeRecent(URL(filePath: project.path)))
}
}

// MARK: - RecentProjectRow

private struct RecentProjectRow: View {

let project: ProjectModel
let project: ProjectInfo
let onOpen: () -> Void
let onToggleFavorite: () -> Void
let onDelete: () -> Void
Expand Down
Loading