207 lines
6.5 KiB
Vue
207 lines
6.5 KiB
Vue
<template>
|
|
<div class="chapter-reader" :class="{ rtl: store.readingDirection === 'rtl' }">
|
|
<div v-if="store.isLoading" class="loading">
|
|
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-primary"></div>
|
|
</div>
|
|
|
|
<div v-else-if="store.error" class="error">
|
|
{{ store.error }}
|
|
</div>
|
|
|
|
<div v-else class="reader-content">
|
|
<div class="reader-controls">
|
|
<button @click="store.previousPage" :disabled="store.isFirstPage">
|
|
<ChevronLeftIcon class="h-6 w-6" />
|
|
</button>
|
|
<div class="page-info"> {{ store.currentPage + 1 }} / {{ store.totalPages }} </div>
|
|
<button @click="store.nextPage" :disabled="store.isLastPage">
|
|
<ChevronRightIcon class="h-6 w-6" />
|
|
</button>
|
|
</div>
|
|
|
|
<div class="page-container" :style="{ transform: `scale(${store.zoom})` }">
|
|
<div v-if="!store.currentPageData" class="error"> Aucune donnée d'image disponible </div>
|
|
<div v-else-if="!store.currentPageData.base64Content" class="error"> Contenu de l'image manquant </div>
|
|
<img
|
|
v-else
|
|
:src="imageSource"
|
|
:alt="`Page ${store.currentPage + 1}`"
|
|
class="page-image"
|
|
@error="handleImageError"
|
|
@load="handleImageLoad" />
|
|
</div>
|
|
|
|
<div class="reader-settings">
|
|
<button @click="toggleReadingMode">
|
|
{{ store.readingMode === 'single' ? 'Mode Infini' : 'Mode Simple' }}
|
|
</button>
|
|
<button @click="toggleReadingDirection">
|
|
{{ store.readingDirection === 'ltr' ? 'RTL' : 'LTR' }}
|
|
</button>
|
|
<div class="zoom-controls">
|
|
<button @click="zoomOut">-</button>
|
|
<span>{{ Math.round(store.zoom * 100) }}%</span>
|
|
<button @click="zoomIn">+</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
class="debug-info"
|
|
style="
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
background: rgba(0, 0, 0, 0.8);
|
|
color: white;
|
|
padding: 10px;
|
|
font-size: 12px;
|
|
">
|
|
currentPage: {{ store.currentPage }}<br />
|
|
totalPages: {{ store.totalPages }}<br />
|
|
hasCurrentPageData: {{ !!store.currentPageData }}<br />
|
|
hasBase64Content: {{ !!store.currentPageData?.base64Content }}<br />
|
|
mimeType: {{ store.currentPageData?.mimeType }}<br />
|
|
lastAction: {{ store._debug.lastAction }}<br />
|
|
lastUpdate: {{ new Date(store._debug.lastUpdate).toLocaleTimeString() }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { onMounted, onUnmounted, computed, watch } from 'vue';
|
|
import { useReaderStore } from '../../application/store/readerStore';
|
|
import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/vue/24/outline';
|
|
|
|
const props = defineProps({
|
|
chapterId: {
|
|
type: String,
|
|
required: true
|
|
}
|
|
});
|
|
|
|
const store = useReaderStore();
|
|
|
|
const imageSource = computed(() => {
|
|
console.log('Computing imageSource:', {
|
|
hasCurrentPageData: !!store.currentPageData,
|
|
mimeType: store.currentPageData?.mimeType,
|
|
hasContent: !!store.currentPageData?.base64Content
|
|
});
|
|
|
|
if (!store.currentPageData?.base64Content || !store.currentPageData?.mimeType) {
|
|
console.error("Données d'image invalides:", store.currentPageData);
|
|
return '';
|
|
}
|
|
return `data:${store.currentPageData.mimeType};base64,${store.currentPageData.base64Content}`;
|
|
});
|
|
|
|
const handleImageError = e => {
|
|
console.error("Erreur de chargement de l'image:", e);
|
|
console.log("Source de l'image:", imageSource.value);
|
|
console.log('Données de la page:', store.currentPageData);
|
|
};
|
|
|
|
const handleImageLoad = () => {
|
|
console.log('Image chargée avec succès');
|
|
};
|
|
|
|
const toggleReadingMode = () => {
|
|
store.setReadingMode(store.readingMode === 'single' ? 'infinite' : 'single');
|
|
};
|
|
|
|
const toggleReadingDirection = () => {
|
|
store.setReadingDirection(store.readingDirection === 'ltr' ? 'rtl' : 'ltr');
|
|
};
|
|
|
|
const zoomIn = () => {
|
|
store.setZoom(Math.min(store.zoom + 0.1, 2));
|
|
};
|
|
|
|
const zoomOut = () => {
|
|
store.setZoom(Math.max(store.zoom - 0.1, 0.5));
|
|
};
|
|
|
|
const handleKeyPress = event => {
|
|
if (event.key === 'ArrowRight') {
|
|
store.nextPage();
|
|
} else if (event.key === 'ArrowLeft') {
|
|
store.previousPage();
|
|
}
|
|
};
|
|
|
|
watch(
|
|
() => props.chapterId,
|
|
newId => {
|
|
if (newId) {
|
|
console.log('ChapterId changed, loading new chapter:', newId);
|
|
store.loadChapter(newId);
|
|
}
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
|
|
onMounted(() => {
|
|
window.addEventListener('keydown', handleKeyPress);
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
window.removeEventListener('keydown', handleKeyPress);
|
|
});
|
|
</script>
|
|
|
|
<style lang="postcss" scoped>
|
|
.chapter-reader {
|
|
@apply w-full h-full flex flex-col items-center justify-center bg-gray-900 text-white;
|
|
}
|
|
|
|
.loading {
|
|
@apply flex items-center justify-center h-full;
|
|
}
|
|
|
|
.error {
|
|
@apply text-red-500 text-xl;
|
|
}
|
|
|
|
.reader-content {
|
|
@apply w-full h-full flex flex-col;
|
|
}
|
|
|
|
.reader-controls {
|
|
@apply flex items-center justify-between p-4 bg-gray-800;
|
|
}
|
|
|
|
.page-info {
|
|
@apply text-lg font-medium;
|
|
}
|
|
|
|
.page-container {
|
|
@apply flex-1 flex items-center justify-center overflow-hidden;
|
|
transform-origin: center;
|
|
}
|
|
|
|
.page-image {
|
|
@apply max-w-full max-h-full object-contain;
|
|
}
|
|
|
|
.reader-settings {
|
|
@apply flex items-center justify-center gap-4 p-4 bg-gray-800;
|
|
}
|
|
|
|
.zoom-controls {
|
|
@apply flex items-center gap-2;
|
|
}
|
|
|
|
button {
|
|
@apply px-4 py-2 bg-gray-700 rounded hover:bg-gray-600 transition-colors;
|
|
}
|
|
|
|
button:disabled {
|
|
@apply opacity-50 cursor-not-allowed;
|
|
}
|
|
|
|
.rtl {
|
|
direction: rtl;
|
|
}
|
|
</style>
|