~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain-ios

1 collaborator
0
1
main
103 lines · 3.2 KB Raw History
import SwiftUI

extension View {
    /// Long-press context menu for profile avatars/rows.
    /// Omit `onViewProfile` when already on the profile page.
    /// Pass `hasStory: true` and a non-nil `onViewStory` to include "View story".
    func profileContextMenu(
        handle: String?,
        hasStory: Bool,
        onViewProfile: (() -> Void)? = nil,
        onViewStory: (() -> Void)? = nil,
        onAddStory: (() -> Void)? = nil,
        onViewPhoto: (() -> Void)? = nil,
        showSharingActions: Bool = true
    ) -> some View {
        contextMenu {
            profileMenuItems(
                handle: handle,
                hasStory: hasStory,
                onViewProfile: onViewProfile,
                onViewStory: onViewStory,
                onAddStory: onAddStory,
                onViewPhoto: onViewPhoto,
                showSharingActions: showSharingActions
            )
        }
    }

    /// Variant with an explicit preview rendered directly in the system overlay —
    /// use when the default "lift" animation is clipped by the surrounding layout.
    func profileContextMenu(
        handle: String?,
        hasStory: Bool,
        onViewProfile: (() -> Void)? = nil,
        onViewStory: (() -> Void)? = nil,
        onAddStory: (() -> Void)? = nil,
        onViewPhoto: (() -> Void)? = nil,
        showSharingActions: Bool = true,
        @ViewBuilder preview: @escaping () -> some View
    ) -> some View {
        contextMenu {
            profileMenuItems(
                handle: handle,
                hasStory: hasStory,
                onViewProfile: onViewProfile,
                onViewStory: onViewStory,
                onAddStory: onAddStory,
                onViewPhoto: onViewPhoto,
                showSharingActions: showSharingActions
            )
        } preview: {
            preview()
        }
    }

    /// Conditionally apply a view modifier without duplicating the entire view tree.
    @ViewBuilder
    func `if`(_ condition: Bool, transform: (Self) -> some View) -> some View {
        if condition {
            transform(self)
        } else {
            self
        }
    }
}

@ViewBuilder
private func profileMenuItems(
    handle: String?,
    hasStory: Bool,
    onViewProfile: (() -> Void)?,
    onViewStory: (() -> Void)?,
    onAddStory: (() -> Void)?,
    onViewPhoto: (() -> Void)?,
    showSharingActions: Bool = true
) -> some View {
    if hasStory, let onViewStory {
        Button(action: onViewStory) {
            Label("View story", systemImage: "play.circle")
        }
    }
    if let onAddStory {
        Button(action: onAddStory) {
            Label("New story", systemImage: "plus.circle")
        }
    }
    if let onViewProfile {
        Button(action: onViewProfile) {
            Label("View profile", systemImage: "person.circle")
        }
    }
    if let onViewPhoto {
        Button(action: onViewPhoto) {
            Label("View profile photo", systemImage: "person.crop.circle.fill")
        }
    }
    if showSharingActions, let handle {
        Divider()
        ShareLink(item: URL(string: "https://grain.social/profile/\(handle)") ?? URL(string: "https://grain.social")!) {
            Label("Share profile", systemImage: "square.and.arrow.up")
        }
    }
}