forked from shadowfacts/Tusker
Add character counter
This commit is contained in:
parent
d1b45ddf7b
commit
3da1a7badd
|
@ -15,8 +15,10 @@ public class Instance: Decodable {
|
|||
public let email: String
|
||||
public let version: String
|
||||
public let urls: [String: URL]
|
||||
public let languages: [String]
|
||||
public let contactAccount: Account
|
||||
|
||||
// pleroma doesn't currently implement these
|
||||
public let languages: [String]?
|
||||
public let contactAccount: Account?
|
||||
|
||||
// MARK: Unofficial additions to the Mastodon API.
|
||||
public let stats: Stats?
|
||||
|
|
|
@ -17,6 +17,10 @@ class CharacterCounterTests: XCTestCase {
|
|||
override func tearDown() {
|
||||
}
|
||||
|
||||
func testCountEmpty() {
|
||||
XCTAssertEqual(CharacterCounter.count(text: ""), 0)
|
||||
}
|
||||
|
||||
func testCountPlainText() {
|
||||
XCTAssertEqual(CharacterCounter.count(text: "This is an example message"), 26)
|
||||
XCTAssertEqual(CharacterCounter.count(text: "This is an example message with an Emoji: 😄"), 43)
|
||||
|
|
|
@ -19,6 +19,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
if LocalData.shared.onboardingComplete {
|
||||
MastodonController.shared.createClient()
|
||||
MastodonController.shared.getOwnAccount()
|
||||
MastodonController.shared.getOwnInstance()
|
||||
} else {
|
||||
showOnboarding()
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ class MastodonController {
|
|||
var client: Client!
|
||||
|
||||
var account: Account!
|
||||
var instance: Instance!
|
||||
|
||||
private init() {
|
||||
}
|
||||
|
@ -61,4 +62,12 @@ class MastodonController {
|
|||
}
|
||||
}
|
||||
|
||||
func getOwnInstance() {
|
||||
let request = client.getInstance()
|
||||
client.run(request) { (response) in
|
||||
guard case let .success(instance, _) = response else { fatalError() }
|
||||
self.instance = instance
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14313.13.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="8eV-YC-Spl">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.15" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="8eV-YC-Spl">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.9"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.9"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
|
@ -109,8 +109,14 @@
|
|||
<action selector="visibilityPressed:" destination="svD-Ql-HGm" eventType="touchUpInside" id="aeI-Wi-7Mz"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="right" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wg4-nL-Q7B">
|
||||
<rect key="frame" x="138" y="0.0" width="205" height="30"/>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="249" verticalHuggingPriority="251" text="500" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3x1-3A-7c4">
|
||||
<rect key="frame" x="138" y="0.0" width="166" height="30"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="right" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wg4-nL-Q7B">
|
||||
<rect key="frame" x="312" y="0.0" width="31" height="30"/>
|
||||
<state key="normal" title="Post"/>
|
||||
<connections>
|
||||
<action selector="postPressed:" destination="svD-Ql-HGm" eventType="touchUpInside" id="HqO-kb-qcz"/>
|
||||
|
@ -172,6 +178,7 @@
|
|||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="charactersRemainingLabel" destination="3x1-3A-7c4" id="Bek-Rl-mMY"/>
|
||||
<outlet property="contentWarningTextField" destination="Rbm-SO-Cpp" id="d1F-yi-CnV"/>
|
||||
<outlet property="inReplyToAvatarImageView" destination="2fn-pv-lwV" id="SMy-L1-bI1"/>
|
||||
<outlet property="inReplyToContainerView" destination="l0G-YS-00D" id="XO0-HW-WVf"/>
|
||||
|
|
|
@ -37,6 +37,7 @@ class ComposeViewController: UIViewController {
|
|||
@IBOutlet weak var inReplyToContentLabel: StatusContentLabel!
|
||||
@IBOutlet weak var inReplyToLabel: UILabel!
|
||||
@IBOutlet weak var statusTextView: UITextView!
|
||||
@IBOutlet weak var charactersRemainingLabel: UILabel!
|
||||
@IBOutlet weak var visibilityButton: UIButton!
|
||||
@IBOutlet weak var postButton: UIButton!
|
||||
@IBOutlet weak var contentWarningTextField: UITextField!
|
||||
|
@ -72,6 +73,7 @@ class ComposeViewController: UIViewController {
|
|||
statusTextView.placeholder = "What is on your mind?"
|
||||
statusTextView.layer.cornerRadius = 5
|
||||
statusTextView.layer.masksToBounds = true
|
||||
statusTextView.delegate = self
|
||||
visibilityButton.setTitle(visibility.displayName, for: .normal)
|
||||
contentWarningTextField.delegate = self
|
||||
|
||||
|
@ -118,6 +120,8 @@ class ComposeViewController: UIViewController {
|
|||
statusTextView.textViewDidChange(statusTextView)
|
||||
}
|
||||
|
||||
updateCharactersRemaining()
|
||||
|
||||
progressView.progress = 0
|
||||
}
|
||||
|
||||
|
@ -134,6 +138,19 @@ class ComposeViewController: UIViewController {
|
|||
mediaStackView.addArrangedSubview(mediaView)
|
||||
}
|
||||
|
||||
func updateCharactersRemaining() {
|
||||
let count = CharacterCounter.count(text: statusTextView.text)
|
||||
let remaining = (MastodonController.shared.instance.maxStatusCharacters ?? 500) - count
|
||||
if remaining < 0 {
|
||||
charactersRemainingLabel.textColor = .red
|
||||
postButton.isEnabled = false
|
||||
} else {
|
||||
charactersRemainingLabel.textColor = .darkGray
|
||||
postButton.isEnabled = true
|
||||
}
|
||||
charactersRemainingLabel.text = remaining.description
|
||||
}
|
||||
|
||||
// MARK: - Navigation
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
|
@ -279,6 +296,12 @@ extension ComposeViewController: UITextFieldDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
extension ComposeViewController: UITextViewDelegate {
|
||||
func textViewDidChange(_ textView: UITextView) {
|
||||
updateCharactersRemaining()
|
||||
}
|
||||
}
|
||||
|
||||
extension ComposeViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
|
||||
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
|
||||
if let selectedImage = info[.originalImage] as? UIImage {
|
||||
|
|
|
@ -126,6 +126,12 @@ struct XCBActions {
|
|||
var status = ""
|
||||
if let mentioning = mentioning { status += mentioning }
|
||||
if let text = text { status += text }
|
||||
guard CharacterCounter.count(text: status) <= MastodonController.shared.instance.maxStatusCharacters ?? 500 else {
|
||||
session.complete(with: .error, additionalData: [
|
||||
"error": "Too many characters. Instance maximum is \(MastodonController.shared.instance.maxStatusCharacters ?? 500)"
|
||||
])
|
||||
return
|
||||
}
|
||||
let request = MastodonController.shared.client.createStatus(text: status, visibility: Preferences.shared.defaultPostVisibility)
|
||||
MastodonController.shared.client.run(request) { response in
|
||||
if case let .success(status, _) = response {
|
||||
|
|
Loading…
Reference in New Issue