Account Switcher
This commit is contained in:
parent
75483985ab
commit
9a99638896
|
@ -8,7 +8,7 @@ public class AccountManager: ObservableObject {
|
|||
private var client: Client?
|
||||
private var account: Account?
|
||||
|
||||
public static let shared: AccountManager = AccountManager()
|
||||
public static var shared: AccountManager = AccountManager()
|
||||
|
||||
init(client: Client? = nil, account: Account? = nil) {
|
||||
self.client = client
|
||||
|
|
|
@ -5,16 +5,22 @@ import SwiftUI
|
|||
import SwiftData
|
||||
|
||||
@Model
|
||||
class LoggedAccounts {
|
||||
let appAccounts: [AppAccount]
|
||||
let currentAccount: AppAccount
|
||||
class LoggedAccount {
|
||||
let token: OauthToken
|
||||
let acct: String
|
||||
let app: AppAccount?
|
||||
|
||||
static let shared: LoggedAccounts = LoggedAccounts()
|
||||
init(token: OauthToken, acct: String) {
|
||||
self.token = token
|
||||
self.acct = acct
|
||||
self.app = nil
|
||||
}
|
||||
|
||||
init(appAccounts: [AppAccount] = [], current: AppAccount? = nil) {
|
||||
let curr: AppAccount = current ?? AppAccount.loadAsCurrent()!
|
||||
self.appAccounts = appAccounts.count < 1 ? [curr] : appAccounts
|
||||
self.currentAccount = curr
|
||||
init(appAccount: AppAccount) {
|
||||
guard let token = appAccount.oauthToken, let acct = appAccount.accountName else { fatalError("Cannot convert AppAccount to LoggedAccount") }
|
||||
self.token = token
|
||||
self.acct = acct
|
||||
self.app = appAccount
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,6 +28,6 @@ public extension View {
|
|||
@ViewBuilder
|
||||
func modelData() -> some View {
|
||||
self
|
||||
.modelContainer(for: LoggedAccounts.self)
|
||||
.modelContainer(for: LoggedAccount.self)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1399,6 +1399,70 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"settings.account-switcher.add" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Add new account"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Ajouter un nouveau compte"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings.account-switcher.current" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Current"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Actuel"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings.account-switcher.log" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Log in"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Connecter"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings.account-switcher.relog" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "To get access to the Account Switcher, please log out and log back"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Pour avoir accès au Account Switcher, veuillez vous déconnecter puis reconnecter"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings.cancel" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
//Made by Lumaa
|
||||
|
||||
import SwiftUI
|
||||
import SwiftData
|
||||
import AuthenticationServices
|
||||
|
||||
struct AddInstanceView: View {
|
||||
@Environment(\.webAuthenticationSession) private var webAuthenticationSession
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@Environment(\.modelContext) private var modelContext
|
||||
|
||||
@Query private var loggedAccounts: [LoggedAccount]
|
||||
|
||||
// Instance URL and verify
|
||||
@State private var instanceUrl: String = ""
|
||||
|
@ -216,7 +220,7 @@ struct AddInstanceView: View {
|
|||
return
|
||||
}
|
||||
|
||||
if agreedResponsability && responsability {
|
||||
if agreedResponsability && responsability && loggedAccounts.count <= 0 {
|
||||
UserDefaults.standard.setValue(true, forKey: "unsafe")
|
||||
} else {
|
||||
UserDefaults.standard.removeObject(forKey: "unsafe")
|
||||
|
@ -235,10 +239,16 @@ struct AddInstanceView: View {
|
|||
AccountManager.shared.setClient(client)
|
||||
AccountManager.shared.setAccount(account)
|
||||
|
||||
let newLog: LoggedAccount = .init(appAccount: appAcc)
|
||||
modelContext.insert(newLog)
|
||||
|
||||
HapticManager.playHaptics(haptics: Haptic.success)
|
||||
|
||||
signInClient = client
|
||||
logged = true
|
||||
dismiss()
|
||||
} catch {
|
||||
HapticManager.playHaptics(haptics: Haptic.error)
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,66 @@
|
|||
//Made by Lumaa
|
||||
|
||||
import SwiftUI
|
||||
import SwiftData
|
||||
|
||||
//TODO: Bring back "Privacy" with mutelist, blocklist and default visibility
|
||||
|
||||
struct SettingsView: View {
|
||||
@Environment(UniversalNavigator.self) private var uniNav: UniversalNavigator
|
||||
|
||||
@Query private var loggedAccounts: [LoggedAccount]
|
||||
|
||||
@StateObject var navigator: Navigator
|
||||
@State private var switched: Bool = false
|
||||
|
||||
var body: some View {
|
||||
NavigationStack(path: $navigator.path) {
|
||||
List {
|
||||
if loggedAccounts.count > 0 {
|
||||
Section {
|
||||
ForEach(loggedAccounts) { logged in
|
||||
if let app = logged.app {
|
||||
SwitcherRow(app: app)
|
||||
.listRowThreaded()
|
||||
}
|
||||
}
|
||||
if AppDelegate.premium || loggedAccounts.count < 3 {
|
||||
Button {
|
||||
uniNav.presentedSheet = .mastodonLogin(logged: $switched)
|
||||
} label: {
|
||||
Label("settings.account-switcher.add", systemImage: "person.crop.circle.badge.plus")
|
||||
.foregroundStyle(Color.blue)
|
||||
}
|
||||
.listRowThreaded()
|
||||
}
|
||||
}
|
||||
.onChange(of: switched) { _, new in
|
||||
if new == true {
|
||||
// switched correctly
|
||||
HapticManager.playHaptics(haptics: Haptic.success)
|
||||
uniNav.selectedTab = .timeline
|
||||
navigator.path = []
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Section {
|
||||
//MARK: Remove in later update
|
||||
HStack(alignment: .center) {
|
||||
Spacer()
|
||||
Text("settings.account-switcher.relog")
|
||||
.foregroundStyle(Color.gray)
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.caption)
|
||||
Spacer()
|
||||
}
|
||||
.listRowThreaded()
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
.frame(height: 30)
|
||||
.listRowThreaded()
|
||||
|
||||
Section {
|
||||
Button {
|
||||
navigator.navigate(to: .about)
|
||||
|
@ -53,6 +103,7 @@ struct SettingsView: View {
|
|||
.listRowThreaded()
|
||||
}
|
||||
}
|
||||
.environmentObject(navigator)
|
||||
.withAppRouter(navigator)
|
||||
.withCovers(sheetDestination: $navigator.presentedCover)
|
||||
.listThreaded()
|
||||
|
@ -62,6 +113,116 @@ struct SettingsView: View {
|
|||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
SettingsView(navigator: .init())
|
||||
extension SettingsView {
|
||||
struct SwitcherRow: View {
|
||||
@Environment(AccountManager.self) private var accountManager: AccountManager
|
||||
@Environment(UniversalNavigator.self) private var uniNav: UniversalNavigator
|
||||
@EnvironmentObject private var navigator: Navigator
|
||||
|
||||
var app: AppAccount
|
||||
|
||||
@State private var account: Account? = nil
|
||||
@State private var error: Bool = false
|
||||
|
||||
private var currentAccount: Bool {
|
||||
return AccountManager.shared.forceClient().server == app.server
|
||||
}
|
||||
|
||||
init(app: AppAccount) {
|
||||
self.app = app
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
if let acc = account {
|
||||
HStack {
|
||||
ProfilePicture(url: acc.avatar, size: 64)
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
Text(acc.displayName ?? "@\(acc.acct)")
|
||||
.multilineTextAlignment(.leading)
|
||||
|
||||
Text("@\(acc.acct)")
|
||||
.multilineTextAlignment(.leading)
|
||||
.font(.caption)
|
||||
.foregroundStyle(Color.gray)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
if !currentAccount {
|
||||
Button {
|
||||
Task {
|
||||
let c: Client = Client(server: app.server, oauthToken: app.oauthToken)
|
||||
let am: AccountManager = .init(client: c)
|
||||
|
||||
let fetched: Account? = await am.fetchAccount()
|
||||
if fetched == nil {
|
||||
am.clear()
|
||||
error = true
|
||||
} else {
|
||||
AccountManager.shared.setAccount(fetched!)
|
||||
AccountManager.shared.setClient(c)
|
||||
uniNav.selectedTab = .timeline
|
||||
navigator.path = []
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Text("settings.account-switcher.log")
|
||||
}
|
||||
.buttonStyle(LargeButton(filled: true, height: 7.5, disabled: currentAccount))
|
||||
.disabled(currentAccount)
|
||||
} else {
|
||||
Text("settings.account-switcher.current")
|
||||
.foregroundStyle(Color.gray)
|
||||
.font(.caption)
|
||||
.padding(.horizontal)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Circle()
|
||||
.fill(error ? Color.red.opacity(0.45) : Color.gray.opacity(0.45))
|
||||
.frame(width: 54, height: 54)
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
Text(Account.placeholder().displayName ?? "@\(Account.placeholder().acct)")
|
||||
.redacted(reason: .placeholder)
|
||||
.multilineTextAlignment(.leading)
|
||||
|
||||
Text("@\(Account.placeholder().acct)")
|
||||
.redacted(reason: .placeholder)
|
||||
.multilineTextAlignment(.leading)
|
||||
.font(.caption)
|
||||
.foregroundStyle(Color.gray)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Button {
|
||||
print(acct)
|
||||
} label: {
|
||||
Text("settings.account-switcher.log")
|
||||
.redacted(reason: .placeholder)
|
||||
}
|
||||
.buttonStyle(LargeButton(filled: true, height: 7.5))
|
||||
}
|
||||
}
|
||||
.task {
|
||||
account = await findAccount(acct: app.accountName!)
|
||||
}
|
||||
}
|
||||
|
||||
private func findAccount(acct: String) async -> Account? {
|
||||
guard let client = accountManager.getClient() else { return nil }
|
||||
do {
|
||||
try await Task.sleep(for: .milliseconds(250))
|
||||
let results: SearchResults = try await client.get(endpoint: Search.search(query: acct, type: "accounts", offset: nil, following: nil), forceVersion: .v2)
|
||||
return results.accounts.first
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue