bug fixes

This commit is contained in:
otsmr 2025-02-09 19:50:15 +01:00
parent 5f2a9890ba
commit 84448f79ca
12 changed files with 155 additions and 47 deletions

View file

@ -0,0 +1,68 @@
import 'package:flutter/material.dart';
class ConnectionInfo extends StatefulWidget {
const ConnectionInfo({super.key});
@override
State<ConnectionInfo> createState() => _ConnectionInfoWidgetState();
}
class _ConnectionInfoWidgetState extends State<ConnectionInfo> {
int redColorOpacity = 100; // Initial opacity value
bool redColorGoUp = true; // Direction of the opacity change
double screenWidth = 0; // To hold the screen width
@override
void initState() {
super.initState();
_startColorAnimation();
}
void _startColorAnimation() {
// Change the color every 200 milliseconds
Future.delayed(Duration(milliseconds: 200), () {
setState(() {
if (redColorOpacity <= 100) {
redColorGoUp = true;
}
if (redColorOpacity >= 150) {
redColorGoUp = false;
}
if (redColorGoUp) {
redColorOpacity += 10;
} else {
redColorOpacity -= 10;
}
});
_startColorAnimation(); // Repeat the animation
});
}
@override
Widget build(BuildContext context) {
screenWidth = MediaQuery.of(context).size.width; // Get the screen width
return Stack(
children: [
Positioned(
top: 3, // Position it at the top
left: (screenWidth * 0.5) / 2, // Center it horizontally
child: AnimatedContainer(
duration: Duration(milliseconds: 100),
width: screenWidth * 0.5, // 50% of the screen width
decoration: BoxDecoration(
border: Border.all(
color: Colors.red[600]!
.withAlpha(redColorOpacity), // Use the animated opacity
width: 2.0, // Red border width
),
borderRadius: BorderRadius.all(
Radius.circular(10.0),
), // Rounded corners
),
),
),
],
);
}
}

View file

@ -68,6 +68,10 @@
"contactVerifyNumberMarkAsVerified": "Als verifiziert markieren",
"contactVerifyNumberClearVerification": "Verifizierung aufheben",
"contactVerifyNumberLongDesc": "Um die Ende-zu-Ende-Verschlüsselung mit {username} zu verifizieren, vergleiche die Zahlen mit ihrem Gerät. Die Person kann auch deinen Code mit ihrem Gerät scannen.",
"contactNickame": "Spitzname",
"contactBlock": "Blockieren",
"contactBlockTitle": "Blockiere {username}",
"contactBlockBody": "Ein blockierter Benutzer kann dir keine Nachrichten mehr senden, und sein Profil ist nicht mehr sichtbar. Um die Blockierung eines Benutzers aufzuheben, navigiere einfach zu Einstellungen > Datenschutz > Blockierte Benutzer.",
"undo": "Rückgängig",
"redo": "Wiederholen",
"next": "Weiter",

View file

@ -68,6 +68,10 @@
"contactVerifyNumberMarkAsVerified": "Mark as verified",
"contactVerifyNumberClearVerification": "Clear verification",
"contactVerifyNumberLongDesc": "To verify the end-to-end encryption with {username}, compare the numbers with their device. The person can also scan your code with their device.",
"contactNickame": "Nickname",
"contactBlock": "Block",
"contactBlockTitle": "Block {username}",
"contactBlockBody": "A blocked user will no longer be able to send you messages and their profile will be hidden from view. To unblock a user, simply navigate to Settings > Privacy > Blocked Users.",
"undo": "Undo",
"redo": "Redo",
"next": "Next",

View file

