Initial social.io Swift app
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
import Foundation
|
||||
|
||||
enum AppNavigationCommand: Equatable {
|
||||
case mailbox(mailbox: Mailbox, search: String?, unreadOnly: Bool?)
|
||||
case thread(
|
||||
threadRouteID: String,
|
||||
mailbox: Mailbox?,
|
||||
messageRouteID: String?,
|
||||
search: String?,
|
||||
unreadOnly: Bool?
|
||||
)
|
||||
case compose(draft: ComposeDraft)
|
||||
|
||||
static let routeEnvironmentKey = "SOCIALIO_ROUTE"
|
||||
static let jsonEnvironmentKey = "SOCIALIO_COMMAND_JSON"
|
||||
|
||||
static func from(environment: [String: String]) -> AppNavigationCommand? {
|
||||
if let json = environment[jsonEnvironmentKey] {
|
||||
return from(json: json)
|
||||
}
|
||||
|
||||
if let route = environment[routeEnvironmentKey] {
|
||||
return parse(route)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
static func parse(_ rawValue: String) -> AppNavigationCommand? {
|
||||
let trimmed = rawValue.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmed.isEmpty else { return nil }
|
||||
|
||||
if trimmed.hasPrefix("{") {
|
||||
return from(json: trimmed)
|
||||
}
|
||||
|
||||
guard let url = URL(string: trimmed) else { return nil }
|
||||
return from(url: url)
|
||||
}
|
||||
|
||||
static func from(url: URL) -> AppNavigationCommand? {
|
||||
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let host = (components.host ?? "").lowercased()
|
||||
let pathComponents = url.path
|
||||
.split(separator: "/")
|
||||
.map(String.init)
|
||||
|
||||
let queryItems = components.queryItems ?? []
|
||||
let mailbox = queryItems.value(named: "mailbox").flatMap(Mailbox.init(rawValue:))
|
||||
let threadRouteID = queryItems.value(named: "thread")
|
||||
let messageRouteID = queryItems.value(named: "message")
|
||||
let search = queryItems.value(named: "search")
|
||||
let unreadOnly = queryItems.value(named: "unreadOnly").flatMap(Bool.init)
|
||||
|
||||
switch host {
|
||||
case "mailbox":
|
||||
guard let mailboxID = pathComponents.first, let mailbox = Mailbox(rawValue: mailboxID) else {
|
||||
return nil
|
||||
}
|
||||
return .mailbox(mailbox: mailbox, search: search, unreadOnly: unreadOnly)
|
||||
|
||||
case "thread":
|
||||
guard let routeID = pathComponents.first else { return nil }
|
||||
return .thread(
|
||||
threadRouteID: routeID,
|
||||
mailbox: mailbox,
|
||||
messageRouteID: messageRouteID,
|
||||
search: search,
|
||||
unreadOnly: unreadOnly
|
||||
)
|
||||
|
||||
case "compose":
|
||||
return .compose(
|
||||
draft: ComposeDraft(
|
||||
to: queryItems.value(named: "to") ?? "",
|
||||
subject: queryItems.value(named: "subject") ?? "",
|
||||
body: queryItems.value(named: "body") ?? ""
|
||||
)
|
||||
)
|
||||
|
||||
case "open", "":
|
||||
if let threadRouteID {
|
||||
return .thread(
|
||||
threadRouteID: threadRouteID,
|
||||
mailbox: mailbox,
|
||||
messageRouteID: messageRouteID,
|
||||
search: search,
|
||||
unreadOnly: unreadOnly
|
||||
)
|
||||
}
|
||||
|
||||
if let mailbox {
|
||||
return .mailbox(mailbox: mailbox, search: search, unreadOnly: unreadOnly)
|
||||
}
|
||||
|
||||
if queryItems.value(named: "to") != nil ||
|
||||
queryItems.value(named: "subject") != nil ||
|
||||
queryItems.value(named: "body") != nil {
|
||||
return .compose(
|
||||
draft: ComposeDraft(
|
||||
to: queryItems.value(named: "to") ?? "",
|
||||
subject: queryItems.value(named: "subject") ?? "",
|
||||
body: queryItems.value(named: "body") ?? ""
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
static func from(json: String) -> AppNavigationCommand? {
|
||||
guard let data = json.data(using: .utf8) else { return nil }
|
||||
let decoder = JSONDecoder()
|
||||
|
||||
do {
|
||||
let payload = try decoder.decode(AppNavigationPayload.self, from: data)
|
||||
return payload.command
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct AppNavigationPayload: Decodable {
|
||||
enum Kind: String, Decodable {
|
||||
case mailbox
|
||||
case thread
|
||||
case compose
|
||||
}
|
||||
|
||||
let kind: Kind?
|
||||
let mailbox: Mailbox?
|
||||
let threadID: String?
|
||||
let messageID: String?
|
||||
let search: String?
|
||||
let unreadOnly: Bool?
|
||||
let to: String?
|
||||
let subject: String?
|
||||
let body: String?
|
||||
|
||||
var command: AppNavigationCommand? {
|
||||
switch kind {
|
||||
case .mailbox:
|
||||
guard let mailbox else { return nil }
|
||||
return .mailbox(mailbox: mailbox, search: search, unreadOnly: unreadOnly)
|
||||
|
||||
case .thread:
|
||||
guard let threadID else { return nil }
|
||||
return .thread(
|
||||
threadRouteID: threadID,
|
||||
mailbox: mailbox,
|
||||
messageRouteID: messageID,
|
||||
search: search,
|
||||
unreadOnly: unreadOnly
|
||||
)
|
||||
|
||||
case .compose:
|
||||
return .compose(
|
||||
draft: ComposeDraft(
|
||||
to: to ?? "",
|
||||
subject: subject ?? "",
|
||||
body: body ?? ""
|
||||
)
|
||||
)
|
||||
|
||||
case nil:
|
||||
if let threadID {
|
||||
return .thread(
|
||||
threadRouteID: threadID,
|
||||
mailbox: mailbox,
|
||||
messageRouteID: messageID,
|
||||
search: search,
|
||||
unreadOnly: unreadOnly
|
||||
)
|
||||
}
|
||||
|
||||
if let mailbox {
|
||||
return .mailbox(mailbox: mailbox, search: search, unreadOnly: unreadOnly)
|
||||
}
|
||||
|
||||
if to != nil || subject != nil || body != nil {
|
||||
return .compose(
|
||||
draft: ComposeDraft(
|
||||
to: to ?? "",
|
||||
subject: subject ?? "",
|
||||
body: body ?? ""
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension [URLQueryItem] {
|
||||
func value(named name: String) -> String? {
|
||||
first(where: { $0.name == name })?.value
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user