Files
MangaReader/ios-app/Sources/Examples/IntegrationExample.swift
renato97 b474182dd9 Initial commit: MangaReader iOS App
 Features:
- App iOS completa para leer manga sin publicidad
- Scraper con WKWebView para manhwaweb.com
- Sistema de descargas offline
- Lector con zoom y navegación
- Favoritos y progreso de lectura
- Compatible con iOS 15+ y Sideloadly/3uTools

📦 Contenido:
- Backend Node.js con Puppeteer (opcional)
- App iOS con SwiftUI
- Scraper de capítulos e imágenes
- Sistema de almacenamiento local
- Testing completo
- Documentación exhaustiva

🧪 Prueba: Capítulo 789 de One Piece descargado exitosamente
  - 21 páginas descargadas
  - 4.68 MB total
  - URLs verificadas y funcionales

🎉 Generated with Claude Code (https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-04 15:34:18 +01:00

315 lines
9.6 KiB
Swift

import SwiftUI
// MARK: - Ejemplo de Integración del Sistema de Descargas
// Este archivo muestra cómo integrar el sistema de descargas en tu app
/// Ejemplo 1: Agregar DownloadsView a un TabView
struct MainTabViewExample: View {
@State private var selectedTab = 0
var body: some View {
TabView(selection: $selectedTab) {
// Home/Library
ContentView()
.tabItem {
Label("Biblioteca", systemImage: "books.vertical")
}
.tag(0)
// Downloads
DownloadsView()
.tabItem {
Label("Descargas", systemImage: "arrow.down.circle")
}
.tag(1)
// Settings
SettingsView()
.tabItem {
Label("Ajustes", systemImage: "gear")
}
.tag(2)
}
}
}
/// Ejemplo 2: Navegación desde MangaDetailView
struct MangaDetailViewWithNavigation: View {
let manga: Manga
var body: some View {
MangaDetailView(manga: manga)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Menu {
Button {
// Navegar a descargas
} label: {
Label("Ver Descargas", systemImage: "arrow.down.circle")
}
Button {
// Descargar último capítulo
} label: {
Label("Descargar último", systemImage: "arrow.down.doc")
}
} label: {
Image(systemName: "ellipsis.circle")
}
}
}
}
}
/// Ejemplo 3: Badge en TabView para mostrar descargas activas
struct MainTabViewWithBadge: View {
@StateObject private var downloadManager = DownloadManager.shared
@State private var selectedTab = 0
var body: some View {
TabView(selection: $selectedTab) {
ContentView()
.tabItem {
Label("Biblioteca", systemImage: "books.vertical")
}
.tag(0)
DownloadsView()
.tabItem {
Label("Descargas", systemImage: "arrow.down.circle")
}
.badge(downloadManager.activeDownloads.count)
.tag(1)
SettingsView()
.tabItem {
Label("Ajustes", systemImage: "gear")
}
.tag(2)
}
}
}
/// Ejemplo 4: Sheet para mostrar descargas desde cualquier vista
struct DownloadsSheetExample: View {
@State private var showingDownloads = false
@StateObject private var downloadManager = DownloadManager.shared
var body: some View {
VStack {
Text("Contenido Principal")
Button("Ver Descargas") {
showingDownloads = true
}
.disabled(downloadManager.activeDownloads.isEmpty)
}
.sheet(isPresented: $showingDownloads) {
NavigationView {
DownloadsView()
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Cerrar") {
showingDownloads = false
}
}
}
}
}
}
}
/// Ejemplo 5: Vista de configuración con opciones de descarga
struct SettingsView: View {
@StateObject private var storage = StorageService.shared
@StateObject private var downloadManager = DownloadManager.shared
@State private var showingClearAlert = false
var body: some View {
Form {
Section("Descargas") {
HStack {
Text("Almacenamiento usado")
Spacer()
Text(storage.formatFileSize(storage.getStorageSize()))
.foregroundColor(.secondary)
}
Button(role: .destructive) {
showingClearAlert = true
} label: {
Label("Limpiar todas las descargas", systemImage: "trash")
}
.disabled(storage.getStorageSize() == 0)
}
Section("Estadísticas") {
HStack {
Text("Descargas activas")
Spacer()
Text("\(downloadManager.activeDownloads.count)")
.foregroundColor(.secondary)
}
HStack {
Text("Completadas")
Spacer()
Text("\(downloadManager.completedDownloads.count)")
.foregroundColor(.secondary)
}
HStack {
Text("Fallidas")
Spacer()
Text("\(downloadManager.failedDownloads.count)")
.foregroundColor(.secondary)
}
}
Section("Preferencias") {
Toggle("Descargar solo en Wi-Fi", isOn: .constant(true))
Toggle("Notificar descargas completadas", isOn: .constant(true))
}
}
.navigationTitle("Ajustes")
.alert("Limpiar descargas", isPresented: $showingClearAlert) {
Button("Cancelar", role: .cancel) { }
Button("Limpiar", role: .destructive) {
storage.clearAllDownloads()
downloadManager.clearCompletedHistory()
downloadManager.clearFailedHistory()
}
} message: {
Text("Esta acción eliminará todos los capítulos descargados. ¿Estás seguro?")
}
}
}
/// Ejemplo 6: Widget de descargas activas en home
struct ActiveDownloadsWidget: View {
@ObservedObject var downloadManager = DownloadManager.shared
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("Descargas Activas")
.font(.headline)
if downloadManager.activeDownloads.isEmpty {
Text("No hay descargas activas")
.font(.caption)
.foregroundColor(.secondary)
} else {
ForEach(downloadManager.activeDownloads.prefix(3)) { task in
HStack {
VStack(alignment: .leading) {
Text(task.mangaTitle)
.font(.subheadline)
.lineLimit(1)
Text("Cap. \(task.chapterNumber)")
.font(.caption)
.foregroundColor(.secondary)
}
Spacer()
ProgressView(value: task.progress)
.frame(width: 50)
}
}
if downloadManager.activeDownloads.count > 3 {
Text("+\(downloadManager.activeDownloads.count - 3) más")
.font(.caption)
.foregroundColor(.secondary)
}
Button("Ver todas") {
// Navegar a DownloadsView
}
.buttonStyle(.bordered)
}
}
.padding()
.background(
RoundedRectangle(cornerRadius: 12)
.fill(Color(.systemGray6))
)
}
}
/// Ejemplo 7: Modificador para mostrar banner de descargas activas
struct ActiveDownloadsBannerModifier: ViewModifier {
@ObservedObject var downloadManager = DownloadManager.shared
@State private var isVisible = false
func body(content: Content) -> some View {
ZStack(alignment: .top) {
content
if downloadManager.hasActiveDownloads && isVisible {
HStack {
Image(systemName: "arrow.down.circle.fill")
.foregroundColor(.blue)
Text("\(downloadManager.activeDownloads.count) descarga(s) en progreso")
.font(.caption)
Spacer()
Button("Ver") {
// Navegar a DownloadsView
}
.font(.caption)
}
.padding()
.background(
RoundedRectangle(cornerRadius: 10)
.fill(.blue)
.shadow(color: .black.opacity(0.2), radius: 5, x: 0, y: 2)
)
.foregroundColor(.white)
.padding(.horizontal)
.padding(.top, 50)
.transition(.move(edge: .top).combined(with: .opacity))
}
}
.onAppear {
if downloadManager.hasActiveDownloads {
withAnimation(.spring()) {
isVisible = true
}
}
}
.onChange(of: downloadManager.hasActiveDownloads) { hasActive in
withAnimation(.spring()) {
isVisible = hasActive
}
}
}
}
extension View {
func activeDownloadsBanner() -> some View {
modifier(ActiveDownloadsBannerModifier())
}
}
// MARK: - Preview
#Preview {
MainTabViewWithBadge()
}
#Preview("Downloads Widget") {
ActiveDownloadsWidget()
.padding()
}
#Preview("Settings") {
NavigationView {
SettingsView()
}
}