mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-16 03:08:40 +00:00
bug fixes
This commit is contained in:
parent
5f2a9890ba
commit
84448f79ca
12 changed files with 155 additions and 47 deletions
68
lib/src/components/connection_state.dart
Normal file
68
lib/src/components/connection_state.dart
Normal 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
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ class ChatListEntry extends StatelessWidget {
|
|||
}),
|
||||
);
|
||||
} else {
|
||||
tryDownloadMedia(token, force: true);
|
||||
tryDownloadMedia(message.messageId, token, force: true);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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 &&
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue