Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .cursorrules
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
DO NOT GIVE ME HIGH LEVEL SHIT, IF I ASK FOR FIX OR EXPLANATION, I WANT ACTUAL CODE OR EXPLANATION! I DON'T WANT "Here's how you can blablabla"
- Be casual unless otherwise specified
- Be terse
- Suggest solutions that I didn't think about-anticipate my needs
- Treat me as an expert
- Be accurate and thorough
- Give the answer immediately. Provide detailed explanations and restate my query in your own words if necessary after giving the answer
- Value good arguments over authorities, the source is irrelevant
- Consider new technologies and contrarian ideas, not just the conventional wisdom
- You may use high levels of speculation or prediction, just flag it for me
- No moral lectures
- Discuss safety only when it's crucial and non-obvious
- If your content policy is an issue, provide the closest acceptable response and explain the content policy issue afterward
- Cite sources whenever possible at the end, not inline
- No need to mention your knowledge cutoff
- No need to disclose you're an AI
- Please respect my formatting preferences when you provide code.
- Please respect all code comments, they're usually there for a reason. Remove them ONLY if they're completely irrelevant after a code change. if unsure, do not remove the comment.
- Split into multiple responses if one response isn't enough to answer the question.
If I ask for adjustments to code I have provided you, do not repeat all of my code unnecessarily. Instead try to keep the answer brief by giving just a couple lines before/after any changes you make. Multiple code blocks are ok.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@ go.work
data/
bin/
release/

docker-compose-local.yml
9 changes: 7 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
include .env.example
export
ifneq (,$(wildcard ./.env))
include .env
export
else
include .env.example
export
endif

LOCAL_BIN:=$(CURDIR)/bin
PATH:=$(LOCAL_BIN):$(PATH)
Expand Down
32 changes: 26 additions & 6 deletions internal/controller/http/web/books.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,19 @@ import (
"github.com/vanadium23/kompanion/internal/entity"
"github.com/vanadium23/kompanion/internal/library"
"github.com/vanadium23/kompanion/internal/stats"
syncpkg "github.com/vanadium23/kompanion/internal/sync"
"github.com/vanadium23/kompanion/pkg/logger"
)

type booksRoutes struct {
shelf library.Shelf
stats stats.ReadingStats
logger logger.Interface
shelf library.Shelf
stats stats.ReadingStats
progress syncpkg.Progress
logger logger.Interface
}

