Skip to content

Commit

Permalink
Multireddits: Added support for multireddits
Browse files Browse the repository at this point in the history
  • Loading branch information
Dimillian committed May 15, 2021
1 parent 337dc54 commit 7f1be93
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 93 deletions.
22 changes: 22 additions & 0 deletions Packages/Backend/Sources/Backend/Models/Multi.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Foundation

public struct Multi: Codable, Identifiable {
public struct Subreddit: Codable, Identifiable {
public var id: String {
name
}
public let name: String
}

public var id: String {
path
}

public let displayName: String
public let iconURL: String?
public let subreddits: [Subreddit]
public var subredditsAsName: String {
return subreddits.map{ $0.name }.joined(separator: "+")
}
public let path: String
}
4 changes: 3 additions & 1 deletion Packages/Backend/Sources/Backend/Network/Endpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public enum Endpoint {
case searchSubreddit
case comments(name: String, id: String)
case accessToken
case me, mineSubscriptions
case me, mineSubscriptions, mineMulti
case vote, visits, save, unsave
case userAbout(username: String)
case userOverview(usernmame: String)
Expand Down Expand Up @@ -37,6 +37,8 @@ public enum Endpoint {
return "api/v1/me"
case .mineSubscriptions:
return "subreddits/mine/subscriber"
case .mineMulti:
return "api/multi/mine"
case let .subredditAbout(name):
return "r/\(name)/about"
case .vote:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,11 @@ extension User {
.replaceError(with: ListingResponse(error: "error"))
.eraseToAnyPublisher()
}

public func fetchMulti() -> AnyPublisher<[ListingHolder<Multi>], Never> {
return API.shared.request(endpoint: .mineMulti)
.subscribe(on: DispatchQueue.global())
.catch { _ in Empty(completeImmediately: false) }
.eraseToAnyPublisher()
}
}
29 changes: 25 additions & 4 deletions Packages/Backend/Sources/Backend/User/CurrentUserStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@ public class CurrentUserStore: ObservableObject, PersistentDataStore {

@Published public private(set) var user: User? {
didSet {
persistData(data: SaveData(user: user,
subscriptions: subscriptions))
saveUser()
}
}

@Published public private(set) var subscriptions: [Subreddit] = [] {
didSet {
persistData(data: SaveData(user: user,
subscriptions: subscriptions))
saveUser()
}
}

@Published public private(set) var multi: [Multi] = [] {
didSet {
saveUser()
}
}

Expand All @@ -42,6 +46,7 @@ public class CurrentUserStore: ObservableObject, PersistentDataStore {
struct SaveData: Codable {
let user: User?
let subscriptions: [Subreddit]
let multi: [Multi]
}

public init() {
Expand All @@ -59,6 +64,7 @@ public class CurrentUserStore: ObservableObject, PersistentDataStore {
if !self.subscriptionFetched {
self.subscriptionFetched = true
self.fetchSubscription(after: nil)
self.fetchMulti()
}
}
default:
Expand All @@ -67,6 +73,12 @@ public class CurrentUserStore: ObservableObject, PersistentDataStore {
})
}

private func saveUser() {
persistData(data: .init(user: user,
subscriptions: subscriptions,
multi: multi))
}

private func refreshUser() {
let cancellable = User.fetchMe()?
.receive(on: DispatchQueue.main)
Expand Down Expand Up @@ -97,6 +109,15 @@ public class CurrentUserStore: ObservableObject, PersistentDataStore {
disposables.append(cancellable)
}

private func fetchMulti() {
let cancellable = user?.fetchMulti()
.receive(on: DispatchQueue.main)
.sink{ listings in
self.multi = listings.map{ $0.data }
}
disposables.append(cancellable)
}

public func fetchSaved(after: SubredditPost?) {
let cancellable = user?.fetchSaved(after: after)
.receive(on: DispatchQueue.main)
Expand Down
211 changes: 125 additions & 86 deletions RedditOs/Features/Sidebar/SidebarView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,92 +19,11 @@ struct SidebarView: View {

var body: some View {
List(selection: $uiState.sidebarSelection) {
Section {
NavigationLink(destination: SearchMainContentView(),
isActive: uiState.isSearchActive,
label: {
Label("Search", systemImage: "magnifyingglass")
})
.tag(UIState.Constants.searchTag)

ForEach(UIState.DefaultChannels.allCases, id: \.self) { item in
NavigationLink(destination:
SubredditPostsListView(name: item.rawValue)
.equatable()) {
Label(LocalizedStringKey(item.rawValue.capitalized), systemImage: item.icon())
}.tag(item.rawValue)
}.animation(nil)
}

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: subscriptionsHeader) {
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)
}
mainSection
accountSection
favoritesSection
subscriptionSection
multiSection
}
.animation(nil)
.listStyle(SidebarListStyle())
Expand Down Expand Up @@ -154,6 +73,126 @@ struct SidebarView: View {

}
}

private var mainSection: some View {
Section {
NavigationLink(destination: SearchMainContentView(),
isActive: uiState.isSearchActive,
label: {
Label("Search", systemImage: "magnifyingglass")
})
.tag(UIState.Constants.searchTag)

ForEach(UIState.DefaultChannels.allCases, id: \.self) { item in
NavigationLink(destination:
SubredditPostsListView(name: item.rawValue)
.equatable()) {
Label(LocalizedStringKey(item.rawValue.capitalized), systemImage: item.icon())
}.tag(item.rawValue)
}.animation(nil)
}
}

private var accountSection: some View {
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)
}

private var favoritesSection: some View {
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)
}

@ViewBuilder
private var subscriptionSection: some View {
if currentUser.user != nil, (!currentUser.subscriptions.isEmpty || currentUser.isRefreshingSubscriptions) {
Section(header: subscriptionsHeader) {
ForEach(currentUser.subscriptions) { 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)
}
}

@ViewBuilder
private var multiSection: some View {
if currentUser.user != nil && !currentUser.multi.isEmpty {
Section(header: Text("Multireddits")) {
ForEach(currentUser.multi) { multi in
DisclosureGroup {
ForEach(multi.subreddits) { subreddit in
NavigationLink(destination: SubredditPostsListView(name: subreddit.name)
.equatable()) {
Text(subreddit.name)
}
}
} label: {
NavigationLink(destination: SubredditPostsListView(name: multi.subredditsAsName,
customTitle: multi.displayName)
.equatable()) {
Text(multi.displayName)
}
}
}
}
}
}
}

struct Sidebar_Previews: PreviewProvider {
Expand Down
6 changes: 4 additions & 2 deletions RedditOs/Features/Subreddit/SubredditPostsListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct SubredditPostsListView: View, Equatable {
}

private let name: String
private let title: String?
private let loadingPlaceholders = Array(repeating: static_listing, count: 10)

@EnvironmentObject private var uiState: UIState
Expand All @@ -26,8 +27,9 @@ struct SubredditPostsListView: View, Equatable {

@State private var subredditAboutPopoverShown = false

init(name: String) {
init(name: String, customTitle: String? = nil) {
self.name = name
self.title = customTitle
_viewModel = StateObject(wrappedValue: SubredditViewModel(name: name))
}

Expand Down Expand Up @@ -105,7 +107,7 @@ struct SubredditPostsListView: View, Equatable {
}
}
}
.navigationTitle(viewModel.name.capitalized)
.navigationTitle(title ?? viewModel.name.capitalized)
.navigationSubtitle(subtitle)
.frame(minHeight: 500)
.onAppear {
Expand Down

0 comments on commit 7f1be93

Please sign in to comment.