edit
This commit is contained in:
@@ -154,6 +154,7 @@
|
|||||||
isBookLoaded: false,
|
isBookLoaded: false,
|
||||||
fb2CurrentPage: 0,
|
fb2CurrentPage: 0,
|
||||||
fb2TotalPages: 1,
|
fb2TotalPages: 1,
|
||||||
|
toc: []
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== УТИЛИТЫ ==========
|
// ========== УТИЛИТЫ ==========
|
||||||
@@ -287,16 +288,17 @@
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const toc = state.book.navigation.toc || [];
|
const toc = state.book.navigation.toc || [];
|
||||||
const chapters = toc.map(ch => ({
|
state.toc = toc.map(ch => ({
|
||||||
label: (ch.label || '').trim(),
|
label: (ch.label || '').trim(),
|
||||||
href: ch.href || ''
|
href: ch.href || ''
|
||||||
}));
|
}));
|
||||||
sendMessage('chaptersLoaded', { chapters });
|
sendMessage('chaptersLoaded', { chapters: state.toc });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugLog('TOC error: ' + e.message);
|
debugLog('TOC error: ' + e.message);
|
||||||
}
|
}
|
||||||
|
const charsPerScreen = Math.max(600, Math.floor((window.innerWidth * window.innerHeight) / 400));
|
||||||
|
|
||||||
return state.book.locations.generate(1600);
|
return state.book.locations.generate(charsPerScreen);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
state.totalPages = state.book.locations.length();
|
state.totalPages = state.book.locations.length();
|
||||||
@@ -309,13 +311,29 @@
|
|||||||
state.rendition.on('relocated', location => {
|
state.rendition.on('relocated', location => {
|
||||||
if (!location || !location.start) return;
|
if (!location || !location.start) return;
|
||||||
state.currentCfi = location.start.cfi;
|
state.currentCfi = location.start.cfi;
|
||||||
|
// 1. Достаем страницы ВНУТРИ главы
|
||||||
|
// Если epub.js не успел посчитать (бывает на долю секунды), ставим 1
|
||||||
|
const chapterPage = location.start.displayed ? location.start.displayed.page : 1;
|
||||||
|
const chapterTotal = location.start.displayed ? location.start.displayed.total : 1;
|
||||||
|
|
||||||
|
// 2. Ищем красивое название главы по её href
|
||||||
|
const currentHref = location.start.href || '';
|
||||||
|
let chapterName = currentHref; // По умолчанию системное имя
|
||||||
|
|
||||||
|
// Пытаемся найти точное совпадение в сохраненном оглавлении
|
||||||
|
const foundChapter = state.toc.find(ch => currentHref.includes(ch.href));
|
||||||
|
if (foundChapter) {
|
||||||
|
chapterName = foundChapter.label;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
sendMessage('progressUpdate', {
|
sendMessage('progressUpdate', {
|
||||||
progress: state.book.locations.percentageFromCfi(state.currentCfi) || 0,
|
progress: state.book.locations.percentageFromCfi(state.currentCfi) || 0,
|
||||||
cfi: state.currentCfi,
|
cfi: state.currentCfi,
|
||||||
currentPage: location.start.location || 0,
|
currentPage: location.start.location || 0, // Глобальная страница
|
||||||
totalPages: state.totalPages,
|
totalPages: state.totalPages, // Всего глобальных страниц
|
||||||
chapter: location.start.href || ''
|
chapterCurrentPage: chapterPage, // <--- Страница в главе!
|
||||||
|
chapterTotalPages: chapterTotal, // <--- Всего страниц в главе!
|
||||||
|
chapter: chapterName // <--- Красивое имя главы
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugLog('Relocated error: ' + e.message);
|
debugLog('Relocated error: ' + e.message);
|
||||||
|
|||||||
@@ -33,6 +33,19 @@ public partial class ReaderViewModel : BaseViewModel
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string? _selectedChapter;
|
private string? _selectedChapter;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private int _chapterCurrentPage = 1;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private int _chapterTotalPages = 1;
|
||||||
|
|
||||||
|
// Это свойство будет обновляться автоматически при изменении любого из полей выше
|
||||||
|
public string ChapterProgressText => $"{ChapterCurrentPage} из {ChapterTotalPages}";
|
||||||
|
|
||||||
|
// Чтобы ChapterProgressText уведомлял интерфейс, добавим частичные методы (особенность Toolkit)
|
||||||
|
partial void OnChapterCurrentPageChanged(int value) => OnPropertyChanged(nameof(ChapterProgressText));
|
||||||
|
partial void OnChapterTotalPagesChanged(int value) => OnPropertyChanged(nameof(ChapterProgressText));
|
||||||
|
|
||||||
public List<string> AvailableFonts { get; } = new()
|
public List<string> AvailableFonts { get; } = new()
|
||||||
{
|
{
|
||||||
"serif",
|
"serif",
|
||||||
|
|||||||
@@ -20,6 +20,21 @@
|
|||||||
DefaultFile="index.html"
|
DefaultFile="index.html"
|
||||||
HorizontalOptions="Fill"
|
HorizontalOptions="Fill"
|
||||||
VerticalOptions="Fill" />
|
VerticalOptions="Fill" />
|
||||||
|
<VerticalStackLayout VerticalOptions="End"
|
||||||
|
HorizontalOptions="Center"
|
||||||
|
Padding="10"
|
||||||
|
InputTransparent="True">
|
||||||
|
<Frame BackgroundColor="#AA000000"
|
||||||
|
CornerRadius="15"
|
||||||
|
Padding="10,2"
|
||||||
|
BorderColor="Transparent"
|
||||||
|
HasShadow="False">
|
||||||
|
<Label Text="{Binding ChapterProgressText}"
|
||||||
|
TextColor="White"
|
||||||
|
FontSize="12"
|
||||||
|
HorizontalTextAlignment="Center" />
|
||||||
|
</Frame>
|
||||||
|
</VerticalStackLayout>
|
||||||
|
|
||||||
<!-- Overlay Menu -->
|
<!-- Overlay Menu -->
|
||||||
<Grid IsVisible="{Binding IsMenuVisible}"
|
<Grid IsVisible="{Binding IsMenuVisible}"
|
||||||
|
|||||||
@@ -18,10 +18,6 @@ public partial class ReaderPage : ContentPage
|
|||||||
_viewModel = viewModel;
|
_viewModel = viewModel;
|
||||||
BindingContext = viewModel;
|
BindingContext = viewModel;
|
||||||
_viewModel.OnJavaScriptRequested += OnJavaScriptRequested;
|
_viewModel.OnJavaScriptRequested += OnJavaScriptRequested;
|
||||||
//Microsoft.Maui.Controls.Application.Current.Deactivated += async (s, e) =>
|
|
||||||
//{
|
|
||||||
// if (_isActive) await SaveCurrentProgress();
|
|
||||||
//};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async void OnAppearing()
|
protected override async void OnAppearing()
|
||||||
@@ -207,6 +203,15 @@ public partial class ReaderPage : ContentPage
|
|||||||
var chapter = data["chapter"]?.ToString();
|
var chapter = data["chapter"]?.ToString();
|
||||||
var currentPage = data["currentPage"]?.Value<int>() ?? 0;
|
var currentPage = data["currentPage"]?.Value<int>() ?? 0;
|
||||||
var totalPages = data["totalPages"]?.Value<int>() ?? 0;
|
var totalPages = data["totalPages"]?.Value<int>() ?? 0;
|
||||||
|
|
||||||
|
// Ловим новые данные по главе
|
||||||
|
var chapterPage = data["chapterCurrentPage"]?.Value<int>() ?? 1;
|
||||||
|
var chapterTotal = data["chapterTotalPages"]?.Value<int>() ?? 1;
|
||||||
|
// Обновляем ViewModel на главном потоке
|
||||||
|
MainThread.BeginInvokeOnMainThread(() => {
|
||||||
|
_viewModel.ChapterCurrentPage = chapterPage;
|
||||||
|
_viewModel.ChapterTotalPages = chapterTotal;
|
||||||
|
});
|
||||||
await _viewModel.SaveProgressAsync(progress, cfi, chapter, currentPage, totalPages);
|
await _viewModel.SaveProgressAsync(progress, cfi, chapter, currentPage, totalPages);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user