mirror of
				https://gitlab.com/comunic/comunicmobile
				synced 2025-11-04 12:14:11 +00:00 
			
		
		
		
	Can copy message to clipboard
This commit is contained in:
		@@ -1,3 +1,4 @@
 | 
			
		||||
import 'package:clipboard/clipboard.dart';
 | 
			
		||||
import 'package:comunic/models/conversation.dart';
 | 
			
		||||
import 'package:comunic/models/conversation_message.dart';
 | 
			
		||||
import 'package:comunic/models/user.dart';
 | 
			
		||||
@@ -13,7 +14,12 @@ import 'package:flutter/material.dart';
 | 
			
		||||
///
 | 
			
		||||
/// @author Pierre HUBERT
 | 
			
		||||
 | 
			
		||||
enum _MenuChoices { DELETE, REQUEST_UPDATE_CONTENT }
 | 
			
		||||
enum _MenuChoices {
 | 
			
		||||
  COPY_URL,
 | 
			
		||||
  COPY_MESSAGE,
 | 
			
		||||
  DELETE,
 | 
			
		||||
  REQUEST_UPDATE_CONTENT
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef OnRequestMessageUpdate = void Function(ConversationMessage);
 | 
			
		||||
typedef OnRequestMessageDelete = void Function(ConversationMessage);
 | 
			
		||||
@@ -60,6 +66,18 @@ class ConversationMessageTile extends StatelessWidget {
 | 
			
		||||
          width: 35.0,
 | 
			
		||||
        ),
 | 
			
		||||
        itemBuilder: (c) => [
 | 
			
		||||
 | 
			
		||||
          PopupMenuItem(
 | 
			
		||||
            enabled: (message.message?.content ?? "") != "",
 | 
			
		||||
            value: _MenuChoices.COPY_MESSAGE,
 | 
			
		||||
            child: Text(tr("Copy message")),
 | 
			
		||||
          ),
 | 
			
		||||
 | 
			
		||||
          PopupMenuItem(
 | 
			
		||||
            enabled: message.file != null,
 | 
			
		||||
            value: _MenuChoices.COPY_URL,
 | 
			
		||||
            child: Text(tr("Copy URL")),
 | 
			
		||||
          ),
 | 
			
		||||
          // Update message content
 | 
			
		||||
          PopupMenuItem(
 | 
			
		||||
            enabled: message.isOwner,
 | 
			
		||||
@@ -249,6 +267,14 @@ class ConversationMessageTile extends StatelessWidget {
 | 
			
		||||
  /// Process menu choice
 | 
			
		||||
  void _menuOptionSelected(_MenuChoices value) {
 | 
			
		||||
    switch (value) {
 | 
			
		||||
      case _MenuChoices.COPY_MESSAGE:
 | 
			
		||||
        FlutterClipboard.copy(message.message.content);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case _MenuChoices.COPY_URL:
 | 
			
		||||
        FlutterClipboard.copy(message.file.url);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case _MenuChoices.REQUEST_UPDATE_CONTENT:
 | 
			
		||||
        onRequestMessageUpdate(message);
 | 
			
		||||
        break;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,135 +0,0 @@
 | 
			
		||||
import 'dart:io';
 | 
			
		||||
 | 
			
		||||
import 'package:audioplayers/audioplayers.dart';
 | 
			
		||||
import 'package:comunic/utils/date_utils.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
 | 
			
		||||
/// Audio player widget
 | 
			
		||||
///
 | 
			
		||||
/// @author Pierre Hubert
 | 
			
		||||
 | 
			
		||||
class AudioPlayerWidget extends StatefulWidget {
 | 
			
		||||
  final File file;
 | 
			
		||||
 | 
			
		||||
  const AudioPlayerWidget(this.file);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  _AudioPlayerWidgetState createState() => _AudioPlayerWidgetState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _AudioPlayerWidgetState extends State<AudioPlayerWidget> {
 | 
			
		||||
  AudioPlayer _player;
 | 
			
		||||
 | 
			
		||||
  Duration _mediaDuration;
 | 
			
		||||
  Duration _mediaPosition;
 | 
			
		||||
 | 
			
		||||
  double get _max => _mediaDuration?.inMilliseconds?.toDouble() ?? 0.0;
 | 
			
		||||
 | 
			
		||||
  double get _value => _mediaPosition?.inMilliseconds?.toDouble() ?? 0.0;
 | 
			
		||||
 | 
			
		||||
  bool get _playing =>
 | 
			
		||||
      _player != null && _player.state == AudioPlayerState.PLAYING;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void dispose() {
 | 
			
		||||
    super.dispose();
 | 
			
		||||
 | 
			
		||||
    if (_player != null) _player.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Material(
 | 
			
		||||
      textStyle: TextStyle(color: Colors.white),
 | 
			
		||||
      color: Colors.transparent,
 | 
			
		||||
      child: Column(
 | 
			
		||||
        mainAxisAlignment: MainAxisAlignment.center,
 | 
			
		||||
        children: <Widget>[
 | 
			
		||||
          Spacer(),
 | 
			
		||||
          Icon(Icons.audiotrack, color: Colors.white),
 | 
			
		||||
          Spacer(),
 | 
			
		||||
          Slider(
 | 
			
		||||
            value: _value,
 | 
			
		||||
            onChanged: (newValue) =>
 | 
			
		||||
                _player.seek(Duration(milliseconds: newValue.toInt())),
 | 
			
		||||
            max: _max,
 | 
			
		||||
            activeColor: Colors.white,
 | 
			
		||||
            min: 0,
 | 
			
		||||
          ),
 | 
			
		||||
          Spacer(),
 | 
			
		||||
          Row(
 | 
			
		||||
            mainAxisAlignment: MainAxisAlignment.center,
 | 
			
		||||
            children: <Widget>[
 | 
			
		||||
              Spacer(),
 | 
			
		||||
              Text(formatDuration(_mediaPosition ?? Duration())),
 | 
			
		||||
              Spacer(),
 | 
			
		||||
              _AudioButton(
 | 
			
		||||
                  icon: Icons.play_arrow, onTap: _play, visible: !_playing),
 | 
			
		||||
              _AudioButton(icon: Icons.pause, onTap: _pause, visible: _playing),
 | 
			
		||||
              _AudioButton(icon: Icons.stop, onTap: _stop, visible: _playing),
 | 
			
		||||
              Spacer(),
 | 
			
		||||
              _playing ? Container() : Text(formatDuration(_mediaDuration ?? Duration())),
 | 
			
		||||
              Spacer(),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
          Spacer(),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void _play() async {
 | 
			
		||||
    if (_player == null) {
 | 
			
		||||
      _player = AudioPlayer();
 | 
			
		||||
 | 
			
		||||
      _player.onDurationChanged.listen((newDuration) {
 | 
			
		||||
        setState(() => _mediaDuration = newDuration);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      _player.onAudioPositionChanged.listen((newDuration) {
 | 
			
		||||
        setState(() => _mediaPosition = newDuration);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      _player.onPlayerStateChanged.listen((event) => setState(() {}));
 | 
			
		||||
 | 
			
		||||
      _player.onSeekComplete.listen((event) => setState(() {}));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (_player.state != AudioPlayerState.PAUSED)
 | 
			
		||||
      _player.play(widget.file.absolute.path, isLocal: true);
 | 
			
		||||
    else
 | 
			
		||||
      _player.resume();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void _pause() async {
 | 
			
		||||
    _player.pause();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void _stop() {
 | 
			
		||||
    _player.stop();
 | 
			
		||||
    _player.seek(Duration());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _AudioButton extends StatelessWidget {
 | 
			
		||||
  final IconData icon;
 | 
			
		||||
  final void Function() onTap;
 | 
			
		||||
  final bool visible;
 | 
			
		||||
 | 
			
		||||
  const _AudioButton({
 | 
			
		||||
    Key key,
 | 
			
		||||
    @required this.icon,
 | 
			
		||||
    @required this.onTap,
 | 
			
		||||
    @required this.visible,
 | 
			
		||||
  })  : assert(icon != null),
 | 
			
		||||
        assert(onTap != null),
 | 
			
		||||
        assert(visible != null),
 | 
			
		||||
        super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    if (!visible) return Container();
 | 
			
		||||
 | 
			
		||||
    return IconButton(icon: Icon(icon, color: Colors.white), onPressed: onTap);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
/// @author Pierre Hubert
 | 
			
		||||
import 'package:comunic/models/conversation_message.dart';
 | 
			
		||||
import 'package:comunic/ui/widgets/network_image_widget.dart';
 | 
			
		||||
import 'package:filesize/filesize.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:url_launcher/url_launcher.dart';
 | 
			
		||||
 | 
			
		||||
@@ -60,6 +61,11 @@ class _ConversationFileWidgetState extends State<ConversationFileWidget> {
 | 
			
		||||
                  Icon(file.icon, color: Colors.white),
 | 
			
		||||
                  Spacer(),
 | 
			
		||||
                  Text(file.name, textAlign: TextAlign.center),
 | 
			
		||||
                  Spacer(),
 | 
			
		||||
                  Text(
 | 
			
		||||
                    filesize(file.size),
 | 
			
		||||
                    style: TextStyle(fontSize: 10),
 | 
			
		||||
                  ),
 | 
			
		||||
                  Spacer(flex: 2),
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user