Merge branch 'main' into ls-in-detection-and-tokens

pull/1476/head
ggurdin 1 year ago committed by GitHub
commit f9e1f65dd0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -4365,5 +4365,6 @@
"chooseVoice": "Choose a voice", "chooseVoice": "Choose a voice",
"enterLanguageLevel": "Please enter a language level", "enterLanguageLevel": "Please enter a language level",
"enterDiscussionTopic": "Please enter a discussion topic", "enterDiscussionTopic": "Please enter a discussion topic",
"selectBotChatMode": "Select chat mode" "selectBotChatMode": "Select chat mode",
"messageNotInTargetLang": "Message not in target language"
} }

@ -1,5 +1,6 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/config/environment.dart';
import 'package:fluffychat/pangea/controllers/language_list_controller.dart'; import 'package:fluffychat/pangea/controllers/language_list_controller.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/utils/firebase_analytics.dart'; import 'package:fluffychat/pangea/utils/firebase_analytics.dart';
@ -100,6 +101,19 @@ Future<void> startGui(List<Client> clients, SharedPreferences store) async {
await firstClient?.accountDataLoading; await firstClient?.accountDataLoading;
ErrorWidget.builder = (details) => FluffyChatErrorWidget(details); ErrorWidget.builder = (details) => FluffyChatErrorWidget(details);
// #Pangea
// errors seems to happen a lot when users switch better production / staging
// while testing by accident. If the account is a production account but server is
// staging or vice versa, logout.
if (firstClient?.userID?.domain != null) {
final isStagingUser = firstClient!.userID!.domain!.contains("staging");
final isStagingServer = Environment.isStaging;
if (isStagingServer != isStagingUser) {
await firstClient.logout();
}
}
// Pangea#
runApp(FluffyChatApp(clients: clients, pincode: pin, store: store)); runApp(FluffyChatApp(clients: clients, pincode: pin, store: store));
} }

