Simplify SelectableText state management

This commit simplifies the state management and information flow for SelectableText.

This also fixes issues and inconsistencies with the selected text for a highlight action,
which often appeared in some scenarios with the symptom of a highlight
action showing the incorrect or outdated selected text.

Previously, the state of the selected text and highlight action was
tracked in two independent state/binding variables which caused
re-renders when they were modified, often leading to inconsistencies as
those two independent variables would not be changed atomically across
renders leading to inconsistent, undefined behavior

The commit addresses this by using a single state object instead of two,
and a direct callback interface when the highlight button is pressed,
which eliminates the need of relying on view re-renders to apply.

Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
Daniel D’Aquino
2024-08-17 14:38:23 -07:00
parent 9d97886e3f
commit 4f881a5667

View File

@@ -13,8 +13,7 @@ struct SelectableText: View {
let event: NostrEvent?
let attributedString: AttributedString
let textAlignment: NSTextAlignment
@State private var showHighlightPost = false
@State private var selectedText = ""
@State private var highlightPostingState: HighlightPostingState = .hide
@State private var selectedTextHeight: CGFloat = .zero
@State private var selectedTextWidth: CGFloat = .zero
@@ -37,8 +36,9 @@ struct SelectableText: View {
fixedWidth: selectedTextWidth,
textAlignment: self.textAlignment,
enableHighlighting: self.enableHighlighting(),
showHighlightPost: $showHighlightPost,
selectedText: $selectedText,
postHighlight: { selectedText in
self.highlightPostingState = .show_post_view(highlighted_text: selectedText)
},
height: $selectedTextHeight
)
.padding([.leading, .trailing], -1.0)
@@ -53,9 +53,13 @@ struct SelectableText: View {
self.selectedTextWidth = newSize.width
}
}
.sheet(isPresented: $showHighlightPost) {
if let event {
HighlightPostView(damus_state: damus_state, event: event, selectedText: $selectedText)
.sheet(isPresented: Binding(get: {
return self.highlightPostingState.show()
}, set: { newValue in
self.highlightPostingState = newValue ? .show_post_view(highlighted_text: self.highlightPostingState.highlighted_text() ?? "") : .hide
})) {
if let event, case .show_post_view(let highlighted_text) = self.highlightPostingState {
HighlightPostView(damus_state: damus_state, event: event, selectedText: .constant(highlighted_text))
.presentationDragIndicator(.visible)
.presentationDetents([.height(selectedTextHeight + 150), .medium, .large])
}
@@ -66,15 +70,34 @@ struct SelectableText: View {
func enableHighlighting() -> Bool {
self.event != nil
}
enum HighlightPostingState {
case hide
case show_post_view(highlighted_text: String)
func show() -> Bool {
if case .show_post_view(let highlighted_text) = self {
return true
}
return false
}
func highlighted_text() -> String? {
switch self {
case .hide:
return nil
case .show_post_view(highlighted_text: let highlighted_text):
return highlighted_text
}
}
}
}
fileprivate class TextView: UITextView {
@Binding var showHighlightPost: Bool
@Binding var selectedText: String
var postHighlight: (String) -> Void
init(frame: CGRect, textContainer: NSTextContainer?, showHighlightPost: Binding<Bool>, selectedText: Binding<String>) {
self._showHighlightPost = showHighlightPost
self._selectedText = selectedText
init(frame: CGRect, textContainer: NSTextContainer?, postHighlight: @escaping (String) -> Void) {
self.postHighlight = postHighlight
super.init(frame: frame, textContainer: textContainer)
}
@@ -91,8 +114,8 @@ fileprivate class TextView: UITextView {
@objc public func highlightText(_ sender: Any?) {
guard let selectedRange = self.selectedTextRange else { return }
selectedText = self.text(in: selectedRange) ?? ""
showHighlightPost.toggle()
guard let selectedText = self.text(in: selectedRange) else { return }
self.postHighlight(selectedText)
}
}
@@ -105,12 +128,11 @@ fileprivate class TextView: UITextView {
let fixedWidth: CGFloat
let textAlignment: NSTextAlignment
let enableHighlighting: Bool
@Binding var showHighlightPost: Bool
@Binding var selectedText: String
let postHighlight: (String) -> Void
@Binding var height: CGFloat
func makeUIView(context: UIViewRepresentableContext<Self>) -> TextView {
let view = TextView(frame: .zero, textContainer: nil, showHighlightPost: $showHighlightPost, selectedText: $selectedText)
let view = TextView(frame: .zero, textContainer: nil, postHighlight: postHighlight)
view.isEditable = false
view.dataDetectorTypes = .all
view.isSelectable = true