full draft speech to text with word level feedback

This commit is contained in:
William Jordan-Cooley 2024-05-06 15:10:15 -04:00
parent f3bb717245
commit 4a77e6fb53
39 changed files with 637 additions and 274 deletions

View file

@ -3887,7 +3887,7 @@
"more": "More",
"translationTooltip": "Translate",
"audioTooltip": "Play Audio",
"transcriptTooltip": "Transcript",
"speechToTextTooltip": "Transcript",
"certifyAge": "I certify that I am over {age} years of age",
"@certifyAge": {
"type": "text",

View file

@ -4572,7 +4572,7 @@
"more": "Más",
"translationTooltip": "Traducir",
"audioTooltip": "Reproducir audio",
"transcriptTooltip": "Transcripción",
"speechToTextTooltip": "Transcripción",
"yourBirthdayPleaseShort": "Seleccione su grupo de edad",
"certifyAge": "Certifico que soy mayor de {age} años",
"@certifyAge": {

View file

@ -17,11 +17,12 @@ import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/enum/use_type.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/models/choreo_record.dart';
import 'package:fluffychat/pangea/models/class_model.dart';
import 'package:fluffychat/pangea/models/message_data_models.dart';
import 'package:fluffychat/pangea/models/pangea_message_event.dart';
import 'package:fluffychat/pangea/models/representation_content_model.dart';
import 'package:fluffychat/pangea/models/student_analytics_summary_model.dart';
import 'package:fluffychat/pangea/models/tokens_event_content_model.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/utils/firebase_analytics.dart';
import 'package:fluffychat/pangea/utils/report_message.dart';

View file

@ -1,8 +1,8 @@
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pangea/enum/use_type.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/models/language_model.dart';
import 'package:fluffychat/pangea/models/pangea_message_event.dart';
import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart';
import 'package:fluffychat/utils/date_time_extension.dart';
import 'package:fluffychat/utils/string_color.dart';

View file

@ -1,7 +1,7 @@
import 'package:fluffychat/pages/chat/events/html_message.dart';
import 'package:fluffychat/pages/chat/events/video_player.dart';
import 'package:fluffychat/pangea/enum/message_mode_enum.dart';
import 'package:fluffychat/pangea/models/pangea_message_event.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/widgets/chat/message_context_menu.dart';
import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart';
import 'package:fluffychat/pangea/widgets/igc/pangea_rich_text.dart';

View file

@ -13,7 +13,8 @@ import 'package:fluffychat/pangea/enum/edit_type.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/models/class_model.dart';
import 'package:fluffychat/pangea/models/it_step.dart';
import 'package:fluffychat/pangea/models/message_data_models.dart';
import 'package:fluffychat/pangea/models/representation_content_model.dart';
import 'package:fluffychat/pangea/models/tokens_event_content_model.dart';
import 'package:fluffychat/pangea/utils/any_state_holder.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/utils/overlay.dart';

View file

@ -13,8 +13,8 @@ import 'package:matrix/matrix.dart';
import '../constants/class_default_values.dart';
import '../extensions/client_extension.dart';
import '../extensions/pangea_room_extension.dart';
import '../matrix_event_wrappers/construct_analytics_event.dart';
import '../models/chart_analytics_model.dart';
import '../models/construct_analytics_event.dart';
import '../models/student_analytics_event.dart';
import 'base_controller.dart';
import 'pangea_controller.dart';

View file

@ -2,7 +2,8 @@ import 'package:collection/collection.dart';
import 'package:fluffychat/pangea/controllers/base_controller.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/models/message_data_models.dart';
import 'package:fluffychat/pangea/models/representation_content_model.dart';
import 'package:fluffychat/pangea/models/tokens_event_content_model.dart';
import 'package:fluffychat/pangea/repo/tokens_repo.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';

View file

@ -4,6 +4,7 @@ import 'dart:convert';
import 'package:fluffychat/pangea/constants/pangea_event_types.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/models/representation_content_model.dart';
import 'package:fluffychat/pangea/models/speech_to_text_models.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:flutter/foundation.dart';
@ -13,10 +14,10 @@ import '../config/environment.dart';
import '../network/requests.dart';
import '../network/urls.dart';
// Assuming SpeechToTextRequestModel, SpeechToTextResponseModel and related models are already defined as in your provided code.
// Assuming SpeechToTextRequestModel, SpeechToTextModel and related models are already defined as in your provided code.
class _SpeechToTextCacheItem {
Future<SpeechToTextResponseModel> data;
Future<SpeechToTextModel> data;
_SpeechToTextCacheItem({required this.data});
}
@ -43,7 +44,7 @@ class SpeechToTextController {
_cacheClearTimer?.cancel();
}
Future<SpeechToTextResponseModel> get(
Future<SpeechToTextModel> get(
SpeechToTextRequestModel requestModel,
) async {
final int cacheKey = requestModel.hashCode;
@ -51,7 +52,7 @@ class SpeechToTextController {
if (_cache.containsKey(cacheKey)) {
return _cache[cacheKey]!.data;
} else {
final Future<SpeechToTextResponseModel> response = _fetchResponse(
final Future<SpeechToTextModel> response = _fetchResponse(
accessToken: await _pangeaController.userController.accessToken,
requestModel: requestModel,
);
@ -61,8 +62,8 @@ class SpeechToTextController {
}
}
Future<void> saveTranscriptAsMatrixEvent(
SpeechToTextResponseModel response,
Future<void> saveSpeechToTextAsRepresentationEvent(
SpeechToTextModel response,
SpeechToTextRequestModel requestModel,
) {
if (requestModel.audioEvent == null) {
@ -72,19 +73,24 @@ class SpeechToTextController {
return Future.value(null);
}
debugPrint('Saving transcript as matrix event');
final json = response.toJson();
requestModel.audioEvent?.room.sendPangeaEvent(
content: response.toJson(),
content: PangeaRepresentation(
langCode: response.langCode,
text: response.transcript.text,
originalSent: false,
originalWritten: false,
speechToText: response,
).toJson(),
parentEventId: requestModel.audioEvent!.eventId,
type: PangeaEventTypes.transcript,
type: PangeaEventTypes.representation,
);
debugPrint('Transcript saved as matrix event');
return Future.value(null);
}
Future<SpeechToTextResponseModel> _fetchResponse({
Future<SpeechToTextModel> _fetchResponse({
required String accessToken,
required SpeechToTextRequestModel requestModel,
}) async {
@ -101,9 +107,9 @@ class SpeechToTextController {
if (res.statusCode == 200) {
final Map<String, dynamic> json = jsonDecode(utf8.decode(res.bodyBytes));
final response = SpeechToTextResponseModel.fromJson(json);
final response = SpeechToTextModel.fromJson(json);
saveTranscriptAsMatrixEvent(response, requestModel).onError(
saveSpeechToTextAsRepresentationEvent(response, requestModel).onError(
(error, stackTrace) => ErrorHandler.logError(e: error, s: stackTrace),
);

View file

@ -6,6 +6,8 @@ AudioEncodingEnum mimeTypeToAudioEncoding(String mimeType) {
return AudioEncodingEnum.mp4;
case 'audio/ogg':
return AudioEncodingEnum.oggOpus;
case 'audio/x-flac':
return AudioEncodingEnum.flac;
default:
return AudioEncodingEnum.encodingUnspecified;
}

View file

@ -27,7 +27,7 @@ extension MessageModeExtension on MessageMode {
case MessageMode.textToSpeech:
return L10n.of(context)!.messageAudio;
case MessageMode.speechToText:
return L10n.of(context)!.transcriptTooltip;
return L10n.of(context)!.speechToTextTooltip;
case MessageMode.definition:
return L10n.of(context)!.definitions;
default:
@ -43,7 +43,7 @@ extension MessageModeExtension on MessageMode {
case MessageMode.textToSpeech:
return L10n.of(context)!.audioTooltip;
case MessageMode.speechToText:
return L10n.of(context)!.transcriptTooltip;
return L10n.of(context)!.speechToTextTooltip;
case MessageMode.definition:
return L10n.of(context)!.define;
default:

View file

@ -1,12 +1,11 @@
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/constants/pangea_event_types.dart';
import 'package:fluffychat/pangea/models/choreo_record.dart';
import 'package:fluffychat/pangea/models/message_data_models.dart';
import 'package:fluffychat/pangea/models/representation_content_model.dart';
import 'package:fluffychat/pangea/models/tokens_event_content_model.dart';
import 'package:flutter/foundation.dart';
import 'package:matrix/matrix.dart';
extension PangeaEvent on Event {
V getPangeaContent<V>() {

View file

@ -4,9 +4,10 @@ import 'dart:developer';
import 'package:fluffychat/pangea/constants/class_default_values.dart';
import 'package:fluffychat/pangea/constants/model_keys.dart';
import 'package:fluffychat/pangea/constants/pangea_room_types.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/models/bot_options_model.dart';
import 'package:fluffychat/pangea/models/class_model.dart';
import 'package:fluffychat/pangea/models/pangea_message_event.dart';
import 'package:fluffychat/pangea/models/tokens_event_content_model.dart';
import 'package:fluffychat/pangea/utils/bot_name.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:flutter/foundation.dart';
@ -22,10 +23,10 @@ import '../../config/app_config.dart';
import '../constants/pangea_event_types.dart';
import '../enum/construct_type_enum.dart';
import '../enum/use_type.dart';
import '../matrix_event_wrappers/construct_analytics_event.dart';
import '../models/choreo_record.dart';
import '../models/construct_analytics_event.dart';
import '../models/constructs_analytics_model.dart';
import '../models/message_data_models.dart';
import '../models/representation_content_model.dart';
import '../models/student_analytics_event.dart';
import '../models/student_analytics_summary_model.dart';
import '../utils/p_store.dart';

View file

@ -1,11 +1,10 @@
import 'package:flutter/foundation.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/extensions/pangea_event_extension.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:flutter/foundation.dart';
import 'package:matrix/matrix.dart';
import '../constants/pangea_event_types.dart';
import 'choreo_record.dart';
import '../models/choreo_record.dart';
class ChoreoEvent {
Event event;

View file

@ -5,12 +5,13 @@ import 'package:fluffychat/pangea/constants/model_keys.dart';
import 'package:fluffychat/pangea/controllers/text_to_speech_controller.dart';
import 'package:fluffychat/pangea/enum/audio_encoding_enum.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_representation_event.dart';
import 'package:fluffychat/pangea/models/choreo_record.dart';
import 'package:fluffychat/pangea/models/class_model.dart';
import 'package:fluffychat/pangea/models/message_data_models.dart';
import 'package:fluffychat/pangea/models/pangea_match_model.dart';
import 'package:fluffychat/pangea/models/pangea_representation_event.dart';
import 'package:fluffychat/pangea/models/representation_content_model.dart';
import 'package:fluffychat/pangea/models/speech_to_text_models.dart';
import 'package:fluffychat/pangea/models/tokens_event_content_model.dart';
import 'package:fluffychat/pangea/utils/bot_name.dart';
import 'package:fluffychat/pangea/widgets/chat/message_audio_card.dart';
import 'package:flutter/material.dart';
@ -270,36 +271,33 @@ class PangeaMessageEvent {
null;
}).toSet();
Set<Event> get transcriptionEvents => _event.aggregatedEvents(
timeline,
PangeaEventTypes.transcript,
);
Event? get transcriptionEvent => transcriptionEvents.lastOrNull;
Future<SpeechToTextResponseModel?> getSpeechToTextLocal() async {
if (transcriptionEvent == null) return null;
return SpeechToTextResponseModel.fromJson(
transcriptionEvent!.content[PangeaEventTypes.transcript]
as Map<String, dynamic>,
);
}
Future<SpeechToTextResponseModel?> getSpeechToTextGlobal(
Future<SpeechToTextModel?> getSpeechToText(
String l1Code,
String l2Code,
) async {
if (!isAudioMessage) {
ErrorHandler.logError(
e: 'Message is not an audio message ${_event.eventId}',
e: 'Calling getSpeechToText on non-audio message',
s: StackTrace.current,
data: _event.content,
data: {
"content": _event.content,
"eventId": _event.eventId,
"roomId": _event.roomId,
"userId": _event.room.client.userID,
"account_data": _event.room.client.accountData,
},
);
return null;
}
if (transcriptionEvent != null) return getSpeechToTextLocal();
final SpeechToTextModel? speechToTextLocal = representations
.firstWhereOrNull(
(element) => element.content.speechToText != null,
)
?.content
.speechToText;
if (speechToTextLocal != null) return speechToTextLocal;
final matrixFile = await _event.downloadAndDecryptAttachment();
// Pangea#
@ -323,7 +321,10 @@ class PangeaMessageEvent {
// audioFile = file;
final SpeechToTextResponseModel response =
debugPrint("mimeType ${matrixFile.mimeType}");
debugPrint("encoding ${mimeTypeToAudioEncoding(matrixFile.mimeType)}");
final SpeechToTextModel response =
await MatrixState.pangeaController.speechToText.get(
SpeechToTextRequestModel(
audioContent: matrixFile.bytes,
@ -332,7 +333,7 @@ class PangeaMessageEvent {
encoding: mimeTypeToAudioEncoding(matrixFile.mimeType),
//this is the default in the RecordConfig in record package
//TODO: check if this is the correct value and make it a constant somewhere
sampleRateHertz: 44100,
sampleRateHertz: 22050,
userL1: l1Code,
userL2: l2Code,
),

View file

@ -1,8 +1,10 @@
import 'dart:developer';
import 'package:fluffychat/pangea/extensions/pangea_event_extension.dart';
import 'package:fluffychat/pangea/models/pangea_choreo_event.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_choreo_event.dart';
import 'package:fluffychat/pangea/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/models/speech_to_text_models.dart';
import 'package:fluffychat/pangea/models/tokens_event_content_model.dart';
import 'package:fluffychat/pangea/repo/tokens_repo.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -13,9 +15,9 @@ import 'package:sentry_flutter/sentry_flutter.dart';
import '../../widgets/matrix.dart';
import '../constants/language_keys.dart';
import '../constants/pangea_event_types.dart';
import '../models/choreo_record.dart';
import '../models/representation_content_model.dart';
import '../utils/error_handler.dart';
import 'choreo_record.dart';
import 'message_data_models.dart';
import 'pangea_tokens_event.dart';
class RepresentationEvent {
@ -25,12 +27,15 @@ class RepresentationEvent {
ChoreoRecord? _choreo;
Timeline timeline;
SpeechToTextModel? _speechToTextResponse;
RepresentationEvent({
required this.timeline,
Event? event,
PangeaRepresentation? content,
PangeaMessageTokens? tokens,
ChoreoRecord? choreo,
SpeechToTextModel? speechToTextResponse,
}) {
if (event != null && event.type != PangeaEventTypes.representation) {
throw Exception(
@ -41,10 +46,14 @@ class RepresentationEvent {
_content = content;
_tokens = tokens;
_choreo = choreo;
_speechToTextResponse = speechToTextResponse;
}
Event? get event => _event;
// Note: in the case where the event is the originalSent or originalWritten event,
// the content will be set on initialization by the PangeaMessageEvent
// Otherwise, the content will be fetched from the event where it is stored in content[type]
PangeaRepresentation get content {
if (_content != null) return _content!;
_content = _event?.getPangeaContent<PangeaRepresentation>();

View file

@ -1,9 +1,9 @@
import 'package:fluffychat/pangea/extensions/pangea_event_extension.dart';
import 'package:fluffychat/pangea/models/tokens_event_content_model.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/extensions/pangea_event_extension.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import '../constants/pangea_event_types.dart';
import 'message_data_models.dart';
class TokensEvent {
Event event;

View file

@ -1,9 +1,8 @@
import 'dart:convert';
import 'package:fluffychat/pangea/constants/language_keys.dart';
import 'package:fluffychat/pangea/models/speech_to_text_models.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/models/pangea_token_model.dart';
/// this class is contained within a [RepresentationEvent]
/// this event is the child of a [EventTypes.Message]
/// the event has two potential children events -
@ -22,6 +21,9 @@ class PangeaRepresentation {
bool originalSent;
bool originalWritten;
// a representation can be create via speech to text on the original message
SpeechToTextModel? speechToText;
// how do we know which representation was sent by author?
// RepresentationEvent.text == PangeaMessageEvent.event.body
// use: to know whether directUse
@ -49,20 +51,33 @@ class PangeaRepresentation {
required this.text,
required this.originalSent,
required this.originalWritten,
this.speechToText,
});
factory PangeaRepresentation.fromJson(Map<String, dynamic> json) =>
PangeaRepresentation(
langCode: json[_langCodeKey],
text: json[_textKey],
originalSent: json[_originalSentKey] ?? false,
originalWritten: json[_originalWrittenKey] ?? false,
factory PangeaRepresentation.fromJson(Map<String, dynamic> json) {
if (json[_langCodeKey] == LanguageKeys.unknownLanguage) {
ErrorHandler.logError(
e: Exception("Language code cannot be 'unk'"),
s: StackTrace.current,
data: {"rep_content": json},
);
}
return PangeaRepresentation(
langCode: json[_langCodeKey],
text: json[_textKey],
originalSent: json[_originalSentKey] ?? false,
originalWritten: json[_originalWrittenKey] ?? false,
speechToText: json[_speechToTextKey] == null
? null
: SpeechToTextModel.fromJson(json[_speechToTextKey]),
);
}
static const _textKey = "txt";
static const _langCodeKey = "lang";
static const _originalSentKey = "snt";
static const _originalWrittenKey = "wrttn";
static const _speechToTextKey = "stt";
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
@ -70,35 +85,9 @@ class PangeaRepresentation {
data[_langCodeKey] = langCode;
if (originalSent) data[_originalSentKey] = originalSent;
if (originalWritten) data[_originalWrittenKey] = originalWritten;
return data;
}
}
/// this class lives within a [PangeaTokensEvent]
/// it always has a [RepresentationEvent] parent
/// These live as separate event so that anyone can add and edit tokens to
/// representation
class PangeaMessageTokens {
List<PangeaToken> tokens;
PangeaMessageTokens({
required this.tokens,
});
factory PangeaMessageTokens.fromJson(Map<String, dynamic> json) {
return PangeaMessageTokens(
tokens: (jsonDecode(json[_tokensKey] ?? "[]") as Iterable)
.map((e) => PangeaToken.fromJson(e))
.toList()
.cast<PangeaToken>(),
);
}
static const _tokensKey = "tkns";
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data[_tokensKey] = jsonEncode(tokens.map((e) => e.toJson()).toList());
if (speechToText != null) {
data[_speechToTextKey] = speechToText!.toJson();
}
return data;
}
}

View file

@ -1,7 +1,10 @@
import 'dart:convert';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/enum/audio_encoding_enum.dart';
import 'package:fluffychat/pangea/models/pangea_token_model.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
class SpeechToTextAudioConfigModel {
@ -67,21 +70,37 @@ class SpeechToTextRequestModel {
}
}
class WordInfo {
final String word;
class STTToken {
final PangeaToken token;
final Duration? startTime;
final Duration? endTime;
final int? confidence;
WordInfo({
required this.word,
STTToken({
required this.token,
this.startTime,
this.endTime,
this.confidence,
});
factory WordInfo.fromJson(Map<String, dynamic> json) => WordInfo(
word: json['word'],
int get offset => token.text.offset;
int get length => token.text.length;
Color color(BuildContext context) {
if (confidence == null || confidence! > 80) {
return Theme.of(context).brightness == Brightness.dark
? AppConfig.primaryColorLight
: AppConfig.primaryColor;
}
if (confidence! > 50) {
return const Color.fromARGB(255, 184, 142, 43);
}
return Colors.red;
}
factory STTToken.fromJson(Map<String, dynamic> json) => STTToken(
token: PangeaToken.fromJson(json['token']),
startTime: json['start_time'] != null
? Duration(milliseconds: json['start_time'])
: null,
@ -92,7 +111,7 @@ class WordInfo {
);
Map<String, dynamic> toJson() => {
"word": word,
"token": token,
"start_time": startTime?.inMilliseconds,
"end_time": endTime?.inMilliseconds,
"confidence": confidence,
@ -100,27 +119,32 @@ class WordInfo {
}
class Transcript {
final String transcript;
final String text;
final int confidence;
final List<WordInfo> words;
final List<STTToken> sttTokens;
final String langCode;
Transcript({
required this.transcript,
required this.text,
required this.confidence,
required this.words,
required this.sttTokens,
required this.langCode,
});
factory Transcript.fromJson(Map<String, dynamic> json) => Transcript(
transcript: json['transcript'],
text: json['transcript'],
confidence: json['confidence'].toDouble(),
words:
(json['words'] as List).map((e) => WordInfo.fromJson(e)).toList(),
sttTokens: (json['stt_tokens'] as List)
.map((e) => STTToken.fromJson(e))
.toList(),
langCode: json['lang_code'],
);
Map<String, dynamic> toJson() => {
"transcript": transcript,
"transcript": text,
"confidence": confidence,
"words": words.map((e) => e.toJson()).toList(),
"stt_tokens": sttTokens.map((e) => e.toJson()).toList(),
"lang_code": langCode,
};
}
@ -141,15 +165,19 @@ class SpeechToTextResult {
};
}
class SpeechToTextResponseModel {
class SpeechToTextModel {
final List<SpeechToTextResult> results;
SpeechToTextResponseModel({
SpeechToTextModel({
required this.results,
});
factory SpeechToTextResponseModel.fromJson(Map<String, dynamic> json) =>
SpeechToTextResponseModel(
Transcript get transcript => results.first.transcripts.first;
String get langCode => results.first.transcripts.first.langCode;
factory SpeechToTextModel.fromJson(Map<String, dynamic> json) =>
SpeechToTextModel(
results: (json['results'] as List)
.map((e) => SpeechToTextResult.fromJson(e))
.toList(),

View file

@ -0,0 +1,32 @@
import 'dart:convert';
import 'package:fluffychat/pangea/models/pangea_token_model.dart';
/// this class lives within a [PangeaTokensEvent]
/// it always has a [RepresentationEvent] parent
/// These live as separate event so that anyone can add and edit tokens to
/// representation
class PangeaMessageTokens {
List<PangeaToken> tokens;
PangeaMessageTokens({
required this.tokens,
});
factory PangeaMessageTokens.fromJson(Map<String, dynamic> json) {
return PangeaMessageTokens(
tokens: (jsonDecode(json[_tokensKey] ?? "[]") as Iterable)
.map((e) => PangeaToken.fromJson(e))
.toList()
.cast<PangeaToken>(),
);
}
static const _tokensKey = "tkns";
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data[_tokensKey] = jsonEncode(tokens.map((e) => e.toJson()).toList());
return data;
}
}

View file

@ -5,11 +5,11 @@ import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/constants/pangea_event_types.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/enum/construct_type_enum.dart';
import 'package:fluffychat/pangea/models/construct_analytics_event.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/construct_analytics_event.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_representation_event.dart';
import 'package:fluffychat/pangea/models/constructs_analytics_model.dart';
import 'package:fluffychat/pangea/models/pangea_match_model.dart';
import 'package:fluffychat/pangea/models/pangea_message_event.dart';
import 'package:fluffychat/pangea/models/pangea_representation_event.dart';
import 'package:fluffychat/pangea/pages/analytics/base_analytics.dart';
import 'package:fluffychat/utils/date_time_extension.dart';
import 'package:fluffychat/utils/string_color.dart';

View file

@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:flutter/material.dart';
class BotStyle {
static TextStyle text(
@ -25,6 +24,7 @@ class BotStyle {
? AppConfig.primaryColorLight
: AppConfig.primaryColor
: null,
inherit: true,
);
return existingStyle?.merge(botStyle) ?? botStyle;

View file

@ -16,7 +16,7 @@ import 'package:syncfusion_flutter_xlsio/xlsio.dart';
import 'package:universal_html/html.dart' as webFile;
import 'package:fluffychat/pangea/models/class_model.dart';
import 'package:fluffychat/pangea/models/pangea_message_event.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import '../models/choreo_record.dart';

View file

@ -1,8 +1,8 @@
import 'package:fluffychat/pangea/constants/language_keys.dart';
import 'package:fluffychat/pangea/constants/model_keys.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/models/class_model.dart';
import 'package:fluffychat/pangea/models/pangea_message_event.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';

View file

@ -1,6 +1,7 @@
import 'package:fluffychat/pages/chat/events/audio_player.dart';
import 'package:fluffychat/pangea/models/pangea_message_event.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/widgets/chat/toolbar_content_loading_indicator.dart';
import 'package:fluffychat/pangea/widgets/igc/card_error_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -74,17 +75,9 @@ class MessageAudioCardState extends State<MessageAudioCard> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8),
return Container(
child: _isLoading
? SizedBox(
height: 14,
width: 14,
child: CircularProgressIndicator(
strokeWidth: 2.0,
color: Theme.of(context).colorScheme.primary,
),
)
? const ToolbarContentLoadingIndicator()
: localAudioEvent != null || audioFile != null
? Container(
constraints: const BoxConstraints(

View file

@ -1,7 +1,9 @@
import 'package:fluffychat/pangea/models/pangea_message_event.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/models/speech_to_text_models.dart';
import 'package:fluffychat/pangea/utils/bot_style.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/widgets/chat/speech_to_text_score.dart';
import 'package:fluffychat/pangea/widgets/chat/speech_to_text_text.dart';
import 'package:fluffychat/pangea/widgets/chat/toolbar_content_loading_indicator.dart';
import 'package:fluffychat/pangea/widgets/igc/card_error_widget.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
@ -19,7 +21,7 @@ class MessageSpeechToTextCard extends StatefulWidget {
}
class MessageSpeechToTextCardState extends State<MessageSpeechToTextCard> {
SpeechToTextResponseModel? speechToTextResponse;
SpeechToTextModel? speechToTextResponse;
bool _fetchingTranscription = true;
Object? error;
@ -32,9 +34,6 @@ class MessageSpeechToTextCardState extends State<MessageSpeechToTextCard> {
roomID: widget.messageEvent.room.id,
);
String? get transcription => speechToTextResponse
?.results.firstOrNull?.transcripts.firstOrNull?.transcript;
// look for transcription in message event
// if not found, call API to transcribe audio
Future<void> getSpeechToText() async {
@ -43,7 +42,11 @@ class MessageSpeechToTextCardState extends State<MessageSpeechToTextCard> {
throw Exception('Language selection not found');
}
speechToTextResponse ??=
await widget.messageEvent.getSpeechToTextGlobal(l1Code!, l2Code!);
await widget.messageEvent.getSpeechToText(l1Code!, l2Code!);
debugPrint(
'Speech to text transcript: ${speechToTextResponse?.transcript.text}',
);
} catch (e, s) {
error = e;
ErrorHandler.logError(
@ -64,25 +67,23 @@ class MessageSpeechToTextCardState extends State<MessageSpeechToTextCard> {
@override
Widget build(BuildContext context) {
if (!_fetchingTranscription && speechToTextResponse == null) {
if (_fetchingTranscription) {
return const ToolbarContentLoadingIndicator();
}
//done fetchig but not results means some kind of error
if (speechToTextResponse == null) {
return CardErrorWidget(error: error);
}
return Padding(
padding: const EdgeInsets.all(8),
child: _fetchingTranscription
? SizedBox(
height: 14,
width: 14,
child: CircularProgressIndicator(
strokeWidth: 2.0,
color: Theme.of(context).colorScheme.primary,
),
)
: Text(
transcription!,
style: BotStyle.text(context),
),
return Column(
children: [
SpeechToTextText(transcript: speechToTextResponse!.transcript),
const Divider(),
SpeechToTextScoreWidget(
score: speechToTextResponse!.transcript.confidence,
),
],
);
}
}

View file

@ -5,7 +5,7 @@ import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pangea/constants/local.key.dart';
import 'package:fluffychat/pangea/enum/message_mode_enum.dart';
import 'package:fluffychat/pangea/models/pangea_message_event.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/utils/any_state_holder.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/utils/overlay.dart';
@ -264,9 +264,11 @@ class MessageToolbarState extends State<MessageToolbar> {
) ??
true;
autoplay
? updateMode(widget.pangeaMessageEvent.isAudioMessage
? MessageMode.speechToText
: MessageMode.textToSpeech)
? updateMode(
widget.pangeaMessageEvent.isAudioMessage
? MessageMode.speechToText
: MessageMode.textToSpeech,
)
: updateMode(MessageMode.translation);
});
@ -319,7 +321,10 @@ class MessageToolbarState extends State<MessageToolbar> {
duration: FluffyThemes.animationDuration,
child: Column(
children: [
toolbarContent ?? const SizedBox(),
Padding(
padding: const EdgeInsets.all(8.0),
child: toolbarContent ?? const SizedBox(),
),
SizedBox(height: toolbarContent == null ? 0 : 20),
],
),
@ -329,7 +334,7 @@ class MessageToolbarState extends State<MessageToolbar> {
Row(
mainAxisSize: MainAxisSize.min,
children: MessageMode.values.map((mode) {
if ([MessageMode.definition, MessageMode.textToSpeech]
if ([MessageMode.definition, MessageMode.textToSpeech, MessageMode.translation]
.contains(mode) &&
widget.pangeaMessageEvent.isAudioMessage) {
return const SizedBox.shrink();

View file

@ -1,9 +1,10 @@
import 'package:fluffychat/pangea/models/message_data_models.dart';
import 'package:fluffychat/pangea/models/pangea_message_event.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/models/representation_content_model.dart';
import 'package:fluffychat/pangea/repo/full_text_translation_repo.dart';
import 'package:fluffychat/pangea/utils/bot_style.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/widgets/chat/message_text_selection.dart';
import 'package:fluffychat/pangea/widgets/chat/toolbar_content_loading_indicator.dart';
import 'package:fluffychat/pangea/widgets/igc/card_error_widget.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
@ -139,17 +140,9 @@ class MessageTranslationCardState extends State<MessageTranslationCard> {
return const CardErrorWidget();
}
return Padding(
padding: const EdgeInsets.all(8),
return Container(
child: _fetchingRepresentation
? SizedBox(
height: 14,
width: 14,
child: CircularProgressIndicator(
strokeWidth: 2.0,
color: Theme.of(context).colorScheme.primary,
),
)
? const ToolbarContentLoadingIndicator()
: selectionTranslation != null
? Text(
selectionTranslation!,

View file

@ -37,34 +37,31 @@ class MessageUnsubscribedCard extends StatelessWidget {
MatrixState.pAnyState.closeOverlay();
}
return Padding(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
child: Column(
children: [
Text(
style: BotStyle.text(context),
"${L10n.of(context)!.subscribedToUnlockTools} $languageTool",
textAlign: TextAlign.center,
),
const SizedBox(height: 10),
SizedBox(
width: double.infinity,
child: TextButton(
onPressed: onButtonPress,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
(AppConfig.primaryColor).withOpacity(0.1),
),
),
child: Text(
inTrialWindow
? L10n.of(context)!.activateTrial
: L10n.of(context)!.getAccess,
return Column(
children: [
Text(
style: BotStyle.text(context),
"${L10n.of(context)!.subscribedToUnlockTools} $languageTool",
textAlign: TextAlign.center,
),
const SizedBox(height: 10),
SizedBox(
width: double.infinity,
child: TextButton(
onPressed: onButtonPress,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
(AppConfig.primaryColor).withOpacity(0.1),
),
),
child: Text(
inTrialWindow
? L10n.of(context)!.activateTrial
: L10n.of(context)!.getAccess,
),
),
],
),
),
],
);
}
}

View file

@ -1,7 +1,7 @@
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat/events/message_content.dart';
import 'package:fluffychat/pangea/enum/use_type.dart';
import 'package:fluffychat/pangea/models/pangea_message_event.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart';
import 'package:fluffychat/utils/date_time_extension.dart';
import 'package:flutter/material.dart';

View file

@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
class SpeechToTextScoreWidget extends StatefulWidget {
final int score;
const SpeechToTextScoreWidget({super.key, required this.score});
@override
SpeechToTextScoreWidgetState createState() => SpeechToTextScoreWidgetState();
}
class SpeechToTextScoreWidgetState extends State<SpeechToTextScoreWidget> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
transitionBuilder: (Widget child, Animation<double> animation) {
return FadeTransition(opacity: animation, child: child);
},
child: Text(
'Score: ${widget.score}',
key: ValueKey<int>(widget.score),
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
),
);
}
}

View file

@ -0,0 +1,65 @@
import 'package:fluffychat/pangea/models/speech_to_text_models.dart';
import 'package:fluffychat/pangea/utils/bot_style.dart';
import 'package:flutter/material.dart';
class SpeechToTextText extends StatelessWidget {
final Transcript transcript;
const SpeechToTextText({super.key, required this.transcript});
@override
Widget build(BuildContext context) {
return RichText(
text: _buildTranscriptText(context, transcript),
);
}
TextSpan _buildTranscriptText(BuildContext context, Transcript transcript) {
final List<InlineSpan> spans = [];
final String fullText = transcript.text;
int lastEnd = 0;
for (final token in transcript.sttTokens) {
// debugPrint('Token confidence: ${token.confidence}');
// debugPrint('color: ${token.color(context)}');
if (token.offset > lastEnd) {
// Add any plain text before the token
spans.add(
TextSpan(
text: fullText.substring(lastEnd, token.offset),
),
);
// debugPrint('Pre: ${fullText.substring(lastEnd, token.offset)}');
}
spans.add(
TextSpan(
text: fullText.substring(token.offset, token.offset + token.length),
style: BotStyle.text(
context,
existingStyle: TextStyle(color: token.color(context)),
setColor: false,
),
),
);
// debugPrint(
// 'Main: ${fullText.substring(token.offset, token.offset + token.length)}',
// );
lastEnd = token.offset + token.length;
}
if (lastEnd < fullText.length) {
// Add any remaining text after the last token
spans.add(
TextSpan(
text: fullText.substring(lastEnd),
),
);
// debugPrint('Post: ${fullText.substring(lastEnd)}');
}
return TextSpan(children: spans);
}
}

View file

@ -3,7 +3,7 @@ import 'dart:developer';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pages/chat/events/audio_player.dart';
import 'package:fluffychat/pangea/models/pangea_message_event.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/foundation.dart';

View file

@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
class ToolbarContentLoadingIndicator extends StatelessWidget {
const ToolbarContentLoadingIndicator({
super.key,
});
@override
Widget build(BuildContext context) {
return SizedBox(
height: 14,
width: 14,
child: CircularProgressIndicator(
strokeWidth: 2.0,
color: Theme.of(context).colorScheme.primary,
),
);
}
}

View file

@ -4,8 +4,8 @@ import 'dart:ui';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/constants/language_keys.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/models/message_data_models.dart';
import 'package:fluffychat/pangea/models/pangea_message_event.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/models/representation_content_model.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/utils/instructions.dart';
import 'package:fluffychat/pangea/widgets/chat/message_context_menu.dart';
@ -87,8 +87,10 @@ class PangeaRichTextState extends State<PangeaRichText> {
context: context,
langCode: widget.pangeaMessageEvent.messageDisplayLangCode,
)
.onError((error, stackTrace) =>
ErrorHandler.logError(e: error, s: stackTrace))
.onError(
(error, stackTrace) =>
ErrorHandler.logError(e: error, s: stackTrace),
)
.then((event) {
repEvent = event;
widget.toolbarController?.toolbar?.textSelection.setMessageText(

View file

@ -3,7 +3,10 @@ PODS:
- FlutterMacOS
- audio_session (0.0.1):
- FlutterMacOS
- desktop_drop (0.0.1):
- connectivity_plus (0.0.1):
- FlutterMacOS
- ReachabilitySwift
- desktop_lifecycle (0.0.1):
- FlutterMacOS
- device_info_plus (0.0.1):
- FlutterMacOS
@ -13,6 +16,66 @@ PODS:
- FlutterMacOS
- file_selector_macos (0.0.1):
- FlutterMacOS
- Firebase/Analytics (10.18.0):
- Firebase/Core
- Firebase/Core (10.18.0):
- Firebase/CoreOnly
- FirebaseAnalytics (~> 10.18.0)
- Firebase/CoreOnly (10.18.0):
- FirebaseCore (= 10.18.0)
- Firebase/Messaging (10.18.0):
- Firebase/CoreOnly
- FirebaseMessaging (~> 10.18.0)
- firebase_analytics (10.8.0):
- Firebase/Analytics (= 10.18.0)
- firebase_core
- FlutterMacOS
- firebase_core (2.24.2):
- Firebase/CoreOnly (~> 10.18.0)
- FlutterMacOS
- firebase_messaging (14.7.10):
- Firebase/CoreOnly (~> 10.18.0)
- Firebase/Messaging (~> 10.18.0)
- firebase_core
- FlutterMacOS
- FirebaseAnalytics (10.18.0):
- FirebaseAnalytics/AdIdSupport (= 10.18.0)
- FirebaseCore (~> 10.0)
- FirebaseInstallations (~> 10.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
- GoogleUtilities/MethodSwizzler (~> 7.11)
- GoogleUtilities/Network (~> 7.11)
- "GoogleUtilities/NSData+zlib (~> 7.11)"
- nanopb (< 2.30910.0, >= 2.30908.0)
- FirebaseAnalytics/AdIdSupport (10.18.0):
- FirebaseCore (~> 10.0)
- FirebaseInstallations (~> 10.0)
- GoogleAppMeasurement (= 10.18.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
- GoogleUtilities/MethodSwizzler (~> 7.11)
- GoogleUtilities/Network (~> 7.11)
- "GoogleUtilities/NSData+zlib (~> 7.11)"
- nanopb (< 2.30910.0, >= 2.30908.0)
- FirebaseCore (10.18.0):
- FirebaseCoreInternal (~> 10.0)
- GoogleUtilities/Environment (~> 7.12)
- GoogleUtilities/Logger (~> 7.12)
- FirebaseCoreInternal (10.21.0):
- "GoogleUtilities/NSData+zlib (~> 7.8)"
- FirebaseInstallations (10.21.0):
- FirebaseCore (~> 10.0)
- GoogleUtilities/Environment (~> 7.8)
- GoogleUtilities/UserDefaults (~> 7.8)
- PromisesObjC (~> 2.1)
- FirebaseMessaging (10.18.0):
- FirebaseCore (~> 10.0)
- FirebaseInstallations (~> 10.0)
- GoogleDataTransport (~> 9.2)
- GoogleUtilities/AppDelegateSwizzler (~> 7.8)
- GoogleUtilities/Environment (~> 7.8)
- GoogleUtilities/Reachability (~> 7.8)
- GoogleUtilities/UserDefaults (~> 7.8)
- nanopb (< 2.30910.0, >= 2.30908.0)
- flutter_app_badger (1.3.0):
- FlutterMacOS
- flutter_local_notifications (0.0.1):
@ -32,12 +95,63 @@ PODS:
- FMDB/standard (2.7.5)
- geolocator_apple (1.2.0):
- FlutterMacOS
- GoogleAppMeasurement (10.18.0):
- GoogleAppMeasurement/AdIdSupport (= 10.18.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
- GoogleUtilities/MethodSwizzler (~> 7.11)
- GoogleUtilities/Network (~> 7.11)
- "GoogleUtilities/NSData+zlib (~> 7.11)"
- nanopb (< 2.30910.0, >= 2.30908.0)
- GoogleAppMeasurement/AdIdSupport (10.18.0):
- GoogleAppMeasurement/WithoutAdIdSupport (= 10.18.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
- GoogleUtilities/MethodSwizzler (~> 7.11)
- GoogleUtilities/Network (~> 7.11)
- "GoogleUtilities/NSData+zlib (~> 7.11)"
- nanopb (< 2.30910.0, >= 2.30908.0)
- GoogleAppMeasurement/WithoutAdIdSupport (10.18.0):
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
- GoogleUtilities/MethodSwizzler (~> 7.11)
- GoogleUtilities/Network (~> 7.11)
- "GoogleUtilities/NSData+zlib (~> 7.11)"
- nanopb (< 2.30910.0, >= 2.30908.0)
- GoogleDataTransport (9.3.0):
- GoogleUtilities/Environment (~> 7.7)
- nanopb (< 2.30910.0, >= 2.30908.0)
- PromisesObjC (< 3.0, >= 1.2)
- GoogleUtilities/AppDelegateSwizzler (7.12.0):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
- GoogleUtilities/Network
- GoogleUtilities/Environment (7.12.0):
- PromisesObjC (< 3.0, >= 1.2)
- GoogleUtilities/Logger (7.12.0):
- GoogleUtilities/Environment
- GoogleUtilities/MethodSwizzler (7.12.0):
- GoogleUtilities/Logger
- GoogleUtilities/Network (7.12.0):
- GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (7.12.0)"
- GoogleUtilities/Reachability (7.12.0):
- GoogleUtilities/Logger
- GoogleUtilities/UserDefaults (7.12.0):
- GoogleUtilities/Logger
- in_app_purchase_storekit (0.0.1):
- Flutter
- FlutterMacOS
- just_audio (0.0.1):
- FlutterMacOS
- macos_ui (0.1.0):
- FlutterMacOS
- macos_window_utils (1.0.0):
- FlutterMacOS
- nanopb (2.30909.1):
- nanopb/decode (= 2.30909.1)
- nanopb/encode (= 2.30909.1)
- nanopb/decode (2.30909.1)
- nanopb/encode (2.30909.1)
- package_info_plus (0.0.1):
- FlutterMacOS
- pasteboard (0.0.1):
@ -45,8 +159,24 @@ PODS:
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- record_macos (0.2.0):
- PromisesObjC (2.3.1)
- purchases_flutter (5.8.0):
- FlutterMacOS
- PurchasesHybridCommon (= 6.3.0)
- PurchasesHybridCommon (6.3.0):
- RevenueCat (= 4.26.1)
- ReachabilitySwift (5.0.0)
- record_darwin (1.0.0):
- Flutter
- FlutterMacOS
- RevenueCat (4.26.1)
- Sentry/HybridSDK (8.17.2):
- SentryPrivate (= 8.17.2)
- sentry_flutter (0.0.1):
- Flutter
- FlutterMacOS
- Sentry/HybridSDK (= 8.17.2)
- SentryPrivate (8.17.2)
- share_plus (0.0.1):
- FlutterMacOS
- shared_preferences_foundation (0.0.1):
@ -80,11 +210,15 @@ PODS:
DEPENDENCIES:
- appkit_ui_element_colors (from `Flutter/ephemeral/.symlinks/plugins/appkit_ui_element_colors/macos`)
- audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/macos`)
- desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`)
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`)
- desktop_lifecycle (from `Flutter/ephemeral/.symlinks/plugins/desktop_lifecycle/macos`)
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
- dynamic_color (from `Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos`)
- emoji_picker_flutter (from `Flutter/ephemeral/.symlinks/plugins/emoji_picker_flutter/macos`)
- file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`)
- firebase_analytics (from `Flutter/ephemeral/.symlinks/plugins/firebase_analytics/macos`)
- firebase_core (from `Flutter/ephemeral/.symlinks/plugins/firebase_core/macos`)
- firebase_messaging (from `Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos`)
- flutter_app_badger (from `Flutter/ephemeral/.symlinks/plugins/flutter_app_badger/macos`)
- flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`)
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
@ -92,13 +226,16 @@ DEPENDENCIES:
- flutter_webrtc (from `Flutter/ephemeral/.symlinks/plugins/flutter_webrtc/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- geolocator_apple (from `Flutter/ephemeral/.symlinks/plugins/geolocator_apple/macos`)
- in_app_purchase_storekit (from `Flutter/ephemeral/.symlinks/plugins/in_app_purchase_storekit/darwin`)
- just_audio (from `Flutter/ephemeral/.symlinks/plugins/just_audio/macos`)
- macos_ui (from `Flutter/ephemeral/.symlinks/plugins/macos_ui/macos`)
- macos_window_utils (from `Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos`)
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
- pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- record_macos (from `Flutter/ephemeral/.symlinks/plugins/record_macos/macos`)
- purchases_flutter (from `Flutter/ephemeral/.symlinks/plugins/purchases_flutter/macos`)
- record_darwin (from `Flutter/ephemeral/.symlinks/plugins/record_darwin/macos`)
- sentry_flutter (from `Flutter/ephemeral/.symlinks/plugins/sentry_flutter/macos`)
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`)
@ -111,7 +248,23 @@ DEPENDENCIES:
SPEC REPOS:
trunk:
- Firebase
- FirebaseAnalytics
- FirebaseCore
- FirebaseCoreInternal
- FirebaseInstallations
- FirebaseMessaging
- FMDB
- GoogleAppMeasurement
- GoogleDataTransport
- GoogleUtilities
- nanopb
- PromisesObjC
- PurchasesHybridCommon
- ReachabilitySwift
- RevenueCat
- Sentry
- SentryPrivate
- SQLCipher
- WebRTC-SDK
@ -120,8 +273,10 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/appkit_ui_element_colors/macos
audio_session:
:path: Flutter/ephemeral/.symlinks/plugins/audio_session/macos
desktop_drop:
:path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos
connectivity_plus:
:path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos
desktop_lifecycle:
:path: Flutter/ephemeral/.symlinks/plugins/desktop_lifecycle/macos
device_info_plus:
:path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos
dynamic_color:
@ -130,6 +285,12 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/emoji_picker_flutter/macos
file_selector_macos:
:path: Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos
firebase_analytics:
:path: Flutter/ephemeral/.symlinks/plugins/firebase_analytics/macos
firebase_core:
:path: Flutter/ephemeral/.symlinks/plugins/firebase_core/macos
firebase_messaging:
:path: Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos
flutter_app_badger:
:path: Flutter/ephemeral/.symlinks/plugins/flutter_app_badger/macos
flutter_local_notifications:
@ -144,6 +305,8 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral
geolocator_apple:
:path: Flutter/ephemeral/.symlinks/plugins/geolocator_apple/macos
in_app_purchase_storekit:
:path: Flutter/ephemeral/.symlinks/plugins/in_app_purchase_storekit/darwin
just_audio:
:path: Flutter/ephemeral/.symlinks/plugins/just_audio/macos
macos_ui:
@ -156,8 +319,12 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/pasteboard/macos
path_provider_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
record_macos:
:path: Flutter/ephemeral/.symlinks/plugins/record_macos/macos
purchases_flutter:
:path: Flutter/ephemeral/.symlinks/plugins/purchases_flutter/macos
record_darwin:
:path: Flutter/ephemeral/.symlinks/plugins/record_darwin/macos
sentry_flutter:
:path: Flutter/ephemeral/.symlinks/plugins/sentry_flutter/macos
share_plus:
:path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos
shared_preferences_foundation:
@ -180,11 +347,21 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
appkit_ui_element_colors: 39bb2d80be3f19b152ccf4c70d5bbe6cba43d74a
audio_session: dea1f41890dbf1718f04a56f1d6150fd50039b72
desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898
connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747
desktop_lifecycle: a600c10e12fe033c7be9078f2e929b8241f2c1e3
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
dynamic_color: 2eaa27267de1ca20d879fbd6e01259773fb1670f
emoji_picker_flutter: 533634326b1c5de9a181ba14b9758e6dfe967a20
file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9
Firebase: 414ad272f8d02dfbf12662a9d43f4bba9bec2a06
firebase_analytics: 687a47ef9af9c5a8a9fc612c100987f843d0a281
firebase_core: a74ee8b3ab5f91ae6b73f4913eaca996c24458b6
firebase_messaging: 1298099739b30786ab5be9fdbfe00b2019065745
FirebaseAnalytics: 4d310b35c48eaa4a058ddc04bdca6bdb5dc0fe80
FirebaseCore: 2322423314d92f946219c8791674d2f3345b598f
FirebaseCoreInternal: 43c1788eaeee9d1b97caaa751af567ce11010d00
FirebaseInstallations: 390ea1d10a4d02b20c965cbfd527ee9b3b412acb
FirebaseMessaging: 9bc34a98d2e0237e1b121915120d4d48ddcf301e
flutter_app_badger: 55a64b179f8438e89d574320c77b306e327a1730
flutter_local_notifications: 3805ca215b2fb7f397d78b66db91f6a747af52e4
flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea
@ -193,25 +370,38 @@ SPEC CHECKSUMS:
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
geolocator_apple: 821be05bbdb1b49500e029ebcbf2d6acf2dfb966
GoogleAppMeasurement: 70ce9aa438cff1cfb31ea3e660bcc67734cb716e
GoogleDataTransport: 57c22343ab29bc686febbf7cbb13bad167c2d8fe
GoogleUtilities: 0759d1a57ebb953965c2dfe0ba4c82e95ccc2e34
in_app_purchase_storekit: 9e9931234f0adcf71ae323f8c83785b96030edf1
just_audio: 9b67ca7b97c61cfc9784ea23cd8cc55eb226d489
macos_ui: 6229a8922cd97bafb7d9636c8eb8dfb0744183ca
macos_window_utils: 933f91f64805e2eb91a5bd057cf97cd097276663
nanopb: d4d75c12cd1316f4a64e3c6963f879ecd4b5e0d5
package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce
pasteboard: 9b69dba6fedbb04866be632205d532fe2f6b1d99
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
record_macos: 937889e0f2a7a12b6fc14e97a3678e5a18943de6
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4
purchases_flutter: 36a8c669148173e56f19dfc20df724bc734ab475
PurchasesHybridCommon: 5ee5e13fe009876850a03f52bb0349b6fa91d976
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
record_darwin: 1f6619f2abac4d1ca91d3eeab038c980d76f1517
RevenueCat: 4e8899a69fd57180ef166237d1eb670023be05de
Sentry: 64a9f9c3637af913adcf53deced05bbe452d1410
sentry_flutter: 57912cf425e09398bdf47f38842a1fcb9836f1be
SentryPrivate: 024c6fed507ac39ae98e6d087034160f942920d5
share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea
sqflite_sqlcipher: d1ac7c60596e4d624d9757e3ec96e9cfafb734d6
SQLCipher: 905b145f65f349f26da9e60a19901ad24adcd381
url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
video_compress: c896234f100791b5fef7f049afa38f6d2ef7b42f
video_player_avfoundation: e9e6f9cae7d7a6d9b43519b0aab382bca60fcfd1
video_player_avfoundation: 02011213dab73ae3687df27ce441fbbcc82b5579
wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269
WebRTC-SDK: c24d2a6c9f571f2ed42297cb8ffba9557093142b
window_to_front: 4cdc24ddd8461ad1a55fa06286d6a79d8b29e8d8
PODFILE CHECKSUM: d0975b16fbdecb73b109d8fbc88aa77ffe4c7a8d
COCOAPODS: 1.14.3
COCOAPODS: 1.15.2

View file

@ -829,7 +829,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -1683,7 +1683,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -2537,7 +2537,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -3391,7 +3391,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -4245,7 +4245,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -5068,7 +5068,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -5922,7 +5922,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -6776,7 +6776,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -7615,7 +7615,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -8438,7 +8438,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -9292,7 +9292,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -10146,7 +10146,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -11000,7 +11000,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -11854,7 +11854,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -12677,7 +12677,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -13531,7 +13531,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -14385,7 +14385,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -15226,7 +15226,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -16080,7 +16080,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -16934,7 +16934,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -17788,7 +17788,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -18627,7 +18627,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -19481,7 +19481,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -20335,7 +20335,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -21189,7 +21189,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -22043,7 +22043,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -22897,7 +22897,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -23751,7 +23751,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -24605,7 +24605,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -25459,7 +25459,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -26282,7 +26282,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -27136,7 +27136,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -27990,7 +27990,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -28817,7 +28817,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -29671,7 +29671,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -30525,7 +30525,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -31379,7 +31379,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -32233,7 +32233,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -33087,7 +33087,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -33941,7 +33941,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -34780,7 +34780,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -35619,7 +35619,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -36473,7 +36473,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -37312,7 +37312,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",
@ -38166,7 +38166,7 @@
"more",
"translationTooltip",
"audioTooltip",
"transcriptTooltip",
"speechToTextTooltip",
"certifyAge",
"kickBotWarning",
"joinToView",