mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2025-02-02 11:12:20 +00:00
Display pinned statuses on profile
This commit is contained in:
parent
e9991020ec
commit
a1681c3f1b
8 changed files with 95 additions and 8 deletions
|
@ -57,6 +57,9 @@ public struct AccountDetailView: View {
|
||||||
|
|
||||||
switch viewModel.tabState {
|
switch viewModel.tabState {
|
||||||
case .statuses:
|
case .statuses:
|
||||||
|
if viewModel.selectedTab == .statuses {
|
||||||
|
pinnedPostsView
|
||||||
|
}
|
||||||
StatusesListView(fetcher: viewModel)
|
StatusesListView(fetcher: viewModel)
|
||||||
case let .followedTags(tags):
|
case let .followedTags(tags):
|
||||||
makeTagsListView(tags: tags)
|
makeTagsListView(tags: tags)
|
||||||
|
@ -295,6 +298,24 @@ public struct AccountDetailView: View {
|
||||||
Text("Enter the name for your list")
|
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 {
|
struct AccountDetailView_Previews: PreviewProvider {
|
||||||
|
|
|
@ -59,6 +59,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
||||||
@Published var statusesState: StatusesState = .loading
|
@Published var statusesState: StatusesState = .loading
|
||||||
|
|
||||||
@Published var relationship: Relationshionship?
|
@Published var relationship: Relationshionship?
|
||||||
|
@Published var pinned: [Status] = []
|
||||||
@Published var favourites: [Status] = []
|
@Published var favourites: [Status] = []
|
||||||
private var favouritesNextPage: LinkHandler?
|
private var favouritesNextPage: LinkHandler?
|
||||||
@Published var followedTags: [Tag] = []
|
@Published var followedTags: [Tag] = []
|
||||||
|
@ -137,7 +138,17 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
||||||
sinceId: nil,
|
sinceId: nil,
|
||||||
tag: nil,
|
tag: nil,
|
||||||
onlyMedia: selectedTab == .media ? true : 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 {
|
if isCurrentUser {
|
||||||
(favourites, favouritesNextPage) = try await client.getWithLink(endpoint: Accounts.favourites(sinceId: nil))
|
(favourites, favouritesNextPage) = try await client.getWithLink(endpoint: Accounts.favourites(sinceId: nil))
|
||||||
}
|
}
|
||||||
|
@ -159,7 +170,8 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
||||||
sinceId: lastId,
|
sinceId: lastId,
|
||||||
tag: nil,
|
tag: nil,
|
||||||
onlyMedia: selectedTab == .media ? true : nil,
|
onlyMedia: selectedTab == .media ? true : nil,
|
||||||
excludeReplies: selectedTab == .statuses && !isCurrentUser ? true : nil))
|
excludeReplies: selectedTab == .statuses && !isCurrentUser ? true : nil,
|
||||||
|
pinned: nil))
|
||||||
statuses.append(contentsOf: newStatuses)
|
statuses.append(contentsOf: newStatuses)
|
||||||
tabState = .statuses(statusesState: .display(statuses: statuses,
|
tabState = .statuses(statusesState: .display(statuses: statuses,
|
||||||
nextPageState: newStatuses.count < 20 ? .none : .hasNextPage))
|
nextPageState: newStatuses.count < 20 ? .none : .hasNextPage))
|
||||||
|
|
|
@ -6,7 +6,12 @@ public enum Accounts: Endpoint {
|
||||||
case followedTags
|
case followedTags
|
||||||
case featuredTags(id: String)
|
case featuredTags(id: String)
|
||||||
case verifyCredentials
|
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 relationships(ids: [String])
|
||||||
case follow(id: String)
|
case follow(id: String)
|
||||||
case unfollow(id: String)
|
case unfollow(id: String)
|
||||||
|
@ -28,7 +33,7 @@ public enum Accounts: Endpoint {
|
||||||
return "accounts/\(id)/featured_tags"
|
return "accounts/\(id)/featured_tags"
|
||||||
case .verifyCredentials:
|
case .verifyCredentials:
|
||||||
return "accounts/verify_credentials"
|
return "accounts/verify_credentials"
|
||||||
case .statuses(let id, _, _, _, _):
|
case .statuses(let id, _, _, _, _, _):
|
||||||
return "accounts/\(id)/statuses"
|
return "accounts/\(id)/statuses"
|
||||||
case .relationships:
|
case .relationships:
|
||||||
return "accounts/relationships"
|
return "accounts/relationships"
|
||||||
|
@ -51,7 +56,7 @@ public enum Accounts: Endpoint {
|
||||||
|
|
||||||
public func queryItems() -> [URLQueryItem]? {
|
public func queryItems() -> [URLQueryItem]? {
|
||||||
switch self {
|
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] = []
|
var params: [URLQueryItem] = []
|
||||||
if let tag {
|
if let tag {
|
||||||
params.append(.init(name: "tagged", value: tag))
|
params.append(.init(name: "tagged", value: tag))
|
||||||
|
@ -65,6 +70,9 @@ public enum Accounts: Endpoint {
|
||||||
if let excludeReplies {
|
if let excludeReplies {
|
||||||
params.append(.init(name: "exclude_replies", value: excludeReplies ? "true" : "fals"))
|
params.append(.init(name: "exclude_replies", value: excludeReplies ? "true" : "fals"))
|
||||||
}
|
}
|
||||||
|
if let pinned {
|
||||||
|
params.append(.init(name: "pinned", value: pinned ? "true" : "false"))
|
||||||
|
}
|
||||||
return params
|
return params
|
||||||
case let .relationships(ids):
|
case let .relationships(ids):
|
||||||
return ids.map {
|
return ids.map {
|
||||||
|
|
|
@ -20,6 +20,8 @@ public enum Statuses: Endpoint {
|
||||||
case unreblog(id: String)
|
case unreblog(id: String)
|
||||||
case rebloggedBy(id: String, maxId: String?)
|
case rebloggedBy(id: String, maxId: String?)
|
||||||
case favouritedBy(id: String, maxId: String?)
|
case favouritedBy(id: String, maxId: String?)
|
||||||
|
case pin(id: String)
|
||||||
|
case unpin(id: String)
|
||||||
|
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
|
@ -43,6 +45,10 @@ public enum Statuses: Endpoint {
|
||||||
return "statuses/\(id)/reblogged_by"
|
return "statuses/\(id)/reblogged_by"
|
||||||
case .favouritedBy(let id, _):
|
case .favouritedBy(let id, _):
|
||||||
return "statuses/\(id)/favourited_by"
|
return "statuses/\(id)/favourited_by"
|
||||||
|
case let .pin(id):
|
||||||
|
return "statuses/\(id)/pin"
|
||||||
|
case let .unpin(id):
|
||||||
|
return "statuses/\(id)/unpin"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
|
||||||
StatusRowView(viewModel: .init(status: status, isCompact: false))
|
StatusRowView(viewModel: .init(status: status, isCompact: false))
|
||||||
.redacted(reason: .placeholder)
|
.redacted(reason: .placeholder)
|
||||||
.shimmering()
|
.shimmering()
|
||||||
|
.padding(.horizontal, .layoutPadding)
|
||||||
Divider()
|
Divider()
|
||||||
.padding(.vertical, .dividerPadding)
|
.padding(.vertical, .dividerPadding)
|
||||||
}
|
}
|
||||||
|
@ -26,6 +27,7 @@ public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
|
||||||
case let .display(statuses, nextPageState):
|
case let .display(statuses, nextPageState):
|
||||||
ForEach(statuses, id: \.viewId) { status in
|
ForEach(statuses, id: \.viewId) { status in
|
||||||
StatusRowView(viewModel: .init(status: status, isCompact: false))
|
StatusRowView(viewModel: .init(status: status, isCompact: false))
|
||||||
|
.padding(.horizontal, .layoutPadding)
|
||||||
Divider()
|
Divider()
|
||||||
.padding(.vertical, .dividerPadding)
|
.padding(.vertical, .dividerPadding)
|
||||||
}
|
}
|
||||||
|
@ -45,7 +47,6 @@ public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal, .layoutPadding)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var loadingRow: some View {
|
private var loadingRow: some View {
|
||||||
|
@ -54,5 +55,6 @@ public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
.padding(.horizontal, .layoutPadding)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -251,6 +251,17 @@ public struct StatusRowView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
if account.account?.id == viewModel.status.account.id {
|
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 {
|
Button {
|
||||||
routeurPath.presentedSheet = .editStatusEditor(status: viewModel.status)
|
routeurPath.presentedSheet = .editStatusEditor(status: viewModel.status)
|
||||||
} label: {
|
} label: {
|
||||||
|
|
|
@ -11,6 +11,7 @@ public class StatusRowViewModel: ObservableObject {
|
||||||
@Published var favouritesCount: Int
|
@Published var favouritesCount: Int
|
||||||
@Published var isFavourited: Bool
|
@Published var isFavourited: Bool
|
||||||
@Published var isReblogged: Bool
|
@Published var isReblogged: Bool
|
||||||
|
@Published var isPinned: 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?
|
||||||
|
@ -33,9 +34,11 @@ public class StatusRowViewModel: ObservableObject {
|
||||||
if let reblog = status.reblog {
|
if let reblog = status.reblog {
|
||||||
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
|
||||||
} 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.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
|
||||||
|
@ -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 {
|
func delete() async {
|
||||||
guard let client else { return }
|
guard let client else { return }
|
||||||
do {
|
do {
|
||||||
|
@ -140,9 +165,11 @@ public class StatusRowViewModel: ObservableObject {
|
||||||
if let reblog = status.reblog {
|
if let reblog = status.reblog {
|
||||||
isFavourited = reblog.favourited == true
|
isFavourited = reblog.favourited == true
|
||||||
isReblogged = reblog.reblogged == true
|
isReblogged = reblog.reblogged == true
|
||||||
|
isPinned = reblog.pinned == true
|
||||||
} else {
|
} else {
|
||||||
isFavourited = status.favourited == true
|
isFavourited = status.favourited == true
|
||||||
isReblogged = status.reblogged == true
|
isReblogged = status.reblogged == true
|
||||||
|
isPinned = status.pinned == 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
|
||||||
|
|
|
@ -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 .list(list): return Timelines.list(listId: list.id, sinceId: sinceId, maxId: maxId, minId: minId)
|
||||||
case let .hashtag(tag, accountId):
|
case let .hashtag(tag, accountId):
|
||||||
if let 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 {
|
} else {
|
||||||
return Timelines.hashtag(tag: tag, maxId: maxId)
|
return Timelines.hashtag(tag: tag, maxId: maxId)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue