Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support force refresh in feed edit and feed entries page #1989

Merged
merged 1 commit into from
Aug 10, 2023
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
2 changes: 1 addition & 1 deletion api/feed.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (h *handler) refreshFeed(w http.ResponseWriter, r *http.Request) {
return
}

err := feedHandler.RefreshFeed(h.store, userID, feedID)
err := feedHandler.RefreshFeed(h.store, userID, feedID, false)
if err != nil {
json.ServerError(w, r, err)
return
Expand Down
2 changes: 1 addition & 1 deletion cli/refresh_feeds.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func refreshFeeds(store *storage.Storage) {
defer wg.Done()
for job := range jobQueue {
logger.Info("[Cronjob] Refreshing feed #%d for user #%d in worker #%d", job.FeedID, job.UserID, workerID)
if err := feedHandler.RefreshFeed(store, job.UserID, job.FeedID); err != nil {
if err := feedHandler.RefreshFeed(store, job.UserID, job.FeedID, false); err != nil {
logger.Error("[Cronjob] Refreshing the feed #%d returned this error: %v", job.FeedID, err)
}
}
Expand Down
16 changes: 16 additions & 0 deletions http/request/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,22 @@ func QueryInt64Param(r *http.Request, param string, defaultValue int64) int64 {
return val
}

// QueryBoolParam returns a query string parameter as bool.
func QueryBoolParam(r *http.Request, param string, defaultValue bool) bool {
value := r.URL.Query().Get(param)
if value == "" {
return defaultValue
}

val, err := strconv.ParseBool(value)

if err != nil {
return defaultValue
}

return val
}

// HasQueryParam checks if the query string contains the given parameter.
func HasQueryParam(r *http.Request, param string) bool {
values := r.URL.Query()
Expand Down
1 change: 1 addition & 0 deletions locale/translations/de_DE.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"confirm.question": "Sind Sie sicher?",
"confirm.question.refresh": "Möchten Sie eine erzwungene Aktualisierung durchführen?",
"confirm.yes": "ja",
"confirm.no": "nein",
"confirm.loading": "In Arbeit...",
Expand Down
1 change: 1 addition & 0 deletions locale/translations/el_EL.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"confirm.question": "Είστε σίγουροι;",
"confirm.question.refresh": "Θέλετε να επιτελέσετε μια υποχρεωτική ανανέωση;",
"confirm.yes": "ναι",
"confirm.no": "όχι",
"confirm.loading": "Σε εξέλιξη...",
Expand Down
1 change: 1 addition & 0 deletions locale/translations/en_US.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"confirm.question": "Are you sure?",
"confirm.question.refresh": "Are you want to force refresh?",
"confirm.yes": "yes",
"confirm.no": "no",
"confirm.loading": "In progress…",
Expand Down
1 change: 1 addition & 0 deletions locale/translations/es_ES.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"confirm.question": "¿Estás seguro?",
"confirm.question.refresh": "¿Quieres forzar la actualización?",
"confirm.yes": "sí",
"confirm.no": "no",
"confirm.loading": "En progreso...",
Expand Down
1 change: 1 addition & 0 deletions locale/translations/fi_FI.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"confirm.question": "Oletko varma?",
"confirm.question.refresh": "Haluatko pakottaa päivityksen?",
"confirm.yes": "kyllä",
"confirm.no": "ei",
"confirm.loading": "Käynnissä...",
Expand Down
1 change: 1 addition & 0 deletions locale/translations/fr_FR.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"confirm.question": "Êtes-vous sûr ?",
"confirm.question.refresh": "Voulez-vous forcer le rafraîchissement ?",
"confirm.yes": "oui",
"confirm.no": "non",
"confirm.loading": "En cours...",
Expand Down
1 change: 1 addition & 0 deletions locale/translations/hi_IN.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"confirm.question": "मंजूर है?",
"confirm.question.refresh": "क्या आप बल द्वारा ताज़ा करना चाहते हैं?",
"confirm.yes": "हाँ",
"confirm.no": " नहीं",
"confirm.loading": " प्रगति में है ...",
Expand Down
1 change: 1 addition & 0 deletions locale/translations/id_ID.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"confirm.question": "Apakah Anda yakin?",
"confirm.question.refresh": "Apakah Anda ingin memaksa penyegaran?",
"confirm.yes": "ya",
"confirm.no": "tidak",
"confirm.loading": "Sedang progres...",
Expand Down
1 change: 1 addition & 0 deletions locale/translations/it_IT.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"confirm.question": "Sei sicuro?",
"confirm.question.refresh": "Vuoi forzare l'aggiornamento?",
"confirm.yes": "sì",
"confirm.no": "no",
"confirm.loading": "In corso...",
Expand Down
1 change: 1 addition & 0 deletions locale/translations/ja_JP.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"confirm.question": "よろしいですか?",
"confirm.question.refresh": "強制的に更新しますか?",
"confirm.yes": "はい",
"confirm.no": "いいえ",
"confirm.loading": "実行中…",
Expand Down
1 change: 1 addition & 0 deletions locale/translations/nl_NL.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"confirm.question": "Weet je het zeker?",
"confirm.question.refresh": "Wil je een gedwongen vernieuwing uitvoeren?",
"confirm.yes": "ja",
"confirm.no": "nee",
"confirm.loading": "Bezig...",
Expand Down
1 change: 1 addition & 0 deletions locale/translations/pl_PL.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"confirm.question": "Czy jesteś pewny?",
"confirm.question.refresh": "Czy chcesz wymusić odświeżenie?",
"confirm.yes": "tak",
"confirm.no": "nie",
"confirm.loading": "W toku...",
Expand Down
1 change: 1 addition & 0 deletions locale/translations/pt_BR.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"confirm.question": "Tem certeza?",
"confirm.question.refresh": "Você deseja forçar a atualização?",
"confirm.yes": "Sim",
"confirm.no": "Não",
"confirm.loading": "Carregando...",
Expand Down
1 change: 1 addition & 0 deletions locale/translations/ru_RU.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"confirm.question": "Вы уверены?",
"confirm.question.refresh": "Вы хотите выполнить принудительное обновление?",
"confirm.yes": "да",
"confirm.no": "нет",
"confirm.loading": "В процессе…",
Expand Down
1 change: 1 addition & 0 deletions locale/translations/tr_TR.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"confirm.question": "Emin misiniz?",
"confirm.question.refresh": "Zorla yenilemek istiyor musunuz?",
"confirm.yes": "evet",
"confirm.no": "hayır",
"confirm.loading": "Devam ediyor...",
Expand Down
1 change: 1 addition & 0 deletions locale/translations/uk_UA.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"confirm.question": "Ви впевнені?",
"confirm.question.refresh": "Ви хочете змусити оновити?",
"confirm.yes": "так",
"confirm.no": "ні",
"confirm.loading": "В процесі...",
Expand Down
1 change: 1 addition & 0 deletions locale/translations/zh_CN.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"confirm.question": "您确认吗?",
"confirm.question.refresh": "您是否要强制刷新?",
"confirm.yes": "是",
"confirm.no": "否",
"confirm.loading": "执行中…",
Expand Down
1 change: 1 addition & 0 deletions locale/translations/zh_TW.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"confirm.question": "您確認嗎?",
"confirm.question.refresh": "您想要強制刷新嗎?",
"confirm.yes": "是",
"confirm.no": "否",
"confirm.loading": "執行中…",
Expand Down
11 changes: 6 additions & 5 deletions reader/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func CreateFeed(store *storage.Storage, userID int64, feedCreationRequest *model
subscription.WithClientResponse(response)
subscription.CheckedNow()

processor.ProcessFeedEntries(store, subscription, user)
processor.ProcessFeedEntries(store, subscription, user, true)

if storeErr := store.CreateFeed(subscription); storeErr != nil {
return nil, storeErr
Expand All @@ -104,7 +104,7 @@ func CreateFeed(store *storage.Storage, userID int64, feedCreationRequest *model
}

// RefreshFeed refreshes a feed.
func RefreshFeed(store *storage.Storage, userID, feedID int64) error {
func RefreshFeed(store *storage.Storage, userID, feedID int64, forceRefresh bool) error {
defer timer.ExecutionTime(time.Now(), fmt.Sprintf("[RefreshFeed] feedID=%d", feedID))
user, storeErr := store.UserByID(userID)
if storeErr != nil {
Expand Down Expand Up @@ -173,10 +173,11 @@ func RefreshFeed(store *storage.Storage, userID, feedID int64) error {
}

originalFeed.Entries = updatedFeed.Entries
processor.ProcessFeedEntries(store, originalFeed, user)
processor.ProcessFeedEntries(store, originalFeed, user, forceRefresh)

// We don't update existing entries when the crawler is enabled (we crawl only inexisting entries).
if storeErr := store.RefreshFeedEntries(originalFeed.UserID, originalFeed.ID, originalFeed.Entries, !originalFeed.Crawler); storeErr != nil {
// We don't update existing entries when the crawler is enabled (we crawl only inexisting entries). Unless it is forced to refresh
updateExistingEntries := forceRefresh || !originalFeed.Crawler
if storeErr := store.RefreshFeedEntries(originalFeed.UserID, originalFeed.ID, originalFeed.Entries, updateExistingEntries); storeErr != nil {
originalFeed.WithError(storeErr.Error())
store.UpdateFeedError(originalFeed)
return storeErr
Expand Down
4 changes: 2 additions & 2 deletions reader/processor/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ var (
)

// ProcessFeedEntries downloads original web page for entries and apply filters.
func ProcessFeedEntries(store *storage.Storage, feed *model.Feed, user *model.User) {
func ProcessFeedEntries(store *storage.Storage, feed *model.Feed, user *model.User, forceRefresh bool) {
var filteredEntries model.Entries

// array used for bulk push
Expand All @@ -55,7 +55,7 @@ func ProcessFeedEntries(store *storage.Storage, feed *model.Feed, user *model.Us

url := getUrlFromEntry(feed, entry)
entryIsNew := !store.EntryURLExists(feed.ID, entry.URL)
if feed.Crawler && entryIsNew {
if feed.Crawler && (entryIsNew || forceRefresh) {
logger.Debug("[Processor] Crawling entry %q from feed %q", url, feed.FeedURL)

startTime := time.Now()
Expand Down
9 changes: 8 additions & 1 deletion template/templates/views/edit_feed.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@ <h1 dir="auto">{{ .feed.Title }}</h1>
<a href="{{ route "feedEntries" "feedID" .feed.ID }}">{{ icon "entries" }}{{ t "menu.feed_entries" }}</a>
</li>
<li>
<a href="{{ route "refreshFeed" "feedID" .feed.ID }}">{{ icon "refresh" }}{{ t "menu.refresh_feed" }}</a>
<a href="#"
data-confirm="true"
data-label-question="{{ t "confirm.question.refresh" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}"
data-url="{{ route "refreshFeed" "feedID" .feed.ID }}?forceRefresh=true"
data-no-action-url="{{ route "refreshFeed" "feedID" .feed.ID }}?forceRefresh=false">{{ icon "refresh" }}{{ t "menu.refresh_feed" }}</a>
</li>
</ul>
</section>
Expand Down
9 changes: 8 additions & 1 deletion template/templates/views/feed_entries.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,14 @@ <h1 dir="auto">
</li>
{{ end }}
<li>
<a href="{{ route "refreshFeed" "feedID" .feed.ID }}">{{ icon "refresh" }}{{ t "menu.refresh_feed" }}</a>
<a href="#"
data-confirm="true"
data-label-question="{{ t "confirm.question.refresh" }}"
data-label-yes="{{ t "confirm.yes" }}"
data-label-no="{{ t "confirm.no" }}"
data-label-loading="{{ t "confirm.loading" }}"
data-url="{{ route "refreshFeed" "feedID" .feed.ID }}?forceRefresh=true"
data-no-action-url="{{ route "refreshFeed" "feedID" .feed.ID }}?forceRefresh=false">{{ icon "refresh" }}{{ t "menu.refresh_feed" }}</a>
</li>
<li>
<a href="{{ route "editFeed" "feedID" .feed.ID }}">{{ icon "edit" }}{{ t "menu.edit_feed" }}</a>
Expand Down
3 changes: 2 additions & 1 deletion ui/feed_refresh.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import (

func (h *handler) refreshFeed(w http.ResponseWriter, r *http.Request) {
feedID := request.RouteInt64Param(r, "feedID")
if err := feedHandler.RefreshFeed(h.store, request.UserID(r), feedID); err != nil {
forceRefresh := request.QueryBoolParam(r, "forceRefresh", false)
if err := feedHandler.RefreshFeed(h.store, request.UserID(r), feedID, forceRefresh); err != nil {
logger.Error("[UI:RefreshFeed] %v", err)
}

Expand Down
28 changes: 20 additions & 8 deletions ui/static/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -561,18 +561,22 @@ function handleConfirmationMessage(linkElement, callback) {
let containerElement = linkElement.parentNode;
let questionElement = document.createElement("span");

let yesElement = document.createElement("a");
yesElement.href = "#";
yesElement.appendChild(document.createTextNode(linkElement.dataset.labelYes));
yesElement.onclick = (event) => {
event.preventDefault();

function createLoadingElement() {
let loadingElement = document.createElement("span");
loadingElement.className = "loading";
loadingElement.appendChild(document.createTextNode(linkElement.dataset.labelLoading));

questionElement.remove();
containerElement.appendChild(loadingElement);
}

let yesElement = document.createElement("a");
yesElement.href = "#";
yesElement.appendChild(document.createTextNode(linkElement.dataset.labelYes));
yesElement.onclick = (event) => {
event.preventDefault();

createLoadingElement();

callback(linkElement.dataset.url, linkElement.dataset.redirectUrl);
};
Expand All @@ -582,8 +586,16 @@ function handleConfirmationMessage(linkElement, callback) {
noElement.appendChild(document.createTextNode(linkElement.dataset.labelNo));
noElement.onclick = (event) => {
event.preventDefault();
linkElement.style.display = "inline";
questionElement.remove();

const noActionUrl = linkElement.dataset.noActionUrl;
if (noActionUrl) {
createLoadingElement();

callback(noActionUrl, linkElement.dataset.redirectUrl);
} else {
linkElement.style.display = "inline";
questionElement.remove();
}
};

questionElement.className = "confirm";
Expand Down
4 changes: 3 additions & 1 deletion ui/static/js/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@ document.addEventListener("DOMContentLoaded", function () {
onClick("a[data-confirm]", (event) => handleConfirmationMessage(event.target, (url, redirectURL) => {
let request = new RequestBuilder(url);

request.withCallback(() => {
request.withCallback((response) => {
if (redirectURL) {
window.location.href = redirectURL;
} else if (response && response.redirected && response.url) {
window.location.href = response.url;
} else {
window.location.reload();
}
Expand Down
3 changes: 2 additions & 1 deletion ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ func Serve(router *mux.Router, store *storage.Storage, pool *worker.Pool) {
uiRouter.HandleFunc("/feeds/refresh", handler.refreshAllFeeds).Name("refreshAllFeeds").Methods(http.MethodGet)

// Individual feed pages.
uiRouter.HandleFunc("/feed/{feedID}/refresh", handler.refreshFeed).Name("refreshFeed").Methods(http.MethodGet)
uiRouter.HandleFunc("/feed/{feedID}/refresh", handler.refreshFeed).Name("refreshFeed").Methods(http.MethodGet, http.MethodPost)
uiRouter.HandleFunc("/feed/{feedID}/refresh", handler.refreshFeed).Queries("forceRefresh", "{forceRefresh:true|false}").Name("refreshFeed").Methods(http.MethodGet, http.MethodPost)
uiRouter.HandleFunc("/feed/{feedID}/edit", handler.showEditFeedPage).Name("editFeed").Methods(http.MethodGet)
uiRouter.HandleFunc("/feed/{feedID}/remove", handler.removeFeed).Name("removeFeed").Methods(http.MethodPost)
uiRouter.HandleFunc("/feed/{feedID}/update", handler.updateFeed).Name("updateFeed").Methods(http.MethodPost)
Expand Down
2 changes: 1 addition & 1 deletion worker/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func (w *Worker) Run(c chan model.Job) {
logger.Debug("[Worker #%d] Received feed #%d for user #%d", w.id, job.FeedID, job.UserID)

startTime := time.Now()
refreshErr := feedHandler.RefreshFeed(w.store, job.UserID, job.FeedID)
refreshErr := feedHandler.RefreshFeed(w.store, job.UserID, job.FeedID, false)

if config.Opts.HasMetricsCollector() {
status := "success"
Expand Down
Loading