D1APP-53 direct (waiting for waveform descussoin)
This commit is contained in:
parent
2743e46634
commit
627e6ee2df
|
|
@ -1,3 +1,7 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:just_waveform/just_waveform.dart';
|
||||||
|
|
||||||
import 'radar_attachment.dart';
|
import 'radar_attachment.dart';
|
||||||
|
|
||||||
class MessageData {
|
class MessageData {
|
||||||
|
|
@ -8,6 +12,8 @@ class MessageData {
|
||||||
final bool readed;
|
final bool readed;
|
||||||
final String createdAt;
|
final String createdAt;
|
||||||
final RadarAttachment? radar;
|
final RadarAttachment? radar;
|
||||||
|
final File? audioFile;
|
||||||
|
final Waveform? waveform;
|
||||||
|
|
||||||
const MessageData({
|
const MessageData({
|
||||||
required this.id,
|
required this.id,
|
||||||
|
|
@ -17,6 +23,8 @@ class MessageData {
|
||||||
required this.text,
|
required this.text,
|
||||||
required this.audio,
|
required this.audio,
|
||||||
required this.radar,
|
required this.radar,
|
||||||
|
required this.waveform,
|
||||||
|
this.audioFile,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory MessageData.fromJson(Map<String, dynamic> json) => MessageData(
|
factory MessageData.fromJson(Map<String, dynamic> json) => MessageData(
|
||||||
|
|
@ -26,6 +34,16 @@ class MessageData {
|
||||||
writedByAdmin: json['writedByAdmin'],
|
writedByAdmin: json['writedByAdmin'],
|
||||||
readed: json['readed'],
|
readed: json['readed'],
|
||||||
createdAt: json['createdAt'],
|
createdAt: json['createdAt'],
|
||||||
|
waveform: json['waveForm'] != null
|
||||||
|
? Waveform(
|
||||||
|
version: json['version'],
|
||||||
|
flags: json['flags'],
|
||||||
|
sampleRate: json['sampleRate'],
|
||||||
|
samplesPerPixel: json['samplesPerPixel'],
|
||||||
|
length: json['length'],
|
||||||
|
data: json['data'],
|
||||||
|
)
|
||||||
|
: null,
|
||||||
radar: json['radar'] == null
|
radar: json['radar'] == null
|
||||||
? null
|
? null
|
||||||
: RadarAttachment.fromJson(json['radar'] as Map<String, dynamic>),
|
: RadarAttachment.fromJson(json['radar'] as Map<String, dynamic>),
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,13 @@ import 'dart:io';
|
||||||
|
|
||||||
import 'package:didvan/models/enums.dart';
|
import 'package:didvan/models/enums.dart';
|
||||||
import 'package:didvan/models/message_data/message_data.dart';
|
import 'package:didvan/models/message_data/message_data.dart';
|
||||||
|
import 'package:didvan/models/message_data/radar_attachment.dart';
|
||||||
import 'package:didvan/providers/core_provider.dart';
|
import 'package:didvan/providers/core_provider.dart';
|
||||||
import 'package:didvan/services/network/request.dart';
|
import 'package:didvan/services/network/request.dart';
|
||||||
import 'package:didvan/services/network/request_helper.dart';
|
import 'package:didvan/services/network/request_helper.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_vibrate/flutter_vibrate.dart';
|
import 'package:flutter_vibrate/flutter_vibrate.dart';
|
||||||
|
import 'package:just_waveform/just_waveform.dart';
|
||||||
import 'package:record/record.dart';
|
import 'package:record/record.dart';
|
||||||
|
|
||||||
class DirectState extends CoreProvier {
|
class DirectState extends CoreProvier {
|
||||||
|
|
@ -15,7 +17,10 @@ class DirectState extends CoreProvier {
|
||||||
late final int typeId;
|
late final int typeId;
|
||||||
final Map<String, List<int>> dailyMessages = {};
|
final Map<String, List<int>> dailyMessages = {};
|
||||||
|
|
||||||
|
String? text;
|
||||||
|
RadarAttachment? replyRadar;
|
||||||
File? recordedFile;
|
File? recordedFile;
|
||||||
|
Waveform? waveform;
|
||||||
|
|
||||||
bool isRecording = false;
|
bool isRecording = false;
|
||||||
|
|
||||||
|
|
@ -27,14 +32,7 @@ class DirectState extends CoreProvier {
|
||||||
final messageDatas = service.result['messages'];
|
final messageDatas = service.result['messages'];
|
||||||
for (var i = 0; i < messageDatas.length; i++) {
|
for (var i = 0; i < messageDatas.length; i++) {
|
||||||
messages.add(MessageData.fromJson(messageDatas[i]));
|
messages.add(MessageData.fromJson(messageDatas[i]));
|
||||||
final createdAt = messages.last.createdAt.split('T').first;
|
_addToDailyGrouped();
|
||||||
if (!dailyMessages.containsKey(createdAt)) {
|
|
||||||
dailyMessages.addAll({
|
|
||||||
createdAt: [messages.last.id]
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
dailyMessages[createdAt]!.add(messages.last.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
appState = AppState.idle;
|
appState = AppState.idle;
|
||||||
return;
|
return;
|
||||||
|
|
@ -58,7 +56,7 @@ class DirectState extends CoreProvier {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> stopRecording(bool sendImidiately) async {
|
Future<void> stopRecording({required bool sendImidiately}) async {
|
||||||
final path = await _recorder.stop();
|
final path = await _recorder.stop();
|
||||||
isRecording = false;
|
isRecording = false;
|
||||||
if (path == null) {
|
if (path == null) {
|
||||||
|
|
@ -78,5 +76,45 @@ class DirectState extends CoreProvier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> sendMessage() async {}
|
void _addToDailyGrouped() {
|
||||||
|
final createdAt = messages.last.createdAt.split('T').first;
|
||||||
|
if (!dailyMessages.containsKey(createdAt)) {
|
||||||
|
dailyMessages.addAll({
|
||||||
|
createdAt: [messages.last.id]
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
dailyMessages[createdAt]!.add(messages.last.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> sendMessage() async {
|
||||||
|
if (text == null || text!.isEmpty && recordedFile == null) return;
|
||||||
|
final body = {};
|
||||||
|
if (text != null) {
|
||||||
|
body.addAll({'text': text});
|
||||||
|
messages.add(
|
||||||
|
MessageData(
|
||||||
|
id: 0,
|
||||||
|
writedByAdmin: false,
|
||||||
|
readed: false,
|
||||||
|
createdAt: DateTime.now().toString(),
|
||||||
|
text: text,
|
||||||
|
audio: null,
|
||||||
|
audioFile: recordedFile,
|
||||||
|
radar: replyRadar,
|
||||||
|
waveform: waveform,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_addToDailyGrouped();
|
||||||
|
if (replyRadar != null) {
|
||||||
|
body.addAll({'radarId': replyRadar!.id});
|
||||||
|
}
|
||||||
|
text = null;
|
||||||
|
recordedFile = null;
|
||||||
|
notifyListeners();
|
||||||
|
final service =
|
||||||
|
RequestService(RequestHelper.sendDirectMessage(typeId), body: body);
|
||||||
|
await service.post();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@ import 'package:didvan/config/design_config.dart';
|
||||||
import 'package:didvan/config/theme_data.dart';
|
import 'package:didvan/config/theme_data.dart';
|
||||||
import 'package:didvan/models/message_data/message_data.dart';
|
import 'package:didvan/models/message_data/message_data.dart';
|
||||||
import 'package:didvan/pages/home/direct/direct_state.dart';
|
import 'package:didvan/pages/home/direct/direct_state.dart';
|
||||||
|
import 'package:didvan/pages/home/widgets/audio_visualizer.dart';
|
||||||
import 'package:didvan/utils/date_time.dart';
|
import 'package:didvan/utils/date_time.dart';
|
||||||
|
import 'package:didvan/widgets/didvan/divider.dart';
|
||||||
import 'package:didvan/widgets/didvan/text.dart';
|
import 'package:didvan/widgets/didvan/text.dart';
|
||||||
import 'package:didvan/widgets/skeleton_image.dart';
|
import 'package:didvan/widgets/skeleton_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
@ -15,19 +17,18 @@ class Message extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Padding(
|
final firstMessageOfGroupId = context
|
||||||
padding: EdgeInsets.only(
|
.read<DirectState>()
|
||||||
right: message.writedByAdmin ? 20 : 0,
|
.dailyMessages[message.createdAt.split('T').first]!
|
||||||
left: !message.writedByAdmin ? 20 : 0,
|
.last;
|
||||||
),
|
return Column(
|
||||||
child: Column(
|
crossAxisAlignment: message.writedByAdmin
|
||||||
children: [
|
? CrossAxisAlignment.end
|
||||||
if (message.id ==
|
: CrossAxisAlignment.start,
|
||||||
context
|
children: [
|
||||||
.read<DirectState>()
|
if (message.id == firstMessageOfGroupId)
|
||||||
.dailyMessages[message.createdAt.split('T').first]!
|
Center(
|
||||||
.last)
|
child: Container(
|
||||||
Container(
|
|
||||||
margin: const EdgeInsets.only(bottom: 12),
|
margin: const EdgeInsets.only(bottom: 12),
|
||||||
padding: const EdgeInsets.all(4),
|
padding: const EdgeInsets.all(4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
|
@ -42,23 +43,45 @@ class Message extends StatelessWidget {
|
||||||
: Theme.of(context).colorScheme.black,
|
: Theme.of(context).colorScheme.black,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_MessageContainer(
|
|
||||||
isAttachment: false,
|
|
||||||
writedByAdmin: message.writedByAdmin,
|
|
||||||
child:
|
|
||||||
message.text != null ? DidvanText(message.text!) : Container(),
|
|
||||||
),
|
),
|
||||||
if (message.radar != null)
|
Padding(
|
||||||
DidvanText(
|
padding: EdgeInsets.only(
|
||||||
'لینک به مطلب زیر:',
|
right: message.writedByAdmin ? 20 : 0,
|
||||||
style: Theme.of(context).textTheme.overline,
|
left: !message.writedByAdmin ? 20 : 0,
|
||||||
color: Theme.of(context).colorScheme.primary,
|
),
|
||||||
),
|
child: Column(
|
||||||
if (message.radar != null) const SizedBox(height: 4),
|
crossAxisAlignment: message.writedByAdmin
|
||||||
if (message.radar != null) _ReplyRadarOverview(message: message),
|
? CrossAxisAlignment.start
|
||||||
if (message.radar != null) const SizedBox(height: 4),
|
: CrossAxisAlignment.end,
|
||||||
],
|
children: [
|
||||||
),
|
_MessageContainer(
|
||||||
|
writedByAdmin: message.writedByAdmin,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
if (message.text != null) DidvanText(message.text!),
|
||||||
|
if (message.audio != null)
|
||||||
|
AudioVisualizer(
|
||||||
|
audioUrl: message.audio,
|
||||||
|
waveform: message.waveform,
|
||||||
|
),
|
||||||
|
if (message.radar != null) const DidvanDivider(),
|
||||||
|
if (message.radar != null) const SizedBox(height: 4),
|
||||||
|
if (message.radar != null)
|
||||||
|
_ReplyRadarOverview(message: message),
|
||||||
|
if (message.radar != null) const SizedBox(height: 4),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
DidvanText(
|
||||||
|
DateTimeUtils.timeWithAmPm(message.createdAt),
|
||||||
|
style: Theme.of(context).textTheme.overline,
|
||||||
|
color: Theme.of(context).colorScheme.caption,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -70,70 +93,63 @@ class _ReplyRadarOverview extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return _MessageContainer(
|
return Row(
|
||||||
writedByAdmin: message.writedByAdmin,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
isAttachment: true,
|
children: [
|
||||||
child: Row(
|
SkeletonImage(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
imageUrl: message.radar!.image,
|
||||||
children: [
|
height: 52,
|
||||||
SkeletonImage(
|
width: 52,
|
||||||
imageUrl: message.radar!.image,
|
),
|
||||||
height: 80,
|
const SizedBox(width: 8),
|
||||||
width: 80,
|
Expanded(
|
||||||
),
|
child: SizedBox(
|
||||||
const SizedBox(width: 8),
|
height: 52,
|
||||||
Expanded(
|
child: Column(
|
||||||
child: SizedBox(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
height: 80,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
child: Column(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
DidvanText(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
message.radar!.title,
|
||||||
children: [
|
style: Theme.of(context).textTheme.bodyText1,
|
||||||
DidvanText(
|
maxLines: 1,
|
||||||
message.radar!.title,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: Theme.of(context).textTheme.bodyText1,
|
color: Theme.of(context).colorScheme.focusedBorder,
|
||||||
maxLines: 2,
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
Row(
|
||||||
color: Theme.of(context).colorScheme.focusedBorder,
|
children: [
|
||||||
),
|
DidvanText(
|
||||||
Row(
|
'رادار ' + message.radar!.categories.first.label,
|
||||||
children: [
|
style: Theme.of(context).textTheme.overline,
|
||||||
DidvanText(
|
color: Theme.of(context).colorScheme.focusedBorder,
|
||||||
'رادار ' + message.radar!.categories.first.label,
|
),
|
||||||
style: Theme.of(context).textTheme.overline,
|
const Spacer(),
|
||||||
color: Theme.of(context).colorScheme.focusedBorder,
|
DidvanText(
|
||||||
),
|
DateTimeUtils.momentGenerator(message.radar!.createdAt) +
|
||||||
const Spacer(),
|
' | خواندن در ' +
|
||||||
DidvanText(
|
message.radar!.timeToRead.toString() +
|
||||||
DateTimeUtils.momentGenerator(
|
' دقیقه',
|
||||||
message.radar!.createdAt) +
|
color: Theme.of(context).colorScheme.focusedBorder,
|
||||||
' | خواندن در ' +
|
style: Theme.of(context).textTheme.overline,
|
||||||
message.radar!.timeToRead.toString() +
|
),
|
||||||
' دقیقه',
|
// DidvanText('text'),
|
||||||
color: Theme.of(context).colorScheme.focusedBorder,
|
],
|
||||||
style: Theme.of(context).textTheme.overline,
|
),
|
||||||
),
|
],
|
||||||
// DidvanText('text'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MessageContainer extends StatelessWidget {
|
class _MessageContainer extends StatelessWidget {
|
||||||
final bool writedByAdmin;
|
final bool writedByAdmin;
|
||||||
final bool isAttachment;
|
|
||||||
final Widget child;
|
final Widget child;
|
||||||
const _MessageContainer({
|
const _MessageContainer({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.writedByAdmin,
|
required this.writedByAdmin,
|
||||||
required this.isAttachment,
|
|
||||||
required this.child,
|
required this.child,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
|
@ -143,8 +159,8 @@ class _MessageContainer extends StatelessWidget {
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: DesignConfig.highBorderRadius.copyWith(
|
borderRadius: DesignConfig.highBorderRadius.copyWith(
|
||||||
bottomLeft: writedByAdmin && !isAttachment ? Radius.zero : null,
|
bottomLeft: writedByAdmin ? Radius.zero : null,
|
||||||
bottomRight: !writedByAdmin && !isAttachment ? Radius.zero : null,
|
bottomRight: !writedByAdmin ? Radius.zero : null,
|
||||||
),
|
),
|
||||||
color: writedByAdmin ? null : Theme.of(context).colorScheme.focused,
|
color: writedByAdmin ? null : Theme.of(context).colorScheme.focused,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:didvan/config/design_config.dart';
|
||||||
import 'package:didvan/config/theme_data.dart';
|
import 'package:didvan/config/theme_data.dart';
|
||||||
import 'package:didvan/constants/app_icons.dart';
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
import 'package:didvan/pages/home/direct/direct_state.dart';
|
import 'package:didvan/pages/home/direct/direct_state.dart';
|
||||||
|
|
@ -36,32 +37,69 @@ class MessageBox extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Typing extends StatelessWidget {
|
class _Typing extends StatefulWidget {
|
||||||
const _Typing({Key? key}) : super(key: key);
|
const _Typing({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_Typing> createState() => _TypingState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TypingState extends State<_Typing> {
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final state = context.read<DirectState>();
|
final state = context.read<DirectState>();
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
DidvanIconButton(
|
Expanded(
|
||||||
icon: DidvanIcons.mic_solid,
|
flex: 2,
|
||||||
onPressed: state.startRecording,
|
child: AnimatedSwitcher(
|
||||||
size: 32,
|
duration: DesignConfig.lowAnimationDuration,
|
||||||
color: Theme.of(context).colorScheme.focusedBorder,
|
transitionBuilder: (child, animation) => ScaleTransition(
|
||||||
|
scale: animation,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
child: state.text != null && state.text!.isNotEmpty
|
||||||
|
? DidvanIconButton(
|
||||||
|
key: const ValueKey(1),
|
||||||
|
icon: DidvanIcons.send_solid,
|
||||||
|
onPressed: () {
|
||||||
|
_formKey.currentState!.reset();
|
||||||
|
state.sendMessage();
|
||||||
|
},
|
||||||
|
size: 32,
|
||||||
|
color: Theme.of(context).colorScheme.focusedBorder,
|
||||||
|
)
|
||||||
|
: DidvanIconButton(
|
||||||
|
key: const ValueKey(2),
|
||||||
|
icon: DidvanIcons.mic_solid,
|
||||||
|
onPressed: state.startRecording,
|
||||||
|
size: 32,
|
||||||
|
color: Theme.of(context).colorScheme.focusedBorder,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
flex: 15,
|
||||||
textInputAction: TextInputAction.send,
|
child: Form(
|
||||||
decoration: InputDecoration(
|
key: _formKey,
|
||||||
border: InputBorder.none,
|
child: TextField(
|
||||||
hintText: 'بنویسید یا پیام صوتی بگذارید...',
|
textInputAction: TextInputAction.send,
|
||||||
hintStyle: Theme.of(context)
|
style: Theme.of(context).textTheme.bodyText2,
|
||||||
.textTheme
|
decoration: InputDecoration(
|
||||||
.caption!
|
border: InputBorder.none,
|
||||||
.copyWith(color: Theme.of(context).colorScheme.disabledText),
|
hintText: 'بنویسید یا پیام صوتی بگذارید...',
|
||||||
|
hintStyle: Theme.of(context).textTheme.caption!.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.disabledText),
|
||||||
|
),
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value.length <= 1) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
state.text = value;
|
||||||
|
},
|
||||||
),
|
),
|
||||||
onChanged: (value) {},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -79,7 +117,7 @@ class _Recording extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
DidvanIconButton(
|
DidvanIconButton(
|
||||||
icon: DidvanIcons.send_solid,
|
icon: DidvanIcons.send_solid,
|
||||||
onPressed: () => state.stopRecording(true),
|
onPressed: () => state.stopRecording(sendImidiately: true),
|
||||||
gestureSize: 52,
|
gestureSize: 52,
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
|
@ -91,7 +129,7 @@ class _Recording extends StatelessWidget {
|
||||||
DidvanIconButton(
|
DidvanIconButton(
|
||||||
icon: DidvanIcons.stop_circle_solid,
|
icon: DidvanIcons.stop_circle_solid,
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
onPressed: () => state.stopRecording(false),
|
onPressed: () => state.stopRecording(sendImidiately: false),
|
||||||
size: 32,
|
size: 32,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -109,7 +147,7 @@ class _RecordChecking extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
DidvanIconButton(
|
DidvanIconButton(
|
||||||
icon: DidvanIcons.send_solid,
|
icon: DidvanIcons.send_solid,
|
||||||
onPressed: () => state.stopRecording(true),
|
onPressed: () => state.stopRecording(sendImidiately: true),
|
||||||
color: Theme.of(context).colorScheme.focusedBorder,
|
color: Theme.of(context).colorScheme.focusedBorder,
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import 'package:didvan/config/design_config.dart';
|
||||||
import 'package:didvan/config/theme_data.dart';
|
import 'package:didvan/config/theme_data.dart';
|
||||||
import 'package:didvan/constants/app_icons.dart';
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
import 'package:didvan/constants/assets.dart';
|
import 'package:didvan/constants/assets.dart';
|
||||||
|
import 'package:didvan/pages/home/direct/direct_state.dart';
|
||||||
import 'package:didvan/services/storage/storage.dart';
|
import 'package:didvan/services/storage/storage.dart';
|
||||||
import 'package:didvan/utils/date_time.dart';
|
import 'package:didvan/utils/date_time.dart';
|
||||||
import 'package:didvan/widgets/didvan/icon_button.dart';
|
import 'package:didvan/widgets/didvan/icon_button.dart';
|
||||||
|
|
@ -14,13 +15,18 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:just_audio/just_audio.dart';
|
import 'package:just_audio/just_audio.dart';
|
||||||
import 'package:just_waveform/just_waveform.dart';
|
import 'package:just_waveform/just_waveform.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class AudioVisualizer extends StatefulWidget {
|
class AudioVisualizer extends StatefulWidget {
|
||||||
final File audioFile;
|
final File? audioFile;
|
||||||
|
final Waveform? waveform;
|
||||||
|
final String? audioUrl;
|
||||||
|
|
||||||
const AudioVisualizer({
|
const AudioVisualizer({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.audioFile,
|
this.audioFile,
|
||||||
|
this.waveform,
|
||||||
|
this.audioUrl,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -34,9 +40,9 @@ class _AudioVisualizerState extends State<AudioVisualizer> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
if (!kIsWeb) {
|
if (!kIsWeb && widget.audioFile != null) {
|
||||||
waveDataStream = JustWaveform.extract(
|
waveDataStream = JustWaveform.extract(
|
||||||
audioInFile: widget.audioFile,
|
audioInFile: widget.audioFile!,
|
||||||
waveOutFile: File(StorageService.appTempsDir + '/rec-wave.wave'),
|
waveOutFile: File(StorageService.appTempsDir + '/rec-wave.wave'),
|
||||||
zoom: const WaveformZoom.pixelsPerSecond(100),
|
zoom: const WaveformZoom.pixelsPerSecond(100),
|
||||||
);
|
);
|
||||||
|
|
@ -86,36 +92,24 @@ class _AudioVisualizerState extends State<AudioVisualizer> {
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
return SvgPicture.asset(Assets.record);
|
return SvgPicture.asset(Assets.record);
|
||||||
}
|
}
|
||||||
return StreamBuilder<WaveformProgress>(
|
|
||||||
stream: waveDataStream,
|
if (widget.audioFile != null) {
|
||||||
builder: (context, snapshot) {
|
return StreamBuilder<WaveformProgress>(
|
||||||
if (snapshot.data == null) {
|
stream: waveDataStream,
|
||||||
return const SizedBox();
|
builder: (context, snapshot) {
|
||||||
}
|
if (snapshot.data == null) {
|
||||||
if (snapshot.data!.waveform == null) {
|
return const SizedBox();
|
||||||
return const SizedBox();
|
}
|
||||||
}
|
if (snapshot.data!.waveform == null) {
|
||||||
final waveform = snapshot.data!.waveform!;
|
return const SizedBox();
|
||||||
return GestureDetector(
|
}
|
||||||
onHorizontalDragUpdate: _changePosition,
|
final waveform = snapshot.data!.waveform!;
|
||||||
onTapDown: _changePosition,
|
context.read<DirectState>().waveform = waveform;
|
||||||
child: SizedBox(
|
return _waveWidget(waveform);
|
||||||
height: double.infinity,
|
},
|
||||||
width: double.infinity,
|
);
|
||||||
child: _AudioWaveformWidget(
|
}
|
||||||
waveform: waveform,
|
return _waveWidget(widget.waveform!);
|
||||||
audioPlayer: _audioPlayer,
|
|
||||||
start: Duration.zero,
|
|
||||||
scale: 2,
|
|
||||||
strokeWidth: 3,
|
|
||||||
duration: waveform.duration,
|
|
||||||
waveColor:
|
|
||||||
Theme.of(context).colorScheme.focusedBorder,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -136,6 +130,24 @@ class _AudioVisualizerState extends State<AudioVisualizer> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _waveWidget(Waveform waveform) => GestureDetector(
|
||||||
|
onHorizontalDragUpdate: _changePosition,
|
||||||
|
onTapDown: _changePosition,
|
||||||
|
child: SizedBox(
|
||||||
|
height: double.infinity,
|
||||||
|
width: double.infinity,
|
||||||
|
child: _AudioWaveformWidget(
|
||||||
|
waveform: waveform,
|
||||||
|
audioPlayer: _audioPlayer,
|
||||||
|
start: Duration.zero,
|
||||||
|
scale: 2,
|
||||||
|
strokeWidth: 3,
|
||||||
|
duration: waveform.duration,
|
||||||
|
waveColor: Theme.of(context).colorScheme.focusedBorder,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
void _changePosition(details) {
|
void _changePosition(details) {
|
||||||
double posper =
|
double posper =
|
||||||
details.localPosition.dx / (MediaQuery.of(context).size.width - 200);
|
details.localPosition.dx / (MediaQuery.of(context).size.width - 200);
|
||||||
|
|
@ -148,12 +160,14 @@ class _AudioVisualizerState extends State<AudioVisualizer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _setupAudioPlayer() async {
|
Future<void> _setupAudioPlayer() async {
|
||||||
if (kIsWeb) {
|
if (kIsWeb || widget.audioFile == null) {
|
||||||
await _audioPlayer.setUrl(
|
await _audioPlayer.setUrl(
|
||||||
widget.audioFile.uri.path.replaceAll('%3A', ':'),
|
kIsWeb
|
||||||
|
? widget.audioFile!.uri.path.replaceAll('%3A', ':')
|
||||||
|
: widget.audioUrl!,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await _audioPlayer.setFilePath(widget.audioFile.path);
|
await _audioPlayer.setFilePath(widget.audioFile!.path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import 'package:didvan/pages/authentication/authentication.dart';
|
||||||
import 'package:didvan/pages/authentication/authentication_state.dart';
|
import 'package:didvan/pages/authentication/authentication_state.dart';
|
||||||
import 'package:didvan/pages/home/comments/comments.dart';
|
import 'package:didvan/pages/home/comments/comments.dart';
|
||||||
import 'package:didvan/pages/home/comments/comments_state.dart';
|
import 'package:didvan/pages/home/comments/comments_state.dart';
|
||||||
import 'package:didvan/pages/home/direct/widgets/direct.dart';
|
import 'package:didvan/pages/home/direct/direct.dart';
|
||||||
import 'package:didvan/pages/home/direct/direct_state.dart';
|
import 'package:didvan/pages/home/direct/direct_state.dart';
|
||||||
import 'package:didvan/pages/home/home.dart';
|
import 'package:didvan/pages/home/home.dart';
|
||||||
import 'package:didvan/pages/home/home_state.dart';
|
import 'package:didvan/pages/home/home_state.dart';
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ class RequestHelper {
|
||||||
static const String _baseUserUrl = baseUrl + '/user';
|
static const String _baseUserUrl = baseUrl + '/user';
|
||||||
static const String _baseRadarUrl = baseUrl + '/radar';
|
static const String _baseRadarUrl = baseUrl + '/radar';
|
||||||
static const String _baseNewsUrl = baseUrl + '/news';
|
static const String _baseNewsUrl = baseUrl + '/news';
|
||||||
|
static const String _baseDirectUrl = _baseUserUrl + '/direct';
|
||||||
|
|
||||||
static const String confirmUsername = _baseUserUrl + '/confirmUsername';
|
static const String confirmUsername = _baseUserUrl + '/confirmUsername';
|
||||||
static const String login = _baseUserUrl + '/login';
|
static const String login = _baseUserUrl + '/login';
|
||||||
|
|
@ -18,7 +19,9 @@ class RequestHelper {
|
||||||
_baseUserUrl + '/marked/${type ?? ''}';
|
_baseUserUrl + '/marked/${type ?? ''}';
|
||||||
|
|
||||||
static const String directTypes = baseUrl + '/direct/types';
|
static const String directTypes = baseUrl + '/direct/types';
|
||||||
static String direct(int id) => _baseUserUrl + '/direct/$id';
|
static String direct(int id) => _baseDirectUrl + '/$id';
|
||||||
|
static String sendDirectMessage(int id) =>
|
||||||
|
_baseDirectUrl + '/$id/sendMessage';
|
||||||
static String tag({
|
static String tag({
|
||||||
required List<int> ids,
|
required List<int> ids,
|
||||||
required String type,
|
required String type,
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,31 @@ class DateTimeUtils {
|
||||||
return result?.toDateTime().toString();
|
return result?.toDateTime().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String timeWithAmPm(String input) {
|
||||||
|
final dateTime = utcToLocalTime(input);
|
||||||
|
bool isAm = true;
|
||||||
|
int hour = 0;
|
||||||
|
int minute = 0;
|
||||||
|
if (dateTime.hour > 12) {
|
||||||
|
isAm = false;
|
||||||
|
hour = dateTime.hour - 12;
|
||||||
|
} else {
|
||||||
|
hour = dateTime.hour;
|
||||||
|
}
|
||||||
|
minute = dateTime.minute;
|
||||||
|
return '$hour:${_timeNormalizer(minute)} ${isAm ? 'ق.ظ' : 'ب.ظ'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
static DateTime utcToLocalTime(String input) {
|
||||||
|
final dateTime = DateTime.parse(input);
|
||||||
|
return dateTime.add(const Duration(hours: 3, minutes: 30));
|
||||||
|
}
|
||||||
|
|
||||||
|
static String _timeNormalizer(int input) {
|
||||||
|
if (input < 10) return '0$input';
|
||||||
|
return input.toString();
|
||||||
|
}
|
||||||
|
|
||||||
static String momentGenerator(String input) {
|
static String momentGenerator(String input) {
|
||||||
final date = DateTime.parse(input);
|
final date = DateTime.parse(input);
|
||||||
final int seconds = (DateTime.now().difference(date).inSeconds).floor();
|
final int seconds = (DateTime.now().difference(date).inSeconds).floor();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue