Featured tags

This commit is contained in:
Thomas Ricouard 2022-12-21 20:26:38 +01:00
parent 07188a6818
commit 0f2c2df624
8 changed files with 71 additions and 23 deletions

View file

@ -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))
}
}
}

View file

@ -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))
}
}
}

View file

@ -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:

View file

@ -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
}
}

View file

@ -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:

View file

@ -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))

View file

@ -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)
}
}
}
}

View file

@ -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