Skip to content

Commit

Permalink
Adding More Feeds (#251)
Browse files Browse the repository at this point in the history
Adding more Feed Types. For some reason I'm having trouble with Jobs so
omitting that for now.
  • Loading branch information
Rahkeen authored Dec 4, 2024
1 parent 1ea3cff commit b52c577
Show file tree
Hide file tree
Showing 13 changed files with 272 additions and 170 deletions.
8 changes: 8 additions & 0 deletions ios/HackerNews.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
C3AC6AD62CB6E81B006BD22D /* HackerNewsSnapshotTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3AC6AD52CB6E816006BD22D /* HackerNewsSnapshotTest.swift */; };
C3AC6AD92CB6E8F7006BD22D /* SnapshottingTests in Frameworks */ = {isa = PBXBuildFile; productRef = C3AC6AD82CB6E8F7006BD22D /* SnapshottingTests */; };
EC072CAA2CEF02A500D00B8D /* StoryRowV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC072CA92CEF02A500D00B8D /* StoryRowV2.swift */; };
EC29FDA72CFFD074007B1AE9 /* BookmarksScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC29FDA62CFFD074007B1AE9 /* BookmarksScreen.swift */; };
EC29FDA92CFFD0B5007B1AE9 /* SettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC29FDA82CFFD0B5007B1AE9 /* SettingsScreen.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -138,6 +140,8 @@
C3AC6AD52CB6E816006BD22D /* HackerNewsSnapshotTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HackerNewsSnapshotTest.swift; sourceTree = "<group>"; };
C3AC6AD72CB6E854006BD22D /* HackerNews.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = HackerNews.xctestplan; sourceTree = "<group>"; };
EC072CA92CEF02A500D00B8D /* StoryRowV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryRowV2.swift; sourceTree = "<group>"; };
EC29FDA62CFFD074007B1AE9 /* BookmarksScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksScreen.swift; sourceTree = "<group>"; };
EC29FDA82CFFD0B5007B1AE9 /* SettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreen.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -213,6 +217,8 @@
A42705A82A4294EB0057E439 /* LoginScreen.swift */,
A42705AA2A4296BA0057E439 /* PostListScreen.swift */,
A48C0DE62A9818A50034CC0A /* StoryScreen.swift */,
EC29FDA62CFFD074007B1AE9 /* BookmarksScreen.swift */,
EC29FDA82CFFD0B5007B1AE9 /* SettingsScreen.swift */,
);
path = Screens;
sourceTree = "<group>";
Expand Down Expand Up @@ -559,6 +565,7 @@
buildActionMask = 2147483647;
files = (
A423B06A2BAE061600267DDB /* Flags.swift in Sources */,
EC29FDA92CFFD0B5007B1AE9 /* SettingsScreen.swift in Sources */,
1DF161FA2A4346F6001A3F76 /* StoryRow.swift in Sources */,
1DF162002A4365A1001A3F76 /* Colors.swift in Sources */,
A42705AB2A4296BA0057E439 /* PostListScreen.swift in Sources */,
Expand All @@ -567,6 +574,7 @@
EC072CAA2CEF02A500D00B8D /* StoryRowV2.swift in Sources */,
1DF162032A436E7B001A3F76 /* DateUtils.swift in Sources */,
A413E8572A8C40E800C0F867 /* Extensions.swift in Sources */,
EC29FDA72CFFD074007B1AE9 /* BookmarksScreen.swift in Sources */,
A42705A92A4294EB0057E439 /* LoginScreen.swift in Sources */,
A413E8592A8D868500C0F867 /* ThemedButtonStyle.swift in Sources */,
A427057D2A4293B10057E439 /* HNApp.swift in Sources */,
Expand Down
94 changes: 31 additions & 63 deletions ios/HackerNews/Components/StoryRow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,85 +9,53 @@ import Foundation
import SwiftUI

struct StoryRow: View {
@ObservedObject var appState: AppViewModel
@ObservedObject var model: AppViewModel
let story: Story
let index: Int

var body: some View {
VStack(alignment: .leading) {
HStack {
VStack(alignment: .leading, spacing: 8) {
let author = story.by!
Text("@\(author)")
.foregroundColor(.hnOrange)
.fontWeight(/*@START_MENU_TOKEN@*/.bold/*@END_MENU_TOKEN@*/)
Text(story.title)
.font(.headline)
HStack(spacing: 16) {
HStack(spacing: 4) {
Image(systemName: "arrow.up")
.foregroundColor(.green)
Text("\(story.score)")
}
HStack(spacing: 4) {
Image(systemName: "clock")
.foregroundColor(.purple)
Text(story.displayableDate)
}
Spacer()
// Comment Button
Button(action: {
// TODO: Add upvote action
print("Pressed upvote for \(story.title)")
print("Pressed comment button for: \(story.id)")
model.navigationPath.append(
AppViewModel.AppNavigation.storyComments(story: story)
)
}) {
VStack {
Image(systemName: "arrow.up")
.foregroundColor(Color.primary)
Text("\(story.score)")
.font(.caption)
.foregroundColor(Color.primary)
}
}
.padding(.trailing, 4)

VStack(alignment: .leading) {
Text("\(index + 1). \(story.title)")
.font(.subheadline)
.foregroundColor(Color.primary)

if let displayableUrl = story.displayableUrl {
Text("(\(displayableUrl))")
.font(.caption2)
.foregroundColor(Color.primary.opacity(0.6))
}
HStack {
let dateAndAuthor: String = {
if let author = story.by {
return "\(story.displayableDate) by \(author)"
} else {
return story.displayableDate
}
}()
Text(dateAndAuthor)
.font(.caption)
.foregroundColor(Color.primary.opacity(0.6))

if story.commentCount > 0 {
Button(action: {
appState.navigationPath.append(AppViewModel.AppNavigation.storyComments(story: story))
}) {
let commentText: String = {
if story.commentCount == 1 {
return "\(story.commentCount) comment"
} else {
return "\(story.commentCount) comments"
}
}()
Text(commentText)
.font(.caption)
.fontWeight(.medium)
.underline()
.foregroundColor(Color.primary)
}
.buttonStyle(.borderedProminent)
.tint(HNColors.commentBackground)
.frame(maxWidth: .infinity, alignment: .trailing)
}
HStack(spacing: 4) {
Image(systemName: "message.fill")
Text("\(story.commentCount)")
}
.padding(.horizontal, 2)
.padding(.top, 2)
}
.buttonStyle(.bordered)
.buttonBorderShape(ButtonBorderShape.capsule)
}
}
.padding(.top, 4)
}
}

struct StoryRow_Preview: PreviewProvider {
static var previews: some View {
let fakeStory = PreviewHelpers.makeFakeStory(index: 0, descendants: 3, kids: [1, 2, 3])
PreviewVariants {
StoryRow(appState: AppViewModel(), story: fakeStory, index: 0)
StoryRow(model: AppViewModel(), story: fakeStory)
}
}
}
44 changes: 1 addition & 43 deletions ios/HackerNews/Components/StoryRowV2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,49 +12,7 @@ struct StoryRowV2: View {
let story: Story

var body: some View {
VStack(alignment: .leading, spacing: 8) {
let author = story.by!
Text("@\(author)")
.foregroundColor(.hnOrange)
.fontWeight(/*@START_MENU_TOKEN@*/.bold/*@END_MENU_TOKEN@*/)
Text(story.title)
.font(.headline)
HStack(spacing: 16) {
HStack(spacing: 4) {
Image(systemName: "arrow.up")
.foregroundColor(.green)
Text("\(story.score)")
}
HStack(spacing: 4) {
Image(systemName: "clock")
.foregroundColor(.purple)
Text(story.displayableDate)
}
Spacer()
// Comment Button
Button(action: {
print("Pressed comment button for: \(story.id)")
model.navigationPath.append(
AppViewModel.AppNavigation.storyComments(story: story)
)
}) {
HStack(spacing: 4) {
Image(systemName: "message.fill")
Text("\(story.commentCount)")
}
}
.buttonStyle(.bordered)
.buttonBorderShape(ButtonBorderShape.capsule)
}
}
Text("Hello There")
}
}

struct StoryRowV2_Preview: PreviewProvider {
static var previews: some View {
let fakeStory = PreviewHelpers.makeFakeStory(index: 0, descendants: 3, kids: [1, 2, 3])
PreviewVariants {
StoryRowV2(model: AppViewModel(), story: fakeStory)
}
}
}
22 changes: 18 additions & 4 deletions ios/HackerNews/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,17 @@ struct ContentView: View {
var body: some View {
switch appState.authState {
case .loggedIn:
PostListScreen(appState: appState)
TabView {
PostListScreen(appState: appState)
.tag(1)
.tabItem { Label("Feed", systemImage: "list.dash") }
BookmarksScreen()
.tag(2)
.tabItem { Label("Bookmarks", systemImage: "book") }
SettingsScreen()
.tag(3)
.tabItem { Label("Settings", systemImage: "gear") }
}
case .loggedOut:
LoginScreen(appState: appState)
}
Expand All @@ -38,7 +48,7 @@ struct ContentView_LoggedIn_Loading_Previews: PreviewProvider {
static var previews: some View {
let appModel = AppViewModel()
appModel.authState = .loggedIn
appModel.storiesState = .loading
appModel.postListState = PostListState(storiesState: .loading)
return PreviewVariants {
PreviewHelpers.withNavigationView {
ContentView(appState: appModel)
Expand All @@ -51,7 +61,9 @@ struct ContentView_LoggedIn_WithPosts_Previews: PreviewProvider {
static var previews: some View {
let appModel = AppViewModel()
appModel.authState = .loggedIn
appModel.storiesState = .loaded(stories: PreviewHelpers.makeFakeStories())
appModel.postListState = PostListState(
storiesState: .loaded(items: PreviewHelpers.makeFakeStories())
)
return PreviewVariants {
PreviewHelpers.withNavigationView {
ContentView(appState: appModel)
Expand All @@ -64,7 +76,9 @@ struct ContentView_LoggedIn_EmptyPosts_Previews: PreviewProvider {
static var previews: some View {
let appModel = AppViewModel()
appModel.authState = .loggedIn
appModel.storiesState = .loaded(stories: [])
appModel.postListState = PostListState(
storiesState: .loaded(items: [])
)
return PreviewVariants {
PreviewHelpers.withNavigationView {
ContentView(appState: appModel)
Expand Down
56 changes: 45 additions & 11 deletions ios/HackerNews/Models/AppViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,41 @@
import Foundation
import SwiftUI

enum FeedType: CaseIterable {
case top
case new
case best
case ask
case show

var title: String {
switch self {
case .top:
return "Top"
case .new:
return "New"
case .best:
return "Best"
case .ask:
return "Ask"
case .show:
return "Show"
}
}
}

enum StoriesState {
case notStarted
case loading
case loaded(items: [Story])
}

struct PostListState {
var feeds: [FeedType] = FeedType.allCases
var storiesState: StoriesState = .notStarted
var selectedFeed: FeedType = FeedType.top
}

@MainActor
class AppViewModel: ObservableObject {

Expand All @@ -21,14 +56,9 @@ class AppViewModel: ObservableObject {
case loggedOut
}

enum StoriesListState {
case notStarted
case loading
case loaded(stories: [Story])
}

@Published var authState = AuthState.loggedOut
@Published var storiesState = StoriesListState.notStarted
@Published var postListState = PostListState()
@Published var navigationPath = NavigationPath()

private let hnApi = HNApi()
Expand All @@ -43,10 +73,14 @@ class AppViewModel: ObservableObject {
authState = .loggedOut
}

func fetchPosts() async {
storiesState = .loading
let stories = await hnApi.fetchTopStories()
storiesState = .loaded(stories: stories)
func fetchPosts(feedType: FeedType) async {
var updated = postListState
updated.selectedFeed = feedType
updated.storiesState = .loading
postListState = updated

let stories = await hnApi.fetchStories(feedType: feedType)
updated.storiesState = .loaded(items: stories)
postListState = updated
}

}
22 changes: 19 additions & 3 deletions ios/HackerNews/Network/HNApi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,36 @@ import Foundation

class HNApi {

let baseUrl = "https://hacker-news.firebaseio.com/v0/"
let decoder = JSONDecoder()

init() {}

func fetchTopStories() async -> [Story] {
func fetchStories(feedType: FeedType) async -> [Story] {
NotificationCenter.default.post(name: Notification.Name(rawValue: "EmergeMetricStarted"), object: nil, userInfo: [
"metric": "FETCH_STORIES"
])
let url = URL(string: "https://hacker-news.firebaseio.com/v0/topstories.json")!

let feedUrl = switch feedType {
case .top:
"topstories.json"
case .new:
"newstories.json"
case .best:
"beststories.json"
case .ask:
"askstories.json"
case .show:
"showstories.json"
}

let url = URL(string: baseUrl + feedUrl)!

do {
let (data, response) = try await URLSession.shared.data(from: url)
if Flags.isEnabled(.networkDebugger) {
NetworkDebugger.printStats(for: response)
}
let decoder = JSONDecoder()
let storyIds = try decoder.decode([Int64].self, from: data)
let items = await fetchItems(ids: Array(storyIds.prefix(20)))

Expand Down
8 changes: 6 additions & 2 deletions ios/HackerNews/Screens/BookmarksScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@
// BookmarksScreen.swift
// HackerNews
//
// Created by Rikin Marfatia on 11/22/24.
// Created by Rikin Marfatia on 12/3/24.
//

import Foundation
import SwiftUI

struct BookmarksScreen: View {
var body: some View {
Text("Hello Bookmarks")
Text("Bookmarks Screen")
}
}

#Preview {
BookmarksScreen()
}
Loading

0 comments on commit b52c577

Please sign in to comment.