Skip to content

Commit a789a71

Browse files
committed
适配macOS平台UI
1 parent 7a94a18 commit a789a71

File tree

1 file changed

+107
-23
lines changed

1 file changed

+107
-23
lines changed

App/ContentView.swift

Lines changed: 107 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ struct ContentView: View {
1414
@EnvironmentObject private var loginState: LoginState
1515
@State private var selection: RootDestination? = .home
1616
#if os(macOS)
17-
private let homeBoards = Board.defaultBoard()
18-
@State private var sidebarSelection: SidebarSelection? = Board.defaultBoard().first.map { SidebarSelection.board($0) } ?? .home
17+
@State private var sidebarSelection: SidebarSelection? = .home
1918
@State private var columnVisibility: NavigationSplitViewVisibility = .all
2019
@State private var searchText = ""
2120
@StateObject private var favoritesViewModel = FavoritesViewModel()
2221
@StateObject private var profileViewModel = ProfileViewModel()
22+
@StateObject private var macHomeNavigationViewModel = HomeNavigationViewModel()
2323
@State private var detailTopic: Topic?
2424
@State private var detailBoard: Board?
2525
@State private var detailMessage: MessagePreview?
@@ -129,6 +129,9 @@ struct ContentView: View {
129129
.onAppear {
130130
initializeSidebarIfNeeded()
131131
handleLoginStateChange(isLoggedIn: loginState.isLoggedIn)
132+
Task {
133+
await macHomeNavigationViewModel.loadNavigationsIfNeeded()
134+
}
132135
}
133136
.onChange(of: loginState.isLoggedIn) { _, newValue in
134137
handleLoginStateChange(isLoggedIn: newValue, forceReload: true)
@@ -145,12 +148,18 @@ struct ContentView: View {
145148
VStack(spacing: 0) {
146149
sidebarSearchBar
147150
List(selection: $sidebarSelection) {
148-
OutlineGroup(sidebarNodes, children: \.children) { node in
149-
Label(node.title, systemImage: node.iconName)
150-
.tag(node.selection)
151-
.font(.system(.body, design: .rounded))
152-
.padding(.vertical, 4)
153-
.padding(.leading, 8)
151+
ForEach(sidebarNodes) { node in
152+
if let children = node.children, !children.isEmpty {
153+
Section {
154+
ForEach(children) { child in
155+
sidebarRow(for: child, isChild: true)
156+
}
157+
} header: {
158+
sidebarRow(for: node, isChild: false)
159+
}
160+
} else {
161+
sidebarRow(for: node, isChild: false)
162+
}
154163
}
155164
}
156165
.listStyle(.sidebar)
@@ -243,19 +252,28 @@ struct ContentView: View {
243252
.buttonStyle(.plain)
244253
}
245254

255+
@ViewBuilder
256+
private func sidebarRow(for node: SidebarNode, isChild: Bool) -> some View {
257+
Label(node.title, systemImage: node.iconName)
258+
.tag(node.selection)
259+
.font(.system(.body, design: .rounded))
260+
.padding(.vertical, 4)
261+
.padding(.leading, isChild ? 16 : 8)
262+
}
263+
246264
private var sidebarNodes: [SidebarNode] {
247265
[
248266
SidebarNode(
249267
id: "home",
250268
title: "首页",
251269
iconName: "house",
252270
selection: .home,
253-
children: filteredHomeBoards.map { board in
271+
children: filteredNavigations.map { navigation in
254272
SidebarNode(
255-
id: "home.board.\(board.id)",
256-
title: board.title,
257-
iconName: "text.justify.leading",
258-
selection: .board(board)
273+
id: "home.navigation.\(navigation.id)",
274+
title: navigation.name,
275+
iconName: navigationIconName(for: navigation),
276+
selection: .homeNavigation(navigation)
259277
)
260278
}
261279
),
@@ -286,13 +304,28 @@ struct ContentView: View {
286304
]
287305
}
288306

289-
private var filteredHomeBoards: [Board] {
307+
private var filteredNavigations: [Navigation] {
308+
let navigations = macHomeNavigationViewModel.navigations
290309
guard searchText.isEmpty == false else {
291-
return homeBoards
310+
return navigations
292311
}
293-
return homeBoards.filter { board in
294-
board.title.localizedCaseInsensitiveContains(searchText) ||
295-
board.name.localizedCaseInsensitiveContains(searchText)
312+
return navigations.filter { navigation in
313+
navigation.name.localizedCaseInsensitiveContains(searchText)
314+
}
315+
}
316+
317+
private func navigationIconName(for navigation: Navigation) -> String {
318+
switch navigation.type {
319+
case "top":
320+
return "flame"
321+
case "global":
322+
return "globe"
323+
case "channel":
324+
return "square.grid.2x2"
325+
case "album":
326+
return "photo.on.rectangle"
327+
default:
328+
return "list.bullet"
296329
}
297330
}
298331

@@ -301,8 +334,9 @@ struct ContentView: View {
301334
switch selection {
302335
case .home:
303336
HomeView()
304-
case let .board(board):
305-
TopicListView(board: board, onTopicSelected: showTopicDetail)
337+
case let .homeNavigation(navigation):
338+
NavigationTopicColumnView(navigation: navigation, onTopicSelected: showTopicDetail)
339+
.id(navigation.id)
306340
case .favorites:
307341
FavoritesColumnView(
308342
viewModel: favoritesViewModel,
@@ -335,7 +369,7 @@ struct ContentView: View {
335369

336370
private func initializeSidebarIfNeeded() {
337371
if sidebarSelection == nil {
338-
sidebarSelection = homeBoards.first.map { SidebarSelection.board($0) } ?? .home
372+
sidebarSelection = .home
339373
}
340374
}
341375

@@ -408,7 +442,7 @@ private enum RootDestination: String, CaseIterable, Hashable, Identifiable {
408442
case .mine: return "person.circle"
409443
}
410444
}
411-
445+
412446
/// iOS 平台显示的 tab 列表(不包含"我的")
413447
static var iOSCases: [RootDestination] {
414448
[.home, .favorites, .messages]
@@ -418,7 +452,7 @@ private enum RootDestination: String, CaseIterable, Hashable, Identifiable {
418452
#if os(macOS)
419453
private enum SidebarSelection: Hashable {
420454
case home
421-
case board(Board)
455+
case homeNavigation(Navigation)
422456
case favorites
423457
case favoriteBoards
424458
case favoriteTopics
@@ -427,6 +461,56 @@ private enum SidebarSelection: Hashable {
427461
case profile
428462
}
429463

464+
private struct NavigationTopicColumnView: View {
465+
@EnvironmentObject private var browsingHistory: BrowsingHistoryStore
466+
@Environment(\.colorScheme) private var colorScheme
467+
468+
let navigation: Navigation
469+
let onTopicSelected: ((Topic) -> Void)?
470+
471+
@StateObject private var viewModel = NaviTopicListViewModel()
472+
473+
private var isAlbumView: Bool {
474+
navigation.type == "album"
475+
}
476+
477+
var body: some View {
478+
ScrollView {
479+
LazyVStack(spacing: AppTheme.compactSpacing) {
480+
ForEach(viewModel.topics) { topic in
481+
Button {
482+
onTopicSelected?(topic)
483+
} label: {
484+
if isAlbumView {
485+
AlbumTopicRowView(
486+
topic: topic,
487+
isVisited: browsingHistory.visitedTopicIDs.contains(topic.id)
488+
)
489+
} else {
490+
TopicRowView(
491+
topic: topic,
492+
isVisited: browsingHistory.visitedTopicIDs.contains(topic.id)
493+
)
494+
}
495+
}
496+
.buttonStyle(.plain)
497+
.frame(maxWidth: .infinity, alignment: .leading)
498+
.onAppear {
499+
viewModel.loadNextPageIfNeeded(currentItem: topic)
500+
}
501+
}
502+
}
503+
.padding(.top, AppTheme.verticalSpacing)
504+
.padding(.horizontal, AppTheme.verticalSpacing)
505+
}
506+
.smthScaffoldBackground()
507+
.tint(AppTheme.accentColor(for: colorScheme))
508+
.task {
509+
await viewModel.switchNavigation(to: navigation)
510+
}
511+
}
512+
}
513+
430514
private struct SidebarNode: Identifiable {
431515
let id: String
432516
let title: String

0 commit comments

Comments
 (0)