mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-26 02:01:02 +00:00
Bookmarks support close #48
This commit is contained in:
parent
1c1ca7ba0f
commit
662f4be29d
8 changed files with 89 additions and 13 deletions
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue