forked from shadowfacts/Tusker
Add preference to require attachment descriptions before posting
Closes #76
This commit is contained in:
parent
8178a1f339
commit
23de131290
|
@ -73,6 +73,7 @@
|
||||||
D61AC1D5232E9FA600C54D2D /* InstanceSelectorTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61AC1D4232E9FA600C54D2D /* InstanceSelectorTableViewController.swift */; };
|
D61AC1D5232E9FA600C54D2D /* InstanceSelectorTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61AC1D4232E9FA600C54D2D /* InstanceSelectorTableViewController.swift */; };
|
||||||
D61AC1D8232EA42D00C54D2D /* InstanceTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61AC1D6232EA42D00C54D2D /* InstanceTableViewCell.swift */; };
|
D61AC1D8232EA42D00C54D2D /* InstanceTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61AC1D6232EA42D00C54D2D /* InstanceTableViewCell.swift */; };
|
||||||
D61AC1D9232EA42D00C54D2D /* InstanceTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D61AC1D7232EA42D00C54D2D /* InstanceTableViewCell.xib */; };
|
D61AC1D9232EA42D00C54D2D /* InstanceTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D61AC1D7232EA42D00C54D2D /* InstanceTableViewCell.xib */; };
|
||||||
|
D620483223D2A6A3008A63EF /* CompositionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483123D2A6A3008A63EF /* CompositionState.swift */; };
|
||||||
D626493323BD751600612E6E /* ShowCameraCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D626493123BD751600612E6E /* ShowCameraCollectionViewCell.xib */; };
|
D626493323BD751600612E6E /* ShowCameraCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D626493123BD751600612E6E /* ShowCameraCollectionViewCell.xib */; };
|
||||||
D626493523BD94CE00612E6E /* CompositionAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D626493423BD94CE00612E6E /* CompositionAttachment.swift */; };
|
D626493523BD94CE00612E6E /* CompositionAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D626493423BD94CE00612E6E /* CompositionAttachment.swift */; };
|
||||||
D626493823C0FD0000612E6E /* AllPhotosTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D626493623C0FD0000612E6E /* AllPhotosTableViewCell.swift */; };
|
D626493823C0FD0000612E6E /* AllPhotosTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D626493623C0FD0000612E6E /* AllPhotosTableViewCell.swift */; };
|
||||||
|
@ -344,6 +345,7 @@
|
||||||
D61AC1D4232E9FA600C54D2D /* InstanceSelectorTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceSelectorTableViewController.swift; sourceTree = "<group>"; };
|
D61AC1D4232E9FA600C54D2D /* InstanceSelectorTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceSelectorTableViewController.swift; sourceTree = "<group>"; };
|
||||||
D61AC1D6232EA42D00C54D2D /* InstanceTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceTableViewCell.swift; sourceTree = "<group>"; };
|
D61AC1D6232EA42D00C54D2D /* InstanceTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
D61AC1D7232EA42D00C54D2D /* InstanceTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InstanceTableViewCell.xib; sourceTree = "<group>"; };
|
D61AC1D7232EA42D00C54D2D /* InstanceTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InstanceTableViewCell.xib; sourceTree = "<group>"; };
|
||||||
|
D620483123D2A6A3008A63EF /* CompositionState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositionState.swift; sourceTree = "<group>"; };
|
||||||
D626493123BD751600612E6E /* ShowCameraCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ShowCameraCollectionViewCell.xib; sourceTree = "<group>"; };
|
D626493123BD751600612E6E /* ShowCameraCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ShowCameraCollectionViewCell.xib; sourceTree = "<group>"; };
|
||||||
D626493423BD94CE00612E6E /* CompositionAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositionAttachment.swift; sourceTree = "<group>"; };
|
D626493423BD94CE00612E6E /* CompositionAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositionAttachment.swift; sourceTree = "<group>"; };
|
||||||
D626493623C0FD0000612E6E /* AllPhotosTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllPhotosTableViewCell.swift; sourceTree = "<group>"; };
|
D626493623C0FD0000612E6E /* AllPhotosTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllPhotosTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
|
|
|
@ -45,6 +45,7 @@ class Preferences: Codable, ObservableObject {
|
||||||
self.defaultPostVisibility = try container.decode(Status.Visibility.self, forKey: .defaultPostVisibility)
|
self.defaultPostVisibility = try container.decode(Status.Visibility.self, forKey: .defaultPostVisibility)
|
||||||
self.automaticallySaveDrafts = try container.decode(Bool.self, forKey: .automaticallySaveDrafts)
|
self.automaticallySaveDrafts = try container.decode(Bool.self, forKey: .automaticallySaveDrafts)
|
||||||
self.contentWarningCopyMode = try container.decode(ContentWarningCopyMode.self, forKey: .contentWarningCopyMode)
|
self.contentWarningCopyMode = try container.decode(ContentWarningCopyMode.self, forKey: .contentWarningCopyMode)
|
||||||
|
self.requireAttachmentDescriptions = try container.decode(Bool.self, forKey: .requireAttachmentDescriptions)
|
||||||
self.blurAllMedia = try container.decode(Bool.self, forKey: .blurAllMedia)
|
self.blurAllMedia = try container.decode(Bool.self, forKey: .blurAllMedia)
|
||||||
self.openLinksInApps = try container.decode(Bool.self, forKey: .openLinksInApps)
|
self.openLinksInApps = try container.decode(Bool.self, forKey: .openLinksInApps)
|
||||||
self.useInAppSafari = try container.decode(Bool.self, forKey: .useInAppSafari)
|
self.useInAppSafari = try container.decode(Bool.self, forKey: .useInAppSafari)
|
||||||
|
@ -68,6 +69,7 @@ class Preferences: Codable, ObservableObject {
|
||||||
try container.encode(defaultPostVisibility, forKey: .defaultPostVisibility)
|
try container.encode(defaultPostVisibility, forKey: .defaultPostVisibility)
|
||||||
try container.encode(automaticallySaveDrafts, forKey: .automaticallySaveDrafts)
|
try container.encode(automaticallySaveDrafts, forKey: .automaticallySaveDrafts)
|
||||||
try container.encode(contentWarningCopyMode, forKey: .contentWarningCopyMode)
|
try container.encode(contentWarningCopyMode, forKey: .contentWarningCopyMode)
|
||||||
|
try container.encode(requireAttachmentDescriptions, forKey: .requireAttachmentDescriptions)
|
||||||
try container.encode(blurAllMedia, forKey: .blurAllMedia)
|
try container.encode(blurAllMedia, forKey: .blurAllMedia)
|
||||||
try container.encode(openLinksInApps, forKey: .openLinksInApps)
|
try container.encode(openLinksInApps, forKey: .openLinksInApps)
|
||||||
try container.encode(useInAppSafari, forKey: .useInAppSafari)
|
try container.encode(useInAppSafari, forKey: .useInAppSafari)
|
||||||
|
@ -90,6 +92,7 @@ class Preferences: Codable, ObservableObject {
|
||||||
@Published var defaultPostVisibility = Status.Visibility.public
|
@Published var defaultPostVisibility = Status.Visibility.public
|
||||||
@Published var automaticallySaveDrafts = true
|
@Published var automaticallySaveDrafts = true
|
||||||
@Published var contentWarningCopyMode = ContentWarningCopyMode.asIs
|
@Published var contentWarningCopyMode = ContentWarningCopyMode.asIs
|
||||||
|
@Published var requireAttachmentDescriptions = false
|
||||||
@Published var blurAllMedia = false
|
@Published var blurAllMedia = false
|
||||||
@Published var openLinksInApps = true
|
@Published var openLinksInApps = true
|
||||||
@Published var useInAppSafari = true
|
@Published var useInAppSafari = true
|
||||||
|
@ -112,6 +115,7 @@ class Preferences: Codable, ObservableObject {
|
||||||
case defaultPostVisibility
|
case defaultPostVisibility
|
||||||
case automaticallySaveDrafts
|
case automaticallySaveDrafts
|
||||||
case contentWarningCopyMode
|
case contentWarningCopyMode
|
||||||
|
case requireAttachmentDescriptions
|
||||||
case blurAllMedia
|
case blurAllMedia
|
||||||
case openLinksInApps
|
case openLinksInApps
|
||||||
case useInAppSafari
|
case useInAppSafari
|
||||||
|
|
|
@ -37,6 +37,12 @@ class ComposeViewController: UIViewController {
|
||||||
weak var xcbSession: XCBSession?
|
weak var xcbSession: XCBSession?
|
||||||
var postedStatus: Status?
|
var postedStatus: Status?
|
||||||
|
|
||||||
|
var compositionState: CompositionState = .valid {
|
||||||
|
didSet {
|
||||||
|
postBarButtonItem.isEnabled = compositionState.isValid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
weak var postBarButtonItem: UIBarButtonItem!
|
weak var postBarButtonItem: UIBarButtonItem!
|
||||||
var visibilityBarButtonItem: UIBarButtonItem!
|
var visibilityBarButtonItem: UIBarButtonItem!
|
||||||
var contentWarningBarButtonItem: UIBarButtonItem!
|
var contentWarningBarButtonItem: UIBarButtonItem!
|
||||||
|
@ -131,6 +137,7 @@ class ComposeViewController: UIViewController {
|
||||||
// we have to set the font here, because the monospaced digit font is not available in IB
|
// we have to set the font here, because the monospaced digit font is not available in IB
|
||||||
charactersRemainingLabel.font = .monospacedDigitSystemFont(ofSize: 17, weight: .regular)
|
charactersRemainingLabel.font = .monospacedDigitSystemFont(ofSize: 17, weight: .regular)
|
||||||
updateCharactersRemaining()
|
updateCharactersRemaining()
|
||||||
|
updateAttachmentDescriptionsRequired()
|
||||||
updatePlaceholder()
|
updatePlaceholder()
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(contentWarningTextFieldDidChange), name: UITextField.textDidChangeNotification, object: contentWarningTextField)
|
NotificationCenter.default.addObserver(self, selector: #selector(contentWarningTextFieldDidChange), name: UITextField.textDidChangeNotification, object: contentWarningTextField)
|
||||||
|
@ -266,17 +273,29 @@ class ComposeViewController: UIViewController {
|
||||||
scrollView.scrollIndicatorInsets = scrollView.contentInset
|
scrollView.scrollIndicatorInsets = scrollView.contentInset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateAttachmentDescriptionsRequired() {
|
||||||
|
if Preferences.shared.requireAttachmentDescriptions {
|
||||||
|
for case let mediaView as ComposeMediaView in attachmentsStackView.arrangedSubviews {
|
||||||
|
if mediaView.descriptionTextView.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||||
|
compositionState.formUnion(.requiresAttachmentDescriptions)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
compositionState.subtract(.requiresAttachmentDescriptions)
|
||||||
|
}
|
||||||
|
|
||||||
func updateCharactersRemaining() {
|
func updateCharactersRemaining() {
|
||||||
// TODO: include CW char count
|
|
||||||
let count = CharacterCounter.count(text: statusTextView.text)
|
let count = CharacterCounter.count(text: statusTextView.text)
|
||||||
let cwCount = contentWarningEnabled ? (contentWarningTextField.text?.count ?? 0) : 0
|
let cwCount = contentWarningEnabled ? (contentWarningTextField.text?.count ?? 0) : 0
|
||||||
let remaining = (MastodonController.instance.maxStatusCharacters ?? 500) - count - cwCount
|
let remaining = (MastodonController.instance.maxStatusCharacters ?? 500) - count - cwCount
|
||||||
if remaining < 0 {
|
if remaining < 0 {
|
||||||
charactersRemainingLabel.textColor = .red
|
charactersRemainingLabel.textColor = .red
|
||||||
postBarButtonItem.isEnabled = false
|
compositionState.formUnion(.tooManyCharacters)
|
||||||
} else {
|
} else {
|
||||||
charactersRemainingLabel.textColor = .darkGray
|
charactersRemainingLabel.textColor = .darkGray
|
||||||
postBarButtonItem.isEnabled = true
|
compositionState.subtract(.tooManyCharacters)
|
||||||
}
|
}
|
||||||
charactersRemainingLabel.text = String(remaining)
|
charactersRemainingLabel.text = String(remaining)
|
||||||
charactersRemainingLabel.accessibilityLabel = String(format: NSLocalizedString("%d characters remaining", comment: "compose characters remaining accessibility label"), remaining)
|
charactersRemainingLabel.accessibilityLabel = String(format: NSLocalizedString("%d characters remaining", comment: "compose characters remaining accessibility label"), remaining)
|
||||||
|
@ -454,7 +473,7 @@ class ComposeViewController: UIViewController {
|
||||||
saveDraft()
|
saveDraft()
|
||||||
|
|
||||||
// disable post button while sending post request
|
// disable post button while sending post request
|
||||||
postBarButtonItem.isEnabled = false
|
compositionState.formUnion(.currentlyPosting)
|
||||||
|
|
||||||
let contentWarning: String?
|
let contentWarning: String?
|
||||||
if contentWarningEnabled, let cwText = contentWarningTextField.text, !cwText.isEmpty {
|
if contentWarningEnabled, let cwText = contentWarningTextField.text, !cwText.isEmpty {
|
||||||
|
@ -575,6 +594,7 @@ extension ComposeViewController: AssetPickerViewControllerDelegate {
|
||||||
}
|
}
|
||||||
func assetPicker(_ assetPicker: AssetPickerViewController, didSelectAttachments attachments: [CompositionAttachment]) {
|
func assetPicker(_ assetPicker: AssetPickerViewController, didSelectAttachments attachments: [CompositionAttachment]) {
|
||||||
selectedAttachments.append(contentsOf: attachments)
|
selectedAttachments.append(contentsOf: attachments)
|
||||||
|
updateAttachmentDescriptionsRequired()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -583,6 +603,11 @@ extension ComposeViewController: ComposeMediaViewDelegate {
|
||||||
let index = attachmentsStackView.arrangedSubviews.firstIndex(of: mediaView)!
|
let index = attachmentsStackView.arrangedSubviews.firstIndex(of: mediaView)!
|
||||||
selectedAttachments.remove(at: index)
|
selectedAttachments.remove(at: index)
|
||||||
updateAddAttachmentButton()
|
updateAddAttachmentButton()
|
||||||
|
updateAttachmentDescriptionsRequired()
|
||||||
|
}
|
||||||
|
|
||||||
|
func descriptionTextViewDidChange(_ mediaView: ComposeMediaView) {
|
||||||
|
updateAttachmentDescriptionsRequired()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -631,6 +656,8 @@ extension ComposeViewController: DraftsTableViewControllerDelegate {
|
||||||
// call the delegate method manually, since setting the text property doesn't call it
|
// call the delegate method manually, since setting the text property doesn't call it
|
||||||
mediaView.textViewDidChange(mediaView.descriptionTextView)
|
mediaView.textViewDidChange(mediaView.descriptionTextView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateAttachmentDescriptionsRequired()
|
||||||
}
|
}
|
||||||
|
|
||||||
func draftSelectionCompleted() {
|
func draftSelectionCompleted() {
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
//
|
||||||
|
// CompositionState.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 1/17/20.
|
||||||
|
// Copyright © 2020 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct CompositionState: OptionSet {
|
||||||
|
let rawValue: Int
|
||||||
|
|
||||||
|
static let currentlyPosting = CompositionState(rawValue: 1 << 0)
|
||||||
|
static let tooManyCharacters = CompositionState(rawValue: 1 << 1)
|
||||||
|
static let requiresAttachmentDescriptions = CompositionState(rawValue: 1 << 2)
|
||||||
|
|
||||||
|
static let valid: CompositionState = []
|
||||||
|
|
||||||
|
var isValid: Bool {
|
||||||
|
isEmpty
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,9 @@ struct BehaviorPrefsView: View {
|
||||||
Text("Prepend 're: '").tag(ContentWarningCopyMode.prependRe)
|
Text("Prepend 're: '").tag(ContentWarningCopyMode.prependRe)
|
||||||
Text("Don't copy").tag(ContentWarningCopyMode.doNotCopy)
|
Text("Don't copy").tag(ContentWarningCopyMode.doNotCopy)
|
||||||
}
|
}
|
||||||
|
Toggle(isOn: $preferences.requireAttachmentDescriptions) {
|
||||||
|
Text("Require Attachment Descriptions")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import AVFoundation
|
||||||
|
|
||||||
protocol ComposeMediaViewDelegate {
|
protocol ComposeMediaViewDelegate {
|
||||||
func didRemoveMedia(_ mediaView: ComposeMediaView)
|
func didRemoveMedia(_ mediaView: ComposeMediaView)
|
||||||
|
func descriptionTextViewDidChange(_ mediaView: ComposeMediaView)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ComposeMediaView: UIView {
|
class ComposeMediaView: UIView {
|
||||||
|
@ -69,5 +70,6 @@ class ComposeMediaView: UIView {
|
||||||
extension ComposeMediaView: UITextViewDelegate {
|
extension ComposeMediaView: UITextViewDelegate {
|
||||||
func textViewDidChange(_ textView: UITextView) {
|
func textViewDidChange(_ textView: UITextView) {
|
||||||
placeholderLabel.isHidden = !descriptionTextView.text.isEmpty
|
placeholderLabel.isHidden = !descriptionTextView.text.isEmpty
|
||||||
|
delegate?.descriptionTextViewDidChange(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue