@@ -386,6 +386,7 @@ struct TaskspaceCard: View {
386
386
@ObservedObject var projectManager : ProjectManager
387
387
@State private var showingDeleteConfirmation = false
388
388
@State private var deleteBranch = false
389
+ @State private var cachedBranchInfo : ( branchName: String , isMerged: Bool , unmergedCommits: Int , hasUncommittedChanges: Bool ) = ( " " , false , 0 , false )
389
390
@State private var isHovered = false
390
391
@State private var isPressed = false
391
392
@@ -632,12 +633,16 @@ struct TaskspaceCard: View {
632
633
projectManager: projectManager,
633
634
deleteBranch: $deleteBranch,
634
635
onConfirm: {
635
- do {
636
- try projectManager. deleteTaskspace ( taskspace, deleteBranch: deleteBranch)
637
- } catch {
638
- Logger . shared. log ( " Failed to delete taskspace: \( error) " )
636
+ Task {
637
+ do {
638
+ try await projectManager. deleteTaskspace ( taskspace, deleteBranch: deleteBranch)
639
+ } catch {
640
+ Logger . shared. log ( " Failed to delete taskspace: \( error) " )
641
+ }
642
+ await MainActor . run {
643
+ showingDeleteConfirmation = false
644
+ }
639
645
}
640
- showingDeleteConfirmation = false
641
646
} ,
642
647
onCancel: {
643
648
// Send cancellation response for pending deletion request
@@ -672,14 +677,8 @@ struct DeleteTaskspaceDialog: View {
672
677
let onConfirm : ( ) -> Void
673
678
let onCancel : ( ) -> Void
674
679
675
- /// Computed property that gets fresh branch info when dialog renders
676
- ///
677
- /// CRITICAL: This computes fresh data every time the dialog appears, not cached data.
678
- /// Users may make commits between app startup and deletion, so stale info could
679
- /// show incorrect warnings leading to accidental data loss.
680
- private var branchInfo : ( branchName: String , isMerged: Bool , unmergedCommits: Int , hasUncommittedChanges: Bool ) {
681
- projectManager. getTaskspaceBranchInfo ( for: taskspace)
682
- }
680
+ @State private var cachedBranchInfo : ( branchName: String , isMerged: Bool , unmergedCommits: Int , hasUncommittedChanges: Bool ) = ( " " , false , 0 , false )
681
+ @State private var isLoadingBranchInfo = true
683
682
684
683
var body : some View {
685
684
VStack ( spacing: 20 ) {
@@ -689,27 +688,35 @@ struct DeleteTaskspaceDialog: View {
689
688
Text ( " Are you sure you want to delete ' \( taskspaceName) '? This will permanently remove all files and cannot be undone. " )
690
689
. multilineTextAlignment ( . center)
691
690
692
- if !branchInfo. branchName. isEmpty {
691
+ if isLoadingBranchInfo {
692
+ HStack {
693
+ ProgressView ( )
694
+ . scaleEffect ( 0.8 )
695
+ Text ( " Checking branch status... " )
696
+ . font ( . caption)
697
+ . foregroundColor ( . secondary)
698
+ }
699
+ } else if !cachedBranchInfo. branchName. isEmpty {
693
700
VStack ( alignment: . leading, spacing: 8 ) {
694
701
HStack {
695
- Toggle ( " Also delete the branch ` \( branchInfo . branchName) ` from git " , isOn: $deleteBranch)
702
+ Toggle ( " Also delete the branch ` \( cachedBranchInfo . branchName) ` from git " , isOn: $deleteBranch)
696
703
Spacer ( )
697
704
}
698
705
699
- if branchInfo . unmergedCommits > 0 || branchInfo . hasUncommittedChanges {
706
+ if cachedBranchInfo . unmergedCommits > 0 || cachedBranchInfo . hasUncommittedChanges {
700
707
VStack ( alignment: . leading, spacing: 4 ) {
701
- if branchInfo . unmergedCommits > 0 {
708
+ if cachedBranchInfo . unmergedCommits > 0 {
702
709
HStack {
703
710
Image ( systemName: " exclamationmark.triangle.fill " )
704
711
. foregroundColor ( . orange)
705
- Text ( " \( branchInfo . unmergedCommits) commit \( branchInfo . unmergedCommits == 1 ? " " : " s " ) from this branch do not appear in the main branch. " )
712
+ Text ( " \( cachedBranchInfo . unmergedCommits) commit \( cachedBranchInfo . unmergedCommits == 1 ? " " : " s " ) from this branch do not appear in the main branch. " )
706
713
. font ( . caption)
707
714
. foregroundColor ( . orange)
708
715
}
709
716
. padding ( . leading, 20 )
710
717
}
711
718
712
- if branchInfo . hasUncommittedChanges {
719
+ if cachedBranchInfo . hasUncommittedChanges {
713
720
HStack {
714
721
Image ( systemName: " exclamationmark.triangle.fill " )
715
722
. foregroundColor ( . orange)
@@ -720,7 +727,7 @@ struct DeleteTaskspaceDialog: View {
720
727
. padding ( . leading, 20 )
721
728
}
722
729
723
- if branchInfo . unmergedCommits > 0 || branchInfo . hasUncommittedChanges {
730
+ if cachedBranchInfo . unmergedCommits > 0 || cachedBranchInfo . hasUncommittedChanges {
724
731
HStack {
725
732
Image ( systemName: " exclamationmark.triangle.fill " )
726
733
. foregroundColor ( . orange)
@@ -761,10 +768,18 @@ struct DeleteTaskspaceDialog: View {
761
768
}
762
769
}
763
770
. onAppear {
764
- // Set default deleteBranch toggle based on safety analysis
765
- // Safe branches (no unmerged commits, no uncommitted changes): checked by default (encourage cleanup)
766
- // Risky branches: unchecked by default (prevent accidental loss)
767
- deleteBranch = ( branchInfo. unmergedCommits == 0 && !branchInfo. hasUncommittedChanges)
771
+ Task {
772
+ let manager = projectManager
773
+ let ts = taskspace
774
+ cachedBranchInfo = await Task . detached {
775
+ manager. getTaskspaceBranchInfo ( for: ts)
776
+ } . value
777
+
778
+ isLoadingBranchInfo = false
779
+
780
+ // Set default deleteBranch toggle based on safety analysis
781
+ deleteBranch = ( cachedBranchInfo. unmergedCommits == 0 && !cachedBranchInfo. hasUncommittedChanges)
782
+ }
768
783
}
769
784
. padding ( )
770
785
. frame ( width: 400 )
0 commit comments