mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-28 23:54:32 +00:00
Somewhat working file tray
This commit is contained in:
parent
4c7e79b954
commit
30422c3d95
29 changed files with 4032 additions and 65 deletions
135
DynamicIsland/components/Shelf/Views/FileShareView.swift
Normal file
135
DynamicIsland/components/Shelf/Views/FileShareView.swift
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
//
|
||||
// FileShareView.swift
|
||||
// boringNotch
|
||||
//
|
||||
// Created by Alexander on 2025-09-24.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import Defaults
|
||||
import SwiftUI
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
struct FileShareView: View {
|
||||
@EnvironmentObject private var vm: DynamicIslandViewModel
|
||||
@StateObject private var quickShare = QuickShareService.shared
|
||||
@Default(.quickShareProvider) var quickShareProvider: String
|
||||
|
||||
@State private var hostView: NSView?
|
||||
@State private var interactionNonce: UUID = .init()
|
||||
@State private var isProcessing = false
|
||||
|
||||
private var selectedProvider: QuickShareProvider {
|
||||
quickShare.availableProviders.first(where: { $0.id == quickShareProvider }) ?? QuickShareProvider(id: "System Share Menu", imageData: nil, supportsRawText: true)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
dropArea
|
||||
.background(NSViewHost(view: $hostView))
|
||||
.onDrop(of: [.fileURL, .url, .utf8PlainText, .plainText, .data, .image], isTargeted: $vm.dropZoneTargeting) { providers in
|
||||
interactionNonce = .init()
|
||||
vm.dropEvent = true
|
||||
Task { await handleDrop(providers) }
|
||||
return true
|
||||
}
|
||||
.onTapGesture {
|
||||
Task {
|
||||
await handleClick()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var dropArea: some View {
|
||||
ZStack {
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.fill(
|
||||
LinearGradient(colors: [Color.black.opacity(0.35), Color.black.opacity(0.20)], startPoint: .topLeading, endPoint: .bottomTrailing)
|
||||
)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.stroke(
|
||||
vm.dropZoneTargeting
|
||||
? Color.accentColor.opacity(0.9)
|
||||
: Color.white.opacity(0.1),
|
||||
style: StrokeStyle(lineWidth: 3, lineCap: .round, dash: [10])
|
||||
)
|
||||
)
|
||||
.shadow(color: Color.black.opacity(0.6), radius: 6, x: 0, y: 2)
|
||||
|
||||
// Content
|
||||
VStack(spacing: 5) {
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(Color.white.opacity(
|
||||
vm.dropZoneTargeting ? 0.11 : 0.09
|
||||
))
|
||||
.frame(width: 55, height: 55)
|
||||
Image(systemName: "square.and.arrow.up")
|
||||
Group {
|
||||
if let imgData = selectedProvider.imageData, let nsImg = NSImage(data: imgData) {
|
||||
Image(nsImage: nsImg)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
} else {
|
||||
Image(systemName: "square.and.arrow.up")
|
||||
}
|
||||
}
|
||||
.frame(width: 34, height: 34)
|
||||
.foregroundStyle(
|
||||
vm.dropZoneTargeting ? Color.accentColor : Color.gray
|
||||
)
|
||||
.scaleEffect(
|
||||
vm.dropZoneTargeting ? 1.06 : 1.0
|
||||
)
|
||||
.animation(.spring(response: 0.36, dampingFraction: 0.7), value: vm.dropZoneTargeting)
|
||||
}
|
||||
|
||||
Text(selectedProvider.id)
|
||||
.font(.system(.headline, design: .rounded))
|
||||
.foregroundColor(.white.opacity(0.8))
|
||||
|
||||
}
|
||||
.padding(18)
|
||||
|
||||
// Loading overlay
|
||||
if isProcessing || quickShare.isPickerOpen {
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.fill(.black.opacity(0.3))
|
||||
.overlay(
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: .white))
|
||||
.scaleEffect(0.8)
|
||||
)
|
||||
}
|
||||
}
|
||||
.contentShape(RoundedRectangle(cornerRadius: 12))
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
private func handleDrop(_ providers: [NSItemProvider]) async {
|
||||
isProcessing = true
|
||||
defer { isProcessing = false }
|
||||
await quickShare.shareDroppedFiles(providers, using: selectedProvider, from: hostView)
|
||||
}
|
||||
|
||||
private func handleClick() async {
|
||||
await quickShare.showFilePicker(for: selectedProvider, from: hostView)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Host NSView extractor for anchoring share sheet
|
||||
|
||||
private struct NSViewHost: NSViewRepresentable {
|
||||
@Binding var view: NSView?
|
||||
|
||||
func makeNSView(context: Context) -> NSView {
|
||||
let v = NSView(frame: .zero)
|
||||
DispatchQueue.main.async { self.view = v }
|
||||
return v
|
||||
}
|
||||
|
||||
func updateNSView(_ nsView: NSView, context: Context) {
|
||||
DispatchQueue.main.async { self.view = nsView }
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue