2018-08-31 02:30:19 +00:00
|
|
|
//
|
|
|
|
// ComposeViewController.swift
|
|
|
|
// Tusker
|
|
|
|
//
|
|
|
|
// Created by Shadowfacts on 8/28/18.
|
|
|
|
// Copyright © 2018 Shadowfacts. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import UIKit
|
2018-09-11 14:52:21 +00:00
|
|
|
import Pachyderm
|
2018-10-20 14:54:59 +00:00
|
|
|
import Intents
|
2018-08-31 02:30:19 +00:00
|
|
|
|
|
|
|
class ComposeViewController: UIViewController {
|
|
|
|
|
2018-10-21 02:07:04 +00:00
|
|
|
let router: AppRouter
|
|
|
|
|
2018-08-31 02:30:19 +00:00
|
|
|
@IBOutlet weak var scrollView: UIScrollView!
|
|
|
|
@IBOutlet weak var inReplyToContainerView: UIView!
|
|
|
|
@IBOutlet weak var inReplyToAvatarImageView: UIImageView!
|
|
|
|
@IBOutlet weak var inReplyToDisplayNameLabel: UILabel!
|
|
|
|
@IBOutlet weak var inReplyToUsernameLabel: UILabel!
|
|
|
|
@IBOutlet weak var inReplyToContentLabel: StatusContentLabel!
|
|
|
|
@IBOutlet weak var inReplyToLabel: UILabel!
|
|
|
|
@IBOutlet weak var statusTextView: UITextView!
|
2018-09-30 02:28:17 +00:00
|
|
|
@IBOutlet weak var placeholderLabel: UILabel!
|
2018-09-30 02:20:17 +00:00
|
|
|
@IBOutlet weak var charactersRemainingLabel: UILabel!
|
2018-08-31 02:30:19 +00:00
|
|
|
@IBOutlet weak var visibilityButton: UIButton!
|
2018-09-12 13:19:51 +00:00
|
|
|
@IBOutlet weak var postButton: UIButton!
|
2018-08-31 02:30:19 +00:00
|
|
|
@IBOutlet weak var contentWarningTextField: UITextField!
|
|
|
|
@IBOutlet weak var mediaStackView: UIStackView!
|
|
|
|
@IBOutlet weak var paddingView: UIView!
|
2018-09-12 13:19:51 +00:00
|
|
|
@IBOutlet weak var progressView: SteppedProgressView!
|
2018-08-31 02:30:19 +00:00
|
|
|
|
|
|
|
var scrolled = false
|
|
|
|
|
2018-09-18 01:57:46 +00:00
|
|
|
var inReplyToID: String?
|
2018-10-23 02:09:11 +00:00
|
|
|
|
|
|
|
// TODO: cleanup this
|
2018-09-23 16:01:05 +00:00
|
|
|
var mentioningAcct: String?
|
|
|
|
var text: String?
|
2018-10-23 02:09:11 +00:00
|
|
|
var initialText: String?
|
|
|
|
|
|
|
|
var draft: DraftsManager.Draft?
|
2018-09-23 16:01:05 +00:00
|
|
|
|
2018-09-23 22:45:56 +00:00
|
|
|
// Weak so that if a new session is initiated (i.e. XCBManager.currentSession is changed) while the current one is in progress, this one will be released
|
|
|
|
weak var xcbSession: XCBSession?
|
2018-08-31 02:30:19 +00:00
|
|
|
|
2018-09-02 22:11:00 +00:00
|
|
|
var contentWarning = false {
|
|
|
|
didSet {
|
|
|
|
contentWarningTextField.isHidden = !contentWarning
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var visibility = Preferences.shared.defaultPostVisibility {
|
|
|
|
didSet {
|
|
|
|
visibilityButton.setTitle(visibility.displayName, for: .normal)
|
|
|
|
}
|
|
|
|
}
|
2018-08-31 02:30:19 +00:00
|
|
|
|
|
|
|
var status: Status?
|
|
|
|
|
2018-10-21 02:07:04 +00:00
|
|
|
init(inReplyTo inReplyToID: String? = nil, mentioningAcct: String? = nil, text: String? = nil, router: AppRouter) {
|
2018-10-20 16:03:18 +00:00
|
|
|
self.inReplyToID = inReplyToID
|
|
|
|
self.mentioningAcct = mentioningAcct
|
|
|
|
self.text = text
|
2018-10-21 02:07:04 +00:00
|
|
|
self.router = router
|
|
|
|
|
|
|
|
super.init(nibName: "ComposeViewController", bundle: nil)
|
|
|
|
|
2018-10-20 16:03:18 +00:00
|
|
|
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancelPressed))
|
2018-10-23 02:09:11 +00:00
|
|
|
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Drafts", style: .plain, target: self, action: #selector(draftsPressed))
|
2018-10-20 16:03:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
|
|
fatalError("init(coder:) has not been implemented")
|
|
|
|
}
|
|
|
|
|
2018-08-31 02:30:19 +00:00
|
|
|
override func viewDidLoad() {
|
|
|
|
super.viewDidLoad()
|
|
|
|
|
|
|
|
statusTextView.layer.cornerRadius = 5
|
|
|
|
statusTextView.layer.masksToBounds = true
|
2018-09-30 02:20:17 +00:00
|
|
|
statusTextView.delegate = self
|
2018-08-31 02:30:19 +00:00
|
|
|
visibilityButton.setTitle(visibility.displayName, for: .normal)
|
|
|
|
contentWarningTextField.delegate = self
|
|
|
|
|
|
|
|
let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 30))
|
|
|
|
let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
|
|
|
|
let done = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(keyboardDoneButtonPressed))
|
|
|
|
toolbar.setItems([flexSpace, done], animated: false)
|
|
|
|
toolbar.sizeToFit()
|
|
|
|
statusTextView.inputAccessoryView = toolbar
|
|
|
|
|
2018-09-18 01:57:46 +00:00
|
|
|
if let inReplyToID = inReplyToID,
|
2018-09-18 16:59:07 +00:00
|
|
|
let inReplyTo = MastodonCache.status(for: inReplyToID) {
|
2018-08-31 02:30:19 +00:00
|
|
|
inReplyToDisplayNameLabel.text = inReplyTo.account.realDisplayName
|
|
|
|
inReplyToUsernameLabel.text = "@\(inReplyTo.account.username)"
|
2018-09-18 01:57:46 +00:00
|
|
|
inReplyToContentLabel.statusID = inReplyToID
|
2018-08-31 02:30:19 +00:00
|
|
|
inReplyToAvatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: inReplyToAvatarImageView)
|
|
|
|
inReplyToAvatarImageView.layer.masksToBounds = true
|
|
|
|
inReplyToAvatarImageView.image = nil
|
2018-10-02 17:42:37 +00:00
|
|
|
ImageCache.avatars.get(inReplyTo.account.avatar) { (image) in
|
2018-09-11 14:52:21 +00:00
|
|
|
DispatchQueue.main.async {
|
|
|
|
self.inReplyToAvatarImageView.image = image
|
2018-08-31 02:30:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
inReplyToLabel.text = "In reply to \(inReplyTo.account.realDisplayName)"
|
2018-10-02 23:31:00 +00:00
|
|
|
if inReplyTo.account != MastodonController.account {
|
2018-08-31 02:30:19 +00:00
|
|
|
statusTextView.text = "@\(inReplyTo.account.acct) "
|
|
|
|
}
|
2018-10-02 23:31:00 +00:00
|
|
|
statusTextView.text += inReplyTo.mentions.filter({ $0.id != MastodonController.account.id }).map({ "@\($0.acct) " }).joined()
|
2018-09-11 14:52:21 +00:00
|
|
|
contentWarning = inReplyTo.sensitive
|
2018-09-02 22:11:00 +00:00
|
|
|
contentWarningTextField.text = inReplyTo.spoilerText
|
|
|
|
visibility = inReplyTo.visibility
|
2018-08-31 02:30:19 +00:00
|
|
|
} else {
|
|
|
|
inReplyToLabel.isHidden = true
|
|
|
|
inReplyToContainerView.isHidden = true
|
|
|
|
}
|
|
|
|
|
2018-09-23 16:01:05 +00:00
|
|
|
if let mentioningAcct = mentioningAcct {
|
|
|
|
statusTextView.text += "@\(mentioningAcct) "
|
|
|
|
}
|
|
|
|
if let text = text {
|
|
|
|
statusTextView.text += text
|
2018-08-31 02:30:19 +00:00
|
|
|
}
|
2018-09-12 13:19:51 +00:00
|
|
|
|
2018-10-23 02:09:11 +00:00
|
|
|
initialText = statusTextView.text.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
|
|
2018-09-30 02:20:17 +00:00
|
|
|
updateCharactersRemaining()
|
2018-09-30 02:28:17 +00:00
|
|
|
updatePlaceholder()
|
2018-09-30 02:20:17 +00:00
|
|
|
|
2018-09-12 13:19:51 +00:00
|
|
|
progressView.progress = 0
|
2018-10-20 14:54:59 +00:00
|
|
|
|
|
|
|
if let mentioningAcct = mentioningAcct {
|
|
|
|
let req = MastodonController.client.searchForAccount(query: mentioningAcct)
|
|
|
|
MastodonController.client.run(req) { [weak self] (response) in
|
|
|
|
if case let .success(accounts, _) = response {
|
|
|
|
self?.userActivity = UserActivityManager.newPostActivity(mentioning: accounts.first)
|
|
|
|
} else {
|
|
|
|
self?.userActivity = UserActivityManager.newPostActivity()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self.userActivity = UserActivityManager.newPostActivity()
|
|
|
|
}
|
|
|
|
|
2018-08-31 02:30:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
override func viewDidLayoutSubviews() {
|
2018-09-18 01:57:46 +00:00
|
|
|
if inReplyToID != nil && !scrolled {
|
2018-08-31 02:30:19 +00:00
|
|
|
scrollView.contentOffset = CGPoint(x: 0, y: inReplyToContainerView.bounds.height - 44)
|
|
|
|
scrolled = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func addMedia(for image: UIImage) {
|
|
|
|
let mediaView = ComposeMediaView(image: image)
|
2018-08-31 16:39:39 +00:00
|
|
|
mediaView.delegate = self
|
2018-08-31 02:30:19 +00:00
|
|
|
mediaStackView.addArrangedSubview(mediaView)
|
|
|
|
}
|
|
|
|
|
2018-09-30 02:20:17 +00:00
|
|
|
func updateCharactersRemaining() {
|
|
|
|
let count = CharacterCounter.count(text: statusTextView.text)
|
2018-10-02 23:31:00 +00:00
|
|
|
let remaining = (MastodonController.instance.maxStatusCharacters ?? 500) - count
|
2018-09-30 02:20:17 +00:00
|
|
|
if remaining < 0 {
|
|
|
|
charactersRemainingLabel.textColor = .red
|
|
|
|
postButton.isEnabled = false
|
|
|
|
} else {
|
|
|
|
charactersRemainingLabel.textColor = .darkGray
|
|
|
|
postButton.isEnabled = true
|
|
|
|
}
|
|
|
|
charactersRemainingLabel.text = remaining.description
|
|
|
|
}
|
|
|
|
|
2018-09-30 02:28:17 +00:00
|
|
|
func updatePlaceholder() {
|
|
|
|
placeholderLabel.isHidden = !statusTextView.text.isEmpty
|
|
|
|
}
|
|
|
|
|
2018-08-31 02:30:19 +00:00
|
|
|
// MARK: - Navigation
|
2018-10-20 16:03:18 +00:00
|
|
|
|
|
|
|
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
2018-08-31 02:30:19 +00:00
|
|
|
statusTextView.resignFirstResponder()
|
|
|
|
contentWarningTextField.resignFirstResponder()
|
2018-10-20 16:03:18 +00:00
|
|
|
super.dismiss(animated: flag, completion: completion)
|
2018-08-31 02:30:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Interaction
|
|
|
|
|
|
|
|
@IBAction func visibilityPressed(_ sender: Any) {
|
2018-10-26 01:54:07 +00:00
|
|
|
let alertController = UIAlertController(currentVisibility: self.visibility) { (visibility) in
|
|
|
|
guard let visibility = visibility else { return }
|
|
|
|
UIView.performWithoutAnimation {
|
|
|
|
self.visibility = visibility
|
|
|
|
self.visibilityButton.layoutIfNeeded()
|
2018-08-31 02:30:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
present(alertController, animated: true)
|
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func contentWarningPressed(_ sender: Any) {
|
|
|
|
contentWarning = !contentWarning
|
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func mediaPressed(_ sender: Any) {
|
|
|
|
let imagePicker = UIImagePickerController()
|
|
|
|
imagePicker.delegate = self
|
|
|
|
|
|
|
|
let alertController = UIAlertController(title: "Choose Image Source", message: nil, preferredStyle: .actionSheet)
|
|
|
|
if UIImagePickerController.isSourceTypeAvailable(.camera) {
|
|
|
|
alertController.addAction(UIAlertAction(title: "Camera", style: .default, handler: { _ in
|
|
|
|
imagePicker.sourceType = .camera
|
|
|
|
self.present(imagePicker, animated: true)
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
|
|
|
|
alertController.addAction(UIAlertAction(title: "Photo Library", style: .default, handler: { __ in
|
|
|
|
imagePicker.sourceType = .photoLibrary
|
|
|
|
self.present(imagePicker, animated: true)
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
|
|
|
|
present(alertController, animated: true)
|
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func postPressed(_ sender: Any) {
|
|
|
|
guard let text = statusTextView.text,
|
|
|
|
!text.isEmpty else { return }
|
|
|
|
|
2018-09-12 13:19:51 +00:00
|
|
|
postButton.isEnabled = false
|
|
|
|
|
2018-08-31 02:30:19 +00:00
|
|
|
let contentWarning: String?
|
|
|
|
if self.contentWarning,
|
|
|
|
let text = contentWarningTextField.text,
|
|
|
|
!text.isEmpty {
|
|
|
|
contentWarning = text
|
|
|
|
} else {
|
|
|
|
contentWarning = nil
|
|
|
|
}
|
|
|
|
let sensitive = contentWarning != nil
|
|
|
|
|
|
|
|
let visibility = self.visibility
|
|
|
|
|
|
|
|
var attachments: [Attachment?] = []
|
|
|
|
let group = DispatchGroup()
|
|
|
|
for view in mediaStackView.arrangedSubviews {
|
2018-08-31 16:39:39 +00:00
|
|
|
guard let mediaView = view as? ComposeMediaView,
|
|
|
|
let image = mediaView.image,
|
2018-08-31 02:30:19 +00:00
|
|
|
let data = image.pngData() else { continue }
|
|
|
|
let index = attachments.count
|
|
|
|
attachments.append(nil)
|
2018-09-12 13:19:51 +00:00
|
|
|
progressView.steps += 1
|
2018-08-31 02:30:19 +00:00
|
|
|
group.enter()
|
2018-10-02 23:31:00 +00:00
|
|
|
let request = MastodonController.client.upload(attachment: FormAttachment(pngData: data), description: mediaView.mediaDescription)
|
|
|
|
MastodonController.client.run(request) { response in
|
2018-09-11 14:52:21 +00:00
|
|
|
guard case let .success(attachment, _) = response else { fatalError() }
|
2018-08-31 02:30:19 +00:00
|
|
|
attachments[index] = attachment
|
2018-09-12 13:19:51 +00:00
|
|
|
self.progressView.step()
|
2018-08-31 02:30:19 +00:00
|
|
|
group.leave()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-12 13:19:51 +00:00
|
|
|
progressView.steps = 2 + attachments.count
|
|
|
|
progressView.currentStep = 1
|
|
|
|
|
2018-08-31 02:30:19 +00:00
|
|
|
group.notify(queue: .main) {
|
2018-09-11 14:52:21 +00:00
|
|
|
let attachments = attachments.compactMap { $0 }
|
2018-08-31 02:30:19 +00:00
|
|
|
|
2018-10-02 23:31:00 +00:00
|
|
|
let request = MastodonController.client.createStatus(text: text,
|
2018-09-18 01:57:46 +00:00
|
|
|
inReplyTo: self.inReplyToID,
|
2018-09-18 00:58:05 +00:00
|
|
|
media: attachments,
|
|
|
|
sensitive: sensitive,
|
|
|
|
spoilerText: contentWarning,
|
2018-09-24 01:10:45 +00:00
|
|
|
visibility: visibility)
|
2018-10-02 23:31:00 +00:00
|
|
|
MastodonController.client.run(request) { response in
|
2018-09-11 14:52:21 +00:00
|
|
|
guard case let .success(status, _) = response else { fatalError() }
|
2018-08-31 02:30:19 +00:00
|
|
|
self.status = status
|
2018-09-18 16:59:07 +00:00
|
|
|
MastodonCache.add(status: status)
|
2018-10-23 02:09:11 +00:00
|
|
|
|
|
|
|
if let draft = self.draft {
|
|
|
|
DraftsManager.shared.remove(draft)
|
|
|
|
}
|
|
|
|
|
2018-08-31 02:30:19 +00:00
|
|
|
DispatchQueue.main.async {
|
2018-09-12 13:19:51 +00:00
|
|
|
self.progressView.step()
|
2018-10-20 16:03:18 +00:00
|
|
|
self.dismiss(animated: true)
|
|
|
|
// TODO: reorganize routing/navigation
|
|
|
|
(((self.presentingViewController as! MainTabBarViewController).selectedViewController as! UINavigationController).topViewController as! TuskerNavigationDelegate).selected(status: status.id)
|
2018-09-23 16:01:05 +00:00
|
|
|
|
2018-09-23 22:43:33 +00:00
|
|
|
self.xcbSession?.complete(with: .success, additionalData: [
|
2018-09-23 23:04:39 +00:00
|
|
|
"statusURL": status.url?.absoluteString,
|
2018-09-23 22:43:33 +00:00
|
|
|
"statusURI": status.uri
|
|
|
|
])
|
2018-08-31 02:30:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc func imagePressed(_ gesture: UITapGestureRecognizer) {
|
|
|
|
gesture.view!.superview!.removeFromSuperview()
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc func keyboardDoneButtonPressed() {
|
|
|
|
statusTextView.endEditing(false)
|
|
|
|
}
|
|
|
|
|
2018-10-23 02:09:11 +00:00
|
|
|
func saveDraft() {
|
|
|
|
if let draft = draft {
|
|
|
|
draft.update(text: statusTextView.text)
|
|
|
|
} else {
|
|
|
|
DraftsManager.shared.create(text: statusTextView.text)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func close() {
|
2018-10-20 16:03:18 +00:00
|
|
|
dismiss(animated: true)
|
|
|
|
xcbSession?.complete(with: .cancel)
|
|
|
|
}
|
|
|
|
|
2018-10-23 02:09:11 +00:00
|
|
|
@objc func cancelPressed() {
|
|
|
|
guard statusTextView.text.trimmingCharacters(in: .whitespacesAndNewlines) != initialText else {
|
|
|
|
close()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if Preferences.shared.automaticallySaveDrafts {
|
|
|
|
saveDraft()
|
|
|
|
close()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
|
|
|
alert.addAction(UIAlertAction(title: "Save draft", style: .default, handler: { (_) in
|
|
|
|
self.saveDraft()
|
|
|
|
self.close()
|
|
|
|
}))
|
|
|
|
alert.addAction(UIAlertAction(title: "Delete draft", style: .destructive, handler: { (_) in
|
|
|
|
if let draft = self.draft {
|
|
|
|
DraftsManager.shared.remove(draft)
|
|
|
|
}
|
|
|
|
self.close()
|
|
|
|
}))
|
|
|
|
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (_) in
|
|
|
|
}))
|
|
|
|
router.present(alert, animated: true)
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc func draftsPressed() {
|
|
|
|
let drafts = router.drafts()
|
|
|
|
drafts.delegate = self
|
|
|
|
router.present(UINavigationController(rootViewController: drafts), animated: true)
|
|
|
|
}
|
|
|
|
|
2018-08-31 02:30:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
extension ComposeViewController: UITextFieldDelegate {
|
|
|
|
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
|
|
|
textField.endEditing(false)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-30 02:20:17 +00:00
|
|
|
extension ComposeViewController: UITextViewDelegate {
|
|
|
|
func textViewDidChange(_ textView: UITextView) {
|
|
|
|
updateCharactersRemaining()
|
2018-09-30 02:28:17 +00:00
|
|
|
updatePlaceholder()
|
2018-09-30 02:20:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-31 02:30:19 +00:00
|
|
|
extension ComposeViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
|
|
|
|
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
|
|
|
|
if let selectedImage = info[.originalImage] as? UIImage {
|
|
|
|
addMedia(for: selectedImage)
|
|
|
|
dismiss(animated: true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-08-31 16:39:39 +00:00
|
|
|
|
|
|
|
extension ComposeViewController: ComposeMediaViewDelegate {
|
|
|
|
func editDescription(for media: ComposeMediaView) {
|
|
|
|
let alertController = UIAlertController(title: "Media Description", message: nil, preferredStyle: .alert)
|
|
|
|
alertController.addTextField { textField in
|
|
|
|
textField.text = media.mediaDescription
|
|
|
|
}
|
|
|
|
alertController.addAction(UIAlertAction(title: "Confirm", style: .default, handler: { _ in
|
|
|
|
let description = alertController.textFields![0].text
|
|
|
|
media.mediaDescription = description
|
|
|
|
}))
|
|
|
|
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
|
|
|
|
present(alertController, animated: true)
|
|
|
|
}
|
|
|
|
}
|
2018-10-23 02:09:11 +00:00
|
|
|
|
|
|
|
extension ComposeViewController: DraftsTableViewControllerDelegate {
|
|
|
|
func draftSelectionCanceled() {
|
|
|
|
}
|
|
|
|
|
|
|
|
func draftSelected(_ draft: DraftsManager.Draft) {
|
|
|
|
self.draft = draft
|
|
|
|
statusTextView.text = draft.text
|
|
|
|
updatePlaceholder()
|
|
|
|
}
|
|
|
|
}
|