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';
|
||||
|
||||
class MessageData {
|
||||
|
|
@ -8,6 +12,8 @@ class MessageData {
|
|||
final bool readed;
|
||||
final String createdAt;
|
||||
final RadarAttachment? radar;
|
||||
final File? audioFile;
|
||||
final Waveform? waveform;
|
||||
|
||||
const MessageData({
|
||||
required this.id,
|
||||
|
|
@ -17,6 +23,8 @@ class MessageData {
|
|||
required this.text,
|
||||
required this.audio,
|
||||
required this.radar,
|
||||
required this.waveform,
|
||||
this.audioFile,
|
||||
});
|
||||
|
||||
factory MessageData.fromJson(Map<String, dynamic> json) => MessageData(
|
||||
|
|
@ -26,6 +34,16 @@ class MessageData {
|
|||
writedByAdmin: json['writedByAdmin'],
|
||||
readed: json['readed'],
|
||||
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
|
||||
? null
|
||||
: 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/message_data/message_data.dart';
|
||||
import 'package:didvan/models/message_data/radar_attachment.dart';
|
||||
import 'package:didvan/providers/core_provider.dart';
|
||||
import 'package:didvan/services/network/request.dart';
|
||||
import 'package:didvan/services/network/request_helper.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_vibrate/flutter_vibrate.dart';
|
||||
import 'package:just_waveform/just_waveform.dart';
|
||||
import 'package:record/record.dart';
|
||||
|
||||
class DirectState extends CoreProvier {
|
||||
|
|
@ -15,7 +17,10 @@ class DirectState extends CoreProvier {
|
|||
late final int typeId;
|
||||
final Map<String, List<int>> dailyMessages = {};
|
||||
|
||||
String? text;
|
||||
RadarAttachment? replyRadar;
|
||||
File? recordedFile;
|
||||
Waveform? waveform;
|
||||
|
||||
bool isRecording = false;
|
||||
|
||||
|
|
@ -27,14 +32,7 @@ class DirectState extends CoreProvier {
|
|||
final messageDatas = service.result['messages'];
|
||||
for (var i = 0; i < messageDatas.length; i++) {
|
||||
messages.add(MessageData.fromJson(messageDatas[i]));
|
||||
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);
|
||||
}
|
||||
_addToDailyGrouped();
|
||||
}
|
||||
appState = AppState.idle;
|
||||
return;
|
||||
|
|
@ -58,7 +56,7 @@ class DirectState extends CoreProvier {
|
|||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> stopRecording(bool sendImidiately) async {
|
||||
Future<void> stopRecording({required bool sendImidiately}) async {
|
||||
final path = await _recorder.stop();
|
||||
isRecording = false;
|
||||
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/models/message_data/message_data.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/widgets/didvan/divider.dart';
|
||||
import 'package:didvan/widgets/didvan/text.dart';
|
||||
import 'package:didvan/widgets/skeleton_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -15,19 +17,18 @@ class Message extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
right: message.writedByAdmin ? 20 : 0,
|
||||
left: !message.writedByAdmin ? 20 : 0,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
if (message.id ==
|
||||
context
|
||||
final firstMessageOfGroupId = context
|
||||
.read<DirectState>()
|
||||
.dailyMessages[message.createdAt.split('T').first]!
|
||||
.last)
|
||||
Container(
|
||||
.last;
|
||||
return Column(
|
||||
crossAxisAlignment: message.writedByAdmin
|
||||
? CrossAxisAlignment.end
|
||||
: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (message.id == firstMessageOfGroupId)
|
||||
Center(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
|
|
@ -42,23 +43,45 @@ class Message extends StatelessWidget {
|
|||
: Theme.of(context).colorScheme.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
right: message.writedByAdmin ? 20 : 0,
|
||||
left: !message.writedByAdmin ? 20 : 0,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: message.writedByAdmin
|
||||
? CrossAxisAlignment.start
|
||||
: CrossAxisAlignment.end,
|
||||
children: [
|
||||
_MessageContainer(
|
||||
isAttachment: false,
|
||||
writedByAdmin: message.writedByAdmin,
|
||||
child:
|
||||
message.text != null ? DidvanText(message.text!) : Container(),
|
||||
),
|
||||
if (message.radar != null)
|
||||
DidvanText(
|
||||
'لینک به مطلب زیر:',
|
||||
style: Theme.of(context).textTheme.overline,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
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)
|
||||
_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,21 +93,18 @@ class _ReplyRadarOverview extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _MessageContainer(
|
||||
writedByAdmin: message.writedByAdmin,
|
||||
isAttachment: true,
|
||||
child: Row(
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SkeletonImage(
|
||||
imageUrl: message.radar!.image,
|
||||
height: 80,
|
||||
width: 80,
|
||||
height: 52,
|
||||
width: 52,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 80,
|
||||
height: 52,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
|
|
@ -92,7 +112,7 @@ class _ReplyRadarOverview extends StatelessWidget {
|
|||
DidvanText(
|
||||
message.radar!.title,
|
||||
style: Theme.of(context).textTheme.bodyText1,
|
||||
maxLines: 2,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
color: Theme.of(context).colorScheme.focusedBorder,
|
||||
),
|
||||
|
|
@ -105,8 +125,7 @@ class _ReplyRadarOverview extends StatelessWidget {
|
|||
),
|
||||
const Spacer(),
|
||||
DidvanText(
|
||||
DateTimeUtils.momentGenerator(
|
||||
message.radar!.createdAt) +
|
||||
DateTimeUtils.momentGenerator(message.radar!.createdAt) +
|
||||
' | خواندن در ' +
|
||||
message.radar!.timeToRead.toString() +
|
||||
' دقیقه',
|
||||
|
|
@ -121,19 +140,16 @@ class _ReplyRadarOverview extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MessageContainer extends StatelessWidget {
|
||||
final bool writedByAdmin;
|
||||
final bool isAttachment;
|
||||
final Widget child;
|
||||
const _MessageContainer({
|
||||
Key? key,
|
||||
required this.writedByAdmin,
|
||||
required this.isAttachment,
|
||||
required this.child,
|
||||
}) : super(key: key);
|
||||
|
||||
|
|
@ -143,8 +159,8 @@ class _MessageContainer extends StatelessWidget {
|
|||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: DesignConfig.highBorderRadius.copyWith(
|
||||
bottomLeft: writedByAdmin && !isAttachment ? Radius.zero : null,
|
||||
bottomRight: !writedByAdmin && !isAttachment ? Radius.zero : null,
|
||||
bottomLeft: writedByAdmin ? Radius.zero : null,
|
||||
bottomRight: !writedByAdmin ? Radius.zero : null,
|
||||
),
|
||||
color: writedByAdmin ? null : Theme.of(context).colorScheme.focused,
|
||||
border: Border.all(
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:didvan/config/design_config.dart';
|
||||
import 'package:didvan/config/theme_data.dart';
|
||||
import 'package:didvan/constants/app_icons.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);
|
||||
|
||||
@override
|
||||
State<_Typing> createState() => _TypingState();
|
||||
}
|
||||
|
||||
class _TypingState extends State<_Typing> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final state = context.read<DirectState>();
|
||||
return Row(
|
||||
children: [
|
||||
DidvanIconButton(
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: AnimatedSwitcher(
|
||||
duration: DesignConfig.lowAnimationDuration,
|
||||
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(
|
||||
flex: 15,
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: TextField(
|
||||
textInputAction: TextInputAction.send,
|
||||
style: Theme.of(context).textTheme.bodyText2,
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
hintText: 'بنویسید یا پیام صوتی بگذارید...',
|
||||
hintStyle: Theme.of(context)
|
||||
.textTheme
|
||||
.caption!
|
||||
.copyWith(color: Theme.of(context).colorScheme.disabledText),
|
||||
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: [
|
||||
DidvanIconButton(
|
||||
icon: DidvanIcons.send_solid,
|
||||
onPressed: () => state.stopRecording(true),
|
||||
onPressed: () => state.stopRecording(sendImidiately: true),
|
||||
gestureSize: 52,
|
||||
),
|
||||
Expanded(
|
||||
|
|
@ -91,7 +129,7 @@ class _Recording extends StatelessWidget {
|
|||
DidvanIconButton(
|
||||
icon: DidvanIcons.stop_circle_solid,
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
onPressed: () => state.stopRecording(false),
|
||||
onPressed: () => state.stopRecording(sendImidiately: false),
|
||||
size: 32,
|
||||
),
|
||||
],
|
||||
|
|
@ -109,7 +147,7 @@ class _RecordChecking extends StatelessWidget {
|
|||
children: [
|
||||
DidvanIconButton(
|
||||
icon: DidvanIcons.send_solid,
|
||||
onPressed: () => state.stopRecording(true),
|
||||
onPressed: () => state.stopRecording(sendImidiately: true),
|
||||
color: Theme.of(context).colorScheme.focusedBorder,
|
||||
),
|
||||
Expanded(
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import 'package:didvan/config/design_config.dart';
|
|||
import 'package:didvan/config/theme_data.dart';
|
||||
import 'package:didvan/constants/app_icons.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/utils/date_time.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:just_audio/just_audio.dart';
|
||||
import 'package:just_waveform/just_waveform.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class AudioVisualizer extends StatefulWidget {
|
||||
final File audioFile;
|
||||
final File? audioFile;
|
||||
final Waveform? waveform;
|
||||
final String? audioUrl;
|
||||
|
||||
const AudioVisualizer({
|
||||
Key? key,
|
||||
required this.audioFile,
|
||||
this.audioFile,
|
||||
this.waveform,
|
||||
this.audioUrl,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
|
@ -34,9 +40,9 @@ class _AudioVisualizerState extends State<AudioVisualizer> {
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
if (!kIsWeb) {
|
||||
if (!kIsWeb && widget.audioFile != null) {
|
||||
waveDataStream = JustWaveform.extract(
|
||||
audioInFile: widget.audioFile,
|
||||
audioInFile: widget.audioFile!,
|
||||
waveOutFile: File(StorageService.appTempsDir + '/rec-wave.wave'),
|
||||
zoom: const WaveformZoom.pixelsPerSecond(100),
|
||||
);
|
||||
|
|
@ -86,6 +92,8 @@ class _AudioVisualizerState extends State<AudioVisualizer> {
|
|||
if (kIsWeb) {
|
||||
return SvgPicture.asset(Assets.record);
|
||||
}
|
||||
|
||||
if (widget.audioFile != null) {
|
||||
return StreamBuilder<WaveformProgress>(
|
||||
stream: waveDataStream,
|
||||
builder: (context, snapshot) {
|
||||
|
|
@ -96,26 +104,12 @@ class _AudioVisualizerState extends State<AudioVisualizer> {
|
|||
return const SizedBox();
|
||||
}
|
||||
final waveform = snapshot.data!.waveform!;
|
||||
return 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,
|
||||
),
|
||||
),
|
||||
);
|
||||
context.read<DirectState>().waveform = waveform;
|
||||
return _waveWidget(waveform);
|
||||
},
|
||||
);
|
||||
}
|
||||
return _waveWidget(widget.waveform!);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
|
@ -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) {
|
||||
double posper =
|
||||
details.localPosition.dx / (MediaQuery.of(context).size.width - 200);
|
||||
|
|
@ -148,12 +160,14 @@ class _AudioVisualizerState extends State<AudioVisualizer> {
|
|||
}
|
||||
|
||||
Future<void> _setupAudioPlayer() async {
|
||||
if (kIsWeb) {
|
||||
if (kIsWeb || widget.audioFile == null) {
|
||||
await _audioPlayer.setUrl(
|
||||
widget.audioFile.uri.path.replaceAll('%3A', ':'),
|
||||
kIsWeb
|
||||
? widget.audioFile!.uri.path.replaceAll('%3A', ':')
|
||||
: widget.audioUrl!,
|
||||
);
|
||||
} 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/home/comments/comments.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/home.dart';
|
||||
import 'package:didvan/pages/home/home_state.dart';
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ class RequestHelper {
|
|||
static const String _baseUserUrl = baseUrl + '/user';
|
||||
static const String _baseRadarUrl = baseUrl + '/radar';
|
||||
static const String _baseNewsUrl = baseUrl + '/news';
|
||||
static const String _baseDirectUrl = _baseUserUrl + '/direct';
|
||||
|
||||
static const String confirmUsername = _baseUserUrl + '/confirmUsername';
|
||||
static const String login = _baseUserUrl + '/login';
|
||||
|
|
@ -18,7 +19,9 @@ class RequestHelper {
|
|||
_baseUserUrl + '/marked/${type ?? ''}';
|
||||
|
||||
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({
|
||||
required List<int> ids,
|
||||
required String type,
|
||||
|
|
|
|||
|
|
@ -52,6 +52,31 @@ class DateTimeUtils {
|
|||
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) {
|
||||
final date = DateTime.parse(input);
|
||||
final int seconds = (DateTime.now().difference(date).inSeconds).floor();
|
||||
|
|
|
|||
Loading…
Reference in New Issue