diff --git a/GiteaPR.xcodeproj/project.pbxproj b/GiteaPR.xcodeproj/project.pbxproj index d0f6dc6..32044a8 100644 --- a/GiteaPR.xcodeproj/project.pbxproj +++ b/GiteaPR.xcodeproj/project.pbxproj @@ -187,7 +187,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 20; + CURRENT_PROJECT_VERSION = 21; DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_CFBundleDisplayName = "GiteaPR"; @@ -197,7 +197,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.9; + MARKETING_VERSION = 3.0; PRODUCT_BUNDLE_IDENTIFIER = com.bueso.giteapr; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -211,7 +211,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 20; + CURRENT_PROJECT_VERSION = 21; DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_CFBundleDisplayName = "GiteaPR"; @@ -221,7 +221,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.9; + MARKETING_VERSION = 3.0; PRODUCT_BUNDLE_IDENTIFIER = com.bueso.giteapr; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; diff --git a/GiteaPR/Detail/PRDetailView.swift b/GiteaPR/Detail/PRDetailView.swift index 255a782..0338747 100644 --- a/GiteaPR/Detail/PRDetailView.swift +++ b/GiteaPR/Detail/PRDetailView.swift @@ -2,12 +2,14 @@ import SwiftUI struct PRDetailView: View { @StateObject private var vm: PRDetailViewModel + @Environment(\.dismiss) private var dismiss @State private var selectedTab: DetailTab = .overview @State private var showCommentSheet = false @State private var showMergeSheet = false @State private var showCloseConfirm = false @State private var showRerunConfirm: ActionRun? @State private var requestChangesMode = false + @State private var showMergeSuccess = false enum DetailTab: String, CaseIterable, Identifiable { case overview = "Overview" @@ -56,6 +58,7 @@ struct PRDetailView: View { Button("OK", role: .cancel) {} } message: { Text(vm.errorMessage ?? "") } .overlay { if vm.busyMessage != nil { busyOverlay } } + .overlay { if showMergeSuccess { mergeSuccessOverlay } } .confirmationDialog("Close this PR?", isPresented: $showCloseConfirm, titleVisibility: .visible) { Button("Close", role: .destructive) { Task { await vm.setState("closed") } } Button("Cancel", role: .cancel) {} @@ -65,7 +68,19 @@ struct PRDetailView: View { mode: requestChangesMode ? .requestChanges : .comment) .onDisappear { requestChangesMode = false } } - .sheet(isPresented: $showMergeSheet) { MergeSheet(vm: vm) } + .sheet(isPresented: $showMergeSheet) { + MergeSheet(vm: vm) { + // Merge succeeded: show the success animation, then pop back + // to the landing page. + withAnimation(.spring(response: 0.4, dampingFraction: 0.6)) { + showMergeSuccess = true + } + Task { + try? await Task.sleep(nanoseconds: 1_200_000_000) + dismiss() + } + } + } .confirmationDialog("Re-run workflow?", isPresented: .init( get: { showRerunConfirm != nil }, @@ -219,4 +234,24 @@ struct PRDetailView: View { } .ignoresSafeArea() } + + /// Full-screen success animation shown briefly after a merge, then the + /// view pops back to the landing page. + private var mergeSuccessOverlay: some View { + ZStack { + Color.black.opacity(0.4) + .ignoresSafeArea() + VStack(spacing: 12) { + Image(systemName: "checkmark.circle.fill") + .font(.system(size: 72)) + .foregroundColor(.green) + .scaleEffect(showMergeSuccess ? 1 : 0.3) + .opacity(showMergeSuccess ? 1 : 0) + Text("Merged") + .font(.title2.weight(.semibold)) + .foregroundColor(.white) + } + } + .transition(.opacity) + } } diff --git a/GiteaPR/Detail/Sheets.swift b/GiteaPR/Detail/Sheets.swift index f2be0a0..83ed7e4 100644 --- a/GiteaPR/Detail/Sheets.swift +++ b/GiteaPR/Detail/Sheets.swift @@ -65,6 +65,9 @@ struct MergeSheet: View { @ObservedObject var vm: PRDetailViewModel @Environment(\.dismiss) private var dismiss @State private var method = "merge" + /// Called when the merge succeeds, so the parent can show the success + /// animation and pop back to the landing page. + var onSuccess: () -> Void = {} private let methods = [("merge", "Create merge commit"), ("rebase", "Rebase and merge"), @@ -129,7 +132,10 @@ struct MergeSheet: View { private func performMerge() { Task { let success = await vm.merge(method: method) - if success { dismiss() } + if success { + dismiss() + onSuccess() + } } } }