feat: Begins view controller integration into app target.

This commit is contained in:
2025-01-24 09:37:54 -05:00
parent 94f0c660ff
commit ce9cbe168e
3 changed files with 66 additions and 11 deletions

View File

@@ -37,7 +37,8 @@ let package = Package(
.executableTarget(
name: "App",
dependencies: [
"DatabaseClientLive",
.target(name: "DatabaseClientLive"),
.target(name: "ViewControllerLive"),
.product(name: "Fluent", package: "fluent"),
.product(name: "FluentSQLiteDriver", package: "fluent-sqlite-driver"),
.product(name: "Vapor", package: "vapor"),

View File

@@ -1,11 +1,14 @@
import DatabaseClientLive
import Dependencies
import Elementary
import Fluent
import FluentSQLiteDriver
import NIOSSL
import SharedModels
import Vapor
import VaporElementary
@preconcurrency import VaporRouting
import ViewControllerLive
// configures your application
public func configure(
@@ -95,3 +98,52 @@ func siteHandler(
return try await route.handle(request: request)
}
}
extension ViewRoute {
func respond(request: Request) async throws -> any AsyncResponseEncodable {
if self == .index {
return request.redirect(to: ViewRoute.router.path(for: .purchaseOrder(.index)))
} else {
let html = try await view(isHtmxRequest: request.isHtmxRequest, authenticate: { request.auth.login($0) })
// Delete routes return nil, but are valid routes.
guard let html else {
return HTTPStatus.ok
}
return AnyHTMLResponse(value: html)
}
}
}
struct AnyHTMLResponse: AsyncResponseEncodable {
public var chunkSize: Int
public var headers: HTTPHeaders = ["Content-Type": "text/html; charset=utf-8"]
var value: _SendableAnyHTMLBox
init(chunkSize: Int = 1024, additionalHeaders: HTTPHeaders = [:], value: any HTML & Sendable) {
self.chunkSize = chunkSize
if additionalHeaders.contains(name: .contentType) {
self.headers = additionalHeaders
} else {
headers.add(contentsOf: additionalHeaders)
}
self.value = .init(value)
}
func encodeResponse(for request: Request) async throws -> Response {
Response(
status: .ok,
headers: headers,
body: .init(asyncStream: { [value, chunkSize] writer in
guard let html = value.tryTake() else {
assertionFailure("Non-sendable HTML value consumed more than once")
request.logger.error("Non-sendable HTML value consumed more than once")
throw Abort(.internalServerError)
}
try await writer.writeHTML(html, chunkSize: chunkSize)
try await writer.write(.end)
})
)
}
}

View File

@@ -4,9 +4,11 @@ import Elementary
import SharedModels
import Vapor
extension SharedModels.ViewRoute {
public typealias AnySendableHTML = (any HTML & Sendable)
func view(isHtmxRequest: Bool, authenticate: @escaping @Sendable (User) -> Void) async throws -> (any HTML)? {
public extension SharedModels.ViewRoute {
func view(isHtmxRequest: Bool, authenticate: @escaping @Sendable (User) -> Void) async throws -> AnySendableHTML? {
@Dependency(\.database.users) var users
switch self {
case .index:
@@ -60,7 +62,7 @@ extension SharedModels.ViewRoute.EmployeeRoute {
}
}
func view(isHtmxRequest: Bool) async throws -> (any HTML)? {
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML? {
@Dependency(\.database.employees) var employees
switch self {
@@ -110,7 +112,7 @@ extension SharedModels.ViewRoute.PurchaseOrderRoute {
}
}
func view(isHtmxRequest: Bool) async throws -> (any HTML)? {
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML? {
@Dependency(\.database.purchaseOrders) var purchaseOrders
switch self {
@@ -163,7 +165,7 @@ extension SharedModels.ViewRoute.PurchaseOrderRoute.Search {
}
}
func view(isHtmxRequest: Bool) async throws -> any HTML {
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML {
@Dependency(\.database) var database
switch self {
case let .index(context: context, table: table):
@@ -193,7 +195,7 @@ extension SharedModels.ViewRoute.UserRoute {
}
}
func view(isHtmxRequest: Bool) async throws -> (any HTML)? {
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML? {
@Dependency(\.database.users) var users
switch self {
@@ -235,7 +237,7 @@ extension SharedModels.ViewRoute.VendorRoute {
}
}
func view(isHtmxRequest: Bool) async throws -> (any HTML)? {
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML? {
@Dependency(\.database) var database
switch self {
@@ -277,7 +279,7 @@ extension SharedModels.ViewRoute.VendorRoute {
extension SharedModels.ViewRoute.VendorBranchRoute {
func view(isHtmxRequest: Bool) async throws -> (any HTML)? {
func view(isHtmxRequest: Bool) async throws -> AnySendableHTML? {
@Dependency(\.database) var database
switch self {
@@ -350,7 +352,7 @@ private func render<C: HTML>(
_ mainPage: (C) async throws -> any SendableHTMLDocument,
_ isHtmxRequest: Bool,
@HTMLBuilder html: () -> C
) async rethrows -> any HTML {
) async rethrows -> AnySendableHTML where C: Sendable {
guard isHtmxRequest else {
return try await mainPage(html())
}
@@ -361,6 +363,6 @@ private func render<C: HTML>(
_ mainPage: (C) async throws -> any SendableHTMLDocument,
_ isHtmxRequest: Bool,
_ html: @autoclosure @escaping () -> C
) async rethrows -> any HTML {
) async rethrows -> AnySendableHTML where C: Sendable {
try await render(mainPage, isHtmxRequest) { html() }
}