diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2b993ea05..7e75afa95 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,20 +1,22 @@ -version: 2 -updates: - - package-ecosystem: "pub" - directory: "/" - schedule: - interval: "daily" - allow: - - dependency-name: "*" - commit-message: - prefix: "build: " - include: "scope" - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "daily" - allow: - - dependency-name: "*" - commit-message: - prefix: "build: " - include: "scope" +# #Pangea +# version: 2 +# updates: +# - package-ecosystem: "pub" +# directory: "/" +# schedule: +# interval: "daily" +# allow: +# - dependency-name: "*" +# commit-message: +# prefix: "build: " +# include: "scope" +# - package-ecosystem: "github-actions" +# directory: "/" +# schedule: +# interval: "daily" +# allow: +# - dependency-name: "*" +# commit-message: +# prefix: "build: " +# include: "scope" +# Pangea# diff --git a/.github/workflows/auto_pull_request.yaml b/.github/workflows/auto_pull_request.yaml deleted file mode 100644 index cf6bb228c..000000000 --- a/.github/workflows/auto_pull_request.yaml +++ /dev/null @@ -1,41 +0,0 @@ -name: Auto Pull Request - -#on: -# schedule: -# - cron: '0 0 * * 0' # Run at midnight (00:00) every Sunday - -on: - push: - branches: - - auto-pr # Change this to match your main branch name - -jobs: - auto_pull_request: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Test - run: echo ${{ github.head_ref }}.${{ github.sha }} - - - name: Pull changes from FluffyChat - run: | - git config --global user.email "${{ vars.CI_EMAIL }}" - git config --global user.name "${{ vars.CI_USERNAME }}" - git remote add fluffychat https://github.com/krille-chan/fluffychat - git fetch fluffychat main - git merge --no-edit fluffychat/main --allow-unrelated-histories - - - name: Push changes - run: | - git push origin HEAD:main-update-fluffy-automatic - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v3 - with: - token: ${{ secrets.GH_TOKEN }} - title: Updated fork with new fluffy changes - body: | - This is an automatic PR created by GitHub Actions. - branch: main diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml deleted file mode 100644 index aa7a13d70..000000000 --- a/.github/workflows/build-ios.yml +++ /dev/null @@ -1,79 +0,0 @@ -name: build-ios -on: - workflow_call: - inputs: - screenshot: - type: string - required: true - ipa: - description: 'Run IPA build' - type: string - required: true - workflow_dispatch: - inputs: - screenshot: - description: 'Run screenshot build' - type: choice - options: ['true', 'false'] - required: true - ipa: - description: 'Run IPA build' - type: choice - options: ['true', 'false'] - required: true - - -jobs: - build-ios: - runs-on: macos-latest - timeout-minutes: 20 - defaults: - run: - working-directory: ios - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - run: 'echo "$API_KEY" | base64 --decode > AuthKey.p8' - shell: bash - env: - API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }} - - - run: bundle install - - - run: bundle exec fastlane versioning - - - name: Flutter - uses: subosito/flutter-action@v2 - with: - cache: true - cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:' - cache-path: "${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:" - - run: flutter build ios --simulator --target=integration_test/screenshot_test.dart - if: ${{ inputs.screenshot == 'true' }} - - - name: Archive integration ipa - if: ${{ inputs.screenshot == 'true' }} - uses: actions/upload-artifact@v4 - with: - name: app-simulator-build - path: build/ios/iphonesimulator/Runner.app - if-no-files-found: error - retention-days: 3 - - # Build ios Release - - run: flutter build ios --release --config-only --no-codesign --target=lib/main.dart - if: ${{ inputs.ipa == 'true' }} - - - run: bundle exec fastlane build - if: ${{ inputs.ipa == 'true' }} - - - name: Archive ipa - if: ${{ inputs.ipa == 'true' }} - uses: actions/upload-artifact@v4 - with: - name: Runner.ipa - path: ios/Runner.ipa - if-no-files-found: error - retention-days: 3 \ No newline at end of file diff --git a/.github/workflows/dart_format.yaml b/.github/workflows/dart_format.yaml deleted file mode 100644 index 5e1a142c5..000000000 --- a/.github/workflows/dart_format.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# name: Dart Code Formatter - -# on: -# pull_request: -# push: -# branches: main - -# jobs: -# format: -# runs-on: ubuntu-latest - -# steps: -# - name: Checkout code -# uses: actions/checkout@v3 -# with: -# ref: ${{ github.head_ref }} - -# - run: cat .github/workflows/versions.env >> $GITHUB_ENV -# - uses: subosito/flutter-action@v2 -# with: -# flutter-version: ${{ env.FLUTTER_VERSION }} -# cache: true - -# - name: Auto-format Dart code -# run: | -# dart format lib/ test/ -# dart run import_sorter:main --no-comments -# if ! git diff --exit-code; then -# git config user.name "github-actions[bot]" -# git config user.email "41898282+github-actions[bot]@users.noreply.github.com" -# git add . -# git commit -m "generated" -# git push -# fi - -# - name: Check for unformatted files -# if: ${{ failure() }} -# run: | -# echo "Code was formatted. Please verify the changes in the PR." diff --git a/.github/workflows/integrate.yaml b/.github/workflows/integrate.yaml index c4307fadf..dc4df96cf 100644 --- a/.github/workflows/integrate.yaml +++ b/.github/workflows/integrate.yaml @@ -1,107 +1,105 @@ -name: Pull Request Workflow +# #Pangea +# name: Pull Request Workflow -on: - pull_request: - merge_group: +# on: +# pull_request: +# merge_group: -jobs: - code_tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - run: ./scripts/generate-locale-config.sh - - run: git diff --exit-code - - run: cat .github/workflows/versions.env >> $GITHUB_ENV - - uses: subosito/flutter-action@v2 - with: - flutter-version: ${{ env.FLUTTER_VERSION }} - cache: true - - run: flutter pub get - - run: flutter gen-l10n - - name: Check formatting - run: dart format lib/ test/ --set-exit-if-changed - - name: Check import formatting - run: dart run import_sorter:main --no-comments --exit-if-changed - - name: Check license compliance - run: dart run license_checker check-licenses -c licenses.yaml --problematic - - run: flutter analyze - # #Pangea - Commented out the following lines, we already have fcm enabled by default - # - name: Apply google services patch - # run: git apply ./scripts/enable-android-google-services.patch - # Pangea# - - run: flutter analyze - - run: flutter test +# jobs: +# code_tests: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - run: ./scripts/generate-locale-config.sh +# - run: git diff --exit-code +# - run: cat .github/workflows/versions.env >> $GITHUB_ENV +# - uses: subosito/flutter-action@v2 +# with: +# flutter-version: ${{ env.FLUTTER_VERSION }} +# cache: true +# - run: flutter pub get +# - run: flutter gen-l10n +# - name: Check formatting +# run: dart format lib/ test/ --set-exit-if-changed +# - name: Check import formatting +# run: dart run import_sorter:main --no-comments --exit-if-changed +# - name: Check license compliance +# run: dart run license_checker check-licenses -c licenses.yaml --problematic +# - run: flutter analyze +# - name: Apply google services patch +# run: git apply ./scripts/enable-android-google-services.patch +# - run: flutter analyze +# - run: flutter test - build_apk: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - run: cat .github/workflows/versions.env >> $GITHUB_ENV - - uses: actions/setup-java@v4 - with: - java-version: ${{ env.JAVA_VERSION }} - distribution: "zulu" - - uses: subosito/flutter-action@v2 - with: - flutter-version: ${{ env.FLUTTER_VERSION }} - cache: false - - run: flutter pub get - - name: Free Disk Space (Ubuntu) - uses: jlumbroso/free-disk-space@main - with: - # this might remove tools that are actually needed, - # if set to "true" but frees about 6 GB - tool-cache: false - android: false - - run: flutter build apk --debug +# build_apk: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - run: cat .github/workflows/versions.env >> $GITHUB_ENV +# - uses: actions/setup-java@v4 +# with: +# java-version: ${{ env.JAVA_VERSION }} +# distribution: "zulu" +# - uses: subosito/flutter-action@v2 +# with: +# flutter-version: ${{ env.FLUTTER_VERSION }} +# cache: false +# - run: flutter pub get +# - name: Free Disk Space (Ubuntu) +# uses: jlumbroso/free-disk-space@main +# with: +# # this might remove tools that are actually needed, +# # if set to "true" but frees about 6 GB +# tool-cache: false +# android: false +# - run: flutter build apk --debug - build_web: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - run: cat .github/workflows/versions.env >> $GITHUB_ENV - - uses: subosito/flutter-action@v2 - with: - flutter-version: ${{ env.FLUTTER_VERSION }} - cache: false - - run: flutter pub get - - name: Prepare web - run: ./scripts/prepare-web.sh - - run: flutter build web +# build_web: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - run: cat .github/workflows/versions.env >> $GITHUB_ENV +# - uses: subosito/flutter-action@v2 +# with: +# flutter-version: ${{ env.FLUTTER_VERSION }} +# cache: false +# - run: flutter pub get +# - name: Prepare web +# run: ./scripts/prepare-web.sh +# - run: flutter build web - # #Pangea - # commented out because we do not build Pangea Chat to linux - # build_debug_linux: - # strategy: - # matrix: - # arch: [ x64, arm64 ] - # runs-on: ${{ matrix.arch == 'arm64' && 'self-hosted' || 'ubuntu-latest'}} - # steps: - # - uses: actions/checkout@v4 - # - run: cat .github/workflows/versions.env >> $GITHUB_ENV - # - name: Install dependencies - # run: sudo apt-get update && sudo apt-get install git wget curl clang cmake ninja-build pkg-config libgtk-3-dev libblkid-dev liblzma-dev libjsoncpp-dev cmake-data libsecret-1-dev libsecret-1-0 librhash0 libssl-dev libwebkit2gtk-4.1-dev -y - # - name: Install Flutter - # run: | - # git clone --branch ${{ env.FLUTTER_VERSION }} https://github.com/flutter/flutter.git - # ./flutter/bin/flutter doctor - # - run: ./flutter/bin/flutter pub get - # - run: ./flutter/bin/flutter build linux --target-platform linux-${{ matrix.arch }} - # Pangea# +# commented out because we do not build Pangea Chat to linux +# build_debug_linux: +# strategy: +# matrix: +# arch: [ x64, arm64 ] +# runs-on: ${{ matrix.arch == 'arm64' && 'self-hosted' || 'ubuntu-latest'}} +# steps: +# - uses: actions/checkout@v4 +# - run: cat .github/workflows/versions.env >> $GITHUB_ENV +# - name: Install dependencies +# run: sudo apt-get update && sudo apt-get install git wget curl clang cmake ninja-build pkg-config libgtk-3-dev libblkid-dev liblzma-dev libjsoncpp-dev cmake-data libsecret-1-dev libsecret-1-0 librhash0 libssl-dev libwebkit2gtk-4.1-dev -y +# - name: Install Flutter +# run: | +# git clone --branch ${{ env.FLUTTER_VERSION }} https://github.com/flutter/flutter.git +# ./flutter/bin/flutter doctor +# - run: ./flutter/bin/flutter pub get +# - run: ./flutter/bin/flutter build linux --target-platform linux-${{ matrix.arch }} - build_debug_ios: - runs-on: macos-15 - steps: - - uses: actions/checkout@v4 - - run: cat .github/workflows/versions.env >> $GITHUB_ENV - - uses: subosito/flutter-action@v2 - with: - flutter-version: ${{ env.FLUTTER_VERSION }} - cache: true - - name: Setup Xcode version - uses: maxim-lobanov/setup-xcode@v1.6.0 - with: - xcode-version: latest - - run: brew install sqlcipher - - run: flutter pub get - - run: flutter build ipa --no-codesign +# build_debug_ios: +# runs-on: macos-15 +# steps: +# - uses: actions/checkout@v4 +# - run: cat .github/workflows/versions.env >> $GITHUB_ENV +# - uses: subosito/flutter-action@v2 +# with: +# flutter-version: ${{ env.FLUTTER_VERSION }} +# cache: true +# - name: Setup Xcode version +# uses: maxim-lobanov/setup-xcode@v1.6.0 +# with: +# xcode-version: latest +# - run: brew install sqlcipher +# - run: flutter pub get +# - run: flutter build ipa --no-codesign +# Pangea# diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml index 1999c49d3..e9d809e60 100644 --- a/.github/workflows/manual.yml +++ b/.github/workflows/manual.yml @@ -77,13 +77,4 @@ jobs: bundle install bundle update fastlane bundle exec fastlane deploy_internal_test - cd .. - - deploy_ios_testflight: # stashed on old.yml - environment: - name: ${{ inputs.environment }} - env: - WEB_APP_ENV: ${{ vars.WEB_APP_ENV }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 \ No newline at end of file + cd .. \ No newline at end of file diff --git a/.github/workflows/matrix_notification.yaml b/.github/workflows/matrix_notification.yaml deleted file mode 100644 index 1d6dd8085..000000000 --- a/.github/workflows/matrix_notification.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# name: Matrix Notification - -# on: -# issues: -# types: [ opened ] -# issue_comment: -# types: [ created ] - -# jobs: -# notify: -# runs-on: ubuntu-latest - -# steps: -# - name: Send Matrix Notification -# env: -# MATRIX_URL: https://matrix.janian.de/_matrix/client/v3/rooms/${{ secrets.MATRIX_MANAGEMENT_ROOM }}/send/m.room.message -# run: | -# if [ "${{ github.event.action }}" == "opened" ]; then -# PAYLOAD="{\"msgtype\": \"m.notice\", \"body\": \"New Issue from ${{ github.event.issue.user.login }}\\n${{ github.event.issue.title }}\\n\\n${{ github.event.issue.body }}\\n\\nURL: ${{ github.event.issue.html_url }}\"}" -# elif [ "${{ github.event.action }}" == "created" ]; then -# PAYLOAD="{\"msgtype\": \"m.notice\", \"body\": \"New Comment from ${{ github.event.comment.user.login }}\\n\\n${{ github.event.comment.body }}\\n\\nURL: ${{ github.event.comment.html_url }}\"}" -# fi -# curl -X POST -H "Authorization: Bearer ${{ secrets.MATRIX_BOT_TOKEN }}" \ -# -H "Content-Type: application/json" \ -# -d "$PAYLOAD" \ -# $MATRIX_URL diff --git a/.github/workflows/old.yml b/.github/workflows/old.yml deleted file mode 100644 index 62d57416b..000000000 --- a/.github/workflows/old.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Old Release Workflow - -on: - push: - branches: - - master-unused - -concurrency: - group: release_workflow - cancel-in-progress: true - -jobs: - deploy_ios_internal: - runs-on: macos-latest - environment: staging - steps: - - uses: actions/checkout@v4 - - run: cat ../.github/workflows/versions.env >> $GITHUB_ENV - - - name: Flutter - uses: subosito/flutter-action@v2 - with: - cache: true - cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:' - cache-path: "${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:" - - # Build ios Release - - run: flutter build ios --release --config-only --no-codesign --target=lib/main.dart - - - name: Deploy ios - run: | - mkdir -p build/ios - cp build/app/outputs/bundle/release/app-release.aab build/ios/ - cd ios - bundle install - bundle update fastlane - cd .. - - name: Execute fastlane signing - env: - APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }} - APP_STORE_CONNECT_API_KEY_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY_ID }} - APP_STORE_CONNECT_API_KEY_IS_KEY_CONTENT_BASE64: ${{ secrets.APP_STORE_CONNECT_API_KEY_IS_KEY_CONTENT_BASE64 }} - APP_STORE_CONNECT_API_KEY_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY }} - run: bundle exec fastlane ios beta \ No newline at end of file diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index b6751456d..51705779a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -142,36 +142,39 @@ jobs: asset_path: build/app/outputs/flutter-apk/app-release.apk asset_name: pangeachat.apk asset_content_type: application/vnd.android.package-archive - build_linux: - strategy: - matrix: - arch: [ x64 ] - runs-on: ubuntu-latest - needs: create_release - steps: - - uses: actions/checkout@v4 - - run: cat .github/workflows/versions.env >> $GITHUB_ENV - - name: Install dependencies - run: sudo apt-get update && sudo apt-get install curl clang cmake ninja-build pkg-config libgtk-3-dev libblkid-dev liblzma-dev libjsoncpp-dev cmake-data libsecret-1-dev libsecret-1-0 librhash0 libssl-dev libwebkit2gtk-4.1-dev -y - - name: Install dependencies for audio-player - run: sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev - - name: Install Flutter - run: | - git clone --branch ${{ env.FLUTTER_VERSION }} https://github.com/flutter/flutter.git - ./flutter/bin/flutter doctor - - run: ./flutter/bin/flutter pub get - - run: ./flutter/bin/flutter build linux --target-platform linux-${{ matrix.arch }} - - name: Create archive - run: tar -czf pangeachat-linux-${{ matrix.arch }}.tar.gz -C build/linux/${{ matrix.arch }}/release/bundle/ . - - name: Upload to release - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.PAGES_DEPLOY_TOKEN }} - with: - upload_url: ${{ needs.create_release.outputs.upload_url }} - asset_path: pangeachat-linux-${{ matrix.arch }}.tar.gz - asset_name: pangeachat-linux-${{ matrix.arch }}.tar.gz - asset_content_type: application/gzip + + # #Pangea + # build_linux: + # strategy: + # matrix: + # arch: [ x64 ] + # runs-on: ubuntu-latest + # needs: create_release + # steps: + # - uses: actions/checkout@v4 + # - run: cat .github/workflows/versions.env >> $GITHUB_ENV + # - name: Install dependencies + # run: sudo apt-get update && sudo apt-get install curl clang cmake ninja-build pkg-config libgtk-3-dev libblkid-dev liblzma-dev libjsoncpp-dev cmake-data libsecret-1-dev libsecret-1-0 librhash0 libssl-dev libwebkit2gtk-4.1-dev -y + # - name: Install dependencies for audio-player + # run: sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev + # - name: Install Flutter + # run: | + # git clone --branch ${{ env.FLUTTER_VERSION }} https://github.com/flutter/flutter.git + # ./flutter/bin/flutter doctor + # - run: ./flutter/bin/flutter pub get + # - run: ./flutter/bin/flutter build linux --target-platform linux-${{ matrix.arch }} + # - name: Create archive + # run: tar -czf pangeachat-linux-${{ matrix.arch }}.tar.gz -C build/linux/${{ matrix.arch }}/release/bundle/ . + # - name: Upload to release + # uses: actions/upload-release-asset@v1 + # env: + # GITHUB_TOKEN: ${{ secrets.PAGES_DEPLOY_TOKEN }} + # with: + # upload_url: ${{ needs.create_release.outputs.upload_url }} + # asset_path: pangeachat-linux-${{ matrix.arch }}.tar.gz + # asset_name: pangeachat-linux-${{ matrix.arch }}.tar.gz + # asset_content_type: application/gzip + # Pangea# deploy_web: runs-on: ubuntu-latest diff --git a/.github/workflows/upload-release-ios.yml b/.github/workflows/upload-release-ios.yml deleted file mode 100644 index aa70cd9ca..000000000 --- a/.github/workflows/upload-release-ios.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: upload-release-ios -on: - workflow_call: - inputs: - new_release: - required: true - type: string - description: "The new release version number" - new_release_notes: - required: true - type: string - description: "The release notes for the new release" - -jobs: - build: - runs-on: macos-latest - timeout-minutes: 10 - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Download app - uses: actions/download-artifact@v4 - with: - name: Runner.ipa - path: ios/ - - - run: 'echo "$API_KEY" | base64 --decode > AuthKey.p8' - shell: bash - env: - API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }} - - - run: bundle install - - - run: bundle exec fastlane upload_testflight - env: - RELEASE_NOTES: ${{ inputs.new_release_notes }} - - run: bundle exec fastlane upload_metadata_app_store \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index b85b940fe..00ebd337f 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -5031,5 +5031,6 @@ "type": "int" } } - } + }, + "failedToFetchTranscription": "Failed to fetch transcription" } \ No newline at end of file diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart index 06b203f0f..268abbcac 100644 --- a/lib/pages/chat/events/html_message.dart +++ b/lib/pages/chat/events/html_message.dart @@ -185,6 +185,23 @@ class HtmlMessage extends StatelessWidget { result.add(html.substring(lastEnd)); // Remaining text after last tag } + final replyTagIndex = result.indexWhere( + (string) => string.contains(''), + ); + if (replyTagIndex != -1) { + final closingReplyTagIndex = result.indexWhere( + (string) => string.contains(''), + replyTagIndex, + ); + if (closingReplyTagIndex != -1) { + result.replaceRange( + replyTagIndex, + closingReplyTagIndex + 1, + [result.sublist(replyTagIndex, closingReplyTagIndex + 1).join()], + ); + } + } + for (final PangeaToken token in tokens ?? []) { final String tokenText = token.text.content; final substringIndex = result.indexWhere( diff --git a/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart b/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart index 0e7c69f74..e6c91bad8 100644 --- a/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart +++ b/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart @@ -9,6 +9,7 @@ import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_repo.dart'; import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_request.dart'; import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart'; +import 'package:fluffychat/widgets/hover_builder.dart'; import 'package:fluffychat/widgets/matrix.dart'; class PhoneticTranscriptionWidget extends StatefulWidget { @@ -32,51 +33,61 @@ class PhoneticTranscriptionWidget extends StatefulWidget { class _PhoneticTranscriptionWidgetState extends State { - late Future _transcriptionFuture; - bool _hovering = false; bool _isPlaying = false; bool _isLoading = false; - late final StreamSubscription _loadingChoreoSubscription; + Object? _error; + + String? _transcription; @override void initState() { super.initState(); - _transcriptionFuture = _fetchTranscription(); - _loadingChoreoSubscription = - TtsController.loadingChoreoStream.stream.listen((val) { - if (mounted) setState(() => _isLoading = val); - }); + _fetchTranscription(); } - @override - void dispose() { - TtsController.stop(); - _loadingChoreoSubscription.cancel(); - super.dispose(); - } + Future _fetchTranscription() async { + try { + setState(() { + _isLoading = true; + _error = null; + _transcription = null; + }); - Future _fetchTranscription() async { - if (MatrixState.pangeaController.languageController.userL1 == null) { + if (MatrixState.pangeaController.languageController.userL1 == null) { + ErrorHandler.logError( + e: Exception('User L1 is not set'), + data: { + 'text': widget.text, + 'textLanguageCode': widget.textLanguage.langCode, + }, + ); + _error = Exception('User L1 is not set'); + return; + } + final req = PhoneticTranscriptionRequest( + arc: LanguageArc( + l1: MatrixState.pangeaController.languageController.userL1!, + l2: widget.textLanguage, + ), + content: PangeaTokenText.fromString(widget.text), + // arc can be omitted for default empty map + ); + final res = await PhoneticTranscriptionRepo.get(req); + _transcription = res.phoneticTranscriptionResult.phoneticTranscription + .first.phoneticL1Transcription.content; + } catch (e, s) { + _error = e; ErrorHandler.logError( - e: Exception('User L1 is not set'), + e: e, + s: s, data: { 'text': widget.text, 'textLanguageCode': widget.textLanguage.langCode, }, ); - return widget.text; // Fallback to original text if no L1 is set + } finally { + if (mounted) setState(() => _isLoading = false); } - final req = PhoneticTranscriptionRequest( - arc: LanguageArc( - l1: MatrixState.pangeaController.languageController.userL1!, - l2: widget.textLanguage, - ), - content: PangeaTokenText.fromString(widget.text), - // arc can be omitted for default empty map - ); - final res = await PhoneticTranscriptionRepo.get(req); - return res.phoneticTranscriptionResult.phoneticTranscription.first - .phoneticL1Transcription.content; } Future _handleAudioTap(BuildContext context) async { @@ -101,55 +112,66 @@ class _PhoneticTranscriptionWidgetState @override Widget build(BuildContext context) { - return FutureBuilder( - future: _transcriptionFuture, - builder: (context, snapshot) { - final transcription = snapshot.data ?? ''; - return MouseRegion( - onEnter: (_) => setState(() => _hovering = true), - onExit: (_) => setState(() => _hovering = false), - child: GestureDetector( - onTap: () => _handleAudioTap(context), - child: AnimatedContainer( - duration: const Duration(milliseconds: 150), - decoration: BoxDecoration( - color: _hovering - ? Colors.grey.withAlpha((0.2 * 255).round()) - : Colors.transparent, - borderRadius: BorderRadius.circular(6), - ), - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ + return HoverBuilder( + builder: (context, hovering) { + return GestureDetector( + onTap: () => _handleAudioTap(context), + child: AnimatedContainer( + duration: const Duration(milliseconds: 150), + decoration: BoxDecoration( + color: hovering + ? Colors.grey.withAlpha((0.2 * 255).round()) + : Colors.transparent, + borderRadius: BorderRadius.circular(6), + ), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (_error != null) + Row( + spacing: 8.0, + children: [ + Icon( + Icons.error_outline, + size: widget.iconSize ?? 24, + color: Theme.of(context).colorScheme.error, + ), + Text( + L10n.of(context).failedToFetchTranscription, + style: widget.style, + ), + ], + ) + else if (_isLoading || _transcription == null) + const SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator.adaptive(), + ) + else Flexible( child: Text( - "/${transcription.isNotEmpty ? transcription : widget.text}/", + "/$_transcription/", style: widget.style ?? Theme.of(context).textTheme.bodyMedium, ), ), - const SizedBox(width: 8), + const SizedBox(width: 8), + if (_transcription != null && _error == null) Tooltip( message: _isPlaying ? L10n.of(context).stop : L10n.of(context).playAudio, - child: _isLoading - ? const SizedBox( - width: 16, - height: 16, - child: CircularProgressIndicator(strokeWidth: 3), - ) - : Icon( - _isPlaying ? Icons.pause_outlined : Icons.volume_up, - size: widget.iconSize ?? 24, - color: _isPlaying - ? Theme.of(context).colorScheme.primary - : Theme.of(context).iconTheme.color, - ), + child: Icon( + _isPlaying ? Icons.pause_outlined : Icons.volume_up, + size: widget.iconSize ?? 24, + color: _isPlaying + ? Theme.of(context).colorScheme.primary + : Theme.of(context).iconTheme.color, + ), ), - ], - ), + ], ), ), );