Skip to content

Commit 31362ef

Browse files
authored
Offline Mode: Add PublishButton (#22614)
2 parents 44e5760 + 2d62536 commit 31362ef

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import SwiftUI
2+
3+
struct PublishButton: View {
4+
@ObservedObject var viewModel: PublishButtonViewModel
5+
6+
var body: some View {
7+
ZStack {
8+
Button(action: viewModel.onSubmitTapped) {
9+
Text(viewModel.title)
10+
.font(.title3.weight(.medium))
11+
.frame(maxWidth: .infinity)
12+
.opacity(isDisabled ? 0 : 1)
13+
}
14+
.buttonStyle(.borderedProminent)
15+
.controlSize(.large)
16+
.disabled(isDisabled)
17+
.buttonBorderShape(.roundedRectangle(radius: 8))
18+
19+
switch viewModel.state {
20+
case .default:
21+
EmptyView()
22+
case .loading:
23+
ProgressView()
24+
.tint(Color.secondary)
25+
case let .uploading(title, progress):
26+
HStack(spacing: 10) {
27+
ProgressView()
28+
.tint(Color.secondary)
29+
30+
VStack(alignment: .leading) {
31+
Text(title)
32+
.font(.subheadline.weight(.medium))
33+
if let progress {
34+
Text(Strings.progress(progress))
35+
.foregroundStyle(Color.secondary)
36+
.font(.footnote)
37+
.monospacedDigit()
38+
}
39+
}
40+
.lineLimit(1)
41+
.foregroundStyle(Color.primary)
42+
43+
Spacer()
44+
}
45+
.padding(.horizontal)
46+
case let .failed(title, details, onRetryTapped):
47+
HStack {
48+
Image(systemName: "exclamationmark.triangle.fill")
49+
.foregroundStyle(Color.red)
50+
VStack(alignment: .leading) {
51+
Text(title)
52+
.font(.subheadline.weight(.medium))
53+
.foregroundStyle(.primary)
54+
if let details {
55+
Text(details)
56+
.font(.footnote)
57+
.foregroundStyle(.secondary)
58+
}
59+
}
60+
.lineLimit(1)
61+
62+
Spacer()
63+
64+
if let onRetryTapped {
65+
Button(Strings.retry, action: onRetryTapped)
66+
.font(.subheadline)
67+
}
68+
}
69+
.padding(.horizontal)
70+
}
71+
}
72+
}
73+
74+
private var isDisabled: Bool {
75+
switch viewModel.state {
76+
case .default: false
77+
case .loading, .uploading, .failed: true
78+
}
79+
}
80+
}
81+
82+
final class PublishButtonViewModel: ObservableObject {
83+
let title: String
84+
let onSubmitTapped: () -> Void
85+
@Published var state: PublishButtonState = .default
86+
87+
init(title: String, onSubmitTapped: @escaping () -> Void, state: PublishButtonState = .default) {
88+
self.title = title
89+
self.onSubmitTapped = onSubmitTapped
90+
self.state = state
91+
}
92+
}
93+
94+
enum PublishButtonState {
95+
case `default`
96+
case loading
97+
case uploading(title: String, progress: Progress?)
98+
case failed(title: String, details: String? = nil, onRetryTapped: (() -> Void)? = nil)
99+
100+
struct Progress {
101+
let completed: Int64
102+
let total: Int64
103+
}
104+
}
105+
106+
private enum Strings {
107+
static func progress(_ progress: PublishButtonState.Progress) -> String {
108+
let format = NSLocalizedString("publishButton.progress", value: "%@ of %@", comment: "Shows the download or upload progress with two parameters: preformatted completed and total bytes")
109+
return String(format: format, ByteCountFormatter.string(fromByteCount: progress.completed, countStyle: .file), ByteCountFormatter.string(fromByteCount: progress.total, countStyle: .file))
110+
}
111+
112+
static let retry = NSLocalizedString("publishButton.retry", value: "Retry", comment: "Retry button title")
113+
}
114+
115+
#Preview {
116+
VStack(spacing: 16) {
117+
PublishButton(viewModel: .init(title: "Publish", onSubmitTapped: {}, state: .default))
118+
PublishButton(viewModel: .init(title: "Publish", onSubmitTapped: {}, state: .loading))
119+
PublishButton(viewModel: .init(title: "Publish", onSubmitTapped: {}, state: .uploading(title: "Uploading media...", progress: .init(completed: 100, total: 2000))))
120+
PublishButton(viewModel: .init(title: "Publish", onSubmitTapped: {}, state: .failed(title: "Failed to upload media")))
121+
PublishButton(viewModel: .init(title: "Publish", onSubmitTapped: {}, state: .failed(title: "Failed to upload media", details: "Not connected to Internet", onRetryTapped: {})))
122+
}
123+
.padding()
124+
}

WordPress/WordPress.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,8 @@
407407
0A9687BC28B40771009DCD2F /* FullScreenCommentReplyViewModelMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9687BB28B40771009DCD2F /* FullScreenCommentReplyViewModelMock.swift */; };
408408
0C01A6EA2AB37F0F009F7145 /* SiteMediaCollectionCellSelectionOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C01A6E92AB37F0F009F7145 /* SiteMediaCollectionCellSelectionOverlayView.swift */; };
409409
0C01A6EB2AB37F0F009F7145 /* SiteMediaCollectionCellSelectionOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C01A6E92AB37F0F009F7145 /* SiteMediaCollectionCellSelectionOverlayView.swift */; };
410+
0C03AECA2B7D995F00B64A25 /* PublishButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C03AEC92B7D995F00B64A25 /* PublishButton.swift */; };
411+
0C03AECB2B7D995F00B64A25 /* PublishButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C03AEC92B7D995F00B64A25 /* PublishButton.swift */; };
410412
0C0453282AC73343003079C8 /* SiteMediaVideoDurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0453272AC73343003079C8 /* SiteMediaVideoDurationView.swift */; };
411413
0C0453292AC73343003079C8 /* SiteMediaVideoDurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0453272AC73343003079C8 /* SiteMediaVideoDurationView.swift */; };
412414
0C04532B2AC77245003079C8 /* SiteMediaDocumentInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C04532A2AC77245003079C8 /* SiteMediaDocumentInfoView.swift */; };
@@ -6154,6 +6156,7 @@
61546156
0A9610F828B2E56300076EBA /* UserSuggestion+Comparable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserSuggestion+Comparable.swift"; sourceTree = "<group>"; };
61556157
0A9687BB28B40771009DCD2F /* FullScreenCommentReplyViewModelMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenCommentReplyViewModelMock.swift; sourceTree = "<group>"; };
61566158
0C01A6E92AB37F0F009F7145 /* SiteMediaCollectionCellSelectionOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteMediaCollectionCellSelectionOverlayView.swift; sourceTree = "<group>"; };
6159+
0C03AEC92B7D995F00B64A25 /* PublishButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublishButton.swift; sourceTree = "<group>"; };
61576160
0C0453272AC73343003079C8 /* SiteMediaVideoDurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteMediaVideoDurationView.swift; sourceTree = "<group>"; };
61586161
0C04532A2AC77245003079C8 /* SiteMediaDocumentInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteMediaDocumentInfoView.swift; sourceTree = "<group>"; };
61596162
0C0AD1052B0C483F00EC06E6 /* ExternalMediaSelectionTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalMediaSelectionTitleView.swift; sourceTree = "<group>"; };
@@ -18532,6 +18535,7 @@
1853218535
FEDA8D9E2A5B1B1B0081314F /* PrepublishingAutoSharingView.swift */,
1853318536
FE7B9A862A6A613000488791 /* PrepublishingSocialAccountsViewController.swift */,
1853418537
FE7B9A892A6BD20200488791 /* PrepublishingSocialAccountsTableFooterView.swift */,
18538+
0C03AEC92B7D995F00B64A25 /* PublishButton.swift */,
1853518539
);
1853618540
path = Prepublishing;
1853718541
sourceTree = "<group>";
@@ -22871,6 +22875,7 @@
2287122875
0CB4056B29C78F06008EED0A /* BlogDashboardPersonalizationService.swift in Sources */,
2287222876
E17780801C97FA9500FA7E14 /* StoreKit+Debug.swift in Sources */,
2287322877
E1ADE0EB20A9EF6200D6AADC /* PrivacySettingsViewController.swift in Sources */,
22878+
0C03AECA2B7D995F00B64A25 /* PublishButton.swift in Sources */,
2287422879
E6431DE61C4E892900FD8D90 /* SharingDetailViewController.m in Sources */,
2287522880
FA8E2FE027C6377000DA0982 /* DashboardQuickStartCardCell.swift in Sources */,
2287622881
B50EED791C0E5B2400D278CA /* SettingsPickerViewController.swift in Sources */,
@@ -25743,6 +25748,7 @@
2574325748
0C748B4C2A9D71A100809E1A /* SiteMediaCollectionViewController.swift in Sources */,
2574425749
FABB25C52602FC2C00C8785C /* MenusSelectionItemView.m in Sources */,
2574525750
FABB25C62602FC2C00C8785C /* TenorReponseParser.swift in Sources */,
25751+
0C03AECB2B7D995F00B64A25 /* PublishButton.swift in Sources */,
2574625752
FABB25C72602FC2C00C8785C /* WPStyleGuide+Gridicon.swift in Sources */,
2574725753
FABB25C92602FC2C00C8785C /* CreateButtonCoordinator.swift in Sources */,
2574825754
3FBF21B8267AA17A0098335F /* BloggingRemindersAnimator.swift in Sources */,

0 commit comments

Comments
 (0)