diff --git a/ios/HackerNews.xcodeproj/project.pbxproj b/ios/HackerNews.xcodeproj/project.pbxproj index ffc732d0..8fd9d554 100644 --- a/ios/HackerNews.xcodeproj/project.pbxproj +++ b/ios/HackerNews.xcodeproj/project.pbxproj @@ -42,6 +42,10 @@ C3AC6AD42CB6E806006BD22D /* HackerNewsPreviewTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3AC6AD32CB6E7FD006BD22D /* HackerNewsPreviewTest.swift */; }; 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 */; }; + EC8BF4B02CF1030700EF4BA8 /* StoriesScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC8BF4AF2CF1030700EF4BA8 /* StoriesScreen.swift */; }; + EC8BF4B22CF1181800EF4BA8 /* BookmarksScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC8BF4B12CF1181800EF4BA8 /* BookmarksScreen.swift */; }; + EC8BF4B42CF1182400EF4BA8 /* SettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC8BF4B32CF1182400EF4BA8 /* SettingsScreen.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -136,6 +140,10 @@ C3AC6AD32CB6E7FD006BD22D /* HackerNewsPreviewTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HackerNewsPreviewTest.swift; sourceTree = ""; }; C3AC6AD52CB6E816006BD22D /* HackerNewsSnapshotTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HackerNewsSnapshotTest.swift; sourceTree = ""; }; C3AC6AD72CB6E854006BD22D /* HackerNews.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = HackerNews.xctestplan; sourceTree = ""; }; + EC072CA92CEF02A500D00B8D /* StoryRowV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryRowV2.swift; sourceTree = ""; }; + EC8BF4AF2CF1030700EF4BA8 /* StoriesScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoriesScreen.swift; sourceTree = ""; }; + EC8BF4B12CF1181800EF4BA8 /* BookmarksScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksScreen.swift; sourceTree = ""; }; + EC8BF4B32CF1182400EF4BA8 /* SettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreen.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -179,6 +187,7 @@ A413E8582A8D868500C0F867 /* ThemedButtonStyle.swift */, A434C2DF2A8E75960002F488 /* WebView.swift */, A47309B52AA7D1F600201376 /* CommentRow.swift */, + EC072CA92CEF02A500D00B8D /* StoryRowV2.swift */, ); path = Components; sourceTree = ""; @@ -210,6 +219,9 @@ A42705A82A4294EB0057E439 /* LoginScreen.swift */, A42705AA2A4296BA0057E439 /* PostListScreen.swift */, A48C0DE62A9818A50034CC0A /* StoryScreen.swift */, + EC8BF4AF2CF1030700EF4BA8 /* StoriesScreen.swift */, + EC8BF4B12CF1181800EF4BA8 /* BookmarksScreen.swift */, + EC8BF4B32CF1182400EF4BA8 /* SettingsScreen.swift */, ); path = Screens; sourceTree = ""; @@ -561,6 +573,7 @@ A42705AB2A4296BA0057E439 /* PostListScreen.swift in Sources */, A40DC1FA2C20D8B60055B920 /* Previews.swift in Sources */, A427057F2A4293B10057E439 /* ContentView.swift in Sources */, + EC072CAA2CEF02A500D00B8D /* StoryRowV2.swift in Sources */, 1DF162032A436E7B001A3F76 /* DateUtils.swift in Sources */, A413E8572A8C40E800C0F867 /* Extensions.swift in Sources */, A42705A92A4294EB0057E439 /* LoginScreen.swift in Sources */, @@ -569,8 +582,11 @@ A434C2E02A8E75960002F488 /* WebView.swift in Sources */, A49933942AA28B5900DED8B1 /* StoryViewModel.swift in Sources */, A47309B62AA7D1F600201376 /* CommentRow.swift in Sources */, + EC8BF4B02CF1030700EF4BA8 /* StoriesScreen.swift in Sources */, A49933952AA28B6500DED8B1 /* StoryScreen.swift in Sources */, A42705A72A42949D0057E439 /* AppViewModel.swift in Sources */, + EC8BF4B42CF1182400EF4BA8 /* SettingsScreen.swift in Sources */, + EC8BF4B22CF1181800EF4BA8 /* BookmarksScreen.swift in Sources */, A42705AD2A429D2E0057E439 /* HNApi.swift in Sources */, A423B0682BAE05FB00267DDB /* NetworkDebugger.swift in Sources */, ); @@ -740,7 +756,7 @@ CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = "\"HackerNews/Preview Content\""; - DEVELOPMENT_TEAM = 62J2XHNK9T; + DEVELOPMENT_TEAM = LSPBG7SYXK; "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES; ENABLE_PREVIEWS = YES; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -762,7 +778,7 @@ MARKETING_VERSION = 3.4; OTHER_LDFLAGS = "-fprofile-instr-generate"; OTHER_SWIFT_FLAGS = "-profile-generate -profile-coverage-mapping"; - PRODUCT_BUNDLE_IDENTIFIER = "com.emerge.hn.Hacker-News"; + PRODUCT_BUNDLE_IDENTIFIER = com.emergetools.hackernews; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = YES; @@ -783,7 +799,7 @@ CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = "\"HackerNews/Preview Content\""; - DEVELOPMENT_TEAM = 62J2XHNK9T; + DEVELOPMENT_TEAM = LSPBG7SYXK; "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES; ENABLE_PREVIEWS = YES; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -803,7 +819,7 @@ ); LIBRARY_SEARCH_PATHS = "\"$(PROJECT_DIR)/Frameworks/Reaper.xcframework\""; MARKETING_VERSION = 3.4; - PRODUCT_BUNDLE_IDENTIFIER = "com.emerge.hn.Hacker-News"; + PRODUCT_BUNDLE_IDENTIFIER = com.emergetools.hackernews; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = YES; diff --git a/ios/HackerNews.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/HackerNews.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index afc6da4f..55c15ad2 100644 --- a/ios/HackerNews.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ios/HackerNews.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "261ff378e7b15ed930e88adbf89034cf9764a714ee554702b7a367bba1734bed", + "originHash" : "24bebf1b8a7941b76197956b51b5401cff81714df8139818d77c9821c372f77b", "pins" : [ { "identity" : "accessibilitysnapshot", diff --git a/ios/HackerNews/Assets.xcassets/Color.colorset/Contents.json b/ios/HackerNews/Assets.xcassets/Color.colorset/Contents.json new file mode 100644 index 00000000..951b9076 --- /dev/null +++ b/ios/HackerNews/Assets.xcassets/Color.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xFF", + "red" : "0xFE" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xFF", + "red" : "0xFE" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/HackerNews/Assets.xcassets/HNRed.colorset/Contents.json b/ios/HackerNews/Assets.xcassets/HNRed.colorset/Contents.json new file mode 100644 index 00000000..6f33540a --- /dev/null +++ b/ios/HackerNews/Assets.xcassets/HNRed.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x6F", + "green" : "0x47", + "red" : "0xEF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/HackerNews/Components/StoryRowV2.swift b/ios/HackerNews/Components/StoryRowV2.swift new file mode 100644 index 00000000..38ea9dda --- /dev/null +++ b/ios/HackerNews/Components/StoryRowV2.swift @@ -0,0 +1,60 @@ +// +// StoryRowV2.swift +// HackerNews +// +// Created by Rikin Marfatia on 11/20/24. +// + +import SwiftUI + +struct StoryRowV2: View { + @ObservedObject var model: AppViewModel + 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) + } + } + } +} + +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) + } + } +} diff --git a/ios/HackerNews/ContentView.swift b/ios/HackerNews/ContentView.swift index 67815381..8e91e2b4 100644 --- a/ios/HackerNews/ContentView.swift +++ b/ios/HackerNews/ContentView.swift @@ -14,7 +14,23 @@ struct ContentView: View { var body: some View { switch appState.authState { case .loggedIn: - PostListScreen(appState: appState) + TabView { + StoriesScreen(model: appState) + .tabItem { + Label("Feed", systemImage: "list.dash") + } + + BookmarksScreen() + .tabItem { + Label("Bookmarks", systemImage: "book.fill") + } + + SettingsScreen() + .tabItem { + Label("Settings", systemImage: "gear") + } + } + .navigationTitle("Hacker News") case .loggedOut: LoginScreen(appState: appState) } diff --git a/ios/HackerNews/Screens/BookmarksScreen.swift b/ios/HackerNews/Screens/BookmarksScreen.swift new file mode 100644 index 00000000..25b7750f --- /dev/null +++ b/ios/HackerNews/Screens/BookmarksScreen.swift @@ -0,0 +1,15 @@ +// +// BookmarksScreen.swift +// HackerNews +// +// Created by Rikin Marfatia on 11/22/24. +// + +import Foundation +import SwiftUI + +struct BookmarksScreen: View { + var body: some View { + Text("Hello Bookmarks") + } +} diff --git a/ios/HackerNews/Screens/PostListScreen.swift b/ios/HackerNews/Screens/PostListScreen.swift index 8d3eeb8f..8b3a9e59 100644 --- a/ios/HackerNews/Screens/PostListScreen.swift +++ b/ios/HackerNews/Screens/PostListScreen.swift @@ -21,24 +21,26 @@ struct PostListScreen: View { .scaleEffect(2) case .loaded(let stories): List(stories, id: \.id) { story in - let navigationValue: AppViewModel.AppNavigation = { - if let url = story.makeUrl() { - return AppViewModel.AppNavigation.webLink(url: url, title: story.title) - } else { - return AppViewModel.AppNavigation.storyComments(story: story) - } - }() - NavigationLink( - value: navigationValue, - label: { - StoryRow( - appState: appState, - story: story, - index: stories.firstIndex(where: { $0.id == story.id })! - ) - } - ) - .listRowBackground(Color.clear) + let navigationValue: AppViewModel.AppNavigation = { + if let url = story.makeUrl() { + return AppViewModel.AppNavigation.webLink(url: url, title: story.title) + } else { + return AppViewModel.AppNavigation.storyComments(story: story) + } + }() + + StoryRowV2( + model: appState, + story: story + ) + .background( + NavigationLink( + value: navigationValue, + label: {} + ) + .opacity(0.0) + ) + .listRowBackground(Color.clear) } .listStyle(.plain) } diff --git a/ios/HackerNews/Screens/SettingsScreen.swift b/ios/HackerNews/Screens/SettingsScreen.swift new file mode 100644 index 00000000..532d8595 --- /dev/null +++ b/ios/HackerNews/Screens/SettingsScreen.swift @@ -0,0 +1,15 @@ +// +// SettingsScreen.swift +// HackerNews +// +// Created by Rikin Marfatia on 11/22/24. +// + +import Foundation +import SwiftUI + +struct SettingsScreen: View { + var body: some View { + Text("Hello Settings") + } +} diff --git a/ios/HackerNews/Screens/StoriesScreen.swift b/ios/HackerNews/Screens/StoriesScreen.swift new file mode 100644 index 00000000..7457c058 --- /dev/null +++ b/ios/HackerNews/Screens/StoriesScreen.swift @@ -0,0 +1,46 @@ +// +// StoriesScreen.swift +// HackerNews +// +// Created by Rikin Marfatia on 11/22/24. +// + +import Foundation +import SwiftUI + +struct StoriesScreen: View { + @ObservedObject var model: AppViewModel + + var body: some View { + switch model.storiesState { + case .notStarted, .loading: + ProgressView() + case .loaded(let stories): + List(stories, id: \.id) { story in + StoryRowV2(model: model, story: story) + .background( + NavigationLink( + value: navigationForStory(story: story), + label: {} + ) + .opacity(0.0) + ) + .listRowBackground(Color.clear) + } + .listStyle(.plain) + } + } + + private func navigationForStory(story: Story) -> AppViewModel.AppNavigation { + if let url = story.makeUrl() { + return AppViewModel.AppNavigation.webLink(url: url, title: story.title) + } else { + return AppViewModel.AppNavigation.storyComments(story: story) + } + } +} + + +#Preview { + StoriesScreen(model: AppViewModel()) +} diff --git a/ios/HackerNews/Screens/StoryRow.swift b/ios/HackerNews/Screens/StoryRow.swift new file mode 100644 index 00000000..df9edde9 --- /dev/null +++ b/ios/HackerNews/Screens/StoryRow.swift @@ -0,0 +1,9 @@ +// +// StoryRow.swift +// HackerNews +// +// Created by Rikin Marfatia on 11/20/24. +// + +import SwiftUI + diff --git a/ios/HackerNews/Utils/Colors.swift b/ios/HackerNews/Utils/Colors.swift index 8ea7da7e..a80a1972 100644 --- a/ios/HackerNews/Utils/Colors.swift +++ b/ios/HackerNews/Utils/Colors.swift @@ -9,7 +9,8 @@ import Foundation import SwiftUI struct HNColors { - static let orange = Color("HNOrange") - static let background = Color("Background") - static let commentBackground = Color("CommentBackground") + static let orange = Color("HNOrange") + static let background = Color("Background") + static let commentBackground = Color("CommentBackground") + static let red = Color("HNRed") }