بنقرة واحدة
download-system
Background chapter downloads and offline reading
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Background chapter downloads and offline reading
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
Image caching and storage management with expo-image
Reusable component patterns and styling
URL schemes and deep link handling
Common patterns for Expo SDK 54 and React Native development
Paperback-compatible extension runtime and source API
Multi-language support with i18n-js
استنادا إلى تصنيف SOC المهني
| name | Download System |
| description | Background chapter downloads and offline reading |
import { downloadService } from '../services/downloadService';
// Download a chapter
await downloadService.downloadChapter(manga, chapter, sourceId);
// Download multiple chapters
await downloadService.downloadChapters(manga, chapters, sourceId);
// Cancel download
await downloadService.cancelDownload(chapterId);
// Get download status
const status = await downloadService.getDownloadStatus(chapterId);
// Get all downloads
const downloads = await downloadService.getAllDownloads();
// Delete downloaded chapter
await downloadService.deleteDownload(chapterId);
type DownloadStatus =
| 'pending'
| 'downloading'
| 'completed'
| 'failed'
| 'cancelled';
interface Download {
mangaId: string;
chapterId: string;
status: DownloadStatus;
progress: number; // 0-100
totalPages: number;
downloadedPages: number;
error?: string;
}
Uses react-native-background-actions:
import BackgroundService from 'react-native-background-actions';
const options = {
taskName: 'Download',
taskTitle: 'Downloading chapters',
taskDesc: 'Chapter 1 of 10',
taskIcon: { name: 'ic_launcher', type: 'mipmap' },
progressBar: { max: 100, value: 0 },
};
await BackgroundService.start(downloadTask, options);
await BackgroundService.updateNotification({ taskDesc: 'Chapter 2 of 10' });
await BackgroundService.stop();
Required config in plugins/withBackgroundActionsServiceType.js:
// Adds foregroundServiceType="dataSync" to AndroidManifest.xml
module.exports = function withBackgroundActionsServiceType(config) {
return withAndroidManifest(config, (config) => {
// Modify service declaration
return config;
});
};
When the app goes to background, the ExtensionRunner WebView may become unavailable.
The headlessExtensionRuntime.ts provides a fallback that runs extensions directly in
the React Native JS engine:
import { headlessRuntime } from '../services/headlessExtensionRuntime';
// Check if headless runtime is available
if (headlessRuntime.isAvailable()) {
// Run extension method without WebView
const result = await headlessRuntime.runExtensionMethod(
extensionId,
'getChapterDetails',
[mangaId, chapterId]
);
}
The headless runtime:
eval() in a sandboxed environmentApp object with createRequestManager, createSourceStateManager, etc.fetch for HTTP requests (no CORS in React Native)documentDirectory/
└── downloads/
└── {mangaId}/
└── {chapterId}/
├── page_001.jpg
├── page_002.jpg
└── ...
import * as FileSystem from 'expo-file-system';
const downloadDir = `${FileSystem.documentDirectory}downloads/`;
// Create directory
await FileSystem.makeDirectoryAsync(
`${downloadDir}${mangaId}/${chapterId}`,
{ intermediates: true }
);
// Download image
await FileSystem.downloadAsync(
imageUrl,
`${downloadDir}${mangaId}/${chapterId}/page_${index}.jpg`
);
// Check if exists
const info = await FileSystem.getInfoAsync(path);
if (info.exists) { /* ... */ }
// Delete
await FileSystem.deleteAsync(path, { idempotent: true });
// Queue management
const queue: DownloadTask[] = [];
let isProcessing = false;
async function processQueue() {
if (isProcessing || queue.length === 0) return;
isProcessing = true;
const task = queue.shift();
await downloadChapter(task);
isProcessing = false;
processQueue(); // Process next
}
function addToQueue(task: DownloadTask) {
queue.push(task);
processQueue();
}
import * as Notifications from 'expo-notifications';
// Show progress notification
await Notifications.scheduleNotificationAsync({
content: {
title: 'Downloading',
body: `${manga.title} - Chapter ${chapter.chapNum}`,
data: { mangaId, chapterId },
},
trigger: null, // Immediate
});