Vernissage/Vernissage/ViewModifiers/NavigationMenuButtons.swift

209 lines
7.5 KiB
Swift
Raw Normal View History

2023-04-06 13:19:55 +02:00
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the Apache License 2.0.
//
import Foundation
import SwiftUI
2023-04-07 16:59:18 +02:00
import EnvironmentKit
2023-04-15 21:25:32 +02:00
import ServicesKit
2023-10-21 09:44:40 +02:00
import TipKit
import WidgetsKit
2023-04-06 13:19:55 +02:00
2023-10-19 13:24:02 +02:00
@MainActor
2023-04-18 20:34:59 +02:00
extension View {
2023-04-20 15:03:43 +02:00
func navigationMenuButtons(menuPosition: Binding<MenuPosition>,
onViewModeIconTap: @escaping (MainView.ViewMode) -> Void) -> some View {
modifier(NavigationMenuButtons(menuPosition: menuPosition, onViewModeIconTap: onViewModeIconTap))
2023-04-06 13:19:55 +02:00
}
}
2023-10-19 13:24:02 +02:00
@MainActor
2023-04-20 15:03:43 +02:00
private struct NavigationMenuButtons: ViewModifier {
2023-10-22 10:09:02 +02:00
@Environment(ApplicationState.self) var applicationState
2023-10-19 13:24:02 +02:00
@Environment(RouterPath.self) var routerPath
2023-10-20 07:45:18 +02:00
@Environment(\.modelContext) private var modelContext
2023-04-06 13:19:55 +02:00
2023-10-21 09:44:40 +02:00
private let menuCustomizableTip = MenuCustomizableTip()
2023-04-18 20:34:59 +02:00
private let onViewModeIconTap: (MainView.ViewMode) -> Void
private let imageFontSize = 20.0
2023-04-18 20:34:59 +02:00
2023-04-21 16:58:52 +02:00
@State private var displayedCustomMenuItems = [
SelectedMenuItemDetails(position: 1, viewMode: .home),
SelectedMenuItemDetails(position: 2, viewMode: .local),
SelectedMenuItemDetails(position: 3, viewMode: .profile)
2023-04-18 20:34:59 +02:00
]
2023-04-21 16:58:52 +02:00
@State private var hiddenMenuItems: [MainView.ViewMode] = []
2023-04-06 13:19:55 +02:00
@Binding var menuPosition: MenuPosition
2023-04-20 15:03:43 +02:00
init(menuPosition: Binding<MenuPosition>, onViewModeIconTap: @escaping (MainView.ViewMode) -> Void) {
2023-04-18 20:34:59 +02:00
self.onViewModeIconTap = onViewModeIconTap
2023-04-06 13:19:55 +02:00
self._menuPosition = menuPosition
}
func body(content: Content) -> some View {
if self.menuPosition == .top {
content
} else {
ZStack {
content
VStack(alignment: .trailing) {
Spacer()
HStack(alignment: .center) {
2023-04-06 13:19:55 +02:00
if self.menuPosition == .bottomRight {
Spacer()
2023-04-15 21:25:32 +02:00
self.menuContainerView()
.padding(.trailing, 24)
.padding(.bottom, 10)
2023-04-06 13:19:55 +02:00
}
if self.menuPosition == .bottomLeft {
2023-04-15 21:25:32 +02:00
self.menuContainerView()
.padding(.leading, 24)
.padding(.bottom, 10)
2023-04-06 13:19:55 +02:00
Spacer()
}
}
}
2023-04-18 20:34:59 +02:00
.onAppear {
self.loadCustomMenuItems()
}
2023-04-06 13:19:55 +02:00
}
}
}
@ViewBuilder
2023-04-15 21:25:32 +02:00
private func menuContainerView() -> some View {
2023-04-18 20:34:59 +02:00
if self.menuPosition == .bottomRight {
HStack(alignment: .center) {
HStack {
self.contextMenuView()
self.customMenuItemsView()
}
.frame(height: 50)
.padding(.horizontal, 8)
2023-04-18 20:34:59 +02:00
.background(.ultraThinMaterial)
.clipShape(Capsule())
2023-04-15 21:25:32 +02:00
self.composeImageView()
.frame(height: 50)
.padding(.horizontal, 8)
2023-04-18 20:34:59 +02:00
.background(.ultraThinMaterial)
.clipShape(Circle())
2023-04-15 21:25:32 +02:00
}
2023-10-21 09:44:40 +02:00
.popoverTip(menuCustomizableTip, arrowEdge: .bottom)
2023-04-18 20:34:59 +02:00
} else {
HStack(alignment: .center) {
2023-04-15 21:25:32 +02:00
self.composeImageView()
.frame(height: 50)
.padding(.horizontal, 8)
2023-04-18 20:34:59 +02:00
.background(.ultraThinMaterial)
.clipShape(Circle())
HStack {
self.customMenuItemsView()
self.contextMenuView()
}
.frame(height: 50)
.padding(.horizontal, 8)
2023-04-18 20:34:59 +02:00
.background(.ultraThinMaterial)
.clipShape(Capsule())
2023-04-15 21:25:32 +02:00
}
2023-10-21 09:44:40 +02:00
.popoverTip(menuCustomizableTip, arrowEdge: .bottom)
2023-04-15 21:25:32 +02:00
}
}
@ViewBuilder
private func contextMenuView() -> some View {
2023-04-06 13:19:55 +02:00
Menu {
2023-04-21 16:58:52 +02:00
MainNavigationOptions(hiddenMenuItems: $hiddenMenuItems) { viewMode in
2023-04-20 15:03:43 +02:00
self.onViewModeIconTap(viewMode)
}
2023-04-06 13:19:55 +02:00
} label: {
Image(systemName: "ellipsis")
2023-04-18 20:34:59 +02:00
.font(.system(size: self.imageFontSize))
.foregroundColor(.mainTextColor.opacity(0.75))
.padding(.vertical, 10)
.padding(.horizontal, 8)
2023-04-15 21:25:32 +02:00
}
2023-10-23 07:56:32 +02:00
.environment(\.menuOrder, .fixed)
2023-04-15 21:25:32 +02:00
}
2023-04-06 13:19:55 +02:00
2023-04-18 20:34:59 +02:00
@ViewBuilder
private func customMenuItemsView() -> some View {
2023-04-21 16:58:52 +02:00
ForEach(self.displayedCustomMenuItems) { item in
self.customMenuItemView(item)
2023-04-18 20:34:59 +02:00
}
}
@ViewBuilder
2023-04-15 21:25:32 +02:00
private func composeImageView() -> some View {
Button {
HapticService.shared.fireHaptic(of: .buttonPress)
self.routerPath.presentedSheet = .newStatusEditor
} label: {
Image(systemName: "plus")
2023-04-18 20:34:59 +02:00
.font(.system(size: self.imageFontSize))
.foregroundColor(.mainTextColor.opacity(0.75))
.padding(.vertical, 10)
.padding(.horizontal, 8)
}
}
@ViewBuilder
2023-04-21 16:58:52 +02:00
private func customMenuItemView(_ displayedCustomMenuItem: SelectedMenuItemDetails) -> some View {
2023-04-18 20:34:59 +02:00
Button {
2023-04-21 16:58:52 +02:00
self.onViewModeIconTap(displayedCustomMenuItem.viewMode)
2023-04-18 20:34:59 +02:00
} label: {
2023-10-22 10:09:02 +02:00
displayedCustomMenuItem.viewMode.getImage(applicationState: applicationState)
2023-04-18 20:34:59 +02:00
.font(.system(size: self.imageFontSize))
.foregroundColor(.mainTextColor.opacity(0.75))
.padding(.vertical, 10)
.padding(.horizontal, 8)
2023-04-18 20:34:59 +02:00
}.contextMenu {
2023-10-23 07:56:32 +02:00
MainNavigationOptions(hiddenMenuItems: Binding.constant([])) { viewMode in
2023-04-18 20:34:59 +02:00
withAnimation {
2023-10-23 07:56:32 +02:00
displayedCustomMenuItem.viewMode = viewMode
2023-04-18 20:34:59 +02:00
}
2023-10-22 10:09:02 +02:00
// Saving in database.
2023-04-21 16:58:52 +02:00
switch displayedCustomMenuItem.position {
2023-04-18 20:34:59 +02:00
case 1:
2023-10-23 07:56:32 +02:00
ApplicationSettingsHandler.shared.set(customNavigationMenuItem1: viewMode.rawValue, modelContext: modelContext)
2023-04-18 20:34:59 +02:00
case 2:
2023-10-23 07:56:32 +02:00
ApplicationSettingsHandler.shared.set(customNavigationMenuItem2: viewMode.rawValue, modelContext: modelContext)
2023-04-18 20:34:59 +02:00
case 3:
2023-10-23 07:56:32 +02:00
ApplicationSettingsHandler.shared.set(customNavigationMenuItem3: viewMode.rawValue, modelContext: modelContext)
2023-04-18 20:34:59 +02:00
default:
break
}
2023-04-21 16:58:52 +02:00
self.hiddenMenuItems = self.displayedCustomMenuItems.map({ $0.viewMode })
MenuCustomizableTip().invalidate(reason: .actionPerformed)
2023-04-18 20:34:59 +02:00
}
}
}
private func loadCustomMenuItems() {
2023-10-20 07:45:18 +02:00
let applicationSettings = ApplicationSettingsHandler.shared.get(modelContext: modelContext)
2023-04-18 20:34:59 +02:00
2023-04-21 16:58:52 +02:00
self.setCustomMenuItem(position: 1, viewMode: MainView.ViewMode(rawValue: Int(applicationSettings.customNavigationMenuItem1)) ?? .home)
self.setCustomMenuItem(position: 2, viewMode: MainView.ViewMode(rawValue: Int(applicationSettings.customNavigationMenuItem2)) ?? .local)
self.setCustomMenuItem(position: 3, viewMode: MainView.ViewMode(rawValue: Int(applicationSettings.customNavigationMenuItem3)) ?? .profile)
self.hiddenMenuItems = self.displayedCustomMenuItems.map({ $0.viewMode })
2023-04-18 20:34:59 +02:00
}
2023-04-21 16:58:52 +02:00
private func setCustomMenuItem(position: Int, viewMode: MainView.ViewMode) {
2023-10-23 07:56:32 +02:00
if let displayedCustomMenuItem = self.displayedCustomMenuItems.first(where: { $0.position == position }) {
displayedCustomMenuItem.viewMode = viewMode
2023-04-06 13:19:55 +02:00
}
}
}