feat: quote sources — preset buttons, universal parser, expanded local collection
Goal API (route.ts):
- Now actually uses profile.quoteSourceUrls — tries each user-configured URL
in order before falling back to ZenQuotes
- Universal JSON parser handles ZenQuotes [{q,a}], Quotable {content,author},
Forismatic {quoteText,quoteAuthor}, generic {text/quote/body, author/by},
nested {data:{...}}, and advice-slip {slip:{advice}} formats
- ZenQuotes remains the built-in EN fallback when no user URLs are configured
SettingsSidebar:
- Adds clickable preset-source pill buttons below the "Add source" button:
ZenQuotes, Stoic Quotes, Quotable, Forismatic (EN), Advice Slip
- Pills show ✓ and are disabled when the URL is already in the list
- Note explains DE/FR/IT/ES are served from the curated local collection
quotes.ts:
- Updated PRESET_QUOTE_SOURCES to match the 5 working English APIs
- +14 French quotes (Marcus Aurelius, Lombardi, Ali, MLK, Picasso, etc.)
- +13 Spanish quotes (Collier, Eliot, Darwin, Voltaire, Ashe, etc.)
- +13 Italian quotes (Collier, Eliot, Ali, MLK, Aurelius, da Vinci, etc.)
v1.84.0
This commit is contained in:
parent
2016b1cb18
commit
2ec1278b19
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "my-weekly-todo-list",
|
||||
"version": "1.83.4",
|
||||
"version": "1.84.0",
|
||||
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@ -33,6 +33,7 @@ export async function GET(req: Request) {
|
||||
goalDefaultSentence: true,
|
||||
language: true,
|
||||
quoteLanguages: true,
|
||||
quoteSourceUrls: true,
|
||||
}
|
||||
});
|
||||
|
||||
@ -92,10 +93,53 @@ export async function GET(req: Request) {
|
||||
return NextResponse.json({ goal: holidayHint, isHoliday: true });
|
||||
}
|
||||
|
||||
// 4. Fallback to ZenQuotes motivational quote (if English is in quote languages)
|
||||
// 4. Try user-configured quote source URLs, then ZenQuotes
|
||||
const quoteLangs: string[] = (user as any)?.quoteLanguages?.length > 0
|
||||
? (user as any).quoteLanguages
|
||||
: [user?.language || 'en'];
|
||||
|
||||
// Universal JSON quote parser — handles the most common free API response shapes
|
||||
const parseQuoteResponse = (data: any): string | null => {
|
||||
try {
|
||||
// Array responses: pick a random element
|
||||
if (Array.isArray(data)) {
|
||||
if (data.length === 0) return null;
|
||||
data = data[Math.floor(Math.random() * data.length)];
|
||||
}
|
||||
// ZenQuotes: [{q, a}]
|
||||
if (data.q && data.a) return `${data.q} — ${data.a}`;
|
||||
// Quotable: {content, author}
|
||||
if (data.content && data.author) return `${data.content} — ${data.author}`;
|
||||
// Forismatic: {quoteText, quoteAuthor}
|
||||
if (data.quoteText) return data.quoteAuthor ? `${data.quoteText.trim()} — ${data.quoteAuthor.trim()}` : data.quoteText.trim();
|
||||
// Common: {text, author} or {quote, author}
|
||||
const text = data.text || data.quote || data.body || data.sentence;
|
||||
const author = data.author || data.by || data.from || data.speaker;
|
||||
if (text) return author ? `${text} — ${author}` : text;
|
||||
// Nested: {data: {quote, author}}
|
||||
if (data.data) return parseQuoteResponse(data.data);
|
||||
// Nested: {slip: {advice}}
|
||||
if (data.slip?.advice) return data.slip.advice;
|
||||
} catch { /* ignore */ }
|
||||
return null;
|
||||
};
|
||||
|
||||
// Try each user-configured URL first
|
||||
const userUrls: string[] = (user as any)?.quoteSourceUrls?.filter(Boolean) || [];
|
||||
for (const url of userUrls) {
|
||||
try {
|
||||
const res = await fetch(url, { signal: AbortSignal.timeout(4000) });
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
const parsed = parseQuoteResponse(data);
|
||||
if (parsed) return NextResponse.json({ goal: parsed, isQuote: true });
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`Failed to fetch from ${url}:`, e);
|
||||
}
|
||||
}
|
||||
|
||||
// ZenQuotes as built-in English fallback
|
||||
if (quoteLangs.includes('en')) {
|
||||
try {
|
||||
const res = await fetch('https://zenquotes.io/api/random', { signal: AbortSignal.timeout(3000) });
|
||||
|
||||
@ -3771,6 +3771,54 @@ function SettingsSidebar({
|
||||
>
|
||||
<Plus size={14} /> {t.addSource}
|
||||
</button>
|
||||
|
||||
{/* Preset sources */}
|
||||
<div style={{ marginTop: "8px" }}>
|
||||
<p style={{ fontSize: "0.75rem", color: "var(--weekly-text-light)", marginBottom: "6px" }}>
|
||||
{profile.language === "de" ? "Bekannte Quellen (klicken zum Hinzufügen):" : "Known sources (click to add):"}
|
||||
</p>
|
||||
<div style={{ display: "flex", flexWrap: "wrap", gap: "6px" }}>
|
||||
{[
|
||||
{ label: "ZenQuotes (EN)", url: "https://zenquotes.io/api/random" },
|
||||
{ label: "Stoic Quotes (EN)", url: "https://stoic.tekloon.net/stoic-quote" },
|
||||
{ label: "Quotable (EN)", url: "https://api.quotable.io/quotes/random" },
|
||||
{ label: "Forismatic (EN)", url: "https://api.forismatic.com/api/1.0/?method=getQuote&format=json&lang=en" },
|
||||
{ label: "Advice Slip (EN)", url: "https://api.adviceslip.com/advice" },
|
||||
].map(({ label, url }) => {
|
||||
const current: string[] = profile.quoteSourceUrls || [];
|
||||
const already = current.includes(url);
|
||||
return (
|
||||
<button
|
||||
key={url}
|
||||
type="button"
|
||||
disabled={already}
|
||||
onClick={() => {
|
||||
if (already) return;
|
||||
const newUrls = [...current, url];
|
||||
saveField("quoteSourceUrls", newUrls);
|
||||
}}
|
||||
style={{
|
||||
padding: "3px 8px",
|
||||
fontSize: "0.72rem",
|
||||
borderRadius: "12px",
|
||||
border: "1px solid var(--weekly-border)",
|
||||
background: already ? "var(--weekly-teal)" : "var(--weekly-bg)",
|
||||
color: already ? "white" : "var(--weekly-text)",
|
||||
cursor: already ? "default" : "pointer",
|
||||
opacity: already ? 0.7 : 1,
|
||||
}}
|
||||
>
|
||||
{already ? "✓ " : "+ "}{label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<p style={{ fontSize: "0.72rem", color: "var(--weekly-text-light)", marginTop: "6px", fontStyle: "italic" }}>
|
||||
{profile.language === "de"
|
||||
? "Für DE, FR, IT, ES werden kuratierte lokale Zitate verwendet."
|
||||
: "For DE, FR, IT, ES, curated local quotes are used automatically."}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p style={{ fontSize: "0.75rem", color: "var(--weekly-text-light)", marginTop: "4px" }}>
|
||||
{t.urlFormatHelp}
|
||||
|
||||
@ -8,30 +8,11 @@ export interface LocalQuote {
|
||||
}
|
||||
|
||||
export const PRESET_QUOTE_SOURCES = [
|
||||
{
|
||||
name: "ZenQuotes",
|
||||
url: "https://zenquotes.io/api/random",
|
||||
language: "en",
|
||||
tags: ["motivation", "wisdom", "life"],
|
||||
},
|
||||
{
|
||||
name: "Quotable",
|
||||
url: "https://api.quotable.io/random",
|
||||
language: "en",
|
||||
tags: ["motivation", "wisdom", "success", "life"],
|
||||
},
|
||||
{
|
||||
name: "Forismatic",
|
||||
url: "https://api.forismatic.com/api/1.0/?method=getQuote&format=json&lang=de",
|
||||
language: "de",
|
||||
tags: ["motivation", "wisdom", "life"],
|
||||
},
|
||||
{
|
||||
name: "Type.fit",
|
||||
url: "https://type.fit/api/quotes",
|
||||
language: "en",
|
||||
tags: ["motivation", "wisdom"],
|
||||
},
|
||||
{ name: "ZenQuotes", url: "https://zenquotes.io/api/random", language: "en" },
|
||||
{ name: "Stoic Quotes", url: "https://stoic.tekloon.net/stoic-quote", language: "en" },
|
||||
{ name: "Quotable", url: "https://api.quotable.io/quotes/random", language: "en" },
|
||||
{ name: "Forismatic (EN)", url: "https://api.forismatic.com/api/1.0/?method=getQuote&format=json&lang=en", language: "en" },
|
||||
{ name: "Advice Slip", url: "https://api.adviceslip.com/advice", language: "en" },
|
||||
] as const;
|
||||
|
||||
export const LOCAL_QUOTES: LocalQuote[] = [
|
||||
@ -616,6 +597,84 @@ export const LOCAL_QUOTES: LocalQuote[] = [
|
||||
language: "fr",
|
||||
tags: ["creativity", "humor"],
|
||||
},
|
||||
{
|
||||
text: "Vous avez du pouvoir sur votre esprit, pas sur les événements extérieurs. Réalisez cela, et vous trouverez la force.",
|
||||
author: "Marc Aurèle",
|
||||
language: "fr",
|
||||
tags: ["wisdom", "motivation", "perseverance"],
|
||||
},
|
||||
{
|
||||
text: "Le succès c'est tomber sept fois et se relever huit.",
|
||||
author: "Proverbe japonais",
|
||||
language: "fr",
|
||||
tags: ["perseverance", "motivation", "success"],
|
||||
},
|
||||
{
|
||||
text: "Votre temps est limité, ne le gâchez pas en vivant la vie de quelqu'un d'autre.",
|
||||
author: "Steve Jobs",
|
||||
language: "fr",
|
||||
tags: ["life", "wisdom", "motivation"],
|
||||
},
|
||||
{
|
||||
text: "La perfection n'est pas atteignable. Mais en visant la perfection, nous pouvons atteindre l'excellence.",
|
||||
author: "Vince Lombardi",
|
||||
language: "fr",
|
||||
tags: ["success", "motivation", "work"],
|
||||
},
|
||||
{
|
||||
text: "Commencez là où vous êtes. Utilisez ce que vous avez. Faites ce que vous pouvez.",
|
||||
author: "Arthur Ashe",
|
||||
language: "fr",
|
||||
tags: ["motivation", "work", "perseverance"],
|
||||
},
|
||||
{
|
||||
text: "Les grandes choses ne viennent jamais de la zone de confort.",
|
||||
author: "Inconnu",
|
||||
language: "fr",
|
||||
tags: ["motivation", "perseverance", "success"],
|
||||
},
|
||||
{
|
||||
text: "Ne comptez pas les jours, faites compter les jours.",
|
||||
author: "Muhammad Ali",
|
||||
language: "fr",
|
||||
tags: ["motivation", "work", "life"],
|
||||
},
|
||||
{
|
||||
text: "Ce n'est pas parce que c'est difficile que nous n'osons pas, c'est parce que nous n'osons pas que c'est difficile.",
|
||||
author: "Sénèque",
|
||||
language: "fr",
|
||||
tags: ["motivation", "wisdom", "perseverance"],
|
||||
},
|
||||
{
|
||||
text: "Tout ce que vous pouvez imaginer est réel.",
|
||||
author: "Pablo Picasso",
|
||||
language: "fr",
|
||||
tags: ["creativity", "motivation"],
|
||||
},
|
||||
{
|
||||
text: "La vie n'est pas d'attendre que la tempête passe, c'est d'apprendre à danser sous la pluie.",
|
||||
author: "Vivian Greene",
|
||||
language: "fr",
|
||||
tags: ["life", "perseverance", "wisdom"],
|
||||
},
|
||||
{
|
||||
text: "Si vous ne pouvez pas voler, courez. Si vous ne pouvez pas courir, marchez. Si vous ne pouvez pas marcher, rampez. Mais continuez d'avancer.",
|
||||
author: "Martin Luther King Jr.",
|
||||
language: "fr",
|
||||
tags: ["perseverance", "motivation"],
|
||||
},
|
||||
{
|
||||
text: "La seule façon de faire du bon travail est d'aimer ce que vous faites.",
|
||||
author: "Steve Jobs",
|
||||
language: "fr",
|
||||
tags: ["work", "motivation", "success"],
|
||||
},
|
||||
{
|
||||
text: "Chaque expert a d'abord été un débutant.",
|
||||
author: "Helen Hayes",
|
||||
language: "fr",
|
||||
tags: ["perseverance", "motivation", "work"],
|
||||
},
|
||||
// --- Spanish Quotes ---
|
||||
{
|
||||
text: "No es la especie más fuerte la que sobrevive, sino la que mejor se adapta al cambio.",
|
||||
@ -707,6 +766,78 @@ export const LOCAL_QUOTES: LocalQuote[] = [
|
||||
language: "es",
|
||||
tags: ["motivation", "perseverance", "success"],
|
||||
},
|
||||
{
|
||||
text: "El éxito es la suma de pequeños esfuerzos repetidos día tras día.",
|
||||
author: "Robert Collier",
|
||||
language: "es",
|
||||
tags: ["success", "perseverance", "work"],
|
||||
},
|
||||
{
|
||||
text: "Nunca es tarde para ser lo que podrías haber sido.",
|
||||
author: "George Eliot",
|
||||
language: "es",
|
||||
tags: ["motivation", "life", "perseverance"],
|
||||
},
|
||||
{
|
||||
text: "No cuentes los días, haz que los días cuenten.",
|
||||
author: "Muhammad Ali",
|
||||
language: "es",
|
||||
tags: ["motivation", "work", "life"],
|
||||
},
|
||||
{
|
||||
text: "Si no puedes volar, corre. Si no puedes correr, camina. Si no puedes caminar, arrástrate. Pero sigue adelante.",
|
||||
author: "Martin Luther King Jr.",
|
||||
language: "es",
|
||||
tags: ["perseverance", "motivation"],
|
||||
},
|
||||
{
|
||||
text: "Tienes poder sobre tu mente, no sobre los eventos externos. Date cuenta de esto y encontrarás fuerza.",
|
||||
author: "Marco Aurelio",
|
||||
language: "es",
|
||||
tags: ["wisdom", "motivation", "perseverance"],
|
||||
},
|
||||
{
|
||||
text: "El único modo de hacer un gran trabajo es amar lo que haces.",
|
||||
author: "Steve Jobs",
|
||||
language: "es",
|
||||
tags: ["work", "motivation", "success"],
|
||||
},
|
||||
{
|
||||
text: "No importa cuán lento vayas, siempre y cuando no te detengas.",
|
||||
author: "Confucio",
|
||||
language: "es",
|
||||
tags: ["perseverance", "motivation"],
|
||||
},
|
||||
{
|
||||
text: "Todo experto fue alguna vez un principiante.",
|
||||
author: "Helen Hayes",
|
||||
language: "es",
|
||||
tags: ["perseverance", "motivation", "work"],
|
||||
},
|
||||
{
|
||||
text: "El futuro pertenece a quienes creen en la belleza de sus sueños.",
|
||||
author: "Eleanor Roosevelt",
|
||||
language: "es",
|
||||
tags: ["motivation", "life", "creativity"],
|
||||
},
|
||||
{
|
||||
text: "No es la especie más fuerte la que sobrevive, sino la más adaptable al cambio.",
|
||||
author: "Charles Darwin",
|
||||
language: "es",
|
||||
tags: ["wisdom", "perseverance", "life"],
|
||||
},
|
||||
{
|
||||
text: "La perseverancia es el padre del éxito.",
|
||||
author: "Voltaire",
|
||||
language: "es",
|
||||
tags: ["perseverance", "success", "work"],
|
||||
},
|
||||
{
|
||||
text: "Comienza donde estás. Usa lo que tienes. Haz lo que puedes.",
|
||||
author: "Arthur Ashe",
|
||||
language: "es",
|
||||
tags: ["motivation", "work", "perseverance"],
|
||||
},
|
||||
// --- Italian Quotes ---
|
||||
{
|
||||
text: "La semplicità è la raffinatezza suprema.",
|
||||
@ -798,6 +929,78 @@ export const LOCAL_QUOTES: LocalQuote[] = [
|
||||
language: "it",
|
||||
tags: ["motivation", "perseverance", "success"],
|
||||
},
|
||||
{
|
||||
text: "Il successo è la somma di piccoli sforzi ripetuti giorno dopo giorno.",
|
||||
author: "Robert Collier",
|
||||
language: "it",
|
||||
tags: ["success", "perseverance", "work"],
|
||||
},
|
||||
{
|
||||
text: "Non è mai troppo tardi per essere ciò che avresti potuto essere.",
|
||||
author: "George Eliot",
|
||||
language: "it",
|
||||
tags: ["motivation", "life", "perseverance"],
|
||||
},
|
||||
{
|
||||
text: "Non contare i giorni, fai sì che i giorni contino.",
|
||||
author: "Muhammad Ali",
|
||||
language: "it",
|
||||
tags: ["motivation", "work", "life"],
|
||||
},
|
||||
{
|
||||
text: "Se non puoi volare, corri. Se non puoi correre, cammina. Se non puoi camminare, striscia. Ma continua ad andare avanti.",
|
||||
author: "Martin Luther King Jr.",
|
||||
language: "it",
|
||||
tags: ["perseverance", "motivation"],
|
||||
},
|
||||
{
|
||||
text: "Hai potere sulla tua mente, non sugli eventi esterni. Renditi conto di questo e troverai la forza.",
|
||||
author: "Marco Aurelio",
|
||||
language: "it",
|
||||
tags: ["wisdom", "motivation", "perseverance"],
|
||||
},
|
||||
{
|
||||
text: "Ogni esperto è stato prima un principiante.",
|
||||
author: "Helen Hayes",
|
||||
language: "it",
|
||||
tags: ["perseverance", "motivation", "work"],
|
||||
},
|
||||
{
|
||||
text: "Il futuro appartiene a coloro che credono nella bellezza dei propri sogni.",
|
||||
author: "Eleanor Roosevelt",
|
||||
language: "it",
|
||||
tags: ["motivation", "life", "creativity"],
|
||||
},
|
||||
{
|
||||
text: "Inizia dove sei. Usa quello che hai. Fai quello che puoi.",
|
||||
author: "Arthur Ashe",
|
||||
language: "it",
|
||||
tags: ["motivation", "work", "perseverance"],
|
||||
},
|
||||
{
|
||||
text: "La semplicità è la massima sofisticazione.",
|
||||
author: "Leonardo da Vinci",
|
||||
language: "it",
|
||||
tags: ["wisdom", "creativity"],
|
||||
},
|
||||
{
|
||||
text: "Il coraggio non è l'assenza di paura, ma il giudizio che qualcos'altro è più importante della paura.",
|
||||
author: "Ambrose Redmoon",
|
||||
language: "it",
|
||||
tags: ["motivation", "perseverance", "wisdom"],
|
||||
},
|
||||
{
|
||||
text: "Le grandi menti discutono di idee; le menti medie discutono di eventi; le piccole menti discutono di persone.",
|
||||
author: "Eleanor Roosevelt",
|
||||
language: "it",
|
||||
tags: ["wisdom", "creativity"],
|
||||
},
|
||||
{
|
||||
text: "Sii tu il cambiamento che vuoi vedere nel mondo.",
|
||||
author: "Mahatma Gandhi",
|
||||
language: "it",
|
||||
tags: ["wisdom", "life", "motivation"],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
Loading…
Reference in New Issue
Block a user