some tweaks
This commit is contained in:
parent
98c373f299
commit
4b9fd02baf
7 changed files with 356 additions and 328 deletions
|
|
@ -837,35 +837,35 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
PangeaTextController get sendController => choreographer.textController;
|
||||
// Pangea#
|
||||
|
||||
// #Pangea
|
||||
// void setSendingClient(Client c) {
|
||||
// // first cancel typing with the old sending client
|
||||
// if (currentlyTyping) {
|
||||
// // no need to have the setting typing to false be blocking
|
||||
// typingCoolDown?.cancel();
|
||||
// typingCoolDown = null;
|
||||
// room.setTyping(false);
|
||||
// currentlyTyping = false;
|
||||
// }
|
||||
// // then cancel the old timeline
|
||||
// // fixes bug with read reciepts and quick switching
|
||||
// loadTimelineFuture = _getTimeline(eventContextId: room.fullyRead).onError(
|
||||
// ErrorReporter(
|
||||
// context,
|
||||
// 'Unable to load timeline after changing sending Client',
|
||||
// ).onErrorCallback,
|
||||
// );
|
||||
void setSendingClient(Client c) {
|
||||
// #Pangea
|
||||
// // first cancel typing with the old sending client
|
||||
// if (currentlyTyping) {
|
||||
// // no need to have the setting typing to false be blocking
|
||||
// typingCoolDown?.cancel();
|
||||
// typingCoolDown = null;
|
||||
// room.setTyping(false);
|
||||
// currentlyTyping = false;
|
||||
// }
|
||||
// // then cancel the old timeline
|
||||
// // fixes bug with read reciepts and quick switching
|
||||
// loadTimelineFuture = _getTimeline(eventContextId: room.fullyRead).onError(
|
||||
// ErrorReporter(
|
||||
// context,
|
||||
// 'Unable to load timeline after changing sending Client',
|
||||
// ).onErrorCallback,
|
||||
// );
|
||||
|
||||
// // then set the new sending client
|
||||
// setState(() => sendingClient = c);
|
||||
// }
|
||||
// // then set the new sending client
|
||||
// setState(() => sendingClient = c);
|
||||
// }
|
||||
|
||||
// void setActiveClient(Client c) {
|
||||
// setState(() {
|
||||
// Matrix.of(context).setActiveClient(c);
|
||||
// });
|
||||
// }
|
||||
// Pangea#
|
||||
// void setActiveClient(Client c) {
|
||||
// setState(() {
|
||||
// Matrix.of(context).setActiveClient(c);
|
||||
// });
|
||||
// Pangea#
|
||||
}
|
||||
|
||||
// #Pangea
|
||||
Event? pangeaEditingEvent;
|
||||
|
|
@ -2188,6 +2188,23 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
l1 != activityLang;
|
||||
}
|
||||
|
||||
void showNextMatch() {
|
||||
final match = choreographer.igcController.firstOpenMatch;
|
||||
if (match == null) {
|
||||
inputFocus.requestFocus();
|
||||
return;
|
||||
}
|
||||
|
||||
match.updatedMatch.isITStart
|
||||
? choreographer.openIT(match)
|
||||
: OverlayUtil.showIGCMatch(
|
||||
match,
|
||||
choreographer,
|
||||
context,
|
||||
showNextMatch,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> onRequestWritingAssistance({
|
||||
bool manual = false,
|
||||
bool autosend = false,
|
||||
|
|
@ -2198,14 +2215,7 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
|
||||
await choreographer.requestWritingAssistance(manual: manual);
|
||||
if (choreographer.assistanceState == AssistanceStateEnum.fetched) {
|
||||
final match = choreographer.igcController.firstOpenMatch!;
|
||||
match.updatedMatch.isITStart
|
||||
? choreographer.openIT(match)
|
||||
: OverlayUtil.showIGCMatch(
|
||||
match,
|
||||
choreographer,
|
||||
context,
|
||||
);
|
||||
showNextMatch();
|
||||
} else if (autosend) {
|
||||
await send();
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import 'package:fluffychat/config/app_config.dart';
|
|||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/utils/other_party_can_receive.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../../config/themes.dart';
|
||||
import 'chat.dart';
|
||||
import 'input_bar.dart';
|
||||
|
|
@ -254,17 +256,15 @@ class ChatInputRow extends StatelessWidget {
|
|||
onPressed: controller.emojiPickerAction,
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
// if (Matrix.of(context).isMultiAccount &&
|
||||
// Matrix.of(context).hasComplexBundles &&
|
||||
// Matrix.of(context).currentBundle!.length > 1)
|
||||
// Container(
|
||||
// width: height,
|
||||
// height: height,
|
||||
// alignment: Alignment.center,
|
||||
// child: _ChatAccountPicker(controller),
|
||||
// ),
|
||||
// Pangea#
|
||||
if (Matrix.of(context).isMultiAccount &&
|
||||
Matrix.of(context).hasComplexBundles &&
|
||||
Matrix.of(context).currentBundle!.length > 1)
|
||||
Container(
|
||||
width: height,
|
||||
height: height,
|
||||
alignment: Alignment.center,
|
||||
child: _ChatAccountPicker(controller),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 0.0),
|
||||
|
|
@ -301,6 +301,7 @@ class ChatInputRow extends StatelessWidget {
|
|||
onChanged: controller.onInputBarChanged,
|
||||
// #Pangea
|
||||
choreographer: controller.choreographer,
|
||||
showNextMatch: controller.showNextMatch,
|
||||
// Pangea#
|
||||
),
|
||||
),
|
||||
|
|
@ -341,62 +342,60 @@ class ChatInputRow extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
// #Pangea
|
||||
// class _ChatAccountPicker extends StatelessWidget {
|
||||
// final ChatController controller;
|
||||
class _ChatAccountPicker extends StatelessWidget {
|
||||
final ChatController controller;
|
||||
|
||||
// const _ChatAccountPicker(this.controller);
|
||||
const _ChatAccountPicker(this.controller);
|
||||
|
||||
// void _popupMenuButtonSelected(String mxid, BuildContext context) {
|
||||
// final client = Matrix.of(context)
|
||||
// .currentBundle!
|
||||
// .firstWhere((cl) => cl!.userID == mxid, orElse: () => null);
|
||||
// if (client == null) {
|
||||
// Logs().w('Attempted to switch to a non-existing client $mxid');
|
||||
// return;
|
||||
// }
|
||||
// controller.setSendingClient(client);
|
||||
// }
|
||||
void _popupMenuButtonSelected(String mxid, BuildContext context) {
|
||||
final client = Matrix.of(context)
|
||||
.currentBundle!
|
||||
.firstWhere((cl) => cl!.userID == mxid, orElse: () => null);
|
||||
if (client == null) {
|
||||
Logs().w('Attempted to switch to a non-existing client $mxid');
|
||||
return;
|
||||
}
|
||||
controller.setSendingClient(client);
|
||||
}
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// final clients = controller.currentRoomBundle;
|
||||
// return Padding(
|
||||
// padding: const EdgeInsets.all(8.0),
|
||||
// child: FutureBuilder<Profile>(
|
||||
// future: controller.sendingClient.fetchOwnProfile(),
|
||||
// builder: (context, snapshot) => PopupMenuButton<String>(
|
||||
// useRootNavigator: true,
|
||||
// onSelected: (mxid) => _popupMenuButtonSelected(mxid, context),
|
||||
// itemBuilder: (BuildContext context) => clients
|
||||
// .map(
|
||||
// (client) => PopupMenuItem<String>(
|
||||
// value: client!.userID,
|
||||
// child: FutureBuilder<Profile>(
|
||||
// future: client.fetchOwnProfile(),
|
||||
// builder: (context, snapshot) => ListTile(
|
||||
// leading: Avatar(
|
||||
// mxContent: snapshot.data?.avatarUrl,
|
||||
// name: snapshot.data?.displayName ??
|
||||
// client.userID!.localpart,
|
||||
// size: 20,
|
||||
// ),
|
||||
// title: Text(snapshot.data?.displayName ?? client.userID!),
|
||||
// contentPadding: const EdgeInsets.all(0),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// .toList(),
|
||||
// child: Avatar(
|
||||
// mxContent: snapshot.data?.avatarUrl,
|
||||
// name: snapshot.data?.displayName ??
|
||||
// Matrix.of(context).client.userID!.localpart,
|
||||
// size: 20,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// Pangea#
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final clients = controller.currentRoomBundle;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: FutureBuilder<Profile>(
|
||||
future: controller.sendingClient.fetchOwnProfile(),
|
||||
builder: (context, snapshot) => PopupMenuButton<String>(
|
||||
useRootNavigator: true,
|
||||
onSelected: (mxid) => _popupMenuButtonSelected(mxid, context),
|
||||
itemBuilder: (BuildContext context) => clients
|
||||
.map(
|
||||
(client) => PopupMenuItem<String>(
|
||||
value: client!.userID,
|
||||
child: FutureBuilder<Profile>(
|
||||
future: client.fetchOwnProfile(),
|
||||
builder: (context, snapshot) => ListTile(
|
||||
leading: Avatar(
|
||||
mxContent: snapshot.data?.avatarUrl,
|
||||
name: snapshot.data?.displayName ??
|
||||
client.userID!.localpart,
|
||||
size: 20,
|
||||
),
|
||||
title: Text(snapshot.data?.displayName ?? client.userID!),
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
child: Avatar(
|
||||
mxContent: snapshot.data?.avatarUrl,
|
||||
name: snapshot.data?.displayName ??
|
||||
Matrix.of(context).client.userID!.localpart,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import 'package:fluffychat/pangea/choreographer/choreographer.dart';
|
|||
import 'package:fluffychat/pangea/choreographer/choreographer_state_extension.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/text_editing/pangea_text_controller.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/overlay.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart';
|
||||
import 'package:fluffychat/pangea/subscription/controllers/subscription_controller.dart';
|
||||
import 'package:fluffychat/pangea/subscription/widgets/paywall_card.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/utils/shrinkable_text.dart';
|
||||
|
|
@ -36,6 +35,7 @@ class InputBar extends StatelessWidget {
|
|||
// final TextEditingController? controller;
|
||||
final PangeaTextController? controller;
|
||||
final Choreographer choreographer;
|
||||
final VoidCallback showNextMatch;
|
||||
// Pangea#
|
||||
final InputDecoration? decoration;
|
||||
final ValueChanged<String>? onChanged;
|
||||
|
|
@ -58,6 +58,7 @@ class InputBar extends StatelessWidget {
|
|||
this.readOnly = false,
|
||||
// #Pangea
|
||||
required this.choreographer,
|
||||
required this.showNextMatch,
|
||||
// Pangea#
|
||||
super.key,
|
||||
});
|
||||
|
|
@ -429,58 +430,61 @@ class InputBar extends StatelessWidget {
|
|||
}
|
||||
|
||||
// #Pangea
|
||||
String hintText(BuildContext context) {
|
||||
if (choreographer.itController.open.value) {
|
||||
return L10n.of(context).buildTranslation;
|
||||
}
|
||||
SubscriptionStatus get _subscriptionStatus =>
|
||||
MatrixState.pangeaController.subscriptionController.subscriptionStatus;
|
||||
|
||||
return MatrixState.pangeaController.languageController.userL1 != null &&
|
||||
MatrixState.pangeaController.languageController.userL2 != null &&
|
||||
MatrixState.pangeaController.languageController.userL1!.langCode !=
|
||||
LanguageKeys.unknownLanguage &&
|
||||
MatrixState.pangeaController.languageController.userL2!.langCode !=
|
||||
LanguageKeys.unknownLanguage
|
||||
String _defaultHintText(BuildContext context) {
|
||||
final lang = MatrixState.pangeaController.languageController;
|
||||
return lang.languagesSet
|
||||
? L10n.of(context).writeAMessageLangCodes(
|
||||
MatrixState.pangeaController.languageController.userL1!.displayName,
|
||||
MatrixState.pangeaController.languageController.userL2!.displayName,
|
||||
lang.userL1!.displayName,
|
||||
lang.userL2!.displayName,
|
||||
)
|
||||
: L10n.of(context).writeAMessage;
|
||||
}
|
||||
|
||||
void onInputTap(BuildContext context) {
|
||||
if (controller == null || controller!.text.isEmpty) return;
|
||||
final choreographer = controller!.choreographer;
|
||||
void _onInputTap(BuildContext context) {
|
||||
if (_shouldShowPaywall(context)) return;
|
||||
|
||||
final subscriptionStatus =
|
||||
MatrixState.pangeaController.subscriptionController.subscriptionStatus;
|
||||
if (subscriptionStatus == SubscriptionStatus.shouldShowPaywall) {
|
||||
PaywallCard.show(context, ChoreoConstants.inputTransformTargetKey);
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalization matchs are widget spans that mess with offsets,
|
||||
// so we need to adjust the offset accordingly
|
||||
int adjustedOffset = controller!.selection.baseOffset;
|
||||
final normalizationMatches =
|
||||
choreographer.igcController.recentAutomaticCorrections;
|
||||
if (normalizationMatches != null) {
|
||||
for (final match in normalizationMatches) {
|
||||
if (match.updatedMatch.match.offset < adjustedOffset &&
|
||||
match.updatedMatch.match.length > 0) {
|
||||
adjustedOffset += (match.updatedMatch.match.length - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
final baseOffset = controller?.selection.baseOffset;
|
||||
if (baseOffset == null) return;
|
||||
|
||||
final adjustedOffset = _adjustOffsetForNormalization(baseOffset);
|
||||
final match = choreographer.igcController.getMatchByOffset(adjustedOffset);
|
||||
if (match == null) return;
|
||||
match.updatedMatch.isITStart
|
||||
? choreographer.openIT(match)
|
||||
: OverlayUtil.showIGCMatch(
|
||||
match,
|
||||
choreographer,
|
||||
context,
|
||||
);
|
||||
|
||||
if (match.updatedMatch.isITStart) {
|
||||
choreographer.openIT(match);
|
||||
} else {
|
||||
OverlayUtil.showIGCMatch(
|
||||
match,
|
||||
choreographer,
|
||||
context,
|
||||
showNextMatch,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
bool _shouldShowPaywall(BuildContext context) {
|
||||
if (_subscriptionStatus == SubscriptionStatus.shouldShowPaywall) {
|
||||
PaywallCard.show(context, ChoreoConstants.inputTransformTargetKey);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int _adjustOffsetForNormalization(int baseOffset) {
|
||||
int adjustedOffset = baseOffset;
|
||||
final corrections = choreographer.igcController.recentAutomaticCorrections;
|
||||
if (corrections == null) return adjustedOffset;
|
||||
|
||||
for (final correction in corrections) {
|
||||
final match = correction.updatedMatch.match;
|
||||
if (match.offset < adjustedOffset && match.length > 0) {
|
||||
adjustedOffset += (match.length - 1);
|
||||
}
|
||||
}
|
||||
return adjustedOffset;
|
||||
}
|
||||
// Pangea#
|
||||
|
||||
|
|
@ -492,7 +496,6 @@ class InputBar extends StatelessWidget {
|
|||
builder: (context, _, __) {
|
||||
final enableAutocorrect = MatrixState.pangeaController.userController
|
||||
.profile.toolSettings.enableAutocorrect;
|
||||
// Pangea#
|
||||
return TypeAheadField<Map<String, String?>>(
|
||||
direction: VerticalDirection.up,
|
||||
hideOnEmpty: true,
|
||||
|
|
@ -501,8 +504,6 @@ class InputBar extends StatelessWidget {
|
|||
focusNode: focusNode,
|
||||
hideOnSelect: false,
|
||||
debounceDuration: const Duration(milliseconds: 50),
|
||||
// show suggestions after 50ms idle time (default is 300)
|
||||
// #Pangea
|
||||
builder: (context, _, focusNode) {
|
||||
final textField = TextField(
|
||||
enableSuggestions: enableAutocorrect,
|
||||
|
|
@ -550,7 +551,7 @@ class InputBar extends StatelessWidget {
|
|||
style: controller?.exceededMaxLength ?? false
|
||||
? const TextStyle(color: Colors.red)
|
||||
: null,
|
||||
onTap: () => onInputTap(context),
|
||||
onTap: () => _onInputTap(context),
|
||||
decoration: decoration!,
|
||||
onChanged: (text) {
|
||||
// fix for the library for now
|
||||
|
|
@ -567,7 +568,9 @@ class InputBar extends StatelessWidget {
|
|||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: ShrinkableText(
|
||||
text: hintText(context),
|
||||
text: controller!.choreographer.itController.open.value
|
||||
? L10n.of(context).buildTranslation
|
||||
: _defaultHintText(context),
|
||||
maxWidth: double.infinity,
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: Theme.of(context).disabledColor,
|
||||
|
|
@ -578,49 +581,6 @@ class InputBar extends StatelessWidget {
|
|||
],
|
||||
);
|
||||
},
|
||||
// builder: (context, controller, focusNode) => TextField(
|
||||
// controller: controller,
|
||||
// focusNode: focusNode,
|
||||
// readOnly: readOnly,
|
||||
// contextMenuBuilder: (c, e) => markdownContextBuilder(c, e, controller),
|
||||
// contentInsertionConfiguration: ContentInsertionConfiguration(
|
||||
// onContentInserted: (KeyboardInsertedContent content) {
|
||||
// final data = content.data;
|
||||
// if (data == null) return;
|
||||
|
||||
// final file = MatrixFile(
|
||||
// mimeType: content.mimeType,
|
||||
// bytes: data,
|
||||
// name: content.uri.split('/').last,
|
||||
// );
|
||||
// room.sendFileEvent(
|
||||
// file,
|
||||
// shrinkImageMaxDimension: 1600,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// minLines: minLines,
|
||||
// maxLines: maxLines,
|
||||
// keyboardType: keyboardType!,
|
||||
// textInputAction: textInputAction,
|
||||
// autofocus: autofocus!,
|
||||
// inputFormatters: [
|
||||
// LengthLimitingTextInputFormatter((maxPDUSize / 3).floor()),
|
||||
// ],
|
||||
// onSubmitted: (text) {
|
||||
// // fix for library for now
|
||||
// // it sets the types for the callback incorrectly
|
||||
// onSubmitted!(text);
|
||||
// },
|
||||
// decoration: decoration!,
|
||||
// onChanged: (text) {
|
||||
// // fix for the library for now
|
||||
// // it sets the types for the callback incorrectly
|
||||
// onChanged!(text);
|
||||
// },
|
||||
// textCapitalization: TextCapitalization.sentences,
|
||||
// ),
|
||||
// Pangea#
|
||||
suggestionsCallback: getSuggestions,
|
||||
itemBuilder: (c, s) =>
|
||||
buildSuggestion(c, s, Matrix.of(context).client),
|
||||
|
|
@ -635,5 +595,71 @@ class InputBar extends StatelessWidget {
|
|||
);
|
||||
},
|
||||
);
|
||||
// return TypeAheadField<Map<String, String?>>(
|
||||
// direction: VerticalDirection.up,
|
||||
// hideOnEmpty: true,
|
||||
// hideOnLoading: true,
|
||||
// controller: controller,
|
||||
// focusNode: focusNode,
|
||||
// hideOnSelect: false,
|
||||
// debounceDuration: const Duration(milliseconds: 50),
|
||||
// // show suggestions after 50ms idle time (default is 300)
|
||||
// builder: (context, controller, focusNode) => TextField(
|
||||
// controller: controller,
|
||||
// focusNode: focusNode,
|
||||
// readOnly: readOnly,
|
||||
// contextMenuBuilder: (c, e) => markdownContextBuilder(c, e, controller),
|
||||
// contentInsertionConfiguration: ContentInsertionConfiguration(
|
||||
// onContentInserted: (KeyboardInsertedContent content) {
|
||||
// final data = content.data;
|
||||
// if (data == null) return;
|
||||
|
||||
// final file = MatrixFile(
|
||||
// mimeType: content.mimeType,
|
||||
// bytes: data,
|
||||
// name: content.uri.split('/').last,
|
||||
// );
|
||||
// room.sendFileEvent(
|
||||
// file,
|
||||
// shrinkImageMaxDimension: 1600,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// minLines: minLines,
|
||||
// maxLines: maxLines,
|
||||
// keyboardType: keyboardType!,
|
||||
// textInputAction: textInputAction,
|
||||
// autofocus: autofocus!,
|
||||
// inputFormatters: [
|
||||
// LengthLimitingTextInputFormatter((maxPDUSize / 3).floor()),
|
||||
// ],
|
||||
// onSubmitted: (text) {
|
||||
// // fix for library for now
|
||||
// // it sets the types for the callback incorrectly
|
||||
// onSubmitted!(text);
|
||||
// },
|
||||
// maxLength:
|
||||
// AppSettings.textMessageMaxLength.getItem(Matrix.of(context).store),
|
||||
// decoration: decoration,
|
||||
// onChanged: (text) {
|
||||
// // fix for the library for now
|
||||
// // it sets the types for the callback incorrectly
|
||||
// onChanged!(text);
|
||||
// },
|
||||
// textCapitalization: TextCapitalization.sentences,
|
||||
// ),
|
||||
|
||||
// suggestionsCallback: getSuggestions,
|
||||
// itemBuilder: (c, s) => buildSuggestion(c, s, Matrix.of(context).client),
|
||||
// onSelected: (Map<String, String?> suggestion) =>
|
||||
// insertSuggestion(context, suggestion),
|
||||
// errorBuilder: (BuildContext context, Object? error) =>
|
||||
// const SizedBox.shrink(),
|
||||
// loadingBuilder: (BuildContext context) => const SizedBox.shrink(),
|
||||
// // fix loading briefly flickering a dark box
|
||||
// emptyBuilder: (BuildContext context) =>
|
||||
// const SizedBox.shrink(), // fix loading briefly showing no suggestions
|
||||
// );
|
||||
// Pangea#
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -205,6 +205,7 @@ class PangeaChatInputRow extends StatelessWidget {
|
|||
),
|
||||
onChanged: controller.onInputBarChanged,
|
||||
choreographer: controller.choreographer,
|
||||
showNextMatch: controller.showNextMatch,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -8,23 +8,22 @@ import 'package:fluffychat/pangea/choreographer/choreographer.dart';
|
|||
import 'package:fluffychat/pangea/choreographer/igc/pangea_match_state_model.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/igc/span_choice_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/igc/span_data_model.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/igc/span_data_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/feedback_model.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/overlay.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
import '../../../widgets/matrix.dart';
|
||||
import '../../common/widgets/choice_array.dart';
|
||||
import 'why_button.dart';
|
||||
|
||||
class SpanCard extends StatefulWidget {
|
||||
final PangeaMatchState match;
|
||||
final Choreographer choreographer;
|
||||
final VoidCallback showNextMatch;
|
||||
|
||||
const SpanCard({
|
||||
super.key,
|
||||
required this.match,
|
||||
required this.choreographer,
|
||||
required this.showNextMatch,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -125,6 +124,7 @@ class SpanCardState extends State<SpanCard> {
|
|||
void _onMatchUpdate(VoidCallback updateFunc) async {
|
||||
try {
|
||||
updateFunc();
|
||||
widget.showNextMatch();
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
|
|
@ -137,7 +137,6 @@ class SpanCardState extends State<SpanCard> {
|
|||
widget.choreographer.clearMatches(e);
|
||||
return;
|
||||
}
|
||||
_showFirstMatch();
|
||||
}
|
||||
|
||||
void _onAcceptReplacement() => _onMatchUpdate(() {
|
||||
|
|
@ -148,19 +147,6 @@ class SpanCardState extends State<SpanCard> {
|
|||
widget.choreographer.onIgnoreReplacement(match: widget.match);
|
||||
});
|
||||
|
||||
void _showFirstMatch() {
|
||||
final match = widget.choreographer.igcController.firstOpenMatch;
|
||||
if (match == null) {
|
||||
MatrixState.pAnyState.closeAllOverlays();
|
||||
return;
|
||||
}
|
||||
OverlayUtil.showIGCMatch(
|
||||
match,
|
||||
widget.choreographer,
|
||||
context,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
|
|
@ -196,54 +182,10 @@ class SpanCardState extends State<SpanCard> {
|
|||
.pangeaController.languageController
|
||||
.activeL2Code(),
|
||||
),
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
minHeight: 100.0,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ListenableBuilder(
|
||||
listenable: _feedbackModel,
|
||||
builder: (context, _) {
|
||||
if (_loadingChoices) {
|
||||
return const SizedBox(
|
||||
width: 24.0,
|
||||
height: 24.0,
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
|
||||
final state = _feedbackModel.state;
|
||||
return switch (state) {
|
||||
FeedbackIdle<String>() =>
|
||||
_selectedChoice == null
|
||||
? Text(
|
||||
widget.match.updatedMatch.match.type
|
||||
.typeName
|
||||
.defaultPrompt(context),
|
||||
style:
|
||||
BotStyle.text(context).copyWith(
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
)
|
||||
: WhyButton(
|
||||
onPress: _fetchFeedback,
|
||||
loading: false,
|
||||
),
|
||||
FeedbackLoading<String>() => WhyButton(
|
||||
onPress: _fetchFeedback,
|
||||
loading: true,
|
||||
),
|
||||
FeedbackError<String>(:final error) =>
|
||||
ErrorIndicator(message: error.toString()),
|
||||
FeedbackLoaded<String>(:final value) =>
|
||||
Text(value, style: BotStyle.text(context)),
|
||||
};
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
_SpanCardFeedback(
|
||||
_selectedChoice != null,
|
||||
_fetchFeedback,
|
||||
_feedbackModel,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -251,52 +193,126 @@ class SpanCardState extends State<SpanCard> {
|
|||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
_SpanCardButtons(
|
||||
onAccept: _onAcceptReplacement,
|
||||
onIgnore: _onIgnoreMatch,
|
||||
selectedChoice: _selectedChoice,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SpanCardFeedback extends StatelessWidget {
|
||||
final bool hasSelectedChoice;
|
||||
final VoidCallback fetchFeedback;
|
||||
final FeedbackModel<String> feedbackModel;
|
||||
|
||||
const _SpanCardFeedback(
|
||||
this.hasSelectedChoice,
|
||||
this.fetchFeedback,
|
||||
this.feedbackModel,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
minHeight: 100.0,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ListenableBuilder(
|
||||
listenable: feedbackModel,
|
||||
builder: (context, _) {
|
||||
final state = feedbackModel.state;
|
||||
return switch (state) {
|
||||
FeedbackIdle<String>() => hasSelectedChoice
|
||||
? IconButton(
|
||||
onPressed: fetchFeedback,
|
||||
icon: const Icon(Icons.lightbulb_outline, size: 24),
|
||||
)
|
||||
: Text(
|
||||
L10n.of(context).correctionDefaultPrompt,
|
||||
style: BotStyle.text(context).copyWith(
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
FeedbackLoading<String>() => const SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
FeedbackError<String>(:final error) =>
|
||||
ErrorIndicator(message: error.toString()),
|
||||
FeedbackLoaded<String>(:final value) =>
|
||||
Text(value, style: BotStyle.text(context)),
|
||||
};
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SpanCardButtons extends StatelessWidget {
|
||||
final VoidCallback onAccept;
|
||||
final VoidCallback onIgnore;
|
||||
final SpanChoice? selectedChoice;
|
||||
|
||||
const _SpanCardButtons({
|
||||
required this.onAccept,
|
||||
required this.onIgnore,
|
||||
required this.selectedChoice,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
),
|
||||
padding: const EdgeInsets.only(top: 12.0),
|
||||
child: Row(
|
||||
spacing: 10.0,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Opacity(
|
||||
opacity: 0.8,
|
||||
child: TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.primary.withAlpha(25),
|
||||
),
|
||||
onPressed: onIgnore,
|
||||
child: Center(
|
||||
child: Text(L10n.of(context).ignoreInThisText),
|
||||
),
|
||||
),
|
||||
),
|
||||
padding: const EdgeInsets.only(top: 12.0),
|
||||
child: Row(
|
||||
spacing: 10.0,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Opacity(
|
||||
opacity: 0.8,
|
||||
child: TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.primary.withAlpha(25),
|
||||
),
|
||||
onPressed: _onIgnoreMatch,
|
||||
child: Center(
|
||||
child: Text(L10n.of(context).ignoreInThisText),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Opacity(
|
||||
opacity: selectedChoice != null ? 1.0 : 0.5,
|
||||
child: TextButton(
|
||||
onPressed: selectedChoice != null ? onAccept : null,
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: (selectedChoice?.color ??
|
||||
Theme.of(context).colorScheme.primary)
|
||||
.withAlpha(50),
|
||||
side: selectedChoice != null
|
||||
? BorderSide(
|
||||
color: selectedChoice!.color,
|
||||
style: BorderStyle.solid,
|
||||
width: 2.0,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
Expanded(
|
||||
child: Opacity(
|
||||
opacity: _selectedChoice != null ? 1.0 : 0.5,
|
||||
child: TextButton(
|
||||
onPressed:
|
||||
_selectedChoice != null ? _onAcceptReplacement : null,
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: (_selectedChoice?.color ??
|
||||
Theme.of(context).colorScheme.primary)
|
||||
.withAlpha(50),
|
||||
side: _selectedChoice != null
|
||||
? BorderSide(
|
||||
color: _selectedChoice!.color,
|
||||
style: BorderStyle.solid,
|
||||
width: 2.0,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
child: Text(L10n.of(context).replace),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
child: Text(L10n.of(context).replace),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class WhyButton extends StatelessWidget {
|
||||
const WhyButton({
|
||||
super.key,
|
||||
required this.onPress,
|
||||
required this.loading,
|
||||
});
|
||||
|
||||
final VoidCallback onPress;
|
||||
final bool loading;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IconButton(
|
||||
onPressed: loading ? null : onPress,
|
||||
icon: loading
|
||||
? const SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
: const Icon(Icons.lightbulb_outline, size: 24),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -220,6 +220,7 @@ class OverlayUtil {
|
|||
PangeaMatchState match,
|
||||
Choreographer choreographer,
|
||||
BuildContext context,
|
||||
VoidCallback showNextMatch,
|
||||
) {
|
||||
MatrixState.pAnyState.closeAllOverlays();
|
||||
showPositionedCard(
|
||||
|
|
@ -229,6 +230,7 @@ class OverlayUtil {
|
|||
cardToShow: SpanCard(
|
||||
match: match,
|
||||
choreographer: choreographer,
|
||||
showNextMatch: showNextMatch,
|
||||
),
|
||||
maxHeight: 325,
|
||||
maxWidth: 325,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue