2023-01-16 19:51:05 +01:00
|
|
|
import Account
|
2023-01-16 21:15:33 +01:00
|
|
|
import AppAccount
|
2023-01-17 11:36:01 +01:00
|
|
|
import DesignSystem
|
|
|
|
import Env
|
2023-01-25 21:18:34 +01:00
|
|
|
import Models
|
2023-01-27 20:36:40 +01:00
|
|
|
import SwiftUI
|
2023-01-16 19:51:05 +01:00
|
|
|
|
|
|
|
struct SideBarView<Content: View>: View {
|
2023-01-17 19:41:46 +01:00
|
|
|
@EnvironmentObject private var appAccounts: AppAccountsManager
|
2023-01-16 19:51:05 +01:00
|
|
|
@EnvironmentObject private var currentAccount: CurrentAccount
|
|
|
|
@EnvironmentObject private var theme: Theme
|
2023-01-17 13:02:05 +01:00
|
|
|
@EnvironmentObject private var watcher: StreamWatcher
|
|
|
|
@EnvironmentObject private var userPreferences: UserPreferences
|
2023-01-22 06:38:30 +01:00
|
|
|
|
2023-01-16 19:51:05 +01:00
|
|
|
@Binding var selectedTab: Tab
|
|
|
|
@Binding var popToRootTab: Tab
|
|
|
|
var tabs: [Tab]
|
2023-01-17 13:02:05 +01:00
|
|
|
@ObservedObject var routerPath = RouterPath()
|
2023-01-16 21:15:33 +01:00
|
|
|
@ViewBuilder var content: () -> Content
|
2023-01-17 11:36:01 +01:00
|
|
|
|
2023-01-17 13:02:05 +01:00
|
|
|
private func badgeFor(tab: Tab) -> Int {
|
|
|
|
if tab == .notifications && selectedTab != tab {
|
|
|
|
return watcher.unreadNotificationsCount + userPreferences.pushNotificationsCount
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
2023-01-22 06:38:30 +01:00
|
|
|
|
2023-01-17 13:02:05 +01:00
|
|
|
private var profileView: some View {
|
|
|
|
Button {
|
|
|
|
selectedTab = .profile
|
|
|
|
} label: {
|
2023-01-17 15:14:50 +01:00
|
|
|
AppAccountsSelectorView(routerPath: RouterPath(),
|
2023-01-17 13:02:05 +01:00
|
|
|
accountCreationEnabled: false,
|
|
|
|
avatarSize: .status)
|
|
|
|
}
|
|
|
|
.frame(width: .sidebarWidth, height: 60)
|
|
|
|
.background(selectedTab == .profile ? theme.secondaryBackgroundColor : .clear)
|
|
|
|
}
|
2023-01-22 06:38:30 +01:00
|
|
|
|
2023-01-17 13:02:05 +01:00
|
|
|
private func makeIconForTab(tab: Tab) -> some View {
|
|
|
|
ZStack(alignment: .topTrailing) {
|
2023-01-19 11:59:25 +01:00
|
|
|
SideBarIcon(systemIconName: tab.iconName,
|
|
|
|
isSelected: tab == selectedTab)
|
2023-01-17 13:02:05 +01:00
|
|
|
if let badge = badgeFor(tab: tab), badge > 0 {
|
|
|
|
ZStack {
|
|
|
|
Circle()
|
|
|
|
.fill(.red)
|
|
|
|
Text(String(badge))
|
|
|
|
.foregroundColor(.white)
|
2023-01-17 21:08:05 +01:00
|
|
|
.font(.caption2)
|
2023-01-17 13:02:05 +01:00
|
|
|
}
|
|
|
|
.frame(width: 20, height: 20)
|
|
|
|
.offset(x: 10, y: -10)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.contentShape(Rectangle())
|
|
|
|
.frame(width: .sidebarWidth, height: 50)
|
|
|
|
}
|
2023-01-22 06:38:30 +01:00
|
|
|
|
2023-01-17 13:02:05 +01:00
|
|
|
private var postButton: some View {
|
|
|
|
Button {
|
2023-01-25 06:28:16 +01:00
|
|
|
routerPath.presentedSheet = .newStatusEditor(visibility: userPreferences.postVisibility)
|
2023-01-17 13:02:05 +01:00
|
|
|
} label: {
|
|
|
|
Image(systemName: "square.and.pencil")
|
|
|
|
.resizable()
|
|
|
|
.aspectRatio(contentMode: .fit)
|
|
|
|
.frame(width: 20, height: 30)
|
|
|
|
}
|
|
|
|
.buttonStyle(.borderedProminent)
|
|
|
|
.keyboardShortcut("n", modifiers: .command)
|
|
|
|
}
|
2023-01-22 06:38:30 +01:00
|
|
|
|
2023-01-17 19:41:46 +01:00
|
|
|
private func makeAccountButton(account: AppAccount) -> some View {
|
|
|
|
Button {
|
|
|
|
if account.id == appAccounts.currentAccount.id {
|
|
|
|
selectedTab = .profile
|
|
|
|
} else {
|
2023-02-09 18:48:31 +01:00
|
|
|
var transation = Transaction()
|
|
|
|
transation.disablesAnimations = true
|
|
|
|
withTransaction(transation) {
|
2023-01-17 19:41:46 +01:00
|
|
|
appAccounts.currentAccount = account
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} label: {
|
|
|
|
AppAccountView(viewModel: .init(appAccount: account, isCompact: true))
|
|
|
|
}
|
|
|
|
.frame(width: .sidebarWidth, height: 50)
|
|
|
|
.padding(.vertical, 8)
|
|
|
|
.background(selectedTab == .profile && account.id == appAccounts.currentAccount.id ?
|
2023-01-22 06:38:30 +01:00
|
|
|
theme.secondaryBackgroundColor : .clear)
|
2023-01-17 19:41:46 +01:00
|
|
|
}
|
2023-01-22 06:38:30 +01:00
|
|
|
|
2023-01-17 19:41:46 +01:00
|
|
|
private var tabsView: some View {
|
|
|
|
ForEach(tabs) { tab in
|
|
|
|
Button {
|
|
|
|
if tab == selectedTab {
|
|
|
|
popToRootTab = .other
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
|
|
|
popToRootTab = tab
|
|
|
|
}
|
|
|
|
}
|
|
|
|
selectedTab = tab
|
|
|
|
if tab == .notifications {
|
|
|
|
watcher.unreadNotificationsCount = 0
|
|
|
|
userPreferences.pushNotificationsCount = 0
|
|
|
|
}
|
|
|
|
} label: {
|
|
|
|
makeIconForTab(tab: tab)
|
|
|
|
}
|
|
|
|
.background(tab == selectedTab ? theme.secondaryBackgroundColor : .clear)
|
|
|
|
}
|
|
|
|
}
|
2023-01-22 06:38:30 +01:00
|
|
|
|
2023-01-16 19:51:05 +01:00
|
|
|
var body: some View {
|
|
|
|
HStack(spacing: 0) {
|
2023-01-17 13:02:05 +01:00
|
|
|
ScrollView {
|
|
|
|
VStack(alignment: .center) {
|
2023-01-17 19:41:46 +01:00
|
|
|
if appAccounts.availableAccounts.isEmpty {
|
|
|
|
tabsView
|
|
|
|
} else {
|
|
|
|
ForEach(appAccounts.availableAccounts) { account in
|
|
|
|
makeAccountButton(account: account)
|
|
|
|
if account.id == appAccounts.currentAccount.id {
|
|
|
|
tabsView
|
2023-01-16 19:51:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-01-17 13:02:05 +01:00
|
|
|
postButton
|
|
|
|
.padding(.top, 12)
|
|
|
|
Spacer()
|
2023-01-16 19:51:05 +01:00
|
|
|
}
|
|
|
|
}
|
2023-01-17 13:02:05 +01:00
|
|
|
.frame(width: .sidebarWidth)
|
|
|
|
.scrollContentBackground(.hidden)
|
|
|
|
.background(.thinMaterial)
|
2023-01-16 19:51:05 +01:00
|
|
|
Divider()
|
|
|
|
.edgesIgnoringSafeArea(.top)
|
2023-01-16 21:15:33 +01:00
|
|
|
content()
|
2023-01-16 19:51:05 +01:00
|
|
|
}
|
|
|
|
.background(.thinMaterial)
|
2023-01-17 13:02:05 +01:00
|
|
|
.withSheetDestinations(sheetDestinations: $routerPath.presentedSheet)
|
2023-01-16 19:51:05 +01:00
|
|
|
}
|
|
|
|
}
|
2023-01-19 11:59:25 +01:00
|
|
|
|
|
|
|
private struct SideBarIcon: View {
|
|
|
|
@EnvironmentObject private var theme: Theme
|
2023-01-22 06:38:30 +01:00
|
|
|
|
2023-01-19 11:59:25 +01:00
|
|
|
let systemIconName: String
|
|
|
|
let isSelected: Bool
|
2023-01-22 06:38:30 +01:00
|
|
|
|
2023-01-19 11:59:25 +01:00
|
|
|
@State private var isHovered: Bool = false
|
2023-01-22 06:38:30 +01:00
|
|
|
|
2023-01-19 11:59:25 +01:00
|
|
|
var body: some View {
|
|
|
|
Image(systemName: systemIconName)
|
2023-02-10 12:53:59 +01:00
|
|
|
.font(.title2)
|
|
|
|
.fontWeight(.medium)
|
2023-01-19 11:59:25 +01:00
|
|
|
.foregroundColor(isSelected ? theme.tintColor : theme.labelColor)
|
|
|
|
.scaleEffect(isHovered ? 0.8 : 1.0)
|
|
|
|
.onHover { isHovered in
|
|
|
|
withAnimation(.interpolatingSpring(stiffness: 300, damping: 15)) {
|
|
|
|
self.isHovered = isHovered
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|