re-indent
This commit is contained in:
parent
2fdf0e2c3b
commit
8f3bb1a99b
|
@ -1,10 +1,10 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct JSONAdapter: RequestAdapter {
|
struct JSONAdapter: RequestAdapter {
|
||||||
func adapt(_ urlRequest: URLRequest) -> URLRequest {
|
func adapt(_ urlRequest: URLRequest) -> URLRequest {
|
||||||
var request = urlRequest
|
var request = urlRequest
|
||||||
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||||
request.addValue("application/json", forHTTPHeaderField: "Accept")
|
request.addValue("application/json", forHTTPHeaderField: "Accept")
|
||||||
return request
|
return request
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
protocol RequestAdapter {
|
protocol RequestAdapter {
|
||||||
func adapt(_ urlRequest: URLRequest) -> URLRequest
|
func adapt(_ urlRequest: URLRequest) -> URLRequest
|
||||||
func adapt(_ url: URL) -> URL
|
func adapt(_ url: URL) -> URL
|
||||||
}
|
}
|
||||||
|
|
||||||
extension RequestAdapter {
|
extension RequestAdapter {
|
||||||
func adapt(_ url: URL) -> URL {
|
func adapt(_ url: URL) -> URL {
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct TokenAdapter: RequestAdapter {
|
struct TokenAdapter: RequestAdapter {
|
||||||
private let server: Server
|
private let server: Server
|
||||||
|
|
||||||
init(server: Server) {
|
init(server: Server) {
|
||||||
self.server = server
|
self.server = server
|
||||||
}
|
}
|
||||||
|
|
||||||
func adapt(_ url: URL) -> URL {
|
func adapt(_ url: URL) -> URL {
|
||||||
var url = url
|
var url = url
|
||||||
|
|
||||||
url.append(
|
url.append(
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "rc_token", value: server.loggedUser.token),
|
URLQueryItem(name: "rc_token", value: server.loggedUser.token),
|
||||||
URLQueryItem(name: "rc_uid", value: server.loggedUser.id)
|
URLQueryItem(name: "rc_uid", value: server.loggedUser.id)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
func adapt(_ urlRequest: URLRequest) -> URLRequest {
|
func adapt(_ urlRequest: URLRequest) -> URLRequest {
|
||||||
var request = urlRequest
|
var request = urlRequest
|
||||||
request.addValue(server.loggedUser.id, forHTTPHeaderField: "x-user-id")
|
request.addValue(server.loggedUser.id, forHTTPHeaderField: "x-user-id")
|
||||||
request.addValue(server.loggedUser.token, forHTTPHeaderField: "x-auth-token")
|
request.addValue(server.loggedUser.token, forHTTPHeaderField: "x-auth-token")
|
||||||
return request
|
return request
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,42 +3,42 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
extension Date.ISO8601FormatStyle {
|
extension Date.ISO8601FormatStyle {
|
||||||
static let iso8601withFractionalSeconds: Self = .init(includingFractionalSeconds: true)
|
static let iso8601withFractionalSeconds: Self = .init(includingFractionalSeconds: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ParseStrategy where Self == Date.ISO8601FormatStyle {
|
extension ParseStrategy where Self == Date.ISO8601FormatStyle {
|
||||||
static var iso8601withFractionalSeconds: Date.ISO8601FormatStyle { .iso8601withFractionalSeconds }
|
static var iso8601withFractionalSeconds: Date.ISO8601FormatStyle { .iso8601withFractionalSeconds }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension FormatStyle where Self == Date.ISO8601FormatStyle {
|
extension FormatStyle where Self == Date.ISO8601FormatStyle {
|
||||||
static var iso8601withFractionalSeconds: Date.ISO8601FormatStyle { .iso8601withFractionalSeconds }
|
static var iso8601withFractionalSeconds: Date.ISO8601FormatStyle { .iso8601withFractionalSeconds }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Date {
|
extension Date {
|
||||||
init(iso8601withFractionalSeconds parseInput: ParseStrategy.ParseInput) throws {
|
init(iso8601withFractionalSeconds parseInput: ParseStrategy.ParseInput) throws {
|
||||||
try self.init(parseInput, strategy: .iso8601withFractionalSeconds)
|
try self.init(parseInput, strategy: .iso8601withFractionalSeconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
var iso8601withFractionalSeconds: String {
|
var iso8601withFractionalSeconds: String {
|
||||||
formatted(.iso8601withFractionalSeconds)
|
formatted(.iso8601withFractionalSeconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension String {
|
extension String {
|
||||||
func iso8601withFractionalSeconds() throws -> Date {
|
func iso8601withFractionalSeconds() throws -> Date {
|
||||||
try .init(iso8601withFractionalSeconds: self)
|
try .init(iso8601withFractionalSeconds: self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension JSONDecoder.DateDecodingStrategy {
|
extension JSONDecoder.DateDecodingStrategy {
|
||||||
static let iso8601withFractionalSeconds = custom {
|
static let iso8601withFractionalSeconds = custom {
|
||||||
try .init(iso8601withFractionalSeconds: $0.singleValueContainer().decode(String.self))
|
try .init(iso8601withFractionalSeconds: $0.singleValueContainer().decode(String.self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension JSONEncoder.DateEncodingStrategy {
|
extension JSONEncoder.DateEncodingStrategy {
|
||||||
static let iso8601withFractionalSeconds = custom {
|
static let iso8601withFractionalSeconds = custom {
|
||||||
var container = $1.singleValueContainer()
|
var container = $1.singleValueContainer()
|
||||||
try container.encode($0.iso8601withFractionalSeconds)
|
try container.encode($0.iso8601withFractionalSeconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
extension Data {
|
extension Data {
|
||||||
func decode<T: Decodable>(_ type: T.Type) throws -> T {
|
func decode<T: Decodable>(_ type: T.Type) throws -> T {
|
||||||
let decoder = JSONDecoder()
|
let decoder = JSONDecoder()
|
||||||
decoder.dateDecodingStrategy = .iso8601withFractionalSeconds
|
decoder.dateDecodingStrategy = .iso8601withFractionalSeconds
|
||||||
return try decoder.decode(T.self, from: self)
|
return try decoder.decode(T.self, from: self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
extension String {
|
extension String {
|
||||||
static func random(_ count: Int) -> String {
|
static func random(_ count: Int) -> String {
|
||||||
let letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
let letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||||
return String((0..<count).compactMap { _ in letters.randomElement() })
|
return String((0..<count).compactMap { _ in letters.randomElement() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
struct FailableDecodable<Value: Codable & Hashable>: Codable, Hashable {
|
struct FailableDecodable<Value: Codable & Hashable>: Codable, Hashable {
|
||||||
let value: Value?
|
let value: Value?
|
||||||
|
|
||||||
init(from decoder: Decoder) throws {
|
init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.singleValueContainer()
|
let container = try decoder.singleValueContainer()
|
||||||
value = try? container.decode(Value.self)
|
value = try? container.decode(Value.self)
|
||||||
}
|
}
|
||||||
|
|
||||||
func encode(to encoder: Encoder) throws {
|
func encode(to encoder: Encoder) throws {
|
||||||
var container = encoder.singleValueContainer()
|
var container = encoder.singleValueContainer()
|
||||||
try container.encode(value)
|
try container.encode(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
enum HTTPMethod: String {
|
enum HTTPMethod: String {
|
||||||
case get = "GET"
|
case get = "GET"
|
||||||
case post = "POST"
|
case post = "POST"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
protocol Request<Response> {
|
protocol Request<Response> {
|
||||||
associatedtype Response: Codable
|
associatedtype Response: Codable
|
||||||
|
|
||||||
var path: String { get }
|
var path: String { get }
|
||||||
var method: HTTPMethod { get }
|
var method: HTTPMethod { get }
|
||||||
var body: Data? { get }
|
var body: Data? { get }
|
||||||
var queryItems: [URLQueryItem] { get }
|
var queryItems: [URLQueryItem] { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Request {
|
extension Request {
|
||||||
var method: HTTPMethod {
|
var method: HTTPMethod {
|
||||||
.get
|
.get
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: Data? {
|
var body: Data? {
|
||||||
nil
|
nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var queryItems: [URLQueryItem] {
|
var queryItems: [URLQueryItem] {
|
||||||
[]
|
[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,44 +3,44 @@ import Foundation
|
||||||
let HISTORY_MESSAGE_COUNT = 50
|
let HISTORY_MESSAGE_COUNT = 50
|
||||||
|
|
||||||
struct HistoryRequest: Request {
|
struct HistoryRequest: Request {
|
||||||
typealias Response = HistoryResponse
|
typealias Response = HistoryResponse
|
||||||
|
|
||||||
let path: String
|
let path: String
|
||||||
let queryItems: [URLQueryItem]
|
let queryItems: [URLQueryItem]
|
||||||
|
|
||||||
init(roomId: String, roomType: String?, latest: Date) {
|
init(roomId: String, roomType: String?, latest: Date) {
|
||||||
path = "api/v1/\(RoomType.from(roomType).api).history"
|
path = "api/v1/\(RoomType.from(roomType).api).history"
|
||||||
|
|
||||||
queryItems = [
|
queryItems = [
|
||||||
URLQueryItem(name: "roomId", value: roomId),
|
URLQueryItem(name: "roomId", value: roomId),
|
||||||
URLQueryItem(name: "count", value: String(HISTORY_MESSAGE_COUNT)),
|
URLQueryItem(name: "count", value: String(HISTORY_MESSAGE_COUNT)),
|
||||||
URLQueryItem(name: "latest", value: latest.iso8601withFractionalSeconds)
|
URLQueryItem(name: "latest", value: latest.iso8601withFractionalSeconds)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate enum RoomType: String {
|
fileprivate enum RoomType: String {
|
||||||
case direct = "d"
|
case direct = "d"
|
||||||
case group = "p"
|
case group = "p"
|
||||||
case channel = "c"
|
case channel = "c"
|
||||||
case livechat = "l"
|
case livechat = "l"
|
||||||
|
|
||||||
static func from(_ rawValue: String?) -> Self {
|
static func from(_ rawValue: String?) -> Self {
|
||||||
guard let rawValue, let type = RoomType(rawValue: rawValue) else {
|
guard let rawValue, let type = RoomType(rawValue: rawValue) else {
|
||||||
return .channel
|
return .channel
|
||||||
}
|
}
|
||||||
|
|
||||||
return type
|
return type
|
||||||
}
|
}
|
||||||
|
|
||||||
var api: String {
|
var api: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .direct:
|
case .direct:
|
||||||
return "im"
|
return "im"
|
||||||
case .group:
|
case .group:
|
||||||
return "groups"
|
return "groups"
|
||||||
case .channel, .livechat:
|
case .channel, .livechat:
|
||||||
return "channels"
|
return "channels"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct MessagesRequest: Request {
|
struct MessagesRequest: Request {
|
||||||
typealias Response = MessagesResponse
|
typealias Response = MessagesResponse
|
||||||
|
|
||||||
let path: String = "api/v1/chat.syncMessages"
|
let path: String = "api/v1/chat.syncMessages"
|
||||||
let queryItems: [URLQueryItem]
|
let queryItems: [URLQueryItem]
|
||||||
|
|
||||||
init(lastUpdate: Date?, roomId: String) {
|
init(lastUpdate: Date?, roomId: String) {
|
||||||
self.queryItems = [
|
self.queryItems = [
|
||||||
URLQueryItem(name: "roomId", value: roomId),
|
URLQueryItem(name: "roomId", value: roomId),
|
||||||
URLQueryItem(name: "lastUpdate", value: lastUpdate?.ISO8601Format())
|
URLQueryItem(name: "lastUpdate", value: lastUpdate?.ISO8601Format())
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct ReadRequest: Request {
|
struct ReadRequest: Request {
|
||||||
typealias Response = ReadResponse
|
typealias Response = ReadResponse
|
||||||
|
|
||||||
let path: String = "api/v1/subscriptions.read"
|
let path: String = "api/v1/subscriptions.read"
|
||||||
let method: HTTPMethod = .post
|
let method: HTTPMethod = .post
|
||||||
|
|
||||||
var body: Data? {
|
var body: Data? {
|
||||||
try? JSONSerialization.data(withJSONObject: [
|
try? JSONSerialization.data(withJSONObject: [
|
||||||
"rid": rid
|
"rid": rid
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
let rid: String
|
let rid: String
|
||||||
|
|
||||||
init(rid: String) {
|
init(rid: String) {
|
||||||
self.rid = rid
|
self.rid = rid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct RoomsRequest: Request {
|
struct RoomsRequest: Request {
|
||||||
typealias Response = RoomsResponse
|
typealias Response = RoomsResponse
|
||||||
|
|
||||||
let path: String = "api/v1/rooms.get"
|
let path: String = "api/v1/rooms.get"
|
||||||
let queryItems: [URLQueryItem]
|
let queryItems: [URLQueryItem]
|
||||||
|
|
||||||
init(updatedSince: Date?) {
|
init(updatedSince: Date?) {
|
||||||
if let updatedSince {
|
if let updatedSince {
|
||||||
queryItems = [URLQueryItem(name: "updatedSince", value: updatedSince.iso8601withFractionalSeconds)]
|
queryItems = [URLQueryItem(name: "updatedSince", value: updatedSince.iso8601withFractionalSeconds)]
|
||||||
} else {
|
} else {
|
||||||
queryItems = []
|
queryItems = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct SendMessageRequest: Request {
|
struct SendMessageRequest: Request {
|
||||||
typealias Response = SendMessageResponse
|
typealias Response = SendMessageResponse
|
||||||
|
|
||||||
let path: String = "api/v1/chat.sendMessage"
|
let path: String = "api/v1/chat.sendMessage"
|
||||||
let method: HTTPMethod = .post
|
let method: HTTPMethod = .post
|
||||||
|
|
||||||
var body: Data? {
|
var body: Data? {
|
||||||
try? JSONSerialization.data(withJSONObject: [
|
try? JSONSerialization.data(withJSONObject: [
|
||||||
"message": [
|
"message": [
|
||||||
"_id": id,
|
"_id": id,
|
||||||
"rid": rid,
|
"rid": rid,
|
||||||
"msg": msg,
|
"msg": msg,
|
||||||
"tshow": false
|
"tshow": false
|
||||||
]
|
]
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
let id: String
|
let id: String
|
||||||
let rid: String
|
let rid: String
|
||||||
let msg: String
|
let msg: String
|
||||||
|
|
||||||
init(id: String, rid: String, msg: String) {
|
init(id: String, rid: String, msg: String) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.rid = rid
|
self.rid = rid
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct SubscriptionsRequest: Request {
|
struct SubscriptionsRequest: Request {
|
||||||
typealias Response = SubscriptionsResponse
|
typealias Response = SubscriptionsResponse
|
||||||
|
|
||||||
let path: String = "api/v1/subscriptions.get"
|
let path: String = "api/v1/subscriptions.get"
|
||||||
let queryItems: [URLQueryItem]
|
let queryItems: [URLQueryItem]
|
||||||
|
|
||||||
init(updatedSince: Date?) {
|
init(updatedSince: Date?) {
|
||||||
if let updatedSince {
|
if let updatedSince {
|
||||||
queryItems = [URLQueryItem(name: "updatedSince", value: updatedSince.iso8601withFractionalSeconds)]
|
queryItems = [URLQueryItem(name: "updatedSince", value: updatedSince.iso8601withFractionalSeconds)]
|
||||||
} else {
|
} else {
|
||||||
queryItems = []
|
queryItems = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct AttachmentResponse: Codable, Hashable {
|
struct AttachmentResponse: Codable, Hashable {
|
||||||
let imageURL: URL?
|
let imageURL: URL?
|
||||||
let description: String?
|
let description: String?
|
||||||
let dimensions: Dimensions?
|
let dimensions: Dimensions?
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case imageURL = "image_url"
|
case imageURL = "image_url"
|
||||||
case description
|
case description
|
||||||
case dimensions = "image_dimensions"
|
case dimensions = "image_dimensions"
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Dimensions: Codable, Hashable {
|
struct Dimensions: Codable, Hashable {
|
||||||
let width: Double
|
let width: Double
|
||||||
let height: Double
|
let height: Double
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct HistoryResponse: Codable {
|
struct HistoryResponse: Codable {
|
||||||
let messages: [MessageResponse]
|
let messages: [MessageResponse]
|
||||||
let success: Bool
|
let success: Bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct MessageResponse: Codable, Hashable {
|
struct MessageResponse: Codable, Hashable {
|
||||||
let _id: String
|
let _id: String
|
||||||
let rid: String
|
let rid: String
|
||||||
let msg: String
|
let msg: String
|
||||||
let u: UserResponse
|
let u: UserResponse
|
||||||
let ts: Date
|
let ts: Date
|
||||||
let attachments: [AttachmentResponse]?
|
let attachments: [AttachmentResponse]?
|
||||||
let t: String?
|
let t: String?
|
||||||
let groupable: Bool?
|
let groupable: Bool?
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct MessagesResponse: Codable {
|
struct MessagesResponse: Codable {
|
||||||
let result: MessagesResult
|
let result: MessagesResult
|
||||||
let success: Bool
|
let success: Bool
|
||||||
|
|
||||||
struct MessagesResult: Codable {
|
struct MessagesResult: Codable {
|
||||||
let updated: [MessageResponse]
|
let updated: [MessageResponse]
|
||||||
let deleted: [MessageResponse]
|
let deleted: [MessageResponse]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct ReadResponse: Codable {
|
struct ReadResponse: Codable {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct RoomsResponse: Codable {
|
struct RoomsResponse: Codable {
|
||||||
let update: Set<Room>
|
let update: Set<Room>
|
||||||
let remove: Set<Room>
|
let remove: Set<Room>
|
||||||
let success: Bool
|
let success: Bool
|
||||||
|
|
||||||
struct Room: Codable, Hashable {
|
struct Room: Codable, Hashable {
|
||||||
let _id: String
|
let _id: String
|
||||||
let name: String?
|
let name: String?
|
||||||
let fname: String?
|
let fname: String?
|
||||||
let prid: String?
|
let prid: String?
|
||||||
let t: String?
|
let t: String?
|
||||||
let ts: Date?
|
let ts: Date?
|
||||||
let ro: Bool?
|
let ro: Bool?
|
||||||
let _updatedAt: Date?
|
let _updatedAt: Date?
|
||||||
let encrypted: Bool?
|
let encrypted: Bool?
|
||||||
let usernames: [String]?
|
let usernames: [String]?
|
||||||
let uids: [String]?
|
let uids: [String]?
|
||||||
let lastMessage: FailableDecodable<MessageResponse>?
|
let lastMessage: FailableDecodable<MessageResponse>?
|
||||||
let lm: Date?
|
let lm: Date?
|
||||||
let teamMain: Bool?
|
let teamMain: Bool?
|
||||||
let archived: Bool?
|
let archived: Bool?
|
||||||
let broadcast: Bool?
|
let broadcast: Bool?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct SendMessageResponse: Codable {
|
struct SendMessageResponse: Codable {
|
||||||
let message: MessageResponse
|
let message: MessageResponse
|
||||||
let success: Bool
|
let success: Bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct SubscriptionsResponse: Codable {
|
struct SubscriptionsResponse: Codable {
|
||||||
let update: Set<Subscription>
|
let update: Set<Subscription>
|
||||||
let remove: Set<Subscription>
|
let remove: Set<Subscription>
|
||||||
let success: Bool
|
let success: Bool
|
||||||
|
|
||||||
struct Subscription: Codable, Hashable {
|
struct Subscription: Codable, Hashable {
|
||||||
let _id: String
|
let _id: String
|
||||||
let rid: String
|
let rid: String
|
||||||
let name: String?
|
let name: String?
|
||||||
let fname: String?
|
let fname: String?
|
||||||
let t: String
|
let t: String
|
||||||
let unread: Int
|
let unread: Int
|
||||||
let alert: Bool
|
let alert: Bool
|
||||||
let lr: Date?
|
let lr: Date?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Sequence where Element == SubscriptionsResponse.Subscription {
|
extension Sequence where Element == SubscriptionsResponse.Subscription {
|
||||||
func find(withRoomID rid: String) -> SubscriptionsResponse.Subscription? {
|
func find(withRoomID rid: String) -> SubscriptionsResponse.Subscription? {
|
||||||
first { subscription in
|
first { subscription in
|
||||||
subscription.rid == rid
|
subscription.rid == rid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct UserResponse: Codable, Hashable {
|
struct UserResponse: Codable, Hashable {
|
||||||
let _id: String
|
let _id: String
|
||||||
let username: String
|
let username: String
|
||||||
let name: String
|
let name: String
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,90 +2,90 @@ import Combine
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
protocol RocketChatClientProtocol {
|
protocol RocketChatClientProtocol {
|
||||||
func authorizedURL(url: URL) -> URL
|
func authorizedURL(url: URL) -> URL
|
||||||
func getRooms(updatedSince: Date?) -> AnyPublisher<RoomsResponse, RocketChatError>
|
func getRooms(updatedSince: Date?) -> AnyPublisher<RoomsResponse, RocketChatError>
|
||||||
func getSubscriptions(updatedSince: Date?) -> AnyPublisher<SubscriptionsResponse, RocketChatError>
|
func getSubscriptions(updatedSince: Date?) -> AnyPublisher<SubscriptionsResponse, RocketChatError>
|
||||||
func getHistory(rid: String, t: String, latest: Date) -> AnyPublisher<HistoryResponse, RocketChatError>
|
func getHistory(rid: String, t: String, latest: Date) -> AnyPublisher<HistoryResponse, RocketChatError>
|
||||||
func syncMessages(rid: String, updatedSince: Date) -> AnyPublisher<MessagesResponse, RocketChatError>
|
func syncMessages(rid: String, updatedSince: Date) -> AnyPublisher<MessagesResponse, RocketChatError>
|
||||||
func sendMessage(id: String, rid: String, msg: String) -> AnyPublisher<SendMessageResponse, RocketChatError>
|
func sendMessage(id: String, rid: String, msg: String) -> AnyPublisher<SendMessageResponse, RocketChatError>
|
||||||
func sendRead(rid: String) -> AnyPublisher<ReadResponse, RocketChatError>
|
func sendRead(rid: String) -> AnyPublisher<ReadResponse, RocketChatError>
|
||||||
}
|
}
|
||||||
|
|
||||||
final class RocketChatClient {
|
final class RocketChatClient {
|
||||||
private let server: Server
|
private let server: Server
|
||||||
|
|
||||||
init(server: Server) {
|
init(server: Server) {
|
||||||
self.server = server
|
self.server = server
|
||||||
}
|
}
|
||||||
|
|
||||||
private var adapters: [RequestAdapter] {
|
private var adapters: [RequestAdapter] {
|
||||||
[
|
[
|
||||||
TokenAdapter(server: server),
|
TokenAdapter(server: server),
|
||||||
JSONAdapter()
|
JSONAdapter()
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
private func dataTask<T: Request>(for request: T) -> AnyPublisher<T.Response, RocketChatError> {
|
private func dataTask<T: Request>(for request: T) -> AnyPublisher<T.Response, RocketChatError> {
|
||||||
let url = server.url.appending(path: request.path).appending(queryItems: request.queryItems)
|
let url = server.url.appending(path: request.path).appending(queryItems: request.queryItems)
|
||||||
|
|
||||||
var urlRequest = adapters.reduce(URLRequest(url: url), { $1.adapt($0) })
|
var urlRequest = adapters.reduce(URLRequest(url: url), { $1.adapt($0) })
|
||||||
urlRequest.httpMethod = request.method.rawValue
|
urlRequest.httpMethod = request.method.rawValue
|
||||||
urlRequest.httpBody = request.body
|
urlRequest.httpBody = request.body
|
||||||
|
|
||||||
return URLSession.shared.dataTaskPublisher(for: urlRequest)
|
return URLSession.shared.dataTaskPublisher(for: urlRequest)
|
||||||
.tryMap { (data, response) in
|
.tryMap { (data, response) in
|
||||||
guard let httpResponse = response as? HTTPURLResponse, 200...299 ~= httpResponse.statusCode else {
|
guard let httpResponse = response as? HTTPURLResponse, 200...299 ~= httpResponse.statusCode else {
|
||||||
throw RocketChatError.unauthorized
|
throw RocketChatError.unauthorized
|
||||||
}
|
}
|
||||||
|
|
||||||
return try data.decode(T.Response.self)
|
return try data.decode(T.Response.self)
|
||||||
}
|
}
|
||||||
.mapError { error in
|
.mapError { error in
|
||||||
if let error = error as? DecodingError {
|
if let error = error as? DecodingError {
|
||||||
return .decoding(error: error)
|
return .decoding(error: error)
|
||||||
}
|
}
|
||||||
if let error = error as? RocketChatError {
|
if let error = error as? RocketChatError {
|
||||||
return error
|
return error
|
||||||
}
|
}
|
||||||
|
|
||||||
return .unknown(error: error)
|
return .unknown(error: error)
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension RocketChatClient: RocketChatClientProtocol {
|
extension RocketChatClient: RocketChatClientProtocol {
|
||||||
func authorizedURL(url: URL) -> URL {
|
func authorizedURL(url: URL) -> URL {
|
||||||
adapters.reduce(server.url.appending(path: url.relativePath), { $1.adapt($0) })
|
adapters.reduce(server.url.appending(path: url.relativePath), { $1.adapt($0) })
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRooms(updatedSince: Date?) -> AnyPublisher<RoomsResponse, RocketChatError> {
|
func getRooms(updatedSince: Date?) -> AnyPublisher<RoomsResponse, RocketChatError> {
|
||||||
let request = RoomsRequest(updatedSince: updatedSince)
|
let request = RoomsRequest(updatedSince: updatedSince)
|
||||||
return dataTask(for: request)
|
return dataTask(for: request)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSubscriptions(updatedSince: Date?) -> AnyPublisher<SubscriptionsResponse, RocketChatError> {
|
func getSubscriptions(updatedSince: Date?) -> AnyPublisher<SubscriptionsResponse, RocketChatError> {
|
||||||
let request = SubscriptionsRequest(updatedSince: updatedSince)
|
let request = SubscriptionsRequest(updatedSince: updatedSince)
|
||||||
return dataTask(for: request)
|
return dataTask(for: request)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHistory(rid: String, t: String, latest: Date) -> AnyPublisher<HistoryResponse, RocketChatError> {
|
func getHistory(rid: String, t: String, latest: Date) -> AnyPublisher<HistoryResponse, RocketChatError> {
|
||||||
let request = HistoryRequest(roomId: rid, roomType: t, latest: latest)
|
let request = HistoryRequest(roomId: rid, roomType: t, latest: latest)
|
||||||
return dataTask(for: request)
|
return dataTask(for: request)
|
||||||
}
|
}
|
||||||
|
|
||||||
func syncMessages(rid: String, updatedSince: Date) -> AnyPublisher<MessagesResponse, RocketChatError> {
|
func syncMessages(rid: String, updatedSince: Date) -> AnyPublisher<MessagesResponse, RocketChatError> {
|
||||||
let request = MessagesRequest(lastUpdate: updatedSince, roomId: rid)
|
let request = MessagesRequest(lastUpdate: updatedSince, roomId: rid)
|
||||||
return dataTask(for: request)
|
return dataTask(for: request)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendMessage(id: String, rid: String, msg: String) -> AnyPublisher<SendMessageResponse, RocketChatError> {
|
func sendMessage(id: String, rid: String, msg: String) -> AnyPublisher<SendMessageResponse, RocketChatError> {
|
||||||
let request = SendMessageRequest(id: id, rid: rid, msg: msg)
|
let request = SendMessageRequest(id: id, rid: rid, msg: msg)
|
||||||
return dataTask(for: request)
|
return dataTask(for: request)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendRead(rid: String) -> AnyPublisher<ReadResponse, RocketChatError> {
|
func sendRead(rid: String) -> AnyPublisher<ReadResponse, RocketChatError> {
|
||||||
let request = ReadRequest(rid: rid)
|
let request = ReadRequest(rid: rid)
|
||||||
return dataTask(for: request)
|
return dataTask(for: request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum RocketChatError: Error {
|
enum RocketChatError: Error {
|
||||||
case decoding(error: Error)
|
case decoding(error: Error)
|
||||||
case unknown(error: Error)
|
case unknown(error: Error)
|
||||||
case unauthorized
|
case unauthorized
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
import Foundation
|
|
||||||
|
|
||||||
struct RocketChatServer {
|
|
||||||
let url: URL
|
|
||||||
}
|
|
||||||
|
|
||||||
extension RocketChatServer {
|
|
||||||
static var `default`: Self {
|
|
||||||
.init(url: .server("https://open.rocket.chat"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate extension URL {
|
|
||||||
static func server(_ string: String) -> URL {
|
|
||||||
guard let url = URL(string: string) else {
|
|
||||||
fatalError("Could not initialize an url from \(string).")
|
|
||||||
}
|
|
||||||
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -38,7 +38,7 @@ struct MessageListView: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollViewReader { reader in
|
ScrollViewReader { reader in
|
||||||
ScrollView {
|
ScrollView {
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
LazyVStack(alignment: .leading, spacing: 8) {
|
||||||
if room.hasMoreMessages {
|
if room.hasMoreMessages {
|
||||||
Button("Load More...") {
|
Button("Load More...") {
|
||||||
guard let oldestMessage = room.firstMessage?.ts else { return }
|
guard let oldestMessage = room.firstMessage?.ts else { return }
|
||||||
|
|
|
@ -59,7 +59,6 @@
|
||||||
1E29A3032B585B070093C03C /* FailableDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E92B585B070093C03C /* FailableDecodable.swift */; };
|
1E29A3032B585B070093C03C /* FailableDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E92B585B070093C03C /* FailableDecodable.swift */; };
|
||||||
1E29A3042B585B070093C03C /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2EB2B585B070093C03C /* HTTPMethod.swift */; };
|
1E29A3042B585B070093C03C /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2EB2B585B070093C03C /* HTTPMethod.swift */; };
|
||||||
1E29A3052B585B070093C03C /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2EC2B585B070093C03C /* Request.swift */; };
|
1E29A3052B585B070093C03C /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2EC2B585B070093C03C /* Request.swift */; };
|
||||||
1E29A3062B585B070093C03C /* RocketChatServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2ED2B585B070093C03C /* RocketChatServer.swift */; };
|
|
||||||
1E29A3072B585B070093C03C /* RocketChatError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2EE2B585B070093C03C /* RocketChatError.swift */; };
|
1E29A3072B585B070093C03C /* RocketChatError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2EE2B585B070093C03C /* RocketChatError.swift */; };
|
||||||
1E29A30A2B585B370093C03C /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A3092B585B370093C03C /* Data+Extensions.swift */; };
|
1E29A30A2B585B370093C03C /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A3092B585B370093C03C /* Data+Extensions.swift */; };
|
||||||
1E29A30C2B585D1D0093C03C /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A30B2B585D1D0093C03C /* String+Extensions.swift */; };
|
1E29A30C2B585D1D0093C03C /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A30B2B585D1D0093C03C /* String+Extensions.swift */; };
|
||||||
|
@ -367,7 +366,6 @@
|
||||||
1E29A2E92B585B070093C03C /* FailableDecodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FailableDecodable.swift; sourceTree = "<group>"; };
|
1E29A2E92B585B070093C03C /* FailableDecodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FailableDecodable.swift; sourceTree = "<group>"; };
|
||||||
1E29A2EB2B585B070093C03C /* HTTPMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = "<group>"; };
|
1E29A2EB2B585B070093C03C /* HTTPMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = "<group>"; };
|
||||||
1E29A2EC2B585B070093C03C /* Request.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = "<group>"; };
|
1E29A2EC2B585B070093C03C /* Request.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = "<group>"; };
|
||||||
1E29A2ED2B585B070093C03C /* RocketChatServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RocketChatServer.swift; sourceTree = "<group>"; };
|
|
||||||
1E29A2EE2B585B070093C03C /* RocketChatError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RocketChatError.swift; sourceTree = "<group>"; };
|
1E29A2EE2B585B070093C03C /* RocketChatError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RocketChatError.swift; sourceTree = "<group>"; };
|
||||||
1E29A3092B585B370093C03C /* Data+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Extensions.swift"; sourceTree = "<group>"; };
|
1E29A3092B585B370093C03C /* Data+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
1E29A30B2B585D1D0093C03C /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = "<group>"; };
|
1E29A30B2B585D1D0093C03C /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -594,7 +592,6 @@
|
||||||
1E29A2E82B585B070093C03C /* DateCodingStrategy.swift */,
|
1E29A2E82B585B070093C03C /* DateCodingStrategy.swift */,
|
||||||
1E29A2E92B585B070093C03C /* FailableDecodable.swift */,
|
1E29A2E92B585B070093C03C /* FailableDecodable.swift */,
|
||||||
1E29A2EA2B585B070093C03C /* HTTP */,
|
1E29A2EA2B585B070093C03C /* HTTP */,
|
||||||
1E29A2ED2B585B070093C03C /* RocketChatServer.swift */,
|
|
||||||
1E29A2EE2B585B070093C03C /* RocketChatError.swift */,
|
1E29A2EE2B585B070093C03C /* RocketChatError.swift */,
|
||||||
);
|
);
|
||||||
path = Client;
|
path = Client;
|
||||||
|
@ -1863,7 +1860,6 @@
|
||||||
1E29A3032B585B070093C03C /* FailableDecodable.swift in Sources */,
|
1E29A3032B585B070093C03C /* FailableDecodable.swift in Sources */,
|
||||||
1E29A2FE2B585B070093C03C /* ReadRequest.swift in Sources */,
|
1E29A2FE2B585B070093C03C /* ReadRequest.swift in Sources */,
|
||||||
1E9A71692B59B6E100477BA2 /* MessageSender.swift in Sources */,
|
1E9A71692B59B6E100477BA2 /* MessageSender.swift in Sources */,
|
||||||
1E29A3062B585B070093C03C /* RocketChatServer.swift in Sources */,
|
|
||||||
1E29A3072B585B070093C03C /* RocketChatError.swift in Sources */,
|
1E29A3072B585B070093C03C /* RocketChatError.swift in Sources */,
|
||||||
1E9A71672B599E6300477BA2 /* NotificationController.swift in Sources */,
|
1E9A71672B599E6300477BA2 /* NotificationController.swift in Sources */,
|
||||||
1EDFD1082B58AA77002FEE5F /* RoomsLoader.swift in Sources */,
|
1EDFD1082B58AA77002FEE5F /* RoomsLoader.swift in Sources */,
|
||||||
|
|
Loading…
Reference in New Issue