mirror of
				https://gitlab.com/comunic/comunicmobile
				synced 2025-11-04 04:04:18 +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.dart';
 | 
				
			||||||
import 'package:comunic/models/conversation_message.dart';
 | 
					import 'package:comunic/models/conversation_message.dart';
 | 
				
			||||||
import 'package:comunic/models/user.dart';
 | 
					import 'package:comunic/models/user.dart';
 | 
				
			||||||
@@ -13,7 +14,12 @@ import 'package:flutter/material.dart';
 | 
				
			|||||||
///
 | 
					///
 | 
				
			||||||
/// @author Pierre HUBERT
 | 
					/// @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 OnRequestMessageUpdate = void Function(ConversationMessage);
 | 
				
			||||||
typedef OnRequestMessageDelete = void Function(ConversationMessage);
 | 
					typedef OnRequestMessageDelete = void Function(ConversationMessage);
 | 
				
			||||||
@@ -60,6 +66,18 @@ class ConversationMessageTile extends StatelessWidget {
 | 
				
			|||||||
          width: 35.0,
 | 
					          width: 35.0,
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        itemBuilder: (c) => [
 | 
					        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
 | 
					          // Update message content
 | 
				
			||||||
          PopupMenuItem(
 | 
					          PopupMenuItem(
 | 
				
			||||||
            enabled: message.isOwner,
 | 
					            enabled: message.isOwner,
 | 
				
			||||||
@@ -249,6 +267,14 @@ class ConversationMessageTile extends StatelessWidget {
 | 
				
			|||||||
  /// Process menu choice
 | 
					  /// Process menu choice
 | 
				
			||||||
  void _menuOptionSelected(_MenuChoices value) {
 | 
					  void _menuOptionSelected(_MenuChoices value) {
 | 
				
			||||||
    switch (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:
 | 
					      case _MenuChoices.REQUEST_UPDATE_CONTENT:
 | 
				
			||||||
        onRequestMessageUpdate(message);
 | 
					        onRequestMessageUpdate(message);
 | 
				
			||||||
        break;
 | 
					        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
 | 
					/// @author Pierre Hubert
 | 
				
			||||||
import 'package:comunic/models/conversation_message.dart';
 | 
					import 'package:comunic/models/conversation_message.dart';
 | 
				
			||||||
import 'package:comunic/ui/widgets/network_image_widget.dart';
 | 
					import 'package:comunic/ui/widgets/network_image_widget.dart';
 | 
				
			||||||
 | 
					import 'package:filesize/filesize.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:url_launcher/url_launcher.dart';
 | 
					import 'package:url_launcher/url_launcher.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -60,6 +61,11 @@ class _ConversationFileWidgetState extends State<ConversationFileWidget> {
 | 
				
			|||||||
                  Icon(file.icon, color: Colors.white),
 | 
					                  Icon(file.icon, color: Colors.white),
 | 
				
			||||||
                  Spacer(),
 | 
					                  Spacer(),
 | 
				
			||||||
                  Text(file.name, textAlign: TextAlign.center),
 | 
					                  Text(file.name, textAlign: TextAlign.center),
 | 
				
			||||||
 | 
					                  Spacer(),
 | 
				
			||||||
 | 
					                  Text(
 | 
				
			||||||
 | 
					                    filesize(file.size),
 | 
				
			||||||
 | 
					                    style: TextStyle(fontSize: 10),
 | 
				
			||||||
 | 
					                  ),
 | 
				
			||||||
                  Spacer(flex: 2),
 | 
					                  Spacer(flex: 2),
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
              ),
 | 
					              ),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,6 +50,13 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dartlang.org"
 | 
					      url: "https://pub.dartlang.org"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.2.0-nullsafety.1"
 | 
					    version: "1.2.0-nullsafety.1"
 | 
				
			||||||
 | 
					  clipboard:
 | 
				
			||||||
 | 
					    dependency: "direct main"
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: clipboard
 | 
				
			||||||
 | 
					      url: "https://pub.dartlang.org"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "0.1.2+8"
 | 
				
			||||||
  clock:
 | 
					  clock:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -97,6 +97,9 @@ dependencies:
 | 
				
			|||||||
  # Format file size
 | 
					  # Format file size
 | 
				
			||||||
  filesize: ^1.0.4
 | 
					  filesize: ^1.0.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Copy content to clipboard
 | 
				
			||||||
 | 
					  clipboard: ^0.1.2+8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dev_dependencies:
 | 
					dev_dependencies:
 | 
				
			||||||
  flutter_test:
 | 
					  flutter_test:
 | 
				
			||||||
    sdk: flutter
 | 
					    sdk: flutter
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user