* docs: add PT v2 and token-info-feedback design docs - Add phonetic-transcription-v2-design.instructions.md (client PT v2 migration) - Add token-info-feedback-v2.instructions.md (client token feedback v2 migration) * fix: update applyTo path for token info feedback v2 migration * feat: Refactor phonetic transcription to v2 models and repository (in progress) * feat: PT v2 migration - tts_phoneme rename, v1 cleanup, disambiguation, TTS integration * feat: Update phonetic transcription v2 design document for endpoint changes and response structure * docs: fix stale _storageKeys claim in pt-v2 design doc * style: reformat PT v2 files with Dart 3.10 formatter (Flutter 3.38) * feat: add speakingRate to TTS request model (default 0.85) Passes speaking_rate to the choreo TTS endpoint. Default preserves current behavior; can be overridden for single-word playback later. * feat: use normal speed (1.0) for single-word TTS playback The 0.85x slowdown is helpful for full sentences but makes single words sound unnaturally slow. tts_controller._speakFromChoreo now sends speakingRate=1.0. Full-sentence TTS via pangea_message_event still defaults to 0.85. * style: clean up formatting and reduce line breaks in TtsController * fix: env goofiness * formatting, fix linter issues * don't return widgets from functions --------- Co-authored-by: ggurdin <ggurdin@gmail.com> Co-authored-by: ggurdin <46800240+ggurdin@users.noreply.github.com>
203 lines
6.3 KiB
Dart
203 lines
6.3 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:get_storage/get_storage.dart';
|
|
import 'package:matrix/matrix.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
|
|
|
import 'package:fluffychat/config/app_config.dart';
|
|
import 'package:fluffychat/config/setting_keys.dart';
|
|
import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart';
|
|
import 'package:fluffychat/pangea/chat_settings/utils/bot_client_extension.dart';
|
|
import 'package:fluffychat/pangea/common/utils/p_vguard.dart';
|
|
import 'package:fluffychat/pangea/languages/locale_provider.dart';
|
|
import 'package:fluffychat/pangea/languages/p_language_store.dart';
|
|
import 'package:fluffychat/pangea/subscription/controllers/subscription_controller.dart';
|
|
import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart';
|
|
import 'package:fluffychat/pangea/user/pangea_push_rules_extension.dart';
|
|
import 'package:fluffychat/pangea/user/style_settings_repo.dart';
|
|
import 'package:fluffychat/pangea/user/user_controller.dart';
|
|
import 'package:fluffychat/widgets/matrix.dart';
|
|
import '../utils/firebase_analytics.dart';
|
|
|
|
class PangeaController {
|
|
///pangeaControllers
|
|
late UserController userController;
|
|
late SubscriptionController subscriptionController;
|
|
|
|
///store Services
|
|
final pLanguageStore = PLanguageStore();
|
|
|
|
StreamSubscription? _languageSubscription;
|
|
StreamSubscription? _settingsSubscription;
|
|
StreamSubscription? _joinSpaceSubscription;
|
|
|
|
///Matrix Variables
|
|
final MatrixState matrixState;
|
|
|
|
PangeaController({required this.matrixState}) {
|
|
userController = UserController();
|
|
subscriptionController = SubscriptionController(this);
|
|
PAuthGaurd.pController = this;
|
|
_registerSubscriptions();
|
|
}
|
|
|
|
/// Initializes various controllers and settings.
|
|
/// While many of these functions are asynchronous, they are not awaited here,
|
|
/// because of order of execution does not matter,
|
|
/// and running them at the same times speeds them up.
|
|
void initControllers() {
|
|
_initAnalytics();
|
|
subscriptionController.initialize();
|
|
matrixState.client.setPangeaPushRules();
|
|
TtsController.setAvailableLanguages();
|
|
}
|
|
|
|
void _onLogin(BuildContext context, String? userID) {
|
|
initControllers();
|
|
_registerSubscriptions();
|
|
|
|
userController.reinitialize().then((_) {
|
|
final l1 = userController.profile.userSettings.sourceLanguage;
|
|
Provider.of<LocaleProvider>(context, listen: false).setLocale(l1);
|
|
});
|
|
subscriptionController.reinitialize();
|
|
|
|
StyleSettingsRepo.settings(userID!).then((settings) {
|
|
AppSettings.fontSizeFactor.setItem(settings.fontSizeFactor);
|
|
AppConfig.useActivityImageAsChatBackground =
|
|
settings.useActivityImageBackground;
|
|
});
|
|
}
|
|
|
|
void _onLogout(BuildContext context) {
|
|
userController.clear();
|
|
_languageSubscription?.cancel();
|
|
_settingsSubscription?.cancel();
|
|
_joinSpaceSubscription?.cancel();
|
|
_languageSubscription = null;
|
|
_settingsSubscription = null;
|
|
_joinSpaceSubscription = null;
|
|
|
|
GoogleAnalytics.logout();
|
|
_clearCache();
|
|
Provider.of<LocaleProvider>(context, listen: false).setLocale(null);
|
|
}
|
|
|
|
void handleLoginStateChange(
|
|
LoginState state,
|
|
String? userID,
|
|
BuildContext context,
|
|
) {
|
|
switch (state) {
|
|
case LoginState.loggedOut:
|
|
case LoginState.softLoggedOut:
|
|
_onLogout(context);
|
|
break;
|
|
case LoginState.loggedIn:
|
|
_onLogin(context, userID);
|
|
break;
|
|
}
|
|
|
|
Sentry.configureScope(
|
|
(scope) => scope.setUser(SentryUser(id: userID, name: userID)),
|
|
);
|
|
GoogleAnalytics.analyticsUserUpdate(userID);
|
|
}
|
|
|
|
void _registerSubscriptions() {
|
|
_languageSubscription?.cancel();
|
|
_languageSubscription = userController.languageStream.stream.listen(
|
|
_onLanguageUpdate,
|
|
);
|
|
|
|
_settingsSubscription?.cancel();
|
|
_settingsSubscription = userController.settingsUpdateStream.stream.listen((
|
|
update,
|
|
) async {
|
|
await matrixState.client.updateBotOptions(update.userSettings);
|
|
await userController.updatePublicProfile();
|
|
});
|
|
|
|
_joinSpaceSubscription?.cancel();
|
|
_joinSpaceSubscription ??= matrixState.client.onSync.stream
|
|
.where(matrixState.client.isJoinSpaceSyncUpdate)
|
|
.listen((_) => matrixState.client.addAnalyticsRoomsToSpaces());
|
|
}
|
|
|
|
Future<void> _clearCache({List<String> exclude = const []}) async {
|
|
final List<Future<void>> futures = [];
|
|
for (final key in _storageKeys) {
|
|
if (exclude.contains(key)) continue;
|
|
futures.add(GetStorage(key).erase());
|
|
}
|
|
|
|
await Future.wait(futures);
|
|
}
|
|
|
|
Future<void> _initAnalytics() async {
|
|
await GetStorage.init("activity_analytics_storage");
|
|
|
|
matrixState.client.updateAnalyticsRoomJoinRules();
|
|
matrixState.client.addAnalyticsRoomsToSpaces();
|
|
}
|
|
|
|
Future<void> resetAnalytics() async {
|
|
await _initAnalytics();
|
|
}
|
|
|
|
Future<void> _onLanguageUpdate(LanguageUpdate update) async {
|
|
final exclude = [
|
|
'course_location_media_storage',
|
|
'course_location_storage',
|
|
'course_media_storage',
|
|
];
|
|
|
|
// only clear course data if the base language has changed
|
|
if (update.prevBaseLang == update.baseLang) {
|
|
exclude.addAll([
|
|
'course_storage',
|
|
'course_topic_storage',
|
|
'course_activity_storage',
|
|
]);
|
|
}
|
|
|
|
await _clearCache(exclude: exclude);
|
|
await matrixState.client.updateBotOptions(
|
|
userController.profile.userSettings,
|
|
);
|
|
await userController.updatePublicProfile();
|
|
}
|
|
|
|
static final List<String> _storageKeys = [
|
|
'mode_list_storage',
|
|
'activity_plan_storage',
|
|
'bookmarked_activities',
|
|
'objective_list_storage',
|
|
'topic_list_storage',
|
|
'activity_plan_search_storage',
|
|
"version_storage",
|
|
'lemma_storage',
|
|
'svg_cache',
|
|
'morphs_storage',
|
|
'morph_meaning_storage',
|
|
'practice_record_cache',
|
|
'practice_selection_cache',
|
|
'subscription_storage',
|
|
'vocab_storage',
|
|
'onboarding_storage',
|
|
'analytics_request_storage',
|
|
'activity_analytics_storage',
|
|
'course_storage',
|
|
'course_topic_storage',
|
|
'course_media_storage',
|
|
'course_location_storage',
|
|
'course_activity_storage',
|
|
'course_location_media_storage',
|
|
'language_mismatch',
|
|
'phonetic_transcription_storage',
|
|
'phonetic_transcription_v2_storage',
|
|
];
|
|
}
|