mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-05-19 00:38:10 +00:00
196 lines
6.9 KiB
Swift
196 lines
6.9 KiB
Swift
import SwiftUI
|
|
import Network
|
|
import Models
|
|
import Status
|
|
import Env
|
|
|
|
@MainActor
|
|
class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|
let accountId: String
|
|
var client: Client?
|
|
var isCurrentUser: Bool = false
|
|
|
|
enum AccountState {
|
|
case loading, data(account: Account), error(error: Error)
|
|
}
|
|
|
|
enum Tab: Int, CaseIterable {
|
|
case statuses, favourites, followedTags
|
|
|
|
var title: String {
|
|
switch self {
|
|
case .statuses: return "Posts"
|
|
case .favourites: return "Favourites"
|
|
case .followedTags: return "Followed Tags"
|
|
}
|
|
}
|
|
}
|
|
|
|
enum TabState {
|
|
case followedTags(tags: [Tag])
|
|
case statuses(statusesState: StatusesState)
|
|
}
|
|
|
|
@Published var accountState: AccountState = .loading
|
|
@Published 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
|
|
}
|
|
}
|
|
}
|
|
@Published var statusesState: StatusesState = .loading
|
|
|
|
@Published var title: String = ""
|
|
@Published var relationship: Relationshionship?
|
|
@Published var favourites: [Status] = []
|
|
private var favouritesNextPage: LinkHandler?
|
|
@Published var followedTags: [Tag] = []
|
|
@Published var featuredTags: [FeaturedTag] = []
|
|
@Published var fields: [Account.Field] = []
|
|
@Published var familliarFollowers: [Account] = []
|
|
@Published var selectedTab = Tab.statuses {
|
|
didSet {
|
|
reloadTabState()
|
|
}
|
|
}
|
|
|
|
private var account: Account?
|
|
|
|
private(set) var statuses: [Status] = []
|
|
|
|
/// When coming from a URL like a mention tap in a status.
|
|
init(accountId: String) {
|
|
self.accountId = accountId
|
|
self.isCurrentUser = false
|
|
}
|
|
|
|
/// When the account is already fetched by the parent caller.
|
|
init(account: Account) {
|
|
self.accountId = account.id
|
|
self.accountState = .data(account: account)
|
|
}
|
|
|
|
func fetchAccount() async {
|
|
guard let client else { return }
|
|
do {
|
|
async let account: Account = client.get(endpoint: Accounts.accounts(id: accountId))
|
|
async let followedTags: [Tag] = client.get(endpoint: Accounts.followedTags)
|
|
async let relationships: [Relationshionship] = client.get(endpoint: Accounts.relationships(ids: [accountId]))
|
|
async let featuredTags: [FeaturedTag] = client.get(endpoint: Accounts.featuredTags(id: accountId))
|
|
async let familliarFollowers: [FamilliarAccounts] = client.get(endpoint: Accounts.familiarFollowers(withAccount: accountId))
|
|
let loadedAccount = try await account
|
|
self.account = loadedAccount
|
|
self.featuredTags = try await featuredTags
|
|
self.featuredTags.sort { $0.statusesCountInt > $1.statusesCountInt }
|
|
self.fields = loadedAccount.fields
|
|
self.title = loadedAccount.displayName
|
|
if isCurrentUser {
|
|
self.followedTags = try await followedTags
|
|
} else {
|
|
let relationships = try await relationships
|
|
self.relationship = relationships.first
|
|
self.familliarFollowers = try await familliarFollowers.first?.accounts ?? []
|
|
}
|
|
accountState = .data(account: loadedAccount)
|
|
} catch {
|
|
if let account {
|
|
accountState = .data(account: account)
|
|
} else {
|
|
accountState = .error(error: error)
|
|
}
|
|
}
|
|
}
|
|
|
|
func fetchStatuses() async {
|
|
guard let client else { return }
|
|
do {
|
|
tabState = .statuses(statusesState: .loading)
|
|
statuses = try await client.get(endpoint: Accounts.statuses(id: accountId, sinceId: nil, tag: nil))
|
|
if isCurrentUser {
|
|
(favourites, favouritesNextPage) = try await client.getWithLink(endpoint: Accounts.favourites(sinceId: nil))
|
|
}
|
|
reloadTabState()
|
|
} catch {
|
|
tabState = .statuses(statusesState: .error(error: error))
|
|
}
|
|
}
|
|
|
|
func fetchNextPage() async {
|
|
guard let client else { return }
|
|
do {
|
|
switch selectedTab {
|
|
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, tag: nil))
|
|
statuses.append(contentsOf: newStatuses)
|
|
tabState = .statuses(statusesState: .display(statuses: statuses,
|
|
nextPageState: newStatuses.count < 20 ? .none : .hasNextPage))
|
|
case .favourites:
|
|
guard let nextPageId = favouritesNextPage?.maxId else { return }
|
|
let newFavourites: [Status]
|
|
(newFavourites, favouritesNextPage) = try await client.getWithLink(endpoint: Accounts.favourites(sinceId: nextPageId))
|
|
favourites.append(contentsOf: newFavourites)
|
|
tabState = .statuses(statusesState: .display(statuses: favourites, nextPageState: .hasNextPage))
|
|
case .followedTags:
|
|
break
|
|
}
|
|
} catch {
|
|
tabState = .statuses(statusesState: .error(error: error))
|
|
}
|
|
}
|
|
|
|
func follow() async {
|
|
guard let client else { return }
|
|
do {
|
|
relationship = try await client.post(endpoint: Accounts.follow(id: accountId))
|
|
} catch {
|
|
print("Error while following: \(error.localizedDescription)")
|
|
}
|
|
}
|
|
|
|
func unfollow() async {
|
|
guard let client else { return }
|
|
do {
|
|
relationship = try await client.post(endpoint: Accounts.unfollow(id: accountId))
|
|
} catch {
|
|
print("Error while unfollowing: \(error.localizedDescription)")
|
|
}
|
|
}
|
|
|
|
private func reloadTabState() {
|
|
switch selectedTab {
|
|
case .statuses:
|
|
tabState = .statuses(statusesState: .display(statuses: statuses, nextPageState: statuses.count < 20 ? .none : .hasNextPage))
|
|
case .favourites:
|
|
tabState = .statuses(statusesState: .display(statuses: favourites,
|
|
nextPageState: favouritesNextPage != nil ? .hasNextPage : .none))
|
|
case .followedTags:
|
|
tabState = .followedTags(tags: followedTags)
|
|
}
|
|
}
|
|
|
|
func handleEvent(event: any StreamEvent, currentAccount: CurrentAccount) {
|
|
if let event = event as? StreamEventUpdate {
|
|
if event.status.account.id == currentAccount.account?.id {
|
|
statuses.insert(event.status, at: 0)
|
|
statusesState = .display(statuses: statuses, nextPageState: .hasNextPage)
|
|
}
|
|
} else if let event = event as? StreamEventDelete {
|
|
statuses.removeAll(where: { $0.id == event.status })
|
|
statusesState = .display(statuses: statuses, nextPageState: .hasNextPage)
|
|
} else if let event = event as? StreamEventStatusUpdate {
|
|
if let originalIndex = statuses.firstIndex(where: { $0.id == event.status.id }) {
|
|
statuses[originalIndex] = event.status
|
|
statusesState = .display(statuses: statuses, nextPageState: .hasNextPage)
|
|
}
|
|
}
|
|
}
|
|
}
|