native macOS codings agent orchestrator
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

Clear sidebar drag state synchronously

onevcat 261e54ad ab86831c

+98 -34
+47 -24
supacode/Features/Repositories/Views/SidebarDragSupport.swift
··· 54 54 let destination: (DropInfo) -> Int 55 55 let repositoryOrderIDs: [Repository.ID] 56 56 @Binding var targetedDestination: Int? 57 - let onDrop: (IndexSet, Int) -> Void 58 - let onDragEnded: () -> Void 57 + let actions: SidebarDropTargetActions 59 58 60 59 func dropEntered(info: DropInfo) { 61 60 guard isEnabled else { ··· 85 84 } 86 85 let dropDestination = destination(info) 87 86 targetedDestination = nil 87 + if let repositoryID = actions.draggedItemID { 88 + return performDrop(repositoryID: repositoryID, dropDestination: dropDestination) 89 + } 88 90 guard let provider = info.itemProviders(for: [.prowlSidebarDragPayload]).first else { 89 - onDragEnded() 91 + actions.onDragEnded() 90 92 return false 91 93 } 92 94 provider.loadDataRepresentation(forTypeIdentifier: UTType.prowlSidebarDragPayload.identifier) { data, _ in 93 95 guard let data, 94 - let repositoryID = SidebarDragProvider.repositoryID(from: data), 95 - let source = repositoryOrderIDs.firstIndex(of: repositoryID), 96 - source != dropDestination, 97 - source + 1 != dropDestination 96 + let repositoryID = SidebarDragProvider.repositoryID(from: data) 98 97 else { 99 - Task { @MainActor in onDragEnded() } 98 + Task { @MainActor in actions.onDragEnded() } 100 99 return 101 100 } 102 101 Task { @MainActor in 103 - onDrop(IndexSet(integer: source), dropDestination) 104 - onDragEnded() 102 + _ = performDrop(repositoryID: repositoryID, dropDestination: dropDestination) 105 103 } 106 104 } 107 105 return true 108 106 } 107 + 108 + @MainActor 109 + private func performDrop(repositoryID: Repository.ID, dropDestination: Int) -> Bool { 110 + guard let source = repositoryOrderIDs.firstIndex(of: repositoryID), 111 + source != dropDestination, 112 + source + 1 != dropDestination 113 + else { 114 + actions.onDragEnded() 115 + return false 116 + } 117 + actions.onDrop(IndexSet(integer: source), dropDestination) 118 + actions.onDragEnded() 119 + return true 120 + } 109 121 } 110 122 111 123 struct SidebarWorktreeDropDelegate: DropDelegate { ··· 113 125 let destination: (DropInfo) -> Int 114 126 let sectionIDs: [Worktree.ID] 115 127 @Binding var targetedDestination: Int? 116 - let onDrop: (IndexSet, Int) -> Void 117 - let onDragEnded: () -> Void 128 + let actions: SidebarDropTargetActions 118 129 119 130 func dropEntered(info: DropInfo) { 120 131 guard isEnabled else { ··· 144 155 } 145 156 let dropDestination = destination(info) 146 157 targetedDestination = nil 158 + if let worktreeID = actions.draggedItemID { 159 + return performDrop(worktreeID: worktreeID, dropDestination: dropDestination) 160 + } 147 161 guard let provider = info.itemProviders(for: [.prowlSidebarDragPayload]).first else { 148 - onDragEnded() 162 + actions.onDragEnded() 149 163 return false 150 164 } 151 165 provider.loadDataRepresentation(forTypeIdentifier: UTType.prowlSidebarDragPayload.identifier) { data, _ in 152 166 guard let data, 153 - let worktreeID = SidebarDragProvider.worktreeID(from: data), 154 - let source = sectionIDs.firstIndex(of: worktreeID), 155 - source != dropDestination, 156 - source + 1 != dropDestination 167 + let worktreeID = SidebarDragProvider.worktreeID(from: data) 157 168 else { 158 - Task { @MainActor in onDragEnded() } 169 + Task { @MainActor in actions.onDragEnded() } 159 170 return 160 171 } 161 172 Task { @MainActor in 162 - onDrop(IndexSet(integer: source), dropDestination) 163 - onDragEnded() 173 + _ = performDrop(worktreeID: worktreeID, dropDestination: dropDestination) 164 174 } 165 175 } 176 + return true 177 + } 178 + 179 + @MainActor 180 + private func performDrop(worktreeID: Worktree.ID, dropDestination: Int) -> Bool { 181 + guard let source = sectionIDs.firstIndex(of: worktreeID), 182 + source != dropDestination, 183 + source + 1 != dropDestination 184 + else { 185 + actions.onDragEnded() 186 + return false 187 + } 188 + actions.onDrop(IndexSet(integer: source), dropDestination) 189 + actions.onDragEnded() 166 190 return true 167 191 } 168 192 } ··· 211 235 } 212 236 213 237 struct SidebarDropTargetActions { 238 + var draggedItemID: String? 214 239 let onDrop: (IndexSet, Int) -> Void 215 240 let onDragEnded: () -> Void 216 241 } ··· 329 354 }, 330 355 repositoryOrderIDs: repositoryOrderIDs, 331 356 targetedDestination: $targetedDestination, 332 - onDrop: actions.onDrop, 333 - onDragEnded: actions.onDragEnded 357 + actions: actions 334 358 ) 335 359 ) 336 360 } ··· 354 378 }, 355 379 sectionIDs: rowIDs, 356 380 targetedDestination: $targetedDestination, 357 - onDrop: actions.onDrop, 358 - onDragEnded: actions.onDragEnded 381 + actions: actions 359 382 ) 360 383 ) 361 384 }
+15 -4
supacode/Features/Repositories/Views/SidebarListView.swift
··· 35 35 let terminalManager: WorktreeTerminalManager 36 36 @FocusState private var isSidebarFocused: Bool 37 37 @State private var isDragActive = false 38 + @State private var draggingRepositoryID: Repository.ID? 38 39 @State private var targetedRepositoryDropDestination: Int? 39 40 40 41 var body: some View { ··· 204 205 .draggableRepository( 205 206 id: model.repositoryID, 206 207 isEnabled: !model.isRemoving, 207 - beginDrag: beginSidebarDrag 208 + beginDrag: { 209 + beginSidebarDrag(repositoryID: model.repositoryID) 210 + } 208 211 ) 209 212 } 210 213 ··· 233 236 .draggableRepository( 234 237 id: model.id, 235 238 isEnabled: model.isReorderable, 236 - beginDrag: beginSidebarDrag 239 + beginDrag: { 240 + beginSidebarDrag(repositoryID: model.id) 241 + } 237 242 ) 238 243 239 244 case .listHeader, .archivedWorktrees: ··· 246 251 isEnabled: isDragActive, 247 252 targetedDestination: $targetedRepositoryDropDestination, 248 253 actions: SidebarDropTargetActions( 254 + draggedItemID: draggingRepositoryID, 249 255 onDrop: { offsets, destination in 250 - store.send(.worktreeOrdering(.repositoriesMoved(offsets, destination))) 256 + withAnimation(.easeOut(duration: 0.2)) { 257 + _ = store.send(.worktreeOrdering(.repositoriesMoved(offsets, destination))) 258 + } 251 259 }, 252 260 onDragEnded: endSidebarDrag 253 261 ) 254 262 ) 255 263 } 256 264 257 - private func beginSidebarDrag() { 265 + private func beginSidebarDrag(repositoryID: Repository.ID) { 258 266 guard !isDragActive else { return } 267 + draggingRepositoryID = repositoryID 259 268 isDragActive = true 260 269 store.send(.worktreeOrdering(.setSidebarDragActive(true))) 261 270 } 262 271 263 272 private func endSidebarDrag() { 264 273 targetedRepositoryDropDestination = nil 274 + draggingRepositoryID = nil 265 275 isDragActive = false 266 276 store.send(.worktreeOrdering(.setSidebarDragActive(false))) 267 277 } 268 278 269 279 private func resetSidebarDrag() { 270 280 targetedRepositoryDropDestination = nil 281 + draggingRepositoryID = nil 271 282 isDragActive = false 272 283 store.send(.worktreeOrdering(.setSidebarDragActive(false))) 273 284 }
+36 -6
supacode/Features/Repositories/Views/WorktreeRowsView.swift
··· 107 107 isEnabled: isWorktreeDragActive, 108 108 targetedDestination: targetedDestination, 109 109 actions: SidebarDropTargetActions( 110 + draggedItemID: draggingWorktreeIDs.first, 110 111 onDrop: { offsets, destination in 111 112 moveWorktrees(section: section, offsets: offsets, destination: destination) 112 113 }, ··· 139 140 Group { 140 141 if row.isRemovable, let worktree = store.state.worktree(for: row.id), !isRepositoryRemoving { 141 142 baseRow 142 - .contextMenu { 143 - ContextMenuHighlightActivator { 143 + .overlay { 144 + ContextMenuActivationOverlay { 144 145 scheduleContextMenuHighlight(for: row.id) 145 146 } 147 + } 148 + .contextMenu { 146 149 rowContextMenu(worktree: worktree, row: row) 147 150 } 148 151 } else { ··· 519 522 } 520 523 } 521 524 522 - private struct ContextMenuHighlightActivator: View { 525 + private struct ContextMenuActivationOverlay: NSViewRepresentable { 523 526 let activate: () -> Void 524 527 525 - var body: some View { 526 - EmptyView() 527 - .onAppear(perform: activate) 528 + func makeNSView(context: Context) -> RightClickForwardingView { 529 + let view = RightClickForwardingView() 530 + view.activate = activate 531 + return view 532 + } 533 + 534 + func updateNSView(_ nsView: RightClickForwardingView, context: Context) { 535 + nsView.activate = activate 536 + } 537 + 538 + final class RightClickForwardingView: NSView { 539 + var activate: (() -> Void)? 540 + private var isForwardingRightClick = false 541 + 542 + override func hitTest(_ point: NSPoint) -> NSView? { 543 + guard !isForwardingRightClick, 544 + NSApp.currentEvent?.type == .rightMouseDown 545 + else { 546 + return nil 547 + } 548 + return bounds.contains(point) ? self : nil 549 + } 550 + 551 + override func rightMouseDown(with event: NSEvent) { 552 + activate?() 553 + guard let window else { return } 554 + isForwardingRightClick = true 555 + window.sendEvent(event) 556 + isForwardingRightClick = false 557 + } 528 558 } 529 559 }