diff --git a/README.md b/README.md index b6229df..e8ac43c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,14 @@ This repository contains the complete source code of the [twonly](https://twonly.eu) apps. + + Get it on Testflight button + + + Get it on F-Droid button + + ## 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. \ No newline at end of file +This project is licensed under the [GNU AGPL 3.0](LICENSE) license. diff --git a/ios/NotificationService/NotificationService.swift b/ios/NotificationService/NotificationService.swift index 9fb06e1..d3db8e5 100644 --- a/ios/NotificationService/NotificationService.swift +++ b/ios/NotificationService/NotificationService.swift @@ -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.", diff --git a/lib/src/database/daos/groups.dao.dart b/lib/src/database/daos/groups.dao.dart index 5fd2fe6..048c0d9 100644 --- a/lib/src/database/daos/groups.dao.dart +++ b/lib/src/database/daos/groups.dao.dart @@ -287,7 +287,7 @@ class GroupsDao extends DatabaseAccessor 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; diff --git a/lib/src/database/daos/messages.dao.dart b/lib/src/database/daos/messages.dao.dart index 97bd5b7..7269054 100644 --- a/lib/src/database/daos/messages.dao.dart +++ b/lib/src/database/daos/messages.dao.dart @@ -115,7 +115,7 @@ class MessagesDao extends DatabaseAccessor 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 with _$MessagesDaoMixin { m.createdAt.isSmallerThanValue(deletionTime))), )) .go(); - Log.info('Deleted $affected messages.'); } } diff --git a/lib/src/database/daos/receipts.dao.dart b/lib/src/database/daos/receipts.dao.dart index 8b44397..34c05e2 100644 --- a/lib/src/database/daos/receipts.dao.dart +++ b/lib/src/database/daos/receipts.dao.dart @@ -62,7 +62,7 @@ class ReceiptsDao extends DatabaseAccessor 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; } } diff --git a/lib/src/localization/app_en.arb b/lib/src/localization/app_en.arb index df2ccc6..26ada4e 100644 --- a/lib/src/localization/app_en.arb +++ b/lib/src/localization/app_en.arb @@ -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.", diff --git a/lib/src/localization/generated/app_localizations.dart b/lib/src/localization/generated/app_localizations.dart index 54d0805..9cadfcf 100644 --- a/lib/src/localization/generated/app_localizations.dart +++ b/lib/src/localization/generated/app_localizations.dart @@ -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. diff --git a/lib/src/localization/generated/app_localizations_en.dart b/lib/src/localization/generated/app_localizations_en.dart index 5763b12..d011a43 100644 --- a/lib/src/localization/generated/app_localizations_en.dart +++ b/lib/src/localization/generated/app_localizations_en.dart @@ -1345,7 +1345,7 @@ class AppLocalizationsEn extends AppLocalizations { @override String notificationImage(Object inGroup) { - return 'sent a image$inGroup.'; + return 'sent an image$inGroup.'; } @override diff --git a/lib/src/services/api.service.dart b/lib/src/services/api.service.dart index c595be4..dea88dc 100644 --- a/lib/src/services/api.service.dart +++ b/lib/src/services/api.service.dart @@ -185,7 +185,7 @@ class ApiService { } Future _onError(dynamic e) async { - Log.error('websocket error: $e'); + Log.warn('websocket error: $e'); await onClosed(); } @@ -206,7 +206,7 @@ class ApiService { Future _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; } diff --git a/lib/src/services/api/mediafiles/download.service.dart b/lib/src/services/api/mediafiles/download.service.dart index f5108a1..12ea9dc 100644 --- a/lib/src/services/api/mediafiles/download.service.dart +++ b/lib/src/services/api/mediafiles/download.service.dart @@ -100,9 +100,11 @@ Future 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 handleDownloadStatusUpdate(TaskStatusUpdate update) async { } if (failed) { - Log.error('Background media upload failed: ${update.status}'); await requestMediaReupload(mediaId); } else { await handleEncryptedFile(mediaId); diff --git a/lib/src/services/api/messages.dart b/lib/src/services/api/messages.dart index 13affc2..d083505 100644 --- a/lib/src/services/api/messages.dart +++ b/lib/src/services/api/messages.dart @@ -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; } } diff --git a/lib/src/services/api/server_messages.dart b/lib/src/services/api/server_messages.dart index b4b2ddd..d395f80 100644 --- a/lib/src/services/api/server_messages.dart +++ b/lib/src/services/api/server_messages.dart @@ -134,7 +134,6 @@ Future 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; } diff --git a/lib/src/services/fcm.service.dart b/lib/src/services/fcm.service.dart index 33ffaa8..df3ebc5 100644 --- a/lib/src/services/fcm.service.dart +++ b/lib/src/services/fcm.service.dart @@ -22,6 +22,13 @@ Future 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'); diff --git a/lib/src/services/mediafiles/mediafile.service.dart b/lib/src/services/mediafiles/mediafile.service.dart index 0425efa..24b4e37 100644 --- a/lib/src/services/mediafiles/mediafile.service.dart +++ b/lib/src/services/mediafiles/mediafile.service.dart @@ -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; } diff --git a/lib/src/services/signal/encryption.signal.dart b/lib/src/services/signal/encryption.signal.dart index 50aad34..3c24598 100644 --- a/lib/src/services/signal/encryption.signal.dart +++ b/lib/src/services/signal/encryption.signal.dart @@ -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); diff --git a/lib/src/services/signal/prekeys.signal.dart b/lib/src/services/signal/prekeys.signal.dart index e70d239..f2dcde2 100644 --- a/lib/src/services/signal/prekeys.signal.dart +++ b/lib/src/services/signal/prekeys.signal.dart @@ -50,8 +50,7 @@ Future 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 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'); } }); } diff --git a/lib/src/services/twonly_safe/create_backup.twonly_safe.dart b/lib/src/services/twonly_safe/create_backup.twonly_safe.dart index 434b9d5..640e9cd 100644 --- a/lib/src/services/twonly_safe/create_backup.twonly_safe.dart +++ b/lib/src/services/twonly_safe/create_backup.twonly_safe.dart @@ -203,9 +203,6 @@ Future performTwonlySafeBackup({bool force = false}) async { Future 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; diff --git a/lib/src/utils/log.dart b/lib/src/utils/log.dart index fa9c34c..7eefe2a 100644 --- a/lib/src/utils/log.dart +++ b/lib/src/utils/log.dart @@ -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 _writeLogToFile(LogRecord record) async { }); } +Future 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 deleteLogFile() async { final directory = await getApplicationSupportDirectory(); final logFile = File('${directory.path}/app.log'); 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 d5824b0..c6d78cf 100644 --- a/lib/src/views/camera/camera_preview_components/zoom_selector.dart +++ b/lib/src/views/camera/camera_preview_components/zoom_selector.dart @@ -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 { Future 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); diff --git a/lib/src/views/camera/image_editor/layers/text_layer.dart b/lib/src/views/camera/image_editor/layers/text_layer.dart index b6b98e6..5e9627f 100755 --- a/lib/src/views/camera/image_editor/layers/text_layer.dart +++ b/lib/src/views/camera/image_editor/layers/text_layer.dart @@ -90,11 +90,15 @@ class _TextViewState extends State { 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; diff --git a/lib/src/views/camera/share_image_editor_view.dart b/lib/src/views/camera/share_image_editor_view.dart index 8ef97c5..2d2cacc 100644 --- a/lib/src/views/camera/share_image_editor_view.dart +++ b/lib/src/views/camera/share_image_editor_view.dart @@ -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 { // 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; } diff --git a/lib/src/views/chats/chat_messages.view.dart b/lib/src/views/chats/chat_messages.view.dart index 4526e8e..f4dbc51 100644 --- a/lib/src/views/chats/chat_messages.view.dart +++ b/lib/src/views/chats/chat_messages.view.dart @@ -260,6 +260,7 @@ class _ChatMessagesViewState extends State { }); final items = await MemoryItem.convertFromMessages(storedMediaFiles); + if (!mounted) return; galleryItems = items.values.toList(); setState(() {}); } diff --git a/lib/src/views/chats/chat_messages_components/message_input.dart b/lib/src/views/chats/chat_messages_components/message_input.dart index aa9059d..2e979b6 100644 --- a/lib/src/views/chats/chat_messages_components/message_input.dart +++ b/lib/src/views/chats/chat_messages_components/message_input.dart @@ -76,11 +76,7 @@ class _MessageInputState extends State { } void _initializeControllers() { - recorderController = RecorderController() - ..androidEncoder = AndroidEncoder.aac - ..androidOutputFormat = AndroidOutputFormat.mpeg4 - ..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC - ..sampleRate = 44100; + recorderController = RecorderController(); } void _handleTextFocusChange() { diff --git a/lib/src/views/chats/media_viewer.view.dart b/lib/src/views/chats/media_viewer.view.dart index 846b32d..beba83f 100644 --- a/lib/src/views/chats/media_viewer.view.dart +++ b/lib/src/views/chats/media_viewer.view.dart @@ -172,12 +172,7 @@ class _MediaViewerViewState extends State { 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 { 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 { 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 { } }); 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(() {}); }); } diff --git a/lib/src/views/components/flame.dart b/lib/src/views/components/flame.dart index dc83b12..3e33ba3 100644 --- a/lib/src/views/components/flame.dart +++ b/lib/src/views/components/flame.dart @@ -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 { Future 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) { diff --git a/metadata/de-DE/images/logo.png b/metadata/de-DE/icon.png similarity index 100% rename from metadata/de-DE/images/logo.png rename to metadata/de-DE/icon.png diff --git a/metadata/en-US/images/logo.png b/metadata/en-US/icon.png similarity index 100% rename from metadata/en-US/images/logo.png rename to metadata/en-US/icon.png diff --git a/pubspec.lock b/pubspec.lock index e381a20..82b8e83 100644 --- a/pubspec.lock +++ b/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: diff --git a/pubspec.yaml b/pubspec.yaml index ab0fe5c..1fcfc73 100644 --- a/pubspec.yaml +++ b/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