react-native-pdf-jsi 2.2.8 → 3.0.1

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.
@@ -0,0 +1,89 @@
1
+ import { NativeModules } from 'react-native';
2
+
3
+ const { FileManager: NativeFileManager, FileDownloader } = NativeModules;
4
+
5
+ /**
6
+ * FileManager - JavaScript wrapper for file operations
7
+ * Provides methods for file management, downloading, and folder access
8
+ */
9
+ class FileManager {
10
+ /**
11
+ * Open the Downloads folder in the device's file manager
12
+ * Uses multiple fallback strategies for maximum compatibility
13
+ * @returns {Promise<boolean>} Resolves to true if folder opened successfully
14
+ */
15
+ static async openDownloadsFolder() {
16
+ try {
17
+ console.log('📂 [FileManager] Opening Downloads folder');
18
+
19
+ if (!NativeFileManager) {
20
+ throw new Error('FileManager native module not available');
21
+ }
22
+
23
+ const result = await NativeFileManager.openDownloadsFolder();
24
+ console.log('✅ [FileManager] Folder opened successfully');
25
+ return result;
26
+ } catch (error) {
27
+ console.error('❌ [FileManager] Error opening folder:', error);
28
+ throw error;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Download file to public Downloads folder using MediaStore API
34
+ * Ensures files are immediately visible in file managers
35
+ *
36
+ * @param {string} sourcePath Path to source file in app's cache
37
+ * @param {string} fileName Name for the downloaded file
38
+ * @param {string} mimeType MIME type (application/pdf, image/png, image/jpeg)
39
+ * @returns {Promise<string>} Resolves to public file path
40
+ */
41
+ static async downloadToPublicFolder(sourcePath, fileName, mimeType = 'application/pdf') {
42
+ try {
43
+ console.log('📥 [FileManager] Downloading to public folder:', fileName);
44
+
45
+ if (!FileDownloader) {
46
+ throw new Error('FileDownloader native module not available');
47
+ }
48
+
49
+ const publicPath = await FileDownloader.downloadToPublicFolder(sourcePath, fileName, mimeType);
50
+ console.log('✅ [FileManager] File downloaded:', publicPath);
51
+ return publicPath;
52
+ } catch (error) {
53
+ console.error('❌ [FileManager] Error downloading file:', error);
54
+ throw error;
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Download multiple files to public Downloads folder
60
+ *
61
+ * @param {Array<{sourcePath: string, fileName: string, mimeType: string}>} files Array of file objects
62
+ * @returns {Promise<Array<string>>} Resolves to array of public file paths
63
+ */
64
+ static async downloadMultipleFiles(files) {
65
+ try {
66
+ console.log(`📥 [FileManager] Downloading ${files.length} files to public folder`);
67
+
68
+ const results = [];
69
+ for (const file of files) {
70
+ const { sourcePath, fileName, mimeType = 'application/pdf' } = file;
71
+ const publicPath = await this.downloadToPublicFolder(sourcePath, fileName, mimeType);
72
+ results.push(publicPath);
73
+ }
74
+
75
+ console.log(`✅ [FileManager] Downloaded ${results.length} files successfully`);
76
+ return results;
77
+ } catch (error) {
78
+ console.error('❌ [FileManager] Error downloading multiple files:', error);
79
+ throw error;
80
+ }
81
+ }
82
+ }
83
+
84
+ export default FileManager;
85
+
86
+
87
+
88
+
89
+
@@ -0,0 +1,179 @@
1
+ /**
2
+ * ErrorHandler - Comprehensive error handling utilities
3
+ */
4
+
5
+ import {Alert} from 'react-native';
6
+
7
+ export class PDFError extends Error {
8
+ constructor(message, code, details) {
9
+ super(message);
10
+ this.code = code;
11
+ this.details = details;
12
+ this.name = 'PDFError';
13
+ }
14
+ }
15
+
16
+ export const ErrorCodes = {
17
+ // License errors
18
+ LICENSE_INVALID: 'LICENSE_INVALID',
19
+ LICENSE_EXPIRED: 'LICENSE_EXPIRED',
20
+ LICENSE_REQUIRED: 'LICENSE_REQUIRED',
21
+
22
+ // PDF loading errors
23
+ PDF_NOT_FOUND: 'PDF_NOT_FOUND',
24
+ PDF_CORRUPTED: 'PDF_CORRUPTED',
25
+ PDF_PASSWORD_REQUIRED: 'PDF_PASSWORD_REQUIRED',
26
+ PDF_LOAD_TIMEOUT: 'PDF_LOAD_TIMEOUT',
27
+
28
+ // Network errors
29
+ NETWORK_ERROR: 'NETWORK_ERROR',
30
+ NETWORK_TIMEOUT: 'NETWORK_TIMEOUT',
31
+
32
+ // Storage errors
33
+ STORAGE_FULL: 'STORAGE_FULL',
34
+ STORAGE_PERMISSION: 'STORAGE_PERMISSION',
35
+
36
+ // Feature errors
37
+ EXPORT_FAILED: 'EXPORT_FAILED',
38
+ OPERATION_FAILED: 'OPERATION_FAILED',
39
+ BOOKMARK_FAILED: 'BOOKMARK_FAILED',
40
+
41
+ // JSI errors
42
+ JSI_NOT_AVAILABLE: 'JSI_NOT_AVAILABLE',
43
+ NATIVE_MODULE_MISSING: 'NATIVE_MODULE_MISSING',
44
+
45
+ // Generic
46
+ UNKNOWN_ERROR: 'UNKNOWN_ERROR',
47
+ };
48
+
49
+ export const getUserFriendlyMessage = (error) => {
50
+ if (error instanceof PDFError) {
51
+ switch (error.code) {
52
+ case ErrorCodes.LICENSE_INVALID:
53
+ return 'Your license key is invalid. Please check and try again.';
54
+ case ErrorCodes.LICENSE_EXPIRED:
55
+ return 'Your license has expired. Please renew to continue using Pro features.';
56
+ case ErrorCodes.LICENSE_REQUIRED:
57
+ return 'This feature requires a Pro license. Upgrade to unlock!';
58
+ case ErrorCodes.PDF_NOT_FOUND:
59
+ return 'PDF file not found. Please check the file path.';
60
+ case ErrorCodes.PDF_CORRUPTED:
61
+ return 'This PDF file appears to be corrupted or invalid.';
62
+ case ErrorCodes.PDF_PASSWORD_REQUIRED:
63
+ return 'This PDF is password protected. Password support coming soon.';
64
+ case ErrorCodes.NETWORK_ERROR:
65
+ return 'Network error. Please check your internet connection.';
66
+ case ErrorCodes.NETWORK_TIMEOUT:
67
+ return 'Request timed out. Please try again.';
68
+ case ErrorCodes.STORAGE_FULL:
69
+ return 'Not enough storage space. Please free up some space and try again.';
70
+ case ErrorCodes.STORAGE_PERMISSION:
71
+ return 'Storage permission denied. Please grant permission in settings.';
72
+ case ErrorCodes.EXPORT_FAILED:
73
+ return 'Failed to export PDF. Please try again.';
74
+ case ErrorCodes.JSI_NOT_AVAILABLE:
75
+ return 'High-performance mode not available. Using standard mode.';
76
+ default:
77
+ return error.message;
78
+ }
79
+ }
80
+
81
+ // Handle standard errors
82
+ if (error.message) {
83
+ // Password errors
84
+ if (error.message.includes('Password') || error.message.includes('password')) {
85
+ return 'This PDF requires a password. Password support coming soon.';
86
+ }
87
+ // Network errors
88
+ if (error.message.includes('Network') || error.message.includes('fetch')) {
89
+ return 'Network error. Please check your connection and try again.';
90
+ }
91
+ // File errors
92
+ if (error.message.includes('not found') || error.message.includes('ENOENT')) {
93
+ return 'File not found. Please try again.';
94
+ }
95
+ // Permission errors
96
+ if (error.message.includes('permission') || error.message.includes('denied')) {
97
+ return 'Permission denied. Please grant necessary permissions.';
98
+ }
99
+
100
+ return error.message;
101
+ }
102
+
103
+ return 'An unexpected error occurred. Please try again.';
104
+ };
105
+
106
+ export const handleError = (
107
+ error,
108
+ context,
109
+ showAlert = true,
110
+ onRetry
111
+ ) => {
112
+ const message = getUserFriendlyMessage(error);
113
+ console.error(`❌ Error in ${context}:`, error);
114
+
115
+ if (showAlert) {
116
+ const buttons = onRetry
117
+ ? [
118
+ {text: 'Cancel', style: 'cancel'},
119
+ {text: 'Retry', onPress: onRetry},
120
+ ]
121
+ : [{text: 'OK'}];
122
+
123
+ Alert.alert(`Error: ${context}`, message, buttons);
124
+ }
125
+ };
126
+
127
+ export const withErrorHandling = async (
128
+ fn,
129
+ context,
130
+ onError
131
+ ) => {
132
+ try {
133
+ return await fn();
134
+ } catch (error) {
135
+ console.error(`❌ Error in ${context}:`, error);
136
+ if (onError) {
137
+ onError(error);
138
+ } else {
139
+ handleError(error, context);
140
+ }
141
+ return null;
142
+ }
143
+ };
144
+
145
+ export const validatePDFPath = (path) => {
146
+ if (!path || path.trim() === '') {
147
+ throw new PDFError('PDF path is empty', ErrorCodes.PDF_NOT_FOUND);
148
+ }
149
+ };
150
+
151
+ export const validatePageNumber = (page, totalPages) => {
152
+ if (page < 1 || page > totalPages) {
153
+ throw new PDFError(
154
+ `Page number ${page} is out of range (1-${totalPages})`,
155
+ ErrorCodes.UNKNOWN_ERROR
156
+ );
157
+ }
158
+ };
159
+
160
+ export const validateLicense = (licenseManager, featureName) => {
161
+ if (!licenseManager.isProActive()) {
162
+ throw new PDFError(
163
+ `${featureName} requires a Pro license`,
164
+ ErrorCodes.LICENSE_REQUIRED,
165
+ {feature: featureName}
166
+ );
167
+ }
168
+ };
169
+
170
+ export default {
171
+ PDFError,
172
+ ErrorCodes,
173
+ getUserFriendlyMessage,
174
+ handleError,
175
+ withErrorHandling,
176
+ validatePDFPath,
177
+ validatePageNumber,
178
+ validateLicense,
179
+ };
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Test Data - Sample PDFs and Test Scenarios
3
+ * Provides various test cases for Pro feature validation
4
+ */
5
+
6
+ export const SamplePDFs = {
7
+ SMALL: {
8
+ name: 'React Native Book Sample',
9
+ uri: 'http://samples.leanpub.com/thereactnativebook-sample.pdf',
10
+ cache: true,
11
+ description: 'Small PDF for quick testing',
12
+ expectedPages: 10,
13
+ },
14
+ MEDIUM: {
15
+ name: 'Lorem Ipsum Document',
16
+ uri: 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf',
17
+ cache: true,
18
+ description: 'Medium-sized PDF',
19
+ expectedPages: 1,
20
+ },
21
+ LARGE: {
22
+ name: 'JavaScript Guide',
23
+ uri: 'https://eloquentjavascript.net/Eloquent_JavaScript.pdf',
24
+ cache: true,
25
+ description: 'Large PDF for performance testing',
26
+ expectedPages: 472,
27
+ },
28
+ };
29
+
30
+ export const TestScenarios = {
31
+ BOOKMARKS: {
32
+ name: 'Test Bookmarks',
33
+ steps: [
34
+ 'Open PDF',
35
+ 'Navigate to page 3',
36
+ 'Create bookmark with Red color',
37
+ 'Add notes to bookmark',
38
+ 'Navigate to page 5',
39
+ 'Create bookmark with Blue color',
40
+ 'Open bookmark sidebar',
41
+ 'Tap bookmark to jump to page',
42
+ ],
43
+ },
44
+ EXPORT: {
45
+ name: 'Test Export',
46
+ steps: [
47
+ 'Open PDF',
48
+ 'Navigate to page 1',
49
+ 'Tap Export icon',
50
+ 'Choose PNG format',
51
+ 'Verify export success',
52
+ 'Try JPEG export',
53
+ 'Test batch export',
54
+ ],
55
+ },
56
+ OPERATIONS: {
57
+ name: 'Test PDF Operations',
58
+ steps: [
59
+ 'Open PDF',
60
+ 'Tap Operations icon',
61
+ 'Test Split (pages 1-2)',
62
+ 'Test Extract (pages 1,3,5)',
63
+ 'Verify output files',
64
+ ],
65
+ },
66
+ ANALYTICS: {
67
+ name: 'Test Analytics',
68
+ steps: [
69
+ 'Open PDF',
70
+ 'Navigate through 5 pages',
71
+ 'Wait 30 seconds',
72
+ 'Open Analytics panel',
73
+ 'Verify time tracking',
74
+ 'Check reading speed',
75
+ ],
76
+ },
77
+ };
78
+
79
+ export const SampleBookmarks = [
80
+ {
81
+ page: 1,
82
+ name: 'Introduction',
83
+ color: '#FF6B6B',
84
+ notes: 'Start of the document',
85
+ },
86
+ {
87
+ page: 5,
88
+ name: 'Important Section',
89
+ color: '#4ECDC4',
90
+ notes: 'Key information here',
91
+ },
92
+ {
93
+ page: 10,
94
+ name: 'Summary',
95
+ color: '#95E1D3',
96
+ notes: 'Conclusion and takeaways',
97
+ },
98
+ ];
99
+
100
+ export default {
101
+ SamplePDFs,
102
+ TestScenarios,
103
+ SampleBookmarks,
104
+ };
105
+
106
+
107
+
108
+
109
+
110
+
111
+
112
+