Merge pull request #296 from twonlyapp/dev

Hotfix
This commit is contained in:
Tobi 2025-11-08 21:40:09 +01:00 committed by GitHub
commit 112f7feb7d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 114 additions and 57 deletions

View file

@ -241,7 +241,7 @@ PODS:
- sqlite3/perf-threadsafe - sqlite3/perf-threadsafe
- sqlite3/rtree - sqlite3/rtree
- sqlite3/session - sqlite3/session
- SwiftProtobuf (1.32.0) - SwiftProtobuf (1.33.1)
- url_launcher_ios (0.0.1): - url_launcher_ios (0.0.1):
- Flutter - Flutter
- video_player_avfoundation (0.0.1): - video_player_avfoundation (0.0.1):
@ -421,7 +421,7 @@ SPEC CHECKSUMS:
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
sqlite3: 73513155ec6979715d3904ef53a8d68892d4032b sqlite3: 73513155ec6979715d3904ef53a8d68892d4032b
sqlite3_flutter_libs: 83f8e9f5b6554077f1d93119fe20ebaa5f3a9ef1 sqlite3_flutter_libs: 83f8e9f5b6554077f1d93119fe20ebaa5f3a9ef1
SwiftProtobuf: 81e341191afbddd64aa031bd12862dccfab2f639 SwiftProtobuf: 533a18409c3ca3a6156b2b1e46afd0f69e751aba
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
video_player_avfoundation: dd410b52df6d2466a42d28550e33e4146928280a video_player_avfoundation: dd410b52df6d2466a42d28550e33e4146928280a

View file

@ -52,7 +52,7 @@
</Testables> </Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"

View file

