diff --git a/src/features/background/Background.jsx b/src/features/background/Background.jsx index 05be918a..03ee70e5 100644 --- a/src/features/background/Background.jsx +++ b/src/features/background/Background.jsx @@ -5,6 +5,7 @@ import { useBackgroundState } from './hooks/useBackgroundState'; import { useBackgroundLoader } from './hooks/useBackgroundLoader'; import { useBackgroundRenderer, useBackgroundFilters, useBackgroundOverlayFilters } from './hooks/useBackgroundRenderer'; import { useBackgroundEvents } from './hooks/useBackgroundEvents'; +import { useFrequencyInterval } from '../../hooks/useFrequencyInterval'; import './scss/index.scss'; @@ -14,12 +15,13 @@ import './scss/index.scss'; */ export default function Background() { const { backgroundData, updateBackground, resetBackground } = useBackgroundState(); - const { refreshBackground } = useBackgroundLoader(updateBackground, resetBackground); + const { loadBackground, refreshBackground } = useBackgroundLoader(updateBackground, resetBackground); const filterStyle = useBackgroundFilters(); const overlayFilterStyle = useBackgroundOverlayFilters(); useBackgroundRenderer(backgroundData); useBackgroundEvents(backgroundData, refreshBackground); + useFrequencyInterval('background', loadBackground); return ( <> diff --git a/src/features/background/hooks/useBackgroundLoader.js b/src/features/background/hooks/useBackgroundLoader.js index 30cdf56c..ecc0f311 100644 --- a/src/features/background/hooks/useBackgroundLoader.js +++ b/src/features/background/hooks/useBackgroundLoader.js @@ -1,5 +1,6 @@ import { useCallback, useEffect, useRef } from 'react'; import { getBackgroundData } from '../api/backgroundLoader'; +import { shouldUpdateByFrequency, resetStartTime } from 'utils/frequencyManager'; /** * Hook for loading and refreshing background data @@ -25,6 +26,7 @@ export function useBackgroundLoader(updateBackground, resetBackground) { const data = await getBackgroundData(); if (data) { updateBackground(data); + resetStartTime('background'); // Reset timestamp after successful load } } catch (error) { console.error('Failed to load background:', error); @@ -34,21 +36,31 @@ export function useBackgroundLoader(updateBackground, resetBackground) { }, [updateBackground]); const refreshBackground = useCallback(() => { + resetStartTime('background'); // Reset timer on manual refresh resetBackground(); loadBackground(); }, [loadBackground, resetBackground]); - // Initial load - only run once on mount + // Initial load - check frequency before loading useEffect(() => { - const changeMode = localStorage.getItem('backgroundchange'); - const hasStartTime = localStorage.getItem('backgroundStartTime'); - - if (!hasStartTime || changeMode === 'refresh') { - localStorage.setItem('backgroundStartTime', Date.now()); + // Check if we should update based on frequency + if (shouldUpdateByFrequency('background')) { + loadBackground(); + } else { + // Load cached background without fetching new one + const cached = localStorage.getItem('currentBackground'); + if (cached) { + try { + updateBackground(JSON.parse(cached)); + } catch { + // If cache invalid, load new + loadBackground(); + } + } else { + loadBackground(); + } } - - loadBackground(); - }, [loadBackground]); + }, [loadBackground, updateBackground]); return { loadBackground, refreshBackground }; } diff --git a/src/features/background/options/sections/DisplaySettings.jsx b/src/features/background/options/sections/DisplaySettings.jsx index a37da0cd..00d40efd 100644 --- a/src/features/background/options/sections/DisplaySettings.jsx +++ b/src/features/background/options/sections/DisplaySettings.jsx @@ -1,36 +1,70 @@ import variables from 'config/variables'; -import { Checkbox } from 'components/Form/Settings'; +import { Checkbox, Dropdown } from 'components/Form/Settings'; import { Row, Content, Action } from 'components/Layout/Settings/Item'; +import { FREQUENCY_OPTIONS } from 'utils/frequencyManager'; +import { clearQueuesOnSettingChange } from 'utils/queueOperations'; const DisplaySettings = ({ usingImage }) => { return ( - - - - + + - + { + localStorage.setItem('backgroundStartTime', Date.now()); + // Clear queue if switching from refresh to time-based frequency + const oldValue = localStorage.getItem('backgroundFrequency'); + if (oldValue === 'refresh' && value !== 'refresh') { + clearQueuesOnSettingChange('backgroundFrequency'); + } + // Notify the frequency interval hook that the frequency changed + window.dispatchEvent(new CustomEvent('frequencyChanged', { + detail: { type: 'background' } + })); + }} + items={FREQUENCY_OPTIONS.map((opt) => ({ + value: opt.value, + text: variables.getMessage(opt.text), + }))} + /> + + + + - - - + + + + + + + ); }; diff --git a/src/features/quote/Quote.jsx b/src/features/quote/Quote.jsx index 4b625b4b..07731ee9 100644 --- a/src/features/quote/Quote.jsx +++ b/src/features/quote/Quote.jsx @@ -5,6 +5,7 @@ import { ShareModal } from 'components/Elements'; import { useQuoteState, useQuoteLoader, useQuoteActions } from './hooks'; import { AuthorInfo, AuthorInfoLegacy } from './components'; import EventBus from 'utils/eventbus'; +import { useFrequencyInterval } from '../../hooks/useFrequencyInterval'; import './scss/index.scss'; @@ -38,6 +39,9 @@ export default function Quote() { setLocalIsFavourited(!localIsFavourited); }; + // Set up frequency-based interval for automatic quote updates + useFrequencyInterval('quote', getQuote); + useEffect(() => { const handleRefresh = (data) => { if (data === 'quote') { diff --git a/src/features/quote/hooks/useQuoteLoader.js b/src/features/quote/hooks/useQuoteLoader.js index f849e686..0f197690 100644 --- a/src/features/quote/hooks/useQuoteLoader.js +++ b/src/features/quote/hooks/useQuoteLoader.js @@ -1,6 +1,7 @@ import { useCallback } from 'react'; import variables from 'config/variables'; import offline_quotes from '../offline_quotes.json'; +import { shouldUpdateByFrequency, resetStartTime } from 'utils/frequencyManager'; /** * Custom hook for loading quote data from various sources @@ -278,6 +279,21 @@ export function useQuoteLoader(updateQuote) { localStorage.setItem('quotePrefetchEnabled', 'true'); } + // Check if we should update based on frequency + if (!shouldUpdateByFrequency('quote')) { + // Load cached quote without fetching new one + const cached = localStorage.getItem('currentQuote'); + if (cached) { + try { + const cachedQuote = JSON.parse(cached); + updateQuote(cachedQuote); + return; + } catch { + // If cache invalid, continue to fetch new + } + } + } + // SPECIAL CASE: Favourite quote (highest priority, no queue) const favouriteQuote = localStorage.getItem('favouriteQuote'); if (favouriteQuote) { @@ -353,9 +369,10 @@ export function useQuoteLoader(updateQuote) { updateQuote(quoteData); } - // Step 4: Store current quote + // Step 4: Store current quote and reset timestamp try { localStorage.setItem('currentQuote', JSON.stringify(quoteData)); + resetStartTime('quote'); // Reset timestamp after successfully updating quote } catch (e) { console.warn('Could not save currentQuote to localStorage:', e); } diff --git a/src/features/quote/options/QuoteOptions.jsx b/src/features/quote/options/QuoteOptions.jsx index fdf341ff..0d0a10b7 100644 --- a/src/features/quote/options/QuoteOptions.jsx +++ b/src/features/quote/options/QuoteOptions.jsx @@ -12,6 +12,7 @@ import { } from 'components/Layout/Settings'; import { Checkbox, Dropdown, Textarea } from 'components/Form/Settings'; import { Button } from 'components/Elements'; +import { FREQUENCY_OPTIONS } from 'utils/frequencyManager'; const QuoteOptions = ({ currentSubSection, onSubSectionChange, sectionName }) => { const getCustom = () => { @@ -123,6 +124,39 @@ const QuoteOptions = ({ currentSubSection, onSubSectionChange, sectionName }) => ); }; + const FrequencyOptions = () => { + return ( + + + + { + localStorage.setItem('quoteStartTime', Date.now()); + // Clear queue if switching from refresh to time-based frequency + const oldValue = localStorage.getItem('quoteFrequency'); + if (oldValue === 'refresh' && value !== 'refresh') { + localStorage.removeItem('quoteQueue'); + } + // Notify the frequency interval hook that the frequency changed + window.dispatchEvent(new CustomEvent('frequencyChanged', { + detail: { type: 'quote' } + })); + }} + items={FREQUENCY_OPTIONS.map((opt) => ({ + value: opt.value, + text: variables.getMessage(opt.text), + }))} + /> + + + ); + }; + const AdditionalOptions = () => { return ( @@ -282,6 +316,7 @@ const QuoteOptions = ({ currentSubSection, onSubSectionChange, sectionName }) => + )} diff --git a/src/hooks/useFrequencyInterval.js b/src/hooks/useFrequencyInterval.js new file mode 100644 index 00000000..97db1640 --- /dev/null +++ b/src/hooks/useFrequencyInterval.js @@ -0,0 +1,86 @@ +import { useEffect, useState } from 'react'; +import { shouldUpdateByFrequency, FREQUENCY_INTERVALS } from 'utils/frequencyManager'; + +/** + * Hook to manage active intervals for time-based content updates + * Automatically starts/stops intervals based on tab visibility and frequency setting + * + * @param {string} type - 'background' or 'quote' + * @param {Function} updateCallback - Function to call when update is needed + */ +export function useFrequencyInterval(type, updateCallback) { + // Track frequency in state so we can react to changes + const [frequency, setFrequency] = useState(() => + localStorage.getItem(`${type}Frequency`) || 'refresh' + ); + + // Listen for frequency changes via custom storage events + useEffect(() => { + const handleFrequencyChange = (e) => { + // Listen for custom event dispatched when frequency changes + if (e.detail && e.detail.type === type) { + const newFrequency = localStorage.getItem(`${type}Frequency`) || 'refresh'; + setFrequency(newFrequency); + } + }; + + window.addEventListener(`frequencyChanged`, handleFrequencyChange); + + return () => { + window.removeEventListener(`frequencyChanged`, handleFrequencyChange); + }; + }, [type]); + + useEffect(() => { + // No interval needed for refresh mode + if (frequency === 'refresh') { + return; + } + + let intervalId; + + const startInterval = () => { + const interval = FREQUENCY_INTERVALS[frequency]; + if (!interval) return; + + // Set up interval to check and update at the specified frequency + intervalId = setInterval(() => { + if (shouldUpdateByFrequency(type)) { + updateCallback(); + } + }, interval); + }; + + const handleVisibilityChange = () => { + if (document.hidden) { + // Tab hidden - clear interval to save resources + if (intervalId) { + clearInterval(intervalId); + intervalId = null; + } + } else { + // Tab visible - check for catch-up and start interval + if (shouldUpdateByFrequency(type)) { + updateCallback(); + } + startInterval(); + } + }; + + // Start interval if tab is currently visible + if (!document.hidden) { + startInterval(); + } + + // Listen for visibility changes + document.addEventListener('visibilitychange', handleVisibilityChange); + + // Cleanup on unmount or frequency change + return () => { + if (intervalId) { + clearInterval(intervalId); + } + document.removeEventListener('visibilitychange', handleVisibilityChange); + }; + }, [type, updateCallback, frequency]); +} diff --git a/src/i18n/locales/ar.json b/src/i18n/locales/ar.json index bf3cf376..4939627b 100644 --- a/src/i18n/locales/ar.json +++ b/src/i18n/locales/ar.json @@ -187,6 +187,10 @@ "copy": "نسخ", "tweet": "تغريد", "favourite": "إضافة إلى المفضلة" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "العرض", "display_subtitle": "تغيير طريقة تحميل الخلفية ومعلومات الصور", "api": "إعدادات الواجهة البرمجية", - "api_subtitle": "خيارات الحصول على صورة من خدمة خارجية (واجهة برمجية)" + "api_subtitle": "خيارات الحصول على صورة من خدمة خارجية (واجهة برمجية)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "البحث", diff --git a/src/i18n/locales/arz.json b/src/i18n/locales/arz.json index 0fd68c6e..984fb95d 100644 --- a/src/i18n/locales/arz.json +++ b/src/i18n/locales/arz.json @@ -187,6 +187,10 @@ "copy": "Copy", "tweet": "Tweet", "favourite": "Favourite" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -306,7 +310,16 @@ "display": "Display", "display_subtitle": "Change how background and photo information are loaded", "api": "API Settings", - "api_subtitle": "Options for getting an image from an external service (API)" + "api_subtitle": "Options for getting an image from an external service (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Search", diff --git a/src/i18n/locales/az.json b/src/i18n/locales/az.json index 91d4720b..df038194 100644 --- a/src/i18n/locales/az.json +++ b/src/i18n/locales/az.json @@ -187,6 +187,10 @@ "copy": "Kopyala", "tweet": "Tvitter", "favourite": "Sevimli" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Ekran", "display_subtitle": "Fon və foto məlumatlarının necə yüklənəcəyini dəyişdirin", "api": "API Parametrləri", - "api_subtitle": "Xarici xidmətdən (API) şəkil almaq üçün seçimlər" + "api_subtitle": "Xarici xidmətdən (API) şəkil almaq üçün seçimlər", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Axtarış", diff --git a/src/i18n/locales/azb.json b/src/i18n/locales/azb.json index 8b70ff44..4b14f62d 100644 --- a/src/i18n/locales/azb.json +++ b/src/i18n/locales/azb.json @@ -187,6 +187,10 @@ "copy": "Kopyala", "tweet": "Tweet", "favourite": "Sevimli" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Göstərmə", "display_subtitle": "Arxa fon və foto məlumatının necə yükləndiyini dəyişdirin", "api": "API Parametrləri", - "api_subtitle": "Xarici xidmətdən (API) şəkil əldə etmək üçün seçimlər" + "api_subtitle": "Xarici xidmətdən (API) şəkil əldə etmək üçün seçimlər", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Axtarış", diff --git a/src/i18n/locales/bn.json b/src/i18n/locales/bn.json index 454016c3..ccfd4825 100644 --- a/src/i18n/locales/bn.json +++ b/src/i18n/locales/bn.json @@ -187,6 +187,10 @@ "copy": "কপি", "tweet": "টুইট", "favourite": "প্রিয়" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "প্রদর্শন", "display_subtitle": "পটভূমি এবং ছবির তথ্য কিভাবে লোড হয় তা পরিবর্তন করুন", "api": "API Settings", - "api_subtitle": "Options for getting an image from an external service (API)" + "api_subtitle": "Options for getting an image from an external service (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Search", diff --git a/src/i18n/locales/de_DE.json b/src/i18n/locales/de_DE.json index a4b75cf5..7d84573a 100644 --- a/src/i18n/locales/de_DE.json +++ b/src/i18n/locales/de_DE.json @@ -187,6 +187,10 @@ "copy": "Kopieren", "tweet": "Twitter", "favourite": "Favorit" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Anzeige", "display_subtitle": "Ändern Sie, wie Hintergrund- und Fotodaten geladen werden", "api": "API Einstellung", - "api_subtitle": "Optionen für den Abruf eines Bildes von einem externen Anbieter (API)" + "api_subtitle": "Optionen für den Abruf eines Bildes von einem externen Anbieter (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Suche", diff --git a/src/i18n/locales/el.json b/src/i18n/locales/el.json index 9fd94050..25b1cfac 100644 --- a/src/i18n/locales/el.json +++ b/src/i18n/locales/el.json @@ -187,6 +187,10 @@ "copy": "Copy", "tweet": "Tweet", "favourite": "Favourite" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -306,7 +310,16 @@ "display": "Display", "display_subtitle": "Change how background and photo information are loaded", "api": "API Settings", - "api_subtitle": "Options for getting an image from an external service (API)" + "api_subtitle": "Options for getting an image from an external service (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Search", diff --git a/src/i18n/locales/en_GB.json b/src/i18n/locales/en_GB.json index 6a4946da..8d70db6e 100644 --- a/src/i18n/locales/en_GB.json +++ b/src/i18n/locales/en_GB.json @@ -187,6 +187,10 @@ "copy": "Copy", "tweet": "Tweet", "favourite": "Favourite" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Display", "display_subtitle": "Change how background and photo information are loaded", "api": "API Settings", - "api_subtitle": "Options for getting an image from an external service (API)" + "api_subtitle": "Options for getting an image from an external service (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Search", diff --git a/src/i18n/locales/en_US.json b/src/i18n/locales/en_US.json index b9007d8a..0dcf8d51 100644 --- a/src/i18n/locales/en_US.json +++ b/src/i18n/locales/en_US.json @@ -187,6 +187,10 @@ "copy": "Copy", "tweet": "Tweet", "favourite": "Favorite" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -306,7 +310,16 @@ "display": "Display", "display_subtitle": "Change how background and photo information are loaded", "api": "API Settings", - "api_subtitle": "Options for getting an image from an external service (API)" + "api_subtitle": "Options for getting an image from an external service (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Search", diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index 61597ba7..b07e94c4 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -187,6 +187,10 @@ "copy": "Botón de copiar", "tweet": "Botón de Tweet", "favourite": "Botón de favoritos" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Mostrar", "display_subtitle": "Cambiar cómo se carga la información de fondo y de la foto", "api": "Configuración de la API", - "api_subtitle": "Opciones para obtener una imagen de un servicio externo (API)" + "api_subtitle": "Opciones para obtener una imagen de un servicio externo (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Búsqueda", diff --git a/src/i18n/locales/es_419.json b/src/i18n/locales/es_419.json index 25fb4c0f..ca02c833 100644 --- a/src/i18n/locales/es_419.json +++ b/src/i18n/locales/es_419.json @@ -187,6 +187,10 @@ "copy": "Botón de copiar", "tweet": "Botón de Tweet", "favourite": "Botón de favoritos" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Display", "display_subtitle": "Change how background and photo information are loaded", "api": "API Settings", - "api_subtitle": "Options for getting an image from an external service (API)" + "api_subtitle": "Options for getting an image from an external service (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Búsqueda", diff --git a/src/i18n/locales/et.json b/src/i18n/locales/et.json index cc272971..b18cea21 100644 --- a/src/i18n/locales/et.json +++ b/src/i18n/locales/et.json @@ -187,6 +187,10 @@ "copy": "Kopeeri", "tweet": "Tweedi", "favourite": "Lisa lemmikuks" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Kuva", "display_subtitle": "Muuda tausta ja foto teabe laadimise viisi", "api": "API seaded", - "api_subtitle": "Valikud pildi saamiseks välisest teenusest (API)" + "api_subtitle": "Valikud pildi saamiseks välisest teenusest (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Otsing", diff --git a/src/i18n/locales/fa.json b/src/i18n/locales/fa.json index c0ab3db8..2ed0494d 100644 --- a/src/i18n/locales/fa.json +++ b/src/i18n/locales/fa.json @@ -187,6 +187,10 @@ "copy": "Copy", "tweet": "Tweet", "favourite": "Favourite" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -306,7 +310,16 @@ "display": "Display", "display_subtitle": "Change how background and photo information are loaded", "api": "API Settings", - "api_subtitle": "Options for getting an image from an external service (API)" + "api_subtitle": "Options for getting an image from an external service (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Search", diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index f6d7069f..38d79537 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -187,6 +187,10 @@ "copy": "Copier", "tweet": "Tweeter", "favourite": "Favori" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Afficher", "display_subtitle": "Modifier la façon dont le fond d'écran et les informations photo sont chargés", "api": "Paramètres de l'API", - "api_subtitle": "Options pour obtenir une image à partir d'un service externe (API)" + "api_subtitle": "Options pour obtenir une image à partir d'un service externe (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Recherche", diff --git a/src/i18n/locales/hu.json b/src/i18n/locales/hu.json index 6236dfa3..38811309 100644 --- a/src/i18n/locales/hu.json +++ b/src/i18n/locales/hu.json @@ -187,6 +187,10 @@ "copy": "Copy", "tweet": "Tweet", "favourite": "Favourite" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -306,7 +310,16 @@ "display": "Display", "display_subtitle": "Change how background and photo information are loaded", "api": "API Settings", - "api_subtitle": "Options for getting an image from an external service (API)" + "api_subtitle": "Options for getting an image from an external service (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Search", diff --git a/src/i18n/locales/id_ID.json b/src/i18n/locales/id_ID.json index ec9bb512..2f33170d 100644 --- a/src/i18n/locales/id_ID.json +++ b/src/i18n/locales/id_ID.json @@ -187,6 +187,10 @@ "copy": "Salin", "tweet": "Tweet", "favourite": "Favorit" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Display", "display_subtitle": "Change how background and photo information are loaded", "api": "API Settings", - "api_subtitle": "Options for getting an image from an external service (API)" + "api_subtitle": "Options for getting an image from an external service (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Cari", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index fcbcb263..0b031e99 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -187,6 +187,10 @@ "copy": "Copy", "tweet": "Tweet", "favourite": "Favourite" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -306,7 +310,16 @@ "display": "Display", "display_subtitle": "Change how background and photo information are loaded", "api": "API Settings", - "api_subtitle": "Options for getting an image from an external service (API)" + "api_subtitle": "Options for getting an image from an external service (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Search", diff --git a/src/i18n/locales/lt.json b/src/i18n/locales/lt.json index 7ed382f7..5818e14d 100644 --- a/src/i18n/locales/lt.json +++ b/src/i18n/locales/lt.json @@ -187,6 +187,10 @@ "copy": "Kopijuoti", "tweet": "Tviteryje", "favourite": "Mėgstama" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Rodymas", "display_subtitle": "Keisti, kaip įkeliamas fonas ir nuotraukos informacija", "api": "API nustatymai", - "api_subtitle": "Parinktys paveikslėlio gavimui iš išorinės paslaugos (API)" + "api_subtitle": "Parinktys paveikslėlio gavimui iš išorinės paslaugos (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Paieška", diff --git a/src/i18n/locales/lv.json b/src/i18n/locales/lv.json index 4f717b87..e6ccfd2f 100644 --- a/src/i18n/locales/lv.json +++ b/src/i18n/locales/lv.json @@ -187,6 +187,10 @@ "copy": "Kopēt", "tweet": "Tvītot", "favourite": "Pievienot izlasei" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Attēlošana", "display_subtitle": "Mainīt, kā tiek ielādēts fons un fotogrāfijas informācija", "api": "API iestatījumi", - "api_subtitle": "Opcijas attēla iegūšanai no ārēja pakalpojuma (API)" + "api_subtitle": "Opcijas attēla iegūšanai no ārēja pakalpojuma (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Meklēšana", diff --git a/src/i18n/locales/nl.json b/src/i18n/locales/nl.json index e592d23a..12d19e53 100644 --- a/src/i18n/locales/nl.json +++ b/src/i18n/locales/nl.json @@ -187,6 +187,10 @@ "copy": "Kopieerknop", "tweet": "Tweetknop", "favourite": "Knop 'Toevoegen aan favorieten'" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Weergave", "display_subtitle": "Verander hoe achtergrond- en foto informatie geladen wordt", "api": "API Instellingen", - "api_subtitle": "Opties voor het verkrijgen van afbeeldingen van externe bronnen (API)" + "api_subtitle": "Opties voor het verkrijgen van afbeeldingen van externe bronnen (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Zoekbalk", diff --git a/src/i18n/locales/no.json b/src/i18n/locales/no.json index 78bac33a..e00443db 100644 --- a/src/i18n/locales/no.json +++ b/src/i18n/locales/no.json @@ -187,6 +187,10 @@ "copy": "Kopier knapp", "tweet": "Tweet", "favourite": "Favourite" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Display", "display_subtitle": "Change how background and photo information are loaded", "api": "API Settings", - "api_subtitle": "Options for getting an image from an external service (API)" + "api_subtitle": "Options for getting an image from an external service (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Søkebar", diff --git a/src/i18n/locales/peo.json b/src/i18n/locales/peo.json index 712cdbc1..6862421e 100644 --- a/src/i18n/locales/peo.json +++ b/src/i18n/locales/peo.json @@ -187,6 +187,10 @@ "copy": "Copy", "tweet": "Tweet", "favourite": "Favourite" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -306,7 +310,16 @@ "display": "Display", "display_subtitle": "Change how background and photo information are loaded", "api": "API Settings", - "api_subtitle": "Options for getting an image from an external service (API)" + "api_subtitle": "Options for getting an image from an external service (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Search", diff --git a/src/i18n/locales/pt.json b/src/i18n/locales/pt.json index 1148d931..579743e9 100644 --- a/src/i18n/locales/pt.json +++ b/src/i18n/locales/pt.json @@ -187,6 +187,10 @@ "copy": "Copiar", "tweet": "Tweetar", "favourite": "Favorito" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Ecrã", "display_subtitle": "Alterar como as informações de plano de fundo e foto são carregadas", "api": "Configurações da API", - "api_subtitle": "Opções para obter uma imagem de um serviço externo (API)" + "api_subtitle": "Opções para obter uma imagem de um serviço externo (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Pesquisar", diff --git a/src/i18n/locales/pt_BR.json b/src/i18n/locales/pt_BR.json index c43153e0..f89f58e4 100644 --- a/src/i18n/locales/pt_BR.json +++ b/src/i18n/locales/pt_BR.json @@ -187,6 +187,10 @@ "copy": "Copiar", "tweet": "Tweetar", "favourite": "Favorito" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Tela", "display_subtitle": "Alterar como as informações de plano de fundo e foto são carregadas", "api": "Configurações da API", - "api_subtitle": "Opções para obter uma imagem de um serviço externo (API)" + "api_subtitle": "Opções para obter uma imagem de um serviço externo (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Pesquisar", diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json index 4a002c6a..797ad56f 100644 --- a/src/i18n/locales/ru.json +++ b/src/i18n/locales/ru.json @@ -187,6 +187,10 @@ "copy": "Кнопка копирования", "tweet": "Кнопка твита", "favourite": "Кнопка для оценки" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Отображать", "display_subtitle": "Изменение способа загрузки фона и информации о фотографии", "api": "Настройки API", - "api_subtitle": "Варианты получения изображения из внешнего сервиса (API)" + "api_subtitle": "Варианты получения изображения из внешнего сервиса (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Панель поиска", diff --git a/src/i18n/locales/sl.json b/src/i18n/locales/sl.json index 8713caa6..3a0699ec 100644 --- a/src/i18n/locales/sl.json +++ b/src/i18n/locales/sl.json @@ -187,6 +187,10 @@ "copy": "Kopiraj", "tweet": "Tviti", "favourite": "Priljubljeno" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Prikaz", "display_subtitle": "Spremenite, kako se ozadje in informacije o fotografiji nalagajo", "api": "API nastavitve", - "api_subtitle": "Možnosti za pridobivanje slike iz zunanje storitve (API)" + "api_subtitle": "Možnosti za pridobivanje slike iz zunanje storitve (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Iskanje", diff --git a/src/i18n/locales/sv.json b/src/i18n/locales/sv.json index 1753fcb4..3d4a9efb 100644 --- a/src/i18n/locales/sv.json +++ b/src/i18n/locales/sv.json @@ -187,6 +187,10 @@ "copy": "Copy", "tweet": "Tweet", "favourite": "Favourite" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -306,7 +310,16 @@ "display": "Display", "display_subtitle": "Change how background and photo information are loaded", "api": "API Settings", - "api_subtitle": "Options for getting an image from an external service (API)" + "api_subtitle": "Options for getting an image from an external service (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Search", diff --git a/src/i18n/locales/ta.json b/src/i18n/locales/ta.json index 965b5c2b..e51a4820 100644 --- a/src/i18n/locales/ta.json +++ b/src/i18n/locales/ta.json @@ -187,6 +187,10 @@ "copy": "நகலெடு", "tweet": "ட்வீட்", "favourite": "பிடித்த" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "காட்சி", "display_subtitle": "பின்னணி மற்றும் புகைப்படத் தகவல்கள் எவ்வாறு ஏற்றப்படுகின்றன என்பதை மாற்றவும்", "api": "பநிஇ அமைப்புகள்", - "api_subtitle": "வெளிப்புற சேவையிலிருந்து (ஏபிஐ) படத்தைப் பெறுவதற்கான விருப்பங்கள்" + "api_subtitle": "வெளிப்புற சேவையிலிருந்து (ஏபிஐ) படத்தைப் பெறுவதற்கான விருப்பங்கள்", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "தேடல்", diff --git a/src/i18n/locales/tr_TR.json b/src/i18n/locales/tr_TR.json index 9cdeac07..92eec521 100644 --- a/src/i18n/locales/tr_TR.json +++ b/src/i18n/locales/tr_TR.json @@ -187,6 +187,10 @@ "copy": "Kopyala", "tweet": "Tweet'le", "favourite": "Favorilere Ekle" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Görüntüleme", "display_subtitle": "Arka plan ve fotoğraf bilgilerinin nasıl yükleneceğini değiştirin", "api": "API Ayarları", - "api_subtitle": "Harici bir hizmet (API) aracılığıyla görüntü alma seçeneklerini belirleyin." + "api_subtitle": "Harici bir hizmet (API) aracılığıyla görüntü alma seçeneklerini belirleyin.", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Arama", diff --git a/src/i18n/locales/uk.json b/src/i18n/locales/uk.json index a96e0b29..73dbe5cf 100644 --- a/src/i18n/locales/uk.json +++ b/src/i18n/locales/uk.json @@ -187,6 +187,10 @@ "copy": "Скопіювати", "tweet": "Твітнути", "favourite": "Улюблене" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "Відображати", "display_subtitle": "Зміна способу завантаження фону та інформації про фотографію", "api": "Налаштування API", - "api_subtitle": "Варіанти отримання зображення із зовнішнього сервісу (API)" + "api_subtitle": "Варіанти отримання зображення із зовнішнього сервісу (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Пошук", diff --git a/src/i18n/locales/vi.json b/src/i18n/locales/vi.json index bd65fb3c..e19b8c9b 100644 --- a/src/i18n/locales/vi.json +++ b/src/i18n/locales/vi.json @@ -187,6 +187,10 @@ "copy": "Copy", "tweet": "Tweet", "favourite": "Favourite" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -306,7 +310,16 @@ "display": "Display", "display_subtitle": "Change how background and photo information are loaded", "api": "API Settings", - "api_subtitle": "Options for getting an image from an external service (API)" + "api_subtitle": "Options for getting an image from an external service (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Search", diff --git a/src/i18n/locales/zh_CN.json b/src/i18n/locales/zh_CN.json index d799be95..278336f4 100644 --- a/src/i18n/locales/zh_CN.json +++ b/src/i18n/locales/zh_CN.json @@ -187,6 +187,10 @@ "copy": "显示复制按钮", "tweet": "显示发推按钮", "favourite": "显示收藏按钮" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -298,7 +302,16 @@ "display": "显示", "display_subtitle": "Change how background and photo information are loaded", "api": "API Settings", - "api_subtitle": "Options for getting an image from an external service (API)" + "api_subtitle": "Options for getting an image from an external service (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "搜索栏", diff --git a/src/i18n/locales/zh_Hant.json b/src/i18n/locales/zh_Hant.json index e2b6605e..26eb3321 100644 --- a/src/i18n/locales/zh_Hant.json +++ b/src/i18n/locales/zh_Hant.json @@ -187,6 +187,10 @@ "copy": "Copy", "tweet": "Tweet", "favourite": "Favourite" + }, + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the quote changes" } }, "greeting": { @@ -306,7 +310,16 @@ "display": "Display", "display_subtitle": "Change how background and photo information are loaded", "api": "API Settings", - "api_subtitle": "Options for getting an image from an external service (API)" + "api_subtitle": "Options for getting an image from an external service (API)", + "frequency": { + "title": "Update Frequency", + "subtitle": "How often the background changes", + "refresh": "Every tab", + "minute": "Once a minute", + "thirty": "Once every 30 minutes", + "hour": "Once an hour", + "day": "Once a day" + } }, "search": { "title": "Search", diff --git a/src/utils/data/default_settings.json b/src/utils/data/default_settings.json index a5026496..46559a1c 100644 --- a/src/utils/data/default_settings.json +++ b/src/utils/data/default_settings.json @@ -211,6 +211,14 @@ "name": "quoteType", "value": "quote_pack" }, + { + "name": "backgroundFrequency", + "value": "refresh" + }, + { + "name": "quoteFrequency", + "value": "refresh" + }, { "name": "backgroundFilter", "value": "none" diff --git a/src/utils/frequencyManager.js b/src/utils/frequencyManager.js new file mode 100644 index 00000000..b21fe934 --- /dev/null +++ b/src/utils/frequencyManager.js @@ -0,0 +1,89 @@ +/** + * Frequency Manager + * Manages time-based update frequencies for backgrounds and quotes + */ + +export const FREQUENCY_INTERVALS = { + refresh: 0, // Every tab refresh (immediate) + minute: 60 * 1000, // 1 minute + thirty: 30 * 60 * 1000, // 30 minutes + hour: 60 * 60 * 1000, // 1 hour + day: 24 * 60 * 60 * 1000, // 24 hours +}; + +export const FREQUENCY_OPTIONS = [ + { value: 'refresh', text: 'modals.main.settings.sections.background.frequency.refresh' }, + { value: 'minute', text: 'modals.main.settings.sections.background.frequency.minute' }, + { value: 'thirty', text: 'modals.main.settings.sections.background.frequency.thirty' }, + { value: 'hour', text: 'modals.main.settings.sections.background.frequency.hour' }, + { value: 'day', text: 'modals.main.settings.sections.background.frequency.day' }, +]; + +/** + * Check if enough time has elapsed to update + * @param {string} type - 'background' or 'quote' + * @returns {boolean} - true if update needed + */ +export function shouldUpdateByFrequency(type) { + const frequency = localStorage.getItem(`${type}Frequency`) || 'refresh'; + + // Always update on 'refresh' mode + if (frequency === 'refresh') { + return true; + } + + const startTimeKey = `${type}StartTime`; + const startTime = localStorage.getItem(startTimeKey); + + // If no start time, update and set it + if (!startTime) { + localStorage.setItem(startTimeKey, Date.now()); + return true; + } + + const elapsed = Date.now() - parseInt(startTime, 10); + + // Handle clock changes - if time went backwards, reset + if (elapsed < 0) { + localStorage.setItem(startTimeKey, Date.now()); + return true; + } + + // Handle clock changes - if elapsed is unreasonably large (>7 days), reset + const MAX_ELAPSED = 7 * 24 * 60 * 60 * 1000; // 7 days + if (elapsed > MAX_ELAPSED) { + localStorage.setItem(startTimeKey, Date.now()); + return true; + } + + const interval = FREQUENCY_INTERVALS[frequency]; + return elapsed >= interval; +} + +/** + * Reset the start time for a type + * @param {string} type - 'background' or 'quote' + */ +export function resetStartTime(type) { + localStorage.setItem(`${type}StartTime`, Date.now()); +} + +/** + * Get time remaining until next update + * @param {string} type - 'background' or 'quote' + * @returns {number} - milliseconds remaining, or 0 if should update + */ +export function getTimeUntilUpdate(type) { + const frequency = localStorage.getItem(`${type}Frequency`) || 'refresh'; + + if (frequency === 'refresh') return 0; + + const startTime = parseInt(localStorage.getItem(`${type}StartTime`), 10); + if (!startTime) return 0; + + const elapsed = Date.now() - startTime; + const interval = FREQUENCY_INTERVALS[frequency]; + const remaining = interval - elapsed; + + return Math.max(0, remaining); +}