Root cause behind most "stuck loading" reports: the backend changed shape (schedules became an array, location_preferences a string) while the mobile parser still hard-cast to Map/int. The TypeError was swallowed by a silent catch that returned null, and screens only handled the success state, so a parse failure rendered as a permanent spinner. Same class of bug appeared across service lists via non-null map lookups and a total absence of request timeouts. Ported from prosappweb: - in-app suggestions (POST /suggestions) - policies/terms from GET /settings/policies - configurable appointment length (slot_duration_minutes) - block/unblock calendar slots (POST /services/block) - GPS city detection on the profile (Nominatim) - server-side professional search with haversine distance - retry cooldown after a rejected professional application Reliability: - parse schedules array (day_of_week 0=Mon) and string location_preferences - read times as wall clock, so 08:00 stays 08:00 across timezones - carry minutes into hours in TimeOfDay.add; a minute-based step used to loop forever and freeze the calendar (covered by test/time_slots_test.dart) - semver update check instead of string equality, which blocked every build that did not exactly match the configured version - request timeouts across all repositories - surface HTTP >= 400 instead of reporting failed writes as success - error states with retry instead of an indefinite shimmer Includes pre-existing uncommitted work from the UI redesign. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
97 lines
2.7 KiB
Dart
97 lines
2.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:injector/injector.dart';
|
|
import 'package:package_info_plus/package_info_plus.dart';
|
|
import 'package:prosappco/components/general_drawer_item.dart';
|
|
import 'package:prosappco/screens/configuration/policy_text_screen.dart';
|
|
import 'package:setting_repository/setting_repository.dart';
|
|
import 'dart:io' show Platform;
|
|
|
|
class ConfigurationAboutScreen extends StatefulWidget {
|
|
const ConfigurationAboutScreen({super.key});
|
|
|
|
@override
|
|
State<ConfigurationAboutScreen> createState() =>
|
|
_ConfigurationAboutScreenState();
|
|
}
|
|
|
|
class _ConfigurationAboutScreenState extends State<ConfigurationAboutScreen> {
|
|
final settingRepository = Injector.appInstance.get<SettingRepository>();
|
|
SettingEntity? settings;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
_loadSettings();
|
|
|
|
}
|
|
|
|
void _loadSettings() {
|
|
settingRepository.getSettings().then(
|
|
(value) => setState(() {
|
|
settings = value;
|
|
}),
|
|
);
|
|
}
|
|
|
|
Future<void> _openPolicy(String title, String Function(PoliciesEntity) pick) async {
|
|
try {
|
|
final policies = await settingRepository.getPolicies();
|
|
if (!mounted) return;
|
|
Navigator.push(
|
|
context,
|
|
CupertinoPageRoute(
|
|
builder: (_) => PolicyTextScreen(title: title, content: pick(policies)),
|
|
),
|
|
);
|
|
} catch (_) {
|
|
if (!mounted) return;
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('No se pudo cargar el contenido')),
|
|
);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('Acerca de la aplicación'),
|
|
),
|
|
body: ListView(
|
|
children: [
|
|
GeneralDrawerItem(
|
|
label: 'Políticas de privacidad',
|
|
onTap: () => _openPolicy(
|
|
'Políticas de privacidad',
|
|
(p) => p.privacy,
|
|
),
|
|
trailing: true,
|
|
),
|
|
GeneralDrawerItem(
|
|
label: 'Términos y condiciones',
|
|
onTap: () => _openPolicy(
|
|
'Términos y condiciones',
|
|
(p) => p.terms,
|
|
),
|
|
trailing: true,
|
|
),
|
|
Platform.isIOS
|
|
? GeneralDrawerItem(
|
|
label: 'Versión de la aplicación',
|
|
subtitle: settings?.versionIos,
|
|
)
|
|
: Container(),
|
|
Platform.isAndroid
|
|
? GeneralDrawerItem(
|
|
label: 'Versión de la aplicación',
|
|
subtitle: settings?.versionAndroid,
|
|
)
|
|
: Container(),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|