Apply Mastodon poll limits in Compose view

This commit is contained in:
Shadowfacts 2023-02-05 11:37:44 -05:00
parent 32382c4783
commit 2d45fbbd91
2 changed files with 32 additions and 3 deletions

View File

@ -15,15 +15,17 @@ struct ComposeEmojiTextField: UIViewRepresentable {
@Binding var text: String @Binding var text: String
let placeholder: String let placeholder: String
let maxLength: Int?
let becomeFirstResponder: Binding<Bool>? let becomeFirstResponder: Binding<Bool>?
let focusNextView: Binding<Bool>? let focusNextView: Binding<Bool>?
private var didChange: ((String) -> Void)? = nil private var didChange: ((String) -> Void)? = nil
private var didEndEditing: (() -> Void)? = nil private var didEndEditing: (() -> Void)? = nil
private var backgroundColor: UIColor? = nil private var backgroundColor: UIColor? = nil
init(text: Binding<String>, placeholder: String, becomeFirstResponder: Binding<Bool>? = nil, focusNextView: Binding<Bool>? = nil) { init(text: Binding<String>, placeholder: String, maxLength: Int? = nil, becomeFirstResponder: Binding<Bool>? = nil, focusNextView: Binding<Bool>? = nil) {
self._text = text self._text = text
self.placeholder = placeholder self.placeholder = placeholder
self.maxLength = maxLength
self.becomeFirstResponder = becomeFirstResponder self.becomeFirstResponder = becomeFirstResponder
self.focusNextView = focusNextView self.focusNextView = focusNextView
self.didChange = nil self.didChange = nil
@ -74,6 +76,7 @@ struct ComposeEmojiTextField: UIViewRepresentable {
} else { } else {
uiView.text = text uiView.text = text
} }
context.coordinator.maxLength = maxLength
context.coordinator.didChange = didChange context.coordinator.didChange = didChange
context.coordinator.didEndEditing = didEndEditing context.coordinator.didEndEditing = didEndEditing
context.coordinator.focusNextView = focusNextView context.coordinator.focusNextView = focusNextView
@ -95,6 +98,7 @@ struct ComposeEmojiTextField: UIViewRepresentable {
var text: Binding<String>! var text: Binding<String>!
// break retained cycle through ComposeUIState.currentInput // break retained cycle through ComposeUIState.currentInput
unowned var uiState: ComposeUIState! unowned var uiState: ComposeUIState!
var maxLength: Int?
var didChange: ((String) -> Void)? var didChange: ((String) -> Void)?
var didEndEditing: (() -> Void)? var didEndEditing: (() -> Void)?
var focusNextView: Binding<Bool>? var focusNextView: Binding<Bool>?
@ -114,6 +118,14 @@ struct ComposeEmojiTextField: UIViewRepresentable {
focusNextView?.wrappedValue = true focusNextView?.wrappedValue = true
} }
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let maxLength {
return ((textField.text ?? "") as NSString).replacingCharacters(in: range, with: string).count <= maxLength
} else {
return true
}
}
func textFieldDidBeginEditing(_ textField: UITextField) { func textFieldDidBeginEditing(_ textField: UITextField) {
uiState.currentInput = self uiState.currentInput = self
updateAutocompleteState(textField: textField) updateAutocompleteState(textField: textField)

View File

@ -20,6 +20,7 @@ struct ComposePollView: View {
@ObservedObject var draft: Draft @ObservedObject var draft: Draft
@ObservedObject var poll: Draft.Poll @ObservedObject var poll: Draft.Poll
@EnvironmentObject var mastodonController: MastodonController
@Environment(\.colorScheme) var colorScheme: ColorScheme @Environment(\.colorScheme) var colorScheme: ColorScheme
@State private var duration: Duration @State private var duration: Duration
@ -31,6 +32,14 @@ struct ComposePollView: View {
self._duration = State(initialValue: .fromTimeInterval(poll.duration) ?? .oneDay) self._duration = State(initialValue: .fromTimeInterval(poll.duration) ?? .oneDay)
} }
private var canAddOption: Bool {
if let pollConfig = mastodonController.instance?.pollsConfiguration {
return poll.options.count < pollConfig.maxOptions
} else {
return true
}
}
var body: some View { var body: some View {
VStack { VStack {
HStack { HStack {
@ -67,9 +76,15 @@ struct ComposePollView: View {
.frame(height: 44 * CGFloat(poll.options.count)) .frame(height: 44 * CGFloat(poll.options.count))
Button(action: self.addOption) { Button(action: self.addOption) {
Label("Add Option", systemImage: "plus") Label {
Text("Add Option")
} icon: {
Image(systemName: "plus")
.foregroundColor(.accentColor)
}
} }
.buttonStyle(.borderless) .buttonStyle(.borderless)
.disabled(!canAddOption)
HStack { HStack {
MenuPicker(selection: $poll.multiple, options: [ MenuPicker(selection: $poll.multiple, options: [
@ -155,6 +170,8 @@ struct ComposePollOption: View {
@ObservedObject var option: Draft.Poll.Option @ObservedObject var option: Draft.Poll.Option
let optionIndex: Int let optionIndex: Int
@EnvironmentObject private var mastodonController: MastodonController
var body: some View { var body: some View {
HStack(spacing: 4) { HStack(spacing: 4) {
Checkbox(radiusFraction: poll.multiple ? 0.1 : 0.5, borderWidth: 2) Checkbox(radiusFraction: poll.multiple ? 0.1 : 0.5, borderWidth: 2)
@ -173,7 +190,7 @@ struct ComposePollOption: View {
} }
private var textField: some View { private var textField: some View {
var field = ComposeEmojiTextField(text: $option.text, placeholder: "Option \(optionIndex + 1)") var field = ComposeEmojiTextField(text: $option.text, placeholder: "Option \(optionIndex + 1)", maxLength: mastodonController.instance?.pollsConfiguration?.maxCharactersPerOption)
return field.backgroundColor(.systemBackground) return field.backgroundColor(.systemBackground)
} }