func newBooksRoutes(handler *gin.RouterGroup, shelf library.Shelf, stats stats.ReadingStats, l logger.Interface) {
r := &booksRoutes{shelf: shelf, stats: stats, logger: l}
func newBooksRoutes(handler *gin.RouterGroup, shelf library.Shelf, stats stats.ReadingStats, progress syncpkg.Progress, l logger.Interface) {
r := &booksRoutes{shelf: shelf, stats: stats, progress: progress, logger: l}

handler.GET("/", r.listBooks)
handler.POST("/upload", r.uploadBook)
Expand All @@ -44,8 +46,26 @@ func (r *booksRoutes) listBooks(c *gin.Context) {
return
}

// Fetch progress for each book
type BookWithProgress struct {
entity.Book
Progress int
}
booksWithProgress := make([]BookWithProgress, len(books.Books))
for i, book := range books.Books {
progress, err := r.progress.Fetch(c.Request.Context(), book.DocumentID)
if err != nil {
r.logger.Error(err, "failed to fetch progress for book %s", book.ID)
progress = entity.Progress{}
}
booksWithProgress[i] = BookWithProgress{
Book: book,
Progress: int(progress.Percentage * 100),
}
}

c.HTML(200, "books", passStandartContext(c, gin.H{
"books": books.Books,
"books": booksWithProgress,
"pagination": gin.H{
"currentPage": page,
"perPage": perPage,
Expand Down
29 changes: 28 additions & 1 deletion internal/controller/http/web/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import (
"fmt"
"html/template"
"io/fs"
"math"
"net/http"
"path/filepath"
"strings"
"time"

"github.com/foolin/goview"
Expand Down Expand Up @@ -64,6 +66,7 @@ func NewRouter(
"Version": func() string {
return template.HTMLEscapeString(version)
},
"generateProgressBar": generateProgressBar,
}
gv := ginview.New(config)
gv.SetFileHandler(embeddedFH)
Expand All @@ -81,7 +84,7 @@ func NewRouter(
// Product pages
bookGroup := handler.Group("/books")
bookGroup.Use(authMiddleware(a))
newBooksRoutes(bookGroup, shelf, stats, l)
newBooksRoutes(bookGroup, shelf, stats, p, l)

// Stats pages
statsGroup := handler.Group("/stats")
Expand Down Expand Up @@ -114,6 +117,30 @@ func formatDuration(seconds int) string {
return fmt.Sprintf("%ds", secs)
}

func generateProgressBar(percentage int, totalLength int) string {
if percentage < 0 {
percentage = 0
}
numEquals := int(math.Round(float64(percentage) * float64(totalLength) / 100.0))
if numEquals > totalLength {
numEquals = totalLength
}
if numEquals < 0 {
numEquals = 0
}

numDots := totalLength - numEquals

var sb strings.Builder
sb.Grow(totalLength + 2)
sb.WriteString("[")
sb.WriteString(strings.Repeat("▓", numEquals))
sb.WriteString(strings.Repeat("░", numDots))
sb.WriteString("]")

return sb.String()
}

// https://github.com/foolin/goview/issues/25#issuecomment-876889943
func embeddedFH(config goview.Config, tmpl string) (string, error) {
path := filepath.Join(config.Root, tmpl)
Expand Down
4 changes: 4 additions & 0 deletions internal/sync/progress.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ func (uc *ProgressSyncUseCase) Fetch(ctx context.Context, bookID string) (entity
return entity.Progress{}, nil
}

if len(doc) == 0 {
return entity.Progress{}, nil
}

last := doc[0]
// rewrite koreader device with our authed device
last.Device = last.AuthDeviceName
Expand Down
54 changes: 16 additions & 38 deletions web/static/static.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


.edit-book-article {
display: flex;
flex-direction: row;
Expand All @@ -19,50 +17,30 @@
height: auto;
}



/* books */
.book-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 20px;
padding: 20px;
}

.book-item {
.book-card {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
align-items: flex-start;
gap: 1rem;
border: var(--border-thickness) solid var(--text-color);
padding: 1rem;
margin-bottom: 1.5rem;
}

.book-item .cover {
width: 100%;
height: 300px;
/* Фиксированная высота для всех обложек */
display: flex;
align-items: center;
justify-content: center;
background-color: #f0f0f0;
/* Цвет фона для обложек, если изображение отсутствует */
/* Container for the book cover image */
.book-cover {
flex-shrink: 0;
width: 10rem;
}

.book-item img {
/* Style the actual image */
.book-cover img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
/* Обеспечиваем, чтобы изображение полностью занимало контейнер */
height: auto;
}

.book-info {
margin-top: 10px;
}

.book-title {
font-weight: bold;
flex-grow: 1;
margin-top: 0;
}

.book-author {
font-style: italic;
color: #555;
}
42 changes: 25 additions & 17 deletions web/templates/books.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,27 @@
<div>
<input type="file" name="book" accept=".epub,.pdf,.fb2">
</div>
<button>Upload</button>
<button style="flex-grow: 1;">Upload</button>
</form>
</div>
<section class="book-grid">
<section>
{{ range .books }}
<div class="book-item">
<a href="/books/{{.ID}}">
<div class="cover">
<!-- Another example -->
<div class="book-card">
<div class="book-cover">
<a href="/books/{{.ID}}">
<img src="/books/{{.ID}}/cover" alt="{{.Title}} - {{.Author}}">
</div>
<div class="book-info">
<div class="book-title">{{.Title}}</div>
<div class="book-author">{{.Author}}</div>
</div>
</a>
</a>
</div>
<div class="book-info">
<h3 class="book-title">
<a href="/books/{{.ID}}">
{{.Title}}
</a>
</h3>
<p class="book-author">{{.Author}}</p>
<p class="book-progress">{{ generateProgressBar .Progress 15 }} // {{ .Progress }}%</p>
</div>
</div>
{{ end }}
</section>
Expand All @@ -30,26 +36,28 @@
{{ if .hasPrev }}
<a href="?page={{ .prevPage }}" class="pagination-prev">Previous</a>
{{ end }}

{{ if .hasNext }}
<a href="?page={{ .nextPage }}" class="pagination-next">Next</a>
{{ end }}

<ul class="pagination-list">
{{ if gt .currentPage 1 }}
<li><a href="?page=1" class="pagination-link" aria-label="Goto page 1">1</a></li>
{{ if gt .currentPage 2 }}
<li><span class="pagination-ellipsis">&hellip;</span></li>
{{ end }}
{{ end }}

<li><a href="?page={{ .currentPage }}" class="pagination-link is-current" aria-label="Page {{ .currentPage }}" aria-current="page">{{ .currentPage }}</a></li>


<li><a href="?page={{ .currentPage }}" class="pagination-link is-current" aria-label="Page {{ .currentPage }}"
aria-current="page">{{ .currentPage }}</a></li>

{{ if lt .currentPage .totalPages }}
{{ if lt .currentPage (subtract .totalPages 1) }}
<li><span class="pagination-ellipsis">&hellip;</span></li>
{{ end }}
<li><a href="?page={{ .totalPages }}" class="pagination-link" aria-label="Goto page {{ .totalPages }}">{{ .totalPages }}</a></li>
<li><a href="?page={{ .totalPages }}" class="pagination-link" aria-label="Goto page {{ .totalPages }}">{{
.totalPages }}</a></li>
{{ end }}
</ul>
</nav>
Expand Down
2 changes: 1 addition & 1 deletion web/templates/layouts/master.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<header class="header">
<table>
<tr>
<td>KOmpanion</td>
<td style="flex-grow: 1;">KOmpanion</td>
{{ if .isAuthenticated }}
<td><a href="/books/">> Books</a></td>
<td><a href="/stats/">> Statistics</a></td>
Expand Down
Loading