feat: dark mode complet + préférences utilisateur
- Ajout du store userPreferencesStore (thème, vue, tri, pagination, lecteur) - Page UserPreferencesPage pour configurer toutes les préférences - Câblage des prefs dans HomePage (viewMode, sortBy, itemsPerPage), readerStore (fallback prefs), ChapterReader (autoHide, autoFullscreen, sync), useNotifications (toastDuration) - Thème sombre (dark: Tailwind) sur tous les composants Vue : Layout, Pagination, NotificationToast, MangaCard, MangaVolume, MangaDetails, AddManga, HomePage, ActivityPage, JobItem, MangaDeleteModal, MangaEditModal, MangaPreferredSourcesModal, ManageChaptersModal, MangaChapterList, MangaChapter, ConversionPage, FileUploadArea, ConversionProgress, NewImportPage, FileImportCard, MangaMatchCard, StatusBadge, ImportResults - i18n partiellement initialisé Jeremy Guillot
This commit is contained in:
parent
48d819ba72
commit
ec1ef8fe68
@@ -0,0 +1,142 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
const STORAGE_KEY = 'mangarr_preferences';
|
||||
|
||||
const defaultState = {
|
||||
theme: 'system',
|
||||
language: 'fr',
|
||||
defaultView: 'grid',
|
||||
itemsPerPage: 20,
|
||||
sortBy: 'title',
|
||||
readingDirection: 'ltr',
|
||||
readingMode: 'scroll',
|
||||
autoFullscreen: false,
|
||||
autoHideHeaderReader: true,
|
||||
toastDuration: 5000,
|
||||
};
|
||||
|
||||
function loadFromStorage() {
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
if (stored) {
|
||||
return { ...defaultState, ...JSON.parse(stored) };
|
||||
}
|
||||
} catch {
|
||||
// ignore parse errors
|
||||
}
|
||||
return { ...defaultState };
|
||||
}
|
||||
|
||||
let mediaQueryUnsubscribe = null;
|
||||
|
||||
export const useUserPreferencesStore = defineStore('userPreferences', {
|
||||
state: () => loadFromStorage(),
|
||||
|
||||
actions: {
|
||||
applyTheme() {
|
||||
// Nettoyer le listener précédent
|
||||
if (mediaQueryUnsubscribe) {
|
||||
mediaQueryUnsubscribe();
|
||||
mediaQueryUnsubscribe = null;
|
||||
}
|
||||
|
||||
const html = document.documentElement;
|
||||
|
||||
if (this.theme === 'dark') {
|
||||
html.classList.add('dark');
|
||||
} else if (this.theme === 'light') {
|
||||
html.classList.remove('dark');
|
||||
} else {
|
||||
// mode 'system'
|
||||
const mq = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
const handler = (e) => {
|
||||
if (e.matches) {
|
||||
html.classList.add('dark');
|
||||
} else {
|
||||
html.classList.remove('dark');
|
||||
}
|
||||
};
|
||||
handler(mq);
|
||||
mq.addEventListener('change', handler);
|
||||
mediaQueryUnsubscribe = () => mq.removeEventListener('change', handler);
|
||||
}
|
||||
},
|
||||
|
||||
setTheme(theme) {
|
||||
this.theme = theme;
|
||||
this.persist();
|
||||
this.applyTheme();
|
||||
},
|
||||
|
||||
setLanguage(language) {
|
||||
this.language = language;
|
||||
this.persist();
|
||||
},
|
||||
|
||||
setDefaultView(view) {
|
||||
this.defaultView = view;
|
||||
this.persist();
|
||||
},
|
||||
|
||||
setItemsPerPage(count) {
|
||||
this.itemsPerPage = count;
|
||||
this.persist();
|
||||
},
|
||||
|
||||
setSortBy(sort) {
|
||||
this.sortBy = sort;
|
||||
this.persist();
|
||||
},
|
||||
|
||||
setReadingDirection(direction) {
|
||||
this.readingDirection = direction;
|
||||
this.persist();
|
||||
},
|
||||
|
||||
setReadingMode(mode) {
|
||||
this.readingMode = mode;
|
||||
this.persist();
|
||||
},
|
||||
|
||||
setAutoFullscreen(value) {
|
||||
this.autoFullscreen = value;
|
||||
this.persist();
|
||||
},
|
||||
|
||||
setAutoHideHeaderReader(value) {
|
||||
this.autoHideHeaderReader = value;
|
||||
this.persist();
|
||||
},
|
||||
|
||||
setToastDuration(duration) {
|
||||
this.toastDuration = duration;
|
||||
this.persist();
|
||||
},
|
||||
|
||||
resetToDefaults() {
|
||||
Object.assign(this, defaultState);
|
||||
this.persist();
|
||||
this.applyTheme();
|
||||
},
|
||||
|
||||
persist() {
|
||||
try {
|
||||
const data = {
|
||||
theme: this.theme,
|
||||
language: this.language,
|
||||
defaultView: this.defaultView,
|
||||
itemsPerPage: this.itemsPerPage,
|
||||
sortBy: this.sortBy,
|
||||
readingDirection: this.readingDirection,
|
||||
readingMode: this.readingMode,
|
||||
autoFullscreen: this.autoFullscreen,
|
||||
autoHideHeaderReader: this.autoHideHeaderReader,
|
||||
toastDuration: this.toastDuration,
|
||||
};
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
||||
} catch {
|
||||
// ignore storage errors
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user