diff --git a/Resources/Views/Home/_info.leaf b/Resources/Views/Home/_info.leaf index b4ff9731..bd794f24 100644 --- a/Resources/Views/Home/_info.leaf +++ b/Resources/Views/Home/_info.leaf @@ -3,11 +3,17 @@
-

About

+

#if(about):#(about.title)#else:About#endif

+ #if(about): +

Founded by #(about.founderName) in #(about.foundedYear), SwiftLeeds has set out to bring a modern, inclusive conference to the north of the UK.

+

Ran by just a handful of community volunteers, SwiftLeeds is entirely non-profit with every penny going into delivering the best experience possible.

+

In-person conferences are the best way to meet like-minded people who enjoy building apps with Swift. You can also learn from the best people in the industry and chat about all things Swift.

+ #else:

Founded by Adam Rush in 2019, SwiftLeeds has set out to bring a modern, inclusive conference to the north of the UK.

Ran by just a handful of community volunteers, SwiftLeeds is entirely non-profit with every penny going into delivering the best experience possible.

In-person conferences are the best way to meet like-minded people who enjoy building apps with Swift. You can also learn from the best people in the industry and chat about all things Swift.

+ #endif
diff --git a/Resources/Views/Shared/_footer.leaf b/Resources/Views/Shared/_footer.leaf index f9433e81..f1539ee2 100644 --- a/Resources/Views/Shared/_footer.leaf +++ b/Resources/Views/Shared/_footer.leaf @@ -27,6 +27,15 @@
+ #if(social): + #for(link in social.socialLinks): + #if(link.id != "spotify"): + + + + #endif + #endfor + #else: @@ -42,6 +51,7 @@ + #endif
diff --git a/Resources/Views/Shared/_header.leaf b/Resources/Views/Shared/_header.leaf index 7e48e4fb..fe240550 100644 --- a/Resources/Views/Shared/_header.leaf +++ b/Resources/Views/Shared/_header.leaf @@ -41,6 +41,15 @@ + #if(social): + #for(link in social.socialLinks): + + #endfor + #else: + #endif
#if(false): diff --git a/Sources/App/Context/HomeContext.swift b/Sources/App/Context/HomeContext.swift index 186b0a55..9ef27d70 100644 --- a/Sources/App/Context/HomeContext.swift +++ b/Sources/App/Context/HomeContext.swift @@ -18,6 +18,8 @@ struct HomeContext: Content { var schedule: [ScheduleDay] = [] var phase: PhaseContext? = nil var event: EventContext? = nil + var about: AboutResponse? = nil + var social: SocialResponse? = nil } struct EventContext: Codable { diff --git a/Sources/App/Features/About/Controllers/AboutAPIController.swift b/Sources/App/Features/About/Controllers/AboutAPIController.swift new file mode 100644 index 00000000..0b831ded --- /dev/null +++ b/Sources/App/Features/About/Controllers/AboutAPIController.swift @@ -0,0 +1,27 @@ +import Vapor + +struct AboutAPIController: RouteCollection { + func boot(routes: RoutesBuilder) throws { + routes.get(use: onGet) + } + + @Sendable func onGet(request: Request) async throws -> Response { + let aboutData = AboutResponse( + title: "About", + description: [ + "Founded by Adam Rush in 2019, SwiftLeeds has set out to bring a modern, inclusive conference to the north of the UK.", + "Ran by just a handful of community volunteers, SwiftLeeds is entirely non-profit with every penny going into delivering the best experience possible.", + "In-person conferences are the best way to meet like-minded people who enjoy building apps with Swift. You can also learn from the best people in the industry and chat about all things Swift." + ], + foundedYear: "2019", + founderName: "Adam Rush", + founderTwitter: "https://twitter.com/Adam9Rush" + ) + + let response = GenericResponse( + data: aboutData + ) + + return try await response.encodeResponse(for: request) + } +} diff --git a/Sources/App/Features/About/Models/AboutResponse.swift b/Sources/App/Features/About/Models/AboutResponse.swift new file mode 100644 index 00000000..f99a3f66 --- /dev/null +++ b/Sources/App/Features/About/Models/AboutResponse.swift @@ -0,0 +1,10 @@ +import Foundation +import Vapor + +struct AboutResponse: Content { + let title: String + let description: [String] + let foundedYear: String + let founderName: String + let founderTwitter: String +} diff --git a/Sources/App/Features/Home/HomeRouteController.swift b/Sources/App/Features/Home/HomeRouteController.swift index 18306425..c2c833a0 100644 --- a/Sources/App/Features/Home/HomeRouteController.swift +++ b/Sources/App/Features/Home/HomeRouteController.swift @@ -93,6 +93,10 @@ struct HomeRouteController: RouteCollection { let phase = try getPhase(req: req, event: event) + // Fetch About and Social data from API endpoints to maintain single source of truth + let aboutData: AboutResponse? = try await fetchAboutData(req: req) + let socialData: SocialResponse? = try await fetchSocialData(req: req) + let schedule = event.days .map { day in ScheduleDay( @@ -118,7 +122,9 @@ struct HomeRouteController: RouteCollection { dropInSessions: dropInSessions, schedule: phase.showSchedule ? schedule : [], phase: PhaseContext(phase: phase, event: event), - event: eventContext + event: eventContext, + about: aboutData, + social: socialData ) } @@ -188,6 +194,38 @@ struct HomeRouteController: RouteCollection { return try await Event.getCurrent(on: req.db) } + + private func fetchAboutData(req: Request) async throws -> AboutResponse? { + do { + // Make internal request to the About API endpoint + let aboutRequest = Request(application: req.application, method: .GET, url: URI(string: "/api/v2/about"), on: req.eventLoop) + let aboutController = AboutAPIController() + let aboutResponse = try await aboutController.onGet(request: aboutRequest) + + guard let data = aboutResponse.body.data else { return nil } + let genericResponse = try JSONDecoder().decode(GenericResponse.self, from: data) + return genericResponse.data + } catch { + req.logger.warning("Failed to fetch about data from API: \(error)") + return nil + } + } + + private func fetchSocialData(req: Request) async throws -> SocialResponse? { + do { + // Make internal request to the Social API endpoint + let socialRequest = Request(application: req.application, method: .GET, url: URI(string: "/api/v2/social"), on: req.eventLoop) + let socialController = SocialAPIController() + let socialResponse = try await socialController.onGet(request: socialRequest) + + guard let data = socialResponse.body.data else { return nil } + let genericResponse = try JSONDecoder().decode(GenericResponse.self, from: data) + return genericResponse.data + } catch { + req.logger.warning("Failed to fetch social data from API: \(error)") + return nil + } + } } struct Phase { diff --git a/Sources/App/Features/Social/Controllers/SocialAPIController.swift b/Sources/App/Features/Social/Controllers/SocialAPIController.swift new file mode 100644 index 00000000..064436d3 --- /dev/null +++ b/Sources/App/Features/Social/Controllers/SocialAPIController.swift @@ -0,0 +1,24 @@ +import Vapor + +struct SocialAPIController: RouteCollection { + func boot(routes: RoutesBuilder) throws { + routes.get(use: onGet) + } + + @Sendable func onGet(request: Request) async throws -> Response { + let socialData = SocialResponse(socialLinks: [ + SocialLink(id: "twitter", name: "Twitter", url: "https://twitter.com/swift_leeds", icon: "bx bxl-twitter", displayName: "@Swift_Leeds", order: 1), + SocialLink(id: "mastodon", name: "Mastodon", url: "https://iosdev.space/@swiftleeds", icon: "bx bxl-mastodon", displayName: "@swiftleeds", order: 2), + SocialLink(id: "youtube", name: "YouTube", url: "https://www.youtube.com/channel/UCCq1K0eWKZFBCpqaC3n8V1g", icon: "bx bxl-youtube", displayName: nil, order: 3), + SocialLink(id: "slack", name: "Join Slack", url: "https://join.slack.com/t/swiftleedsworkspace/shared_invite/zt-wkmr6pif-ZDCdDeHM60jcBUy0BxHdCQ", icon: "bx bxl-slack", displayName: nil, order: 4), + SocialLink(id: "flickr", name: "Flickr", url: "https://www.flickr.com/photos/196979204@N02/albums/72177720303878744", icon: "bx bxl-flickr", displayName: nil, order: 5), + SocialLink(id: "spotify", name: "Spotify", url: "https://open.spotify.com/show/3pHsjVt54MDDHdzZce7ezl", icon: "bx bxl-spotify", displayName: nil, order: 6) + ]) + + let response = GenericResponse( + data: socialData + ) + + return try await response.encodeResponse(for: request) + } +} diff --git a/Sources/App/Features/Social/Models/SocialResponse.swift b/Sources/App/Features/Social/Models/SocialResponse.swift new file mode 100644 index 00000000..7af158ed --- /dev/null +++ b/Sources/App/Features/Social/Models/SocialResponse.swift @@ -0,0 +1,15 @@ +import Foundation +import Vapor + +struct SocialResponse: Content { + let socialLinks: [SocialLink] +} + +struct SocialLink: Content { + let id: String + let name: String + let url: String + let icon: String + let displayName: String? + let order: Int +} diff --git a/Sources/App/routes.swift b/Sources/App/routes.swift index 1c64a130..5e47ac6e 100644 --- a/Sources/App/routes.swift +++ b/Sources/App/routes.swift @@ -57,6 +57,8 @@ func routes(_ app: Application) throws { let apiV2Routes = app.grouped("api", "v2") try apiV2Routes.grouped("schedule").register(collection: ScheduleAPIControllerV2()) + try apiV2Routes.grouped("about").register(collection: AboutAPIController()) + try apiV2Routes.grouped("social").register(collection: SocialAPIController()) // MARK: - Admin Routes let adminRoutes = app.grouped("admin").grouped(AdminMiddleware())