import 'package:flutter/services.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:shared_preferences/shared_preferences.dart'; class NotaVersionPluri { const NotaVersionPluri({ required this.version, required this.resumen, required this.markdown, }); final String version; final String resumen; final String markdown; } class ContenidoAyudaPluri { const ContenidoAyudaPluri({ required this.onboarding, required this.notas, required this.versionActual, }); final String onboarding; final List notas; final String versionActual; } class ServicioContenidoApp { static const _keyOnboardingVisto = 'pluri_onboarding_visto_v1'; static const _keyVersionVista = 'pluri_ultima_version_novedades_v1'; static const _versiones = ['0.1.47']; Future debeMostrarInicio() async { final prefs = await SharedPreferences.getInstance(); final info = await PackageInfo.fromPlatform(); final versionActual = info.version; return !(prefs.getBool(_keyOnboardingVisto) ?? false) || prefs.getString(_keyVersionVista) != versionActual; } Future marcarVisto() async { final prefs = await SharedPreferences.getInstance(); final info = await PackageInfo.fromPlatform(); await prefs.setBool(_keyOnboardingVisto, true); await prefs.setString(_keyVersionVista, info.version); } Future cargar( String codigoIdioma, { bool soloPendientes = false, }) async { final info = await PackageInfo.fromPlatform(); final prefs = await SharedPreferences.getInstance(); final ultimaVista = prefs.getString(_keyVersionVista); final idioma = _idiomaSoportado(codigoIdioma); final mostrarOnboarding = !soloPendientes || !(prefs.getBool(_keyOnboardingVisto) ?? false); final onboarding = mostrarOnboarding ? await _cargarMarkdown( 'assets/content/onboarding/$idioma.md', fallback: 'assets/content/onboarding/en.md', ) : ''; final notas = []; for (final version in _versiones) { if (soloPendientes && ultimaVista != null && _compararVersiones(version, ultimaVista.split('+').first) <= 0) { continue; } final markdown = await _cargarMarkdown( 'assets/content/updates/$idioma/$version.md', fallback: 'assets/content/updates/en/$version.md', ); if (markdown.trim().isEmpty) continue; notas.add( NotaVersionPluri( version: version, resumen: _resumen(markdown), markdown: markdown, ), ); } return ContenidoAyudaPluri( onboarding: onboarding, notas: notas, versionActual: _versionCompleta(info), ); } Future _cargarMarkdown( String path, { required String fallback, }) async { try { return await rootBundle.loadString(path); } catch (_) { return rootBundle.loadString(fallback); } } String _idiomaSoportado(String codigo) { const soportados = { 'ar', 'bn', 'de', 'en', 'es', 'fr', 'hi', 'id', 'it', 'ja', 'pt', 'ru', 'zh', }; return soportados.contains(codigo) ? codigo : 'en'; } String _resumen(String markdown) { for (final line in markdown.split('\n')) { final limpia = line.trim(); if (limpia.toLowerCase().startsWith('resumen:')) { return limpia.substring(limpia.indexOf(':') + 1).trim(); } if (limpia.toLowerCase().startsWith('summary:')) { return limpia.substring(limpia.indexOf(':') + 1).trim(); } if (limpia.contains(':')) { final etiqueta = limpia.substring(0, limpia.indexOf(':')).toLowerCase(); const etiquetasResumen = { 'résumé', 'zusammenfassung', 'riepilogo', 'resumo', 'ملخص', 'সারাংশ', 'सारांश', 'ringkasan', '概要', '摘要', 'резюме', }; if (etiquetasResumen.contains(etiqueta)) { return limpia.substring(limpia.indexOf(':') + 1).trim(); } } } return ''; } String _versionCompleta(PackageInfo info) => '${info.version}+${info.buildNumber}'; int _compararVersiones(String a, String b) { final pa = a.split('.').map((e) => int.tryParse(e) ?? 0).toList(); final pb = b.split('.').map((e) => int.tryParse(e) ?? 0).toList(); for (var i = 0; i < 3; i++) { final va = i < pa.length ? pa[i] : 0; final vb = i < pb.length ? pb[i] : 0; if (va != vb) return va.compareTo(vb); } return 0; } }