Merge success animation + auto-return to landing page; bump to 3.0
Build iOS App / Build .ipa and publish source (push) Successful in 23s
Build iOS App / Build .ipa and publish source (push) Successful in 23s
On a successful merge, show a brief full-screen green checkmark animation
('Merged'), then automatically pop the PR detail view back to the landing
page. MergeSheet now calls an onSuccess callback; PRDetailView shows the
overlay and dismisses after ~1.2s. Bump to 3.0.
This commit is contained in:
1 parent
5e9ee9e5cf
commit
2301b8a861
3 files changed
+47
-6
No files matched your search
@@ -187,7 +187,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 20;
|
CURRENT_PROJECT_VERSION = 21;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "GiteaPR";
|
INFOPLIST_KEY_CFBundleDisplayName = "GiteaPR";
|
||||||
@@ -197,7 +197,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 2.9;
|
MARKETING_VERSION = 3.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.bueso.giteapr;
|
PRODUCT_BUNDLE_IDENTIFIER = com.bueso.giteapr;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
@@ -211,7 +211,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 20;
|
CURRENT_PROJECT_VERSION = 21;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "GiteaPR";
|
INFOPLIST_KEY_CFBundleDisplayName = "GiteaPR";
|
||||||
@@ -221,7 +221,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 2.9;
|
MARKETING_VERSION = 3.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.bueso.giteapr;
|
PRODUCT_BUNDLE_IDENTIFIER = com.bueso.giteapr;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ import SwiftUI
|
|||||||
|
|
||||||
struct PRDetailView: View {
|
struct PRDetailView: View {
|
||||||
@StateObject private var vm: PRDetailViewModel
|
@StateObject private var vm: PRDetailViewModel
|
||||||
|
@Environment(\.dismiss) private var dismiss
|
||||||
@State private var selectedTab: DetailTab = .overview
|
@State private var selectedTab: DetailTab = .overview
|
||||||
@State private var showCommentSheet = false
|
@State private var showCommentSheet = false
|
||||||
@State private var showMergeSheet = false
|
@State private var showMergeSheet = false
|
||||||
@State private var showCloseConfirm = false
|
@State private var showCloseConfirm = false
|
||||||
@State private var showRerunConfirm: ActionRun?
|
@State private var showRerunConfirm: ActionRun?
|
||||||
@State private var requestChangesMode = false
|
@State private var requestChangesMode = false
|
||||||
|
@State private var showMergeSuccess = false
|
||||||
|
|
||||||
enum DetailTab: String, CaseIterable, Identifiable {
|
enum DetailTab: String, CaseIterable, Identifiable {
|
||||||
case overview = "Overview"
|
case overview = "Overview"
|
||||||
@@ -56,6 +58,7 @@ struct PRDetailView: View {
|
|||||||
Button("OK", role: .cancel) {}
|
Button("OK", role: .cancel) {}
|
||||||
} message: { Text(vm.errorMessage ?? "") }
|
} message: { Text(vm.errorMessage ?? "") }
|
||||||
.overlay { if vm.busyMessage != nil { busyOverlay } }
|
.overlay { if vm.busyMessage != nil { busyOverlay } }
|
||||||
|
.overlay { if showMergeSuccess { mergeSuccessOverlay } }
|
||||||
.confirmationDialog("Close this PR?", isPresented: $showCloseConfirm, titleVisibility: .visible) {
|
.confirmationDialog("Close this PR?", isPresented: $showCloseConfirm, titleVisibility: .visible) {
|
||||||
Button("Close", role: .destructive) { Task { await vm.setState("closed") } }
|
Button("Close", role: .destructive) { Task { await vm.setState("closed") } }
|
||||||
Button("Cancel", role: .cancel) {}
|
Button("Cancel", role: .cancel) {}
|
||||||
@@ -65,7 +68,19 @@ struct PRDetailView: View {
|
|||||||
mode: requestChangesMode ? .requestChanges : .comment)
|
mode: requestChangesMode ? .requestChanges : .comment)
|
||||||
.onDisappear { requestChangesMode = false }
|
.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?",
|
.confirmationDialog("Re-run workflow?",
|
||||||
isPresented: .init(
|
isPresented: .init(
|
||||||
get: { showRerunConfirm != nil },
|
get: { showRerunConfirm != nil },
|
||||||
@@ -219,4 +234,24 @@ struct PRDetailView: View {
|
|||||||
}
|
}
|
||||||
.ignoresSafeArea()
|
.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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -65,6 +65,9 @@ struct MergeSheet: View {
|
|||||||
@ObservedObject var vm: PRDetailViewModel
|
@ObservedObject var vm: PRDetailViewModel
|
||||||
@Environment(\.dismiss) private var dismiss
|
@Environment(\.dismiss) private var dismiss
|
||||||
@State private var method = "merge"
|
@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"),
|
private let methods = [("merge", "Create merge commit"),
|
||||||
("rebase", "Rebase and merge"),
|
("rebase", "Rebase and merge"),
|
||||||
@@ -129,7 +132,10 @@ struct MergeSheet: View {
|
|||||||
private func performMerge() {
|
private func performMerge() {
|
||||||
Task {
|
Task {
|
||||||
let success = await vm.merge(method: method)
|
let success = await vm.merge(method: method)
|
||||||
if success { dismiss() }
|
if success {
|
||||||
|
dismiss()
|
||||||
|
onSuccess()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user