mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2025-02-16 17:55:13 +00:00
Fix #1836
This commit is contained in:
parent
e725b6be4d
commit
5d24c4d2e8
4 changed files with 84 additions and 132 deletions
|
@ -63,9 +63,15 @@ public struct AccountDetailView: View {
|
|||
ForEach(isCurrentUser ? AccountDetailViewModel.Tab.currentAccountTabs : AccountDetailViewModel.Tab.accountTabs,
|
||||
id: \.self)
|
||||
{ tab in
|
||||
Image(systemName: tab.iconName)
|
||||
.tag(tab)
|
||||
.accessibilityLabel(tab.accessibilityLabel)
|
||||
if tab == .boosts {
|
||||
Image("Rocket")
|
||||
.tag(tab)
|
||||
.accessibilityLabel(tab.accessibilityLabel)
|
||||
} else {
|
||||
Image(systemName: tab.iconName)
|
||||
.tag(tab)
|
||||
.accessibilityLabel(tab.accessibilityLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
|
@ -73,19 +79,12 @@ public struct AccountDetailView: View {
|
|||
.applyAccountDetailsRowStyle(theme: theme)
|
||||
.id("status")
|
||||
|
||||
switch viewModel.tabState {
|
||||
case .statuses:
|
||||
if viewModel.selectedTab == .statuses {
|
||||
pinnedPostsView
|
||||
}
|
||||
StatusesListView(fetcher: viewModel,
|
||||
client: client,
|
||||
routerPath: routerPath)
|
||||
case .followedTags:
|
||||
tagsListView
|
||||
case .lists:
|
||||
listsListView
|
||||
if viewModel.selectedTab == .statuses {
|
||||
pinnedPostsView
|
||||
}
|
||||
StatusesListView(fetcher: viewModel,
|
||||
client: client,
|
||||
routerPath: routerPath)
|
||||
}
|
||||
.environment(\.defaultMinListRowHeight, 1)
|
||||
.listStyle(.plain)
|
||||
|
@ -236,56 +235,6 @@ public struct AccountDetailView: View {
|
|||
}
|
||||
}
|
||||
|
||||
private var tagsListView: some View {
|
||||
Group {
|
||||
ForEach(currentAccount.sortedTags) { tag in
|
||||
HStack {
|
||||
TagRowView(tag: tag)
|
||||
Spacer()
|
||||
Image(systemName: "chevron.right")
|
||||
}
|
||||
#if !os(visionOS)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
#endif
|
||||
}
|
||||
}.task {
|
||||
await currentAccount.fetchFollowedTags()
|
||||
}
|
||||
}
|
||||
|
||||
private var listsListView: some View {
|
||||
Group {
|
||||
ForEach(currentAccount.sortedLists) { list in
|
||||
NavigationLink(value: RouterDestination.list(list: list)) {
|
||||
Text(list.title)
|
||||
.font(.scaledHeadline)
|
||||
.foregroundColor(theme.labelColor)
|
||||
}
|
||||
#if !os(visionOS)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
#endif
|
||||
.contextMenu {
|
||||
Button("account.list.delete", role: .destructive) {
|
||||
Task {
|
||||
await currentAccount.deleteList(list: list)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Button("account.list.create") {
|
||||
routerPath.presentedSheet = .listCreate
|
||||
}
|
||||
.tint(theme.tintColor)
|
||||
.buttonStyle(.borderless)
|
||||
#if !os(visionOS)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
#endif
|
||||
}
|
||||
.task {
|
||||
await currentAccount.fetchLists()
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var pinnedPostsView: some View {
|
||||
if !viewModel.pinned.isEmpty {
|
||||
|
|
|
@ -16,14 +16,14 @@ import SwiftUI
|
|||
}
|
||||
|
||||
enum Tab: Int {
|
||||
case statuses, favorites, bookmarks, followedTags, postsAndReplies, media, lists
|
||||
case statuses, favorites, bookmarks, replies, boosts, media
|
||||
|
||||
static var currentAccountTabs: [Tab] {
|
||||
[.statuses, .favorites, .bookmarks, .followedTags, .lists]
|
||||
[.statuses, .replies, .boosts, .favorites, .bookmarks]
|
||||
}
|
||||
|
||||
static var accountTabs: [Tab] {
|
||||
[.statuses, .postsAndReplies, .media]
|
||||
[.statuses, .replies, .boosts, .media]
|
||||
}
|
||||
|
||||
var iconName: String {
|
||||
|
@ -31,10 +31,9 @@ import SwiftUI
|
|||
case .statuses: "bubble.right"
|
||||
case .favorites: "star"
|
||||
case .bookmarks: "bookmark"
|
||||
case .followedTags: "tag"
|
||||
case .postsAndReplies: "bubble.left.and.bubble.right"
|
||||
case .replies: "bubble.left.and.bubble.right"
|
||||
case .boosts: ""
|
||||
case .media: "photo.on.rectangle.angled"
|
||||
case .lists: "list.bullet"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,34 +42,14 @@ import SwiftUI
|
|||
case .statuses: "accessibility.tabs.profile.picker.statuses"
|
||||
case .favorites: "accessibility.tabs.profile.picker.favorites"
|
||||
case .bookmarks: "accessibility.tabs.profile.picker.bookmarks"
|
||||
case .followedTags: "accessibility.tabs.profile.picker.followed-tags"
|
||||
case .postsAndReplies: "accessibility.tabs.profile.picker.posts-and-replies"
|
||||
case .replies: "accessibility.tabs.profile.picker.posts-and-replies"
|
||||
case .boosts: "accessibility.tabs.profile.picker.boosts"
|
||||
case .media: "accessibility.tabs.profile.picker.media"
|
||||
case .lists: "accessibility.tabs.profile.picker.lists"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum TabState {
|
||||
case followedTags
|
||||
case statuses(statusesState: StatusesState)
|
||||
case lists
|
||||
}
|
||||
|
||||
var accountState: AccountState = .loading
|
||||
var tabState: TabState = .statuses(statusesState: .loading) {
|
||||
didSet {
|
||||
/// Forward viewModel tabState related to statusesState to statusesState property
|
||||
/// for `StatusesFetcher` conformance as we wrap StatusesState in TabState
|
||||
switch tabState {
|
||||
case let .statuses(statusesState):
|
||||
self.statusesState = statusesState
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var statusesState: StatusesState = .loading
|
||||
|
||||
var relationship: Relationship?
|
||||
|
@ -85,7 +64,7 @@ import SwiftUI
|
|||
var selectedTab = Tab.statuses {
|
||||
didSet {
|
||||
switch selectedTab {
|
||||
case .statuses, .postsAndReplies, .media:
|
||||
case .statuses, .replies, .boosts, .media:
|
||||
tabTask?.cancel()
|
||||
tabTask = Task {
|
||||
await fetchNewestStatuses(pullToRefresh: false)
|
||||
|
@ -105,6 +84,8 @@ import SwiftUI
|
|||
private var tabTask: Task<Void, Never>?
|
||||
|
||||
private(set) var statuses: [Status] = []
|
||||
|
||||
var boosts: [Status] = []
|
||||
|
||||
/// When coming from a URL like a mention tap in a status.
|
||||
init(accountId: String) {
|
||||
|
@ -173,22 +154,28 @@ import SwiftUI
|
|||
func fetchNewestStatuses(pullToRefresh: Bool) async {
|
||||
guard let client else { return }
|
||||
do {
|
||||
tabState = .statuses(statusesState: .loading)
|
||||
statusesState = .loading
|
||||
boosts = []
|
||||
statuses =
|
||||
try await client.get(endpoint: Accounts.statuses(id: accountId,
|
||||
sinceId: nil,
|
||||
tag: nil,
|
||||
onlyMedia: selectedTab == .media ? true : nil,
|
||||
excludeReplies: selectedTab == .statuses && !isCurrentUser ? true : nil,
|
||||
onlyMedia: selectedTab == .media,
|
||||
excludeReplies: selectedTab != .replies,
|
||||
excludeReblogs: selectedTab != .boosts,
|
||||
pinned: nil))
|
||||
StatusDataControllerProvider.shared.updateDataControllers(for: statuses, client: client)
|
||||
if selectedTab == .boosts {
|
||||
boosts = statuses.filter{ $0.reblog != nil }
|
||||
}
|
||||
if selectedTab == .statuses {
|
||||
pinned =
|
||||
try await client.get(endpoint: Accounts.statuses(id: accountId,
|
||||
sinceId: nil,
|
||||
tag: nil,
|
||||
onlyMedia: nil,
|
||||
excludeReplies: nil,
|
||||
onlyMedia: false,
|
||||
excludeReplies: false,
|
||||
excludeReblogs: false,
|
||||
pinned: true))
|
||||
StatusDataControllerProvider.shared.updateDataControllers(for: pinned, client: client)
|
||||
}
|
||||
|
@ -200,7 +187,7 @@ import SwiftUI
|
|||
}
|
||||
reloadTabState()
|
||||
} catch {
|
||||
tabState = .statuses(statusesState: .error(error: error))
|
||||
statusesState = .error(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,56 +195,66 @@ import SwiftUI
|
|||
guard let client else { return }
|
||||
do {
|
||||
switch selectedTab {
|
||||
case .statuses, .postsAndReplies, .media:
|
||||
case .statuses, .replies, .boosts, .media:
|
||||
guard let lastId = statuses.last?.id else { return }
|
||||
tabState = .statuses(statusesState: .display(statuses: statuses, nextPageState: .loadingNextPage))
|
||||
if selectedTab == .boosts {
|
||||
statusesState = .display(statuses: boosts, nextPageState: .loadingNextPage)
|
||||
} else {
|
||||
statusesState = .display(statuses: statuses, nextPageState: .loadingNextPage)
|
||||
}
|
||||
let newStatuses: [Status] =
|
||||
try await client.get(endpoint: Accounts.statuses(id: accountId,
|
||||
sinceId: lastId,
|
||||
tag: nil,
|
||||
onlyMedia: selectedTab == .media ? true : nil,
|
||||
excludeReplies: selectedTab == .statuses && !isCurrentUser ? true : nil,
|
||||
onlyMedia: selectedTab == .media,
|
||||
excludeReplies: selectedTab != .replies,
|
||||
excludeReblogs: selectedTab != .boosts,
|
||||
pinned: nil))
|
||||
statuses.append(contentsOf: newStatuses)
|
||||
if selectedTab == .boosts {
|
||||
let newBoosts = statuses.filter{ $0.reblog != nil }
|
||||
self.boosts.append(contentsOf: newBoosts)
|
||||
}
|
||||
StatusDataControllerProvider.shared.updateDataControllers(for: newStatuses, client: client)
|
||||
tabState = .statuses(statusesState: .display(statuses: statuses,
|
||||
nextPageState: newStatuses.count < 20 ? .none : .hasNextPage))
|
||||
if selectedTab == .boosts {
|
||||
statusesState = .display(statuses: boosts,
|
||||
nextPageState: newStatuses.count < 20 ? .none : .hasNextPage)
|
||||
} else {
|
||||
statusesState = .display(statuses: statuses,
|
||||
nextPageState: newStatuses.count < 20 ? .none : .hasNextPage)
|
||||
}
|
||||
case .favorites:
|
||||
guard let nextPageId = favoritesNextPage?.maxId else { return }
|
||||
let newFavorites: [Status]
|
||||
(newFavorites, favoritesNextPage) = try await client.getWithLink(endpoint: Accounts.favorites(sinceId: nextPageId))
|
||||
favorites.append(contentsOf: newFavorites)
|
||||
StatusDataControllerProvider.shared.updateDataControllers(for: newFavorites, client: client)
|
||||
tabState = .statuses(statusesState: .display(statuses: favorites, nextPageState: .hasNextPage))
|
||||
statusesState = .display(statuses: favorites, nextPageState: .hasNextPage)
|
||||
case .bookmarks:
|
||||
guard let nextPageId = bookmarksNextPage?.maxId else { return }
|
||||
let newBookmarks: [Status]
|
||||
(newBookmarks, bookmarksNextPage) = try await client.getWithLink(endpoint: Accounts.bookmarks(sinceId: nextPageId))
|
||||
StatusDataControllerProvider.shared.updateDataControllers(for: newBookmarks, client: client)
|
||||
bookmarks.append(contentsOf: newBookmarks)
|
||||
tabState = .statuses(statusesState: .display(statuses: bookmarks, nextPageState: .hasNextPage))
|
||||
case .followedTags, .lists:
|
||||
break
|
||||
statusesState = .display(statuses: bookmarks, nextPageState: .hasNextPage)
|
||||
}
|
||||
} catch {
|
||||
tabState = .statuses(statusesState: .error(error: error))
|
||||
statusesState = .error(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func reloadTabState() {
|
||||
switch selectedTab {
|
||||
case .statuses, .postsAndReplies, .media:
|
||||
tabState = .statuses(statusesState: .display(statuses: statuses, nextPageState: statuses.count < 20 ? .none : .hasNextPage))
|
||||
case .statuses, .replies, .media:
|
||||
statusesState = .display(statuses: statuses, nextPageState: statuses.count < 20 ? .none : .hasNextPage)
|
||||
case .boosts:
|
||||
statusesState = .display(statuses: boosts, nextPageState: statuses.count < 20 ? .none : .hasNextPage)
|
||||
case .favorites:
|
||||
tabState = .statuses(statusesState: .display(statuses: favorites,
|
||||
nextPageState: favoritesNextPage != nil ? .hasNextPage : .none))
|
||||
statusesState = .display(statuses: favorites,
|
||||
nextPageState: favoritesNextPage != nil ? .hasNextPage : .none)
|
||||
case .bookmarks:
|
||||
tabState = .statuses(statusesState: .display(statuses: bookmarks,
|
||||
nextPageState: bookmarksNextPage != nil ? .hasNextPage : .none))
|
||||
case .followedTags:
|
||||
tabState = .followedTags
|
||||
case .lists:
|
||||
tabState = .lists
|
||||
statusesState = .display(statuses: bookmarks,
|
||||
nextPageState: bookmarksNextPage != nil ? .hasNextPage : .none)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,8 +14,9 @@ public enum Accounts: Endpoint {
|
|||
case statuses(id: String,
|
||||
sinceId: String?,
|
||||
tag: String?,
|
||||
onlyMedia: Bool?,
|
||||
excludeReplies: Bool?,
|
||||
onlyMedia: Bool,
|
||||
excludeReplies: Bool,
|
||||
excludeReblogs: Bool,
|
||||
pinned: Bool?)
|
||||
case relationships(ids: [String])
|
||||
case follow(id: String, notify: Bool, reblogs: Bool)
|
||||
|
@ -50,7 +51,7 @@ public enum Accounts: Endpoint {
|
|||
"accounts/verify_credentials"
|
||||
case .updateCredentials, .updateCredentialsMedia:
|
||||
"accounts/update_credentials"
|
||||
case let .statuses(id, _, _, _, _, _):
|
||||
case let .statuses(id, _, _, _, _, _, _):
|
||||
"accounts/\(id)/statuses"
|
||||
case .relationships:
|
||||
"accounts/relationships"
|
||||
|
@ -89,7 +90,7 @@ public enum Accounts: Endpoint {
|
|||
return [
|
||||
.init(name: "acct", value: name),
|
||||
]
|
||||
case let .statuses(_, sinceId, tag, onlyMedia, excludeReplies, pinned):
|
||||
case let .statuses(_, sinceId, tag, onlyMedia, excludeReplies, excludeReblogs, pinned):
|
||||
var params: [URLQueryItem] = []
|
||||
if let tag {
|
||||
params.append(.init(name: "tagged", value: tag))
|
||||
|
@ -97,12 +98,11 @@ public enum Accounts: Endpoint {
|
|||
if let sinceId {
|
||||
params.append(.init(name: "max_id", value: sinceId))
|
||||
}
|
||||
if let onlyMedia {
|
||||
params.append(.init(name: "only_media", value: onlyMedia ? "true" : "false"))
|
||||
}
|
||||
if let excludeReplies {
|
||||
params.append(.init(name: "exclude_replies", value: excludeReplies ? "true" : "false"))
|
||||
}
|
||||
|
||||
params.append(.init(name: "only_media", value: onlyMedia ? "true" : "false"))
|
||||
params.append(.init(name: "exclude_replies", value: excludeReplies ? "true" : "false"))
|
||||
params.append(.init(name: "exclude_reblogs", value: excludeReblogs ? "true" : "false"))
|
||||
|
||||
if let pinned {
|
||||
params.append(.init(name: "pinned", value: pinned ? "true" : "false"))
|
||||
}
|
||||
|
|
|
@ -160,7 +160,13 @@ public enum TimelineFilter: Hashable, Equatable, Identifiable {
|
|||
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, pinned: nil)
|
||||
return Accounts.statuses(id: accountId,
|
||||
sinceId: nil,
|
||||
tag: tag,
|
||||
onlyMedia: false,
|
||||
excludeReplies: false,
|
||||
excludeReblogs: false,
|
||||
pinned: nil)
|
||||
} else {
|
||||
return Timelines.hashtag(tag: tag, additional: nil, maxId: maxId, minId: minId)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue