forked from zelo72/mastodon-ios
feat: add content warning editor for status compose scene
This commit is contained in:
parent
df66cc6b4a
commit
610ee36835
Localization
Mastodon.xcodeproj
Mastodon
Diffiable
Generated
Resources/en.lproj
Scene
Compose
CollectionViewCell
ComposeViewController.swiftComposeViewModel+PublishState.swiftComposeViewModel.swiftView
Share/View/Content
MastodonSDK/Sources/MastodonSDK/API
|
@ -216,6 +216,9 @@
|
|||
"one_day": "1 Day",
|
||||
"three_days": "3 Days",
|
||||
"seven_days": "7 Days"
|
||||
},
|
||||
"content_warning": {
|
||||
"placeholder": "Write an accurate warning here..."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -245,6 +245,7 @@
|
|||
DBA0A11325FB3FC10079C110 /* ComposeToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA0A11225FB3FC10079C110 /* ComposeToolbarView.swift */; };
|
||||
DBABE3EC25ECAC4B00879EE5 /* WelcomeIllustrationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBABE3EB25ECAC4B00879EE5 /* WelcomeIllustrationView.swift */; };
|
||||
DBBE1B4525F3474B0081417A /* MastodonPickServerAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBBE1B4425F3474B0081417A /* MastodonPickServerAppearance.swift */; };
|
||||
DBC7A672260C897100E57475 /* StatusContentWarningEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC7A671260C897100E57475 /* StatusContentWarningEditorView.swift */; };
|
||||
DBCCC71E25F73297007E1AB6 /* APIService+Reblog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBCCC71D25F73297007E1AB6 /* APIService+Reblog.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 */; };
|
||||
|
@ -553,6 +554,7 @@
|
|||
DBA0A11225FB3FC10079C110 /* ComposeToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeToolbarView.swift; sourceTree = "<group>"; };
|
||||
DBABE3EB25ECAC4B00879EE5 /* WelcomeIllustrationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeIllustrationView.swift; sourceTree = "<group>"; };
|
||||
DBBE1B4425F3474B0081417A /* MastodonPickServerAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPickServerAppearance.swift; sourceTree = "<group>"; };
|
||||
DBC7A671260C897100E57475 /* StatusContentWarningEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusContentWarningEditorView.swift; sourceTree = "<group>"; };
|
||||
DBCCC71D25F73297007E1AB6 /* APIService+Reblog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Reblog.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>"; };
|
||||
|
@ -1122,6 +1124,7 @@
|
|||
DB9A486B26032AC1008B817C /* AttachmentContainerView+EmptyStateView.swift */,
|
||||
DB44767A260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift */,
|
||||
DB221B15260C395900AEFE46 /* CustomEmojiPickerInputViewModel.swift */,
|
||||
DBC7A671260C897100E57475 /* StatusContentWarningEditorView.swift */,
|
||||
);
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1904,6 +1907,7 @@
|
|||
2DF75BA125D0E29D00694EC8 /* StatusProvider+StatusTableViewCellDelegate.swift in Sources */,
|
||||
5DF1056425F887CB00D6C0D4 /* AVPlayer.swift in Sources */,
|
||||
2DA6054725F716A2006356F9 /* PlaybackState.swift in Sources */,
|
||||
DBC7A672260C897100E57475 /* StatusContentWarningEditorView.swift in Sources */,
|
||||
DB59F10E25EF724F001F1DAB /* APIService+Poll.swift in Sources */,
|
||||
DB47229725F9EFAD00DA7F53 /* NSManagedObjectContext.swift in Sources */,
|
||||
2D42FF7E25C82218004A627A /* ActionToolBarContainer.swift in Sources */,
|
||||
|
|
|
@ -32,11 +32,16 @@ extension ComposeStatusItem {
|
|||
let username = CurrentValueSubject<String?, Never>(nil)
|
||||
let composeContent = CurrentValueSubject<String?, Never>(nil)
|
||||
|
||||
let isContentWarningComposing = CurrentValueSubject<Bool, Never>(false)
|
||||
let contentWarningContent = CurrentValueSubject<String, Never>("")
|
||||
|
||||
static func == (lhs: ComposeStatusAttribute, rhs: ComposeStatusAttribute) -> 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
|
||||
lhs.username.value == rhs.username.value &&
|
||||
lhs.composeContent.value == rhs.composeContent.value &&
|
||||
lhs.isContentWarningComposing.value == rhs.isContentWarningComposing.value &&
|
||||
lhs.contentWarningContent.value == rhs.contentWarningContent.value
|
||||
}
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
|
|
|
@ -68,6 +68,37 @@ extension ComposeStatusSection {
|
|||
attribute.composeContent.value = text
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
attribute.isContentWarningComposing
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { isContentWarningComposing in
|
||||
// self size input cell
|
||||
collectionView.collectionViewLayout.invalidateLayout()
|
||||
cell.statusContentWarningEditorView.containerView.isHidden = !isContentWarningComposing
|
||||
cell.statusContentWarningEditorView.alpha = 0
|
||||
UIView.animate(withDuration: 0.33, delay: 0, options: [.curveEaseOut]) {
|
||||
cell.statusContentWarningEditorView.alpha = 1
|
||||
} completion: { _ in
|
||||
if isContentWarningComposing {
|
||||
cell.statusContentWarningEditorView.textView.becomeFirstResponder()
|
||||
}
|
||||
// do nothing
|
||||
}
|
||||
// restore responder if needs
|
||||
if cell.statusContentWarningEditorView.textView.isFirstResponder {
|
||||
cell.textEditorView.isEditing = true
|
||||
}
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
cell.contentWarningContent
|
||||
.removeDuplicates()
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { text in
|
||||
// self size input cell
|
||||
collectionView.collectionViewLayout.invalidateLayout()
|
||||
// bind input data
|
||||
attribute.contentWarningContent.value = text
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
ComposeStatusSection.configureCustomEmojiPicker(viewModel: customEmojiPickerInputViewModel, customEmojiReplacableTextInput: cell.textEditorView, disposeBag: &cell.disposeBag)
|
||||
return cell
|
||||
case .attachment(let attachmentService):
|
||||
|
|
|
@ -162,6 +162,10 @@ internal enum L10n {
|
|||
/// video
|
||||
internal static let video = L10n.tr("Localizable", "Scene.Compose.Attachment.Video")
|
||||
}
|
||||
internal enum ContentWarning {
|
||||
/// Write an accurate warning here...
|
||||
internal static let placeholder = L10n.tr("Localizable", "Scene.Compose.ContentWarning.Placeholder")
|
||||
}
|
||||
internal enum MediaSelection {
|
||||
/// Browse
|
||||
internal static let browse = L10n.tr("Localizable", "Scene.Compose.MediaSelection.Browse")
|
||||
|
|
|
@ -47,6 +47,7 @@ uploaded to Mastodon.";
|
|||
"Scene.Compose.Attachment.Video" = "video";
|
||||
"Scene.Compose.ComposeAction" = "Publish";
|
||||
"Scene.Compose.ContentInputPlaceholder" = "Type or paste what's on your mind";
|
||||
"Scene.Compose.ContentWarning.Placeholder" = "Write an accurate warning here...";
|
||||
"Scene.Compose.MediaSelection.Browse" = "Browse";
|
||||
"Scene.Compose.MediaSelection.Camera" = "Take Photo";
|
||||
"Scene.Compose.MediaSelection.PhotoLibrary" = "Photo Library";
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// Created by MainasuK Cirno on 2021-3-11.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import UIKit
|
||||
import Combine
|
||||
import TwitterTextEditor
|
||||
|
@ -15,6 +16,8 @@ final class ComposeStatusContentCollectionViewCell: UICollectionViewCell {
|
|||
|
||||
let statusView = StatusView()
|
||||
|
||||
let statusContentWarningEditorView = StatusContentWarningEditorView()
|
||||
|
||||
let textEditorView: TextEditorView = {
|
||||
let textEditorView = TextEditorView()
|
||||
textEditorView.font = .preferredFont(forTextStyle: .body)
|
||||
|
@ -25,7 +28,9 @@ final class ComposeStatusContentCollectionViewCell: UICollectionViewCell {
|
|||
return textEditorView
|
||||
}()
|
||||
|
||||
// output
|
||||
let composeContent = PassthroughSubject<String, Never>()
|
||||
let contentWarningContent = PassthroughSubject<String, Never>()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
@ -45,10 +50,20 @@ extension ComposeStatusContentCollectionViewCell {
|
|||
// selectionStyle = .none
|
||||
preservesSuperviewLayoutMargins = true
|
||||
|
||||
statusContentWarningEditorView.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(statusContentWarningEditorView)
|
||||
NSLayoutConstraint.activate([
|
||||
statusContentWarningEditorView.topAnchor.constraint(equalTo: contentView.topAnchor),
|
||||
statusContentWarningEditorView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
|
||||
statusContentWarningEditorView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
|
||||
])
|
||||
statusContentWarningEditorView.preservesSuperviewLayoutMargins = true
|
||||
statusContentWarningEditorView.containerBackgroundView.isHidden = false
|
||||
|
||||
statusView.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(statusView)
|
||||
NSLayoutConstraint.activate([
|
||||
statusView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20),
|
||||
statusView.topAnchor.constraint(equalTo: statusContentWarningEditorView.bottomAnchor, constant: 20),
|
||||
statusView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
|
||||
statusView.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor),
|
||||
])
|
||||
|
@ -70,23 +85,39 @@ extension ComposeStatusContentCollectionViewCell {
|
|||
textEditorView.heightAnchor.constraint(greaterThanOrEqualToConstant: 44).priority(.defaultHigh),
|
||||
])
|
||||
textEditorView.setContentCompressionResistancePriority(.required - 2, for: .vertical)
|
||||
|
||||
// TODO:
|
||||
|
||||
|
||||
statusContentWarningEditorView.textView.delegate = self
|
||||
textEditorView.changeObserver = self
|
||||
}
|
||||
|
||||
override func didMoveToWindow() {
|
||||
super.didMoveToWindow()
|
||||
|
||||
statusContentWarningEditorView.containerView.isHidden = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - UITextViewDelegate
|
||||
// MARK: - TextEditorViewChangeObserver
|
||||
extension ComposeStatusContentCollectionViewCell: TextEditorViewChangeObserver {
|
||||
func textEditorView(_ textEditorView: TextEditorView, didChangeWithChangeResult changeResult: TextEditorViewChangeResult) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: text: %s", ((#file as NSString).lastPathComponent), #line, #function, textEditorView.text)
|
||||
guard changeResult.isTextChanged else { return }
|
||||
composeContent.send(textEditorView.text)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITextViewDelegate
|
||||
extension ComposeStatusContentCollectionViewCell: UITextViewDelegate {
|
||||
|
||||
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
||||
// disable input line break
|
||||
guard text != "\n" else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
func textViewDidChange(_ textView: UITextView) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: text: %s", ((#file as NSString).lastPathComponent), #line, #function, textView.text)
|
||||
guard textView === statusContentWarningEditorView.textView else { return }
|
||||
// replace line break with space
|
||||
textView.text = textView.text.replacingOccurrences(of: "\n", with: " ")
|
||||
contentWarningContent.send(textView.text)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -623,6 +623,7 @@ extension ComposeViewController: ComposeToolbarViewDelegate {
|
|||
}
|
||||
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, contentWarningButtonDidPressed sender: UIButton) {
|
||||
viewModel.isContentWarningComposing.value.toggle()
|
||||
}
|
||||
|
||||
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, visibilityButtonDidPressed sender: UIButton) {
|
||||
|
|
|
@ -61,6 +61,14 @@ extension ComposeViewModel.PublishState {
|
|||
guard viewModel.isPollComposing.value else { return nil }
|
||||
return viewModel.pollExpiresOptionAttribute.expiresOption.value.seconds
|
||||
}()
|
||||
let sensitive: Bool = viewModel.isContentWarningComposing.value
|
||||
let spoilerText: String? = {
|
||||
let text = viewModel.composeStatusAttribute.contentWarningContent.value.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !text.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
return text
|
||||
}()
|
||||
|
||||
let updateMediaQuerySubscriptions: [AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Attachment>, Error>] = {
|
||||
var subscriptions: [AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Attachment>, Error>] = []
|
||||
|
@ -92,7 +100,9 @@ extension ComposeViewModel.PublishState {
|
|||
status: viewModel.composeStatusAttribute.composeContent.value,
|
||||
mediaIDs: mediaIDs.isEmpty ? nil : mediaIDs,
|
||||
pollOptions: pollOptions,
|
||||
pollExpiresIn: pollExpiresIn
|
||||
pollExpiresIn: pollExpiresIn,
|
||||
sensitive: sensitive,
|
||||
spoilerText: spoilerText
|
||||
)
|
||||
return viewModel.context.apiService.publishStatus(
|
||||
domain: domain,
|
||||
|
|
|
@ -21,6 +21,7 @@ final class ComposeViewModel {
|
|||
let composeStatusAttribute = ComposeStatusItem.ComposeStatusAttribute()
|
||||
let isPollComposing = CurrentValueSubject<Bool, Never>(false)
|
||||
let isCustomEmojiComposing = CurrentValueSubject<Bool, Never>(false)
|
||||
let isContentWarningComposing = CurrentValueSubject<Bool, Never>(false)
|
||||
let activeAuthentication: CurrentValueSubject<MastodonAuthentication?, Never>
|
||||
let activeAuthenticationBox: CurrentValueSubject<AuthenticationService.MastodonAuthenticationBox?, Never>
|
||||
|
||||
|
@ -76,6 +77,10 @@ final class ComposeViewModel {
|
|||
.assign(to: \.value, on: customEmojiPickerInputViewModel.isCustomEmojiComposing)
|
||||
.store(in: &disposeBag)
|
||||
|
||||
isContentWarningComposing
|
||||
.assign(to: \.value, on: composeStatusAttribute.isContentWarningComposing)
|
||||
.store(in: &disposeBag)
|
||||
|
||||
// bind active authentication
|
||||
context.authenticationService.activeMastodonAuthentication
|
||||
.assign(to: \.value, on: activeAuthentication)
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
//
|
||||
// StatusContentWarningEditorView.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-3-25.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
final class StatusContentWarningEditorView: UIView {
|
||||
|
||||
let containerView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = Asset.Colors.Background.secondarySystemBackground.color
|
||||
return view
|
||||
}()
|
||||
|
||||
// due to section following readable inset. We overlap the bleeding to make backgorund fill
|
||||
// default hidden
|
||||
let containerBackgroundView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = Asset.Colors.Background.secondarySystemBackground.color
|
||||
view.isHidden = true
|
||||
return view
|
||||
}()
|
||||
|
||||
let iconImageView: UIImageView = {
|
||||
let imageView = UIImageView()
|
||||
imageView.image = UIImage(systemName: "exclamationmark.shield")!.withConfiguration(UIImage.SymbolConfiguration(pointSize: 30, weight: .regular)).withRenderingMode(.alwaysTemplate)
|
||||
imageView.tintColor = Asset.Colors.Label.primary.color
|
||||
imageView.contentMode = .center
|
||||
return imageView
|
||||
}()
|
||||
|
||||
let textView: UITextView = {
|
||||
let textView = UITextView()
|
||||
textView.font = .preferredFont(forTextStyle: .body)
|
||||
textView.isScrollEnabled = false
|
||||
textView.placeholder = L10n.Scene.Compose.ContentWarning.placeholder
|
||||
textView.backgroundColor = .clear
|
||||
return textView
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
_init()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
_init()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension StatusContentWarningEditorView {
|
||||
private func _init() {
|
||||
let contentWarningStackView = UIStackView()
|
||||
contentWarningStackView.axis = .horizontal
|
||||
contentWarningStackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(contentWarningStackView)
|
||||
NSLayoutConstraint.activate([
|
||||
contentWarningStackView.topAnchor.constraint(equalTo: topAnchor),
|
||||
contentWarningStackView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
contentWarningStackView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
contentWarningStackView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
])
|
||||
contentWarningStackView.addArrangedSubview(containerView)
|
||||
|
||||
containerBackgroundView.translatesAutoresizingMaskIntoConstraints = false
|
||||
containerView.addSubview(containerBackgroundView)
|
||||
NSLayoutConstraint.activate([
|
||||
containerBackgroundView.topAnchor.constraint(equalTo: containerView.topAnchor),
|
||||
containerBackgroundView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: -1024),
|
||||
containerBackgroundView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: 1024),
|
||||
containerBackgroundView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
|
||||
])
|
||||
|
||||
iconImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
containerView.addSubview(iconImageView)
|
||||
NSLayoutConstraint.activate([
|
||||
iconImageView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor),
|
||||
iconImageView.leadingAnchor.constraint(equalTo: containerView.readableContentGuide.leadingAnchor),
|
||||
iconImageView.widthAnchor.constraint(equalToConstant: StatusView.avatarImageSize.width).priority(.defaultHigh), // center alignment to avatar
|
||||
])
|
||||
iconImageView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||
|
||||
textView.translatesAutoresizingMaskIntoConstraints = false
|
||||
containerView.addSubview(textView)
|
||||
NSLayoutConstraint.activate([
|
||||
textView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 6),
|
||||
textView.leadingAnchor.constraint(equalTo: iconImageView.trailingAnchor, constant: StatusView.avatarToLabelSpacing - 4), // align to name label. minus magic 4pt to remove addtion inset
|
||||
textView.trailingAnchor.constraint(equalTo: containerView.readableContentGuide.trailingAnchor),
|
||||
containerView.bottomAnchor.constraint(equalTo: textView.bottomAnchor, constant: 6),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
#if canImport(SwiftUI) && DEBUG
|
||||
import SwiftUI
|
||||
|
||||
struct StatusContentWarningEditorView_Previews: PreviewProvider {
|
||||
|
||||
static var previews: some View {
|
||||
UIViewPreview(width: 375) {
|
||||
StatusContentWarningEditorView()
|
||||
}
|
||||
.previewLayout(.fixed(width: 375, height: 100))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -23,6 +23,7 @@ final class StatusView: UIView {
|
|||
|
||||
static let avatarImageSize = CGSize(width: 42, height: 42)
|
||||
static let avatarImageCornerRadius: CGFloat = 4
|
||||
static let avatarToLabelSpacing: CGFloat = 5
|
||||
static let contentWarningBlurRadius: CGFloat = 12
|
||||
|
||||
static let boostIconImage: UIImage = {
|
||||
|
@ -249,7 +250,7 @@ extension StatusView {
|
|||
let authorContainerStackView = UIStackView()
|
||||
containerStackView.addArrangedSubview(authorContainerStackView)
|
||||
authorContainerStackView.axis = .horizontal
|
||||
authorContainerStackView.spacing = 5
|
||||
authorContainerStackView.spacing = StatusView.avatarToLabelSpacing
|
||||
|
||||
// avatar
|
||||
avatarView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
|
|
@ -98,12 +98,24 @@ extension Mastodon.API.Statuses {
|
|||
public let mediaIDs: [String]?
|
||||
public let pollOptions: [String]?
|
||||
public let pollExpiresIn: Int?
|
||||
public let sensitive: Bool?
|
||||
public let spoilerText: String?
|
||||
|
||||
public init(status: String?, mediaIDs: [String]?, pollOptions: [String]?, pollExpiresIn: Int?) {
|
||||
public init(
|
||||
status: String?,
|
||||
mediaIDs: [String]?,
|
||||
pollOptions: [String]?,
|
||||
pollExpiresIn: Int?,
|
||||
sensitive: Bool?,
|
||||
spoilerText: String?
|
||||
) {
|
||||
self.status = status
|
||||
self.mediaIDs = mediaIDs
|
||||
self.pollOptions = pollOptions
|
||||
self.pollExpiresIn = pollExpiresIn
|
||||
self.sensitive = sensitive
|
||||
self.spoilerText = spoilerText
|
||||
|
||||
}
|
||||
|
||||
var contentType: String? {
|
||||
|
@ -121,6 +133,8 @@ extension Mastodon.API.Statuses {
|
|||
data.append(Data.multipart(key: "poll[options][]", value: pollOption))
|
||||
}
|
||||
pollExpiresIn.flatMap { data.append(Data.multipart(key: "poll[expires_in]", value: $0)) }
|
||||
sensitive.flatMap { data.append(Data.multipart(key: "sensitive", value: $0)) }
|
||||
spoilerText.flatMap { data.append(Data.multipart(key: "spoiler_text", value: $0)) }
|
||||
|
||||
data.append(Data.multipartEnd())
|
||||
return data
|
||||
|
|
Loading…
Reference in New Issue