Files
damus/damus/Features/Timeline/Views/SideMenuView.swift
ericholguin 36b40f53af Damus Labs
This PR adds the new Damus Labs view.
This will allow us to make the new things we work on more prominent.
Any new features we want to iterate fast on and get to our users a lot faster
are perfect for Damus Labs. This will be exclusive to Damus Purple Subscribers.

Changelog-Added: Added Damus Labs

Signed-off-by: ericholguin <ericholguin@apache.org>
2025-10-26 15:07:39 -07:00

260 lines
10 KiB
Swift

//
// SideMenuView.swift
// damus
//
// Created by Ben Weeks on 1/6/23.
// Ref: https://blog.logrocket.com/create-custom-collapsible-sidebar-swiftui/
import SwiftUI
@MainActor
struct SideMenuView: View {
let damus_state: DamusState
@Binding var isSidebarVisible: Bool
@Binding var selected: Timeline
@State var confirm_logout: Bool = false
@State private var showQRCode = false
var sideBarWidth = min(UIScreen.main.bounds.size.width * 0.65, 400.0)
let verticalSpacing: CGFloat = 25
let padding: CGFloat = 30
var body: some View {
ZStack {
GeometryReader { _ in
EmptyView()
}
.background(DamusColors.darkGrey.opacity(0.6))
.opacity(isSidebarVisible ? 1 : 0)
.animation(.default, value: isSidebarVisible)
.onTapGesture {
isSidebarVisible.toggle()
}
content
}
}
func SidemenuItems(profile_model: ProfileModel, followers: FollowersModel) -> some View {
return VStack(spacing: verticalSpacing) {
NavigationLink(value: Route.Profile(profile: profile_model, followers: followers)) {
navLabel(title: NSLocalizedString("Profile", comment: "Sidebar menu label for Profile view."), img: "user")
}
.accessibilityIdentifier(AppAccessibilityIdentifiers.side_menu_profile_button.rawValue)
NavigationLink(value: Route.Wallet(wallet: damus_state.wallet)) {
navLabel(title: NSLocalizedString("Wallet", comment: "Sidebar menu label for Wallet view."), img: "wallet")
}
if damus_state.purple.enable_purple {
NavigationLink(destination: DamusPurpleView(damus_state: damus_state)) {
HStack(spacing: 23) {
Image("nostr-hashtag")
Text("Purple")
.foregroundColor(DamusColors.purple)
.font(.title2.weight(.semibold))
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}
NavigationLink(destination: DamusLabsView(damus_state: damus_state)) {
HStack(spacing: 23) {
Image(systemName: "flask")
.fontWeight(.bold)
.tint(DamusColors.adaptableBlack)
Text("Labs")
.font(.title2.weight(.semibold))
.foregroundColor(DamusColors.adaptableBlack)
.frame(maxWidth: .infinity, alignment: .leading)
.dynamicTypeSize(.xSmall)
.minimumScaleFactor(0.5)
.lineLimit(1)
}
.frame(maxWidth: .infinity, alignment: .leading)
}
NavigationLink(value: Route.MuteList) {
navLabel(title: NSLocalizedString("Muted", comment: "Sidebar menu label for muted users view."), img: "mute")
}
NavigationLink(value: Route.RelayConfig) {
navLabel(title: NSLocalizedString("Relays", comment: "Sidebar menu label for Relays view."), img: "world-relays")
}
NavigationLink(value: Route.Bookmarks) {
navLabel(title: NSLocalizedString("Bookmarks", comment: "Sidebar menu label for Bookmarks view."), img: "bookmark")
}
Link(destination: URL(string: "https://store.damus.io/?ref=damus_ios_app")!) {
navLabel(title: NSLocalizedString("Merch", comment: "Sidebar menu label for merch store link."), img: "shop")
}
NavigationLink(value: Route.Config) {
navLabel(title: NSLocalizedString("Settings", comment: "Sidebar menu label for accessing the app settings"), img: "settings")
}
Button(action: {
if damus_state.keypair.privkey == nil {
logout(damus_state)
} else {
confirm_logout = true
}
}, label: {
navLabel(title: NSLocalizedString("Logout", comment: "Sidebar menu label to sign out of the account."), img: "logout")
})
}
}
var TopProfile: some View {
var name: String? = nil
var display_name: String? = nil
do {
let profile_txn = damus_state.ndb.lookup_profile(damus_state.pubkey, txn_name: "top_profile")
let profile = profile_txn?.unsafeUnownedValue?.profile
name = profile?.name
display_name = profile?.display_name
}
return VStack(alignment: .leading) {
HStack(spacing: 10) {
ProfilePicView(pubkey: damus_state.pubkey, size: 50, highlight: .none, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation)
Spacer()
Button(action: {
present_sheet(.user_status)
isSidebarVisible = false
}, label: {
Image("add-reaction")
.resizable()
.frame(width: 25, height: 25)
.padding(5)
.foregroundColor(DamusColors.adaptableBlack)
.background {
Circle()
.foregroundColor(DamusColors.neutral3)
}
})
Button(action: {
showQRCode.toggle()
isSidebarVisible = false
}, label: {
Image("qr-code")
.resizable()
.frame(width: 25, height: 25)
.padding(5)
.foregroundColor(DamusColors.adaptableBlack)
.background {
Circle()
.foregroundColor(DamusColors.neutral3)
}
}).damus_full_screen_cover($showQRCode, damus_state: damus_state) {
QRCodeView(damus_state: damus_state, pubkey: damus_state.pubkey)
}
}
VStack(alignment: .leading) {
if let display_name {
Text(display_name)
.font(.title2.weight(.bold))
.foregroundColor(DamusColors.adaptableBlack)
.frame(maxWidth: .infinity, alignment: .leading)
.dynamicTypeSize(.xSmall)
.lineLimit(1)
}
if let name {
if !name.isEmpty {
Text(verbatim: "@" + name)
.foregroundColor(DamusColors.mediumGrey)
.font(.body)
.lineLimit(1)
}
}
PubkeyView(pubkey: damus_state.pubkey, sidemenu: true)
.pubkey_context_menu(pubkey: damus_state.pubkey)
.simultaneousGesture(TapGesture().onEnded{
isSidebarVisible = true
})
}
}
}
var MainSidemenu: some View {
VStack(alignment: .leading, spacing: 0) {
let followers = FollowersModel(damus_state: damus_state, target: damus_state.pubkey)
let profile_model = ProfileModel(pubkey: damus_state.pubkey, damus: damus_state)
NavigationLink(value: Route.Profile(profile: profile_model, followers: followers), label: {
TopProfile
.padding(.bottom, verticalSpacing)
})
.simultaneousGesture(TapGesture().onEnded {
isSidebarVisible = false
})
ScrollView {
SidemenuItems(profile_model: profile_model, followers: followers)
.simultaneousGesture(TapGesture().onEnded {
isSidebarVisible = false
})
}
.scrollIndicators(.hidden)
}
}
var content: some View {
HStack(alignment: .top) {
ZStack(alignment: .top) {
DamusColors.adaptableWhite
.ignoresSafeArea()
MainSidemenu
.padding([.leading, .trailing], padding)
}
.frame(width: sideBarWidth)
.offset(x: isSidebarVisible ? 0 : -(sideBarWidth + padding))
.animation(.default, value: isSidebarVisible)
.alert("Logout", isPresented: $confirm_logout) {
Button(NSLocalizedString("Cancel", comment: "Cancel out of logging out the user."), role: .cancel) {
confirm_logout = false
}
Button(NSLocalizedString("Logout", comment: "Button for logging out the user."), role: .destructive) {
logout(damus_state)
}
} message: {
Text("Make sure your nsec account key is saved before you logout or you will lose access to this account", comment: "Reminder message in alert to get customer to verify that their private security account key is saved saved before logging out.")
}
Spacer()
}
}
func navLabel(title: String, img: String) -> some View {
HStack(spacing: 20) {
Image(img)
.tint(DamusColors.adaptableBlack)
Text(title)
.font(.title2.weight(.semibold))
.foregroundColor(DamusColors.adaptableBlack)
.frame(maxWidth: .infinity, alignment: .leading)
.dynamicTypeSize(.xSmall)
.minimumScaleFactor(0.5)
.lineLimit(1)
}
}
}
struct Previews_SideMenuView_Previews: PreviewProvider {
static var previews: some View {
let ds = test_damus_state
SideMenuView(damus_state: ds, isSidebarVisible: .constant(true), selected: .constant(.home))
}
}