mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 07:48:40 +00:00
Merge pull request #325 from twonlyapp/dev
Some checks failed
Publish on Github / build_and_publish (push) Has been cancelled
Some checks failed
Publish on Github / build_and_publish (push) Has been cancelled
Multiple bug fixes
This commit is contained in:
commit
b306165681
29 changed files with 148 additions and 94 deletions
12
README.md
12
README.md
|
|
@ -4,6 +4,14 @@
|
|||
|
||||
This repository contains the complete source code of the [twonly](https://twonly.eu) apps.
|
||||
|
||||
<a href="https://testflight.apple.com/join/U9B3v2rk" >
|
||||
<img alt="Get it on Testflight button" src="https://twonly.eu/assets/buttons/get-it-on-testflight.png"
|
||||
width="100px" />
|
||||
</a>
|
||||
<a href="https://releases.twonly.eu/fdroid/repo/">
|
||||
<img alt="Get it on F-Droid button" src="https://twonly.eu/assets/buttons/get-it-on-f-droid.webp" width="100px" />
|
||||
</a>
|
||||
|
||||
## Features
|
||||
|
||||
- Offer a Snapchat™ like experience
|
||||
|
|
@ -54,11 +62,11 @@ run-as eu.twonly.testing ls /data/user/0/eu.twonly.testing/
|
|||
|
||||
## Signing Keys
|
||||
|
||||
When you download the app **via GitHub** you can verify the signing keys using for example the [AppVerifyer](https://github.com/soupslurpr/AppVerifier) and the following SHA-256 fingerprint of the signing certificate.
|
||||
When you download the app **via GitHub or F-Droid** you can verify the signing keys using for example the [AppVerifyer](https://github.com/soupslurpr/AppVerifier) and the following SHA-256 fingerprint of the signing certificate.
|
||||
|
||||
eu.twonly
|
||||
E3:C4:4D:56:8C:67:F9:32:AC:8C:33:90:99:8A:B9:5E:E8:FF:2D:7A:07:3C:24:E3:66:77:93:E6:EA:CD:77:0A
|
||||
|
||||
|
||||
## License
|
||||
This project is licensed under the [GNU AGPL 3.0](LICENSE) license.
|
||||
This project is licensed under the [GNU AGPL 3.0](LICENSE) license.
|
||||
|
|
|
|||
|
|
@ -238,7 +238,7 @@ func getPushNotificationText(pushNotification: PushNotification) -> (String, Str
|
|||
.text: "sent a message{inGroup}.",
|
||||
.twonly: "sent a twonly{inGroup}.",
|
||||
.video: "sent a video{inGroup}.",
|
||||
.image: "sent a image{inGroup}.",
|
||||
.image: "sent an image{inGroup}.",
|
||||
.audio: "sent a voice message{inGroup}.",
|
||||
.contactRequest: "wants to connect with you.",
|
||||
.acceptRequest: "is now connected with you.",
|
||||
|
|
|
|||
|
|
@ -287,7 +287,7 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
|
|||
..where((t) => t.groupId.equals(groupId)))
|
||||
.getSingle();
|
||||
|
||||
final totalMediaCounter = group.totalMediaCounter + (received ? 0 : 1);
|
||||
final totalMediaCounter = group.totalMediaCounter + 1;
|
||||
var flameCounter = group.flameCounter;
|
||||
var maxFlameCounter = group.maxFlameCounter;
|
||||
var maxFlameCounterFrom = group.maxFlameCounterFrom;
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
|||
milliseconds: group.deleteMessagesAfterMilliseconds,
|
||||
),
|
||||
);
|
||||
final affected = await (delete(messages)
|
||||
await (delete(messages)
|
||||
..where(
|
||||
(m) =>
|
||||
m.groupId.equals(group.groupId) &
|
||||
|
|
@ -127,7 +127,6 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
|||
m.createdAt.isSmallerThanValue(deletionTime))),
|
||||
))
|
||||
.go();
|
||||
Log.info('Deleted $affected messages.');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
|
|||
return await (select(receipts)..where((t) => t.rowId.equals(id)))
|
||||
.getSingle();
|
||||
} catch (e) {
|
||||
Log.error(e);
|
||||
// ignore error, receipts is already in the database...
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -562,7 +562,7 @@
|
|||
"notificationText": "sent a message{inGroup}.",
|
||||
"notificationTwonly": "sent a twonly{inGroup}.",
|
||||
"notificationVideo": "sent a video{inGroup}.",
|
||||
"notificationImage": "sent a image{inGroup}.",
|
||||
"notificationImage": "sent an image{inGroup}.",
|
||||
"notificationAudio": "sent a voice message{inGroup}.",
|
||||
"notificationAddedToGroup": "has added you to \"{groupname}\"",
|
||||
"notificationContactRequest": "wants to connect with you.",
|
||||
|
|
|
|||
|
|
@ -2477,7 +2477,7 @@ abstract class AppLocalizations {
|
|||
/// No description provided for @notificationImage.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'sent a image{inGroup}.'**
|
||||
/// **'sent an image{inGroup}.'**
|
||||
String notificationImage(Object inGroup);
|
||||
|
||||
/// No description provided for @notificationAudio.
|
||||
|
|
|
|||
|
|
@ -1345,7 +1345,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String notificationImage(Object inGroup) {
|
||||
return 'sent a image$inGroup.';
|
||||
return 'sent an image$inGroup.';
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ class ApiService {
|
|||
}
|
||||
|
||||
Future<void> _onError(dynamic e) async {
|
||||
Log.error('websocket error: $e');
|
||||
Log.warn('websocket error: $e');
|
||||
await onClosed();
|
||||
}
|
||||
|
||||
|
|
@ -206,7 +206,7 @@ class ApiService {
|
|||
Future<server.ServerToClient?> _waitForResponse(Int64 seq) async {
|
||||
final startTime = DateTime.now();
|
||||
|
||||
const timeout = Duration(seconds: 20);
|
||||
const timeout = Duration(seconds: 60);
|
||||
|
||||
while (true) {
|
||||
if (messagesV0[seq] != null) {
|
||||
|
|
@ -215,7 +215,7 @@ class ApiService {
|
|||
return tmp;
|
||||
}
|
||||
if (DateTime.now().difference(startTime) > timeout) {
|
||||
Log.error('Timeout for message $seq');
|
||||
Log.warn('Timeout for message $seq');
|
||||
return null;
|
||||
}
|
||||
await Future.delayed(const Duration(milliseconds: 10));
|
||||
|
|
@ -283,10 +283,6 @@ class ApiService {
|
|||
request.v0.seq = seq;
|
||||
final requestBytes = request.writeToBuffer();
|
||||
|
||||
Log.info(
|
||||
'Sending ${requestBytes.length} bytes to the server via WebSocket.',
|
||||
);
|
||||
|
||||
if (ensureRetransmission) {
|
||||
await addToRetransmissionBuffer(seq, requestBytes);
|
||||
}
|
||||
|
|
@ -421,7 +417,7 @@ class ApiService {
|
|||
|
||||
final result = await sendRequestSync(req, authenticated: false);
|
||||
if (result.isError) {
|
||||
Log.error('could not request auth challenge', result);
|
||||
Log.warn('could not request auth challenge', result);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -100,9 +100,11 @@ Future<void> handleDownloadStatusUpdate(TaskStatusUpdate update) async {
|
|||
failed = false;
|
||||
} else {
|
||||
failed = true;
|
||||
Log.error(
|
||||
'Got invalid response status code: ${update.responseStatusCode}',
|
||||
);
|
||||
if (update.responseStatusCode != null) {
|
||||
Log.error(
|
||||
'Got invalid response status code: ${update.responseStatusCode}',
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.info('Got ${update.status} for $mediaId');
|
||||
|
|
@ -110,7 +112,6 @@ Future<void> handleDownloadStatusUpdate(TaskStatusUpdate update) async {
|
|||
}
|
||||
|
||||
if (failed) {
|
||||
Log.error('Background media upload failed: ${update.status}');
|
||||
await requestMediaReupload(mediaId);
|
||||
} else {
|
||||
await handleEncryptedFile(mediaId);
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ Future<(Uint8List, Uint8List?)?> tryToSendCompleteMessage({
|
|||
if (receipt == null) {
|
||||
receipt = await twonlyDB.receiptsDao.getReceiptById(receiptId!);
|
||||
if (receipt == null) {
|
||||
Log.error('Receipt not found.');
|
||||
Log.warn('Receipt not found.');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,7 +134,6 @@ Future<void> handleClient2ClientMessage(int fromUserId, Uint8List body) async {
|
|||
..receiptId = receiptId
|
||||
..type = Message_Type.PLAINTEXT_CONTENT
|
||||
..plaintextContent = responsePlaintextContent;
|
||||
Log.error('Sending decryption error');
|
||||
} else {
|
||||
response = Message()..type = Message_Type.SENDER_DELIVERY_RECEIPT;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,13 @@ Future<void> initFCMAfterAuthenticated() async {
|
|||
final storedToken = await storage.read(key: SecureStorageKeys.googleFcm);
|
||||
|
||||
try {
|
||||
if (Platform.isIOS) {
|
||||
final apnsToken = await FirebaseMessaging.instance.getAPNSToken();
|
||||
if (apnsToken == null) {
|
||||
Log.error('Error getting apnsToken');
|
||||
return;
|
||||
}
|
||||
}
|
||||
final fcmToken = await FirebaseMessaging.instance.getToken();
|
||||
if (fcmToken == null) {
|
||||
Log.error('Error getting fcmToken');
|
||||
|
|
|
|||
|
|
@ -35,11 +35,7 @@ class MediaFileService {
|
|||
|
||||
final service = await MediaFileService.fromMediaId(mediaId);
|
||||
|
||||
if (service == null) {
|
||||
Log.error(
|
||||
'Purging media file, as it is not in the database $mediaId.',
|
||||
);
|
||||
} else {
|
||||
if (service != null) {
|
||||
if (service.mediaFile.isDraftMedia) {
|
||||
delete = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ Future<(EncryptedContent?, PlaintextContent_DecryptionErrorMessage_Type?)>
|
|||
|
||||
return (EncryptedContent.fromBuffer(plaintext), null);
|
||||
} on InvalidKeyIdException catch (e) {
|
||||
Log.error(e);
|
||||
Log.warn(e);
|
||||
return (null, PlaintextContent_DecryptionErrorMessage_Type.PREKEY_UNKNOWN);
|
||||
} catch (e) {
|
||||
Log.error(e);
|
||||
|
|
|
|||
|
|
@ -50,8 +50,7 @@ Future<void> requestNewPrekeysForContact(int contactId) async {
|
|||
.toList();
|
||||
await twonlyDB.signalDao.insertPreKeys(preKeys);
|
||||
} else {
|
||||
// 104400
|
||||
Log.error('[PREKEY] Could not load new pre keys for user $contactId');
|
||||
Log.warn('[PREKEY] Could not load new pre keys for user $contactId');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -85,7 +84,7 @@ Future<void> requestNewSignedPreKeyForContact(int contactId) async {
|
|||
),
|
||||
);
|
||||
} else {
|
||||
Log.error('could not load new signed pre key for user $contactId');
|
||||
Log.warn('could not load new signed pre key for user $contactId');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,9 +203,6 @@ Future<void> performTwonlySafeBackup({bool force = false}) async {
|
|||
Future<void> handleBackupStatusUpdate(TaskStatusUpdate update) async {
|
||||
if (update.status == TaskStatus.failed ||
|
||||
update.status == TaskStatus.canceled) {
|
||||
Log.error(
|
||||
'twonly Backup upload failed. ${update.responseStatusCode} ${update.responseBody} ${update.responseHeaders} ${update.exception}',
|
||||
);
|
||||
await updateUserdata((user) {
|
||||
if (user.twonlySafeBackup != null) {
|
||||
user.twonlySafeBackup!.backupUploadState = LastBackupUploadState.failed;
|
||||
|
|
|
|||
|
|
@ -18,10 +18,24 @@ void initLogger() {
|
|||
);
|
||||
}
|
||||
});
|
||||
cleanLogFile();
|
||||
}
|
||||
|
||||
class Log {
|
||||
static void error(Object? message, [Object? error, StackTrace? stackTrace]) {
|
||||
static String filterLogMessage(String msg) {
|
||||
if (msg.contains('SqliteException')) {
|
||||
// Do not log data which would be inserted into the DB.
|
||||
return msg.substring(0, msg.indexOf('parameters: '));
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
static void error(
|
||||
Object? messageInput, [
|
||||
Object? error,
|
||||
StackTrace? stackTrace,
|
||||
]) {
|
||||
final message = filterLogMessage('$messageInput');
|
||||
if (globalAllowErrorTrackingViaSentry) {
|
||||
try {
|
||||
throw Exception(message);
|
||||
|
|
@ -32,11 +46,21 @@ class Log {
|
|||
Logger(_getCallerSourceCodeFilename()).shout(message, error, stackTrace);
|
||||
}
|
||||
|
||||
static void warn(Object? message, [Object? error, StackTrace? stackTrace]) {
|
||||
static void warn(
|
||||
Object? messageInput, [
|
||||
Object? error,
|
||||
StackTrace? stackTrace,
|
||||
]) {
|
||||
final message = filterLogMessage('$messageInput');
|
||||
Logger(_getCallerSourceCodeFilename()).warning(message, error, stackTrace);
|
||||
}
|
||||
|
||||
static void info(Object? message, [Object? error, StackTrace? stackTrace]) {
|
||||
static void info(
|
||||
Object? messageInput, [
|
||||
Object? error,
|
||||
StackTrace? stackTrace,
|
||||
]) {
|
||||
final message = filterLogMessage('$messageInput');
|
||||
Logger(_getCallerSourceCodeFilename()).fine(message, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
|
@ -77,6 +101,23 @@ Future<void> _writeLogToFile(LogRecord record) async {
|
|||
});
|
||||
}
|
||||
|
||||
Future<void> cleanLogFile() async {
|
||||
final directory = await getApplicationSupportDirectory();
|
||||
final logFile = File('${directory.path}/app.log');
|
||||
|
||||
if (logFile.existsSync()) {
|
||||
final lines = await logFile.readAsLines();
|
||||
|
||||
if (lines.length <= 5000) return;
|
||||
|
||||
final removeCount = lines.length - 5000;
|
||||
final remaining = lines.sublist(removeCount);
|
||||
|
||||
final sink = logFile.openWrite()..writeAll(remaining, '\n');
|
||||
await sink.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> deleteLogFile() async {
|
||||
final directory = await getApplicationSupportDirectory();
|
||||
final logFile = File('${directory.path}/app.log');
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import 'dart:math';
|
|||
import 'package:camera/camera.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_controller_view.dart';
|
||||
|
||||
class CameraZoomButtons extends StatefulWidget {
|
||||
|
|
@ -51,7 +50,6 @@ class _CameraZoomButtonsState extends State<CameraZoomButtons> {
|
|||
|
||||
Future<void> initAsync() async {
|
||||
showWideAngleZoom = (await widget.controller.getMinZoomLevel()) < 1;
|
||||
Log.info('Found ${gCameras.length} cameras for zoom.');
|
||||
|
||||
var index =
|
||||
gCameras.indexWhere((t) => t.lensType == CameraLensType.ultraWide);
|
||||
|
|
|
|||
|
|
@ -90,11 +90,15 @@ class _TextViewState extends State<TextLayer> {
|
|||
final bottom = MediaQuery.of(context).viewInsets.bottom +
|
||||
MediaQuery.of(context).viewPadding.bottom;
|
||||
|
||||
// On Android it is possible to close the keyboard without `onEditingComplete` is triggered.
|
||||
if (maxBottomInset > bottom) {
|
||||
maxBottomInset = 0;
|
||||
if (widget.layerData.isEditing) {
|
||||
widget.layerData.isEditing = false;
|
||||
onEditionComplete();
|
||||
// prevent that the text element will be disappearing in case the keyboard just switches for example to the emoji page
|
||||
if (bottom < 20) {
|
||||
maxBottomInset = 0;
|
||||
if (widget.layerData.isEditing) {
|
||||
widget.layerData.isEditing = false;
|
||||
onEditionComplete();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
maxBottomInset = bottom;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import 'package:drift/drift.dart' show Value;
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:hashlib/random.dart';
|
||||
import 'package:screenshot/screenshot.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||
|
|
@ -431,9 +430,17 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
|||
|
||||
// In case the image was already stored, then rename the stored image.
|
||||
if (mediaService.storedPath.existsSync()) {
|
||||
final newPath = mediaService.storedPath.absolute.path
|
||||
.replaceFirst(media.mediaId, uuid.v7());
|
||||
mediaService.storedPath.renameSync(newPath);
|
||||
final mediaFile = await twonlyDB.mediaFilesDao.insertMedia(
|
||||
MediaFilesCompanion(
|
||||
type: Value(mediaService.mediaFile.type),
|
||||
createdAt: Value(DateTime.now()),
|
||||
stored: const Value(true),
|
||||
),
|
||||
);
|
||||
if (mediaFile != null) {
|
||||
mediaService.storedPath
|
||||
.renameSync(MediaFileService(mediaFile).storedPath.path);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -260,6 +260,7 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
|||
});
|
||||
|
||||
final items = await MemoryItem.convertFromMessages(storedMediaFiles);
|
||||
if (!mounted) return;
|
||||
galleryItems = items.values.toList();
|
||||
setState(() {});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,11 +76,7 @@ class _MessageInputState extends State<MessageInput> {
|
|||
}
|
||||
|
||||
void _initializeControllers() {
|
||||
recorderController = RecorderController()
|
||||
..androidEncoder = AndroidEncoder.aac
|
||||
..androidOutputFormat = AndroidOutputFormat.mpeg4
|
||||
..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC
|
||||
..sampleRate = 44100;
|
||||
recorderController = RecorderController();
|
||||
}
|
||||
|
||||
void _handleTextFocusChange() {
|
||||
|
|
|
|||
|
|
@ -172,12 +172,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
showSendTextMessageInput = false;
|
||||
});
|
||||
|
||||
// if (Platform.isAndroid) {
|
||||
// await flutterLocalNotificationsPlugin
|
||||
// .cancel(allMediaFiles.first.contactId);
|
||||
// } else {
|
||||
await flutterLocalNotificationsPlugin.cancelAll();
|
||||
// }
|
||||
unawaited(flutterLocalNotificationsPlugin.cancelAll());
|
||||
|
||||
final stream =
|
||||
twonlyDB.mediaFilesDao.watchMedia(allMediaFiles.first.mediaId!);
|
||||
|
|
@ -261,6 +256,8 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
return nextMediaOrExit();
|
||||
}
|
||||
|
||||
var timerRequired = false;
|
||||
|
||||
if (currentMediaLocal.mediaFile.type == MediaType.video) {
|
||||
videoController = VideoPlayerController.file(currentMediaLocal.tempPath);
|
||||
await videoController?.setLooping(
|
||||
|
|
@ -292,12 +289,17 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
currentMediaLocal.mediaFile.displayLimitInMilliseconds!,
|
||||
),
|
||||
);
|
||||
timerRequired = true;
|
||||
}
|
||||
}
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
currentMedia = currentMediaLocal;
|
||||
});
|
||||
if (timerRequired) {
|
||||
startTimer();
|
||||
}
|
||||
}
|
||||
setState(() {
|
||||
currentMedia = currentMediaLocal;
|
||||
});
|
||||
}
|
||||
|
||||
void startTimer() {
|
||||
|
|
@ -310,14 +312,16 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
}
|
||||
});
|
||||
progressTimer = Timer.periodic(const Duration(milliseconds: 10), (timer) {
|
||||
if (currentMedia!.mediaFile.displayLimitInMilliseconds == null ||
|
||||
final mediaFile = currentMedia?.mediaFile;
|
||||
if (mediaFile == null) return;
|
||||
if (mediaFile.displayLimitInMilliseconds == null ||
|
||||
canBeSeenUntil == null) {
|
||||
return;
|
||||
}
|
||||
final difference = canBeSeenUntil!.difference(DateTime.now());
|
||||
// Calculate the progress as a value between 0.0 and 1.0
|
||||
progress = difference.inMilliseconds /
|
||||
(currentMedia!.mediaFile.displayLimitInMilliseconds!);
|
||||
progress =
|
||||
difference.inMilliseconds / (mediaFile.displayLimitInMilliseconds!);
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
import 'package:twonly/src/views/components/animate_icon.dart';
|
||||
|
||||
class FlameCounterWidget extends StatefulWidget {
|
||||
|
|
@ -38,18 +39,20 @@ class _FlameCounterWidgetState extends State<FlameCounterWidget> {
|
|||
|
||||
Future<void> initAsync() async {
|
||||
var groupId = widget.groupId;
|
||||
late Group? group;
|
||||
if (widget.groupId == null && widget.contactId != null) {
|
||||
final group = await twonlyDB.groupsDao.getDirectChat(widget.contactId!);
|
||||
group = await twonlyDB.groupsDao.getDirectChat(widget.contactId!);
|
||||
groupId = group?.groupId;
|
||||
} else if (groupId != null) {
|
||||
// do not display the flame counter for groups
|
||||
final group = await twonlyDB.groupsDao.getGroup(groupId);
|
||||
group = await twonlyDB.groupsDao.getGroup(groupId);
|
||||
if (!(group?.isDirectChat ?? false)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (groupId != null) {
|
||||
isBestFriend = gUser.myBestFriendGroupId == groupId;
|
||||
if (groupId != null && group != null) {
|
||||
isBestFriend =
|
||||
gUser.myBestFriendGroupId == groupId && group.alsoBestFriend;
|
||||
final stream = twonlyDB.groupsDao.watchFlameCounter(groupId);
|
||||
flameCounterSub = stream.listen((counter) {
|
||||
if (mounted) {
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 7 KiB After Width: | Height: | Size: 7 KiB |
|
Before Width: | Height: | Size: 7 KiB After Width: | Height: | Size: 7 KiB |
31
pubspec.lock
31
pubspec.lock
|
|
@ -61,10 +61,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: audio_waveforms
|
||||
sha256: "658fef41bbab299184b65ba2fd749e8ec658c1f7d54a21f7cf97fa96b173b4ce"
|
||||
sha256: "3a34bdd15dd63a6d1501218449048b28ebe8e1f795bf00ec310acd7b70648f07"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "2.0.0"
|
||||
avatar_maker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -77,10 +77,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: background_downloader
|
||||
sha256: a913b37cc47a656a225e9562b69576000d516f705482f392e2663500e6ff6032
|
||||
sha256: a3b340e42bc45598918944e378dc6a05877e587fcd0e1b8d2ea26339de87bdf9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.3.0"
|
||||
version: "9.4.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -397,11 +397,12 @@ packages:
|
|||
emoji_picker_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: emoji_picker_flutter
|
||||
sha256: "9a44c102079891ea5877f78c70f2e3c6e9df7b7fe0a01757d31f1046eeaa016d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.3.0"
|
||||
path: "."
|
||||
ref: HEAD
|
||||
resolved-ref: c5bffd3414c1e640389b41165b831df7df1cf517
|
||||
url: "https://github.com/otsmr/emoji_picker_flutter.git"
|
||||
source: git
|
||||
version: "4.4.0"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1741,14 +1742,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
universal_io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: universal_io
|
||||
sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
url_launcher:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1865,10 +1858,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: video_player
|
||||
sha256: "0d55b1f1a31e5ad4c4967bfaa8ade0240b07d20ee4af1dfef5f531056512961a"
|
||||
sha256: "096bc28ce10d131be80dfb00c223024eb0fba301315a406728ab43dd99c45bdf"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.10.0"
|
||||
version: "2.10.1"
|
||||
video_player_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
15
pubspec.yaml
15
pubspec.yaml
|
|
@ -3,16 +3,16 @@ description: "twonly, a privacy-friendly way to connect with friends through sec
|
|||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 0.0.71+71
|
||||
version: 0.0.72+72
|
||||
|
||||
environment:
|
||||
sdk: ^3.6.0
|
||||
|
||||
dependencies:
|
||||
archive: ^4.0.7
|
||||
audio_waveforms: ^1.3.0
|
||||
audio_waveforms: ^2.0.0
|
||||
avatar_maker: ^0.4.0
|
||||
background_downloader: ^9.2.2
|
||||
background_downloader: ^9.4.0
|
||||
cached_network_image: ^3.4.1
|
||||
camera: ^0.11.2
|
||||
collection: ^1.18.0
|
||||
|
|
@ -74,9 +74,9 @@ dependencies:
|
|||
sentry_flutter: ^9.8.0
|
||||
share_plus: ^12.0.0
|
||||
tutorial_coach_mark: ^1.3.0
|
||||
url_launcher: ^6.3.1
|
||||
url_launcher: ^6.3.2
|
||||
vector_graphics: ^1.1.19
|
||||
video_player: ^2.9.5
|
||||
video_player: ^2.10.1
|
||||
web_socket_channel: ^3.0.1
|
||||
|
||||
dependency_overrides:
|
||||
|
|
@ -86,6 +86,11 @@ dependency_overrides:
|
|||
url: https://github.com/otsmr/flutter-packages.git
|
||||
path: packages/camera/camera_android_camerax
|
||||
ref: aef58af205a5f3ce6588a5c59bb2e734aab943f0
|
||||
emoji_picker_flutter:
|
||||
# Fixes the issue with recent emojis (solved by https://github.com/Fintasys/emoji_picker_flutter/pull/238)
|
||||
# Using override until this gets merged.
|
||||
git:
|
||||
url: https://github.com/otsmr/emoji_picker_flutter.git
|
||||
flutter_android_volume_keydown:
|
||||
git:
|
||||
url: https://github.com/yenchieh/flutter_android_volume_keydown.git
|
||||
|
|
|
|||
Loading…
Reference in a new issue