@ -1419,7 +1419,7 @@ class ChatController extends State<ChatPageWithRoom>
void onSelectMessage(Event event) { void onSelectMessage(Event event) {
// #Pangea // #Pangea
if (choreographer.itController.isOpen) { if (choreographer.itController.willOpen) {
return; return;
} }
// Pangea# // Pangea#

@ -321,6 +321,7 @@ class ChatInputRow extends StatelessWidget {
// #Pangea // #Pangea
// hintText: L10n.of(context)!.writeAMessage, // hintText: L10n.of(context)!.writeAMessage,
hintText: hintText(), hintText: hintText(),
disabledBorder: InputBorder.none,
// Pangea# // Pangea#
hintMaxLines: 1, hintMaxLines: 1,
border: InputBorder.none, border: InputBorder.none,

@ -478,6 +478,8 @@ class InputBar extends StatelessWidget {
// builder: (context, controller, focusNode) => TextField( // builder: (context, controller, focusNode) => TextField(
builder: (context, _, focusNode) => TextField( builder: (context, _, focusNode) => TextField(
enableSuggestions: false, enableSuggestions: false,
readOnly:
controller != null && controller!.choreographer.isRunningIT,
// Pangea# // Pangea#
controller: controller, controller: controller,
focusNode: focusNode, focusNode: focusNode,

@ -68,7 +68,7 @@ class Choreographer {
} }
void send(BuildContext context) { void send(BuildContext context) {
if (isFetching) return; if (!canSendMessage) return;
if (pangeaController.subscriptionController.subscriptionStatus == if (pangeaController.subscriptionController.subscriptionStatus ==
SubscriptionStatus.showPaywall) { SubscriptionStatus.showPaywall) {
@ -92,7 +92,7 @@ class Choreographer {
} }
Future<void> _sendWithIGC(BuildContext context) async { Future<void> _sendWithIGC(BuildContext context) async {
if (!igc.canSendMessage) { if (!canSendMessage) {
igc.showFirstMatch(context); igc.showFirstMatch(context);
return; return;
} }
@ -255,13 +255,16 @@ class Choreographer {
} }
startLoading(); startLoading();
// if getting language assistance after finishing IT,
// reset the itController
if (choreoMode == ChoreoMode.it && if (choreoMode == ChoreoMode.it &&
itController.isTranslationDone && itController.isTranslationDone &&
!onlyTokensAndLanguageDetection) { !onlyTokensAndLanguageDetection) {
// debugger(when: kDebugMode); itController.clear();
} }
await (choreoMode == ChoreoMode.it && !itController.isTranslationDone await (isRunningIT
? itController.getTranslationData(_useCustomInput) ? itController.getTranslationData(_useCustomInput)
: igc.getIGCTextData( : igc.getIGCTextData(
onlyTokensAndLanguageDetection: onlyTokensAndLanguageDetection, onlyTokensAndLanguageDetection: onlyTokensAndLanguageDetection,
@ -418,7 +421,7 @@ class Choreographer {
setState(); setState();
} }
giveInputFocus() { void giveInputFocus() {
Future.delayed(Duration.zero, () { Future.delayed(Duration.zero, () {
chatController.inputFocus.requestFocus(); chatController.inputFocus.requestFocus();
}); });
@ -478,6 +481,9 @@ class Choreographer {
bool get _noChange => bool get _noChange =>
_lastChecked != null && _lastChecked == _textController.text; _lastChecked != null && _lastChecked == _textController.text;
bool get isRunningIT =>
choreoMode == ChoreoMode.it && !itController.isTranslationDone;
void startLoading() { void startLoading() {
_lastChecked = _textController.text; _lastChecked = _textController.text;
isFetching = true; isFetching = true;
@ -505,8 +511,6 @@ class Choreographer {
} }
} }
bool get showIsError => !itController.isOpen && errorService.isError;
LayerLinkAndKey get itBarLinkAndKey => LayerLinkAndKey get itBarLinkAndKey =>
MatrixState.pAnyState.layerLinkAndKey(itBarTransformTargetKey); MatrixState.pAnyState.layerLinkAndKey(itBarTransformTargetKey);
@ -570,7 +574,7 @@ class Choreographer {
return AssistanceState.noMessage; return AssistanceState.noMessage;
} }
if (igc.igcTextData?.matches.isNotEmpty ?? false) { if ((igc.igcTextData?.matches.isNotEmpty ?? false) || isRunningIT) {
return AssistanceState.fetched; return AssistanceState.fetched;
} }
@ -584,4 +588,33 @@ class Choreographer {
return AssistanceState.complete; return AssistanceState.complete;
} }
bool get canSendMessage {
// if there's an error, let them send. we don't want to block them from sending in this case
if (errorService.isError) return true;
// if they're in IT mode, don't let them send
if (itEnabled && isRunningIT) return false;
// if they've turned off IGC then let them send the message when they want
if (!isAutoIGCEnabled) return true;
// if we're in the middle of fetching results, don't let them send
if (isFetching) return false;
// they're supposed to run IGC but haven't yet, don't let them send
if (isAutoIGCEnabled && igc.igcTextData == null) return false;
// if they have relevant matches, don't let them send
final hasITMatches =
igc.igcTextData!.matches.any((match) => match.isITStart);
final hasIGCMatches =
igc.igcTextData!.matches.any((match) => !match.isITStart);
if ((itEnabled && hasITMatches) || (igcEnabled && hasIGCMatches)) {
return false;
}
// otherwise, let them send
return true;
}
} }

@ -192,18 +192,4 @@ class IgcController {
// Not sure why this is here // Not sure why this is here
// MatrixState.pAnyState.closeOverlay(); // MatrixState.pAnyState.closeOverlay();
} }
bool get canSendMessage {
if (choreographer.isFetching) return false;
if (igcTextData == null ||
choreographer.errorService.isError ||
igcTextData!.matches.isEmpty) {
return true;
}
return !((choreographer.itEnabled &&
igcTextData!.matches.any((match) => match.isOutOfTargetMatch)) ||
(choreographer.igcEnabled &&
igcTextData!.matches.any((match) => !match.isOutOfTargetMatch)));
}
} }

@ -68,9 +68,10 @@ class ITController {
} }
void closeIT() { void closeIT() {
//if they close it before completing, just put their text back // if the user hasn't gone through any IT steps, reset the text
//PTODO - explore using last itStep if (completedITSteps.isEmpty && sourceText != null) {
choreographer.textController.text = sourceText ?? ""; choreographer.textController.text = sourceText!;
}
clear(); clear();
} }

@ -1,15 +1,15 @@
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../controllers/pangea_controller.dart';
import '../controllers/error_service.dart'; import '../controllers/error_service.dart';
class ChoreographerHasErrorButton extends StatelessWidget { class ChoreographerHasErrorButton extends StatelessWidget {
final ChoreoError error; final ChoreoError error;
final PangeaController pangeaController; final Choreographer choreographer;
const ChoreographerHasErrorButton( const ChoreographerHasErrorButton(
this.pangeaController, this.error,
this.error, { this.choreographer, {
super.key, super.key,
}); });
@ -26,6 +26,7 @@ class ChoreographerHasErrorButton extends StatelessWidget {
), ),
), ),
); );
choreographer.errorService.resetError();
} }
}, },
mini: true, mini: true,

