diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05b13277..4815c1bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: - uses: oven-sh/setup-bun@v2.2.0 with: - bun-version: "1.3.14" + bun-version: "1.4.2" - name: Install dependencies run: bun install --frozen-lockfile @@ -76,7 +76,7 @@ jobs: - uses: oven-sh/setup-bun@v2.2.0 with: - bun-version: "1.3.14" + bun-version: "1.4.2" - name: Install dependencies run: bun install --frozen-lockfile diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index ec7019c5..e3415848 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -17,7 +17,7 @@ jobs: - uses: oven-sh/setup-bun@v2.2.0 with: - bun-version: "1.3.14" + bun-version: "1.4.2" - name: Install dependencies run: bun install --frozen-lockfile diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index e8aa031c..670dde17 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -34,7 +34,7 @@ jobs: - uses: oven-sh/setup-bun@v2.2.0 with: - bun-version: "1.3.14" + bun-version: "1.4.2" - name: Resolve build metadata id: info diff --git a/.github/workflows/localization.yml b/.github/workflows/localization.yml index bb064ba0..9f6f43ca 100644 --- a/.github/workflows/localization.yml +++ b/.github/workflows/localization.yml @@ -18,7 +18,7 @@ jobs: - uses: oven-sh/setup-bun@v2.2.0 with: - bun-version: "1.3.14" + bun-version: "1.4.2" - name: Install dependencies run: bun install --frozen-lockfile diff --git a/Dockerfile b/Dockerfile index 7fc722c9..d2b605f4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM oven/bun:1.3.14-alpine AS builder +FROM --platform=$BUILDPLATFORM oven/bun:1.4.2-alpine AS builder WORKDIR /app @@ -22,7 +22,13 @@ ARG BUILD_REVISION=development ARG BUILD_TIME=unknown # Let the official entrypoint derive the resolver from the runtime network. -ENV NGINX_ENTRYPOINT_LOCAL_RESOLVERS=1 +ENV NGINX_ENTRYPOINT_LOCAL_RESOLVERS=1 \ + TYPETYPE_SERVER_HOST=typetype-server \ + TYPETYPE_SERVER_PORT=8080 \ + TYPETYPE_TOKEN_HOST=typetype-token \ + TYPETYPE_TOKEN_PORT=8081 \ + TYPETYPE_DOWNLOADER_HOST=typetype-downloader \ + TYPETYPE_DOWNLOADER_PORT=18093 RUN apk upgrade --no-cache libxml2 libcrypto3 libssl3 libexpat diff --git a/apps/web/messages/de.json b/apps/web/messages/de.json index edb21ab2..7cd3fddf 100644 --- a/apps/web/messages/de.json +++ b/apps/web/messages/de.json @@ -1,5 +1,10 @@ { "$schema": "https://inlang.com/schema/inlang-message-format", + "search_panel_suggestions": "Vorschläge", + "search_panel_no_history": "Keine letzten Suchanfragen", + "search_panel_no_suggestions": "Keine Vorschläge. Sie können trotzdem nach Ihrem Text suchen.", + "search_panel_no_videos": "Zurzeit keine Empfehlungen", + "search_panel_clear_failed": "Der Suchverlauf konnte nicht gelöscht werden. Bitte erneut versuchen.", "admin_access_description": "Lege fest, wer beitreten darf und welche Inhalte verfügbar sind.", "admin_access_label": "Zugriff", "admin_access_mode_description": "Die Jugendschutzfunktion beschränkt das Abrufen und Entdecken von Inhalten auf zugelassene Kanäle.", @@ -66,7 +71,7 @@ "nav_subscriptions": "Abonnements", "shell_built_by_community": "Erstellt von der TypeType Community", "shell_search": "Suchen", - "shell_support": "Hilfe", + "shell_support": "Unterstützen", "shell_support_typetype": "Unterstütze TypeType", "watch_audio_loading": "Lade Ton…", "watch_audio_only": "Nur Ton", @@ -167,7 +172,7 @@ "settings_blocked_label": "Gesperrter Inhalt", "settings_data_description": "Importiere, exportiere und stelle Accountdaten wieder her.", "settings_data_label": "Daten", - "settings_interface_description": "Entscheide was TypeType öffnet und hervorhebt.", + "settings_interface_description": "Wähle aus, was TypeType öffnet und hervorhebt.", "settings_interface_label": "Benutzeroberfläche", "settings_language_description": "Benutzeroberflächen-, Ton- und Untertitel- Einstellungen.", "settings_language_label": "Sprachen", @@ -177,7 +182,7 @@ "settings_privacy_description": "Wiedergabeverlauf, Sitzungen und Datenschutz des Kontos verwalten.", "settings_privacy_label": "Datenschutz", "settings_rss_description": "Private Feeds für dieses Konto verwalten.", - "settings_services_description": "Wähle den Anbieter welcher standardmäßig genutzt wird.", + "settings_services_description": "Wähle den Anbieter, welcher standardmäßig genutzt wird.", "settings_services_label": "Dienste", "settings_subtitle": "Passe Wiedergabe, Inhalte, Datenschutz und Kontodaten an. Die Änderungen gelten für dein gesamtes TypeType-Konto.", "settings_subtitle_default_description": "Aktiviere Untertitel standardmäßig", @@ -185,7 +190,7 @@ "settings_subtitle_language_description": "Bevorzugte Untertitelspur", "settings_subtitle_language_label": "Untertitelsprache", "settings_title": "Einstellungen", - "settings_audio_language_description": "Bevorzugte Audiospur", + "settings_audio_language_description": "Bevorzugte Tonspur", "settings_audio_language_ignored": "Wird ignoriert, wenn die Originalsprache erzwungen wird", "settings_audio_language_label": "Tonsprache", "settings_original_audio_description": "Verwende immer die ursprüngliche Tonspur", @@ -213,6 +218,17 @@ "profile_username_too_long": "Benutzername darf maximal 32 Zeichen lang sein.", "profile_bio_too_long": "Die Kurzbiografie darf maximal 280 Zeichen lang sein.", "profile_update_failed": "Profil konnte nicht aktualisiert werden.", + "profile_switcher": "Profile", + "profile_active": "Aktives Profil", + "profile_add": "Profil hinzufügen", + "profile_rename": "Profil umbenennen", + "profile_delete": "Profil löschen", + "profile_set_default": "Als Standardprofil festlegen", + "profile_save_name": "Profilnamen speichern", + "profile_name_placeholder": "Name des neuen Profils", + "profile_name_label": "Profilname", + "profile_delete_confirm": "Dieses Profil und alle privaten Daten löschen?", + "profile_action_failed": "Profile konnten nicht aktualisiert werden.", "account_identity_title": "Kontoidentität", "account_identity_description": "Wird für die Anmeldung und die Anzeige des Kontos verwendet.", "account_identity_managed": "Verwaltet von deinem Identitätsanbieter.", @@ -236,7 +252,7 @@ "avatar_update_failed": "Profilbild kann nicht aktualisiert werden", "avatar_clear_failed": "Profilbild kann nicht gelöscht werden", "avatar_emoji_source": "Emojis von", - "avatar_search": "Emojis suchen", + "avatar_search": "Emojis durchsuchen", "avatar_scroll_left": "Emoji-Liste nach links scrollen", "avatar_scroll_right": "Emoji-Liste nach rechts scrollen", "avatar_clear": "Leeren", @@ -269,9 +285,9 @@ "portability_import_title": "Daten importieren", "portability_import_description": "Vorschau anzeigen und auswählen, was wiederhergestellt werden soll.", "portability_import_from": "Importieren von", - "portability_choose_or_drop": "Backup auswählen oder ablegen", - "portability_drop_original_prefix": "Original ablegen", - "portability_drop_original_suffix": "Datei. Es wird nichts importiert bis du die Vorschau überprüfst.", + "portability_choose_or_drop": "Sicherungskopie auswählen oder ablegen", + "portability_drop_original_prefix": "Lege die originale", + "portability_drop_original_suffix": "Datei ab. Es wird nichts importiert, bis du die Vorschau überprüft hast.", "portability_duplicate_records": "Doppelte Daten gefunden", "portability_different_file": "Andere Datei", "portability_compatibility_notes": "Kompatibilitätshinweise", @@ -300,14 +316,14 @@ "portability_status_partial": "Teilunterstützung", "portability_status_supported": "Unterstützt", "portability_status_not_supported": "Nicht unterstützt", - "portability_get_backup": "Erhalte dein", + "portability_get_backup": "Erhalte deine", "portability_backup_suffix": "Sicherungskopie", "portability_guide_typetype_description": "Daten aus einer anderen TypeType-Instanz übertragen oder eine Sicherungskopie wiederherstellen.", "portability_guide_typetype_step_one": "Öffne die Einstellungen und anschließend Datenübertragbarkeit auf der ursprünglichen Instanz.", "portability_guide_typetype_step_two": "Wähle TypeType als Ziel aus und lade den Export herunter.", "portability_guide_typetype_step_three": "Lege unten die heruntergeladene Sicherungskopie unverändert ab.", "portability_guide_takeout_description": "Google Takeout kann Abonnements, Wiedergabeverlauf, Wiedergabelisten, Videos in Später ansehen und mit Gefällt mir markierte Videos bereitstellen.", - "portability_guide_takeout_step_one": "Öffne Google Takeout mit YouTube und Meine Aktivitäten ausgewählt.", + "portability_guide_takeout_step_one": "Öffne Google Takeout und wähle YouTube und Meine Aktivitäten aus.", "portability_guide_takeout_step_two": "Erstelle einen ZIP-Export und lade diesen von Google herunter.", "portability_guide_takeout_step_three": "Lege die originale ZIP unten ab. Wenn Google mehrere ZIPs erstellt hat, importiere sie einzeln.", "portability_guide_takeout_action": "Öffne Google Takeout", @@ -415,7 +431,7 @@ "admin_users_account_access": "Kontozugriff", "admin_users_suspend": "Ausschließen", "admin_users_unsuspend": "Ausschluss aufheben", - "admin_users_reset_token": "Token zurücksetzen", + "admin_users_reset_token": "Reset-Token", "admin_users_previous_page": "Vorherige Seite", "admin_users_next_page": "Nächste Seite", "admin_users_page": "Seite", @@ -515,7 +531,7 @@ "ui_connect_with_youtube": "Mit YouTube verbinden", "ui_connect_youtube_inside_typetype": "Verbinde YouTube innerhalb von TypeType", "ui_connect_youtube_on_typetype": "Verbinde YouTube auf TypeType", - "ui_content": "Verbinden", + "ui_content": "Inhalt", "ui_content_controls": "Verbindungssteuerung", "ui_continue": "Fortsetzen", "ui_continue_watching": "Ansehen fortsetzen", @@ -649,7 +665,7 @@ "ui_no_playlists_on_this_channel": "Keine Wiedergabelisten auf diesem Kanal.", "ui_no_playlists_yet": "Noch keine Wiedergabelisten.", "ui_no_podcast_selected": "Keine Podcasts ausgewählt.", - "ui_no_preference": "Keine Vorlieben", + "ui_no_preference": "Keine Präferenz", "ui_no_private_feeds": "Keine privaten Feeds", "ui_no_private_feeds_have_been_created": "Es wurden keine privaten Feeds erstellt.", "ui_no_results_for": "Keine Ergebnisse für “", @@ -764,7 +780,7 @@ "ui_startup": "Hochfahren", "ui_status": "Status:", "ui_stored_data": "Gespeicherte Daten", - "ui_subscribe_to_channels_to_unlock_a_personalized_home_feed": "Abonniere Kanäle, um einen personalisierte Startseite freizuschalten.", + "ui_subscribe_to_channels_to_unlock_a_personalized_home_feed": "Abonniere Kanäle, um eine personalisierte Startseite freizuschalten.", "ui_subscribers": "Abonnenten", "ui_subscription_views": "Ansichten von Abonnements", "ui_subscriptions": "Abonnements,", @@ -802,7 +818,7 @@ "ui_unblock": "Freigeben", "ui_unpacking_and_syncing_your_data_this_may_take_a_moment": "Extrahieren und synchronisieren deiner Daten. Dies kann einen Moment dauern.", "ui_unsubscribe_all": "Alle deabonnieren", - "ui_unsubscribe_from_all_channels_in_settings": "Deabonnieren aller Kanäle in den Einstellungen.", + "ui_unsubscribe_from_all_channels_in_settings": "Deabonniere alle Kanäle in den Einstellungen.", "ui_up_next": "Als Nächstes", "ui_updated": "Aktualisiert", "ui_use_a_secondary_youtube_account_the_remote_browser_is_temporary_and_c": "Verwende ein sekundäres YouTube-Konto. Der Remote-Browser ist nur vorübergehend aktiv und wird nach der Verbindung, nach Ablauf der Zeit oder bei Abbruch geschlossen.", @@ -1030,13 +1046,13 @@ "video_paid_message": "Dieses Video muss auf YouTube gekauft werden, bevor es wiedergegeben werden kann.", "video_members_only": "Video nur für Kanalmitglieder", "video_members_only_message": "Dieses Video ist nur für Kanalmitglieder verfügbar", - "channel_sort_newest": "Neuste", + "channel_sort_newest": "Neueste", "channel_sort_popular": "Beliebt", "channel_sort_oldest": "Älteste", "playlist_sort_manual": "Manuell", - "playlist_sort_added_newest": "Hinzugefügt am (zuletzt)", + "playlist_sort_added_newest": "Hinzugefügt am (neueste)", "playlist_sort_added_oldest": "Hinzugefügt am (älteste)", - "playlist_sort_posted_newest": "Hochgeladen am (neuste)", + "playlist_sort_posted_newest": "Hochgeladen am (neueste)", "playlist_sort_posted_oldest": "Hochgeladen am (älteste)", "playlist_sort_popularity": "Beliebtheit", "search_filter_sort_by": "Sortieren nach", @@ -1077,7 +1093,7 @@ "ui_home_recommendations": "Empfehlungen auf der Startseite", "ui_hide_personalized_recommendations_from_the_home_page": "Personalisierte Empfehlungen auf der Startseite ausblenden.", "ui_hide_the_suggestions_column_on_watch_pages": "Die Spalte mit den Vorschlägen auf den Wiedergabeseite ausblenden.", - "ui_watch_and_shorts": "Wiedergeben + Shorts", + "ui_watch_and_shorts": "Wiedergabe + Shorts", "ui_hide_shorts_navigation_and_block_the_shorts_page": "Die Shorts-Navigation ausblenden und die Shorts-Seite sperren.", "ui_hide_active_and_scheduled_live_streams_from_the_subscriptions_feed": "Laufende und geplante Livestreams aus dem Abonnement-Feed ausblenden.", "ui_hide_videos_that_require_a_paid_youtube_channel_membership": "Videos ausblenden, welche eine bezahlte YouTube Kanalmitgliedschaft benötigen.", @@ -1275,7 +1291,7 @@ "ui_downloader_insufficient_storage": "Die Instanz hat nicht genug Speicherplatz um diesen Download abzuschließen.", "ui_downloader_insufficient_storage_help": "Gebe Speicherplatz frei und versuche es nochmal. Wenn das Problem weiterhin besteht, kontaktiere den Administrator der Instanz mit dem Code: insufficient_storage (HTTP 507).", "portability_progress_of": "{processed} von {total}", - "portability_progress_records": "Aufzeichnungen", + "portability_progress_records": "Einträge", "portability_progress_categories": "Kategorien", "portability_progress_bytes": "Bytes", "ui_download_started": "Download begonnen: {selected}", @@ -1294,6 +1310,15 @@ "watch_share_typetype_link": "TypeType Link teilen", "ui_close_player": "Player schließen", "ui_move_player": "Player bewegen", + "player_return_to_video": "Zurück zum Video", + "community_lemmy_title": "Bis bald auf Lemmy!", + "community_lemmy_reason": "Leider gibt es eine traurige Nachricht: Die TypeType-Community wurde auf Reddit gesperrt...\nWir versuchen noch herauszufinden, warum.\n\nEntschuldigt diesen unerwarteten Wechsel!", + "community_lemmy_move": "Aber das Abenteuer geht weiter!\nNach Rücksprache haben wir beschlossen, TypeType auf Lemmy ein neues Zuhause zu geben. Wir hoffen dort auf einen freundlicheren Empfang und mehr Respekt für das Projekt.\n\nWir freuen uns darauf, euch dort wiederzusehen!", + "community_lemmy_support": "Die beste Art, TypeType zu unterstützen, ist, der Lemmy-Community beizutreten!\nTragt bei, teilt eure Ideen und beteiligt euch an den Diskussionen: Das hält das Projekt am Leben.\n\nDanke, dass ihr dabei seid und Teil des Abenteuers seid!", + "community_lemmy_join": "Zu TypeType auf Lemmy!", + "community_lemmy_announcement": "Die vollständige Ankündigung lesen", + "community_lemmy_voyager": "Unterwegs? Probiert Voyager aus!", + "community_lemmy_acknowledge": "ICH HABE ES VERSTANDEN! Alles klar", "portability_preparing_upload": "Kontodaten werden vorbereitet und hochgeladen...", "portability_takeout_metadata_limit": "Die Kontodaten in diesem Archiv überschreiten die Importgrenzen oder sind verschlüsselt. Exportiere YouTube separat in kleineren ZIP-Dateien.", "portability_takeout_no_metadata": "Keine YouTube-Kontodaten gefunden. Wähle eine originale Takeout-ZIP mit deinem Verlauf, deinen Abonnements oder deinen Wiedergabelisten statt eines reinen Video-Teils oder einer ZIP-Datei, die weitere ZIP-Dateien enthält." diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 043ee004..b002ba80 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -1,5 +1,10 @@ { "$schema": "https://inlang.com/schema/inlang-message-format", + "search_panel_suggestions": "Suggestions", + "search_panel_no_history": "No recent searches", + "search_panel_no_suggestions": "No suggestions. You can still search with your text.", + "search_panel_no_videos": "No recommendations right now", + "search_panel_clear_failed": "Could not clear search history. Please try again.", "admin_access_description": "Define who can join and which content is available.", "admin_access_label": "Access", "admin_access_mode_description": "Parental controls restrict extraction and discovery to approved channels.", @@ -215,6 +220,17 @@ "profile_username_too_long": "Username must be at most 32 characters.", "profile_bio_too_long": "Bio must be at most 280 characters.", "profile_update_failed": "Unable to update profile.", + "profile_switcher": "Profiles", + "profile_active": "Active profile", + "profile_add": "Add profile", + "profile_rename": "Rename profile", + "profile_delete": "Delete profile", + "profile_set_default": "Set as default profile", + "profile_save_name": "Save profile name", + "profile_name_placeholder": "New profile name", + "profile_name_label": "Profile name", + "profile_delete_confirm": "Delete this profile and all of its private data?", + "profile_action_failed": "Unable to update profiles.", "account_identity_title": "Account identity", "account_identity_description": "Used for sign-in and account display.", "account_identity_managed": "Managed by your identity provider.", @@ -626,6 +642,15 @@ "ui_minimum_segment_duration": "Minimum segment duration", "ui_move_data_between_supported_apps": "Move data between supported apps", "ui_move_player": "Move player", + "player_return_to_video": "Return to video", + "community_lemmy_title": "See you on Lemmy!", + "community_lemmy_reason": "A bit of sad news: the TypeType community has been banned from Reddit...\nWe're still trying to understand why.\n\nSorry for this unexpected change!", + "community_lemmy_move": "But the adventure continues!\nAfter talking it over, we've decided to give TypeType a new home on Lemmy. We're hoping for a friendlier welcome and more respect for the project.\n\nWe can't wait to see you there!", + "community_lemmy_support": "The best way to support TypeType is to join the Lemmy community!\nContribute, share your ideas, and join the conversation: that's what keeps the project alive.\n\nThank you for being here and being part of the adventure!", + "community_lemmy_join": "Join TypeType on Lemmy!", + "community_lemmy_announcement": "Read the full announcement", + "community_lemmy_voyager": "On mobile? Try Voyager!", + "community_lemmy_acknowledge": "I UNDERSTAND! All good", "ui_move_down": "Move down", "ui_move_up": "Move up", "ui_name": "Name", diff --git a/apps/web/messages/fr.json b/apps/web/messages/fr.json index be1ac33a..27a153c9 100644 --- a/apps/web/messages/fr.json +++ b/apps/web/messages/fr.json @@ -1,5 +1,10 @@ { "$schema": "https://inlang.com/schema/inlang-message-format", + "search_panel_suggestions": "Suggestions", + "search_panel_no_history": "Aucune recherche récente", + "search_panel_no_suggestions": "Aucune suggestion. Vous pouvez rechercher avec votre texte.", + "search_panel_no_videos": "Aucune recommandation pour le moment", + "search_panel_clear_failed": "Impossible d'effacer l'historique. Veuillez réessayer.", "admin_access_description": "Définissez qui peut rejoindre l'instance et quels contenus sont disponibles.", "admin_access_label": "Accès", "admin_access_mode_description": "Le contrôle parental limite l'extraction et la découverte aux chaînes approuvées.", @@ -215,6 +220,17 @@ "profile_username_too_long": "Le nom d'utilisateur doit comporter au plus 32 caractères.", "profile_bio_too_long": "La bio doit comporter au plus 280 caractères.", "profile_update_failed": "Impossible de mettre à jour le profil.", + "profile_switcher": "Profils", + "profile_active": "Profil actif", + "profile_add": "Ajouter un profil", + "profile_rename": "Renommer le profil", + "profile_delete": "Supprimer le profil", + "profile_set_default": "Définir comme profil par défaut", + "profile_save_name": "Enregistrer le nom du profil", + "profile_name_placeholder": "Nom du nouveau profil", + "profile_name_label": "Nom du profil", + "profile_delete_confirm": "Supprimer ce profil et toutes ses données privées ?", + "profile_action_failed": "Impossible de mettre à jour les profils.", "account_identity_title": "Identité du compte", "account_identity_description": "Utilisée pour la connexion et l'affichage du compte.", "account_identity_managed": "Gérée par votre fournisseur d'identité.", @@ -626,6 +642,15 @@ "ui_minimum_segment_duration": "Minimum segment duration", "ui_move_data_between_supported_apps": "Move data between supported apps", "ui_move_player": "Déplacer le lecteur", + "player_return_to_video": "Revenir à la vidéo", + "community_lemmy_title": "Rendez-vous sur Lemmy !", + "community_lemmy_reason": "Petite nouvelle pas très chouette : la communauté TypeType a été bannie de Reddit...\nOn essaie de comprendre pourquoi.\n\nDésolé pour ce changement de dernière minute !", + "community_lemmy_move": "Mais l'aventure continue !\nAprès en avoir discuté, on a décidé de faire une nouvelle place à TypeType sur Lemmy. On espère que Lemmy nous fera un meilleur accueil !\n\nOn a hâte de vous y retrouver !", + "community_lemmy_support": "La meilleure façon de soutenir TypeType, c'est de rejoindre la communauté sur Lemmy !\nContribuez, partagez vos idées et participez aux discussions : c'est ce qui fait vivre le projet.\n\nMerci d'être là et de faire partie de l'aventure !", + "community_lemmy_join": "Retrouver TypeType sur Lemmy !", + "community_lemmy_announcement": "Lire l'annonce complète", + "community_lemmy_voyager": "Sur mobile ? Découvrez Voyager !", + "community_lemmy_acknowledge": "J'AI COMPRIS ! C'est bon", "ui_move_down": "Move down", "ui_move_up": "Move up", "ui_name": "Nom", diff --git a/apps/web/package.json b/apps/web/package.json index ca125604..616e9a6d 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,7 +1,7 @@ { "name": "@typetype/web", "private": true, - "version": "1.8.0", + "version": "1.8.1", "type": "module", "scripts": { "dev": "vite", @@ -12,30 +12,30 @@ "test:coverage": "bun test --preload ./tests/setup.ts --coverage --coverage-reporter=lcov" }, "dependencies": { - "@tanstack/react-query": "^5.101.2", - "@tanstack/react-router": "^1.170.17", + "@tanstack/react-query": "^5.102.8", + "@tanstack/react-router": "^1.170.33", "@typetype/mse": "0.1.60", "@vidstack/react": "1.12.13", - "@zip.js/zip.js": "^2.11.2", - "dashjs": "^5.2.0", - "hls.js": "1.6.16", - "lucide-react": "^1.24.0", + "@zip.js/zip.js": "^2.13.1", + "dashjs": "^5.2.1", + "hls.js": "1.7.2", + "lucide-react": "^1.43.0", "media-icons": "1.1.5", - "react": "^19.2.7", - "react-dom": "^19.2.7", - "simple-icons": "^16.25.0", - "zustand": "^5.0.14" + "react": "^19.2.8", + "react-dom": "^19.2.8", + "simple-icons": "^16.30.0", + "zustand": "^5.0.15" }, "devDependencies": { - "@inlang/paraglide-js": "2.24.1", - "@tailwindcss/vite": "^4.3.2", - "@tanstack/router-plugin": "^1.168.19", - "@types/node": "^26.1.1", - "@types/react": "^19.2.17", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.3", - "tailwindcss": "^4.3.2", + "@inlang/paraglide-js": "2.25.1", + "@tailwindcss/vite": "^4.3.3", + "@tanstack/router-plugin": "^1.168.36", + "@types/node": "^26.5.0", + "@types/react": "^19.2.18", + "@types/react-dom": "^19.2.7", + "@vitejs/plugin-react": "^6.1.1", + "tailwindcss": "^4.3.3", "typescript": "~7.0.2", - "vite": "^8.1.4" + "vite": "^8.2.2" } } diff --git a/apps/web/public/THIRD_PARTY_NOTICES.txt b/apps/web/public/THIRD_PARTY_NOTICES.txt index 1fc99a96..8714375d 100644 --- a/apps/web/public/THIRD_PARTY_NOTICES.txt +++ b/apps/web/public/THIRD_PARTY_NOTICES.txt @@ -1,5 +1,24 @@ TypeType Frontend includes software distributed under third-party licenses. +Brand assets +------------ + +The store icons used by the community announcement are downloaded from the +following sources and remain under their respective trademark and license +terms: + +Google Play icon +https://commons.wikimedia.org/wiki/File:Google_Play_2022_icon.svg +Source: Google Play + +App Store icon +https://commons.wikimedia.org/wiki/File:App_Store_(iOS).svg +Source: Apple + +F-Droid icon +https://gitlab.com/fdroid/fdroid-website/-/blob/master/assets/fdroid-logo.svg +The F-Droid logo is available under CC BY-SA 3.0 or GPLv3+. + zip.js https://github.com/gildas-lormeau/zip.js BSD 3-Clause License diff --git a/apps/web/public/brand/app-store.svg b/apps/web/public/brand/app-store.svg new file mode 100644 index 00000000..8e3f54cc --- /dev/null +++ b/apps/web/public/brand/app-store.svg @@ -0,0 +1,17 @@ + + + + + + + + + + diff --git a/apps/web/public/brand/fdroid.svg b/apps/web/public/brand/fdroid.svg new file mode 100644 index 00000000..0746785c --- /dev/null +++ b/apps/web/public/brand/fdroid.svg @@ -0,0 +1,310 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/brand/google-play.svg b/apps/web/public/brand/google-play.svg new file mode 100644 index 00000000..3ed3390a --- /dev/null +++ b/apps/web/public/brand/google-play.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/components/app-footer.tsx b/apps/web/src/components/app-footer.tsx index 534552e9..27489379 100644 --- a/apps/web/src/components/app-footer.tsx +++ b/apps/web/src/components/app-footer.tsx @@ -1,17 +1,19 @@ import { DollarSign } from "lucide-react"; -import { siGithub, siReddit } from "simple-icons"; +import { siGithub, siLemmy } from "simple-icons"; import { useInterfaceLocale } from "../hooks/use-interface-locale"; +import { LEMMY_COMMUNITY_URL } from "../lib/community-announcement"; import { m } from "../paraglide/messages.js"; +import { CommunityAnnouncement } from "./community-announcement"; import { ServiceIcon } from "./service-icon"; const COMMUNITY_URL = "https://github.com/TypeType-Video"; -const REDDIT_URL = "https://www.reddit.com/r/TypeType/"; const SPONSOR_URL = "https://github.com/sponsors/Priveetee"; export function AppFooter() { useInterfaceLocale(); return (