Bookmarks support close #48

This commit is contained in:
Thomas Ricouard 2023-01-09 19:26:56 +01:00
parent 1c1ca7ba0f
commit 662f4be29d
8 changed files with 89 additions and 13 deletions

View file

@ -46,7 +46,8 @@ public struct AccountDetailView: View {
Picker("", selection: $viewModel.selectedTab) {
ForEach(isCurrentUser ? AccountDetailViewModel.Tab.currentAccountTabs : AccountDetailViewModel.Tab.accountTabs,
id: \.self) { tab in
Text(tab.title).tag(tab)
Image(systemName: tab.iconName)
.tag(tab)
}
}
.pickerStyle(.segmented)

View file

@ -15,24 +15,25 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
}
enum Tab: Int {
case statuses, favourites, followedTags, postsAndReplies, media, lists
case statuses, favourites, bookmarks, followedTags, postsAndReplies, media, lists
static var currentAccountTabs: [Tab] {
[.statuses, .favourites, .followedTags, .lists]
[.statuses, .favourites, .bookmarks, .followedTags, .lists]
}
static var accountTabs: [Tab] {
[.statuses, .postsAndReplies, .media]
}
var title: String {
var iconName: String {
switch self {
case .statuses: return "Posts"
case .favourites: return "Favorites"
case .followedTags: return "Tags"
case .postsAndReplies: return "Posts & Replies"
case .media: return "Media"
case .lists: return "Lists"
case .statuses: return "bubble.right"
case .favourites: return "star"
case .bookmarks: return "bookmark"
case .followedTags: return "tag"
case .postsAndReplies: return "bubble.left.and.bubble.right"
case .media: return "photo.on.rectangle.angled"
case .lists: return "list.bullet"
}
}
}
@ -61,7 +62,9 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
@Published var relationship: Relationshionship?
@Published var pinned: [Status] = []
@Published var favourites: [Status] = []
@Published var bookmarks: [Status] = []
private var favouritesNextPage: LinkHandler?
private var bookmarksNextPage: LinkHandler?
@Published var featuredTags: [FeaturedTag] = []
@Published var fields: [Account.Field] = []
@Published var familliarFollowers: [Account] = []
@ -145,6 +148,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
}
if isCurrentUser {
(favourites, favouritesNextPage) = try await client.getWithLink(endpoint: Accounts.favourites(sinceId: nil))
(bookmarks, bookmarksNextPage) = try await client.getWithLink(endpoint: Accounts.bookmarks(sinceId: nil))
}
reloadTabState()
} catch {
@ -175,6 +179,12 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
(newFavourites, favouritesNextPage) = try await client.getWithLink(endpoint: Accounts.favourites(sinceId: nextPageId))
favourites.append(contentsOf: newFavourites)
tabState = .statuses(statusesState: .display(statuses: favourites, nextPageState: .hasNextPage))
case .bookmarks:
guard let nextPageId = bookmarksNextPage?.maxId else { return }
let newBookmarks: [Status]
(newBookmarks, bookmarksNextPage) = try await client.getWithLink(endpoint: Accounts.bookmarks(sinceId: nextPageId))
bookmarks.append(contentsOf: newBookmarks)
tabState = .statuses(statusesState: .display(statuses: bookmarks, nextPageState: .hasNextPage))
case .followedTags, .lists:
break
}
@ -208,6 +218,9 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
case .favourites:
tabState = .statuses(statusesState: .display(statuses: favourites,
nextPageState: favouritesNextPage != nil ? .hasNextPage : .none))
case .bookmarks:
tabState = .statuses(statusesState: .display(statuses: bookmarks,
nextPageState: bookmarksNextPage != nil ? .hasNextPage : .none))
case .followedTags:
tabState = .followedTags
case .lists:

View file