@ -51,9 +51,9 @@ final lockRetransStore = Mutex();
/// errors or network changes. /// errors or network changes.
class ApiService { class ApiService {
ApiService(); ApiService();
// final String apiHost = kReleaseMode ? 'api.twonly.eu' : '10.99.0.140:3030'; final String apiHost = kReleaseMode ? 'api.twonly.eu' : '10.99.0.140:3030';
final String apiHost = kReleaseMode ? 'api.twonly.eu' : 'dev.twonly.eu'; // final String apiHost = kReleaseMode ? 'api.twonly.eu' : 'dev.twonly.eu';
final String apiSecure = kReleaseMode ? 's' : 's'; final String apiSecure = kReleaseMode ? 's' : '';
bool appIsOutdated = false; bool appIsOutdated = false;
bool isAuthenticated = false; bool isAuthenticated = false;

View file

@ -66,15 +66,23 @@ Future<void> handleMedia(
mediaType = MediaType.audio; mediaType = MediaType.audio;
} }
int? displayLimitInMilliseconds;
if (media.hasDisplayLimitInMilliseconds()) {
if (media.displayLimitInMilliseconds.toInt() < 1000) {
displayLimitInMilliseconds =
media.displayLimitInMilliseconds.toInt() * 1000;
} else {
displayLimitInMilliseconds = media.displayLimitInMilliseconds.toInt();
}
}
final mediaFile = await twonlyDB.mediaFilesDao.insertMedia( final mediaFile = await twonlyDB.mediaFilesDao.insertMedia(
MediaFilesCompanion( MediaFilesCompanion(
downloadState: const Value(DownloadState.pending), downloadState: const Value(DownloadState.pending),
type: Value(mediaType), type: Value(mediaType),
requiresAuthentication: Value(media.requiresAuthentication), requiresAuthentication: Value(media.requiresAuthentication),
displayLimitInMilliseconds: Value( displayLimitInMilliseconds: Value(
media.hasDisplayLimitInMilliseconds() displayLimitInMilliseconds,
? media.displayLimitInMilliseconds.toInt()
: null,
), ),
downloadToken: Value(Uint8List.fromList(media.downloadToken)), downloadToken: Value(Uint8List.fromList(media.downloadToken)),
encryptionKey: Value(Uint8List.fromList(media.encryptionKey)), encryptionKey: Value(Uint8List.fromList(media.encryptionKey)),

View file

@ -54,6 +54,11 @@ Future<MediaFileService?> initializeMediaUpload(
int? displayLimitInMilliseconds, { int? displayLimitInMilliseconds, {
bool isDraftMedia = false, bool isDraftMedia = false,
}) async { }) async {
if (displayLimitInMilliseconds != null && displayLimitInMilliseconds < 1000) {
// in case the time was set in seconds...
// ignore: parameter_assignments
displayLimitInMilliseconds = displayLimitInMilliseconds * 1000;
}
final chacha20 = FlutterChacha20.poly1305Aead(); final chacha20 = FlutterChacha20.poly1305Aead();
final encryptionKey = await (await chacha20.newSecretKey()).extract(); final encryptionKey = await (await chacha20.newSecretKey()).extract();
final encryptionNonce = chacha20.newNonce(); final encryptionNonce = chacha20.newNonce();

View file

@ -157,7 +157,7 @@ class MediaFileService {
MediaFilesCompanion( MediaFilesCompanion(
requiresAuthentication: Value(requiresAuthentication), requiresAuthentication: Value(requiresAuthentication),
displayLimitInMilliseconds: displayLimitInMilliseconds:
requiresAuthentication ? const Value(12) : const Value.absent(), requiresAuthentication ? const Value(12000) : const Value.absent(),
), ),
); );
await updateFromDB(); await updateFromDB();

View file

@ -362,36 +362,49 @@ Future<Uint8List?> encryptPushNotification(
Future<List<PushUser>> getPushKeys(String storageKey) async { Future<List<PushUser>> getPushKeys(String storageKey) async {
const storage = FlutterSecureStorage(); const storage = FlutterSecureStorage();
final pushKeysProto = await storage.read( try {
key: storageKey, final pushKeysProto = await storage.read(
iOptions: const IOSOptions( key: storageKey,
groupId: 'CN332ZUGRP.eu.twonly.shared', iOptions: const IOSOptions(
accessibility: KeychainAccessibility.first_unlock, groupId: 'CN332ZUGRP.eu.twonly.shared',
), accessibility: KeychainAccessibility.first_unlock,
); ),
if (pushKeysProto == null) return []; );
final pushKeysRaw = base64Decode(pushKeysProto); if (pushKeysProto == null) return [];
return PushUsers.fromBuffer(pushKeysRaw).users; final pushKeysRaw = base64Decode(pushKeysProto);
return PushUsers.fromBuffer(pushKeysRaw).users;
} catch (e) {
Log.error(e);
}
return [];
} }
Future<void> setPushKeys(String storageKey, List<PushUser> pushKeys) async { Future<void> setPushKeys(String storageKey, List<PushUser> pushKeys) async {
const storage = FlutterSecureStorage(); const storage = FlutterSecureStorage();
await storage.delete( try {
key: storageKey, await storage.delete(
iOptions: const IOSOptions( key: storageKey,
groupId: 'CN332ZUGRP.eu.twonly.shared', iOptions: const IOSOptions(
accessibility: KeychainAccessibility.first_unlock, groupId: 'CN332ZUGRP.eu.twonly.shared',
), accessibility: KeychainAccessibility.first_unlock,
); ),
);
} catch (e) {
Log.error(e);
}
final jsonString = base64Encode(PushUsers(users: pushKeys).writeToBuffer()); final jsonString = base64Encode(PushUsers(users: pushKeys).writeToBuffer());
await storage.write( try {
key: storageKey, await storage.write(
value: jsonString, key: storageKey,
iOptions: const IOSOptions( value: jsonString,
groupId: 'CN332ZUGRP.eu.twonly.shared', iOptions: const IOSOptions(
accessibility: KeychainAccessibility.first_unlock, groupId: 'CN332ZUGRP.eu.twonly.shared',
), accessibility: KeychainAccessibility.first_unlock,
); ),
);
} catch (e) {
Log.error(e);
}
} }

View file

@ -192,7 +192,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
? '0' ? '0'
: media.displayLimitInMilliseconds == null : media.displayLimitInMilliseconds == null
? '' ? ''
: media.displayLimitInMilliseconds.toString(), : (media.displayLimitInMilliseconds! ~/ 1000).toString(),
child: ActionButton( child: ActionButton(
(media.type == MediaType.video) (media.type == MediaType.video)
? media.displayLimitInMilliseconds == null ? media.displayLimitInMilliseconds == null
@ -211,11 +211,13 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
} }
int? maxShowTime; int? maxShowTime;
if (media.displayLimitInMilliseconds == null) { if (media.displayLimitInMilliseconds == null) {
maxShowTime = 1; maxShowTime = 1000;
} else if (media.displayLimitInMilliseconds == 1) { } else if (media.displayLimitInMilliseconds == 1000) {
maxShowTime = 5; maxShowTime = 5000;
} else if (media.displayLimitInMilliseconds == 5) { } else if (media.displayLimitInMilliseconds == 5000) {
maxShowTime = 20; maxShowTime = 12000;
} else if (media.displayLimitInMilliseconds == 12000) {
maxShowTime = 20000;
} }
await mediaService.setDisplayLimit(maxShowTime); await mediaService.setDisplayLimit(maxShowTime);
if (!mounted) return; if (!mounted) return;

View file

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:collection';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:lottie/lottie.dart'; import 'package:lottie/lottie.dart';
@ -54,7 +55,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
bool imageSaved = false; bool imageSaved = false;
bool imageSaving = false; bool imageSaving = false;
bool displayTwonlyPresent = true; bool displayTwonlyPresent = false;
final emojiKey = GlobalKey<EmojiFloatWidgetState>(); final emojiKey = GlobalKey<EmojiFloatWidgetState>();
StreamSubscription<MediaFile?>? downloadStateListener; StreamSubscription<MediaFile?>? downloadStateListener;
@ -63,6 +64,8 @@ class _MediaViewerViewState extends State<MediaViewerView> {
late StreamSubscription<List<Message>> _subscription; late StreamSubscription<List<Message>> _subscription;
TextEditingController textMessageController = TextEditingController(); TextEditingController textMessageController = TextEditingController();
final HashSet<String> _alreadyOpenedMediaIds = HashSet();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -92,6 +95,13 @@ class _MediaViewerViewState extends State<MediaViewerView> {
_subscription = messages.listen((messages) async { _subscription = messages.listen((messages) async {
for (final msg in messages) { for (final msg in messages) {
if (_alreadyOpenedMediaIds.contains(msg.mediaId)) {
continue;
}
if (msg.mediaId == null) {
continue;
}
if (msg.mediaId == currentMedia?.mediaFile.mediaId) { if (msg.mediaId == currentMedia?.mediaFile.mediaId) {
// The update of the current Media in case of a download is done in loadCurrentMediaFile // The update of the current Media in case of a download is done in loadCurrentMediaFile
continue; continue;
@ -195,13 +205,17 @@ class _MediaViewerViewState extends State<MediaViewerView> {
bool showTwonly, bool showTwonly,
) async { ) async {
if (allMediaFiles.isEmpty) return; if (allMediaFiles.isEmpty) return;
currentMessage = allMediaFiles.removeAt(0);
final currentMediaLocal = final currentMediaLocal =
await MediaFileService.fromMediaId(currentMessage!.mediaId!); await MediaFileService.fromMediaId(allMediaFiles.first.mediaId!);
if (currentMediaLocal == null || !mounted) return; if (currentMediaLocal == null || !mounted) return;
if (currentMediaLocal.mediaFile.requiresAuthentication) { if (currentMediaLocal.mediaFile.requiresAuthentication) {
if (!showTwonly) return; if (!showTwonly) {
setState(() {
displayTwonlyPresent = true;
});
return;
}
final isAuth = await authenticateUser( final isAuth = await authenticateUser(
context.lang.mediaViewerAuthReason, context.lang.mediaViewerAuthReason,
@ -209,10 +223,20 @@ class _MediaViewerViewState extends State<MediaViewerView> {
); );
if (!isAuth) { if (!isAuth) {
await nextMediaOrExit(); await nextMediaOrExit();
setState(() {
displayTwonlyPresent = false;
});
return; return;
} }
} }
_alreadyOpenedMediaIds.add(allMediaFiles.first.mediaId!);
currentMessage = allMediaFiles.removeAt(0);
setState(() {
displayTwonlyPresent = false;
});
await notifyContactAboutOpeningMessage( await notifyContactAboutOpeningMessage(
currentMessage!.senderId!, currentMessage!.senderId!,
[currentMessage!.messageId], [currentMessage!.messageId],
@ -490,9 +514,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
), ),
), ),
), ),
if (currentMedia != null && if (displayTwonlyPresent)
currentMedia!.mediaFile.requiresAuthentication &&
displayTwonlyPresent)
Positioned.fill( Positioned.fill(
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {

View file

@ -705,18 +705,19 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: flutter_secure_storage path: flutter_secure_storage
ref: "71b75a36f35f2ce945998e20c6c6aa1820babfc6" ref: a06ead81809c900e7fc421a30db0adf3b5919139
resolved-ref: "71b75a36f35f2ce945998e20c6c6aa1820babfc6" resolved-ref: a06ead81809c900e7fc421a30db0adf3b5919139
url: "https://github.com/juliansteenbakker/flutter_secure_storage.git" url: "https://github.com/juliansteenbakker/flutter_secure_storage.git"
source: git source: git
version: "10.0.0-beta.4" version: "10.0.0-beta.4"
flutter_secure_storage_darwin: flutter_secure_storage_darwin:
dependency: transitive dependency: "direct overridden"
description: description:
name: flutter_secure_storage_darwin path: flutter_secure_storage_darwin
sha256: f226f2a572bed96bc6542198ebaec227150786e34311d455a7e2d3d06d951845 ref: a06ead81809c900e7fc421a30db0adf3b5919139
url: "https://pub.dev" resolved-ref: a06ead81809c900e7fc421a30db0adf3b5919139
source: hosted url: "https://github.com/juliansteenbakker/flutter_secure_storage.git"
source: git
version: "0.1.0" version: "0.1.0"
flutter_secure_storage_linux: flutter_secure_storage_linux:
dependency: transitive dependency: transitive

View file

@ -3,7 +3,7 @@ description: "twonly, a privacy-friendly way to connect with friends through sec
publish_to: 'none' publish_to: 'none'
version: 0.0.64+64 version: 0.0.66+66
environment: environment:
sdk: ^3.6.0 sdk: ^3.6.0
@ -36,7 +36,7 @@ dependencies:
flutter_secure_storage: flutter_secure_storage:
git: git:
url: https://github.com/juliansteenbakker/flutter_secure_storage.git url: https://github.com/juliansteenbakker/flutter_secure_storage.git
ref: 71b75a36f35f2ce945998e20c6c6aa1820babfc6 # from develop ref: a06ead81809c900e7fc421a30db0adf3b5919139 # from develop
path: flutter_secure_storage/ path: flutter_secure_storage/
flutter_svg: ^2.0.17 flutter_svg: ^2.0.17
flutter_volume_controller: ^1.3.4 flutter_volume_controller: ^1.3.4
@ -77,6 +77,12 @@ dependencies:
dependency_overrides: dependency_overrides:
flutter_secure_storage_darwin:
git:
url: https://github.com/juliansteenbakker/flutter_secure_storage.git
ref: a06ead81809c900e7fc421a30db0adf3b5919139 # from develop
path: flutter_secure_storage_darwin/
flutter_android_volume_keydown: flutter_android_volume_keydown:
git: git:
url: https://github.com/yenchieh/flutter_android_volume_keydown.git url: https://github.com/yenchieh/flutter_android_volume_keydown.git