// // 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 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) } } }