Post boosted by / favourited by
This commit is contained in:
parent
569aedeaeb
commit
70ee6e0d27
|
@ -18,9 +18,13 @@ extension View {
|
|||
case let .hashTag(tag, accountId):
|
||||
TimelineView(timeline: .hashtag(tag: tag, accountId: accountId))
|
||||
case let .following(id):
|
||||
AccountsListView(accountId: id, mode: .following)
|
||||
AccountsListView(mode: .followers(accountId: id))
|
||||
case let .followers(id):
|
||||
AccountsListView(accountId: id, mode: .followers)
|
||||
AccountsListView(mode: .followers(accountId: id))
|
||||
case let .favouritedBy(id):
|
||||
AccountsListView(mode: .favouritedBy(statusId: id))
|
||||
case let .rebloggedBy(id):
|
||||
AccountsListView(mode: .rebloggedBy(statusId: id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ struct AccountDetailHeaderView: View {
|
|||
VStack(alignment: .leading, spacing: 0) {
|
||||
account.displayNameWithEmojis
|
||||
.font(.headline)
|
||||
Text(account.acct)
|
||||
Text("@\(account.acct)")
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
|
|
|
@ -38,7 +38,8 @@ public struct AccountsListRow: View {
|
|||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
Text(viewModel.account.note.asSafeAttributedString)
|
||||
.font(.callout)
|
||||
.font(.footnote)
|
||||
.lineLimit(3)
|
||||
.environment(\.openURL, OpenURLAction { url in
|
||||
routeurPath.handle(url: url)
|
||||
})
|
||||
|
|
|
@ -9,8 +9,8 @@ public struct AccountsListView: View {
|
|||
@StateObject private var viewModel: AccountsListViewModel
|
||||
@State private var didAppear: Bool = false
|
||||
|
||||
public init(accountId: String, mode: AccountsListMode) {
|
||||
_viewModel = StateObject(wrappedValue: .init(accountId: accountId, mode: mode))
|
||||
public init(mode: AccountsListMode) {
|
||||
_viewModel = StateObject(wrappedValue: .init(mode: mode))
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
|
@ -50,7 +50,7 @@ public struct AccountsListView: View {
|
|||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.navigationTitle(viewModel.mode.rawValue.capitalized)
|
||||
.navigationTitle(viewModel.mode.title)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.task {
|
||||
viewModel.client = client
|
||||
|
|
|
@ -2,15 +2,28 @@ import SwiftUI
|
|||
import Models
|
||||
import Network
|
||||
|
||||
public enum AccountsListMode: String {
|
||||
case following, followers
|
||||
public enum AccountsListMode {
|
||||
case following(accountId: String), followers(accountId: String)
|
||||
case favouritedBy(statusId: String), rebloggedBy(statusId: String)
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .following:
|
||||
return "Following"
|
||||
case .followers:
|
||||
return "Followers"
|
||||
case .favouritedBy:
|
||||
return "Favourited by"
|
||||
case .rebloggedBy:
|
||||
return "Boosted by"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
class AccountsListViewModel: ObservableObject {
|
||||
var client: Client?
|
||||
|
||||
let accountId: String
|
||||
let mode: AccountsListMode
|
||||
|
||||
public enum State {
|
||||
|
@ -31,8 +44,7 @@ class AccountsListViewModel: ObservableObject {
|
|||
|
||||
private var nextPageId: String?
|
||||
|
||||
init(accountId: String, mode: AccountsListMode) {
|
||||
self.accountId = accountId
|
||||
init(mode: AccountsListMode) {
|
||||
self.mode = mode
|
||||
}
|
||||
|
||||
|
@ -42,12 +54,18 @@ class AccountsListViewModel: ObservableObject {
|
|||
state = .loading
|
||||
let link: LinkHandler?
|
||||
switch mode {
|
||||
case .followers:
|
||||
case let .followers(accountId):
|
||||
(accounts, link) = try await client.getWithLink(endpoint: Accounts.followers(id: accountId,
|
||||
sinceId: nil))
|
||||
case .following:
|
||||
maxId: nil))
|
||||
case let .following(accountId):
|
||||
(accounts, link) = try await client.getWithLink(endpoint: Accounts.following(id: accountId,
|
||||
sinceId: nil))
|
||||
maxId: nil))
|
||||
case let .rebloggedBy(statusId):
|
||||
(accounts, link) = try await client.getWithLink(endpoint: Statuses.rebloggedBy(id: statusId,
|
||||
maxId: nil))
|
||||
case let .favouritedBy(statusId):
|
||||
(accounts, link) = try await client.getWithLink(endpoint: Statuses.favouritedBy(id: statusId,
|
||||
maxId: nil))
|
||||
}
|
||||
nextPageId = link?.maxId
|
||||
relationships = try await client.get(endpoint:
|
||||
|
@ -65,12 +83,18 @@ class AccountsListViewModel: ObservableObject {
|
|||
let newAccounts: [Account]
|
||||
let link: LinkHandler?
|
||||
switch mode {
|
||||
case .followers:
|
||||
case let .followers(accountId):
|
||||
(newAccounts, link) = try await client.getWithLink(endpoint: Accounts.followers(id: accountId,
|
||||
sinceId: nextPageId))
|
||||
case .following:
|
||||
maxId: nextPageId))
|
||||
case let .following(accountId):
|
||||
(newAccounts, link) = try await client.getWithLink(endpoint: Accounts.following(id: accountId,
|
||||
sinceId: nextPageId))
|
||||
maxId: nextPageId))
|
||||
case let .rebloggedBy(statusId):
|
||||
(newAccounts, link) = try await client.getWithLink(endpoint: Statuses.rebloggedBy(id: statusId,
|
||||
maxId: nextPageId))
|
||||
case let .favouritedBy(statusId):
|
||||
(newAccounts, link) = try await client.getWithLink(endpoint: Statuses.favouritedBy(id: statusId,
|
||||
maxId: nextPageId))
|
||||
}
|
||||
accounts.append(contentsOf: newAccounts)
|
||||
let newRelationships: [Relationshionship] =
|
||||
|
|
|
@ -9,6 +9,8 @@ public enum RouteurDestinations: Hashable {
|
|||
case hashTag(tag: String, account: String?)
|
||||
case followers(id: String)
|
||||
case following(id: String)
|
||||
case favouritedBy(id: String)
|
||||
case rebloggedBy(id: String)
|
||||
}
|
||||
|
||||
public enum SheetDestinations: Identifiable {
|
||||
|
|
|
@ -12,8 +12,8 @@ public enum Accounts: Endpoint {
|
|||
case unfollow(id: String)
|
||||
case familiarFollowers(withAccount: String)
|
||||
case suggestions
|
||||
case followers(id: String, sinceId: String?)
|
||||
case following(id: String, sinceId: String?)
|
||||
case followers(id: String, maxId: String?)
|
||||
case following(id: String, maxId: String?)
|
||||
|
||||
public func path() -> String {
|
||||
switch self {
|
||||
|
@ -63,12 +63,10 @@ public enum Accounts: Endpoint {
|
|||
}
|
||||
case let .familiarFollowers(withAccount):
|
||||
return [.init(name: "id[]", value: withAccount)]
|
||||
case let .followers(_, sinceId):
|
||||
guard let sinceId else { return nil }
|
||||
return [.init(name: "max_id", value: sinceId)]
|
||||
case let .following(_, sinceId):
|
||||
guard let sinceId else { return nil }
|
||||
return [.init(name: "max_id", value: sinceId)]
|
||||
case let .followers(_, maxId):
|
||||
return makePaginationParam(sinceId: nil, maxId: maxId)
|
||||
case let .following(_, maxId):
|
||||
return makePaginationParam(sinceId: nil, maxId: maxId)
|
||||
case let .favourites(sinceId):
|
||||
guard let sinceId else { return nil }
|
||||
return [.init(name: "max_id", value: sinceId)]
|
||||
|
|
|
@ -7,6 +7,8 @@ public enum Statuses: Endpoint {
|
|||
case unfavourite(id: String)
|
||||
case reblog(id: String)
|
||||
case unreblog(id: String)
|
||||
case rebloggedBy(id: String, maxId: String?)
|
||||
case favouritedBy(id: String, maxId: String?)
|
||||
|
||||
public func path() -> String {
|
||||
switch self {
|
||||
|
@ -22,11 +24,19 @@ public enum Statuses: Endpoint {
|
|||
return "statuses/\(id)/reblog"
|
||||
case .unreblog(let id):
|
||||
return "statuses/\(id)/unreblog"
|
||||
case .rebloggedBy(let id, _):
|
||||
return "statuses/\(id)/reblogged_by"
|
||||
case .favouritedBy(let id, _):
|
||||
return "statuses/\(id)/favourited_by"
|
||||
}
|
||||
}
|
||||
|
||||
public func queryItems() -> [URLQueryItem]? {
|
||||
switch self {
|
||||
case let .rebloggedBy(_, maxId):
|
||||
return makePaginationParam(sinceId: nil, maxId: maxId)
|
||||
case let .favouritedBy(_, maxId):
|
||||
return makePaginationParam(sinceId: nil, maxId: maxId)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ struct NotificationRowView: View {
|
|||
if let type = notification.supportedType {
|
||||
HStack(alignment: .top, spacing: 8) {
|
||||
makeAvatarView(type: type)
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
makeMainLabel(type: type)
|
||||
makeContent(type: type)
|
||||
}
|
||||
|
@ -77,14 +77,14 @@ struct NotificationRowView: View {
|
|||
)
|
||||
.padding(.top, 8)
|
||||
} else {
|
||||
Text(notification.account.acct)
|
||||
Text("@\(notification.account.acct)")
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
|
||||
if type == .follow {
|
||||
Text(notification.account.note.asSafeAttributedString)
|
||||
.lineLimit(3)
|
||||
.font(.body)
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
.environment(\.openURL, OpenURLAction { url in
|
||||
routeurPath.handle(url: url)
|
||||
|
|
|
@ -34,11 +34,12 @@ public struct StatusDetailView: View {
|
|||
.padding(.vertical, DS.Constants.dividerPadding)
|
||||
}
|
||||
}
|
||||
StatusRowView(viewModel: .init(status: status, isEmbed: false))
|
||||
StatusRowView(viewModel: .init(status: status,
|
||||
isEmbed: false,
|
||||
isFocused: true))
|
||||
.id(status.id)
|
||||
makeStatusInfoDetailView(status: status)
|
||||
Divider()
|
||||
.padding(.vertical, DS.Constants.dividerPadding * 2)
|
||||
.padding(.bottom, DS.Constants.dividerPadding * 2)
|
||||
if !context.descendants.isEmpty {
|
||||
ForEach(context.descendants) { descendant in
|
||||
StatusRowView(viewModel: .init(status: descendant, isEmbed: false))
|
||||
|
@ -66,15 +67,4 @@ public struct StatusDetailView: View {
|
|||
.navigationTitle(viewModel.title)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func makeStatusInfoDetailView(status: Status) -> some View {
|
||||
HStack {
|
||||
Text(status.createdAt.asDate, style: .date)
|
||||
Text(status.createdAt.asDate, style: .time)
|
||||
Spacer()
|
||||
Text(status.application?.name ?? "")
|
||||
}
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import Network
|
|||
struct StatusActionsView: View {
|
||||
@EnvironmentObject private var routeurPath: RouterPath
|
||||
@ObservedObject var viewModel: StatusRowViewModel
|
||||
|
||||
|
||||
@MainActor
|
||||
enum Actions: CaseIterable {
|
||||
case respond, boost, favourite, share
|
||||
|
@ -39,32 +39,73 @@ struct StatusActionsView: View {
|
|||
}
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
ForEach(Actions.allCases, id: \.self) { action in
|
||||
if action == .share {
|
||||
if let url = viewModel.status.reblog?.url ?? viewModel.status.url {
|
||||
ShareLink(item: url) {
|
||||
Image(systemName: action.iconName(viewModel: viewModel))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Button {
|
||||
handleAction(action: action)
|
||||
} label: {
|
||||
HStack(spacing: 2) {
|
||||
Image(systemName: action.iconName(viewModel: viewModel))
|
||||
if let count = action.count(viewModel: viewModel) {
|
||||
Text("\(count)")
|
||||
.font(.footnote)
|
||||
VStack(spacing: 12) {
|
||||
HStack {
|
||||
ForEach(Actions.allCases, id: \.self) { action in
|
||||
if action == .share {
|
||||
if let url = viewModel.status.reblog?.url ?? viewModel.status.url {
|
||||
ShareLink(item: url) {
|
||||
Image(systemName: action.iconName(viewModel: viewModel))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Button {
|
||||
handleAction(action: action)
|
||||
} label: {
|
||||
HStack(spacing: 2) {
|
||||
Image(systemName: action.iconName(viewModel: viewModel))
|
||||
if let count = action.count(viewModel: viewModel) {
|
||||
Text("\(count)")
|
||||
.font(.footnote)
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
Spacer()
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
if viewModel.isFocused {
|
||||
summaryView
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var summaryView: some View {
|
||||
HStack {
|
||||
Text(viewModel.status.createdAt.asDate, style: .date)
|
||||
Text(viewModel.status.createdAt.asDate, style: .time)
|
||||
Spacer()
|
||||
Text(viewModel.status.application?.name ?? "")
|
||||
}
|
||||
.font(.caption)
|
||||
if viewModel.favouritesCount > 0 {
|
||||
Divider()
|
||||
Button {
|
||||
routeurPath.navigate(to: .favouritedBy(id: viewModel.status.id))
|
||||
} label: {
|
||||
HStack {
|
||||
Text("\(viewModel.favouritesCount) favorites")
|
||||
Spacer()
|
||||
Image(systemName: "chevron.right")
|
||||
}
|
||||
.font(.callout)
|
||||
}
|
||||
}
|
||||
if viewModel.reblogsCount > 0 {
|
||||
Divider()
|
||||
Button {
|
||||
routeurPath.navigate(to: .rebloggedBy(id: viewModel.status.id))
|
||||
} label: {
|
||||
HStack {
|
||||
Text("\(viewModel.reblogsCount) boosts")
|
||||
Spacer()
|
||||
Image(systemName: "chevron.right")
|
||||
}
|
||||
.font(.callout)
|
||||
}
|
||||
}
|
||||
.tint(.gray)
|
||||
}
|
||||
|
||||
private func handleAction(action: Actions) {
|
||||
|
|
|
@ -24,6 +24,7 @@ public struct StatusRowView: View {
|
|||
if !viewModel.isEmbed {
|
||||
StatusActionsView(viewModel: viewModel)
|
||||
.padding(.vertical, 8)
|
||||
.tint(viewModel.isFocused ? .brand : .gray)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
|
|
|
@ -6,6 +6,7 @@ import Network
|
|||
public class StatusRowViewModel: ObservableObject {
|
||||
let status: Status
|
||||
let isEmbed: Bool
|
||||
let isFocused: Bool
|
||||
|
||||
@Published var favouritesCount: Int
|
||||
@Published var isFavourited: Bool
|
||||
|
@ -15,9 +16,12 @@ public class StatusRowViewModel: ObservableObject {
|
|||
|
||||
var client: Client?
|
||||
|
||||
public init(status: Status, isEmbed: Bool) {
|
||||
public init(status: Status,
|
||||
isEmbed: Bool = false,
|
||||
isFocused: Bool = false) {
|
||||
self.status = status
|
||||
self.isEmbed = isEmbed
|
||||
self.isFocused = isFocused
|
||||
if let reblog = status.reblog {
|
||||
self.isFavourited = reblog.favourited == true
|
||||
self.isReblogged = reblog.reblogged == true
|
||||
|
|
|
@ -53,7 +53,7 @@ public struct TimelineView: View {
|
|||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("#\(tag.name)")
|
||||
.font(.headline)
|
||||
Text("\(tag.totalUses) posts from \(tag.totalAccounts) participants")
|
||||
Text("\(tag.totalUses) recent posts from \(tag.totalAccounts) participants")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue