Skip to content

Commit ef32060

Browse files
committed
Added MultilineEditableTextRow
1 parent 93c726e commit ef32060

File tree

5 files changed

+172
-26
lines changed

5 files changed

+172
-26
lines changed

WooCommerce/Classes/ViewModels/Booking Details/BookingDetailsViewModel.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ private extension BookingDetailsViewModel {
7272

7373
let bookingNotes = Section(
7474
header: .title(Localization.bookingNotesSectionHeaderTitle.uppercased()),
75+
footerText: "This is a private note. It'll not be shared with the customer.",
7576
content: .bookingNotes
7677
)
7778

WooCommerce/Classes/ViewRelated/Bookings/Booking Details/BookingDetailsView.swift

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -239,30 +239,9 @@ private extension BookingDetailsView {
239239
}
240240

241241
func bookingNotesView() -> some View {
242-
NavigationLink {
243-
// TODO: push booking notes editor
244-
Text(viewModel.note)
245-
} label: {
246-
HStack(alignment: .center, spacing: Layout.contentSidePadding) {
247-
if viewModel.note.isEmpty {
248-
Image(systemName: "plus")
249-
.font(.title3.weight(.medium))
250-
251-
Text(Localization.bookingNotesRowText)
252-
.rowTextStyle()
253-
.foregroundColor(.accentColor)
254-
} else {
255-
Text(viewModel.note)
256-
.rowTextStyle()
257-
.multilineTextAlignment(.leading)
258-
}
259-
260-
Spacer()
261-
262-
DisclosureIndicator()
263-
}
264-
.padding(.vertical, Layout.rowTextVerticalPadding)
265-
}
242+
MultilineEditableTextRow(value: viewModel.note,
243+
placeholder: Localization.bookingNotesRowText,
244+
detailTitle: Localization.bookingNoteNavbarText)
266245
}
267246
}
268247

@@ -321,6 +300,12 @@ extension BookingDetailsView {
321300
value: "Add a note",
322301
comment: "Add a booking note section in booking details view."
323302
)
303+
304+
static let bookingNoteNavbarText = NSLocalizedString(
305+
"BookingDetailsView.bookingNote.navbar.title",
306+
value: "Booking note",
307+
comment: "Title of navigation bar when editing a booking note."
308+
)
324309
}
325310
}
326311

