Extract AI Scribe suggestion logic
In Scribe desktop, the suggestion is displayed in a popup. In Scribe mobile, it will be displayed in a full screen modal. But it share the same logic about loading, success and error. So here I extract this logic and use it in the AI Scribe desktop to be able to use it later in AI Scribe mobile.
This commit is contained in:
@@ -25,6 +25,7 @@ export 'scribe/ai/presentation/utils/ai_scribe_constants.dart';
|
|||||||
export 'scribe/ai/presentation/utils/context_menu/hover_submenu_controller.dart';
|
export 'scribe/ai/presentation/utils/context_menu/hover_submenu_controller.dart';
|
||||||
export 'scribe/ai/presentation/utils/context_menu/context_submenu_controller.dart';
|
export 'scribe/ai/presentation/utils/context_menu/context_submenu_controller.dart';
|
||||||
export 'scribe/ai/presentation/utils/modal/ai_scribe_modal_manager.dart';
|
export 'scribe/ai/presentation/utils/modal/ai_scribe_modal_manager.dart';
|
||||||
|
export 'scribe/ai/presentation/utils/modal/ai_scribe_suggestion_state_mixin.dart';
|
||||||
export 'scribe/ai/presentation/utils/modal/anchored_modal_layout_calculator.dart';
|
export 'scribe/ai/presentation/utils/modal/anchored_modal_layout_calculator.dart';
|
||||||
export 'scribe/ai/presentation/widgets/button/ai_assistant_button.dart';
|
export 'scribe/ai/presentation/widgets/button/ai_assistant_button.dart';
|
||||||
export 'scribe/ai/presentation/widgets/button/inline_ai_assist_button.dart';
|
export 'scribe/ai/presentation/widgets/button/inline_ai_assist_button.dart';
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:core/presentation/state/failure.dart';
|
||||||
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:dartz/dartz.dart' as dartz;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:scribe/scribe.dart';
|
||||||
|
import 'package:scribe/scribe/ai/data/network/ai_api_exception.dart';
|
||||||
|
|
||||||
|
mixin AiScribeSuggestionStateMixin<T extends StatefulWidget> on State<T> {
|
||||||
|
GenerateAITextInteractor? get interactor => _interactor;
|
||||||
|
GenerateAITextInteractor? _interactor;
|
||||||
|
|
||||||
|
ValueNotifier<dartz.Either<Failure, Success>> get suggestionState => _suggestionState;
|
||||||
|
final ValueNotifier<dartz.Either<Failure, Success>> _suggestionState =
|
||||||
|
ValueNotifier(dartz.Right(GenerateAITextLoading()));
|
||||||
|
|
||||||
|
AIAction get aiAction;
|
||||||
|
String? get content;
|
||||||
|
ImagePaths get imagePaths;
|
||||||
|
OnSelectAiScribeSuggestionAction get onSelectAction;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
if (!Get.isRegistered<GenerateAITextInteractor>()) {
|
||||||
|
_suggestionState.value = dartz.Left(
|
||||||
|
GenerateAITextFailure(
|
||||||
|
GenerateAITextInteractorIsNotRegisteredException(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_interactor = Get.find<GenerateAITextInteractor>();
|
||||||
|
loadSuggestion();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> loadSuggestion() async {
|
||||||
|
_suggestionState.value = dartz.Right(GenerateAITextLoading());
|
||||||
|
|
||||||
|
final result = await _interactor!.execute(
|
||||||
|
aiAction,
|
||||||
|
content,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
|
result.fold(
|
||||||
|
(failure) => _suggestionState.value = dartz.Left(failure),
|
||||||
|
(success) => _suggestionState.value = dartz.Right(success),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildStateContent(
|
||||||
|
BuildContext context,
|
||||||
|
ScribeLocalizations localizations,
|
||||||
|
) {
|
||||||
|
return ValueListenableBuilder<dartz.Either<Failure, Success>>(
|
||||||
|
valueListenable: _suggestionState,
|
||||||
|
builder: (_, stateValue, __) {
|
||||||
|
return stateValue.fold(
|
||||||
|
(failure) => buildErrorState(localizations),
|
||||||
|
(value) {
|
||||||
|
if (value is GenerateAITextSuccess) {
|
||||||
|
final hasContent = content?.trim().isNotEmpty == true;
|
||||||
|
|
||||||
|
return buildSuccessState(
|
||||||
|
value.response.result,
|
||||||
|
hasContent,
|
||||||
|
localizations,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return buildLoadingState(localizations);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildLoadingState(ScribeLocalizations localizations) {
|
||||||
|
return AiScribeSuggestionLoading(
|
||||||
|
imagePaths: imagePaths,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildErrorState(ScribeLocalizations localizations) {
|
||||||
|
return AiScribeSuggestionError(
|
||||||
|
imagePaths: imagePaths,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildSuccessState(
|
||||||
|
String suggestionText,
|
||||||
|
bool hasContent,
|
||||||
|
ScribeLocalizations localizations,
|
||||||
|
) {
|
||||||
|
return AiScribeSuggestionSuccess(
|
||||||
|
imagePaths: imagePaths,
|
||||||
|
suggestionText: suggestionText,
|
||||||
|
hasContent: hasContent,
|
||||||
|
onSelectAction: onSelectAction,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_suggestionState.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,9 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:core/presentation/resources/image_paths.dart';
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
import 'package:core/presentation/state/failure.dart';
|
|
||||||
import 'package:core/presentation/state/success.dart';
|
|
||||||
import 'package:dartz/dartz.dart' as dartz;
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
import 'package:scribe/scribe.dart';
|
import 'package:scribe/scribe.dart';
|
||||||
import 'package:scribe/scribe/ai/data/network/ai_api_exception.dart';
|
|
||||||
|
|
||||||
class AiScribeSuggestionWidget extends StatefulWidget {
|
class AiScribeSuggestionWidget extends StatefulWidget {
|
||||||
final AIAction aiAction;
|
final AIAction aiAction;
|
||||||
@@ -37,42 +32,20 @@ class AiScribeSuggestionWidget extends StatefulWidget {
|
|||||||
_AiScribeSuggestionWidgetState();
|
_AiScribeSuggestionWidgetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AiScribeSuggestionWidgetState extends State<AiScribeSuggestionWidget> {
|
class _AiScribeSuggestionWidgetState extends State<AiScribeSuggestionWidget>
|
||||||
GenerateAITextInteractor? _interactor;
|
with AiScribeSuggestionStateMixin {
|
||||||
|
@override
|
||||||
final ValueNotifier<dartz.Either<Failure, Success>> _state =
|
AIAction get aiAction => widget.aiAction;
|
||||||
ValueNotifier(dartz.Right(GenerateAITextLoading()));
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
String? get content => widget.content;
|
||||||
super.initState();
|
|
||||||
|
|
||||||
if (!Get.isRegistered<GenerateAITextInteractor>()) {
|
@override
|
||||||
_state.value = dartz.Left(
|
ImagePaths get imagePaths => widget.imagePaths;
|
||||||
GenerateAITextFailure(
|
|
||||||
const GenerateAITextInteractorIsNotRegisteredException(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_interactor = Get.find<GenerateAITextInteractor>();
|
@override
|
||||||
_loadSuggestion();
|
OnSelectAiScribeSuggestionAction get onSelectAction =>
|
||||||
}
|
widget.onSelectAiScribeSuggestionAction;
|
||||||
|
|
||||||
Future<void> _loadSuggestion() async {
|
|
||||||
final result = await _interactor!.execute(
|
|
||||||
widget.aiAction,
|
|
||||||
widget.content,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!mounted) return;
|
|
||||||
|
|
||||||
result.fold(
|
|
||||||
(failure) => _state.value = dartz.Left(failure),
|
|
||||||
(success) => _state.value = dartz.Right(success),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -147,30 +120,7 @@ class _AiScribeSuggestionWidgetState extends State<AiScribeSuggestionWidget> {
|
|||||||
imagePaths: widget.imagePaths,
|
imagePaths: widget.imagePaths,
|
||||||
),
|
),
|
||||||
Flexible(
|
Flexible(
|
||||||
child: ValueListenableBuilder<dartz.Either<Failure, Success>>(
|
child: buildStateContent(context, localizations),
|
||||||
valueListenable: _state,
|
|
||||||
builder: (_, state, __) {
|
|
||||||
return state.fold(
|
|
||||||
(_) => AiScribeSuggestionError(
|
|
||||||
imagePaths: widget.imagePaths,
|
|
||||||
),
|
|
||||||
(value) {
|
|
||||||
if (value is GenerateAITextSuccess) {
|
|
||||||
return AiScribeSuggestionSuccess(
|
|
||||||
imagePaths: widget.imagePaths,
|
|
||||||
suggestionText: value.response.result,
|
|
||||||
hasContent: hasContent,
|
|
||||||
onSelectAction: widget.onSelectAiScribeSuggestionAction,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return AiScribeSuggestionLoading(
|
|
||||||
imagePaths: widget.imagePaths,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -205,7 +155,7 @@ class _AiScribeSuggestionWidgetState extends State<AiScribeSuggestionWidget> {
|
|||||||
widget.buttonPosition != null && widget.buttonSize != null;
|
widget.buttonPosition != null && widget.buttonSize != null;
|
||||||
|
|
||||||
void _handleClickOutside() {
|
void _handleClickOutside() {
|
||||||
final shouldDismiss = _state.value.fold(
|
final shouldDismiss = suggestionState.value.fold(
|
||||||
(failure) => failure is GenerateAITextFailure,
|
(failure) => failure is GenerateAITextFailure,
|
||||||
(success) => success is GenerateAITextSuccess,
|
(success) => success is GenerateAITextSuccess,
|
||||||
);
|
);
|
||||||
@@ -213,10 +163,4 @@ class _AiScribeSuggestionWidgetState extends State<AiScribeSuggestionWidget> {
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_state.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user