Merge pull request #4809 from pangeachat/add-gender-setting

Add gender setting
This commit is contained in:
ggurdin 2025-12-05 12:39:26 -05:00 committed by GitHub
commit 6d5116e084
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 204 additions and 3 deletions

View file

@ -4991,5 +4991,10 @@
},
"pickDifferentActivity": "Pick a different activity",
"messageLanguageMismatchMessage": "Your target language doesn't match this message. Update your target language?",
"blockLemmaConfirmation": "This vocab word will be permanently removed from your analytics"
"blockLemmaConfirmation": "This vocab word will be permanently removed from your analytics",
"woman": "Woman",
"man": "Man",
"otherGender": "Other",
"unselectedGender": "Select a gender option",
"gender": "Gender"
}

View file

@ -23,6 +23,7 @@ import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/languages/language_model.dart';
import 'package:fluffychat/pangea/learning_settings/gender_enum.dart';
import 'package:fluffychat/pangea/practice_activities/practice_selection_repo.dart';
import 'package:fluffychat/widgets/matrix.dart';
@ -49,6 +50,9 @@ class GetAnalyticsController extends BaseController {
LanguageModel? get _l1 => _pangeaController.userController.userL1;
LanguageModel? get _l2 => _pangeaController.userController.userL2;
GenderEnum get gender =>
MatrixState.pangeaController.userController.profile.userSettings.gender;
Client get _client => _pangeaController.matrixState.client;
// the minimum XP required for a given level
@ -564,6 +568,7 @@ class GetAnalyticsController extends BaseController {
messages: messages,
userL1: _l1!.langCodeShort,
userL2: _l2!.langCodeShort,
userGender: gender,
upperLevel: upperLevel,
lowerLevel: lowerLevel,
);

View file

@ -71,6 +71,8 @@ class IgcController {
userId: MatrixState.pangeaController.userController.client.userID!,
userL1: MatrixState.pangeaController.userController.userL1Code!,
userL2: MatrixState.pangeaController.userController.userL2Code!,
userGender: MatrixState
.pangeaController.userController.profile.userSettings.gender,
enableIGC: true,
enableIT: true,
prevMessages: prevMessages,

View file

@ -1,11 +1,13 @@
import 'dart:convert';
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
import 'package:fluffychat/pangea/learning_settings/gender_enum.dart';
class IGCRequestModel {
final String fullText;
final String userL1;
final String userL2;
final GenderEnum userGender;
final bool enableIT;
final bool enableIGC;
final String userId;
@ -15,6 +17,7 @@ class IGCRequestModel {
required this.fullText,
required this.userL1,
required this.userL2,
required this.userGender,
required this.enableIGC,
required this.enableIT,
required this.userId,
@ -25,6 +28,7 @@ class IGCRequestModel {
ModelKey.fullText: fullText,
ModelKey.userL1: userL1,
ModelKey.userL2: userL2,
ModelKey.userGender: userGender.string,
"enable_it": enableIT,
"enable_igc": enableIGC,
ModelKey.userId: userId,
@ -42,6 +46,7 @@ class IGCRequestModel {
fullText == other.fullText &&
userL1 == other.userL1 &&
userL2 == other.userL2 &&
userGender == other.userGender &&
enableIT == other.enableIT &&
userId == other.userId;
}
@ -51,6 +56,7 @@ class IGCRequestModel {
fullText.trim(),
userL1,
userL2,
userGender,
enableIT,
enableIGC,
userId,

View file

@ -65,6 +65,8 @@ class ITBarState extends State<ITBar> with SingleTickerProviderStateMixin {
tgtLang: MatrixState.pangeaController.userController.userL1!.langCode,
userL1: MatrixState.pangeaController.userController.userL1!.langCode,
userL2: MatrixState.pangeaController.userController.userL2!.langCode,
userGender: MatrixState
.pangeaController.userController.profile.userSettings.gender,
);
void _openListener() {

View file

@ -47,6 +47,7 @@ class ModelKey {
static const String userL1 = "user_l1";
static const String userL2 = "user_l2";
static const String userGender = "user_gender";
static const String fullText = "full_text";
static const String fullTextLang = "full_text_lang";
static const String tokens = "tokens";

View file

@ -4,8 +4,10 @@ import 'package:http/http.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/common/config/environment.dart';
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
import 'package:fluffychat/pangea/common/network/requests.dart';
import 'package:fluffychat/pangea/common/network/urls.dart';
import 'package:fluffychat/pangea/learning_settings/gender_enum.dart';
import 'package:fluffychat/widgets/matrix.dart';
class ConstructSummary {
@ -69,6 +71,7 @@ class ConstructSummaryRequest {
final List<Map<String, dynamic>> messages;
final String userL1;
final String userL2;
final GenderEnum userGender;
final int upperLevel;
final int lowerLevel;
@ -77,6 +80,7 @@ class ConstructSummaryRequest {
required this.messages,
required this.userL1,
required this.userL2,
required this.userGender,
required this.upperLevel,
required this.lowerLevel,
});
@ -87,6 +91,7 @@ class ConstructSummaryRequest {
'msgs': messages,
'user_l1': userL1,
'user_l2': userL2,
ModelKey.userGender: userGender.string,
'language': userL1,
'upper_level': upperLevel,
'lower_level': lowerLevel,

View file

@ -18,6 +18,7 @@ import 'package:fluffychat/pangea/events/repo/language_detection_repo.dart';
import 'package:fluffychat/pangea/events/repo/language_detection_request.dart';
import 'package:fluffychat/pangea/events/repo/language_detection_response.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/learning_settings/gender_enum.dart';
import 'package:fluffychat/pangea/learning_settings/tool_settings_enum.dart';
import 'package:fluffychat/pangea/speech_to_text/audio_encoding_enum.dart';
import 'package:fluffychat/pangea/speech_to_text/speech_to_text_repo.dart';
@ -79,6 +80,9 @@ class PangeaMessageEvent {
String? get _l1Code =>
MatrixState.pangeaController.userController.userL1?.langCode;
GenderEnum get gender =>
MatrixState.pangeaController.userController.profile.userSettings.gender;
Event? _latestEditCache;
Event get _latestEdit => _latestEditCache ??= _event
.aggregatedEvents(
@ -365,6 +369,7 @@ class PangeaMessageEvent {
langCode: langCode,
userL1: _l1Code ?? LanguageKeys.unknownLanguage,
userL2: _l2Code ?? LanguageKeys.unknownLanguage,
userGender: gender,
);
final result = await TextToSpeechRepo.get(
@ -572,6 +577,7 @@ class PangeaMessageEvent {
tgtLang: targetLang,
userL2: _l2Code ?? LanguageKeys.unknownLanguage,
userL1: _l1Code ?? LanguageKeys.unknownLanguage,
userGender: gender,
),
);

View file

@ -272,6 +272,8 @@ class RepresentationEvent {
tgtLang: userL1,
userL2: userL2,
userL1: userL1,
userGender: MatrixState
.pangeaController.userController.profile.userSettings.gender,
),
);

View file

@ -0,0 +1,77 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/common/widgets/dropdown_text_button.dart';
import 'package:fluffychat/pangea/learning_settings/gender_enum.dart';
class GenderDropdown extends StatelessWidget {
final GenderEnum initialGender;
final Function(GenderEnum)? onChanged;
final FormFieldValidator<Object>? validator;
final bool enabled;
final Color? backgroundColor;
const GenderDropdown({
super.key,
this.initialGender = GenderEnum.unselected,
this.onChanged,
this.validator,
this.enabled = true,
this.backgroundColor,
});
@override
Widget build(BuildContext context) {
final l10n = L10n.of(context);
return DropdownButtonFormField2<GenderEnum>(
customButton:
CustomDropdownTextButton(text: initialGender.title(context)),
menuItemStyleData: const MenuItemStyleData(
padding: EdgeInsets.zero,
),
decoration: InputDecoration(
labelText: l10n.gender,
),
isExpanded: true,
dropdownStyleData: DropdownStyleData(
maxHeight: kIsWeb ? 500 : null,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14.0),
color: Theme.of(context).colorScheme.surfaceContainerHigh,
),
),
items: GenderEnum.values.map((GenderEnum genderOption) {
return DropdownMenuItem(
enabled: genderOption != GenderEnum.unselected,
value: genderOption,
child: Container(
color: Colors.transparent,
padding: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 12,
),
child: Text(
genderOption.title(context),
style: const TextStyle().copyWith(
color: Theme.of(context).textTheme.bodyLarge!.color,
fontSize: 14,
),
overflow: TextOverflow.ellipsis,
),
),
);
}).toList(),
onChanged: enabled
? (value) {
if (value != null) onChanged?.call(value);
}
: null,
value: initialGender,
validator: validator,
);
}
}

View file

@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/l10n/l10n.dart';
enum GenderEnum {
unselected,
woman,
man,
other,
}
extension GenderEnumExtension on GenderEnum {
String get string {
switch (this) {
case GenderEnum.unselected:
return 'unselected';
case GenderEnum.woman:
return 'woman';
case GenderEnum.man:
return 'man';
case GenderEnum.other:
return 'other';
}
}
static GenderEnum fromString(String? value) {
switch (value) {
case 'woman':
return GenderEnum.woman;
case 'man':
return GenderEnum.man;
case 'other':
return GenderEnum.other;
default:
return GenderEnum.unselected;
}
}
String title(BuildContext context) {
final L10n l10n = L10n.of(context);
switch (this) {
case GenderEnum.unselected:
return l10n.unselectedGender;
case GenderEnum.woman:
return l10n.woman;
case GenderEnum.man:
return l10n.man;
case GenderEnum.other:
return l10n.otherGender;
}
}
}

View file

@ -15,6 +15,7 @@ import 'package:fluffychat/pangea/instructions/instruction_settings.dart';
import 'package:fluffychat/pangea/languages/language_model.dart';
import 'package:fluffychat/pangea/languages/language_service.dart';
import 'package:fluffychat/pangea/languages/p_language_store.dart';
import 'package:fluffychat/pangea/learning_settings/gender_enum.dart';
import 'package:fluffychat/pangea/learning_settings/language_level_type_enum.dart';
import 'package:fluffychat/pangea/learning_settings/settings_learning_view.dart';
import 'package:fluffychat/pangea/learning_settings/tool_settings_enum.dart';
@ -165,6 +166,11 @@ class SettingsLearningController extends State<SettingsLearning> {
if (mounted) setState(() {});
}
void setGender(GenderEnum? gender) {
_profile.userSettings.gender = gender ?? GenderEnum.unselected;
if (mounted) setState(() {});
}
void setPublicProfile(bool isPublic) {
_profile.userSettings.publicProfile = isPublic;
if (mounted) setState(() {});
@ -282,6 +288,8 @@ class SettingsLearningController extends State<SettingsLearning> {
? PLanguageStore.byLangCode(_profile.userSettings.targetLanguage!)
: null;
GenderEnum get gender => _profile.userSettings.gender;
bool getToolSetting(ToolSetting toolSetting) {
final toolSettings = _profile.toolSettings;
switch (toolSetting) {

View file

@ -9,6 +9,7 @@ import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart';
import 'package:fluffychat/pangea/instructions/reset_instructions_list_tile.dart';
import 'package:fluffychat/pangea/languages/language_model.dart';
import 'package:fluffychat/pangea/learning_settings/country_picker_tile.dart';
import 'package:fluffychat/pangea/learning_settings/gender_dropdown.dart';
import 'package:fluffychat/pangea/learning_settings/p_language_dropdown.dart';
import 'package:fluffychat/pangea/learning_settings/p_settings_switch_list_tile.dart';
import 'package:fluffychat/pangea/learning_settings/settings_learning.dart';
@ -137,6 +138,10 @@ class SettingsLearningView extends StatelessWidget {
initialLevel: controller.cefrLevel,
onChanged: controller.setCefrLevel,
),
GenderDropdown(
initialGender: controller.gender,
onChanged: controller.setGender,
),
Container(
decoration: BoxDecoration(
border: Border.all(

View file

@ -1,11 +1,13 @@
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart';
import 'package:fluffychat/pangea/learning_settings/gender_enum.dart';
class TextToSpeechRequestModel {
String text;
String langCode;
String userL1;
String userL2;
GenderEnum userGender;
List<PangeaTokenText> tokens;
TextToSpeechRequestModel({
@ -13,6 +15,7 @@ class TextToSpeechRequestModel {
required this.langCode,
required this.userL1,
required this.userL2,
required this.userGender,
required this.tokens,
});
@ -21,6 +24,7 @@ class TextToSpeechRequestModel {
ModelKey.langCode: langCode,
ModelKey.userL1: userL1,
ModelKey.userL2: userL2,
ModelKey.userGender: userGender.string,
ModelKey.tokens: tokens.map((token) => token.toJson()).toList(),
};
@ -30,9 +34,10 @@ class TextToSpeechRequestModel {
return other is TextToSpeechRequestModel &&
other.text == text &&
other.langCode == langCode;
other.langCode == langCode &&
other.userGender == userGender;
}
@override
int get hashCode => text.hashCode ^ langCode.hashCode;
int get hashCode => text.hashCode ^ langCode.hashCode ^ userGender.hashCode;
}

View file

@ -279,6 +279,8 @@ class TtsController {
LanguageKeys.unknownLanguage,
userL2: MatrixState.pangeaController.userController.userL2Code ??
LanguageKeys.unknownLanguage,
userGender: MatrixState
.pangeaController.userController.profile.userSettings.gender,
),
);
loadingChoreoStream.add(false);

View file

@ -1,4 +1,5 @@
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
import 'package:fluffychat/pangea/learning_settings/gender_enum.dart';
class FullTextTranslationRequestModel {
final String text;
@ -6,6 +7,7 @@ class FullTextTranslationRequestModel {
final String tgtLang;
final String userL1;
final String userL2;
final GenderEnum userGender;
final bool? deepL;
final int? offset;
final int? length;
@ -16,6 +18,7 @@ class FullTextTranslationRequestModel {
required this.tgtLang,
required this.userL2,
required this.userL1,
required this.userGender,
this.deepL = false,
this.offset,
this.length,
@ -27,6 +30,7 @@ class FullTextTranslationRequestModel {
ModelKey.tgtLang: tgtLang,
ModelKey.userL2: userL2,
ModelKey.userL1: userL1,
ModelKey.userGender: userGender.string,
ModelKey.deepL: deepL,
ModelKey.offset: offset,
ModelKey.length: length,
@ -43,6 +47,7 @@ class FullTextTranslationRequestModel {
other.tgtLang == tgtLang &&
other.userL2 == userL2 &&
other.userL1 == userL1 &&
other.userGender == userGender &&
other.deepL == deepL &&
other.offset == offset &&
other.length == length;
@ -55,6 +60,7 @@ class FullTextTranslationRequestModel {
tgtLang.hashCode ^
userL2.hashCode ^
userL1.hashCode ^
userGender.hashCode ^
deepL.hashCode ^
offset.hashCode ^
length.hashCode;

View file

@ -3,6 +3,7 @@ import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/instructions/instruction_settings.dart';
import 'package:fluffychat/pangea/learning_settings/gender_enum.dart';
import 'package:fluffychat/pangea/learning_settings/language_level_type_enum.dart';
import 'package:fluffychat/pangea/learning_settings/tool_settings_enum.dart';
import 'package:fluffychat/widgets/matrix.dart';
@ -15,6 +16,7 @@ class UserSettings {
bool? publicProfile;
String? targetLanguage;
String? sourceLanguage;
GenderEnum gender;
String? country;
LanguageLevelTypeEnum cefrLevel;
@ -24,6 +26,7 @@ class UserSettings {
this.publicProfile,
this.targetLanguage,
this.sourceLanguage,
this.gender = GenderEnum.unselected,
this.country,
this.cefrLevel = LanguageLevelTypeEnum.a1,
});
@ -38,6 +41,11 @@ class UserSettings {
publicProfile: json[ModelKey.publicProfile],
targetLanguage: json[ModelKey.l2LanguageKey],
sourceLanguage: json[ModelKey.l1LanguageKey],
gender: json[ModelKey.userGender] is String
? GenderEnumExtension.fromString(
json[ModelKey.userGender],
)
: GenderEnum.unselected,
country: json[ModelKey.userCountry],
cefrLevel: json[ModelKey.cefrLevel] is String
? LanguageLevelTypeEnum.fromString(
@ -53,6 +61,7 @@ class UserSettings {
data[ModelKey.publicProfile] = publicProfile;
data[ModelKey.l2LanguageKey] = targetLanguage;
data[ModelKey.l1LanguageKey] = sourceLanguage;
data[ModelKey.userGender] = gender.string;
data[ModelKey.userCountry] = country;
data[ModelKey.cefrLevel] = cefrLevel.string;
return data;
@ -111,6 +120,7 @@ class UserSettings {
publicProfile: publicProfile,
targetLanguage: targetLanguage,
sourceLanguage: sourceLanguage,
gender: gender,
country: country,
cefrLevel: cefrLevel,
);
@ -126,6 +136,7 @@ class UserSettings {
(other.publicProfile ?? false) == (publicProfile ?? false) &&
other.targetLanguage == targetLanguage &&
other.sourceLanguage == sourceLanguage &&
other.gender == gender &&
other.country == country &&
other.cefrLevel == cefrLevel;
}
@ -137,6 +148,7 @@ class UserSettings {
(publicProfile ?? false).hashCode,
targetLanguage.hashCode,
sourceLanguage.hashCode,
gender.hashCode,
country.hashCode,
cefrLevel.hashCode,
]);