Generate pages, image descriptions
This commit is contained in:
@ -0,0 +1,19 @@
|
||||
|
||||
struct ContentPageVideo: HtmlProducer {
|
||||
|
||||
let filePath: String
|
||||
|
||||
let videoType: String
|
||||
|
||||
let options: [VideoOption]
|
||||
|
||||
func populate(_ result: inout String) {
|
||||
result += "<video\(optionString)>Video not supported."
|
||||
result += "<source src='\(filePath)' type='\(videoType)'>"
|
||||
result += "</video>"
|
||||
}
|
||||
|
||||
private var optionString: String {
|
||||
options.map { " " + $0.rawValue }.joined()
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
|
||||
struct DownloadButtons {
|
||||
|
||||
struct Item {
|
||||
|
||||
let filePath: String
|
||||
|
||||
let text: String
|
||||
|
||||
let downloadFileName: String?
|
||||
}
|
||||
|
||||
let items: [Item]
|
||||
|
||||
init(items: [Item]) {
|
||||
self.items = items
|
||||
}
|
||||
|
||||
var content: String {
|
||||
var result = "<p style='display: flex'>"
|
||||
for item in items {
|
||||
addButton(of: item, to: &result)
|
||||
}
|
||||
result += "</p>"
|
||||
return result
|
||||
}
|
||||
|
||||
private func addButton(of item: Item, to result: inout String) {
|
||||
let downloadText = item.downloadFileName.map { " download='\($0)'" } ?? ""
|
||||
result += "<a class='download-button' href='\(item.filePath)'\(downloadText)>"
|
||||
result += "\(item.text)<span class='icon icon-download'></span>"
|
||||
result += "</a>"
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
|
||||
struct HikingStatistics {
|
||||
|
||||
private let time: String?
|
||||
|
||||
private let elevationUp: String?
|
||||
|
||||
private let elevationDown: String?
|
||||
|
||||
private let distance: String?
|
||||
|
||||
private let calories: String?
|
||||
|
||||
init(time: String?, elevationUp: String?, elevationDown: String?, distance: String?, calories: String?) {
|
||||
self.time = time
|
||||
self.elevationUp = elevationUp
|
||||
self.elevationDown = elevationDown
|
||||
self.distance = distance
|
||||
self.calories = calories
|
||||
}
|
||||
|
||||
var content: String {
|
||||
var result = "<div class='stats-container'>"
|
||||
if let time {
|
||||
result += "<div><svg><use href='#icon-clock'></use></svg>\(time)</div>"
|
||||
}
|
||||
if let elevationUp {
|
||||
result += "<div><svg><use href='#icon-arrow-up'></use></svg>\(elevationUp)</div>"
|
||||
}
|
||||
if let elevationDown {
|
||||
result += "<div><svg><use href='#icon-arrow-down'></use></svg>\(elevationDown)</div>"
|
||||
}
|
||||
if let distance {
|
||||
result += "<div><svg><use href='#icon-sign'></use></svg>\(distance)</div>"
|
||||
}
|
||||
if let calories {
|
||||
result += "<div><svg><use href='#icon-flame'></use></svg>\(calories)</div>"
|
||||
}
|
||||
result += "</div>"
|
||||
return result
|
||||
}
|
||||
}
|
29
CHDataManagement/Page Elements/ContentElements/TagList.swift
Normal file
29
CHDataManagement/Page Elements/ContentElements/TagList.swift
Normal file
@ -0,0 +1,29 @@
|
||||
protocol HtmlProducer {
|
||||
|
||||
func populate(_ result: inout String)
|
||||
}
|
||||
|
||||
extension HtmlProducer {
|
||||
|
||||
var content: String {
|
||||
var result = ""
|
||||
populate(&result)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
struct TagList: HtmlProducer {
|
||||
|
||||
let tags: [FeedEntryData.Tag]
|
||||
|
||||
func populate(_ result: inout String) {
|
||||
guard !tags.isEmpty else {
|
||||
return
|
||||
}
|
||||
result += "<div class='tags'>"
|
||||
for tag in tags {
|
||||
result += "<span class='tag' onclick=\"location.href='\(tag.url)'; event.stopPropagation();\">\(tag.name)</span>"
|
||||
}
|
||||
result += "</div>"
|
||||
}
|
||||
}
|
@ -2,15 +2,18 @@ import Foundation
|
||||
|
||||
struct FeedEntry {
|
||||
|
||||
let data: FeedEntryData
|
||||
private let data: FeedEntryData
|
||||
|
||||
init(data: FeedEntryData) {
|
||||
self.data = data
|
||||
}
|
||||
|
||||
private var cardLinkClassText: String {
|
||||
data.link != nil ? " linked-card" : ""
|
||||
}
|
||||
|
||||
var content: String {
|
||||
#warning("TODO: Select CSS classes based on existence of link (hover effects, mouse pointer")
|
||||
var result = "<div class='card'>"
|
||||
var result = "<div class='card\(cardLinkClassText)'>"
|
||||
ImageGallery(id: data.entryId, images: data.images)
|
||||
.addContent(to: &result)
|
||||
|
||||
@ -23,14 +26,8 @@ struct FeedEntry {
|
||||
if let title = data.title {
|
||||
result += "<h2>\(title.htmlEscaped())</h2>"
|
||||
}
|
||||
if !data.tags.isEmpty {
|
||||
result += "<div class='tags'>"
|
||||
for tag in data.tags {
|
||||
result += "<span class='tag' onclick=\"location.href='\(tag.url)'; event.stopPropagation();\">\(tag.name)</span>"
|
||||
//result += "<a class='tag' href='\(tag.url)'>\(tag.name)</a>"
|
||||
}
|
||||
result += "</div>"
|
||||
}
|
||||
result += TagList(tags: data.tags).content
|
||||
|
||||
for paragraph in data.text {
|
||||
result += "<p>\(paragraph)</p>"
|
||||
}
|
||||
|
@ -43,19 +43,13 @@ struct FeedEntryData {
|
||||
|
||||
struct Image {
|
||||
|
||||
let rawImagePath: String
|
||||
|
||||
let width: Int
|
||||
|
||||
let height: Int
|
||||
|
||||
let altText: String
|
||||
|
||||
let avif1x: String
|
||||
|
||||
let avif2x: String
|
||||
|
||||
let webp1x: String
|
||||
|
||||
let webp2x: String
|
||||
|
||||
let jpg1x: String
|
||||
|
||||
let jpg2x: String
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -15,16 +15,6 @@ struct ImageGallery {
|
||||
self.images = images
|
||||
}
|
||||
|
||||
private func imageCode(_ image: FeedEntryData.Image) -> String {
|
||||
//return "<img src='\(image.mainImageUrl)' loading='lazy' alt='\(image.altText.htmlEscaped())'>"
|
||||
var result = "<picture>"
|
||||
result += "<source type='image/avif' srcset='\(image.avif1x) 1x, \(image.avif2x) 2x'/>"
|
||||
result += "<source type='image/webp' srcset='\(image.webp1x) 1x, \(image.webp2x) 2x'/>"
|
||||
result += "<img srcset='\(image.jpg2x) 2x' src='\(image.jpg1x)' loading='lazy' alt='\(image.altText.htmlEscaped())'/>"
|
||||
result += "</picture>"
|
||||
return result
|
||||
}
|
||||
|
||||
func addContent(to result: inout String) {
|
||||
guard !images.isEmpty else {
|
||||
return
|
||||
@ -33,7 +23,7 @@ struct ImageGallery {
|
||||
result += "<div id='\(htmlSafeId)' class='swiper'><div class='swiper-wrapper'>"
|
||||
|
||||
guard images.count > 1 else {
|
||||
result += imageCode(images[0])
|
||||
result += WebsiteImage(image: images[0]).content
|
||||
result += "</div></div>" // Close swiper, swiper-wrapper
|
||||
return
|
||||
}
|
||||
@ -42,7 +32,7 @@ struct ImageGallery {
|
||||
// TODO: Use different images based on device
|
||||
result += "<div class='swiper-slide'>"
|
||||
|
||||
result += imageCode(image)
|
||||
result += WebsiteImage(image: image).content
|
||||
|
||||
result += "<div class='swiper-lazy-preloader swiper-lazy-preloader-white'></div>"
|
||||
|
||||
|
27
CHDataManagement/Page Elements/PageImage.swift
Normal file
27
CHDataManagement/Page Elements/PageImage.swift
Normal file
@ -0,0 +1,27 @@
|
||||
import Foundation
|
||||
|
||||
struct PageImage {
|
||||
|
||||
let imageId: String
|
||||
|
||||
let thumbnail: FeedEntryData.Image
|
||||
|
||||
let largeImage: FeedEntryData.Image
|
||||
|
||||
let caption: String?
|
||||
|
||||
var content: String {
|
||||
var result = ""
|
||||
result += "<div class='content-image' onclick=\"document.getElementById('\(imageId)').classList.add('active')\">"
|
||||
result += WebsiteImage(image: thumbnail).content
|
||||
result += "</div>"
|
||||
result += "<div id='\(imageId)' class='fullscreen-image' onclick=\"document.getElementById('\(imageId)').classList.remove('active')\">"
|
||||
result += WebsiteImage(image: largeImage).content
|
||||
if let caption {
|
||||
result += "<div class='caption'>\(caption)</div>"
|
||||
}
|
||||
result += "<div class='close'></div>"
|
||||
result += "</div>"
|
||||
return result
|
||||
}
|
||||
}
|
35
CHDataManagement/Page Elements/WebsiteImage.swift
Normal file
35
CHDataManagement/Page Elements/WebsiteImage.swift
Normal file
@ -0,0 +1,35 @@
|
||||
|
||||
struct WebsiteImage {
|
||||
|
||||
private let prefix1x: String
|
||||
|
||||
private let prefix2x: String
|
||||
|
||||
private let altText: String
|
||||
|
||||
private let ext: String
|
||||
|
||||
init(image: FeedEntryData.Image) {
|
||||
self.init(rawImagePath: image.rawImagePath,
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
altText: image.altText)
|
||||
}
|
||||
|
||||
init(rawImagePath: String, width: Int, height: Int, altText: String) {
|
||||
let (prefix, ext) = rawImagePath.fileNameAndExtension
|
||||
self.prefix1x = "\(prefix)@\(width)x\(height)"
|
||||
self.prefix2x = "\(prefix)@\(width*2)x\(height*2)"
|
||||
self.altText = altText.htmlEscaped()
|
||||
self.ext = ext ?? "jpg"
|
||||
}
|
||||
|
||||
var content: String {
|
||||
var result = "<picture>"
|
||||
result += "<source type='image/avif' srcset='\(prefix1x).avif 1x, \(prefix2x).avif 2x'/>"
|
||||
result += "<source type='image/webp' srcset='\(prefix1x).webm 1x, \(prefix1x).webm 2x'/>"
|
||||
result += "<img srcset='\(prefix2x).\(ext) 2x' src='\(prefix1x).\(ext)' loading='lazy' alt='\(altText)'/>"
|
||||
result += "</picture>"
|
||||
return result
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user