// // OldDraft.swift // Tusker // // Created by Shadowfacts on 8/18/20. // Copyright © 2020 Shadowfacts. All rights reserved. // import Foundation import Pachyderm class OldDraft: Codable, ObservableObject { let id: UUID var lastModified: Date @Published var accountID: String @Published var text: String @Published var contentWarningEnabled: Bool @Published var contentWarning: String @Published var attachments: [CompositionAttachment] @Published var inReplyToID: String? @Published var visibility: Visibility @Published var poll: Poll? @Published var localOnly: Bool var initialText: String var hasContent: Bool { (!text.isEmpty && text != initialText) || (contentWarningEnabled && !contentWarning.isEmpty) || attachments.count > 0 || poll?.hasContent == true } init(accountID: String) { self.id = UUID() self.lastModified = Date() self.accountID = accountID self.text = "" self.contentWarningEnabled = false self.contentWarning = "" self.attachments = [] self.inReplyToID = nil self.visibility = Preferences.shared.defaultPostVisibility self.poll = nil self.localOnly = false self.initialText = "" } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decode(UUID.self, forKey: .id) self.lastModified = try container.decode(Date.self, forKey: .lastModified) self.accountID = try container.decode(String.self, forKey: .accountID) self.text = try container.decode(String.self, forKey: .text) self.contentWarningEnabled = try container.decode(Bool.self, forKey: .contentWarningEnabled) self.contentWarning = try container.decode(String.self, forKey: .contentWarning) self.attachments = try container.decode([CompositionAttachment].self, forKey: .attachments) self.inReplyToID = try container.decode(String?.self, forKey: .inReplyToID) self.visibility = try container.decode(Visibility.self, forKey: .visibility) self.poll = try container.decode(Poll?.self, forKey: .poll) self.localOnly = try container.decodeIfPresent(Bool.self, forKey: .localOnly) ?? false self.initialText = try container.decode(String.self, forKey: .initialText) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) try container.encode(lastModified, forKey: .lastModified) try container.encode(accountID, forKey: .accountID) try container.encode(text, forKey: .text) try container.encode(contentWarningEnabled, forKey: .contentWarningEnabled) try container.encode(contentWarning, forKey: .contentWarning) try container.encode(attachments, forKey: .attachments) try container.encode(inReplyToID, forKey: .inReplyToID) try container.encode(visibility, forKey: .visibility) try container.encode(poll, forKey: .poll) try container.encode(localOnly, forKey: .localOnly) try container.encode(initialText, forKey: .initialText) } } extension OldDraft: Equatable { static func ==(lhs: OldDraft, rhs: OldDraft) -> Bool { return lhs.id == rhs.id } } extension OldDraft: Identifiable {} extension OldDraft { enum CodingKeys: String, CodingKey { case id case lastModified case accountID case text case contentWarningEnabled case contentWarning case attachments case inReplyToID case visibility case poll case localOnly case initialText } } extension OldDraft { class Poll: Codable, ObservableObject { @Published var options: [Option] @Published var multiple: Bool @Published var duration: TimeInterval var hasContent: Bool { options.contains { !$0.text.isEmpty } } init() { self.options = [Option(""), Option("")] self.multiple = false self.duration = 24 * 60 * 60 // 1 day } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.options = try container.decode([Option].self, forKey: .options) self.multiple = try container.decode(Bool.self, forKey: .multiple) self.duration = try container.decode(TimeInterval.self, forKey: .duration) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(options, forKey: .options) try container.encode(multiple, forKey: .multiple) try container.encode(duration, forKey: .duration) } private enum CodingKeys: String, CodingKey { case options case multiple case duration } class Option: Identifiable, Codable, ObservableObject { let id = UUID() @Published var text: String init(_ text: String) { self.text = text } required init(from decoder: Decoder) throws { self.text = try decoder.singleValueContainer().decode(String.self) } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(text) } } } } extension MastodonController { func createOldDraft(inReplyToID: String? = nil, mentioningAcct: String? = nil) -> OldDraft { var acctsToMention = [String]() var visibility = inReplyToID != nil ? Preferences.shared.defaultReplyVisibility.resolved : Preferences.shared.defaultPostVisibility var localOnly = false var contentWarning = "" if let inReplyToID = inReplyToID, let inReplyTo = persistentContainer.status(for: inReplyToID) { acctsToMention.append(inReplyTo.account.acct) acctsToMention.append(contentsOf: inReplyTo.mentions.map(\.acct)) visibility = min(visibility, inReplyTo.visibility) localOnly = instanceFeatures.localOnlyPosts && inReplyTo.localOnly if !inReplyTo.spoilerText.isEmpty { switch Preferences.shared.contentWarningCopyMode { case .doNotCopy: break case .asIs: contentWarning = inReplyTo.spoilerText case .prependRe: if inReplyTo.spoilerText.lowercased().starts(with: "re:") { contentWarning = inReplyTo.spoilerText } else { contentWarning = "re: \(inReplyTo.spoilerText)" } } } } if let mentioningAcct = mentioningAcct { acctsToMention.append(mentioningAcct) } if let ownAccount = self.account { acctsToMention.removeAll(where: { $0 == ownAccount.acct }) } acctsToMention = acctsToMention.uniques() let draft = OldDraft(accountID: accountInfo!.id) draft.inReplyToID = inReplyToID draft.text = acctsToMention.map { "@\($0) " }.joined() draft.initialText = draft.text draft.visibility = visibility draft.localOnly = localOnly draft.contentWarning = contentWarning draft.contentWarningEnabled = !contentWarning.isEmpty OldDraftsManager.shared.add(draft) return draft } }