mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-26 10:11:00 +00:00
Multi accounts
This commit is contained in:
parent
dd5a6a8b45
commit
3a076492a1
16 changed files with 146 additions and 30 deletions
|
@ -12,6 +12,8 @@
|
||||||
9F2B92F6295AE04800DE16D0 /* Tabs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F2B92F5295AE04800DE16D0 /* Tabs.swift */; };
|
9F2B92F6295AE04800DE16D0 /* Tabs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F2B92F5295AE04800DE16D0 /* Tabs.swift */; };
|
||||||
9F2B92FA295DA7D700DE16D0 /* AddAccountsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F2B92F9295DA7D700DE16D0 /* AddAccountsView.swift */; };
|
9F2B92FA295DA7D700DE16D0 /* AddAccountsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F2B92F9295DA7D700DE16D0 /* AddAccountsView.swift */; };
|
||||||
9F2B92FC295DA94500DE16D0 /* InstanceInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F2B92FB295DA94500DE16D0 /* InstanceInfoView.swift */; };
|
9F2B92FC295DA94500DE16D0 /* InstanceInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F2B92FB295DA94500DE16D0 /* InstanceInfoView.swift */; };
|
||||||
|
9F2B92FF295EB87100DE16D0 /* AppAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F2B92FE295EB87100DE16D0 /* AppAccountView.swift */; };
|
||||||
|
9F2B9301295EB8A100DE16D0 /* AppAccountViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F2B9300295EB8A100DE16D0 /* AppAccountViewModel.swift */; };
|
||||||
9F35DB44294F9A7D00B3281A /* Status in Frameworks */ = {isa = PBXBuildFile; productRef = 9F35DB43294F9A7D00B3281A /* Status */; };
|
9F35DB44294F9A7D00B3281A /* Status in Frameworks */ = {isa = PBXBuildFile; productRef = 9F35DB43294F9A7D00B3281A /* Status */; };
|
||||||
9F35DB4729506F6600B3281A /* NotificationTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F35DB4629506F6600B3281A /* NotificationTab.swift */; };
|
9F35DB4729506F6600B3281A /* NotificationTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F35DB4629506F6600B3281A /* NotificationTab.swift */; };
|
||||||
9F35DB4A29506FA100B3281A /* Notifications in Frameworks */ = {isa = PBXBuildFile; productRef = 9F35DB4929506FA100B3281A /* Notifications */; };
|
9F35DB4A29506FA100B3281A /* Notifications in Frameworks */ = {isa = PBXBuildFile; productRef = 9F35DB4929506FA100B3281A /* Notifications */; };
|
||||||
|
@ -40,6 +42,8 @@
|
||||||
9F2B92F5295AE04800DE16D0 /* Tabs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tabs.swift; sourceTree = "<group>"; };
|
9F2B92F5295AE04800DE16D0 /* Tabs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tabs.swift; sourceTree = "<group>"; };
|
||||||
9F2B92F9295DA7D700DE16D0 /* AddAccountsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAccountsView.swift; sourceTree = "<group>"; };
|
9F2B92F9295DA7D700DE16D0 /* AddAccountsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAccountsView.swift; sourceTree = "<group>"; };
|
||||||
9F2B92FB295DA94500DE16D0 /* InstanceInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceInfoView.swift; sourceTree = "<group>"; };
|
9F2B92FB295DA94500DE16D0 /* InstanceInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceInfoView.swift; sourceTree = "<group>"; };
|
||||||
|
9F2B92FE295EB87100DE16D0 /* AppAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAccountView.swift; sourceTree = "<group>"; };
|
||||||
|
9F2B9300295EB8A100DE16D0 /* AppAccountViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAccountViewModel.swift; sourceTree = "<group>"; };
|
||||||
9F35DB42294F9A2900B3281A /* Status */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Status; path = Packages/Status; sourceTree = "<group>"; };
|
9F35DB42294F9A2900B3281A /* Status */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Status; path = Packages/Status; sourceTree = "<group>"; };
|
||||||
9F35DB45294FA04C00B3281A /* DesignSystem */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = DesignSystem; path = Packages/DesignSystem; sourceTree = "<group>"; };
|
9F35DB45294FA04C00B3281A /* DesignSystem */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = DesignSystem; path = Packages/DesignSystem; sourceTree = "<group>"; };
|
||||||
9F35DB4629506F6600B3281A /* NotificationTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationTab.swift; sourceTree = "<group>"; };
|
9F35DB4629506F6600B3281A /* NotificationTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationTab.swift; sourceTree = "<group>"; };
|
||||||
|
@ -120,6 +124,8 @@
|
||||||
children = (
|
children = (
|
||||||
9FAE4AD029379AD600772766 /* AppAccount.swift */,
|
9FAE4AD029379AD600772766 /* AppAccount.swift */,
|
||||||
9FAE4AD22937A0C600772766 /* AppAccountsManager.swift */,
|
9FAE4AD22937A0C600772766 /* AppAccountsManager.swift */,
|
||||||
|
9F2B92FE295EB87100DE16D0 /* AppAccountView.swift */,
|
||||||
|
9F2B9300295EB8A100DE16D0 /* AppAccountViewModel.swift */,
|
||||||
);
|
);
|
||||||
path = AppAccounts;
|
path = AppAccounts;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -269,9 +275,11 @@
|
||||||
9F35DB4C2952005C00B3281A /* AccountTab.swift in Sources */,
|
9F35DB4C2952005C00B3281A /* AccountTab.swift in Sources */,
|
||||||
9FAE4ACB293783B000772766 /* SettingsTab.swift in Sources */,
|
9FAE4ACB293783B000772766 /* SettingsTab.swift in Sources */,
|
||||||
9FAE4AD32937A0C600772766 /* AppAccountsManager.swift in Sources */,
|
9FAE4AD32937A0C600772766 /* AppAccountsManager.swift in Sources */,
|
||||||
|
9F2B92FF295EB87100DE16D0 /* AppAccountView.swift in Sources */,
|
||||||
9F2B92F6295AE04800DE16D0 /* Tabs.swift in Sources */,
|
9F2B92F6295AE04800DE16D0 /* Tabs.swift in Sources */,
|
||||||
9F398AB329360A4C00A889F2 /* TimelineTab.swift in Sources */,
|
9F398AB329360A4C00A889F2 /* TimelineTab.swift in Sources */,
|
||||||
9F398AA62935FE8A00A889F2 /* AppRouteur.swift in Sources */,
|
9F398AA62935FE8A00A889F2 /* AppRouteur.swift in Sources */,
|
||||||
|
9F2B9301295EB8A100DE16D0 /* AppAccountViewModel.swift in Sources */,
|
||||||
9FBFE63D292A715500C250E9 /* IceCubesApp.swift in Sources */,
|
9FBFE63D292A715500C250E9 /* IceCubesApp.swift in Sources */,
|
||||||
9F2B92FA295DA7D700DE16D0 /* AddAccountsView.swift in Sources */,
|
9F2B92FA295DA7D700DE16D0 /* AddAccountsView.swift in Sources */,
|
||||||
9F35DB4729506F6600B3281A /* NotificationTab.swift in Sources */,
|
9F35DB4729506F6600B3281A /* NotificationTab.swift in Sources */,
|
||||||
|
|
|
@ -4,10 +4,14 @@ import Network
|
||||||
import KeychainSwift
|
import KeychainSwift
|
||||||
import Models
|
import Models
|
||||||
|
|
||||||
struct AppAccount: Codable {
|
struct AppAccount: Codable, Identifiable {
|
||||||
let server: String
|
let server: String
|
||||||
let oauthToken: OauthToken?
|
let oauthToken: OauthToken?
|
||||||
|
|
||||||
|
var id: String {
|
||||||
|
key
|
||||||
|
}
|
||||||
|
|
||||||
var key: String {
|
var key: String {
|
||||||
if let oauthToken {
|
if let oauthToken {
|
||||||
return "\(server):\(oauthToken.createdAt)"
|
return "\(server):\(oauthToken.createdAt)"
|
||||||
|
|
37
IceCubesApp/App/AppAccounts/AppAccountView.swift
Normal file
37
IceCubesApp/App/AppAccounts/AppAccountView.swift
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import SwiftUI
|
||||||
|
import DesignSystem
|
||||||
|
|
||||||
|
struct AppAccountView: View {
|
||||||
|
@EnvironmentObject var appAccounts: AppAccountsManager
|
||||||
|
@StateObject var viewModel: AppAccountViewModel
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack {
|
||||||
|
if let account = viewModel.account {
|
||||||
|
ZStack(alignment: .topTrailing) {
|
||||||
|
AvatarView(url: account.avatar)
|
||||||
|
if viewModel.appAccount.id == appAccounts.currentAccount.id {
|
||||||
|
Image(systemName: "checkmark.circle.fill")
|
||||||
|
.foregroundColor(.green)
|
||||||
|
.offset(x: 5, y: -5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(viewModel.appAccount.server)
|
||||||
|
.font(.headline)
|
||||||
|
if let account = viewModel.account {
|
||||||
|
Text(account.displayName)
|
||||||
|
Text(account.username)
|
||||||
|
.font(.footnote)
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
Task {
|
||||||
|
await viewModel.fetchAccount()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
IceCubesApp/App/AppAccounts/AppAccountViewModel.swift
Normal file
24
IceCubesApp/App/AppAccounts/AppAccountViewModel.swift
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import SwiftUI
|
||||||
|
import Models
|
||||||
|
import Network
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
public class AppAccountViewModel: ObservableObject {
|
||||||
|
let appAccount: AppAccount
|
||||||
|
let client: Client
|
||||||
|
|
||||||
|
@Published var account: Account?
|
||||||
|
|
||||||
|
init(appAccount: AppAccount) {
|
||||||
|
self.appAccount = appAccount
|
||||||
|
self.client = .init(server: appAccount.server, oauthToken: appAccount.oauthToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchAccount() async {
|
||||||
|
do {
|
||||||
|
account = try await client.get(endpoint: Accounts.verifyCredentials)
|
||||||
|
} catch {
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,11 @@ import SwiftUI
|
||||||
import Network
|
import Network
|
||||||
|
|
||||||
class AppAccountsManager: ObservableObject {
|
class AppAccountsManager: ObservableObject {
|
||||||
|
@AppStorage("latestCurrentAccountKey") static public var latestCurrentAccountKey: String = ""
|
||||||
|
|
||||||
@Published var currentAccount: AppAccount {
|
@Published var currentAccount: AppAccount {
|
||||||
didSet {
|
didSet {
|
||||||
|
Self.latestCurrentAccountKey = currentAccount.id
|
||||||
currentClient = .init(server: currentAccount.server,
|
currentClient = .init(server: currentAccount.server,
|
||||||
oauthToken: currentAccount.oauthToken)
|
oauthToken: currentAccount.oauthToken)
|
||||||
}
|
}
|
||||||
|
@ -16,10 +19,15 @@ class AppAccountsManager: ObservableObject {
|
||||||
do {
|
do {
|
||||||
let keychainAccounts = try AppAccount.retrieveAll()
|
let keychainAccounts = try AppAccount.retrieveAll()
|
||||||
availableAccounts = keychainAccounts
|
availableAccounts = keychainAccounts
|
||||||
defaultAccount = keychainAccounts.last ?? defaultAccount
|
if let currentAccount = keychainAccounts.first(where: { $0.id == Self.latestCurrentAccountKey }) {
|
||||||
} catch {}
|
defaultAccount = currentAccount
|
||||||
|
} else {
|
||||||
|
defaultAccount = keychainAccounts.last ?? defaultAccount
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
availableAccounts = [defaultAccount]
|
||||||
|
}
|
||||||
currentAccount = defaultAccount
|
currentAccount = defaultAccount
|
||||||
availableAccounts = [defaultAccount]
|
|
||||||
currentClient = .init(server: defaultAccount.server, oauthToken: defaultAccount.oauthToken)
|
currentClient = .init(server: defaultAccount.server, oauthToken: defaultAccount.oauthToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,12 +35,15 @@ class AppAccountsManager: ObservableObject {
|
||||||
do {
|
do {
|
||||||
try account.save()
|
try account.save()
|
||||||
currentAccount = account
|
currentAccount = account
|
||||||
|
availableAccounts.append(account)
|
||||||
} catch { }
|
} catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
func delete(account: AppAccount) {
|
func delete(account: AppAccount) {
|
||||||
|
availableAccounts.removeAll(where: { $0.id == account.id })
|
||||||
account.delete()
|
account.delete()
|
||||||
AppAccount.deleteAll()
|
if currentAccount.id == account.id {
|
||||||
currentAccount = AppAccount(server: IceCubesApp.defaultServer, oauthToken: nil)
|
currentAccount = availableAccounts.first ?? AppAccount(server: IceCubesApp.defaultServer, oauthToken: nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ struct AccountTab: View {
|
||||||
.toolbar {
|
.toolbar {
|
||||||
statusEditorToolbarItem(routeurPath: routeurPath)
|
statusEditorToolbarItem(routeurPath: routeurPath)
|
||||||
}
|
}
|
||||||
|
.id(account.id)
|
||||||
} else {
|
} else {
|
||||||
AccountDetailView(account: .placeholder())
|
AccountDetailView(account: .placeholder())
|
||||||
.redacted(reason: .placeholder)
|
.redacted(reason: .placeholder)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import Env
|
||||||
import Network
|
import Network
|
||||||
|
|
||||||
struct ExploreTab: View {
|
struct ExploreTab: View {
|
||||||
|
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||||
@EnvironmentObject private var client: Client
|
@EnvironmentObject private var client: Client
|
||||||
@StateObject private var routeurPath = RouterPath()
|
@StateObject private var routeurPath = RouterPath()
|
||||||
@Binding var popToRootTab: Tab
|
@Binding var popToRootTab: Tab
|
||||||
|
|
|
@ -7,6 +7,7 @@ import Notifications
|
||||||
struct NotificationsTab: View {
|
struct NotificationsTab: View {
|
||||||
@EnvironmentObject private var client: Client
|
@EnvironmentObject private var client: Client
|
||||||
@EnvironmentObject private var watcher: StreamWatcher
|
@EnvironmentObject private var watcher: StreamWatcher
|
||||||
|
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||||
@StateObject private var routeurPath = RouterPath()
|
@StateObject private var routeurPath = RouterPath()
|
||||||
@Binding var popToRootTab: Tab
|
@Binding var popToRootTab: Tab
|
||||||
|
|
||||||
|
@ -18,6 +19,7 @@ struct NotificationsTab: View {
|
||||||
.toolbar {
|
.toolbar {
|
||||||
statusEditorToolbarItem(routeurPath: routeurPath)
|
statusEditorToolbarItem(routeurPath: routeurPath)
|
||||||
}
|
}
|
||||||
|
.id(currentAccount.account?.id)
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
routeurPath.client = client
|
routeurPath.client = client
|
||||||
|
|
|
@ -8,7 +8,6 @@ import DesignSystem
|
||||||
|
|
||||||
struct SettingsTabs: View {
|
struct SettingsTabs: View {
|
||||||
@EnvironmentObject private var client: Client
|
@EnvironmentObject private var client: Client
|
||||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
|
||||||
@EnvironmentObject private var currentInstance: CurrentInstance
|
@EnvironmentObject private var currentInstance: CurrentInstance
|
||||||
@EnvironmentObject private var appAccountsManager: AppAccountsManager
|
@EnvironmentObject private var appAccountsManager: AppAccountsManager
|
||||||
@EnvironmentObject private var theme: Theme
|
@EnvironmentObject private var theme: Theme
|
||||||
|
@ -30,7 +29,6 @@ struct SettingsTabs: View {
|
||||||
}
|
}
|
||||||
.task {
|
.task {
|
||||||
if appAccountsManager.currentAccount.oauthToken != nil {
|
if appAccountsManager.currentAccount.oauthToken != nil {
|
||||||
await currentAccount.fetchCurrentAccount()
|
|
||||||
await currentInstance.fetchCurrentInstance()
|
await currentInstance.fetchCurrentInstance()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,19 +36,21 @@ struct SettingsTabs: View {
|
||||||
|
|
||||||
private var accountsSection: some View {
|
private var accountsSection: some View {
|
||||||
Section("Account") {
|
Section("Account") {
|
||||||
if let accountData = currentAccount.account {
|
ForEach(appAccountsManager.availableAccounts) { account in
|
||||||
HStack {
|
HStack {
|
||||||
AvatarView(url: accountData.avatar)
|
AppAccountView(viewModel: .init(appAccount: account))
|
||||||
VStack(alignment: .leading) {
|
}
|
||||||
Text(appAccountsManager.currentAccount.server)
|
.onTapGesture {
|
||||||
.font(.headline)
|
withAnimation {
|
||||||
Text(accountData.displayName)
|
appAccountsManager.currentAccount = account
|
||||||
Text(accountData.username)
|
|
||||||
.font(.footnote)
|
|
||||||
.foregroundColor(.gray)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
signOutButton
|
}
|
||||||
|
.onDelete { indexSet in
|
||||||
|
if let index = indexSet.first {
|
||||||
|
let account = appAccountsManager.availableAccounts[index]
|
||||||
|
appAccountsManager.delete(account: account)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
addAccountButton
|
addAccountButton
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import Network
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
struct TimelineTab: View {
|
struct TimelineTab: View {
|
||||||
|
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||||
@EnvironmentObject private var client: Client
|
@EnvironmentObject private var client: Client
|
||||||
@StateObject private var routeurPath = RouterPath()
|
@StateObject private var routeurPath = RouterPath()
|
||||||
@Binding var popToRootTab: Tab
|
@Binding var popToRootTab: Tab
|
||||||
|
@ -23,6 +24,7 @@ struct TimelineTab: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.id(currentAccount.account?.id)
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
routeurPath.client = client
|
routeurPath.client = client
|
||||||
|
|
|
@ -85,6 +85,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
||||||
async let featuredTags: [FeaturedTag] = client.get(endpoint: Accounts.featuredTags(id: accountId))
|
async let featuredTags: [FeaturedTag] = client.get(endpoint: Accounts.featuredTags(id: accountId))
|
||||||
async let familliarFollowers: [FamilliarAccounts] = client.get(endpoint: Accounts.familiarFollowers(withAccount: accountId))
|
async let familliarFollowers: [FamilliarAccounts] = client.get(endpoint: Accounts.familiarFollowers(withAccount: accountId))
|
||||||
let loadedAccount = try await account
|
let loadedAccount = try await account
|
||||||
|
self.account = loadedAccount
|
||||||
self.featuredTags = try await featuredTags
|
self.featuredTags = try await featuredTags
|
||||||
self.featuredTags.sort { $0.statusesCountInt > $1.statusesCountInt }
|
self.featuredTags.sort { $0.statusesCountInt > $1.statusesCountInt }
|
||||||
self.fields = loadedAccount.fields
|
self.fields = loadedAccount.fields
|
||||||
|
@ -96,10 +97,13 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
||||||
self.relationship = relationships.first
|
self.relationship = relationships.first
|
||||||
self.familliarFollowers = try await familliarFollowers.first?.accounts ?? []
|
self.familliarFollowers = try await familliarFollowers.first?.accounts ?? []
|
||||||
}
|
}
|
||||||
self.account = loadedAccount
|
|
||||||
accountState = .data(account: loadedAccount)
|
accountState = .data(account: loadedAccount)
|
||||||
} catch {
|
} catch {
|
||||||
accountState = .error(error: error)
|
if let account {
|
||||||
|
accountState = .data(account: account)
|
||||||
|
} else {
|
||||||
|
accountState = .error(error: error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,6 @@ public struct ExploreView: View {
|
||||||
}
|
}
|
||||||
.task {
|
.task {
|
||||||
viewModel.client = client
|
viewModel.client = client
|
||||||
guard !viewModel.isLoaded else { return }
|
|
||||||
await viewModel.fetchTrending()
|
await viewModel.fetchTrending()
|
||||||
}
|
}
|
||||||
.refreshable {
|
.refreshable {
|
||||||
|
|
|
@ -4,7 +4,18 @@ import Network
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
class ExploreViewModel: ObservableObject {
|
class ExploreViewModel: ObservableObject {
|
||||||
var client: Client?
|
var client: Client? {
|
||||||
|
didSet {
|
||||||
|
if oldValue != client {
|
||||||
|
isLoaded = false
|
||||||
|
results = [:]
|
||||||
|
trendingTags = []
|
||||||
|
trendingLinks = []
|
||||||
|
trendingStatuses = []
|
||||||
|
suggestedAccounts = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum Token: String, Identifiable {
|
enum Token: String, Identifiable {
|
||||||
case user = "@user"
|
case user = "@user"
|
||||||
|
@ -56,8 +67,6 @@ class ExploreViewModel: ObservableObject {
|
||||||
func fetchTrending() async {
|
func fetchTrending() async {
|
||||||
guard let client else { return }
|
guard let client else { return }
|
||||||
do {
|
do {
|
||||||
isLoaded = false
|
|
||||||
|
|
||||||
async let suggestedAccounts: [Account] = client.get(endpoint: Accounts.suggestions)
|
async let suggestedAccounts: [Account] = client.get(endpoint: Accounts.suggestions)
|
||||||
async let trendingTags: [Tag] = client.get(endpoint: Trends.tags)
|
async let trendingTags: [Tag] = client.get(endpoint: Trends.tags)
|
||||||
async let trendingStatuses: [Status] = client.get(endpoint: Trends.statuses)
|
async let trendingStatuses: [Status] = client.get(endpoint: Trends.statuses)
|
||||||
|
@ -71,7 +80,9 @@ class ExploreViewModel: ObservableObject {
|
||||||
self.suggestedAccountsRelationShips = try await client.get(endpoint: Accounts.relationships(ids: self.suggestedAccounts.map{ $0.id }))
|
self.suggestedAccountsRelationShips = try await client.get(endpoint: Accounts.relationships(ids: self.suggestedAccounts.map{ $0.id }))
|
||||||
|
|
||||||
isLoaded = true
|
isLoaded = true
|
||||||
} catch { }
|
} catch {
|
||||||
|
isLoaded = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func search() {
|
func search() {
|
||||||
|
|
|
@ -108,7 +108,7 @@ extension Models.Notification.NotificationType {
|
||||||
case .status:
|
case .status:
|
||||||
return "posted a status"
|
return "posted a status"
|
||||||
case .mention:
|
case .mention:
|
||||||
return "mentionned you"
|
return "mentioned you"
|
||||||
case .reblog:
|
case .reblog:
|
||||||
return "boosted"
|
return "boosted"
|
||||||
case .follow:
|
case .follow:
|
||||||
|
|
|
@ -19,7 +19,13 @@ class NotificationsViewModel: ObservableObject {
|
||||||
case mentions = "Mentions"
|
case mentions = "Mentions"
|
||||||
}
|
}
|
||||||
|
|
||||||
var client: Client?
|
var client: Client? {
|
||||||
|
didSet {
|
||||||
|
if oldValue != client {
|
||||||
|
notifications = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@Published var state: State = .loading
|
@Published var state: State = .loading
|
||||||
@Published var tab: Tab = .all {
|
@Published var tab: Tab = .all {
|
||||||
didSet {
|
didSet {
|
||||||
|
|
|
@ -6,7 +6,13 @@ import Env
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
class TimelineViewModel: ObservableObject, StatusesFetcher {
|
class TimelineViewModel: ObservableObject, StatusesFetcher {
|
||||||
var client: Client?
|
var client: Client? {
|
||||||
|
didSet {
|
||||||
|
if oldValue != client {
|
||||||
|
statuses = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Internal source of truth for a timeline.
|
// Internal source of truth for a timeline.
|
||||||
private var statuses: [Status] = []
|
private var statuses: [Status] = []
|
||||||
|
@ -66,14 +72,14 @@ class TimelineViewModel: ObservableObject, StatusesFetcher {
|
||||||
pendingStatuses = []
|
pendingStatuses = []
|
||||||
statusesState = .loading
|
statusesState = .loading
|
||||||
statuses = try await client.get(endpoint: timeline.endpoint(sinceId: nil, maxId: nil, minId: nil))
|
statuses = try await client.get(endpoint: timeline.endpoint(sinceId: nil, maxId: nil, minId: nil))
|
||||||
statusesState = .display(statuses: statuses, nextPageState: .hasNextPage)
|
statusesState = .display(statuses: statuses, nextPageState: statuses.count < 20 ? .none : .hasNextPage)
|
||||||
} else if let first = statuses.first {
|
} else if let first = statuses.first {
|
||||||
var newStatuses: [Status] = await fetchNewPages(minId: first.id, maxPages: 10)
|
var newStatuses: [Status] = await fetchNewPages(minId: first.id, maxPages: 10)
|
||||||
if userIntent || !pendingStatusesEnabled {
|
if userIntent || !pendingStatusesEnabled {
|
||||||
pendingStatuses = []
|
pendingStatuses = []
|
||||||
statuses.insert(contentsOf: newStatuses, at: 0)
|
statuses.insert(contentsOf: newStatuses, at: 0)
|
||||||
withAnimation {
|
withAnimation {
|
||||||
statusesState = .display(statuses: statuses, nextPageState: .hasNextPage)
|
statusesState = .display(statuses: statuses, nextPageState: statuses.count < 20 ? .none : .hasNextPage)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newStatuses = newStatuses.filter { status in
|
newStatuses = newStatuses.filter { status in
|
||||||
|
|
Loading…
Reference in a new issue