fix multiple issues

This commit is contained in:
otsmr 2025-11-07 17:44:51 +01:00
parent 95c9db86d6
commit da4917ad46
33 changed files with 125 additions and 290 deletions

View file

@ -55,6 +55,9 @@ android {
debug { debug {
applicationIdSuffix ".testing" applicationIdSuffix ".testing"
} }
// profile {
// applicationIdSuffix ".STOP"
// }
release { release {
signingConfig signingConfigs.release signingConfig signingConfigs.release
} }

View file

@ -6,4 +6,6 @@
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.CAMERA"/>
<application android:usesCleartextTraffic="true" >
</application>
</manifest> </manifest>

View file

@ -21,6 +21,7 @@ import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
import 'package:twonly/src/services/fcm.service.dart'; import 'package:twonly/src/services/fcm.service.dart';
import 'package:twonly/src/services/mediafiles/mediafile.service.dart'; import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
import 'package:twonly/src/services/notifications/setup.notifications.dart'; import 'package:twonly/src/services/notifications/setup.notifications.dart';
import 'package:twonly/src/services/twonly_safe/create_backup.twonly_safe.dart';
import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/utils/storage.dart';
@ -63,13 +64,7 @@ void main() async {
unawaited(createPushAvatars()); unawaited(createPushAvatars());
await twonlyDB.messagesDao.purgeMessageTable(); await twonlyDB.messagesDao.purgeMessageTable();
// await twonlyDB.messagesDao.resetPendingDownloadState(); unawaited(performTwonlySafeBackup());
// await twonlyDB.messageRetransmissionDao.purgeOldRetransmissions();
// await twonlyDB.signalDao.purgeOutDatedPreKeys();
// unawaited(purgeSendMediaFiles());
// unawaited(performTwonlySafeBackup());
runApp( runApp(
MultiProvider( MultiProvider(

View file

@ -109,6 +109,7 @@ class ContactsDao extends DatabaseAccessor<TwonlyDB> with _$ContactsDaoMixin {
..where( ..where(
contacts.requested.equals(true) & contacts.requested.equals(true) &
contacts.accepted.equals(false) & contacts.accepted.equals(false) &
contacts.deletedByUser.equals(false) &
contacts.blocked.equals(false), contacts.blocked.equals(false),
) )
..addColumns([count]); ..addColumns([count]);

View file

@ -281,7 +281,7 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
..where((t) => t.groupId.equals(groupId))) ..where((t) => t.groupId.equals(groupId)))
.getSingle(); .getSingle();
final totalMediaCounter = group.totalMediaCounter + 1; final totalMediaCounter = group.totalMediaCounter + (received ? 0 : 1);
var flameCounter = group.flameCounter; var flameCounter = group.flameCounter;
var maxFlameCounter = group.maxFlameCounter; var maxFlameCounter = group.maxFlameCounter;
var maxFlameCounterFrom = group.maxFlameCounterFrom; var maxFlameCounterFrom = group.maxFlameCounterFrom;
@ -321,7 +321,11 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
if (updateFlame) { if (updateFlame) {
flameCounter += 1; flameCounter += 1;
lastFlameCounterChange = Value(timestamp); lastFlameCounterChange = Value(timestamp);
if ((flameCounter + 1) >= maxFlameCounter) { // Overwrite max flame counter either the current is bigger or the th max flame counter is older then 4 days
if ((flameCounter + 1) >= maxFlameCounter ||
maxFlameCounterFrom == null ||
maxFlameCounterFrom
.isBefore(DateTime.now().subtract(const Duration(days: 5)))) {
maxFlameCounter = flameCounter + 1; maxFlameCounter = flameCounter + 1;
maxFlameCounterFrom = DateTime.now(); maxFlameCounterFrom = DateTime.now();
} }
@ -351,6 +355,15 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
); );
} }
Stream<int> watchSumTotalMediaCounter() {
final query = selectOnly(groups)
..addColumns([groups.totalMediaCounter.sum()]);
return query.watch().map((rows) {
final expr = rows.first.read(groups.totalMediaCounter.sum());
return expr ?? 0;
});
}
Future<void> increaseLastMessageExchange( Future<void> increaseLastMessageExchange(
String groupId, String groupId,
DateTime newLastMessage, DateTime newLastMessage,

View file

@ -816,5 +816,6 @@
"deleteChatAfterADay": "einem Tag.", "deleteChatAfterADay": "einem Tag.",
"deleteChatAfterAWeek": "einer Woche.", "deleteChatAfterAWeek": "einer Woche.",
"deleteChatAfterAMonth": "einem Monat.", "deleteChatAfterAMonth": "einem Monat.",
"deleteChatAfterAYear": "einem Jahr." "deleteChatAfterAYear": "einem Jahr.",
"yourTwonlyScore": "Dein twonly-Score"
} }

View file

@ -594,5 +594,6 @@
"deleteChatAfterADay": "one day.", "deleteChatAfterADay": "one day.",
"deleteChatAfterAWeek": "one week.", "deleteChatAfterAWeek": "one week.",
"deleteChatAfterAMonth": "one month.", "deleteChatAfterAMonth": "one month.",
"deleteChatAfterAYear": "one year." "deleteChatAfterAYear": "one year.",
"yourTwonlyScore": "Your twonly-Score"
} }

View file

@ -2671,6 +2671,12 @@ abstract class AppLocalizations {
/// In en, this message translates to: /// In en, this message translates to:
/// **'one year.'** /// **'one year.'**
String get deleteChatAfterAYear; String get deleteChatAfterAYear;
/// No description provided for @yourTwonlyScore.
///
/// In en, this message translates to:
/// **'Your twonly-Score'**
String get yourTwonlyScore;
} }
class _AppLocalizationsDelegate class _AppLocalizationsDelegate

