diff options
24 files changed, 550 insertions, 230 deletions
diff --git a/asset-sources/fietsboek.ts b/asset-sources/fietsboek.ts index b885271..ae9e724 100644 --- a/asset-sources/fietsboek.ts +++ b/asset-sources/fietsboek.ts @@ -22,6 +22,19 @@ interface Window { */ type Selector = string; + +/** + * Gets the value of a single cookie. + * + * @param name - Name of the cookie. + * @return The cookie value, or null. + */ +function getCookie(name: string): string | undefined { + return document.cookie.split("; ") + .find((row) => row.startsWith(`${name}=`)) + ?.split("=")[1]; +} + /** * Installs a listener to the given DOM objects. * @@ -400,9 +413,7 @@ addHandler(".button-clear-input", "click", clearInputButtonClicked); * @param event - The triggering event. */ function changeHomeSorting(_event: MouseEvent) { - const currentSorting = document.cookie.split("; ") - .find((row) => row.startsWith("home_sorting=")) - ?.split("=")[1] ?? "asc"; + const currentSorting = getCookie("home_sorting") ?? "asc"; const newSorting = currentSorting == "asc" ? "desc" : "asc"; document.cookie = `home_sorting=${newSorting}; SameSite=Lax`; window.location.reload(); @@ -411,6 +422,40 @@ function changeHomeSorting(_event: MouseEvent) { addHandler("#changeHomeSorting", "click", changeHomeSorting); +/** + * Handler to toggle the favourite status of a track. + * + * This is applied to .favourite-star elements and expects the track ID in + * data-track-id. + * + * @param event - The triggering event. + */ +function toggleTrackFavourite(event: MouseEvent) { + const target = event.target as HTMLElement; + const trackId = target.getAttribute("data-track-id"); + if (trackId === null) { + return; + } + const url = new URL("/me/toggle-favourite", window.location.href); + const formData = new URLSearchParams(); + formData.append("track-id", trackId); + formData.append("csrf_token", getCookie("csrf_token") ?? ""); + fetch(url, { + "method": "POST", + "body": formData, + }).then(response => response.json().then(data => { + const isNowFavourite = data["favourite"]; + if (isNowFavourite) { + target.classList.replace("bi-star", "bi-star-fill"); + } else { + target.classList.replace("bi-star-fill", "bi-star"); + } + })); +} + +addHandler(".favourite-star", "click", toggleTrackFavourite); + + document.addEventListener('DOMContentLoaded', function() { window.fietsboekImageIndex = 0; diff --git a/asset-sources/theme.scss b/asset-sources/theme.scss index faa4f44..8e0f001 100644 --- a/asset-sources/theme.scss +++ b/asset-sources/theme.scss @@ -118,6 +118,14 @@ strong { } } +.favourite-star, .favourite-star-ni { + color: orange; +} + +.favourite-star { + cursor: pointer; +} + /* Ensure a consistent width of the cells in the browse view. */ .browse-summary th, .browse-summary td { width: 25%; diff --git a/fietsboek/alembic/versions/20230830_8f4e4eae5eb2.py b/fietsboek/alembic/versions/20230830_8f4e4eae5eb2.py new file mode 100644 index 0000000..e8ac627 --- /dev/null +++ b/fietsboek/alembic/versions/20230830_8f4e4eae5eb2.py @@ -0,0 +1,31 @@ +"""add favourite association table + +Revision ID: 8f4e4eae5eb2 +Revises: 3149aa2d0114 +Create Date: 2023-08-30 19:19:54.545517 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = '8f4e4eae5eb2' +down_revision = '3149aa2d0114' +branch_labels = None +depends_on = None + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('track_favourite_assoc', + sa.Column('track_id', sa.Integer(), nullable=False), + sa.Column('user_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['track_id'], ['tracks.id'], name=op.f('fk_track_favourite_assoc_track_id_tracks')), + sa.ForeignKeyConstraint(['user_id'], ['users.id'], name=op.f('fk_track_favourite_assoc_user_id_users')), + sa.PrimaryKeyConstraint('track_id', 'user_id', name=op.f('pk_track_favourite_assoc')) + ) + # ### end Alembic commands ### + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('track_favourite_assoc') + # ### end Alembic commands ### diff --git a/fietsboek/locale/de/LC_MESSAGES/messages.mo b/fietsboek/locale/de/LC_MESSAGES/messages.mo Binary files differindex 1a44e6b..e6dd1c6 100644 --- a/fietsboek/locale/de/LC_MESSAGES/messages.mo +++ b/fietsboek/locale/de/LC_MESSAGES/messages.mo diff --git a/fietsboek/locale/de/LC_MESSAGES/messages.po b/fietsboek/locale/de/LC_MESSAGES/messages.po index be0aca1..b34e849 100644 --- a/fietsboek/locale/de/LC_MESSAGES/messages.po +++ b/fietsboek/locale/de/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-07-22 14:31+0200\n" +"POT-Creation-Date: 2023-08-17 22:51+0200\n" "PO-Revision-Date: 2022-07-02 17:35+0200\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language: de\n" @@ -37,35 +37,35 @@ msgstr "Passwörter stimmen nicht überein" msgid "password_constraint.length" msgstr "Passwort zu kurz" -#: fietsboek/models/track.py:589 +#: fietsboek/models/track.py:602 msgid "tooltip.table.length" msgstr "Länge" -#: fietsboek/models/track.py:590 +#: fietsboek/models/track.py:603 msgid "tooltip.table.people" msgstr "# Personen" -#: fietsboek/models/track.py:591 +#: fietsboek/models/track.py:604 msgid "tooltip.table.uphill" msgstr "Bergauf" -#: fietsboek/models/track.py:592 +#: fietsboek/models/track.py:605 msgid "tooltip.table.downhill" msgstr "Bergab" -#: fietsboek/models/track.py:593 +#: fietsboek/models/track.py:606 msgid "tooltip.table.moving_time" msgstr "Fahrzeit" -#: fietsboek/models/track.py:594 +#: fietsboek/models/track.py:607 msgid "tooltip.table.stopped_time" msgstr "Haltezeit" -#: fietsboek/models/track.py:596 +#: fietsboek/models/track.py:609 msgid "tooltip.table.max_speed" msgstr "Maximalgeschwindigkeit" -#: fietsboek/models/track.py:600 +#: fietsboek/models/track.py:613 msgid "tooltip.table.avg_speed" msgstr "Durchschnittsgeschwindigkeit" @@ -137,109 +137,121 @@ msgstr "Aufnahme (natürlich)" msgid "page.browse.filter.type.synthetic" msgstr "Vorlage (synthetisch)" -#: fietsboek/templates/browse.jinja2:86 +#: fietsboek/templates/browse.jinja2:82 +msgid "page.browse.filter.favourite.all" +msgstr "Alle Strecken" + +#: fietsboek/templates/browse.jinja2:83 +msgid "page.browse.filter.favourite.yes" +msgstr "Nur Favoriten" + +#: fietsboek/templates/browse.jinja2:84 +msgid "page.browse.filter.favourite.no" +msgstr "Keine Favoriten" + +#: fietsboek/templates/browse.jinja2:97 msgid "page.browse.filter.my_tracks.only" msgstr "Zeige nur eigene Strecken" -#: fietsboek/templates/browse.jinja2:87 +#: fietsboek/templates/browse.jinja2:98 msgid "page.browse.filter.friends_tracks_only" msgstr "Zeige nur Strecken von Freunden" -#: fietsboek/templates/browse.jinja2:88 +#: fietsboek/templates/browse.jinja2:99 msgid "page.browse.filter.me_tagged_only" msgstr "Zeige nur Strecken, in denen ich markiert bin" -#: fietsboek/templates/browse.jinja2:98 +#: fietsboek/templates/browse.jinja2:109 msgid "page.browse.filters.apply" msgstr "Filter anwenden" -#: fietsboek/templates/browse.jinja2:102 +#: fietsboek/templates/browse.jinja2:113 msgid "page.browse.filters.clear_all" msgstr "Filter zurücksetzen" -#: fietsboek/templates/browse.jinja2:105 +#: fietsboek/templates/browse.jinja2:116 msgid "page.browse.filters.expand_advanced" msgstr "Erweitert" -#: fietsboek/templates/browse.jinja2:110 fietsboek/templates/browse.jinja2:111 +#: fietsboek/templates/browse.jinja2:121 fietsboek/templates/browse.jinja2:122 msgid "page.browse.sort.date" msgstr "Nach Datum sortieren" -#: fietsboek/templates/browse.jinja2:112 fietsboek/templates/browse.jinja2:113 +#: fietsboek/templates/browse.jinja2:123 fietsboek/templates/browse.jinja2:124 msgid "page.browse.sort.length" msgstr "Nach Länge sortieren" -#: fietsboek/templates/browse.jinja2:114 fietsboek/templates/browse.jinja2:115 +#: fietsboek/templates/browse.jinja2:125 fietsboek/templates/browse.jinja2:126 msgid "page.browse.sort.duration" msgstr "Nach Dauer sortieren" -#: fietsboek/templates/browse.jinja2:132 +#: fietsboek/templates/browse.jinja2:148 msgid "page.browse.organic_tooltip" msgstr "Dies ist eine Aufnahme einer Strecke" -#: fietsboek/templates/browse.jinja2:134 +#: fietsboek/templates/browse.jinja2:150 msgid "page.browse.synthetic_tooltip" msgstr "Dies ist eine geplante Strecke" -#: fietsboek/templates/browse.jinja2:142 fietsboek/templates/details.jinja2:90 +#: fietsboek/templates/browse.jinja2:158 fietsboek/templates/details.jinja2:103 #: fietsboek/templates/profile.jinja2:15 msgid "page.details.date" msgstr "Datum" -#: fietsboek/templates/browse.jinja2:144 fietsboek/templates/details.jinja2:104 +#: fietsboek/templates/browse.jinja2:160 fietsboek/templates/details.jinja2:117 #: fietsboek/templates/profile.jinja2:17 msgid "page.details.length" msgstr "Länge" -#: fietsboek/templates/browse.jinja2:149 fietsboek/templates/details.jinja2:95 +#: fietsboek/templates/browse.jinja2:165 fietsboek/templates/details.jinja2:108 #: fietsboek/templates/profile.jinja2:21 msgid "page.details.start_time" msgstr "Startzeit" -#: fietsboek/templates/browse.jinja2:151 fietsboek/templates/details.jinja2:99 +#: fietsboek/templates/browse.jinja2:167 fietsboek/templates/details.jinja2:112 #: fietsboek/templates/profile.jinja2:23 msgid "page.details.end_time" msgstr "Endzeit" -#: fietsboek/templates/browse.jinja2:156 fietsboek/templates/details.jinja2:108 +#: fietsboek/templates/browse.jinja2:172 fietsboek/templates/details.jinja2:121 #: fietsboek/templates/profile.jinja2:27 msgid "page.details.uphill" msgstr "Bergauf" -#: fietsboek/templates/browse.jinja2:158 fietsboek/templates/details.jinja2:112 +#: fietsboek/templates/browse.jinja2:174 fietsboek/templates/details.jinja2:125 #: fietsboek/templates/profile.jinja2:29 msgid "page.details.downhill" msgstr "Bergab" -#: fietsboek/templates/browse.jinja2:163 fietsboek/templates/details.jinja2:117 +#: fietsboek/templates/browse.jinja2:179 fietsboek/templates/details.jinja2:130 #: fietsboek/templates/profile.jinja2:33 msgid "page.details.moving_time" msgstr "Fahrzeit" -#: fietsboek/templates/browse.jinja2:165 fietsboek/templates/details.jinja2:121 +#: fietsboek/templates/browse.jinja2:181 fietsboek/templates/details.jinja2:134 #: fietsboek/templates/profile.jinja2:35 msgid "page.details.stopped_time" msgstr "Haltezeit" -#: fietsboek/templates/browse.jinja2:169 fietsboek/templates/details.jinja2:125 +#: fietsboek/templates/browse.jinja2:185 fietsboek/templates/details.jinja2:138 #: fietsboek/templates/profile.jinja2:39 msgid "page.details.max_speed" msgstr "maximale Geschwindigkeit" -#: fietsboek/templates/browse.jinja2:171 fietsboek/templates/details.jinja2:129 +#: fietsboek/templates/browse.jinja2:187 fietsboek/templates/details.jinja2:142 #: fietsboek/templates/profile.jinja2:41 msgid "page.details.avg_speed" msgstr "durchschnittliche Geschwindigkeit" -#: fietsboek/templates/browse.jinja2:189 +#: fietsboek/templates/browse.jinja2:205 msgid "page.browse.download_multiple" msgstr "ausgewählte Herunterladen" -#: fietsboek/templates/browse.jinja2:191 +#: fietsboek/templates/browse.jinja2:207 msgid "page.browse.no_results" msgstr "Es wurden keine Strecken gefunden, die den Filtern entsprechen." -#: fietsboek/templates/browse.jinja2:193 +#: fietsboek/templates/browse.jinja2:209 msgid "page.browse.no_tracks" msgstr "" "Es wurden keine Strecken gefunden, auf die Du Zugriff hast. Versuche, " @@ -285,90 +297,90 @@ msgstr "Passwort wiederholen" msgid "page.create_account.create" msgstr "Erstellen" -#: fietsboek/templates/details.jinja2:6 +#: fietsboek/templates/details.jinja2:7 msgid "page.details.title" msgstr "Details" -#: fietsboek/templates/details.jinja2:9 +#: fietsboek/templates/details.jinja2:20 msgid "page.details.edit" msgstr "Bearbeiten" -#: fietsboek/templates/details.jinja2:10 +#: fietsboek/templates/details.jinja2:21 msgid "page.details.share" msgstr "Teilen" -#: fietsboek/templates/details.jinja2:11 +#: fietsboek/templates/details.jinja2:22 msgid "page.details.delete" msgstr "Löschen" -#: fietsboek/templates/details.jinja2:17 +#: fietsboek/templates/details.jinja2:28 msgid "page.details.sharelink.title" msgstr "Link zum Teilen" -#: fietsboek/templates/details.jinja2:21 +#: fietsboek/templates/details.jinja2:32 msgid "page.details.sharelink.info" msgstr "Jeder mit Zugang zu diesem Link kann die Strecke ansehen!" -#: fietsboek/templates/details.jinja2:28 +#: fietsboek/templates/details.jinja2:39 msgid "page.details.sharelink.invalidate" msgstr "Link invalidieren" -#: fietsboek/templates/details.jinja2:30 +#: fietsboek/templates/details.jinja2:41 msgid "page.details.sharelink.close" msgstr "Schließen" -#: fietsboek/templates/details.jinja2:40 +#: fietsboek/templates/details.jinja2:51 msgid "page.details.delete.title" msgstr "Strecke Löschen" -#: fietsboek/templates/details.jinja2:44 +#: fietsboek/templates/details.jinja2:55 msgid "page.details.delete.info" msgstr "Das Löschen der Strecke wird alle damit verbundenen Informationen löschen!" -#: fietsboek/templates/details.jinja2:49 +#: fietsboek/templates/details.jinja2:60 msgid "page.details.delete.delete" msgstr "Löschen" -#: fietsboek/templates/details.jinja2:51 +#: fietsboek/templates/details.jinja2:62 msgid "page.details.delete.close" msgstr "Abbrechen" -#: fietsboek/templates/details.jinja2:70 +#: fietsboek/templates/details.jinja2:81 msgid "page.details.tags" msgstr "Schlagwörter" -#: fietsboek/templates/details.jinja2:80 fietsboek/templates/edit.jinja2:10 +#: fietsboek/templates/details.jinja2:91 fietsboek/templates/edit.jinja2:10 #: fietsboek/templates/finish_upload.jinja2:10 msgid "page.noscript" msgstr "" "JavaScript ist deaktiviert, zum Nutzen aller Funktionen bitte JavaScript " "aktivieren" -#: fietsboek/templates/details.jinja2:85 +#: fietsboek/templates/details.jinja2:97 msgid "page.details.download" msgstr "Herunterladen" -#: fietsboek/templates/details.jinja2:174 +#: fietsboek/templates/details.jinja2:187 msgid "page.details.comments" msgstr "Kommentare" -#: fietsboek/templates/details.jinja2:178 +#: fietsboek/templates/details.jinja2:191 msgid "page.details.comments.author" msgstr "Kommentar von {}" -#: fietsboek/templates/details.jinja2:195 +#: fietsboek/templates/details.jinja2:208 msgid "page.details.comments.new.title" msgstr "Kommentar erstellen" -#: fietsboek/templates/details.jinja2:198 +#: fietsboek/templates/details.jinja2:211 msgid "page.details.comments.new.input_title" msgstr "Titel" -#: fietsboek/templates/details.jinja2:199 +#: fietsboek/templates/details.jinja2:212 msgid "page.details.comments.new.input_comment" msgstr "Kommentar" -#: fietsboek/templates/details.jinja2:202 +#: fietsboek/templates/details.jinja2:215 msgid "page.details.comments.new.submit" msgstr "Absenden" @@ -504,13 +516,13 @@ msgstr "" "Links, um sie fortzusetzen:" #: fietsboek/templates/home.jinja2:31 fietsboek/templates/home.jinja2:38 -#: fietsboek/templates/home.jinja2:59 +#: fietsboek/templates/home.jinja2:66 msgid "page.home.summary.track" msgid_plural "page.home.summary.tracks" msgstr[0] "%(num)d Strecke" msgstr[1] "%(num)d Strecken" -#: fietsboek/templates/home.jinja2:59 +#: fietsboek/templates/home.jinja2:66 msgid "page.home.total" msgstr "Gesamt" @@ -824,23 +836,23 @@ msgstr "Konto noch nicht bestätigt" msgid "flash.logged_in" msgstr "Du bist nun angemeldet" -#: fietsboek/views/default.py:142 +#: fietsboek/views/default.py:143 msgid "flash.logged_out" msgstr "Du bist nun abgemeldet" -#: fietsboek/views/default.py:172 +#: fietsboek/views/default.py:173 msgid "flash.reset_invalid_email" msgstr "Ungültige E-Mail-Adresse angegeben" -#: fietsboek/views/default.py:177 +#: fietsboek/views/default.py:178 msgid "flash.password_token_generated" msgstr "Ein Link zum Zurücksetzen des Passworts wurde versandt" -#: fietsboek/views/default.py:182 +#: fietsboek/views/default.py:183 msgid "page.password_reset.email.subject" msgstr "Fietsboek Passwortzurücksetzung" -#: fietsboek/views/default.py:185 +#: fietsboek/views/default.py:186 msgid "page.password_reset.email.body" msgstr "" "Du kannst Dein Fietsboek-Passwort hier zurücksetzen: {}\n" @@ -848,27 +860,27 @@ msgstr "" "Falls Du keine Passwortzurücksetzung beantragt hast, dann ignoriere diese" " E-Mail." -#: fietsboek/views/default.py:224 +#: fietsboek/views/default.py:225 msgid "flash.resend_verification_email_fail" msgstr "Ungültige E-Mail-Adresse angegeben" -#: fietsboek/views/default.py:229 +#: fietsboek/views/default.py:230 msgid "flash.verification_token_generated" msgstr "Ein Link zur Bestätigung Deines Kontos wurde versandt" -#: fietsboek/views/default.py:249 +#: fietsboek/views/default.py:250 msgid "flash.token_expired" msgstr "Der Link ist nicht mehr gültig" -#: fietsboek/views/default.py:255 +#: fietsboek/views/default.py:256 msgid "flash.email_verified" msgstr "E-Mail-Adresse bestätigt" -#: fietsboek/views/default.py:269 +#: fietsboek/views/default.py:270 msgid "flash.password_updated" msgstr "Passwort aktualisiert" -#: fietsboek/views/detail.py:155 +#: fietsboek/views/detail.py:161 msgid "flash.track_deleted" msgstr "Strecke gelöscht" @@ -888,23 +900,23 @@ msgstr "Hochladen erfolgreich" msgid "flash.upload_cancelled" msgstr "Hochladen abgebrochen" -#: fietsboek/views/user_data.py:60 +#: fietsboek/views/user_data.py:61 msgid "flash.personal_data_updated" msgstr "Persönliche Daten wurden gespeichert" -#: fietsboek/views/user_data.py:78 +#: fietsboek/views/user_data.py:79 msgid "flash.friend_not_found" msgstr "Das angegebene Konto wurde nicht gefunden" -#: fietsboek/views/user_data.py:84 +#: fietsboek/views/user_data.py:85 msgid "flash.friend_already_exists" msgstr "Dieser Freund existiert bereits" -#: fietsboek/views/user_data.py:92 +#: fietsboek/views/user_data.py:93 msgid "flash.friend_added" msgstr "Freund hinzugefügt" -#: fietsboek/views/user_data.py:102 +#: fietsboek/views/user_data.py:103 msgid "flash.friend_request_sent" msgstr "Freundschaftsanfrage gesendet" diff --git a/fietsboek/locale/en/LC_MESSAGES/messages.mo b/fietsboek/locale/en/LC_MESSAGES/messages.mo Binary files differindex 2a6205f..56137c1 100644 --- a/fietsboek/locale/en/LC_MESSAGES/messages.mo +++ b/fietsboek/locale/en/LC_MESSAGES/messages.mo diff --git a/fietsboek/locale/en/LC_MESSAGES/messages.po b/fietsboek/locale/en/LC_MESSAGES/messages.po index 6c13d78..c1ff29e 100644 --- a/fietsboek/locale/en/LC_MESSAGES/messages.po +++ b/fietsboek/locale/en/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-07-22 14:31+0200\n" +"POT-Creation-Date: 2023-08-17 22:51+0200\n" "PO-Revision-Date: 2023-04-03 20:42+0200\n" "Last-Translator: \n" "Language: en\n" @@ -37,35 +37,35 @@ msgstr "Passwords don't match" msgid "password_constraint.length" msgstr "Password not long enough" -#: fietsboek/models/track.py:589 +#: fietsboek/models/track.py:602 msgid "tooltip.table.length" msgstr "Length" -#: fietsboek/models/track.py:590 +#: fietsboek/models/track.py:603 msgid "tooltip.table.people" msgstr "# People" -#: fietsboek/models/track.py:591 +#: fietsboek/models/track.py:604 msgid "tooltip.table.uphill" msgstr "Uphill" -#: fietsboek/models/track.py:592 +#: fietsboek/models/track.py:605 msgid "tooltip.table.downhill" msgstr "Downhill" -#: fietsboek/models/track.py:593 +#: fietsboek/models/track.py:606 msgid "tooltip.table.moving_time" msgstr "Moving Time" -#: fietsboek/models/track.py:594 +#: fietsboek/models/track.py:607 msgid "tooltip.table.stopped_time" msgstr "Stopped Time" -#: fietsboek/models/track.py:596 +#: fietsboek/models/track.py:609 msgid "tooltip.table.max_speed" msgstr "Max Speed" -#: fietsboek/models/track.py:600 +#: fietsboek/models/track.py:613 msgid "tooltip.table.avg_speed" msgstr "Average Speed" @@ -137,109 +137,121 @@ msgstr "Recording (organic)" msgid "page.browse.filter.type.synthetic" msgstr "Template (synthetic)" -#: fietsboek/templates/browse.jinja2:86 +#: fietsboek/templates/browse.jinja2:82 +msgid "page.browse.filter.favourite.all" +msgstr "All Tracks" + +#: fietsboek/templates/browse.jinja2:83 +msgid "page.browse.filter.favourite.yes" +msgstr "Only Favourites" + +#: fietsboek/templates/browse.jinja2:84 +msgid "page.browse.filter.favourite.no" +msgstr "No Favourites" + +#: fietsboek/templates/browse.jinja2:97 msgid "page.browse.filter.my_tracks.only" msgstr "Show only my own tracks" -#: fietsboek/templates/browse.jinja2:87 +#: fietsboek/templates/browse.jinja2:98 msgid "page.browse.filter.friends_tracks_only" msgstr "Show only tracks of my friends" -#: fietsboek/templates/browse.jinja2:88 +#: fietsboek/templates/browse.jinja2:99 msgid "page.browse.filter.me_tagged_only" msgstr "Show only tracks in which I'm tagged" -#: fietsboek/templates/browse.jinja2:98 +#: fietsboek/templates/browse.jinja2:109 msgid "page.browse.filters.apply" msgstr "Apply filters" -#: fietsboek/templates/browse.jinja2:102 +#: fietsboek/templates/browse.jinja2:113 msgid "page.browse.filters.clear_all" msgstr "Remove filters" -#: fietsboek/templates/browse.jinja2:105 +#: fietsboek/templates/browse.jinja2:116 msgid "page.browse.filters.expand_advanced" msgstr "Advanced" -#: fietsboek/templates/browse.jinja2:110 fietsboek/templates/browse.jinja2:111 +#: fietsboek/templates/browse.jinja2:121 fietsboek/templates/browse.jinja2:122 msgid "page.browse.sort.date" msgstr "Sort by date" -#: fietsboek/templates/browse.jinja2:112 fietsboek/templates/browse.jinja2:113 +#: fietsboek/templates/browse.jinja2:123 fietsboek/templates/browse.jinja2:124 msgid "page.browse.sort.length" msgstr "Sort by length" -#: fietsboek/templates/browse.jinja2:114 fietsboek/templates/browse.jinja2:115 +#: fietsboek/templates/browse.jinja2:125 fietsboek/templates/browse.jinja2:126 msgid "page.browse.sort.duration" msgstr "Sort by duration" -#: fietsboek/templates/browse.jinja2:132 +#: fietsboek/templates/browse.jinja2:148 msgid "page.browse.organic_tooltip" msgstr "This is a recording of a track" -#: fietsboek/templates/browse.jinja2:134 +#: fietsboek/templates/browse.jinja2:150 msgid "page.browse.synthetic_tooltip" msgstr "This is a pre-planned track" -#: fietsboek/templates/browse.jinja2:142 fietsboek/templates/details.jinja2:90 +#: fietsboek/templates/browse.jinja2:158 fietsboek/templates/details.jinja2:103 #: fietsboek/templates/profile.jinja2:15 msgid "page.details.date" msgstr "Date" -#: fietsboek/templates/browse.jinja2:144 fietsboek/templates/details.jinja2:104 +#: fietsboek/templates/browse.jinja2:160 fietsboek/templates/details.jinja2:117 #: fietsboek/templates/profile.jinja2:17 msgid "page.details.length" msgstr "Length" -#: fietsboek/templates/browse.jinja2:149 fietsboek/templates/details.jinja2:95 +#: fietsboek/templates/browse.jinja2:165 fietsboek/templates/details.jinja2:108 #: fietsboek/templates/profile.jinja2:21 msgid "page.details.start_time" msgstr "Record Start" -#: fietsboek/templates/browse.jinja2:151 fietsboek/templates/details.jinja2:99 +#: fietsboek/templates/browse.jinja2:167 fietsboek/templates/details.jinja2:112 #: fietsboek/templates/profile.jinja2:23 msgid "page.details.end_time" msgstr "Record End" -#: fietsboek/templates/browse.jinja2:156 fietsboek/templates/details.jinja2:108 +#: fietsboek/templates/browse.jinja2:172 fietsboek/templates/details.jinja2:121 #: fietsboek/templates/profile.jinja2:27 msgid "page.details.uphill" msgstr "Uphill" -#: fietsboek/templates/browse.jinja2:158 fietsboek/templates/details.jinja2:112 +#: fietsboek/templates/browse.jinja2:174 fietsboek/templates/details.jinja2:125 #: fietsboek/templates/profile.jinja2:29 msgid "page.details.downhill" msgstr "Downhill" -#: fietsboek/templates/browse.jinja2:163 fietsboek/templates/details.jinja2:117 +#: fietsboek/templates/browse.jinja2:179 fietsboek/templates/details.jinja2:130 #: fietsboek/templates/profile.jinja2:33 msgid "page.details.moving_time" msgstr "Moving Time" -#: fietsboek/templates/browse.jinja2:165 fietsboek/templates/details.jinja2:121 +#: fietsboek/templates/browse.jinja2:181 fietsboek/templates/details.jinja2:134 #: fietsboek/templates/profile.jinja2:35 msgid "page.details.stopped_time" msgstr "Stopped Time" -#: fietsboek/templates/browse.jinja2:169 fietsboek/templates/details.jinja2:125 +#: fietsboek/templates/browse.jinja2:185 fietsboek/templates/details.jinja2:138 #: fietsboek/templates/profile.jinja2:39 msgid "page.details.max_speed" msgstr "Max Speed" -#: fietsboek/templates/browse.jinja2:171 fietsboek/templates/details.jinja2:129 +#: fietsboek/templates/browse.jinja2:187 fietsboek/templates/details.jinja2:142 #: fietsboek/templates/profile.jinja2:41 msgid "page.details.avg_speed" msgstr "Average Speed" -#: fietsboek/templates/browse.jinja2:189 +#: fietsboek/templates/browse.jinja2:205 msgid "page.browse.download_multiple" msgstr "Download selected" -#: fietsboek/templates/browse.jinja2:191 +#: fietsboek/templates/browse.jinja2:207 msgid "page.browse.no_results" msgstr "No results matching the filters were found." -#: fietsboek/templates/browse.jinja2:193 +#: fietsboek/templates/browse.jinja2:209 msgid "page.browse.no_tracks" msgstr "You currently do not have access to any tracks. Try logging in." @@ -283,88 +295,88 @@ msgstr "Repeat password" msgid "page.create_account.create" msgstr "Create" -#: fietsboek/templates/details.jinja2:6 +#: fietsboek/templates/details.jinja2:7 msgid "page.details.title" msgstr "Track Details" -#: fietsboek/templates/details.jinja2:9 +#: fietsboek/templates/details.jinja2:20 msgid "page.details.edit" msgstr "Edit" -#: fietsboek/templates/details.jinja2:10 +#: fietsboek/templates/details.jinja2:21 msgid "page.details.share" msgstr "Share" -#: fietsboek/templates/details.jinja2:11 +#: fietsboek/templates/details.jinja2:22 msgid "page.details.delete" msgstr "Delete" -#: fietsboek/templates/details.jinja2:17 +#: fietsboek/templates/details.jinja2:28 msgid "page.details.sharelink.title" msgstr "Share Link" -#: fietsboek/templates/details.jinja2:21 +#: fietsboek/templates/details.jinja2:32 msgid "page.details.sharelink.info" msgstr "Everyone with access to this link can view the track!" -#: fietsboek/templates/details.jinja2:28 +#: fietsboek/templates/details.jinja2:39 msgid "page.details.sharelink.invalidate" msgstr "Invalidate link" -#: fietsboek/templates/details.jinja2:30 +#: fietsboek/templates/details.jinja2:41 msgid "page.details.sharelink.close" msgstr "Close" -#: fietsboek/templates/details.jinja2:40 +#: fietsboek/templates/details.jinja2:51 msgid "page.details.delete.title" msgstr "Delete Track" -#: fietsboek/templates/details.jinja2:44 +#: fietsboek/templates/details.jinja2:55 msgid "page.details.delete.info" msgstr "Deleting this track will remove all associated information with it!" -#: fietsboek/templates/details.jinja2:49 +#: fietsboek/templates/details.jinja2:60 msgid "page.details.delete.delete" msgstr "Delete" -#: fietsboek/templates/details.jinja2:51 +#: fietsboek/templates/details.jinja2:62 msgid "page.details.delete.close" msgstr "Abort" -#: fietsboek/templates/details.jinja2:70 +#: fietsboek/templates/details.jinja2:81 msgid "page.details.tags" msgstr "Tagged as" -#: fietsboek/templates/details.jinja2:80 fietsboek/templates/edit.jinja2:10 +#: fietsboek/templates/details.jinja2:91 fietsboek/templates/edit.jinja2:10 #: fietsboek/templates/finish_upload.jinja2:10 msgid "page.noscript" msgstr "JavaScript is disabled, please enable JavaScript" -#: fietsboek/templates/details.jinja2:85 +#: fietsboek/templates/details.jinja2:97 msgid "page.details.download" msgstr "Download Tour" -#: fietsboek/templates/details.jinja2:174 +#: fietsboek/templates/details.jinja2:187 msgid "page.details.comments" msgstr "Comments" -#: fietsboek/templates/details.jinja2:178 +#: fietsboek/templates/details.jinja2:191 msgid "page.details.comments.author" msgstr "Comment by {}" -#: fietsboek/templates/details.jinja2:195 +#: fietsboek/templates/details.jinja2:208 msgid "page.details.comments.new.title" msgstr "Create a new comment" -#: fietsboek/templates/details.jinja2:198 +#: fietsboek/templates/details.jinja2:211 msgid "page.details.comments.new.input_title" msgstr "Title" -#: fietsboek/templates/details.jinja2:199 +#: fietsboek/templates/details.jinja2:212 msgid "page.details.comments.new.input_comment" msgstr "Comment" -#: fietsboek/templates/details.jinja2:202 +#: fietsboek/templates/details.jinja2:215 msgid "page.details.comments.new.submit" msgstr "Submit" @@ -498,13 +510,13 @@ msgid "page.home.unfinished_uploads" msgstr "You have unfinished uploads. Click on the links below to resume them:" #: fietsboek/templates/home.jinja2:31 fietsboek/templates/home.jinja2:38 -#: fietsboek/templates/home.jinja2:59 +#: fietsboek/templates/home.jinja2:66 msgid "page.home.summary.track" msgid_plural "page.home.summary.tracks" msgstr[0] "%(num)d track" msgstr[1] "%(num)d tracks" -#: fietsboek/templates/home.jinja2:59 +#: fietsboek/templates/home.jinja2:66 msgid "page.home.total" msgstr "Total" @@ -814,50 +826,50 @@ msgstr "Your account is not verified yet" msgid "flash.logged_in" msgstr "You are now logged in" -#: fietsboek/views/default.py:142 +#: fietsboek/views/default.py:143 msgid "flash.logged_out" msgstr "You have been logged out" -#: fietsboek/views/default.py:172 +#: fietsboek/views/default.py:173 msgid "flash.reset_invalid_email" msgstr "Invalid email address provided" -#: fietsboek/views/default.py:177 +#: fietsboek/views/default.py:178 msgid "flash.password_token_generated" msgstr "A password reset email has been sent" -#: fietsboek/views/default.py:182 +#: fietsboek/views/default.py:183 msgid "page.password_reset.email.subject" msgstr "Fietsboek Password Reset" -#: fietsboek/views/default.py:185 +#: fietsboek/views/default.py:186 msgid "page.password_reset.email.body" msgstr "" "You can reset your Fietsboek password here: {}\n" "\n" "If you did not request a password reset, ignore this email." -#: fietsboek/views/default.py:224 +#: fietsboek/views/default.py:225 msgid "flash.resend_verification_email_fail" msgstr "Invalid email address provided" -#: fietsboek/views/default.py:229 +#: fietsboek/views/default.py:230 msgid "flash.verification_token_generated" msgstr "A verification email has been sent" -#: fietsboek/views/default.py:249 +#: fietsboek/views/default.py:250 msgid "flash.token_expired" msgstr "The link has expired" -#: fietsboek/views/default.py:255 +#: fietsboek/views/default.py:256 msgid "flash.email_verified" msgstr "Your email address has been verified" -#: fietsboek/views/default.py:269 +#: fietsboek/views/default.py:270 msgid "flash.password_updated" msgstr "Password has been updated" -#: fietsboek/views/detail.py:155 +#: fietsboek/views/detail.py:161 msgid "flash.track_deleted" msgstr "Track has been deleted" @@ -877,23 +889,23 @@ msgstr "Upload successful" msgid "flash.upload_cancelled" msgstr "Upload cancelled" -#: fietsboek/views/user_data.py:60 +#: fietsboek/views/user_data.py:61 msgid "flash.personal_data_updated" msgstr "Personal data has been updated" -#: fietsboek/views/user_data.py:78 +#: fietsboek/views/user_data.py:79 msgid "flash.friend_not_found" msgstr "The friend was not found" -#: fietsboek/views/user_data.py:84 +#: fietsboek/views/user_data.py:85 msgid "flash.friend_already_exists" msgstr "Friend already exists" -#: fietsboek/views/user_data.py:92 +#: fietsboek/views/user_data.py:93 msgid "flash.friend_added" msgstr "Friend has been added" -#: fietsboek/views/user_data.py:102 +#: fietsboek/views/user_data.py:103 msgid "flash.friend_request_sent" msgstr "Friend request sent" diff --git a/fietsboek/locale/fietslog.pot b/fietsboek/locale/fietslog.pot index f6577af..ed93194 100644 --- a/fietsboek/locale/fietslog.pot +++ b/fietsboek/locale/fietslog.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-07-22 14:31+0200\n" +"POT-Creation-Date: 2023-08-17 22:51+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -33,35 +33,35 @@ msgstr "" msgid "password_constraint.length" msgstr "" -#: fietsboek/models/track.py:589 +#: fietsboek/models/track.py:602 msgid "tooltip.table.length" msgstr "" -#: fietsboek/models/track.py:590 +#: fietsboek/models/track.py:603 msgid "tooltip.table.people" msgstr "" -#: fietsboek/models/track.py:591 +#: fietsboek/models/track.py:604 msgid "tooltip.table.uphill" msgstr "" -#: fietsboek/models/track.py:592 +#: fietsboek/models/track.py:605 msgid "tooltip.table.downhill" msgstr "" -#: fietsboek/models/track.py:593 +#: fietsboek/models/track.py:606 msgid "tooltip.table.moving_time" msgstr "" -#: fietsboek/models/track.py:594 +#: fietsboek/models/track.py:607 msgid "tooltip.table.stopped_time" msgstr "" -#: fietsboek/models/track.py:596 +#: fietsboek/models/track.py:609 msgid "tooltip.table.max_speed" msgstr "" -#: fietsboek/models/track.py:600 +#: fietsboek/models/track.py:613 msgid "tooltip.table.avg_speed" msgstr "" @@ -133,109 +133,121 @@ msgstr "" msgid "page.browse.filter.type.synthetic" msgstr "" -#: fietsboek/templates/browse.jinja2:86 +#: fietsboek/templates/browse.jinja2:82 +msgid "page.browse.filter.favourite.all" +msgstr "" + +#: fietsboek/templates/browse.jinja2:83 +msgid "page.browse.filter.favourite.yes" +msgstr "" + +#: fietsboek/templates/browse.jinja2:84 +msgid "page.browse.filter.favourite.no" +msgstr "" + +#: fietsboek/templates/browse.jinja2:97 msgid "page.browse.filter.my_tracks.only" msgstr "" -#: fietsboek/templates/browse.jinja2:87 +#: fietsboek/templates/browse.jinja2:98 msgid "page.browse.filter.friends_tracks_only" msgstr "" -#: fietsboek/templates/browse.jinja2:88 +#: fietsboek/templates/browse.jinja2:99 msgid "page.browse.filter.me_tagged_only" msgstr "" -#: fietsboek/templates/browse.jinja2:98 +#: fietsboek/templates/browse.jinja2:109 msgid "page.browse.filters.apply" msgstr "" -#: fietsboek/templates/browse.jinja2:102 +#: fietsboek/templates/browse.jinja2:113 msgid "page.browse.filters.clear_all" msgstr "" -#: fietsboek/templates/browse.jinja2:105 +#: fietsboek/templates/browse.jinja2:116 msgid "page.browse.filters.expand_advanced" msgstr "" -#: fietsboek/templates/browse.jinja2:110 fietsboek/templates/browse.jinja2:111 +#: fietsboek/templates/browse.jinja2:121 fietsboek/templates/browse.jinja2:122 msgid "page.browse.sort.date" msgstr "" -#: fietsboek/templates/browse.jinja2:112 fietsboek/templates/browse.jinja2:113 +#: fietsboek/templates/browse.jinja2:123 fietsboek/templates/browse.jinja2:124 msgid "page.browse.sort.length" msgstr "" -#: fietsboek/templates/browse.jinja2:114 fietsboek/templates/browse.jinja2:115 +#: fietsboek/templates/browse.jinja2:125 fietsboek/templates/browse.jinja2:126 msgid "page.browse.sort.duration" msgstr "" -#: fietsboek/templates/browse.jinja2:132 +#: fietsboek/templates/browse.jinja2:148 msgid "page.browse.organic_tooltip" msgstr "" -#: fietsboek/templates/browse.jinja2:134 +#: fietsboek/templates/browse.jinja2:150 msgid "page.browse.synthetic_tooltip" msgstr "" -#: fietsboek/templates/browse.jinja2:142 fietsboek/templates/details.jinja2:90 +#: fietsboek/templates/browse.jinja2:158 fietsboek/templates/details.jinja2:103 #: fietsboek/templates/profile.jinja2:15 msgid "page.details.date" msgstr "" -#: fietsboek/templates/browse.jinja2:144 fietsboek/templates/details.jinja2:104 +#: fietsboek/templates/browse.jinja2:160 fietsboek/templates/details.jinja2:117 #: fietsboek/templates/profile.jinja2:17 msgid "page.details.length" msgstr "" -#: fietsboek/templates/browse.jinja2:149 fietsboek/templates/details.jinja2:95 +#: fietsboek/templates/browse.jinja2:165 fietsboek/templates/details.jinja2:108 #: fietsboek/templates/profile.jinja2:21 msgid "page.details.start_time" msgstr "" -#: fietsboek/templates/browse.jinja2:151 fietsboek/templates/details.jinja2:99 +#: fietsboek/templates/browse.jinja2:167 fietsboek/templates/details.jinja2:112 #: fietsboek/templates/profile.jinja2:23 msgid "page.details.end_time" msgstr "" -#: fietsboek/templates/browse.jinja2:156 fietsboek/templates/details.jinja2:108 +#: fietsboek/templates/browse.jinja2:172 fietsboek/templates/details.jinja2:121 #: fietsboek/templates/profile.jinja2:27 msgid "page.details.uphill" msgstr "" -#: fietsboek/templates/browse.jinja2:158 fietsboek/templates/details.jinja2:112 +#: fietsboek/templates/browse.jinja2:174 fietsboek/templates/details.jinja2:125 #: fietsboek/templates/profile.jinja2:29 msgid "page.details.downhill" msgstr "" -#: fietsboek/templates/browse.jinja2:163 fietsboek/templates/details.jinja2:117 +#: fietsboek/templates/browse.jinja2:179 fietsboek/templates/details.jinja2:130 #: fietsboek/templates/profile.jinja2:33 msgid "page.details.moving_time" msgstr "" -#: fietsboek/templates/browse.jinja2:165 fietsboek/templates/details.jinja2:121 +#: fietsboek/templates/browse.jinja2:181 fietsboek/templates/details.jinja2:134 #: fietsboek/templates/profile.jinja2:35 msgid "page.details.stopped_time" msgstr "" -#: fietsboek/templates/browse.jinja2:169 fietsboek/templates/details.jinja2:125 +#: fietsboek/templates/browse.jinja2:185 fietsboek/templates/details.jinja2:138 #: fietsboek/templates/profile.jinja2:39 msgid "page.details.max_speed" msgstr "" -#: fietsboek/templates/browse.jinja2:171 fietsboek/templates/details.jinja2:129 +#: fietsboek/templates/browse.jinja2:187 fietsboek/templates/details.jinja2:142 #: fietsboek/templates/profile.jinja2:41 msgid "page.details.avg_speed" msgstr "" -#: fietsboek/templates/browse.jinja2:189 +#: fietsboek/templates/browse.jinja2:205 msgid "page.browse.download_multiple" msgstr "" -#: fietsboek/templates/browse.jinja2:191 +#: fietsboek/templates/browse.jinja2:207 msgid "page.browse.no_results" msgstr "" -#: fietsboek/templates/browse.jinja2:193 +#: fietsboek/templates/browse.jinja2:209 msgid "page.browse.no_tracks" msgstr "" @@ -279,88 +291,88 @@ msgstr "" msgid "page.create_account.create" msgstr "" -#: fietsboek/templates/details.jinja2:6 +#: fietsboek/templates/details.jinja2:7 msgid "page.details.title" msgstr "" -#: fietsboek/templates/details.jinja2:9 +#: fietsboek/templates/details.jinja2:20 msgid "page.details.edit" msgstr "" -#: fietsboek/templates/details.jinja2:10 +#: fietsboek/templates/details.jinja2:21 msgid "page.details.share" msgstr "" -#: fietsboek/templates/details.jinja2:11 +#: fietsboek/templates/details.jinja2:22 msgid "page.details.delete" msgstr "" -#: fietsboek/templates/details.jinja2:17 +#: fietsboek/templates/details.jinja2:28 msgid "page.details.sharelink.title" msgstr "" -#: fietsboek/templates/details.jinja2:21 +#: fietsboek/templates/details.jinja2:32 msgid "page.details.sharelink.info" msgstr "" -#: fietsboek/templates/details.jinja2:28 +#: fietsboek/templates/details.jinja2:39 msgid "page.details.sharelink.invalidate" msgstr "" -#: fietsboek/templates/details.jinja2:30 +#: fietsboek/templates/details.jinja2:41 msgid "page.details.sharelink.close" msgstr "" -#: fietsboek/templates/details.jinja2:40 +#: fietsboek/templates/details.jinja2:51 msgid "page.details.delete.title" msgstr "" -#: fietsboek/templates/details.jinja2:44 +#: fietsboek/templates/details.jinja2:55 msgid "page.details.delete.info" msgstr "" -#: fietsboek/templates/details.jinja2:49 +#: fietsboek/templates/details.jinja2:60 msgid "page.details.delete.delete" msgstr "" -#: fietsboek/templates/details.jinja2:51 +#: fietsboek/templates/details.jinja2:62 msgid "page.details.delete.close" msgstr "" -#: fietsboek/templates/details.jinja2:70 +#: fietsboek/templates/details.jinja2:81 msgid "page.details.tags" msgstr "" -#: fietsboek/templates/details.jinja2:80 fietsboek/templates/edit.jinja2:10 +#: fietsboek/templates/details.jinja2:91 fietsboek/templates/edit.jinja2:10 #: fietsboek/templates/finish_upload.jinja2:10 msgid "page.noscript" msgstr "" -#: fietsboek/templates/details.jinja2:85 +#: fietsboek/templates/details.jinja2:97 msgid "page.details.download" msgstr "" -#: fietsboek/templates/details.jinja2:174 +#: fietsboek/templates/details.jinja2:187 msgid "page.details.comments" msgstr "" -#: fietsboek/templates/details.jinja2:178 +#: fietsboek/templates/details.jinja2:191 msgid "page.details.comments.author" msgstr "" -#: fietsboek/templates/details.jinja2:195 +#: fietsboek/templates/details.jinja2:208 msgid "page.details.comments.new.title" msgstr "" -#: fietsboek/templates/details.jinja2:198 +#: fietsboek/templates/details.jinja2:211 msgid "page.details.comments.new.input_title" msgstr "" -#: fietsboek/templates/details.jinja2:199 +#: fietsboek/templates/details.jinja2:212 msgid "page.details.comments.new.input_comment" msgstr "" -#: fietsboek/templates/details.jinja2:202 +#: fietsboek/templates/details.jinja2:215 msgid "page.details.comments.new.submit" msgstr "" @@ -492,13 +504,13 @@ msgid "page.home.unfinished_uploads" msgstr "" #: fietsboek/templates/home.jinja2:31 fietsboek/templates/home.jinja2:38 -#: fietsboek/templates/home.jinja2:59 +#: fietsboek/templates/home.jinja2:66 msgid "page.home.summary.track" msgid_plural "page.home.summary.tracks" msgstr[0] "" msgstr[1] "" -#: fietsboek/templates/home.jinja2:59 +#: fietsboek/templates/home.jinja2:66 msgid "page.home.total" msgstr "" @@ -806,47 +818,47 @@ msgstr "" msgid "flash.logged_in" msgstr "" -#: fietsboek/views/default.py:142 +#: fietsboek/views/default.py:143 msgid "flash.logged_out" msgstr "" -#: fietsboek/views/default.py:172 +#: fietsboek/views/default.py:173 msgid "flash.reset_invalid_email" msgstr "" -#: fietsboek/views/default.py:177 +#: fietsboek/views/default.py:178 msgid "flash.password_token_generated" msgstr "" -#: fietsboek/views/default.py:182 +#: fietsboek/views/default.py:183 msgid "page.password_reset.email.subject" msgstr "" -#: fietsboek/views/default.py:185 +#: fietsboek/views/default.py:186 msgid "page.password_reset.email.body" msgstr "" -#: fietsboek/views/default.py:224 +#: fietsboek/views/default.py:225 msgid "flash.resend_verification_email_fail" msgstr "" -#: fietsboek/views/default.py:229 +#: fietsboek/views/default.py:230 msgid "flash.verification_token_generated" msgstr "" -#: fietsboek/views/default.py:249 +#: fietsboek/views/default.py:250 msgid "flash.token_expired" msgstr "" -#: fietsboek/views/default.py:255 +#: fietsboek/views/default.py:256 msgid "flash.email_verified" msgstr "" -#: fietsboek/views/default.py:269 +#: fietsboek/views/default.py:270 msgid "flash.password_updated" msgstr "" -#: fietsboek/views/detail.py:155 +#: fietsboek/views/detail.py:161 msgid "flash.track_deleted" msgstr "" @@ -866,23 +878,23 @@ msgstr "" msgid "flash.upload_cancelled" msgstr "" -#: fietsboek/views/user_data.py:60 +#: fietsboek/views/user_data.py:61 msgid "flash.personal_data_updated" msgstr "" -#: fietsboek/views/user_data.py:78 +#: fietsboek/views/user_data.py:79 msgid "flash.friend_not_found" msgstr "" -#: fietsboek/views/user_data.py:84 +#: fietsboek/views/user_data.py:85 msgid "flash.friend_already_exists" msgstr "" -#: fietsboek/views/user_data.py:92 +#: fietsboek/views/user_data.py:93 msgid "flash.friend_added" msgstr "" -#: fietsboek/views/user_data.py:102 +#: fietsboek/views/user_data.py:103 msgid "flash.friend_request_sent" msgstr "" diff --git a/fietsboek/models/track.py b/fietsboek/models/track.py index a78e56a..0f5fd39 100644 --- a/fietsboek/models/track.py +++ b/fietsboek/models/track.py @@ -123,6 +123,14 @@ track_badge_assoc = Table( Column("badge_id", ForeignKey("badges.id"), primary_key=True), ) + +track_favourite_assoc = Table( + "track_favourite_assoc", + Base.metadata, + Column("track_id", ForeignKey("tracks.id"), primary_key=True), + Column("user_id", ForeignKey("users.id"), primary_key=True), +) + # Some words about timezone handling in saved tracks: # https://www.youtube.com/watch?v=-5wpm-gesOY # @@ -190,6 +198,8 @@ class Track(Base): :vartype comments: list[fietsboek.models.comment.Comment] :ivar images: Metadata of the images saved for this track. :vartype images: list[fietsboek.models.image.ImageMetadata] + :ivar favourees: List of users that have this track as a favourite. + :vartype favourees: list[fietsboek.models.user.User] """ __tablename__ = "tracks" @@ -223,6 +233,9 @@ class Track(Base): images: Mapped[list["models.ImageMetadata"]] = relationship( "ImageMetadata", back_populates="track", cascade="all, delete-orphan" ) + favourees: Mapped[list["models.User"]] = relationship( + "User", secondary=track_favourite_assoc, back_populates="favourite_tracks" + ) @classmethod def factory(cls, request): @@ -683,6 +696,11 @@ class TrackWithMetadata: """Images of the underlying track.""" return self.track.images[:] + @property + def favourees(self) -> List["models.User"]: + """People who have favoured this track.""" + return self.track.favourees[:] + def text_tags(self) -> Set[str]: """Returns a set of textual tags. diff --git a/fietsboek/models/user.py b/fietsboek/models/user.py index 9dd2298..432c61d 100644 --- a/fietsboek/models/user.py +++ b/fietsboek/models/user.py @@ -97,7 +97,7 @@ class User(Base): :ivar tracks: Tracks owned by this user. :vartype tracks: list[fietsboek.models.track.Track] :ivar tagged_tracks: Tracks in which this user is tagged. - :vartype tracks: list[fietsboek.models.track.Track] + :vartype tagged_tracks: list[fietsboek.models.track.Track] :ivar uploads: Currently ongoing uploads by this user. :vartype uploads: list[fietsboek.models.track.Upload] :ivar tokens: List of tokens that this user can use. @@ -121,6 +121,9 @@ class User(Base): tagged_tracks: Mapped[list["Track"]] = relationship( "Track", secondary="track_people_assoc", back_populates="tagged_people" ) + favourite_tracks: Mapped[list["Track"]] = relationship( + "Track", secondary="track_favourite_assoc", back_populates="favourees" + ) uploads: Mapped[list["Upload"]] = relationship( "Upload", back_populates="owner", cascade="all, delete-orphan" ) @@ -424,6 +427,16 @@ class User(Base): stmt = friends_assoc.insert().values(user_1_id=self.id, user_2_id=friend.id) session.execute(stmt) + def toggle_favourite(self, track: "Track"): + """Toggles the favourite status for the given track. + + :param track: The track to (un)favour. + """ + if track in self.favourite_tracks: + self.favourite_tracks.remove(track) + else: + self.favourite_tracks.append(track) + def autocomplete_tags(self): """Returns all tags the user has ever used, suitable for autocompletion lists. diff --git a/fietsboek/routes.py b/fietsboek/routes.py index cfcc7bf..36233e6 100644 --- a/fietsboek/routes.py +++ b/fietsboek/routes.py @@ -59,6 +59,7 @@ def includeme(config): config.add_route("delete-friend", "/me/delete-friend") config.add_route("accept-friend", "/me/accept-friend") config.add_route("json-friends", "/me/friends.json") + config.add_route("toggle-favourite", "/me/toggle-favourite") config.add_route("profile", "/user/{user_id}", factory="fietsboek.models.User.factory") config.add_route( diff --git a/fietsboek/static/fietsboek.js b/fietsboek/static/fietsboek.js index 0179005..c9dfe2a 100644 --- a/fietsboek/static/fietsboek.js +++ b/fietsboek/static/fietsboek.js @@ -2,6 +2,17 @@ // Make eslint happy about the Window redefinition (_) => null; /** + * Gets the value of a single cookie. + * + * @param name - Name of the cookie. + * @return The cookie value, or null. + */ +function getCookie(name) { + var _a; + return (_a = document.cookie.split("; ") + .find((row) => row.startsWith(`${name}=`))) === null || _a === void 0 ? void 0 : _a.split("=")[1]; +} +/** * Installs a listener to the given DOM objects. * * @param selector - The query selector to find the DOM objects. @@ -329,14 +340,46 @@ addHandler(".button-clear-input", "click", clearInputButtonClicked); * @param event - The triggering event. */ function changeHomeSorting(_event) { - var _a, _b; - const currentSorting = (_b = (_a = document.cookie.split("; ") - .find((row) => row.startsWith("home_sorting="))) === null || _a === void 0 ? void 0 : _a.split("=")[1]) !== null && _b !== void 0 ? _b : "asc"; + var _a; + const currentSorting = (_a = getCookie("home_sorting")) !== null && _a !== void 0 ? _a : "asc"; const newSorting = currentSorting == "asc" ? "desc" : "asc"; document.cookie = `home_sorting=${newSorting}; SameSite=Lax`; window.location.reload(); } addHandler("#changeHomeSorting", "click", changeHomeSorting); +/** + * Handler to toggle the favourite status of a track. + * + * This is applied to .favourite-star elements and expects the track ID in + * data-track-id. + * + * @param event - The triggering event. + */ +function toggleTrackFavourite(event) { + var _a; + const target = event.target; + const trackId = target.getAttribute("data-track-id"); + if (trackId === null) { + return; + } + const url = new URL("/me/toggle-favourite", window.location.href); + const formData = new URLSearchParams(); + formData.append("track-id", trackId); + formData.append("csrf_token", (_a = getCookie("csrf_token")) !== null && _a !== void 0 ? _a : ""); + fetch(url, { + "method": "POST", + "body": formData, + }).then(response => response.json().then(data => { + const isNowFavourite = data["favourite"]; + if (isNowFavourite) { + target.classList.replace("bi-star", "bi-star-fill"); + } + else { + target.classList.replace("bi-star-fill", "bi-star"); + } + })); +} +addHandler(".favourite-star", "click", toggleTrackFavourite); document.addEventListener('DOMContentLoaded', function () { window.fietsboekImageIndex = 0; /* Enable tooltips */ diff --git a/fietsboek/static/fietsboek.js.map b/fietsboek/static/fietsboek.js.map index 7304128..557b442 100644 --- a/fietsboek/static/fietsboek.js.map +++ b/fietsboek/static/fietsboek.js.map @@ -1 +1 @@ -{"version":3,"file":"fietsboek.js","sourceRoot":"","sources":["../../asset-sources/fietsboek.ts"],"names":[],"mappings":"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"}
\ No newline at end of file +{"version":3,"file":"fietsboek.js","sourceRoot":"","sources":["../../asset-sources/fietsboek.ts"],"names":[],"mappings":"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"}
\ No newline at end of file diff --git a/fietsboek/static/theme.css b/fietsboek/static/theme.css index d97192b..f319c4c 100644 --- a/fietsboek/static/theme.css +++ b/fietsboek/static/theme.css @@ -112,6 +112,14 @@ strong { padding-left: 1em; } +.favourite-star, .favourite-star-ni { + color: orange; +} + +.favourite-star { + cursor: pointer; +} + /* Ensure a consistent width of the cells in the browse view. */ .browse-summary th, .browse-summary td { width: 25%; diff --git a/fietsboek/static/theme.css.map b/fietsboek/static/theme.css.map index 632d85f..36a6762 100644 --- a/fietsboek/static/theme.css.map +++ b/fietsboek/static/theme.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../asset-sources/theme.scss"],"names":[],"mappings":"AAAA;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EAqCE;EACA;EACA;EACA;EACA;EACA;;AAzCA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAWJ;EACI;;;AAGJ;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;;AAIJ;AACA;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE","file":"theme.css"}
\ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../../asset-sources/theme.scss"],"names":[],"mappings":"AAAA;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EAqCE;EACA;EACA;EACA;EACA;EACA;;AAzCA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAWJ;EACI;;;AAGJ;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;;;AAGF;EACE;;;AAGF;AACA;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE","file":"theme.css"}
\ No newline at end of file diff --git a/fietsboek/templates/browse.jinja2 b/fietsboek/templates/browse.jinja2 index 9860201..1eae88d 100644 --- a/fietsboek/templates/browse.jinja2 +++ b/fietsboek/templates/browse.jinja2 @@ -74,6 +74,17 @@ <option value="SYNTHETIC"{% if "SYNTHETIC" in request.params.getall("type[]") %} selected{% endif %}>{{ _("page.browse.filter.type.synthetic") }}</option> </select> </div> + + {% if request.identity %} + <div class="input-group mt-2"> + <button type="button" class="btn btn-outline-secondary button-clear-input"><i class="bi bi-eraser-fill"></i></button> + <select class="form-select" name="favourite"> + <option value="">{{ _("page.browse.filter.favourite.all") }}</option> + <option value="true"{% if request.params.get("favourite") == "true" %} selected{% endif %}>{{ _("page.browse.filter.favourite.yes") }}</option> + <option value="false"{% if request.params.get("favourite") == "false" %} selected{% endif %}>{{ _("page.browse.filter.favourite.no") }}</option> + </select> + </div> + {% endif %} </div> <div class="col-md-6"> {% if request.identity %} @@ -123,6 +134,11 @@ <div class="card mb-3"> <h5 class="card-header"> <input type="checkbox" class="form-check-input archive-checkbox" name="track_id[]" value="{{ track.id }}"> + {% if request.identity and request.identity in track.favourees %} + <i class="bi bi-star-fill favourite-star" data-track-id="{{ track.id }}"></i> + {% else %} + <i class="bi bi-star favourite-star" data-track-id="{{ track.id }}"></i> + {% endif %} <a href="{{ request.route_url('details', track_id=track.id) }}">{{ track.title | default(track.date, true) }}</a> {% if track.text_tags() %} {% for tag in track.tags %}<span class="badge bg-info text-dark">{{ tag.tag }}</span> {% endfor %} diff --git a/fietsboek/templates/details.jinja2 b/fietsboek/templates/details.jinja2 index b7ebefe..7307270 100644 --- a/fietsboek/templates/details.jinja2 +++ b/fietsboek/templates/details.jinja2 @@ -3,7 +3,18 @@ {% block content %} <div class="container"> - <h1>{{ track.title | default(_("page.details.title"), true) }}</h1> + <h1> + {{ track.title | default(_("page.details.title"), true) }} + {% if request.identity %} + <span style="display: inline-flex; vertical-align: middle;"> + {% if request.identity in track.favourees %} + <i class="bi bi-star-fill favourite-star" data-track-id="{{ track.id }}"></i> + {% else %} + <i class="bi bi-star favourite-star" data-track-id="{{ track.id }}"></i> + {% endif %} + </span> + {% endif %} + </h1> {% if show_edit_link %} <div class="btn-group" role="group"> <a class="btn btn-success" href="{{ request.route_path('edit', track_id=track.id) }}"><i class="bi-pencil-square"></i> {{ _("page.details.edit") }}</a> diff --git a/fietsboek/templates/home.jinja2 b/fietsboek/templates/home.jinja2 index 383e90a..d3725ca 100644 --- a/fietsboek/templates/home.jinja2 +++ b/fietsboek/templates/home.jinja2 @@ -44,6 +44,9 @@ <a href="{{ request.route_url('details', track_id=track.id) }}" data-bs-toggle="tooltip" data-bs-container="body" data-bs-html="true" title="{{ track.html_tooltip(request.localizer) }}"> {{ track.title | default(track.date, true) }} </a> + {% if request.identity in track.favourees %} + <i class="bi bi-star-fill favourite-star-ni"></i> + {% endif %} <span class="float-end"> {% if track.tagged_people %} <span data-bs-toggle="tooltip" data-bs-container="body" data-bs-html="true" title="{{ track.html_tooltip_tagged_people() }}"> diff --git a/fietsboek/views/browse.py b/fietsboek/views/browse.py index d4f48b7..f23d3a1 100644 --- a/fietsboek/views/browse.py +++ b/fietsboek/views/browse.py @@ -10,7 +10,7 @@ from pyramid.httpexceptions import HTTPBadRequest, HTTPForbidden, HTTPNotFound from pyramid.request import Request from pyramid.response import Response from pyramid.view import view_config -from sqlalchemy import func, or_, select +from sqlalchemy import func, not_, or_, select from sqlalchemy.orm import aliased from sqlalchemy.sql import Select @@ -226,6 +226,38 @@ class UserTaggedFilter(Filter): return track.owner == self.user or self.user in track.tagged_people +class FavouriteFilter(Filter): + """A :class:`Filter` that accepts only favoured or non-favoured tracks.""" + + def __init__(self, user: models.User, favourite: bool): + """Sets up the filter. + + :param user: The user for which the favourite status should be checked. + :param favourite: ``True`` if only favourites should be returned, + ``False`` otherwise. + """ + self.user = user + self.favourite = favourite + + def compile(self, query: Select, track: AliasedTrack) -> Select: + tfa = models.track.track_favourite_assoc + if self.favourite: + invert = lambda x: x + else: + invert = not_ + return query.where( + invert( + select(tfa) + .where(tfa.c.track_id == track.id) + .where(tfa.c.user_id == self.user.id) + .exists() + ) + ) + + def apply(self, track: TrackWithMetadata) -> bool: + return (self.user in track.favourees) == self.favourite + + class FilterCollection(Filter): """A class that applies multiple :class:`Filter`.""" @@ -345,6 +377,10 @@ class FilterCollection(Filter): ) ) + if request.identity and request.params.get("favourite"): + favourite = request.params.get("favourite") == "true" + filters.append(FavouriteFilter(request.identity, favourite)) + return cls(filters) diff --git a/fietsboek/views/user_data.py b/fietsboek/views/user_data.py index a6ad11d..a7680e4 100644 --- a/fietsboek/views/user_data.py +++ b/fietsboek/views/user_data.py @@ -3,6 +3,7 @@ import datetime from pyramid.httpexceptions import HTTPForbidden, HTTPFound, HTTPNotFound from pyramid.i18n import TranslationString as _ +from pyramid.request import Request from pyramid.view import view_config from sqlalchemy import select @@ -157,3 +158,21 @@ def json_friends(request): """ friends = [{"name": friend.name, "id": friend.id} for friend in request.identity.get_friends()] return friends + + +@view_config( + route_name="toggle-favourite", renderer="json", permission="user", request_method="POST" +) +def do_toggle_favourite(request: Request) -> dict: + """Toggles the favourite status for the given track. + + :param request: The Pyramid request. + :return: The data to return to the client. + """ + track = request.dbsession.execute( + select(models.Track).filter_by(id=request.params["track-id"]) + ).scalar_one_or_none() + if track is None: + return HTTPNotFound() + request.identity.toggle_favourite(track) + return {"favourite": request.identity in track.favourees} diff --git a/pylint.toml b/pylint.toml index 3f011cb..b6dbaef 100644 --- a/pylint.toml +++ b/pylint.toml @@ -345,7 +345,17 @@ confidence = ["HIGH", "CONTROL_FLOW", "INFERENCE", "INFERENCE_FAILURE", "UNDEFIN # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". -disable = ["raw-checker-failed", "bad-inline-option", "locally-disabled", "file-ignored", "suppressed-message", "useless-suppression", "deprecated-pragma", "use-symbolic-message-instead"] +disable = [ + "raw-checker-failed", + "bad-inline-option", + "locally-disabled", + "file-ignored", + "suppressed-message", + "useless-suppression", + "deprecated-pragma", + "use-symbolic-message-instead", + "unnecessary-lambda-assignment", +] # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option diff --git a/tests/integration/test_upload.py b/tests/integration/test_upload.py index 454785f..f09b468 100644 --- a/tests/integration/test_upload.py +++ b/tests/integration/test_upload.py @@ -1,3 +1,5 @@ +import re + from sqlalchemy import select, func from webtest import Upload @@ -30,7 +32,7 @@ def test_upload(testapp, dbsession, route_path, logged_in): ] result = finish_form.submit().maybe_follow() - assert "<h1>FoOoOo Bar's Testtrack</h1>" in result.text + assert re.search("<h1>\\s*FoOoOo Bar's Testtrack", result.text) # Step 4: Ensure the track is stored right track = dbsession.execute(select(models.Track)).scalar_one() diff --git a/tests/playwright/test_basic.py b/tests/playwright/test_basic.py index a98e52d..66ec326 100644 --- a/tests/playwright/test_basic.py +++ b/tests/playwright/test_basic.py @@ -146,3 +146,23 @@ def test_delete(page: Page, playwright_helper, dbaccess, data_manager): assert track is None with pytest.raises(FileNotFoundError): data_manager.open(track_id) + + +def test_favour(page: Page, playwright_helper, dbaccess, data_manager): + john = playwright_helper.john_doe() + playwright_helper.login() + track_id = playwright_helper.add_track().id + + page.goto(f"/track/{track_id}") + + with page.expect_request_finished(predicate=lambda req: "/toggle-favourite" in req.url): + page.locator(".favourite-star").click() + + track = dbaccess.execute(select(models.Track).filter_by(id=track_id)).scalar_one_or_none() + assert john.id in [user.id for user in track.favourees] + + with page.expect_request_finished(predicate=lambda req: "/toggle-favourite" in req.url): + page.locator(".favourite-star").click() + + dbaccess.refresh(track, ["favourees"]) + assert john.id not in [user.id for user in track.favourees] @@ -1,7 +1,7 @@ [flake8] max-line-length = 100 exclude = fietsboek/alembic -extend-ignore = E203 +extend-ignore = E203 E731 per-file-ignores = fietsboek/models/__init__.py:F401 |