Skip to content

Commit 13b5711

Browse files
authored
Merge pull request #241 from casualWaist/218-improve-interactive-translator
improve ITStep Transitions
2 parents 5b4c268 + 27c641e commit 13b5711

File tree

4 files changed

+223
-61
lines changed

4 files changed

+223
-61
lines changed

lib/pangea/choreographer/controllers/it_controller.dart

+65-24
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class ITController {
2828
String? sourceText;
2929
List<ITStep> completedITSteps = [];
3030
CurrentITStep? currentITStep;
31+
CurrentITStep? nextITStep;
3132
GoldRouteTracker goldRouteTracker = GoldRouteTracker.defaultTracker;
3233
List<int> payLoadIds = [];
3334

@@ -42,6 +43,7 @@ class ITController {
4243
sourceText = null;
4344
completedITSteps = [];
4445
currentITStep = null;
46+
nextITStep = null;
4547
goldRouteTracker = GoldRouteTracker.defaultTracker;
4648
payLoadIds = [];
4749

@@ -130,36 +132,75 @@ class ITController {
130132
);
131133
}
132134

133-
currentITStep = null;
134-
135-
final ITResponseModel res = await _customInputTranslation(currentText);
136-
// final ITResponseModel res = await (useCustomInput ||
137-
// currentText.isEmpty ||
138-
// translationId == null ||
139-
// completedITSteps.last.chosenContinuance?.indexSavedByServer ==
140-
// null
141-
// ? _customInputTranslation(currentText)
142-
// : _systemChoiceTranslation(translationId));
143-
144-
if (res.goldContinuances != null && res.goldContinuances!.isNotEmpty) {
145-
goldRouteTracker = GoldRouteTracker(
146-
res.goldContinuances!,
147-
sourceText!,
135+
if (nextITStep == null) {
136+
currentITStep = null;
137+
138+
final ITResponseModel res = await _customInputTranslation(currentText);
139+
// final ITResponseModel res = await (useCustomInput ||
140+
// currentText.isEmpty ||
141+
// translationId == null ||
142+
// completedITSteps.last.chosenContinuance?.indexSavedByServer ==
143+
// null
144+
// ? _customInputTranslation(currentText)
145+
// : _systemChoiceTranslation(translationId));
146+
147+
if (res.goldContinuances != null && res.goldContinuances!.isNotEmpty) {
148+
goldRouteTracker = GoldRouteTracker(
149+
res.goldContinuances!,
150+
sourceText!,
151+
);
152+
}
153+
154+
currentITStep = CurrentITStep(
155+
sourceText: sourceText!,
156+
currentText: currentText,
157+
responseModel: res,
158+
storedGoldContinuances: goldRouteTracker.continuances,
148159
);
149-
}
150-
151-
currentITStep = CurrentITStep(
152-
sourceText: sourceText!,
153-
currentText: currentText,
154-
responseModel: res,
155-
storedGoldContinuances: goldRouteTracker.continuances,
156-
);
157160

158-
_addPayloadId(res);
161+
_addPayloadId(res);
162+
} else {
163+
currentITStep = nextITStep;
164+
nextITStep = null;
165+
}
159166

160167
if (isTranslationDone) {
161168
choreographer.altTranslator.setTranslationFeedback();
162169
choreographer.getLanguageHelp(true);
170+
} else {
171+
getNextTranslationData();
172+
}
173+
} catch (e, s) {
174+
debugger(when: kDebugMode);
175+
if (e is! http.Response) {
176+
ErrorHandler.logError(e: e, s: s);
177+
}
178+
choreographer.errorService.setErrorAndLock(
179+
ChoreoError(type: ChoreoErrorType.unknown, raw: e),
180+
);
181+
} finally {
182+
choreographer.stopLoading();
183+
}
184+
}
185+
186+
Future<void>getNextTranslationData() async {
187+
try {
188+
if (completedITSteps.length < goldRouteTracker.continuances.length) {
189+
final String currentText = choreographer.currentText;
190+
final String nextText =
191+
goldRouteTracker.continuances[completedITSteps.length].text;
192+
193+
final ITResponseModel res =
194+
await _customInputTranslation(currentText + nextText);
195+
196+
nextITStep = CurrentITStep(
197+
sourceText: sourceText!,
198+
currentText: nextText,
199+
responseModel: res,
200+
storedGoldContinuances: goldRouteTracker.continuances,
201+
);
202+
} else {
203+
nextITStep = null;
163204
}
164205
} catch (e, s) {
165206
debugger(when: kDebugMode);

lib/pangea/choreographer/widgets/choice_array.dart

+152-36
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import 'dart:developer';
2+
import 'dart:math';
23

34
import 'package:flutter/foundation.dart';
45
import 'package:flutter/material.dart';
56

67
import 'package:flutter_gen/gen_l10n/l10n.dart';
8+
import 'package:matrix/matrix.dart';
79

810
import '../../utils/bot_style.dart';
911
import 'it_shimmer.dart';
@@ -56,10 +58,12 @@ class Choice {
5658
Choice({
5759
this.color,
5860
required this.text,
61+
this.isGold = false,
5962
});
6063

6164
final Color? color;
6265
final String text;
66+
final bool isGold;
6367
}
6468

6569
class ChoiceItem extends StatelessWidget {
@@ -86,45 +90,50 @@ class ChoiceItem extends StatelessWidget {
8690
waitDuration: onLongPress != null
8791
? const Duration(milliseconds: 500)
8892
: const Duration(days: 1),
89-
child: Container(
90-
margin: const EdgeInsets.all(2),
91-
padding: EdgeInsets.zero,
92-
decoration: isSelected
93-
? BoxDecoration(
94-
borderRadius: const BorderRadius.all(Radius.circular(10)),
95-
border: Border.all(
96-
color: entry.value.color ?? theme.colorScheme.primary,
97-
style: BorderStyle.solid,
98-
width: 2.0,
93+
child: ChoiceAnimationWidget(
94+
key: ValueKey(entry.value.text),
95+
selected: entry.value.color != null,
96+
isGold: entry.value.isGold,
97+
child: Container(
98+
margin: const EdgeInsets.all(2),
99+
padding: EdgeInsets.zero,
100+
decoration: isSelected
101+
? BoxDecoration(
102+
borderRadius: const BorderRadius.all(Radius.circular(10)),
103+
border: Border.all(
104+
color: entry.value.color ?? theme.colorScheme.primary,
105+
style: BorderStyle.solid,
106+
width: 2.0,
107+
),
108+
)
109+
: null,
110+
child: TextButton(
111+
style: ButtonStyle(
112+
padding: MaterialStateProperty.all(
113+
const EdgeInsets.symmetric(horizontal: 7),
114+
),
115+
//if index is selected, then give the background a slight primary color
116+
backgroundColor: MaterialStateProperty.all<Color>(
117+
entry.value.color != null
118+
? entry.value.color!.withOpacity(0.2)
119+
: theme.colorScheme.primary.withOpacity(0.1),
120+
),
121+
textStyle: MaterialStateProperty.all(
122+
BotStyle.text(context),
123+
),
124+
shape: MaterialStateProperty.all(
125+
RoundedRectangleBorder(
126+
borderRadius: BorderRadius.circular(10),
99127
),
100-
)
101-
: null,
102-
child: TextButton(
103-
style: ButtonStyle(
104-
padding: MaterialStateProperty.all(
105-
const EdgeInsets.symmetric(horizontal: 7),
106-
),
107-
//if index is selected, then give the background a slight primary color
108-
backgroundColor: MaterialStateProperty.all<Color>(
109-
entry.value.color != null
110-
? entry.value.color!.withOpacity(0.2)
111-
: theme.colorScheme.primary.withOpacity(0.1),
112-
),
113-
textStyle: MaterialStateProperty.all(
114-
BotStyle.text(context),
115-
),
116-
shape: MaterialStateProperty.all(
117-
RoundedRectangleBorder(
118-
borderRadius: BorderRadius.circular(10),
119128
),
120129
),
121-
),
122-
onLongPress:
123-
onLongPress != null ? () => onLongPress!(entry.key) : null,
124-
onPressed: () => onPressed(entry.key),
125-
child: Text(
126-
entry.value.text,
127-
style: BotStyle.text(context),
130+
onLongPress:
131+
onLongPress != null ? () => onLongPress!(entry.key) : null,
132+
onPressed: () => onPressed(entry.key),
133+
child: Text(
134+
entry.value.text,
135+
style: BotStyle.text(context),
136+
),
128137
),
129138
),
130139
),
@@ -135,3 +144,110 @@ class ChoiceItem extends StatelessWidget {
135144
}
136145
}
137146
}
147+
148+
class ChoiceAnimationWidget extends StatefulWidget {
149+
final Widget child;
150+
final bool selected;
151+
final bool isGold;
152+
153+
const ChoiceAnimationWidget({
154+
super.key,
155+
required this.child,
156+
required this.selected,
157+
this.isGold = false,
158+
});
159+
160+
@override
161+
ChoiceAnimationWidgetState createState() => ChoiceAnimationWidgetState();
162+
}
163+
164+
class ChoiceAnimationWidgetState extends State<ChoiceAnimationWidget>
165+
with SingleTickerProviderStateMixin {
166+
late final AnimationController _controller;
167+
late final Animation<double> _animation;
168+
bool animationPlayed = false;
169+
170+
@override
171+
void initState() {
172+
super.initState();
173+
174+
_controller = AnimationController(
175+
duration: const Duration(milliseconds: 300),
176+
vsync: this,
177+
);
178+
179+
_animation = widget.isGold
180+
? Tween<double>(begin: 1.0, end: 1.2).animate(_controller)
181+
: TweenSequence<double>([
182+
TweenSequenceItem<double>(
183+
tween: Tween<double>(begin: 0, end: -8 * pi / 180),
184+
weight: 1.0,
185+
),
186+
TweenSequenceItem<double>(
187+
tween: Tween<double>(begin: -8 * pi / 180, end: 16 * pi / 180),
188+
weight: 2.0,
189+
),
190+
TweenSequenceItem<double>(
191+
tween: Tween<double>(begin: 16 * pi / 180, end: 0),
192+
weight: 1.0,
193+
),
194+
]).animate(_controller);
195+
196+
if (widget.selected && !animationPlayed) {
197+
_controller.forward();
198+
animationPlayed = true;
199+
setState(() {});
200+
}
201+
_controller.addStatusListener((status) {
202+
if (status == AnimationStatus.completed) {
203+
_controller.reverse();
204+
} else if (status == AnimationStatus.dismissed) {
205+
_controller.stop();
206+
_controller.reset();
207+
}
208+
});
209+
}
210+
211+
@override
212+
void didUpdateWidget(ChoiceAnimationWidget oldWidget) {
213+
super.didUpdateWidget(oldWidget);
214+
if (widget.selected && !animationPlayed) {
215+
_controller.forward();
216+
animationPlayed = true;
217+
setState(() {});
218+
}
219+
}
220+
221+
@override
222+
Widget build(BuildContext context) {
223+
return widget.isGold
224+
? AnimatedBuilder(
225+
key: UniqueKey(),
226+
animation: _animation,
227+
builder: (context, child) {
228+
return Transform.scale(
229+
scale: _animation.value,
230+
child: child,
231+
);
232+
},
233+
child: widget.child,
234+
)
235+
: AnimatedBuilder(
236+
key: UniqueKey(),
237+
animation: _animation,
238+
builder: (context, child) {
239+
return Transform.rotate(
240+
angle: _animation.value,
241+
child: child,
242+
);
243+
},
244+
child: widget.child,
245+
);
246+
}
247+
248+
@override
249+
void dispose() {
250+
_controller.dispose();
251+
super.dispose();
252+
}
253+
}

lib/pangea/choreographer/widgets/it_bar.dart

+5-1
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,11 @@ class ITChoices extends StatelessWidget {
280280
originalSpan: "dummy",
281281
choices: controller.currentITStep!.continuances.map((e) {
282282
try {
283-
return Choice(text: e.text.trim(), color: e.color);
283+
return Choice(
284+
text: e.text.trim(),
285+
color: e.color,
286+
isGold: e.description == "best",
287+
);
284288
} catch (e) {
285289
debugger(when: kDebugMode);
286290
return Choice(text: "error", color: Colors.red);

lib/pangea/widgets/igc/span_card.dart

+1
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ class WordMatchContent extends StatelessWidget {
198198
(e) => Choice(
199199
text: e.value,
200200
color: e.selected ? e.type.color : null,
201+
isGold: e.type.name == 'bestCorrection',
201202
),
202203
)
203204
.toList(),

0 commit comments

Comments
 (0)