Skip to content

Commit

Permalink
All new accounts selector
Browse files Browse the repository at this point in the history
  • Loading branch information
Dimillian committed Mar 8, 2023
1 parent 5c69aa6 commit 15b704c
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 119 deletions.
6 changes: 6 additions & 0 deletions Packages/Account/Sources/Account/AccountDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,12 @@ public struct AccountDetailView: View {
} label: {
Label("settings.push.navigation-title", systemImage: "bell")
}

Button {
routerPath.presentedSheet = .settings
} label: {
Label("settings.title", systemImage: "gear")
}
}
} label: {
Image(systemName: "ellipsis.circle")
Expand Down
25 changes: 21 additions & 4 deletions Packages/AppAccount/Sources/AppAccount/AppAccountView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import SwiftUI

public struct AppAccountView: View {
@EnvironmentObject private var routerPath: RouterPath
@EnvironmentObject var appAccounts: AppAccountsManager
@EnvironmentObject private var appAccounts: AppAccountsManager
@EnvironmentObject private var preferences: UserPreferences

@StateObject var viewModel: AppAccountViewModel

public init(viewModel: AppAccountViewModel) {
Expand Down Expand Up @@ -47,6 +49,19 @@ public struct AppAccountView: View {
Image(systemName: "checkmark.circle.fill")
.foregroundStyle(.white, .green)
.offset(x: 5, y: -5)
} else if viewModel.showBadge,
let token = viewModel.appAccount.oauthToken,
let notificationsCount = preferences.getNotificationsCount(for: token),
notificationsCount > 0{
ZStack {
Circle()
.fill(.red)
Text(notificationsCount > 99 ? "99+" : String(notificationsCount))
.foregroundColor(.white)
.font(.system(size: 9))
}
.frame(width: 20, height: 20)
.offset(x: 5, y: -5)
}
}
} else {
Expand All @@ -66,9 +81,11 @@ public struct AppAccountView: View {
.foregroundColor(.gray)
}
}
Spacer()
Image(systemName: "chevron.right")
.foregroundColor(.gray)
if viewModel.isInNavigation {
Spacer()
Image(systemName: "chevron.right")
.foregroundColor(.gray)
}
}
.contentShape(Rectangle())
.onTapGesture {
Expand Down
28 changes: 5 additions & 23 deletions Packages/AppAccount/Sources/AppAccount/AppAccountViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public class AppAccountViewModel: ObservableObject {
var appAccount: AppAccount
let client: Client
let isCompact: Bool
let isInNavigation: Bool
let showBadge: Bool

@Published var account: Account? {
didSet {
Expand All @@ -21,8 +23,6 @@ public class AppAccountViewModel: ObservableObject {
}
}

@Published var roundedAvatar: UIImage?

var acct: String {
if let acct = appAccount.accountName {
return acct
Expand All @@ -31,24 +31,20 @@ public class AppAccountViewModel: ObservableObject {
}
}

public init(appAccount: AppAccount, isCompact: Bool = false) {
public init(appAccount: AppAccount, isCompact: Bool = false, isInNavigation: Bool = true, showBadge: Bool = false) {
self.appAccount = appAccount
self.isCompact = isCompact
self.isInNavigation = isInNavigation
self.showBadge = showBadge
client = .init(server: appAccount.server, oauthToken: appAccount.oauthToken)
}

func fetchAccount() async {
do {
account = Self.accountsCache[appAccount.id]
roundedAvatar = Self.avatarsCache[appAccount.id]

account = try await client.get(endpoint: Accounts.verifyCredentials)
Self.accountsCache[appAccount.id] = account

if let account {
await refreshAvatar(account: account)
}

} catch {}
}

Expand All @@ -60,18 +56,4 @@ public class AppAccountViewModel: ObservableObject {
}
} catch {}
}

private func refreshAvatar(account: Account) async {
// Warning: Non-sendable type '(any URLSessionTaskDelegate)?' exiting main actor-isolated
// context in call to non-isolated instance method 'data(for:delegate:)' cannot cross actor
// boundary.
// This is on the defaulted-to-nil second parameter of `.data(from:delegate:)`.
// There is a Radar tracking this & others like it.
if let (data, _) = try? await URLSession.shared.data(from: account.avatar),
let image = UIImage(data: data)?.roundedImage
{
roundedAvatar = image
Self.avatarsCache[account.id] = image
}
}
}
142 changes: 65 additions & 77 deletions Packages/AppAccount/Sources/AppAccount/AppAccountsSelectorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ public struct AppAccountsSelectorView: View {
@EnvironmentObject private var preferences: UserPreferences
@EnvironmentObject private var currentAccount: CurrentAccount
@EnvironmentObject private var appAccounts: AppAccountsManager
@EnvironmentObject private var theme: Theme

@ObservedObject var routerPath: RouterPath

@State private var accountsViewModel: [AppAccountViewModel] = []
@State private var isPresented: Bool = false

private let accountCreationEnabled: Bool
private let avatarSize: AvatarView.Size
Expand All @@ -32,26 +34,18 @@ public struct AppAccountsSelectorView: View {
}

public var body: some View {
Group {
if UIDevice.current.userInterfaceIdiom == .pad {
labelView
.contextMenu {
menuView
}
} else {
Menu {
menuView
} label: {
labelView
}
}
}
.onTapGesture {
Button {
isPresented.toggle()
HapticManager.shared.fireHaptic(of: .buttonPress)
} label: {
labelView
}
.onAppear {
refreshAccounts()
}
.sheet(isPresented: $isPresented, content: {
accountsView.presentationDetents([.medium])
.onAppear {
refreshAccounts()
}
})
.onChange(of: currentAccount.account?.id) { _ in
refreshAccounts()
}
Expand All @@ -67,7 +61,7 @@ public struct AppAccountsSelectorView: View {
.redacted(reason: .placeholder)
}
}.overlay(alignment: .topTrailing) {
if !currentAccount.followRequests.isEmpty || showNotificationBadge {
if (!currentAccount.followRequests.isEmpty || showNotificationBadge) && accountCreationEnabled {
Circle()
.fill(Color.red)
.frame(width: 9, height: 9)
Expand All @@ -76,77 +70,71 @@ public struct AppAccountsSelectorView: View {
.accessibilityLabel("accessibility.app-account.selector.accounts")
}

@ViewBuilder
private var menuView: some View {
ForEach(accountsViewModel.sorted { $0.acct < $1.acct }, id: \.appAccount.id) { viewModel in
Section(viewModel.acct) {
Button {
if let account = currentAccount.account,
viewModel.account?.id == account.id
{
routerPath.navigate(to: .accountDetailWithAccount(account: account))
} else {
var transation = Transaction()
transation.disablesAnimations = true
withTransaction(transation) {
appAccounts.currentAccount = viewModel.appAccount
}
private var accountsView: some View {
NavigationStack {
List {
Section {
ForEach(accountsViewModel.sorted { $0.acct < $1.acct }, id: \.appAccount.id) { viewModel in
AppAccountView(viewModel: viewModel)
}

HapticManager.shared.fireHaptic(of: .buttonPress)
} label: {
HStack {
if let image = viewModel.roundedAvatar {
Image(uiImage: image)
}

let name = viewModel.account.flatMap { account in
account.displayName?.isEmpty != false ? "@\(account.acct)" : account.displayName
} ?? ""
if let token = viewModel.appAccount.oauthToken,
preferences.getNotificationsCount(for: token) > 0
{
Text("\(name) (\(preferences.getNotificationsCount(for: token)))")
} else {
Text("\(name)")
}
.listRowBackground(theme.primaryBackgroundColor)

if accountCreationEnabled {
Section {
Button {
isPresented = false
HapticManager.shared.fireHaptic(of: .buttonPress)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
routerPath.presentedSheet = .addAccount
}
} label: {
Label("app-account.button.add", systemImage: "person.badge.plus")
}
settingsButton
}
.listRowBackground(theme.primaryBackgroundColor)
}
}
}
if accountCreationEnabled {
Divider()
Button {
HapticManager.shared.fireHaptic(of: .buttonPress)
routerPath.presentedSheet = .addAccount
} label: {
Label("app-account.button.add", systemImage: "person.badge.plus")
.listStyle(.insetGrouped)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.navigationTitle("settings.section.accounts")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
isPresented.toggle()
} label: {
Image(systemName: "xmark.circle")
}
}
if accountCreationEnabled {
ToolbarItem(placement: .navigationBarTrailing) {
settingsButton
}
}
}
}

if UIDevice.current.userInterfaceIdiom == .phone && accountCreationEnabled {
Divider()
Button {
HapticManager.shared.fireHaptic(of: .buttonPress)
}

private var settingsButton: some View {
Button {
isPresented = false
HapticManager.shared.fireHaptic(of: .buttonPress)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
routerPath.presentedSheet = .settings
} label: {
Label("tab.settings", systemImage: "gear")
}
} label: {
Label("tab.settings", systemImage: "gear")
}
}

private func refreshAccounts() {
if accountsViewModel.isEmpty || appAccounts.availableAccounts.count != accountsViewModel.count {
accountsViewModel = []
for account in appAccounts.availableAccounts {
let viewModel: AppAccountViewModel = .init(appAccount: account)
Task {
await viewModel.fetchAccount()
if !accountsViewModel.contains(where: { $0.acct == viewModel.acct }) {
accountsViewModel.append(viewModel)
}
}
}
accountsViewModel = []
for account in appAccounts.availableAccounts {
let viewModel: AppAccountViewModel = .init(appAccount: account, isInNavigation: false, showBadge: true)
accountsViewModel.append(viewModel)
}
}
}
15 changes: 0 additions & 15 deletions Packages/DesignSystem/Sources/DesignSystem/UIImage.swift

This file was deleted.

0 comments on commit 15b704c

Please sign in to comment.