multiple bug fixes

This commit is contained in:
otsmr 2025-07-19 02:31:07 +02:00
parent 0ec9e18046
commit 5209825604
15 changed files with 125 additions and 88 deletions

View file

@ -4,7 +4,7 @@ on:
workflow_dispatch: {}
push:
branches:
- main
- main_ignore
# paths:
# - lib/**
# - pubspec.lock

11
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,11 @@
{
"configurations": [
{
"name": "Flutter",
"type": "dart",
"request": "launch",
"program": "lib/main.dart",
"flutterMode": "profile"
}
]
}

View file

@ -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";

View file

@ -81,9 +81,13 @@ Future<void> initFileDownloader() async {
await FileDownloader().start();
try {
await FileDownloader().configure(androidConfig: [
(Config.bypassTLSCertificateValidation, kDebugMode),
]);
} catch (e) {
Log.error(e);
}
if (kDebugMode) {
FileDownloader().configureNotification(

View file

@ -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<void> customLocalPushNotification(String title, String msg) async {
);
await flutterLocalNotificationsPlugin.show(
999999 + Random.secure().nextInt(9999),
gMediaShowInfinite + Random.secure().nextInt(9999),
title,
msg,
notificationDetails,

View file

@ -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<void> Function(int sCameraId, bool init, bool enableAudio)
selectCamera;
@override
State<CameraZoomButtons> createState() => _CameraZoomButtonsState();
@ -78,6 +79,15 @@ class _CameraZoomButtonsState extends State<CameraZoomButtons> {
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<CameraZoomButtons> {
),
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<CameraZoomButtons> {
},
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(
: Text(
widget.scaleFactor < 1
? '${currentLevel}x'
: '${minLevel}x',
style: zoomTextStyle,
);
} else {
return const Text('');
}
},
),
),
TextButton(
@ -128,10 +126,10 @@ class _CameraZoomButtonsState extends State<CameraZoomButtons> {
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<CameraZoomButtons> {
.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),
)
],
),

View file

@ -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<CameraController?> 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<CameraController?> 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<CameraPreviewView> {
}
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<CameraPreviewView> {
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<CameraPreviewView> {
});
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<CameraPreviewView> {
Icons.repeat_rounded,
tooltipText: context.lang.switchFrontAndBackCamera,
onPressed: () async {
widget.selectCamera(
await widget.selectCamera(
(widget.selectedCameraDetails.cameraId + 1) % 2,
false,
false);

View file

@ -31,7 +31,8 @@ class CameraSendToViewState extends State<CameraSendToView> {
super.dispose();
}
Future<void> selectCamera(int sCameraId, bool init, bool enableAudio) async {
Future<CameraController?> 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<CameraSendToView> {
cameraController = opts.$2;
}
setState(() {});
return cameraController;
}
Future<void> toggleSelectedCamera() async {

View file

@ -193,12 +193,12 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
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,

View file

@ -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<UserListItem> {
int lastMessageInSeconds = 0;
MessageSendState state = MessageSendState.send;
Message? currentMessage;
@ -321,18 +321,14 @@ class _UserListItem extends State<UserListItem> {
List<Message> 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<UserListItem> {
});
}
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<UserListItem> {
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,

View file

@ -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<LastMessageTime> createState() => _LastMessageTimeState();
}
class _LastMessageTimeState extends State<LastMessageTime> {
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),
);
}
}

View file

@ -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<ChatMediaEntry> {
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(() {

View file

@ -53,7 +53,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
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<MediaViewerView> {
videoController = null;
imageBytes = null;
canBeSeenUntil = null;
maxShowTime = 999999;
maxShowTime = gMediaShowInfinite;
imageSaving = false;
imageSaved = false;
mirrorVideo = false;

View file

@ -108,7 +108,8 @@ class HomeViewState extends State<HomeView> {
super.dispose();
}
Future<void> selectCamera(int sCameraId, bool init, bool enableAudio) async {
Future<CameraController?> 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<HomeView> {
initCameraStarted = false;
}
setState(() {});
return cameraController;
}
Future<void> toggleSelectedCamera() async {