Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
svera committed Oct 15, 2023
1 parent 99a2802 commit fb669d7
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 476 deletions.
21 changes: 12 additions & 9 deletions internal/controller/highlights.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

type highlightsRepository interface {
Highlights(userID int, page int, resultsPerPage int) ([]string, int64, error)
Highlights(userID int, page int, resultsPerPage int) (search.PaginatedResult, error)
Highlight(userID int, documentPath string) error
Remove(userID int, documentPath string) error
}
Expand Down Expand Up @@ -50,20 +50,23 @@ func (h *Highlights) Highlights(c *fiber.Ctx) error {
h.wordsPerMinute = session.WordsPerMinute
}

highlights, total, err := h.hlRepository.Highlights(int(session.ID), page, model.ResultsPerPage)
highlights, err := h.hlRepository.Highlights(int(session.ID), page, model.ResultsPerPage)
if err != nil {
return fiber.ErrInternalServerError
}
searchResults, err := h.idx.Documents(highlights)
if err != nil {
return fiber.ErrInternalServerError
for i, highlight := range highlights.Hits {
docs, err := h.idx.Documents([]string{highlight.ID})
if err != nil {
return fiber.ErrInternalServerError
}
highlights.Hits[i] = docs[0]
highlights.Hits[i].Highlighted = true
}
totalPages := search.CalculateTotalPages(uint64(total), model.ResultsPerPage)

return c.Render("results-no-searchbox", fiber.Map{
"Results": searchResults,
"Total": total,
"Paginator": pagination(model.MaxPagesNavigator, totalPages, page, nil),
"Results": highlights.Hits,
"Total": highlights.TotalHits,
"Paginator": pagination(model.MaxPagesNavigator, highlights.TotalPages, page, nil),
"Title": "Highlights",
"Version": c.App().Config().AppName,
"EmailSendingConfigured": emailSendingConfigured,
Expand Down
7 changes: 2 additions & 5 deletions internal/model/highlight.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@ package model

import (
"time"

"gorm.io/gorm"
)

type Highlight struct {
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
UserID int `gorm:"primaryKey"`
Path string `gorm:"primaryKey"`
UserID int `gorm:"primaryKey"`
Path string `gorm:"primaryKey"`
}
17 changes: 15 additions & 2 deletions internal/model/highlight_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,28 @@ type HighlightRepository struct {
DB *gorm.DB
}

func (u *HighlightRepository) Highlights(userID int, page int, resultsPerPage int) ([]string, int64, error) {
func (u *HighlightRepository) Highlights(userID int, page int, resultsPerPage int) (search.PaginatedResult, error) {
highlights := []string{}
var total int64

result := u.DB.Scopes(Paginate(page, resultsPerPage)).Table("highlights").Select("path").Where("user_id = ?", userID).Order("created_at DESC").Pluck("path", &highlights)
if result.Error != nil {
log.Printf("error listing highlights: %s\n", result.Error)
}
u.DB.Table("highlights").Where("user_id = ?", userID).Count(&total)
return highlights, total, result.Error

paginatedResult := search.PaginatedResult{
Page: page,
Hits: make([]search.Document, len(highlights)),
TotalHits: int(total),
TotalPages: search.CalculateTotalPages(uint64(total), ResultsPerPage),
}

for i, path := range highlights {
paginatedResult.Hits[i].ID = path
}

return paginatedResult, result.Error
}

func (u *HighlightRepository) Highlighted(userID int, documents []search.Document) []search.Document {
Expand Down
152 changes: 152 additions & 0 deletions internal/webserver/embedded/js/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
const deleteModal = document.getElementById('deleteModal');

deleteModal.addEventListener('show.bs.modal', event => {
const link = event.relatedTarget;
const slug = link.getAttribute('data-bs-slug');
const modalInputSlug = deleteModal.querySelector('.slug');

modalInputSlug.value = slug;
})

deleteModal.addEventListener('hidden.bs.modal', event => {
let message = document.getElementById('delete-document-message');
message.classList.add("visually-hidden");
})

function send(index) {
event.preventDefault();
let formID = "send-email-" + index;
let submit = document.querySelector('#'+formID+' button');
let envelope = document.querySelector('#envelope-'+index);
let spinner = document.querySelector('#spinner-'+index);
form = document.getElementById(formID);
submit.setAttribute("disabled", true);
spinner.classList.remove("visually-hidden");
envelope.classList.add("visually-hidden");
fetch('/send', {
method: "POST",
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
'email': form.elements[0].value,
'slug': form.elements[1].value,
})
})
.then((response) => {
message = document.getElementById("send-email-message-" + index)
message.classList.remove("visually-hidden");
if (!response.ok) {
message.innerHTML = '{{t .Lang "There was an error sending the document, please try again later"}}';
message.classList.add("text-danger");
} else {
message.innerHTML = '{{t .Lang "Document sent succesfully"}}';
message.classList.add("text-success");
}
submit.removeAttribute("disabled");
envelope.classList.remove("visually-hidden");
spinner.classList.add("visually-hidden");
})
.catch(function (error) {
// Catch errors
console.log(error);
});
}

function remove() {
event.preventDefault();
form = document.getElementById("delete-form");
fetch('/delete', {
method: "POST",
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
'slug': form.elements['slug'].value,
})
})
.then((response) => {
if (response.ok) {
location.reload();
} else {
message = document.getElementById("delete-document-message")
message.classList.remove("visually-hidden");
message.innerHTML = '{{t .Lang "There was an error deleting the document"}}';
}
})
.catch(function (error) {
// Catch errors
console.log(error);
});
}

function highlight(index) {
event.preventDefault();
let highlightFormParent = document.querySelector("#highlight-" + index);
let dehighlightFormParent = document.querySelector("#dehighlight-" + index);
let highlightForm = highlightFormParent.querySelector("form");
let submit = highlightForm.querySelector('button');
fetch('/highlight', {
method: "POST",
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
'slug': highlightForm.elements['slug'].value,
})
})
.then((response) => {
if (response.ok) {
highlightFormParent.classList.add("visually-hidden");
dehighlightFormParent.classList.remove("visually-hidden");
} else {
console.log(response.body)
}
})
.catch(function (error) {
// Catch errors
console.log(error);
});
}

function dehighlight(index, slug, el) {
event.preventDefault();
let highlightFormParent = document.querySelector("#highlight-" + index);
let dehighlightLinkParent = document.querySelector("#dehighlight-" + index);
fetch(
el.getAttribute("href"), {
method: "DELETE",
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
credentials: "same-origin",
body: new URLSearchParams({
'slug': slug,
})
}
)
.then((response) => {
if (response.ok) {
dehighlightLinkParent.classList.add("visually-hidden");
highlightFormParent.classList.remove("visually-hidden");
} else {
console.log(response.body)
}
})
.catch(function (error) {
// Catch errors
console.log(error);
});
}

window.onload = function() {
let imgs = document.querySelectorAll('.cover');
for (i = 0; i < imgs.length; i++) {
if (imgs[i].getAttribute('data-src')) {
imgs[i].addEventListener('error', function onError(e) {
this.setAttribute('src', '/images/generic.png');
});
imgs[i].setAttribute('src', imgs[i].getAttribute('data-src'));
}
}
}
76 changes: 76 additions & 0 deletions internal/webserver/embedded/views/partials/docs-list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{{$lang := .Lang}}
{{$emailSendingConfigured := .EmailSendingConfigured}}
{{$session := .Session}}
{{$emailFrom := .EmailFrom}}
{{$wordsPerMinute := .WordsPerMinute}}

<div class="list-group list-group-flush">
{{if .Results}} {{range $i, $book := .Results}}
<div class="list-group-item">
<div class="row">
<div class="col-md-2 col-sm-12">
<img src="/images/generic.png" data-src="/cover/{{$book.Slug}}" loading="lazy" class="border border-2 text-center mb-3 cover img-fluid" alt='{{t $lang "\"%s\" cover" $book.Title}}'>
</div>
<div class="col-md-10 col-sm-12">
<div class="row">
<div class="col-7">
{{ if ne $book.Series "" }} {{$seriesTitle := t $lang "Search for more titles belonging to %s"
$book.Series}}
<p class="text-start text-muted text-uppercase mb-0"><a
href="/{{$lang}}?search=SeriesEq&colon;&quot;{{$book.Series}}&quot;"
title={{$seriesTitle}}>{{$book.Series}} {{$book.SeriesIndex}}</a></p>
{{ end }}
<h4 class="text-start">
<a href="/{{$lang}}/document/{{$book.Slug}}">{{$book.Title}}</a> <small class="small text-muted">{{$book.Year}}</small>
</h4>
</div>
<div class="col-5 text-end">
{{ template "partials/actions" dict "Lang" $lang "Book" $book "EmailSendingConfigured"
$emailSendingConfigured "Index" $i "Session" $session "EmailFrom" $emailFrom}}
</div>
</div>
<div class="row">
<div class="col-12 text-start">
{{if $book.Authors}}
<h5>
{{range $i, $author := $book.Authors}}
{{$authorTitle := t $lang "Search for more titles by %s" $author}}
<a href="/{{$lang}}?search=AuthorsEq&colon;&quot;{{$author}}&quot;" title={{$authorTitle}}>{{$author}}</a>{{if notLast $book.Authors $i}}, {{end}}
{{end}}
</h5>
{{else}}
<h5>{{t $lang "Unknown author"}}</h5>
{{end}}

{{ if gt $book.Words 0.0 }}
<p class="text-muted"><em>{{t $lang "Estimated reading time"}}:
{{$book.ReadingTime $wordsPerMinute}}</em></p>
{{ end }}

{{ if $book.Pages }}
<p class="text-muted"><em>{{t $lang "%d pages" $book.Pages}}</em></p>
{{ end }}

{{ if $book.Subjects }}
<div class="mb-3">
{{range $i, $subject := $book.Subjects}}
{{$subjectTitle := t $lang "Search for more titles in %s" $subject}}
<a class="btn btn-secondary btn-sm" href="/{{$lang}}?search=Subjects&colon;&quot;{{$subject}}&quot;" title={{$subjectTitle}}>{{$subject}}</a>
{{end}}
</div>
{{ end }}

{{if $book.Description}}
<div class="mb-1 collapse" id="collapse-desc{{$i}}">{{$book.Description}}</div>
{{else}}
<div class="mb-1"><em>{{t $lang "No description available"}}</em></div>
{{end}}

<a data-bs-toggle="collapse" class="collapse-control collapsed" href="#collapse-desc{{$i}}"></a>
</div>
</div>
</div>
</div>
</div>
{{end}} {{end}}
</div>
Loading

0 comments on commit fb669d7

Please sign in to comment.