mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2025-04-27 10:24:44 +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) {
|
Picker("", selection: $viewModel.selectedTab) {
|
||||||
ForEach(isCurrentUser ? AccountDetailViewModel.Tab.currentAccountTabs : AccountDetailViewModel.Tab.accountTabs,
|
ForEach(isCurrentUser ? AccountDetailViewModel.Tab.currentAccountTabs : AccountDetailViewModel.Tab.accountTabs,
|
||||||
id: \.self) { tab in
|
id: \.self) { tab in
|
||||||
Text(tab.title).tag(tab)
|
Image(systemName: tab.iconName)
|
||||||
|
.tag(tab)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.pickerStyle(.segmented)
|
.pickerStyle(.segmented)
|
||||||
|
|
|
@ -15,24 +15,25 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Tab: Int {
|
enum Tab: Int {
|
||||||
case statuses, favourites, followedTags, postsAndReplies, media, lists
|
case statuses, favourites, bookmarks, followedTags, postsAndReplies, media, lists
|
||||||
|
|
||||||
static var currentAccountTabs: [Tab] {
|
static var currentAccountTabs: [Tab] {
|
||||||
[.statuses, .favourites, .followedTags, .lists]
|
[.statuses, .favourites, .bookmarks, .followedTags, .lists]
|
||||||
}
|
}
|
||||||
|
|
||||||
static var accountTabs: [Tab] {
|
static var accountTabs: [Tab] {
|
||||||
[.statuses, .postsAndReplies, .media]
|
[.statuses, .postsAndReplies, .media]
|
||||||
}
|
}
|
||||||
|
|
||||||
var title: String {
|
var iconName: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .statuses: return "Posts"
|
case .statuses: return "bubble.right"
|
||||||
case .favourites: return "Favorites"
|
case .favourites: return "star"
|
||||||
case .followedTags: return "Tags"
|
case .bookmarks: return "bookmark"
|
||||||
case .postsAndReplies: return "Posts & Replies"
|
case .followedTags: return "tag"
|
||||||
case .media: return "Media"
|
case .postsAndReplies: return "bubble.left.and.bubble.right"
|
||||||
case .lists: return "Lists"
|
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 relationship: Relationshionship?
|
||||||
@Published var pinned: [Status] = []
|
@Published var pinned: [Status] = []
|
||||||
@Published var favourites: [Status] = []
|
@Published var favourites: [Status] = []
|
||||||
|
@Published var bookmarks: [Status] = []
|
||||||
private var favouritesNextPage: LinkHandler?
|
private var favouritesNextPage: LinkHandler?
|
||||||
|
private var bookmarksNextPage: LinkHandler?
|
||||||
@Published var featuredTags: [FeaturedTag] = []
|
@Published var featuredTags: [FeaturedTag] = []
|
||||||
@Published var fields: [Account.Field] = []
|
@Published var fields: [Account.Field] = []
|
||||||
@Published var familliarFollowers: [Account] = []
|
@Published var familliarFollowers: [Account] = []
|
||||||
|
@ -145,6 +148,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
||||||
}
|
}
|
||||||
if isCurrentUser {
|
if isCurrentUser {
|
||||||
(favourites, favouritesNextPage) = try await client.getWithLink(endpoint: Accounts.favourites(sinceId: nil))
|
(favourites, favouritesNextPage) = try await client.getWithLink(endpoint: Accounts.favourites(sinceId: nil))
|
||||||
|
(bookmarks, bookmarksNextPage) = try await client.getWithLink(endpoint: Accounts.bookmarks(sinceId: nil))
|
||||||
}
|
}
|
||||||
reloadTabState()
|
reloadTabState()
|
||||||
} catch {
|
} catch {
|
||||||
|
@ -175,6 +179,12 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
||||||
(newFavourites, favouritesNextPage) = try await client.getWithLink(endpoint: Accounts.favourites(sinceId: nextPageId))
|
(newFavourites, favouritesNextPage) = try await client.getWithLink(endpoint: Accounts.favourites(sinceId: nextPageId))
|
||||||
favourites.append(contentsOf: newFavourites)
|
favourites.append(contentsOf: newFavourites)
|
||||||
tabState = .statuses(statusesState: .display(statuses: favourites, nextPageState: .hasNextPage))
|
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:
|
case .followedTags, .lists:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -208,6 +218,9 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
||||||
case .favourites:
|
case .favourites:
|
||||||
tabState = .statuses(statusesState: .display(statuses: favourites,
|
tabState = .statuses(statusesState: .display(statuses: favourites,
|
||||||
nextPageState: favouritesNextPage != nil ? .hasNextPage : .none))
|
nextPageState: favouritesNextPage != nil ? .hasNextPage : .none))
|
||||||
|
case .bookmarks:
|
||||||
|
tabState = .statuses(statusesState: .display(statuses: bookmarks,
|
||||||
|
nextPageState: bookmarksNextPage != nil ? .hasNextPage : .none))
|
||||||
case .followedTags:
|
case .followedTags:
|
||||||
tabState = .followedTags
|
tabState = .followedTags
|
||||||
case .lists:
|
case .lists:
|
||||||
|
|
|
@ -40,6 +40,7 @@ public protocol AnyStatus {
|
||||||
var favourited: Bool? { get }
|
var favourited: Bool? { get }
|
||||||
var reblogged: Bool? { get }
|
var reblogged: Bool? { get }
|
||||||
var pinned: Bool? { get }
|
var pinned: Bool? { get }
|
||||||
|
var bookmarked: Bool? { get }
|
||||||
var emojis: [Emoji] { get }
|
var emojis: [Emoji] { get }
|
||||||
var url: URL? { get }
|
var url: URL? { get }
|
||||||
var application: Application? { get }
|
var application: Application? { get }
|
||||||
|
@ -71,6 +72,7 @@ public struct Status: AnyStatus, Codable, Identifiable {
|
||||||
public let favourited: Bool?
|
public let favourited: Bool?
|
||||||
public let reblogged: Bool?
|
public let reblogged: Bool?
|
||||||
public let pinned: Bool?
|
public let pinned: Bool?
|
||||||
|
public let bookmarked: Bool?
|
||||||
public let emojis: [Emoji]
|
public let emojis: [Emoji]
|
||||||
public let url: URL?
|
public let url: URL?
|
||||||
public let application: Application?
|
public let application: Application?
|
||||||
|
@ -96,6 +98,7 @@ public struct Status: AnyStatus, Codable, Identifiable {
|
||||||
favourited: false,
|
favourited: false,
|
||||||
reblogged: false,
|
reblogged: false,
|
||||||
pinned: false,
|
pinned: false,
|
||||||
|
bookmarked: false,
|
||||||
emojis: [],
|
emojis: [],
|
||||||
url: nil,
|
url: nil,
|
||||||
application: nil,
|
application: nil,
|
||||||
|
@ -130,6 +133,7 @@ public struct ReblogStatus: AnyStatus, Codable, Identifiable {
|
||||||
public let favourited: Bool?
|
public let favourited: Bool?
|
||||||
public let reblogged: Bool?
|
public let reblogged: Bool?
|
||||||
public let pinned: Bool?
|
public let pinned: Bool?
|
||||||
|
public let bookmarked: Bool?
|
||||||
public let emojis: [Emoji]
|
public let emojis: [Emoji]
|
||||||
public let url: URL?
|
public let url: URL?
|
||||||
public var application: Application?
|
public var application: Application?
|
||||||
|
|
|
@ -3,6 +3,7 @@ import Foundation
|
||||||
public enum Accounts: Endpoint {
|
public enum Accounts: Endpoint {
|
||||||
case accounts(id: String)
|
case accounts(id: String)
|
||||||
case favourites(sinceId: String?)
|
case favourites(sinceId: String?)
|
||||||
|
case bookmarks(sinceId: String?)
|
||||||
case followedTags
|
case followedTags
|
||||||
case featuredTags(id: String)
|
case featuredTags(id: String)
|
||||||
case verifyCredentials
|
case verifyCredentials
|
||||||
|
@ -27,6 +28,8 @@ public enum Accounts: Endpoint {
|
||||||
return "accounts/\(id)"
|
return "accounts/\(id)"
|
||||||
case .favourites:
|
case .favourites:
|
||||||
return "favourites"
|
return "favourites"
|
||||||
|
case .bookmarks:
|
||||||
|
return "bookmarks"
|
||||||
case .followedTags:
|
case .followedTags:
|
||||||
return "followed_tags"
|
return "followed_tags"
|
||||||
case .featuredTags(let id):
|
case .featuredTags(let id):
|
||||||
|
@ -87,6 +90,9 @@ public enum Accounts: Endpoint {
|
||||||
case let .favourites(sinceId):
|
case let .favourites(sinceId):
|
||||||
guard let sinceId else { return nil }
|
guard let sinceId else { return nil }
|
||||||
return [.init(name: "max_id", value: sinceId)]
|
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:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ public enum Statuses: Endpoint {
|
||||||
case favouritedBy(id: String, maxId: String?)
|
case favouritedBy(id: String, maxId: String?)
|
||||||
case pin(id: String)
|
case pin(id: String)
|
||||||
case unpin(id: String)
|
case unpin(id: String)
|
||||||
|
case bookmark(id: String)
|
||||||
|
case unbookmark(id: String)
|
||||||
|
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
|
@ -49,6 +51,10 @@ public enum Statuses: Endpoint {
|
||||||
return "statuses/\(id)/pin"
|
return "statuses/\(id)/pin"
|
||||||
case let .unpin(id):
|
case let .unpin(id):
|
||||||
return "statuses/\(id)/unpin"
|
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
|
@MainActor
|
||||||
enum Actions: CaseIterable {
|
enum Actions: CaseIterable {
|
||||||
case respond, boost, favourite, share
|
case respond, boost, favourite, bookmark, share
|
||||||
|
|
||||||
func iconName(viewModel: StatusRowViewModel) -> String {
|
func iconName(viewModel: StatusRowViewModel) -> String {
|
||||||
switch self {
|
switch self {
|
||||||
|
@ -24,6 +24,8 @@ struct StatusActionsView: View {
|
||||||
return viewModel.isReblogged ? "arrow.left.arrow.right.circle.fill" : "arrow.left.arrow.right.circle"
|
return viewModel.isReblogged ? "arrow.left.arrow.right.circle.fill" : "arrow.left.arrow.right.circle"
|
||||||
case .favourite:
|
case .favourite:
|
||||||
return viewModel.isFavourited ? "star.fill" : "star"
|
return viewModel.isFavourited ? "star.fill" : "star"
|
||||||
|
case .bookmark:
|
||||||
|
return viewModel.isBookmarked ? "bookmark.fill" : "bookmark"
|
||||||
case .share:
|
case .share:
|
||||||
return "square.and.arrow.up"
|
return "square.and.arrow.up"
|
||||||
}
|
}
|
||||||
|
@ -40,7 +42,7 @@ struct StatusActionsView: View {
|
||||||
return viewModel.favouritesCount
|
return viewModel.favouritesCount
|
||||||
case .boost:
|
case .boost:
|
||||||
return viewModel.reblogsCount
|
return viewModel.reblogsCount
|
||||||
case .share:
|
case .share, .bookmark:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,6 +53,8 @@ struct StatusActionsView: View {
|
||||||
return nil
|
return nil
|
||||||
case .favourite:
|
case .favourite:
|
||||||
return viewModel.isFavourited ? .yellow : nil
|
return viewModel.isFavourited ? .yellow : nil
|
||||||
|
case .bookmark:
|
||||||
|
return viewModel.isBookmarked ? .pink : nil
|
||||||
case .boost:
|
case .boost:
|
||||||
return viewModel.isReblogged ? theme.tintColor : nil
|
return viewModel.isReblogged ? theme.tintColor : nil
|
||||||
}
|
}
|
||||||
|
@ -150,6 +154,12 @@ struct StatusActionsView: View {
|
||||||
} else {
|
} else {
|
||||||
await viewModel.favourite()
|
await viewModel.favourite()
|
||||||
}
|
}
|
||||||
|
case .bookmark:
|
||||||
|
if viewModel.isBookmarked {
|
||||||
|
await viewModel.unbookmark()
|
||||||
|
} else {
|
||||||
|
await viewModel.bookmark()
|
||||||
|
}
|
||||||
case .boost:
|
case .boost:
|
||||||
if viewModel.isReblogged {
|
if viewModel.isReblogged {
|
||||||
await viewModel.unReblog()
|
await viewModel.unReblog()
|
||||||
|
|
|
@ -30,7 +30,16 @@ struct StatusRowContextMenu: View {
|
||||||
} } label: {
|
} } label: {
|
||||||
Label(viewModel.isReblogged ? "Unboost" : "Boost", systemImage: "arrow.left.arrow.right.circle")
|
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 {
|
if viewModel.status.visibility == .pub, !viewModel.isRemote {
|
||||||
|
|
|
@ -14,6 +14,7 @@ public class StatusRowViewModel: ObservableObject {
|
||||||
@Published var isFavourited: Bool
|
@Published var isFavourited: Bool
|
||||||
@Published var isReblogged: Bool
|
@Published var isReblogged: Bool
|
||||||
@Published var isPinned: Bool
|
@Published var isPinned: Bool
|
||||||
|
@Published var isBookmarked: Bool
|
||||||
@Published var reblogsCount: Int
|
@Published var reblogsCount: Int
|
||||||
@Published var repliesCount: Int
|
@Published var repliesCount: Int
|
||||||
@Published var embededStatus: Status?
|
@Published var embededStatus: Status?
|
||||||
|
@ -39,10 +40,12 @@ public class StatusRowViewModel: ObservableObject {
|
||||||
self.isFavourited = reblog.favourited == true
|
self.isFavourited = reblog.favourited == true
|
||||||
self.isReblogged = reblog.reblogged == true
|
self.isReblogged = reblog.reblogged == true
|
||||||
self.isPinned = reblog.pinned == true
|
self.isPinned = reblog.pinned == true
|
||||||
|
self.isBookmarked = reblog.bookmarked == true
|
||||||
} else {
|
} else {
|
||||||
self.isFavourited = status.favourited == true
|
self.isFavourited = status.favourited == true
|
||||||
self.isReblogged = status.reblogged == true
|
self.isReblogged = status.reblogged == true
|
||||||
self.isPinned = status.pinned == true
|
self.isPinned = status.pinned == true
|
||||||
|
self.isBookmarked = status.bookmarked == true
|
||||||
}
|
}
|
||||||
self.favouritesCount = status.reblog?.favouritesCount ?? status.favouritesCount
|
self.favouritesCount = status.reblog?.favouritesCount ?? status.favouritesCount
|
||||||
self.reblogsCount = status.reblog?.reblogsCount ?? status.reblogsCount
|
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 {
|
func delete() async {
|
||||||
guard let client else { return }
|
guard let client else { return }
|
||||||
do {
|
do {
|
||||||
|
@ -178,10 +203,12 @@ public class StatusRowViewModel: ObservableObject {
|
||||||
isFavourited = reblog.favourited == true
|
isFavourited = reblog.favourited == true
|
||||||
isReblogged = reblog.reblogged == true
|
isReblogged = reblog.reblogged == true
|
||||||
isPinned = reblog.pinned == true
|
isPinned = reblog.pinned == true
|
||||||
|
isBookmarked = reblog.bookmarked == true
|
||||||
} else {
|
} else {
|
||||||
isFavourited = status.favourited == true
|
isFavourited = status.favourited == true
|
||||||
isReblogged = status.reblogged == true
|
isReblogged = status.reblogged == true
|
||||||
isPinned = status.pinned == true
|
isPinned = status.pinned == true
|
||||||
|
isBookmarked = status.bookmarked == true
|
||||||
}
|
}
|
||||||
favouritesCount = status.reblog?.favouritesCount ?? status.favouritesCount
|
favouritesCount = status.reblog?.favouritesCount ?? status.favouritesCount
|
||||||
reblogsCount = status.reblog?.reblogsCount ?? status.reblogsCount
|
reblogsCount = status.reblog?.reblogsCount ?? status.reblogsCount
|
||||||
|
|
Loading…
Reference in a new issue