feat: make text editor automatic grow height during input
This commit is contained in:
parent
2b2759c2cc
commit
d9e2453464
|
@ -178,7 +178,9 @@
|
|||
"title": {
|
||||
"new_toot": "New Toot",
|
||||
"new_reply": "New Reply"
|
||||
}
|
||||
},
|
||||
"content_input_placeholder": "Type or paste what's on your mind",
|
||||
"compose_action": "Toot"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -202,6 +202,7 @@
|
|||
DB9D6C2425E502C60051B173 /* MosaicImageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB9D6C2225E502C60051B173 /* MosaicImageViewModel.swift */; };
|
||||
DB9D6C2E25E504AC0051B173 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB9D6C2D25E504AC0051B173 /* Attachment.swift */; };
|
||||
DB9D6C3825E508BE0051B173 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB9D6C3725E508BE0051B173 /* Attachment.swift */; };
|
||||
DBA0A10925FB3C2B0079C110 /* RoundedEdgesButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA0A10825FB3C2B0079C110 /* RoundedEdgesButton.swift */; };
|
||||
DBBE1B4525F3474B0081417A /* MastodonPickServerAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBBE1B4425F3474B0081417A /* MastodonPickServerAppearance.swift */; };
|
||||
DBD9149025DF6D8D00903DFD /* APIService+Onboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBD9148F25DF6D8D00903DFD /* APIService+Onboarding.swift */; };
|
||||
DBE0821525CD382600FD6BBD /* MastodonRegisterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBE0821425CD382600FD6BBD /* MastodonRegisterViewController.swift */; };
|
||||
|
@ -464,6 +465,7 @@
|
|||
DB9D6C2225E502C60051B173 /* MosaicImageViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MosaicImageViewModel.swift; sourceTree = "<group>"; };
|
||||
DB9D6C2D25E504AC0051B173 /* Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Attachment.swift; sourceTree = "<group>"; };
|
||||
DB9D6C3725E508BE0051B173 /* Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Attachment.swift; sourceTree = "<group>"; };
|
||||
DBA0A10825FB3C2B0079C110 /* RoundedEdgesButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedEdgesButton.swift; sourceTree = "<group>"; };
|
||||
DBBE1B4425F3474B0081417A /* MastodonPickServerAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPickServerAppearance.swift; sourceTree = "<group>"; };
|
||||
DBD9148F25DF6D8D00903DFD /* APIService+Onboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Onboarding.swift"; sourceTree = "<group>"; };
|
||||
DBE0821425CD382600FD6BBD /* MastodonRegisterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonRegisterViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -649,6 +651,7 @@
|
|||
2D42FF8425C8224F004A627A /* HitTestExpandedButton.swift */,
|
||||
DB118A8B25E4BFB500FAB162 /* HighlightDimmableButton.swift */,
|
||||
0FAA101125E105390017CCDE /* PrimaryActionButton.swift */,
|
||||
DBA0A10825FB3C2B0079C110 /* RoundedEdgesButton.swift */,
|
||||
);
|
||||
path = Button;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1633,6 +1636,7 @@
|
|||
2D38F1F125CD477D00561493 /* HomeTimelineViewModel+LoadMiddleState.swift in Sources */,
|
||||
DB68A06325E905E000CFDF14 /* UIApplication.swift in Sources */,
|
||||
DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */,
|
||||
DBA0A10925FB3C2B0079C110 /* RoundedEdgesButton.swift in Sources */,
|
||||
2D152A8C25C295CC009AA50C /* StatusView.swift in Sources */,
|
||||
2D42FF8525C8224F004A627A /* HitTestExpandedButton.swift in Sources */,
|
||||
DB72601C25E36A2100235243 /* MastodonServerRulesViewController.swift in Sources */,
|
||||
|
|
|
@ -6,11 +6,34 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import CoreData
|
||||
|
||||
enum ComposeStatusItem {
|
||||
case replyTo(tootObjectID: NSManagedObjectID)
|
||||
case toot(replyToTootObjectID: NSManagedObjectID?)
|
||||
case toot(replyToTootObjectID: NSManagedObjectID?, attribute: ComposeTootAttribute)
|
||||
}
|
||||
|
||||
extension ComposeStatusItem: Hashable { }
|
||||
|
||||
extension ComposeStatusItem {
|
||||
final class ComposeTootAttribute: Equatable, Hashable {
|
||||
private let id = UUID()
|
||||
|
||||
let avatarURL = CurrentValueSubject<URL?, Never>(nil)
|
||||
let displayName = CurrentValueSubject<String?, Never>(nil)
|
||||
let username = CurrentValueSubject<String?, Never>(nil)
|
||||
let composeContent = CurrentValueSubject<String?, Never>(nil)
|
||||
|
||||
static func == (lhs: ComposeTootAttribute, rhs: ComposeTootAttribute) -> Bool {
|
||||
return lhs.avatarURL.value == rhs.avatarURL.value &&
|
||||
lhs.displayName.value == rhs.displayName.value &&
|
||||
lhs.username.value == rhs.username.value &&
|
||||
lhs.composeContent.value == rhs.composeContent.value
|
||||
}
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,82 @@
|
|||
// Created by MainasuK Cirno on 2021-3-11.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Combine
|
||||
import CoreData
|
||||
import CoreDataStack
|
||||
|
||||
enum ComposeStatusSection: Equatable, Hashable {
|
||||
case repliedTo
|
||||
case status
|
||||
}
|
||||
|
||||
extension ComposeStatusSection {
|
||||
enum ComposeKind {
|
||||
case toot
|
||||
case replyToot(tootObjectID: NSManagedObjectID)
|
||||
}
|
||||
}
|
||||
|
||||
extension ComposeStatusSection {
|
||||
static func tableViewDiffableDataSource(
|
||||
for tableView: UITableView,
|
||||
dependency: NeedsDependency,
|
||||
managedObjectContext: NSManagedObjectContext,
|
||||
composeKind: ComposeKind
|
||||
) -> UITableViewDiffableDataSource<ComposeStatusSection, ComposeStatusItem> {
|
||||
UITableViewDiffableDataSource<ComposeStatusSection, ComposeStatusItem>(tableView: tableView) { tableView, indexPath, item -> UITableViewCell? in
|
||||
switch item {
|
||||
case .replyTo(let tootObjectID):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ComposeRepliedToTootContentTableViewCell.self), for: indexPath) as! ComposeRepliedToTootContentTableViewCell
|
||||
// TODO:
|
||||
return cell
|
||||
case .toot(let replyToTootObjectID, let attribute):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ComposeTootContentTableViewCell.self), for: indexPath) as! ComposeTootContentTableViewCell
|
||||
managedObjectContext.perform {
|
||||
guard let replyToTootObjectID = replyToTootObjectID,
|
||||
let replyTo = managedObjectContext.object(with: replyToTootObjectID) as? Toot else {
|
||||
cell.statusView.headerContainerStackView.isHidden = true
|
||||
return
|
||||
}
|
||||
cell.statusView.headerContainerStackView.isHidden = false
|
||||
cell.statusView.headerInfoLabel.text = "[TODO] \(replyTo.author.displayName)"
|
||||
}
|
||||
ComposeStatusSection.configureComposeTootContent(cell: cell, attribute: attribute)
|
||||
// self size input cell
|
||||
cell.composeContent
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { text in
|
||||
tableView.beginUpdates()
|
||||
tableView.endUpdates()
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
return cell
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ComposeStatusSection {
|
||||
static func configureComposeTootContent(
|
||||
cell: ComposeTootContentTableViewCell,
|
||||
attribute: ComposeStatusItem.ComposeTootAttribute
|
||||
) {
|
||||
attribute.avatarURL
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { avatarURL in
|
||||
cell.statusView.configure(with: AvatarConfigurableViewConfiguration(avatarImageURL: avatarURL))
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
Publishers.CombineLatest(
|
||||
attribute.displayName.eraseToAnyPublisher(),
|
||||
attribute.username.eraseToAnyPublisher()
|
||||
)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { displayName, username in
|
||||
cell.statusView.nameLabel.text = displayName
|
||||
cell.statusView.usernameLabel.text = username
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ internal enum Asset {
|
|||
internal static let actionToolbar = ColorAsset(name: "Colors/Button/action.toolbar")
|
||||
internal static let disabled = ColorAsset(name: "Colors/Button/disabled")
|
||||
internal static let highlight = ColorAsset(name: "Colors/Button/highlight")
|
||||
internal static let normal = ColorAsset(name: "Colors/Button/normal")
|
||||
}
|
||||
internal enum Icon {
|
||||
internal static let photo = ColorAsset(name: "Colors/Icon/photo")
|
||||
|
|
|
@ -128,6 +128,10 @@ internal enum L10n {
|
|||
|
||||
internal enum Scene {
|
||||
internal enum Compose {
|
||||
/// Toot
|
||||
internal static let composeAction = L10n.tr("Localizable", "Scene.Compose.ComposeAction")
|
||||
/// Type or paste what's on your mind
|
||||
internal static let contentInputPlaceholder = L10n.tr("Localizable", "Scene.Compose.ContentInputPlaceholder")
|
||||
internal enum Title {
|
||||
/// New Reply
|
||||
internal static let newReply = L10n.tr("Localizable", "Scene.Compose.Title.NewReply")
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.784",
|
||||
"green" : "0.682",
|
||||
"red" : "0.608"
|
||||
"blue" : "140",
|
||||
"green" : "130",
|
||||
"red" : "110"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "217",
|
||||
"green" : "144",
|
||||
"red" : "43"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -34,6 +34,8 @@
|
|||
"Common.Controls.Timeline.LoadMore" = "Load More";
|
||||
"Common.Countable.Photo.Multiple" = "photos";
|
||||
"Common.Countable.Photo.Single" = "photo";
|
||||
"Scene.Compose.ComposeAction" = "Toot";
|
||||
"Scene.Compose.ContentInputPlaceholder" = "Type or paste what's on your mind";
|
||||
"Scene.Compose.Title.NewReply" = "New Reply";
|
||||
"Scene.Compose.Title.NewToot" = "New Toot";
|
||||
"Scene.ConfirmEmail.Button.DontReceiveEmail" = "I never got an email";
|
||||
|
|
|
@ -18,6 +18,20 @@ final class ComposeViewController: UIViewController, NeedsDependency {
|
|||
var disposeBag = Set<AnyCancellable>()
|
||||
var viewModel: ComposeViewModel!
|
||||
|
||||
let composeTootBarButtonItem: UIBarButtonItem = {
|
||||
let button = RoundedEdgesButton(type: .custom)
|
||||
button.setTitle(L10n.Scene.Compose.composeAction, for: .normal)
|
||||
button.titleLabel?.font = .systemFont(ofSize: 14, weight: .bold)
|
||||
button.setBackgroundImage(.placeholder(color: Asset.Colors.Button.normal.color), for: .normal)
|
||||
button.setBackgroundImage(.placeholder(color: Asset.Colors.Button.normal.color.withAlphaComponent(0.5)), for: .highlighted)
|
||||
button.setBackgroundImage(.placeholder(color: Asset.Colors.Button.disabled.color), for: .disabled)
|
||||
button.setTitleColor(.white, for: .normal)
|
||||
button.contentEdgeInsets = UIEdgeInsets(top: 3, left: 16, bottom: 3, right: 16)
|
||||
button.adjustsImageWhenHighlighted = false
|
||||
let barButtonItem = UIBarButtonItem(customView: button)
|
||||
return barButtonItem
|
||||
}()
|
||||
|
||||
let tableView: UITableView = {
|
||||
let tableView = ControlContainableTableView()
|
||||
tableView.register(ComposeRepliedToTootContentTableViewCell.self, forCellReuseIdentifier: String(describing: ComposeRepliedToTootContentTableViewCell.self))
|
||||
|
@ -34,7 +48,6 @@ extension ComposeViewController {
|
|||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.backgroundColor = Asset.Colors.Background.systemBackground.color
|
||||
viewModel.title
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] title in
|
||||
|
@ -42,7 +55,10 @@ extension ComposeViewController {
|
|||
self.title = title
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
view.backgroundColor = Asset.Colors.Background.systemBackground.color
|
||||
navigationItem.leftBarButtonItem = UIBarButtonItem(title: L10n.Common.Controls.Actions.cancel, style: .plain, target: self, action: #selector(ComposeViewController.cancelBarButtonItemPressed(_:)))
|
||||
navigationItem.rightBarButtonItem = composeTootBarButtonItem
|
||||
|
||||
|
||||
tableView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(tableView)
|
||||
|
@ -54,9 +70,7 @@ extension ComposeViewController {
|
|||
])
|
||||
|
||||
tableView.delegate = self
|
||||
viewModel.setupDiffableDataSource(for: tableView)
|
||||
|
||||
|
||||
viewModel.setupDiffableDataSource(for: tableView, dependency: self)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
|
@ -111,7 +125,9 @@ extension ComposeViewController: TextEditorViewTextAttributesDelegate {
|
|||
|
||||
// MARK: - UITableViewDelegate
|
||||
extension ComposeViewController: UITableViewDelegate {
|
||||
|
||||
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
return UITableView.automaticDimension
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ComposeViewController
|
||||
|
|
|
@ -9,30 +9,25 @@ import UIKit
|
|||
|
||||
extension ComposeViewModel {
|
||||
|
||||
func setupDiffableDataSource(for tableView: UITableView) {
|
||||
diffableDataSource = UITableViewDiffableDataSource(tableView: tableView) { [weak self] tableView, indexPath, item -> UITableViewCell? in
|
||||
guard let self = self else { return nil }
|
||||
|
||||
switch item {
|
||||
case .replyTo(let tootObjectID):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ComposeRepliedToTootContentTableViewCell.self), for: indexPath) as! ComposeRepliedToTootContentTableViewCell
|
||||
// TODO:
|
||||
return cell
|
||||
case .toot(let attribute):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ComposeTootContentTableViewCell.self), for: indexPath) as! ComposeTootContentTableViewCell
|
||||
// TODO:
|
||||
return cell
|
||||
}
|
||||
}
|
||||
func setupDiffableDataSource(
|
||||
for tableView: UITableView,
|
||||
dependency: NeedsDependency
|
||||
) {
|
||||
diffableDataSource = ComposeStatusSection.tableViewDiffableDataSource(
|
||||
for: tableView,
|
||||
dependency: dependency,
|
||||
managedObjectContext: context.managedObjectContext,
|
||||
composeKind: composeKind
|
||||
)
|
||||
|
||||
var snapshot = NSDiffableDataSourceSnapshot<ComposeStatusSection, ComposeStatusItem>()
|
||||
snapshot.appendSections([.repliedTo, .status])
|
||||
switch composeKind {
|
||||
case .replyToot(let tootObjectID):
|
||||
snapshot.appendItems([.replyTo(tootObjectID: tootObjectID)], toSection: .repliedTo)
|
||||
snapshot.appendItems([.toot(replyToTootObjectID: tootObjectID)], toSection: .status)
|
||||
snapshot.appendItems([.toot(replyToTootObjectID: tootObjectID, attribute: composeTootAttribute)], toSection: .status)
|
||||
case .toot:
|
||||
snapshot.appendItems([.toot(replyToTootObjectID: nil)], toSection: .status)
|
||||
snapshot.appendItems([.toot(replyToTootObjectID: nil, attribute: composeTootAttribute)], toSection: .status)
|
||||
}
|
||||
diffableDataSource.apply(snapshot, animatingDifferences: false)
|
||||
}
|
||||
|
|
|
@ -12,18 +12,25 @@ import CoreDataStack
|
|||
|
||||
final class ComposeViewModel {
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
// input
|
||||
let context: AppContext
|
||||
let composeKind: ComposeKind
|
||||
let composeKind: ComposeStatusSection.ComposeKind
|
||||
let composeTootAttribute = ComposeStatusItem.ComposeTootAttribute()
|
||||
let composeContent = CurrentValueSubject<String, Never>("")
|
||||
let activeAuthentication: CurrentValueSubject<MastodonAuthentication?, Never>
|
||||
|
||||
// output
|
||||
var diffableDataSource: UITableViewDiffableDataSource<ComposeStatusSection, ComposeStatusItem>!
|
||||
|
||||
// UI & UX
|
||||
let title: CurrentValueSubject<String, Never>
|
||||
let shouldDismiss = CurrentValueSubject<Bool, Never>(true)
|
||||
|
||||
init(
|
||||
context: AppContext,
|
||||
composeKind: ComposeKind
|
||||
composeKind: ComposeStatusSection.ComposeKind
|
||||
) {
|
||||
self.context = context
|
||||
self.composeKind = composeKind
|
||||
|
@ -31,14 +38,30 @@ final class ComposeViewModel {
|
|||
case .toot: self.title = CurrentValueSubject(L10n.Scene.Compose.Title.newToot)
|
||||
case .replyToot: self.title = CurrentValueSubject(L10n.Scene.Compose.Title.newReply)
|
||||
}
|
||||
self.activeAuthentication = CurrentValueSubject(context.authenticationService.activeMastodonAuthentication.value)
|
||||
// end init
|
||||
|
||||
// bind active authentication
|
||||
context.authenticationService.activeMastodonAuthentication
|
||||
.assign(to: \.value, on: activeAuthentication)
|
||||
.store(in: &disposeBag)
|
||||
|
||||
activeAuthentication
|
||||
.sink { [weak self] mastodonAuthentication in
|
||||
guard let self = self else { return }
|
||||
let mastodonUser = mastodonAuthentication?.user
|
||||
let username = mastodonUser?.username ?? " "
|
||||
|
||||
self.composeTootAttribute.avatarURL.value = mastodonUser?.avatarImageURL()
|
||||
self.composeTootAttribute.displayName.value = {
|
||||
guard let displayName = mastodonUser?.displayName, !displayName.isEmpty else {
|
||||
return username
|
||||
}
|
||||
return displayName
|
||||
}()
|
||||
self.composeTootAttribute.username.value = username
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ComposeViewModel {
|
||||
enum ComposeKind {
|
||||
case toot
|
||||
case replyToot(tootObjectID: NSManagedObjectID)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,19 +6,26 @@
|
|||
//
|
||||
|
||||
import UIKit
|
||||
import Combine
|
||||
import TwitterTextEditor
|
||||
|
||||
final class ComposeTootContentTableViewCell: UITableViewCell {
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
let statusView = StatusView()
|
||||
|
||||
let textEditorView: TextEditorView = {
|
||||
let textEditorView = TextEditorView()
|
||||
textEditorView.font = .preferredFont(forTextStyle: .body)
|
||||
// textEditorView.scrollView.isScrollEnabled = false
|
||||
textEditorView.scrollView.isScrollEnabled = false
|
||||
textEditorView.isScrollEnabled = false
|
||||
textEditorView.placeholderText = L10n.Scene.Compose.contentInputPlaceholder
|
||||
return textEditorView
|
||||
}()
|
||||
|
||||
let composeContent = PassthroughSubject<String, Never>()
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
_init()
|
||||
|
@ -56,19 +63,9 @@ extension ComposeTootContentTableViewCell {
|
|||
textEditorView.heightAnchor.constraint(greaterThanOrEqualToConstant: 44).priority(.defaultHigh),
|
||||
])
|
||||
|
||||
// let containerStackView = UIStackView()
|
||||
// containerStackView.axis = .vertical
|
||||
// containerStackView.spacing = 8
|
||||
// containerStackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
// contentView.addSubview(containerStackView)
|
||||
// NSLayoutConstraint.activate([
|
||||
// containerStackView.topAnchor.constraint(equalTo: statusView.bottomAnchor, constant: 10),
|
||||
// containerStackView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
|
||||
// containerStackView.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor),
|
||||
// contentView.bottomAnchor.constraint(equalTo: containerStackView.bottomAnchor, constant: 20),
|
||||
// ])
|
||||
|
||||
// TODO:
|
||||
|
||||
textEditorView.changeObserver = self
|
||||
}
|
||||
|
||||
override func didMoveToWindow() {
|
||||
|
@ -81,3 +78,11 @@ extension ComposeTootContentTableViewCell {
|
|||
extension ComposeTootContentTableViewCell {
|
||||
|
||||
}
|
||||
|
||||
// MARK: - UITextViewDelegate
|
||||
extension ComposeTootContentTableViewCell: TextEditorViewChangeObserver {
|
||||
func textEditorView(_ textEditorView: TextEditorView, didChangeWithChangeResult changeResult: TextEditorViewChangeResult) {
|
||||
guard changeResult.isTextChanged else { return }
|
||||
composeContent.send(textEditorView.text)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// RoundedEdgesButton.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-3-12.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
final class RoundedEdgesButton: UIButton {
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
layer.masksToBounds = true
|
||||
layer.cornerRadius = bounds.height * 0.5
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
//
|
||||
// Mastodon+API+Statuses.swift
|
||||
//
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-3-12.
|
||||
//
|
||||
|
||||
import Foundation
|
|
@ -96,6 +96,7 @@ extension Mastodon.API {
|
|||
public enum Onboarding { }
|
||||
public enum Polls { }
|
||||
public enum Timeline { }
|
||||
public enum Statuses { }
|
||||
public enum Favorites { }
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue