185 lines
5.6 KiB
Dart
185 lines
5.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../../../../core/logging/logger.dart';
|
|
import '../../../../core/network/api_problem.dart';
|
|
import '../../../../core/network/api_problem_mapper.dart';
|
|
import '../../../../l10n/app_localizations.dart';
|
|
import '../../../../shared/theme/design_tokens.dart';
|
|
import '../../../../shared/widgets/toast/toast.dart';
|
|
import '../../../../shared/widgets/toast/toast_type.dart';
|
|
import '../../data/models/divination_params.dart';
|
|
import '../../data/models/divination_result.dart';
|
|
import '../../data/services/divination_run_service.dart';
|
|
import 'divination_result_screen.dart';
|
|
|
|
enum _ProcessingStep { preparing, deriving, done }
|
|
|
|
class DivinationProcessingScreen extends StatefulWidget {
|
|
const DivinationProcessingScreen({
|
|
super.key,
|
|
required this.params,
|
|
required this.yaoStates,
|
|
required this.runService,
|
|
});
|
|
|
|
final DivinationParams params;
|
|
final List<YaoType> yaoStates;
|
|
final DivinationRunService runService;
|
|
|
|
@override
|
|
State<DivinationProcessingScreen> createState() =>
|
|
_DivinationProcessingScreenState();
|
|
}
|
|
|
|
class _DivinationProcessingScreenState
|
|
extends State<DivinationProcessingScreen> {
|
|
static final Logger _logger = getLogger(
|
|
'features.divination.processing_screen',
|
|
);
|
|
_ProcessingStep _step = _ProcessingStep.preparing;
|
|
DivinationResultData? _resultData;
|
|
String? _errorMessage;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_startRun();
|
|
}
|
|
|
|
Future<void> _startRun() async {
|
|
try {
|
|
final aggregate = await widget.runService.run(
|
|
params: widget.params,
|
|
yaoStates: widget.yaoStates,
|
|
onDerived: () {
|
|
if (!mounted) {
|
|
return;
|
|
}
|
|
setState(() {
|
|
_step = _ProcessingStep.deriving;
|
|
});
|
|
},
|
|
onTextMessageEnd: () {
|
|
if (!mounted) {
|
|
return;
|
|
}
|
|
setState(() {
|
|
_step = _ProcessingStep.done;
|
|
});
|
|
},
|
|
);
|
|
if (!mounted) {
|
|
return;
|
|
}
|
|
setState(() {
|
|
_resultData = aggregate.toViewData(widget.params);
|
|
_step = _ProcessingStep.done;
|
|
});
|
|
} catch (error, stackTrace) {
|
|
_logger.error(
|
|
message: 'Divination processing failed while waiting result events',
|
|
error: error,
|
|
stackTrace: stackTrace,
|
|
extra: <String, dynamic>{
|
|
'step': _step.name,
|
|
'method': widget.params.method.name,
|
|
'questionType': widget.params.questionType.name,
|
|
},
|
|
);
|
|
if (!mounted) {
|
|
return;
|
|
}
|
|
final l10n = AppLocalizations.of(context)!;
|
|
final message = error is ApiProblem
|
|
? mapApiProblemToMessage(error, l10n)
|
|
: l10n.errorRequestGeneric;
|
|
setState(() {
|
|
_errorMessage = message;
|
|
});
|
|
Toast.show(context, message, type: ToastType.error);
|
|
}
|
|
}
|
|
|
|
void _openResult() {
|
|
final data = _resultData;
|
|
if (data == null) {
|
|
return;
|
|
}
|
|
Navigator.of(context).pushReplacement(
|
|
MaterialPageRoute<void>(
|
|
builder: (_) => DivinationResultScreen(data: data),
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l10n = AppLocalizations.of(context)!;
|
|
final colors = Theme.of(context).colorScheme;
|
|
|
|
final text = switch (_step) {
|
|
_ProcessingStep.preparing => l10n.transitionPreparing,
|
|
_ProcessingStep.deriving => l10n.transitionDeriving,
|
|
_ProcessingStep.done => l10n.transitionDone,
|
|
};
|
|
|
|
final canContinue = _step == _ProcessingStep.done && _resultData != null;
|
|
|
|
return Scaffold(
|
|
backgroundColor: colors.surface,
|
|
body: SafeArea(
|
|
child: Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(AppSpacing.xl),
|
|
child: _errorMessage == null
|
|
? GestureDetector(
|
|
onTap: canContinue ? _openResult : null,
|
|
child: Container(
|
|
width: 220,
|
|
height: 320,
|
|
decoration: BoxDecoration(
|
|
color: colors.surface,
|
|
borderRadius: BorderRadius.circular(AppRadius.lg),
|
|
border: Border.all(
|
|
color: colors.primary.withValues(alpha: 0.2),
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: colors.shadow.withValues(alpha: 0.25),
|
|
blurRadius: 22,
|
|
offset: const Offset(0, 12),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Icon(
|
|
canContinue ? Icons.visibility : Icons.auto_awesome,
|
|
color: colors.primary,
|
|
size: 34,
|
|
),
|
|
const SizedBox(height: AppSpacing.md),
|
|
Text(
|
|
text,
|
|
textAlign: TextAlign.center,
|
|
style: Theme.of(context).textTheme.titleMedium,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
)
|
|
: Text(
|
|
_errorMessage!,
|
|
textAlign: TextAlign.center,
|
|
style: Theme.of(
|
|
context,
|
|
).textTheme.titleMedium?.copyWith(color: colors.error),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|