Display pinned statuses on profile

This commit is contained in:
Thomas Ricouard 2023-01-03 18:22:08 +01:00
parent e9991020ec
commit a1681c3f1b
8 changed files with 95 additions and 8 deletions

View file

@ -57,6 +57,9 @@ public struct AccountDetailView: View {
switch viewModel.tabState {
case .statuses:
if viewModel.selectedTab == .statuses {
pinnedPostsView
}
StatusesListView(fetcher: viewModel)
case let .followedTags(tags):
makeTagsListView(tags: tags)
@ -295,6 +298,24 @@ public struct AccountDetailView: View {
Text("Enter the name for your list")
}
}
@ViewBuilder
private var pinnedPostsView: some View {
if !viewModel.pinned.isEmpty {
ForEach(viewModel.pinned) { status in
VStack(alignment: .leading) {
Label("Pinned post", systemImage: "pin.fill")
.font(.footnote)
.foregroundColor(.gray)
.fontWeight(.semibold)
StatusRowView(viewModel: .init(status: status))
}
.padding(.horizontal, .layoutPadding)
Divider()
.padding(.vertical, .dividerPadding)
}
}
}
}
struct AccountDetailView_Previews: PreviewProvider {

View file

@ -59,6 +59,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
@Published var statusesState: StatusesState = .loading
@Published var relationship: Relationshionship?
@Published var pinned: [Status] = []
@Published var favourites: [Status] = []
private var favouritesNextPage: LinkHandler?
@Published var followedTags: [Tag] = []
@ -137,7 +138,17 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
sinceId: nil,
tag: nil,
onlyMedia: selectedTab == .media ? true : nil,
excludeReplies: selectedTab == .statuses && !isCurrentUser ? true : nil))
excludeReplies: selectedTab == .statuses && !isCurrentUser ? true : nil,
pinned: nil))
if selectedTab == .statuses {
pinned =
try await client.get(endpoint: Accounts.statuses(id: accountId,
sinceId: nil,
tag: nil,
onlyMedia: nil,
excludeReplies: nil,
pinned: true))
}
if isCurrentUser {
(favourites, favouritesNextPage) = try await client.getWithLink(endpoint: Accounts.favourites(sinceId: nil))
}
@ -159,7 +170,8 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
sinceId: lastId,
tag: nil,
onlyMedia: selectedTab == .media ? true : nil,
excludeReplies: selectedTab == .statuses && !isCurrentUser ? true : nil))
excludeReplies: selectedTab == .statuses && !isCurrentUser ? true : nil,
pinned: nil))
statuses.append(contentsOf: newStatuses)
tabState = .statuses(statusesState: .display(statuses: statuses,
nextPageState: newStatuses.count < 20 ? .none : .hasNextPage))

View file