@ -103,7 +103,7 @@ class ITFeedbackCardController extends State<ITFeedbackCard> {
@override @override
Widget build(BuildContext context) => error == null Widget build(BuildContext context) => error == null
? ITFeedbackCardView(controller: this) ? ITFeedbackCardView(controller: this)
: CardErrorWidget(error: error); : CardErrorWidget(error: error!);
} }
class ITFeedbackCardView extends StatelessWidget { class ITFeedbackCardView extends StatelessWidget {

@ -56,7 +56,10 @@ class ChoreographerSendButtonState extends State<ChoreographerSendButton> {
color: widget.controller.choreographer.assistanceState color: widget.controller.choreographer.assistanceState
.stateColor(context), .stateColor(context),
onPressed: () { onPressed: () {
widget.controller.choreographer.send(context); widget.controller.choreographer.canSendMessage
? widget.controller.choreographer.send(context)
: widget.controller.choreographer.igc
.showFirstMatch(context);
}, },
tooltip: L10n.of(context)!.send, tooltip: L10n.of(context)!.send,
), ),

@ -66,10 +66,12 @@ extension MessageModeExtension on MessageMode {
} }
} }
bool isValidMode(Event event) { bool shouldShowAsToolbarButton(Event event) {
switch (this) { switch (this) {
case MessageMode.translation: case MessageMode.translation:
return event.messageType == MessageTypes.Text;
case MessageMode.textToSpeech: case MessageMode.textToSpeech:
return event.messageType == MessageTypes.Text;
case MessageMode.definition: case MessageMode.definition:
return event.messageType == MessageTypes.Text; return event.messageType == MessageTypes.Text;
case MessageMode.speechToText: case MessageMode.speechToText:

@ -10,7 +10,7 @@ extension AnalyticsRoomExtension on Room {
return; return;
} }
if (!isRoomAdmin) return; if (client.userID == null || !isRoomAdmin) return;
final spaceHierarchy = await client.getSpaceHierarchy( final spaceHierarchy = await client.getSpaceHierarchy(
id, id,
maxDepth: 1, maxDepth: 1,

@ -76,8 +76,8 @@ class ChatFloatingActionButtonState extends State<ChatFloatingActionButton> {
} }
if (widget.controller.choreographer.errorService.error != null) { if (widget.controller.choreographer.errorService.error != null) {
return ChoreographerHasErrorButton( return ChoreographerHasErrorButton(
widget.controller.pangeaController,
widget.controller.choreographer.errorService.error!, widget.controller.choreographer.errorService.error!,
widget.controller.choreographer,
); );
} }

@ -63,6 +63,11 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
/// If we don't have any good activities for them, we'll decrease this number /// If we don't have any good activities for them, we'll decrease this number
static const int neededActivities = 3; static const int neededActivities = 3;
int activitiesLeftToComplete = neededActivities; int activitiesLeftToComplete = neededActivities;
bool get messageInUserL2 =>
pangeaMessageEvent.messageDisplayLangCode ==
MatrixState.pangeaController.languageController.userL2?.langCode;
PangeaMessageEvent get pangeaMessageEvent => widget._pangeaMessageEvent; PangeaMessageEvent get pangeaMessageEvent => widget._pangeaMessageEvent;
final TtsController tts = TtsController(); final TtsController tts = TtsController();
@ -160,6 +165,11 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
toolbarMode = MessageMode.speechToText; toolbarMode = MessageMode.speechToText;
return; return;
} }
// if (!messageInUserL2) {
// activitiesLeftToComplete = 0;
// toolbarMode = MessageMode.nullMode;
// return;
// }
if (activitiesLeftToComplete > 0) { if (activitiesLeftToComplete > 0) {
toolbarMode = MessageMode.practiceActivity; toolbarMode = MessageMode.practiceActivity;
@ -385,6 +395,10 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
} }
Size? get messageSize { Size? get messageSize {
if (messageRenderBox == null || !messageRenderBox!.hasSize) {
return null;
}
try { try {
return messageRenderBox?.size; return messageRenderBox?.size;
} catch (e, s) { } catch (e, s) {
@ -394,6 +408,10 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
} }
Offset? get messageOffset { Offset? get messageOffset {
if (messageRenderBox == null || !messageRenderBox!.hasSize) {
return null;
}
try { try {
return messageRenderBox?.localToGlobal(Offset.zero); return messageRenderBox?.localToGlobal(Offset.zero);
} catch (e, s) { } catch (e, s) {

@ -152,7 +152,7 @@ class MessageSpeechToTextCardState extends State<MessageSpeechToTextCard> {
// done fetchig but not results means some kind of error // done fetchig but not results means some kind of error
if (speechToTextResponse == null) { if (speechToTextResponse == null) {
return CardErrorWidget( return CardErrorWidget(
error: error, error: error ?? "Failed to fetch speech to text",
maxWidth: AppConfig.toolbarMinWidth, maxWidth: AppConfig.toolbarMinWidth,
); );
} }

@ -12,11 +12,12 @@ import 'package:fluffychat/pangea/widgets/chat/message_translation_card.dart';
import 'package:fluffychat/pangea/widgets/chat/message_unsubscribed_card.dart'; import 'package:fluffychat/pangea/widgets/chat/message_unsubscribed_card.dart';
import 'package:fluffychat/pangea/widgets/chat/tts_controller.dart'; import 'package:fluffychat/pangea/widgets/chat/tts_controller.dart';
import 'package:fluffychat/pangea/widgets/igc/word_data_card.dart'; import 'package:fluffychat/pangea/widgets/igc/word_data_card.dart';
import 'package:fluffychat/pangea/widgets/message_display_card.dart';
import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart';
import 'package:fluffychat/pangea/widgets/select_to_define.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
const double minCardHeight = 70; const double minCardHeight = 70;
@ -32,7 +33,7 @@ class MessageToolbar extends StatelessWidget {
required this.tts, required this.tts,
}); });
Widget get toolbarContent { Widget toolbarContent(BuildContext context) {
final bool subscribed = final bool subscribed =
MatrixState.pangeaController.subscriptionController.isSubscribed; MatrixState.pangeaController.subscriptionController.isSubscribed;
@ -42,6 +43,18 @@ class MessageToolbar extends StatelessWidget {
); );
} }
// Check if the message is in the user's second language
final bool messageInUserL2 = pangeaMessageEvent.messageDisplayLangCode ==
MatrixState.pangeaController.languageController.userL2?.langCode;
// If not in the target language show specific messsage
if (!messageInUserL2) {
return MessageDisplayCard(
displayText:
L10n.of(context)!.messageNotInTargetLang, // Pass the display text,
);
}
switch (overLayController.toolbarMode) { switch (overLayController.toolbarMode) {
case MessageMode.translation: case MessageMode.translation:
return MessageTranslationCard( return MessageTranslationCard(
@ -62,7 +75,9 @@ class MessageToolbar extends StatelessWidget {
); );
case MessageMode.definition: case MessageMode.definition:
if (!overLayController.isSelection) { if (!overLayController.isSelection) {
return const SelectToDefine(); return MessageDisplayCard(
displayText: L10n.of(context)!.selectToDefine,
);
} else { } else {
try { try {
final selectedText = overLayController.targetText; final selectedText = overLayController.targetText;
@ -127,7 +142,7 @@ class MessageToolbar extends StatelessWidget {
child: SingleChildScrollView( child: SingleChildScrollView(
child: AnimatedSize( child: AnimatedSize(
duration: FluffyThemes.animationDuration, duration: FluffyThemes.animationDuration,
child: toolbarContent, child: toolbarContent(context),
), ),
), ),
); );

@ -22,7 +22,7 @@ class ToolbarButtons extends StatelessWidget {
overlayController.pangeaMessageEvent; overlayController.pangeaMessageEvent;
List<MessageMode> get modes => MessageMode.values List<MessageMode> get modes => MessageMode.values
.where((mode) => mode.isValidMode(pangeaMessageEvent.event)) .where((mode) => mode.shouldShowAsToolbarButton(pangeaMessageEvent.event))
.toList(); .toList();
static const double iconWidth = 36.0; static const double iconWidth = 36.0;

@ -241,6 +241,8 @@ class ConversationBotSettingsDialogState
updateFromTextControllers(); updateFromTextControllers();
Navigator.of(context).pop(botOptions);
final bool isBotRoomMember = final bool isBotRoomMember =
await widget.room.botIsInRoom; await widget.room.botIsInRoom;
if (addBot && !isBotRoomMember) { if (addBot && !isBotRoomMember) {
@ -248,8 +250,6 @@ class ConversationBotSettingsDialogState
} else if (!addBot && isBotRoomMember) { } else if (!addBot && isBotRoomMember) {
await widget.room.kick(BotName.byEnvironment); await widget.room.kick(BotName.byEnvironment);
} }
Navigator.of(context).pop(botOptions);
}, },
child: Text(L10n.of(context)!.confirm), child: Text(L10n.of(context)!.confirm),
), ),

@ -6,14 +6,14 @@ import 'package:fluffychat/pangea/widgets/igc/card_header.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class CardErrorWidget extends StatelessWidget { class CardErrorWidget extends StatelessWidget {
final Object? error; final Object error;
final Choreographer? choreographer; final Choreographer? choreographer;
final int? offset; final int? offset;
final double? maxWidth; final double? maxWidth;
const CardErrorWidget({ const CardErrorWidget({
super.key, super.key,
this.error, required this.error,
this.choreographer, this.choreographer,
this.offset, this.offset,
this.maxWidth, this.maxWidth,

@ -167,7 +167,7 @@ class WordDataCardView extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (controller.wordNetError != null) { if (controller.wordNetError != null) {
return CardErrorWidget( return CardErrorWidget(
error: controller.wordNetError, error: controller.wordNetError!,
maxWidth: AppConfig.toolbarMinWidth, maxWidth: AppConfig.toolbarMinWidth,
); );
} }

@ -1,10 +1,12 @@
import 'package:fluffychat/pangea/utils/bot_style.dart'; import 'package:fluffychat/pangea/utils/bot_style.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
class SelectToDefine extends StatelessWidget { class MessageDisplayCard extends StatelessWidget {
const SelectToDefine({ final String displayText;
const MessageDisplayCard({
super.key, super.key,
required this.displayText,
}); });
@override @override
@ -12,7 +14,7 @@ class SelectToDefine extends StatelessWidget {
return Padding( return Padding(
padding: const EdgeInsets.fromLTRB(16, 20, 16, 16), padding: const EdgeInsets.fromLTRB(16, 20, 16, 16),
child: Text( child: Text(
L10n.of(context)!.selectToDefine, displayText,
style: BotStyle.text(context), style: BotStyle.text(context),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),

@ -6,7 +6,7 @@ description: Learn a language while texting your friends.
# Pangea# # Pangea#
publish_to: none publish_to: none
# On version bump also increase the build number for F-Droid # On version bump also increase the build number for F-Droid
version: 1.22.6+3556 version: 1.22.9+3559
environment: environment:
sdk: ">=3.0.0 <4.0.0" sdk: ">=3.0.0 <4.0.0"

Loading…
Cancel
Save