novel rework wip

This commit is contained in:
perennial
2024-10-09 18:21:24 +11:00
parent c08c8f51c5
commit d5b3aa67c0
4 changed files with 206 additions and 195 deletions
+70 -75
View File
@@ -1,82 +1,77 @@
<div class="col mt-1">
<!-- NOTE: bg-charcoal-surface1 doesn't look that nice, but the inconsistent content of the cards means that we need a way to visually group them -->
<div class="custom-novel-card bg-black p-3 py-4 d-flex flex-column thumbnail-hover-dark">
<div class="row g-3 flex-grow-1">
<div class="col-4 col-md-3">
<a href="/novel/{{ .ID }}" class="text-decoration-none">
<div class="ratio ratio-3x4">
<div class="thumbnail-wrapper rounded overflow-hidden">
<img src="{{ .CoverURL }}" alt="{{ .Title }}" class="img-fluid object-fit-cover w-100 h-100" loading="lazy" />
</div>
</div>
</a>
<!-- NOTE: bg-charcoal-surface1 doesn't look that nice, but the inconsistent content of the cards means that we need a way to visually group them -->
<div class="row">
<div class="col-4 col-md-3">
<a href="/novel/{{ .ID }}" class="text-decoration-none">
<div class="ratio ratio-3x4">
<div class="thumbnail-wrapper rounded overflow-hidden">
<img src="{{ .CoverURL }}" alt="{{ .Title }}" class="img-fluid object-fit-cover w-100 h-100" loading="lazy" />
</div>
</div>
<div class="col-8 col-md-9 d-flex flex-column">
<!-- Novel title -->
<h4 class="text-truncate">
<a href="/novel/{{ .ID }}" class="text-body text-decoration-none">{{ .Title }}</a>
</h4>
</a>
</div>
<div class="col-8 col-md-9">
<!-- Novel title -->
<h4 class="text-truncate">
<a href="/novel/{{ .ID }}" class="text-body text-decoration-none">{{ .Title }}</a>
</h4>
<!-- Series name -->
{{- if .SeriesID }}
<p class="text-truncate mb-1">
<a href="/novel/series/{{ .SeriesID }}" class="small text-body-secondary text-decoration-none">{{ .SeriesTitle }}</a>
</p>
<!-- Series name -->
{{- if .SeriesID }}
<p class="text-truncate mb-1">
<a href="/novel/series/{{ .SeriesID }}" class="small text-body-secondary text-decoration-none">{{ .SeriesTitle }}</a>
</p>
{{- end }}
<!-- Word count and reading time -->
<p class="card-text text-muted small mb-2"><span>{{ .WordCount }} word(s)</span>&nbsp;<span>{{ floor: .ReadingTime / 60 }} mins</span></p>
<!-- Author info -->
<a href="/users/{{ .UserID }}" class="text-decoration-none d-flex align-items-center me-auto mb-3">
<img src="{{ .UserAvatar }}" alt="{{ .UserName }}" class="rounded-circle object-fit-cover me-2" style="width: 24px; height: 24px" />
<span class="text-body-secondary">{{ .UserName }}</span>
</a>
{*
<!-- Description -->
<!-- TODO: figure this out -->
<!-- <p class="text-truncate mb-1">
{{ raw: .Description }}
</p> -->
*}
<!-- Tags -->
<div class="d-flex flex-wrap align-items-center mb-2">
{{- if .AiType == 2 }}
<span class="badge bg-warning text-dark me-2 mb-2">AI-generated</span>
{{- end }}
{{- range .Tags }}
{* NOTE: early block to catch the R-18 tag *}
{{- if isEmphasize(.) }}
<span class="badge bg-danger me-2 mb-2">{{ . }}</span>
{{- end }}
{{- end }}
{{- if .IsOriginal }}
<span class="badge bg-primary me-2 mb-2">Original</span>
{{- end }}
{{- if .Genre != "0" }}
<span class="badge bg-secondary me-2 mb-2">{{ novelGenre: .Genre }}</span>
{{- end }}
{{- range .Tags }}
{{- if isEmphasize(.) }}
{* Intentionally empty to not render anything to avoid duplication *}
{{- else }}
<span class="badge fw-normal text-muted me-2 mb-2 p-0 text-wrap text-break text-start">
<a href="/tags/{{ escapeString(.) }}" class="text-decoration-none text-wrap text-break">#{{ . }}</a>
</span>
{{- end }}
{{- end }}
</div>
<!-- Word count and reading time -->
<p class="card-text text-muted small mb-2"><span>{{ .WordCount }} word(s)</span>&nbsp;<span>{{ floor: .ReadingTime / 60 }} mins</span></p>
<!-- Author info -->
<a href="/users/{{ .UserID }}" class="text-decoration-none d-flex align-items-center me-auto mb-3">
<img src="{{ .UserAvatar }}" alt="{{ .UserName }}" class="rounded-circle object-fit-cover me-2" style="width: 24px; height: 24px" />
<span class="text-body-secondary">{{ .UserName }}</span>
</a>
{*
<!-- Description -->
<!-- TODO: figure this out -->
<!-- <p class="text-truncate mb-1">
{{ raw: .Description }}
</p> -->
*}
<!-- Tags -->
<div class="d-flex flex-wrap align-items-center mb-2">
{{- if .AiType == 2 }}
<span class="badge bg-warning text-dark me-2 mb-2">AI-generated</span>
{{- end }}
{{- range .Tags }}
{* NOTE: early block to catch the R-18 tag *}
{{- if isEmphasize(.) }}
<span class="badge bg-danger me-2 mb-2">{{ . }}</span>
{{- end }}
{{- end }}
{{- if .IsOriginal }}
<span class="badge bg-primary me-2 mb-2">Original</span>
{{- end }}
{{- if .Genre != "0" }}
<span class="badge bg-secondary me-2 mb-2">{{ novelGenre: .Genre }}</span>
{{- end }}
{{- range .Tags }}
{{- if isEmphasize(.) }}
{* Intentionally empty to not render anything to avoid duplication *}
{{- else }}
<span class="badge fw-normal text-muted me-2 mb-2 p-0 text-wrap text-break text-start">
<a href="/tags/{{ escapeString(.) }}" class="text-decoration-none text-wrap text-break">#{{ . }}</a>
</span>
{{- end }}
{{- end }}
</div>
<!-- Metadata (bookmarks and creation date) -->
<div class="d-flex justify-content-start align-items-center">
<span class="text-body-secondary small me-3"> <i class="bi bi-heart-fill me-2"></i>{{ .Bookmarks }} </span>
<span class="text-body-secondary small"> <i class="bi bi-calendar me-2"></i>{{ parseTime: .CreateDate }} </span>
</div>
</div>
<!-- Metadata (bookmarks and creation date) -->
<div class="d-flex justify-content-start align-items-center">
<span class="text-body-secondary small me-3"> <i class="bi bi-heart-fill me-2"></i>{{ .Bookmarks }} </span>
<span class="text-body-secondary small"> <i class="bi bi-calendar me-2"></i>{{ parseTime: .CreateDate }} </span>
</div>
</div>
<!-- TODO: don't display a horizontal rule for the last two novels on a page -->
<hr class="my-0">
</div>
<!-- TODO: don't display a horizontal rule for the last two novels on a page -->
+123 -117
View File
@@ -30,7 +30,7 @@
<!-- Author -->
<div class="d-flex align-items-center mb-2">
<a href="/users/{{ .User.ID }}" class="text-decoration-none">
<a href="/users/{{ .User.ID }}/novels#checkpoint" class="text-decoration-none">
<img src="{{ .User.Avatar }}" alt="{{ .User.Name }}" class="rounded-circle me-2" style="width: 30px; height: 30px" />
</a>
<a href="/users/{{ .User.ID }}" class="text-decoration-none">
@@ -204,7 +204,7 @@
<div class="custom-card">
<div class="custom-card-body bg-charcoal-surface1 py-4 px-0">
<!-- NOTE: mx-0 because the row element overflows the card horizontally without it -->
<div class="row mx-0">
<div class="row justify-content-center mx-0">
<!-- NOTE: px-0 to undo the extra spacing caused by mx-0 -->
<div class="col-12 px-0">
<div class="d-flex flex-column align-items-center position-relative mx-4 mb-4 justify-content-center">
@@ -244,128 +244,129 @@
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Series navigation, if present -->
{{- if .Novel.SeriesNavData }}
<div class="col-12 col-lg-9">
<div class="custom-card">
<div class="custom-card-body bg-charcoal-surface1 p-4">
<h2 class="text-center mb-2">
<a href="/novel/series/{{ .Novel.SeriesNavData.SeriesID }}" class="text-body text-decoration-none">
{{- .Novel.SeriesNavData.Title }}
</a>
</h2>
<h3 class="text-body-secondary text-center">
<a href="/novel/series/{{ .Novel.SeriesNavData.SeriesID }}" class="text-body-secondary text-decoration-none">
<i class="bi bi-stack me-2"></i>{{ len(.NovelSeriesContentTitles) }}
{{- if len(.NovelSeriesContentTitles) == 1 }}
work in this series
{{- else }}
works in this series
{{- end }}
</a>
</h3>
<hr class="my-4">
<div class="row row-col-auto nav nav-underline justify-content-center pb-2 mx-1">
{{ activeState := .Novel.ID }}
{{ yield NovelNav(
baseURL="/novel/",
paths=.NovelSeriesIDs,
names=.NovelSeriesTitles,
activeState=activeState
) }}
</div>
</div>
</div>
</div>
{{- end }}
<div class="col-12 col-lg-9">
<div class="row">
<!-- Author information -->
<div class="col-12 col-lg-6">
<div class="custom-card">
<div class="custom-card-body bg-charcoal-surface1 py-3 px-4">
<div class="d-flex align-items-center">
<a href="/users/{{ .User.ID }}" class="me-3">
<img src="{{ .User.Avatar }}" alt="{{ .User.Name }}" class="rounded-circle" style="width: 64px; height: 64px;" />
</a>
<div>
<h3 class="mb-0"><a href="/users/{{ .User.ID }}" class="text-decoration-none">{{ .User.Name }}</a></h3>
</div>
</div>
</div>
</div>
</div>
<!-- Comments section -->
<!--
TODO: currently duplicated unnecessarily across artwork.jet.html and novel.jet.html
Need to extract this out to a block that takes parameters
-->
<div class="col-12 col-lg-6">
<div class="custom-card">
<div class="custom-card-body bg-charcoal-surface1 p-4 pb-0">
{{- if .Novel.CommentOff == 1 || .Novel.CommentCount == 0 }}
<h2>0 Comments</h2>
{{- else if .Novel.CommentCount == 1 }}
<h2>1 Comment</h2>
{{- else }}
<h2>{{ .Novel.CommentCount }} Comments</h2>
{{- end }}
{{- if .Novel.CommentOff == 1 }}
<p class="mb-4">The creator turned comments off</p>
{{- else if .Novel.CommentCount == 1 }}
<p class="mb-4">There are no comments yet</p>
{{- else }}
<div class="list-group" id="comments-container">
{{ range .Novel.CommentsList }}
<!-- Element for a single comment -->
<!-- NOTE: py-1 to give each comment a bit more breathing space -->
<div class="list-group-item border-0 bg-charcoal-surface1 py-1 px-0 mb-4 comment-item">
<div class="row">
<!-- Comment author avatar image -->
<div class="col-auto">
<a href="/users/{{ .AuthorID }}">
<img class="img-fluid rounded-circle object-fit-cover" src="{{ .Avatar }}" alt="{{ .AuthorName }}" style="width: 40px; height: 40px" />
</a>
</div>
<div class="col px-2">
<!-- Comment author name and date -->
<div class="d-md-flex align-items-center mb-2">
<div class="me-md-2">
<a href="/users/{{ .AuthorID }}" class="text-body text-decoration-none">
<h6 class="mb-0">{{- .AuthorName }}</h6>
</a>
</div>
<div>
<small class="text-muted">{{ .Date }}</small>
</div>
</div>
<!-- Stamp or emoji -->
{{- if .Stamp }}
<img class="stamp img-fluid rounded object-fit-cover me-2" src="/proxy/s.pximg.net/common/images/stamp/generated-stamps/{{ .Stamp }}_s.jpg" alt="/proxy/s.pximg.net/common/images/stamp/generated-stamps/{{ .Stamp }}_s.jpg"/>
<!-- Series navigation, if present -->
{{- if .Novel.SeriesNavData }}
<div class="col-9 px-0">
<div class="custom-card">
<div class="custom-card-body bg-charcoal-surface2 p-4">
<h2 class="text-center mb-2">
<a href="/novel/series/{{ .Novel.SeriesNavData.SeriesID }}" class="text-body text-decoration-none">
{{- .Novel.SeriesNavData.Title }}
</a>
</h2>
<h3 class="text-body-secondary text-center">
<a href="/novel/series/{{ .Novel.SeriesNavData.SeriesID }}" class="text-body-secondary text-decoration-none">
<i class="bi bi-stack me-2"></i>{{ len(.NovelSeriesContentTitles) }}
{{- if len(.NovelSeriesContentTitles) == 1 }}
work in this series
{{- else }}
<!-- Comment with emojis -->
<p class="d-flex align-items-center m-0">{{ raw: parseEmojis(.Context) }}</p>
{{ end }}
</div>
works in this series
{{- end }}
</a>
</h3>
<hr class="my-4">
<div class="row row-col-auto nav nav-underline justify-content-center pb-2 mx-1">
{{ activeState := .Novel.ID }}
{{ yield NovelNav(
baseURL="/novel/",
paths=.NovelSeriesIDs,
names=.NovelSeriesTitles,
activeState=activeState
) }}
</div>
</div>
{{- end }}
<div class="d-flex justify-content-center">
<button type="button" class="custom-btn-secondary-flex mb-4" id="load-more-btn">Load more</button>
</div>
</div>
{{- end }}
</div>
</div>
</div>
</div>
<div class="col-12 col-lg-9">
<!-- Author information -->
<div class="custom-card bg-black mb-4 mb-lg-0">
<div class="card-header border-0 rounded-5 bg-charcoal-surface1 py-3 px-4">
<div class="d-flex justify-content-between align-items-center">
<a class="text-decoration-none text-light d-flex align-items-center" href="/users/{{ .User.ID }}/novels#checkpoint">
<img src="{{ .User.Avatar }}" alt="{{ .User.Name }}" class="rounded-circle object-fit-cover me-3" style="width: 48px; height: 48px" />
<h2 class="m-0">{{ .User.Name }}</h2>
</a>
</div>
</div>
<div class="overflow-auto p-4 pb-0" id="horizontal-scroll">
<div class="row flex-nowrap">
{{- range .NovelRelated }}
{{- include "fragments/novel-tn" . }}
{{- end }}
</div>
</div>
</div>
</div>
<!-- Comments section -->
<!--
TODO: currently duplicated unnecessarily across artwork.jet.html and novel.jet.html
Need to extract this out to a block that takes parameters
-->
<div class="col-12 col-lg-9">
<div class="custom-card">
<div class="custom-card-body bg-charcoal-surface1 p-4 pb-0">
{{- if .Novel.CommentOff == 1 || .Novel.CommentCount == 0 }}
<h2>0 Comments</h2>
{{- else if .Novel.CommentCount == 1 }}
<h2>1 Comment</h2>
{{- else }}
<h2>{{ .Novel.CommentCount }} Comments</h2>
{{- end }}
{{- if .Novel.CommentOff == 1 }}
<p class="mb-4">The creator turned comments off</p>
{{- else if .Novel.CommentCount == 1 }}
<p class="mb-4">There are no comments yet</p>
{{- else }}
<div class="list-group" id="comments-container">
{{ range .Novel.CommentsList }}
<!-- Element for a single comment -->
<!-- NOTE: py-1 to give each comment a bit more breathing space -->
<div class="list-group-item border-0 bg-charcoal-surface1 py-1 px-0 mb-4 comment-item">
<div class="row">
<!-- Comment author avatar image -->
<div class="col-auto">
<a href="/users/{{ .AuthorID }}">
<img class="img-fluid rounded-circle object-fit-cover" src="{{ .Avatar }}" alt="{{ .AuthorName }}" style="width: 40px; height: 40px" />
</a>
</div>
<div class="col px-2">
<!-- Comment author name and date -->
<div class="d-md-flex align-items-center mb-2">
<div class="me-md-2">
<a href="/users/{{ .AuthorID }}" class="text-body text-decoration-none">
<h6 class="mb-0">{{- .AuthorName }}</h6>
</a>
</div>
<div>
<small class="text-muted">{{ .Date }}</small>
</div>
</div>
<!-- Stamp or emoji -->
{{- if .Stamp }}
<img class="stamp img-fluid rounded object-fit-cover me-2" src="/proxy/s.pximg.net/common/images/stamp/generated-stamps/{{ .Stamp }}_s.jpg" alt="/proxy/s.pximg.net/common/images/stamp/generated-stamps/{{ .Stamp }}_s.jpg"/>
{{- else }}
<!-- Comment with emojis -->
<p class="d-flex align-items-center m-0">{{ raw: parseEmojis(.Context) }}</p>
{{ end }}
</div>
</div>
{{ end }}
</div>
{{- end }}
<div class="d-flex justify-content-center">
<button type="button" class="custom-btn-secondary-flex mb-4" id="load-more-btn">Load more</button>
</div>
</div>
{{ end }}
</div>
</div>
</div>
@@ -374,7 +375,12 @@
<h2>Related works</h2>
<div class="row row-cols-1 row-cols-lg-2 g-4">
{{- range .NovelRelated }}
{{- include "fragments/novel-tn" . }}
<div class="col mt-1">
<div class="custom-novel-card bg-black p-3 py-4 thumbnail-hover-dark">
{{- include "fragments/novel-tn" . }}
</div>
<hr class="my-0">
</div>
{{- end }}
</div>
</div>
+7 -2
View File
@@ -25,14 +25,19 @@
<!-- Main content -->
<div class="row row-cols-1 row-cols-lg-2 g-4">
{{- range .Novels }}
{{- include "fragments/novel-tn" . }}
<div class="col mt-1">
<div class="custom-novel-card bg-black p-3 py-4 d-flex flex-column thumbnail-hover-dark">
{{- include "fragments/novel-tn" . }}
</div>
<hr class="my-0">
</div>
{{- end }}
</div>
</div>
<div class="d-flex justify-content-center">
<a href="" class="custom-btn-secondary-flex">Reload</a>
<a href="" class="custom-btn-secondary-flex">Refresh</a>
</div>
</div>
+6 -1
View File
@@ -90,7 +90,12 @@
<h2>Novels</h2>
<div class="row row-cols-1 row-cols-lg-2 g-4">
{{- range .User.Novels }}
{{- include "fragments/novel-tn" . }}
<div class="col mt-1">
<div class="custom-novel-card bg-black p-3 py-4 thumbnail-hover-dark">
{{- include "fragments/novel-tn" . }}
</div>
<hr class="my-0">
</div>
{{- end }}
</div>
{{- end }}