208 lines
6.2 KiB
Swift
208 lines
6.2 KiB
Swift
|
|
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
|
||
|
|
}
|
||
|
|
}
|