didvan-app/lib/views/direct/widgets/message.dart

439 lines
17 KiB
Dart

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/utils/date_time.dart';
import 'package:didvan/views/ai/widgets/audio_wave.dart';
import 'package:didvan/views/direct/direct_state.dart';
import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:provider/provider.dart';
import 'package:persian_number_utility/persian_number_utility.dart';
class Message extends StatefulWidget {
final MessageData message;
const Message({Key? key, required this.message}) : super(key: key);
@override
State<Message> createState() => _MessageState();
}
class _MessageState extends State<Message> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<Offset> _slideAnimation;
late Animation<double> _opacityAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
);
_slideAnimation = Tween<Offset>(
begin: const Offset(0, 0.5),
end: Offset.zero,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeOut,
));
_opacityAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeIn,
));
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final state = context.read<DirectState>();
final firstMessageOfGroupId = state
.dailyMessages[
widget.message.createdAt.replaceAll('T', ' ').split(' ').first]!
.last;
return FadeTransition(
opacity: _opacityAnimation,
child: SlideTransition(
position: _slideAnimation,
child: GestureDetector(
onLongPress: () {
if (state.deletionQueue.contains(widget.message.id) ||
widget.message.writedByAdmin) {
return;
}
state.deletionQueue.add(widget.message.id);
state.update();
},
onTap: () {
if (state.deletionQueue.isEmpty || widget.message.writedByAdmin)
return;
if (!state.deletionQueue.contains(widget.message.id)) {
state.deletionQueue.add(widget.message.id);
} else {
state.deletionQueue.remove(widget.message.id);
}
state.update();
},
child: Container(
color: state.deletionQueue.contains(widget.message.id)
? Theme.of(context)
.colorScheme
.secondaryDisabled
.withValues(alpha: 0.5)
: null,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
crossAxisAlignment: widget.message.writedByAdmin
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: [
if (widget.message.id == firstMessageOfGroupId)
Center(
child: Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.splash,
borderRadius: DesignConfig.mediumBorderRadius,
),
child: Padding(
padding: const EdgeInsets.all(3.0),
child: DidvanText(
DateTime.parse(widget.message.createdAt)
.toPersianDateStr(),
style: Theme.of(context).textTheme.labelMedium,
color: DesignConfig.isDark
? Theme.of(context).colorScheme.white
: Theme.of(context).colorScheme.black,
),
),
),
),
Padding(
padding: const EdgeInsets.all(0),
child: Column(
crossAxisAlignment: widget.message.writedByAdmin
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: [
_MessageContainer(
writedByAdmin: widget.message.writedByAdmin,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (widget.message.text != null)
DidvanText(
widget.message.text!,
fontWeight: FontWeight.normal,
),
if (widget.message.audio != null)
AudioWave(
file: widget.message.audio!,
totalDuration: widget.message.duration != null
? Duration(
seconds: widget.message.duration!)
: null,
),
if (widget.message.radar != null ||
widget.message.news != null)
const DidvanDivider(),
if (widget.message.radar != null ||
widget.message.news != null)
const SizedBox(height: 4),
if (widget.message.radar != null)
_ReplyRadarOverview(message: widget.message),
if (widget.message.news != null)
_ReplyNewsOverview(message: widget.message),
if (widget.message.radar != null ||
widget.message.news != null)
const SizedBox(height: 4),
],
),
),
const SizedBox(height: 4),
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: widget.message.writedByAdmin
? MainAxisAlignment.end
: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (widget.message.writedByAdmin)
Padding(
padding: const EdgeInsets.only(left: 8),
child: Container(
width: 20,
height: 20,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: const Color.fromARGB(
255, 184, 184, 184))),
child: Center(
child: SvgPicture.asset(
'lib/assets/icons/Didvan.svg',
width: 12,
height: 12,
color: DesignConfig.isDark
? Colors.white
: null,
),
),
))
else
Padding(
padding: const EdgeInsets.only(left: 8),
child: widget.message.writerPhoto != null &&
widget.message.writerPhoto!.isNotEmpty
? Container(
width: 20,
height: 20,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: const Color.fromARGB(
255, 184, 184, 184),
width: 1,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.network(
widget.message.writerPhoto!,
fit: BoxFit.cover,
errorBuilder:
(context, error, stackTrace) {
return Container(
width: 20,
height: 20,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: const Color.fromARGB(
255, 184, 184, 184),
width: 1,
),
),
child: Icon(
Icons.person_rounded,
size: 12,
color: DesignConfig.isDark
? Colors.white
: Colors.black,
),
);
},
),
),
)
: Container(
width: 20,
height: 20,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: const Color.fromARGB(
255, 184, 184, 184),
width: 1,
),
),
child: Icon(
Icons.person_rounded,
size: 12,
color: DesignConfig.isDark
? Colors.white
: Colors.black,
),
),
),
DidvanText(
DateTimeUtils.timeWithAmPm(widget.message.createdAt)
.toPersianDigit(),
style: Theme.of(context).textTheme.labelSmall,
color: Theme.of(context).colorScheme.caption,
),
const SizedBox(width: 4),
if (!widget.message.writedByAdmin)
SvgPicture.asset(
widget.message.readed
? 'lib/assets/icons/Seen.svg'
: 'lib/assets/icons/unseen-tick.svg',
height: 10,
)
],
),
],
),
),
],
),
),
),
),
);
}
}
class _ReplyRadarOverview extends StatelessWidget {
final MessageData message;
const _ReplyRadarOverview({Key? key, required this.message})
: super(key: key);
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SkeletonImage(
imageUrl: message.radar!.image,
height: 52,
width: 52,
),
const SizedBox(width: 8),
Expanded(
child: SizedBox(
height: 52,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
DidvanText(
message.radar!.title,
style: Theme.of(context).textTheme.bodyLarge,
maxLines: 1,
overflow: TextOverflow.ellipsis,
color: const Color.fromARGB(255, 0, 126, 167),
),
Row(
children: [
Expanded(
child: DidvanText(
'رادار ${message.radar!.categories.first.label}',
style: Theme.of(context)
.textTheme
.labelSmall
?.copyWith(fontWeight: FontWeight.bold),
color: const Color.fromARGB(255, 0, 126, 167),
),
),
DidvanText(
'${DateTimeUtils.momentGenerator(message.radar!.createdAt)} / خواندن در ${message.radar!.timeToRead} دقیقه',
color: const Color.fromARGB(255, 0, 126, 167),
style: Theme.of(context)
.textTheme
.labelSmall
?.copyWith(fontWeight: FontWeight.bold),
),
],
),
],
),
),
),
],
);
}
}
class _ReplyNewsOverview extends StatelessWidget {
final MessageData message;
const _ReplyNewsOverview({Key? key, required this.message}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SkeletonImage(
imageUrl: message.news!.image,
height: 52,
width: 52,
),
const SizedBox(width: 8),
Expanded(
child: SizedBox(
height: 52,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
DidvanText(
message.news!.title,
style: Theme.of(context).textTheme.bodyLarge,
maxLines: 1,
overflow: TextOverflow.ellipsis,
color: Theme.of(context).colorScheme.focusedBorder,
),
Row(
children: [
DidvanText(
'خبر',
style: Theme.of(context).textTheme.labelSmall,
color: Theme.of(context).colorScheme.focusedBorder,
),
const Spacer(),
DidvanText(
DateTimeUtils.momentGenerator(message.news!.createdAt),
color: Theme.of(context).colorScheme.focusedBorder,
style: Theme.of(context).textTheme.labelSmall,
),
],
),
],
),
),
),
],
);
}
}
class _MessageContainer extends StatelessWidget {
final bool writedByAdmin;
final Widget child;
const _MessageContainer({
Key? key,
required this.writedByAdmin,
required this.child,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
decoration: BoxDecoration(
borderRadius: DesignConfig.highBorderRadius.copyWith(
bottomLeft: writedByAdmin ? Radius.zero : null,
bottomRight: !writedByAdmin ? Radius.zero : null,
),
border: Border.all(
color: writedByAdmin
? const Color.fromARGB(255, 184, 184, 184)
: Colors.transparent,
width: 0.5,
),
color: (writedByAdmin
? Theme.of(context).colorScheme.surface
: Theme.of(context).colorScheme.focused)
.withValues(alpha: 0.9),
),
child: child,
);
}
}