-
Notifications
You must be signed in to change notification settings - Fork 48
feat: add feed filter menu with Reddit-style dropdown #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
80aefac
2d07e45
997295c
cffc7c4
1902324
90b4217
c968208
34f520e
cf15d12
61953eb
1105ce7
0e574b3
55cf03f
1231b29
2e5ba8f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -24,36 +24,59 @@ enum Tab: String { | |||||||||||||||||||||||
| case members | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| func displayName() -> String { | ||||||||||||||||||||||||
| var name: String? = nil | ||||||||||||||||||||||||
| switch(self) { | ||||||||||||||||||||||||
| 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 "" | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| func needsLogin() -> Bool { | ||||||||||||||||||||||||
| return self == .nodes || self == .members | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| func supportsLoadMore() -> Bool { | ||||||||||||||||||||||||
| return self == .all | ||||||||||||||||||||||||
|
Comment on lines
+61
to
+62
|
||||||||||||||||||||||||
| func supportsLoadMore() -> Bool { | |
| return self == .all | |
| /// Returns true if the tab supports pagination ("load more") according to the V2EX API. | |
| /// As of 2024, the following tabs support pagination: all, tech, creative, play, apple, jobs, deals, city, qna. | |
| func supportsLoadMore() -> Bool { | |
| switch self { | |
| case .all, .tech, .creative, .play, .apple, .jobs, .deals, .city, .qna: | |
| return true | |
| default: | |
| return false | |
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,15 +23,23 @@ func feedStateReducer(_ state: FeedState, _ action: Action) -> (FeedState, Actio | |
| if case let .success(newsInfo) = action.result { | ||
| state.feedInfo = newsInfo ?? FeedInfo() | ||
| state.willLoadPage = 1 | ||
| let supportsLoadMore = state.selectedTab.supportsLoadMore() | ||
| state.hasMoreData = supportsLoadMore | ||
|
Comment on lines
+26
to
+27
|
||
| // Trigger scroll to top after successfully loading new filter data | ||
| if action.isFromFilterChange { | ||
| state.scrollToTop = Int.random(in: 1...Int.max) | ||
| } | ||
| } else { } | ||
| case let action as FeedActions.LoadMore.Start: | ||
| guard !state.refreshing else { break } | ||
| guard !state.loadingMore else { break } | ||
| guard state.selectedTab.supportsLoadMore() else { break } | ||
| state.loadingMore = true | ||
| break | ||
| case let action as FeedActions.LoadMore.Done: | ||
| state.loadingMore = false | ||
| state.hasMoreData = true // todo check vary tabs | ||
| let supportsLoadMore = state.selectedTab.supportsLoadMore() | ||
| state.hasMoreData = supportsLoadMore | ||
|
Comment on lines
+41
to
+42
|
||
| if case let .success(newsInfo) = action.result { | ||
| state.willLoadPage += 1 | ||
| state.feedInfo.append(feedInfo: newsInfo!) | ||
|
|
@@ -40,6 +48,16 @@ func feedStateReducer(_ state: FeedState, _ action: Action) -> (FeedState, Actio | |
| } | ||
| case let action as FeedActions.ClearMsgBadge: | ||
| state.feedInfo.unReadNums = 0 | ||
| case let action as FeedActions.SelectTab: | ||
| state.selectedTab = action.tab | ||
| Tab.saveSelectedTab(action.tab) | ||
| state.showFilterMenu = false | ||
| state.showProgressView = true | ||
| let supportsLoadMore = action.tab.supportsLoadMore() | ||
| state.hasMoreData = supportsLoadMore | ||
|
Comment on lines
+56
to
+57
|
||
| followingAction = FeedActions.FetchData.Start(isFromFilterChange: true) | ||
| case let action as FeedActions.ToggleFilterMenu: | ||
| state.showFilterMenu.toggle() | ||
| default: | ||
| break | ||
| } | ||
|
|
@@ -53,21 +71,34 @@ struct FeedActions { | |
| struct FetchData { | ||
| struct Start: AwaitAction { | ||
| var target: Reducer = reducer | ||
| let tab: Tab = .all | ||
| var page: Int = 0 | ||
| var autoLoad: Bool = false | ||
| var isFromFilterChange: Bool = false | ||
|
|
||
| init(page: Int = 0, autoLoad: Bool = false, isFromFilterChange: Bool = false) { | ||
| self.page = page | ||
| self.autoLoad = autoLoad | ||
| self.isFromFilterChange = isFromFilterChange | ||
| } | ||
|
|
||
| func execute(in store: Store) async { | ||
| let tab = store.appState.feedState.selectedTab | ||
| let result: APIResult<FeedInfo> = await APIService.shared | ||
| .htmlGet(endpoint: .tab, ["tab": tab.rawValue]) | ||
| dispatch(FetchData.Done(result: result)) | ||
| dispatch(FetchData.Done(result: result, isFromFilterChange: isFromFilterChange)) | ||
| } | ||
| } | ||
|
|
||
| struct Done: Action { | ||
| var target: Reducer = reducer | ||
|
|
||
| let result: APIResult<FeedInfo> | ||
| let isFromFilterChange: Bool | ||
|
|
||
| init(result: APIResult<FeedInfo>, isFromFilterChange: Bool = false) { | ||
| self.result = result | ||
| self.isFromFilterChange = isFromFilterChange | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -98,4 +129,13 @@ struct FeedActions { | |
| var target: Reducer = reducer | ||
| } | ||
|
|
||
| struct SelectTab: Action { | ||
| var target: Reducer = reducer | ||
| let tab: Tab | ||
| } | ||
|
|
||
| struct ToggleFilterMenu: Action { | ||
| var target: Reducer = reducer | ||
| } | ||
|
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The objectVersion was changed from 52 to 54, but this change should be documented or verified to ensure compatibility with the minimum supported Xcode version for the project.