From 5209825604aa9a1b85f6b4351465d9c375eb64f7 Mon Sep 17 00:00:00 2001 From: otsmr Date: Sat, 19 Jul 2025 02:31:07 +0200 Subject: [PATCH] multiple bug fixes --- .github/workflows/release_github.yml | 2 +- .vscode/launch.json | 11 ++++ ios/Runner.xcodeproj/project.pbxproj | 10 +--- lib/src/database/daos/contacts_dao.dart | 2 +- lib/src/services/api/media_upload.dart | 10 +++- .../background.notifications.dart | 4 +- .../zoom_selector.dart | 57 +++++++------------ .../camera_preview_controller_view.dart | 16 ++++-- lib/src/views/camera/camera_send_to_view.dart | 4 +- .../views/camera/share_image_editor_view.dart | 4 +- lib/src/views/chats/chat_list.view.dart | 28 +-------- .../last_message_time.dart | 49 ++++++++++++++++ .../chat_media_entry.dart | 8 +++ lib/src/views/chats/media_viewer.view.dart | 4 +- lib/src/views/home.view.dart | 4 +- 15 files changed, 125 insertions(+), 88 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 lib/src/views/chats/chat_list_components/last_message_time.dart diff --git a/.github/workflows/release_github.yml b/.github/workflows/release_github.yml index 8323c4f..fd8038d 100644 --- a/.github/workflows/release_github.yml +++ b/.github/workflows/release_github.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: {} push: branches: - - main + - main_ignore # paths: # - lib/** # - pubspec.lock diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..763eb9e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "configurations": [ + { + "name": "Flutter", + "type": "dart", + "request": "launch", + "program": "lib/main.dart", + "flutterMode": "profile" + } + ] +} \ No newline at end of file diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index a410578..ed10d98 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 77; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -401,14 +401,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; @@ -497,14 +493,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; diff --git a/lib/src/database/daos/contacts_dao.dart b/lib/src/database/daos/contacts_dao.dart index f2be674..f1a4123 100644 --- a/lib/src/database/daos/contacts_dao.dart +++ b/lib/src/database/daos/contacts_dao.dart @@ -220,7 +220,7 @@ String getContactDisplayName(Contact user) { name = applyStrikethrough(name); } if (name.length > 12) { - return '${name.substring(0, 12)} ...'; + return '${name.substring(0, 12)}...'; } return name; } diff --git a/lib/src/services/api/media_upload.dart b/lib/src/services/api/media_upload.dart index e72c9e3..d8a0054 100644 --- a/lib/src/services/api/media_upload.dart +++ b/lib/src/services/api/media_upload.dart @@ -81,9 +81,13 @@ Future initFileDownloader() async { await FileDownloader().start(); - await FileDownloader().configure(androidConfig: [ - (Config.bypassTLSCertificateValidation, kDebugMode), - ]); + try { + await FileDownloader().configure(androidConfig: [ + (Config.bypassTLSCertificateValidation, kDebugMode), + ]); + } catch (e) { + Log.error(e); + } if (kDebugMode) { FileDownloader().configureNotification( diff --git a/lib/src/services/notifications/background.notifications.dart b/lib/src/services/notifications/background.notifications.dart index 1f7e6da..fc6c455 100644 --- a/lib/src/services/notifications/background.notifications.dart +++ b/lib/src/services/notifications/background.notifications.dart @@ -10,6 +10,8 @@ import 'package:twonly/src/constants/secure_storage_keys.dart'; import 'package:twonly/src/model/protobuf/push_notification/push_notification.pb.dart'; import 'package:twonly/src/services/notifications/pushkeys.notifications.dart'; import 'package:twonly/src/utils/log.dart'; +import 'package:twonly/src/views/camera/share_image_editor_view.dart' + show gMediaShowInfinite; final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); @@ -30,7 +32,7 @@ Future customLocalPushNotification(String title, String msg) async { ); await flutterLocalNotificationsPlugin.show( - 999999 + Random.secure().nextInt(9999), + gMediaShowInfinite + Random.secure().nextInt(9999), title, msg, notificationDetails, diff --git a/lib/src/views/camera/camera_preview_components/zoom_selector.dart b/lib/src/views/camera/camera_preview_components/zoom_selector.dart index 1f9d526..a6d4400 100644 --- a/lib/src/views/camera/camera_preview_components/zoom_selector.dart +++ b/lib/src/views/camera/camera_preview_components/zoom_selector.dart @@ -22,7 +22,8 @@ class CameraZoomButtons extends StatefulWidget { final double scaleFactor; final Function updateScaleFactor; final SelectedCameraDetails selectedCameraDetails; - final void Function(int sCameraId, bool init, bool enableAudio) selectCamera; + final Future Function(int sCameraId, bool init, bool enableAudio) + selectCamera; @override State createState() => _CameraZoomButtonsState(); @@ -78,6 +79,15 @@ class _CameraZoomButtonsState extends State { final isMiddleFocused = widget.scaleFactor >= 1 && widget.scaleFactor < 2 && !(showWideAngleZoomIOS && widget.selectedCameraDetails.cameraId == 2); + + final maxLevel = max( + min(widget.selectedCameraDetails.maxAvailableZoom, 2), + widget.scaleFactor, + ); + + final minLevel = + beautifulZoomScale(widget.selectedCameraDetails.minAvailableZoom); + final currentLevel = beautifulZoomScale(widget.scaleFactor); return Center( child: ClipRRect( borderRadius: BorderRadius.circular(40), @@ -95,7 +105,7 @@ class _CameraZoomButtonsState extends State { ), onPressed: () async { if (showWideAngleZoomIOS) { - widget.selectCamera(2, true, false); + await widget.selectCamera(2, true, false); } else { final level = await widget.controller.getMinZoomLevel(); widget.updateScaleFactor(level); @@ -103,23 +113,11 @@ class _CameraZoomButtonsState extends State { }, child: showWideAngleZoomIOS ? const Text('0.5') - : FutureBuilder( - future: widget.controller.getMinZoomLevel(), - builder: (context, snap) { - if (snap.hasData) { - final minLevel = beautifulZoomScale(snap.data!); - final currentLevel = - beautifulZoomScale(widget.scaleFactor); - return Text( - widget.scaleFactor < 1 - ? '${currentLevel}x' - : '${minLevel}x', - style: zoomTextStyle, - ); - } else { - return const Text(''); - } - }, + : Text( + widget.scaleFactor < 1 + ? '${currentLevel}x' + : '${minLevel}x', + style: zoomTextStyle, ), ), TextButton( @@ -128,10 +126,10 @@ class _CameraZoomButtonsState extends State { isMiddleFocused ? Colors.yellow : Colors.white, ), ), - onPressed: () { + onPressed: () async { if (showWideAngleZoomIOS && widget.selectedCameraDetails.cameraId == 2) { - widget.selectCamera(0, true, false); + await widget.selectCamera(0, true, false); } else { widget.updateScaleFactor(1.0); } @@ -154,21 +152,8 @@ class _CameraZoomButtonsState extends State { .toDouble(); widget.updateScaleFactor(level); }, - child: FutureBuilder( - future: widget.controller.getMaxZoomLevel(), - builder: (context, snap) { - if (snap.hasData) { - final maxLevel = max( - min((snap.data?.toInt())!, 2), - widget.scaleFactor, - ); - return Text( - '${beautifulZoomScale(maxLevel.toDouble())}x', - style: zoomTextStyle); - } else { - return const Text(''); - } - }), + child: Text('${beautifulZoomScale(maxLevel.toDouble())}x', + style: zoomTextStyle), ) ], ), diff --git a/lib/src/views/camera/camera_preview_controller_view.dart b/lib/src/views/camera/camera_preview_controller_view.dart index 44705e6..9a7acf1 100644 --- a/lib/src/views/camera/camera_preview_controller_view.dart +++ b/lib/src/views/camera/camera_preview_controller_view.dart @@ -92,7 +92,8 @@ class CameraPreviewControllerView extends StatelessWidget { this.sendTo, }); final Contact? sendTo; - final void Function(int sCameraId, bool init, bool enableAudio) selectCamera; + final Future Function( + int sCameraId, bool init, bool enableAudio) selectCamera; final CameraController? cameraController; final SelectedCameraDetails selectedCameraDetails; final ScreenshotController screenshotController; @@ -135,7 +136,8 @@ class CameraPreviewView extends StatefulWidget { this.sendTo, }); final Contact? sendTo; - final void Function(int sCameraId, bool init, bool enableAudio) selectCamera; + final Future Function( + int sCameraId, bool init, bool enableAudio) selectCamera; final CameraController? cameraController; final SelectedCameraDetails selectedCameraDetails; final ScreenshotController screenshotController; @@ -299,7 +301,8 @@ class _CameraPreviewViewState extends State { } return true; } - widget.selectCamera(widget.selectedCameraDetails.cameraId, false, false); + await widget.selectCamera( + widget.selectedCameraDetails.cameraId, false, false); return false; } @@ -355,8 +358,9 @@ class _CameraPreviewViewState extends State { widget.cameraController!.value.isRecordingVideo) { return; } + var cameraController = widget.cameraController; if (hasAudioPermission && videoWithAudio) { - widget.selectCamera( + cameraController = await widget.selectCamera( widget.selectedCameraDetails.cameraId, false, await Permission.microphone.isGranted && videoWithAudio, @@ -368,7 +372,7 @@ class _CameraPreviewViewState extends State { }); try { - await widget.cameraController?.startVideoRecording(); + await cameraController?.startVideoRecording(); videoRecordingTimer = Timer.periodic(const Duration(milliseconds: 15), (timer) { setState(() { @@ -522,7 +526,7 @@ class _CameraPreviewViewState extends State { Icons.repeat_rounded, tooltipText: context.lang.switchFrontAndBackCamera, onPressed: () async { - widget.selectCamera( + await widget.selectCamera( (widget.selectedCameraDetails.cameraId + 1) % 2, false, false); diff --git a/lib/src/views/camera/camera_send_to_view.dart b/lib/src/views/camera/camera_send_to_view.dart index 07231d5..4f7d9fa 100644 --- a/lib/src/views/camera/camera_send_to_view.dart +++ b/lib/src/views/camera/camera_send_to_view.dart @@ -31,7 +31,8 @@ class CameraSendToViewState extends State { super.dispose(); } - Future selectCamera(int sCameraId, bool init, bool enableAudio) async { + Future selectCamera( + int sCameraId, bool init, bool enableAudio) async { final opts = await initializeCameraController( selectedCameraDetails, sCameraId, init, enableAudio); if (opts != null) { @@ -39,6 +40,7 @@ class CameraSendToViewState extends State { cameraController = opts.$2; } setState(() {}); + return cameraController; } Future toggleSelectedCamera() async { diff --git a/lib/src/views/camera/share_image_editor_view.dart b/lib/src/views/camera/share_image_editor_view.dart index 3610255..69f02bb 100644 --- a/lib/src/views/camera/share_image_editor_view.dart +++ b/lib/src/views/camera/share_image_editor_view.dart @@ -193,12 +193,12 @@ class _ShareImageEditorView extends State { NotificationBadge( count: (widget.videoFilePath != null) ? '0' - : maxShowTime == 999999 + : maxShowTime == gMediaShowInfinite ? '∞' : maxShowTime.toString(), child: ActionButton( (widget.videoFilePath != null) - ? maxShowTime == 999999 + ? maxShowTime == gMediaShowInfinite ? Icons.repeat_rounded : Icons.repeat_one_rounded : Icons.timer_outlined, diff --git a/lib/src/views/chats/chat_list.view.dart b/lib/src/views/chats/chat_list.view.dart index 38cb4b5..64bf230 100644 --- a/lib/src/views/chats/chat_list.view.dart +++ b/lib/src/views/chats/chat_list.view.dart @@ -19,6 +19,7 @@ import 'package:twonly/src/views/chats/add_new_user.view.dart'; import 'package:twonly/src/views/chats/chat_list_components/backup_notice.card.dart'; import 'package:twonly/src/views/chats/chat_list_components/connection_info.comp.dart'; import 'package:twonly/src/views/chats/chat_list_components/demo_user.card.dart'; +import 'package:twonly/src/views/chats/chat_list_components/last_message_time.dart'; import 'package:twonly/src/views/chats/chat_messages.view.dart'; import 'package:twonly/src/views/chats/media_viewer.view.dart'; import 'package:twonly/src/views/chats/start_new_chat.view.dart'; @@ -309,7 +310,6 @@ class UserListItem extends StatefulWidget { } class _UserListItem extends State { - int lastMessageInSeconds = 0; MessageSendState state = MessageSendState.send; Message? currentMessage; @@ -321,18 +321,14 @@ class _UserListItem extends State { List previewMessages = []; - Timer? updateTime; - @override void initState() { super.initState(); initStreams(); - lastUpdateTime(); } @override void dispose() { - updateTime?.cancel(); messagesNotOpenedStream.cancel(); lastMessageStream.cancel(); super.dispose(); @@ -385,22 +381,6 @@ class _UserListItem extends State { }); } - void lastUpdateTime() { - // Change the color every 200 milliseconds - updateTime = Timer.periodic(const Duration(milliseconds: 200), (timer) { - setState(() { - if (currentMessage != null) { - lastMessageInSeconds = DateTime.now() - .difference(currentMessage!.openedAt ?? currentMessage!.sendAt) - .inSeconds; - if (lastMessageInSeconds < 0) { - lastMessageInSeconds = 0; - } - } - }); - }); - } - @override Widget build(BuildContext context) { final flameCounter = getFlameCounterFromContact(widget.user); @@ -432,10 +412,8 @@ class _UserListItem extends State { MessageSendStateIcon(previewMessages), const Text('•'), const SizedBox(width: 5), - Text( - formatDuration(lastMessageInSeconds), - style: const TextStyle(fontSize: 12), - ), + if (currentMessage != null) + LastMessageTime(message: currentMessage!), if (flameCounter > 0) FlameCounterWidget( widget.user, diff --git a/lib/src/views/chats/chat_list_components/last_message_time.dart b/lib/src/views/chats/chat_list_components/last_message_time.dart new file mode 100644 index 0000000..b1f229f --- /dev/null +++ b/lib/src/views/chats/chat_list_components/last_message_time.dart @@ -0,0 +1,49 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:twonly/src/database/twonly_database.dart'; +import 'package:twonly/src/utils/misc.dart'; + +class LastMessageTime extends StatefulWidget { + const LastMessageTime({required this.message, super.key}); + + final Message message; + + @override + State createState() => _LastMessageTimeState(); +} + +class _LastMessageTimeState extends State { + Timer? updateTime; + int lastMessageInSeconds = 0; + + @override + void initState() { + super.initState(); + // Change the color every 200 milliseconds + updateTime = Timer.periodic(const Duration(milliseconds: 500), (timer) { + setState(() { + lastMessageInSeconds = DateTime.now() + .difference(widget.message.openedAt ?? widget.message.sendAt) + .inSeconds; + if (lastMessageInSeconds < 0) { + lastMessageInSeconds = 0; + } + }); + }); + } + + @override + void dispose() { + updateTime?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Text( + formatDuration(lastMessageInSeconds), + style: const TextStyle(fontSize: 12), + ); + } +} diff --git a/lib/src/views/chats/chat_messages_components/chat_media_entry.dart b/lib/src/views/chats/chat_messages_components/chat_media_entry.dart index 514291d..09b477c 100644 --- a/lib/src/views/chats/chat_messages_components/chat_media_entry.dart +++ b/lib/src/views/chats/chat_messages_components/chat_media_entry.dart @@ -8,6 +8,8 @@ import 'package:twonly/src/model/memory_item.model.dart'; import 'package:twonly/src/model/protobuf/push_notification/push_notification.pbserver.dart'; import 'package:twonly/src/services/api/media_download.dart' as received; import 'package:twonly/src/services/api/messages.dart'; +import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/views/camera/share_image_editor_view.dart'; import 'package:twonly/src/views/chats/chat_messages_components/in_chat_media_viewer.dart'; import 'package:twonly/src/views/chats/media_viewer.view.dart'; import 'package:twonly/src/views/tutorial/tutorials.dart'; @@ -46,6 +48,12 @@ class _ChatMediaEntryState extends State { widget.message.mediaStored) { return; } + final content = getMediaContent(widget.message); + if (content == null || + content.isRealTwonly || + content.maxShowTime != gMediaShowInfinite) { + return; + } if (await received.existsMediaFile(widget.message.messageId, 'png')) { if (mounted) { setState(() { diff --git a/lib/src/views/chats/media_viewer.view.dart b/lib/src/views/chats/media_viewer.view.dart index 4fa1811..de08f44 100644 --- a/lib/src/views/chats/media_viewer.view.dart +++ b/lib/src/views/chats/media_viewer.view.dart @@ -53,7 +53,7 @@ class _MediaViewerViewState extends State { VideoPlayerController? videoController; DateTime? canBeSeenUntil; - int maxShowTime = 999999; + int maxShowTime = gMediaShowInfinite; double progress = 0; bool isRealTwonly = false; bool mirrorVideo = false; @@ -157,7 +157,7 @@ class _MediaViewerViewState extends State { videoController = null; imageBytes = null; canBeSeenUntil = null; - maxShowTime = 999999; + maxShowTime = gMediaShowInfinite; imageSaving = false; imageSaved = false; mirrorVideo = false; diff --git a/lib/src/views/home.view.dart b/lib/src/views/home.view.dart index 38db6a1..b4f28a2 100644 --- a/lib/src/views/home.view.dart +++ b/lib/src/views/home.view.dart @@ -108,7 +108,8 @@ class HomeViewState extends State { super.dispose(); } - Future selectCamera(int sCameraId, bool init, bool enableAudio) async { + Future selectCamera( + int sCameraId, bool init, bool enableAudio) async { final opts = await initializeCameraController( selectedCameraDetails, sCameraId, init, enableAudio); if (opts != null) { @@ -117,6 +118,7 @@ class HomeViewState extends State { initCameraStarted = false; } setState(() {}); + return cameraController; } Future toggleSelectedCamera() async {