Tusker/ShareExtension/SwitchAccountContainerView....

142 lines
4.2 KiB
Swift

//
// SwitchAccountContainerView.swift
// ShareExtension
//
// Created by Shadowfacts on 4/19/23.
// Copyright © 2023 Shadowfacts. All rights reserved.
//
import SwiftUI
import UserAccounts
import TuskerPreferences
import Pachyderm
import Combine
import ComposeUI
struct SwitchAccountContainerView: View {
let content: AnyView
let mastodonContextPublisher: CurrentValueSubject<ShareMastodonContext, Never>
var accounts: [UserAccountInfo] {
UserAccountsManager.shared.accounts
}
var body: some View {
if accounts.count > 1 {
Menu {
ForEach(accounts) { account in
Button(action: { selectAccount(account) }) {
AccountButtonLabel(account: account)
}
}
} label: {
HStack(alignment: .center) {
VStack(spacing: 2) {
Image(systemName: "arrowtriangle.up.fill")
.resizable()
.frame(width: 10, height: 5)
Image(systemName: "arrowtriangle.down.fill")
.resizable()
.frame(width: 10, height: 5)
}
.foregroundColor(.secondary)
content
}
}
} else {
content
}
}
private func selectAccount(_ account: UserAccountInfo) {
mastodonContextPublisher.send(ShareMastodonContext(accountInfo: account))
}
}
@MainActor
private struct AccountButtonLabel: View {
static let urlSession = URLSession(configuration: .ephemeral)
let account: UserAccountInfo
@State private var avatarImage: Image?
var body: some View {
label
.task {
await fetchAvatar()
}
}
@ViewBuilder
private var label: some View {
// subtitles only started being supported on 16.4
if #available(iOS 16.4, *) {
Label {
Text(account.username)
} icon: {
avatar
}
Text(account.instanceURL.host!)
} else {
Label {
Text("@\(account.username)@\(account.instanceURL.host!)")
} icon: {
avatar
}
}
}
@ViewBuilder
private var avatar: some View {
if let avatarImage {
avatarImage
} else {
avatarPlaceholder
}
}
private var avatarPlaceholder: Image {
switch Preferences.shared.avatarStyle {
case .circle:
return Image(systemName: "person.crop.circle")
case .roundRect:
return Image(systemName: "person.crop.square")
}
}
private func fetchAvatar() async {
let client = Client(baseURL: account.instanceURL, accessToken: account.accessToken, session: Self.urlSession)
let account: Account? = await withCheckedContinuation({ continuation in
client.run(Client.getSelfAccount()) { response in
switch response {
case .success(let account, _):
continuation.resume(returning: account)
case .failure(_):
continuation.resume(returning: nil)
}
}
})
if let account,
let avatarURL = account.avatar,
let data = try? await Self.urlSession.data(from: avatarURL).0,
let image = UIImage(data: data) {
let size = CGSize(width: 50, height: 50)
let renderer = UIGraphicsImageRenderer(size: size)
let clipped = renderer.image { context in
let bounds = CGRect(origin: .zero, size: size)
let path: UIBezierPath
switch Preferences.shared.avatarStyle {
case .circle:
path = UIBezierPath(ovalIn: bounds)
case .roundRect:
path = UIBezierPath(roundedRect: bounds, cornerRadius: 5)
}
path.addClip()
image.draw(in: bounds)
}
self.avatarImage = Image(uiImage: clipped)
}
}
}