mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-04-18 14:22:53 +00:00
add troubleshooting for notifications & video stab
This commit is contained in:
parent
c48ce31bc6
commit
955992d5d2
25 changed files with 763 additions and 479 deletions
|
|
@ -1,5 +1,11 @@
|
|||
# Changelog
|
||||
|
||||
## 0.1.2
|
||||
|
||||
- New: Developer settings to reduce flames
|
||||
- Improve: Improved troubleshooting for issues with push notifications
|
||||
- Fix: Flash not activated when starting a video recording
|
||||
|
||||
## 0.1.1
|
||||
|
||||
- New: Groups can now collect flames as well
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ class Routes {
|
|||
static const String chatsStartNewChat = '/chats/start_new_chat';
|
||||
static const String chatsCameraSendTo = '/chats/camera_send_to';
|
||||
static const String chatsMediaViewer = '/chats/media_viewer';
|
||||
static const String chatsMessages = '/chats/messages';
|
||||
|
||||
static String chatsMessages(String groupId) => '/chats/messages/$groupId';
|
||||
|
||||
static String groupCreateSelectMember(String? groupId) =>
|
||||
'/group/create/select_member${groupId == null ? '' : '/$groupId'}';
|
||||
|
|
@ -53,5 +54,7 @@ class Routes {
|
|||
'/settings/developer/retransmission_database';
|
||||
static const String settingsDeveloperAutomatedTesting =
|
||||
'/settings/developer/automated_testing';
|
||||
static const String settingsDeveloperReduceFlames =
|
||||
'/settings/developer/reduce_flames';
|
||||
static const String settingsInvite = '/settings/invite';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -733,9 +733,33 @@ abstract class AppLocalizations {
|
|||
/// No description provided for @settingsNotifyTroubleshootingNoProblemDesc.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Press OK to receive a test notification. When you receive no message even after waiting for 10 minutes, please send us your debug log in Settings > Help > Debug log, so we can look at that issue.'**
|
||||
/// **'Press OK to receive a test notification. If you do not receive the test notification, please click on the new menu item that appears after you click “OK”.'**
|
||||
String get settingsNotifyTroubleshootingNoProblemDesc;
|
||||
|
||||
/// No description provided for @settingsNotifyResetTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Didn\'t receive a test notification?'**
|
||||
String get settingsNotifyResetTitle;
|
||||
|
||||
/// No description provided for @settingsNotifyResetTitleSubtitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'If you haven\'t received any test notifications, click here to reset your notification tokens.'**
|
||||
String get settingsNotifyResetTitleSubtitle;
|
||||
|
||||
/// No description provided for @settingsNotifyResetTitleReset.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Your notification tokens have been reset.'**
|
||||
String get settingsNotifyResetTitleReset;
|
||||
|
||||
/// No description provided for @settingsNotifyResetTitleResetDesc.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'If the problem persists, please send us your debug log via Settings > Help so we can investigate the issue.'**
|
||||
String get settingsNotifyResetTitleResetDesc;
|
||||
|
||||
/// No description provided for @settingsHelp.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -356,7 +356,22 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get settingsNotifyTroubleshootingNoProblemDesc =>
|
||||
'Klicke auf OK, um eine Testbenachrichtigung zu erhalten. Wenn du auch nach 10 Minuten warten keine Nachricht erhältst, sende uns bitte dein Diagnoseprotokoll unter Einstellungen > Hilfe > Diagnoseprotokoll, damit wir uns das Problem ansehen können.';
|
||||
'Um eine Testbenachrichtigung zu erhalten, klicke auf OK. Falls du die Testbenachrichtigung nicht erhältst, klicke bitte auf den neuen Menüpunkt, der nach dem Klicken auf „OK“ angezeigt wird.';
|
||||
|
||||
@override
|
||||
String get settingsNotifyResetTitle => 'Keine Testbenachrichtigung erhalten?';
|
||||
|
||||
@override
|
||||
String get settingsNotifyResetTitleSubtitle =>
|
||||
'Falls du keine Testbenachrichtigungen erhalten hast, klicke hier, um deine Benachrichtigungstoken zurückzusetzen.';
|
||||
|
||||
@override
|
||||
String get settingsNotifyResetTitleReset =>
|
||||
'Deine Benachrichtigungstoken wurden zurückgesetzt.';
|
||||
|
||||
@override
|
||||
String get settingsNotifyResetTitleResetDesc =>
|
||||
'Sollte das Problem weiterhin bestehen, sende uns bitte dein Debug-Protokoll über „Einstellungen“ > „Hilfe“, damit wir das Problem untersuchen können.';
|
||||
|
||||
@override
|
||||
String get settingsHelp => 'Hilfe';
|
||||
|
|
|
|||
|
|
@ -351,7 +351,22 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get settingsNotifyTroubleshootingNoProblemDesc =>
|
||||
'Press OK to receive a test notification. When you receive no message even after waiting for 10 minutes, please send us your debug log in Settings > Help > Debug log, so we can look at that issue.';
|
||||
'Press OK to receive a test notification. If you do not receive the test notification, please click on the new menu item that appears after you click “OK”.';
|
||||
|
||||
@override
|
||||
String get settingsNotifyResetTitle => 'Didn\'t receive a test notification?';
|
||||
|
||||
@override
|
||||
String get settingsNotifyResetTitleSubtitle =>
|
||||
'If you haven\'t received any test notifications, click here to reset your notification tokens.';
|
||||
|
||||
@override
|
||||
String get settingsNotifyResetTitleReset =>
|
||||
'Your notification tokens have been reset.';
|
||||
|
||||
@override
|
||||
String get settingsNotifyResetTitleResetDesc =>
|
||||
'If the problem persists, please send us your debug log via Settings > Help so we can investigate the issue.';
|
||||
|
||||
@override
|
||||
String get settingsHelp => 'Help';
|
||||
|
|
|
|||
|
|
@ -351,7 +351,22 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get settingsNotifyTroubleshootingNoProblemDesc =>
|
||||
'Press OK to receive a test notification. When you receive no message even after waiting for 10 minutes, please send us your debug log in Settings > Help > Debug log, so we can look at that issue.';
|
||||
'Press OK to receive a test notification. If you do not receive the test notification, please click on the new menu item that appears after you click “OK”.';
|
||||
|
||||
@override
|
||||
String get settingsNotifyResetTitle => 'Didn\'t receive a test notification?';
|
||||
|
||||
@override
|
||||
String get settingsNotifyResetTitleSubtitle =>
|
||||
'If you haven\'t received any test notifications, click here to reset your notification tokens.';
|
||||
|
||||
@override
|
||||
String get settingsNotifyResetTitleReset =>
|
||||
'Your notification tokens have been reset.';
|
||||
|
||||
@override
|
||||
String get settingsNotifyResetTitleResetDesc =>
|
||||
'If the problem persists, please send us your debug log via Settings > Help so we can investigate the issue.';
|
||||
|
||||
@override
|
||||
String get settingsHelp => 'Help';
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ class UserData {
|
|||
@JsonKey(defaultValue: false)
|
||||
bool requestedAudioPermission = false;
|
||||
|
||||
@JsonKey(defaultValue: false)
|
||||
bool videoStabilizationEnabled = false;
|
||||
|
||||
@JsonKey(defaultValue: true)
|
||||
bool showFeedbackShortcut = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) =>
|
|||
..defaultShowTime = (json['defaultShowTime'] as num?)?.toInt()
|
||||
..requestedAudioPermission =
|
||||
json['requestedAudioPermission'] as bool? ?? false
|
||||
..videoStabilizationEnabled =
|
||||
json['videoStabilizationEnabled'] as bool? ?? false
|
||||
..showFeedbackShortcut = json['showFeedbackShortcut'] as bool? ?? true
|
||||
..showShowImagePreviewWhenSending =
|
||||
json['showShowImagePreviewWhenSending'] as bool? ?? false
|
||||
|
|
@ -105,6 +107,7 @@ Map<String, dynamic> _$UserDataToJson(UserData instance) => <String, dynamic>{
|
|||
'themeMode': _$ThemeModeEnumMap[instance.themeMode]!,
|
||||
'defaultShowTime': instance.defaultShowTime,
|
||||
'requestedAudioPermission': instance.requestedAudioPermission,
|
||||
'videoStabilizationEnabled': instance.videoStabilizationEnabled,
|
||||
'showFeedbackShortcut': instance.showFeedbackShortcut,
|
||||
'showShowImagePreviewWhenSending': instance.showShowImagePreviewWhenSending,
|
||||
'startWithCameraOpen': instance.startWithCameraOpen,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import 'package:twonly/src/views/settings/data_and_storage/export_media.view.dar
|
|||
import 'package:twonly/src/views/settings/data_and_storage/import_media.view.dart';
|
||||
import 'package:twonly/src/views/settings/developer/automated_testing.view.dart';
|
||||
import 'package:twonly/src/views/settings/developer/developer.view.dart';
|
||||
import 'package:twonly/src/views/settings/developer/reduce_flames.view.dart';
|
||||
import 'package:twonly/src/views/settings/developer/retransmission_data.view.dart';
|
||||
import 'package:twonly/src/views/settings/help/changelog.view.dart';
|
||||
import 'package:twonly/src/views/settings/help/contact_us.view.dart';
|
||||
|
|
@ -84,10 +85,10 @@ final routerProvider = GoRouter(
|
|||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'messages',
|
||||
path: 'messages/:groupId',
|
||||
builder: (context, state) {
|
||||
final group = state.extra! as Group;
|
||||
return ChatMessagesView(group);
|
||||
final groupId = state.pathParameters['groupId']!;
|
||||
return ChatMessagesView(groupId);
|
||||
},
|
||||
),
|
||||
],
|
||||
|
|
@ -280,6 +281,10 @@ final routerProvider = GoRouter(
|
|||
path: 'automated_testing',
|
||||
builder: (context, state) => const AutomatedTestingView(),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'reduce_flames',
|
||||
builder: (context, state) => const ReduceFlamesView(),
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
|
|
|
|||
|
|
@ -280,6 +280,14 @@ Future<void> _createUploadRequest(MediaFileService media) async {
|
|||
}
|
||||
}
|
||||
|
||||
final contact = await twonlyDB.contactsDao.getContactById(
|
||||
groupMember.contactId,
|
||||
);
|
||||
|
||||
if (contact == null || contact.accountDeleted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final downloadToken = getRandomUint8List(32);
|
||||
|
||||
late EncryptedContent_Media_Type type;
|
||||
|
|
@ -329,10 +337,11 @@ Future<void> _createUploadRequest(MediaFileService media) async {
|
|||
Log.error(
|
||||
'Could not generate ciphertext message for ${groupMember.contactId}',
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
final messageOnSuccess = TextMessage()
|
||||
..body = cipherText!.$1
|
||||
..body = cipherText.$1
|
||||
..userId = Int64(groupMember.contactId);
|
||||
|
||||
if (cipherText.$2 != null) {
|
||||
|
|
|
|||
|
|
@ -13,8 +13,9 @@ Future<void> syncFlameCounters({String? forceForGroup}) async {
|
|||
final groups = await twonlyDB.groupsDao.getAllGroups();
|
||||
if (groups.isEmpty) return;
|
||||
final maxMessageCounter = groups.map((x) => x.totalMediaCounter).max;
|
||||
final bestFriend =
|
||||
groups.firstWhere((x) => x.totalMediaCounter == maxMessageCounter);
|
||||
final bestFriend = groups.firstWhere(
|
||||
(x) => x.totalMediaCounter == maxMessageCounter,
|
||||
);
|
||||
|
||||
if (gUser.myBestFriendGroupId != bestFriend.groupId) {
|
||||
await updateUserdata((user) {
|
||||
|
|
@ -42,8 +43,9 @@ Future<void> syncFlameCounters({String? forceForGroup}) async {
|
|||
EncryptedContent(
|
||||
flameSync: EncryptedContent_FlameSync(
|
||||
flameCounter: Int64(flameCounter),
|
||||
lastFlameCounterChange:
|
||||
Int64(group.lastFlameCounterChange!.millisecondsSinceEpoch),
|
||||
lastFlameCounterChange: Int64(
|
||||
group.lastFlameCounterChange!.millisecondsSinceEpoch,
|
||||
),
|
||||
bestFriend: group.groupId == bestFriend.groupId,
|
||||
forceUpdate: group.groupId == forceForGroup,
|
||||
),
|
||||
|
|
@ -134,8 +136,9 @@ Future<void> incFlameCounter(
|
|||
// Overwrite max flame counter either the current is bigger or the the max flame counter is older then 4 days
|
||||
if (flameCounter >= maxFlameCounter ||
|
||||
maxFlameCounterFrom == null ||
|
||||
maxFlameCounterFrom
|
||||
.isBefore(clock.now().subtract(const Duration(days: 5)))) {
|
||||
maxFlameCounterFrom.isBefore(
|
||||
clock.now().subtract(const Duration(days: 5)),
|
||||
)) {
|
||||
maxFlameCounter = flameCounter;
|
||||
maxFlameCounterFrom = clock.now();
|
||||
}
|
||||
|
|
@ -172,6 +175,7 @@ bool isItPossibleToRestoreFlames(Group group) {
|
|||
final flameCounter = getFlameCounterFromGroup(group);
|
||||
return group.maxFlameCounter > 2 &&
|
||||
flameCounter < group.maxFlameCounter &&
|
||||
group.maxFlameCounterFrom!
|
||||
.isAfter(clock.now().subtract(const Duration(days: 5)));
|
||||
group.maxFlameCounterFrom!.isAfter(
|
||||
clock.now().subtract(const Duration(days: 5)),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:firebase_app_installations/firebase_app_installations.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
|
|
@ -41,22 +42,31 @@ Future<void> checkForTokenUpdates() async {
|
|||
Log.error('Could not get fcm token');
|
||||
return;
|
||||
}
|
||||
Log.info('Loaded fcm token');
|
||||
|
||||
Log.info('Loaded FCM token.');
|
||||
|
||||
if (storedToken == null || fcmToken != storedToken) {
|
||||
Log.info('Got new FCM TOKEN.');
|
||||
await storage.write(key: SecureStorageKeys.googleFcm, value: fcmToken);
|
||||
await updateUserdata((u) {
|
||||
u.updateFCMToken = true;
|
||||
return u;
|
||||
});
|
||||
await storage.write(key: SecureStorageKeys.googleFcm, value: fcmToken);
|
||||
}
|
||||
|
||||
FirebaseMessaging.instance.onTokenRefresh.listen((fcmToken) async {
|
||||
FirebaseMessaging.instance.onTokenRefresh
|
||||
.listen((fcmToken) async {
|
||||
Log.info('Got new FCM TOKEN.');
|
||||
await storage.write(
|
||||
key: SecureStorageKeys.googleFcm,
|
||||
value: fcmToken,
|
||||
);
|
||||
await updateUserdata((u) {
|
||||
u.updateFCMToken = true;
|
||||
return u;
|
||||
});
|
||||
await storage.write(key: SecureStorageKeys.googleFcm, value: fcmToken);
|
||||
}).onError((err) {
|
||||
})
|
||||
.onError((err) {
|
||||
Log.error('could not listen on token refresh');
|
||||
});
|
||||
} catch (e) {
|
||||
|
|
@ -64,21 +74,35 @@ Future<void> checkForTokenUpdates() async {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> initFCMAfterAuthenticated() async {
|
||||
if (gUser.updateFCMToken) {
|
||||
Future<void> initFCMAfterAuthenticated({bool force = false}) async {
|
||||
if (gUser.updateFCMToken || force) {
|
||||
const storage = FlutterSecureStorage();
|
||||
final storedToken = await storage.read(key: SecureStorageKeys.googleFcm);
|
||||
if (storedToken != null) {
|
||||
final res = await apiService.updateFCMToken(storedToken);
|
||||
if (res.isSuccess) {
|
||||
Log.info('Uploaded new fmt token!');
|
||||
Log.info('Uploaded new FCM token!');
|
||||
await updateUserdata((u) {
|
||||
u.updateFCMToken = false;
|
||||
return u;
|
||||
});
|
||||
} else {
|
||||
Log.error('Could not update FCM token!');
|
||||
}
|
||||
} else {
|
||||
Log.error('Could not send FCM update to server as token is empty.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> resetFCMTokens() async {
|
||||
await FirebaseInstallations.instance.delete();
|
||||
Log.info('Firebase Installation successfully deleted.');
|
||||
await FirebaseMessaging.instance.deleteToken();
|
||||
Log.info('Old FCM deleted.');
|
||||
await const FlutterSecureStorage().delete(key: SecureStorageKeys.googleFcm);
|
||||
await checkForTokenUpdates();
|
||||
await initFCMAfterAuthenticated(force: true);
|
||||
}
|
||||
|
||||
Future<void> initFCMService() async {
|
||||
|
|
|
|||
|
|
@ -129,9 +129,9 @@ Future<void> cleanLogFile() async {
|
|||
if (logFile.existsSync()) {
|
||||
final lines = await logFile.readAsLines();
|
||||
|
||||
if (lines.length <= 10000) return;
|
||||
if (lines.length <= 100000) return;
|
||||
|
||||
final removeCount = lines.length - 10000;
|
||||
final removeCount = lines.length - 100000;
|
||||
final remaining = lines.sublist(removeCount, lines.length);
|
||||
|
||||
final sink = logFile.openWrite()..writeAll(remaining, '\n');
|
||||
|
|
|
|||
|
|
@ -181,8 +181,9 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
// Maybe this is the reason?
|
||||
return;
|
||||
} else {
|
||||
androidVolumeDownSub =
|
||||
FlutterAndroidVolumeKeydown.stream.listen((event) {
|
||||
androidVolumeDownSub = FlutterAndroidVolumeKeydown.stream.listen((
|
||||
event,
|
||||
) {
|
||||
if (widget.isVisible) {
|
||||
takePicture();
|
||||
} else {
|
||||
|
|
@ -297,8 +298,9 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
return;
|
||||
}
|
||||
|
||||
final image = await mc.screenshotController
|
||||
.capture(pixelRatio: MediaQuery.of(context).devicePixelRatio);
|
||||
final image = await mc.screenshotController.capture(
|
||||
pixelRatio: MediaQuery.of(context).devicePixelRatio,
|
||||
);
|
||||
|
||||
if (await pushMediaEditor(image, null)) {
|
||||
return;
|
||||
|
|
@ -314,7 +316,8 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
bool sharedFromGallery = false,
|
||||
MediaType? mediaType,
|
||||
}) async {
|
||||
final type = mediaType ??
|
||||
final type =
|
||||
mediaType ??
|
||||
((videoFilePath != null) ? MediaType.video : MediaType.image);
|
||||
final mediaFileService = await initializeMediaUpload(
|
||||
type,
|
||||
|
|
@ -340,7 +343,8 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
await deInitVolumeControl();
|
||||
if (!mounted) return true;
|
||||
|
||||
final shouldReturn = await Navigator.push(
|
||||
final shouldReturn =
|
||||
await Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
opaque: false,
|
||||
|
|
@ -352,13 +356,15 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
mainCameraController: mc,
|
||||
previewLink: mc.sharedLinkForPreview,
|
||||
),
|
||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
||||
transitionsBuilder:
|
||||
(context, animation, secondaryAnimation, child) {
|
||||
return child;
|
||||
},
|
||||
transitionDuration: Duration.zero,
|
||||
reverseTransitionDuration: Duration.zero,
|
||||
),
|
||||
) as bool?;
|
||||
)
|
||||
as bool?;
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
mc.isSharePreviewIsShown = false;
|
||||
|
|
@ -396,13 +402,15 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
return;
|
||||
}
|
||||
|
||||
mc.selectedCameraDetails.scaleFactor = (_baseScaleFactor +
|
||||
mc.selectedCameraDetails.scaleFactor =
|
||||
(_baseScaleFactor +
|
||||
// ignore: avoid_dynamic_calls
|
||||
(_basePanY - (details.localPosition.dy as double)) / 30)
|
||||
.clamp(1, mc.selectedCameraDetails.maxAvailableZoom);
|
||||
|
||||
await mc.cameraController!
|
||||
.setZoomLevel(mc.selectedCameraDetails.scaleFactor);
|
||||
await mc.cameraController!.setZoomLevel(
|
||||
mc.selectedCameraDetails.scaleFactor,
|
||||
);
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
|
|
@ -434,8 +442,9 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
ScreenshotImage? image;
|
||||
MediaType? mediaType;
|
||||
|
||||
final isImage =
|
||||
imageExtensions.any((ext) => pickedFile.name.contains(ext));
|
||||
final isImage = imageExtensions.any(
|
||||
(ext) => pickedFile.name.contains(ext),
|
||||
);
|
||||
if (isImage) {
|
||||
if (pickedFile.name.contains('.gif')) {
|
||||
mediaType = MediaType.gif;
|
||||
|
|
@ -497,10 +506,15 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
mc.isVideoRecording = true;
|
||||
});
|
||||
|
||||
if (mc.selectedCameraDetails.isFlashOn) {
|
||||
await mc.cameraController?.setFlashMode(FlashMode.torch);
|
||||
}
|
||||
|
||||
try {
|
||||
await mc.cameraController?.startVideoRecording();
|
||||
_videoRecordingTimer =
|
||||
Timer.periodic(const Duration(milliseconds: 15), (timer) {
|
||||
_videoRecordingTimer = Timer.periodic(const Duration(milliseconds: 15), (
|
||||
timer,
|
||||
) {
|
||||
setState(() {
|
||||
_currentTime = clock.now();
|
||||
});
|
||||
|
|
@ -521,6 +535,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
mc.isVideoRecording = false;
|
||||
});
|
||||
_showCameraException(e);
|
||||
await mc.cameraController?.setFlashMode(FlashMode.off);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -531,6 +546,8 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
_videoRecordingTimer = null;
|
||||
}
|
||||
|
||||
await mc.cameraController?.setFlashMode(FlashMode.off);
|
||||
|
||||
setState(() {
|
||||
_videoRecordingStarted = null;
|
||||
mc.isVideoRecording = false;
|
||||
|
|
@ -601,8 +618,12 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
keyTriggerButton.currentContext!.findRenderObject()! as RenderBox;
|
||||
final localPosition = renderBox.globalToLocal(details.globalPosition);
|
||||
|
||||
final containerRect =
|
||||
Rect.fromLTWH(0, 0, renderBox.size.width, renderBox.size.height);
|
||||
final containerRect = Rect.fromLTWH(
|
||||
0,
|
||||
0,
|
||||
renderBox.size.width,
|
||||
renderBox.size.height,
|
||||
);
|
||||
|
||||
if (containerRect.contains(localPosition)) {
|
||||
startVideoRecording();
|
||||
|
|
@ -676,12 +697,14 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
: Colors.white.withAlpha(160),
|
||||
onPressed: () async {
|
||||
if (mc.selectedCameraDetails.isFlashOn) {
|
||||
await mc.cameraController
|
||||
?.setFlashMode(FlashMode.off);
|
||||
await mc.cameraController?.setFlashMode(
|
||||
FlashMode.off,
|
||||
);
|
||||
mc.selectedCameraDetails.isFlashOn = false;
|
||||
} else {
|
||||
await mc.cameraController
|
||||
?.setFlashMode(FlashMode.always);
|
||||
await mc.cameraController?.setFlashMode(
|
||||
FlashMode.always,
|
||||
);
|
||||
mc.selectedCameraDetails.isFlashOn = true;
|
||||
}
|
||||
setState(() {});
|
||||
|
|
@ -786,7 +809,8 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
mc.isSelectingFaceFilters
|
||||
? mc.currentFilterType.index ==
|
||||
FaceFilterType
|
||||
.values.length -
|
||||
.values
|
||||
.length -
|
||||
1
|
||||
? FontAwesomeIcons.xmark
|
||||
: FontAwesomeIcons.arrowRight
|
||||
|
|
@ -936,10 +960,13 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
: 'assets/animations/failed.lottie',
|
||||
repeat: false,
|
||||
onLoaded: (p0) {
|
||||
Future.delayed(const Duration(seconds: 4),
|
||||
Future.delayed(
|
||||
const Duration(seconds: 4),
|
||||
() {
|
||||
widget.mainCameraController.setState();
|
||||
});
|
||||
widget.mainCameraController
|
||||
.setState();
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -133,6 +133,11 @@ class MainCameraController {
|
|||
await cameraController?.initialize();
|
||||
await cameraController?.startImageStream(_processCameraImage);
|
||||
await cameraController?.setZoomLevel(selectedCameraDetails.scaleFactor);
|
||||
if (gUser.videoStabilizationEnabled) {
|
||||
await cameraController?.setVideoStabilizationMode(
|
||||
VideoStabilizationMode.level1,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (!isVideoRecording) {
|
||||
|
|
|
|||
|
|
@ -88,11 +88,13 @@ class _UserListItem extends State<GroupListItem> {
|
|||
});
|
||||
});
|
||||
|
||||
_lastMediaFilesStream =
|
||||
twonlyDB.mediaFilesDao.watchNewestMediaFiles().listen((mediaFiles) {
|
||||
_lastMediaFilesStream = twonlyDB.mediaFilesDao
|
||||
.watchNewestMediaFiles()
|
||||
.listen((mediaFiles) {
|
||||
for (final mediaFile in mediaFiles) {
|
||||
final index = _previewMediaFiles
|
||||
.indexWhere((t) => t.mediaId == mediaFile.mediaId);
|
||||
final index = _previewMediaFiles.indexWhere(
|
||||
(t) => t.mediaId == mediaFile.mediaId,
|
||||
);
|
||||
if (index >= 0) {
|
||||
_previewMediaFiles[index] = mediaFile;
|
||||
}
|
||||
|
|
@ -113,8 +115,9 @@ class _UserListItem extends State<GroupListItem> {
|
|||
_previewMessages = [];
|
||||
} else if (newMessagesNotOpened.isNotEmpty) {
|
||||
// Filter for the preview non opened messages. First messages which where send but not yet opened by the other side.
|
||||
final receivedMessages =
|
||||
newMessagesNotOpened.where((x) => x.senderId != null).toList();
|
||||
final receivedMessages = newMessagesNotOpened
|
||||
.where((x) => x.senderId != null)
|
||||
.toList();
|
||||
|
||||
if (receivedMessages.isNotEmpty) {
|
||||
_previewMessages = receivedMessages;
|
||||
|
|
@ -145,8 +148,9 @@ class _UserListItem extends State<GroupListItem> {
|
|||
for (final message in _previewMessages) {
|
||||
if (message.mediaId != null &&
|
||||
!_previewMediaFiles.any((t) => t.mediaId == message.mediaId)) {
|
||||
final mediaFile =
|
||||
await twonlyDB.mediaFilesDao.getMediaFileById(message.mediaId!);
|
||||
final mediaFile = await twonlyDB.mediaFilesDao.getMediaFileById(
|
||||
message.mediaId!,
|
||||
);
|
||||
if (mediaFile != null) {
|
||||
_previewMediaFiles.add(mediaFile);
|
||||
}
|
||||
|
|
@ -171,8 +175,9 @@ class _UserListItem extends State<GroupListItem> {
|
|||
final msgs = _previewMessages
|
||||
.where((x) => x.type == MessageType.media.name)
|
||||
.toList();
|
||||
final mediaFile =
|
||||
await twonlyDB.mediaFilesDao.getMediaFileById(msgs.first.mediaId!);
|
||||
final mediaFile = await twonlyDB.mediaFilesDao.getMediaFileById(
|
||||
msgs.first.mediaId!,
|
||||
);
|
||||
if (mediaFile?.type != MediaType.audio) {
|
||||
if (mediaFile?.downloadState == null) return;
|
||||
if (mediaFile!.downloadState! == DownloadState.pending) {
|
||||
|
|
@ -190,10 +195,7 @@ class _UserListItem extends State<GroupListItem> {
|
|||
}
|
||||
}
|
||||
if (!mounted) return;
|
||||
await context.push(
|
||||
Routes.chatsMessages,
|
||||
extra: widget.group,
|
||||
);
|
||||
await context.push(Routes.chatsMessages(widget.group.groupId));
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -239,8 +241,9 @@ class _UserListItem extends State<GroupListItem> {
|
|||
leading: GestureDetector(
|
||||
onTap: () async {
|
||||
if (widget.group.isDirectChat) {
|
||||
final contacts = await twonlyDB.groupsDao
|
||||
.getGroupContact(widget.group.groupId);
|
||||
final contacts = await twonlyDB.groupsDao.getGroupContact(
|
||||
widget.group.groupId,
|
||||
);
|
||||
if (!context.mounted) return;
|
||||
await context.push(Routes.profileContact(contacts.first.userId));
|
||||
return;
|
||||
|
|
@ -253,12 +256,16 @@ class _UserListItem extends State<GroupListItem> {
|
|||
trailing: (widget.group.leftGroup)
|
||||
? null
|
||||
: IconButton(
|
||||
onPressed: () => context.push(
|
||||
_hasNonOpenedMediaFile
|
||||
? Routes.chatsMessages
|
||||
: Routes.chatsCameraSendTo,
|
||||
onPressed: () {
|
||||
if (_hasNonOpenedMediaFile) {
|
||||
context.push(Routes.chatsMessages(widget.group.groupId));
|
||||
} else {
|
||||
context.push(
|
||||
Routes.chatsCameraSendTo,
|
||||
extra: widget.group,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
icon: FaIcon(
|
||||
_hasNonOpenedMediaFile
|
||||
? FontAwesomeIcons.solidComments
|
||||
|
|
|
|||
|
|
@ -23,40 +23,11 @@ import 'package:twonly/src/views/components/blink.component.dart';
|
|||
import 'package:twonly/src/views/components/flame.dart';
|
||||
import 'package:twonly/src/views/components/verified_shield.dart';
|
||||
|
||||
Color getMessageColor(Message message) {
|
||||
return (message.senderId == null)
|
||||
? const Color.fromARGB(255, 58, 136, 102)
|
||||
: const Color.fromARGB(233, 68, 137, 255);
|
||||
}
|
||||
|
||||
class ChatItem {
|
||||
const ChatItem._({
|
||||
this.message,
|
||||
this.date,
|
||||
this.groupAction,
|
||||
});
|
||||
factory ChatItem.date(DateTime date) {
|
||||
return ChatItem._(date: date);
|
||||
}
|
||||
factory ChatItem.message(Message message) {
|
||||
return ChatItem._(message: message);
|
||||
}
|
||||
factory ChatItem.groupAction(GroupHistory groupAction) {
|
||||
return ChatItem._(groupAction: groupAction);
|
||||
}
|
||||
final GroupHistory? groupAction;
|
||||
final Message? message;
|
||||
final DateTime? date;
|
||||
bool get isMessage => message != null;
|
||||
bool get isDate => date != null;
|
||||
bool get isGroupAction => groupAction != null;
|
||||
}
|
||||
|
||||
/// Displays detailed information about a SampleItem.
|
||||
class ChatMessagesView extends StatefulWidget {
|
||||
const ChatMessagesView(this.group, {super.key});
|
||||
const ChatMessagesView(this.groupId, {super.key});
|
||||
|
||||
final Group group;
|
||||
final String groupId;
|
||||
|
||||
@override
|
||||
State<ChatMessagesView> createState() => _ChatMessagesViewState();
|
||||
|
|
@ -64,12 +35,13 @@ class ChatMessagesView extends StatefulWidget {
|
|||
|
||||
class _ChatMessagesViewState extends State<ChatMessagesView> {
|
||||
HashSet<int> alreadyReportedOpened = HashSet<int>();
|
||||
late Group group;
|
||||
late StreamSubscription<Group?> userSub;
|
||||
late StreamSubscription<List<Message>> messageSub;
|
||||
StreamSubscription<List<GroupHistory>>? groupActionsSub;
|
||||
StreamSubscription<List<Contact>>? contactSub;
|
||||
|
||||
Group? _group;
|
||||
|
||||
Map<int, Contact> userIdToContact = {};
|
||||
|
||||
List<ChatItem> messages = [];
|
||||
|
|
@ -85,7 +57,6 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
group = widget.group;
|
||||
textFieldFocus = FocusNode();
|
||||
initStreams();
|
||||
}
|
||||
|
|
@ -102,16 +73,18 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
|||
Mutex protectMessageUpdating = Mutex();
|
||||
|
||||
Future<void> initStreams() async {
|
||||
final groupStream = twonlyDB.groupsDao.watchGroup(group.groupId);
|
||||
final groupStream = twonlyDB.groupsDao.watchGroup(widget.groupId);
|
||||
userSub = groupStream.listen((newGroup) {
|
||||
if (newGroup == null) return;
|
||||
setState(() {
|
||||
group = newGroup;
|
||||
});
|
||||
_group = newGroup;
|
||||
});
|
||||
|
||||
if (!widget.group.isDirectChat) {
|
||||
final actionsStream = twonlyDB.groupsDao.watchGroupActions(group.groupId);
|
||||
protectMessageUpdating.protect(() async {
|
||||
if (groupActionsSub == null && !newGroup.isDirectChat) {
|
||||
final actionsStream = twonlyDB.groupsDao.watchGroupActions(
|
||||
newGroup.groupId,
|
||||
);
|
||||
groupActionsSub = actionsStream.listen((update) async {
|
||||
groupActions = update;
|
||||
await setMessages(allMessages, update);
|
||||
|
|
@ -124,8 +97,10 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
|||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
final msgStream = twonlyDB.messagesDao.watchByGroupId(group.groupId);
|
||||
final msgStream = twonlyDB.messagesDao.watchByGroupId(widget.groupId);
|
||||
messageSub = msgStream.listen((update) async {
|
||||
allMessages = update;
|
||||
await protectMessageUpdating.protect(() async {
|
||||
|
|
@ -153,8 +128,9 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
|||
if (groupHistoryIndex < groupActions.length) {
|
||||
for (; groupHistoryIndex < groupActions.length; groupHistoryIndex++) {
|
||||
if (msg.createdAt.isAfter(groupActions[groupHistoryIndex].actionAt)) {
|
||||
chatItems
|
||||
.add(ChatItem.groupAction(groupActions[groupHistoryIndex]));
|
||||
chatItems.add(
|
||||
ChatItem.groupAction(groupActions[groupHistoryIndex]),
|
||||
);
|
||||
// groupHistoryIndex++;
|
||||
} else {
|
||||
break;
|
||||
|
|
@ -230,6 +206,8 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_group == null) return Container();
|
||||
final group = _group!;
|
||||
return GestureDetector(
|
||||
onTap: () => FocusScope.of(context).unfocus(),
|
||||
child: Scaffold(
|
||||
|
|
@ -237,12 +215,14 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
|||
title: GestureDetector(
|
||||
onTap: () async {
|
||||
if (group.isDirectChat) {
|
||||
final member =
|
||||
await twonlyDB.groupsDao.getAllGroupMembers(group.groupId);
|
||||
final member = await twonlyDB.groupsDao.getAllGroupMembers(
|
||||
group.groupId,
|
||||
);
|
||||
if (!context.mounted) return;
|
||||
if (member.isEmpty) return;
|
||||
await context
|
||||
.push(Routes.profileContact(member.first.contactId));
|
||||
await context.push(
|
||||
Routes.profileContact(member.first.contactId),
|
||||
);
|
||||
} else {
|
||||
await context.push(Routes.profileGroup(group.groupId));
|
||||
}
|
||||
|
|
@ -372,3 +352,32 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
Color getMessageColor(Message message) {
|
||||
return (message.senderId == null)
|
||||
? const Color.fromARGB(255, 58, 136, 102)
|
||||
: const Color.fromARGB(233, 68, 137, 255);
|
||||
}
|
||||
|
||||
class ChatItem {
|
||||
const ChatItem._({
|
||||
this.message,
|
||||
this.date,
|
||||
this.groupAction,
|
||||
});
|
||||
factory ChatItem.date(DateTime date) {
|
||||
return ChatItem._(date: date);
|
||||
}
|
||||
factory ChatItem.message(Message message) {
|
||||
return ChatItem._(message: message);
|
||||
}
|
||||
factory ChatItem.groupAction(GroupHistory groupAction) {
|
||||
return ChatItem._(groupAction: groupAction);
|
||||
}
|
||||
final GroupHistory? groupAction;
|
||||
final Message? message;
|
||||
final DateTime? date;
|
||||
bool get isMessage => message != null;
|
||||
bool get isDate => date != null;
|
||||
bool get isGroupAction => groupAction != null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,8 +102,9 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
}
|
||||
|
||||
Future<void> asyncLoadNextMedia(bool firstRun) async {
|
||||
final messages =
|
||||
twonlyDB.messagesDao.watchMediaNotOpened(widget.group.groupId);
|
||||
final messages = twonlyDB.messagesDao.watchMediaNotOpened(
|
||||
widget.group.groupId,
|
||||
);
|
||||
|
||||
_subscription = messages.listen((messages) async {
|
||||
for (final msg in messages) {
|
||||
|
|
@ -121,8 +122,9 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
|
||||
/// If the messages was already there just replace it and go to the next...
|
||||
|
||||
final index =
|
||||
allMediaFiles.indexWhere((m) => m.messageId == msg.messageId);
|
||||
final index = allMediaFiles.indexWhere(
|
||||
(m) => m.messageId == msg.messageId,
|
||||
);
|
||||
|
||||
if (index >= 1) {
|
||||
allMediaFiles[index] = msg;
|
||||
|
|
@ -160,7 +162,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
if (group != null &&
|
||||
group.draftMessage != null &&
|
||||
group.draftMessage != '') {
|
||||
context.replace(Routes.chatsMessages, extra: group);
|
||||
context.replace(Routes.chatsMessages(group.groupId));
|
||||
} else {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
|
@ -190,8 +192,9 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
|
||||
unawaited(flutterLocalNotificationsPlugin.cancelAll());
|
||||
|
||||
final stream =
|
||||
twonlyDB.mediaFilesDao.watchMedia(allMediaFiles.first.mediaId!);
|
||||
final stream = twonlyDB.mediaFilesDao.watchMedia(
|
||||
allMediaFiles.first.mediaId!,
|
||||
);
|
||||
|
||||
var downloadTriggered = false;
|
||||
|
||||
|
|
@ -204,8 +207,9 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
});
|
||||
if (!downloadTriggered) {
|
||||
downloadTriggered = true;
|
||||
final mediaFile = await twonlyDB.mediaFilesDao
|
||||
.getMediaFileById(allMediaFiles.first.mediaId!);
|
||||
final mediaFile = await twonlyDB.mediaFilesDao.getMediaFileById(
|
||||
allMediaFiles.first.mediaId!,
|
||||
);
|
||||
if (mediaFile == null) return;
|
||||
await startDownloadMedia(mediaFile, true);
|
||||
unawaited(tryDownloadAllMediaFiles(force: true));
|
||||
|
|
@ -226,8 +230,9 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
setState(() {
|
||||
_showDownloadingLoader = false;
|
||||
});
|
||||
final currentMediaLocal =
|
||||
await MediaFileService.fromMediaId(allMediaFiles.first.mediaId!);
|
||||
final currentMediaLocal = await MediaFileService.fromMediaId(
|
||||
allMediaFiles.first.mediaId!,
|
||||
);
|
||||
if (currentMediaLocal == null || !mounted) return;
|
||||
|
||||
if (currentMediaLocal.mediaFile.requiresAuthentication) {
|
||||
|
|
@ -259,8 +264,9 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
});
|
||||
|
||||
if (!widget.group.isDirectChat) {
|
||||
final sender =
|
||||
await twonlyDB.contactsDao.getContactById(currentMessage!.senderId!);
|
||||
final sender = await twonlyDB.contactsDao.getContactById(
|
||||
currentMessage!.senderId!,
|
||||
);
|
||||
if (sender != null) {
|
||||
_currentMediaSender =
|
||||
'${getContactDisplayName(sender)} (${widget.group.groupName})';
|
||||
|
|
@ -285,24 +291,29 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
await videoController?.setLooping(
|
||||
currentMediaLocal.mediaFile.displayLimitInMilliseconds == null,
|
||||
);
|
||||
await videoController?.initialize().then((_) {
|
||||
await videoController
|
||||
?.initialize()
|
||||
.then((_) {
|
||||
if (videoController == null) return;
|
||||
videoController?.play();
|
||||
videoController?.addListener(() {
|
||||
setState(() {
|
||||
progress = 1 -
|
||||
progress =
|
||||
1 -
|
||||
videoController!.value.position.inSeconds /
|
||||
videoController!.value.duration.inSeconds;
|
||||
});
|
||||
if (currentMediaLocal.mediaFile.displayLimitInMilliseconds != null) {
|
||||
if (currentMediaLocal.mediaFile.displayLimitInMilliseconds !=
|
||||
null) {
|
||||
if (videoController?.value.position ==
|
||||
videoController?.value.duration) {
|
||||
nextMediaOrExit();
|
||||
}
|
||||
}
|
||||
});
|
||||
// ignore: invalid_return_type_for_catch_error, argument_type_not_assignable_to_error_handler
|
||||
}).catchError(Log.error);
|
||||
})
|
||||
// ignore: argument_type_not_assignable_to_error_handler, invalid_return_type_for_catch_error
|
||||
.catchError(Log.error);
|
||||
} else {
|
||||
if (currentMediaLocal.mediaFile.displayLimitInMilliseconds != null) {
|
||||
canBeSeenUntil = clock.now().add(
|
||||
|
|
@ -434,8 +445,8 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
height: 8,
|
||||
child: Center(
|
||||
child: EmojiAnimation(
|
||||
emoji:
|
||||
EmojiAnimation.animatedIcons.keys.toList()[index],
|
||||
emoji: EmojiAnimation.animatedIcons.keys
|
||||
.toList()[index],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -35,8 +35,9 @@ class _StartNewChatView extends State<StartNewChatView> {
|
|||
void initState() {
|
||||
super.initState();
|
||||
|
||||
contactSub =
|
||||
twonlyDB.contactsDao.watchAllAcceptedContacts().listen((update) async {
|
||||
contactSub = twonlyDB.contactsDao.watchAllAcceptedContacts().listen((
|
||||
update,
|
||||
) async {
|
||||
update.sort(
|
||||
(a, b) => getContactDisplayName(a).compareTo(getContactDisplayName(b)),
|
||||
);
|
||||
|
|
@ -46,8 +47,9 @@ class _StartNewChatView extends State<StartNewChatView> {
|
|||
await filterUsers();
|
||||
});
|
||||
|
||||
allNonDirectGroupsSub =
|
||||
twonlyDB.groupsDao.watchGroupsForStartNewChat().listen((update) async {
|
||||
allNonDirectGroupsSub = twonlyDB.groupsDao
|
||||
.watchGroupsForStartNewChat()
|
||||
.listen((update) async {
|
||||
setState(() {
|
||||
allNonDirectGroups = update;
|
||||
});
|
||||
|
|
@ -72,16 +74,16 @@ class _StartNewChatView extends State<StartNewChatView> {
|
|||
}
|
||||
final usersFiltered = allContacts
|
||||
.where(
|
||||
(user) => getContactDisplayName(user)
|
||||
.toLowerCase()
|
||||
.contains(searchUserName.value.text.toLowerCase()),
|
||||
(user) => getContactDisplayName(
|
||||
user,
|
||||
).toLowerCase().contains(searchUserName.value.text.toLowerCase()),
|
||||
)
|
||||
.toList();
|
||||
final groupsFiltered = allNonDirectGroups
|
||||
.where(
|
||||
(g) => g.groupName
|
||||
.toLowerCase()
|
||||
.contains(searchUserName.value.text.toLowerCase()),
|
||||
(g) => g.groupName.toLowerCase().contains(
|
||||
searchUserName.value.text.toLowerCase(),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
setState(() {
|
||||
|
|
@ -108,7 +110,7 @@ class _StartNewChatView extends State<StartNewChatView> {
|
|||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return ChatMessagesView(directChat!);
|
||||
return ChatMessagesView(directChat!.groupId);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
|
@ -119,7 +121,7 @@ class _StartNewChatView extends State<StartNewChatView> {
|
|||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return ChatMessagesView(group);
|
||||
return ChatMessagesView(group.groupId);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
|
@ -133,8 +135,12 @@ class _StartNewChatView extends State<StartNewChatView> {
|
|||
),
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(bottom: 40, left: 10, top: 20, right: 10),
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 40,
|
||||
left: 10,
|
||||
top: 20,
|
||||
right: 10,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
|
|
@ -167,8 +173,9 @@ class _StartNewChatView extends State<StartNewChatView> {
|
|||
size: 13,
|
||||
),
|
||||
),
|
||||
onTap: () => context
|
||||
.push(Routes.groupCreateSelectMember(null)),
|
||||
onTap: () => context.push(
|
||||
Routes.groupCreateSelectMember(null),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (i == 1) {
|
||||
|
|
|
|||
|
|
@ -46,10 +46,7 @@ class GroupContextMenu extends StatelessWidget {
|
|||
),
|
||||
ContextMenuItem(
|
||||
title: context.lang.contextMenuOpenChat,
|
||||
onTap: () => context.push(
|
||||
Routes.chatsMessages,
|
||||
extra: group,
|
||||
),
|
||||
onTap: () => context.push(Routes.chatsMessages(group.groupId)),
|
||||
icon: FontAwesomeIcons.comments,
|
||||
),
|
||||
if (!group.archived)
|
||||
|
|
|
|||
|
|
@ -36,8 +36,9 @@ class _ContactViewState extends State<ContactView> {
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
_contactSub =
|
||||
twonlyDB.contactsDao.watchContact(widget.userId).listen((update) {
|
||||
_contactSub = twonlyDB.contactsDao.watchContact(widget.userId).listen((
|
||||
update,
|
||||
) {
|
||||
setState(() {
|
||||
_contact = update;
|
||||
});
|
||||
|
|
@ -81,8 +82,9 @@ class _ContactViewState extends State<ContactView> {
|
|||
|
||||
final remove = await showAlertDialog(
|
||||
context,
|
||||
context.lang
|
||||
.contactRemoveTitle(getContactDisplayName(contact, maxLength: 20)),
|
||||
context.lang.contactRemoveTitle(
|
||||
getContactDisplayName(contact, maxLength: 20),
|
||||
),
|
||||
context.lang.contactRemoveBody,
|
||||
);
|
||||
if (remove) {
|
||||
|
|
@ -177,13 +179,11 @@ class _ContactViewState extends State<ContactView> {
|
|||
icon: FontAwesomeIcons.solidComments,
|
||||
text: context.lang.contactViewMessage,
|
||||
onTap: () async {
|
||||
final group =
|
||||
await twonlyDB.groupsDao.getDirectChat(contact.userId);
|
||||
if (group != null && context.mounted) {
|
||||
await context.push(
|
||||
Routes.chatsMessages,
|
||||
extra: group,
|
||||
final group = await twonlyDB.groupsDao.getDirectChat(
|
||||
contact.userId,
|
||||
);
|
||||
if (group != null && context.mounted) {
|
||||
await context.push(Routes.chatsMessages(group.groupId));
|
||||
}
|
||||
},
|
||||
),
|
||||
|
|
@ -196,8 +196,10 @@ class _ContactViewState extends State<ContactView> {
|
|||
|
||||
if (context.mounted && nickName != null && nickName != '') {
|
||||
final update = ContactsCompanion(nickName: Value(nickName));
|
||||
await twonlyDB.contactsDao
|
||||
.updateContact(contact.userId, update);
|
||||
await twonlyDB.contactsDao.updateContact(
|
||||
contact.userId,
|
||||
update,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
|
|
@ -247,8 +249,9 @@ Future<String?> showNicknameChangeDialog(
|
|||
BuildContext context,
|
||||
Contact contact,
|
||||
) {
|
||||
final controller =
|
||||
TextEditingController(text: getContactDisplayName(contact));
|
||||
final controller = TextEditingController(
|
||||
text: getContactDisplayName(contact),
|
||||
);
|
||||
|
||||
return showDialog<String>(
|
||||
context: context,
|
||||
|
|
@ -258,8 +261,9 @@ Future<String?> showNicknameChangeDialog(
|
|||
content: TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
decoration:
|
||||
InputDecoration(hintText: context.lang.contactNicknameNew),
|
||||
decoration: InputDecoration(
|
||||
hintText: context.lang.contactNicknameNew,
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
|
|
@ -271,8 +275,9 @@ Future<String?> showNicknameChangeDialog(
|
|||
TextButton(
|
||||
child: Text(context.lang.ok),
|
||||
onPressed: () {
|
||||
Navigator.of(context)
|
||||
.pop(controller.text); // Return the input text
|
||||
Navigator.of(
|
||||
context,
|
||||
).pop(controller.text); // Return the input text
|
||||
},
|
||||
),
|
||||
],
|
||||
|
|
@ -291,8 +296,9 @@ Future<String?> showReportDialog(
|
|||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title:
|
||||
Text(context.lang.reportUserTitle(getContactDisplayName(contact))),
|
||||
title: Text(
|
||||
context.lang.reportUserTitle(getContactDisplayName(contact)),
|
||||
),
|
||||
content: TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
|
|
|
|||
|
|
@ -124,14 +124,15 @@ class GroupMemberContextMenu extends StatelessWidget {
|
|||
ContextMenuItem(
|
||||
title: context.lang.contextMenuOpenChat,
|
||||
onTap: () async {
|
||||
final directChat =
|
||||
await twonlyDB.groupsDao.getDirectChat(contact.userId);
|
||||
final directChat = await twonlyDB.groupsDao.getDirectChat(
|
||||
contact.userId,
|
||||
);
|
||||
if (directChat == null) {
|
||||
// create
|
||||
return;
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
await context.push(Routes.chatsMessages, extra: directChat);
|
||||
await context.push(Routes.chatsMessages(directChat.groupId));
|
||||
},
|
||||
icon: FontAwesomeIcons.message,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -13,26 +13,32 @@ import 'package:twonly/src/utils/misc.dart';
|
|||
import 'package:twonly/src/utils/storage.dart';
|
||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||
|
||||
class NotificationView extends StatelessWidget {
|
||||
class NotificationView extends StatefulWidget {
|
||||
const NotificationView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(context.lang.settingsNotification),
|
||||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(context.lang.settingsNotifyTroubleshooting),
|
||||
subtitle: Text(context.lang.settingsNotifyTroubleshootingDesc),
|
||||
onTap: () async {
|
||||
await initFCMAfterAuthenticated();
|
||||
final storedToken = await (const FlutterSecureStorage()
|
||||
.read(key: SecureStorageKeys.googleFcm));
|
||||
State<NotificationView> createState() => _NotificationViewState();
|
||||
}
|
||||
|
||||
class _NotificationViewState extends State<NotificationView> {
|
||||
bool _isLoadingTroubleshooting = false;
|
||||
bool _isLoadingReset = false;
|
||||
bool _troubleshootingDidRun = false;
|
||||
|
||||
Future<void> _troubleshooting() async {
|
||||
setState(() {
|
||||
_isLoadingTroubleshooting = true;
|
||||
});
|
||||
|
||||
await initFCMAfterAuthenticated(force: true);
|
||||
|
||||
final storedToken = await (const FlutterSecureStorage().read(
|
||||
key: SecureStorageKeys.googleFcm,
|
||||
));
|
||||
|
||||
await setupNotificationWithUsers(force: true);
|
||||
if (!context.mounted) return;
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
if (storedToken == null) {
|
||||
final platform = Platform.isAndroid ? "Google's" : "Apple's";
|
||||
|
|
@ -64,9 +70,66 @@ class NotificationView extends StatelessWidget {
|
|||
pushData,
|
||||
);
|
||||
}
|
||||
_troubleshootingDidRun = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
setState(() {
|
||||
_isLoadingTroubleshooting = false;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> resetTokens() async {
|
||||
setState(() {
|
||||
_isLoadingReset = true;
|
||||
});
|
||||
await resetFCMTokens();
|
||||
if (!mounted) return;
|
||||
await showAlertDialog(
|
||||
context,
|
||||
context.lang.settingsNotifyResetTitleReset,
|
||||
context.lang.settingsNotifyResetTitleResetDesc,
|
||||
);
|
||||
setState(() {
|
||||
_isLoadingReset = false;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(context.lang.settingsNotification),
|
||||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(context.lang.settingsNotifyTroubleshooting),
|
||||
subtitle: Text(context.lang.settingsNotifyTroubleshootingDesc),
|
||||
trailing: _isLoadingTroubleshooting
|
||||
? const SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
onTap: _isLoadingTroubleshooting ? null : _troubleshooting,
|
||||
),
|
||||
if (_troubleshootingDidRun)
|
||||
ListTile(
|
||||
title: Text(context.lang.settingsNotifyResetTitle),
|
||||
subtitle: Text(context.lang.settingsNotifyResetTitleSubtitle),
|
||||
trailing: _isLoadingReset
|
||||
? const SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
onTap: _isLoadingReset ? null : resetTokens,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
214
pubspec.lock
214
pubspec.lock
|
|
@ -13,10 +13,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: _flutterfire_internals
|
||||
sha256: afe15ce18a287d2f89da95566e62892df339b1936bbe9b83587df45b944ee72a
|
||||
sha256: f698de6eb8a0dd7a9a931bbfe13568e8b77e702eb2deb13dd83480c5373e7746
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.67"
|
||||
version: "1.3.68"
|
||||
adaptive_number:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
|
|
@ -84,10 +84,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
||||
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.0"
|
||||
version: "2.13.1"
|
||||
audio_waveforms:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -124,18 +124,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
sha256: "275bf6bb2a00a9852c28d4e0b410da1d833a734d57d39d44f94bfc895a484ec3"
|
||||
sha256: aadd943f4f8cc946882c954c187e6115a84c98c81ad1d9c6cbf0895a8c85da9c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.4"
|
||||
version: "4.0.5"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_config
|
||||
sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187"
|
||||
sha256: "4070d2a59f8eec34c97c86ceb44403834899075f66e8a9d59706f8e7834f6f71"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.3.0"
|
||||
build_daemon:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -148,10 +148,10 @@ packages:
|
|||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: "39ad4ca8a2876779737c60e4228b4bcd35d4352ef7e14e47514093edc012c734"
|
||||
sha256: "521daf8d189deb79ba474e43a696b41c49fb3987818dbacf3308f1e03673a75e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.1"
|
||||
version: "2.13.1"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -164,10 +164,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
sha256: "6ae8a6435a8c6520c7077b107e77f1fb4ba7009633259a4d49a8afd8e7efc5e9"
|
||||
sha256: "0730c18c770d05636a8f945c32a4d7d81cb6e0f0148c8db4ad12e7748f7e49af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.12.4"
|
||||
version: "8.12.5"
|
||||
cached_network_image:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -196,27 +196,27 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: camera
|
||||
sha256: "4142a19a38e388d3bab444227636610ba88982e36dff4552d5191a86f65dc437"
|
||||
sha256: "034c38cb8014d29698dcae6d20276688a1bf74e6487dfeb274d70ea05d5f7777"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.4"
|
||||
version: "0.12.0+1"
|
||||
camera_android_camerax:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
path: "packages/camera/camera_android_camerax"
|
||||
ref: "43b87faec960306f98d767253b9bf2cee61be630"
|
||||
resolved-ref: "43b87faec960306f98d767253b9bf2cee61be630"
|
||||
ref: e83fb3a27d4da2c37a3c8acbf2486283965b4f69
|
||||
resolved-ref: e83fb3a27d4da2c37a3c8acbf2486283965b4f69
|
||||
url: "https://github.com/otsmr/flutter-packages.git"
|
||||
source: git
|
||||
version: "0.6.25+1"
|
||||
version: "0.7.1+2"
|
||||
camera_avfoundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: camera_avfoundation
|
||||
sha256: "11b4aee2f5e5e038982e152b4a342c749b414aa27857899d20f4323e94cb5f0b"
|
||||
sha256: "90e4cc3fde331581a3b2d35d83be41dbb7393af0ab857eb27b732174289cb96d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.23+2"
|
||||
version: "0.10.1"
|
||||
camera_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -301,18 +301,18 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: connectivity_plus
|
||||
sha256: "33bae12a398f841c6cda09d1064212957265869104c478e5ad51e2fb26c3973c"
|
||||
sha256: b8fe52979ff12432ecf8f0abf6ff70410b1bb734be1c9e4f2f86807ad7166c79
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
version: "7.1.0"
|
||||
connectivity_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: connectivity_plus_platform_interface
|
||||
sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204"
|
||||
sha256: "3c09627c536d22fd24691a905cdd8b14520de69da52c7a97499c8be5284a32ed"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "2.1.0"
|
||||
convert:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -365,10 +365,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: "6f6b30cba0301e7b38f32bdc9a6bdae6f5921a55f0a1eb9450e1e6515645dbb2"
|
||||
sha256: "29f7ecc274a86d32920b1d9cfc7502fa87220da41ec60b55f329559d5732e2b2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.6"
|
||||
version: "3.1.7"
|
||||
dbus:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -381,10 +381,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: device_info_plus
|
||||
sha256: "4df8babf73058181227e18b08e6ea3520cf5fc5d796888d33b7cb0f33f984b7c"
|
||||
sha256: b4fed1b2835da9d670d7bed7db79ae2a94b0f5ad6312268158a9b5479abbacdd
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "12.3.0"
|
||||
version: "12.4.0"
|
||||
device_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -504,54 +504,78 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.3+5"
|
||||
firebase_app_installations:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_app_installations
|
||||
sha256: "9103cac19ec40561b49a023e8e583f007f77a499c2058f5a1d82ba480c2367d7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.1"
|
||||
firebase_app_installations_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_app_installations_platform_interface
|
||||
sha256: "0811d37b91c992cc0c98200cca79652d0375a16c58b364a4db5601571c198ee5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.4+67"
|
||||
firebase_app_installations_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_app_installations_web
|
||||
sha256: "02f2a96e85581bd1f78319b503dc92c80afa1527cbc299c7c921995b75595bbd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.7+4"
|
||||
firebase_core:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_core
|
||||
sha256: f0997fee80fbb6d2c658c5b88ae87ba1f9506b5b37126db64fc2e75d8e977fbb
|
||||
sha256: "2f988dab915efde3b3105268dbd69efce0e8570d767a218ccd914afd0c10c8cc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.0"
|
||||
version: "4.6.0"
|
||||
firebase_core_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_platform_interface
|
||||
sha256: cccb4f572325dc14904c02fcc7db6323ad62ba02536833dddb5c02cac7341c64
|
||||
sha256: "0ecda14c1bfc9ed8cac303dd0f8d04a320811b479362a9a4efb14fd331a473ce"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.2"
|
||||
version: "6.0.3"
|
||||
firebase_core_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_web
|
||||
sha256: "856ca92bf2d75a63761286ab8e791bda3a85184c2b641764433b619647acfca6"
|
||||
sha256: "1399ab1f0ac3b503d8a9be64a4c997fc066bbf33f701f42866e5569f26205ebe"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.5.0"
|
||||
version: "3.5.1"
|
||||
firebase_messaging:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_messaging
|
||||
sha256: bd17823b70e629877904d384841cda72ed2cc197517404c0c90da5c0ba786a8c
|
||||
sha256: "8dc372085b1647f05e3ec1b8bc1dada87c0062f93b2a6976f620eb85edc44f97"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "16.1.2"
|
||||
version: "16.1.3"
|
||||
firebase_messaging_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_messaging_platform_interface
|
||||
sha256: "550435235cc7d53683f32bf0762c28ef8cfc20a8d36318a033676ae09526d7fb"
|
||||
sha256: "6ea10f7df747542b17679d5939213c09163aab9c301b2f9b858cb55f38efdb54"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.7.7"
|
||||
version: "4.7.8"
|
||||
firebase_messaging_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_messaging_web
|
||||
sha256: "6b1b93ed90309fbce91c219e3cd32aa831e8eccaf4a61f3afaea1625479275d2"
|
||||
sha256: "1f9798c8021ccf22b7e43e7fba81becd42252cb168228379fcabb7c2ef7dd638"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.3"
|
||||
version: "4.1.4"
|
||||
fixnum:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -727,10 +751,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: flutter_plugin_android_lifecycle
|
||||
sha256: ee8068e0e1cd16c4a82714119918efdeed33b3ba7772c54b5d094ab53f9b7fd1
|
||||
sha256: "38d1c268de9097ff59cf0e844ac38759fc78f76836d37edad06fa21e182055a0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.33"
|
||||
version: "2.0.34"
|
||||
flutter_secure_storage:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -790,10 +814,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_svg
|
||||
sha256: "87fbd7c534435b6c5d9d98b01e1fd527812b82e68ddd8bd35fc45ed0fa8f0a95"
|
||||
sha256: "1ded017b39c8e15c8948ea855070a5ff8ff8b3d5e83f3446e02d6bb12add7ad9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
version: "2.2.4"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
|
|
@ -848,10 +872,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: go_router
|
||||
sha256: "7974313e217a7771557add6ff2238acb63f635317c35fa590d348fb238f00896"
|
||||
sha256: "48fb2f42ad057476fa4b733cb95e9f9ea7b0b010bb349ea491dca7dbdb18ffc4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "17.1.0"
|
||||
version: "17.2.0"
|
||||
google_mlkit_barcode_scanning:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -917,10 +941,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: hooks
|
||||
sha256: "7a08a0d684cb3b8fb604b78455d5d352f502b68079f7b80b831c62220ab0a4f6"
|
||||
sha256: e79ed1e8e1929bc6ecb6ec85f0cb519c887aa5b423705ded0d0f2d9226def388
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
version: "1.0.2"
|
||||
html:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -973,10 +997,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_android
|
||||
sha256: eda9b91b7e266d9041084a42d605a74937d996b87083395c5e47835916a86156
|
||||
sha256: "66810af8e99b2657ee98e5c6f02064f69bb63f7a70e343937f70946c5f8c6622"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.13+14"
|
||||
version: "0.8.13+16"
|
||||
image_picker_for_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1037,10 +1061,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: in_app_purchase_android
|
||||
sha256: abb254ae159a5a9d4f867795ecb076864faeba59ce015ab81d4cca380f23df45
|
||||
sha256: "634bee4734b17fe55f370f0ac07a22431a9666e0f3a870c6d20350856e8bbf71"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.0+8"
|
||||
version: "0.4.0+10"
|
||||
in_app_purchase_platform_interface:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
|
@ -1053,10 +1077,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: in_app_purchase_storekit
|
||||
sha256: "2f1a1db44798158076ced07d401b349880dd24a29c7c50a1b1a0de230b7f2f62"
|
||||
sha256: "1d512809edd9f12ff88fce4596a13a18134e2499013f4d6a8894b04699363c93"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.8"
|
||||
version: "0.4.8+1"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1100,10 +1124,10 @@ packages:
|
|||
dependency: "direct dev"
|
||||
description:
|
||||
name: json_serializable
|
||||
sha256: "44729f5c45748e6748f6b9a57ab8f7e4336edc8ae41fc295070e3814e616a6c0"
|
||||
sha256: fbcf404b03520e6e795f6b9b39badb2b788407dfc0a50cf39158a6ae1ca78925
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.13.0"
|
||||
version: "6.13.1"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1155,10 +1179,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: local_auth_android
|
||||
sha256: dc9663a7bc8ac33d7d988e63901974f63d527ebef260eabd19c479447cc9c911
|
||||
sha256: b41970749c2d43791790724b76917eeee1e90de76e6b0eec3edca03a329bf44c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
version: "2.0.7"
|
||||
local_auth_darwin:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1241,10 +1265,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: native_toolchain_c
|
||||
sha256: "89e83885ba09da5fdf2cdacc8002a712ca238c28b7f717910b34bcd27b0d03ac"
|
||||
sha256: "6ba77bb18063eebe9de401f5e6437e95e1438af0a87a3a39084fbd37c90df572"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.17.4"
|
||||
version: "0.17.6"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1303,10 +1327,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: package_info_plus
|
||||
sha256: f69da0d3189a4b4ceaeb1a3defb0f329b3b352517f52bed4290f83d4f06bc08d
|
||||
sha256: "468c26b4254ab01979fa5e4a98cb343ea3631b9acee6f21028997419a80e1a20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.0.0"
|
||||
version: "9.0.1"
|
||||
package_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1343,10 +1367,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e
|
||||
sha256: "149441ca6e4f38193b2e004c0ca6376a3d11f51fa5a77552d8bd4d2b0c0912ba"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.22"
|
||||
version: "2.2.23"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1485,10 +1509,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: pro_video_editor
|
||||
sha256: "5aa37aed1399333a3ac4b78ce00c7dcba77c5e407b6420960bba43751895fa22"
|
||||
sha256: cfed1424b3ca3d5981cc81efdd20b844c995c0ad2818e185eb5bc06a8674f728
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.2"
|
||||
version: "1.14.2"
|
||||
protobuf:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1570,26 +1594,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: sentry
|
||||
sha256: "605ad1f6f1ae5b72018cbe8fc20f490fa3bd53e58882e5579566776030d8c8c1"
|
||||
sha256: "288aee3d35f252ac0dc3a4b0accbbe7212fa2867604027f2cc5bc65334afd743"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.14.0"
|
||||
version: "9.16.0"
|
||||
sentry_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sentry_flutter
|
||||
sha256: "7fd0fb80050c1f6a77ae185bda997a76d384326d6777cf5137a6c38952c4ac7d"
|
||||
sha256: f9e87d5895cc437902aa2b081727ee7e46524fe7cc2e1910f535480a3eeb8bed
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.14.0"
|
||||
version: "9.16.0"
|
||||
share_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: share_plus
|
||||
sha256: "14c8860d4de93d3a7e53af51bff479598c4e999605290756bbbe45cf65b37840"
|
||||
sha256: "223873d106614442ea6f20db5a038685cc5b32a2fba81cdecaefbbae0523f7fa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "12.0.1"
|
||||
version: "12.0.2"
|
||||
share_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1602,18 +1626,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64"
|
||||
sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
version: "2.5.5"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "8374d6200ab33ac99031a852eba4c8eb2170c4bf20778b3e2c9eccb45384fb41"
|
||||
sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.21"
|
||||
version: "2.4.23"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1634,10 +1658,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||
sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
version: "2.4.2"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1679,18 +1703,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: source_gen
|
||||
sha256: "1d562a3c1f713904ebbed50d2760217fd8a51ca170ac4b05b0db490699dbac17"
|
||||
sha256: "732792cfd197d2161a65bb029606a46e0a18ff30ef9e141a7a82172b05ea8ecd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.0"
|
||||
version: "4.2.2"
|
||||
source_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_helper
|
||||
sha256: "4a85e90b50694e652075cbe4575665539d253e6ec10e46e76b45368ab5e3caae"
|
||||
sha256: "1d3b229b2934034fb2e691fbb3d53e0f75a4af7b1407f88425ed8f209bcb1b8f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.10"
|
||||
version: "1.3.11"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1711,10 +1735,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_android
|
||||
sha256: ecd684501ebc2ae9a83536e8b15731642b9570dc8623e0073d227d0ee2bfea88
|
||||
sha256: "881e28efdcc9950fd8e9bb42713dcf1103e62a2e7168f23c9338d82db13dec40"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2+2"
|
||||
version: "2.4.2+3"
|
||||
sqflite_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1751,10 +1775,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: sqlite3_flutter_libs
|
||||
sha256: "1e800ebe7f85a80a66adacaa6febe4d5f4d8b75f244e9838a27cb2ffc7aec08d"
|
||||
sha256: eeb9e3a45207649076b808f8a5a74d68770d0b7f26ccef6d5f43106eee5375ad
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.41"
|
||||
version: "0.5.42"
|
||||
sqlparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1847,10 +1871,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: "767344bf3063897b5cf0db830e94f904528e6dd50a6dfaf839f0abf509009611"
|
||||
sha256: "3bb000251e55d4a209aa0e2e563309dc9bb2befea2295fd0cec1f51760aac572"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.28"
|
||||
version: "6.3.29"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1911,10 +1935,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: vector_graphics
|
||||
sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6
|
||||
sha256: "81da85e9ca8885ade47f9685b953cb098970d11be4821ac765580a6607ea4373"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.19"
|
||||
version: "1.1.21"
|
||||
vector_graphics_codec:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1951,26 +1975,26 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: video_player
|
||||
sha256: "08bfba72e311d48219acad4e191b1f9c27ff8cf928f2c7234874592d9c9d7341"
|
||||
sha256: "48a7bdaa38a3d50ec10c78627abdbfad863fdf6f0d6e08c7c3c040cfd80ae36f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
version: "2.11.1"
|
||||
video_player_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_android
|
||||
sha256: "9862c67c4661c98f30fe707bc1a4f97d6a0faa76784f485d282668e4651a7ac3"
|
||||
sha256: "877a6c7ba772456077d7bfd71314629b3fe2b73733ce503fc77c3314d43a0ca0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.9.4"
|
||||
version: "2.9.5"
|
||||
video_player_avfoundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_avfoundation
|
||||
sha256: f93b93a3baa12ca0ff7d00ca8bc60c1ecd96865568a01ff0c18a99853ee201a5
|
||||
sha256: af0e5b8a7a4876fb37e7cc8cb2a011e82bb3ecfa45844ef672e32cb14a1f259e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.9.3"
|
||||
version: "2.9.4"
|
||||
video_player_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ dependencies:
|
|||
|
||||
|
||||
# Trusted publisher flutter.dev
|
||||
camera: ^0.11.2
|
||||
camera: ^0.12.0+1
|
||||
flutter_svg: ^2.0.17
|
||||
image_picker: ^1.1.2
|
||||
local_auth: ^3.0.0
|
||||
|
|
@ -54,6 +54,7 @@ dependencies:
|
|||
# Trustworthy publishers
|
||||
firebase_core: ^4.3.0 # firebase.google.com
|
||||
firebase_messaging: ^16.1.0 # firebase.google.com
|
||||
firebase_app_installations: ^0.4.1 # firebase.google.com
|
||||
json_annotation: ^4.9.0 # google.dev
|
||||
protobuf: ^4.0.0 # google.dev
|
||||
scrollable_positioned_list: ^0.3.8 # google.dev
|
||||
|
|
@ -152,7 +153,7 @@ dependency_overrides:
|
|||
git:
|
||||
url: https://github.com/otsmr/flutter-packages.git
|
||||
path: packages/camera/camera_android_camerax
|
||||
ref: 43b87faec960306f98d767253b9bf2cee61be630
|
||||
ref: e83fb3a27d4da2c37a3c8acbf2486283965b4f69
|
||||
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.
|
||||
|
|
|
|||
Loading…
Reference in a new issue