mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 11:18:41 +00:00
fixing #321
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
This commit is contained in:
parent
a63343ccdd
commit
004f00f308
3 changed files with 270 additions and 2 deletions
|
|
@ -251,7 +251,8 @@ class _MessageInputState extends State<MessageInput> {
|
||||||
width: (100 - _cancelSlideOffset) % 101,
|
width: (100 - _cancelSlideOffset) % 101,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
context.lang.voiceMessageSlideToCancel),
|
context.lang.voiceMessageSlideToCancel,
|
||||||
|
),
|
||||||
] else ...[
|
] else ...[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(),
|
child: Container(),
|
||||||
|
|
@ -265,7 +266,7 @@ class _MessageInputState extends State<MessageInput> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 20)
|
const SizedBox(width: 20),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_send_to_view.dart';
|
import 'package:twonly/src/views/camera/camera_send_to_view.dart';
|
||||||
import 'package:twonly/src/views/chats/media_viewer_components/reaction_buttons.component.dart';
|
import 'package:twonly/src/views/chats/media_viewer_components/reaction_buttons.component.dart';
|
||||||
import 'package:twonly/src/views/components/animate_icon.dart';
|
import 'package:twonly/src/views/components/animate_icon.dart';
|
||||||
|
import 'package:twonly/src/views/components/loader.dart';
|
||||||
import 'package:twonly/src/views/components/media_view_sizing.dart';
|
import 'package:twonly/src/views/components/media_view_sizing.dart';
|
||||||
import 'package:video_player/video_player.dart';
|
import 'package:video_player/video_player.dart';
|
||||||
|
|
||||||
|
|
@ -58,6 +59,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
bool imageSaved = false;
|
bool imageSaved = false;
|
||||||
bool imageSaving = false;
|
bool imageSaving = false;
|
||||||
bool displayTwonlyPresent = false;
|
bool displayTwonlyPresent = false;
|
||||||
|
bool _showDownloadingLoader = false;
|
||||||
late String _currentMediaSender;
|
late String _currentMediaSender;
|
||||||
final emojiKey = GlobalKey<EmojiFloatWidgetState>();
|
final emojiKey = GlobalKey<EmojiFloatWidgetState>();
|
||||||
|
|
||||||
|
|
@ -183,6 +185,9 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
downloadStateListener = stream.listen((updated) async {
|
downloadStateListener = stream.listen((updated) async {
|
||||||
if (updated == null) return;
|
if (updated == null) return;
|
||||||
if (updated.downloadState != DownloadState.ready) {
|
if (updated.downloadState != DownloadState.ready) {
|
||||||
|
setState(() {
|
||||||
|
_showDownloadingLoader = true;
|
||||||
|
});
|
||||||
if (!downloadTriggered) {
|
if (!downloadTriggered) {
|
||||||
downloadTriggered = true;
|
downloadTriggered = true;
|
||||||
final mediaFile = await twonlyDB.mediaFilesDao
|
final mediaFile = await twonlyDB.mediaFilesDao
|
||||||
|
|
@ -204,6 +209,9 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
bool showTwonly,
|
bool showTwonly,
|
||||||
) async {
|
) async {
|
||||||
if (allMediaFiles.isEmpty) return;
|
if (allMediaFiles.isEmpty) return;
|
||||||
|
setState(() {
|
||||||
|
_showDownloadingLoader = false;
|
||||||
|
});
|
||||||
final currentMediaLocal =
|
final currentMediaLocal =
|
||||||
await MediaFileService.fromMediaId(allMediaFiles.first.mediaId!);
|
await MediaFileService.fromMediaId(allMediaFiles.first.mediaId!);
|
||||||
if (currentMediaLocal == null || !mounted) return;
|
if (currentMediaLocal == null || !mounted) return;
|
||||||
|
|
@ -503,6 +511,17 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
child: Stack(
|
child: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
|
if (_showDownloadingLoader)
|
||||||
|
Center(
|
||||||
|
child: SizedBox(
|
||||||
|
height: 60,
|
||||||
|
width: 60,
|
||||||
|
child: ThreeRotatingDots(
|
||||||
|
size: 40,
|
||||||
|
color: context.color.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
if ((currentMedia != null || videoController != null) &&
|
if ((currentMedia != null || videoController != null) &&
|
||||||
(canBeSeenUntil == null || progress >= 0))
|
(canBeSeenUntil == null || progress >= 0))
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
|
|
|
||||||
248
lib/src/views/components/loader.dart
Normal file
248
lib/src/views/components/loader.dart
Normal file
|
|
@ -0,0 +1,248 @@
|
||||||
|
// Credits: https://github.com/watery-desert/loading_animation_widget/tree/main/lib/src/three_rotating_dots
|
||||||
|
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
extension LoadingAnimationControllerX on AnimationController {
|
||||||
|
T eval<T>(Tween<T> tween, {Curve curve = Curves.linear}) =>
|
||||||
|
tween.transform(curve.transform(value));
|
||||||
|
|
||||||
|
double evalDouble({
|
||||||
|
double from = 0,
|
||||||
|
double to = 1,
|
||||||
|
double begin = 0,
|
||||||
|
double end = 1,
|
||||||
|
Curve curve = Curves.linear,
|
||||||
|
}) {
|
||||||
|
return eval(
|
||||||
|
Tween<double>(begin: from, end: to),
|
||||||
|
curve: Interval(begin, end, curve: curve),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ThreeRotatingDots extends StatefulWidget {
|
||||||
|
const ThreeRotatingDots({
|
||||||
|
required this.color,
|
||||||
|
required this.size,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
final double size;
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ThreeRotatingDots> createState() => _ThreeRotatingDotsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ThreeRotatingDotsState extends State<ThreeRotatingDots>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
late AnimationController _animationController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
_animationController = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(milliseconds: 2000),
|
||||||
|
)..repeat();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final color = widget.color;
|
||||||
|
final size = widget.size;
|
||||||
|
final dotSize = size / 3;
|
||||||
|
final edgeOffset = (size - dotSize) / 2;
|
||||||
|
|
||||||
|
const firstDotsInterval = Interval(
|
||||||
|
0,
|
||||||
|
0.50,
|
||||||
|
curve: Curves.easeInOutCubic,
|
||||||
|
);
|
||||||
|
const secondDotsInterval = Interval(
|
||||||
|
0.50,
|
||||||
|
1,
|
||||||
|
curve: Curves.easeInOutCubic,
|
||||||
|
);
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: _animationController,
|
||||||
|
builder: (_, __) => Transform.translate(
|
||||||
|
offset: Offset(0, size / 12),
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
_BuildDot.first(
|
||||||
|
color: color,
|
||||||
|
size: dotSize,
|
||||||
|
controller: _animationController,
|
||||||
|
dotOffset: edgeOffset,
|
||||||
|
beginAngle: math.pi,
|
||||||
|
endAngle: 0,
|
||||||
|
interval: firstDotsInterval,
|
||||||
|
),
|
||||||
|
|
||||||
|
_BuildDot.first(
|
||||||
|
color: color,
|
||||||
|
size: dotSize,
|
||||||
|
controller: _animationController,
|
||||||
|
dotOffset: edgeOffset,
|
||||||
|
beginAngle: 5 * math.pi / 3,
|
||||||
|
endAngle: 2 * math.pi / 3,
|
||||||
|
interval: firstDotsInterval,
|
||||||
|
),
|
||||||
|
|
||||||
|
_BuildDot.first(
|
||||||
|
color: color,
|
||||||
|
size: dotSize,
|
||||||
|
controller: _animationController,
|
||||||
|
dotOffset: edgeOffset,
|
||||||
|
beginAngle: 7 * math.pi / 3,
|
||||||
|
endAngle: 4 * math.pi / 3,
|
||||||
|
interval: firstDotsInterval,
|
||||||
|
),
|
||||||
|
|
||||||
|
/// Next 3 dots
|
||||||
|
|
||||||
|
_BuildDot.second(
|
||||||
|
controller: _animationController,
|
||||||
|
beginAngle: 0,
|
||||||
|
endAngle: -math.pi,
|
||||||
|
interval: secondDotsInterval,
|
||||||
|
dotOffset: edgeOffset,
|
||||||
|
color: color,
|
||||||
|
size: dotSize,
|
||||||
|
),
|
||||||
|
|
||||||
|
_BuildDot.second(
|
||||||
|
controller: _animationController,
|
||||||
|
beginAngle: 2 * math.pi / 3,
|
||||||
|
endAngle: -math.pi / 3,
|
||||||
|
interval: secondDotsInterval,
|
||||||
|
dotOffset: edgeOffset,
|
||||||
|
color: color,
|
||||||
|
size: dotSize,
|
||||||
|
),
|
||||||
|
|
||||||
|
_BuildDot.second(
|
||||||
|
controller: _animationController,
|
||||||
|
beginAngle: 4 * math.pi / 3,
|
||||||
|
endAngle: math.pi / 3,
|
||||||
|
interval: secondDotsInterval,
|
||||||
|
dotOffset: edgeOffset,
|
||||||
|
color: color,
|
||||||
|
size: dotSize,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_animationController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BuildDot extends StatelessWidget {
|
||||||
|
const _BuildDot.second({
|
||||||
|
required this.controller,
|
||||||
|
required this.beginAngle,
|
||||||
|
required this.endAngle,
|
||||||
|
required this.interval,
|
||||||
|
required this.dotOffset,
|
||||||
|
required this.color,
|
||||||
|
required this.size,
|
||||||
|
}) : first = false;
|
||||||
|
|
||||||
|
const _BuildDot.first({
|
||||||
|
required this.controller,
|
||||||
|
required this.beginAngle,
|
||||||
|
required this.endAngle,
|
||||||
|
required this.interval,
|
||||||
|
required this.dotOffset,
|
||||||
|
required this.color,
|
||||||
|
required this.size,
|
||||||
|
}) : first = true;
|
||||||
|
final AnimationController controller;
|
||||||
|
final double beginAngle;
|
||||||
|
final double endAngle;
|
||||||
|
final Interval interval;
|
||||||
|
final double dotOffset;
|
||||||
|
final Color color;
|
||||||
|
final double size;
|
||||||
|
final bool first;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Visibility(
|
||||||
|
visible: first
|
||||||
|
? controller.value <= interval.end
|
||||||
|
: controller.value >= interval.begin,
|
||||||
|
child: Transform.rotate(
|
||||||
|
angle: controller.eval(
|
||||||
|
Tween<double>(begin: beginAngle, end: endAngle),
|
||||||
|
curve: interval,
|
||||||
|
),
|
||||||
|
child: Transform.translate(
|
||||||
|
offset: controller.eval(
|
||||||
|
Tween<Offset>(
|
||||||
|
begin: first ? Offset.zero : Offset(0, -dotOffset),
|
||||||
|
end: first ? Offset(0, -dotOffset) : Offset.zero,
|
||||||
|
),
|
||||||
|
curve: interval,
|
||||||
|
),
|
||||||
|
child: DrawDot.circular(
|
||||||
|
color: color,
|
||||||
|
dotSize: size,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DrawDot extends StatelessWidget {
|
||||||
|
const DrawDot.circular({
|
||||||
|
required double dotSize,
|
||||||
|
required this.color,
|
||||||
|
super.key,
|
||||||
|
}) : width = dotSize,
|
||||||
|
height = dotSize,
|
||||||
|
circular = true;
|
||||||
|
|
||||||
|
const DrawDot.elliptical({
|
||||||
|
required this.width,
|
||||||
|
required this.height,
|
||||||
|
required this.color,
|
||||||
|
super.key,
|
||||||
|
}) : circular = false;
|
||||||
|
final double width;
|
||||||
|
final double height;
|
||||||
|
final bool circular;
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: color,
|
||||||
|
shape: circular ? BoxShape.circle : BoxShape.rectangle,
|
||||||
|
borderRadius: circular
|
||||||
|
? null
|
||||||
|
: BorderRadius.all(Radius.elliptical(width, height)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue