some tweaks

This commit is contained in:
ggurdin 2025-11-11 16:31:09 -05:00
parent 98c373f299
commit 4b9fd02baf
No known key found for this signature in database
GPG key ID: A01CB41737CBB478
7 changed files with 356 additions and 328 deletions

View file

@ -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 {

View file

@ -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,
),
),
),
);
}
}

View file

@ -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#
}
}

View file

@ -205,6 +205,7 @@ class PangeaChatInputRow extends StatelessWidget {
),
onChanged: controller.onInputBarChanged,
choreographer: controller.choreographer,
showNextMatch: controller.showNextMatch,
),
),
),

View file

@ -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),
),
),
),
],

View file

@ -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),
);
}
}

View file

@ -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,