mirror of
https://gitlab.com/comunic/comunicmobile
synced 2024-11-22 12:59:21 +00:00
Can copy message to clipboard
This commit is contained in:
parent
8705aa1b0d
commit
217111e3fd
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user