From f1ee53791c6f328507f15bc5c341d7319500c511 Mon Sep 17 00:00:00 2001 From: Jack Palevich Date: Fri, 31 Jul 2020 22:56:47 -0700 Subject: [PATCH 01/30] Use template where clause to avoid a runtime type test. --- .../Sources/Backend/Models/Listing.swift | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/Packages/Backend/Sources/Backend/Models/Listing.swift b/Packages/Backend/Sources/Backend/Models/Listing.swift index ff8f781..ad98b5d 100644 --- a/Packages/Backend/Sources/Backend/Models/Listing.swift +++ b/Packages/Backend/Sources/Backend/Models/Listing.swift @@ -28,14 +28,24 @@ public struct ListingHolder: Decodable { case kind, data } - public init(from decoder: Decoder) throws { + public init(from decoder: Decoder) throws + where T == GenericListingContent + { let container = try decoder.container(keyedBy: CodingKeys.self) kind = try container.decode(String.self, forKey: .kind) - if T.self == GenericListingContent.self { - data = try GenericListingContent(from: decoder) as! T - } else { - data = try container.decode(T.self, forKey: .data) - } + data = try T(from: decoder) + } + + public init(from decoder: Decoder) throws + { + let container = try decoder.container(keyedBy: CodingKeys.self) + kind = try container.decode(String.self, forKey: .kind) + // This assert can be removed before merging the + // patch. It's just here to verify that + // the specialized init, above, is used for + // when T is a GenericListingContent. + assert(T.self != GenericListingContent.self) + data = try container.decode(T.self, forKey: .data) } } From 77660312ebcd9ba8ce31fb07ee91c8c601ccd867 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Wed, 5 Aug 2020 10:56:25 +0200 Subject: [PATCH 02/30] Beta 4 fixes --- RedditOs.xcodeproj/project.pbxproj | 12 ++++++------ .../Features/Search/ToolbarSearchBar.swift | 4 ++++ .../Subreddit/SubredditPostsListView.swift | 19 ++++++++++--------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/RedditOs.xcodeproj/project.pbxproj b/RedditOs.xcodeproj/project.pbxproj index 655271f..02c2d54 100644 --- a/RedditOs.xcodeproj/project.pbxproj +++ b/RedditOs.xcodeproj/project.pbxproj @@ -577,7 +577,7 @@ CODE_SIGN_ENTITLEMENTS = RedditOs/RedditOs.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 10; DEVELOPMENT_ASSET_PATHS = "\"RedditOs/Preview Content\""; DEVELOPMENT_TEAM = Z6P74P6T99; ENABLE_HARDENED_RUNTIME = YES; @@ -588,8 +588,8 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.16; - MARKETING_VERSION = 0.1; - PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.RedditOs; + MARKETING_VERSION = 0.0.2; + PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.curiosity; PRODUCT_NAME = Curiosity; SWIFT_VERSION = 5.0; }; @@ -604,7 +604,7 @@ CODE_SIGN_ENTITLEMENTS = RedditOs/RedditOs.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 10; DEVELOPMENT_ASSET_PATHS = "\"RedditOs/Preview Content\""; DEVELOPMENT_TEAM = Z6P74P6T99; ENABLE_HARDENED_RUNTIME = YES; @@ -615,8 +615,8 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.16; - MARKETING_VERSION = 0.1; - PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.RedditOs; + MARKETING_VERSION = 0.0.2; + PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.curiosity; PRODUCT_NAME = Curiosity; SWIFT_VERSION = 5.0; }; diff --git a/RedditOs/Features/Search/ToolbarSearchBar.swift b/RedditOs/Features/Search/ToolbarSearchBar.swift index 30471e6..4321917 100644 --- a/RedditOs/Features/Search/ToolbarSearchBar.swift +++ b/RedditOs/Features/Search/ToolbarSearchBar.swift @@ -24,6 +24,10 @@ struct ToolbarSearchBar: View { .background(Color.black.opacity(0.2).cornerRadius(8))) .textFieldStyle(PlainTextFieldStyle()) .frame(width: 500) + .popover(isPresented: $isFocused) { + Text("Search results coming soon") + .frame(width: 500, height: 500) + } } } diff --git a/RedditOs/Features/Subreddit/SubredditPostsListView.swift b/RedditOs/Features/Subreddit/SubredditPostsListView.swift index d6b66f9..8458186 100644 --- a/RedditOs/Features/Subreddit/SubredditPostsListView.swift +++ b/RedditOs/Features/Subreddit/SubredditPostsListView.swift @@ -47,21 +47,18 @@ struct SubredditPostsListView: View { .navigationSubtitle(subtitle) .toolbar { ToolbarItem(placement: .primaryAction) { - ToolbarSearchBar() + Spacer() } ToolbarItem(placement: .primaryAction) { - Picker(selection: $displayMode, - label: Text("Display"), + Picker("", + selection: $displayMode, content: { ForEach(SubredditPostRow.DisplayMode.allCases, id: \.self) { mode in - HStack { - Text(mode.rawValue.capitalized) - Image(systemName: mode.iconName()) - .tag(mode) - } + Image(systemName: mode.iconName()) + .tag(mode) } - }) + }).pickerStyle(InlinePickerStyle()) } ToolbarItem(placement: .primaryAction) { @@ -95,6 +92,10 @@ struct SubredditPostsListView: View { } .keyboardShortcut("s", modifiers: .command) } + + ToolbarItem(placement: .primaryAction) { + ToolbarSearchBar() + } } .onAppear(perform: viewModel.fetchListings) .onAppear { From 10bf0a339b4df50fb885d23bb87ee5875249598e Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Wed, 5 Aug 2020 14:02:52 +0200 Subject: [PATCH 03/30] Proper settings view --- RedditOs/Features/Settings/SettingsView.swift | 88 +++++++------------ RedditOs/Features/Sidebar/Sidebar.swift | 5 +- 2 files changed, 32 insertions(+), 61 deletions(-) diff --git a/RedditOs/Features/Settings/SettingsView.swift b/RedditOs/Features/Settings/SettingsView.swift index 74ccd69..fd020a8 100644 --- a/RedditOs/Features/Settings/SettingsView.swift +++ b/RedditOs/Features/Settings/SettingsView.swift @@ -9,64 +9,36 @@ import SwiftUI struct SettingsView: View { var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) - .toolbar { - ToolbarItem { - Button(action: { - - }, label: { - VStack { - Image(systemName: "gearshape").imageScale(.large) - Text("General") - } - }) - } - - ToolbarItem { - Button(action: { - - }, label: { - VStack { - Image(systemName: "textformat.alt").imageScale(.large) - Text("Apperance") - } - }) - } - - - ToolbarItem { - Button(action: { - - }, label: { - VStack { - Image(systemName: "stop.circle").imageScale(.large) - Text("Filters") - } - }) - } - - ToolbarItem { - Button(action: { - - }, label: { - VStack { - Image(systemName: "magnifyingglass").imageScale(.large) - Text("Search") - } - }) - } - - - ToolbarItem { - Button(action: { - - }, label: { - VStack { - Image(systemName: "person").imageScale(.large) - Text("Accounts") - } - }) - } + TabView { + Text("General") + .tabItem { + Image(systemName: "gearshape").imageScale(.large) + Text("General") + } + + Text("Apperance") + .tabItem { + Image(systemName: "textformat.alt").imageScale(.large) + Text("Apperance") + } + + Text("Filters") + .tabItem { + Image(systemName: "stop.circle").imageScale(.large) + Text("Filters") + } + + Text("Search") + .tabItem { + Image(systemName: "magnifyingglass").imageScale(.large) + Text("Search") + } + + Text("Accounts") + .tabItem { + Image(systemName: "person").imageScale(.large) + Text("Accounts") + } }.frame(width: 1000, height: 500) } } diff --git a/RedditOs/Features/Sidebar/Sidebar.swift b/RedditOs/Features/Sidebar/Sidebar.swift index 305ac2d..4ea37ab 100644 --- a/RedditOs/Features/Sidebar/Sidebar.swift +++ b/RedditOs/Features/Sidebar/Sidebar.swift @@ -38,11 +38,11 @@ struct Sidebar: View { Label("Inbox", systemImage: "envelope") NavigationLink(destination: SubmittedPostsListView()) { Label("Posts", systemImage: "square.and.pencil") - } + }.tag("Posts") Label("Comments", systemImage: "text.bubble") NavigationLink(destination: SavedPostsListView()) { Label("Saved", systemImage: "archivebox") - } + }.tag("Saved") }.listItemTint(.redditBlue) Section(header: subredditsHeader) { @@ -105,7 +105,6 @@ struct Sidebar: View { .onHover { hovered in isHovered = hovered } - .padding(.top, 16) } private var subredditsHeader: some View { From 8e43c8a145f5f0a173b8e998271bb75b7c470c5c Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Wed, 5 Aug 2020 19:48:07 +0200 Subject: [PATCH 04/30] WIP quick search + display awards --- .../Sources/Backend/Models/Award.swift | 25 +++++ .../Sources/Backend/Models/Media.swift | 52 +++++++++ .../Backend/Models/SubredditPost.swift | 46 +------- RedditOs.xcodeproj/project.pbxproj | 30 +++-- RedditOs/Environements/UIState.swift | 4 + .../GlobalSearchPopoverView.swift | 104 ++++++++++++++++++ .../PopoverSearchSubredditRow.swift | 0 .../PopoverSearchSubredditView.swift | 2 +- ...l.swift => SubredditSearchViewModel.swift} | 11 +- .../Features/Search/ToolbarSearchBar.swift | 9 +- RedditOs/Features/Sidebar/Sidebar.swift | 7 +- RedditOs/Shared/PostAwardView.swift | 60 ++++++++++ RedditOs/Shared/PostInfoView.swift | 29 ++--- 13 files changed, 304 insertions(+), 75 deletions(-) create mode 100644 Packages/Backend/Sources/Backend/Models/Award.swift create mode 100644 Packages/Backend/Sources/Backend/Models/Media.swift create mode 100644 RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift rename RedditOs/Features/Search/{Subreddit Popover => Subreddit Search Popover}/PopoverSearchSubredditRow.swift (100%) rename RedditOs/Features/Search/{Subreddit Popover => Subreddit Search Popover}/PopoverSearchSubredditView.swift (94%) rename RedditOs/Features/Search/{Subreddit Popover/PopoverSearchSubredditViewModel.swift => SubredditSearchViewModel.swift} (83%) create mode 100644 RedditOs/Shared/PostAwardView.swift diff --git a/Packages/Backend/Sources/Backend/Models/Award.swift b/Packages/Backend/Sources/Backend/Models/Award.swift new file mode 100644 index 0000000..6d2bd1d --- /dev/null +++ b/Packages/Backend/Sources/Backend/Models/Award.swift @@ -0,0 +1,25 @@ +// +// File.swift +// +// +// Created by Thomas Ricouard on 05/08/2020. +// + +import Foundation + +public struct Award: Decodable, Identifiable { + public let id: String + public let name: String + public let staticIconUrl: URL + public let description: String + public let count: Int + public let coinPrice: Int +} + + +public let static_award = Award(id: "award", + name: "Awesome", + staticIconUrl: URL(string: "https://i.redd.it/award_images/t5_22cerq/5smbysczm1w41_Hugz.png")!, + description: "Awesome reward", + count: 5, + coinPrice: 200) diff --git a/Packages/Backend/Sources/Backend/Models/Media.swift b/Packages/Backend/Sources/Backend/Models/Media.swift new file mode 100644 index 0000000..9d07fbf --- /dev/null +++ b/Packages/Backend/Sources/Backend/Models/Media.swift @@ -0,0 +1,52 @@ +// +// File.swift +// +// +// Created by Thomas Ricouard on 05/08/2020. +// + +import Foundation + +public struct SecureMedia: Decodable { + public let redditVideo: RedditVideo? + public let oembed: Oembed? + + public var video: Video? { + if let video = redditVideo { + return Video(url: video.fallbackUrl, width: video.width, height: video.height) + } else if oembed?.type == "video", + let oembed = oembed, + let url = oembed.url, + let width = oembed.width, + let height = oembed.height { + return Video(url: url, width: width, height: height) + } + return nil + } +} + +public struct RedditVideo: Decodable { + public let fallbackUrl: URL + public let height: Int + public let width: Int +} + +public struct Oembed: Decodable { + public let providerUrl: URL? + public let thumbnailUrl: String? + public var thumbnailUrlAsURL: URL? { + thumbnailUrl != nil ? URL(string: thumbnailUrl!) : nil + } + public let url: URL? + public let width: Int? + public let height: Int? + public let thumbnailWidth: Int? + public let thumbnailHeight: Int? + public let type: String? +} + +public struct Video { + public let url: URL + public let width: Int + public let height: Int +} diff --git a/Packages/Backend/Sources/Backend/Models/SubredditPost.swift b/Packages/Backend/Sources/Backend/Models/SubredditPost.swift index 883b451..c455a0d 100644 --- a/Packages/Backend/Sources/Backend/Models/SubredditPost.swift +++ b/Packages/Backend/Sources/Backend/Models/SubredditPost.swift @@ -42,6 +42,7 @@ public struct SubredditPost: Decodable, Identifiable, Hashable { public let linkFlairText: String? public let linkFlairBackgroundColor: String? public let linkFlairTextColor: String? + public let allAwardings: [Award] public var visited: Bool public var saved: Bool public var redditURL: URL? { @@ -53,50 +54,6 @@ public struct SubredditPost: Decodable, Identifiable, Hashable { public var likes: Bool? } -public struct SecureMedia: Decodable { - public let redditVideo: RedditVideo? - public let oembed: Oembed? - - public var video: Video? { - if let video = redditVideo { - return Video(url: video.fallbackUrl, width: video.width, height: video.height) - } else if oembed?.type == "video", - let oembed = oembed, - let url = oembed.url, - let width = oembed.width, - let height = oembed.height { - return Video(url: url, width: width, height: height) - } - return nil - } -} - -public struct RedditVideo: Decodable { - public let fallbackUrl: URL - public let height: Int - public let width: Int -} - -public struct Oembed: Decodable { - public let providerUrl: URL? - public let thumbnailUrl: String? - public var thumbnailUrlAsURL: URL? { - thumbnailUrl != nil ? URL(string: thumbnailUrl!) : nil - } - public let url: URL? - public let width: Int? - public let height: Int? - public let thumbnailWidth: Int? - public let thumbnailHeight: Int? - public let type: String? -} - -public struct Video { - public let url: URL - public let width: Int - public let height: Int -} - public let static_listing = SubredditPost(id: "0", name: "t3_0", title: "A very long title to be able to debug the UI correctly as it should be displayed on mutliple lines.", @@ -116,6 +73,7 @@ public let static_listing = SubredditPost(id: "0", linkFlairText: nil, linkFlairBackgroundColor: nil, linkFlairTextColor: nil, + allAwardings: [], visited: false, saved: false, likes: nil) diff --git a/RedditOs.xcodeproj/project.pbxproj b/RedditOs.xcodeproj/project.pbxproj index 02c2d54..1c847aa 100644 --- a/RedditOs.xcodeproj/project.pbxproj +++ b/RedditOs.xcodeproj/project.pbxproj @@ -35,7 +35,7 @@ 6970A0B324B77D1200B11031 /* PostVoteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0B224B77D1200B11031 /* PostVoteView.swift */; }; 6970A0B624B783FE00B11031 /* LinkPresentationRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0B524B783FE00B11031 /* LinkPresentationRepresentable.swift */; }; 6970A0B924B79AFD00B11031 /* PopoverSearchSubredditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0B824B79AFD00B11031 /* PopoverSearchSubredditView.swift */; }; - 6970A0BB24B79F5900B11031 /* PopoverSearchSubredditViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0BA24B79F5900B11031 /* PopoverSearchSubredditViewModel.swift */; }; + 6970A0BB24B79F5900B11031 /* SubredditSearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0BA24B79F5900B11031 /* SubredditSearchViewModel.swift */; }; 6970A0BD24B82E1C00B11031 /* LoadingRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0BC24B82E1C00B11031 /* LoadingRow.swift */; }; 6970A0BF24B8343100B11031 /* PopoverSearchSubredditRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0BE24B8343100B11031 /* PopoverSearchSubredditRow.swift */; }; 6970A0C124B88BA200B11031 /* PostViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0C024B88BA200B11031 /* PostViewModel.swift */; }; @@ -50,6 +50,8 @@ 69EACF1C24B7272E00303A16 /* Backend in Frameworks */ = {isa = PBXBuildFile; productRef = 69EACF1B24B7272E00303A16 /* Backend */; }; 69EACF1F24B7298800303A16 /* SidebarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69EACF1E24B7298800303A16 /* SidebarViewModel.swift */; }; 69EACF2524B73DF400303A16 /* SubredditViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69EACF2424B73DF400303A16 /* SubredditViewModel.swift */; }; + 69F74E9324DAE65100E58BD8 /* PostAwardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F74E9224DAE65100E58BD8 /* PostAwardView.swift */; }; + 69F74E9624DB0B7300E58BD8 /* GlobalSearchPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F74E9524DB0B7300E58BD8 /* GlobalSearchPopoverView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -80,7 +82,7 @@ 6970A0B224B77D1200B11031 /* PostVoteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostVoteView.swift; sourceTree = ""; }; 6970A0B524B783FE00B11031 /* LinkPresentationRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPresentationRepresentable.swift; sourceTree = ""; }; 6970A0B824B79AFD00B11031 /* PopoverSearchSubredditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverSearchSubredditView.swift; sourceTree = ""; }; - 6970A0BA24B79F5900B11031 /* PopoverSearchSubredditViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverSearchSubredditViewModel.swift; sourceTree = ""; }; + 6970A0BA24B79F5900B11031 /* SubredditSearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubredditSearchViewModel.swift; sourceTree = ""; }; 6970A0BC24B82E1C00B11031 /* LoadingRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingRow.swift; sourceTree = ""; }; 6970A0BE24B8343100B11031 /* PopoverSearchSubredditRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverSearchSubredditRow.swift; sourceTree = ""; }; 6970A0C024B88BA200B11031 /* PostViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostViewModel.swift; sourceTree = ""; }; @@ -98,6 +100,8 @@ 69EACF1924B7223E00303A16 /* Backend */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Backend; path = Packages/Backend; sourceTree = ""; }; 69EACF1E24B7298800303A16 /* SidebarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarViewModel.swift; sourceTree = ""; }; 69EACF2424B73DF400303A16 /* SubredditViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubredditViewModel.swift; sourceTree = ""; }; + 69F74E9224DAE65100E58BD8 /* PostAwardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostAwardView.swift; sourceTree = ""; }; + 69F74E9524DB0B7300E58BD8 /* GlobalSearchPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalSearchPopoverView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -184,14 +188,13 @@ path = Profile; sourceTree = ""; }; - 693F85D224D06AA700224ADB /* Subreddit Popover */ = { + 693F85D224D06AA700224ADB /* Subreddit Search Popover */ = { isa = PBXGroup; children = ( 6970A0B824B79AFD00B11031 /* PopoverSearchSubredditView.swift */, - 6970A0BA24B79F5900B11031 /* PopoverSearchSubredditViewModel.swift */, 6970A0BE24B8343100B11031 /* PopoverSearchSubredditRow.swift */, ); - path = "Subreddit Popover"; + path = "Subreddit Search Popover"; sourceTree = ""; }; 6970A0AC24B7494C00B11031 /* Shared */ = { @@ -203,6 +206,7 @@ 6918A8CA24C1FEDC008A74E1 /* FlairView.swift */, 69222AA024CC015E009F31B4 /* PostsListView.swift */, 69222AA224CC0291009F31B4 /* PostNoSelectionPlaceholder.swift */, + 69F74E9224DAE65100E58BD8 /* PostAwardView.swift */, ); path = Shared; sourceTree = ""; @@ -218,8 +222,10 @@ 6970A0B724B79AF400B11031 /* Search */ = { isa = PBXGroup; children = ( - 693F85D224D06AA700224ADB /* Subreddit Popover */, + 6970A0BA24B79F5900B11031 /* SubredditSearchViewModel.swift */, 693F85D324D0715000224ADB /* ToolbarSearchBar.swift */, + 69F74E9424DB0B5600E58BD8 /* Global Search Popopver */, + 693F85D224D06AA700224ADB /* Subreddit Search Popover */, ); path = Search; sourceTree = ""; @@ -332,6 +338,14 @@ path = Post; sourceTree = ""; }; + 69F74E9424DB0B5600E58BD8 /* Global Search Popopver */ = { + isa = PBXGroup; + children = ( + 69F74E9524DB0B7300E58BD8 /* GlobalSearchPopoverView.swift */, + ); + path = "Global Search Popopver"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -428,9 +442,11 @@ 6924D54A24CDDDD9005487CA /* UserSheetCommentsView.swift in Sources */, 6970A0C324B896CD00B11031 /* PostDetailCommentsSection.swift in Sources */, 6970A0B924B79AFD00B11031 /* PopoverSearchSubredditView.swift in Sources */, + 69F74E9624DB0B7300E58BD8 /* GlobalSearchPopoverView.swift in Sources */, 69222AA324CC0291009F31B4 /* PostNoSelectionPlaceholder.swift in Sources */, 6924D53C24CD949D005487CA /* UserPopoverView.swift in Sources */, - 6970A0BB24B79F5900B11031 /* PopoverSearchSubredditViewModel.swift in Sources */, + 69F74E9324DAE65100E58BD8 /* PostAwardView.swift in Sources */, + 6970A0BB24B79F5900B11031 /* SubredditSearchViewModel.swift in Sources */, 6924D54524CDD7D7005487CA /* UserSheetOverviewView.swift in Sources */, 6906880D24B743900067D973 /* SubredditPostRow.swift in Sources */, 69EACF1F24B7298800303A16 /* SidebarViewModel.swift in Sources */, diff --git a/RedditOs/Environements/UIState.swift b/RedditOs/Environements/UIState.swift index 070e4b4..f4984a1 100644 --- a/RedditOs/Environements/UIState.swift +++ b/RedditOs/Environements/UIState.swift @@ -31,4 +31,8 @@ class UIState: ObservableObject { } @Published var presentedRoute: Route? + + @Published var searchedSubreddit = "" + @Published var searchedUser = "" + @Published var displaySearch = false } diff --git a/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift b/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift new file mode 100644 index 0000000..cd6c4f6 --- /dev/null +++ b/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift @@ -0,0 +1,104 @@ +// +// GlobalSearchPopoverView.swift +// RedditOs +// +// Created by Thomas Ricouard on 05/08/2020. +// + +import SwiftUI +import Backend +import SDWebImageSwiftUI + +struct GlobalSearchPopoverView: View { + @EnvironmentObject private var uiState: UIState + @EnvironmentObject private var currentUser: CurrentUserStore + + @ObservedObject var viewModel: SubredditSearchViewModel + + var body: some View { + ScrollView { + VStack(alignment: .leading) { + makeTitle("Quick access") + makeQuickAccess() + + Divider() + + makeTitle("My subscriptions") + makeMySubscriptionsSearch() + + Divider() + + makeTitle("Subreddit search") + makeSubredditSearch() + + }.padding() + }.frame(width: 300, height: 500) + } + + private func makeTitle(_ title: String) -> some View { + Text(title) + .font(.headline) + .fontWeight(.bold) + } + + private func makeQuickAccess() -> some View { + Group { + Label("Go to r/\(viewModel.searchText)", systemImage: "globe") + .onTapGesture { + uiState.searchedSubreddit = viewModel.searchText + uiState.displaySearch = true + } + Label("Go to u/\(viewModel.searchText)", systemImage: "person") + }.padding(4) + } + + private func makeMySubscriptionsSearch() -> some View { + Group { + let subs = currentUser.subscriptions.filter{ $0.displayName.lowercased().contains(viewModel.searchText.lowercased()) } + if !subs.isEmpty { + ForEach(subs) { sub in + makeSubRow(icon: sub.iconImg, name: sub.displayName) + } + } else { + Label("No matching subscriptions for \(viewModel.searchText)", systemImage: "magnifyingglass") + } + }.padding(4) + } + + private func makeSubredditSearch() -> some View { + Group { + if let results = viewModel.results { + if results.isEmpty { + Label("No matching search for \(viewModel.searchText)", systemImage: "magnifyingglass") + } else { + ForEach(results) { sub in + makeSubRow(icon: sub.iconImg, name: sub.name) + } + } + } else if viewModel.isLoading { + LoadingRow(text: nil) + } + }.padding(4) + } + + private func makeSubRow(icon: String?, name: String) -> some View { + HStack { + if let image = icon, + let url = URL(string: image) { + WebImage(url: url) + .resizable() + .frame(width: 16, height: 16) + .cornerRadius(8) + } else { + Image(systemName: "globe") + .resizable() + .frame(width: 16, height: 16) + } + Text(name) + } + .onTapGesture { + uiState.searchedSubreddit = name + uiState.displaySearch = true + } + } +} diff --git a/RedditOs/Features/Search/Subreddit Popover/PopoverSearchSubredditRow.swift b/RedditOs/Features/Search/Subreddit Search Popover/PopoverSearchSubredditRow.swift similarity index 100% rename from RedditOs/Features/Search/Subreddit Popover/PopoverSearchSubredditRow.swift rename to RedditOs/Features/Search/Subreddit Search Popover/PopoverSearchSubredditRow.swift diff --git a/RedditOs/Features/Search/Subreddit Popover/PopoverSearchSubredditView.swift b/RedditOs/Features/Search/Subreddit Search Popover/PopoverSearchSubredditView.swift similarity index 94% rename from RedditOs/Features/Search/Subreddit Popover/PopoverSearchSubredditView.swift rename to RedditOs/Features/Search/Subreddit Search Popover/PopoverSearchSubredditView.swift index dca8308..0cfc1e5 100644 --- a/RedditOs/Features/Search/Subreddit Popover/PopoverSearchSubredditView.swift +++ b/RedditOs/Features/Search/Subreddit Search Popover/PopoverSearchSubredditView.swift @@ -10,7 +10,7 @@ import Backend struct PopoverSearchSubredditView: View { @EnvironmentObject private var userData: LocalDataStore - @StateObject private var viewModel = PopoverSearchSubredditViewModel() + @StateObject private var viewModel = SubredditSearchViewModel() var body: some View { List { diff --git a/RedditOs/Features/Search/Subreddit Popover/PopoverSearchSubredditViewModel.swift b/RedditOs/Features/Search/SubredditSearchViewModel.swift similarity index 83% rename from RedditOs/Features/Search/Subreddit Popover/PopoverSearchSubredditViewModel.swift rename to RedditOs/Features/Search/SubredditSearchViewModel.swift index 1237003..c84e136 100644 --- a/RedditOs/Features/Search/Subreddit Popover/PopoverSearchSubredditViewModel.swift +++ b/RedditOs/Features/Search/SubredditSearchViewModel.swift @@ -10,7 +10,7 @@ import SwiftUI import Combine import Backend -class PopoverSearchSubredditViewModel: ObservableObject { +class SubredditSearchViewModel: ObservableObject { @Published var searchText = "" @Published var results: [SubredditSmall]? @Published var isLoading = false @@ -27,8 +27,13 @@ class PopoverSearchSubredditViewModel: ObservableObject { .filter { !$0.isEmpty } .receive(on: DispatchQueue.main) .sink(receiveValue: { [weak self] text in - self?.isLoading = true - self?.search(with: text) + if text.isEmpty { + self?.isLoading = false + self?.results = nil + } else { + self?.isLoading = true + self?.search(with: text) + } }) } diff --git a/RedditOs/Features/Search/ToolbarSearchBar.swift b/RedditOs/Features/Search/ToolbarSearchBar.swift index 4321917..2e59105 100644 --- a/RedditOs/Features/Search/ToolbarSearchBar.swift +++ b/RedditOs/Features/Search/ToolbarSearchBar.swift @@ -9,10 +9,10 @@ import SwiftUI struct ToolbarSearchBar: View { @State private var isFocused = false - @State private var searchText = "" + @StateObject private var searchViewModel = SubredditSearchViewModel() var body: some View { - TextField("Search anything", text: $searchText) { editing in + TextField("Search anything", text: $searchViewModel.searchText) { editing in isFocused = editing } onCommit: { @@ -23,10 +23,9 @@ struct ToolbarSearchBar: View { .stroke(isFocused ? Color.accentColor : Color.clear) .background(Color.black.opacity(0.2).cornerRadius(8))) .textFieldStyle(PlainTextFieldStyle()) - .frame(width: 500) + .frame(width: 300) .popover(isPresented: $isFocused) { - Text("Search results coming soon") - .frame(width: 500, height: 500) + GlobalSearchPopoverView(viewModel: searchViewModel) } } } diff --git a/RedditOs/Features/Sidebar/Sidebar.swift b/RedditOs/Features/Sidebar/Sidebar.swift index 4ea37ab..9639237 100644 --- a/RedditOs/Features/Sidebar/Sidebar.swift +++ b/RedditOs/Features/Sidebar/Sidebar.swift @@ -10,6 +10,7 @@ import Backend import SDWebImageSwiftUI struct Sidebar: View { + @EnvironmentObject private var uiState: UIState @EnvironmentObject private var localData: LocalDataStore @EnvironmentObject private var currentUser: CurrentUserStore @StateObject private var viewModel = SidebarViewModel() @@ -25,6 +26,10 @@ struct Sidebar: View { Label(LocalizedStringKey(item.rawValue.capitalized), systemImage: item.icon()) }.tag(item.rawValue) } + NavigationLink(destination: SubredditPostsListView(name: uiState.searchedSubreddit), + isActive: $uiState.displaySearch) { + EmptyView() + }.hidden() } Section(header: Text("Account")) { @@ -120,7 +125,7 @@ struct Sidebar: View { } .buttonStyle(BorderlessButtonStyle()) .popover(isPresented: $isSearchPopoverPresented) { - PopoverSearchSubredditView().environmentObject(localData) + PopoverSearchSubredditView() } Button { diff --git a/RedditOs/Shared/PostAwardView.swift b/RedditOs/Shared/PostAwardView.swift new file mode 100644 index 0000000..f328a12 --- /dev/null +++ b/RedditOs/Shared/PostAwardView.swift @@ -0,0 +1,60 @@ +// +// PostAwardView.swift +// RedditOs +// +// Created by Thomas Ricouard on 05/08/2020. +// + +import SwiftUI +import Backend +import SDWebImageSwiftUI + +struct PostAwardsView: View { + let awards: [Award] + + @State private var popoverPresented = false + @State private var descriptionPopoverPresented = false + + var body: some View { + HStack { + ForEach(awards.prefix(3)) { award in + HStack(spacing: 2) { + WebImage(url: award.staticIconUrl, isAnimating: .constant(true)) + .resizable() + .frame(width: 16, height: 16) + } + } + Text("\(awards.map{ $0.count }.reduce(0, +))") + .foregroundColor(.gray) + .font(.footnote) + } + .onTapGesture(count: 1, perform: { + popoverPresented = true + }) + .popover(isPresented: $popoverPresented, content: { + ScrollView { + VStack { + ForEach(awards) { award in + HStack(spacing: 8) { + WebImage(url: award.staticIconUrl) + .resizable() + .frame(width: 30, height: 30) + Text(award.name) + Spacer() + Text("\(award.count)") + } + Divider() + } + }.padding() + }.frame(width: 250, height: 300) + }) + } +} + +struct PostAwardsView_Previews: PreviewProvider { + static var previews: some View { + PostAwardsView(awards: [static_award, static_award, + static_award, static_award, + static_award, static_award]) + } +} diff --git a/RedditOs/Shared/PostInfoView.swift b/RedditOs/Shared/PostInfoView.swift index 45d7684..f1e9ef4 100644 --- a/RedditOs/Shared/PostInfoView.swift +++ b/RedditOs/Shared/PostInfoView.swift @@ -19,21 +19,19 @@ struct PostInfoView: View { Text("r/\(post.subreddit)") .fontWeight(.bold) .font(.subheadline) + Button(action: { + showUserPopover = true + }, label: { + HStack(spacing: 4) { + Image(systemName: "person") + Text("u/\(post.author)") + } + }) + .buttonStyle(BorderlessButtonStyle()) + .popover(isPresented: $showUserPopover, content: { + UserPopoverView(username: post.author) + }) HStack(spacing: 12) { - Button(action: { - showUserPopover = true - }, label: { - HStack(spacing: 4) { - Image(systemName: "person") - Text("u/\(post.author)") - } - }) - .buttonStyle(BorderlessButtonStyle()) - .popover(isPresented: $showUserPopover, content: { - UserPopoverView(username: post.author) - .environmentObject(uiState) - }) - HStack(spacing: 4) { Image(systemName: "clock") Text(post.createdUtc, style: .offset) @@ -43,6 +41,9 @@ struct PostInfoView: View { .imageScale(.small) Text("\(post.numComments)") } + if !post.allAwardings.isEmpty { + PostAwardsView(awards: post.allAwardings) + } } .font(.callout) .foregroundColor(.gray) From 3194a45d26c075eefcc3adfdd2dc92ec35938a6b Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Sun, 9 Aug 2020 08:18:03 +0200 Subject: [PATCH 05/30] Settings + WIP search --- .../Backend/User/CurrentUserStore.swift | 2 + RedditOs.xcodeproj/project.pbxproj | 20 ++++++---- RedditOs/Environements/SettingsKey.swift | 13 +++++++ RedditOs/Environements/UIState.swift | 16 +++++++- .../GlobalSearchPopoverView.swift | 37 +++++++----------- .../GlobalSearchSubRow.swift | 38 +++++++++++++++++++ ...hViewModel.swift => SearchViewModel.swift} | 26 ++++++++++--- .../PopoverSearchSubredditView.swift | 2 +- .../Features/Search/ToolbarSearchBar.swift | 2 +- RedditOs/Features/Settings/SettingsView.swift | 27 ++++++++++++- RedditOs/Features/Sidebar/Sidebar.swift | 7 +--- .../Features/Sidebar/SidebarViewModel.swift | 28 -------------- .../Subreddit/SubredditPostsListView.swift | 10 ++--- .../Subreddit/SubredditViewModel.swift | 4 +- RedditOs/RedditOsApp.swift | 2 +- 15 files changed, 151 insertions(+), 83 deletions(-) create mode 100644 RedditOs/Environements/SettingsKey.swift create mode 100644 RedditOs/Features/Search/Global Search Popopver/GlobalSearchSubRow.swift rename RedditOs/Features/Search/{SubredditSearchViewModel.swift => SearchViewModel.swift} (63%) delete mode 100644 RedditOs/Features/Sidebar/SidebarViewModel.swift diff --git a/Packages/Backend/Sources/Backend/User/CurrentUserStore.swift b/Packages/Backend/Sources/Backend/User/CurrentUserStore.swift index 5c746fc..e0544ca 100644 --- a/Packages/Backend/Sources/Backend/User/CurrentUserStore.swift +++ b/Packages/Backend/Sources/Backend/User/CurrentUserStore.swift @@ -4,6 +4,8 @@ import Combine public class CurrentUserStore: ObservableObject, PersistentDataStore { + public static let shared = CurrentUserStore() + @Published public private(set) var user: User? { didSet { persistData(data: SaveData(user: user, diff --git a/RedditOs.xcodeproj/project.pbxproj b/RedditOs.xcodeproj/project.pbxproj index 1c847aa..0677008 100644 --- a/RedditOs.xcodeproj/project.pbxproj +++ b/RedditOs.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 6906880D24B743900067D973 /* SubredditPostRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6906880C24B743900067D973 /* SubredditPostRow.swift */; }; + 691747E224DBEA240017E068 /* GlobalSearchSubRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 691747E124DBEA240017E068 /* GlobalSearchSubRow.swift */; }; 6918A8CB24C1FEDC008A74E1 /* FlairView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6918A8CA24C1FEDC008A74E1 /* FlairView.swift */; }; 691FD7B624C75CCD002E2C9C /* SubredditPostThumbnailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 691FD7B524C75CCD002E2C9C /* SubredditPostThumbnailView.swift */; }; 69222AA124CC015E009F31B4 /* PostsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69222AA024CC015E009F31B4 /* PostsListView.swift */; }; @@ -35,12 +36,13 @@ 6970A0B324B77D1200B11031 /* PostVoteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0B224B77D1200B11031 /* PostVoteView.swift */; }; 6970A0B624B783FE00B11031 /* LinkPresentationRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0B524B783FE00B11031 /* LinkPresentationRepresentable.swift */; }; 6970A0B924B79AFD00B11031 /* PopoverSearchSubredditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0B824B79AFD00B11031 /* PopoverSearchSubredditView.swift */; }; - 6970A0BB24B79F5900B11031 /* SubredditSearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0BA24B79F5900B11031 /* SubredditSearchViewModel.swift */; }; + 6970A0BB24B79F5900B11031 /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0BA24B79F5900B11031 /* SearchViewModel.swift */; }; 6970A0BD24B82E1C00B11031 /* LoadingRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0BC24B82E1C00B11031 /* LoadingRow.swift */; }; 6970A0BF24B8343100B11031 /* PopoverSearchSubredditRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0BE24B8343100B11031 /* PopoverSearchSubredditRow.swift */; }; 6970A0C124B88BA200B11031 /* PostViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0C024B88BA200B11031 /* PostViewModel.swift */; }; 6970A0C324B896CD00B11031 /* PostDetailCommentsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0C224B896CD00B11031 /* PostDetailCommentsSection.swift */; }; 69D076C824B9E871001619AC /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69D076C724B9E871001619AC /* Color.swift */; }; + 69DB093824DFCBC60026811F /* SettingsKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DB093724DFCBC60026811F /* SettingsKey.swift */; }; 69EACF0324B63D5800303A16 /* RedditOsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69EACF0224B63D5800303A16 /* RedditOsApp.swift */; }; 69EACF0724B63D5900303A16 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 69EACF0624B63D5900303A16 /* Assets.xcassets */; }; 69EACF0A24B63D5900303A16 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 69EACF0924B63D5900303A16 /* Preview Assets.xcassets */; }; @@ -48,7 +50,6 @@ 69EACF1524B6F1E200303A16 /* SubredditPostsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69EACF1424B6F1E200303A16 /* SubredditPostsListView.swift */; }; 69EACF1724B6F2BA00303A16 /* PostDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69EACF1624B6F2BA00303A16 /* PostDetailView.swift */; }; 69EACF1C24B7272E00303A16 /* Backend in Frameworks */ = {isa = PBXBuildFile; productRef = 69EACF1B24B7272E00303A16 /* Backend */; }; - 69EACF1F24B7298800303A16 /* SidebarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69EACF1E24B7298800303A16 /* SidebarViewModel.swift */; }; 69EACF2524B73DF400303A16 /* SubredditViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69EACF2424B73DF400303A16 /* SubredditViewModel.swift */; }; 69F74E9324DAE65100E58BD8 /* PostAwardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F74E9224DAE65100E58BD8 /* PostAwardView.swift */; }; 69F74E9624DB0B7300E58BD8 /* GlobalSearchPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F74E9524DB0B7300E58BD8 /* GlobalSearchPopoverView.swift */; }; @@ -56,6 +57,7 @@ /* Begin PBXFileReference section */ 6906880C24B743900067D973 /* SubredditPostRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubredditPostRow.swift; sourceTree = ""; }; + 691747E124DBEA240017E068 /* GlobalSearchSubRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalSearchSubRow.swift; sourceTree = ""; }; 6918A8CA24C1FEDC008A74E1 /* FlairView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlairView.swift; sourceTree = ""; }; 691FD7B524C75CCD002E2C9C /* SubredditPostThumbnailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubredditPostThumbnailView.swift; sourceTree = ""; }; 69222AA024CC015E009F31B4 /* PostsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostsListView.swift; sourceTree = ""; }; @@ -82,12 +84,13 @@ 6970A0B224B77D1200B11031 /* PostVoteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostVoteView.swift; sourceTree = ""; }; 6970A0B524B783FE00B11031 /* LinkPresentationRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPresentationRepresentable.swift; sourceTree = ""; }; 6970A0B824B79AFD00B11031 /* PopoverSearchSubredditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverSearchSubredditView.swift; sourceTree = ""; }; - 6970A0BA24B79F5900B11031 /* SubredditSearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubredditSearchViewModel.swift; sourceTree = ""; }; + 6970A0BA24B79F5900B11031 /* SearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewModel.swift; sourceTree = ""; }; 6970A0BC24B82E1C00B11031 /* LoadingRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingRow.swift; sourceTree = ""; }; 6970A0BE24B8343100B11031 /* PopoverSearchSubredditRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverSearchSubredditRow.swift; sourceTree = ""; }; 6970A0C024B88BA200B11031 /* PostViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostViewModel.swift; sourceTree = ""; }; 6970A0C224B896CD00B11031 /* PostDetailCommentsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostDetailCommentsSection.swift; sourceTree = ""; }; 69D076C724B9E871001619AC /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; + 69DB093724DFCBC60026811F /* SettingsKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKey.swift; sourceTree = ""; }; 69EACEFF24B63D5800303A16 /* Curiosity.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Curiosity.app; sourceTree = BUILT_PRODUCTS_DIR; }; 69EACF0224B63D5800303A16 /* RedditOsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedditOsApp.swift; sourceTree = ""; }; 69EACF0624B63D5900303A16 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -98,7 +101,6 @@ 69EACF1424B6F1E200303A16 /* SubredditPostsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubredditPostsListView.swift; sourceTree = ""; }; 69EACF1624B6F2BA00303A16 /* PostDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostDetailView.swift; sourceTree = ""; }; 69EACF1924B7223E00303A16 /* Backend */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Backend; path = Packages/Backend; sourceTree = ""; }; - 69EACF1E24B7298800303A16 /* SidebarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarViewModel.swift; sourceTree = ""; }; 69EACF2424B73DF400303A16 /* SubredditViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubredditViewModel.swift; sourceTree = ""; }; 69F74E9224DAE65100E58BD8 /* PostAwardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostAwardView.swift; sourceTree = ""; }; 69F74E9524DB0B7300E58BD8 /* GlobalSearchPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalSearchPopoverView.swift; sourceTree = ""; }; @@ -139,6 +141,7 @@ isa = PBXGroup; children = ( 6924D54224CDD049005487CA /* UIState.swift */, + 69DB093724DFCBC60026811F /* SettingsKey.swift */, ); path = Environements; sourceTree = ""; @@ -222,7 +225,7 @@ 6970A0B724B79AF400B11031 /* Search */ = { isa = PBXGroup; children = ( - 6970A0BA24B79F5900B11031 /* SubredditSearchViewModel.swift */, + 6970A0BA24B79F5900B11031 /* SearchViewModel.swift */, 693F85D324D0715000224ADB /* ToolbarSearchBar.swift */, 69F74E9424DB0B5600E58BD8 /* Global Search Popopver */, 693F85D224D06AA700224ADB /* Subreddit Search Popover */, @@ -307,7 +310,6 @@ 69EACF2124B7299000303A16 /* Sidebar */ = { isa = PBXGroup; children = ( - 69EACF1E24B7298800303A16 /* SidebarViewModel.swift */, 69EACF1224B668F200303A16 /* Sidebar.swift */, 694C634E24C0AA6D0017897D /* SidebarSubredditRow.swift */, ); @@ -342,6 +344,7 @@ isa = PBXGroup; children = ( 69F74E9524DB0B7300E58BD8 /* GlobalSearchPopoverView.swift */, + 691747E124DBEA240017E068 /* GlobalSearchSubRow.swift */, ); path = "Global Search Popopver"; sourceTree = ""; @@ -442,14 +445,15 @@ 6924D54A24CDDDD9005487CA /* UserSheetCommentsView.swift in Sources */, 6970A0C324B896CD00B11031 /* PostDetailCommentsSection.swift in Sources */, 6970A0B924B79AFD00B11031 /* PopoverSearchSubredditView.swift in Sources */, + 691747E224DBEA240017E068 /* GlobalSearchSubRow.swift in Sources */, 69F74E9624DB0B7300E58BD8 /* GlobalSearchPopoverView.swift in Sources */, 69222AA324CC0291009F31B4 /* PostNoSelectionPlaceholder.swift in Sources */, 6924D53C24CD949D005487CA /* UserPopoverView.swift in Sources */, 69F74E9324DAE65100E58BD8 /* PostAwardView.swift in Sources */, - 6970A0BB24B79F5900B11031 /* SubredditSearchViewModel.swift in Sources */, + 6970A0BB24B79F5900B11031 /* SearchViewModel.swift in Sources */, 6924D54524CDD7D7005487CA /* UserSheetOverviewView.swift in Sources */, 6906880D24B743900067D973 /* SubredditPostRow.swift in Sources */, - 69EACF1F24B7298800303A16 /* SidebarViewModel.swift in Sources */, + 69DB093824DFCBC60026811F /* SettingsKey.swift in Sources */, 6927894924B9B75200EEFBF2 /* ProfileView.swift in Sources */, 691FD7B624C75CCD002E2C9C /* SubredditPostThumbnailView.swift in Sources */, 69EACF1724B6F2BA00303A16 /* PostDetailView.swift in Sources */, diff --git a/RedditOs/Environements/SettingsKey.swift b/RedditOs/Environements/SettingsKey.swift new file mode 100644 index 0000000..152c451 --- /dev/null +++ b/RedditOs/Environements/SettingsKey.swift @@ -0,0 +1,13 @@ +// +// SettingsKey.swift +// RedditOs +// +// Created by Thomas Ricouard on 09/08/2020. +// + +import Foundation + +struct SettingsKey { + static let subreddit_display_mode = "postDisplayMode" + static let subreddit_defaut_sort_order = "defaultSortOrder" +} diff --git a/RedditOs/Environements/UIState.swift b/RedditOs/Environements/UIState.swift index f4984a1..9c89aab 100644 --- a/RedditOs/Environements/UIState.swift +++ b/RedditOs/Environements/UIState.swift @@ -30,8 +30,22 @@ class UIState: ObservableObject { } } - @Published var presentedRoute: Route? + enum DefaultChannels: String, CaseIterable { + case hot, best, new, top, rising + + func icon() -> String { + switch self { + case .best: return "rosette" + case .hot: return "flame" + case .new: return "calendar.circle" + case .top: return "chart.bar" + case .rising: return "waveform.path.ecg" + } + } + } + @Published var presentedRoute: Route? + @Published var sidebarSelection: Set = [DefaultChannels.hot.rawValue] @Published var searchedSubreddit = "" @Published var searchedUser = "" @Published var displaySearch = false diff --git a/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift b/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift index cd6c4f6..897e30d 100644 --- a/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift +++ b/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift @@ -13,7 +13,7 @@ struct GlobalSearchPopoverView: View { @EnvironmentObject private var uiState: UIState @EnvironmentObject private var currentUser: CurrentUserStore - @ObservedObject var viewModel: SubredditSearchViewModel + @ObservedObject var viewModel: SearchViewModel var body: some View { ScrollView { @@ -43,24 +43,27 @@ struct GlobalSearchPopoverView: View { private func makeQuickAccess() -> some View { Group { - Label("Go to r/\(viewModel.searchText)", systemImage: "globe") + GlobalSearchSubRow(icon: nil, + name: "Go to r/\(viewModel.searchText)") .onTapGesture { uiState.searchedSubreddit = viewModel.searchText uiState.displaySearch = true } - Label("Go to u/\(viewModel.searchText)", systemImage: "person") + GlobalSearchSubRow(icon: nil, + name: "Go to u/\(viewModel.searchText)") }.padding(4) } private func makeMySubscriptionsSearch() -> some View { Group { - let subs = currentUser.subscriptions.filter{ $0.displayName.lowercased().contains(viewModel.searchText.lowercased()) } - if !subs.isEmpty { - ForEach(subs) { sub in - makeSubRow(icon: sub.iconImg, name: sub.displayName) + if let subs = viewModel.filteredSubscriptions { + if subs.isEmpty { + Label("No matching subscriptions for \(viewModel.searchText)", systemImage: "magnifyingglass") + } else { + ForEach(subs) { sub in + makeSubRow(icon: sub.iconImg, name: sub.displayName) + } } - } else { - Label("No matching subscriptions for \(viewModel.searchText)", systemImage: "magnifyingglass") } }.padding(4) } @@ -82,21 +85,7 @@ struct GlobalSearchPopoverView: View { } private func makeSubRow(icon: String?, name: String) -> some View { - HStack { - if let image = icon, - let url = URL(string: image) { - WebImage(url: url) - .resizable() - .frame(width: 16, height: 16) - .cornerRadius(8) - } else { - Image(systemName: "globe") - .resizable() - .frame(width: 16, height: 16) - } - Text(name) - } - .onTapGesture { + GlobalSearchSubRow(icon: icon, name: name).onTapGesture { uiState.searchedSubreddit = name uiState.displaySearch = true } diff --git a/RedditOs/Features/Search/Global Search Popopver/GlobalSearchSubRow.swift b/RedditOs/Features/Search/Global Search Popopver/GlobalSearchSubRow.swift new file mode 100644 index 0000000..a08f462 --- /dev/null +++ b/RedditOs/Features/Search/Global Search Popopver/GlobalSearchSubRow.swift @@ -0,0 +1,38 @@ +// +// GlobalSearchSubRow.swift +// RedditOs +// +// Created by Thomas Ricouard on 06/08/2020. +// + +import SwiftUI +import SDWebImageSwiftUI + +struct GlobalSearchSubRow: View { + let icon: String? + let name: String + + @State private var isHovered = false + + var body: some View { + HStack { + if let image = icon, + let url = URL(string: image) { + WebImage(url: url) + .resizable() + .frame(width: 16, height: 16) + .cornerRadius(8) + } else { + Image(systemName: "globe") + .resizable() + .frame(width: 16, height: 16) + } + Text(name).foregroundColor(isHovered ? .accentColor : nil) + } + .scaleEffect(isHovered ? 1.05 : 1.0) + .animation(.interactiveSpring()) + .onHover { hovered in + isHovered = hovered + } + } +} diff --git a/RedditOs/Features/Search/SubredditSearchViewModel.swift b/RedditOs/Features/Search/SearchViewModel.swift similarity index 63% rename from RedditOs/Features/Search/SubredditSearchViewModel.swift rename to RedditOs/Features/Search/SearchViewModel.swift index c84e136..95f1448 100644 --- a/RedditOs/Features/Search/SubredditSearchViewModel.swift +++ b/RedditOs/Features/Search/SearchViewModel.swift @@ -10,21 +10,26 @@ import SwiftUI import Combine import Backend -class SubredditSearchViewModel: ObservableObject { +class SearchViewModel: ObservableObject { @Published var searchText = "" @Published var results: [SubredditSmall]? + @Published var filteredSubscriptions: [Subreddit]? @Published var isLoading = false - private var searchCancellable: AnyCancellable? + private var currentUser: CurrentUserStore + + private var delayedSearchCancellable: AnyCancellable? + private var instantSearchCancellable: AnyCancellable? private var apiPublisher: AnyPublisher? private var apiCancellable: AnyCancellable? - init() { - searchCancellable = $searchText + init(currentUser: CurrentUserStore = .shared) { + self.currentUser = currentUser + + delayedSearchCancellable = $searchText .subscribe(on: DispatchQueue.global()) .debounce(for: .milliseconds(500), scheduler: DispatchQueue.main) .removeDuplicates() - .filter { !$0.isEmpty } .receive(on: DispatchQueue.main) .sink(receiveValue: { [weak self] text in if text.isEmpty { @@ -35,6 +40,17 @@ class SubredditSearchViewModel: ObservableObject { self?.search(with: text) } }) + + instantSearchCancellable = $searchText + .receive(on: DispatchQueue.main) + .sink(receiveValue: { [weak self] text in + guard let w = self else { return } + if text.isEmpty { + w.filteredSubscriptions = nil + } else { + w.filteredSubscriptions = w.currentUser.subscriptions.filter{ $0.displayName.lowercased().contains(text.lowercased()) } + } + }) } private func search(with text: String) { diff --git a/RedditOs/Features/Search/Subreddit Search Popover/PopoverSearchSubredditView.swift b/RedditOs/Features/Search/Subreddit Search Popover/PopoverSearchSubredditView.swift index 0cfc1e5..157fa76 100644 --- a/RedditOs/Features/Search/Subreddit Search Popover/PopoverSearchSubredditView.swift +++ b/RedditOs/Features/Search/Subreddit Search Popover/PopoverSearchSubredditView.swift @@ -10,7 +10,7 @@ import Backend struct PopoverSearchSubredditView: View { @EnvironmentObject private var userData: LocalDataStore - @StateObject private var viewModel = SubredditSearchViewModel() + @StateObject private var viewModel = SearchViewModel() var body: some View { List { diff --git a/RedditOs/Features/Search/ToolbarSearchBar.swift b/RedditOs/Features/Search/ToolbarSearchBar.swift index 2e59105..e12d182 100644 --- a/RedditOs/Features/Search/ToolbarSearchBar.swift +++ b/RedditOs/Features/Search/ToolbarSearchBar.swift @@ -9,7 +9,7 @@ import SwiftUI struct ToolbarSearchBar: View { @State private var isFocused = false - @StateObject private var searchViewModel = SubredditSearchViewModel() + @StateObject private var searchViewModel = SearchViewModel() var body: some View { TextField("Search anything", text: $searchViewModel.searchText) { editing in diff --git a/RedditOs/Features/Settings/SettingsView.swift b/RedditOs/Features/Settings/SettingsView.swift index fd020a8..5c0d6f5 100644 --- a/RedditOs/Features/Settings/SettingsView.swift +++ b/RedditOs/Features/Settings/SettingsView.swift @@ -8,9 +8,12 @@ import SwiftUI struct SettingsView: View { + @AppStorage(SettingsKey.subreddit_display_mode) private var displayMode = SubredditPostRow.DisplayMode.large + @AppStorage(SettingsKey.subreddit_defaut_sort_order) private var sortOrder = SubredditViewModel.SortOrder.hot + var body: some View { TabView { - Text("General") + generalView .tabItem { Image(systemName: "gearshape").imageScale(.large) Text("General") @@ -41,6 +44,28 @@ struct SettingsView: View { } }.frame(width: 1000, height: 500) } + + private var generalView: some View { + Form { + Section(header: Text("Subreddit settings")) { + Picker("Display mode", + selection: $displayMode, + content: { + ForEach(SubredditPostRow.DisplayMode.allCases, id: \.self) { mode in + Label(mode.rawValue, systemImage: mode.iconName()).tag(mode) + } + }) + + Picker(selection: $sortOrder, + label: Text("Default sort"), + content: { + ForEach(SubredditViewModel.SortOrder.allCases, id: \.self) { sort in + Text(sort.rawValue.capitalized).tag(sort) + } + }) + } + }.frame(width: 500) + } } struct SettingsView_Previews: PreviewProvider { diff --git a/RedditOs/Features/Sidebar/Sidebar.swift b/RedditOs/Features/Sidebar/Sidebar.swift index 9639237..5917aa1 100644 --- a/RedditOs/Features/Sidebar/Sidebar.swift +++ b/RedditOs/Features/Sidebar/Sidebar.swift @@ -13,15 +13,14 @@ struct Sidebar: View { @EnvironmentObject private var uiState: UIState @EnvironmentObject private var localData: LocalDataStore @EnvironmentObject private var currentUser: CurrentUserStore - @StateObject private var viewModel = SidebarViewModel() @State private var isSearchPopoverPresented = false @State private var isHovered = false @State private var isInEditMode = false var body: some View { - List(selection: $viewModel.selection) { + List(selection: $uiState.sidebarSelection) { Section { - ForEach(SidebarViewModel.MainSubreddits.allCases, id: \.self) { item in + ForEach(UIState.DefaultChannels.allCases, id: \.self) { item in NavigationLink(destination: SubredditPostsListView(name: item.rawValue)) { Label(LocalizedStringKey(item.rawValue.capitalized), systemImage: item.icon()) }.tag(item.rawValue) @@ -75,8 +74,6 @@ struct Sidebar: View { if let subs = currentUser.subscriptions, currentUser.user != nil { Section(header: Text("Subscriptions")) { - TextField("Filter", text: $viewModel.subscriptionFilter) - .textFieldStyle(RoundedBorderTextFieldStyle()) ForEach(subs) { reddit in HStack { SidebarSubredditRow(name: reddit.displayName, diff --git a/RedditOs/Features/Sidebar/SidebarViewModel.swift b/RedditOs/Features/Sidebar/SidebarViewModel.swift deleted file mode 100644 index 1d56e00..0000000 --- a/RedditOs/Features/Sidebar/SidebarViewModel.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// SidebarViewModel.swift -// RedditOs -// -// Created by Thomas Ricouard on 09/07/2020. -// - -import Foundation -import SwiftUI - -class SidebarViewModel: ObservableObject { - enum MainSubreddits: String, CaseIterable { - case hot, best, new, top, rising - - func icon() -> String { - switch self { - case .best: return "rosette" - case .hot: return "flame" - case .new: return "calendar.circle" - case .top: return "chart.bar" - case .rising: return "waveform.path.ecg" - } - } - } - - @Published var selection: Set = [MainSubreddits.hot.rawValue] - @Published var subscriptionFilter = "" -} diff --git a/RedditOs/Features/Subreddit/SubredditPostsListView.swift b/RedditOs/Features/Subreddit/SubredditPostsListView.swift index 8458186..b8cfc9a 100644 --- a/RedditOs/Features/Subreddit/SubredditPostsListView.swift +++ b/RedditOs/Features/Subreddit/SubredditPostsListView.swift @@ -15,14 +15,14 @@ struct SubredditPostsListView: View { @EnvironmentObject private var localData: LocalDataStore @StateObject private var viewModel: SubredditViewModel - @AppStorage("postDisplayMode") private var displayMode = SubredditPostRow.DisplayMode.large + @AppStorage(SettingsKey.subreddit_display_mode) private var displayMode = SubredditPostRow.DisplayMode.large init(name: String) { _viewModel = StateObject(wrappedValue: SubredditViewModel(name: name)) } var isDefaultChannel: Bool { - SidebarViewModel.MainSubreddits.allCases.map{ $0.rawValue }.contains(viewModel.name) + UIState.DefaultChannels.allCases.map{ $0.rawValue }.contains(viewModel.name) } var subtitle: String { @@ -46,10 +46,6 @@ struct SubredditPostsListView: View { .navigationTitle(viewModel.name.capitalized) .navigationSubtitle(subtitle) .toolbar { - ToolbarItem(placement: .primaryAction) { - Spacer() - } - ToolbarItem(placement: .primaryAction) { Picker("", selection: $displayMode, @@ -71,7 +67,7 @@ struct SubredditPostsListView: View { ForEach(SubredditViewModel.SortOrder.allCases, id: \.self) { sort in Text(sort.rawValue.capitalized).tag(sort) } - }) + }) } } diff --git a/RedditOs/Features/Subreddit/SubredditViewModel.swift b/RedditOs/Features/Subreddit/SubredditViewModel.swift index 65aa26b..e405863 100644 --- a/RedditOs/Features/Subreddit/SubredditViewModel.swift +++ b/RedditOs/Features/Subreddit/SubredditViewModel.swift @@ -22,12 +22,13 @@ class SubredditViewModel: ObservableObject { @Published var subreddit: Subreddit? @Published var listings: [SubredditPost]? - @Published var sortOrder = SortOrder.hot { + @AppStorage(SettingsKey.subreddit_defaut_sort_order) var sortOrder = SortOrder.hot { didSet { listings = nil fetchListings() } } + @Published var errorLoadingAbout = false init(name: String) { self.name = name @@ -37,6 +38,7 @@ class SubredditViewModel: ObservableObject { subredditCancellable = Subreddit.fetchAbout(name: name) .receive(on: DispatchQueue.main) .sink { [weak self] holder in + self?.errorLoadingAbout = holder == nil self?.subreddit = holder?.data } } diff --git a/RedditOs/RedditOsApp.swift b/RedditOs/RedditOsApp.swift index de50c22..cbf44f5 100644 --- a/RedditOs/RedditOsApp.swift +++ b/RedditOs/RedditOsApp.swift @@ -22,7 +22,7 @@ struct RedditOsApp: App { .frame(minHeight: 400, idealHeight: 800) .environmentObject(LocalDataStore()) .environmentObject(OauthClient.shared) - .environmentObject(CurrentUserStore()) + .environmentObject(CurrentUserStore.shared) .environmentObject(uiState) .onOpenURL { url in OauthClient.shared.handleNextURL(url: url) From 124533e37666d05a61509bc4883104db4288792e Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Mon, 10 Aug 2020 19:28:22 +0200 Subject: [PATCH 06/30] Rich text suppor for flair + flair & awards for comments. --- .../Sources/Backend/Models/Comment.swift | 15 +++++- .../Backend/Models/FlairRichText.swift | 23 ++++++++ .../Backend/Models/SubredditPost.swift | 13 +++++ RedditOs.xcodeproj/project.pbxproj | 8 +-- RedditOs/Environements/UIState.swift | 1 + RedditOs/Features/Comments/CommentRow.swift | 23 ++++++-- RedditOs/Features/Post/PostDetailView.swift | 7 +++ .../Features/Subreddit/SubredditPostRow.swift | 7 ++- RedditOs/RedditOsApp.swift | 13 +++++ .../{PostAwardView.swift => AwardView.swift} | 4 +- RedditOs/Shared/FlairView.swift | 54 +++++++++++++------ RedditOs/Shared/PostInfoView.swift | 32 ++++++----- 12 files changed, 162 insertions(+), 38 deletions(-) create mode 100644 Packages/Backend/Sources/Backend/Models/FlairRichText.swift rename RedditOs/Shared/{PostAwardView.swift => AwardView.swift} (95%) diff --git a/Packages/Backend/Sources/Backend/Models/Comment.swift b/Packages/Backend/Sources/Backend/Models/Comment.swift index 616113c..b546100 100644 --- a/Packages/Backend/Sources/Backend/Models/Comment.swift +++ b/Packages/Backend/Sources/Backend/Models/Comment.swift @@ -17,12 +17,19 @@ public struct Comment: Decodable, Identifiable { public let id: String public let name: String public let body: String? + public let isSubmitter: Bool? public let author: String? public let lindId: String? public let created: Date? public let createdUtc: Date? public let replies: Replies? public let score: Int? + public let allAwardings: [Award]? + + public let authorFlairRichtext: [FlairRichText]? + public let authorFlairText: String? + public let authorFlairTextColor: String? + public let authorFlairBackgroundColor: String? public var repliesComments: [Comment]? { if let replies = replies { @@ -54,10 +61,16 @@ public enum Replies: Decodable { public let static_comment = Comment(id: UUID().uuidString, name: "t1_id", body: "Comment text with a long line of text \n and another line.", + isSubmitter: false, author: "TestUser", lindId: "", created: Date(), createdUtc: Date(), replies: .none(""), - score: 2500) + score: 2500, + allAwardings: [], + authorFlairRichtext: nil, + authorFlairText: nil, + authorFlairTextColor: nil, + authorFlairBackgroundColor: nil) public let static_comments = [static_comment, static_comment, static_comment] diff --git a/Packages/Backend/Sources/Backend/Models/FlairRichText.swift b/Packages/Backend/Sources/Backend/Models/FlairRichText.swift new file mode 100644 index 0000000..7b5209e --- /dev/null +++ b/Packages/Backend/Sources/Backend/Models/FlairRichText.swift @@ -0,0 +1,23 @@ +// +// File.swift +// +// +// Created by Thomas Ricouard on 10/08/2020. +// + +import Foundation + +public struct FlairRichText: Decodable, Hashable { + public func hash(into hasher: inout Hasher) { + hasher.combine(e) + hasher.combine(u) + hasher.combine(t) + } + + /// type + public let e: String + /// image URL + public let u: URL? + /// text + public let t: String? +} diff --git a/Packages/Backend/Sources/Backend/Models/SubredditPost.swift b/Packages/Backend/Sources/Backend/Models/SubredditPost.swift index c455a0d..c84be83 100644 --- a/Packages/Backend/Sources/Backend/Models/SubredditPost.swift +++ b/Packages/Backend/Sources/Backend/Models/SubredditPost.swift @@ -39,9 +39,17 @@ public struct SubredditPost: Decodable, Identifiable, Hashable { public let secureMedia: SecureMedia? public let url: String? public let permalink: String? + public let linkFlairText: String? + public let linkFlairRichtext: [FlairRichText]? public let linkFlairBackgroundColor: String? public let linkFlairTextColor: String? + + public let authorFlairRichtext: [FlairRichText]? + public let authorFlairText: String? + public let authorFlairTextColor: String? + public let authorFlairBackgroundColor: String? + public let allAwardings: [Award] public var visited: Bool public var saved: Bool @@ -71,8 +79,13 @@ public let static_listing = SubredditPost(id: "0", url: "https://test.com", permalink: nil, linkFlairText: nil, + linkFlairRichtext: nil, linkFlairBackgroundColor: nil, linkFlairTextColor: nil, + authorFlairRichtext: nil, + authorFlairText: nil, + authorFlairTextColor: nil, + authorFlairBackgroundColor: nil, allAwardings: [], visited: false, saved: false, diff --git a/RedditOs.xcodeproj/project.pbxproj b/RedditOs.xcodeproj/project.pbxproj index 0677008..8de93d4 100644 --- a/RedditOs.xcodeproj/project.pbxproj +++ b/RedditOs.xcodeproj/project.pbxproj @@ -51,7 +51,7 @@ 69EACF1724B6F2BA00303A16 /* PostDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69EACF1624B6F2BA00303A16 /* PostDetailView.swift */; }; 69EACF1C24B7272E00303A16 /* Backend in Frameworks */ = {isa = PBXBuildFile; productRef = 69EACF1B24B7272E00303A16 /* Backend */; }; 69EACF2524B73DF400303A16 /* SubredditViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69EACF2424B73DF400303A16 /* SubredditViewModel.swift */; }; - 69F74E9324DAE65100E58BD8 /* PostAwardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F74E9224DAE65100E58BD8 /* PostAwardView.swift */; }; + 69F74E9324DAE65100E58BD8 /* AwardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F74E9224DAE65100E58BD8 /* AwardView.swift */; }; 69F74E9624DB0B7300E58BD8 /* GlobalSearchPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F74E9524DB0B7300E58BD8 /* GlobalSearchPopoverView.swift */; }; /* End PBXBuildFile section */ @@ -102,7 +102,7 @@ 69EACF1624B6F2BA00303A16 /* PostDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostDetailView.swift; sourceTree = ""; }; 69EACF1924B7223E00303A16 /* Backend */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Backend; path = Packages/Backend; sourceTree = ""; }; 69EACF2424B73DF400303A16 /* SubredditViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubredditViewModel.swift; sourceTree = ""; }; - 69F74E9224DAE65100E58BD8 /* PostAwardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostAwardView.swift; sourceTree = ""; }; + 69F74E9224DAE65100E58BD8 /* AwardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AwardView.swift; sourceTree = ""; }; 69F74E9524DB0B7300E58BD8 /* GlobalSearchPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalSearchPopoverView.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -209,7 +209,7 @@ 6918A8CA24C1FEDC008A74E1 /* FlairView.swift */, 69222AA024CC015E009F31B4 /* PostsListView.swift */, 69222AA224CC0291009F31B4 /* PostNoSelectionPlaceholder.swift */, - 69F74E9224DAE65100E58BD8 /* PostAwardView.swift */, + 69F74E9224DAE65100E58BD8 /* AwardView.swift */, ); path = Shared; sourceTree = ""; @@ -449,7 +449,7 @@ 69F74E9624DB0B7300E58BD8 /* GlobalSearchPopoverView.swift in Sources */, 69222AA324CC0291009F31B4 /* PostNoSelectionPlaceholder.swift in Sources */, 6924D53C24CD949D005487CA /* UserPopoverView.swift in Sources */, - 69F74E9324DAE65100E58BD8 /* PostAwardView.swift in Sources */, + 69F74E9324DAE65100E58BD8 /* AwardView.swift in Sources */, 6970A0BB24B79F5900B11031 /* SearchViewModel.swift in Sources */, 6924D54524CDD7D7005487CA /* UserSheetOverviewView.swift in Sources */, 6906880D24B743900067D973 /* SubredditPostRow.swift in Sources */, diff --git a/RedditOs/Environements/UIState.swift b/RedditOs/Environements/UIState.swift index 9c89aab..0f38f68 100644 --- a/RedditOs/Environements/UIState.swift +++ b/RedditOs/Environements/UIState.swift @@ -44,6 +44,7 @@ class UIState: ObservableObject { } } + @Published var selectedPost: SubredditPost? @Published var presentedRoute: Route? @Published var sidebarSelection: Set = [DefaultChannels.hot.rawValue] @Published var searchedSubreddit = "" diff --git a/RedditOs/Features/Comments/CommentRow.swift b/RedditOs/Features/Comments/CommentRow.swift index c65ead5..3b62139 100644 --- a/RedditOs/Features/Comments/CommentRow.swift +++ b/RedditOs/Features/Comments/CommentRow.swift @@ -13,9 +13,23 @@ struct CommentRow: View { var body: some View { VStack(alignment: .leading, spacing: 4) { HStack(spacing: 0) { - Text(comment.author ?? "Unknown") - .font(.callout) - .fontWeight(.bold) + HStack(spacing: 6) { + if let richText = comment.authorFlairRichtext, !richText.isEmpty { + FlairView(richText: richText, + textColorHex: comment.authorFlairTextColor, + backgroundColorHex: comment.authorFlairBackgroundColor, + display: .small) + } + if comment.isSubmitter == true { + Image(systemName: "music.mic") + .foregroundColor(.redditBlue) + } else { + Image(systemName: "person") + } + Text(comment.author ?? "Unknown") + .font(.callout) + .fontWeight(.bold) + } if let score = comment.score { Text(" · \(score.toRoundedSuffixAsString()) points · ") .foregroundColor(.gray) @@ -26,6 +40,9 @@ struct CommentRow: View { .foregroundColor(.gray) .font(.caption) } + if let awards = comment.allAwardings, !awards.isEmpty { + AwardsView(awards: awards).padding(.leading, 8) + } } Text(comment.body ?? "No comment content") .font(.body) diff --git a/RedditOs/Features/Post/PostDetailView.swift b/RedditOs/Features/Post/PostDetailView.swift index 59f106a..a21bc05 100644 --- a/RedditOs/Features/Post/PostDetailView.swift +++ b/RedditOs/Features/Post/PostDetailView.swift @@ -10,6 +10,7 @@ import Backend import AVKit struct PostDetailView: View { + @EnvironmentObject private var uiState: UIState @ObservedObject var viewModel: PostViewModel @State private var redrawLink = false @@ -30,6 +31,12 @@ struct PostDetailView: View { } .onAppear(perform: viewModel.fechComments) .onAppear(perform: viewModel.postVisit) + .onAppear(perform: { + uiState.selectedPost = viewModel.post + }) + .onDisappear(perform: { + uiState.selectedPost = nil + }) .frame(minWidth: 500, maxWidth: .infinity, maxHeight: .infinity) diff --git a/RedditOs/Features/Subreddit/SubredditPostRow.swift b/RedditOs/Features/Subreddit/SubredditPostRow.swift index 95db4a9..cb97dff 100644 --- a/RedditOs/Features/Subreddit/SubredditPostRow.swift +++ b/RedditOs/Features/Subreddit/SubredditPostRow.swift @@ -48,7 +48,12 @@ struct SubredditPostRow: View { .lineLimit(displayMode == .compact ? 2 : nil) .foregroundColor(viewModel.post.visited ? .gray : nil) HStack { - FlairView(post: viewModel.post) + if let richText = viewModel.post.linkFlairRichtext, !richText.isEmpty { + FlairView(richText: richText, + textColorHex: viewModel.post.linkFlairTextColor, + backgroundColorHex: viewModel.post.linkFlairBackgroundColor, + display: .normal) + } if (viewModel.post.selftext == nil || viewModel.post.selftext?.isEmpty == true), displayMode == .large, let urlString = viewModel.post.url, diff --git a/RedditOs/RedditOsApp.swift b/RedditOs/RedditOsApp.swift index cbf44f5..6f9ad9f 100644 --- a/RedditOs/RedditOsApp.swift +++ b/RedditOs/RedditOsApp.swift @@ -49,6 +49,19 @@ struct RedditOsApp: App { } } + CommandMenu("Post") { + Button(action: { + + }) { + Text("Upvote") + }.disabled(uiState.selectedPost != nil) + Button(action: { + + }) { + Text("Downvote") + }.disabled(uiState.selectedPost != nil) + } + #if DEBUG CommandMenu("Debug") { Button(action: { diff --git a/RedditOs/Shared/PostAwardView.swift b/RedditOs/Shared/AwardView.swift similarity index 95% rename from RedditOs/Shared/PostAwardView.swift rename to RedditOs/Shared/AwardView.swift index f328a12..fc2d844 100644 --- a/RedditOs/Shared/PostAwardView.swift +++ b/RedditOs/Shared/AwardView.swift @@ -9,7 +9,7 @@ import SwiftUI import Backend import SDWebImageSwiftUI -struct PostAwardsView: View { +struct AwardsView: View { let awards: [Award] @State private var popoverPresented = false @@ -53,7 +53,7 @@ struct PostAwardsView: View { struct PostAwardsView_Previews: PreviewProvider { static var previews: some View { - PostAwardsView(awards: [static_award, static_award, + AwardsView(awards: [static_award, static_award, static_award, static_award, static_award, static_award]) } diff --git a/RedditOs/Shared/FlairView.swift b/RedditOs/Shared/FlairView.swift index 9ec751e..e7f223f 100644 --- a/RedditOs/Shared/FlairView.swift +++ b/RedditOs/Shared/FlairView.swift @@ -7,12 +7,20 @@ import SwiftUI import Backend +import SDWebImageSwiftUI struct FlairView: View { - let post: SubredditPost + enum Display { + case small, normal + } + + let richText: [FlairRichText]? + let textColorHex: String? + let backgroundColorHex: String? + let display: Display var backgroundColor: Color { - if let color = post.linkFlairBackgroundColor{ + if let color = backgroundColorHex { if color.isEmpty { return .gray } @@ -25,29 +33,45 @@ struct FlairView: View { if backgroundColor == .gray { return .white } - return post.linkFlairTextColor == "dark" ? .black : .white + return textColorHex == "dark" ? .black : .white } @ViewBuilder var body: some View { - if post.linkFlairText == nil || post.linkFlairText?.isEmpty == true { - EmptyView() + if let texts = richText { + HStack(spacing: 4) { + ForEach(texts, id: \.self) { text in + if text.e == "emoji" { + WebImage(url: text.u!) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 20, height: 20) + } else if text.e == "text" { + Text(text.t!) + .foregroundColor(textColor) + .font(display == .small ? .footnote : .callout) + .fontWeight(.light) + } else { + EmptyView() + } + } + } + .padding(4) + .background( + RoundedRectangle(cornerRadius: 6) + .fill(backgroundColor) + ) } else { - Text(post.linkFlairText!) - .foregroundColor(textColor) - .font(.callout) - .fontWeight(.light) - .padding(4) - .background( - RoundedRectangle(cornerRadius: 6) - .fill(backgroundColor) - ) + EmptyView() } } } struct FlairView_Previews: PreviewProvider { static var previews: some View { - FlairView(post: static_listing) + FlairView(richText: nil, + textColorHex: nil, + backgroundColorHex: "#dadada", + display: .normal) } } diff --git a/RedditOs/Shared/PostInfoView.swift b/RedditOs/Shared/PostInfoView.swift index f1e9ef4..d9cf9f0 100644 --- a/RedditOs/Shared/PostInfoView.swift +++ b/RedditOs/Shared/PostInfoView.swift @@ -19,18 +19,26 @@ struct PostInfoView: View { Text("r/\(post.subreddit)") .fontWeight(.bold) .font(.subheadline) - Button(action: { - showUserPopover = true - }, label: { - HStack(spacing: 4) { - Image(systemName: "person") - Text("u/\(post.author)") + HStack(spacing: 6) { + Button(action: { + showUserPopover = true + }, label: { + HStack(spacing: 4) { + Image(systemName: "person") + Text("u/\(post.author)") + } + }) + .buttonStyle(BorderlessButtonStyle()) + .popover(isPresented: $showUserPopover, content: { + UserPopoverView(username: post.author) + }) + if let richText = post.authorFlairRichtext, !richText.isEmpty { + FlairView(richText: richText, + textColorHex: post.authorFlairTextColor, + backgroundColorHex: post.authorFlairBackgroundColor, + display: .small) } - }) - .buttonStyle(BorderlessButtonStyle()) - .popover(isPresented: $showUserPopover, content: { - UserPopoverView(username: post.author) - }) + } HStack(spacing: 12) { HStack(spacing: 4) { Image(systemName: "clock") @@ -42,7 +50,7 @@ struct PostInfoView: View { Text("\(post.numComments)") } if !post.allAwardings.isEmpty { - PostAwardsView(awards: post.allAwardings) + AwardsView(awards: post.allAwardings) } } .font(.callout) From 67e3a9d12d327ec52f24a562dc0b2008fa230a60 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Wed, 12 Aug 2020 10:43:07 +0200 Subject: [PATCH 07/30] About community + subreddit icon in toolbar --- .../Sources/Backend/Models/Subreddit.swift | 14 +++++ RedditOs.xcodeproj/project.pbxproj | 4 ++ .../Subreddit/SubredditAboutPopoverView.swift | 56 +++++++++++++++++++ .../Subreddit/SubredditPostsListView.swift | 20 ++++++- 4 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 RedditOs/Features/Subreddit/SubredditAboutPopoverView.swift diff --git a/Packages/Backend/Sources/Backend/Models/Subreddit.swift b/Packages/Backend/Sources/Backend/Models/Subreddit.swift index 5edc107..f484ea5 100644 --- a/Packages/Backend/Sources/Backend/Models/Subreddit.swift +++ b/Packages/Backend/Sources/Backend/Models/Subreddit.swift @@ -12,4 +12,18 @@ public struct Subreddit: Codable, Identifiable { public let bannerImg: String? public let subscribers: Int? public let accountsActive: Int? + public let createdUtc: Date } + +public let static_subreddit_full = Subreddit(id: "games", + displayName: "games", + title: "games", + publicDescription: "a description", + primaryColor: "#545452", + keyColor: "#545452", + bannerBackgroundColor: "#545452", + iconImg: "https://a.thumbs.redditmedia.com/8hr1PTpJ9iWLNWP67vZN0w3IEP8uI3eAQ1kE4XLRg88.png", + bannerImg: "https://a.thumbs.redditmedia.com/8hr1PTpJ9iWLNWP67vZN0w3IEP8uI3eAQ1kE4XLRg88.png", + subscribers: 1000, + accountsActive: 500, + createdUtc: Date()) diff --git a/RedditOs.xcodeproj/project.pbxproj b/RedditOs.xcodeproj/project.pbxproj index 8de93d4..b43e224 100644 --- a/RedditOs.xcodeproj/project.pbxproj +++ b/RedditOs.xcodeproj/project.pbxproj @@ -41,6 +41,7 @@ 6970A0BF24B8343100B11031 /* PopoverSearchSubredditRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0BE24B8343100B11031 /* PopoverSearchSubredditRow.swift */; }; 6970A0C124B88BA200B11031 /* PostViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0C024B88BA200B11031 /* PostViewModel.swift */; }; 6970A0C324B896CD00B11031 /* PostDetailCommentsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0C224B896CD00B11031 /* PostDetailCommentsSection.swift */; }; + 69CCB3EA24E2BEAC003FAAD7 /* SubredditAboutPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69CCB3E924E2BEAC003FAAD7 /* SubredditAboutPopoverView.swift */; }; 69D076C824B9E871001619AC /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69D076C724B9E871001619AC /* Color.swift */; }; 69DB093824DFCBC60026811F /* SettingsKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DB093724DFCBC60026811F /* SettingsKey.swift */; }; 69EACF0324B63D5800303A16 /* RedditOsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69EACF0224B63D5800303A16 /* RedditOsApp.swift */; }; @@ -89,6 +90,7 @@ 6970A0BE24B8343100B11031 /* PopoverSearchSubredditRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverSearchSubredditRow.swift; sourceTree = ""; }; 6970A0C024B88BA200B11031 /* PostViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostViewModel.swift; sourceTree = ""; }; 6970A0C224B896CD00B11031 /* PostDetailCommentsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostDetailCommentsSection.swift; sourceTree = ""; }; + 69CCB3E924E2BEAC003FAAD7 /* SubredditAboutPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubredditAboutPopoverView.swift; sourceTree = ""; }; 69D076C724B9E871001619AC /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; 69DB093724DFCBC60026811F /* SettingsKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKey.swift; sourceTree = ""; }; 69EACEFF24B63D5800303A16 /* Curiosity.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Curiosity.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -323,6 +325,7 @@ 69EACF1424B6F1E200303A16 /* SubredditPostsListView.swift */, 6906880C24B743900067D973 /* SubredditPostRow.swift */, 691FD7B524C75CCD002E2C9C /* SubredditPostThumbnailView.swift */, + 69CCB3E924E2BEAC003FAAD7 /* SubredditAboutPopoverView.swift */, ); path = Subreddit; sourceTree = ""; @@ -442,6 +445,7 @@ 6924D53E24CD94B0005487CA /* UserViewModel.swift in Sources */, 69222AA724CD6D6C009F31B4 /* SubmittedPostsListView.swift in Sources */, 692566DA24B8A3830014E0D4 /* PostDetailHeader.swift in Sources */, + 69CCB3EA24E2BEAC003FAAD7 /* SubredditAboutPopoverView.swift in Sources */, 6924D54A24CDDDD9005487CA /* UserSheetCommentsView.swift in Sources */, 6970A0C324B896CD00B11031 /* PostDetailCommentsSection.swift in Sources */, 6970A0B924B79AFD00B11031 /* PopoverSearchSubredditView.swift in Sources */, diff --git a/RedditOs/Features/Subreddit/SubredditAboutPopoverView.swift b/RedditOs/Features/Subreddit/SubredditAboutPopoverView.swift new file mode 100644 index 0000000..4518db0 --- /dev/null +++ b/RedditOs/Features/Subreddit/SubredditAboutPopoverView.swift @@ -0,0 +1,56 @@ +// +// SubredditAboutPopoverView.swift +// RedditOs +// +// Created by Thomas Ricouard on 11/08/2020. +// + +import SwiftUI +import Backend + +struct SubredditAboutPopoverView: View { + let subreddit: Subreddit? + + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 12) { + Text("About Community") + .font(.title3) + Text(subreddit?.publicDescription ?? "") + .font(.body) + if let subscribers = subreddit?.subscribers, let connected = subreddit?.accountsActive { + HStack(spacing: 16) { + VStack(alignment: .leading) { + Text("\(subscribers.toRoundedSuffixAsString())") + .fontWeight(.bold) + Text("Members") + } + + VStack(alignment: .leading) { + Text("\(connected.toRoundedSuffixAsString())") + .fontWeight(.bold) + Text("Online") + } + } + } + + Divider() + + HStack(spacing: 4) { + Image(systemName: "calendar.circle") + .font(.title3) + .foregroundColor(.white) + Text(" Created ") + + Text(subreddit?.createdUtc ?? Date(), style: .date) + } + + }.padding() + }.frame(width: 250, height: 350) + } +} + +struct SubredditAboutPopoverView_Previews: PreviewProvider { + static var previews: some View { + SubredditAboutPopoverView(subreddit: static_subreddit_full) + } +} diff --git a/RedditOs/Features/Subreddit/SubredditPostsListView.swift b/RedditOs/Features/Subreddit/SubredditPostsListView.swift index b8cfc9a..b0f459d 100644 --- a/RedditOs/Features/Subreddit/SubredditPostsListView.swift +++ b/RedditOs/Features/Subreddit/SubredditPostsListView.swift @@ -7,6 +7,7 @@ import SwiftUI import Backend +import SDWebImageSwiftUI struct SubredditPostsListView: View { let posts = Array(repeating: 0, count: 20) @@ -16,6 +17,7 @@ struct SubredditPostsListView: View { @EnvironmentObject private var localData: LocalDataStore @StateObject private var viewModel: SubredditViewModel @AppStorage(SettingsKey.subreddit_display_mode) private var displayMode = SubredditPostRow.DisplayMode.large + @State private var subredditAboutPopoverShown = false init(name: String) { _viewModel = StateObject(wrappedValue: SubredditViewModel(name: name)) @@ -30,7 +32,7 @@ struct SubredditPostsListView: View { return "" } if let subscribers = viewModel.subreddit?.subscribers, let connected = viewModel.subreddit?.accountsActive { - return "\(subscribers.toRoundedSuffixAsString()) subscribers - \(connected.toRoundedSuffixAsString()) active" + return "\(subscribers.toRoundedSuffixAsString()) members - \(connected.toRoundedSuffixAsString()) online" } return "" } @@ -46,6 +48,22 @@ struct SubredditPostsListView: View { .navigationTitle(viewModel.name.capitalized) .navigationSubtitle(subtitle) .toolbar { + ToolbarItem(placement: .navigation) { + if let icon = viewModel.subreddit?.iconImg, let url = URL(string: icon) { + WebImage(url: url) + .resizable() + .frame(width: 20, height: 20) + .cornerRadius(10) + .onTapGesture { + subredditAboutPopoverShown = true + } + .popover(isPresented: $subredditAboutPopoverShown, + content: { SubredditAboutPopoverView(subreddit: viewModel.subreddit) }) + } else { + EmptyView() + } + } + ToolbarItem(placement: .primaryAction) { Picker("", selection: $displayMode, From e16f7769513498ad6859a31ee858f45d2fd832b0 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Wed, 12 Aug 2020 13:26:58 +0200 Subject: [PATCH 08/30] New commnts system + share picker --- .../Sources/Backend/Models/Comment.swift | 14 +++- .../Backend/Sources/Backend/Models/Vote.swift | 12 +++ .../Network/Models/Comment+Networking.swift | 33 +++++++- .../Models/SubredditPost+Networking.swift | 6 +- Packages/UI/.gitignore | 5 ++ Packages/UI/Package.swift | 24 ++++++ Packages/UI/README.md | 3 + Packages/UI/Sources/UI/RecursiveView.swift | 59 ++++++++++++++ Packages/UI/Tests/UITests/UITests.swift | 15 ++++ .../UI/Tests/UITests/XCTestManifests.swift | 9 +++ RedditOs.xcodeproj/project.pbxproj | 25 ++++++ RedditOs/Environements/SettingsKey.swift | 1 + .../Comments/CommentActionsView.swift | 52 ++++++++++++ RedditOs/Features/Comments/CommentRow.swift | 80 ++++++++++--------- .../Features/Comments/CommentViewModel.swift | 46 +++++++++++ .../Features/Comments/CommentVoteView.swift | 43 ++++++++++ .../Features/Post/PostDetailActionsView.swift | 54 ++++++------- .../Post/PostDetailCommentsSection.swift | 28 ++++--- RedditOs/Features/Post/PostDetailView.swift | 2 +- RedditOs/Features/Post/PostViewModel.swift | 21 ++++- RedditOs/Representables/SharingPicker.swift | 47 +++++++++++ 21 files changed, 492 insertions(+), 87 deletions(-) create mode 100644 Packages/Backend/Sources/Backend/Models/Vote.swift create mode 100644 Packages/UI/.gitignore create mode 100644 Packages/UI/Package.swift create mode 100644 Packages/UI/README.md create mode 100644 Packages/UI/Sources/UI/RecursiveView.swift create mode 100644 Packages/UI/Tests/UITests/UITests.swift create mode 100644 Packages/UI/Tests/UITests/XCTestManifests.swift create mode 100644 RedditOs/Features/Comments/CommentActionsView.swift create mode 100644 RedditOs/Features/Comments/CommentViewModel.swift create mode 100644 RedditOs/Features/Comments/CommentVoteView.swift create mode 100644 RedditOs/Representables/SharingPicker.swift diff --git a/Packages/Backend/Sources/Backend/Models/Comment.swift b/Packages/Backend/Sources/Backend/Models/Comment.swift index b546100..99efb3f 100644 --- a/Packages/Backend/Sources/Backend/Models/Comment.swift +++ b/Packages/Backend/Sources/Backend/Models/Comment.swift @@ -23,8 +23,16 @@ public struct Comment: Decodable, Identifiable { public let created: Date? public let createdUtc: Date? public let replies: Replies? - public let score: Int? + public var score: Int? + public var likes: Bool? public let allAwardings: [Award]? + public var saved: Bool? + + public let permalink: String? + public var permalinkURL: URL? { + guard let permalink = permalink else { return nil } + return URL(string: "https://reddit.com\(permalink)") + } public let authorFlairRichtext: [FlairRichText]? public let authorFlairText: String? @@ -60,7 +68,7 @@ public enum Replies: Decodable { public let static_comment = Comment(id: UUID().uuidString, name: "t1_id", - body: "Comment text with a long line of text \n and another line.", + body: "Comment text with a long line of text\nThis is another line.", isSubmitter: false, author: "TestUser", lindId: "", @@ -69,6 +77,8 @@ public let static_comment = Comment(id: UUID().uuidString, replies: .none(""), score: 2500, allAwardings: [], + saved: false, + permalink: "", authorFlairRichtext: nil, authorFlairText: nil, authorFlairTextColor: nil, diff --git a/Packages/Backend/Sources/Backend/Models/Vote.swift b/Packages/Backend/Sources/Backend/Models/Vote.swift new file mode 100644 index 0000000..d92e24d --- /dev/null +++ b/Packages/Backend/Sources/Backend/Models/Vote.swift @@ -0,0 +1,12 @@ +// +// File.swift +// +// +// Created by Thomas Ricouard on 12/08/2020. +// + +import Foundation + +public enum Vote: Int { + case upvote = 1, downvote = -1, neutral = 0 +} diff --git a/Packages/Backend/Sources/Backend/Network/Models/Comment+Networking.swift b/Packages/Backend/Sources/Backend/Network/Models/Comment+Networking.swift index d410d22..66b4a1c 100644 --- a/Packages/Backend/Sources/Backend/Network/Models/Comment+Networking.swift +++ b/Packages/Backend/Sources/Backend/Network/Models/Comment+Networking.swift @@ -9,10 +9,39 @@ import Foundation import Combine extension Comment { - static public func fetch(subreddit: String, id: String) -> AnyPublisher<[ListingResponse], Never> { - API.shared.request(endpoint: .comments(name: subreddit, id: id)) + public enum Sort: String, CaseIterable { + case best = "confidence" + case top, new, controversial, old, qa + } + + static public func fetch(subreddit: String, id: String, sort: Sort = .top) -> AnyPublisher<[ListingResponse], Never> { + let params: [String: String] = ["sort": sort.rawValue] + return API.shared.request(endpoint: .comments(name: subreddit, id: id), params: params) .subscribe(on: DispatchQueue.global()) .replaceError(with: []) .eraseToAnyPublisher() } + + public mutating func vote(vote: Vote) -> AnyPublisher { + switch vote { + case .upvote: + likes = true + case .downvote: + likes = false + case .neutral: + likes = nil + } + return API.shared.POST(endpoint: .vote, + params: ["id": name, "dir": "\(vote.rawValue)"]) + } + + public mutating func save() -> AnyPublisher { + saved = true + return API.shared.POST(endpoint: .save, params: ["id": name]) + } + + public mutating func unsave() -> AnyPublisher { + saved = false + return API.shared.POST(endpoint: .unsave, params: ["id": name]) + } } diff --git a/Packages/Backend/Sources/Backend/Network/Models/SubredditPost+Networking.swift b/Packages/Backend/Sources/Backend/Network/Models/SubredditPost+Networking.swift index 5d2791c..04df462 100644 --- a/Packages/Backend/Sources/Backend/Network/Models/SubredditPost+Networking.swift +++ b/Packages/Backend/Sources/Backend/Network/Models/SubredditPost+Networking.swift @@ -8,11 +8,7 @@ import Foundation import Combine -extension SubredditPost { - public enum Vote: Int { - case upvote = 1, downvote = -1, neutral = 0 - } - +extension SubredditPost { static public func fetch(subreddit: String, sort: String, after: SubredditPost?) -> AnyPublisher, Never> { diff --git a/Packages/UI/.gitignore b/Packages/UI/.gitignore new file mode 100644 index 0000000..95c4320 --- /dev/null +++ b/Packages/UI/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ diff --git a/Packages/UI/Package.swift b/Packages/UI/Package.swift new file mode 100644 index 0000000..9232011 --- /dev/null +++ b/Packages/UI/Package.swift @@ -0,0 +1,24 @@ +// swift-tools-version:5.3 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "UI", + platforms: [ + .macOS("10.16"), .iOS("14"), .tvOS("14"), .watchOS("7") + ], + products: [ + .library( + name: "UI", + targets: ["UI"]), + ], + targets: [ + .target( + name: "UI", + dependencies: []), + .testTarget( + name: "UITests", + dependencies: ["UI"]), + ] +) diff --git a/Packages/UI/README.md b/Packages/UI/README.md new file mode 100644 index 0000000..096aa1d --- /dev/null +++ b/Packages/UI/README.md @@ -0,0 +1,3 @@ +# UI + +A description of this package. diff --git a/Packages/UI/Sources/UI/RecursiveView.swift b/Packages/UI/Sources/UI/RecursiveView.swift new file mode 100644 index 0000000..a6fcab0 --- /dev/null +++ b/Packages/UI/Sources/UI/RecursiveView.swift @@ -0,0 +1,59 @@ +// +// SwiftUIView.swift +// +// +// Created by Thomas Ricouard on 12/08/2020. +// + +import SwiftUI + +public struct RecursiveView: View where Data: RandomAccessCollection, + Data.Element: Identifiable, + RowContent: View { + let data: Data + let children: KeyPath + let rowContent: (Data.Element) -> RowContent + + public init(data: Data, children: KeyPath, rowContent: @escaping (Data.Element) -> RowContent) { + self.data = data + self.children = children + self.rowContent = rowContent + } + + public var body: some View { + ForEach(data) { child in + if self.containsSub(child) { + CustomDisclosureGroup(content: { + RecursiveView(data: child[keyPath: children]!, + children: children, + rowContent: rowContent) + .padding(.leading, 8) + }, label: { + rowContent(child) + }) + } else { + rowContent(child) + } + } + } + + func containsSub(_ element: Data.Element) -> Bool { + element[keyPath: children] != nil + } +} + +struct CustomDisclosureGroup: View where Label: View, Content: View { + @State var isExpanded: Bool = true + var content: () -> Content + var label: () -> Label + + var body: some View { + label() + .onTapGesture { + isExpanded .toggle() + } + if isExpanded { + content() + } + } +} diff --git a/Packages/UI/Tests/UITests/UITests.swift b/Packages/UI/Tests/UITests/UITests.swift new file mode 100644 index 0000000..4fc13f2 --- /dev/null +++ b/Packages/UI/Tests/UITests/UITests.swift @@ -0,0 +1,15 @@ +import XCTest +@testable import UI + +final class UITests: XCTestCase { + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct + // results. + XCTAssertEqual(UI().text, "Hello, World!") + } + + static var allTests = [ + ("testExample", testExample), + ] +} diff --git a/Packages/UI/Tests/UITests/XCTestManifests.swift b/Packages/UI/Tests/UITests/XCTestManifests.swift new file mode 100644 index 0000000..aff9111 --- /dev/null +++ b/Packages/UI/Tests/UITests/XCTestManifests.swift @@ -0,0 +1,9 @@ +import XCTest + +#if !canImport(ObjectiveC) +public func allTests() -> [XCTestCaseEntry] { + return [ + testCase(UITests.allTests), + ] +} +#endif diff --git a/RedditOs.xcodeproj/project.pbxproj b/RedditOs.xcodeproj/project.pbxproj index b43e224..d0a0149 100644 --- a/RedditOs.xcodeproj/project.pbxproj +++ b/RedditOs.xcodeproj/project.pbxproj @@ -41,6 +41,11 @@ 6970A0BF24B8343100B11031 /* PopoverSearchSubredditRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0BE24B8343100B11031 /* PopoverSearchSubredditRow.swift */; }; 6970A0C124B88BA200B11031 /* PostViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0C024B88BA200B11031 /* PostViewModel.swift */; }; 6970A0C324B896CD00B11031 /* PostDetailCommentsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0C224B896CD00B11031 /* PostDetailCommentsSection.swift */; }; + 697E324524E3E7D90006F00F /* UI in Frameworks */ = {isa = PBXBuildFile; productRef = 697E324424E3E7D90006F00F /* UI */; }; + 697E324724E3ED620006F00F /* CommentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 697E324624E3ED620006F00F /* CommentViewModel.swift */; }; + 697E324924E3EDE70006F00F /* CommentVoteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 697E324824E3EDE70006F00F /* CommentVoteView.swift */; }; + 697E324B24E3EFCB0006F00F /* CommentActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 697E324A24E3EFCB0006F00F /* CommentActionsView.swift */; }; + 697E324D24E3F2900006F00F /* SharingPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 697E324C24E3F2900006F00F /* SharingPicker.swift */; }; 69CCB3EA24E2BEAC003FAAD7 /* SubredditAboutPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69CCB3E924E2BEAC003FAAD7 /* SubredditAboutPopoverView.swift */; }; 69D076C824B9E871001619AC /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69D076C724B9E871001619AC /* Color.swift */; }; 69DB093824DFCBC60026811F /* SettingsKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DB093724DFCBC60026811F /* SettingsKey.swift */; }; @@ -90,6 +95,11 @@ 6970A0BE24B8343100B11031 /* PopoverSearchSubredditRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverSearchSubredditRow.swift; sourceTree = ""; }; 6970A0C024B88BA200B11031 /* PostViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostViewModel.swift; sourceTree = ""; }; 6970A0C224B896CD00B11031 /* PostDetailCommentsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostDetailCommentsSection.swift; sourceTree = ""; }; + 697E324324E3E6BF0006F00F /* UI */ = {isa = PBXFileReference; lastKnownFileType = folder; name = UI; path = Packages/UI; sourceTree = ""; }; + 697E324624E3ED620006F00F /* CommentViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentViewModel.swift; sourceTree = ""; }; + 697E324824E3EDE70006F00F /* CommentVoteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentVoteView.swift; sourceTree = ""; }; + 697E324A24E3EFCB0006F00F /* CommentActionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentActionsView.swift; sourceTree = ""; }; + 697E324C24E3F2900006F00F /* SharingPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingPicker.swift; sourceTree = ""; }; 69CCB3E924E2BEAC003FAAD7 /* SubredditAboutPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubredditAboutPopoverView.swift; sourceTree = ""; }; 69D076C724B9E871001619AC /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; 69DB093724DFCBC60026811F /* SettingsKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKey.swift; sourceTree = ""; }; @@ -114,6 +124,7 @@ buildActionMask = 2147483647; files = ( 6970A0B124B74B7900B11031 /* SDWebImageSwiftUI in Frameworks */, + 697E324524E3E7D90006F00F /* UI in Frameworks */, 69EACF1C24B7272E00303A16 /* Backend in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -125,6 +136,9 @@ isa = PBXGroup; children = ( 69222AA924CD7518009F31B4 /* CommentRow.swift */, + 697E324624E3ED620006F00F /* CommentViewModel.swift */, + 697E324824E3EDE70006F00F /* CommentVoteView.swift */, + 697E324A24E3EFCB0006F00F /* CommentActionsView.swift */, ); path = Comments; sourceTree = ""; @@ -220,6 +234,7 @@ isa = PBXGroup; children = ( 6970A0B524B783FE00B11031 /* LinkPresentationRepresentable.swift */, + 697E324C24E3F2900006F00F /* SharingPicker.swift */, ); path = Representables; sourceTree = ""; @@ -248,6 +263,7 @@ isa = PBXGroup; children = ( 69EACF0124B63D5800303A16 /* RedditOs */, + 697E324324E3E6BF0006F00F /* UI */, 69EACF1924B7223E00303A16 /* Backend */, 69EACF0024B63D5800303A16 /* Products */, 69EACF1A24B7272E00303A16 /* Frameworks */, @@ -371,6 +387,7 @@ packageProductDependencies = ( 69EACF1B24B7272E00303A16 /* Backend */, 6970A0B024B74B7900B11031 /* SDWebImageSwiftUI */, + 697E324424E3E7D90006F00F /* UI */, ); productName = RedditOs; productReference = 69EACEFF24B63D5800303A16 /* Curiosity.app */; @@ -433,6 +450,7 @@ 69222AA124CC015E009F31B4 /* PostsListView.swift in Sources */, 6918A8CB24C1FEDC008A74E1 /* FlairView.swift in Sources */, 69D076C824B9E871001619AC /* Color.swift in Sources */, + 697E324924E3EDE70006F00F /* CommentVoteView.swift in Sources */, 6970A0AE24B74A9D00B11031 /* PostInfoView.swift in Sources */, 6924D54D24CDF92A005487CA /* SettingsView.swift in Sources */, 6970A0B624B783FE00B11031 /* LinkPresentationRepresentable.swift in Sources */, @@ -441,9 +459,11 @@ 694C634F24C0AA6D0017897D /* SidebarSubredditRow.swift in Sources */, 693F85D424D0715000224ADB /* ToolbarSearchBar.swift in Sources */, 69EACF1524B6F1E200303A16 /* SubredditPostsListView.swift in Sources */, + 697E324D24E3F2900006F00F /* SharingPicker.swift in Sources */, 692F237624CB3A7B006C9D40 /* SavedPostsListView.swift in Sources */, 6924D53E24CD94B0005487CA /* UserViewModel.swift in Sources */, 69222AA724CD6D6C009F31B4 /* SubmittedPostsListView.swift in Sources */, + 697E324724E3ED620006F00F /* CommentViewModel.swift in Sources */, 692566DA24B8A3830014E0D4 /* PostDetailHeader.swift in Sources */, 69CCB3EA24E2BEAC003FAAD7 /* SubredditAboutPopoverView.swift in Sources */, 6924D54A24CDDDD9005487CA /* UserSheetCommentsView.swift in Sources */, @@ -462,6 +482,7 @@ 691FD7B624C75CCD002E2C9C /* SubredditPostThumbnailView.swift in Sources */, 69EACF1724B6F2BA00303A16 /* PostDetailView.swift in Sources */, 692566D824B8A3190014E0D4 /* PostDetailActionsView.swift in Sources */, + 697E324B24E3EFCB0006F00F /* CommentActionsView.swift in Sources */, 69EACF0324B63D5800303A16 /* RedditOsApp.swift in Sources */, 69EACF2524B73DF400303A16 /* SubredditViewModel.swift in Sources */, 6924D54024CDCED0005487CA /* UserSheetView.swift in Sources */, @@ -686,6 +707,10 @@ package = 6970A0AF24B74B7900B11031 /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */; productName = SDWebImageSwiftUI; }; + 697E324424E3E7D90006F00F /* UI */ = { + isa = XCSwiftPackageProductDependency; + productName = UI; + }; 69EACF1B24B7272E00303A16 /* Backend */ = { isa = XCSwiftPackageProductDependency; productName = Backend; diff --git a/RedditOs/Environements/SettingsKey.swift b/RedditOs/Environements/SettingsKey.swift index 152c451..cdccc3c 100644 --- a/RedditOs/Environements/SettingsKey.swift +++ b/RedditOs/Environements/SettingsKey.swift @@ -10,4 +10,5 @@ import Foundation struct SettingsKey { static let subreddit_display_mode = "postDisplayMode" static let subreddit_defaut_sort_order = "defaultSortOrder" + static let comments_default_sort_order = "defaultCommentsSortOrder" } diff --git a/RedditOs/Features/Comments/CommentActionsView.swift b/RedditOs/Features/Comments/CommentActionsView.swift new file mode 100644 index 0000000..24442f1 --- /dev/null +++ b/RedditOs/Features/Comments/CommentActionsView.swift @@ -0,0 +1,52 @@ +// +// CommentActionsView.swift +// RedditOs +// +// Created by Thomas Ricouard on 12/08/2020. +// + +import SwiftUI +import Backend + +struct CommentActionsView: View { + @ObservedObject var viewModel: CommentViewModel + @State private var showPicker = false + + var body: some View { + HStack(spacing: 16) { + Button(action: { + + }, label: { + Label("Reply", systemImage: "bubble.right") + }).buttonStyle(BorderlessButtonStyle()) + + Button(action: { + showPicker.toggle() + }, label: { + Label("Share", systemImage: "square.and.arrow.up") + }) + .buttonStyle(BorderlessButtonStyle()) + .background(SharingsPicker(isPresented: $showPicker, + sharingItems: [viewModel.comment.permalinkURL ?? ""])) + + Button(action: { + viewModel.toggleSave() + }, label: { + Label("Save", + systemImage: viewModel.comment.saved == true ? "bookmark.fill" : "bookmark") + }).buttonStyle(BorderlessButtonStyle()) + + Button(action: { + + }, label: { + Label("Report", systemImage: "flag") + }).buttonStyle(BorderlessButtonStyle()) + } + } +} + +struct CommentActionsView_Previews: PreviewProvider { + static var previews: some View { + CommentActionsView(viewModel: CommentViewModel(comment: static_comment)) + } +} diff --git a/RedditOs/Features/Comments/CommentRow.swift b/RedditOs/Features/Comments/CommentRow.swift index 3b62139..1b799ad 100644 --- a/RedditOs/Features/Comments/CommentRow.swift +++ b/RedditOs/Features/Comments/CommentRow.swift @@ -9,46 +9,54 @@ import SwiftUI import Backend struct CommentRow: View { - let comment: Comment + @StateObject private var viewModel: CommentViewModel + + init(comment: Comment) { + _viewModel = StateObject(wrappedValue: CommentViewModel(comment: comment)) + } + var body: some View { - VStack(alignment: .leading, spacing: 4) { - HStack(spacing: 0) { - HStack(spacing: 6) { - if let richText = comment.authorFlairRichtext, !richText.isEmpty { - FlairView(richText: richText, - textColorHex: comment.authorFlairTextColor, - backgroundColorHex: comment.authorFlairBackgroundColor, - display: .small) + HStack(alignment: .top) { + CommentVoteView(viewModel: viewModel).padding(.top, 4) + VStack(alignment: .leading, spacing: 8) { + HStack(spacing: 0) { + HStack(spacing: 6) { + if let richText = viewModel.comment.authorFlairRichtext, !richText.isEmpty { + FlairView(richText: richText, + textColorHex: viewModel.comment.authorFlairTextColor, + backgroundColorHex: viewModel.comment.authorFlairBackgroundColor, + display: .small) + } + if viewModel.comment.isSubmitter == true { + Image(systemName: "music.mic") + .foregroundColor(.redditBlue) + } else { + Image(systemName: "person") + } + Text(viewModel.comment.author ?? "Unknown") + .font(.callout) + .fontWeight(.bold) } - if comment.isSubmitter == true { - Image(systemName: "music.mic") - .foregroundColor(.redditBlue) - } else { - Image(systemName: "person") + if let score = viewModel.comment.score { + Text(" · \(score.toRoundedSuffixAsString()) points · ") + .foregroundColor(.gray) + .font(.caption) + } + if let date = viewModel.comment.createdUtc { + Text(date, style: .relative) + .foregroundColor(.gray) + .font(.caption) + } + if let awards = viewModel.comment.allAwardings, !awards.isEmpty { + AwardsView(awards: awards).padding(.leading, 8) } - Text(comment.author ?? "Unknown") - .font(.callout) - .fontWeight(.bold) - } - if let score = comment.score { - Text(" · \(score.toRoundedSuffixAsString()) points · ") - .foregroundColor(.gray) - .font(.caption) - } - if let date = comment.createdUtc { - Text(date, style: .relative) - .foregroundColor(.gray) - .font(.caption) - } - if let awards = comment.allAwardings, !awards.isEmpty { - AwardsView(awards: awards).padding(.leading, 8) } - } - Text(comment.body ?? "No comment content") - .font(.body) - .padding(.bottom, 4) - Divider() - }.padding(.vertical, 4) + Text(viewModel.comment.body ?? "No comment content") + .font(.body) + CommentActionsView(viewModel: viewModel) + Divider() + }.padding(.vertical, 4) + } } } diff --git a/RedditOs/Features/Comments/CommentViewModel.swift b/RedditOs/Features/Comments/CommentViewModel.swift new file mode 100644 index 0000000..8f1036b --- /dev/null +++ b/RedditOs/Features/Comments/CommentViewModel.swift @@ -0,0 +1,46 @@ +// +// CommentViewModel.swift +// RedditOs +// +// Created by Thomas Ricouard on 12/08/2020. +// + +import Foundation +import SwiftUI +import Combine +import Backend + +class CommentViewModel: ObservableObject { + @Published var comment: Comment + + private var cancellableStore: [AnyCancellable] = [] + + init(comment: Comment) { + self.comment = comment + } + + func postVote(vote: Vote) { + let oldValue = comment.likes + let cancellable = comment.vote(vote: vote) + .receive(on: DispatchQueue.main) + .sink{ [weak self] response in + if response.error != nil { + self?.comment.likes = oldValue + } + } + cancellableStore.append(cancellable) + } + + func toggleSave() { + let oldValue = comment.saved + let cancellable = (comment.saved == true ? comment.unsave() : comment.save()) + .receive(on: DispatchQueue.main) + .sink{ [weak self] response in + if response.error != nil { + self?.comment.saved = oldValue + } + } + cancellableStore.append(cancellable) + } + +} diff --git a/RedditOs/Features/Comments/CommentVoteView.swift b/RedditOs/Features/Comments/CommentVoteView.swift new file mode 100644 index 0000000..847f958 --- /dev/null +++ b/RedditOs/Features/Comments/CommentVoteView.swift @@ -0,0 +1,43 @@ +// +// CommentVoteView.swift +// RedditOs +// +// Created by Thomas Ricouard on 12/08/2020. +// + +import SwiftUI +import Backend + +struct CommentVoteView: View { + @ObservedObject var viewModel: CommentViewModel + + var body: some View { + VStack(spacing: 6) { + Button(action: { + viewModel.postVote(vote: viewModel.comment.likes == true ? .neutral : .upvote) + }, + label: { + Image(systemName: "arrowtriangle.up.circle") + .resizable() + .frame(width: 16, height: 16) + .foregroundColor(viewModel.comment.likes == true ? .accentColor : nil) + }).buttonStyle(BorderlessButtonStyle()) + + Button(action: { + viewModel.postVote(vote: viewModel.comment.likes == false ? .neutral : .downvote) + }, + label: { + Image(systemName: "arrowtriangle.down.circle") + .resizable() + .frame(width: 16, height: 16) + .foregroundColor(viewModel.comment.likes == false ? .redditBlue : nil) + }).buttonStyle(BorderlessButtonStyle()) + }.frame(width: 20) + } +} + +struct CommentVoteView_Previews: PreviewProvider { + static var previews: some View { + CommentVoteView(viewModel: CommentViewModel(comment: static_comment)) + } +} diff --git a/RedditOs/Features/Post/PostDetailActionsView.swift b/RedditOs/Features/Post/PostDetailActionsView.swift index e9115b8..db7130f 100644 --- a/RedditOs/Features/Post/PostDetailActionsView.swift +++ b/RedditOs/Features/Post/PostDetailActionsView.swift @@ -10,42 +10,42 @@ import Backend struct PostDetailActionsView: View { @ObservedObject var viewModel: PostViewModel + @State private var showPicker = false var body: some View { HStack(spacing: 16) { - HStack(spacing: 6) { - Image(systemName: "bubble.middle.bottom.fill") - .imageScale(.medium) - Text("\(viewModel.post.numComments) comments") + Button(action: { + + }, label: { + Label("\(viewModel.post.numComments) comments", systemImage: "bubble.middle.bottom.fill") .foregroundColor(.white) - } + }).buttonStyle(BorderlessButtonStyle()) - HStack(spacing: 6) { - Image(systemName: "square.and.arrow.up") - .imageScale(.medium) - Text("Share") + Button(action: { + showPicker.toggle() + }, label: { + Label("Share", systemImage: "square.and.arrow.up") .foregroundColor(.white) - } + }) + .buttonStyle(BorderlessButtonStyle()) + .background(SharingsPicker(isPresented: $showPicker, + sharingItems: [viewModel.post.redditURL ?? ""])) - HStack(spacing: 6) { - Button(action: { - viewModel.toggleSave() - }) { - Image(systemName: viewModel.post.saved ? "bookmark.fill": "bookmark") - .imageScale(.medium) - .foregroundColor(viewModel.post.saved ? .accentColor : .white) - Text("Save") - .foregroundColor(.white) - }.buttonStyle(BorderlessButtonStyle()) - } + + Button(action: { + viewModel.toggleSave() + }, label: { + Label(viewModel.post.saved ? "Saved" : "Save", + systemImage: viewModel.post.saved ? "bookmark.fill": "bookmark") + .foregroundColor(viewModel.post.saved ? .accentColor : .white) + }).buttonStyle(BorderlessButtonStyle()) - HStack(spacing: 6) { - Image(systemName: "flag") - .imageScale(.medium) - Text("Report") + Button(action: { + + }, label: { + Label("Report", systemImage: "flag") .foregroundColor(.white) - } - + }).buttonStyle(BorderlessButtonStyle()) } } } diff --git a/RedditOs/Features/Post/PostDetailCommentsSection.swift b/RedditOs/Features/Post/PostDetailCommentsSection.swift index 31a8bb0..dd55fa0 100644 --- a/RedditOs/Features/Post/PostDetailCommentsSection.swift +++ b/RedditOs/Features/Post/PostDetailCommentsSection.swift @@ -7,24 +7,28 @@ import SwiftUI import Backend +import UI struct PostDetailCommentsSection: View { - let comments: [Comment]? + @ObservedObject var viewModel: PostViewModel private let placeholderComments = Array(repeating: static_comment, count: 10) var body: some View { - OutlineGroup(comments ?? placeholderComments, - children: \.repliesComments) { comment in - CommentRow(comment: comment) - .redacted(reason: comments == nil ? .placeholder : []) + Divider() + + Picker("Sort by", selection: $viewModel.commentsSort) { + ForEach(Comment.Sort.allCases, id: \.self) { sort in + Text(sort.label()).tag(sort) + } } - } -} - -struct PostCommentsSection_Previews: PreviewProvider { - static var previews: some View { - List { - PostDetailCommentsSection(comments: static_comments) + .pickerStyle(MenuPickerStyle()) + .frame(width: 170) + .padding(.bottom, 8) + + RecursiveView(data: viewModel.comments ?? placeholderComments, + children: \.repliesComments) { comment in + CommentRow(comment: comment) + .redacted(reason: viewModel.comments == nil ? .placeholder : []) } } } diff --git a/RedditOs/Features/Post/PostDetailView.swift b/RedditOs/Features/Post/PostDetailView.swift index a21bc05..159a576 100644 --- a/RedditOs/Features/Post/PostDetailView.swift +++ b/RedditOs/Features/Post/PostDetailView.swift @@ -27,7 +27,7 @@ struct PostDetailView: View { PostDetailContent(listing: viewModel.post, redrawLink: $redrawLink) PostDetailActionsView(viewModel: viewModel) }.padding(.bottom, 16) - PostDetailCommentsSection(comments: viewModel.comments) + PostDetailCommentsSection(viewModel: viewModel) } .onAppear(perform: viewModel.fechComments) .onAppear(perform: viewModel.postVisit) diff --git a/RedditOs/Features/Post/PostViewModel.swift b/RedditOs/Features/Post/PostViewModel.swift index ba53ee9..fe58483 100644 --- a/RedditOs/Features/Post/PostViewModel.swift +++ b/RedditOs/Features/Post/PostViewModel.swift @@ -13,6 +13,12 @@ import Backend class PostViewModel: ObservableObject { @Published var post: SubredditPost @Published var comments: [Comment]? + @AppStorage(SettingsKey.comments_default_sort_order) var commentsSort = Comment.Sort.top { + didSet { + comments = nil + fechComments() + } + } private var cancellableStore: [AnyCancellable] = [] @@ -32,7 +38,7 @@ class PostViewModel: ObservableObject { cancellableStore.append(cancellable) } - func postVote(vote: SubredditPost.Vote) { + func postVote(vote: Vote) { let oldValue = post.likes let cancellable = post.vote(vote: vote) .receive(on: DispatchQueue.main) @@ -57,7 +63,7 @@ class PostViewModel: ObservableObject { } func fechComments() { - let cancellable = Comment.fetch(subreddit: post.subreddit, id: post.id) + let cancellable = Comment.fetch(subreddit: post.subreddit, id: post.id, sort: commentsSort) .receive(on: DispatchQueue.main) .map{ $0.last?.comments } .sink{ [weak self] comments in @@ -66,3 +72,14 @@ class PostViewModel: ObservableObject { cancellableStore.append(cancellable) } } + +extension Comment.Sort { + public func label() -> String { + switch self { + case .best: + return "Best" + default: + return self.rawValue.capitalized + } + } +} diff --git a/RedditOs/Representables/SharingPicker.swift b/RedditOs/Representables/SharingPicker.swift new file mode 100644 index 0000000..a0f5d5d --- /dev/null +++ b/RedditOs/Representables/SharingPicker.swift @@ -0,0 +1,47 @@ +// +// SharingPicker.swift +// RedditOs +// +// Created by Thomas Ricouard on 12/08/2020. +// + +import Foundation +import AppKit +import SwiftUI + +struct SharingsPicker: NSViewRepresentable { + @Binding var isPresented: Bool + var sharingItems: [Any] = [] + + func makeNSView(context: Context) -> NSView { + let view = NSView() + return view + } + + func updateNSView(_ nsView: NSView, context: Context) { + if isPresented { + let picker = NSSharingServicePicker(items: sharingItems) + picker.delegate = context.coordinator + DispatchQueue.main.async { + picker.show(relativeTo: .zero, of: nsView, preferredEdge: .minY) + } + } + } + + func makeCoordinator() -> Coordinator { + Coordinator(owner: self) + } + + class Coordinator: NSObject, NSSharingServicePickerDelegate { + let owner: SharingsPicker + + init(owner: SharingsPicker) { + self.owner = owner + } + + func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, didChoose service: NSSharingService?) { + sharingServicePicker.delegate = nil + self.owner.isPresented = false + } + } +} From de011fe8da533c697bd020e300b832abbc494789 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Wed, 12 Aug 2020 14:26:50 +0200 Subject: [PATCH 09/30] New menu shortcuts --- .../Sources/Backend/Models/Subreddit.swift | 7 ++- RedditOs.xcodeproj/project.pbxproj | 4 +- RedditOs/Environements/UIState.swift | 3 +- RedditOs/Features/Post/PostDetailView.swift | 2 +- RedditOs/Features/Post/PostViewModel.swift | 2 +- RedditOs/Features/Sidebar/Sidebar.swift | 1 + .../Subreddit/SubredditPostsListView.swift | 53 +++++++++------- RedditOs/RedditOsApp.swift | 63 ++++++++++++++----- 8 files changed, 91 insertions(+), 44 deletions(-) diff --git a/Packages/Backend/Sources/Backend/Models/Subreddit.swift b/Packages/Backend/Sources/Backend/Models/Subreddit.swift index f484ea5..9cd18d4 100644 --- a/Packages/Backend/Sources/Backend/Models/Subreddit.swift +++ b/Packages/Backend/Sources/Backend/Models/Subreddit.swift @@ -13,6 +13,10 @@ public struct Subreddit: Codable, Identifiable { public let subscribers: Int? public let accountsActive: Int? public let createdUtc: Date + public let url: String + public var redditURL: URL { + URL(string: "https://reddit.com\(url)")! + } } public let static_subreddit_full = Subreddit(id: "games", @@ -26,4 +30,5 @@ public let static_subreddit_full = Subreddit(id: "games", bannerImg: "https://a.thumbs.redditmedia.com/8hr1PTpJ9iWLNWP67vZN0w3IEP8uI3eAQ1kE4XLRg88.png", subscribers: 1000, accountsActive: 500, - createdUtc: Date()) + createdUtc: Date(), + url: "/r/games") diff --git a/RedditOs.xcodeproj/project.pbxproj b/RedditOs.xcodeproj/project.pbxproj index d0a0149..de15138 100644 --- a/RedditOs.xcodeproj/project.pbxproj +++ b/RedditOs.xcodeproj/project.pbxproj @@ -633,7 +633,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.16; - MARKETING_VERSION = 0.0.2; + MARKETING_VERSION = 0.0.3; PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.curiosity; PRODUCT_NAME = Curiosity; SWIFT_VERSION = 5.0; @@ -660,7 +660,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.16; - MARKETING_VERSION = 0.0.2; + MARKETING_VERSION = 0.0.3; PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.curiosity; PRODUCT_NAME = Curiosity; SWIFT_VERSION = 5.0; diff --git a/RedditOs/Environements/UIState.swift b/RedditOs/Environements/UIState.swift index 0f38f68..01e9fd7 100644 --- a/RedditOs/Environements/UIState.swift +++ b/RedditOs/Environements/UIState.swift @@ -44,7 +44,8 @@ class UIState: ObservableObject { } } - @Published var selectedPost: SubredditPost? + @Published var selectedSubreddit: SubredditViewModel? + @Published var selectedPost: PostViewModel? @Published var presentedRoute: Route? @Published var sidebarSelection: Set = [DefaultChannels.hot.rawValue] @Published var searchedSubreddit = "" diff --git a/RedditOs/Features/Post/PostDetailView.swift b/RedditOs/Features/Post/PostDetailView.swift index 159a576..96af378 100644 --- a/RedditOs/Features/Post/PostDetailView.swift +++ b/RedditOs/Features/Post/PostDetailView.swift @@ -32,7 +32,7 @@ struct PostDetailView: View { .onAppear(perform: viewModel.fechComments) .onAppear(perform: viewModel.postVisit) .onAppear(perform: { - uiState.selectedPost = viewModel.post + uiState.selectedPost = viewModel }) .onDisappear(perform: { uiState.selectedPost = nil diff --git a/RedditOs/Features/Post/PostViewModel.swift b/RedditOs/Features/Post/PostViewModel.swift index fe58483..f768851 100644 --- a/RedditOs/Features/Post/PostViewModel.swift +++ b/RedditOs/Features/Post/PostViewModel.swift @@ -15,7 +15,6 @@ class PostViewModel: ObservableObject { @Published var comments: [Comment]? @AppStorage(SettingsKey.comments_default_sort_order) var commentsSort = Comment.Sort.top { didSet { - comments = nil fechComments() } } @@ -63,6 +62,7 @@ class PostViewModel: ObservableObject { } func fechComments() { + comments = nil let cancellable = Comment.fetch(subreddit: post.subreddit, id: post.id, sort: commentsSort) .receive(on: DispatchQueue.main) .map{ $0.last?.comments } diff --git a/RedditOs/Features/Sidebar/Sidebar.swift b/RedditOs/Features/Sidebar/Sidebar.swift index 5917aa1..f49d909 100644 --- a/RedditOs/Features/Sidebar/Sidebar.swift +++ b/RedditOs/Features/Sidebar/Sidebar.swift @@ -13,6 +13,7 @@ struct Sidebar: View { @EnvironmentObject private var uiState: UIState @EnvironmentObject private var localData: LocalDataStore @EnvironmentObject private var currentUser: CurrentUserStore + @State private var isSearchPopoverPresented = false @State private var isHovered = false @State private var isInEditMode = false diff --git a/RedditOs/Features/Subreddit/SubredditPostsListView.swift b/RedditOs/Features/Subreddit/SubredditPostsListView.swift index b0f459d..36764a3 100644 --- a/RedditOs/Features/Subreddit/SubredditPostsListView.swift +++ b/RedditOs/Features/Subreddit/SubredditPostsListView.swift @@ -7,6 +7,7 @@ import SwiftUI import Backend +import UI import SDWebImageSwiftUI struct SubredditPostsListView: View { @@ -14,10 +15,14 @@ struct SubredditPostsListView: View { private let loadingPlaceholders = Array(repeating: static_listing, count: 10) + @EnvironmentObject private var uiState: UIState @EnvironmentObject private var localData: LocalDataStore + @StateObject private var viewModel: SubredditViewModel @AppStorage(SettingsKey.subreddit_display_mode) private var displayMode = SubredditPostRow.DisplayMode.large + @State private var subredditAboutPopoverShown = false + @State private var sharePickerShown = false init(name: String) { _viewModel = StateObject(wrappedValue: SubredditViewModel(name: name)) @@ -49,19 +54,26 @@ struct SubredditPostsListView: View { .navigationSubtitle(subtitle) .toolbar { ToolbarItem(placement: .navigation) { - if let icon = viewModel.subreddit?.iconImg, let url = URL(string: icon) { - WebImage(url: url) - .resizable() - .frame(width: 20, height: 20) - .cornerRadius(10) - .onTapGesture { - subredditAboutPopoverShown = true - } - .popover(isPresented: $subredditAboutPopoverShown, - content: { SubredditAboutPopoverView(subreddit: viewModel.subreddit) }) - } else { - EmptyView() + Group { + if isDefaultChannel { + EmptyView() + } else if let icon = viewModel.subreddit?.iconImg, let url = URL(string: icon) { + WebImage(url: url) + .resizable() + .frame(width: 20, height: 20) + .cornerRadius(10) + } else { + + Image(systemName: "globe") + .resizable() + .frame(width: 20, height: 20) + } + } + .onTapGesture { + subredditAboutPopoverShown = true } + .popover(isPresented: $subredditAboutPopoverShown, + content: { SubredditAboutPopoverView(subreddit: viewModel.subreddit) }) } ToolbarItem(placement: .primaryAction) { @@ -91,20 +103,14 @@ struct SubredditPostsListView: View { ToolbarItem(placement: .primaryAction) { Button(action: { - - }) { - Image(systemName: "info") - } - .keyboardShortcut("i", modifiers: .command) - } - - ToolbarItem(placement: .primaryAction) { - Button(action: { - + sharePickerShown.toggle() }) { Image(systemName: "square.and.arrow.up") } - .keyboardShortcut("s", modifiers: .command) + .background(SharingsPicker(isPresented: $sharePickerShown, + sharingItems: [uiState.selectedPost?.post.redditURL ?? + viewModel.subreddit?.redditURL ?? + ""])) } ToolbarItem(placement: .primaryAction) { @@ -116,6 +122,7 @@ struct SubredditPostsListView: View { if !isDefaultChannel { viewModel.fetchAbout() } + uiState.selectedSubreddit = viewModel } } } diff --git a/RedditOs/RedditOsApp.swift b/RedditOs/RedditOsApp.swift index 6f9ad9f..6a51bd4 100644 --- a/RedditOs/RedditOsApp.swift +++ b/RedditOs/RedditOsApp.swift @@ -12,6 +12,7 @@ import Backend @main struct RedditOsApp: App { @StateObject private var uiState = UIState() + @StateObject private var localData = LocalDataStore() @SceneBuilder var body: some Scene { @@ -20,7 +21,7 @@ struct RedditOsApp: App { Sidebar() } .frame(minHeight: 400, idealHeight: 800) - .environmentObject(LocalDataStore()) + .environmentObject(localData) .environmentObject(OauthClient.shared) .environmentObject(CurrentUserStore.shared) .environmentObject(uiState) @@ -29,37 +30,69 @@ struct RedditOsApp: App { } .sheet(item: $uiState.presentedRoute, content: { $0.makeSheet() }) } - .commands{ + .commands { CommandMenu("Subreddit") { Button(action: { - + uiState.selectedSubreddit?.listings = nil + uiState.selectedSubreddit?.fetchListings() }) { - Text("Search") - } - Button(action: { - - }) { - Text("Navigate to") + Text("Refresh") } + .disabled(uiState.selectedSubreddit != nil) + .keyboardShortcut("r", modifiers: [.command]) + Divider() + Button(action: { - + if let subreddit = uiState.selectedSubreddit?.subreddit { + let small = SubredditSmall.makeSubredditSmall(with: subreddit) + if localData.favorites.contains(small) { + localData.remove(favorite: small) + } else { + localData.add(favorite: small) + } + } + }) { - Text("Favorite") + Text("Toggle favorite") } + .disabled(uiState.selectedSubreddit != nil) + .keyboardShortcut("f", modifiers: [.command, .shift]) } CommandMenu("Post") { Button(action: { - + uiState.selectedPost?.fechComments() + }) { + Text("Refresh comments") + } + .disabled(uiState.selectedPost != nil) + .keyboardShortcut("r", modifiers: [.command, .shift]) + + Button(action: { + uiState.selectedPost?.toggleSave() + }) { + Text(uiState.selectedPost?.post.saved == true ? "Unsave" : "Save") + } + .disabled(uiState.selectedPost != nil) + .keyboardShortcut("s", modifiers: .command) + + Divider() + Button(action: { + uiState.selectedPost?.postVote(vote: .upvote) }) { Text("Upvote") - }.disabled(uiState.selectedPost != nil) + } + .disabled(uiState.selectedPost != nil) + .keyboardShortcut(.upArrow, modifiers: .shift) + Button(action: { - + uiState.selectedPost?.postVote(vote: .downvote) }) { Text("Downvote") - }.disabled(uiState.selectedPost != nil) + } + .disabled(uiState.selectedPost != nil) + .keyboardShortcut(.downArrow, modifiers: .shift) } #if DEBUG From 4004482212ea3ac194276a8124176e088c71eda1 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Thu, 13 Aug 2020 14:45:22 +0200 Subject: [PATCH 10/30] Better route handling --- RedditOs.xcodeproj/project.pbxproj | 12 +- RedditOs/Environements/Route.swift | 53 ++++++ RedditOs/Environements/UIState.swift | 34 ++-- RedditOs/Features/Post/PostDetailView.swift | 3 +- .../GlobalSearchPopoverView.swift | 9 +- RedditOs/Features/Sidebar/Sidebar.swift | 12 +- RedditOs/Features/Sidebar/SidebarView.swift | 151 ++++++++++++++++++ .../Features/Subreddit/SubredditPostRow.swift | 26 +-- .../Users/popover/UserPopoverView.swift | 2 +- RedditOs/RedditOsApp.swift | 4 +- RedditOs/Shared/PostInfoView.swift | 100 +++++++----- RedditOs/Shared/PostsListView.swift | 3 +- 12 files changed, 317 insertions(+), 92 deletions(-) create mode 100644 RedditOs/Environements/Route.swift create mode 100644 RedditOs/Features/Sidebar/SidebarView.swift diff --git a/RedditOs.xcodeproj/project.pbxproj b/RedditOs.xcodeproj/project.pbxproj index de15138..64d137c 100644 --- a/RedditOs.xcodeproj/project.pbxproj +++ b/RedditOs.xcodeproj/project.pbxproj @@ -48,11 +48,12 @@ 697E324D24E3F2900006F00F /* SharingPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 697E324C24E3F2900006F00F /* SharingPicker.swift */; }; 69CCB3EA24E2BEAC003FAAD7 /* SubredditAboutPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69CCB3E924E2BEAC003FAAD7 /* SubredditAboutPopoverView.swift */; }; 69D076C824B9E871001619AC /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69D076C724B9E871001619AC /* Color.swift */; }; + 69D8663424E568060052A2B0 /* Route.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69D8663324E568060052A2B0 /* Route.swift */; }; 69DB093824DFCBC60026811F /* SettingsKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DB093724DFCBC60026811F /* SettingsKey.swift */; }; 69EACF0324B63D5800303A16 /* RedditOsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69EACF0224B63D5800303A16 /* RedditOsApp.swift */; }; 69EACF0724B63D5900303A16 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 69EACF0624B63D5900303A16 /* Assets.xcassets */; }; 69EACF0A24B63D5900303A16 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 69EACF0924B63D5900303A16 /* Preview Assets.xcassets */; }; - 69EACF1324B668F200303A16 /* Sidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69EACF1224B668F200303A16 /* Sidebar.swift */; }; + 69EACF1324B668F200303A16 /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69EACF1224B668F200303A16 /* SidebarView.swift */; }; 69EACF1524B6F1E200303A16 /* SubredditPostsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69EACF1424B6F1E200303A16 /* SubredditPostsListView.swift */; }; 69EACF1724B6F2BA00303A16 /* PostDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69EACF1624B6F2BA00303A16 /* PostDetailView.swift */; }; 69EACF1C24B7272E00303A16 /* Backend in Frameworks */ = {isa = PBXBuildFile; productRef = 69EACF1B24B7272E00303A16 /* Backend */; }; @@ -102,6 +103,7 @@ 697E324C24E3F2900006F00F /* SharingPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingPicker.swift; sourceTree = ""; }; 69CCB3E924E2BEAC003FAAD7 /* SubredditAboutPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubredditAboutPopoverView.swift; sourceTree = ""; }; 69D076C724B9E871001619AC /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; + 69D8663324E568060052A2B0 /* Route.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Route.swift; sourceTree = ""; }; 69DB093724DFCBC60026811F /* SettingsKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsKey.swift; sourceTree = ""; }; 69EACEFF24B63D5800303A16 /* Curiosity.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Curiosity.app; sourceTree = BUILT_PRODUCTS_DIR; }; 69EACF0224B63D5800303A16 /* RedditOsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedditOsApp.swift; sourceTree = ""; }; @@ -109,7 +111,7 @@ 69EACF0924B63D5900303A16 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 69EACF0B24B63D5900303A16 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 69EACF0C24B63D5900303A16 /* RedditOs.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RedditOs.entitlements; sourceTree = ""; }; - 69EACF1224B668F200303A16 /* Sidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sidebar.swift; sourceTree = ""; }; + 69EACF1224B668F200303A16 /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = ""; }; 69EACF1424B6F1E200303A16 /* SubredditPostsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubredditPostsListView.swift; sourceTree = ""; }; 69EACF1624B6F2BA00303A16 /* PostDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostDetailView.swift; sourceTree = ""; }; 69EACF1924B7223E00303A16 /* Backend */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Backend; path = Packages/Backend; sourceTree = ""; }; @@ -158,6 +160,7 @@ children = ( 6924D54224CDD049005487CA /* UIState.swift */, 69DB093724DFCBC60026811F /* SettingsKey.swift */, + 69D8663324E568060052A2B0 /* Route.swift */, ); path = Environements; sourceTree = ""; @@ -328,7 +331,7 @@ 69EACF2124B7299000303A16 /* Sidebar */ = { isa = PBXGroup; children = ( - 69EACF1224B668F200303A16 /* Sidebar.swift */, + 69EACF1224B668F200303A16 /* SidebarView.swift */, 694C634E24C0AA6D0017897D /* SidebarSubredditRow.swift */, ); path = Sidebar; @@ -445,7 +448,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 69EACF1324B668F200303A16 /* Sidebar.swift in Sources */, + 69EACF1324B668F200303A16 /* SidebarView.swift in Sources */, 6970A0BF24B8343100B11031 /* PopoverSearchSubredditRow.swift in Sources */, 69222AA124CC015E009F31B4 /* PostsListView.swift in Sources */, 6918A8CB24C1FEDC008A74E1 /* FlairView.swift in Sources */, @@ -466,6 +469,7 @@ 697E324724E3ED620006F00F /* CommentViewModel.swift in Sources */, 692566DA24B8A3830014E0D4 /* PostDetailHeader.swift in Sources */, 69CCB3EA24E2BEAC003FAAD7 /* SubredditAboutPopoverView.swift in Sources */, + 69D8663424E568060052A2B0 /* Route.swift in Sources */, 6924D54A24CDDDD9005487CA /* UserSheetCommentsView.swift in Sources */, 6970A0C324B896CD00B11031 /* PostDetailCommentsSection.swift in Sources */, 6970A0B924B79AFD00B11031 /* PopoverSearchSubredditView.swift in Sources */, diff --git a/RedditOs/Environements/Route.swift b/RedditOs/Environements/Route.swift new file mode 100644 index 0000000..fb065be --- /dev/null +++ b/RedditOs/Environements/Route.swift @@ -0,0 +1,53 @@ +// +// Route.swift +// RedditOs +// +// Created by Thomas Ricouard on 13/08/2020. +// + +import Foundation +import SwiftUI +import Combine +import Backend + +enum Route: Identifiable, Hashable { + static func == (lhs: Route, rhs: Route) -> Bool { + lhs.id == rhs.id + } + + func hash(into hasher: inout Hasher) { + hasher.combine(id) + } + + case user(user: User) + case subreddit(subreddit: String) + case defaultChannel(chanel: UIState.DefaultChannels) + case none + + var id: String { + switch self { + case .user: + return "user" + case .subreddit: + return "subreddit" + case .none: + return "none" + case let .defaultChannel(chanel): + return chanel.rawValue + } + } + + @ViewBuilder + func makeView() -> some View { + switch self { + case let .user(user): + UserSheetView(user: user) + case let .subreddit(subreddit): + SubredditPostsListView(name: subreddit) + case let .defaultChannel(chanel): + SubredditPostsListView(name: chanel.rawValue) + case .none: + EmptyView() + } + } +} diff --git a/RedditOs/Environements/UIState.swift b/RedditOs/Environements/UIState.swift index 01e9fd7..0ed7362 100644 --- a/RedditOs/Environements/UIState.swift +++ b/RedditOs/Environements/UIState.swift @@ -11,25 +11,6 @@ import Combine import Backend class UIState: ObservableObject { - enum Route: Identifiable { - case user(user: User) - - var id: String { - switch self { - case .user: - return "user" - } - } - - @ViewBuilder - func makeSheet() -> some View { - switch self { - case let .user(user): - UserSheetView(user: user) - } - } - } - enum DefaultChannels: String, CaseIterable { case hot, best, new, top, rising @@ -43,12 +24,19 @@ class UIState: ObservableObject { } } } + @Published var selectedSubreddit: SubredditViewModel? @Published var selectedPost: PostViewModel? - @Published var presentedRoute: Route? + + @Published var presentedSheetRoute: Route? + @Published var presentedNavigationRoute: Route? { + didSet { + if let route = presentedNavigationRoute { + sidebarSelection = [route.id] + } + } + } + @Published var sidebarSelection: Set = [DefaultChannels.hot.rawValue] - @Published var searchedSubreddit = "" - @Published var searchedUser = "" - @Published var displaySearch = false } diff --git a/RedditOs/Features/Post/PostDetailView.swift b/RedditOs/Features/Post/PostDetailView.swift index 96af378..78510a2 100644 --- a/RedditOs/Features/Post/PostDetailView.swift +++ b/RedditOs/Features/Post/PostDetailView.swift @@ -20,7 +20,7 @@ struct PostDetailView: View { HStack { PostVoteView(viewModel: viewModel) VStack(alignment: .leading) { - PostInfoView(post: viewModel.post) + PostInfoView(post: viewModel.post, display: .horizontal) PostDetailHeader(listing: viewModel.post) } } @@ -29,6 +29,7 @@ struct PostDetailView: View { }.padding(.bottom, 16) PostDetailCommentsSection(viewModel: viewModel) } + .animation(nil) .onAppear(perform: viewModel.fechComments) .onAppear(perform: viewModel.postVisit) .onAppear(perform: { diff --git a/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift b/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift index 897e30d..e91b961 100644 --- a/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift +++ b/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift @@ -46,8 +46,7 @@ struct GlobalSearchPopoverView: View { GlobalSearchSubRow(icon: nil, name: "Go to r/\(viewModel.searchText)") .onTapGesture { - uiState.searchedSubreddit = viewModel.searchText - uiState.displaySearch = true + uiState.presentedNavigationRoute = .subreddit(subreddit: viewModel.searchText) } GlobalSearchSubRow(icon: nil, name: "Go to u/\(viewModel.searchText)") @@ -85,9 +84,9 @@ struct GlobalSearchPopoverView: View { } private func makeSubRow(icon: String?, name: String) -> some View { - GlobalSearchSubRow(icon: icon, name: name).onTapGesture { - uiState.searchedSubreddit = name - uiState.displaySearch = true + GlobalSearchSubRow(icon: icon, name: name) + .onTapGesture { + uiState.presentedNavigationRoute = .subreddit(subreddit: name) } } } diff --git a/RedditOs/Features/Sidebar/Sidebar.swift b/RedditOs/Features/Sidebar/Sidebar.swift index f49d909..fa20f30 100644 --- a/RedditOs/Features/Sidebar/Sidebar.swift +++ b/RedditOs/Features/Sidebar/Sidebar.swift @@ -9,7 +9,7 @@ import SwiftUI import Backend import SDWebImageSwiftUI -struct Sidebar: View { +struct SidebarView: View { @EnvironmentObject private var uiState: UIState @EnvironmentObject private var localData: LocalDataStore @EnvironmentObject private var currentUser: CurrentUserStore @@ -25,7 +25,7 @@ struct Sidebar: View { NavigationLink(destination: SubredditPostsListView(name: item.rawValue)) { Label(LocalizedStringKey(item.rawValue.capitalized), systemImage: item.icon()) }.tag(item.rawValue) - } + }.animation(nil) NavigationLink(destination: SubredditPostsListView(name: uiState.searchedSubreddit), isActive: $uiState.displaySearch) { EmptyView() @@ -68,7 +68,7 @@ struct Sidebar: View { .buttonStyle(BorderlessButtonStyle()) } } - } + }.animation(nil) } .listItemTint(.redditGold) .animation(.easeInOut) @@ -98,11 +98,11 @@ struct Sidebar: View { .buttonStyle(BorderlessButtonStyle()) } } - - } + }.animation(nil) }.listItemTint(.redditBlue) } } + .animation(nil) .listStyle(SidebarListStyle()) .frame(minWidth: 200, idealWidth: 200, maxWidth: 200, maxHeight: .infinity) .onHover { hovered in @@ -142,6 +142,6 @@ struct Sidebar: View { struct Sidebar_Previews: PreviewProvider { static var previews: some View { - Sidebar() + SidebarView() } } diff --git a/RedditOs/Features/Sidebar/SidebarView.swift b/RedditOs/Features/Sidebar/SidebarView.swift new file mode 100644 index 0000000..4b10bc6 --- /dev/null +++ b/RedditOs/Features/Sidebar/SidebarView.swift @@ -0,0 +1,151 @@ +// +// Sidebar.swift +// RedditOs +// +// Created by Thomas Ricouard on 08/07/2020. +// + +import SwiftUI +import Backend +import SDWebImageSwiftUI + +struct SidebarView: View { + @EnvironmentObject private var uiState: UIState + @EnvironmentObject private var localData: LocalDataStore + @EnvironmentObject private var currentUser: CurrentUserStore + + @State private var isSearchPopoverPresented = false + @State private var isHovered = false + @State private var isInEditMode = false + + var body: some View { + List(selection: $uiState.sidebarSelection) { + Section { + ForEach(UIState.DefaultChannels.allCases, id: \.self) { item in + NavigationLink(destination: SubredditPostsListView(name: item.rawValue)) { + Label(LocalizedStringKey(item.rawValue.capitalized), systemImage: item.icon()) + }.tag(item.rawValue) + }.animation(nil) + + NavigationLink( + destination: uiState.presentedNavigationRoute?.makeView() ?? Route.none.makeView(), + tag: uiState.presentedNavigationRoute ?? Route.none, + selection: $uiState.presentedNavigationRoute, + label: { + EmptyView() + }) + } + + Section(header: Text("Account")) { + NavigationLink(destination: ProfileView()) { + if let user = currentUser.user { + Label(user.name, systemImage: "person.crop.circle") + } else { + Label("Profile", systemImage: "person.crop.circle") + } + }.tag("profile") + Label("Inbox", systemImage: "envelope") + NavigationLink(destination: SubmittedPostsListView()) { + Label("Posts", systemImage: "square.and.pencil") + }.tag("Posts") + Label("Comments", systemImage: "text.bubble") + NavigationLink(destination: SavedPostsListView()) { + Label("Saved", systemImage: "archivebox") + }.tag("Saved") + }.listItemTint(.redditBlue) + + Section(header: subredditsHeader) { + ForEach(localData.favorites) { reddit in + HStack { + SidebarSubredditRow(name: reddit.name, + iconURL: reddit.iconImg) + .tag("local\(reddit.name)") + if isInEditMode { + Spacer() + Button { + localData.remove(favorite: reddit) + } label: { + Image(systemName: "minus.circle.fill") + .imageScale(.large) + .foregroundColor(.red) + } + .buttonStyle(BorderlessButtonStyle()) + } + } + }.animation(nil) + } + .listItemTint(.redditGold) + .animation(.easeInOut) + + if let subs = currentUser.subscriptions, currentUser.user != nil { + Section(header: Text("Subscriptions")) { + ForEach(subs) { reddit in + HStack { + SidebarSubredditRow(name: reddit.displayName, + iconURL: reddit.iconImg) + .tag(reddit.displayName) + Spacer() + if isHovered { + let isfavorite = localData.favorites.first(where: { $0.name == reddit.displayName}) != nil + Button { + if isfavorite { + localData.remove(favoriteNamed: reddit.displayName) + } else { + localData.add(favorite: SubredditSmall.makeSubredditSmall(with: reddit)) + } + } label: { + Image(systemName: isfavorite ? "star.fill" : "star") + .imageScale(.large) + .foregroundColor(.yellow) + .opacity(/*@START_MENU_TOKEN@*/0.8/*@END_MENU_TOKEN@*/) + } + .buttonStyle(BorderlessButtonStyle()) + } + } + }.animation(nil) + }.listItemTint(.redditBlue) + } + } + .animation(nil) + .listStyle(SidebarListStyle()) + .frame(minWidth: 200, idealWidth: 200, maxWidth: 200, maxHeight: .infinity) + .onHover { hovered in + isHovered = hovered + } + } + + private var subredditsHeader: some View { + HStack(spacing: 8) { + Text("Favorites") + if isHovered { + Button { + isSearchPopoverPresented = true + } label: { + Image(systemName: "plus.circle") + .imageScale(.large) + .foregroundColor(.blue) + } + .buttonStyle(BorderlessButtonStyle()) + .popover(isPresented: $isSearchPopoverPresented) { + PopoverSearchSubredditView() + } + + Button { + isInEditMode.toggle() + } label: { + Image(systemName: isInEditMode ? "trash.circle.fill" : "trash.circle") + .imageScale(.large) + .foregroundColor(.blue) + } + .buttonStyle(BorderlessButtonStyle()) + } + + } + } +} + +struct Sidebar_Previews: PreviewProvider { + static var previews: some View { + SidebarView() + } +} diff --git a/RedditOs/Features/Subreddit/SubredditPostRow.swift b/RedditOs/Features/Subreddit/SubredditPostRow.swift index cb97dff..ee73a5b 100644 --- a/RedditOs/Features/Subreddit/SubredditPostRow.swift +++ b/RedditOs/Features/Subreddit/SubredditPostRow.swift @@ -44,7 +44,7 @@ struct SubredditPostRow: View { VStack(alignment: .leading, spacing: 4) { Text(viewModel.post.title) .fontWeight(.bold) - .font(.headline) + .font(.body) .lineLimit(displayMode == .compact ? 2 : nil) .foregroundColor(viewModel.post.visited ? .gray : nil) HStack { @@ -63,7 +63,7 @@ struct SubredditPostRow: View { } } } - PostInfoView(post: viewModel.post) + PostInfoView(post: viewModel.post, display: .vertical) } } } @@ -100,16 +100,18 @@ struct SubredditPostRow: View { struct SubredditPostRow_Previews: PreviewProvider { static var previews: some View { - List { - SubredditPostRow(post: static_listing, displayMode: .constant(.large)) - SubredditPostRow(post: static_listing, displayMode: .constant(.large)) - SubredditPostRow(post: static_listing, displayMode: .constant(.large)) - - Divider() - - SubredditPostRow(post: static_listing, displayMode: .constant(.compact)) - SubredditPostRow(post: static_listing, displayMode: .constant(.compact)) - SubredditPostRow(post: static_listing, displayMode: .constant(.compact)) + NavigationView { + List { + SubredditPostRow(post: static_listing, displayMode: .constant(.large)) + SubredditPostRow(post: static_listing, displayMode: .constant(.large)) + SubredditPostRow(post: static_listing, displayMode: .constant(.large)) + + Divider() + + SubredditPostRow(post: static_listing, displayMode: .constant(.compact)) + SubredditPostRow(post: static_listing, displayMode: .constant(.compact)) + SubredditPostRow(post: static_listing, displayMode: .constant(.compact)) + }.frame(width: 500) } } } diff --git a/RedditOs/Features/Users/popover/UserPopoverView.swift b/RedditOs/Features/Users/popover/UserPopoverView.swift index 40445fc..1591af2 100644 --- a/RedditOs/Features/Users/popover/UserPopoverView.swift +++ b/RedditOs/Features/Users/popover/UserPopoverView.swift @@ -29,7 +29,7 @@ struct UserPopoverView: View { Button(action: { if let user = viewModel.user { presentation.wrappedValue.dismiss() - uiState.presentedRoute = .user(user: user) + uiState.presentedSheetRoute = .user(user: user) } }) { Text("View full profile") diff --git a/RedditOs/RedditOsApp.swift b/RedditOs/RedditOsApp.swift index 6a51bd4..6549cab 100644 --- a/RedditOs/RedditOsApp.swift +++ b/RedditOs/RedditOsApp.swift @@ -18,7 +18,7 @@ struct RedditOsApp: App { var body: some Scene { WindowGroup { NavigationView { - Sidebar() + SidebarView() } .frame(minHeight: 400, idealHeight: 800) .environmentObject(localData) @@ -28,7 +28,7 @@ struct RedditOsApp: App { .onOpenURL { url in OauthClient.shared.handleNextURL(url: url) } - .sheet(item: $uiState.presentedRoute, content: { $0.makeSheet() }) + .sheet(item: $uiState.presentedSheetRoute, content: { $0.makeView() }) } .commands { CommandMenu("Subreddit") { diff --git a/RedditOs/Shared/PostInfoView.swift b/RedditOs/Shared/PostInfoView.swift index d9cf9f0..21f3fa8 100644 --- a/RedditOs/Shared/PostInfoView.swift +++ b/RedditOs/Shared/PostInfoView.swift @@ -9,58 +9,84 @@ import SwiftUI import Backend struct PostInfoView: View { + enum Display { + case vertical, horizontal + } + @EnvironmentObject private var uiState: UIState let post: SubredditPost + let display: Display + @State private var showUserPopover = false var body: some View { - VStack(alignment: .leading, spacing: 6) { - Text("r/\(post.subreddit)") - .fontWeight(.bold) - .font(.subheadline) - HStack(spacing: 6) { - Button(action: { - showUserPopover = true - }, label: { - HStack(spacing: 4) { - Image(systemName: "person") - Text("u/\(post.author)") - } - }) - .buttonStyle(BorderlessButtonStyle()) - .popover(isPresented: $showUserPopover, content: { - UserPopoverView(username: post.author) - }) - if let richText = post.authorFlairRichtext, !richText.isEmpty { - FlairView(richText: richText, - textColorHex: post.authorFlairTextColor, - backgroundColorHex: post.authorFlairBackgroundColor, - display: .small) - } - } + switch display { + case .vertical: + VStack(alignment: .leading, spacing: 6) { + content + }.font(.callout) + case .horizontal: HStack(spacing: 12) { + content + }.font(.callout) + } + } + + @ViewBuilder + var content: some View { + HStack(spacing: 6) { + Button(action: { + uiState.presentedNavigationRoute = .subreddit(subreddit: post.subreddit) + }, label: { + Text("r/\(post.subreddit)") + .fontWeight(.bold) + .foregroundColor(.white) + }) + .buttonStyle(BorderlessButtonStyle()) + + Button(action: { + showUserPopover = true + }, label: { HStack(spacing: 4) { - Image(systemName: "clock") - Text(post.createdUtc, style: .offset) - } - HStack(spacing: 4) { - Image(systemName: "bubble.middle.bottom") - .imageScale(.small) - Text("\(post.numComments)") - } - if !post.allAwardings.isEmpty { - AwardsView(awards: post.allAwardings) + Image(systemName: "person") + Text("u/\(post.author)") } + }) + .buttonStyle(BorderlessButtonStyle()) + .popover(isPresented: $showUserPopover, content: { + UserPopoverView(username: post.author) + }) + if let richText = post.authorFlairRichtext, !richText.isEmpty { + FlairView(richText: richText, + textColorHex: post.authorFlairTextColor, + backgroundColorHex: post.authorFlairBackgroundColor, + display: .small) + } + } + HStack(spacing: 12) { + HStack(spacing: 4) { + Image(systemName: "clock") + Text(post.createdUtc, style: .offset) + } + HStack(spacing: 4) { + Image(systemName: "bubble.middle.bottom") + .imageScale(.small) + Text("\(post.numComments)") + } + if !post.allAwardings.isEmpty { + AwardsView(awards: post.allAwardings) } - .font(.callout) - .foregroundColor(.gray) } + .foregroundColor(.gray) } } struct ListingInfoView_Previews: PreviewProvider { static var previews: some View { - PostInfoView(post: static_listing) + Group { + PostInfoView(post: static_listing, display: .horizontal) + PostInfoView(post: static_listing, display: .vertical) + } } } diff --git a/RedditOs/Shared/PostsListView.swift b/RedditOs/Shared/PostsListView.swift index 6068102..5877782 100644 --- a/RedditOs/Shared/PostsListView.swift +++ b/RedditOs/Shared/PostsListView.swift @@ -20,7 +20,7 @@ struct PostsListView: View { SubredditPostRow(post: post, displayMode: $displayMode) .redacted(reason: posts == nil ? .placeholder : []) - } + }.animation(nil) if posts != nil { LoadingRow(text: "Loading next page") .onAppear { @@ -28,6 +28,7 @@ struct PostsListView: View { } } } + .animation(nil) .listStyle(InsetListStyle()) .frame(width: 500) } From 54959d9ee02971edce326cecbcb81850d8532de9 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Tue, 18 Aug 2020 15:31:44 +0200 Subject: [PATCH 11/30] Subscribe / Favorite from about popover --- .../Sources/Backend/Models/Subreddit.swift | 6 ++- .../Backend/Models/SubredditPost.swift | 4 +- .../Backend/Models/SubredditSmall.swift | 4 ++ .../Sources/Backend/Network/Endpoint.swift | 3 ++ .../Network/Models/Subreddit+Networking.swift | 10 ++++ .../Sources/Backend/User/LocalDataStore.swift | 4 +- RedditOs/Environements/Route.swift | 4 +- .../Features/Search/ToolbarSearchBar.swift | 3 +- .../Subreddit/SubredditAboutPopoverView.swift | 52 +++++++++++++++++-- .../Subreddit/SubredditPostsListView.swift | 2 +- .../Subreddit/SubredditViewModel.swift | 21 ++++++++ RedditOs/Shared/FlairView.swift | 2 +- 12 files changed, 102 insertions(+), 13 deletions(-) diff --git a/Packages/Backend/Sources/Backend/Models/Subreddit.swift b/Packages/Backend/Sources/Backend/Models/Subreddit.swift index 9cd18d4..f5fffaa 100644 --- a/Packages/Backend/Sources/Backend/Models/Subreddit.swift +++ b/Packages/Backend/Sources/Backend/Models/Subreddit.swift @@ -2,6 +2,7 @@ import Foundation public struct Subreddit: Codable, Identifiable { public let id: String + public let name: String public let displayName: String public let title: String public let publicDescription: String? @@ -17,9 +18,11 @@ public struct Subreddit: Codable, Identifiable { public var redditURL: URL { URL(string: "https://reddit.com\(url)")! } + public var userIsSubscriber: Bool? } public let static_subreddit_full = Subreddit(id: "games", + name: "t3_fjfj", displayName: "games", title: "games", publicDescription: "a description", @@ -31,4 +34,5 @@ public let static_subreddit_full = Subreddit(id: "games", subscribers: 1000, accountsActive: 500, createdUtc: Date(), - url: "/r/games") + url: "/r/games", + userIsSubscriber: false) diff --git a/Packages/Backend/Sources/Backend/Models/SubredditPost.swift b/Packages/Backend/Sources/Backend/Models/SubredditPost.swift index c84be83..2a28024 100644 --- a/Packages/Backend/Sources/Backend/Models/SubredditPost.swift +++ b/Packages/Backend/Sources/Backend/Models/SubredditPost.swift @@ -9,7 +9,9 @@ import Foundation public struct SubredditPost: Decodable, Identifiable, Hashable { public static func == (lhs: SubredditPost, rhs: SubredditPost) -> Bool { - lhs.id == rhs.id && lhs.likes == rhs.likes + lhs.id == rhs.id && + lhs.likes == rhs.likes && + lhs.saved == rhs.saved } public func hash(into hasher: inout Hasher) { diff --git a/Packages/Backend/Sources/Backend/Models/SubredditSmall.swift b/Packages/Backend/Sources/Backend/Models/SubredditSmall.swift index c713bd3..ca3a1ca 100644 --- a/Packages/Backend/Sources/Backend/Models/SubredditSmall.swift +++ b/Packages/Backend/Sources/Backend/Models/SubredditSmall.swift @@ -9,6 +9,10 @@ public struct SubredditResponse: Decodable { } public struct SubredditSmall: Codable, Identifiable, Equatable, Hashable { + public static func == (lhs: SubredditSmall, rhs: SubredditSmall) -> Bool { + lhs.id == rhs.id + } + public var id: String { name } public let name: String public let subscriberCount: Int diff --git a/Packages/Backend/Sources/Backend/Network/Endpoint.swift b/Packages/Backend/Sources/Backend/Network/Endpoint.swift index f37172f..668ca16 100644 --- a/Packages/Backend/Sources/Backend/Network/Endpoint.swift +++ b/Packages/Backend/Sources/Backend/Network/Endpoint.swift @@ -3,6 +3,7 @@ import Foundation public enum Endpoint { case subreddit(name: String, sort: String?) case subredditAbout(name: String) + case subscribe case searchSubreddit case comments(name: String, id: String) case accessToken @@ -26,6 +27,8 @@ public enum Endpoint { } case .searchSubreddit: return "api/search_subreddits" + case .subscribe: + return "api/subscribe" case let .comments(name, id): return "r/\(name)/comments/\(id)" case .accessToken: diff --git a/Packages/Backend/Sources/Backend/Network/Models/Subreddit+Networking.swift b/Packages/Backend/Sources/Backend/Network/Models/Subreddit+Networking.swift index 7c19a42..fb5623d 100644 --- a/Packages/Backend/Sources/Backend/Network/Models/Subreddit+Networking.swift +++ b/Packages/Backend/Sources/Backend/Network/Models/Subreddit+Networking.swift @@ -21,5 +21,15 @@ extension Subreddit { .replaceError(with: nil) .eraseToAnyPublisher() } + + public mutating func subscribe() -> AnyPublisher { + userIsSubscriber = true + return API.shared.POST(endpoint: .subscribe, params: ["action": "sub", "sr": name]) + } + + public mutating func unSubscribe() -> AnyPublisher { + userIsSubscriber = false + return API.shared.POST(endpoint: .subscribe, params: ["action": "unsub", "sr": name]) + } } diff --git a/Packages/Backend/Sources/Backend/User/LocalDataStore.swift b/Packages/Backend/Sources/Backend/User/LocalDataStore.swift index e38e758..ed7267b 100644 --- a/Packages/Backend/Sources/Backend/User/LocalDataStore.swift +++ b/Packages/Backend/Sources/Backend/User/LocalDataStore.swift @@ -15,7 +15,9 @@ public class LocalDataStore: ObservableObject, PersistentDataStore { } public init() { - self.favorites = restorePersistedData()?.favorites ?? [] + var favorites = restorePersistedData()?.favorites ?? [] + favorites.sort{ $0.name.lowercased() < $1.name.lowercased() } + self.favorites = favorites } // MARK: - Favorites management diff --git a/RedditOs/Environements/Route.swift b/RedditOs/Environements/Route.swift index fb065be..11e7f55 100644 --- a/RedditOs/Environements/Route.swift +++ b/RedditOs/Environements/Route.swift @@ -28,8 +28,8 @@ enum Route: Identifiable, Hashable { switch self { case .user: return "user" - case .subreddit: - return "subreddit" + case let .subreddit(subreddit): + return subreddit case .none: return "none" case let .defaultChannel(chanel): diff --git a/RedditOs/Features/Search/ToolbarSearchBar.swift b/RedditOs/Features/Search/ToolbarSearchBar.swift index e12d182..48bdaa1 100644 --- a/RedditOs/Features/Search/ToolbarSearchBar.swift +++ b/RedditOs/Features/Search/ToolbarSearchBar.swift @@ -8,6 +8,7 @@ import SwiftUI struct ToolbarSearchBar: View { + @EnvironmentObject private var uiState: UIState @State private var isFocused = false @StateObject private var searchViewModel = SearchViewModel() @@ -15,7 +16,7 @@ struct ToolbarSearchBar: View { TextField("Search anything", text: $searchViewModel.searchText) { editing in isFocused = editing } onCommit: { - + uiState.presentedNavigationRoute = .subreddit(subreddit: searchViewModel.searchText) } .keyboardShortcut("f", modifiers: .command) .padding(8) diff --git a/RedditOs/Features/Subreddit/SubredditAboutPopoverView.swift b/RedditOs/Features/Subreddit/SubredditAboutPopoverView.swift index 4518db0..6cd681e 100644 --- a/RedditOs/Features/Subreddit/SubredditAboutPopoverView.swift +++ b/RedditOs/Features/Subreddit/SubredditAboutPopoverView.swift @@ -9,16 +9,58 @@ import SwiftUI import Backend struct SubredditAboutPopoverView: View { - let subreddit: Subreddit? + @EnvironmentObject private var localData: LocalDataStore + @ObservedObject var viewModel: SubredditViewModel + @State private var isSubscribeHovered = false + + var isFavorite: Bool { + guard let subreddit = viewModel.subreddit else { + return false + } + return localData.favorites.contains(SubredditSmall.makeSubredditSmall(with: subreddit)) + } + + var isSubscriber: Bool { + viewModel.subreddit?.userIsSubscriber == true + } var body: some View { ScrollView { VStack(alignment: .leading, spacing: 12) { + HStack(alignment: .center, spacing: 16) { + if let subreddit = viewModel.subreddit { + Button(action: { + viewModel.toggleSubscribe() + }, label: { + if isSubscribeHovered { + Text(isSubscriber ? "Unsubscribe?" : "Subscribe?") + } else { + Text(isSubscriber ? "Subscribed" : "Subscribe") + } + }).onHover(perform: { hovering in + isSubscribeHovered = hovering + }) + + Button(action: { + if isFavorite { + localData.remove(favorite: SubredditSmall.makeSubredditSmall(with: subreddit)) + } else { + localData.add(favorite: SubredditSmall.makeSubredditSmall(with: subreddit)) + } + }, label: { + Image(systemName: isFavorite ? "star.fill" : "star") + .resizable() + .imageScale(.large) + .foregroundColor(isFavorite ? .redditGold : nil) + }).buttonStyle(BorderlessButtonStyle()) + } + } Text("About Community") .font(.title3) - Text(subreddit?.publicDescription ?? "") + Text(viewModel.subreddit?.publicDescription ?? "") .font(.body) - if let subscribers = subreddit?.subscribers, let connected = subreddit?.accountsActive { + if let subscribers = viewModel.subreddit?.subscribers, + let connected = viewModel.subreddit?.accountsActive { HStack(spacing: 16) { VStack(alignment: .leading) { Text("\(subscribers.toRoundedSuffixAsString())") @@ -41,7 +83,7 @@ struct SubredditAboutPopoverView: View { .font(.title3) .foregroundColor(.white) Text(" Created ") + - Text(subreddit?.createdUtc ?? Date(), style: .date) + Text(viewModel.subreddit?.createdUtc ?? Date(), style: .date) } }.padding() @@ -51,6 +93,6 @@ struct SubredditAboutPopoverView: View { struct SubredditAboutPopoverView_Previews: PreviewProvider { static var previews: some View { - SubredditAboutPopoverView(subreddit: static_subreddit_full) + SubredditAboutPopoverView(viewModel: SubredditViewModel(name: static_subreddit.name)).environmentObject(LocalDataStore()) } } diff --git a/RedditOs/Features/Subreddit/SubredditPostsListView.swift b/RedditOs/Features/Subreddit/SubredditPostsListView.swift index 36764a3..b96f01d 100644 --- a/RedditOs/Features/Subreddit/SubredditPostsListView.swift +++ b/RedditOs/Features/Subreddit/SubredditPostsListView.swift @@ -73,7 +73,7 @@ struct SubredditPostsListView: View { subredditAboutPopoverShown = true } .popover(isPresented: $subredditAboutPopoverShown, - content: { SubredditAboutPopoverView(subreddit: viewModel.subreddit) }) + content: { SubredditAboutPopoverView(viewModel: viewModel) }) } ToolbarItem(placement: .primaryAction) { diff --git a/RedditOs/Features/Subreddit/SubredditViewModel.swift b/RedditOs/Features/Subreddit/SubredditViewModel.swift index e405863..63642b6 100644 --- a/RedditOs/Features/Subreddit/SubredditViewModel.swift +++ b/RedditOs/Features/Subreddit/SubredditViewModel.swift @@ -19,6 +19,7 @@ class SubredditViewModel: ObservableObject { private var subredditCancellable: AnyCancellable? private var listingCancellable: AnyCancellable? + private var subscribeCancellable: AnyCancellable? @Published var subreddit: Subreddit? @Published var listings: [SubredditPost]? @@ -57,4 +58,24 @@ class SubredditViewModel: ObservableObject { } } } + + func toggleSubscribe() { + if subreddit?.userIsSubscriber == true { + subscribeCancellable = subreddit?.unSubscribe() + .receive(on: DispatchQueue.main) + .sink { [weak self] response in + if response.error != nil { + self?.subreddit?.userIsSubscriber = true + } + } + } else { + subscribeCancellable = subreddit?.subscribe() + .receive(on: DispatchQueue.main) + .sink { [weak self] response in + if response.error != nil { + self?.subreddit?.userIsSubscriber = false + } + } + } + } } diff --git a/RedditOs/Shared/FlairView.swift b/RedditOs/Shared/FlairView.swift index e7f223f..d47a046 100644 --- a/RedditOs/Shared/FlairView.swift +++ b/RedditOs/Shared/FlairView.swift @@ -50,7 +50,7 @@ struct FlairView: View { Text(text.t!) .foregroundColor(textColor) .font(display == .small ? .footnote : .callout) - .fontWeight(.light) + .fontWeight(.semibold) } else { EmptyView() } From 343d75a103a753d976017ba103b74c3488483925 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Wed, 19 Aug 2020 17:00:00 +0200 Subject: [PATCH 12/30] WIP --- .../Sources/Backend/Network/OauthClient.swift | 3 ++- RedditOs.xcodeproj/project.pbxproj | 17 +++++++++++++++++ .../xcshareddata/swiftpm/Package.resolved | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Packages/Backend/Sources/Backend/Network/OauthClient.swift b/Packages/Backend/Sources/Backend/Network/OauthClient.swift index 741d855..b1994ad 100644 --- a/Packages/Backend/Sources/Backend/Network/OauthClient.swift +++ b/Packages/Backend/Sources/Backend/Network/OauthClient.swift @@ -24,7 +24,8 @@ public class OauthClient: ObservableObject { private let baseURL = "https://www.reddit.com/api/v1/authorize" private let secrets: [String: AnyObject]? private let scopes = ["mysubreddits", "identity", "edit", "save", - "vote", "subscribe", "read", "submit", "history"] + "vote", "subscribe", "read", "submit", "history", + "privatemessages"] private let state = UUID().uuidString private let redirectURI = "redditos://auth" private let duration = "permanent" diff --git a/RedditOs.xcodeproj/project.pbxproj b/RedditOs.xcodeproj/project.pbxproj index 64d137c..45cdc49 100644 --- a/RedditOs.xcodeproj/project.pbxproj +++ b/RedditOs.xcodeproj/project.pbxproj @@ -46,6 +46,7 @@ 697E324924E3EDE70006F00F /* CommentVoteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 697E324824E3EDE70006F00F /* CommentVoteView.swift */; }; 697E324B24E3EFCB0006F00F /* CommentActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 697E324A24E3EFCB0006F00F /* CommentActionsView.swift */; }; 697E324D24E3F2900006F00F /* SharingPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 697E324C24E3F2900006F00F /* SharingPicker.swift */; }; + 69C1925A24EC0FEB00BA3C09 /* Parma in Frameworks */ = {isa = PBXBuildFile; productRef = 69C1925924EC0FEB00BA3C09 /* Parma */; }; 69CCB3EA24E2BEAC003FAAD7 /* SubredditAboutPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69CCB3E924E2BEAC003FAAD7 /* SubredditAboutPopoverView.swift */; }; 69D076C824B9E871001619AC /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69D076C724B9E871001619AC /* Color.swift */; }; 69D8663424E568060052A2B0 /* Route.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69D8663324E568060052A2B0 /* Route.swift */; }; @@ -127,6 +128,7 @@ files = ( 6970A0B124B74B7900B11031 /* SDWebImageSwiftUI in Frameworks */, 697E324524E3E7D90006F00F /* UI in Frameworks */, + 69C1925A24EC0FEB00BA3C09 /* Parma in Frameworks */, 69EACF1C24B7272E00303A16 /* Backend in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -391,6 +393,7 @@ 69EACF1B24B7272E00303A16 /* Backend */, 6970A0B024B74B7900B11031 /* SDWebImageSwiftUI */, 697E324424E3E7D90006F00F /* UI */, + 69C1925924EC0FEB00BA3C09 /* Parma */, ); productName = RedditOs; productReference = 69EACEFF24B63D5800303A16 /* Curiosity.app */; @@ -421,6 +424,7 @@ mainGroup = 69EACEF624B63D5800303A16; packageReferences = ( 6970A0AF24B74B7900B11031 /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */, + 69C1925824EC0FEB00BA3C09 /* XCRemoteSwiftPackageReference "Parma" */, ); productRefGroup = 69EACF0024B63D5800303A16 /* Products */; projectDirPath = ""; @@ -703,6 +707,14 @@ minimumVersion = 1.5.0; }; }; + 69C1925824EC0FEB00BA3C09 /* XCRemoteSwiftPackageReference "Parma" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/dasautoooo/Parma"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.1.0; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -715,6 +727,11 @@ isa = XCSwiftPackageProductDependency; productName = UI; }; + 69C1925924EC0FEB00BA3C09 /* Parma */ = { + isa = XCSwiftPackageProductDependency; + package = 69C1925824EC0FEB00BA3C09 /* XCRemoteSwiftPackageReference "Parma" */; + productName = Parma; + }; 69EACF1B24B7272E00303A16 /* Backend */ = { isa = XCSwiftPackageProductDependency; productName = Backend; diff --git a/RedditOs.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/RedditOs.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index d643439..00c9aef 100644 --- a/RedditOs.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/RedditOs.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,6 +1,15 @@ { "object": { "pins": [ + { + "package": "Down", + "repositoryURL": "https://github.com/iwasrobbed/Down", + "state": { + "branch": null, + "revision": "427aec0a0ab342246ec02369dea398597b61174a", + "version": "0.9.3" + } + }, { "package": "KeychainAccess", "repositoryURL": "https://github.com/kishikawakatsumi/KeychainAccess", @@ -10,6 +19,15 @@ "version": "4.2.0" } }, + { + "package": "Parma", + "repositoryURL": "https://github.com/dasautoooo/Parma", + "state": { + "branch": null, + "revision": "d620d6e8fdd59cd0a8110a08f4202b9eed5178aa", + "version": "0.1.0" + } + }, { "package": "SDWebImage", "repositoryURL": "https://github.com/SDWebImage/SDWebImage.git", From ddec324d1fa0849fe5441fac7f7341fac683cc72 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Fri, 4 Sep 2020 12:34:34 +0200 Subject: [PATCH 13/30] Fixes on toolbar and post row --- .../Features/Post/PostDetailActionsView.swift | 2 +- RedditOs/Features/Post/PostDetailView.swift | 13 +- RedditOs/Features/Post/PostViewModel.swift | 6 +- .../Features/Subreddit/SubredditPostRow.swift | 11 +- .../SubredditPostThumbnailView.swift | 2 +- .../Subreddit/SubredditPostsListView.swift | 119 +++++++++--------- RedditOs/Shared/PostVoteView.swift | 2 +- 7 files changed, 78 insertions(+), 77 deletions(-) diff --git a/RedditOs/Features/Post/PostDetailActionsView.swift b/RedditOs/Features/Post/PostDetailActionsView.swift index db7130f..99b2163 100644 --- a/RedditOs/Features/Post/PostDetailActionsView.swift +++ b/RedditOs/Features/Post/PostDetailActionsView.swift @@ -52,6 +52,6 @@ struct PostDetailActionsView: View { struct PostDetailActions_Previews: PreviewProvider { static var previews: some View { - PostDetailActionsView(viewModel: PostViewModel(post: static_listing)) + PostDetailActionsView(viewModel: PostViewModel()) } } diff --git a/RedditOs/Features/Post/PostDetailView.swift b/RedditOs/Features/Post/PostDetailView.swift index 78510a2..d4a5774 100644 --- a/RedditOs/Features/Post/PostDetailView.swift +++ b/RedditOs/Features/Post/PostDetailView.swift @@ -13,6 +13,7 @@ struct PostDetailView: View { @EnvironmentObject private var uiState: UIState @ObservedObject var viewModel: PostViewModel @State private var redrawLink = false + @State private var sharePickerShown = false var body: some View { List { @@ -38,6 +39,16 @@ struct PostDetailView: View { .onDisappear(perform: { uiState.selectedPost = nil }) + .toolbar { + Button(action: { + sharePickerShown.toggle() + }) { + Image(systemName: "square.and.arrow.up") + } + .background(SharingsPicker(isPresented: $sharePickerShown, + sharingItems: [viewModel.post.redditURL ?? ""])) + ToolbarSearchBar() + } .frame(minWidth: 500, maxWidth: .infinity, maxHeight: .infinity) @@ -46,6 +57,6 @@ struct PostDetailView: View { struct PostDetail_Previews: PreviewProvider { static var previews: some View { - PostDetailView(viewModel: PostViewModel(post: static_listing)) + PostDetailView(viewModel: PostViewModel()) } } diff --git a/RedditOs/Features/Post/PostViewModel.swift b/RedditOs/Features/Post/PostViewModel.swift index f768851..b6f2f65 100644 --- a/RedditOs/Features/Post/PostViewModel.swift +++ b/RedditOs/Features/Post/PostViewModel.swift @@ -11,7 +11,7 @@ import Combine import Backend class PostViewModel: ObservableObject { - @Published var post: SubredditPost + @Published var post: SubredditPost = static_listing @Published var comments: [Comment]? @AppStorage(SettingsKey.comments_default_sort_order) var commentsSort = Comment.Sort.top { didSet { @@ -21,10 +21,6 @@ class PostViewModel: ObservableObject { private var cancellableStore: [AnyCancellable] = [] - init(post: SubredditPost) { - self.post = post - } - func postVisit() { let oldValue = post.visited let cancellable = post.visit() diff --git a/RedditOs/Features/Subreddit/SubredditPostRow.swift b/RedditOs/Features/Subreddit/SubredditPostRow.swift index ee73a5b..8fe020b 100644 --- a/RedditOs/Features/Subreddit/SubredditPostRow.swift +++ b/RedditOs/Features/Subreddit/SubredditPostRow.swift @@ -21,16 +21,12 @@ struct SubredditPostRow: View { } } - @StateObject var viewModel: PostViewModel + let post: SubredditPost + @StateObject var viewModel: PostViewModel = PostViewModel() @Binding var displayMode: DisplayMode @Environment(\.openURL) private var openURL - init(post: SubredditPost, displayMode: Binding) { - _viewModel = StateObject(wrappedValue: PostViewModel(post: post)) - _displayMode = displayMode - } - var body: some View { NavigationLink(destination: PostDetailView(viewModel: viewModel)) { HStack { @@ -70,6 +66,9 @@ struct SubredditPostRow: View { Spacer() } } + .onAppear(perform: { + viewModel.post = post + }) .frame(width: 470) .padding(.vertical, 8) .contextMenu { diff --git a/RedditOs/Features/Subreddit/SubredditPostThumbnailView.swift b/RedditOs/Features/Subreddit/SubredditPostThumbnailView.swift index d7e9bab..9468ae3 100644 --- a/RedditOs/Features/Subreddit/SubredditPostThumbnailView.swift +++ b/RedditOs/Features/Subreddit/SubredditPostThumbnailView.swift @@ -56,6 +56,6 @@ struct SubredditPostThumbnailView: View { struct SubredditPostThubnailView_Previews: PreviewProvider { static var previews: some View { - SubredditPostThumbnailView(viewModel: PostViewModel(post: static_listing)) + SubredditPostThumbnailView(viewModel: PostViewModel()) } } diff --git a/RedditOs/Features/Subreddit/SubredditPostsListView.swift b/RedditOs/Features/Subreddit/SubredditPostsListView.swift index b96f01d..7f2d067 100644 --- a/RedditOs/Features/Subreddit/SubredditPostsListView.swift +++ b/RedditOs/Features/Subreddit/SubredditPostsListView.swift @@ -47,76 +47,71 @@ struct SubredditPostsListView: View { PostsListView(posts: viewModel.listings, displayMode: .constant(displayMode)) { viewModel.fetchListings() - }.onAppear(perform: viewModel.fetchListings) - PostNoSelectionPlaceholder() - } - .navigationTitle(viewModel.name.capitalized) - .navigationSubtitle(subtitle) - .toolbar { - ToolbarItem(placement: .navigation) { - Group { - if isDefaultChannel { - EmptyView() - } else if let icon = viewModel.subreddit?.iconImg, let url = URL(string: icon) { - WebImage(url: url) - .resizable() - .frame(width: 20, height: 20) - .cornerRadius(10) - } else { - - Image(systemName: "globe") - .resizable() - .frame(width: 20, height: 20) - } - } - .onTapGesture { - subredditAboutPopoverShown = true - } - .popover(isPresented: $subredditAboutPopoverShown, - content: { SubredditAboutPopoverView(viewModel: viewModel) }) } - - ToolbarItem(placement: .primaryAction) { - Picker("", - selection: $displayMode, - content: { - ForEach(SubredditPostRow.DisplayMode.allCases, id: \.self) { mode in - Image(systemName: mode.iconName()) - .tag(mode) + .onAppear(perform: viewModel.fetchListings) + .toolbar { + ToolbarItem(placement: .navigation) { + Group { + if isDefaultChannel { + EmptyView() + } else if let icon = viewModel.subreddit?.iconImg, let url = URL(string: icon) { + WebImage(url: url) + .resizable() + .frame(width: 20, height: 20) + .cornerRadius(10) + } else { + + Image(systemName: "globe") + .resizable() + .frame(width: 20, height: 20) } - }).pickerStyle(InlinePickerStyle()) - } - - ToolbarItem(placement: .primaryAction) { - if isDefaultChannel { - Text("") - } else { - Picker(selection: $viewModel.sortOrder, - label: Text("Sorting"), + } + .onTapGesture { + subredditAboutPopoverShown = true + } + .popover(isPresented: $subredditAboutPopoverShown, + content: { SubredditAboutPopoverView(viewModel: viewModel) }) + } + + ToolbarItem(placement: .primaryAction) { + Picker("", + selection: $displayMode, content: { - ForEach(SubredditViewModel.SortOrder.allCases, id: \.self) { sort in - Text(sort.rawValue.capitalized).tag(sort) + ForEach(SubredditPostRow.DisplayMode.allCases, id: \.self) { mode in + Image(systemName: mode.iconName()) + .tag(mode) } - }) + }).pickerStyle(InlinePickerStyle()) } - } - - ToolbarItem(placement: .primaryAction) { - Button(action: { - sharePickerShown.toggle() - }) { - Image(systemName: "square.and.arrow.up") + + ToolbarItem(placement: .primaryAction) { + if isDefaultChannel { + Text("") + } else { + Picker(selection: $viewModel.sortOrder, + label: Text("Sorting"), + content: { + ForEach(SubredditViewModel.SortOrder.allCases, id: \.self) { sort in + Text(sort.rawValue.capitalized).tag(sort) + } + }) + } } - .background(SharingsPicker(isPresented: $sharePickerShown, - sharingItems: [uiState.selectedPost?.post.redditURL ?? - viewModel.subreddit?.redditURL ?? - ""])) - } - - ToolbarItem(placement: .primaryAction) { - ToolbarSearchBar() } + PostNoSelectionPlaceholder() + .toolbar { + Button(action: { + sharePickerShown.toggle() + }) { + Image(systemName: "square.and.arrow.up") + } + .background(SharingsPicker(isPresented: $sharePickerShown, + sharingItems: [viewModel.subreddit?.redditURL ?? ""])) + ToolbarSearchBar() + } } + .navigationTitle(viewModel.name.capitalized) + .navigationSubtitle(subtitle) .onAppear(perform: viewModel.fetchListings) .onAppear { if !isDefaultChannel { diff --git a/RedditOs/Shared/PostVoteView.swift b/RedditOs/Shared/PostVoteView.swift index 953ac08..d35188d 100644 --- a/RedditOs/Shared/PostVoteView.swift +++ b/RedditOs/Shared/PostVoteView.swift @@ -43,6 +43,6 @@ struct PostVoteView: View { struct ListingVoteView_Previews: PreviewProvider { static var previews: some View { - PostVoteView(viewModel: PostViewModel(post: static_listing)) + PostVoteView(viewModel: PostViewModel()) } } From 11e74fbbb7b31dddbd356febcd729c7d010bec84 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Fri, 4 Sep 2020 12:48:47 +0200 Subject: [PATCH 14/30] Replace SDWebImage with KingFisher --- RedditOs.xcodeproj/project.pbxproj | 20 +++++++-------- .../xcshareddata/swiftpm/Package.resolved | 25 ++++++------------- .../Features/Post/PostDetailContent.swift | 5 ++-- RedditOs/Features/Post/PostDetailHeader.swift | 4 +-- RedditOs/Features/Profile/ProfileView.swift | 1 - .../GlobalSearchPopoverView.swift | 1 - .../GlobalSearchSubRow.swift | 4 +-- .../PopoverSearchSubredditRow.swift | 4 +-- .../Sidebar/SidebarSubredditRow.swift | 4 +-- RedditOs/Features/Sidebar/SidebarView.swift | 1 - .../Features/Subreddit/SubredditPostRow.swift | 1 - .../SubredditPostThumbnailView.swift | 4 +-- .../Subreddit/SubredditPostsListView.swift | 4 +-- .../Users/shared/UserHeaderView.swift | 4 +-- RedditOs/Shared/AwardView.swift | 6 ++--- RedditOs/Shared/FlairView.swift | 4 +-- 16 files changed, 39 insertions(+), 53 deletions(-) diff --git a/RedditOs.xcodeproj/project.pbxproj b/RedditOs.xcodeproj/project.pbxproj index 45cdc49..c30bada 100644 --- a/RedditOs.xcodeproj/project.pbxproj +++ b/RedditOs.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 69222AA524CD6A89009F31B4 /* UserHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69222AA424CD6A89009F31B4 /* UserHeaderView.swift */; }; 69222AA724CD6D6C009F31B4 /* SubmittedPostsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69222AA624CD6D6C009F31B4 /* SubmittedPostsListView.swift */; }; 69222AAA24CD7518009F31B4 /* CommentRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69222AA924CD7518009F31B4 /* CommentRow.swift */; }; + 6923F8CD250250FC0003870F /* KingfisherSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 6923F8CC250250FC0003870F /* KingfisherSwiftUI */; }; 6924D53C24CD949D005487CA /* UserPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6924D53B24CD949D005487CA /* UserPopoverView.swift */; }; 6924D53E24CD94B0005487CA /* UserViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6924D53D24CD94B0005487CA /* UserViewModel.swift */; }; 6924D54024CDCED0005487CA /* UserSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6924D53F24CDCED0005487CA /* UserSheetView.swift */; }; @@ -32,7 +33,6 @@ 693F85D424D0715000224ADB /* ToolbarSearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693F85D324D0715000224ADB /* ToolbarSearchBar.swift */; }; 694C634F24C0AA6D0017897D /* SidebarSubredditRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 694C634E24C0AA6D0017897D /* SidebarSubredditRow.swift */; }; 6970A0AE24B74A9D00B11031 /* PostInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0AD24B74A9D00B11031 /* PostInfoView.swift */; }; - 6970A0B124B74B7900B11031 /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 6970A0B024B74B7900B11031 /* SDWebImageSwiftUI */; }; 6970A0B324B77D1200B11031 /* PostVoteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0B224B77D1200B11031 /* PostVoteView.swift */; }; 6970A0B624B783FE00B11031 /* LinkPresentationRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0B524B783FE00B11031 /* LinkPresentationRepresentable.swift */; }; 6970A0B924B79AFD00B11031 /* PopoverSearchSubredditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6970A0B824B79AFD00B11031 /* PopoverSearchSubredditView.swift */; }; @@ -126,10 +126,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 6970A0B124B74B7900B11031 /* SDWebImageSwiftUI in Frameworks */, 697E324524E3E7D90006F00F /* UI in Frameworks */, 69C1925A24EC0FEB00BA3C09 /* Parma in Frameworks */, 69EACF1C24B7272E00303A16 /* Backend in Frameworks */, + 6923F8CD250250FC0003870F /* KingfisherSwiftUI in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -391,9 +391,9 @@ name = RedditOs; packageProductDependencies = ( 69EACF1B24B7272E00303A16 /* Backend */, - 6970A0B024B74B7900B11031 /* SDWebImageSwiftUI */, 697E324424E3E7D90006F00F /* UI */, 69C1925924EC0FEB00BA3C09 /* Parma */, + 6923F8CC250250FC0003870F /* KingfisherSwiftUI */, ); productName = RedditOs; productReference = 69EACEFF24B63D5800303A16 /* Curiosity.app */; @@ -423,8 +423,8 @@ ); mainGroup = 69EACEF624B63D5800303A16; packageReferences = ( - 6970A0AF24B74B7900B11031 /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */, 69C1925824EC0FEB00BA3C09 /* XCRemoteSwiftPackageReference "Parma" */, + 6923F8CB250250FC0003870F /* XCRemoteSwiftPackageReference "Kingfisher" */, ); productRefGroup = 69EACF0024B63D5800303A16 /* Products */; projectDirPath = ""; @@ -699,12 +699,12 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 6970A0AF24B74B7900B11031 /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */ = { + 6923F8CB250250FC0003870F /* XCRemoteSwiftPackageReference "Kingfisher" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/SDWebImage/SDWebImageSwiftUI"; + repositoryURL = "https://github.com/onevcat/Kingfisher"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 1.5.0; + minimumVersion = 5.15.0; }; }; 69C1925824EC0FEB00BA3C09 /* XCRemoteSwiftPackageReference "Parma" */ = { @@ -718,10 +718,10 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 6970A0B024B74B7900B11031 /* SDWebImageSwiftUI */ = { + 6923F8CC250250FC0003870F /* KingfisherSwiftUI */ = { isa = XCSwiftPackageProductDependency; - package = 6970A0AF24B74B7900B11031 /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */; - productName = SDWebImageSwiftUI; + package = 6923F8CB250250FC0003870F /* XCRemoteSwiftPackageReference "Kingfisher" */; + productName = KingfisherSwiftUI; }; 697E324424E3E7D90006F00F /* UI */ = { isa = XCSwiftPackageProductDependency; diff --git a/RedditOs.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/RedditOs.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 00c9aef..6428711 100644 --- a/RedditOs.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/RedditOs.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -20,30 +20,21 @@ } }, { - "package": "Parma", - "repositoryURL": "https://github.com/dasautoooo/Parma", + "package": "Kingfisher", + "repositoryURL": "https://github.com/onevcat/Kingfisher", "state": { "branch": null, - "revision": "d620d6e8fdd59cd0a8110a08f4202b9eed5178aa", - "version": "0.1.0" + "revision": "175eeb4618b0a6ef4d69a7409b6a74ddd235a093", + "version": "5.15.0" } }, { - "package": "SDWebImage", - "repositoryURL": "https://github.com/SDWebImage/SDWebImage.git", - "state": { - "branch": null, - "revision": "f876da50318666141d72963e1568afee6f84f008", - "version": "5.8.3" - } - }, - { - "package": "SDWebImageSwiftUI", - "repositoryURL": "https://github.com/SDWebImage/SDWebImageSwiftUI", + "package": "Parma", + "repositoryURL": "https://github.com/dasautoooo/Parma", "state": { "branch": null, - "revision": "4c7f169e39bc35d6b80d42b8eb8301bee9cd0907", - "version": "1.5.0" + "revision": "d620d6e8fdd59cd0a8110a08f4202b9eed5178aa", + "version": "0.1.0" } } ] diff --git a/RedditOs/Features/Post/PostDetailContent.swift b/RedditOs/Features/Post/PostDetailContent.swift index ddb47f3..f5bfff7 100644 --- a/RedditOs/Features/Post/PostDetailContent.swift +++ b/RedditOs/Features/Post/PostDetailContent.swift @@ -8,7 +8,7 @@ import SwiftUI import Backend import AVKit -import SDWebImageSwiftUI +import KingfisherSwiftUI struct PostDetailContent: View { let listing: SubredditPost @@ -31,9 +31,8 @@ struct PostDetailContent: View { if realURL.pathExtension == "jpg" || realURL.pathExtension == "png" { HStack { Spacer() - WebImage(url: realURL) + KFImage(realURL) .resizable() - .indicator(.activity) .aspectRatio(contentMode: .fit) .background(Color.gray) .frame(maxHeight: 400) diff --git a/RedditOs/Features/Post/PostDetailHeader.swift b/RedditOs/Features/Post/PostDetailHeader.swift index 19d921b..82ddc44 100644 --- a/RedditOs/Features/Post/PostDetailHeader.swift +++ b/RedditOs/Features/Post/PostDetailHeader.swift @@ -7,7 +7,7 @@ import SwiftUI import Backend -import SDWebImageSwiftUI +import KingfisherSwiftUI struct PostDetailHeader: View { let listing: SubredditPost @@ -20,7 +20,7 @@ struct PostDetailHeader: View { .multilineTextAlignment(.leading) .truncationMode(.tail) if let url = listing.thumbnailURL, url.pathExtension != "jpg", url.pathExtension != "png" { - WebImage(url: url) + KFImage(url) .frame(width: 80, height: 60) .aspectRatio(contentMode: .fit) .cornerRadius(8) diff --git a/RedditOs/Features/Profile/ProfileView.swift b/RedditOs/Features/Profile/ProfileView.swift index ea9d80f..854d5af 100644 --- a/RedditOs/Features/Profile/ProfileView.swift +++ b/RedditOs/Features/Profile/ProfileView.swift @@ -7,7 +7,6 @@ import SwiftUI import Backend -import SDWebImageSwiftUI struct ProfileView: View { enum OverviewFilter: String, CaseIterable { diff --git a/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift b/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift index e91b961..a185d5b 100644 --- a/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift +++ b/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift @@ -7,7 +7,6 @@ import SwiftUI import Backend -import SDWebImageSwiftUI struct GlobalSearchPopoverView: View { @EnvironmentObject private var uiState: UIState diff --git a/RedditOs/Features/Search/Global Search Popopver/GlobalSearchSubRow.swift b/RedditOs/Features/Search/Global Search Popopver/GlobalSearchSubRow.swift index a08f462..506fb2b 100644 --- a/RedditOs/Features/Search/Global Search Popopver/GlobalSearchSubRow.swift +++ b/RedditOs/Features/Search/Global Search Popopver/GlobalSearchSubRow.swift @@ -6,7 +6,7 @@ // import SwiftUI -import SDWebImageSwiftUI +import KingfisherSwiftUI struct GlobalSearchSubRow: View { let icon: String? @@ -18,7 +18,7 @@ struct GlobalSearchSubRow: View { HStack { if let image = icon, let url = URL(string: image) { - WebImage(url: url) + KFImage(url) .resizable() .frame(width: 16, height: 16) .cornerRadius(8) diff --git a/RedditOs/Features/Search/Subreddit Search Popover/PopoverSearchSubredditRow.swift b/RedditOs/Features/Search/Subreddit Search Popover/PopoverSearchSubredditRow.swift index 2ae1b15..691bb45 100644 --- a/RedditOs/Features/Search/Subreddit Search Popover/PopoverSearchSubredditRow.swift +++ b/RedditOs/Features/Search/Subreddit Search Popover/PopoverSearchSubredditRow.swift @@ -7,7 +7,7 @@ import SwiftUI import Backend -import SDWebImageSwiftUI +import KingfisherSwiftUI struct PopoverSearchSubredditRow: View { let subreddit: SubredditSmall @@ -17,7 +17,7 @@ struct PopoverSearchSubredditRow: View { HStack { if let image = subreddit.iconImg, let url = URL(string: image) { - WebImage(url: url) + KFImage(url) .resizable() .frame(width: 30, height: 30) .cornerRadius(15) diff --git a/RedditOs/Features/Sidebar/SidebarSubredditRow.swift b/RedditOs/Features/Sidebar/SidebarSubredditRow.swift index cf17d2d..0bd0007 100644 --- a/RedditOs/Features/Sidebar/SidebarSubredditRow.swift +++ b/RedditOs/Features/Sidebar/SidebarSubredditRow.swift @@ -7,7 +7,7 @@ import SwiftUI import Backend -import SDWebImageSwiftUI +import KingfisherSwiftUI struct SidebarSubredditRow: View { let name: String @@ -18,7 +18,7 @@ struct SidebarSubredditRow: View { HStack { if let image = iconURL, let url = URL(string: image) { - WebImage(url: url) + KFImage(url) .resizable() .frame(width: 16, height: 16) .cornerRadius(8) diff --git a/RedditOs/Features/Sidebar/SidebarView.swift b/RedditOs/Features/Sidebar/SidebarView.swift index 4b10bc6..44835f6 100644 --- a/RedditOs/Features/Sidebar/SidebarView.swift +++ b/RedditOs/Features/Sidebar/SidebarView.swift @@ -7,7 +7,6 @@ import SwiftUI import Backend -import SDWebImageSwiftUI struct SidebarView: View { @EnvironmentObject private var uiState: UIState diff --git a/RedditOs/Features/Subreddit/SubredditPostRow.swift b/RedditOs/Features/Subreddit/SubredditPostRow.swift index 8fe020b..9d52f5b 100644 --- a/RedditOs/Features/Subreddit/SubredditPostRow.swift +++ b/RedditOs/Features/Subreddit/SubredditPostRow.swift @@ -7,7 +7,6 @@ import SwiftUI import Backend -import SDWebImageSwiftUI struct SubredditPostRow: View { enum DisplayMode: String, CaseIterable { diff --git a/RedditOs/Features/Subreddit/SubredditPostThumbnailView.swift b/RedditOs/Features/Subreddit/SubredditPostThumbnailView.swift index 9468ae3..c2d85cc 100644 --- a/RedditOs/Features/Subreddit/SubredditPostThumbnailView.swift +++ b/RedditOs/Features/Subreddit/SubredditPostThumbnailView.swift @@ -7,7 +7,7 @@ import SwiftUI import Backend -import SDWebImageSwiftUI +import KingfisherSwiftUI struct SubredditPostThumbnailView: View { @ObservedObject var viewModel: PostViewModel @@ -19,7 +19,7 @@ struct SubredditPostThumbnailView: View { var body: some View { ZStack(alignment: .topLeading) { if let url = post.thumbnailURL ?? post.secureMedia?.oembed?.thumbnailUrlAsURL { - WebImage(url: url) + KFImage(url) .frame(width: 80, height: 60) .aspectRatio(contentMode: .fit) .cornerRadius(8) diff --git a/RedditOs/Features/Subreddit/SubredditPostsListView.swift b/RedditOs/Features/Subreddit/SubredditPostsListView.swift index 7f2d067..6eb73ec 100644 --- a/RedditOs/Features/Subreddit/SubredditPostsListView.swift +++ b/RedditOs/Features/Subreddit/SubredditPostsListView.swift @@ -8,7 +8,7 @@ import SwiftUI import Backend import UI -import SDWebImageSwiftUI +import KingfisherSwiftUI struct SubredditPostsListView: View { let posts = Array(repeating: 0, count: 20) @@ -55,7 +55,7 @@ struct SubredditPostsListView: View { if isDefaultChannel { EmptyView() } else if let icon = viewModel.subreddit?.iconImg, let url = URL(string: icon) { - WebImage(url: url) + KFImage(url) .resizable() .frame(width: 20, height: 20) .cornerRadius(10) diff --git a/RedditOs/Features/Users/shared/UserHeaderView.swift b/RedditOs/Features/Users/shared/UserHeaderView.swift index f161199..fe1a25a 100644 --- a/RedditOs/Features/Users/shared/UserHeaderView.swift +++ b/RedditOs/Features/Users/shared/UserHeaderView.swift @@ -7,7 +7,7 @@ import SwiftUI import Backend -import SDWebImageSwiftUI +import KingfisherSwiftUI struct UserHeaderView: View { let user: User @@ -16,7 +16,7 @@ struct UserHeaderView: View { HStack(spacing: 32) { Spacer() if let avatar = user.avatarURL { - WebImage(url: avatar) + KFImage(avatar) .resizable() .frame(width: 100, height: 100) .cornerRadius(8) diff --git a/RedditOs/Shared/AwardView.swift b/RedditOs/Shared/AwardView.swift index fc2d844..7e3183d 100644 --- a/RedditOs/Shared/AwardView.swift +++ b/RedditOs/Shared/AwardView.swift @@ -7,7 +7,7 @@ import SwiftUI import Backend -import SDWebImageSwiftUI +import KingfisherSwiftUI struct AwardsView: View { let awards: [Award] @@ -19,7 +19,7 @@ struct AwardsView: View { HStack { ForEach(awards.prefix(3)) { award in HStack(spacing: 2) { - WebImage(url: award.staticIconUrl, isAnimating: .constant(true)) + KFImage(award.staticIconUrl) .resizable() .frame(width: 16, height: 16) } @@ -36,7 +36,7 @@ struct AwardsView: View { VStack { ForEach(awards) { award in HStack(spacing: 8) { - WebImage(url: award.staticIconUrl) + KFImage(award.staticIconUrl) .resizable() .frame(width: 30, height: 30) Text(award.name) diff --git a/RedditOs/Shared/FlairView.swift b/RedditOs/Shared/FlairView.swift index d47a046..63a0154 100644 --- a/RedditOs/Shared/FlairView.swift +++ b/RedditOs/Shared/FlairView.swift @@ -7,7 +7,7 @@ import SwiftUI import Backend -import SDWebImageSwiftUI +import KingfisherSwiftUI struct FlairView: View { enum Display { @@ -42,7 +42,7 @@ struct FlairView: View { HStack(spacing: 4) { ForEach(texts, id: \.self) { text in if text.e == "emoji" { - WebImage(url: text.u!) + KFImage(text.u!) .resizable() .aspectRatio(contentMode: .fit) .frame(width: 20, height: 20) From eacc4c3e0cb29f05a8c773069dfa0548972d8078 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Tue, 8 Sep 2020 09:14:57 +0200 Subject: [PATCH 15/30] Cleanup --- RedditOs/Features/Post/PostDetailActionsView.swift | 2 +- RedditOs/Features/Post/PostDetailView.swift | 2 +- RedditOs/Features/Post/PostViewModel.swift | 6 +++++- RedditOs/Features/Subreddit/SubredditPostRow.swift | 11 ++++++----- .../Subreddit/SubredditPostThumbnailView.swift | 2 +- RedditOs/Shared/PostVoteView.swift | 2 +- 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/RedditOs/Features/Post/PostDetailActionsView.swift b/RedditOs/Features/Post/PostDetailActionsView.swift index 99b2163..db7130f 100644 --- a/RedditOs/Features/Post/PostDetailActionsView.swift +++ b/RedditOs/Features/Post/PostDetailActionsView.swift @@ -52,6 +52,6 @@ struct PostDetailActionsView: View { struct PostDetailActions_Previews: PreviewProvider { static var previews: some View { - PostDetailActionsView(viewModel: PostViewModel()) + PostDetailActionsView(viewModel: PostViewModel(post: static_listing)) } } diff --git a/RedditOs/Features/Post/PostDetailView.swift b/RedditOs/Features/Post/PostDetailView.swift index d4a5774..69582d0 100644 --- a/RedditOs/Features/Post/PostDetailView.swift +++ b/RedditOs/Features/Post/PostDetailView.swift @@ -57,6 +57,6 @@ struct PostDetailView: View { struct PostDetail_Previews: PreviewProvider { static var previews: some View { - PostDetailView(viewModel: PostViewModel()) + PostDetailView(viewModel: PostViewModel(post: static_listing)) } } diff --git a/RedditOs/Features/Post/PostViewModel.swift b/RedditOs/Features/Post/PostViewModel.swift index b6f2f65..f768851 100644 --- a/RedditOs/Features/Post/PostViewModel.swift +++ b/RedditOs/Features/Post/PostViewModel.swift @@ -11,7 +11,7 @@ import Combine import Backend class PostViewModel: ObservableObject { - @Published var post: SubredditPost = static_listing + @Published var post: SubredditPost @Published var comments: [Comment]? @AppStorage(SettingsKey.comments_default_sort_order) var commentsSort = Comment.Sort.top { didSet { @@ -21,6 +21,10 @@ class PostViewModel: ObservableObject { private var cancellableStore: [AnyCancellable] = [] + init(post: SubredditPost) { + self.post = post + } + func postVisit() { let oldValue = post.visited let cancellable = post.visit() diff --git a/RedditOs/Features/Subreddit/SubredditPostRow.swift b/RedditOs/Features/Subreddit/SubredditPostRow.swift index 9d52f5b..2505253 100644 --- a/RedditOs/Features/Subreddit/SubredditPostRow.swift +++ b/RedditOs/Features/Subreddit/SubredditPostRow.swift @@ -20,12 +20,16 @@ struct SubredditPostRow: View { } } - let post: SubredditPost - @StateObject var viewModel: PostViewModel = PostViewModel() + @StateObject var viewModel: PostViewModel @Binding var displayMode: DisplayMode @Environment(\.openURL) private var openURL + init(post: SubredditPost, displayMode: Binding) { + _viewModel = StateObject(wrappedValue: PostViewModel(post: post)) + _displayMode = displayMode + } + var body: some View { NavigationLink(destination: PostDetailView(viewModel: viewModel)) { HStack { @@ -65,9 +69,6 @@ struct SubredditPostRow: View { Spacer() } } - .onAppear(perform: { - viewModel.post = post - }) .frame(width: 470) .padding(.vertical, 8) .contextMenu { diff --git a/RedditOs/Features/Subreddit/SubredditPostThumbnailView.swift b/RedditOs/Features/Subreddit/SubredditPostThumbnailView.swift index c2d85cc..26657d6 100644 --- a/RedditOs/Features/Subreddit/SubredditPostThumbnailView.swift +++ b/RedditOs/Features/Subreddit/SubredditPostThumbnailView.swift @@ -56,6 +56,6 @@ struct SubredditPostThumbnailView: View { struct SubredditPostThubnailView_Previews: PreviewProvider { static var previews: some View { - SubredditPostThumbnailView(viewModel: PostViewModel()) + SubredditPostThumbnailView(viewModel: PostViewModel(post: static_listing)) } } diff --git a/RedditOs/Shared/PostVoteView.swift b/RedditOs/Shared/PostVoteView.swift index d35188d..953ac08 100644 --- a/RedditOs/Shared/PostVoteView.swift +++ b/RedditOs/Shared/PostVoteView.swift @@ -43,6 +43,6 @@ struct PostVoteView: View { struct ListingVoteView_Previews: PreviewProvider { static var previews: some View { - PostVoteView(viewModel: PostViewModel()) + PostVoteView(viewModel: PostViewModel(post: static_listing)) } } From 0fa0475b522b13d91774b00325fdeb4be2c225c0 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Tue, 22 Sep 2020 16:22:55 +0200 Subject: [PATCH 16/30] Big Sur Beta 7 update --- RedditOs.xcodeproj/project.pbxproj | 14 +++++--- .../xcshareddata/xcschemes/RedditOs.xcscheme | 2 +- .../Features/Post/PostDetailToolbar.swift | 35 +++++++++++++++++++ RedditOs/Features/Post/PostDetailView.swift | 9 +---- .../Subreddit/SubredditPostsListView.swift | 21 ++++------- 5 files changed, 52 insertions(+), 29 deletions(-) create mode 100644 RedditOs/Features/Post/PostDetailToolbar.swift diff --git a/RedditOs.xcodeproj/project.pbxproj b/RedditOs.xcodeproj/project.pbxproj index c30bada..52507e2 100644 --- a/RedditOs.xcodeproj/project.pbxproj +++ b/RedditOs.xcodeproj/project.pbxproj @@ -29,6 +29,7 @@ 692566DA24B8A3830014E0D4 /* PostDetailHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692566D924B8A3830014E0D4 /* PostDetailHeader.swift */; }; 6927894924B9B75200EEFBF2 /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6927894824B9B75200EEFBF2 /* ProfileView.swift */; }; 692F237624CB3A7B006C9D40 /* SavedPostsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692F237524CB3A7B006C9D40 /* SavedPostsListView.swift */; }; + 693BD7732518C4FB00CA5214 /* PostDetailToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693BD7722518C4FB00CA5214 /* PostDetailToolbar.swift */; }; 693F85D124D0690500224ADB /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693F85D024D0690500224ADB /* NSTextField.swift */; }; 693F85D424D0715000224ADB /* ToolbarSearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693F85D324D0715000224ADB /* ToolbarSearchBar.swift */; }; 694C634F24C0AA6D0017897D /* SidebarSubredditRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 694C634E24C0AA6D0017897D /* SidebarSubredditRow.swift */; }; @@ -85,6 +86,7 @@ 692566D924B8A3830014E0D4 /* PostDetailHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostDetailHeader.swift; sourceTree = ""; }; 6927894824B9B75200EEFBF2 /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = ""; }; 692F237524CB3A7B006C9D40 /* SavedPostsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedPostsListView.swift; sourceTree = ""; }; + 693BD7722518C4FB00CA5214 /* PostDetailToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostDetailToolbar.swift; sourceTree = ""; }; 693F85D024D0690500224ADB /* NSTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSTextField.swift; sourceTree = ""; }; 693F85D324D0715000224ADB /* ToolbarSearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarSearchBar.swift; sourceTree = ""; }; 694C634E24C0AA6D0017897D /* SidebarSubredditRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarSubredditRow.swift; sourceTree = ""; }; @@ -360,6 +362,7 @@ 692566D524B8A25A0014E0D4 /* PostDetailContent.swift */, 6970A0C224B896CD00B11031 /* PostDetailCommentsSection.swift */, 692566D724B8A3190014E0D4 /* PostDetailActionsView.swift */, + 693BD7722518C4FB00CA5214 /* PostDetailToolbar.swift */, ); path = Post; sourceTree = ""; @@ -406,7 +409,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1200; - LastUpgradeCheck = 1200; + LastUpgradeCheck = 1220; TargetAttributes = { 69EACEFE24B63D5800303A16 = { CreatedOnToolsVersion = 12.0; @@ -460,6 +463,7 @@ 697E324924E3EDE70006F00F /* CommentVoteView.swift in Sources */, 6970A0AE24B74A9D00B11031 /* PostInfoView.swift in Sources */, 6924D54D24CDF92A005487CA /* SettingsView.swift in Sources */, + 693BD7732518C4FB00CA5214 /* PostDetailToolbar.swift in Sources */, 6970A0B624B783FE00B11031 /* LinkPresentationRepresentable.swift in Sources */, 6924D54324CDD049005487CA /* UIState.swift in Sources */, 692566D624B8A25A0014E0D4 /* PostDetailContent.swift in Sources */, @@ -624,10 +628,10 @@ 69EACF1024B63D5900303A16 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = RedditOs/RedditOs.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 10; @@ -640,7 +644,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.16; + MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 0.0.3; PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.curiosity; PRODUCT_NAME = Curiosity; @@ -651,10 +655,10 @@ 69EACF1124B63D5900303A16 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = RedditOs/RedditOs.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 10; @@ -667,7 +671,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.16; + MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 0.0.3; PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.curiosity; PRODUCT_NAME = Curiosity; diff --git a/RedditOs.xcodeproj/xcshareddata/xcschemes/RedditOs.xcscheme b/RedditOs.xcodeproj/xcshareddata/xcschemes/RedditOs.xcscheme index 802f7ed..ee70bc3 100644 --- a/RedditOs.xcodeproj/xcshareddata/xcschemes/RedditOs.xcscheme +++ b/RedditOs.xcodeproj/xcshareddata/xcschemes/RedditOs.xcscheme @@ -1,6 +1,6 @@ Date: Wed, 30 Sep 2020 17:36:09 +0200 Subject: [PATCH 17/30] Cleanup profile + improve search --- RedditOs/Features/Comments/CommentRow.swift | 1 + RedditOs/Features/Profile/ProfileView.swift | 42 ++++----------------- RedditOs/Features/Sidebar/SidebarView.swift | 20 ++++++---- 3 files changed, 20 insertions(+), 43 deletions(-) diff --git a/RedditOs/Features/Comments/CommentRow.swift b/RedditOs/Features/Comments/CommentRow.swift index 1b799ad..de524fa 100644 --- a/RedditOs/Features/Comments/CommentRow.swift +++ b/RedditOs/Features/Comments/CommentRow.swift @@ -54,6 +54,7 @@ struct CommentRow: View { Text(viewModel.comment.body ?? "No comment content") .font(.body) CommentActionsView(viewModel: viewModel) + .foregroundColor(.gray) Divider() }.padding(.vertical, 4) } diff --git a/RedditOs/Features/Profile/ProfileView.swift b/RedditOs/Features/Profile/ProfileView.swift index 854d5af..2942cc3 100644 --- a/RedditOs/Features/Profile/ProfileView.swift +++ b/RedditOs/Features/Profile/ProfileView.swift @@ -9,14 +9,9 @@ import SwiftUI import Backend struct ProfileView: View { - enum OverviewFilter: String, CaseIterable { - case posts, comments - } - @EnvironmentObject private var oauthClient: OauthClient @EnvironmentObject private var currentUser: CurrentUserStore @Environment(\.openURL) private var openURL - @State private var overviewFilter = OverviewFilter.posts private let loadingPlaceholders = Array(repeating: static_listing, count: 10) @@ -27,7 +22,7 @@ struct ProfileView: View { if currentUser.user != nil { userOverview } - } + }.listStyle(InsetListStyle()) PostNoSelectionPlaceholder() } @@ -60,33 +55,6 @@ struct ProfileView: View { @ViewBuilder private var userOverview: some View { if let overview = currentUser.overview { - Picker("", selection: $overviewFilter) { - ForEach(OverviewFilter.allCases, id: \.self) { filter in - Text(filter.rawValue.capitalized) - .tag(filter) - } - } - .pickerStyle(SegmentedPickerStyle()) - .padding(.vertical, 12) - - if overviewFilter == .posts { - let posts = overview.compactMap{ $0.post } - ForEach(posts) { post in - SubredditPostRow(post: post, displayMode: .constant(.large)) - } - } else if overviewFilter == .comments { - let comments = overview.compactMap{ $0.comment } - ForEach(comments) { comment in - CommentRow(comment: comment) - } - } - LoadingRow(text: "Loading next page") - .onAppear { - currentUser.fetchOverview() - } - - // This is crashing SwiftUI for now. - /* ForEach(overview) { content in switch content { case let .post(post): @@ -97,10 +65,14 @@ struct ProfileView: View { Text("Unsupported view") } } - */ + LoadingRow(text: "Loading next page") + .onAppear { + currentUser.fetchOverview() + } } else { ForEach(loadingPlaceholders) { post in - SubredditPostRow(post: post, displayMode: .constant(.large)) + SubredditPostRow(post: post, + displayMode: .constant(.large)) .redacted(reason: .placeholder) } } diff --git a/RedditOs/Features/Sidebar/SidebarView.swift b/RedditOs/Features/Sidebar/SidebarView.swift index 44835f6..b7b1c47 100644 --- a/RedditOs/Features/Sidebar/SidebarView.swift +++ b/RedditOs/Features/Sidebar/SidebarView.swift @@ -19,20 +19,24 @@ struct SidebarView: View { var body: some View { List(selection: $uiState.sidebarSelection) { + if let route = uiState.presentedNavigationRoute { + Section { + NavigationLink( + destination: route.makeView(), + tag: route, + selection: $uiState.presentedNavigationRoute, + label: { + Label("Search", systemImage: "magnifyingglass") + }) + } + } + Section { ForEach(UIState.DefaultChannels.allCases, id: \.self) { item in NavigationLink(destination: SubredditPostsListView(name: item.rawValue)) { Label(LocalizedStringKey(item.rawValue.capitalized), systemImage: item.icon()) }.tag(item.rawValue) }.animation(nil) - - NavigationLink( - destination: uiState.presentedNavigationRoute?.makeView() ?? Route.none.makeView(), - tag: uiState.presentedNavigationRoute ?? Route.none, - selection: $uiState.presentedNavigationRoute, - label: { - EmptyView() - }) } Section(header: Text("Account")) { From dcb7102e58e61182628b51781f0035c3c0cd6895 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Wed, 30 Sep 2020 18:35:45 +0200 Subject: [PATCH 18/30] Fixes --- Packages/UI/Package.swift | 2 +- Packages/UI/Sources/UI/RecursiveView.swift | 11 ++++++++--- RedditOs/Features/Comments/CommentRow.swift | 20 +++++++++++++++----- RedditOs/Features/Post/PostDetailView.swift | 1 - 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/Packages/UI/Package.swift b/Packages/UI/Package.swift index 9232011..3fa773a 100644 --- a/Packages/UI/Package.swift +++ b/Packages/UI/Package.swift @@ -6,7 +6,7 @@ import PackageDescription let package = Package( name: "UI", platforms: [ - .macOS("10.16"), .iOS("14"), .tvOS("14"), .watchOS("7") + .macOS("11"), .iOS("14"), .tvOS("14"), .watchOS("7") ], products: [ .library( diff --git a/Packages/UI/Sources/UI/RecursiveView.swift b/Packages/UI/Sources/UI/RecursiveView.swift index a6fcab0..6d69bb7 100644 --- a/Packages/UI/Sources/UI/RecursiveView.swift +++ b/Packages/UI/Sources/UI/RecursiveView.swift @@ -48,9 +48,14 @@ struct CustomDisclosureGroup: View where Label: View, Content: V var label: () -> Label var body: some View { - label() - .onTapGesture { - isExpanded .toggle() + HStack(alignment: .top, spacing: 8) { + Image(systemName: "chevron.right") + .rotationEffect(isExpanded ? .degrees(90) : .degrees(0)) + .padding(.top, 4) + .onTapGesture { + isExpanded.toggle() + } + label() } if isExpanded { content() diff --git a/RedditOs/Features/Comments/CommentRow.swift b/RedditOs/Features/Comments/CommentRow.swift index de524fa..1a08ca0 100644 --- a/RedditOs/Features/Comments/CommentRow.swift +++ b/RedditOs/Features/Comments/CommentRow.swift @@ -33,9 +33,14 @@ struct CommentRow: View { } else { Image(systemName: "person") } - Text(viewModel.comment.author ?? "Unknown") - .font(.callout) - .fontWeight(.bold) + if let author = viewModel.comment.author { + Text(author) + .font(.callout) + .fontWeight(.bold) + } else { + Text("Deleted user") + .font(.footnote) + } } if let score = viewModel.comment.score { Text(" · \(score.toRoundedSuffixAsString()) points · ") @@ -51,8 +56,13 @@ struct CommentRow: View { AwardsView(awards: awards).padding(.leading, 8) } } - Text(viewModel.comment.body ?? "No comment content") - .font(.body) + if let body = viewModel.comment.body { + Text(body).font(.body) + } else { + Text("Deleted comment") + .font(.footnote) + .foregroundColor(.gray) + } CommentActionsView(viewModel: viewModel) .foregroundColor(.gray) Divider() diff --git a/RedditOs/Features/Post/PostDetailView.swift b/RedditOs/Features/Post/PostDetailView.swift index 253cebd..e980a82 100644 --- a/RedditOs/Features/Post/PostDetailView.swift +++ b/RedditOs/Features/Post/PostDetailView.swift @@ -30,7 +30,6 @@ struct PostDetailView: View { }.padding(.bottom, 16) PostDetailCommentsSection(viewModel: viewModel) } - .animation(nil) .onAppear(perform: viewModel.fechComments) .onAppear(perform: viewModel.postVisit) .onAppear(perform: { From a2bcd59a59a0afc4c3acb66bac80087c242b98ed Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Thu, 1 Oct 2020 12:08:34 +0200 Subject: [PATCH 19/30] Various improvements + update KinfFisher --- RedditOs.xcodeproj/project.pbxproj | 8 ++--- .../xcshareddata/swiftpm/Package.resolved | 12 ++++---- RedditOs/Features/Comments/CommentRow.swift | 29 +++++++++++++------ .../Features/Users/sheet/UserSheetView.swift | 24 ++++++++------- RedditOs/RedditOsApp.swift | 2 +- RedditOs/Shared/PostsListView.swift | 1 - 6 files changed, 44 insertions(+), 32 deletions(-) diff --git a/RedditOs.xcodeproj/project.pbxproj b/RedditOs.xcodeproj/project.pbxproj index 52507e2..7d32615 100644 --- a/RedditOs.xcodeproj/project.pbxproj +++ b/RedditOs.xcodeproj/project.pbxproj @@ -634,7 +634,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 10; + CURRENT_PROJECT_VERSION = 25; DEVELOPMENT_ASSET_PATHS = "\"RedditOs/Preview Content\""; DEVELOPMENT_TEAM = Z6P74P6T99; ENABLE_HARDENED_RUNTIME = YES; @@ -645,7 +645,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 0.0.3; + MARKETING_VERSION = 0.1; PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.curiosity; PRODUCT_NAME = Curiosity; SWIFT_VERSION = 5.0; @@ -661,7 +661,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 10; + CURRENT_PROJECT_VERSION = 25; DEVELOPMENT_ASSET_PATHS = "\"RedditOs/Preview Content\""; DEVELOPMENT_TEAM = Z6P74P6T99; ENABLE_HARDENED_RUNTIME = YES; @@ -672,7 +672,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 0.0.3; + MARKETING_VERSION = 0.1; PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.curiosity; PRODUCT_NAME = Curiosity; SWIFT_VERSION = 5.0; diff --git a/RedditOs.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/RedditOs.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 6428711..ccc62d3 100644 --- a/RedditOs.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/RedditOs.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -15,8 +15,8 @@ "repositoryURL": "https://github.com/kishikawakatsumi/KeychainAccess", "state": { "branch": null, - "revision": "3d0ea2c0806791abcc5d7f0d9f62f1cfd4a7264d", - "version": "4.2.0" + "revision": "654d52d30f3dd4592e944c3e0bccb53178c992f6", + "version": "4.2.1" } }, { @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/onevcat/Kingfisher", "state": { "branch": null, - "revision": "175eeb4618b0a6ef4d69a7409b6a74ddd235a093", - "version": "5.15.0" + "revision": "2a6d1135af3915547c4b08c3b154a05e6f1075a3", + "version": "5.15.5" } }, { @@ -33,8 +33,8 @@ "repositoryURL": "https://github.com/dasautoooo/Parma", "state": { "branch": null, - "revision": "d620d6e8fdd59cd0a8110a08f4202b9eed5178aa", - "version": "0.1.0" + "revision": "82f825cead4b408fa3e0059affa3e5d296d3b745", + "version": "0.1.1" } } ] diff --git a/RedditOs/Features/Comments/CommentRow.swift b/RedditOs/Features/Comments/CommentRow.swift index 1a08ca0..7ab72ca 100644 --- a/RedditOs/Features/Comments/CommentRow.swift +++ b/RedditOs/Features/Comments/CommentRow.swift @@ -10,6 +10,7 @@ import Backend struct CommentRow: View { @StateObject private var viewModel: CommentViewModel + @State private var showUserPopover = false init(comment: Comment) { _viewModel = StateObject(wrappedValue: CommentViewModel(comment: comment)) @@ -27,16 +28,26 @@ struct CommentRow: View { backgroundColorHex: viewModel.comment.authorFlairBackgroundColor, display: .small) } - if viewModel.comment.isSubmitter == true { - Image(systemName: "music.mic") - .foregroundColor(.redditBlue) - } else { - Image(systemName: "person") - } if let author = viewModel.comment.author { - Text(author) - .font(.callout) - .fontWeight(.bold) + Button(action: { + showUserPopover = true + }, label: { + HStack(spacing: 4) { + if viewModel.comment.isSubmitter == true { + Image(systemName: "music.mic") + .foregroundColor(.redditBlue) + } else { + Image(systemName: "person") + } + Text(author) + .font(.callout) + .fontWeight(.bold) + }.foregroundColor(.white) + }) + .buttonStyle(BorderlessButtonStyle()) + .popover(isPresented: $showUserPopover, content: { + UserPopoverView(username: author) + }) } else { Text("Deleted user") .font(.footnote) diff --git a/RedditOs/Features/Users/sheet/UserSheetView.swift b/RedditOs/Features/Users/sheet/UserSheetView.swift index fb99d38..d2e24b8 100644 --- a/RedditOs/Features/Users/sheet/UserSheetView.swift +++ b/RedditOs/Features/Users/sheet/UserSheetView.swift @@ -24,15 +24,6 @@ struct UserSheetView: View { var body: some View { NavigationView { List(selection: $sidebarSelection) { - Button { - presentation.wrappedValue.dismiss() - } label: { - Label("Close", systemImage: "xmark.circle") - .foregroundColor(.accentColor) - } - .buttonStyle(BorderlessButtonStyle()) - .tag(0) - Section(header: Text(viewModel.username)) { Label("Overview", systemImage: "square.and.pencil").tag(1) Label("Posts", systemImage: "square.and.pencil").tag(2) @@ -40,7 +31,8 @@ struct UserSheetView: View { Label("Awards", systemImage: "rosette").tag(4) }.accentColor(.redditBlue) - }.listStyle(SidebarListStyle()) + } + .listStyle(SidebarListStyle()) NavigationView { if sidebarSelection.first == 1 { @@ -57,7 +49,17 @@ struct UserSheetView: View { PostNoSelectionPlaceholder() } - }.frame(width: 1500, height: 700) + } + .frame(width: 1500, height: 700) + .toolbar { + ToolbarItem(placement: .confirmationAction) { + Button { + presentation.wrappedValue.dismiss() + } label: { + Text("Close") + } + } + } } } diff --git a/RedditOs/RedditOsApp.swift b/RedditOs/RedditOsApp.swift index 6549cab..7a38940 100644 --- a/RedditOs/RedditOsApp.swift +++ b/RedditOs/RedditOsApp.swift @@ -20,7 +20,7 @@ struct RedditOsApp: App { NavigationView { SidebarView() } - .frame(minHeight: 400, idealHeight: 800) + .frame(minWidth: 1300, minHeight: 800) .environmentObject(localData) .environmentObject(OauthClient.shared) .environmentObject(CurrentUserStore.shared) diff --git a/RedditOs/Shared/PostsListView.swift b/RedditOs/Shared/PostsListView.swift index 5877782..45b8b3b 100644 --- a/RedditOs/Shared/PostsListView.swift +++ b/RedditOs/Shared/PostsListView.swift @@ -28,7 +28,6 @@ struct PostsListView: View { } } } - .animation(nil) .listStyle(InsetListStyle()) .frame(width: 500) } From 42c0021d9a16a2df686ff0efee3d9b7667d2bd4d Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Tue, 6 Oct 2020 11:47:29 +0200 Subject: [PATCH 20/30] Color fixes --- RedditOs/Features/Comments/CommentRow.swift | 2 +- RedditOs/Features/Subreddit/SubredditPostsListView.swift | 1 - RedditOs/Shared/PostInfoView.swift | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/RedditOs/Features/Comments/CommentRow.swift b/RedditOs/Features/Comments/CommentRow.swift index 7ab72ca..c84ba20 100644 --- a/RedditOs/Features/Comments/CommentRow.swift +++ b/RedditOs/Features/Comments/CommentRow.swift @@ -42,7 +42,7 @@ struct CommentRow: View { Text(author) .font(.callout) .fontWeight(.bold) - }.foregroundColor(.white) + } }) .buttonStyle(BorderlessButtonStyle()) .popover(isPresented: $showUserPopover, content: { diff --git a/RedditOs/Features/Subreddit/SubredditPostsListView.swift b/RedditOs/Features/Subreddit/SubredditPostsListView.swift index 53b16a5..9a3c055 100644 --- a/RedditOs/Features/Subreddit/SubredditPostsListView.swift +++ b/RedditOs/Features/Subreddit/SubredditPostsListView.swift @@ -57,7 +57,6 @@ struct SubredditPostsListView: View { .frame(width: 20, height: 20) .cornerRadius(10) } else { - Image(systemName: "globe") .resizable() .frame(width: 20, height: 20) diff --git a/RedditOs/Shared/PostInfoView.swift b/RedditOs/Shared/PostInfoView.swift index 21f3fa8..b388d0d 100644 --- a/RedditOs/Shared/PostInfoView.swift +++ b/RedditOs/Shared/PostInfoView.swift @@ -41,7 +41,6 @@ struct PostInfoView: View { }, label: { Text("r/\(post.subreddit)") .fontWeight(.bold) - .foregroundColor(.white) }) .buttonStyle(BorderlessButtonStyle()) From ab54c18eac037e8cc047e24a7cb3e82780e0bf66 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Fri, 13 Nov 2020 13:07:00 +0100 Subject: [PATCH 21/30] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 76560f8..378adf2 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ # RedditOS A SwiftUI Reddit client for macOS -This is bleeding edge, you need macOS Big Sur beta and Xcode 12 beta. +You'll need Xcode 12 and macOS Big Sur 12 to build & run the app. +This is quite bleeding edge, the performances are not quite there yet as SwiftUI higher order components on macOS (like `List` and `NavigationView`) are not really very smooth yet. I hope it'll improve during Big Sur update cycle and I exept a big boost with SwiftUI 3 next summer. (One can hope) +But I'll continue to work on this applicatio, add features, optimize it and eventually release it on the Mac App Store. ![Image](Images/image1.png?) If you want to login with your Reddit account building the project from the source you'll need to create a file `secrets.plist` in `Packages/Backend/Sources/Backend/Resources` with your Reddit app secret as `client_id` key/value. I'll periodically release pre built version of the app in the repository. -The app is still early and SwiftUI on macOS is not that fast (yet) but I'm planning to release it on the Mac App Store with the release of Big Sur. \ No newline at end of file +The app is still early and SwiftUI on macOS is not that fast (yet) but I'm planning to release it on the Mac App Store with the release of Big Sur. From 914cd1b8097e77cfd13230d3507fafc313fab5a0 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Tue, 1 Dec 2020 14:20:21 +0100 Subject: [PATCH 22/30] Fix tests --- RedditOs/Environements/UIState.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RedditOs/Environements/UIState.swift b/RedditOs/Environements/UIState.swift index 0ed7362..2db5e9c 100644 --- a/RedditOs/Environements/UIState.swift +++ b/RedditOs/Environements/UIState.swift @@ -33,10 +33,10 @@ class UIState: ObservableObject { @Published var presentedNavigationRoute: Route? { didSet { if let route = presentedNavigationRoute { - sidebarSelection = [route.id] + sidebarSelection = route.id } } } - @Published var sidebarSelection: Set = [DefaultChannels.hot.rawValue] + @Published var sidebarSelection: String? = DefaultChannels.hot.rawValue } From da6c20168af480e82cd0cab33e0622da6cc7544d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dar=C3=ADo=20Here=C3=B1=C3=BA?= Date: Wed, 18 Nov 2020 21:55:13 -0300 Subject: [PATCH 23/30] Fixed typo on line 07 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 378adf2..73c284b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A SwiftUI Reddit client for macOS You'll need Xcode 12 and macOS Big Sur 12 to build & run the app. This is quite bleeding edge, the performances are not quite there yet as SwiftUI higher order components on macOS (like `List` and `NavigationView`) are not really very smooth yet. I hope it'll improve during Big Sur update cycle and I exept a big boost with SwiftUI 3 next summer. (One can hope) -But I'll continue to work on this applicatio, add features, optimize it and eventually release it on the Mac App Store. +But I'll continue to work on this application, add features, optimize it and eventually release it on the Mac App Store. ![Image](Images/image1.png?) If you want to login with your Reddit account building the project from the source you'll need to create a file `secrets.plist` in `Packages/Backend/Sources/Backend/Resources` with your Reddit app secret as `client_id` key/value. From 840810b6ffa5d938ee03544165100dac0a925f5e Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Wed, 16 Dec 2020 14:53:37 +0100 Subject: [PATCH 24/30] Various fixes --- RedditOs.xcodeproj/project.pbxproj | 25 +++---------------- .../xcshareddata/swiftpm/Package.resolved | 18 ------------- RedditOs/Environements/Route.swift | 12 ++++----- RedditOs/Environements/UIState.swift | 7 +++++- RedditOs/Features/Comments/CommentRow.swift | 4 ++- .../Features/Post/PostDetailContent.swift | 4 ++- RedditOs/Features/Profile/ProfileView.swift | 4 ++- .../GlobalSearchPopoverView.swift | 4 +-- .../Features/Search/ToolbarSearchBar.swift | 2 +- .../Subreddit/SubredditPostsListView.swift | 5 +++- .../Users/popover/UserPopoverView.swift | 4 +-- RedditOs/RedditOsApp.swift | 2 +- RedditOs/Shared/PostInfoView.swift | 2 +- 13 files changed, 36 insertions(+), 57 deletions(-) diff --git a/RedditOs.xcodeproj/project.pbxproj b/RedditOs.xcodeproj/project.pbxproj index 7d32615..6c9da07 100644 --- a/RedditOs.xcodeproj/project.pbxproj +++ b/RedditOs.xcodeproj/project.pbxproj @@ -47,7 +47,6 @@ 697E324924E3EDE70006F00F /* CommentVoteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 697E324824E3EDE70006F00F /* CommentVoteView.swift */; }; 697E324B24E3EFCB0006F00F /* CommentActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 697E324A24E3EFCB0006F00F /* CommentActionsView.swift */; }; 697E324D24E3F2900006F00F /* SharingPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 697E324C24E3F2900006F00F /* SharingPicker.swift */; }; - 69C1925A24EC0FEB00BA3C09 /* Parma in Frameworks */ = {isa = PBXBuildFile; productRef = 69C1925924EC0FEB00BA3C09 /* Parma */; }; 69CCB3EA24E2BEAC003FAAD7 /* SubredditAboutPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69CCB3E924E2BEAC003FAAD7 /* SubredditAboutPopoverView.swift */; }; 69D076C824B9E871001619AC /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69D076C724B9E871001619AC /* Color.swift */; }; 69D8663424E568060052A2B0 /* Route.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69D8663324E568060052A2B0 /* Route.swift */; }; @@ -129,7 +128,6 @@ buildActionMask = 2147483647; files = ( 697E324524E3E7D90006F00F /* UI in Frameworks */, - 69C1925A24EC0FEB00BA3C09 /* Parma in Frameworks */, 69EACF1C24B7272E00303A16 /* Backend in Frameworks */, 6923F8CD250250FC0003870F /* KingfisherSwiftUI in Frameworks */, ); @@ -395,7 +393,6 @@ packageProductDependencies = ( 69EACF1B24B7272E00303A16 /* Backend */, 697E324424E3E7D90006F00F /* UI */, - 69C1925924EC0FEB00BA3C09 /* Parma */, 6923F8CC250250FC0003870F /* KingfisherSwiftUI */, ); productName = RedditOs; @@ -426,7 +423,6 @@ ); mainGroup = 69EACEF624B63D5800303A16; packageReferences = ( - 69C1925824EC0FEB00BA3C09 /* XCRemoteSwiftPackageReference "Parma" */, 6923F8CB250250FC0003870F /* XCRemoteSwiftPackageReference "Kingfisher" */, ); productRefGroup = 69EACF0024B63D5800303A16 /* Products */; @@ -634,7 +630,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 25; + CURRENT_PROJECT_VERSION = 15122020; DEVELOPMENT_ASSET_PATHS = "\"RedditOs/Preview Content\""; DEVELOPMENT_TEAM = Z6P74P6T99; ENABLE_HARDENED_RUNTIME = YES; @@ -645,7 +641,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 0.1; + MARKETING_VERSION = 0.2; PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.curiosity; PRODUCT_NAME = Curiosity; SWIFT_VERSION = 5.0; @@ -661,7 +657,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 25; + CURRENT_PROJECT_VERSION = 15122020; DEVELOPMENT_ASSET_PATHS = "\"RedditOs/Preview Content\""; DEVELOPMENT_TEAM = Z6P74P6T99; ENABLE_HARDENED_RUNTIME = YES; @@ -672,7 +668,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 0.1; + MARKETING_VERSION = 0.2; PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.curiosity; PRODUCT_NAME = Curiosity; SWIFT_VERSION = 5.0; @@ -711,14 +707,6 @@ minimumVersion = 5.15.0; }; }; - 69C1925824EC0FEB00BA3C09 /* XCRemoteSwiftPackageReference "Parma" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/dasautoooo/Parma"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 0.1.0; - }; - }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -731,11 +719,6 @@ isa = XCSwiftPackageProductDependency; productName = UI; }; - 69C1925924EC0FEB00BA3C09 /* Parma */ = { - isa = XCSwiftPackageProductDependency; - package = 69C1925824EC0FEB00BA3C09 /* XCRemoteSwiftPackageReference "Parma" */; - productName = Parma; - }; 69EACF1B24B7272E00303A16 /* Backend */ = { isa = XCSwiftPackageProductDependency; productName = Backend; diff --git a/RedditOs.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/RedditOs.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index ccc62d3..f863fbd 100644 --- a/RedditOs.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/RedditOs.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,15 +1,6 @@ { "object": { "pins": [ - { - "package": "Down", - "repositoryURL": "https://github.com/iwasrobbed/Down", - "state": { - "branch": null, - "revision": "427aec0a0ab342246ec02369dea398597b61174a", - "version": "0.9.3" - } - }, { "package": "KeychainAccess", "repositoryURL": "https://github.com/kishikawakatsumi/KeychainAccess", @@ -27,15 +18,6 @@ "revision": "2a6d1135af3915547c4b08c3b154a05e6f1075a3", "version": "5.15.5" } - }, - { - "package": "Parma", - "repositoryURL": "https://github.com/dasautoooo/Parma", - "state": { - "branch": null, - "revision": "82f825cead4b408fa3e0059affa3e5d296d3b745", - "version": "0.1.1" - } } ] }, diff --git a/RedditOs/Environements/Route.swift b/RedditOs/Environements/Route.swift index 11e7f55..77fb7f3 100644 --- a/RedditOs/Environements/Route.swift +++ b/RedditOs/Environements/Route.swift @@ -19,8 +19,8 @@ enum Route: Identifiable, Hashable { hasher.combine(id) } - case user(user: User) - case subreddit(subreddit: String) + case user(user: User, isSheet: Bool) + case subreddit(subreddit: String, isSheet: Bool) case defaultChannel(chanel: UIState.DefaultChannels) case none @@ -28,7 +28,7 @@ enum Route: Identifiable, Hashable { switch self { case .user: return "user" - case let .subreddit(subreddit): + case let .subreddit(subreddit, _): return subreddit case .none: return "none" @@ -40,10 +40,10 @@ enum Route: Identifiable, Hashable { @ViewBuilder func makeView() -> some View { switch self { - case let .user(user): + case let .user(user, _): UserSheetView(user: user) - case let .subreddit(subreddit): - SubredditPostsListView(name: subreddit) + case let .subreddit(subreddit, isSheet): + SubredditPostsListView(name: subreddit, isSheet: isSheet).environmentObject(UIState.shared) case let .defaultChannel(chanel): SubredditPostsListView(name: chanel.rawValue) case .none: diff --git a/RedditOs/Environements/UIState.swift b/RedditOs/Environements/UIState.swift index 2db5e9c..ad85b7e 100644 --- a/RedditOs/Environements/UIState.swift +++ b/RedditOs/Environements/UIState.swift @@ -11,6 +11,8 @@ import Combine import Backend class UIState: ObservableObject { + public static let shared = UIState() + enum DefaultChannels: String, CaseIterable { case hot, best, new, top, rising @@ -24,7 +26,10 @@ class UIState: ObservableObject { } } } - + + private init() { + + } @Published var selectedSubreddit: SubredditViewModel? @Published var selectedPost: PostViewModel? diff --git a/RedditOs/Features/Comments/CommentRow.swift b/RedditOs/Features/Comments/CommentRow.swift index c84ba20..d68e2bd 100644 --- a/RedditOs/Features/Comments/CommentRow.swift +++ b/RedditOs/Features/Comments/CommentRow.swift @@ -68,7 +68,9 @@ struct CommentRow: View { } } if let body = viewModel.comment.body { - Text(body).font(.body) + Text(body) + .font(.body) + .fixedSize(horizontal: false, vertical: true) } else { Text("Deleted comment") .font(.footnote) diff --git a/RedditOs/Features/Post/PostDetailContent.swift b/RedditOs/Features/Post/PostDetailContent.swift index f5bfff7..6fdff8f 100644 --- a/RedditOs/Features/Post/PostDetailContent.swift +++ b/RedditOs/Features/Post/PostDetailContent.swift @@ -17,7 +17,9 @@ struct PostDetailContent: View { @ViewBuilder var body: some View { if let text = listing.selftext ?? listing.description { - Text(text).font(.body) + Text(text) + .font(.body) + .fixedSize(horizontal: false, vertical: true) } if let video = listing.secureMedia?.video { HStack { diff --git a/RedditOs/Features/Profile/ProfileView.swift b/RedditOs/Features/Profile/ProfileView.swift index 2942cc3..1ea45ab 100644 --- a/RedditOs/Features/Profile/ProfileView.swift +++ b/RedditOs/Features/Profile/ProfileView.swift @@ -22,7 +22,9 @@ struct ProfileView: View { if currentUser.user != nil { userOverview } - }.listStyle(InsetListStyle()) + } + .listStyle(InsetListStyle()) + .frame(width: 500) PostNoSelectionPlaceholder() } diff --git a/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift b/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift index a185d5b..3fdbda0 100644 --- a/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift +++ b/RedditOs/Features/Search/Global Search Popopver/GlobalSearchPopoverView.swift @@ -45,7 +45,7 @@ struct GlobalSearchPopoverView: View { GlobalSearchSubRow(icon: nil, name: "Go to r/\(viewModel.searchText)") .onTapGesture { - uiState.presentedNavigationRoute = .subreddit(subreddit: viewModel.searchText) + uiState.presentedNavigationRoute = .subreddit(subreddit: viewModel.searchText, isSheet: false) } GlobalSearchSubRow(icon: nil, name: "Go to u/\(viewModel.searchText)") @@ -85,7 +85,7 @@ struct GlobalSearchPopoverView: View { private func makeSubRow(icon: String?, name: String) -> some View { GlobalSearchSubRow(icon: icon, name: name) .onTapGesture { - uiState.presentedNavigationRoute = .subreddit(subreddit: name) + uiState.presentedNavigationRoute = .subreddit(subreddit: name, isSheet: false) } } } diff --git a/RedditOs/Features/Search/ToolbarSearchBar.swift b/RedditOs/Features/Search/ToolbarSearchBar.swift index 48bdaa1..bf8f046 100644 --- a/RedditOs/Features/Search/ToolbarSearchBar.swift +++ b/RedditOs/Features/Search/ToolbarSearchBar.swift @@ -16,7 +16,7 @@ struct ToolbarSearchBar: View { TextField("Search anything", text: $searchViewModel.searchText) { editing in isFocused = editing } onCommit: { - uiState.presentedNavigationRoute = .subreddit(subreddit: searchViewModel.searchText) + uiState.presentedNavigationRoute = .subreddit(subreddit: searchViewModel.searchText, isSheet: false) } .keyboardShortcut("f", modifiers: .command) .padding(8) diff --git a/RedditOs/Features/Subreddit/SubredditPostsListView.swift b/RedditOs/Features/Subreddit/SubredditPostsListView.swift index 9a3c055..08e9a01 100644 --- a/RedditOs/Features/Subreddit/SubredditPostsListView.swift +++ b/RedditOs/Features/Subreddit/SubredditPostsListView.swift @@ -13,6 +13,7 @@ import KingfisherSwiftUI struct SubredditPostsListView: View { let posts = Array(repeating: 0, count: 20) + private let isSheet: Bool private let loadingPlaceholders = Array(repeating: static_listing, count: 10) @EnvironmentObject private var uiState: UIState @@ -23,7 +24,8 @@ struct SubredditPostsListView: View { @State private var subredditAboutPopoverShown = false - init(name: String) { + init(name: String, isSheet: Bool = false) { + self.isSheet = isSheet _viewModel = StateObject(wrappedValue: SubredditViewModel(name: name)) } @@ -103,6 +105,7 @@ struct SubredditPostsListView: View { } .navigationTitle(viewModel.name.capitalized) .navigationSubtitle(subtitle) + .frame(minHeight: 600) .onAppear { if !isDefaultChannel { viewModel.fetchAbout() diff --git a/RedditOs/Features/Users/popover/UserPopoverView.swift b/RedditOs/Features/Users/popover/UserPopoverView.swift index 1591af2..b451eae 100644 --- a/RedditOs/Features/Users/popover/UserPopoverView.swift +++ b/RedditOs/Features/Users/popover/UserPopoverView.swift @@ -29,7 +29,7 @@ struct UserPopoverView: View { Button(action: { if let user = viewModel.user { presentation.wrappedValue.dismiss() - uiState.presentedSheetRoute = .user(user: user) + uiState.presentedSheetRoute = .user(user: user, isSheet: true) } }) { Text("View full profile") @@ -45,6 +45,6 @@ struct UserPopoverView: View { struct UserPopoverView_Previews: PreviewProvider { static var previews: some View { - UserPopoverView(username: "").environmentObject(UIState()) + UserPopoverView(username: "").environmentObject(UIState.shared) } } diff --git a/RedditOs/RedditOsApp.swift b/RedditOs/RedditOsApp.swift index 7a38940..5640058 100644 --- a/RedditOs/RedditOsApp.swift +++ b/RedditOs/RedditOsApp.swift @@ -11,7 +11,7 @@ import Backend @main struct RedditOsApp: App { - @StateObject private var uiState = UIState() + @StateObject private var uiState = UIState.shared @StateObject private var localData = LocalDataStore() @SceneBuilder diff --git a/RedditOs/Shared/PostInfoView.swift b/RedditOs/Shared/PostInfoView.swift index b388d0d..bf196bb 100644 --- a/RedditOs/Shared/PostInfoView.swift +++ b/RedditOs/Shared/PostInfoView.swift @@ -37,7 +37,7 @@ struct PostInfoView: View { var content: some View { HStack(spacing: 6) { Button(action: { - uiState.presentedNavigationRoute = .subreddit(subreddit: post.subreddit) + uiState.presentedSheetRoute = .subreddit(subreddit: post.subreddit, isSheet: true) }, label: { Text("r/\(post.subreddit)") .fontWeight(.bold) From 79368dd0873c7f2d2b891e4d4ad7a85790268434 Mon Sep 17 00:00:00 2001 From: Radu Ursache <3800336+rursache@users.noreply.github.com> Date: Sat, 5 Dec 2020 23:31:09 +0200 Subject: [PATCH 25/30] added clear instructions of how to add secrets.plist --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 73c284b..5c6b83b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This is quite bleeding edge, the performances are not quite there yet as SwiftUI But I'll continue to work on this application, add features, optimize it and eventually release it on the Mac App Store. ![Image](Images/image1.png?) -If you want to login with your Reddit account building the project from the source you'll need to create a file `secrets.plist` in `Packages/Backend/Sources/Backend/Resources` with your Reddit app secret as `client_id` key/value. +If you want to login with your Reddit account building the project from the source you'll need to create a file `secrets.plist` in `Packages/Backend/Sources/Backend/Resources` with your Reddit app id as `client_id` key/value. Create an reddit app (here)[https://www.reddit.com/prefs/apps] and use `redditos://auth` as redirect url. I'll periodically release pre built version of the app in the repository. From 2b37ee456f9dedda619eaf279f05c5f07745c413 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Thu, 31 Dec 2020 13:33:41 +0100 Subject: [PATCH 26/30] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c6b83b..5168441 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This is quite bleeding edge, the performances are not quite there yet as SwiftUI But I'll continue to work on this application, add features, optimize it and eventually release it on the Mac App Store. ![Image](Images/image1.png?) -If you want to login with your Reddit account building the project from the source you'll need to create a file `secrets.plist` in `Packages/Backend/Sources/Backend/Resources` with your Reddit app id as `client_id` key/value. Create an reddit app (here)[https://www.reddit.com/prefs/apps] and use `redditos://auth` as redirect url. +If you want to login with your Reddit account building the project from the source you'll need to create a file `secrets.plist` in `Packages/Backend/Sources/Backend/Resources` with your Reddit app id as `client_id` key/value. Create an reddit app [here](https://www.reddit.com/prefs/apps) and use `redditos://auth` as redirect url. I'll periodically release pre built version of the app in the repository. From 46aa0e0cd913b59da60149b4c6ddef77b5cfff6e Mon Sep 17 00:00:00 2001 From: Dan Korkelia Date: Fri, 1 Jan 2021 18:05:47 +0000 Subject: [PATCH 27/30] Introduce url extension to help avoid force unwrapping for static urls and add unit test for changed model --- .../Backend/Extensions/URL+StaticString.swift | 19 +++++++++++++++++++ .../Sources/Backend/Models/Award.swift | 15 +++++++-------- .../BackendTests/Models/AwardTests.swift | 15 +++++++++++++++ RedditOs.xcodeproj/project.pbxproj | 4 ++-- RedditOs/Shared/AwardView.swift | 6 +++--- 5 files changed, 46 insertions(+), 13 deletions(-) create mode 100644 Packages/Backend/Sources/Backend/Extensions/URL+StaticString.swift create mode 100644 Packages/Backend/Tests/BackendTests/Models/AwardTests.swift diff --git a/Packages/Backend/Sources/Backend/Extensions/URL+StaticString.swift b/Packages/Backend/Sources/Backend/Extensions/URL+StaticString.swift new file mode 100644 index 0000000..3740fac --- /dev/null +++ b/Packages/Backend/Sources/Backend/Extensions/URL+StaticString.swift @@ -0,0 +1,19 @@ +// +// URL+StaticString.swift +// +// +// Created by Dan Korkelia on 01/01/2021. +// + +import Foundation + +extension URL { + /// Use this init for static URL strings to avoid using force unwrap or doing redundant error handling + /// - Parameter string: static url ie https://www.example.com/privacy/ + init(_ string: StaticString) { + guard let url = URL(string: "\(string)") else { + fatalError("URL is illegal: \(string)") + } + self = url + } +} diff --git a/Packages/Backend/Sources/Backend/Models/Award.swift b/Packages/Backend/Sources/Backend/Models/Award.swift index 6d2bd1d..cfc05c8 100644 --- a/Packages/Backend/Sources/Backend/Models/Award.swift +++ b/Packages/Backend/Sources/Backend/Models/Award.swift @@ -14,12 +14,11 @@ public struct Award: Decodable, Identifiable { public let description: String public let count: Int public let coinPrice: Int + + public static let `default` = Award(id: "award", + name: "Awesome", + staticIconUrl: URL("https://i.redd.it/award_images/t5_22cerq/5smbysczm1w41_Hugz.png"), + description: "Awesome reward", + count: 5, + coinPrice: 200) } - - -public let static_award = Award(id: "award", - name: "Awesome", - staticIconUrl: URL(string: "https://i.redd.it/award_images/t5_22cerq/5smbysczm1w41_Hugz.png")!, - description: "Awesome reward", - count: 5, - coinPrice: 200) diff --git a/Packages/Backend/Tests/BackendTests/Models/AwardTests.swift b/Packages/Backend/Tests/BackendTests/Models/AwardTests.swift new file mode 100644 index 0000000..9a1c164 --- /dev/null +++ b/Packages/Backend/Tests/BackendTests/Models/AwardTests.swift @@ -0,0 +1,15 @@ +import XCTest +@testable import Backend + +final class AwardTests: XCTestCase { + + func test_defaultValueAreCorrect() { + XCTAssertEqual(Award.default.id, "award") + XCTAssertEqual(Award.default.name, "Awesome") + XCTAssertEqual(Award.default.staticIconUrl, URL("https://i.redd.it/award_images/t5_22cerq/5smbysczm1w41_Hugz.png")) + XCTAssertEqual(Award.default.description, "Awesome reward") + XCTAssertEqual(Award.default.count, 5) + XCTAssertEqual(Award.default.coinPrice, 200) + } + +} diff --git a/RedditOs.xcodeproj/project.pbxproj b/RedditOs.xcodeproj/project.pbxproj index 6c9da07..de4f7d7 100644 --- a/RedditOs.xcodeproj/project.pbxproj +++ b/RedditOs.xcodeproj/project.pbxproj @@ -632,7 +632,7 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 15122020; DEVELOPMENT_ASSET_PATHS = "\"RedditOs/Preview Content\""; - DEVELOPMENT_TEAM = Z6P74P6T99; + DEVELOPMENT_TEAM = Z8XR48Z685; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = RedditOs/Info.plist; @@ -659,7 +659,7 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 15122020; DEVELOPMENT_ASSET_PATHS = "\"RedditOs/Preview Content\""; - DEVELOPMENT_TEAM = Z6P74P6T99; + DEVELOPMENT_TEAM = Z8XR48Z685; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = RedditOs/Info.plist; diff --git a/RedditOs/Shared/AwardView.swift b/RedditOs/Shared/AwardView.swift index 7e3183d..e092914 100644 --- a/RedditOs/Shared/AwardView.swift +++ b/RedditOs/Shared/AwardView.swift @@ -53,8 +53,8 @@ struct AwardsView: View { struct PostAwardsView_Previews: PreviewProvider { static var previews: some View { - AwardsView(awards: [static_award, static_award, - static_award, static_award, - static_award, static_award]) + AwardsView(awards: [Award.default, Award.default, + Award.default, Award.default, + Award.default, Award.default]) } } From aa2687f23682e273fccb6f2fb7541b50add62886 Mon Sep 17 00:00:00 2001 From: Dan Korkelia Date: Fri, 1 Jan 2021 18:11:35 +0000 Subject: [PATCH 28/30] Restore development team name in project file --- RedditOs.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RedditOs.xcodeproj/project.pbxproj b/RedditOs.xcodeproj/project.pbxproj index de4f7d7..6c9da07 100644 --- a/RedditOs.xcodeproj/project.pbxproj +++ b/RedditOs.xcodeproj/project.pbxproj @@ -632,7 +632,7 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 15122020; DEVELOPMENT_ASSET_PATHS = "\"RedditOs/Preview Content\""; - DEVELOPMENT_TEAM = Z8XR48Z685; + DEVELOPMENT_TEAM = Z6P74P6T99; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = RedditOs/Info.plist; @@ -659,7 +659,7 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 15122020; DEVELOPMENT_ASSET_PATHS = "\"RedditOs/Preview Content\""; - DEVELOPMENT_TEAM = Z8XR48Z685; + DEVELOPMENT_TEAM = Z6P74P6T99; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = RedditOs/Info.plist; From 81fd956336547624877d071a6e081af469d35384 Mon Sep 17 00:00:00 2001 From: Dan Korkelia Date: Fri, 1 Jan 2021 18:31:44 +0000 Subject: [PATCH 29/30] Update function signiture --- .../Sources/Backend/Extensions/URL+StaticString.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Packages/Backend/Sources/Backend/Extensions/URL+StaticString.swift b/Packages/Backend/Sources/Backend/Extensions/URL+StaticString.swift index 3740fac..d3a741e 100644 --- a/Packages/Backend/Sources/Backend/Extensions/URL+StaticString.swift +++ b/Packages/Backend/Sources/Backend/Extensions/URL+StaticString.swift @@ -10,9 +10,9 @@ import Foundation extension URL { /// Use this init for static URL strings to avoid using force unwrap or doing redundant error handling /// - Parameter string: static url ie https://www.example.com/privacy/ - init(_ string: StaticString) { - guard let url = URL(string: "\(string)") else { - fatalError("URL is illegal: \(string)") + init(_ staticString: StaticString) { + guard let url = URL(string: "\(staticString)") else { + fatalError("URL is illegal: \(staticString)") } self = url } From 31f9ad33f86d722778e9b95262baeb3fa495c991 Mon Sep 17 00:00:00 2001 From: Dan Korkelia Date: Fri, 1 Jan 2021 18:49:12 +0000 Subject: [PATCH 30/30] Improve call site usage --- .../Backend/Sources/Backend/Extensions/URL+StaticString.swift | 2 +- Packages/Backend/Sources/Backend/Models/Award.swift | 2 +- Packages/Backend/Tests/BackendTests/Models/AwardTests.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Packages/Backend/Sources/Backend/Extensions/URL+StaticString.swift b/Packages/Backend/Sources/Backend/Extensions/URL+StaticString.swift index d3a741e..1e257f3 100644 --- a/Packages/Backend/Sources/Backend/Extensions/URL+StaticString.swift +++ b/Packages/Backend/Sources/Backend/Extensions/URL+StaticString.swift @@ -10,7 +10,7 @@ import Foundation extension URL { /// Use this init for static URL strings to avoid using force unwrap or doing redundant error handling /// - Parameter string: static url ie https://www.example.com/privacy/ - init(_ staticString: StaticString) { + init(staticString: StaticString) { guard let url = URL(string: "\(staticString)") else { fatalError("URL is illegal: \(staticString)") } diff --git a/Packages/Backend/Sources/Backend/Models/Award.swift b/Packages/Backend/Sources/Backend/Models/Award.swift index cfc05c8..1273d42 100644 --- a/Packages/Backend/Sources/Backend/Models/Award.swift +++ b/Packages/Backend/Sources/Backend/Models/Award.swift @@ -17,7 +17,7 @@ public struct Award: Decodable, Identifiable { public static let `default` = Award(id: "award", name: "Awesome", - staticIconUrl: URL("https://i.redd.it/award_images/t5_22cerq/5smbysczm1w41_Hugz.png"), + staticIconUrl: URL(staticString: "https://i.redd.it/award_images/t5_22cerq/5smbysczm1w41_Hugz.png"), description: "Awesome reward", count: 5, coinPrice: 200) diff --git a/Packages/Backend/Tests/BackendTests/Models/AwardTests.swift b/Packages/Backend/Tests/BackendTests/Models/AwardTests.swift index 9a1c164..7256f4c 100644 --- a/Packages/Backend/Tests/BackendTests/Models/AwardTests.swift +++ b/Packages/Backend/Tests/BackendTests/Models/AwardTests.swift @@ -6,7 +6,7 @@ final class AwardTests: XCTestCase { func test_defaultValueAreCorrect() { XCTAssertEqual(Award.default.id, "award") XCTAssertEqual(Award.default.name, "Awesome") - XCTAssertEqual(Award.default.staticIconUrl, URL("https://i.redd.it/award_images/t5_22cerq/5smbysczm1w41_Hugz.png")) + XCTAssertEqual(Award.default.staticIconUrl, URL(staticString: "https://i.redd.it/award_images/t5_22cerq/5smbysczm1w41_Hugz.png")) XCTAssertEqual(Award.default.description, "Awesome reward") XCTAssertEqual(Award.default.count, 5) XCTAssertEqual(Award.default.coinPrice, 200)