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