From 1318629997bb1013af16aeb757352c0b4f91799e Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 23 Jul 2025 15:51:05 -0400 Subject: [PATCH] chore: prevent any user interaction with unsent audio message bubble (#3550) --- lib/pages/chat/events/audio_player.dart | 478 ++++++++++++------------ 1 file changed, 235 insertions(+), 243 deletions(-) diff --git a/lib/pages/chat/events/audio_player.dart b/lib/pages/chat/events/audio_player.dart index d14a1ab68..edfa12263 100644 --- a/lib/pages/chat/events/audio_player.dart +++ b/lib/pages/chat/events/audio_player.dart @@ -164,14 +164,6 @@ class AudioPlayerState extends State { } void _onButtonTap() async { - // #Pangea - // https://github.com/pangeachat/client/issues/3458 - if (widget.event != null && !widget.event!.status.isSent) { - widget.chatController.showToolbar(widget.event!); - return; - } - // Pangea# - WidgetsBinding.instance.addPostFrameCallback((_) { ScaffoldMessenger.of(matrix.context).clearMaterialBanners(); }); @@ -441,13 +433,6 @@ class AudioPlayerState extends State { final duration = Duration(milliseconds: durationInt); _durationString = duration.minuteSecondString; } - - // #Pangea - if (widget.autoplay && - (widget.event == null || widget.event!.status.isSent)) { - _onButtonTap(); - } - // Pangea# } @override @@ -470,258 +455,265 @@ class AudioPlayerState extends State { final fileDescription = widget.event?.fileDescription; // Pangea# - return StreamBuilder( - stream: audioPlayer == null - ? null - : StreamGroup.merge([ - audioPlayer.positionStream.asBroadcastStream(), - audioPlayer.playerStateStream.asBroadcastStream(), - ]), - builder: (context, _) { - final maxPosition = - audioPlayer?.duration?.inMilliseconds.toDouble() ?? 1.0; - var currentPosition = - audioPlayer?.position.inMilliseconds.toDouble() ?? 0.0; - if (currentPosition > maxPosition) currentPosition = maxPosition; - - final wavePosition = - (currentPosition / maxPosition) * AudioPlayerWidget.wavesCount; - - final statusText = audioPlayer == null - ? _durationString ?? '00:00' - : audioPlayer.position.minuteSecondString; - return Padding( - padding: const EdgeInsets.all(12.0), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: FluffyThemes.columnWidth, - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - width: buttonSize, - height: buttonSize, - child: status == AudioPlayerStatus.downloading - ? CircularProgressIndicator( - strokeWidth: 2, - color: widget.color, - ) - : InkWell( - borderRadius: BorderRadius.circular(64), - // #Pangea - // onLongPress: () => - // widget.event.saveFile(context), - onLongPress: () => - widget.event?.saveFile(context), - // Pangea# - onTap: _onButtonTap, - child: Material( - color: widget.color.withAlpha(64), + // #Pangea + // return StreamBuilder( + return AbsorbPointer( + absorbing: widget.event != null && !widget.event!.status.isSent, + child: StreamBuilder( + // Pangea# + stream: audioPlayer == null + ? null + : StreamGroup.merge([ + audioPlayer.positionStream.asBroadcastStream(), + audioPlayer.playerStateStream.asBroadcastStream(), + ]), + builder: (context, _) { + final maxPosition = + audioPlayer?.duration?.inMilliseconds.toDouble() ?? 1.0; + var currentPosition = + audioPlayer?.position.inMilliseconds.toDouble() ?? 0.0; + if (currentPosition > maxPosition) currentPosition = maxPosition; + + final wavePosition = (currentPosition / maxPosition) * + AudioPlayerWidget.wavesCount; + + final statusText = audioPlayer == null + ? _durationString ?? '00:00' + : audioPlayer.position.minuteSecondString; + return Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: FluffyThemes.columnWidth, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: buttonSize, + height: buttonSize, + child: status == AudioPlayerStatus.downloading + ? CircularProgressIndicator( + strokeWidth: 2, + color: widget.color, + ) + : InkWell( borderRadius: BorderRadius.circular(64), - child: Icon( - audioPlayer?.playing == true && - audioPlayer?.isAtEndPosition == - false - ? Icons.pause_outlined - : Icons.play_arrow_outlined, - color: widget.color, + // #Pangea + // onLongPress: () => + // widget.event.saveFile(context), + onLongPress: () => + widget.event?.saveFile(context), + // Pangea# + onTap: _onButtonTap, + child: Material( + color: widget.color.withAlpha(64), + borderRadius: BorderRadius.circular(64), + child: Icon( + audioPlayer?.playing == true && + audioPlayer?.isAtEndPosition == + false + ? Icons.pause_outlined + : Icons.play_arrow_outlined, + color: widget.color, + ), ), ), - ), - ), - const SizedBox(width: 8), - Expanded( - child: Stack( - children: [ - if (waveform != null) - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - child: Row( - children: [ - for (var i = 0; - i < AudioPlayerWidget.wavesCount; - i++) - Expanded( - child: Container( - height: 32, - alignment: Alignment.center, + ), + const SizedBox(width: 8), + Expanded( + child: Stack( + children: [ + if (waveform != null) + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + ), + child: Row( + children: [ + for (var i = 0; + i < AudioPlayerWidget.wavesCount; + i++) + Expanded( child: Container( - margin: - const EdgeInsets.symmetric( - horizontal: 1, + height: 32, + alignment: Alignment.center, + child: Container( + margin: + const EdgeInsets.symmetric( + horizontal: 1, + ), + decoration: BoxDecoration( + color: i < wavePosition + ? widget.color + : widget.color + .withAlpha(128), + borderRadius: + BorderRadius.circular(64), + ), + height: + 32 * (waveform[i] / 1024), ), - decoration: BoxDecoration( - color: i < wavePosition - ? widget.color - : widget.color - .withAlpha(128), - borderRadius: - BorderRadius.circular(64), - ), - height: 32 * (waveform[i] / 1024), ), ), - ), - ], + ], + ), ), - ), - SizedBox( - height: 32, - child: Slider( - // #Pangea - // thumbColor: widget.event.senderId == - // widget.event.room.client.userID - // ? theme.colorScheme.onPrimary - // : theme.colorScheme.primary, - thumbColor: widget.senderId == - Matrix.of(context).client.userID - ? widget.color - : theme.colorScheme.onSurface, - // Pangea# - activeColor: waveform == null - ? widget.color - : Colors.transparent, - inactiveColor: waveform == null - ? widget.color.withAlpha(128) - : Colors.transparent, - max: maxPosition, - value: currentPosition, - onChanged: (position) => audioPlayer == null - ? _onButtonTap() - : audioPlayer.seek( - Duration( - milliseconds: position.round(), + SizedBox( + height: 32, + child: Slider( + // #Pangea + // thumbColor: widget.event.senderId == + // widget.event.room.client.userID + // ? theme.colorScheme.onPrimary + // : theme.colorScheme.primary, + thumbColor: widget.senderId == + Matrix.of(context).client.userID + ? widget.color + : theme.colorScheme.onSurface, + // Pangea# + activeColor: waveform == null + ? widget.color + : Colors.transparent, + inactiveColor: waveform == null + ? widget.color.withAlpha(128) + : Colors.transparent, + max: maxPosition, + value: currentPosition, + onChanged: (position) => audioPlayer == null + ? _onButtonTap() + : audioPlayer.seek( + Duration( + milliseconds: position.round(), + ), ), - ), + ), ), - ), - ], + ], + ), ), - ), - const SizedBox(width: 8), - // #Pangea - // SizedBox( - // width: 36, - // child: Text( - // statusText, - // style: TextStyle( - // color: widget.color, - // fontSize: 12, - // ), - // ), - // ), - Text( - statusText, - style: TextStyle( - color: widget.color, - fontSize: 12, + const SizedBox(width: 8), + // #Pangea + // SizedBox( + // width: 36, + // child: Text( + // statusText, + // style: TextStyle( + // color: widget.color, + // fontSize: 12, + // ), + // ), + // ), + Text( + statusText, + style: TextStyle( + color: widget.color, + fontSize: 12, + ), ), - ), - // Pangea# - const SizedBox(width: 8), - // #Pangea - Material( - color: widget.color.withAlpha(64), - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), - child: InkWell( + // Pangea# + const SizedBox(width: 8), + // #Pangea + Material( + color: widget.color.withAlpha(64), borderRadius: BorderRadius.circular(AppConfig.borderRadius), - onTap: _toggleSpeed, - child: SizedBox( - width: 32, - height: 20, - child: Center( - child: Text( - '${audioPlayer?.speed.toString() ?? playbackSpeed}x', - style: TextStyle( - color: widget.color, - fontSize: 9, + child: InkWell( + borderRadius: + BorderRadius.circular(AppConfig.borderRadius), + onTap: _toggleSpeed, + child: SizedBox( + width: 32, + height: 20, + child: Center( + child: Text( + '${audioPlayer?.speed.toString() ?? playbackSpeed}x', + style: TextStyle( + color: widget.color, + fontSize: 9, + ), ), ), ), ), ), - ), - // AnimatedCrossFade( - // firstChild: Padding( - // padding: const EdgeInsets.only(right: 8.0), - // child: Icon( - // Icons.mic_none_outlined, - // color: widget.color, - // ), - // ), - // secondChild: Material( - // color: widget.color.withAlpha(64), - // borderRadius: - // BorderRadius.circular(AppConfig.borderRadius), - // child: InkWell( - // borderRadius: - // BorderRadius.circular(AppConfig.borderRadius), - // onTap: _toggleSpeed, - // child: SizedBox( - // width: 32, - // height: 20, - // child: Center( - // child: Text( - // '${audioPlayer?.speed.toString()}x', - // style: TextStyle( - // color: widget.color, - // fontSize: 9, - // ), - // ), - // ), - // ), - // ), - // ), - // alignment: Alignment.center, - // crossFadeState: audioPlayer == null - // ? CrossFadeState.showFirst - // : CrossFadeState.showSecond, - // duration: FluffyThemes.animationDuration, - // ), - // Pangea# - ], - ), - ), - if (fileDescription != null) ...[ - const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 8, + // AnimatedCrossFade( + // firstChild: Padding( + // padding: const EdgeInsets.only(right: 8.0), + // child: Icon( + // Icons.mic_none_outlined, + // color: widget.color, + // ), + // ), + // secondChild: Material( + // color: widget.color.withAlpha(64), + // borderRadius: + // BorderRadius.circular(AppConfig.borderRadius), + // child: InkWell( + // borderRadius: + // BorderRadius.circular(AppConfig.borderRadius), + // onTap: _toggleSpeed, + // child: SizedBox( + // width: 32, + // height: 20, + // child: Center( + // child: Text( + // '${audioPlayer?.speed.toString()}x', + // style: TextStyle( + // color: widget.color, + // fontSize: 9, + // ), + // ), + // ), + // ), + // ), + // ), + // alignment: Alignment.center, + // crossFadeState: audioPlayer == null + // ? CrossFadeState.showFirst + // : CrossFadeState.showSecond, + // duration: FluffyThemes.animationDuration, + // ), + // Pangea# + ], ), - child: Linkify( - text: fileDescription, - textScaleFactor: - MediaQuery.textScalerOf(context).scale(1), - style: TextStyle( - color: widget.color, - fontSize: widget.fontSize, + ), + if (fileDescription != null) ...[ + const SizedBox(height: 8), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 8, ), - options: const LinkifyOptions(humanize: false), - linkStyle: TextStyle( - color: widget.linkColor, - fontSize: widget.fontSize, - decoration: TextDecoration.underline, - decorationColor: widget.linkColor, + child: Linkify( + text: fileDescription, + textScaleFactor: + MediaQuery.textScalerOf(context).scale(1), + style: TextStyle( + color: widget.color, + fontSize: widget.fontSize, + ), + options: const LinkifyOptions(humanize: false), + linkStyle: TextStyle( + color: widget.linkColor, + fontSize: widget.fontSize, + decoration: TextDecoration.underline, + decorationColor: widget.linkColor, + ), + onOpen: (url) => + UrlLauncher(context, url.url).launchUrl(), ), - onOpen: (url) => - UrlLauncher(context, url.url).launchUrl(), ), - ), + ], ], - ], - ), - ); - }, + ), + ); + }, + ), ); }, );