@@ -350,8 +335,8 @@ struct BookingDetailsView_Previews: PreviewProvider {
350335
localTimezone: "America/New_York",
351336
currency: "USD",
352337
orderInfo: nil,
353-
// note: ""
354-
note: "note note 123 note note note note note note note note note note note note note n312 ote note note note note note note note 123 note note note note note note note note note note note note note note note note note note note note note note note note note note "
338+
note: ""
339+
// note: "note note 123 note note note note note note note note note note note note note n312 ote note note note note note note note 123 note note note note note note note note note note note note note note note note note note note note note note note note note note "
355340
)
356341
let viewModel = BookingDetailsViewModel(booking: sampleBooking)
357342
return BookingDetailsView(viewModel)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import SwiftUI
2+
3+
struct MultilineEditableTextDetailView: View {
4+
@Environment(\.dismiss) private var dismiss
5+
6+
@Binding var text: String
7+
@State private var editedText: String
8+
@State private var showDiscardChangesDialog = false
9+
@FocusState private var isFocused: Bool
10+
11+
let title: String?
12+
13+
init(text: Binding<String>, title: String? = nil) {
14+
self._text = text
15+
self._editedText = State(initialValue: text.wrappedValue)
16+
self.title = title
17+
}
18+
19+
var body: some View {
20+
VStack {
21+
TextEditor(text: $editedText)
22+
.focused($isFocused)
23+
.padding(.horizontal, Layout.horizontalPadding)
24+
.padding(.vertical, Layout.verticalPadding)
25+
}
26+
.if(title != nil) {
27+
$0.navigationTitle(title ?? "")
28+
}
29+
.navigationBarTitleDisplayMode(.inline)
30+
.navigationBarBackButtonHidden(true)
31+
.toolbar {
32+
ToolbarItem(placement: .topBarLeading) {
33+
Button(action: handleBackButtonTap) {
34+
Image(systemName: "chevron.backward")
35+
.font(.body.weight(.semibold))
36+
}
37+
}
38+
39+
if editedText != text {
40+
ToolbarItem(placement: .primaryAction) {
41+
Button(Localization.doneButtonTitle) {
42+
text = editedText
43+
dismiss()
44+
}
45+
.fontWeight(.medium)
46+
}
47+
}
48+
}
49+
.confirmationDialog(
50+
Localization.discardChangesAlertTitle,
51+
isPresented: $showDiscardChangesDialog,
52+
titleVisibility: .visible
53+
) {
54+
Button(Localization.discardChangesActionTitle, role: .destructive) {
55+
dismiss()
56+
}
57+
Button(Localization.cancelActionTitle, role: .cancel) {}
58+
}
59+
.wooNavigationBarStyle()
60+
.onAppear { isFocused = true }
61+
}
62+
63+
private func handleBackButtonTap() {
64+
if editedText != text {
65+
showDiscardChangesDialog = true
66+
} else {
67+
dismiss()
68+
}
69+
}
70+
}
71+
72+
fileprivate extension MultilineEditableTextDetailView {
73+
enum Layout {
74+
static let horizontalPadding: CGFloat = 16
75+
static let verticalPadding: CGFloat = 12
76+
}
77+
}
78+
79+
extension MultilineEditableTextDetailView {
80+
enum Localization {
81+
static let discardChangesAlertTitle = NSLocalizedString(
82+
"MultilineEditableTextDetailView.discardChanges.alert.title",
83+
value: "Are you sure you want to discard these changes?",
84+
comment: "Title for the confirmation dialog when the user attempts to discard changes in the multiline text editor."
85+
)
86+
static let discardChangesActionTitle = NSLocalizedString(
87+
"MultilineEditableTextDetailView.discardChanges.alert.discardAction",
88+
value: "Discard changes",
89+
comment: "Destructive action button title to discard changes in the multiline text editor."
90+
)
91+
static let cancelActionTitle = NSLocalizedString(
92+
"MultilineEditableTextDetailView.discardChanges.alert.cancelAction",
93+
value: "Cancel",
94+
comment: "Cancel button title for the discard changes confirmation dialog in the multiline text editor."
95+
)
96+
static let doneButtonTitle = NSLocalizedString(
97+
"MultilineEditableTextDetailView.doneButton.title",
98+
value: "Done",
99+
comment: "Navigation bar button title used to save changes and close the multiline text editor."
100+
)
101+
}
102+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import SwiftUI
2+
3+
struct MultilineEditableTextRow: View {
4+
@State var value: String
5+
let placeholder: String
6+
let detailTitle: String?
7+
8+
init(value: String, placeholder: String, detailTitle: String? = nil) {
9+
self.value = value
10+
self.placeholder = placeholder
11+
self.detailTitle = detailTitle
12+
}
13+
14+
var body: some View {
15+
NavigationLink {
16+
MultilineEditableTextDetailView(text: $value, title: detailTitle)
17+
} label: {
18+
HStack(alignment: .center, spacing: Layout.spacing) {
19+
if value.isEmpty {
20+
Text(placeholder)
21+
.rowTextStyle()
22+
.foregroundStyle(Color(.textSubtle))
23+
} else {
24+
Text(value)
25+
.multilineTextAlignment(.leading)
26+
.foregroundStyle(Color.primary)
27+
}
28+
29+
Spacer()
30+
31+
DisclosureIndicator()
32+
}
33+
.padding(.vertical, Layout.padding)
34+
}
35+
}
36+
}
37+
38+
fileprivate extension MultilineEditableTextRow {
39+
enum Layout {
40+
static let spacing: CGFloat = 10
41+
static let padding: CGFloat = 12
42+
}
43+
}
44+
45+
#if DEBUG
46+
#Preview {
47+
@Previewable @State var text: String = ""
48+
49+
NavigationStack {
50+
MultilineEditableTextRow(value: text, placeholder: "Add note")
51+
.padding(.horizontal, 16)
52+
}
53+
.preferredColorScheme(.dark)
54+
}
55+
#endif

WooCommerce/WooCommerce.xcodeproj/project.pbxproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5785,6 +5785,7 @@
57855785
3FD9BFBE2E0A2533004A8DC8 /* WooCommerceScreenshots */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (3FD9BFC42E0A2534004A8DC8 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = WooCommerceScreenshots; sourceTree = "<group>"; };
57865786
646A2C682E9FCD7E003A32A1 /* Routing */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Routing; sourceTree = "<group>"; };
57875787
6489D8522EA667AC00D96802 /* Routing */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Routing; sourceTree = "<group>"; };
5788+
64EA08E42EC214FA00050202 /* MultilineEditableTextRow */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = MultilineEditableTextRow; sourceTree = "<group>"; };
57885789
DEDB5D342E7A68950022E5A1 /* Bookings */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Bookings; sourceTree = "<group>"; };
57895790
/* End PBXFileSystemSynchronizedRootGroup section */
57905791

@@ -8487,6 +8488,7 @@
84878488
45D875D72611EA3D00226C3F /* SwiftUI Components */ = {
84888489
isa = PBXGroup;
84898490
children = (
8491+
64EA08E42EC214FA00050202 /* MultilineEditableTextRow */,
84908492
2D880B482DFB2F3D00A6FB2C /* OptionalBinding.swift */,
84918493
EE3B17B52AA03837004D3E0C /* CelebrationView.swift */,
84928494
DE7E5E8B2B4E9353002E28D2 /* ErrorStateView.swift */,
@@ -13262,6 +13264,7 @@
1326213264
fileSystemSynchronizedGroups = (
1326313265
2D33E6B02DD1453E000C7198 /* WooShippingPaymentMethod */,
1326413266
646A2C682E9FCD7E003A32A1 /* Routing */,
13267+
64EA08E42EC214FA00050202 /* MultilineEditableTextRow */,
1326513268
DEDB5D342E7A68950022E5A1 /* Bookings */,
1326613269
);
1326713270
name = WooCommerce;

0 commit comments

Comments
 (0)