@ -6,7 +6,12 @@ public enum Accounts: Endpoint {
case followedTags
case featuredTags(id: String)
case verifyCredentials
case statuses(id: String, sinceId: String?, tag: String?, onlyMedia: Bool?, excludeReplies: Bool?)
case statuses(id: String,
sinceId: String?,
tag: String?,
onlyMedia: Bool?,
excludeReplies: Bool?,
pinned: Bool?)
case relationships(ids: [String])
case follow(id: String)
case unfollow(id: String)
@ -28,7 +33,7 @@ public enum Accounts: Endpoint {
return "accounts/\(id)/featured_tags"
case .verifyCredentials:
return "accounts/verify_credentials"
case .statuses(let id, _, _, _, _):
case .statuses(let id, _, _, _, _, _):
return "accounts/\(id)/statuses"
case .relationships:
return "accounts/relationships"
@ -51,7 +56,7 @@ public enum Accounts: Endpoint {
public func queryItems() -> [URLQueryItem]? {
switch self {
case .statuses(_, let sinceId, let tag, let onlyMedia, let excludeReplies):
case .statuses(_, let sinceId, let tag, let onlyMedia, let excludeReplies, let pinned):
var params: [URLQueryItem] = []
if let tag {
params.append(.init(name: "tagged", value: tag))
@ -65,6 +70,9 @@ public enum Accounts: Endpoint {
if let excludeReplies {
params.append(.init(name: "exclude_replies", value: excludeReplies ? "true" : "fals"))
}
if let pinned {
params.append(.init(name: "pinned", value: pinned ? "true" : "false"))
}
return params
case let .relationships(ids):
return ids.map {

View file

@ -20,6 +20,8 @@ public enum Statuses: Endpoint {
case unreblog(id: String)
case rebloggedBy(id: String, maxId: String?)
case favouritedBy(id: String, maxId: String?)
case pin(id: String)
case unpin(id: String)
public func path() -> String {
switch self {
@ -43,6 +45,10 @@ public enum Statuses: Endpoint {
return "statuses/\(id)/reblogged_by"
case .favouritedBy(let id, _):
return "statuses/\(id)/favourited_by"
case let .pin(id):
return "statuses/\(id)/pin"
case let .unpin(id):
return "statuses/\(id)/unpin"
}
}

View file

@ -18,6 +18,7 @@ public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
StatusRowView(viewModel: .init(status: status, isCompact: false))
.redacted(reason: .placeholder)
.shimmering()
.padding(.horizontal, .layoutPadding)
Divider()
.padding(.vertical, .dividerPadding)
}
@ -26,6 +27,7 @@ public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
case let .display(statuses, nextPageState):
ForEach(statuses, id: \.viewId) { status in
StatusRowView(viewModel: .init(status: status, isCompact: false))
.padding(.horizontal, .layoutPadding)
Divider()
.padding(.vertical, .dividerPadding)
}
@ -45,7 +47,6 @@ public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
}
}
}
.padding(.horizontal, .layoutPadding)
}
private var loadingRow: some View {
@ -54,5 +55,6 @@ public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
ProgressView()
Spacer()
}
.padding(.horizontal, .layoutPadding)
}
}

View file

@ -249,8 +249,19 @@ public struct StatusRowView: View {
Label("View in Browser", systemImage: "safari")
}
}
if account.account?.id == viewModel.status.account.id {
Button {
Task {
if viewModel.isPinned {
await viewModel.unPin()
} else {
await viewModel.pin()
}
}
} label: {
Label(viewModel.isPinned ? "Unpin": "Pin", systemImage: viewModel.isPinned ? "pin.fill" : "pin")
}
Button {
routeurPath.presentedSheet = .editStatusEditor(status: viewModel.status)
} label: {

View file

@ -11,6 +11,7 @@ public class StatusRowViewModel: ObservableObject {
@Published var favouritesCount: Int
@Published var isFavourited: Bool
@Published var isReblogged: Bool
@Published var isPinned: Bool
@Published var reblogsCount: Int
@Published var repliesCount: Int
@Published var embededStatus: Status?
@ -33,9 +34,11 @@ public class StatusRowViewModel: ObservableObject {
if let reblog = status.reblog {
self.isFavourited = reblog.favourited == true
self.isReblogged = reblog.reblogged == true
self.isPinned = reblog.pinned == true
} else {
self.isFavourited = status.favourited == true
self.isReblogged = status.reblogged == true
self.isPinned = status.pinned == true
}
self.favouritesCount = status.reblog?.favouritesCount ?? status.favouritesCount
self.reblogsCount = status.reblog?.reblogsCount ?? status.reblogsCount
@ -129,6 +132,28 @@ public class StatusRowViewModel: ObservableObject {
}
}
func pin() async {
guard let client, client.isAuth else { return }
isPinned = true
do {
let status: Status = try await client.post(endpoint: Statuses.pin(id: status.reblog?.id ?? status.id))
updateFromStatus(status: status)
} catch {
isPinned = false
}
}
func unPin() async {
guard let client, client.isAuth else { return }
isPinned = false
do {
let status: Status = try await client.post(endpoint: Statuses.unpin(id: status.reblog?.id ?? status.id))
updateFromStatus(status: status)
} catch {
isPinned = true
}
}
func delete() async {
guard let client else { return }
do {
@ -140,9 +165,11 @@ public class StatusRowViewModel: ObservableObject {
if let reblog = status.reblog {
isFavourited = reblog.favourited == true
isReblogged = reblog.reblogged == true
isPinned = reblog.pinned == true
} else {
isFavourited = status.favourited == true
isReblogged = status.reblogged == true
isPinned = status.pinned == true
}
favouritesCount = status.reblog?.favouritesCount ?? status.favouritesCount
reblogsCount = status.reblog?.reblogsCount ?? status.reblogsCount

View file

@ -61,7 +61,7 @@ public enum TimelineFilter: Hashable, Equatable {
case let .list(list): return Timelines.list(listId: list.id, sinceId: sinceId, maxId: maxId, minId: minId)
case let .hashtag(tag, accountId):
if let accountId {
return Accounts.statuses(id: accountId, sinceId: nil, tag: tag, onlyMedia: nil, excludeReplies: nil)
return Accounts.statuses(id: accountId, sinceId: nil, tag: tag, onlyMedia: nil, excludeReplies: nil, pinned: nil)
} else {
return Timelines.hashtag(tag: tag, maxId: maxId)
}