Skip to content
Closed
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
29 changes: 13 additions & 16 deletions V2er/State/DataFlow/Model/TabInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,36 +24,33 @@ enum Tab: String {
case members

func displayName() -> String {
var name: String? = nil
switch(self) {
case .all:
name = "全部"
return "全部"
case .tech:
name = "技术"
return "技术"
case .creative:
name = "创意"
return "创意"
case .play:
name = "好玩"
return "好玩"
case .apple:
name = "Apple"
return "Apple"
case .jobs:
name = "酷工作"
return "酷工作"
case .deals:
name = "交易"
return "交易"
case .city:
name = "城市"
return "城市"
case .qna:
name = "问与答"
return "问与答"
case .hot:
name = "最热"
return "最热"
case .r2:
name = "r2"
return "r2"
case .nodes:
name = "节点"
return "节点"
case .members:
name = "关注"
return "关注"
}
assert(name != nil , "Tab display name shouldn't be null")
return ""
}
}
28 changes: 26 additions & 2 deletions V2er/State/DataFlow/Reducers/FeedReducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ func feedStateReducer(_ state: FeedState, _ action: Action) -> (FeedState, Actio
state.feedInfo = newsInfo ?? FeedInfo()
state.willLoadPage = 1
} else { }
case let action as FeedActions.ChangeTab:
guard action.tab != state.selectedTab else { break }
state.selectedTab = action.tab
// Reset state when changing tabs
state.feedInfo = FeedInfo()
state.willLoadPage = 0
state.hasLoadedOnce = false
// Trigger data fetch for new tab
followingAction = FeedActions.FetchData.Start(tab: action.tab, autoLoad: true)
case let action as FeedActions.LoadMore.Start:
guard !state.refreshing else { break }
guard !state.loadingMore else { break }
Expand Down Expand Up @@ -53,10 +62,16 @@ struct FeedActions {
struct FetchData {
struct Start: AwaitAction {
var target: Reducer = reducer
let tab: Tab = .all
let tab: Tab
var page: Int = 0
var autoLoad: Bool = false

init(tab: Tab = .all, page: Int = 0, autoLoad: Bool = false) {
self.tab = tab
self.page = page
self.autoLoad = autoLoad
}

func execute(in store: Store) async {
let result: APIResult<FeedInfo> = await APIService.shared
.htmlGet(endpoint: .tab, ["tab": tab.rawValue])
Expand All @@ -71,6 +86,11 @@ struct FeedActions {
}
}

struct ChangeTab: Action {
var target: Reducer = reducer
let tab: Tab
}

struct LoadMore {
struct Start: AwaitAction {
var target: Reducer = reducer
Expand All @@ -81,9 +101,13 @@ struct FeedActions {
}

func execute(in store: Store) async {
let state = store.appState.feedState
let endpoint: Endpoint = willLoadPage >= 1 ? .recent : .tab
let params: [String: String] = willLoadPage >= 1 ?
["p": willLoadPage.string] :
["tab": state.selectedTab.rawValue, "p": willLoadPage.string]
let result: APIResult<FeedInfo> = await APIService.shared
.htmlGet(endpoint: endpoint, ["p": willLoadPage.string])
.htmlGet(endpoint: endpoint, params)
dispatch(FeedActions.LoadMore.Done(result: result))
}
}
Expand Down
1 change: 1 addition & 0 deletions V2er/State/DataFlow/State/FeedState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ struct FeedState: FluxState {
var willLoadPage: Int = 0
var hasMoreData: Bool = true
var feedInfo: FeedInfo = FeedInfo()
var selectedTab: Tab = .all
}
9 changes: 7 additions & 2 deletions V2er/View/Feed/FeedPage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ struct FeedPage: BaseHomePageView {
var isSelected: Bool {
let selected = selecedTab == .feed
if selected && !state.hasLoadedOnce {
dispatch(FeedActions.FetchData.Start(autoLoad: true))
dispatch(FeedActions.FetchData.Start(tab: state.selectedTab, autoLoad: true))
}
return selected
}
Expand All @@ -34,6 +34,11 @@ struct FeedPage: BaseHomePageView {
@ViewBuilder
private var contentView: some View {
VStack(spacing: 0) {
FeedTabFilter(selectedTab: bindingState.selectedTab) { tab in
dispatch(FeedActions.ChangeTab(tab: tab))
}
Divider()
.light()
LazyVStack(spacing: 0) {
ForEach(state.feedInfo.items) { item in
NavigationLink(destination: FeedDetailPage(initData: item)) {
Expand All @@ -44,7 +49,7 @@ struct FeedPage: BaseHomePageView {
}
.updatable(autoRefresh: state.showProgressView, hasMoreData: state.hasMoreData, scrollTop(tab: .feed)) {
if AccountState.hasSignIn() {
await run(action: FeedActions.FetchData.Start())
await run(action: FeedActions.FetchData.Start(tab: state.selectedTab))
}
} loadMore: {
if AccountState.hasSignIn() {
Expand Down
57 changes: 57 additions & 0 deletions V2er/View/Feed/FeedTabFilter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// FeedTabFilter.swift
// V2er
//
// Created by copilot on 2024/09/20.
// Copyright © 2024 lessmore.io. All rights reserved.
//

import SwiftUI

struct FeedTabFilter: View {
@Binding var selectedTab: Tab
let onTabChange: (Tab) -> Void

private let tabs: [Tab] = [.all, .tech, .creative, .play, .apple, .jobs, .deals, .city, .qna, .hot]

var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 16) {
ForEach(tabs, id: \.self) { tab in
Button {
onTabChange(tab)
} label: {
Text(tab.displayName())
.font(.subheadline)
.fontWeight(selectedTab == tab ? .semibold : .regular)
.foregroundColor(selectedTab == tab ? .white : .primary)
.padding(.horizontal, 12)
.padding(.vertical, 6)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(selectedTab == tab ? Color.tintColor : Color.clear)
)
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(selectedTab == tab ? Color.clear : Color.secondaryText.opacity(0.3), lineWidth: 1)
)
}
.forceClickable()
}
}
.padding(.horizontal, 16)
}
.padding(.vertical, 8)
.background(Color.bgColor)
}
}

struct FeedTabFilter_Previews: PreviewProvider {
@State static var selectedTab: Tab = .all

static var previews: some View {
FeedTabFilter(selectedTab: $selectedTab) { tab in
selectedTab = tab
}
}
}
Loading