Merge branch 'develop' into feature/audio
This commit is contained in:
commit
04d960c890
|
@ -7,7 +7,7 @@
|
|||
<key>CoreDataStack.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>7</integer>
|
||||
<integer>10</integer>
|
||||
</dict>
|
||||
<key>Mastodon - RTL.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
|
@ -22,7 +22,7 @@
|
|||
<key>Mastodon.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>8</integer>
|
||||
<integer>7</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
|
|
|
@ -111,7 +111,24 @@ extension MastodonConfirmEmailViewController {
|
|||
case .failure(let error):
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: swap user access token swap fail: %s", (#file as NSString).lastPathComponent, #line, #function, error.localizedDescription)
|
||||
case .finished:
|
||||
break
|
||||
// upload avatar and set display name in the background
|
||||
self.context.apiService.accountUpdateCredentials(
|
||||
domain: self.viewModel.authenticateInfo.domain,
|
||||
query: self.viewModel.updateCredentialQuery,
|
||||
authorization: Mastodon.API.OAuth.Authorization(accessToken: self.viewModel.userToken.accessToken)
|
||||
)
|
||||
.retry(3)
|
||||
.sink { completion in
|
||||
switch completion {
|
||||
case .failure(let error):
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: setup avatar & display name fail: %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
|
||||
case .finished:
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: setup avatar & display name success", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
}
|
||||
} receiveValue: { _ in
|
||||
// do nothing
|
||||
}
|
||||
.store(in: &self.context.disposeBag) // execute in the background
|
||||
}
|
||||
} receiveValue: { response in
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: user %s's email confirmed", ((#file as NSString).lastPathComponent), #line, #function, response.value.username)
|
||||
|
|
|
@ -12,20 +12,29 @@ import MastodonSDK
|
|||
final class MastodonConfirmEmailViewModel {
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
// input
|
||||
let context: AppContext
|
||||
var email: String
|
||||
let authenticateInfo: AuthenticationViewModel.AuthenticateInfo
|
||||
let userToken: Mastodon.Entity.Token
|
||||
let updateCredentialQuery: Mastodon.API.Account.UpdateCredentialQuery
|
||||
|
||||
let timestampUpdatePublisher = Timer.publish(every: 4.0, on: .main, in: .common)
|
||||
.autoconnect()
|
||||
.share()
|
||||
.eraseToAnyPublisher()
|
||||
|
||||
init(context: AppContext, email: String, authenticateInfo: AuthenticationViewModel.AuthenticateInfo, userToken: Mastodon.Entity.Token) {
|
||||
init(
|
||||
context: AppContext,
|
||||
email: String,
|
||||
authenticateInfo: AuthenticationViewModel.AuthenticateInfo,
|
||||
userToken: Mastodon.Entity.Token,
|
||||
updateCredentialQuery: Mastodon.API.Account.UpdateCredentialQuery
|
||||
) {
|
||||
self.context = context
|
||||
self.email = email
|
||||
self.authenticateInfo = authenticateInfo
|
||||
self.userToken = userToken
|
||||
self.updateCredentialQuery = updateCredentialQuery
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
// Created by MainasuK Cirno on 2021-2-5.
|
||||
//
|
||||
|
||||
import AlamofireImage
|
||||
import Combine
|
||||
import MastodonSDK
|
||||
import os.log
|
||||
import PhotosUI
|
||||
import UIKit
|
||||
import UITextField_Shake
|
||||
|
||||
final class MastodonRegisterViewController: UIViewController, NeedsDependency, OnboardingViewControllerAppearance {
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
@ -623,10 +623,10 @@ extension MastodonRegisterViewController {
|
|||
username: username,
|
||||
email: email,
|
||||
password: password,
|
||||
agreement: true, // TODO:
|
||||
locale: "en" // TODO:
|
||||
agreement: true, // user confirmed in the server rules scene
|
||||
locale: Locale.current.languageCode ?? "en"
|
||||
)
|
||||
|
||||
|
||||
// register without show server rules
|
||||
context.apiService.accountRegister(
|
||||
domain: viewModel.domain,
|
||||
|
@ -646,7 +646,21 @@ extension MastodonRegisterViewController {
|
|||
} receiveValue: { [weak self] response in
|
||||
guard let self = self else { return }
|
||||
let userToken = response.value
|
||||
let viewModel = MastodonConfirmEmailViewModel(context: self.context, email: email, authenticateInfo: self.viewModel.authenticateInfo, userToken: userToken)
|
||||
let updateCredentialQuery: Mastodon.API.Account.UpdateCredentialQuery = {
|
||||
let displayName: String? = self.viewModel.displayName.value.isEmpty ? nil : self.viewModel.displayName.value
|
||||
let avatar: Mastodon.Query.MediaAttachment? = {
|
||||
guard let avatarImage = self.viewModel.avatarImage.value else { return nil }
|
||||
guard avatarImage.size.width <= 400 else {
|
||||
return .jpeg(avatarImage.af.imageScaled(to: CGSize(width: 400, height: 400)).jpegData(compressionQuality: 0.8))
|
||||
}
|
||||
return .jpeg(avatarImage.jpegData(compressionQuality: 0.8))
|
||||
}()
|
||||
return Mastodon.API.Account.UpdateCredentialQuery(
|
||||
displayName: displayName,
|
||||
avatar: avatar
|
||||
)
|
||||
}()
|
||||
let viewModel = MastodonConfirmEmailViewModel(context: self.context, email: email, authenticateInfo: self.viewModel.authenticateInfo, userToken: userToken, updateCredentialQuery: updateCredentialQuery)
|
||||
self.coordinator.present(scene: .mastodonConfirmEmail(viewModel: viewModel), from: self, transition: .show)
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
|
|
@ -43,6 +43,39 @@ extension APIService {
|
|||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func accountUpdateCredentials(
|
||||
domain: String,
|
||||
query: Mastodon.API.Account.UpdateCredentialQuery,
|
||||
authorization: Mastodon.API.OAuth.Authorization
|
||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Account>, Error> {
|
||||
return Mastodon.API.Account.updateCredentials(
|
||||
session: session,
|
||||
domain: domain,
|
||||
query: query,
|
||||
authorization: authorization
|
||||
)
|
||||
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Account>, Error> in
|
||||
let log = OSLog.api
|
||||
let account = response.value
|
||||
|
||||
return self.backgroundManagedObjectContext.performChanges {
|
||||
let (mastodonUser, isCreated) = APIService.CoreData.createOrMergeMastodonUser(
|
||||
into: self.backgroundManagedObjectContext,
|
||||
for: nil,
|
||||
in: domain,
|
||||
entity: account,
|
||||
networkDate: response.networkDate,
|
||||
log: log)
|
||||
let flag = isCreated ? "+" : "-"
|
||||
os_log(.info, log: log, "%{public}s[%{public}ld], %{public}s: mastodon user [%s](%s)%s verifed", ((#file as NSString).lastPathComponent), #line, #function, flag, mastodonUser.id, mastodonUser.username)
|
||||
}
|
||||
.setFailureType(to: Error.self)
|
||||
.map { _ in return response }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func accountRegister(
|
||||
domain: String,
|
||||
query: Mastodon.API.Account.RegisterQuery,
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
//
|
||||
// Mastodon+API+Account+Credentials.swift
|
||||
//
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-3-8.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
// MARK: - Account credentials
|
||||
extension Mastodon.API.Account {
|
||||
|
||||
static func accountsEndpointURL(domain: String) -> URL {
|
||||
return Mastodon.API.endpointURL(domain: domain).appendingPathComponent("accounts")
|
||||
}
|
||||
|
||||
/// Register an account
|
||||
///
|
||||
/// Creates a user and account records.
|
||||
///
|
||||
/// - Since: 2.7.0
|
||||
/// - Version: 3.3.0
|
||||
/// # Last Update
|
||||
/// 2021/2/9
|
||||
/// # Reference
|
||||
/// [Document](https://docs.joinmastodon.org/methods/accounts/)
|
||||
/// - Parameters:
|
||||
/// - session: `URLSession`
|
||||
/// - domain: Mastodon instance domain. e.g. "example.com"
|
||||
/// - query: `RegisterQuery` with account registration information
|
||||
/// - authorization: App token
|
||||
/// - Returns: `AnyPublisher` contains `Token` nested in the response
|
||||
public static func register(
|
||||
session: URLSession,
|
||||
domain: String,
|
||||
query: RegisterQuery,
|
||||
authorization: Mastodon.API.OAuth.Authorization
|
||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Token>, Error> {
|
||||
let request = Mastodon.API.post(
|
||||
url: accountsEndpointURL(domain: domain),
|
||||
query: query,
|
||||
authorization: authorization
|
||||
)
|
||||
return session.dataTaskPublisher(for: request)
|
||||
.tryMap { data, response in
|
||||
let value = try Mastodon.API.decode(type: Mastodon.Entity.Token.self, from: data, response: response)
|
||||
return Mastodon.Response.Content(value: value, response: response)
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
public struct RegisterQuery: Codable, PostQuery {
|
||||
public let reason: String?
|
||||
public let username: String
|
||||
public let email: String
|
||||
public let password: String
|
||||
public let agreement: Bool
|
||||
public let locale: String
|
||||
|
||||
public init(reason: String? = nil, username: String, email: String, password: String, agreement: Bool, locale: String) {
|
||||
self.reason = reason
|
||||
self.username = username
|
||||
self.email = email
|
||||
self.password = password
|
||||
self.agreement = agreement
|
||||
self.locale = locale
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Mastodon.API.Account {
|
||||
|
||||
static func verifyCredentialsEndpointURL(domain: String) -> URL {
|
||||
return Mastodon.API.endpointURL(domain: domain).appendingPathComponent("accounts/verify_credentials")
|
||||
}
|
||||
|
||||
/// Verify account credentials
|
||||
///
|
||||
/// Test to make sure that the user token works.
|
||||
///
|
||||
/// - Since: 0.0.0
|
||||
/// - Version: 3.3.0
|
||||
/// # Last Update
|
||||
/// 2021/2/9
|
||||
/// # Reference
|
||||
/// [Document](https://docs.joinmastodon.org/methods/accounts/)
|
||||
/// - Parameters:
|
||||
/// - session: `URLSession`
|
||||
/// - domain: Mastodon instance domain. e.g. "example.com"
|
||||
/// - authorization: App token
|
||||
/// - Returns: `AnyPublisher` contains `Account` nested in the response
|
||||
public static func verifyCredentials(
|
||||
session: URLSession,
|
||||
domain: String,
|
||||
authorization: Mastodon.API.OAuth.Authorization
|
||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Account>, Error> {
|
||||
let request = Mastodon.API.get(
|
||||
url: verifyCredentialsEndpointURL(domain: domain),
|
||||
query: nil,
|
||||
authorization: authorization
|
||||
)
|
||||
return session.dataTaskPublisher(for: request)
|
||||
.tryMap { data, response in
|
||||
let value = try Mastodon.API.decode(type: Mastodon.Entity.Account.self, from: data, response: response)
|
||||
return Mastodon.Response.Content(value: value, response: response)
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
static func updateCredentialsEndpointURL(domain: String) -> URL {
|
||||
return Mastodon.API.endpointURL(domain: domain).appendingPathComponent("accounts/update_credentials")
|
||||
}
|
||||
|
||||
/// Update account credentials
|
||||
///
|
||||
/// Update the user's display and preferences.
|
||||
///
|
||||
/// - Since: 1.1.1
|
||||
/// - Version: 3.3.0
|
||||
/// # Last Update
|
||||
/// 2021/2/9
|
||||
/// # Reference
|
||||
/// [Document](https://docs.joinmastodon.org/methods/accounts/)
|
||||
/// - Parameters:
|
||||
/// - session: `URLSession`
|
||||
/// - domain: Mastodon instance domain. e.g. "example.com"
|
||||
/// - query: `CredentialQuery` with update credential information
|
||||
/// - authorization: user token
|
||||
/// - Returns: `AnyPublisher` contains updated `Account` nested in the response
|
||||
public static func updateCredentials(
|
||||
session: URLSession,
|
||||
domain: String,
|
||||
query: UpdateCredentialQuery,
|
||||
authorization: Mastodon.API.OAuth.Authorization
|
||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Account>, Error> {
|
||||
let request = Mastodon.API.patch(
|
||||
url: updateCredentialsEndpointURL(domain: domain),
|
||||
query: query,
|
||||
authorization: authorization
|
||||
)
|
||||
return session.dataTaskPublisher(for: request)
|
||||
.tryMap { data, response in
|
||||
let value = try Mastodon.API.decode(type: Mastodon.Entity.Account.self, from: data, response: response)
|
||||
return Mastodon.Response.Content(value: value, response: response)
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
public struct UpdateCredentialQuery: PatchQuery {
|
||||
public let discoverable: Bool?
|
||||
public let bot: Bool?
|
||||
public let displayName: String?
|
||||
public let note: String?
|
||||
public let avatar: Mastodon.Query.MediaAttachment?
|
||||
public let header: Mastodon.Query.MediaAttachment?
|
||||
public let locked: Bool?
|
||||
public let source: Mastodon.Entity.Source?
|
||||
public let fieldsAttributes: [Mastodon.Entity.Field]?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case discoverable
|
||||
case bot
|
||||
case displayName = "display_name"
|
||||
case note
|
||||
|
||||
case avatar
|
||||
case header
|
||||
case locked
|
||||
case source
|
||||
case fieldsAttributes = "fields_attributes"
|
||||
}
|
||||
|
||||
public init(
|
||||
discoverable: Bool? = nil,
|
||||
bot: Bool? = nil,
|
||||
displayName: String? = nil,
|
||||
note: String? = nil,
|
||||
avatar: Mastodon.Query.MediaAttachment? = nil,
|
||||
header: Mastodon.Query.MediaAttachment? = nil,
|
||||
locked: Bool? = nil,
|
||||
source: Mastodon.Entity.Source? = nil,
|
||||
fieldsAttributes: [Mastodon.Entity.Field]? = nil
|
||||
) {
|
||||
self.discoverable = discoverable
|
||||
self.bot = bot
|
||||
self.displayName = displayName
|
||||
self.note = note
|
||||
self.avatar = avatar
|
||||
self.header = header
|
||||
self.locked = locked
|
||||
self.source = source
|
||||
self.fieldsAttributes = fieldsAttributes
|
||||
}
|
||||
|
||||
var contentType: String? {
|
||||
return Self.multipartContentType()
|
||||
}
|
||||
|
||||
var body: Data? {
|
||||
var data = Data()
|
||||
|
||||
discoverable.flatMap { data.append(Data.multipart(key: "discoverable", value: $0)) }
|
||||
bot.flatMap { data.append(Data.multipart(key: "bot", value: $0)) }
|
||||
displayName.flatMap { data.append(Data.multipart(key: "display_name", value: $0)) }
|
||||
note.flatMap { data.append(Data.multipart(key: "note", value: $0)) }
|
||||
avatar.flatMap { data.append(Data.multipart(key: "avatar", value: $0)) }
|
||||
header.flatMap { data.append(Data.multipart(key: "header", value: $0)) }
|
||||
locked.flatMap { data.append(Data.multipart(key: "locked", value: $0)) }
|
||||
if let source = source {
|
||||
source.privacy.flatMap { data.append(Data.multipart(key: "source[privacy]", value: $0.rawValue)) }
|
||||
source.sensitive.flatMap { data.append(Data.multipart(key: "source[privacy]", value: $0)) }
|
||||
source.language.flatMap { data.append(Data.multipart(key: "source[privacy]", value: $0)) }
|
||||
}
|
||||
fieldsAttributes.flatMap { fieldsAttributes in
|
||||
for fieldsAttribute in fieldsAttributes {
|
||||
data.append(Data.multipart(key: "fields_attributes[name][]", value: fieldsAttribute.name))
|
||||
data.append(Data.multipart(key: "fields_attributes[value][]", value: fieldsAttribute.value))
|
||||
}
|
||||
}
|
||||
data.append(Data.multipartEnd())
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -8,119 +8,17 @@
|
|||
import Foundation
|
||||
import Combine
|
||||
|
||||
// MARK: - Retrieve information
|
||||
extension Mastodon.API.Account {
|
||||
|
||||
static func verifyCredentialsEndpointURL(domain: String) -> URL {
|
||||
return Mastodon.API.endpointURL(domain: domain).appendingPathComponent("accounts/verify_credentials")
|
||||
}
|
||||
static func accountsEndpointURL(domain: String) -> URL {
|
||||
return Mastodon.API.endpointURL(domain: domain).appendingPathComponent("accounts")
|
||||
}
|
||||
|
||||
static func accountsInfoEndpointURL(domain: String, id: String) -> URL {
|
||||
return Mastodon.API.endpointURL(domain: domain).appendingPathComponent("accounts")
|
||||
return Mastodon.API.endpointURL(domain: domain)
|
||||
.appendingPathComponent("accounts")
|
||||
.appendingPathComponent(id)
|
||||
}
|
||||
static func updateCredentialsEndpointURL(domain: String) -> URL {
|
||||
return Mastodon.API.endpointURL(domain: domain).appendingPathComponent("accounts/update_credentials")
|
||||
}
|
||||
|
||||
/// Test to make sure that the user token works.
|
||||
/// Retrieve information
|
||||
///
|
||||
/// - Since: 0.0.0
|
||||
/// - Version: 3.3.0
|
||||
/// # Last Update
|
||||
/// 2021/2/9
|
||||
/// # Reference
|
||||
/// [Document](https://docs.joinmastodon.org/methods/accounts/)
|
||||
/// - Parameters:
|
||||
/// - session: `URLSession`
|
||||
/// - domain: Mastodon instance domain. e.g. "example.com"
|
||||
/// - authorization: App token
|
||||
/// - Returns: `AnyPublisher` contains `Account` nested in the response
|
||||
public static func verifyCredentials(
|
||||
session: URLSession,
|
||||
domain: String,
|
||||
authorization: Mastodon.API.OAuth.Authorization
|
||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Account>, Error> {
|
||||
let request = Mastodon.API.get(
|
||||
url: verifyCredentialsEndpointURL(domain: domain),
|
||||
query: nil,
|
||||
authorization: authorization
|
||||
)
|
||||
return session.dataTaskPublisher(for: request)
|
||||
.tryMap { data, response in
|
||||
let value = try Mastodon.API.decode(type: Mastodon.Entity.Account.self, from: data, response: response)
|
||||
return Mastodon.Response.Content(value: value, response: response)
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
/// Creates a user and account records.
|
||||
///
|
||||
/// - Since: 2.7.0
|
||||
/// - Version: 3.3.0
|
||||
/// # Last Update
|
||||
/// 2021/2/9
|
||||
/// # Reference
|
||||
/// [Document](https://docs.joinmastodon.org/methods/accounts/)
|
||||
/// - Parameters:
|
||||
/// - session: `URLSession`
|
||||
/// - domain: Mastodon instance domain. e.g. "example.com"
|
||||
/// - query: `RegisterQuery` with account registration information
|
||||
/// - authorization: App token
|
||||
/// - Returns: `AnyPublisher` contains `Token` nested in the response
|
||||
public static func register(
|
||||
session: URLSession,
|
||||
domain: String,
|
||||
query: RegisterQuery,
|
||||
authorization: Mastodon.API.OAuth.Authorization
|
||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Token>, Error> {
|
||||
let request = Mastodon.API.post(
|
||||
url: accountsEndpointURL(domain: domain),
|
||||
query: query,
|
||||
authorization: authorization
|
||||
)
|
||||
return session.dataTaskPublisher(for: request)
|
||||
.tryMap { data, response in
|
||||
let value = try Mastodon.API.decode(type: Mastodon.Entity.Token.self, from: data, response: response)
|
||||
return Mastodon.Response.Content(value: value, response: response)
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
/// Update the user's display and preferences.
|
||||
///
|
||||
/// - Since: 1.1.1
|
||||
/// - Version: 3.3.0
|
||||
/// # Last Update
|
||||
/// 2021/2/9
|
||||
/// # Reference
|
||||
/// [Document](https://docs.joinmastodon.org/methods/accounts/)
|
||||
/// - Parameters:
|
||||
/// - session: `URLSession`
|
||||
/// - domain: Mastodon instance domain. e.g. "example.com"
|
||||
/// - query: `CredentialQuery` with update credential information
|
||||
/// - authorization: user token
|
||||
/// - Returns: `AnyPublisher` contains updated `Account` nested in the response
|
||||
public static func updateCredentials(
|
||||
session: URLSession,
|
||||
domain: String,
|
||||
query: UpdateCredentialQuery,
|
||||
authorization: Mastodon.API.OAuth.Authorization
|
||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Account>, Error> {
|
||||
let request = Mastodon.API.patch(
|
||||
url: updateCredentialsEndpointURL(domain: domain),
|
||||
query: query,
|
||||
authorization: authorization
|
||||
)
|
||||
return session.dataTaskPublisher(for: request)
|
||||
.tryMap { data, response in
|
||||
let value = try Mastodon.API.decode(type: Mastodon.Entity.Account.self, from: data, response: response)
|
||||
return Mastodon.Response.Content(value: value, response: response)
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
/// View information about a profile.
|
||||
///
|
||||
/// - Since: 0.0.0
|
||||
|
@ -138,11 +36,11 @@ extension Mastodon.API.Account {
|
|||
public static func accountInfo(
|
||||
session: URLSession,
|
||||
domain: String,
|
||||
query: AccountInfoQuery,
|
||||
userID: Mastodon.Entity.Account.ID,
|
||||
authorization: Mastodon.API.OAuth.Authorization?
|
||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Account>, Error> {
|
||||
let request = Mastodon.API.get(
|
||||
url: accountsInfoEndpointURL(domain: domain, id: query.id),
|
||||
url: accountsInfoEndpointURL(domain: domain, id: userID),
|
||||
query: nil,
|
||||
authorization: authorization
|
||||
)
|
||||
|
@ -155,79 +53,3 @@ extension Mastodon.API.Account {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
extension Mastodon.API.Account {
|
||||
|
||||
public struct RegisterQuery: Codable, PostQuery {
|
||||
public let reason: String?
|
||||
public let username: String
|
||||
public let email: String
|
||||
public let password: String
|
||||
public let agreement: Bool
|
||||
public let locale: String
|
||||
|
||||
public init(reason: String? = nil, username: String, email: String, password: String, agreement: Bool, locale: String) {
|
||||
self.reason = reason
|
||||
self.username = username
|
||||
self.email = email
|
||||
self.password = password
|
||||
self.agreement = agreement
|
||||
self.locale = locale
|
||||
}
|
||||
}
|
||||
|
||||
public struct UpdateCredentialQuery: Codable, PatchQuery {
|
||||
|
||||
public var discoverable: Bool?
|
||||
public var bot: Bool?
|
||||
public var displayName: String?
|
||||
public var note: String?
|
||||
public var avatar: String?
|
||||
public var header: String?
|
||||
public var locked: Bool?
|
||||
public var source: Mastodon.Entity.Source?
|
||||
public var fieldsAttributes: [Mastodon.Entity.Field]?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case discoverable
|
||||
case bot
|
||||
case displayName = "display_name"
|
||||
case note
|
||||
|
||||
case avatar
|
||||
case header
|
||||
case locked
|
||||
case source
|
||||
case fieldsAttributes = "fields_attributes"
|
||||
}
|
||||
|
||||
public init(
|
||||
discoverable: Bool? = nil,
|
||||
bot: Bool? = nil,
|
||||
displayName: String? = nil,
|
||||
note: String? = nil,
|
||||
avatar: Mastodon.Entity.MediaAttachment? = nil,
|
||||
header: Mastodon.Entity.MediaAttachment? = nil,
|
||||
locked: Bool? = nil,
|
||||
source: Mastodon.Entity.Source? = nil,
|
||||
fieldsAttributes: [Mastodon.Entity.Field]? = nil
|
||||
) {
|
||||
self.discoverable = discoverable
|
||||
self.bot = bot
|
||||
self.displayName = displayName
|
||||
self.note = note
|
||||
self.avatar = avatar?.base64EncondedString
|
||||
self.header = header?.base64EncondedString
|
||||
self.locked = locked
|
||||
self.source = source
|
||||
self.fieldsAttributes = fieldsAttributes
|
||||
}
|
||||
}
|
||||
|
||||
public struct AccountInfoQuery: GetQuery {
|
||||
|
||||
public let id: String
|
||||
|
||||
var queryItems: [URLQueryItem]? { nil }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,8 +140,13 @@ extension Mastodon.API {
|
|||
timeoutInterval: Mastodon.API.timeoutInterval
|
||||
)
|
||||
request.httpMethod = method.rawValue
|
||||
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
||||
request.httpBody = query?.body
|
||||
if let contentType = query?.contentType {
|
||||
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
|
||||
}
|
||||
if let body = query?.body {
|
||||
request.httpBody = body
|
||||
request.setValue("\(body.count)", forHTTPHeaderField: "Content-Length")
|
||||
}
|
||||
if let authorization = authorization {
|
||||
request.setValue(
|
||||
"Bearer \(authorization.accessToken)",
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// Data.swift
|
||||
//
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-3-8.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Data {
|
||||
|
||||
static func multipart(
|
||||
boundary: String = Multipart.boundary,
|
||||
key: String,
|
||||
value: MultipartFormValue
|
||||
) -> Data {
|
||||
var data = Data()
|
||||
data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
|
||||
data.append("Content-Disposition: form-data; name=\"\(key)\"".data(using: .utf8)!)
|
||||
if let filename = value.multipartFilename {
|
||||
data.append("; filename=\"\(filename)\"\r\n".data(using: .utf8)!)
|
||||
} else {
|
||||
data.append("\r\n".data(using: .utf8)!)
|
||||
}
|
||||
if let contentType = value.multipartContentType {
|
||||
data.append("Content-Type: \(contentType)\r\n".data(using: .utf8)!)
|
||||
}
|
||||
data.append("\r\n".data(using: .utf8)!)
|
||||
data.append(value.multipartValue)
|
||||
return data
|
||||
}
|
||||
|
||||
static func multipartEnd(boundary: String = Multipart.boundary) -> Data {
|
||||
return "\r\n--\(boundary)--\r\n".data(using: .utf8)!
|
||||
}
|
||||
|
||||
}
|
|
@ -12,4 +12,5 @@ public enum Mastodon {
|
|||
public enum Response { }
|
||||
public enum API { }
|
||||
public enum Entity { }
|
||||
public enum Query { }
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Mastodon+Entity+MediaAttachment.swift
|
||||
// MediaAttachment.swift
|
||||
//
|
||||
//
|
||||
// Created by jk234ert on 2/9/21.
|
||||
|
@ -7,7 +7,7 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
extension Mastodon.Entity {
|
||||
extension Mastodon.Query {
|
||||
public enum MediaAttachment {
|
||||
/// JPEG (Joint Photographic Experts Group) image
|
||||
case jpeg(Data?)
|
||||
|
@ -20,7 +20,7 @@ extension Mastodon.Entity {
|
|||
}
|
||||
}
|
||||
|
||||
extension Mastodon.Entity.MediaAttachment {
|
||||
extension Mastodon.Query.MediaAttachment {
|
||||
var data: Data? {
|
||||
switch self {
|
||||
case .jpeg(let data): return data
|
||||
|
@ -31,11 +31,12 @@ extension Mastodon.Entity.MediaAttachment {
|
|||
}
|
||||
|
||||
var fileName: String {
|
||||
let name = UUID().uuidString
|
||||
switch self {
|
||||
case .jpeg: return "file.jpg"
|
||||
case .gif: return "file.gif"
|
||||
case .png: return "file.png"
|
||||
case .other(_, let fileExtension, _): return "file.\(fileExtension)"
|
||||
case .jpeg: return "\(name).jpg"
|
||||
case .gif: return "\(name).gif"
|
||||
case .png: return "\(name).png"
|
||||
case .other(_, let fileExtension, _): return "\(name).\(fileExtension)"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,3 +54,8 @@ extension Mastodon.Entity.MediaAttachment {
|
|||
}
|
||||
}
|
||||
|
||||
extension Mastodon.Query.MediaAttachment: MultipartFormValue {
|
||||
var multipartValue: Data { return data ?? Data() }
|
||||
var multipartContentType: String? { return mimeType }
|
||||
var multipartFilename: String? { return fileName }
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// MultipartFormValue.swift
|
||||
//
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-3-8.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum Multipart {
|
||||
static let boundary = "__boundary__"
|
||||
}
|
||||
|
||||
protocol MultipartFormValue {
|
||||
var multipartValue: Data { get }
|
||||
var multipartContentType: String? { get }
|
||||
var multipartFilename: String? { get }
|
||||
}
|
||||
|
||||
extension Bool: MultipartFormValue {
|
||||
var multipartValue: Data {
|
||||
switch self {
|
||||
case true: return "true".data(using: .utf8)!
|
||||
case false: return "false".data(using: .utf8)!
|
||||
}
|
||||
}
|
||||
var multipartContentType: String? { return nil }
|
||||
var multipartFilename: String? { return nil }
|
||||
}
|
||||
|
||||
extension String: MultipartFormValue {
|
||||
var multipartValue: Data {
|
||||
return self.data(using: .utf8)!
|
||||
}
|
||||
var multipartContentType: String? { return nil }
|
||||
var multipartFilename: String? { return nil }
|
||||
}
|
|
@ -14,12 +14,22 @@ enum RequestMethod: String {
|
|||
protocol RequestQuery {
|
||||
// All kinds of queries could have queryItems and body
|
||||
var queryItems: [URLQueryItem]? { get }
|
||||
var contentType: String? { get }
|
||||
var body: Data? { get }
|
||||
}
|
||||
|
||||
extension RequestQuery {
|
||||
static func multipartContentType(boundary: String = Multipart.boundary) -> String {
|
||||
return "multipart/form-data; charset=utf-8; boundary=\"\(boundary)\""
|
||||
}
|
||||
}
|
||||
|
||||
// An `Encodable` query provides its body by encoding itself
|
||||
// A `Get` query only contains queryItems, it should not be `Encodable`
|
||||
extension RequestQuery where Self: Encodable {
|
||||
var contentType: String? {
|
||||
return "application/json; charset=utf-8"
|
||||
}
|
||||
var body: Data? {
|
||||
return try? Mastodon.API.encoder.encode(self)
|
||||
}
|
||||
|
@ -30,18 +40,20 @@ protocol GetQuery: RequestQuery { }
|
|||
extension GetQuery {
|
||||
// By default a `GetQuery` does not has data body
|
||||
var body: Data? { nil }
|
||||
var contentType: String? { nil }
|
||||
}
|
||||
|
||||
protocol PostQuery: RequestQuery & Encodable { }
|
||||
protocol PostQuery: RequestQuery { }
|
||||
|
||||
extension PostQuery {
|
||||
// By default a `GetQuery` does not has query items
|
||||
// By default a `PostQuery` does not has query items
|
||||
var queryItems: [URLQueryItem]? { nil }
|
||||
}
|
||||
|
||||
protocol PatchQuery: RequestQuery & Encodable { }
|
||||
protocol PatchQuery: RequestQuery { }
|
||||
|
||||
extension PatchQuery {
|
||||
// By default a `GetQuery` does not has query items
|
||||
// By default a `PatchQuery` does not has query items
|
||||
var queryItems: [URLQueryItem]? { nil }
|
||||
}
|
||||
|
|
@ -8,9 +8,11 @@
|
|||
import os.log
|
||||
import XCTest
|
||||
import Combine
|
||||
import UIKit
|
||||
@testable import MastodonSDK
|
||||
|
||||
extension MastodonSDKTests {
|
||||
|
||||
func testVerifyCredentials() throws {
|
||||
let theExpectation = expectation(description: "Verify Account Credentials")
|
||||
|
||||
|
@ -44,11 +46,14 @@ extension MastodonSDKTests {
|
|||
.flatMap({ (result) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Account>, Error> in
|
||||
|
||||
// TODO: replace with test account acct
|
||||
XCTAssertEqual(result.value.acct, "")
|
||||
XCTAssert(!result.value.acct.isEmpty)
|
||||
theExpectation1.fulfill()
|
||||
|
||||
var query = Mastodon.API.Account.UpdateCredentialQuery()
|
||||
query.note = dateString
|
||||
|
||||
let query = Mastodon.API.Account.UpdateCredentialQuery(
|
||||
bot: !(result.value.bot ?? false),
|
||||
note: dateString,
|
||||
header: Mastodon.Query.MediaAttachment.jpeg(UIImage(systemName: "house")!.jpegData(compressionQuality: 0.8))
|
||||
)
|
||||
return Mastodon.API.Account.updateCredentials(session: self.session, domain: self.domain, query: query, authorization: authorization)
|
||||
})
|
||||
.sink { completion in
|
||||
|
@ -73,8 +78,7 @@ extension MastodonSDKTests {
|
|||
func testRetrieveAccountInfo() throws {
|
||||
let theExpectation = expectation(description: "Verify Account Credentials")
|
||||
|
||||
let query = Mastodon.API.Account.AccountInfoQuery(id: "1")
|
||||
Mastodon.API.Account.accountInfo(session: session, domain: domain, query: query, authorization: nil)
|
||||
Mastodon.API.Account.accountInfo(session: session, domain: "mastodon.online", userID: "1", authorization: nil)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { completion in
|
||||
switch completion {
|
||||
|
@ -91,4 +95,5 @@ extension MastodonSDKTests {
|
|||
|
||||
wait(for: [theExpectation], timeout: 5.0)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue