From da39cdbb81846d353d7b0f12ec3c5f110c4bc9f8 Mon Sep 17 00:00:00 2001 From: otsmr Date: Sat, 19 Jul 2025 12:59:13 +0200 Subject: [PATCH] some more bug fixes --- .github/workflows/release_github.yml | 8 +- lib/globals.dart | 1 - lib/src/services/api/media_upload.dart | 2 +- lib/src/services/api/messages.dart | 4 - .../camera_preview_controller_view.dart | 50 +++---- .../views/camera/share_image_editor_view.dart | 3 + lib/src/views/chats/chat_list.view.dart | 131 ++++++++---------- .../backup_notice.card.dart | 7 +- .../connection_info.comp.dart | 3 +- .../chat_list_components/feedback_btn.dart | 51 +++++++ lib/src/views/home.view.dart | 3 + lib/src/views/onboarding/register.view.dart | 61 ++++---- lib/src/views/settings/notification.view.dart | 4 +- 13 files changed, 171 insertions(+), 157 deletions(-) create mode 100644 lib/src/views/chats/chat_list_components/feedback_btn.dart diff --git a/.github/workflows/release_github.yml b/.github/workflows/release_github.yml index 53506d5..b183d00 100644 --- a/.github/workflows/release_github.yml +++ b/.github/workflows/release_github.yml @@ -1,14 +1,12 @@ -name: Publish (Android) +name: Publish on Github on: workflow_dispatch: {} push: branches: - main - # paths: - # - lib/** - # - pubspec.lock - # - pubspec.yaml + paths: + - pubspec.yaml jobs: build_and_publish: diff --git a/lib/globals.dart b/lib/globals.dart index b80e36a..b5c0a9c 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -8,7 +8,6 @@ late ApiService apiService; late TwonlyDatabase twonlyDB; List gCameras = []; -bool gIsDemoUser = false; // The following global function can be called from anywhere to update // the UI when something changed. The callbacks will be set by diff --git a/lib/src/services/api/media_upload.dart b/lib/src/services/api/media_upload.dart index d8a0054..6205e7a 100644 --- a/lib/src/services/api/media_upload.dart +++ b/lib/src/services/api/media_upload.dart @@ -40,7 +40,7 @@ Future isAllowedToSend() async { var todaysImageCounter = user.todaysImageCounter; if (user.lastImageSend != null && user.todaysImageCounter != null) { if (isToday(user.lastImageSend!)) { - if (user.todaysImageCounter == 3) { + if (user.todaysImageCounter == 10) { return ErrorCode.PlanLimitReached; } todaysImageCounter = user.todaysImageCounter! + 1; diff --git a/lib/src/services/api/messages.dart b/lib/src/services/api/messages.dart index 34eeede..969827a 100644 --- a/lib/src/services/api/messages.dart +++ b/lib/src/services/api/messages.dart @@ -157,10 +157,6 @@ Future encryptAndSendMessageAsync( MessageJson msg, { PushNotification? pushNotification, }) async { - if (gIsDemoUser) { - return; - } - Uint8List? pushData; if (pushNotification != null) { pushData = await getPushData(userId, pushNotification); diff --git a/lib/src/views/camera/camera_preview_controller_view.dart b/lib/src/views/camera/camera_preview_controller_view.dart index 9a7acf1..652c613 100644 --- a/lib/src/views/camera/camera_preview_controller_view.dart +++ b/lib/src/views/camera/camera_preview_controller_view.dart @@ -246,13 +246,17 @@ class _CameraPreviewViewState extends State { } await widget.cameraController?.pausePreview(); - if (!mounted) return; + if (!mounted) { + return; + } - await widget.cameraController?.setFlashMode( - widget.selectedCameraDetails.isFlashOn - ? FlashMode.always - : FlashMode.off); - if (!mounted) return; + if (Platform.isIOS) { + // android has a problem with this. Flash is turned off in the pausePreview function. + await widget.cameraController?.setFlashMode(FlashMode.off); + } + if (!mounted) { + return; + } imageBytes = widget.screenshotController .capture(pixelRatio: MediaQuery.of(context).devicePixelRatio); @@ -260,6 +264,9 @@ class _CameraPreviewViewState extends State { if (await pushMediaEditor(imageBytes, null)) { return; } + setState(() { + sharePreviewIsShown = false; + }); } Future pushMediaEditor( @@ -404,17 +411,22 @@ class _CameraPreviewViewState extends State { videoRecordingTimer?.cancel(); videoRecordingTimer = null; } + + setState(() { + videoRecordingStarted = null; + isVideoRecording = false; + }); + if (widget.cameraController == null || !widget.cameraController!.value.isRecordingVideo) { return; } + setState(() { + sharePreviewIsShown = true; + }); + try { - setState(() { - videoRecordingStarted = null; - isVideoRecording = false; - sharePreviewIsShown = true; - }); File? videoPathFile; final videoPath = await widget.cameraController?.stopVideoRecording(); if (videoPath != null) { @@ -553,22 +565,6 @@ class _CameraPreviewViewState extends State { setState(() {}); }, ), - // if (!isFront) - // ActionButton( - // Icons.hd_rounded, - // tooltipText: context.lang.toggleHighQuality, - // color: useHighQuality - // ? Colors.white - // : Colors.white.withAlpha(160), - // onPressed: () async { - // useHighQuality = !useHighQuality; - // setState(() {}); - // await updateUserdata((user) { - // user.useHighQuality = useHighQuality; - // return user; - // }); - // }, - // ), if (!hasAudioPermission) ActionButton( Icons.mic_off_rounded, diff --git a/lib/src/views/camera/share_image_editor_view.dart b/lib/src/views/camera/share_image_editor_view.dart index 69f02bb..8acc396 100644 --- a/lib/src/views/camera/share_image_editor_view.dart +++ b/lib/src/views/camera/share_image_editor_view.dart @@ -78,6 +78,9 @@ class _ShareImageEditorView extends State { initAsync(); initMediaFileUpload(); layers.add(FilterLayerData()); + if (widget.sendTo != null) { + selectedUserIds.add(widget.sendTo!.userId); + } if (widget.imageBytes != null) { loadImage(widget.imageBytes!); } else if (widget.videoFilePath != null) { diff --git a/lib/src/views/chats/chat_list.view.dart b/lib/src/views/chats/chat_list.view.dart index 64bf230..353cc6a 100644 --- a/lib/src/views/chats/chat_list.view.dart +++ b/lib/src/views/chats/chat_list.view.dart @@ -18,7 +18,7 @@ import 'package:twonly/src/views/camera/camera_send_to_view.dart'; import 'package:twonly/src/views/chats/add_new_user.view.dart'; import 'package:twonly/src/views/chats/chat_list_components/backup_notice.card.dart'; import 'package:twonly/src/views/chats/chat_list_components/connection_info.comp.dart'; -import 'package:twonly/src/views/chats/chat_list_components/demo_user.card.dart'; +import 'package:twonly/src/views/chats/chat_list_components/feedback_btn.dart'; import 'package:twonly/src/views/chats/chat_list_components/last_message_time.dart'; import 'package:twonly/src/views/chats/chat_messages.view.dart'; import 'package:twonly/src/views/chats/media_viewer.view.dart'; @@ -29,7 +29,6 @@ import 'package:twonly/src/views/components/message_send_state_icon.dart'; import 'package:twonly/src/views/components/notification_badge.dart'; import 'package:twonly/src/views/components/user_context_menu.dart'; import 'package:twonly/src/views/settings/help/changelog.view.dart'; -import 'package:twonly/src/views/settings/help/contact_us.view.dart'; import 'package:twonly/src/views/settings/settings_main.view.dart'; import 'package:twonly/src/views/settings/subscription/subscription.view.dart'; import 'package:twonly/src/views/tutorial/tutorials.dart'; @@ -77,10 +76,6 @@ class _ChatListViewState extends State { final user = await getUser(); if (user == null) return; - setState(() { - showFeedbackShortcut = user.showFeedbackShortcut; - }); - final changeLog = await rootBundle.loadString('CHANGELOG.md'); final changeLogHash = (await compute(Sha256().hash, changeLog.codeUnits)).bytes; @@ -91,11 +86,15 @@ class _ChatListViewState extends State { return u; }); if (!mounted) return; - await Navigator.push(context, MaterialPageRoute(builder: (context) { - return ChangeLogView( - changeLog: changeLog, - ); - })); + // only show changelog to people who already have contacts + // this prevents that this is shown directly after the user registered + if (_contacts.isNotEmpty) { + await Navigator.push(context, MaterialPageRoute(builder: (context) { + return ChangeLogView( + changeLog: changeLog, + ); + })); + } } } @@ -139,20 +138,7 @@ class _ChatListViewState extends State { ), ]), actions: [ - if (showFeedbackShortcut) - IconButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ContactUsView(), - ), - ); - }, - color: Colors.grey, - tooltip: context.lang.feedbackTooltip, - icon: const FaIcon(FontAwesomeIcons.commentDots, size: 19), - ), + const FeedbackIconButton(), StreamBuilder( stream: twonlyDB.contactsDao.watchContactsRequested(), builder: (context, snapshot) { @@ -226,7 +212,6 @@ class _ChatListViewState extends State { child: ListView.builder( itemCount: _pinnedContacts.length + (_pinnedContacts.isNotEmpty ? 1 : 0) + - (gIsDemoUser ? 1 : 0) + _contacts.length + 1, itemBuilder: (context, index) { @@ -234,20 +219,13 @@ class _ChatListViewState extends State { return const BackupNoticeCard(); } index -= 1; - if (gIsDemoUser) { - if (index == 0) { - return const DemoUserCard(); - } - index -= 1; - } // Check if the index is for the pinned users if (index < _pinnedContacts.length) { final contact = _pinnedContacts[index]; return UserListItem( key: ValueKey(contact.userId), user: contact, - firstUserListItemKey: (index == 0 && !gIsDemoUser || - index == 1 && gIsDemoUser) + firstUserListItemKey: (index == 0 || index == 1) ? firstUserListItemKey : null, ); @@ -381,6 +359,45 @@ class _UserListItem extends State { }); } + Future onTap() async { + if (currentMessage == null) { + await Navigator.push(context, MaterialPageRoute( + builder: (context) { + return CameraSendToView(widget.user); + }, + )); + return; + } + final msgs = + previewMessages.where((x) => x.kind == MessageKind.media).toList(); + if (msgs.isNotEmpty && + msgs.first.kind == MessageKind.media && + msgs.first.messageOtherId != null && + msgs.first.openedAt == null) { + switch (msgs.first.downloadState) { + case DownloadState.pending: + await startDownloadMedia(msgs.first, true); + return; + case DownloadState.downloaded: + await Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return MediaViewerView(widget.user); + }), + ); + return; + case DownloadState.downloading: + return; + } + } + await Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return ChatMessagesView(widget.user); + }), + ); + } + @override Widget build(BuildContext context) { final flameCounter = getFlameCounterFromContact(widget.user); @@ -433,48 +450,12 @@ class _UserListItem extends State { }, )); }, - icon: FaIcon(FontAwesomeIcons.camera, - color: context.color.outline.withAlpha(150)), + icon: FaIcon( + FontAwesomeIcons.camera, + color: context.color.outline.withAlpha(150), + ), ), - onTap: () { - if (currentMessage == null) { - Navigator.push(context, MaterialPageRoute( - builder: (context) { - return CameraSendToView(widget.user); - }, - )); - return; - } - final msgs = previewMessages - .where((x) => x.kind == MessageKind.media) - .toList(); - if (msgs.isNotEmpty && - msgs.first.kind == MessageKind.media && - msgs.first.messageOtherId != null && - msgs.first.openedAt == null) { - switch (msgs.first.downloadState) { - case DownloadState.pending: - startDownloadMedia(msgs.first, true); - return; - case DownloadState.downloaded: - Navigator.push( - context, - MaterialPageRoute(builder: (context) { - return MediaViewerView(widget.user); - }), - ); - return; - case DownloadState.downloading: - return; - } - } - Navigator.push( - context, - MaterialPageRoute(builder: (context) { - return ChatMessagesView(widget.user); - }), - ); - }, + onTap: onTap, ), ) ], diff --git a/lib/src/views/chats/chat_list_components/backup_notice.card.dart b/lib/src/views/chats/chat_list_components/backup_notice.card.dart index 4241670..bb717ce 100644 --- a/lib/src/views/chats/chat_list_components/backup_notice.card.dart +++ b/lib/src/views/chats/chat_list_components/backup_notice.card.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:twonly/globals.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/views/settings/backup/backup.view.dart'; @@ -26,11 +25,13 @@ class _BackupNoticeCardState extends State { if (user != null && (user.nextTimeToShowBackupNotice == null || DateTime.now().isAfter(user.nextTimeToShowBackupNotice!))) { - if (!gIsDemoUser && (user.twonlySafeBackup == null)) { + if (user.twonlySafeBackup == null) { showBackupNotice = true; } } - setState(() {}); + if (mounted) { + setState(() {}); + } } @override diff --git a/lib/src/views/chats/chat_list_components/connection_info.comp.dart b/lib/src/views/chats/chat_list_components/connection_info.comp.dart index 267a64d..524a273 100644 --- a/lib/src/views/chats/chat_list_components/connection_info.comp.dart +++ b/lib/src/views/chats/chat_list_components/connection_info.comp.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:twonly/globals.dart'; import 'package:twonly/src/utils/misc.dart'; class ConnectionInfo extends StatefulWidget { @@ -61,7 +60,7 @@ class _ConnectionInfoWidgetState extends State @override Widget build(BuildContext context) { - if (!showAnimation || gIsDemoUser) return Container(); + if (!showAnimation) return Container(); final screenWidth = MediaQuery.of(context).size.width; return SizedBox( diff --git a/lib/src/views/chats/chat_list_components/feedback_btn.dart b/lib/src/views/chats/chat_list_components/feedback_btn.dart new file mode 100644 index 0000000..108d3b8 --- /dev/null +++ b/lib/src/views/chats/chat_list_components/feedback_btn.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/utils/storage.dart'; +import 'package:twonly/src/views/settings/help/contact_us.view.dart'; + +class FeedbackIconButton extends StatefulWidget { + const FeedbackIconButton({super.key}); + + @override + State createState() => _FeedbackIconButtonState(); +} + +class _FeedbackIconButtonState extends State { + bool showFeedbackShortcut = false; + + @override + void initState() { + super.initState(); + initAsync(); + } + + Future initAsync() async { + final user = await getUser(); + if (user == null || !mounted) return; + setState(() { + showFeedbackShortcut = user.showFeedbackShortcut; + }); + } + + @override + Widget build(BuildContext context) { + if (!showFeedbackShortcut) { + return const SizedBox.shrink(); + } + + return IconButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const ContactUsView(), + ), + ); + }, + color: Colors.grey, + tooltip: context.lang.feedbackTooltip, + icon: const FaIcon(FontAwesomeIcons.commentDots, size: 19), + ); + } +} diff --git a/lib/src/views/home.view.dart b/lib/src/views/home.view.dart index b4f28a2..1009030 100644 --- a/lib/src/views/home.view.dart +++ b/lib/src/views/home.view.dart @@ -51,6 +51,7 @@ class HomeViewState extends State { double buttonDiameter = 100; double offsetRatio = 0; double offsetFromOne = 0; + double lastChange = 0; Timer? disableCameraTimer; bool initCameraStarted = true; @@ -62,6 +63,8 @@ class HomeViewState extends State { bool onPageView(ScrollNotification notification) { disableCameraTimer?.cancel(); if (notification.depth == 0 && notification is ScrollUpdateNotification) { + final page = homeViewPageController.page ?? 0; + lastChange = page; setState(() { offsetFromOne = 1.0 - (homeViewPageController.page ?? 0); offsetRatio = offsetFromOne.abs(); diff --git a/lib/src/views/onboarding/register.view.dart b/lib/src/views/onboarding/register.view.dart index 6b584ee..748e247 100644 --- a/lib/src/views/onboarding/register.view.dart +++ b/lib/src/views/onboarding/register.view.dart @@ -33,14 +33,14 @@ class _RegisterViewState extends State { bool _isValidUserName = false; bool _showUserNameError = false; - Future createNewUser({bool isDemoAccount = false}) async { + Future createNewUser() async { if (!_isValidUserName) { setState(() { _showUserNameError = true; }); return; } - final username = isDemoAccount ? '' : usernameController.text; + final username = usernameController.text; final inviteCode = inviteCodeController.text; setState(() { @@ -52,29 +52,27 @@ class _RegisterViewState extends State { var userId = 0; - if (!isDemoAccount) { - final res = await apiService.register(username, inviteCode); - if (res.isSuccess) { - Log.info('Got user_id ${res.value} from server'); - userId = res.value.userid.toInt() as int; - } else { - if (res.error == ErrorCode.UserIdAlreadyTaken) { - Log.error('User ID already token. Tying again.'); - await deleteLocalUserData(); - return createNewUser(); - } - if (mounted) { - setState(() { - _isTryingToRegister = false; - }); - await showAlertDialog( - context, - 'Oh no!', - errorCodeToText(context, res.error as ErrorCode), - ); - } - return; + final res = await apiService.register(username, inviteCode); + if (res.isSuccess) { + Log.info('Got user_id ${res.value} from server'); + userId = res.value.userid.toInt() as int; + } else { + if (res.error == ErrorCode.UserIdAlreadyTaken) { + Log.error('User ID already token. Tying again.'); + await deleteLocalUserData(); + return createNewUser(); } + if (mounted) { + setState(() { + _isTryingToRegister = false; + }); + await showAlertDialog( + context, + 'Oh no!', + errorCodeToText(context, res.error as ErrorCode), + ); + } + return; } setState(() { @@ -86,18 +84,13 @@ class _RegisterViewState extends State { username: username, displayName: username, subscriptionPlan: 'Preview', - isDemoUser: isDemoAccount, + isDemoUser: false, ); await const FlutterSecureStorage() .write(key: SecureStorageKeys.userData, value: jsonEncode(userData)); - if (!isDemoAccount) { - await apiService.authenticate(); - } else { - gIsDemoUser = true; - await createFakeDemoData(); - } + await apiService.authenticate(); widget.callbackOnSuccess(); } @@ -228,12 +221,6 @@ class _RegisterViewState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - // OutlinedButton.icon( - // onPressed: () { - // createNewUser(isDemoAccount: true); - // }, - // label: const Text('Demo'), - // ), OutlinedButton.icon( onPressed: () { Navigator.push(context, MaterialPageRoute( diff --git a/lib/src/views/settings/notification.view.dart b/lib/src/views/settings/notification.view.dart index 2b0cf1e..a4518a6 100644 --- a/lib/src/views/settings/notification.view.dart +++ b/lib/src/views/settings/notification.view.dart @@ -30,8 +30,8 @@ class NotificationView extends StatelessWidget { subtitle: Text(context.lang.settingsNotifyTroubleshootingDesc), onTap: () async { await initFCMAfterAuthenticated(); - final storedToken = const FlutterSecureStorage() - .read(key: SecureStorageKeys.googleFcm) as String?; + final storedToken = await (const FlutterSecureStorage() + .read(key: SecureStorageKeys.googleFcm)); await setupNotificationWithUsers(force: true); if (!context.mounted) return;