react-native-pdf-jsi 2.2.7 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +299 -11
- package/android/src/main/java/org/wonday/pdf/FileDownloader.java +292 -0
- package/android/src/main/java/org/wonday/pdf/FileManager.java +123 -0
- package/android/src/main/java/org/wonday/pdf/LicenseVerifier.java +311 -0
- package/android/src/main/java/org/wonday/pdf/PDFExporter.java +769 -0
- package/android/src/main/java/org/wonday/pdf/RNPDFPackage.java +7 -0
- package/index.js +58 -0
- package/ios/RNPDFPdf/PDFExporter.h +16 -0
- package/ios/RNPDFPdf/PDFExporter.m +537 -0
- package/package.json +7 -6
- package/src/components/AnalyticsPanel.jsx +243 -0
- package/src/components/BookmarkIndicator.jsx +66 -0
- package/src/components/BookmarkListModal.jsx +378 -0
- package/src/components/BookmarkModal.jsx +253 -0
- package/src/components/BottomSheet.jsx +121 -0
- package/src/components/ExportMenu.jsx +223 -0
- package/src/components/LoadingOverlay.jsx +52 -0
- package/src/components/OperationsMenu.jsx +231 -0
- package/src/components/SidePanel.jsx +95 -0
- package/src/components/Toast.jsx +140 -0
- package/src/components/Toolbar.jsx +135 -0
- package/src/managers/AnalyticsManager.js +695 -0
- package/src/managers/BookmarkManager.js +538 -0
- package/src/managers/ExportManager.js +687 -0
- package/src/managers/FileManager.js +89 -0
- package/src/utils/ErrorHandler.js +179 -0
- package/src/utils/TestData.js +112 -0
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BookmarkManager - Smart Bookmark Management System
|
|
3
|
+
* Handles bookmark CRUD operations and reading progress tracking
|
|
4
|
+
*
|
|
5
|
+
* LICENSE:
|
|
6
|
+
* - Basic bookmarks (CRUD): MIT License (Free)
|
|
7
|
+
* - Enhanced features (colors, analytics): Commercial License (Paid)
|
|
8
|
+
*
|
|
9
|
+
* @author Punith M
|
|
10
|
+
* @version 1.0.0
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
14
|
+
import licenseManager, { ProFeature } from '../license/LicenseManager';
|
|
15
|
+
|
|
16
|
+
const STORAGE_KEY = '@react-native-pdf-jsi/bookmarks';
|
|
17
|
+
const PROGRESS_KEY = '@react-native-pdf-jsi/progress';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* BookmarkManager Class
|
|
21
|
+
*/
|
|
22
|
+
export class BookmarkManager {
|
|
23
|
+
constructor() {
|
|
24
|
+
this.bookmarks = new Map();
|
|
25
|
+
this.progress = new Map();
|
|
26
|
+
this.initialized = false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Initialize bookmark manager
|
|
31
|
+
* Load bookmarks and progress from AsyncStorage
|
|
32
|
+
*/
|
|
33
|
+
async initialize() {
|
|
34
|
+
if (this.initialized) return;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
// Load bookmarks
|
|
38
|
+
const bookmarksData = await AsyncStorage.getItem(STORAGE_KEY);
|
|
39
|
+
if (bookmarksData) {
|
|
40
|
+
const parsed = JSON.parse(bookmarksData);
|
|
41
|
+
this.bookmarks = new Map(Object.entries(parsed));
|
|
42
|
+
console.log(`📚 BookmarkManager: Loaded ${this.bookmarks.size} PDF bookmarks`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Load progress
|
|
46
|
+
const progressData = await AsyncStorage.getItem(PROGRESS_KEY);
|
|
47
|
+
if (progressData) {
|
|
48
|
+
const parsed = JSON.parse(progressData);
|
|
49
|
+
this.progress = new Map(Object.entries(parsed));
|
|
50
|
+
console.log(`📊 BookmarkManager: Loaded progress for ${this.progress.size} PDFs`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this.initialized = true;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error('📚 BookmarkManager: Initialization error:', error);
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Save bookmarks to AsyncStorage
|
|
62
|
+
*/
|
|
63
|
+
async saveBookmarks() {
|
|
64
|
+
try {
|
|
65
|
+
const data = Object.fromEntries(this.bookmarks);
|
|
66
|
+
await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
|
67
|
+
console.log('📚 BookmarkManager: Bookmarks saved');
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error('📚 BookmarkManager: Save error:', error);
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Save progress to AsyncStorage
|
|
76
|
+
*/
|
|
77
|
+
async saveProgress() {
|
|
78
|
+
try {
|
|
79
|
+
const data = Object.fromEntries(this.progress);
|
|
80
|
+
await AsyncStorage.setItem(PROGRESS_KEY, JSON.stringify(data));
|
|
81
|
+
console.log('📊 BookmarkManager: Progress saved');
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error('📊 BookmarkManager: Progress save error:', error);
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Create a bookmark
|
|
90
|
+
* @param {string} pdfId - PDF identifier
|
|
91
|
+
* @param {Object} bookmark - Bookmark data
|
|
92
|
+
* @returns {Promise<Object>} Created bookmark
|
|
93
|
+
*/
|
|
94
|
+
async createBookmark(pdfId, bookmark) {
|
|
95
|
+
await this.initialize();
|
|
96
|
+
|
|
97
|
+
// Check if using Pro features (colors)
|
|
98
|
+
if (bookmark.color && bookmark.color !== '#000000') {
|
|
99
|
+
licenseManager.requirePro('Bookmark Colors');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const newBookmark = {
|
|
103
|
+
id: this.generateId(),
|
|
104
|
+
pdfId,
|
|
105
|
+
page: bookmark.page,
|
|
106
|
+
name: bookmark.name || `Page ${bookmark.page}`,
|
|
107
|
+
color: bookmark.color || '#000000', // Default to black (free), colors require Pro
|
|
108
|
+
notes: bookmark.notes || '',
|
|
109
|
+
createdAt: new Date().toISOString(),
|
|
110
|
+
updatedAt: new Date().toISOString()
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Get or create bookmarks array for this PDF
|
|
114
|
+
const pdfBookmarks = this.bookmarks.get(pdfId) || [];
|
|
115
|
+
pdfBookmarks.push(newBookmark);
|
|
116
|
+
this.bookmarks.set(pdfId, pdfBookmarks);
|
|
117
|
+
|
|
118
|
+
await this.saveBookmarks();
|
|
119
|
+
|
|
120
|
+
console.log(`📚 BookmarkManager: Created bookmark for ${pdfId} page ${bookmark.page}`);
|
|
121
|
+
return newBookmark;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get all bookmarks for a PDF
|
|
126
|
+
* @param {string} pdfId - PDF identifier
|
|
127
|
+
* @returns {Promise<Array>} Array of bookmarks
|
|
128
|
+
*/
|
|
129
|
+
async getBookmarks(pdfId) {
|
|
130
|
+
await this.initialize();
|
|
131
|
+
|
|
132
|
+
const bookmarks = this.bookmarks.get(pdfId) || [];
|
|
133
|
+
return bookmarks.sort((a, b) => a.page - b.page);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get bookmark by ID
|
|
138
|
+
* @param {string} pdfId - PDF identifier
|
|
139
|
+
* @param {string} bookmarkId - Bookmark ID
|
|
140
|
+
* @returns {Promise<Object|null>} Bookmark or null
|
|
141
|
+
*/
|
|
142
|
+
async getBookmark(pdfId, bookmarkId) {
|
|
143
|
+
await this.initialize();
|
|
144
|
+
|
|
145
|
+
const bookmarks = this.bookmarks.get(pdfId) || [];
|
|
146
|
+
return bookmarks.find(b => b.id === bookmarkId) || null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Update a bookmark
|
|
151
|
+
* @param {string} pdfId - PDF identifier
|
|
152
|
+
* @param {string} bookmarkId - Bookmark ID
|
|
153
|
+
* @param {Object} updates - Fields to update
|
|
154
|
+
* @returns {Promise<Object>} Updated bookmark
|
|
155
|
+
*/
|
|
156
|
+
async updateBookmark(pdfId, bookmarkId, updates) {
|
|
157
|
+
await this.initialize();
|
|
158
|
+
|
|
159
|
+
const bookmarks = this.bookmarks.get(pdfId) || [];
|
|
160
|
+
const index = bookmarks.findIndex(b => b.id === bookmarkId);
|
|
161
|
+
|
|
162
|
+
if (index === -1) {
|
|
163
|
+
throw new Error(`Bookmark ${bookmarkId} not found`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const updatedBookmark = {
|
|
167
|
+
...bookmarks[index],
|
|
168
|
+
...updates,
|
|
169
|
+
updatedAt: new Date().toISOString()
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
bookmarks[index] = updatedBookmark;
|
|
173
|
+
this.bookmarks.set(pdfId, bookmarks);
|
|
174
|
+
|
|
175
|
+
await this.saveBookmarks();
|
|
176
|
+
|
|
177
|
+
console.log(`📚 BookmarkManager: Updated bookmark ${bookmarkId}`);
|
|
178
|
+
return updatedBookmark;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Delete a bookmark
|
|
183
|
+
* @param {string} pdfId - PDF identifier
|
|
184
|
+
* @param {string} bookmarkId - Bookmark ID
|
|
185
|
+
* @returns {Promise<boolean>} Success status
|
|
186
|
+
*/
|
|
187
|
+
async deleteBookmark(pdfId, bookmarkId) {
|
|
188
|
+
await this.initialize();
|
|
189
|
+
|
|
190
|
+
const bookmarks = this.bookmarks.get(pdfId) || [];
|
|
191
|
+
const filtered = bookmarks.filter(b => b.id !== bookmarkId);
|
|
192
|
+
|
|
193
|
+
if (filtered.length === bookmarks.length) {
|
|
194
|
+
return false; // Bookmark not found
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
this.bookmarks.set(pdfId, filtered);
|
|
198
|
+
await this.saveBookmarks();
|
|
199
|
+
|
|
200
|
+
console.log(`📚 BookmarkManager: Deleted bookmark ${bookmarkId}`);
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Delete all bookmarks for a PDF
|
|
206
|
+
* @param {string} pdfId - PDF identifier
|
|
207
|
+
* @returns {Promise<number>} Number of bookmarks deleted
|
|
208
|
+
*/
|
|
209
|
+
async deleteAllBookmarks(pdfId) {
|
|
210
|
+
await this.initialize();
|
|
211
|
+
|
|
212
|
+
const bookmarks = this.bookmarks.get(pdfId) || [];
|
|
213
|
+
const count = bookmarks.length;
|
|
214
|
+
|
|
215
|
+
this.bookmarks.delete(pdfId);
|
|
216
|
+
await this.saveBookmarks();
|
|
217
|
+
|
|
218
|
+
console.log(`📚 BookmarkManager: Deleted ${count} bookmarks for ${pdfId}`);
|
|
219
|
+
return count;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Get bookmarks by page
|
|
224
|
+
* @param {string} pdfId - PDF identifier
|
|
225
|
+
* @param {number} page - Page number
|
|
226
|
+
* @returns {Promise<Array>} Bookmarks on that page
|
|
227
|
+
*/
|
|
228
|
+
async getBookmarksOnPage(pdfId, page) {
|
|
229
|
+
await this.initialize();
|
|
230
|
+
|
|
231
|
+
const bookmarks = this.bookmarks.get(pdfId) || [];
|
|
232
|
+
return bookmarks.filter(b => b.page === page);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Check if page has bookmark
|
|
237
|
+
* @param {string} pdfId - PDF identifier
|
|
238
|
+
* @param {number} page - Page number
|
|
239
|
+
* @returns {Promise<boolean>} True if page has bookmark
|
|
240
|
+
*/
|
|
241
|
+
async hasBookmarkOnPage(pdfId, page) {
|
|
242
|
+
const bookmarks = await this.getBookmarksOnPage(pdfId, page);
|
|
243
|
+
return bookmarks.length > 0;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ============================================
|
|
247
|
+
// READING PROGRESS TRACKING
|
|
248
|
+
// ============================================
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Update reading progress
|
|
252
|
+
* @param {string} pdfId - PDF identifier
|
|
253
|
+
* @param {Object} progressData - Progress data
|
|
254
|
+
*/
|
|
255
|
+
async updateProgress(pdfId, progressData) {
|
|
256
|
+
await this.initialize();
|
|
257
|
+
|
|
258
|
+
// Basic progress tracking is free
|
|
259
|
+
// Advanced analytics require Pro
|
|
260
|
+
if (progressData.timeSpent || progressData.sessions) {
|
|
261
|
+
licenseManager.requirePro('Reading Progress Tracking');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const currentProgress = this.progress.get(pdfId) || {
|
|
265
|
+
pdfId,
|
|
266
|
+
currentPage: 1,
|
|
267
|
+
totalPages: progressData.totalPages || 0,
|
|
268
|
+
pagesRead: [],
|
|
269
|
+
timeSpent: 0,
|
|
270
|
+
sessions: 0,
|
|
271
|
+
lastRead: null,
|
|
272
|
+
createdAt: new Date().toISOString()
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
// Update progress
|
|
276
|
+
const updatedProgress = {
|
|
277
|
+
...currentProgress,
|
|
278
|
+
currentPage: progressData.currentPage || currentProgress.currentPage,
|
|
279
|
+
totalPages: progressData.totalPages || currentProgress.totalPages,
|
|
280
|
+
lastRead: new Date().toISOString(),
|
|
281
|
+
updatedAt: new Date().toISOString()
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// Track unique pages read
|
|
285
|
+
if (progressData.currentPage && !currentProgress.pagesRead.includes(progressData.currentPage)) {
|
|
286
|
+
updatedProgress.pagesRead = [...currentProgress.pagesRead, progressData.currentPage].sort((a, b) => a - b);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Track time spent
|
|
290
|
+
if (progressData.timeSpent) {
|
|
291
|
+
updatedProgress.timeSpent = currentProgress.timeSpent + progressData.timeSpent;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Track sessions
|
|
295
|
+
if (progressData.newSession) {
|
|
296
|
+
updatedProgress.sessions = currentProgress.sessions + 1;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
this.progress.set(pdfId, updatedProgress);
|
|
300
|
+
await this.saveProgress();
|
|
301
|
+
|
|
302
|
+
return updatedProgress;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Get reading progress
|
|
307
|
+
* @param {string} pdfId - PDF identifier
|
|
308
|
+
* @returns {Promise<Object>} Progress data
|
|
309
|
+
*/
|
|
310
|
+
async getProgress(pdfId) {
|
|
311
|
+
await this.initialize();
|
|
312
|
+
|
|
313
|
+
const progress = this.progress.get(pdfId);
|
|
314
|
+
|
|
315
|
+
if (!progress) {
|
|
316
|
+
return {
|
|
317
|
+
pdfId,
|
|
318
|
+
currentPage: 1,
|
|
319
|
+
totalPages: 0,
|
|
320
|
+
pagesRead: [],
|
|
321
|
+
percentage: 0,
|
|
322
|
+
timeSpent: 0,
|
|
323
|
+
sessions: 0,
|
|
324
|
+
lastRead: null
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Calculate percentage
|
|
329
|
+
const percentage = progress.totalPages > 0
|
|
330
|
+
? (progress.pagesRead.length / progress.totalPages) * 100
|
|
331
|
+
: 0;
|
|
332
|
+
|
|
333
|
+
return {
|
|
334
|
+
...progress,
|
|
335
|
+
percentage: Math.round(percentage)
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Get all reading progress
|
|
341
|
+
* @returns {Promise<Array>} Array of progress for all PDFs
|
|
342
|
+
*/
|
|
343
|
+
async getAllProgress() {
|
|
344
|
+
await this.initialize();
|
|
345
|
+
|
|
346
|
+
const allProgress = [];
|
|
347
|
+
for (const [pdfId, progress] of this.progress.entries()) {
|
|
348
|
+
const percentage = progress.totalPages > 0
|
|
349
|
+
? (progress.pagesRead.length / progress.totalPages) * 100
|
|
350
|
+
: 0;
|
|
351
|
+
|
|
352
|
+
allProgress.push({
|
|
353
|
+
...progress,
|
|
354
|
+
percentage: Math.round(percentage)
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return allProgress.sort((a, b) =>
|
|
359
|
+
new Date(b.lastRead) - new Date(a.lastRead)
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Delete reading progress
|
|
365
|
+
* @param {string} pdfId - PDF identifier
|
|
366
|
+
* @returns {Promise<boolean>} Success status
|
|
367
|
+
*/
|
|
368
|
+
async deleteProgress(pdfId) {
|
|
369
|
+
await this.initialize();
|
|
370
|
+
|
|
371
|
+
const existed = this.progress.has(pdfId);
|
|
372
|
+
this.progress.delete(pdfId);
|
|
373
|
+
|
|
374
|
+
if (existed) {
|
|
375
|
+
await this.saveProgress();
|
|
376
|
+
console.log(`📊 BookmarkManager: Deleted progress for ${pdfId}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return existed;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Get statistics for a PDF
|
|
384
|
+
* @param {string} pdfId - PDF identifier
|
|
385
|
+
* @returns {Promise<Object>} Statistics
|
|
386
|
+
*/
|
|
387
|
+
async getStatistics(pdfId) {
|
|
388
|
+
await this.initialize();
|
|
389
|
+
|
|
390
|
+
const bookmarks = await this.getBookmarks(pdfId);
|
|
391
|
+
const progress = await this.getProgress(pdfId);
|
|
392
|
+
|
|
393
|
+
return {
|
|
394
|
+
totalBookmarks: bookmarks.length,
|
|
395
|
+
pagesWithBookmarks: [...new Set(bookmarks.map(b => b.page))].length,
|
|
396
|
+
currentPage: progress.currentPage,
|
|
397
|
+
totalPages: progress.totalPages,
|
|
398
|
+
pagesRead: progress.pagesRead.length,
|
|
399
|
+
percentage: progress.percentage,
|
|
400
|
+
timeSpent: progress.timeSpent,
|
|
401
|
+
sessions: progress.sessions,
|
|
402
|
+
lastRead: progress.lastRead,
|
|
403
|
+
avgTimePerPage: progress.pagesRead.length > 0
|
|
404
|
+
? Math.round(progress.timeSpent / progress.pagesRead.length)
|
|
405
|
+
: 0
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// ============================================
|
|
410
|
+
// UTILITY METHODS
|
|
411
|
+
// ============================================
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Generate unique ID
|
|
415
|
+
* @returns {string} Unique ID
|
|
416
|
+
*/
|
|
417
|
+
generateId() {
|
|
418
|
+
return `bookmark_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Export bookmarks and progress
|
|
423
|
+
* @param {string} pdfId - PDF identifier (optional, exports all if not provided)
|
|
424
|
+
* @returns {Promise<Object>} Export data
|
|
425
|
+
*/
|
|
426
|
+
async exportData(pdfId = null) {
|
|
427
|
+
await this.initialize();
|
|
428
|
+
|
|
429
|
+
if (pdfId) {
|
|
430
|
+
return {
|
|
431
|
+
bookmarks: await this.getBookmarks(pdfId),
|
|
432
|
+
progress: await this.getProgress(pdfId),
|
|
433
|
+
exportedAt: new Date().toISOString()
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Export all
|
|
438
|
+
const allBookmarks = Object.fromEntries(this.bookmarks);
|
|
439
|
+
const allProgress = await this.getAllProgress();
|
|
440
|
+
|
|
441
|
+
return {
|
|
442
|
+
bookmarks: allBookmarks,
|
|
443
|
+
progress: allProgress,
|
|
444
|
+
exportedAt: new Date().toISOString()
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Import bookmarks and progress
|
|
450
|
+
* @param {Object} data - Import data
|
|
451
|
+
* @returns {Promise<Object>} Import summary
|
|
452
|
+
*/
|
|
453
|
+
async importData(data) {
|
|
454
|
+
await this.initialize();
|
|
455
|
+
|
|
456
|
+
let bookmarksImported = 0;
|
|
457
|
+
let progressImported = 0;
|
|
458
|
+
|
|
459
|
+
try {
|
|
460
|
+
// Import bookmarks
|
|
461
|
+
if (data.bookmarks) {
|
|
462
|
+
if (typeof data.bookmarks === 'object' && !Array.isArray(data.bookmarks)) {
|
|
463
|
+
// Import all PDFs
|
|
464
|
+
for (const [pdfId, bookmarks] of Object.entries(data.bookmarks)) {
|
|
465
|
+
this.bookmarks.set(pdfId, bookmarks);
|
|
466
|
+
bookmarksImported += bookmarks.length;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
await this.saveBookmarks();
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Import progress
|
|
473
|
+
if (data.progress) {
|
|
474
|
+
if (Array.isArray(data.progress)) {
|
|
475
|
+
data.progress.forEach(p => {
|
|
476
|
+
this.progress.set(p.pdfId, p);
|
|
477
|
+
progressImported++;
|
|
478
|
+
});
|
|
479
|
+
} else if (typeof data.progress === 'object') {
|
|
480
|
+
this.progress.set(data.progress.pdfId, data.progress);
|
|
481
|
+
progressImported++;
|
|
482
|
+
}
|
|
483
|
+
await this.saveProgress();
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
console.log(`📚 BookmarkManager: Imported ${bookmarksImported} bookmarks and ${progressImported} progress entries`);
|
|
487
|
+
|
|
488
|
+
return {
|
|
489
|
+
success: true,
|
|
490
|
+
bookmarksImported,
|
|
491
|
+
progressImported
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
} catch (error) {
|
|
495
|
+
console.error('📚 BookmarkManager: Import error:', error);
|
|
496
|
+
throw error;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Clear all data (use with caution!)
|
|
502
|
+
*/
|
|
503
|
+
async clearAll() {
|
|
504
|
+
this.bookmarks.clear();
|
|
505
|
+
this.progress.clear();
|
|
506
|
+
await AsyncStorage.multiRemove([STORAGE_KEY, PROGRESS_KEY]);
|
|
507
|
+
console.log('📚 BookmarkManager: All data cleared');
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Get storage size estimate
|
|
512
|
+
* @returns {Promise<Object>} Size information
|
|
513
|
+
*/
|
|
514
|
+
async getStorageSize() {
|
|
515
|
+
await this.initialize();
|
|
516
|
+
|
|
517
|
+
const bookmarksStr = JSON.stringify(Object.fromEntries(this.bookmarks));
|
|
518
|
+
const progressStr = JSON.stringify(Object.fromEntries(this.progress));
|
|
519
|
+
|
|
520
|
+
return {
|
|
521
|
+
bookmarks: {
|
|
522
|
+
count: Array.from(this.bookmarks.values()).flat().length,
|
|
523
|
+
sizeKB: Math.round(new Blob([bookmarksStr]).size / 1024)
|
|
524
|
+
},
|
|
525
|
+
progress: {
|
|
526
|
+
count: this.progress.size,
|
|
527
|
+
sizeKB: Math.round(new Blob([progressStr]).size / 1024)
|
|
528
|
+
},
|
|
529
|
+
totalKB: Math.round((new Blob([bookmarksStr]).size + new Blob([progressStr]).size) / 1024)
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Create singleton instance
|
|
535
|
+
const bookmarkManager = new BookmarkManager();
|
|
536
|
+
|
|
537
|
+
export default bookmarkManager;
|
|
538
|
+
|