@ -143,7 +143,7 @@ class DbMessages extends CvModelBase {
}
}
static Future deleteMessageById(int messageId) async {
static Future<int?> deleteMessageById(int messageId) async {
await dbProvider.db!.delete(
tableName,
where: '$columnMessageId = ?',
@ -153,6 +153,7 @@ class DbMessages extends CvModelBase {
if (fromUserId != null) {
globalCallBackOnMessageChange(fromUserId);
}
return fromUserId;
}
static Future<int?> getFromUserIdByMessageId(int messageId) async {

View file

@ -216,7 +216,9 @@ Future sendImage(
}
}
Future tryDownloadMedia(List<int> mediaToken, {bool force = false}) async {
Future tryDownloadMedia(int messageId, List<int> mediaToken,
{bool force = false}) async {
if (globalIsAppInBackground) return;
if (!force) {
// TODO: create option to enable download via mobile data
final List<ConnectivityResult> connectivityResult =
@ -234,6 +236,7 @@ Future tryDownloadMedia(List<int> mediaToken, {bool force = false}) async {
offset = media.length;
}
globalCallBackOnDownloadChange(mediaToken, true);
box.put("${mediaToken}_messageId", messageId);
apiProvider.triggerDownload(mediaToken, offset);
}
@ -254,7 +257,13 @@ Future userOpenedOtherMessage(int fromUserId, int messageOtherId) async {
Future<Uint8List?> getDownloadedMedia(
List<int> mediaToken, int messageOtherId) async {
final box = await getMediaStorage();
Uint8List? media = box.get("${mediaToken}_downloaded");
Uint8List? media;
try {
media = box.get("${mediaToken}_downloaded");
} catch (e) {
return null;
}
if (media == null) return null;
int fromUserId = box.get("${mediaToken}_fromUserId");
await userOpenedOtherMessage(fromUserId, messageOtherId);

View file

@ -59,9 +59,9 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
int? messageId = box.get("${data.uploadToken}_messageId");
if (messageId != null) {
await DbMessages.deleteMessageById(messageId);
int? fromUserId = await DbMessages.deleteMessageById(messageId);
box.delete(boxId);
int? fromUserId = box.get("${data.uploadToken}_fromUserId");
// int? fromUserId = box.get("${data.uploadToken}_fromUserId");
if (fromUserId != null) {
globalCallBackOnMessageChange(fromUserId);
}
@ -70,6 +70,10 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
globalCallBackOnDownloadChange(data.uploadToken, false);
var ok = client.Response_Ok()..none = true;
return client.Response()..ok = ok;
} else {
globalCallBackOnDownloadChange(data.uploadToken, false);
var ok = client.Response_Ok()..none = true;
return client.Response()..ok = ok;
}
}
@ -183,8 +187,7 @@ Future<client.Response> handleNewMessage(
List<int> downloadToken = content.downloadToken;
Box box = await getMediaStorage();
box.put("${downloadToken}_fromUserId", fromUserId.toInt());
box.put("${downloadToken}_messageId", messageId);
tryDownloadMedia(downloadToken);
tryDownloadMedia(messageId, downloadToken);
}
}
localPushNotificationNewMessage(

View file

@ -179,20 +179,31 @@ class ApiProvider {
return Result.error(ErrorCode.InternalError);
}
}
if (authenticated) {
await authenticate();
if (_channel == null) {
return Result.error(ErrorCode.InternalError);
}
var seq = Int64(Random().nextInt(4294967296));
while (messagesV0.containsKey(seq)) {
seq = Int64(Random().nextInt(4294967296));
}
request.v0.seq = seq;
final requestBytes = request.writeToBuffer();
_channel!.sink.add(requestBytes);
return asResult(await _waitForResponse(seq));
Result res = asResult(await _waitForResponse(seq));
if (res.isError) {
if (res.error == ErrorCode.SessionNotAuthenticated) {
isAuthenticated = false;
if (authenticated) {
await authenticate();
// this will send the request one more time.
return _sendRequestV0(request, authenticated: false);
}
}
}
return res;
}
Future authenticate() async {

View file

@ -41,6 +41,7 @@ class WebsocketForegroundTask extends TaskHandler {
// Called when data is sent using `FlutterForegroundTask.sendDataToTask`.
@override
void onReceiveData(Object data) {
apiProvider.close(() {});
print('onReceiveData: $data');
}
@ -53,7 +54,7 @@ class WebsocketForegroundTask extends TaskHandler {
// Called when the notification itself is pressed.
@override
void onNotificationPressed() {
print('onNotificationPressed');
apiProvider.close(() {});
}
// Called when the notification itself is dismissed.

View file

@ -79,7 +79,7 @@ class ChatListEntry extends StatelessWidget {
}),
);
} else {
tryDownloadMedia(token, force: true);
tryDownloadMedia(message.messageId, token, force: true);
}
}
},

View file

@ -213,7 +213,7 @@ class _UserListItem extends State<UserListItem> {
onTap: () {
if (isDownloading) return;
if (!widget.lastMessage.isDownloaded) {
tryDownloadMedia(token, force: true);
tryDownloadMedia(widget.lastMessage.messageId, token, force: true);
return;
}
if (state == MessageSendState.received &&

View file

@ -1,7 +1,6 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:local_auth/local_auth.dart';
import 'package:lottie/lottie.dart';
import 'package:no_screenshot/no_screenshot.dart';
@ -74,6 +73,12 @@ class _MediaViewerViewState extends State<MediaViewerView> {
List<int> token = content.downloadToken;
_imageByte =
await getDownloadedMedia(token, widget.message.messageOtherId!);
if (_imageByte == null) {
// image already deleted
if (context.mounted) {
Navigator.pop(context);
}
}
setState(() {});
}
@ -204,31 +209,31 @@ class _MediaViewerViewState extends State<MediaViewerView> {
],
),
),
if (_imageByte != null && false)
Positioned(
bottom: 30,
left: 0,
right: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// const SizedBox(width: 20),
FilledButton.icon(
icon: FaIcon(FontAwesomeIcons.solidPaperPlane),
onPressed: () async {},
style: ButtonStyle(
padding: WidgetStateProperty.all<EdgeInsets>(
EdgeInsets.symmetric(vertical: 10, horizontal: 30),
),
),
label: Text(
"Respond",
style: TextStyle(fontSize: 17),
),
),
],
),
),
// if (_imageByte != null)
// Positioned(
// bottom: 30,
// left: 0,
// right: 0,
// child: Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// // const SizedBox(width: 20),
// FilledButton.icon(
// icon: FaIcon(FontAwesomeIcons.solidPaperPlane),
// onPressed: () async {},
// style: ButtonStyle(
// padding: WidgetStateProperty.all<EdgeInsets>(
// EdgeInsets.symmetric(vertical: 10, horizontal: 30),
// ),
// ),
// label: Text(
// "Respond",
// style: TextStyle(fontSize: 17),
// ),
// ),
// ],
// ),
// ),
],
),
),

View file

@ -8,6 +8,7 @@ import 'package:twonly/src/components/verified_shield.dart';
import 'package:twonly/src/model/contacts_model.dart';
import 'package:flutter/material.dart';
import 'package:twonly/src/providers/messages_change_provider.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/contact/contact_verify_view.dart';
class ContactView extends StatefulWidget {
@ -67,7 +68,7 @@ class _ContactViewState extends State<ContactView> {
SizedBox(height: 50),
BetterListTile(
icon: FontAwesomeIcons.pencil,
text: "Nickname",
text: context.lang.contactNickame,
onTap: () async {
String? newUsername =
await showNicknameChangeDialog(context, widget.contact);
@ -80,7 +81,7 @@ class _ContactViewState extends State<ContactView> {
const Divider(),
BetterListTile(
icon: FontAwesomeIcons.shieldHeart,
text: "Verify safety number",
text: context.lang.contactVerifyNumberTitle,
onTap: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) {
@ -92,12 +93,13 @@ class _ContactViewState extends State<ContactView> {
BetterListTile(
icon: FontAwesomeIcons.ban,
color: Colors.red,
text: "Block",
text: context.lang.contactBlock,
onTap: () async {
bool block = await showAlertDialog(
context,
"Block ${widget.contact.displayName}",
"A blocked user will no longer be able to send you messages and their profile will be hidden from view. To unblock a user, simply navigate to Settings > Privacy > Blocked Users..");
context,
context.lang.contactBlockTitle(widget.contact.displayName),
context.lang.contactBlockBody,
);
if (block) {
await DbContacts.blockUser(widget.contact.userId.toInt());
// go back to the first