Featured tags
This commit is contained in:
parent
07188a6818
commit
0f2c2df624
|
@ -15,8 +15,8 @@ extension View {
|
|||
AccountDetailView(account: account)
|
||||
case let .statusDetail(id):
|
||||
StatusDetailView(statusId: id)
|
||||
case let .hashTag(tag):
|
||||
TimelineView(timeline: .hashtag(tag: tag))
|
||||
case let .hashTag(tag, accountId):
|
||||
TimelineView(timeline: .hashtag(tag: tag, accountId: accountId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,8 @@ public struct AccountDetailView: View {
|
|||
} content: {
|
||||
LazyVStack {
|
||||
headerView
|
||||
featuredTagsView
|
||||
.offset(y: -36)
|
||||
if isCurrentUser {
|
||||
Picker("", selection: $viewModel.selectedTab) {
|
||||
ForEach(AccountDetailViewModel.Tab.allCases, id: \.self) { tab in
|
||||
|
@ -107,6 +109,29 @@ public struct AccountDetailView: View {
|
|||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var featuredTagsView: some View {
|
||||
if !viewModel.featuredTags.isEmpty {
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack(spacing: 4) {
|
||||
ForEach(viewModel.featuredTags) { tag in
|
||||
Button {
|
||||
routeurPath.navigate(to: .hashTag(tag: tag.name, account: viewModel.accountId))
|
||||
} label: {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text("#\(tag.name)")
|
||||
.font(.callout)
|
||||
Text("\(tag.statusesCount) posts")
|
||||
.font(.caption2)
|
||||
}
|
||||
}.buttonStyle(.bordered)
|
||||
}
|
||||
}
|
||||
.padding(.leading, DS.Constants.layoutPadding)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func makeTagsListView(tags: [Tag]) -> some View {
|
||||
Group {
|
||||
ForEach(tags) { tag in
|
||||
|
@ -123,7 +148,7 @@ public struct AccountDetailView: View {
|
|||
.padding(.horizontal, DS.Constants.layoutPadding)
|
||||
.padding(.vertical, 8)
|
||||
.onTapGesture {
|
||||
routeurPath.navigate(to: .hashTag(tag: tag.name))
|
||||
routeurPath.navigate(to: .hashTag(tag: tag.name, account: nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
@Published var relationship: Relationshionship?
|
||||
@Published var favourites: [Status] = []
|
||||
@Published var followedTags: [Tag] = []
|
||||
@Published var featuredTags: [FeaturedTag] = []
|
||||
@Published var selectedTab = Tab.statuses {
|
||||
didSet {
|
||||
reloadTabState()
|
||||
|
@ -82,6 +83,8 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
let relationships: [Relationshionship] = try await client.get(endpoint: Accounts.relationships(id: accountId))
|
||||
self.relationship = relationships.first
|
||||
}
|
||||
self.featuredTags = try await client.get(endpoint: Accounts.featuredTags(id: accountId))
|
||||
self.featuredTags.sort { $0.statusesCountInt > $1.statusesCountInt }
|
||||
self.title = account.displayName
|
||||
accountState = .data(account: account)
|
||||
} catch {
|
||||
|
@ -93,7 +96,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
guard let client else { return }
|
||||
do {
|
||||
tabState = .statuses(statusesState: .loading)
|
||||
statuses = try await client.get(endpoint: Accounts.statuses(id: accountId, sinceId: nil))
|
||||
statuses = try await client.get(endpoint: Accounts.statuses(id: accountId, sinceId: nil, tag: nil))
|
||||
if isCurrentUser {
|
||||
favourites = try await client.get(endpoint: Accounts.favourites)
|
||||
}
|
||||
|
@ -110,7 +113,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
case .statuses:
|
||||
guard let lastId = statuses.last?.id else { return }
|
||||
tabState = .statuses(statusesState: .display(statuses: statuses, nextPageState: .loadingNextPage))
|
||||
let newStatuses: [Status] = try await client.get(endpoint: Accounts.statuses(id: accountId, sinceId: lastId))
|
||||
let newStatuses: [Status] = try await client.get(endpoint: Accounts.statuses(id: accountId, sinceId: lastId, tag: nil))
|
||||
statuses.append(contentsOf: newStatuses)
|
||||
tabState = .statuses(statusesState: .display(statuses: statuses, nextPageState: .hasNextPage))
|
||||
case .favourites, .followedTags:
|
||||
|
|
|
@ -25,3 +25,13 @@ public struct Tag: Codable, Identifiable {
|
|||
history.compactMap{ Int($0.accounts) }.reduce(0, +)
|
||||
}
|
||||
}
|
||||
|
||||
public struct FeaturedTag: Codable, Identifiable {
|
||||
public let id: String
|
||||
public let name: String
|
||||
public let url: URL
|
||||
public let statusesCount: String
|
||||
public var statusesCountInt: Int {
|
||||
Int(statusesCount) ?? 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@ public enum Accounts: Endpoint {
|
|||
case accounts(id: String)
|
||||
case favourites
|
||||
case followedTags
|
||||
case featuredTags
|
||||
case featuredTags(id: String)
|
||||
case verifyCredentials
|
||||
case statuses(id: String, sinceId: String?)
|
||||
case statuses(id: String, sinceId: String?, tag: String?)
|
||||
case relationships(id: String)
|
||||
case follow(id: String)
|
||||
case unfollow(id: String)
|
||||
|
@ -19,11 +19,11 @@ public enum Accounts: Endpoint {
|
|||
return "favourites"
|
||||
case .followedTags:
|
||||
return "followed_tags"
|
||||
case .featuredTags:
|
||||
return "featured_tags"
|
||||
case .featuredTags(let id):
|
||||
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"
|
||||
|
@ -36,9 +36,15 @@ public enum Accounts: Endpoint {
|
|||
|
||||
public func queryItems() -> [URLQueryItem]? {
|
||||
switch self {
|
||||
case .statuses(_, let sinceId):
|
||||
guard let sinceId else { return nil }
|
||||
return [.init(name: "max_id", value: sinceId)]
|
||||
case .statuses(_, let sinceId, let tag):
|
||||
var params: [URLQueryItem] = []
|
||||
if let tag {
|
||||
params.append(.init(name: "tagged", value: tag))
|
||||
}
|
||||
if let sinceId {
|
||||
params.append(.init(name: "max_id", value: sinceId))
|
||||
}
|
||||
return params
|
||||
case let .relationships(id):
|
||||
return [.init(name: "id", value: id)]
|
||||
default:
|
||||
|
|
|
@ -6,7 +6,7 @@ public enum RouteurDestinations: Hashable {
|
|||
case accountDetail(id: String)
|
||||
case accountDetailWithAccount(account: Account)
|
||||
case statusDetail(id: String)
|
||||
case hashTag(tag: String)
|
||||
case hashTag(tag: String, account: String?)
|
||||
}
|
||||
|
||||
public enum SheetDestinations: Identifiable {
|
||||
|
@ -33,7 +33,7 @@ public class RouterPath: ObservableObject {
|
|||
public func handleStatus(status: AnyStatus, url: URL) -> OpenURLAction.Result {
|
||||
if url.pathComponents.contains(where: { $0 == "tags" }),
|
||||
let tag = url.pathComponents.last {
|
||||
navigate(to: .hashTag(tag: tag))
|
||||
navigate(to: .hashTag(tag: tag, account: nil))
|
||||
return .handled
|
||||
} else if let mention = status.mentions.first(where: { $0.url == url }) {
|
||||
navigate(to: .accountDetail(id: mention.id))
|
||||
|
|
|
@ -4,7 +4,7 @@ import Network
|
|||
|
||||
public enum TimelineFilter: Hashable, Equatable {
|
||||
case pub, home
|
||||
case hashtag(tag: String)
|
||||
case hashtag(tag: String, accountId: String?)
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(title())
|
||||
|
@ -20,17 +20,21 @@ public enum TimelineFilter: Hashable, Equatable {
|
|||
return "Public"
|
||||
case .home:
|
||||
return "Home"
|
||||
case let .hashtag(tag):
|
||||
case let .hashtag(tag, _):
|
||||
return "#\(tag)"
|
||||
}
|
||||
}
|
||||
|
||||
func endpoint(sinceId: String?) -> Timelines {
|
||||
func endpoint(sinceId: String?) -> Endpoint {
|
||||
switch self {
|
||||
case .pub: return .pub(sinceId: sinceId)
|
||||
case .home: return .home(sinceId: sinceId)
|
||||
case let .hashtag(tag):
|
||||
return .hashtag(tag: tag, sinceId: sinceId)
|
||||
case .pub: return Timelines.pub(sinceId: sinceId)
|
||||
case .home: return Timelines.home(sinceId: sinceId)
|
||||
case let .hashtag(tag, accountId):
|
||||
if let accountId {
|
||||
return Accounts.statuses(id: accountId, sinceId: nil, tag: tag)
|
||||
} else {
|
||||
return Timelines.hashtag(tag: tag, sinceId: sinceId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ class TimelineViewModel: ObservableObject, StatusesFetcher {
|
|||
Task {
|
||||
await fetchStatuses()
|
||||
switch timeline {
|
||||
case let .hashtag(tag):
|
||||
case let .hashtag(tag, _):
|
||||
await fetchTag(id: tag)
|
||||
default:
|
||||
break
|
||||
|
|
Loading…
Reference in New Issue