@ -40,6 +40,7 @@ public protocol AnyStatus {
var favourited: Bool? { get }
var reblogged: Bool? { get }
var pinned: Bool? { get }
var bookmarked: Bool? { get }
var emojis: [Emoji] { get }
var url: URL? { get }
var application: Application? { get }
@ -71,6 +72,7 @@ public struct Status: AnyStatus, Codable, Identifiable {
public let favourited: Bool?
public let reblogged: Bool?
public let pinned: Bool?
public let bookmarked: Bool?
public let emojis: [Emoji]
public let url: URL?
public let application: Application?
@ -96,6 +98,7 @@ public struct Status: AnyStatus, Codable, Identifiable {
favourited: false,
reblogged: false,
pinned: false,
bookmarked: false,
emojis: [],
url: nil,
application: nil,
@ -130,6 +133,7 @@ public struct ReblogStatus: AnyStatus, Codable, Identifiable {
public let favourited: Bool?
public let reblogged: Bool?
public let pinned: Bool?
public let bookmarked: Bool?
public let emojis: [Emoji]
public let url: URL?
public var application: Application?

View file

@ -3,6 +3,7 @@ import Foundation
public enum Accounts: Endpoint {
case accounts(id: String)
case favourites(sinceId: String?)
case bookmarks(sinceId: String?)
case followedTags
case featuredTags(id: String)
case verifyCredentials
@ -27,6 +28,8 @@ public enum Accounts: Endpoint {
return "accounts/\(id)"
case .favourites:
return "favourites"
case .bookmarks:
return "bookmarks"
case .followedTags:
return "followed_tags"
case .featuredTags(let id):
@ -87,6 +90,9 @@ public enum Accounts: Endpoint {
case let .favourites(sinceId):
guard let sinceId else { return nil }
return [.init(name: "max_id", value: sinceId)]
case let .bookmarks(sinceId):
guard let sinceId else { return nil }
return [.init(name: "max_id", value: sinceId)]
default:
return nil
}

View file

@ -22,6 +22,8 @@ public enum Statuses: Endpoint {
case favouritedBy(id: String, maxId: String?)
case pin(id: String)
case unpin(id: String)
case bookmark(id: String)
case unbookmark(id: String)
public func path() -> String {
switch self {
@ -49,6 +51,10 @@ public enum Statuses: Endpoint {
return "statuses/\(id)/pin"
case let .unpin(id):
return "statuses/\(id)/unpin"
case let .bookmark(id):
return "statuses/\(id)/bookmark"
case let .unbookmark(id):
return "statuses/\(id)/unbookmark"
}
}

View file

@ -14,7 +14,7 @@ struct StatusActionsView: View {
@MainActor
enum Actions: CaseIterable {
case respond, boost, favourite, share
case respond, boost, favourite, bookmark, share
func iconName(viewModel: StatusRowViewModel) -> String {
switch self {
@ -24,6 +24,8 @@ struct StatusActionsView: View {
return viewModel.isReblogged ? "arrow.left.arrow.right.circle.fill" : "arrow.left.arrow.right.circle"
case .favourite:
return viewModel.isFavourited ? "star.fill" : "star"
case .bookmark:
return viewModel.isBookmarked ? "bookmark.fill" : "bookmark"
case .share:
return "square.and.arrow.up"
}
@ -40,7 +42,7 @@ struct StatusActionsView: View {
return viewModel.favouritesCount
case .boost:
return viewModel.reblogsCount
case .share:
case .share, .bookmark:
return nil
}
}
@ -51,6 +53,8 @@ struct StatusActionsView: View {
return nil
case .favourite:
return viewModel.isFavourited ? .yellow : nil
case .bookmark:
return viewModel.isBookmarked ? .pink : nil
case .boost:
return viewModel.isReblogged ? theme.tintColor : nil
}
@ -150,6 +154,12 @@ struct StatusActionsView: View {
} else {
await viewModel.favourite()
}
case .bookmark:
if viewModel.isBookmarked {
await viewModel.unbookmark()
} else {
await viewModel.bookmark()
}
case .boost:
if viewModel.isReblogged {
await viewModel.unReblog()

View file

@ -30,7 +30,16 @@ struct StatusRowContextMenu: View {
} } label: {
Label(viewModel.isReblogged ? "Unboost" : "Boost", systemImage: "arrow.left.arrow.right.circle")
}
Button { Task {
if viewModel.isBookmarked {
await viewModel.unbookmark()
} else {
await viewModel.bookmark()
}
} } label: {
Label(viewModel.isReblogged ? "Unbookmark" : "Bookmark",
systemImage: "bookmark")
}
}
if viewModel.status.visibility == .pub, !viewModel.isRemote {

View file

@ -14,6 +14,7 @@ public class StatusRowViewModel: ObservableObject {
@Published var isFavourited: Bool
@Published var isReblogged: Bool
@Published var isPinned: Bool
@Published var isBookmarked: Bool
@Published var reblogsCount: Int
@Published var repliesCount: Int
@Published var embededStatus: Status?
@ -39,10 +40,12 @@ public class StatusRowViewModel: ObservableObject {
self.isFavourited = reblog.favourited == true
self.isReblogged = reblog.reblogged == true
self.isPinned = reblog.pinned == true
self.isBookmarked = reblog.bookmarked == true
} else {
self.isFavourited = status.favourited == true
self.isReblogged = status.reblogged == true
self.isPinned = status.pinned == true
self.isBookmarked = status.bookmarked == true
}
self.favouritesCount = status.reblog?.favouritesCount ?? status.favouritesCount
self.reblogsCount = status.reblog?.reblogsCount ?? status.reblogsCount
@ -166,6 +169,28 @@ public class StatusRowViewModel: ObservableObject {
}
}
func bookmark() async {
guard let client, client.isAuth else { return }
isBookmarked = true
do {
let status: Status = try await client.post(endpoint: Statuses.bookmark(id: status.reblog?.id ?? status.id))
updateFromStatus(status: status)
} catch {
isBookmarked = false
}
}
func unbookmark() async {
guard let client, client.isAuth else { return }
isBookmarked = false
do {
let status: Status = try await client.post(endpoint: Statuses.unbookmark(id: status.reblog?.id ?? status.id))
updateFromStatus(status: status)
} catch {
isBookmarked = true
}
}
func delete() async {
guard let client else { return }
do {
@ -178,10 +203,12 @@ public class StatusRowViewModel: ObservableObject {
isFavourited = reblog.favourited == true
isReblogged = reblog.reblogged == true
isPinned = reblog.pinned == true
isBookmarked = reblog.bookmarked == true
} else {
isFavourited = status.favourited == true
isReblogged = status.reblogged == true
isPinned = status.pinned == true
isBookmarked = status.bookmarked == true
}
favouritesCount = status.reblog?.favouritesCount ?? status.favouritesCount
reblogsCount = status.reblog?.reblogsCount ?? status.reblogsCount