2022-12-25 05:55:33 +00:00
|
|
|
import SwiftUI
|
|
|
|
import DesignSystem
|
2023-01-13 06:30:15 +00:00
|
|
|
import Env
|
2022-12-25 07:17:16 +00:00
|
|
|
import Models
|
|
|
|
import Network
|
2022-12-25 18:15:35 +00:00
|
|
|
import PhotosUI
|
2022-12-25 05:55:33 +00:00
|
|
|
|
|
|
|
@MainActor
|
2022-12-26 07:24:55 +00:00
|
|
|
public class StatusEditorViewModel: ObservableObject {
|
2022-12-27 12:38:10 +00:00
|
|
|
struct ImageContainer: Identifiable {
|
|
|
|
let id = UUID().uuidString
|
2022-12-27 15:16:25 +00:00
|
|
|
let image: UIImage?
|
|
|
|
let mediaAttachement: MediaAttachement?
|
|
|
|
let error: Error?
|
2022-12-26 07:24:55 +00:00
|
|
|
}
|
|
|
|
|
2022-12-27 12:38:10 +00:00
|
|
|
var mode: Mode
|
|
|
|
let generator = UINotificationFeedbackGenerator()
|
|
|
|
|
|
|
|
var client: Client?
|
2022-12-30 21:49:09 +00:00
|
|
|
var currentAccount: Account?
|
2022-12-31 11:11:42 +00:00
|
|
|
var theme: Theme?
|
2022-12-26 07:24:55 +00:00
|
|
|
|
2022-12-27 12:38:10 +00:00
|
|
|
@Published var statusText = NSMutableAttributedString(string: "") {
|
2022-12-25 05:55:33 +00:00
|
|
|
didSet {
|
2023-01-13 11:23:48 +00:00
|
|
|
processText()
|
2022-12-27 12:38:10 +00:00
|
|
|
checkEmbed()
|
2022-12-25 05:55:33 +00:00
|
|
|
}
|
|
|
|
}
|
2023-01-13 17:43:02 +00:00
|
|
|
@Published var backupStatustext: NSAttributedString?
|
2023-01-13 06:30:15 +00:00
|
|
|
|
|
|
|
@Published var showPoll: Bool = false
|
|
|
|
@Published var pollVotingFrequency = PollVotingFrequency.oneVote
|
|
|
|
@Published var pollDuration = PollDuration.oneDay
|
|
|
|
@Published var pollOptions: [String] = ["", ""]
|
|
|
|
|
2022-12-28 09:45:05 +00:00
|
|
|
@Published var spoilerOn: Bool = false
|
|
|
|
@Published var spoilerText: String = ""
|
|
|
|
|
2022-12-27 18:10:31 +00:00
|
|
|
@Published var selectedRange: NSRange = .init(location: 0, length: 0)
|
|
|
|
|
2022-12-25 16:46:51 +00:00
|
|
|
@Published var isPosting: Bool = false
|
2022-12-25 18:15:35 +00:00
|
|
|
@Published var selectedMedias: [PhotosPickerItem] = [] {
|
|
|
|
didSet {
|
2022-12-27 18:10:31 +00:00
|
|
|
if selectedMedias.count > 4 {
|
|
|
|
selectedMedias = selectedMedias.prefix(4).map{ $0 }
|
|
|
|
}
|
2022-12-25 18:15:35 +00:00
|
|
|
inflateSelectedMedias()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@Published var mediasImages: [ImageContainer] = []
|
2022-12-30 21:49:09 +00:00
|
|
|
@Published var replyToStatus: Status?
|
2022-12-27 12:38:10 +00:00
|
|
|
@Published var embededStatus: Status?
|
2023-01-10 13:44:11 +00:00
|
|
|
var canPost: Bool {
|
|
|
|
statusText.length > 0 || !selectedMedias.isEmpty
|
|
|
|
}
|
2023-01-13 06:30:15 +00:00
|
|
|
|
|
|
|
var shouldDisablePollButton: Bool {
|
|
|
|
showPoll || !selectedMedias.isEmpty
|
|
|
|
}
|
2022-12-27 15:16:25 +00:00
|
|
|
|
2022-12-27 18:10:31 +00:00
|
|
|
@Published var visibility: Models.Visibility = .pub
|
|
|
|
|
2022-12-31 08:10:27 +00:00
|
|
|
@Published var mentionsSuggestions: [Account] = []
|
|
|
|
@Published var tagsSuggestions: [Tag] = []
|
|
|
|
private var currentSuggestionRange: NSRange?
|
|
|
|
|
2022-12-30 11:00:09 +00:00
|
|
|
private var embededStatusURL: URL? {
|
|
|
|
return embededStatus?.reblog?.url ?? embededStatus?.url
|
|
|
|
}
|
|
|
|
|
2022-12-27 15:16:25 +00:00
|
|
|
private var uploadTask: Task<Void, Never>?
|
2022-12-27 12:38:10 +00:00
|
|
|
|
2022-12-26 07:24:55 +00:00
|
|
|
init(mode: Mode) {
|
|
|
|
self.mode = mode
|
2022-12-25 07:17:16 +00:00
|
|
|
}
|
|
|
|
|
2022-12-27 18:10:31 +00:00
|
|
|
func insertStatusText(text: String) {
|
|
|
|
let string = statusText
|
|
|
|
string.mutableString.insert(text, at: selectedRange.location)
|
|
|
|
statusText = string
|
|
|
|
selectedRange = NSRange(location: selectedRange.location + text.utf16.count, length: 0)
|
|
|
|
}
|
|
|
|
|
2022-12-31 08:10:27 +00:00
|
|
|
func replaceTextWith(text: String, inRange: NSRange) {
|
|
|
|
let string = statusText
|
|
|
|
string.mutableString.deleteCharacters(in: inRange)
|
|
|
|
string.mutableString.insert(text, at: inRange.location)
|
|
|
|
statusText = string
|
|
|
|
selectedRange = NSRange(location: inRange.location + text.utf16.count, length: 0)
|
|
|
|
}
|
2023-01-13 17:43:02 +00:00
|
|
|
|
|
|
|
func replaceTextWith(text: String) {
|
|
|
|
statusText = .init(string: text)
|
|
|
|
selectedRange = .init(location: text.utf16.count, length: 0)
|
|
|
|
}
|
2023-01-13 06:30:15 +00:00
|
|
|
|
2023-01-15 13:51:09 +00:00
|
|
|
private func getPollOptionsForAPI() -> [String]? {
|
|
|
|
let options = pollOptions.filter { !$0.trimmingCharacters(in: .whitespaces).isEmpty }
|
|
|
|
return options.isEmpty ? nil : options
|
2023-01-13 06:30:15 +00:00
|
|
|
}
|
2022-12-31 08:10:27 +00:00
|
|
|
|
2022-12-25 07:17:16 +00:00
|
|
|
func postStatus() async -> Status? {
|
|
|
|
guard let client else { return nil }
|
|
|
|
do {
|
2022-12-25 16:46:51 +00:00
|
|
|
isPosting = true
|
2022-12-26 07:24:55 +00:00
|
|
|
let postStatus: Status?
|
2023-01-15 13:51:09 +00:00
|
|
|
var pollData: StatusData.PollData?
|
|
|
|
if let pollOptions = getPollOptionsForAPI() {
|
|
|
|
pollData = .init(options: pollOptions,
|
|
|
|
multiple: pollVotingFrequency.canVoteMultipleTimes,
|
|
|
|
expires_in: pollDuration.rawValue)
|
|
|
|
}
|
|
|
|
let data = StatusData(status: statusText.string,
|
|
|
|
visibility: visibility,
|
|
|
|
inReplyToId: mode.replyToStatus?.id,
|
|
|
|
spoilerText: spoilerOn ? spoilerText : nil,
|
|
|
|
mediaIds: mediasImages.compactMap{ $0.mediaAttachement?.id },
|
|
|
|
poll: pollData)
|
2022-12-26 07:24:55 +00:00
|
|
|
switch mode {
|
2023-01-04 17:37:58 +00:00
|
|
|
case .new, .replyTo, .quote, .mention:
|
2023-01-15 13:51:09 +00:00
|
|
|
postStatus = try await client.post(endpoint: Statuses.postStatus(json: data))
|
2022-12-26 07:24:55 +00:00
|
|
|
case let .edit(status):
|
2023-01-15 13:51:09 +00:00
|
|
|
postStatus = try await client.put(endpoint: Statuses.editStatus(id: status.id, json: data))
|
2022-12-26 07:24:55 +00:00
|
|
|
}
|
2022-12-25 16:46:51 +00:00
|
|
|
generator.notificationOccurred(.success)
|
|
|
|
isPosting = false
|
2022-12-26 07:24:55 +00:00
|
|
|
return postStatus
|
2022-12-25 07:17:16 +00:00
|
|
|
} catch {
|
2022-12-25 16:46:51 +00:00
|
|
|
isPosting = false
|
|
|
|
generator.notificationOccurred(.error)
|
2022-12-25 07:17:16 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-26 07:24:55 +00:00
|
|
|
func prepareStatusText() {
|
|
|
|
switch mode {
|
2023-01-05 11:21:54 +00:00
|
|
|
case let .new(visibility):
|
|
|
|
self.visibility = visibility
|
2022-12-26 07:24:55 +00:00
|
|
|
case let .replyTo(status):
|
2023-01-05 11:21:54 +00:00
|
|
|
var mentionString = ""
|
|
|
|
if (status.reblog?.account.acct ?? status.account.acct) != currentAccount?.acct {
|
|
|
|
mentionString = "@\(status.reblog?.account.acct ?? status.account.acct)"
|
|
|
|
}
|
2022-12-30 21:49:09 +00:00
|
|
|
for mention in status.mentions where mention.acct != currentAccount?.acct {
|
2023-01-13 11:23:48 +00:00
|
|
|
if !mentionString.isEmpty {
|
|
|
|
mentionString += " "
|
|
|
|
}
|
|
|
|
mentionString += "@\(mention.acct)"
|
2022-12-30 21:49:09 +00:00
|
|
|
}
|
|
|
|
mentionString += " "
|
|
|
|
replyToStatus = status
|
2023-01-03 20:14:42 +00:00
|
|
|
visibility = status.visibility
|
2022-12-30 21:49:09 +00:00
|
|
|
statusText = .init(string: mentionString)
|
|
|
|
selectedRange = .init(location: mentionString.utf16.count, length: 0)
|
2023-01-04 17:37:58 +00:00
|
|
|
case let .mention(account, visibility):
|
|
|
|
statusText = .init(string: "@\(account.acct) ")
|
|
|
|
self.visibility = visibility
|
|
|
|
selectedRange = .init(location: statusText.string.utf16.count, length: 0)
|
2022-12-26 07:24:55 +00:00
|
|
|
case let .edit(status):
|
2023-01-13 13:11:21 +00:00
|
|
|
statusText = .init(status.content.asMarkdown.asSafeAttributedString)
|
2022-12-28 09:45:05 +00:00
|
|
|
selectedRange = .init(location: statusText.string.utf16.count, length: 0)
|
|
|
|
spoilerOn = !status.spoilerText.isEmpty
|
|
|
|
spoilerText = status.spoilerText
|
2023-01-03 20:14:42 +00:00
|
|
|
visibility = status.visibility
|
2022-12-28 09:45:05 +00:00
|
|
|
mediasImages = status.mediaAttachments.map{ .init(image: nil, mediaAttachement: $0, error: nil )}
|
2022-12-27 06:51:44 +00:00
|
|
|
case let .quote(status):
|
2022-12-27 12:38:10 +00:00
|
|
|
self.embededStatus = status
|
2022-12-30 11:00:09 +00:00
|
|
|
if let url = embededStatusURL {
|
2022-12-27 12:38:10 +00:00
|
|
|
statusText = .init(string: "\n\nFrom: @\(status.reblog?.account.acct ?? status.account.acct)\n\(url)")
|
2022-12-27 18:10:31 +00:00
|
|
|
selectedRange = .init(location: 0, length: 0)
|
2022-12-27 06:51:44 +00:00
|
|
|
}
|
2022-12-25 07:17:16 +00:00
|
|
|
}
|
|
|
|
}
|
2022-12-25 05:55:33 +00:00
|
|
|
|
2023-01-13 11:23:48 +00:00
|
|
|
private func processText() {
|
2022-12-27 12:38:10 +00:00
|
|
|
statusText.addAttributes([.foregroundColor: UIColor(Color.label)],
|
|
|
|
range: NSMakeRange(0, statusText.string.utf16.count))
|
2022-12-25 05:55:33 +00:00
|
|
|
let hashtagPattern = "(#+[a-zA-Z0-9(_)]{1,})"
|
2023-01-08 18:45:11 +00:00
|
|
|
let mentionPattern = "(@+[a-zA-Z0-9(_).-]{1,})"
|
2022-12-27 06:51:44 +00:00
|
|
|
let urlPattern = "(?i)https?://(?:www\\.)?\\S+(?:/|\\b)"
|
2022-12-25 05:55:33 +00:00
|
|
|
|
2022-12-25 11:46:42 +00:00
|
|
|
do {
|
|
|
|
let hashtagRegex = try NSRegularExpression(pattern: hashtagPattern, options: [])
|
|
|
|
let mentionRegex = try NSRegularExpression(pattern: mentionPattern, options: [])
|
2022-12-27 06:51:44 +00:00
|
|
|
let urlRegex = try NSRegularExpression(pattern: urlPattern, options: [])
|
2022-12-25 11:46:42 +00:00
|
|
|
|
2023-01-13 11:23:48 +00:00
|
|
|
let range = NSMakeRange(0, statusText.string.utf16.count)
|
2022-12-27 12:38:10 +00:00
|
|
|
var ranges = hashtagRegex.matches(in: statusText.string,
|
2022-12-25 11:46:42 +00:00
|
|
|
options: [],
|
2023-01-13 11:23:48 +00:00
|
|
|
range: range).map { $0.range }
|
2022-12-27 12:38:10 +00:00
|
|
|
ranges.append(contentsOf: mentionRegex.matches(in: statusText.string,
|
2022-12-25 11:46:42 +00:00
|
|
|
options: [],
|
2023-01-13 11:23:48 +00:00
|
|
|
range: range).map {$0.range})
|
2022-12-27 06:51:44 +00:00
|
|
|
|
2022-12-27 12:38:10 +00:00
|
|
|
let urlRanges = urlRegex.matches(in: statusText.string,
|
2022-12-27 06:51:44 +00:00
|
|
|
options: [],
|
2023-01-13 11:23:48 +00:00
|
|
|
range:range).map { $0.range }
|
2022-12-25 05:55:33 +00:00
|
|
|
|
2022-12-31 08:10:27 +00:00
|
|
|
var foundSuggestionRange: Bool = false
|
|
|
|
for nsRange in ranges {
|
2022-12-31 11:11:42 +00:00
|
|
|
statusText.addAttributes([.foregroundColor: UIColor(theme?.tintColor ?? .brand)],
|
2022-12-31 08:10:27 +00:00
|
|
|
range: nsRange)
|
|
|
|
if selectedRange.location == (nsRange.location + nsRange.length),
|
|
|
|
let range = Range(nsRange, in: statusText.string) {
|
|
|
|
foundSuggestionRange = true
|
|
|
|
currentSuggestionRange = nsRange
|
|
|
|
loadAutoCompleteResults(query: String(statusText.string[range]))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !foundSuggestionRange || ranges.isEmpty{
|
|
|
|
resetAutoCompletion()
|
2022-12-25 11:46:42 +00:00
|
|
|
}
|
2022-12-27 06:51:44 +00:00
|
|
|
|
|
|
|
for range in urlRanges {
|
2022-12-31 11:11:42 +00:00
|
|
|
statusText.addAttributes([.foregroundColor: UIColor(theme?.tintColor ?? .brand),
|
2022-12-27 06:51:44 +00:00
|
|
|
.underlineStyle: NSUnderlineStyle.single,
|
2022-12-31 11:11:42 +00:00
|
|
|
.underlineColor: UIColor(theme?.tintColor ?? .brand)],
|
2022-12-27 06:51:44 +00:00
|
|
|
range: NSRange(location: range.location, length: range.length))
|
|
|
|
}
|
2023-01-13 11:23:48 +00:00
|
|
|
|
|
|
|
var attachementsToRemove: [NSRange] = []
|
|
|
|
statusText.enumerateAttribute(.attachment, in: range) { attachement, raneg, _ in
|
|
|
|
if let attachement = attachement as? NSTextAttachment, let image = attachement.image {
|
|
|
|
attachementsToRemove.append(range)
|
|
|
|
mediasImages.append(.init(image: image, mediaAttachement: nil, error: nil))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !attachementsToRemove.isEmpty {
|
|
|
|
processMediasToUpload()
|
|
|
|
for range in attachementsToRemove {
|
|
|
|
statusText.removeAttribute(.attachment, range: range)
|
|
|
|
}
|
|
|
|
}
|
2022-12-25 11:46:42 +00:00
|
|
|
} catch {
|
|
|
|
|
2022-12-25 05:55:33 +00:00
|
|
|
}
|
|
|
|
}
|
2023-01-13 06:30:15 +00:00
|
|
|
|
|
|
|
func resetPollDefaults() {
|
|
|
|
pollOptions = ["", ""]
|
|
|
|
pollDuration = .oneDay
|
|
|
|
pollVotingFrequency = .oneVote
|
|
|
|
}
|
2022-12-25 18:15:35 +00:00
|
|
|
|
2022-12-27 12:38:10 +00:00
|
|
|
private func checkEmbed() {
|
2022-12-30 11:00:09 +00:00
|
|
|
if let url = embededStatusURL,
|
2022-12-30 09:11:05 +00:00
|
|
|
!statusText.string.contains(url.absoluteString) {
|
2022-12-27 12:38:10 +00:00
|
|
|
self.embededStatus = nil
|
2023-01-05 11:21:54 +00:00
|
|
|
self.mode = .new(vivibilty: visibility)
|
2022-12-27 12:38:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-31 08:10:27 +00:00
|
|
|
// MARK: - Autocomplete
|
|
|
|
|
|
|
|
private func loadAutoCompleteResults(query: String) {
|
|
|
|
guard let client, query.utf8.count > 1 else { return }
|
|
|
|
Task {
|
|
|
|
do {
|
|
|
|
var results: SearchResults?
|
|
|
|
switch query.first {
|
|
|
|
case "#":
|
|
|
|
results = try await client.get(endpoint: Search.search(query: query,
|
|
|
|
type: "hashtags",
|
|
|
|
offset: 0,
|
|
|
|
following: nil),
|
|
|
|
forceVersion: .v2)
|
|
|
|
withAnimation {
|
|
|
|
tagsSuggestions = results?.hashtags ?? []
|
|
|
|
}
|
|
|
|
case "@":
|
|
|
|
results = try await client.get(endpoint: Search.search(query: query,
|
|
|
|
type: "accounts",
|
|
|
|
offset: 0,
|
|
|
|
following: true),
|
|
|
|
forceVersion: .v2)
|
|
|
|
withAnimation {
|
|
|
|
mentionsSuggestions = results?.accounts ?? []
|
|
|
|
}
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
break
|
|
|
|
}
|
|
|
|
} catch {
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func resetAutoCompletion() {
|
|
|
|
tagsSuggestions = []
|
|
|
|
mentionsSuggestions = []
|
|
|
|
currentSuggestionRange = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func selectMentionSuggestion(account: Account) {
|
|
|
|
if let range = currentSuggestionRange {
|
|
|
|
replaceTextWith(text: "@\(account.acct) ", inRange: range)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func selectHashtagSuggestion(tag: Tag) {
|
|
|
|
if let range = currentSuggestionRange {
|
|
|
|
replaceTextWith(text: "#\(tag.name) ", inRange: range)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-13 17:43:02 +00:00
|
|
|
// MARK: - OpenAI Prompt
|
|
|
|
func runOpenAI(prompt: OpenAIClient.Prompts) async {
|
|
|
|
do {
|
|
|
|
let client = OpenAIClient()
|
|
|
|
let response = try await client.request(prompt)
|
|
|
|
if var text = response.choices.first?.text {
|
|
|
|
text.removeFirst()
|
|
|
|
text.removeFirst()
|
|
|
|
backupStatustext = statusText
|
|
|
|
replaceTextWith(text: text)
|
|
|
|
}
|
|
|
|
} catch { }
|
|
|
|
}
|
|
|
|
|
2022-12-27 18:10:31 +00:00
|
|
|
// MARK: - Media related function
|
|
|
|
|
|
|
|
private func indexOf(container: ImageContainer) -> Int? {
|
|
|
|
mediasImages.firstIndex(where: { $0.id == container.id })
|
|
|
|
}
|
|
|
|
|
2022-12-25 18:15:35 +00:00
|
|
|
func inflateSelectedMedias() {
|
2022-12-27 15:16:25 +00:00
|
|
|
self.mediasImages = []
|
|
|
|
|
|
|
|
Task {
|
|
|
|
var medias: [ImageContainer] = []
|
|
|
|
for media in selectedMedias {
|
|
|
|
do {
|
|
|
|
if let data = try await media.loadTransferable(type: Data.self),
|
|
|
|
let image = UIImage(data: data) {
|
|
|
|
medias.append(.init(image: image, mediaAttachement: nil, error: nil))
|
|
|
|
}
|
|
|
|
} catch {
|
|
|
|
medias.append(.init(image: nil, mediaAttachement: nil, error: error))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DispatchQueue.main.async { [weak self] in
|
|
|
|
self?.mediasImages = medias
|
2023-01-13 11:23:48 +00:00
|
|
|
self?.processMediasToUpload()
|
2022-12-27 15:16:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-13 11:23:48 +00:00
|
|
|
private func processMediasToUpload() {
|
2022-12-27 15:16:25 +00:00
|
|
|
uploadTask?.cancel()
|
|
|
|
let mediasCopy = mediasImages
|
|
|
|
uploadTask = Task {
|
2022-12-27 18:10:31 +00:00
|
|
|
for media in mediasCopy {
|
|
|
|
if !Task.isCancelled {
|
|
|
|
await upload(container: media)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func upload(container: ImageContainer) async {
|
|
|
|
if let index = indexOf(container: container) {
|
|
|
|
let originalContainer = mediasImages[index]
|
|
|
|
let newContainer = ImageContainer(image: originalContainer.image, mediaAttachement: nil, error: nil)
|
|
|
|
mediasImages[index] = newContainer
|
|
|
|
do {
|
|
|
|
if let data = originalContainer.image?.jpegData(compressionQuality: 0.90) {
|
|
|
|
let uploadedMedia = try await uploadMedia(data: data)
|
|
|
|
if let index = indexOf(container: newContainer) {
|
2022-12-27 15:16:25 +00:00
|
|
|
mediasImages[index] = .init(image: nil, mediaAttachement: uploadedMedia, error: nil)
|
2022-12-25 18:15:35 +00:00
|
|
|
}
|
2022-12-27 18:10:31 +00:00
|
|
|
}
|
|
|
|
} catch {
|
|
|
|
if let index = indexOf(container: newContainer) {
|
|
|
|
mediasImages[index] = .init(image: originalContainer.image, mediaAttachement: nil, error: error)
|
2022-12-25 18:15:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-01-03 18:30:27 +00:00
|
|
|
|
|
|
|
func addDescription(container: ImageContainer, description: String) async {
|
|
|
|
guard let client, let attachment = container.mediaAttachement else { return }
|
|
|
|
if let index = indexOf(container: container) {
|
|
|
|
do {
|
|
|
|
let media: MediaAttachement = try await client.put(endpoint: Media.media(id: attachment.id,
|
|
|
|
description: description))
|
|
|
|
mediasImages[index] = .init(image: nil, mediaAttachement: media, error: nil)
|
|
|
|
} catch {
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-12-25 05:55:33 +00:00
|
|
|
|
2022-12-27 15:16:25 +00:00
|
|
|
private func uploadMedia(data: Data) async throws -> MediaAttachement? {
|
|
|
|
guard let client else { return nil }
|
2023-01-10 07:44:29 +00:00
|
|
|
return try await client.mediaUpload(endpoint: Media.medias,
|
|
|
|
version: .v2,
|
|
|
|
method: "POST",
|
|
|
|
mimeType: "image/jpeg",
|
|
|
|
filename: "file",
|
|
|
|
data: data)
|
2022-12-27 15:16:25 +00:00
|
|
|
}
|
2022-12-25 05:55:33 +00:00
|
|
|
}
|