View file

@ -1474,4 +1474,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get deleteChatAfterAYear => 'einem Jahr.'; String get deleteChatAfterAYear => 'einem Jahr.';
@override
String get yourTwonlyScore => 'Dein twonly-Score';
} }

View file

@ -1464,4 +1464,7 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get deleteChatAfterAYear => 'one year.'; String get deleteChatAfterAYear => 'one year.';
@override
String get yourTwonlyScore => 'Your twonly-Score';
} }

View file

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

View file

@ -35,15 +35,15 @@ Future<void> initFileDownloader() async {
try { try {
var androidConfig = []; var androidConfig = [];
if (kDebugMode) { if (!kReleaseMode) {
androidConfig = [(Config.bypassTLSCertificateValidation, kDebugMode)]; androidConfig = [(Config.bypassTLSCertificateValidation, true)];
} }
await FileDownloader().configure(androidConfig: androidConfig); await FileDownloader().configure(androidConfig: androidConfig);
} catch (e) { } catch (e) {
Log.error(e); Log.error(e);
} }
if (kDebugMode) { if (!kReleaseMode) {
FileDownloader().configureNotification( FileDownloader().configureNotification(
running: const TaskNotification( running: const TaskNotification(
'Uploading/Downloading', 'Uploading/Downloading',

View file

@ -23,9 +23,10 @@ Future<void> customLocalPushNotification(String title, String msg) async {
'1', '1',
'System', 'System',
channelDescription: 'System messages.', channelDescription: 'System messages.',
importance: Importance.max, importance: Importance.high,
priority: Priority.max, priority: Priority.high,
styleInformation: BigTextStyleInformation(msg), styleInformation: BigTextStyleInformation(msg),
icon: 'ic_launcher_foreground',
); );
const darwinNotificationDetails = DarwinNotificationDetails(); const darwinNotificationDetails = DarwinNotificationDetails();
@ -34,8 +35,10 @@ Future<void> customLocalPushNotification(String title, String msg) async {
iOS: darwinNotificationDetails, iOS: darwinNotificationDetails,
); );
final id = Random.secure().nextInt(9999);
await flutterLocalNotificationsPlugin.show( await flutterLocalNotificationsPlugin.show(
Random.secure().nextInt(9999), id,
title, title,
msg, msg,
notificationDetails, notificationDetails,
@ -95,11 +98,11 @@ Future<void> handlePushData(String pushDataB64) async {
} }
} }
} catch (e) { } catch (e) {
Log.error(e);
await customLocalPushNotification( await customLocalPushNotification(
'Du hast eine neue Nachricht.', 'Du hast eine neue Nachricht.',
'Öffne twonly um mehr zu erfahren.', 'Öffne twonly um mehr zu erfahren.',
); );
Log.error(e);
} }
} }
@ -161,6 +164,7 @@ Future<void> showLocalPushNotification(
priority: Priority.max, priority: Priority.max,
ticker: 'You got a new message.', ticker: 'You got a new message.',
largeIcon: styleInformation, largeIcon: styleInformation,
icon: 'ic_launcher_foreground',
); );
const darwinNotificationDetails = DarwinNotificationDetails(); const darwinNotificationDetails = DarwinNotificationDetails();
@ -174,7 +178,7 @@ Future<void> showLocalPushNotification(
title, title,
body, body,
notificationDetails, notificationDetails,
payload: pushNotification.kind.name, // payload: pushNotification.kind.name,
); );
} }

View file

@ -51,7 +51,7 @@ Future<void> setupNotificationWithUsers({
final pushUser = final pushUser =
pushUsers.firstWhereOrNull((x) => x.userId == contact.userId); pushUsers.firstWhereOrNull((x) => x.userId == contact.userId);
if (pushUser != null) { if (pushUser != null && pushUser.pushKeys.isNotEmpty) {
// make it harder to predict the change of the key // make it harder to predict the change of the key
final timeBefore = final timeBefore =
DateTime.now().subtract(Duration(days: 5 + random.nextInt(5))); DateTime.now().subtract(Duration(days: 5 + random.nextInt(5)));

View file

@ -9,7 +9,7 @@ void initLogger() {
Logger.root.level = Level.ALL; Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) async { Logger.root.onRecord.listen((record) async {
await _writeLogToFile(record); await _writeLogToFile(record);
if (kDebugMode) { if (!kReleaseMode) {
print( print(
'${record.level.name} [twonly] ${record.loggerName} > ${record.message}', '${record.level.name} [twonly] ${record.loggerName} > ${record.message}',
); );

View file

@ -263,7 +263,7 @@ bool isUUIDNewer(String uuid1, String uuid2) {
return timestamp1 > timestamp2; return timestamp1 > timestamp2;
} catch (e) { } catch (e) {
Log.error(e); Log.error(e);
return true; return false;
} }
} }

View file

@ -3,10 +3,10 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:camera/camera.dart'; import 'package:camera/camera.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/globals.dart'; import 'package:twonly/globals.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/views/camera/camera_preview_controller_view.dart'; import 'package:twonly/src/views/camera/camera_preview_controller_view.dart';
class CameraZoomButtons extends StatefulWidget { class CameraZoomButtons extends StatefulWidget {
@ -50,6 +50,7 @@ class _CameraZoomButtonsState extends State<CameraZoomButtons> {
Future<void> initAsync() async { Future<void> initAsync() async {
showWideAngleZoom = (await widget.controller.getMinZoomLevel()) < 1; showWideAngleZoom = (await widget.controller.getMinZoomLevel()) < 1;
Log.info('Found ${gCameras.length} cameras for zoom.');
if (!showWideAngleZoom && Platform.isIOS && gCameras.length == 3) { if (!showWideAngleZoom && Platform.isIOS && gCameras.length == 3) {
showWideAngleZoomIOS = true; showWideAngleZoomIOS = true;
} }

View file

@ -131,7 +131,7 @@ Future<List<Sticker>> getStickerIndex() async {
final indexFile = File('${directory.path}/stickers.json'); final indexFile = File('${directory.path}/stickers.json');
var res = <Sticker>[]; var res = <Sticker>[];
if (indexFile.existsSync() && !kDebugMode) { if (indexFile.existsSync() && kReleaseMode) {
final lastModified = indexFile.lastModifiedSync(); final lastModified = indexFile.lastModifiedSync();
final difference = DateTime.now().difference(lastModified); final difference = DateTime.now().difference(lastModified);
final content = await indexFile.readAsString(); final content = await indexFile.readAsString();

View file

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
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';
@ -45,10 +47,10 @@ class ReactionRow extends StatelessWidget {
child: Center( child: Center(
child: Text( child: Text(
reaction.emoji, reaction.emoji,
style: const TextStyle(fontSize: 18), style: TextStyle(fontSize: Platform.isIOS ? 18 : 15),
strutStyle: const StrutStyle( strutStyle: StrutStyle(
forceStrutHeight: true, forceStrutHeight: true,
height: 1.6, height: Platform.isIOS ? 1.6 : 1.3,
), ),
), ),
), ),
@ -114,7 +116,7 @@ class ReactionRow extends StatelessWidget {
entry.$2.toString(), entry.$2.toString(),
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontSize: 15, fontSize: 13,
color: color:
isDarkMode(context) ? Colors.white : Colors.black, isDarkMode(context) ? Colors.white : Colors.black,
decoration: TextDecoration.none, decoration: TextDecoration.none,

View file

@ -67,9 +67,10 @@ class _ChatMediaEntryState extends State<ChatMediaEntry> {
if (widget.message.openedAt == null || widget.message.mediaStored) { if (widget.message.openedAt == null || widget.message.mediaStored) {
return; return;
} }
if (widget.mediaService.tempPath.existsSync()) { if (widget.mediaService.tempPath.existsSync() &&
await sendCipherTextToGroup( widget.message.senderId != null) {
widget.message.groupId, await sendCipherText(
widget.message.senderId!,
EncryptedContent( EncryptedContent(
mediaUpdate: EncryptedContent_MediaUpdate( mediaUpdate: EncryptedContent_MediaUpdate(
type: EncryptedContent_MediaUpdate_Type.REOPENED, type: EncryptedContent_MediaUpdate_Type.REOPENED,

View file

@ -77,10 +77,10 @@ class ChatTextEntry extends StatelessWidget {
children: [ children: [
if (info.expanded) if (info.expanded)
Expanded( Expanded(
child: BetterText(text: text, textColor: info.textColor), child: BetterText(text: info.text, textColor: info.textColor),
) )
else ...[ else ...[
BetterText(text: text, textColor: info.textColor), BetterText(text: info.text, textColor: info.textColor),
SizedBox( SizedBox(
width: info.spacerWidth, width: info.spacerWidth,
), ),

View file

@ -242,10 +242,10 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
child: Center( child: Center(
child: Text( child: Text(
widget.lastReaction!.emoji, widget.lastReaction!.emoji,
style: const TextStyle(fontSize: 18), style: const TextStyle(fontSize: 15),
strutStyle: const StrutStyle( strutStyle: const StrutStyle(
forceStrutHeight: true, forceStrutHeight: true,
height: 1.6, height: 1.4,
), ),
), ),
), ),

View file

@ -1,242 +0,0 @@
import 'dart:io';
import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:audio_waveforms_example/chat_bubble.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Audio Waveforms',
debugShowCheckedModeBanner: false,
home: Home(),
);
}
}
class Home extends StatefulWidget {
const Home({super.key});
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
late final RecorderController recorderController;
String? path;
String? musicFile;
bool isRecording = false;
bool isRecordingCompleted = false;
bool isLoading = true;
late Directory appDirectory;
@override
void initState() {
super.initState();
_getDir();
_initialiseControllers();
}
void _getDir() async {
appDirectory = await getApplicationDocumentsDirectory();
path = "${appDirectory.path}/recording.m4a";
isLoading = false;
setState(() {});
}
void _initialiseControllers() {
recorderController = RecorderController()
..androidEncoder = AndroidEncoder.aac
..androidOutputFormat = AndroidOutputFormat.mpeg4
..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC
..sampleRate = 44100;
}
void _pickFile() async {
FilePickerResult? result = await FilePicker.platform.pickFiles();
if (result != null) {
musicFile = result.files.single.path;
setState(() {});
} else {
debugPrint("File not picked");
}
}
@override
void dispose() {
recorderController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF252331),
appBar: AppBar(
backgroundColor: const Color(0xFF252331),
elevation: 1,
centerTitle: true,
shadowColor: Colors.grey,
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'assets/images/logo.png',
scale: 1.5,
),
const SizedBox(width: 10),
const Text(
'Simform',
style: TextStyle(color: Colors.white),
),
],
),
),
body: isLoading
? const Center(
child: CircularProgressIndicator(),
)
: SafeArea(
child: Column(
children: [
const SizedBox(height: 20),
Expanded(
child: ListView.builder(
itemCount: 4,
itemBuilder: (_, index) {
return WaveBubble(
index: index + 1,
isSender: index.isOdd,
width: MediaQuery.of(context).size.width / 2,
appDirectory: appDirectory,
);
},
),
),
if (isRecordingCompleted)
WaveBubble(
path: path,
isSender: true,
appDirectory: appDirectory,
),
if (musicFile != null)
WaveBubble(
path: musicFile,
isSender: true,
appDirectory: appDirectory,
),
SafeArea(
child: Row(
children: [
AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child: isRecording
? AudioWaveforms(
enableGesture: true,
size: Size(
MediaQuery.of(context).size.width / 2,
50),
recorderController: recorderController,
waveStyle: const WaveStyle(
waveColor: Colors.white,
extendWaveform: true,
showMiddleLine: false,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
color: const Color(0xFF1E1B26),
),
padding: const EdgeInsets.only(left: 18),
margin: const EdgeInsets.symmetric(
horizontal: 15),
)
: Container(
width:
MediaQuery.of(context).size.width / 1.7,
height: 50,
decoration: BoxDecoration(
color: const Color(0xFF1E1B26),
borderRadius: BorderRadius.circular(12.0),
),
padding: const EdgeInsets.only(left: 18),
margin: const EdgeInsets.symmetric(
horizontal: 15),
child: TextField(
readOnly: true,
decoration: InputDecoration(
hintText: "Type Something...",
hintStyle: const TextStyle(
color: Colors.white54),
contentPadding:
const EdgeInsets.only(top: 16),
border: InputBorder.none,
suffixIcon: IconButton(
onPressed: _pickFile,
icon: Icon(Icons.adaptive.share),
color: Colors.white54,
),
),
),
),
),
IconButton(
onPressed: _refreshWave,
icon: Icon(
isRecording ? Icons.refresh : Icons.send,
color: Colors.white,
),
),
const SizedBox(width: 16),
IconButton(
onPressed: _startOrStopRecording,
icon: Icon(isRecording ? Icons.stop : Icons.mic),
color: Colors.white,
iconSize: 28,
),
],
),
),
],
),
),
);
}
void _startOrStopRecording() async {
try {
if (isRecording) {
recorderController.reset();
path = await recorderController.stop(false);
if (path != null) {
isRecordingCompleted = true;
debugPrint(path);
debugPrint("Recorded file size: ${File(path!).lengthSync()}");
}
} else {
await recorderController.record(path: path); // Path is optional
}
} catch (e) {
debugPrint(e.toString());
} finally {
if (recorderController.hasPermission) {
setState(() {
isRecording = !isRecording;
});
}
}
}
void _refreshWave() {
if (isRecording) recorderController.refresh();
}
}

View file

@ -82,6 +82,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
_subscription.cancel(); _subscription.cancel();
downloadStateListener?.cancel(); downloadStateListener?.cancel();
videoController?.dispose(); videoController?.dispose();
videoController = null;
super.dispose(); super.dispose();
} }
@ -141,7 +142,9 @@ class _MediaViewerViewState extends State<MediaViewerView> {
Future<void> loadCurrentMediaFile({bool showTwonly = false}) async { Future<void> loadCurrentMediaFile({bool showTwonly = false}) async {
if (!mounted || !context.mounted) return; if (!mounted || !context.mounted) return;
if (allMediaFiles.isEmpty) return nextMediaOrExit(); if (allMediaFiles.isEmpty || allMediaFiles.first.mediaId == null) {
return nextMediaOrExit();
}
await _noScreenshot.screenshotOff(); await _noScreenshot.screenshotOff();
setState(() { setState(() {
@ -175,7 +178,8 @@ class _MediaViewerViewState extends State<MediaViewerView> {
downloadTriggered = true; downloadTriggered = true;
final mediaFile = await twonlyDB.mediaFilesDao final mediaFile = await twonlyDB.mediaFilesDao
.getMediaFileById(allMediaFiles.first.mediaId!); .getMediaFileById(allMediaFiles.first.mediaId!);
await startDownloadMedia(mediaFile!, true); if (mediaFile == null) return;
await startDownloadMedia(mediaFile, true);
unawaited(tryDownloadAllMediaFiles(force: true)); unawaited(tryDownloadAllMediaFiles(force: true));
} }
return; return;
@ -269,6 +273,10 @@ class _MediaViewerViewState extends State<MediaViewerView> {
} }
}); });
progressTimer = Timer.periodic(const Duration(milliseconds: 10), (timer) { progressTimer = Timer.periodic(const Duration(milliseconds: 10), (timer) {
if (currentMedia!.mediaFile.displayLimitInMilliseconds == null ||
canBeSeenUntil == null) {
return;
}
final difference = canBeSeenUntil!.difference(DateTime.now()); final difference = canBeSeenUntil!.difference(DateTime.now());
// Calculate the progress as a value between 0.0 and 1.0 // Calculate the progress as a value between 0.0 and 1.0
progress = difference.inMilliseconds / progress = difference.inMilliseconds /
@ -312,10 +320,12 @@ class _MediaViewerViewState extends State<MediaViewerView> {
void displayShortReactions() { void displayShortReactions() {
final renderBox = final renderBox =
mediaWidgetKey.currentContext!.findRenderObject()! as RenderBox; mediaWidgetKey.currentContext!.findRenderObject() as RenderBox?;
setState(() { setState(() {
showShortReactions = true; showShortReactions = true;
if (renderBox != null) {
mediaViewerDistanceFromBottom = renderBox.size.height; mediaViewerDistanceFromBottom = renderBox.size.height;
}
}); });
} }

View file

@ -85,7 +85,7 @@ class _MaxFlameListTitleState extends State<MaxFlameListTitle> {
if (_directChat == null || if (_directChat == null ||
_directChat!.maxFlameCounter == 0 || _directChat!.maxFlameCounter == 0 ||
_flameCounter >= (_directChat!.maxFlameCounter + 1) || _flameCounter >= (_directChat!.maxFlameCounter + 1) ||
_directChat!.lastFlameCounterChange! _directChat!.maxFlameCounterFrom!
.isBefore(DateTime.now().subtract(const Duration(days: 4)))) { .isBefore(DateTime.now().subtract(const Duration(days: 4)))) {
return Container(); return Container();
} }

View file

@ -37,7 +37,7 @@ class _AccountViewState extends State<AccountView> {
.where( .where(
(x) => (x) =>
x.transactionType != Response_TransactionTypes.ThanksForTesting || x.transactionType != Response_TransactionTypes.ThanksForTesting ||
kDebugMode, !kReleaseMode,
) )
.map((a) => a.depositCents.toInt()) .map((a) => a.depositCents.toInt())
.sum; .sum;
@ -101,7 +101,7 @@ class _AccountViewState extends State<AccountView> {
), ),
) )
: Text(context.lang.settingsAccountDeleteAccountNoBallance), : Text(context.lang.settingsAccountDeleteAccountNoBallance),
onLongPress: kDebugMode onLongPress: !kReleaseMode
? () async { ? () async {
await deleteLocalUserData(); await deleteLocalUserData();
await Restart.restartApp( await Restart.restartApp(

View file

@ -204,7 +204,7 @@ class _TwonlyIdentityBackupViewState extends State<TwonlyIdentityBackupView> {
onPressed: (!isLoading && onPressed: (!isLoading &&
(passwordCtrl.text == repeatedPasswordCtrl.text && (passwordCtrl.text == repeatedPasswordCtrl.text &&
passwordCtrl.text.length >= 8 || passwordCtrl.text.length >= 8 ||
kDebugMode)) !kReleaseMode))
? onPressedEnableTwonlySafe ? onPressedEnableTwonlySafe
: null, : null,
icon: isLoading icon: isLoading

View file

@ -32,7 +32,7 @@ class _AutomatedTestingViewState extends State<AutomatedTestingView> {
), ),
body: ListView( body: ListView(
children: [ children: [
if (kDebugMode) if (!kReleaseMode)
ListTile( ListTile(
title: const Text('Sending a lot of messages.'), title: const Text('Sending a lot of messages.'),
subtitle: Text(lotsOfMessagesStatus), subtitle: Text(lotsOfMessagesStatus),

View file

@ -66,7 +66,7 @@ class _DeveloperSettingsViewState extends State<DeveloperSettingsView> {
); );
}, },
), ),
// if (kDebugMode) // if (!kReleaseMode)
// ListTile( // ListTile(
// title: const Text('FlameSync Test'), // title: const Text('FlameSync Test'),
// onTap: () async { // onTap: () async {
@ -74,7 +74,7 @@ class _DeveloperSettingsViewState extends State<DeveloperSettingsView> {
// await syncFlameCounters(); // await syncFlameCounters();
// }, // },
// ), // ),
if (kDebugMode) if (!kReleaseMode)
ListTile( ListTile(
title: const Text('Automated Testing'), title: const Text('Automated Testing'),
onTap: () async { onTap: () async {

View file

@ -25,11 +25,26 @@ class _ProfileViewState extends State<ProfileView> {
final AvatarMakerController _avatarMakerController = final AvatarMakerController _avatarMakerController =
PersistentAvatarMakerController(customizedPropertyCategories: []); PersistentAvatarMakerController(customizedPropertyCategories: []);
int twonlyScore = 0;
late StreamSubscription<int> twonlyScoreSub;
@override @override
void initState() { void initState() {
twonlyScoreSub =
twonlyDB.groupsDao.watchSumTotalMediaCounter().listen((update) {
setState(() {
twonlyScore = update;
});
});
super.initState(); super.initState();
} }
@override
void dispose() {
twonlyScoreSub.cancel();
super.dispose();
}
Future<void> updateUserDisplayName(String displayName) async { Future<void> updateUserDisplayName(String displayName) async {
await updateUserdata((user) { await updateUserdata((user) {
user user
@ -156,6 +171,17 @@ class _ProfileViewState extends State<ProfileView> {
} }
}, },
), ),
BetterListTile(
text: context.lang.yourTwonlyScore,
icon: FontAwesomeIcons.trophy,
trailing: Text(
twonlyScore.toString(),
style: TextStyle(
color: context.color.primary,
fontSize: 18,
),
),
),
], ],
), ),
); );

View file

@ -102,6 +102,7 @@ class _CheckoutViewState extends State<CheckoutView> {
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),
child: Card( child: Card(
color: context.color.surfaceContainer,
child: Padding( child: Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Row( child: Row(

View file

@ -111,6 +111,7 @@ class _SelectPaymentViewState extends State<SelectPaymentView> {
Padding( Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Card( child: Card(
color: context.color.surfaceContainer,
child: Padding( child: Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Row( child: Row(
@ -193,6 +194,7 @@ class _SelectPaymentViewState extends State<SelectPaymentView> {
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),
child: Card( child: Card(
color: context.color.surfaceContainer,
child: Padding( child: Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Row( child: Row(
@ -215,6 +217,7 @@ class _SelectPaymentViewState extends State<SelectPaymentView> {
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),
child: Card( child: Card(
color: context.color.surfaceContainer,
child: Padding( child: Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Row( child: Row(

View file

@ -444,6 +444,7 @@ class PlanCard extends StatelessWidget {
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
color: context.color.surfaceContainer,
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Column( child: Column(